@runpod/typescript-api-sdk 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.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/THIRD_PARTY_NOTICES.txt +47 -0
- package/dist/index.cjs +1357 -0
- package/dist/index.d.cts +6449 -0
- package/dist/index.d.ts +6449 -0
- package/dist/index.js +1323 -0
- package/package.json +76 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1323 @@
|
|
|
1
|
+
// node_modules/.pnpm/openapi-fetch@0.17.0/node_modules/openapi-fetch/dist/index.mjs
|
|
2
|
+
var PATH_PARAM_RE = /\{[^{}]+\}/g;
|
|
3
|
+
var supportsRequestInitExt = () => {
|
|
4
|
+
return typeof process === "object" && Number.parseInt(process?.versions?.node?.substring(0, 2)) >= 18 && process.versions.undici;
|
|
5
|
+
};
|
|
6
|
+
function randomID() {
|
|
7
|
+
return Math.random().toString(36).slice(2, 11);
|
|
8
|
+
}
|
|
9
|
+
function createClient(clientOptions) {
|
|
10
|
+
let {
|
|
11
|
+
baseUrl = "",
|
|
12
|
+
Request: CustomRequest = globalThis.Request,
|
|
13
|
+
fetch: baseFetch = globalThis.fetch,
|
|
14
|
+
querySerializer: globalQuerySerializer,
|
|
15
|
+
bodySerializer: globalBodySerializer,
|
|
16
|
+
pathSerializer: globalPathSerializer,
|
|
17
|
+
headers: baseHeaders,
|
|
18
|
+
requestInitExt = void 0,
|
|
19
|
+
...baseOptions
|
|
20
|
+
} = { ...clientOptions };
|
|
21
|
+
requestInitExt = supportsRequestInitExt() ? requestInitExt : void 0;
|
|
22
|
+
baseUrl = removeTrailingSlash(baseUrl);
|
|
23
|
+
const globalMiddlewares = [];
|
|
24
|
+
async function coreFetch(schemaPath, fetchOptions) {
|
|
25
|
+
const {
|
|
26
|
+
baseUrl: localBaseUrl,
|
|
27
|
+
fetch = baseFetch,
|
|
28
|
+
Request: Request2 = CustomRequest,
|
|
29
|
+
headers,
|
|
30
|
+
params = {},
|
|
31
|
+
parseAs = "json",
|
|
32
|
+
querySerializer: requestQuerySerializer,
|
|
33
|
+
bodySerializer = globalBodySerializer ?? defaultBodySerializer,
|
|
34
|
+
pathSerializer: requestPathSerializer,
|
|
35
|
+
body,
|
|
36
|
+
middleware: requestMiddlewares = [],
|
|
37
|
+
...init
|
|
38
|
+
} = fetchOptions || {};
|
|
39
|
+
let finalBaseUrl = baseUrl;
|
|
40
|
+
if (localBaseUrl) {
|
|
41
|
+
finalBaseUrl = removeTrailingSlash(localBaseUrl) ?? baseUrl;
|
|
42
|
+
}
|
|
43
|
+
let querySerializer = typeof globalQuerySerializer === "function" ? globalQuerySerializer : createQuerySerializer(globalQuerySerializer);
|
|
44
|
+
if (requestQuerySerializer) {
|
|
45
|
+
querySerializer = typeof requestQuerySerializer === "function" ? requestQuerySerializer : createQuerySerializer({
|
|
46
|
+
...typeof globalQuerySerializer === "object" ? globalQuerySerializer : {},
|
|
47
|
+
...requestQuerySerializer
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const pathSerializer = requestPathSerializer || globalPathSerializer || defaultPathSerializer;
|
|
51
|
+
const serializedBody = body === void 0 ? void 0 : bodySerializer(
|
|
52
|
+
body,
|
|
53
|
+
// Note: we declare mergeHeaders() both here and below because it’s a bit of a chicken-or-egg situation:
|
|
54
|
+
// bodySerializer() needs all headers so we aren’t dropping ones set by the user, however,
|
|
55
|
+
// the result of this ALSO sets the lowest-priority content-type header. So we re-merge below,
|
|
56
|
+
// setting the content-type at the very beginning to be overwritten.
|
|
57
|
+
// Lastly, based on the way headers work, it’s not a simple “present-or-not” check becauase null intentionally un-sets headers.
|
|
58
|
+
mergeHeaders(baseHeaders, headers, params.header)
|
|
59
|
+
);
|
|
60
|
+
const finalHeaders = mergeHeaders(
|
|
61
|
+
// with no body, we should not to set Content-Type
|
|
62
|
+
serializedBody === void 0 || // if serialized body is FormData; browser will correctly set Content-Type & boundary expression
|
|
63
|
+
serializedBody instanceof FormData ? {} : {
|
|
64
|
+
"Content-Type": "application/json"
|
|
65
|
+
},
|
|
66
|
+
baseHeaders,
|
|
67
|
+
headers,
|
|
68
|
+
params.header
|
|
69
|
+
);
|
|
70
|
+
const finalMiddlewares = [...globalMiddlewares, ...requestMiddlewares];
|
|
71
|
+
const requestInit = {
|
|
72
|
+
redirect: "follow",
|
|
73
|
+
...baseOptions,
|
|
74
|
+
...init,
|
|
75
|
+
body: serializedBody,
|
|
76
|
+
headers: finalHeaders
|
|
77
|
+
};
|
|
78
|
+
let id;
|
|
79
|
+
let options;
|
|
80
|
+
let request = new Request2(
|
|
81
|
+
createFinalURL(schemaPath, { baseUrl: finalBaseUrl, params, querySerializer, pathSerializer }),
|
|
82
|
+
requestInit
|
|
83
|
+
);
|
|
84
|
+
let response;
|
|
85
|
+
for (const key in init) {
|
|
86
|
+
if (!(key in request)) {
|
|
87
|
+
request[key] = init[key];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (finalMiddlewares.length) {
|
|
91
|
+
id = randomID();
|
|
92
|
+
options = Object.freeze({
|
|
93
|
+
baseUrl: finalBaseUrl,
|
|
94
|
+
fetch,
|
|
95
|
+
parseAs,
|
|
96
|
+
querySerializer,
|
|
97
|
+
bodySerializer,
|
|
98
|
+
pathSerializer
|
|
99
|
+
});
|
|
100
|
+
for (const m of finalMiddlewares) {
|
|
101
|
+
if (m && typeof m === "object" && typeof m.onRequest === "function") {
|
|
102
|
+
const result = await m.onRequest({
|
|
103
|
+
request,
|
|
104
|
+
schemaPath,
|
|
105
|
+
params,
|
|
106
|
+
options,
|
|
107
|
+
id
|
|
108
|
+
});
|
|
109
|
+
if (result) {
|
|
110
|
+
if (result instanceof Request2) {
|
|
111
|
+
request = result;
|
|
112
|
+
} else if (result instanceof Response) {
|
|
113
|
+
response = result;
|
|
114
|
+
break;
|
|
115
|
+
} else {
|
|
116
|
+
throw new Error("onRequest: must return new Request() or Response() when modifying the request");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!response) {
|
|
123
|
+
try {
|
|
124
|
+
response = await fetch(request, requestInitExt);
|
|
125
|
+
} catch (error2) {
|
|
126
|
+
let errorAfterMiddleware = error2;
|
|
127
|
+
if (finalMiddlewares.length) {
|
|
128
|
+
for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
|
|
129
|
+
const m = finalMiddlewares[i];
|
|
130
|
+
if (m && typeof m === "object" && typeof m.onError === "function") {
|
|
131
|
+
const result = await m.onError({
|
|
132
|
+
request,
|
|
133
|
+
error: errorAfterMiddleware,
|
|
134
|
+
schemaPath,
|
|
135
|
+
params,
|
|
136
|
+
options,
|
|
137
|
+
id
|
|
138
|
+
});
|
|
139
|
+
if (result) {
|
|
140
|
+
if (result instanceof Response) {
|
|
141
|
+
errorAfterMiddleware = void 0;
|
|
142
|
+
response = result;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
if (result instanceof Error) {
|
|
146
|
+
errorAfterMiddleware = result;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
throw new Error("onError: must return new Response() or instance of Error");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (errorAfterMiddleware) {
|
|
155
|
+
throw errorAfterMiddleware;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (finalMiddlewares.length) {
|
|
159
|
+
for (let i = finalMiddlewares.length - 1; i >= 0; i--) {
|
|
160
|
+
const m = finalMiddlewares[i];
|
|
161
|
+
if (m && typeof m === "object" && typeof m.onResponse === "function") {
|
|
162
|
+
const result = await m.onResponse({
|
|
163
|
+
request,
|
|
164
|
+
response,
|
|
165
|
+
schemaPath,
|
|
166
|
+
params,
|
|
167
|
+
options,
|
|
168
|
+
id
|
|
169
|
+
});
|
|
170
|
+
if (result) {
|
|
171
|
+
if (!(result instanceof Response)) {
|
|
172
|
+
throw new Error("onResponse: must return new Response() when modifying the response");
|
|
173
|
+
}
|
|
174
|
+
response = result;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const contentLength = response.headers.get("Content-Length");
|
|
181
|
+
if (response.status === 204 || request.method === "HEAD" || contentLength === "0" && !response.headers.get("Transfer-Encoding")?.includes("chunked")) {
|
|
182
|
+
return response.ok ? { data: void 0, response } : { error: void 0, response };
|
|
183
|
+
}
|
|
184
|
+
if (response.ok) {
|
|
185
|
+
const getResponseData = async () => {
|
|
186
|
+
if (parseAs === "stream") {
|
|
187
|
+
return response.body;
|
|
188
|
+
}
|
|
189
|
+
if (parseAs === "json" && !contentLength) {
|
|
190
|
+
const raw = await response.text();
|
|
191
|
+
return raw ? JSON.parse(raw) : void 0;
|
|
192
|
+
}
|
|
193
|
+
return await response[parseAs]();
|
|
194
|
+
};
|
|
195
|
+
return { data: await getResponseData(), response };
|
|
196
|
+
}
|
|
197
|
+
let error = await response.text();
|
|
198
|
+
try {
|
|
199
|
+
error = JSON.parse(error);
|
|
200
|
+
} catch {
|
|
201
|
+
}
|
|
202
|
+
return { error, response };
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
request(method, url, init) {
|
|
206
|
+
return coreFetch(url, { ...init, method: method.toUpperCase() });
|
|
207
|
+
},
|
|
208
|
+
/** Call a GET endpoint */
|
|
209
|
+
GET(url, init) {
|
|
210
|
+
return coreFetch(url, { ...init, method: "GET" });
|
|
211
|
+
},
|
|
212
|
+
/** Call a PUT endpoint */
|
|
213
|
+
PUT(url, init) {
|
|
214
|
+
return coreFetch(url, { ...init, method: "PUT" });
|
|
215
|
+
},
|
|
216
|
+
/** Call a POST endpoint */
|
|
217
|
+
POST(url, init) {
|
|
218
|
+
return coreFetch(url, { ...init, method: "POST" });
|
|
219
|
+
},
|
|
220
|
+
/** Call a DELETE endpoint */
|
|
221
|
+
DELETE(url, init) {
|
|
222
|
+
return coreFetch(url, { ...init, method: "DELETE" });
|
|
223
|
+
},
|
|
224
|
+
/** Call a OPTIONS endpoint */
|
|
225
|
+
OPTIONS(url, init) {
|
|
226
|
+
return coreFetch(url, { ...init, method: "OPTIONS" });
|
|
227
|
+
},
|
|
228
|
+
/** Call a HEAD endpoint */
|
|
229
|
+
HEAD(url, init) {
|
|
230
|
+
return coreFetch(url, { ...init, method: "HEAD" });
|
|
231
|
+
},
|
|
232
|
+
/** Call a PATCH endpoint */
|
|
233
|
+
PATCH(url, init) {
|
|
234
|
+
return coreFetch(url, { ...init, method: "PATCH" });
|
|
235
|
+
},
|
|
236
|
+
/** Call a TRACE endpoint */
|
|
237
|
+
TRACE(url, init) {
|
|
238
|
+
return coreFetch(url, { ...init, method: "TRACE" });
|
|
239
|
+
},
|
|
240
|
+
/** Register middleware */
|
|
241
|
+
use(...middleware) {
|
|
242
|
+
for (const m of middleware) {
|
|
243
|
+
if (!m) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
if (typeof m !== "object" || !("onRequest" in m || "onResponse" in m || "onError" in m)) {
|
|
247
|
+
throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");
|
|
248
|
+
}
|
|
249
|
+
globalMiddlewares.push(m);
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
/** Unregister middleware */
|
|
253
|
+
eject(...middleware) {
|
|
254
|
+
for (const m of middleware) {
|
|
255
|
+
const i = globalMiddlewares.indexOf(m);
|
|
256
|
+
if (i !== -1) {
|
|
257
|
+
globalMiddlewares.splice(i, 1);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function serializePrimitiveParam(name, value, options) {
|
|
264
|
+
if (value === void 0 || value === null) {
|
|
265
|
+
return "";
|
|
266
|
+
}
|
|
267
|
+
if (typeof value === "object") {
|
|
268
|
+
throw new Error(
|
|
269
|
+
"Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
return `${name}=${options?.allowReserved === true ? value : encodeURIComponent(value)}`;
|
|
273
|
+
}
|
|
274
|
+
function serializeObjectParam(name, value, options) {
|
|
275
|
+
if (!value || typeof value !== "object") {
|
|
276
|
+
return "";
|
|
277
|
+
}
|
|
278
|
+
const values = [];
|
|
279
|
+
const joiner = {
|
|
280
|
+
simple: ",",
|
|
281
|
+
label: ".",
|
|
282
|
+
matrix: ";"
|
|
283
|
+
}[options.style] || "&";
|
|
284
|
+
if (options.style !== "deepObject" && options.explode === false) {
|
|
285
|
+
for (const k in value) {
|
|
286
|
+
values.push(k, options.allowReserved === true ? value[k] : encodeURIComponent(value[k]));
|
|
287
|
+
}
|
|
288
|
+
const final2 = values.join(",");
|
|
289
|
+
switch (options.style) {
|
|
290
|
+
case "form": {
|
|
291
|
+
return `${name}=${final2}`;
|
|
292
|
+
}
|
|
293
|
+
case "label": {
|
|
294
|
+
return `.${final2}`;
|
|
295
|
+
}
|
|
296
|
+
case "matrix": {
|
|
297
|
+
return `;${name}=${final2}`;
|
|
298
|
+
}
|
|
299
|
+
default: {
|
|
300
|
+
return final2;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
for (const k in value) {
|
|
305
|
+
const finalName = options.style === "deepObject" ? `${name}[${k}]` : k;
|
|
306
|
+
values.push(serializePrimitiveParam(finalName, value[k], options));
|
|
307
|
+
}
|
|
308
|
+
const final = values.join(joiner);
|
|
309
|
+
return options.style === "label" || options.style === "matrix" ? `${joiner}${final}` : final;
|
|
310
|
+
}
|
|
311
|
+
function serializeArrayParam(name, value, options) {
|
|
312
|
+
if (!Array.isArray(value)) {
|
|
313
|
+
return "";
|
|
314
|
+
}
|
|
315
|
+
if (options.explode === false) {
|
|
316
|
+
const joiner2 = { form: ",", spaceDelimited: "%20", pipeDelimited: "|" }[options.style] || ",";
|
|
317
|
+
const final = (options.allowReserved === true ? value : value.map((v) => encodeURIComponent(v))).join(joiner2);
|
|
318
|
+
switch (options.style) {
|
|
319
|
+
case "simple": {
|
|
320
|
+
return final;
|
|
321
|
+
}
|
|
322
|
+
case "label": {
|
|
323
|
+
return `.${final}`;
|
|
324
|
+
}
|
|
325
|
+
case "matrix": {
|
|
326
|
+
return `;${name}=${final}`;
|
|
327
|
+
}
|
|
328
|
+
// case "spaceDelimited":
|
|
329
|
+
// case "pipeDelimited":
|
|
330
|
+
default: {
|
|
331
|
+
return `${name}=${final}`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const joiner = { simple: ",", label: ".", matrix: ";" }[options.style] || "&";
|
|
336
|
+
const values = [];
|
|
337
|
+
for (const v of value) {
|
|
338
|
+
if (options.style === "simple" || options.style === "label") {
|
|
339
|
+
values.push(options.allowReserved === true ? v : encodeURIComponent(v));
|
|
340
|
+
} else {
|
|
341
|
+
values.push(serializePrimitiveParam(name, v, options));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return options.style === "label" || options.style === "matrix" ? `${joiner}${values.join(joiner)}` : values.join(joiner);
|
|
345
|
+
}
|
|
346
|
+
function createQuerySerializer(options) {
|
|
347
|
+
return function querySerializer(queryParams) {
|
|
348
|
+
const search = [];
|
|
349
|
+
if (queryParams && typeof queryParams === "object") {
|
|
350
|
+
for (const name in queryParams) {
|
|
351
|
+
const value = queryParams[name];
|
|
352
|
+
if (value === void 0 || value === null) {
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (Array.isArray(value)) {
|
|
356
|
+
if (value.length === 0) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
search.push(
|
|
360
|
+
serializeArrayParam(name, value, {
|
|
361
|
+
style: "form",
|
|
362
|
+
explode: true,
|
|
363
|
+
...options?.array,
|
|
364
|
+
allowReserved: options?.allowReserved || false
|
|
365
|
+
})
|
|
366
|
+
);
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (typeof value === "object") {
|
|
370
|
+
search.push(
|
|
371
|
+
serializeObjectParam(name, value, {
|
|
372
|
+
style: "deepObject",
|
|
373
|
+
explode: true,
|
|
374
|
+
...options?.object,
|
|
375
|
+
allowReserved: options?.allowReserved || false
|
|
376
|
+
})
|
|
377
|
+
);
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
search.push(serializePrimitiveParam(name, value, options));
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return search.join("&");
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
function defaultPathSerializer(pathname, pathParams) {
|
|
387
|
+
let nextURL = pathname;
|
|
388
|
+
for (const match of pathname.match(PATH_PARAM_RE) ?? []) {
|
|
389
|
+
let name = match.substring(1, match.length - 1);
|
|
390
|
+
let explode = false;
|
|
391
|
+
let style = "simple";
|
|
392
|
+
if (name.endsWith("*")) {
|
|
393
|
+
explode = true;
|
|
394
|
+
name = name.substring(0, name.length - 1);
|
|
395
|
+
}
|
|
396
|
+
if (name.startsWith(".")) {
|
|
397
|
+
style = "label";
|
|
398
|
+
name = name.substring(1);
|
|
399
|
+
} else if (name.startsWith(";")) {
|
|
400
|
+
style = "matrix";
|
|
401
|
+
name = name.substring(1);
|
|
402
|
+
}
|
|
403
|
+
if (!pathParams || pathParams[name] === void 0 || pathParams[name] === null) {
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
const value = pathParams[name];
|
|
407
|
+
if (Array.isArray(value)) {
|
|
408
|
+
nextURL = nextURL.replace(match, serializeArrayParam(name, value, { style, explode }));
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (typeof value === "object") {
|
|
412
|
+
nextURL = nextURL.replace(match, serializeObjectParam(name, value, { style, explode }));
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (style === "matrix") {
|
|
416
|
+
nextURL = nextURL.replace(match, `;${serializePrimitiveParam(name, value)}`);
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
nextURL = nextURL.replace(match, style === "label" ? `.${encodeURIComponent(value)}` : encodeURIComponent(value));
|
|
420
|
+
}
|
|
421
|
+
return nextURL;
|
|
422
|
+
}
|
|
423
|
+
function defaultBodySerializer(body, headers) {
|
|
424
|
+
if (body instanceof FormData) {
|
|
425
|
+
return body;
|
|
426
|
+
}
|
|
427
|
+
if (headers) {
|
|
428
|
+
const contentType = headers.get instanceof Function ? headers.get("Content-Type") ?? headers.get("content-type") : headers["Content-Type"] ?? headers["content-type"];
|
|
429
|
+
if (contentType === "application/x-www-form-urlencoded") {
|
|
430
|
+
return new URLSearchParams(body).toString();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
return JSON.stringify(body);
|
|
434
|
+
}
|
|
435
|
+
function createFinalURL(pathname, options) {
|
|
436
|
+
let finalURL = `${options.baseUrl}${pathname}`;
|
|
437
|
+
if (options.params?.path) {
|
|
438
|
+
finalURL = options.pathSerializer(finalURL, options.params.path);
|
|
439
|
+
}
|
|
440
|
+
let search = options.querySerializer(options.params.query ?? {});
|
|
441
|
+
if (search.startsWith("?")) {
|
|
442
|
+
search = search.substring(1);
|
|
443
|
+
}
|
|
444
|
+
if (search) {
|
|
445
|
+
finalURL += `?${search}`;
|
|
446
|
+
}
|
|
447
|
+
return finalURL;
|
|
448
|
+
}
|
|
449
|
+
function mergeHeaders(...allHeaders) {
|
|
450
|
+
const finalHeaders = new Headers();
|
|
451
|
+
for (const h of allHeaders) {
|
|
452
|
+
if (!h || typeof h !== "object") {
|
|
453
|
+
continue;
|
|
454
|
+
}
|
|
455
|
+
const iterator = h instanceof Headers ? h.entries() : Object.entries(h);
|
|
456
|
+
for (const [k, v] of iterator) {
|
|
457
|
+
if (v === null) {
|
|
458
|
+
finalHeaders.delete(k);
|
|
459
|
+
} else if (Array.isArray(v)) {
|
|
460
|
+
for (const v2 of v) {
|
|
461
|
+
finalHeaders.append(k, v2);
|
|
462
|
+
}
|
|
463
|
+
} else if (v !== void 0) {
|
|
464
|
+
finalHeaders.set(k, v);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return finalHeaders;
|
|
469
|
+
}
|
|
470
|
+
function removeTrailingSlash(url) {
|
|
471
|
+
if (url.endsWith("/")) {
|
|
472
|
+
return url.substring(0, url.length - 1);
|
|
473
|
+
}
|
|
474
|
+
return url;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// node_modules/.pnpm/structured-headers@2.1.0/node_modules/structured-headers/dist/util.js
|
|
478
|
+
var asciiRe = /^[\x20-\x7E]*$/;
|
|
479
|
+
var tokenRe = /^[a-zA-Z*][:/!#$%&'*+\-.^_`|~A-Za-z0-9]*$/;
|
|
480
|
+
function isAscii(str) {
|
|
481
|
+
return asciiRe.test(str);
|
|
482
|
+
}
|
|
483
|
+
function isValidTokenStr(str) {
|
|
484
|
+
return tokenRe.test(str);
|
|
485
|
+
}
|
|
486
|
+
function base64ToArrayBuffer(b64) {
|
|
487
|
+
const binaryString = atob(b64);
|
|
488
|
+
const len = binaryString.length;
|
|
489
|
+
const bytes = new Uint8Array(len);
|
|
490
|
+
for (let i = 0; i < len; i++) {
|
|
491
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
492
|
+
}
|
|
493
|
+
return bytes.buffer;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// node_modules/.pnpm/structured-headers@2.1.0/node_modules/structured-headers/dist/token.js
|
|
497
|
+
var Token = class {
|
|
498
|
+
constructor(value) {
|
|
499
|
+
if (!isValidTokenStr(value)) {
|
|
500
|
+
throw new TypeError("Invalid character in Token string. Tokens must start with *, A-Z and the rest of the string may only contain a-z, A-Z, 0-9, :/!#$%&'*+-.^_`|~");
|
|
501
|
+
}
|
|
502
|
+
this.value = value;
|
|
503
|
+
}
|
|
504
|
+
toString() {
|
|
505
|
+
return this.value;
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// node_modules/.pnpm/structured-headers@2.1.0/node_modules/structured-headers/dist/displaystring.js
|
|
510
|
+
var DisplayString = class {
|
|
511
|
+
constructor(value) {
|
|
512
|
+
this.value = value;
|
|
513
|
+
}
|
|
514
|
+
toString() {
|
|
515
|
+
return this.value;
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// node_modules/.pnpm/structured-headers@2.1.0/node_modules/structured-headers/dist/parser.js
|
|
520
|
+
function parseList(input) {
|
|
521
|
+
const parser = new Parser(input);
|
|
522
|
+
return parser.parseList();
|
|
523
|
+
}
|
|
524
|
+
var ParseError = class extends Error {
|
|
525
|
+
constructor(position, message) {
|
|
526
|
+
super(`Parse error: ${message} at offset ${position}`);
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
var Parser = class {
|
|
530
|
+
constructor(input) {
|
|
531
|
+
this.input = input;
|
|
532
|
+
this.pos = 0;
|
|
533
|
+
}
|
|
534
|
+
parseDictionary() {
|
|
535
|
+
this.skipWS();
|
|
536
|
+
const dictionary = /* @__PURE__ */ new Map();
|
|
537
|
+
while (!this.eof()) {
|
|
538
|
+
const thisKey = this.parseKey();
|
|
539
|
+
let member;
|
|
540
|
+
if (this.lookChar() === "=") {
|
|
541
|
+
this.pos++;
|
|
542
|
+
member = this.parseItemOrInnerList();
|
|
543
|
+
} else {
|
|
544
|
+
member = [true, this.parseParameters()];
|
|
545
|
+
}
|
|
546
|
+
dictionary.set(thisKey, member);
|
|
547
|
+
this.skipOWS();
|
|
548
|
+
if (this.eof()) {
|
|
549
|
+
return dictionary;
|
|
550
|
+
}
|
|
551
|
+
this.expectChar(",");
|
|
552
|
+
this.pos++;
|
|
553
|
+
this.skipOWS();
|
|
554
|
+
if (this.eof()) {
|
|
555
|
+
throw new ParseError(this.pos, "Dictionary contained a trailing comma");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return dictionary;
|
|
559
|
+
}
|
|
560
|
+
parseList() {
|
|
561
|
+
this.skipWS();
|
|
562
|
+
const members = [];
|
|
563
|
+
while (!this.eof()) {
|
|
564
|
+
members.push(this.parseItemOrInnerList());
|
|
565
|
+
this.skipOWS();
|
|
566
|
+
if (this.eof()) {
|
|
567
|
+
return members;
|
|
568
|
+
}
|
|
569
|
+
this.expectChar(",");
|
|
570
|
+
this.pos++;
|
|
571
|
+
this.skipOWS();
|
|
572
|
+
if (this.eof()) {
|
|
573
|
+
throw new ParseError(this.pos, "A list may not end with a trailing comma");
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return members;
|
|
577
|
+
}
|
|
578
|
+
parseItem(standaloneItem = true) {
|
|
579
|
+
if (standaloneItem)
|
|
580
|
+
this.skipWS();
|
|
581
|
+
const result = [
|
|
582
|
+
this.parseBareItem(),
|
|
583
|
+
this.parseParameters()
|
|
584
|
+
];
|
|
585
|
+
if (standaloneItem)
|
|
586
|
+
this.checkTrail();
|
|
587
|
+
return result;
|
|
588
|
+
}
|
|
589
|
+
parseItemOrInnerList() {
|
|
590
|
+
if (this.lookChar() === "(") {
|
|
591
|
+
return this.parseInnerList();
|
|
592
|
+
} else {
|
|
593
|
+
return this.parseItem(false);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
parseInnerList() {
|
|
597
|
+
this.expectChar("(");
|
|
598
|
+
this.pos++;
|
|
599
|
+
const innerList = [];
|
|
600
|
+
while (!this.eof()) {
|
|
601
|
+
this.skipWS();
|
|
602
|
+
if (this.lookChar() === ")") {
|
|
603
|
+
this.pos++;
|
|
604
|
+
return [
|
|
605
|
+
innerList,
|
|
606
|
+
this.parseParameters()
|
|
607
|
+
];
|
|
608
|
+
}
|
|
609
|
+
innerList.push(this.parseItem(false));
|
|
610
|
+
const nextChar = this.lookChar();
|
|
611
|
+
if (nextChar !== " " && nextChar !== ")") {
|
|
612
|
+
throw new ParseError(this.pos, "Expected a whitespace or ) after every item in an inner list");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
throw new ParseError(this.pos, "Could not find end of inner list");
|
|
616
|
+
}
|
|
617
|
+
parseBareItem() {
|
|
618
|
+
const char = this.lookChar();
|
|
619
|
+
if (char === void 0) {
|
|
620
|
+
throw new ParseError(this.pos, "Unexpected end of string");
|
|
621
|
+
}
|
|
622
|
+
if (char.match(/^[-0-9]/)) {
|
|
623
|
+
return this.parseIntegerOrDecimal();
|
|
624
|
+
}
|
|
625
|
+
if (char === '"') {
|
|
626
|
+
return this.parseString();
|
|
627
|
+
}
|
|
628
|
+
if (char.match(/^[A-Za-z*]/)) {
|
|
629
|
+
return this.parseToken();
|
|
630
|
+
}
|
|
631
|
+
if (char === ":") {
|
|
632
|
+
return this.parseByteSequence();
|
|
633
|
+
}
|
|
634
|
+
if (char === "?") {
|
|
635
|
+
return this.parseBoolean();
|
|
636
|
+
}
|
|
637
|
+
if (char === "@") {
|
|
638
|
+
return this.parseDate();
|
|
639
|
+
}
|
|
640
|
+
if (char === "%") {
|
|
641
|
+
return this.parseDisplayString();
|
|
642
|
+
}
|
|
643
|
+
throw new ParseError(this.pos, "Unexpected input");
|
|
644
|
+
}
|
|
645
|
+
parseParameters() {
|
|
646
|
+
const parameters = /* @__PURE__ */ new Map();
|
|
647
|
+
while (!this.eof()) {
|
|
648
|
+
const char = this.lookChar();
|
|
649
|
+
if (char !== ";") {
|
|
650
|
+
break;
|
|
651
|
+
}
|
|
652
|
+
this.pos++;
|
|
653
|
+
this.skipWS();
|
|
654
|
+
const key = this.parseKey();
|
|
655
|
+
let value = true;
|
|
656
|
+
if (this.lookChar() === "=") {
|
|
657
|
+
this.pos++;
|
|
658
|
+
value = this.parseBareItem();
|
|
659
|
+
}
|
|
660
|
+
parameters.set(key, value);
|
|
661
|
+
}
|
|
662
|
+
return parameters;
|
|
663
|
+
}
|
|
664
|
+
parseIntegerOrDecimal() {
|
|
665
|
+
let type = "integer";
|
|
666
|
+
let sign = 1;
|
|
667
|
+
let inputNumber = "";
|
|
668
|
+
if (this.lookChar() === "-") {
|
|
669
|
+
sign = -1;
|
|
670
|
+
this.pos++;
|
|
671
|
+
}
|
|
672
|
+
if (!isDigit(this.lookChar())) {
|
|
673
|
+
throw new ParseError(this.pos, "Expected a digit (0-9)");
|
|
674
|
+
}
|
|
675
|
+
while (!this.eof()) {
|
|
676
|
+
const char = this.getChar();
|
|
677
|
+
if (isDigit(char)) {
|
|
678
|
+
inputNumber += char;
|
|
679
|
+
} else if (type === "integer" && char === ".") {
|
|
680
|
+
if (inputNumber.length > 12) {
|
|
681
|
+
throw new ParseError(this.pos, "Exceeded maximum decimal length");
|
|
682
|
+
}
|
|
683
|
+
inputNumber += ".";
|
|
684
|
+
type = "decimal";
|
|
685
|
+
} else {
|
|
686
|
+
this.pos--;
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
if (type === "integer" && inputNumber.length > 15) {
|
|
690
|
+
throw new ParseError(this.pos, "Exceeded maximum integer length");
|
|
691
|
+
}
|
|
692
|
+
if (type === "decimal" && inputNumber.length > 16) {
|
|
693
|
+
throw new ParseError(this.pos, "Exceeded maximum decimal length");
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
if (type === "integer") {
|
|
697
|
+
return parseInt(inputNumber, 10) * sign;
|
|
698
|
+
} else {
|
|
699
|
+
if (inputNumber.endsWith(".")) {
|
|
700
|
+
throw new ParseError(this.pos, "Decimal cannot end on a period");
|
|
701
|
+
}
|
|
702
|
+
if (inputNumber.split(".")[1].length > 3) {
|
|
703
|
+
throw new ParseError(this.pos, "Number of digits after the decimal point cannot exceed 3");
|
|
704
|
+
}
|
|
705
|
+
return parseFloat(inputNumber) * sign;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
parseString() {
|
|
709
|
+
let outputString = "";
|
|
710
|
+
this.expectChar('"');
|
|
711
|
+
this.pos++;
|
|
712
|
+
while (!this.eof()) {
|
|
713
|
+
const char = this.getChar();
|
|
714
|
+
if (char === "\\") {
|
|
715
|
+
if (this.eof()) {
|
|
716
|
+
throw new ParseError(this.pos, "Unexpected end of input");
|
|
717
|
+
}
|
|
718
|
+
const nextChar = this.getChar();
|
|
719
|
+
if (nextChar !== "\\" && nextChar !== '"') {
|
|
720
|
+
throw new ParseError(this.pos, "A backslash must be followed by another backslash or double quote");
|
|
721
|
+
}
|
|
722
|
+
outputString += nextChar;
|
|
723
|
+
} else if (char === '"') {
|
|
724
|
+
return outputString;
|
|
725
|
+
} else if (!isAscii(char)) {
|
|
726
|
+
throw new ParseError(this.pos, "Strings must be in the ASCII range");
|
|
727
|
+
} else {
|
|
728
|
+
outputString += char;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
throw new ParseError(this.pos, "Unexpected end of input");
|
|
732
|
+
}
|
|
733
|
+
parseDisplayString() {
|
|
734
|
+
const chars = this.getChars(2);
|
|
735
|
+
if (chars !== '%"') {
|
|
736
|
+
throw new ParseError(this.pos, "Unexpected character. Display strings should start with %=");
|
|
737
|
+
}
|
|
738
|
+
const result = [];
|
|
739
|
+
while (!this.eof()) {
|
|
740
|
+
const char = this.getChar();
|
|
741
|
+
if (char.charCodeAt(0) <= 31 || char.charCodeAt(0) >= 127 && char.charCodeAt(0) <= 255) {
|
|
742
|
+
throw new ParseError(this.pos, "Invalid char found in DisplayString. Did you forget to escape?");
|
|
743
|
+
}
|
|
744
|
+
if (char === "%") {
|
|
745
|
+
const hexChars = this.getChars(2);
|
|
746
|
+
if (/^[0-9a-f]{2}$/.test(hexChars)) {
|
|
747
|
+
result.push(parseInt(hexChars, 16));
|
|
748
|
+
} else {
|
|
749
|
+
throw new ParseError(this.pos, `Unexpected sequence after % in DispalyString: "${hexChars}". Note that hexidecimals must be lowercase`);
|
|
750
|
+
}
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
if (char === '"') {
|
|
754
|
+
const textDecoder = new TextDecoder("utf-8", {
|
|
755
|
+
fatal: true
|
|
756
|
+
});
|
|
757
|
+
try {
|
|
758
|
+
return new DisplayString(textDecoder.decode(new Uint8Array(result)));
|
|
759
|
+
} catch {
|
|
760
|
+
throw new ParseError(this.pos, "Fatal error decoding UTF-8 sequence in Display String");
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
result.push(char.charCodeAt(0));
|
|
764
|
+
}
|
|
765
|
+
throw new ParseError(this.pos, "Unexpected end of input");
|
|
766
|
+
}
|
|
767
|
+
parseToken() {
|
|
768
|
+
let outputString = "";
|
|
769
|
+
while (!this.eof()) {
|
|
770
|
+
const char = this.lookChar();
|
|
771
|
+
if (char === void 0 || !/^[:/!#$%&'*+\-.^_`|~A-Za-z0-9]$/.test(char)) {
|
|
772
|
+
return new Token(outputString);
|
|
773
|
+
}
|
|
774
|
+
outputString += this.getChar();
|
|
775
|
+
}
|
|
776
|
+
return new Token(outputString);
|
|
777
|
+
}
|
|
778
|
+
parseByteSequence() {
|
|
779
|
+
this.expectChar(":");
|
|
780
|
+
this.pos++;
|
|
781
|
+
const endPos = this.input.indexOf(":", this.pos);
|
|
782
|
+
if (endPos === -1) {
|
|
783
|
+
throw new ParseError(this.pos, 'Could not find a closing ":" character to mark end of Byte Sequence');
|
|
784
|
+
}
|
|
785
|
+
const b64Content = this.input.substring(this.pos, endPos);
|
|
786
|
+
this.pos += b64Content.length + 1;
|
|
787
|
+
if (!/^[A-Za-z0-9+/=]*$/.test(b64Content)) {
|
|
788
|
+
throw new ParseError(this.pos, "ByteSequence does not contain a valid base64 string");
|
|
789
|
+
}
|
|
790
|
+
try {
|
|
791
|
+
return base64ToArrayBuffer(b64Content);
|
|
792
|
+
} catch {
|
|
793
|
+
throw new ParseError(this.pos, "ByteSequence does not contain a valid base64 string");
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
parseBoolean() {
|
|
797
|
+
this.expectChar("?");
|
|
798
|
+
this.pos++;
|
|
799
|
+
const char = this.getChar();
|
|
800
|
+
if (char === "1") {
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
803
|
+
if (char === "0") {
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
throw new ParseError(this.pos, 'Unexpected character. Expected a "1" or a "0"');
|
|
807
|
+
}
|
|
808
|
+
parseDate() {
|
|
809
|
+
this.expectChar("@");
|
|
810
|
+
this.pos++;
|
|
811
|
+
let sign = 1;
|
|
812
|
+
let inputNumber = "";
|
|
813
|
+
if (this.lookChar() === "-") {
|
|
814
|
+
sign = -1;
|
|
815
|
+
this.pos++;
|
|
816
|
+
}
|
|
817
|
+
if (!isDigit(this.lookChar())) {
|
|
818
|
+
throw new ParseError(this.pos, "Expected a digit (0-9)");
|
|
819
|
+
}
|
|
820
|
+
while (!this.eof()) {
|
|
821
|
+
const char = this.getChar();
|
|
822
|
+
if (isDigit(char)) {
|
|
823
|
+
inputNumber += char;
|
|
824
|
+
} else {
|
|
825
|
+
throw new ParseError(this.pos, "Expected a digit (0-9), whitespace or EOL");
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if (inputNumber.length > 15) {
|
|
829
|
+
throw new ParseError(this.pos, "Exceeded max length for a date value");
|
|
830
|
+
}
|
|
831
|
+
return new Date(parseInt(inputNumber, 10) * sign * 1e3);
|
|
832
|
+
}
|
|
833
|
+
parseKey() {
|
|
834
|
+
if (!this.lookChar()?.match(/^[a-z*]/)) {
|
|
835
|
+
throw new ParseError(this.pos, "A key must begin with an asterisk or letter (a-z)");
|
|
836
|
+
}
|
|
837
|
+
let outputString = "";
|
|
838
|
+
while (!this.eof()) {
|
|
839
|
+
const char = this.lookChar();
|
|
840
|
+
if (char === void 0 || !/^[a-z0-9_\-.*]$/.test(char)) {
|
|
841
|
+
return outputString;
|
|
842
|
+
}
|
|
843
|
+
outputString += this.getChar();
|
|
844
|
+
}
|
|
845
|
+
return outputString;
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Looks at the next character without advancing the cursor.
|
|
849
|
+
*
|
|
850
|
+
* Returns undefined if we were at the end of the string.
|
|
851
|
+
*/
|
|
852
|
+
lookChar() {
|
|
853
|
+
return this.input[this.pos];
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Checks if the next character is 'char', and fail otherwise.
|
|
857
|
+
*/
|
|
858
|
+
expectChar(char) {
|
|
859
|
+
if (this.lookChar() !== char) {
|
|
860
|
+
throw new ParseError(this.pos, `Expected ${char}`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
getChar() {
|
|
864
|
+
return this.input[this.pos++];
|
|
865
|
+
}
|
|
866
|
+
getChars(count) {
|
|
867
|
+
const result = this.input.substr(this.pos, count);
|
|
868
|
+
this.pos += count;
|
|
869
|
+
return result;
|
|
870
|
+
}
|
|
871
|
+
eof() {
|
|
872
|
+
return this.pos >= this.input.length;
|
|
873
|
+
}
|
|
874
|
+
// Advances the pointer to skip all whitespace.
|
|
875
|
+
skipOWS() {
|
|
876
|
+
while (true) {
|
|
877
|
+
const c = this.input.substr(this.pos, 1);
|
|
878
|
+
if (c === " " || c === " ") {
|
|
879
|
+
this.pos++;
|
|
880
|
+
} else {
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
// Advances the pointer to skip all spaces
|
|
886
|
+
skipWS() {
|
|
887
|
+
while (this.lookChar() === " ") {
|
|
888
|
+
this.pos++;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
// At the end of parsing, we need to make sure there are no bytes after the
|
|
892
|
+
// header except whitespace.
|
|
893
|
+
checkTrail() {
|
|
894
|
+
this.skipWS();
|
|
895
|
+
if (!this.eof()) {
|
|
896
|
+
throw new ParseError(this.pos, "Unexpected characters at end of input");
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
};
|
|
900
|
+
var isDigitRegex = /^[0-9]$/;
|
|
901
|
+
function isDigit(char) {
|
|
902
|
+
if (char === void 0)
|
|
903
|
+
return false;
|
|
904
|
+
return isDigitRegex.test(char);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/rate-limit.ts
|
|
908
|
+
function parseRetryAfter(header, now = Date.now()) {
|
|
909
|
+
if (header === null) return void 0;
|
|
910
|
+
const value = header.trim();
|
|
911
|
+
if (/^\d+$/.test(value)) {
|
|
912
|
+
const ms = Number(value) * 1e3;
|
|
913
|
+
return Number.isSafeInteger(ms) ? ms : void 0;
|
|
914
|
+
}
|
|
915
|
+
if (!/^[A-Za-z]{3},? /.test(value)) return void 0;
|
|
916
|
+
const date = Date.parse(value);
|
|
917
|
+
return Number.isFinite(date) ? Math.max(0, date - now) : void 0;
|
|
918
|
+
}
|
|
919
|
+
function secondsToMilliseconds(value) {
|
|
920
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) return void 0;
|
|
921
|
+
const milliseconds = value * 1e3;
|
|
922
|
+
return Number.isSafeInteger(milliseconds) ? milliseconds : void 0;
|
|
923
|
+
}
|
|
924
|
+
function getRateLimitInfo(headers, now = Date.now()) {
|
|
925
|
+
const retryAfter = headers.get("Retry-After");
|
|
926
|
+
const retryAfterMs = parseRetryAfter(retryAfter, now);
|
|
927
|
+
const info = {
|
|
928
|
+
windows: [],
|
|
929
|
+
retryAfterMs,
|
|
930
|
+
retryDelayMs: retryAfterMs,
|
|
931
|
+
incomplete: retryAfter !== null && retryAfterMs === void 0
|
|
932
|
+
};
|
|
933
|
+
const raw = headers.get("RateLimit");
|
|
934
|
+
if (raw === null) return info;
|
|
935
|
+
if (!raw.trim()) return { ...info, incomplete: true };
|
|
936
|
+
try {
|
|
937
|
+
for (const [value, params] of parseList(raw)) {
|
|
938
|
+
const name = value instanceof Token ? value.toString() : value;
|
|
939
|
+
const remaining = params.get("r");
|
|
940
|
+
if (typeof name !== "string" || typeof remaining !== "number" || !Number.isSafeInteger(remaining) || remaining < 0) {
|
|
941
|
+
info.incomplete = true;
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
const resetAfterMs = secondsToMilliseconds(params.get("t"));
|
|
945
|
+
info.windows.push({ name, remaining, resetAfterMs });
|
|
946
|
+
if (remaining !== 0) continue;
|
|
947
|
+
if (resetAfterMs === void 0) {
|
|
948
|
+
info.incomplete = true;
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
info.retryDelayMs = Math.max(info.retryDelayMs ?? 0, resetAfterMs);
|
|
952
|
+
}
|
|
953
|
+
} catch {
|
|
954
|
+
info.incomplete = true;
|
|
955
|
+
}
|
|
956
|
+
return info;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// src/retry.ts
|
|
960
|
+
var DEFAULTS = {
|
|
961
|
+
maxAttempts: 4,
|
|
962
|
+
minBackoffMs: 1e3,
|
|
963
|
+
maxBackoffMs: 3e4,
|
|
964
|
+
maxRetryAfterMs: 6e4
|
|
965
|
+
};
|
|
966
|
+
var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "PUT", "DELETE", "OPTIONS"]);
|
|
967
|
+
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([500, 502, 503, 504]);
|
|
968
|
+
function isAbortError(error) {
|
|
969
|
+
const name = error?.name;
|
|
970
|
+
return name === "AbortError" || name === "TimeoutError";
|
|
971
|
+
}
|
|
972
|
+
function shouldRetry(method, response) {
|
|
973
|
+
const idempotent = IDEMPOTENT_METHODS.has(method);
|
|
974
|
+
if (response === void 0) return idempotent;
|
|
975
|
+
if (response.status === 429) return true;
|
|
976
|
+
return idempotent && RETRYABLE_STATUS.has(response.status);
|
|
977
|
+
}
|
|
978
|
+
function sleepWithSignal(ms, signal, custom) {
|
|
979
|
+
return new Promise((resolve, reject) => {
|
|
980
|
+
let timer;
|
|
981
|
+
const cleanup = () => {
|
|
982
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
983
|
+
signal.removeEventListener("abort", onAbort);
|
|
984
|
+
};
|
|
985
|
+
const onAbort = () => {
|
|
986
|
+
cleanup();
|
|
987
|
+
reject(signal.reason);
|
|
988
|
+
};
|
|
989
|
+
if (signal.aborted) {
|
|
990
|
+
reject(signal.reason);
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
994
|
+
const done = () => {
|
|
995
|
+
cleanup();
|
|
996
|
+
resolve();
|
|
997
|
+
};
|
|
998
|
+
if (custom) {
|
|
999
|
+
Promise.resolve().then(() => {
|
|
1000
|
+
signal.throwIfAborted();
|
|
1001
|
+
return custom(ms);
|
|
1002
|
+
}).then(done, (error) => {
|
|
1003
|
+
cleanup();
|
|
1004
|
+
reject(error);
|
|
1005
|
+
});
|
|
1006
|
+
} else {
|
|
1007
|
+
timer = setTimeout(done, ms);
|
|
1008
|
+
}
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
1011
|
+
function createRetryFetch(options = {}) {
|
|
1012
|
+
const maxAttempts = options.maxAttempts ?? DEFAULTS.maxAttempts;
|
|
1013
|
+
const minBackoffMs = options.minBackoffMs ?? DEFAULTS.minBackoffMs;
|
|
1014
|
+
const maxBackoffMs = options.maxBackoffMs ?? DEFAULTS.maxBackoffMs;
|
|
1015
|
+
const maxRetryAfterMs = options.maxRetryAfterMs ?? DEFAULTS.maxRetryAfterMs;
|
|
1016
|
+
const baseFetch = options.fetch ?? globalThis.fetch;
|
|
1017
|
+
const random = options.random ?? Math.random;
|
|
1018
|
+
if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {
|
|
1019
|
+
throw new RangeError("maxAttempts must be a positive safe integer");
|
|
1020
|
+
}
|
|
1021
|
+
for (const [name, value] of Object.entries({ minBackoffMs, maxBackoffMs, maxRetryAfterMs })) {
|
|
1022
|
+
if (!Number.isFinite(value) || value < 0 || value > 2147483647) {
|
|
1023
|
+
throw new RangeError(`${name} must be finite and between 0 and 2147483647`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
if (minBackoffMs > maxBackoffMs) {
|
|
1027
|
+
throw new RangeError("minBackoffMs must not exceed maxBackoffMs");
|
|
1028
|
+
}
|
|
1029
|
+
return async (input, init) => {
|
|
1030
|
+
const original = new Request(input, init);
|
|
1031
|
+
for (let attempt = 1; ; attempt++) {
|
|
1032
|
+
original.signal.throwIfAborted();
|
|
1033
|
+
let response;
|
|
1034
|
+
let networkError;
|
|
1035
|
+
try {
|
|
1036
|
+
response = await baseFetch(original.clone());
|
|
1037
|
+
} catch (error) {
|
|
1038
|
+
original.signal.throwIfAborted();
|
|
1039
|
+
if (isAbortError(error)) throw error;
|
|
1040
|
+
networkError = error;
|
|
1041
|
+
}
|
|
1042
|
+
if (response !== void 0 && !shouldRetry(original.method, response)) return response;
|
|
1043
|
+
if (response === void 0 && !shouldRetry(original.method, void 0)) throw networkError;
|
|
1044
|
+
if (attempt >= maxAttempts) {
|
|
1045
|
+
if (response !== void 0) return response;
|
|
1046
|
+
throw networkError;
|
|
1047
|
+
}
|
|
1048
|
+
const rateLimit = response ? getRateLimitInfo(response.headers) : void 0;
|
|
1049
|
+
let delay = rateLimit?.retryDelayMs;
|
|
1050
|
+
if (response && (rateLimit?.incomplete || delay !== void 0 && delay > maxRetryAfterMs)) return response;
|
|
1051
|
+
if (delay === void 0) {
|
|
1052
|
+
const shift = Math.min(attempt - 1, 20);
|
|
1053
|
+
const backoff = Math.min(minBackoffMs * 2 ** shift, maxBackoffMs);
|
|
1054
|
+
delay = backoff / 2 + random() * (backoff / 2);
|
|
1055
|
+
}
|
|
1056
|
+
await response?.body?.cancel();
|
|
1057
|
+
await sleepWithSignal(delay, original.signal, options.sleep);
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// src/abort.ts
|
|
1063
|
+
function abortable(operation, signal) {
|
|
1064
|
+
if (!signal) return operation;
|
|
1065
|
+
return new Promise((resolve, reject) => {
|
|
1066
|
+
const onAbort = () => reject(signal.reason);
|
|
1067
|
+
const cleanup = () => signal.removeEventListener("abort", onAbort);
|
|
1068
|
+
if (signal.aborted) onAbort();
|
|
1069
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
1070
|
+
operation.then(
|
|
1071
|
+
(value) => {
|
|
1072
|
+
cleanup();
|
|
1073
|
+
resolve(value);
|
|
1074
|
+
},
|
|
1075
|
+
(error) => {
|
|
1076
|
+
cleanup();
|
|
1077
|
+
reject(error);
|
|
1078
|
+
}
|
|
1079
|
+
);
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// src/deadline.ts
|
|
1084
|
+
function wrapResponse(response, signal, cleanup) {
|
|
1085
|
+
const reader = response.body.getReader();
|
|
1086
|
+
let finished = false;
|
|
1087
|
+
let bodyController;
|
|
1088
|
+
function finish() {
|
|
1089
|
+
finished = true;
|
|
1090
|
+
signal.removeEventListener("abort", onAbort);
|
|
1091
|
+
cleanup();
|
|
1092
|
+
}
|
|
1093
|
+
function cancelReader(reason) {
|
|
1094
|
+
void reader.cancel(reason).catch(() => {
|
|
1095
|
+
});
|
|
1096
|
+
reader.releaseLock();
|
|
1097
|
+
}
|
|
1098
|
+
function onAbort() {
|
|
1099
|
+
if (finished) return;
|
|
1100
|
+
finish();
|
|
1101
|
+
bodyController.error(signal.reason);
|
|
1102
|
+
cancelReader(signal.reason);
|
|
1103
|
+
}
|
|
1104
|
+
const body = new ReadableStream({
|
|
1105
|
+
start(controller) {
|
|
1106
|
+
bodyController = controller;
|
|
1107
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1108
|
+
if (signal.aborted) onAbort();
|
|
1109
|
+
},
|
|
1110
|
+
async pull(controller) {
|
|
1111
|
+
try {
|
|
1112
|
+
const chunk = await reader.read();
|
|
1113
|
+
if (finished) return;
|
|
1114
|
+
if (!chunk.done) {
|
|
1115
|
+
controller.enqueue(chunk.value);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
finish();
|
|
1119
|
+
reader.releaseLock();
|
|
1120
|
+
controller.close();
|
|
1121
|
+
} catch (error) {
|
|
1122
|
+
if (finished) return;
|
|
1123
|
+
finish();
|
|
1124
|
+
reader.releaseLock();
|
|
1125
|
+
controller.error(error);
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
cancel(reason) {
|
|
1129
|
+
finish();
|
|
1130
|
+
cancelReader(reason);
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
const wrapped = new Response(body, {
|
|
1134
|
+
status: response.status,
|
|
1135
|
+
statusText: response.statusText,
|
|
1136
|
+
headers: response.headers
|
|
1137
|
+
});
|
|
1138
|
+
for (const key of ["url", "redirected", "type"]) {
|
|
1139
|
+
Object.defineProperty(wrapped, key, { value: response[key] });
|
|
1140
|
+
}
|
|
1141
|
+
return wrapped;
|
|
1142
|
+
}
|
|
1143
|
+
function createDeadlineFetch(fetchImpl, timeoutMs) {
|
|
1144
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) {
|
|
1145
|
+
throw new RangeError("timeoutMs must be positive and at most 2147483647");
|
|
1146
|
+
}
|
|
1147
|
+
return async (input, init) => {
|
|
1148
|
+
const original = new Request(input, init);
|
|
1149
|
+
original.signal.throwIfAborted();
|
|
1150
|
+
const controller = new AbortController();
|
|
1151
|
+
const onCallerAbort = () => controller.abort(original.signal.reason);
|
|
1152
|
+
const timer = setTimeout(() => {
|
|
1153
|
+
controller.abort(new DOMException(`Runpod request exceeded ${timeoutMs}ms`, "TimeoutError"));
|
|
1154
|
+
}, timeoutMs);
|
|
1155
|
+
const cleanup = () => {
|
|
1156
|
+
clearTimeout(timer);
|
|
1157
|
+
original.signal.removeEventListener("abort", onCallerAbort);
|
|
1158
|
+
};
|
|
1159
|
+
original.signal.addEventListener("abort", onCallerAbort, { once: true });
|
|
1160
|
+
try {
|
|
1161
|
+
const operation = Promise.resolve().then(async () => {
|
|
1162
|
+
controller.signal.throwIfAborted();
|
|
1163
|
+
const response2 = await fetchImpl(new Request(original, { signal: controller.signal }));
|
|
1164
|
+
if (controller.signal.aborted) void response2.body?.cancel().catch(() => {
|
|
1165
|
+
});
|
|
1166
|
+
controller.signal.throwIfAborted();
|
|
1167
|
+
return response2;
|
|
1168
|
+
});
|
|
1169
|
+
const response = await abortable(operation, controller.signal);
|
|
1170
|
+
if (response.body) return wrapResponse(response, controller.signal, cleanup);
|
|
1171
|
+
cleanup();
|
|
1172
|
+
return response;
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
cleanup();
|
|
1175
|
+
throw error;
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// src/logs.ts
|
|
1181
|
+
function parseLogEntry(raw) {
|
|
1182
|
+
try {
|
|
1183
|
+
const parsed = JSON.parse(raw);
|
|
1184
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { raw };
|
|
1185
|
+
const entry = parsed;
|
|
1186
|
+
const valid = ["source", "line", "ts", "raw"].every(
|
|
1187
|
+
(key) => !(key in entry) || typeof entry[key] === "string"
|
|
1188
|
+
);
|
|
1189
|
+
return valid ? entry : { raw };
|
|
1190
|
+
} catch {
|
|
1191
|
+
return { raw };
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function utf8Size(character) {
|
|
1195
|
+
const codePoint = character.codePointAt(0);
|
|
1196
|
+
if (codePoint <= 127) return 1;
|
|
1197
|
+
if (codePoint <= 2047) return 2;
|
|
1198
|
+
return codePoint <= 65535 ? 3 : 4;
|
|
1199
|
+
}
|
|
1200
|
+
var LogEventParser = class {
|
|
1201
|
+
constructor(maxEventBytes) {
|
|
1202
|
+
this.maxEventBytes = maxEventBytes;
|
|
1203
|
+
}
|
|
1204
|
+
maxEventBytes;
|
|
1205
|
+
line = "";
|
|
1206
|
+
dataLines = [];
|
|
1207
|
+
eventName = "";
|
|
1208
|
+
lastEventId;
|
|
1209
|
+
eventBytes = 0;
|
|
1210
|
+
afterCR = false;
|
|
1211
|
+
push(character) {
|
|
1212
|
+
if (this.afterCR && character === "\n") {
|
|
1213
|
+
this.afterCR = false;
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
this.afterCR = character === "\r";
|
|
1217
|
+
this.eventBytes += utf8Size(character);
|
|
1218
|
+
if (this.eventBytes > this.maxEventBytes) {
|
|
1219
|
+
throw new RangeError("SSE log event exceeds maxEventBytes");
|
|
1220
|
+
}
|
|
1221
|
+
if (character === "\r" || character === "\n") return this.consumeLine();
|
|
1222
|
+
this.line += character;
|
|
1223
|
+
}
|
|
1224
|
+
consumeLine() {
|
|
1225
|
+
const line = this.line;
|
|
1226
|
+
this.line = "";
|
|
1227
|
+
if (line === "") return this.finishEvent();
|
|
1228
|
+
if (line.startsWith(":")) return;
|
|
1229
|
+
const colon = line.indexOf(":");
|
|
1230
|
+
const field = colon < 0 ? line : line.slice(0, colon);
|
|
1231
|
+
const value = colon < 0 ? "" : line.slice(colon + 1).replace(/^ /, "");
|
|
1232
|
+
switch (field) {
|
|
1233
|
+
case "data":
|
|
1234
|
+
this.dataLines.push(value);
|
|
1235
|
+
break;
|
|
1236
|
+
case "event":
|
|
1237
|
+
this.eventName = value;
|
|
1238
|
+
break;
|
|
1239
|
+
case "id":
|
|
1240
|
+
if (!value.includes("\0")) this.lastEventId = value;
|
|
1241
|
+
break;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
finishEvent() {
|
|
1245
|
+
const event = this.dataLines.length ? {
|
|
1246
|
+
event: this.eventName || "message",
|
|
1247
|
+
id: this.lastEventId,
|
|
1248
|
+
data: parseLogEntry(this.dataLines.join("\n"))
|
|
1249
|
+
} : void 0;
|
|
1250
|
+
this.dataLines = [];
|
|
1251
|
+
this.eventName = "";
|
|
1252
|
+
this.eventBytes = 0;
|
|
1253
|
+
return event;
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
async function* iterateLogEvents(stream, options = {}) {
|
|
1257
|
+
const maxEventBytes = options.maxEventBytes ?? 1048576;
|
|
1258
|
+
if (!Number.isSafeInteger(maxEventBytes) || maxEventBytes <= 0) {
|
|
1259
|
+
throw new RangeError("maxEventBytes must be a positive safe integer");
|
|
1260
|
+
}
|
|
1261
|
+
const parser = new LogEventParser(maxEventBytes);
|
|
1262
|
+
const decoder = new TextDecoder();
|
|
1263
|
+
const reader = stream.getReader();
|
|
1264
|
+
const signal = options.signal;
|
|
1265
|
+
const onAbort = () => {
|
|
1266
|
+
void reader.cancel(signal?.reason).catch(() => {
|
|
1267
|
+
});
|
|
1268
|
+
};
|
|
1269
|
+
let finished = false;
|
|
1270
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1271
|
+
try {
|
|
1272
|
+
while (true) {
|
|
1273
|
+
signal?.throwIfAborted();
|
|
1274
|
+
const chunk = await abortable(reader.read(), signal);
|
|
1275
|
+
signal?.throwIfAborted();
|
|
1276
|
+
if (chunk.done) {
|
|
1277
|
+
finished = true;
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
for (const character of decoder.decode(chunk.value, { stream: true })) {
|
|
1281
|
+
signal?.throwIfAborted();
|
|
1282
|
+
const event = parser.push(character);
|
|
1283
|
+
if (event) yield event;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
} finally {
|
|
1287
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1288
|
+
if (!finished) void reader.cancel().catch(() => {
|
|
1289
|
+
});
|
|
1290
|
+
reader.releaseLock();
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
// src/index.ts
|
|
1295
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
1296
|
+
var DEFAULT_BASE_URL = "https://api.runpod.io";
|
|
1297
|
+
function createRunpodClient(options = {}) {
|
|
1298
|
+
const apiKey = options.apiKey ?? process.env.RUNPOD_API_KEY;
|
|
1299
|
+
if (!apiKey) {
|
|
1300
|
+
throw new Error("runpod: API key required (set RUNPOD_API_KEY or pass apiKey)");
|
|
1301
|
+
}
|
|
1302
|
+
const baseUrl = options.baseUrl?.trim() || process.env.RUNPOD_API_BASE_URL?.trim() || DEFAULT_BASE_URL;
|
|
1303
|
+
const baseFetch = options.fetch ?? globalThis.fetch;
|
|
1304
|
+
const retryFetch = options.retry === false ? baseFetch : createRetryFetch({ ...options.retry, fetch: baseFetch });
|
|
1305
|
+
const requestFetch = options.timeoutMs === false ? retryFetch : createDeadlineFetch(retryFetch, options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
1306
|
+
return createClient({
|
|
1307
|
+
baseUrl,
|
|
1308
|
+
fetch: requestFetch,
|
|
1309
|
+
// Generation rejects contracts with a different array query encoding.
|
|
1310
|
+
querySerializer: { array: { style: "form", explode: false } },
|
|
1311
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
export {
|
|
1315
|
+
DEFAULT_BASE_URL,
|
|
1316
|
+
DEFAULT_TIMEOUT_MS,
|
|
1317
|
+
createDeadlineFetch,
|
|
1318
|
+
createRetryFetch,
|
|
1319
|
+
createRunpodClient,
|
|
1320
|
+
getRateLimitInfo,
|
|
1321
|
+
iterateLogEvents,
|
|
1322
|
+
parseRetryAfter
|
|
1323
|
+
};
|