create-request 0.0.1-beta.2

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.
@@ -0,0 +1,616 @@
1
+ /**
2
+ * Enum for HTTP methods
3
+ */
4
+ var HttpMethod;
5
+ (function (HttpMethod) {
6
+ HttpMethod["GET"] = "GET";
7
+ HttpMethod["POST"] = "POST";
8
+ HttpMethod["PUT"] = "PUT";
9
+ HttpMethod["DELETE"] = "DELETE";
10
+ HttpMethod["PATCH"] = "PATCH";
11
+ HttpMethod["HEAD"] = "HEAD";
12
+ HttpMethod["OPTIONS"] = "OPTIONS";
13
+ })(HttpMethod || (HttpMethod = {}));
14
+ /**
15
+ * Enum for request priorities
16
+ */
17
+ var RequestPriority;
18
+ (function (RequestPriority) {
19
+ RequestPriority["HIGH"] = "high";
20
+ RequestPriority["LOW"] = "low";
21
+ RequestPriority["AUTO"] = "auto";
22
+ })(RequestPriority || (RequestPriority = {}));
23
+ /**
24
+ * Enum for credentials policies
25
+ */
26
+ var CredentialsPolicy;
27
+ (function (CredentialsPolicy) {
28
+ CredentialsPolicy["INCLUDE"] = "include";
29
+ CredentialsPolicy["SAME_ORIGIN"] = "same-origin";
30
+ CredentialsPolicy["OMIT"] = "omit";
31
+ })(CredentialsPolicy || (CredentialsPolicy = {}));
32
+ /**
33
+ * Enum for request modes
34
+ */
35
+ var RequestMode;
36
+ (function (RequestMode) {
37
+ RequestMode["CORS"] = "cors";
38
+ RequestMode["NO_CORS"] = "no-cors";
39
+ RequestMode["SAME_ORIGIN"] = "same-origin";
40
+ RequestMode["NAVIGATE"] = "navigate";
41
+ })(RequestMode || (RequestMode = {}));
42
+ /**
43
+ * Enum for redirect modes
44
+ */
45
+ var RedirectMode;
46
+ (function (RedirectMode) {
47
+ RedirectMode["FOLLOW"] = "follow";
48
+ RedirectMode["ERROR"] = "error";
49
+ RedirectMode["MANUAL"] = "manual";
50
+ })(RedirectMode || (RedirectMode = {}));
51
+ /**
52
+ * Enum for cache modes
53
+ */
54
+ var CacheMode;
55
+ (function (CacheMode) {
56
+ CacheMode["DEFAULT"] = "default";
57
+ CacheMode["NO_STORE"] = "no-store";
58
+ CacheMode["RELOAD"] = "reload";
59
+ CacheMode["NO_CACHE"] = "no-cache";
60
+ CacheMode["FORCE_CACHE"] = "force-cache";
61
+ CacheMode["ONLY_IF_CACHED"] = "only-if-cached";
62
+ })(CacheMode || (CacheMode = {}));
63
+
64
+ class RequestError extends Error {
65
+ constructor(message, url, method, options = {}) {
66
+ super(message);
67
+ this.name = "RequestError";
68
+ this.url = url;
69
+ this.method = method;
70
+ this.status = options.status;
71
+ this.response = options.response;
72
+ this.timeoutError = options.timeoutError;
73
+ // For better stack traces in modern environments
74
+ if (Error.captureStackTrace) {
75
+ Error.captureStackTrace(this, RequestError);
76
+ }
77
+ // Maintains proper prototype chain for instanceof checks
78
+ Object.setPrototypeOf(this, RequestError.prototype);
79
+ }
80
+ /**
81
+ * Static methods for creating specific types of RequestError
82
+ */
83
+ static timeout(url, method, timeoutMs) {
84
+ return new RequestError(`Request timed out after ${timeoutMs}ms`, url, method, {
85
+ timeoutError: true,
86
+ });
87
+ }
88
+ static fromResponse(response, url, method) {
89
+ return new RequestError(`Request failed with status ${response.status}`, url, method, {
90
+ status: response.status,
91
+ response,
92
+ });
93
+ }
94
+ static networkError(url, method, originalError) {
95
+ const error = new RequestError(`Network error: ${originalError.message}`, url, method);
96
+ error.stack = originalError.stack;
97
+ return error;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Wrapper for HTTP responses with methods to transform the response data
103
+ */
104
+ class ResponseWrapper {
105
+ constructor(response) {
106
+ this.response = response;
107
+ this.ok = response.status >= 200 && response.status < 300; // Standard Fetch API behavior
108
+ }
109
+ /**
110
+ * Get the raw Response object
111
+ */
112
+ get raw() {
113
+ return this.response;
114
+ }
115
+ /**
116
+ * Get the HTTP status code
117
+ */
118
+ get status() {
119
+ return this.response.status;
120
+ }
121
+ /**
122
+ * Get the HTTP status text
123
+ */
124
+ get statusText() {
125
+ return this.response.statusText;
126
+ }
127
+ /**
128
+ * Get response headers
129
+ */
130
+ get headers() {
131
+ return this.response.headers;
132
+ }
133
+ /**
134
+ * Parse response as JSON
135
+ */
136
+ async json() {
137
+ return this.response.json();
138
+ }
139
+ /**
140
+ * Get response as text
141
+ */
142
+ async text() {
143
+ return this.response.text();
144
+ }
145
+ /**
146
+ * Get response as Blob
147
+ */
148
+ async blob() {
149
+ return this.response.blob();
150
+ }
151
+ /**
152
+ * Get response as ArrayBuffer
153
+ */
154
+ async arrayBuffer() {
155
+ return this.response.arrayBuffer();
156
+ }
157
+ /**
158
+ * Get response as ReadableStream
159
+ */
160
+ body() {
161
+ return this.response.body;
162
+ }
163
+ }
164
+
165
+ // Base class with common functionality for all request types
166
+ class BaseRequest {
167
+ constructor() {
168
+ this.requestOptions = {};
169
+ this.queryParams = new URLSearchParams();
170
+ }
171
+ withHeaders(headers) {
172
+ this.requestOptions.headers = {
173
+ ...(this.requestOptions.headers || {}),
174
+ ...headers,
175
+ };
176
+ return this;
177
+ }
178
+ withTimeout(timeout) {
179
+ this.requestOptions.timeout = timeout;
180
+ return this;
181
+ }
182
+ withRetries(retries) {
183
+ this.requestOptions.retries = retries;
184
+ return this;
185
+ }
186
+ onRetry(callback) {
187
+ this.requestOptions.onRetry = callback;
188
+ return this;
189
+ }
190
+ /**
191
+ * Sets the credentials policy for the request
192
+ * @param credentialsPolicy - Can be 'omit', 'same-origin', or 'include'
193
+ * @returns The instance for chaining
194
+ */
195
+ withCredentials(credentialsPolicy = CredentialsPolicy.SAME_ORIGIN) {
196
+ this.requestOptions.credentials = credentialsPolicy;
197
+ return this;
198
+ }
199
+ /**
200
+ * Allows providing an external AbortController to cancel the request
201
+ * @param controller - The AbortController to use for this request
202
+ * @returns The instance for chaining
203
+ */
204
+ withAbortController(controller) {
205
+ this.abortController = controller;
206
+ return this;
207
+ }
208
+ /**
209
+ * Sets the referrer for the request
210
+ * @param referrer - The referrer URL or policy
211
+ * @returns The instance for chaining
212
+ */
213
+ withReferrer(referrer) {
214
+ this.requestOptions.referrer = referrer;
215
+ return this;
216
+ }
217
+ /**
218
+ * Sets the redirect policy for the request
219
+ * @param redirect - Can be 'follow', 'error', or 'manual'
220
+ * @returns The instance for chaining
221
+ */
222
+ withRedirect(redirect) {
223
+ this.requestOptions.redirect = redirect;
224
+ return this;
225
+ }
226
+ /**
227
+ * Sets the keepalive flag for the request
228
+ * @param keepalive - Whether to allow the request to outlive the page
229
+ * @returns The instance for chaining
230
+ */
231
+ withKeepAlive(keepalive) {
232
+ this.requestOptions.keepalive = keepalive;
233
+ return this;
234
+ }
235
+ /**
236
+ * Sets the priority of the request
237
+ * @param priority - Can be 'high', 'low', or 'auto'
238
+ * @returns The instance for chaining
239
+ */
240
+ withPriority(priority) {
241
+ this.requestOptions.priority = priority;
242
+ return this;
243
+ }
244
+ /**
245
+ * Adds query parameters to the request URL
246
+ * @param params - An object containing the query parameters
247
+ * @returns The instance for chaining
248
+ */
249
+ withQueryParams(params) {
250
+ Object.entries(params).forEach(([key, value]) => {
251
+ if (value === null || value === undefined) {
252
+ return;
253
+ }
254
+ if (Array.isArray(value)) {
255
+ // Handle array values - add multiple entries with the same key
256
+ value.forEach(v => this.queryParams.append(key, String(v)));
257
+ }
258
+ else {
259
+ this.queryParams.append(key, String(value));
260
+ }
261
+ });
262
+ return this;
263
+ }
264
+ /**
265
+ * Adds a single query parameter
266
+ * @param key - The parameter name
267
+ * @param value - The parameter value
268
+ * @returns The instance for chaining
269
+ */
270
+ withQueryParam(key, value) {
271
+ if (value !== null && value !== undefined) {
272
+ this.queryParams.append(key, String(value));
273
+ }
274
+ return this;
275
+ }
276
+ /**
277
+ * Sets the mode of the request
278
+ * @param mode - The mode for the request (cors, no-cors, same-origin, navigate)
279
+ * @returns The instance for chaining
280
+ */
281
+ withMode(mode) {
282
+ this.requestOptions.mode = mode;
283
+ return this;
284
+ }
285
+ /**
286
+ * Shorthand for setting the Content-Type header
287
+ * @param contentType - The content type
288
+ * @returns The instance for chaining
289
+ */
290
+ withContentType(contentType) {
291
+ return this.withHeaders({
292
+ "Content-Type": contentType,
293
+ });
294
+ }
295
+ /**
296
+ * Shorthand for setting the Authorization header
297
+ * @param authValue - The authorization value
298
+ * @returns The instance for chaining
299
+ */
300
+ withAuthorization(authValue) {
301
+ return this.withHeaders({
302
+ Authorization: authValue,
303
+ });
304
+ }
305
+ /**
306
+ * Shorthand for setting up Basic authentication
307
+ * @param username - The username
308
+ * @param password - The password
309
+ * @returns The instance for chaining
310
+ */
311
+ withBasicAuth(username, password) {
312
+ const credentials = btoa(`${username}:${password}`);
313
+ return this.withAuthorization(`Basic ${credentials}`);
314
+ }
315
+ /**
316
+ * Sets the bearer token for authentication
317
+ * @param token - The bearer token
318
+ * @returns The instance for chaining
319
+ */
320
+ withBearerToken(token) {
321
+ return this.withAuthorization(`Bearer ${token}`);
322
+ }
323
+ /**
324
+ * Sets cookies for the request
325
+ * @param cookies - An object containing cookie name-value pairs
326
+ * @returns The instance for chaining
327
+ */
328
+ withCookies(cookies) {
329
+ const cookieEntries = Object.entries(cookies);
330
+ if (cookieEntries.length === 0) {
331
+ return this;
332
+ }
333
+ // Format cookies as name=value pairs
334
+ const cookieString = cookieEntries
335
+ .map(([name, value]) => `${encodeURIComponent(name)}=${encodeURIComponent(value)}`)
336
+ .join("; ");
337
+ // Get current headers or initialize empty object
338
+ const currentHeaders = this.requestOptions.headers || {};
339
+ // If there's already a Cookie header, append to it; otherwise create a new one
340
+ const existingCookies = currentHeaders["Cookie"] || "";
341
+ const newCookieValue = existingCookies ? `${existingCookies}; ${cookieString}` : cookieString;
342
+ // Set the Cookie header
343
+ return this.withHeaders({
344
+ Cookie: newCookieValue,
345
+ });
346
+ }
347
+ /**
348
+ * Send the request to the specified URL
349
+ * @param url The URL to send the request to
350
+ */
351
+ sendTo(url) {
352
+ // Format the URL with query parameters
353
+ url = this.formatUrlWithQueryParams(url);
354
+ // Cast the request options to RequestInit to ensure compatibility
355
+ const fetchOptions = {
356
+ ...this.requestOptions,
357
+ method: this.method,
358
+ };
359
+ // Create the base promise
360
+ const basePromise = !this.requestOptions.retries
361
+ ? this.executeRequest(url, fetchOptions)
362
+ : this.executeWithRetries(url, fetchOptions);
363
+ // Enhance the promise with response methods
364
+ const responsePromise = basePromise.then(response => response);
365
+ // Attach convenience methods to the promise
366
+ responsePromise.json = async () => {
367
+ const response = await basePromise;
368
+ return response.json();
369
+ };
370
+ responsePromise.text = async () => {
371
+ const response = await basePromise;
372
+ return response.text();
373
+ };
374
+ responsePromise.blob = async () => {
375
+ const response = await basePromise;
376
+ return response.blob();
377
+ };
378
+ responsePromise.arrayBuffer = async () => {
379
+ const response = await basePromise;
380
+ return response.arrayBuffer();
381
+ };
382
+ responsePromise.body = async () => {
383
+ const response = await basePromise;
384
+ return response.body();
385
+ };
386
+ return responsePromise;
387
+ }
388
+ /**
389
+ * Formats the URL with any query parameters
390
+ * @param url The base URL
391
+ * @returns The URL with query parameters appended
392
+ */
393
+ formatUrlWithQueryParams(url) {
394
+ if (!this.queryParams.toString()) {
395
+ return url;
396
+ }
397
+ const urlObj = new URL(url);
398
+ // Merge our query params with any that might be in the URL already
399
+ this.queryParams.forEach((value, key) => {
400
+ urlObj.searchParams.append(key, value);
401
+ });
402
+ return urlObj.toString();
403
+ }
404
+ /**
405
+ * Executes a request with configured retry logic
406
+ * @param url The formatted URL to send the request to
407
+ * @param fetchOptions The fetch options to use
408
+ * @returns A wrapped response object
409
+ */
410
+ async executeWithRetries(url, fetchOptions) {
411
+ let retryCount = 0;
412
+ const maxRetries = this.requestOptions.retries || 0;
413
+ // eslint-disable-next-line no-constant-condition
414
+ while (true) {
415
+ try {
416
+ return await this.executeRequest(url, fetchOptions);
417
+ }
418
+ catch (error) {
419
+ const requestError = error instanceof RequestError
420
+ ? error
421
+ : RequestError.networkError(url, fetchOptions.method, error);
422
+ if (retryCount >= maxRetries || requestError.timeoutError) {
423
+ throw requestError;
424
+ }
425
+ retryCount++;
426
+ if (this.requestOptions.onRetry) {
427
+ await this.requestOptions.onRetry({ retryCount, error: requestError });
428
+ }
429
+ }
430
+ }
431
+ }
432
+ async executeRequest(url, fetchOptions) {
433
+ // Create a request controller for timeout if needed, or use the provided one
434
+ const controller = this.abortController || new AbortController();
435
+ const { signal } = controller;
436
+ // Use the controller signal for the fetch request, but don't overwrite an existing signal
437
+ // that might have been set in requestOptions if it exists
438
+ fetchOptions.signal = fetchOptions.signal || signal;
439
+ let timeoutId;
440
+ // If we have a timeout and we're using our own controller (not provided externally)
441
+ if (this.requestOptions.timeout && !this.abortController) {
442
+ timeoutId = setTimeout(() => {
443
+ controller.abort();
444
+ }, this.requestOptions.timeout);
445
+ }
446
+ try {
447
+ let response;
448
+ try {
449
+ response = await fetch(url, fetchOptions);
450
+ }
451
+ catch (error) {
452
+ // Check if this is an abort error from our timeout
453
+ if (error instanceof DOMException && error.name === "AbortError" && timeoutId) {
454
+ throw RequestError.timeout(url, fetchOptions.method, this.requestOptions.timeout);
455
+ }
456
+ // Otherwise it's a network error
457
+ throw RequestError.networkError(url, fetchOptions.method, error);
458
+ }
459
+ if (!response.ok) {
460
+ throw RequestError.fromResponse(response, url, fetchOptions.method);
461
+ }
462
+ // Return a wrapped response instead of parsing it
463
+ return new ResponseWrapper(response);
464
+ }
465
+ finally {
466
+ if (timeoutId) {
467
+ clearTimeout(timeoutId);
468
+ }
469
+ }
470
+ }
471
+ }
472
+
473
+ // An extension of BaseRequest for methods that support request bodies
474
+ class BodyRequest extends BaseRequest {
475
+ /**
476
+ * Sets the request body with appropriate content type handling
477
+ */
478
+ withBody(body) {
479
+ if (body === undefined || body === null) {
480
+ this.requestOptions.body = null;
481
+ return this;
482
+ }
483
+ // Handle special body types that don't need transformation
484
+ if (body instanceof Blob ||
485
+ body instanceof FormData ||
486
+ body instanceof URLSearchParams ||
487
+ body instanceof ArrayBuffer ||
488
+ body instanceof ReadableStream) {
489
+ this.requestOptions.body = body;
490
+ // For FormData, make sure to NOT explicitly set Content-Type
491
+ // The browser will set it automatically with the correct boundary
492
+ // If we set it manually, it would be missing the boundary parameter
493
+ return this;
494
+ }
495
+ // Handle strings
496
+ if (typeof body === "string") {
497
+ this.requestOptions.body = body;
498
+ return this;
499
+ }
500
+ // For objects and other types, stringify and set JSON content-type
501
+ try {
502
+ this.requestOptions.body = JSON.stringify(body);
503
+ this.requestOptions.headers = {
504
+ "Content-Type": "application/json",
505
+ ...(this.requestOptions.headers || {}),
506
+ };
507
+ }
508
+ catch (error) {
509
+ const errorMessage = error instanceof Error ? error.message : String(error);
510
+ throw new Error(`Failed to stringify request body: ${errorMessage}`);
511
+ }
512
+ return this;
513
+ }
514
+ }
515
+
516
+ // Methods without body
517
+ class GetRequest extends BaseRequest {
518
+ constructor() {
519
+ super(...arguments);
520
+ this.method = HttpMethod.GET;
521
+ }
522
+ }
523
+ class HeadRequest extends BaseRequest {
524
+ constructor() {
525
+ super(...arguments);
526
+ this.method = HttpMethod.HEAD;
527
+ }
528
+ }
529
+ class OptionsRequest extends BaseRequest {
530
+ constructor() {
531
+ super(...arguments);
532
+ this.method = HttpMethod.OPTIONS;
533
+ }
534
+ }
535
+ class DeleteRequest extends BaseRequest {
536
+ constructor() {
537
+ super(...arguments);
538
+ this.method = HttpMethod.DELETE;
539
+ }
540
+ }
541
+ // Methods with body
542
+ class PostRequest extends BodyRequest {
543
+ constructor() {
544
+ super(...arguments);
545
+ this.method = HttpMethod.POST;
546
+ }
547
+ }
548
+ class PutRequest extends BodyRequest {
549
+ constructor() {
550
+ super(...arguments);
551
+ this.method = HttpMethod.PUT;
552
+ }
553
+ }
554
+ class PatchRequest extends BodyRequest {
555
+ constructor() {
556
+ super(...arguments);
557
+ this.method = HttpMethod.PATCH;
558
+ }
559
+ }
560
+
561
+ /**
562
+ * Create a GET request
563
+ */
564
+ function get() {
565
+ return new GetRequest();
566
+ }
567
+ /**
568
+ * Create a POST request
569
+ */
570
+ function post() {
571
+ return new PostRequest();
572
+ }
573
+ /**
574
+ * Create a PUT request
575
+ */
576
+ function put() {
577
+ return new PutRequest();
578
+ }
579
+ /**
580
+ * Create a DELETE request
581
+ * Note: We use 'del' to avoid JS keyword conflict
582
+ */
583
+ function del() {
584
+ return new DeleteRequest();
585
+ }
586
+ /**
587
+ * Create a PATCH request
588
+ */
589
+ function patch() {
590
+ return new PatchRequest();
591
+ }
592
+ /**
593
+ * Create a HEAD request
594
+ */
595
+ function head() {
596
+ return new HeadRequest();
597
+ }
598
+ /**
599
+ * Create an OPTIONS request
600
+ */
601
+ function options() {
602
+ return new OptionsRequest();
603
+ }
604
+
605
+ // Create the main API object
606
+ const create = {
607
+ get,
608
+ post,
609
+ put,
610
+ del,
611
+ patch,
612
+ head,
613
+ options,
614
+ };
615
+
616
+ export { CacheMode, CredentialsPolicy, DeleteRequest, GetRequest, HeadRequest, HttpMethod, OptionsRequest, PatchRequest, PostRequest, PutRequest, RedirectMode, RequestError, RequestMode, RequestPriority, ResponseWrapper, create as default };