aspi 1.2.1 → 2.0.1
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 +402 -209
- package/dist/index.cjs +488 -140
- package/dist/index.d.cts +580 -136
- package/dist/index.d.ts +580 -136
- package/dist/index.js +488 -140
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -75,7 +75,7 @@ var CustomError = class extends Error {
|
|
|
75
75
|
}
|
|
76
76
|
};
|
|
77
77
|
var isAspiError = (error) => {
|
|
78
|
-
return error instanceof AspiError;
|
|
78
|
+
return error instanceof AspiError && error.tag === "aspiError";
|
|
79
79
|
};
|
|
80
80
|
var isCustomError = (error) => {
|
|
81
81
|
return error instanceof CustomError;
|
|
@@ -287,19 +287,17 @@ var Request = class {
|
|
|
287
287
|
#shouldBeResult = false;
|
|
288
288
|
#bodySchemaIssues = [];
|
|
289
289
|
#throwOnError = false;
|
|
290
|
-
constructor(method, path, {
|
|
291
|
-
requestConfig,
|
|
292
|
-
retryConfig,
|
|
293
|
-
middlewares,
|
|
294
|
-
errorCbs,
|
|
295
|
-
throwOnError
|
|
296
|
-
}) {
|
|
290
|
+
constructor(method, path, requestOptions) {
|
|
297
291
|
this.#path = path;
|
|
298
|
-
this.#middlewares = middlewares || [];
|
|
299
|
-
this.#localRequestInit = {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
292
|
+
this.#middlewares = requestOptions.middlewares || [];
|
|
293
|
+
this.#localRequestInit = {
|
|
294
|
+
...requestOptions.requestConfig,
|
|
295
|
+
method
|
|
296
|
+
};
|
|
297
|
+
this.#retryConfig = requestOptions.retryConfig;
|
|
298
|
+
this.#customErrorCbs = requestOptions.errorCbs || {};
|
|
299
|
+
this.#throwOnError = requestOptions.throwOnError || false;
|
|
300
|
+
this.#shouldBeResult = requestOptions.shouldBeResult || false;
|
|
303
301
|
}
|
|
304
302
|
/**
|
|
305
303
|
* Sets the base URL for the request.
|
|
@@ -334,18 +332,25 @@ var Request = class {
|
|
|
334
332
|
return this;
|
|
335
333
|
}
|
|
336
334
|
/**
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
* @
|
|
335
|
+
* Merges the provided headers into the request configuration.
|
|
336
|
+
*
|
|
337
|
+
* @param {HeadersInit} headers - An object or iterable containing header name/value pairs.
|
|
338
|
+
* Existing headers are retained unless a key in this object overwrites them.
|
|
339
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
340
|
+
*
|
|
340
341
|
* @example
|
|
342
|
+
* // Set common JSON headers
|
|
341
343
|
* const request = new Request('/users', config);
|
|
342
344
|
* request.setHeaders({
|
|
343
345
|
* 'Content-Type': 'application/json',
|
|
344
|
-
* 'Accept': 'application/json'
|
|
346
|
+
* 'Accept': 'application/json',
|
|
345
347
|
* });
|
|
346
348
|
*/
|
|
347
349
|
setHeaders(headers) {
|
|
348
|
-
this.#localRequestInit.headers =
|
|
350
|
+
this.#localRequestInit.headers = {
|
|
351
|
+
...this.#localRequestInit.headers ?? {},
|
|
352
|
+
...headers
|
|
353
|
+
};
|
|
349
354
|
return this;
|
|
350
355
|
}
|
|
351
356
|
/**
|
|
@@ -359,7 +364,7 @@ var Request = class {
|
|
|
359
364
|
*/
|
|
360
365
|
setHeader(key, value) {
|
|
361
366
|
this.#localRequestInit.headers = {
|
|
362
|
-
...this.#localRequestInit.headers,
|
|
367
|
+
...this.#localRequestInit.headers ?? {},
|
|
363
368
|
[key]: value
|
|
364
369
|
};
|
|
365
370
|
return this;
|
|
@@ -421,6 +426,8 @@ var Request = class {
|
|
|
421
426
|
} else {
|
|
422
427
|
this.#localRequestInit.body = JSON.stringify(body);
|
|
423
428
|
}
|
|
429
|
+
} else {
|
|
430
|
+
this.#localRequestInit.body = JSON.stringify(body);
|
|
424
431
|
}
|
|
425
432
|
return this;
|
|
426
433
|
}
|
|
@@ -555,29 +562,54 @@ var Request = class {
|
|
|
555
562
|
return this.error("internalServerError", "INTERNAL_SERVER_ERROR", cb);
|
|
556
563
|
}
|
|
557
564
|
/**
|
|
558
|
-
*
|
|
559
|
-
*
|
|
560
|
-
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
565
|
+
* Register a custom error handler for a specific HTTP status code.
|
|
566
|
+
*
|
|
567
|
+
* When the response matches the provided `status`, the supplied callback `cb`
|
|
568
|
+
* is invoked and its return value is wrapped in a {@link CustomError} with the
|
|
569
|
+
* given `tag`. The method also augments the request's generic `Opts['error']`
|
|
570
|
+
* type so that the custom error is reflected in the resulting `Result`
|
|
571
|
+
* union.
|
|
572
|
+
*
|
|
573
|
+
* @template Tag - A string literal used as the error tag.
|
|
574
|
+
* @template A - The shape of the data returned by the callback.
|
|
575
|
+
*
|
|
576
|
+
* @param {Tag} tag
|
|
577
|
+
* A unique identifier for the custom error. This value becomes the `tag`
|
|
578
|
+
* property of the {@link CustomError} produced by the handler.
|
|
579
|
+
*
|
|
580
|
+
* @param {HttpErrorStatus} status
|
|
581
|
+
* The HTTP status code (e.g. `'BAD_REQUEST'`, `'NOT_FOUND'`) that should
|
|
582
|
+
* trigger the custom handler.
|
|
583
|
+
*
|
|
584
|
+
* @param {CustomErrorCb<TRequest, A>} cb
|
|
585
|
+
* A callback that receives the failing request and response objects and
|
|
586
|
+
* returns an object describing the error payload.
|
|
587
|
+
*
|
|
588
|
+
* @returns {Request<Method, TRequest, Merge<Omit<Opts, 'error'>, { error: { [K in Tag | keyof Opts['error']]: K extends Tag ? CustomError<Tag, A> : Opts['error'][K]; } }>>}
|
|
589
|
+
* The same {@link Request} instance, now typed with the newly added error
|
|
590
|
+
* variant, allowing method‑chaining.
|
|
591
|
+
*
|
|
563
592
|
* @example
|
|
593
|
+
* ```ts
|
|
564
594
|
* const request = new Request('/users', config);
|
|
595
|
+
*
|
|
596
|
+
* // Attach a custom handler for a 400 Bad Request response
|
|
565
597
|
* request
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
598
|
+
* withResult()
|
|
599
|
+
* .error('customError', 'BAD_REQUEST', (ctx) => {
|
|
600
|
+
* console.log('Bad request error:', ctx);
|
|
601
|
+
* return {
|
|
602
|
+
* message: 'Invalid input',
|
|
603
|
+
* details: ctx.response.responseData,
|
|
604
|
+
* };
|
|
605
|
+
* });
|
|
574
606
|
*
|
|
575
|
-
* // Later when
|
|
607
|
+
* // Later, when executing the request:
|
|
576
608
|
* const result = await request.json();
|
|
577
|
-
* if (Result.isErr(result)) {
|
|
578
|
-
* if(result.tag === 'customError') {
|
|
609
|
+
* if (Result.isErr(result) && result.tag === 'customError') {
|
|
579
610
|
* console.log(result.error.data.message); // 'Invalid input'
|
|
580
611
|
* }
|
|
612
|
+
* ```
|
|
581
613
|
*/
|
|
582
614
|
error(tag, status, cb) {
|
|
583
615
|
this.#customErrorCbs[httpErrors[status]] = {
|
|
@@ -587,58 +619,106 @@ var Request = class {
|
|
|
587
619
|
return this;
|
|
588
620
|
}
|
|
589
621
|
/**
|
|
590
|
-
* Sets query parameters for the request URL.
|
|
591
|
-
*
|
|
592
|
-
*
|
|
622
|
+
* Sets the query parameters for the request URL.
|
|
623
|
+
*
|
|
624
|
+
* Accepts any value that can be passed to the `URLSearchParams` constructor:
|
|
625
|
+
* - an object mapping keys to string values,
|
|
626
|
+
* - an iterable of `[key, value]` tuples,
|
|
627
|
+
* - a raw query string, or
|
|
628
|
+
* - an existing {@link URLSearchParams} instance.
|
|
629
|
+
*
|
|
630
|
+
* The supplied parameters replace any previously defined query parameters.
|
|
631
|
+
*
|
|
632
|
+
* @template T - The concrete type of the supplied parameters.
|
|
633
|
+
* @param {T} params - The query parameters to apply.
|
|
634
|
+
* @returns {this} The request instance for method‑chaining.
|
|
635
|
+
*
|
|
593
636
|
* @example
|
|
594
637
|
* const request = new Request('/users', config);
|
|
595
638
|
* request.setQueryParams({
|
|
596
639
|
* page: '1',
|
|
597
640
|
* limit: '10',
|
|
598
|
-
* sort: 'desc'
|
|
641
|
+
* sort: 'desc',
|
|
599
642
|
* });
|
|
643
|
+
*
|
|
644
|
+
* // Using a raw query string
|
|
645
|
+
* request.setQueryParams('page=1&limit=10');
|
|
646
|
+
*
|
|
647
|
+
* // Using an array of entries
|
|
648
|
+
* request.setQueryParams([['page', '1'], ['limit', '10']]);
|
|
649
|
+
*
|
|
650
|
+
* // Using an existing URLSearchParams instance
|
|
651
|
+
* const qp = new URLSearchParams({ page: '1' });
|
|
652
|
+
* request.setQueryParams(qp);
|
|
600
653
|
*/
|
|
601
654
|
setQueryParams(params) {
|
|
602
655
|
this.#queryParams = new URLSearchParams(params);
|
|
603
656
|
return this;
|
|
604
657
|
}
|
|
605
658
|
/**
|
|
606
|
-
* Sets
|
|
607
|
-
*
|
|
608
|
-
* @
|
|
659
|
+
* Sets a validation schema for the response data.
|
|
660
|
+
*
|
|
661
|
+
* The provided {@link StandardSchemaV1} schema will be used to validate the
|
|
662
|
+
* response payload when the request is executed. If validation fails, a
|
|
663
|
+
* `parseError` is added to the request's error type.
|
|
664
|
+
*
|
|
665
|
+
* @template TSchema - A type extending {@link StandardSchemaV1}
|
|
666
|
+
* @param schema - The schema used to validate the response data
|
|
667
|
+
* @returns The request instance for chaining with an updated generic type that
|
|
668
|
+
* includes the schema and a possible `parseError` in the error union
|
|
669
|
+
*
|
|
609
670
|
* @example
|
|
671
|
+
* ```ts
|
|
610
672
|
* import { z } from 'zod';
|
|
611
673
|
*
|
|
612
674
|
* const userSchema = z.object({
|
|
613
675
|
* id: z.number(),
|
|
614
676
|
* name: z.string(),
|
|
615
|
-
* email: z.string().email()
|
|
677
|
+
* email: z.string().email(),
|
|
616
678
|
* });
|
|
617
679
|
*
|
|
618
680
|
* const request = new Request('/users', config);
|
|
619
681
|
* const result = await request
|
|
620
682
|
* .withResult()
|
|
621
|
-
* .
|
|
683
|
+
* .schema(userSchema)
|
|
622
684
|
* .json();
|
|
623
685
|
*
|
|
624
686
|
* if (Result.isOk(result)) {
|
|
625
687
|
* const user = result.value; // Typed and validated user data
|
|
626
688
|
* }
|
|
689
|
+
* ```
|
|
627
690
|
*/
|
|
628
691
|
schema(schema) {
|
|
629
692
|
this.#schema = schema;
|
|
630
693
|
return this;
|
|
631
694
|
}
|
|
632
695
|
/**
|
|
633
|
-
*
|
|
634
|
-
*
|
|
696
|
+
* Configures the request to **throw** an exception when the response status
|
|
697
|
+
* indicates a failure (i.e., `!response.ok`). This disables the “Result”
|
|
698
|
+
* mode (`withResult`) and enables “throwable” mode, causing
|
|
699
|
+
* `await request.json()` (or other response helpers) to either resolve with
|
|
700
|
+
* the successful payload **or** reject with an `AspiError`/`CustomError`.
|
|
701
|
+
*
|
|
702
|
+
* Use this when you prefer traditional `try / catch` error handling over
|
|
703
|
+
* the explicit `Result` type returned by {@link withResult}.
|
|
704
|
+
*
|
|
705
|
+
* @returns This {@link Request} instance, now typed with `throwable: true` and
|
|
706
|
+
* `withResult: false` for proper chaining.
|
|
707
|
+
*
|
|
635
708
|
* @example
|
|
709
|
+
* ```ts
|
|
636
710
|
* const request = new Request('/users', config);
|
|
637
|
-
* const result = await request
|
|
638
|
-
* .withResult()
|
|
639
|
-
* .throwable()
|
|
640
|
-
* .json();
|
|
641
711
|
*
|
|
712
|
+
* try {
|
|
713
|
+
* const user = await request
|
|
714
|
+
* .throwable() // Enable throwing on HTTP errors
|
|
715
|
+
* .json<User>(); // Will throw if the response is not ok
|
|
716
|
+
* console.log(user);
|
|
717
|
+
* } catch (err) {
|
|
718
|
+
* // err is either AspiError or a CustomError returned by a custom handler
|
|
719
|
+
* console.error('Request failed:', err);
|
|
720
|
+
* }
|
|
721
|
+
* ```
|
|
642
722
|
*/
|
|
643
723
|
throwable() {
|
|
644
724
|
this.#shouldBeResult = false;
|
|
@@ -646,43 +726,100 @@ var Request = class {
|
|
|
646
726
|
return this;
|
|
647
727
|
}
|
|
648
728
|
/**
|
|
649
|
-
*
|
|
650
|
-
*
|
|
729
|
+
* Sends the request and parses the response body as JSON.
|
|
730
|
+
*
|
|
731
|
+
* The resolved value of the returned promise varies based on the request mode:
|
|
732
|
+
*
|
|
733
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} that
|
|
734
|
+
* contains either an {@link AspiResultOk} with the parsed payload or a union
|
|
735
|
+
* of possible error types (HTTP errors, custom errors, JSON‑parse errors,
|
|
736
|
+
* schema‑validation errors, etc.).
|
|
737
|
+
*
|
|
738
|
+
* - **Throwable mode** (`throwable()`): resolves directly to the successful
|
|
739
|
+
* payload (`AspiPlainResponse`) and throws a {@link AspiError} or
|
|
740
|
+
* {@link CustomError} on failure.
|
|
741
|
+
*
|
|
742
|
+
* - **Default mode** (no explicit mode): resolves to a tuple
|
|
743
|
+
* `[value, error]` where exactly one element is non‑null.
|
|
744
|
+
*
|
|
745
|
+
* @template T - The inferred output type of the response schema (if a schema
|
|
746
|
+
* was supplied via {@link schema}). When no schema is provided `T` defaults
|
|
747
|
+
* to `any`.
|
|
748
|
+
*
|
|
749
|
+
* @returns A promise whose shape depends on the selected mode (see description).
|
|
750
|
+
* In Result mode it is `Result<ResultOk<…>, …>`, in throwable mode it is
|
|
751
|
+
* `AspiPlainResponse<…>`, and otherwise a tuple
|
|
752
|
+
* `[AspiResultOk<…> | null, Error | null]`.
|
|
753
|
+
*
|
|
651
754
|
* @example
|
|
755
|
+
* // Using the Result API
|
|
652
756
|
* const request = new Request('/users', config);
|
|
653
757
|
* const result = await request
|
|
654
758
|
* .setQueryParams({ id: '123' })
|
|
655
|
-
* .
|
|
656
|
-
|
|
657
|
-
* .notFound((error) => ({ message: 'User not found' }))
|
|
759
|
+
* .withResult()
|
|
760
|
+
* .notFound(() => ({ message: 'User not found' }))
|
|
658
761
|
* .json<User>();
|
|
659
762
|
*
|
|
660
763
|
* if (Result.isOk(result)) {
|
|
661
|
-
*
|
|
764
|
+
* // `result.value` has type `User`
|
|
765
|
+
* console.log(result.value);
|
|
662
766
|
* } else {
|
|
663
|
-
* console.error(result.error);
|
|
767
|
+
* console.error(result.error);
|
|
664
768
|
* }
|
|
665
769
|
*/
|
|
666
770
|
async json() {
|
|
667
|
-
const output = await this.#makeRequest(
|
|
668
|
-
|
|
771
|
+
const output = await this.#makeRequest(async (response) => {
|
|
772
|
+
if (response.status === 204 || response.status >= 300 && response.status < 400) {
|
|
773
|
+
return null;
|
|
774
|
+
}
|
|
775
|
+
return response.json().catch(
|
|
669
776
|
(e) => new CustomError("jsonParseError", {
|
|
670
777
|
message: e instanceof Error ? e.message : "Failed to parse JSON"
|
|
671
778
|
})
|
|
672
|
-
)
|
|
673
|
-
|
|
674
|
-
);
|
|
779
|
+
);
|
|
780
|
+
}, true);
|
|
675
781
|
return this.#mapResponse(output);
|
|
676
782
|
}
|
|
677
783
|
/**
|
|
678
|
-
* Executes the request and returns the response as plain text.
|
|
679
|
-
*
|
|
784
|
+
* Executes the request and returns the response body as plain text.
|
|
785
|
+
*
|
|
786
|
+
* The method respects the request mode:
|
|
787
|
+
*
|
|
788
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result}
|
|
789
|
+
* containing either an {@link AspiResultOk} with the text payload or an
|
|
790
|
+
* error variant.
|
|
791
|
+
* - **Throwable mode** (`throwable()`): resolves directly to the text string
|
|
792
|
+
* and throws on error.
|
|
793
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one
|
|
794
|
+
* element is `null`.
|
|
795
|
+
*
|
|
796
|
+
* @returns {Promise<
|
|
797
|
+
* Opts['withResult'] extends true
|
|
798
|
+
* ? Result.Result<
|
|
799
|
+
* AspiResultOk<TRequest, string>,
|
|
800
|
+
* AspiError<TRequest> |
|
|
801
|
+
* (Opts extends { error: any } ? Opts['error'][keyof Opts['error']] : never)
|
|
802
|
+
* >
|
|
803
|
+
* : Opts['throwable'] extends true
|
|
804
|
+
* ? AspiPlainResponse<TRequest, string>
|
|
805
|
+
* : [
|
|
806
|
+
* AspiResultOk<TRequest, string> | null,
|
|
807
|
+
* (
|
|
808
|
+
* | AspiError<TRequest>
|
|
809
|
+
* | (Opts extends { error: any } ? Opts['error'][keyof Opts['error']] : never)
|
|
810
|
+
* | null
|
|
811
|
+
* )
|
|
812
|
+
* ]
|
|
813
|
+
* }>
|
|
814
|
+
* A promise that resolves according to the selected request mode.
|
|
815
|
+
*
|
|
680
816
|
* @example
|
|
817
|
+
* ```ts
|
|
681
818
|
* const request = new Request('/data.txt', config);
|
|
682
819
|
* const result = await request
|
|
683
820
|
* .setQueryParams({ version: '1' })
|
|
684
821
|
* .withResult()
|
|
685
|
-
* .notFound((
|
|
822
|
+
* .notFound(() => ({ message: 'Text file not found' }))
|
|
686
823
|
* .text();
|
|
687
824
|
*
|
|
688
825
|
* if (Result.isOk(result)) {
|
|
@@ -690,22 +827,58 @@ var Request = class {
|
|
|
690
827
|
* } else {
|
|
691
828
|
* console.error(result.error); // Error handling
|
|
692
829
|
* }
|
|
830
|
+
* ```
|
|
693
831
|
*/
|
|
694
832
|
async text() {
|
|
695
|
-
const output = await this.#makeRequest(
|
|
696
|
-
|
|
697
|
-
);
|
|
833
|
+
const output = await this.#makeRequest((response) => {
|
|
834
|
+
return response.text();
|
|
835
|
+
});
|
|
698
836
|
return this.#mapResponse(output);
|
|
699
837
|
}
|
|
700
838
|
/**
|
|
701
|
-
* Executes the request and returns the response as a Blob.
|
|
702
|
-
*
|
|
839
|
+
* Executes the request and returns the response body as a {@link Blob}.
|
|
840
|
+
*
|
|
841
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
842
|
+
*
|
|
843
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
844
|
+
* either an {@link AspiResultOk} with `Blob` data or an error variant.
|
|
845
|
+
* - **Throwable mode** (`throwable()`): resolves directly to a {@link Blob}
|
|
846
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
847
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
848
|
+
* is `null`.
|
|
849
|
+
*
|
|
850
|
+
* @returns {Promise<
|
|
851
|
+
* Opts['withResult'] extends true
|
|
852
|
+
* ? Result.Result<
|
|
853
|
+
* AspiResultOk<TRequest, Blob>,
|
|
854
|
+
* | AspiError<TRequest>
|
|
855
|
+
* | (Opts extends { error: any }
|
|
856
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
857
|
+
* : never)
|
|
858
|
+
* >
|
|
859
|
+
* : Opts['throwable'] extends true
|
|
860
|
+
* ? AspiPlainResponse<TRequest, Blob>
|
|
861
|
+
* : [
|
|
862
|
+
* AspiResultOk<TRequest, Blob> | null,
|
|
863
|
+
* (
|
|
864
|
+
* | (
|
|
865
|
+
* | AspiError<TRequest>
|
|
866
|
+
* | (Opts extends { error: any }
|
|
867
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
868
|
+
* : never)
|
|
869
|
+
* )
|
|
870
|
+
* | null
|
|
871
|
+
* ),
|
|
872
|
+
* ]
|
|
873
|
+
* }>
|
|
874
|
+
*
|
|
703
875
|
* @example
|
|
876
|
+
* ```ts
|
|
704
877
|
* const request = new Request('/image.jpg', config);
|
|
705
878
|
* const result = await request
|
|
706
879
|
* .setQueryParams({ size: 'large' })
|
|
707
880
|
* .withResult()
|
|
708
|
-
* .notFound((
|
|
881
|
+
* .notFound(() => ({ message: 'Image not found' }))
|
|
709
882
|
* .blob();
|
|
710
883
|
*
|
|
711
884
|
* if (Result.isOk(result)) {
|
|
@@ -713,43 +886,77 @@ var Request = class {
|
|
|
713
886
|
* } else {
|
|
714
887
|
* console.error(result.error); // Error handling
|
|
715
888
|
* }
|
|
889
|
+
* ```
|
|
716
890
|
*/
|
|
717
891
|
async blob() {
|
|
718
892
|
const output = await this.#makeRequest((response) => response.blob());
|
|
719
893
|
return this.#mapResponse(output);
|
|
720
894
|
}
|
|
721
895
|
#url() {
|
|
722
|
-
const
|
|
896
|
+
const passedBaseUrl = typeof this.#localRequestInit.baseUrl === "string" ? this.#localRequestInit.baseUrl : this.#localRequestInit.baseUrl.toString();
|
|
897
|
+
const baseUrl = passedBaseUrl.replace(/\/+$/, "") ?? "";
|
|
723
898
|
const path = this.#path.replace(/^\/+/, "/");
|
|
724
899
|
const queryString = this.#queryParams ? `?${this.#queryParams.toString()}` : "";
|
|
725
900
|
const url = [baseUrl, path, queryString].filter(Boolean).join("");
|
|
726
901
|
return url;
|
|
727
902
|
}
|
|
728
903
|
/**
|
|
729
|
-
* Returns the
|
|
730
|
-
*
|
|
904
|
+
* Returns the fully‑qualified URL that will be used for the request.
|
|
905
|
+
*
|
|
906
|
+
* The URL is constructed from the configured base URL, the request path,
|
|
907
|
+
* and any query parameters added via {@link setQueryParams}.
|
|
908
|
+
*
|
|
909
|
+
* @returns {string} The complete request URL.
|
|
910
|
+
*
|
|
731
911
|
* @example
|
|
912
|
+
* ```ts
|
|
732
913
|
* const request = new Request('/users', config);
|
|
733
914
|
* request.setBaseUrl('https://api.example.com');
|
|
734
915
|
* request.setQueryParams({ id: '123' });
|
|
735
|
-
*
|
|
916
|
+
*
|
|
917
|
+
* console.log(request.url());
|
|
918
|
+
* // => 'https://api.example.com/users?id=123'
|
|
919
|
+
* ```
|
|
736
920
|
*/
|
|
737
921
|
url() {
|
|
738
922
|
return this.#url();
|
|
739
923
|
}
|
|
740
924
|
/**
|
|
741
|
-
*
|
|
742
|
-
*
|
|
925
|
+
* Switches the request into **Result** mode.
|
|
926
|
+
*
|
|
927
|
+
* In Result mode the response helpers (`json`, `text`, `blob`, …) resolve to a
|
|
928
|
+
* {@link Result.Result} instance instead of the default tuple
|
|
929
|
+
* `[value, error]`. This allows callers to use pattern matching
|
|
930
|
+
* (`Result.isOk`, `Result.isErr`) to handle success and failure.
|
|
931
|
+
*
|
|
932
|
+
* Calling `withResult` disables the “throwable” behaviour (see {@link throwable}).
|
|
933
|
+
*
|
|
934
|
+
* @returns {Request<
|
|
935
|
+
* Method,
|
|
936
|
+
* TRequest,
|
|
937
|
+
* Merge<
|
|
938
|
+
* Omit<Opts, 'withResult' | 'throwable'>,
|
|
939
|
+
* {
|
|
940
|
+
* withResult: true;
|
|
941
|
+
* throwable: false;
|
|
942
|
+
* }
|
|
943
|
+
* >
|
|
944
|
+
* >} The same {@link Request} instance, now typed with `withResult: true` and
|
|
945
|
+
* `throwable: false` for fluent chaining.
|
|
946
|
+
*
|
|
743
947
|
* @example
|
|
948
|
+
* ```ts
|
|
744
949
|
* const request = new Request('/users', config);
|
|
950
|
+
*
|
|
745
951
|
* const result = await request
|
|
746
|
-
* .withResult()
|
|
952
|
+
* .withResult() // enable Result mode
|
|
747
953
|
* .json<User>();
|
|
748
954
|
*
|
|
749
|
-
* // Returns Result type instead of tuple
|
|
750
955
|
* if (Result.isOk(result)) {
|
|
751
|
-
*
|
|
956
|
+
* // `result.value` is of type `User`
|
|
957
|
+
* console.log(result.value);
|
|
752
958
|
* }
|
|
959
|
+
* ```
|
|
753
960
|
*/
|
|
754
961
|
withResult() {
|
|
755
962
|
this.#throwOnError = false;
|
|
@@ -769,6 +976,9 @@ var Request = class {
|
|
|
769
976
|
return [null, getErrorOrNull(value)];
|
|
770
977
|
}
|
|
771
978
|
}
|
|
979
|
+
#isSuccessResponse(response) {
|
|
980
|
+
return response.ok || response.status >= 300 && response.status < 400;
|
|
981
|
+
}
|
|
772
982
|
async #makeRequest(responseParser, isJson = false) {
|
|
773
983
|
if (this.#bodySchemaIssues.length) {
|
|
774
984
|
return err(new CustomError("parseError", this.#bodySchemaIssues));
|
|
@@ -779,20 +989,23 @@ var Request = class {
|
|
|
779
989
|
const requestInit = request.requestInit;
|
|
780
990
|
const url = this.#url();
|
|
781
991
|
let attempts = 1;
|
|
782
|
-
let response
|
|
783
|
-
|
|
992
|
+
let response = new Response(null, {
|
|
993
|
+
status: 500,
|
|
994
|
+
statusText: "Internal Server Error"
|
|
995
|
+
});
|
|
996
|
+
let responseData = null;
|
|
784
997
|
while (attempts <= retries) {
|
|
785
998
|
try {
|
|
786
999
|
response = await fetch(url, requestInit);
|
|
787
1000
|
responseData = await responseParser(response);
|
|
788
|
-
if (responseData instanceof
|
|
1001
|
+
if (responseData instanceof Error) {
|
|
789
1002
|
return err(responseData);
|
|
790
1003
|
}
|
|
791
1004
|
const retryWhileCondition = retryWhile ? await retryWhile(
|
|
792
1005
|
request,
|
|
793
1006
|
this.#makeResponse(response, responseData)
|
|
794
1007
|
) : false;
|
|
795
|
-
if (response
|
|
1008
|
+
if (this.#isSuccessResponse(response) || !retryOn.includes(response.status) && !retryWhileCondition) {
|
|
796
1009
|
break;
|
|
797
1010
|
}
|
|
798
1011
|
if (response.status in this.#customErrorCbs && attempts === retries) {
|
|
@@ -815,9 +1028,31 @@ var Request = class {
|
|
|
815
1028
|
request,
|
|
816
1029
|
this.#makeResponse(response, responseData)
|
|
817
1030
|
) : retryDelay;
|
|
818
|
-
await
|
|
1031
|
+
await this.#abortDelay(delay, request);
|
|
819
1032
|
}
|
|
820
1033
|
} catch (e) {
|
|
1034
|
+
if (e instanceof Error && e.name === "AbortError") {
|
|
1035
|
+
if (500 in this.#customErrorCbs) {
|
|
1036
|
+
const result = this.#customErrorCbs[response.status].cb({
|
|
1037
|
+
request,
|
|
1038
|
+
response: this.#makeResponse(response, responseData)
|
|
1039
|
+
});
|
|
1040
|
+
return err(
|
|
1041
|
+
new CustomError(
|
|
1042
|
+
// @ts-ignore
|
|
1043
|
+
this.#customErrorCbs[response.status].tag,
|
|
1044
|
+
result
|
|
1045
|
+
)
|
|
1046
|
+
);
|
|
1047
|
+
}
|
|
1048
|
+
return err(
|
|
1049
|
+
new AspiError(
|
|
1050
|
+
e.message,
|
|
1051
|
+
this.#request(),
|
|
1052
|
+
this.#makeResponse(response, responseData)
|
|
1053
|
+
)
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
821
1056
|
if (attempts === retries) throw e;
|
|
822
1057
|
const delay = typeof retryDelay === "function" ? await retryDelay(
|
|
823
1058
|
retries - attempts - 1,
|
|
@@ -825,14 +1060,14 @@ var Request = class {
|
|
|
825
1060
|
request,
|
|
826
1061
|
this.#makeResponse(response, responseData)
|
|
827
1062
|
) : retryDelay;
|
|
828
|
-
await
|
|
1063
|
+
await this.#abortDelay(delay, request);
|
|
829
1064
|
}
|
|
830
1065
|
if (onRetry) {
|
|
831
1066
|
onRetry(request, this.#makeResponse(response, responseData));
|
|
832
1067
|
}
|
|
833
1068
|
attempts++;
|
|
834
1069
|
}
|
|
835
|
-
if (!response
|
|
1070
|
+
if (!this.#isSuccessResponse(response)) {
|
|
836
1071
|
if (response.status in this.#customErrorCbs) {
|
|
837
1072
|
const result = this.#customErrorCbs[response.status].cb({
|
|
838
1073
|
request,
|
|
@@ -902,17 +1137,38 @@ var Request = class {
|
|
|
902
1137
|
);
|
|
903
1138
|
}
|
|
904
1139
|
}
|
|
1140
|
+
#abortDelay(ms, request) {
|
|
1141
|
+
return new Promise((resolve, reject) => {
|
|
1142
|
+
const timer = setTimeout(resolve, ms);
|
|
1143
|
+
const signal = request.requestInit.signal;
|
|
1144
|
+
if (signal) {
|
|
1145
|
+
if (signal.aborted) {
|
|
1146
|
+
clearTimeout(timer);
|
|
1147
|
+
reject(new DOMException("The user aborted a request.", "AbortError"));
|
|
1148
|
+
} else {
|
|
1149
|
+
const abortHandler = () => {
|
|
1150
|
+
clearTimeout(timer);
|
|
1151
|
+
reject(
|
|
1152
|
+
new DOMException("The user aborted a request.", "AbortError")
|
|
1153
|
+
);
|
|
1154
|
+
};
|
|
1155
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
905
1160
|
#request() {
|
|
906
1161
|
let requestInit = this.#localRequestInit;
|
|
907
1162
|
for (const middleware of this.#middlewares) {
|
|
908
|
-
requestInit = middleware(
|
|
1163
|
+
requestInit = middleware(requestInit);
|
|
909
1164
|
}
|
|
910
1165
|
return {
|
|
911
|
-
requestInit
|
|
912
|
-
|
|
1166
|
+
requestInit: {
|
|
1167
|
+
...requestInit,
|
|
1168
|
+
retryConfig: this.#sanitisedRetryConfig()
|
|
1169
|
+
},
|
|
913
1170
|
path: this.#path,
|
|
914
|
-
queryParams: this.#queryParams || null
|
|
915
|
-
retryConfig: this.#sanitisedRetryConfig()
|
|
1171
|
+
queryParams: this.#queryParams || null
|
|
916
1172
|
};
|
|
917
1173
|
}
|
|
918
1174
|
#sanitisedRetryConfig() {
|
|
@@ -937,6 +1193,68 @@ var Request = class {
|
|
|
937
1193
|
responseData
|
|
938
1194
|
};
|
|
939
1195
|
}
|
|
1196
|
+
/**
|
|
1197
|
+
* Returns the underlying {@link AspiRequest} object that will be used for the fetch call.
|
|
1198
|
+
*
|
|
1199
|
+
* This method does not perform any network activity; it simply builds and returns the
|
|
1200
|
+
* request configuration, including any applied middlewares, query parameters, etc.
|
|
1201
|
+
*
|
|
1202
|
+
* @returns {AspiRequest<TRequest>} The constructed request object.
|
|
1203
|
+
*/
|
|
1204
|
+
getRequest() {
|
|
1205
|
+
return this.#request();
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Retrieves the registry of custom error callbacks that have been
|
|
1209
|
+
* registered via {@link error}. The returned object maps HTTP status
|
|
1210
|
+
* codes to their corresponding callback functions and tags.
|
|
1211
|
+
*
|
|
1212
|
+
* @returns {ErrorCallbacks} A shallow copy of the internal error callback registry.
|
|
1213
|
+
*/
|
|
1214
|
+
getErrorCallbackRegistry() {
|
|
1215
|
+
return { ...this.#customErrorCbs };
|
|
1216
|
+
}
|
|
1217
|
+
/**
|
|
1218
|
+
* Returns whether the request is configured to return a {@link Result.Result}
|
|
1219
|
+
* instead of the default tuple or throwing.
|
|
1220
|
+
*
|
|
1221
|
+
* @returns {boolean} `true` when {@link withResult} has been called.
|
|
1222
|
+
*/
|
|
1223
|
+
isResult() {
|
|
1224
|
+
return this.#shouldBeResult;
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Returns whether the request is configured to throw on HTTP errors.
|
|
1228
|
+
*
|
|
1229
|
+
* @returns {boolean} `true` when {@link throwable} has been called.
|
|
1230
|
+
*/
|
|
1231
|
+
isThrowable() {
|
|
1232
|
+
return this.#throwOnError;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Returns the effective retry configuration for this request, including defaulted values.
|
|
1236
|
+
*
|
|
1237
|
+
* The returned object contains:
|
|
1238
|
+
* - `retries` – number of retry attempts (default 1)
|
|
1239
|
+
* - `retryDelay` – delay between attempts in milliseconds or a function that returns a delay
|
|
1240
|
+
* - `retryOn` – array of HTTP status codes that should trigger a retry
|
|
1241
|
+
* - `retryWhile` – optional custom predicate executed after each response
|
|
1242
|
+
* - `onRetry` – optional callback invoked after a retry attempt
|
|
1243
|
+
*
|
|
1244
|
+
* A shallow copy is returned to avoid accidental mutation of the internal state.
|
|
1245
|
+
*
|
|
1246
|
+
* @returns {{
|
|
1247
|
+
* retries: number;
|
|
1248
|
+
* retryDelay: number | ((attempt: number, maxAttempts: number, request: AspiRequest<TRequest>, response: AspiResponse<any, true>) => number);
|
|
1249
|
+
* retryOn: number[];
|
|
1250
|
+
* retryWhile?: (request: AspiRequest<TRequest>, response: AspiResponse<any, true>) => boolean | Promise<boolean>;
|
|
1251
|
+
* onRetry?: (request: AspiRequest<TRequest>, response: AspiResponse<any, true>) => void;
|
|
1252
|
+
* }}
|
|
1253
|
+
*/
|
|
1254
|
+
getRetryConfig() {
|
|
1255
|
+
const cfg = this.#sanitisedRetryConfig();
|
|
1256
|
+
return { ...cfg };
|
|
1257
|
+
}
|
|
940
1258
|
};
|
|
941
1259
|
|
|
942
1260
|
// src/aspi.ts
|
|
@@ -946,15 +1264,20 @@ var Aspi2 = class {
|
|
|
946
1264
|
#retryConfig;
|
|
947
1265
|
#customErrorCbs = {};
|
|
948
1266
|
#throwOnError = false;
|
|
1267
|
+
#shouldBeResult = false;
|
|
949
1268
|
constructor(config) {
|
|
950
|
-
const { retryConfig, ...
|
|
951
|
-
this.#globalRequestInit =
|
|
1269
|
+
const { retryConfig, ...requestInit } = config;
|
|
1270
|
+
this.#globalRequestInit = requestInit;
|
|
952
1271
|
this.#retryConfig = retryConfig;
|
|
953
1272
|
}
|
|
954
1273
|
/**
|
|
955
|
-
* Sets the base URL for all API requests
|
|
956
|
-
*
|
|
957
|
-
*
|
|
1274
|
+
* Sets or overrides the base URL used for all subsequent API requests.
|
|
1275
|
+
*
|
|
1276
|
+
* Accepts either a string or a `URL` instance. If a `URL` object is provided,
|
|
1277
|
+
* it is converted to its string representation.
|
|
1278
|
+
*
|
|
1279
|
+
* @param url - The new base URL.
|
|
1280
|
+
* @returns The current {@link Aspi} instance for chaining.
|
|
958
1281
|
* @example
|
|
959
1282
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
960
1283
|
* api.setBaseUrl('https://api.newdomain.com');
|
|
@@ -983,17 +1306,17 @@ var Aspi2 = class {
|
|
|
983
1306
|
};
|
|
984
1307
|
return this;
|
|
985
1308
|
}
|
|
986
|
-
#createRequest(method, path
|
|
1309
|
+
#createRequest(method, path) {
|
|
987
1310
|
return new Request(method, path, {
|
|
988
1311
|
requestConfig: {
|
|
989
1312
|
...this.#globalRequestInit,
|
|
990
|
-
method
|
|
991
|
-
body
|
|
1313
|
+
method
|
|
992
1314
|
},
|
|
993
|
-
retryConfig: this.#retryConfig,
|
|
994
1315
|
middlewares: this.#middlewares,
|
|
995
1316
|
errorCbs: this.#customErrorCbs,
|
|
996
|
-
throwOnError: this.#throwOnError
|
|
1317
|
+
throwOnError: this.#throwOnError,
|
|
1318
|
+
shouldBeResult: this.#shouldBeResult,
|
|
1319
|
+
retryConfig: this.#retryConfig
|
|
997
1320
|
});
|
|
998
1321
|
}
|
|
999
1322
|
/**
|
|
@@ -1008,40 +1331,39 @@ var Aspi2 = class {
|
|
|
1008
1331
|
return this.#createRequest("GET", path);
|
|
1009
1332
|
}
|
|
1010
1333
|
/**
|
|
1011
|
-
* Makes a POST request to the specified path
|
|
1012
|
-
*
|
|
1013
|
-
* @param {
|
|
1014
|
-
* @returns {Request} A Request instance for chaining
|
|
1334
|
+
* Makes a POST request to the specified path.
|
|
1335
|
+
*
|
|
1336
|
+
* @param {string} path - The path to make the request to.
|
|
1337
|
+
* @returns {Request} A Request instance for chaining.
|
|
1338
|
+
*
|
|
1015
1339
|
* @example
|
|
1016
1340
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1017
|
-
* const response = await api.post('/users'
|
|
1341
|
+
* const response = await api.post('/users').json();
|
|
1018
1342
|
*/
|
|
1019
|
-
post(path
|
|
1020
|
-
return this.#createRequest("POST", path
|
|
1343
|
+
post(path) {
|
|
1344
|
+
return this.#createRequest("POST", path);
|
|
1021
1345
|
}
|
|
1022
1346
|
/**
|
|
1023
|
-
* Makes a PUT request to the specified path
|
|
1024
|
-
* @param {string} path - The path to make the request to
|
|
1025
|
-
* @
|
|
1026
|
-
* @returns {Request} A Request instance for chaining
|
|
1347
|
+
* Makes a PUT request to the specified path.
|
|
1348
|
+
* @param {string} path - The path to make the request to.
|
|
1349
|
+
* @returns {Request} A Request instance for chaining.
|
|
1027
1350
|
* @example
|
|
1028
1351
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1029
|
-
* const response = await api.put('/users/1'
|
|
1352
|
+
* const response = await api.put('/users/1').json();
|
|
1030
1353
|
*/
|
|
1031
|
-
put(path
|
|
1032
|
-
return this.#createRequest("PUT", path
|
|
1354
|
+
put(path) {
|
|
1355
|
+
return this.#createRequest("PUT", path);
|
|
1033
1356
|
}
|
|
1034
1357
|
/**
|
|
1035
|
-
* Makes a PATCH request to the specified path
|
|
1036
|
-
* @param {string} path - The path to make the request to
|
|
1037
|
-
* @
|
|
1038
|
-
* @returns {Request} A Request instance for chaining
|
|
1358
|
+
* Makes a PATCH request to the specified path.
|
|
1359
|
+
* @param {string} path - The path to make the request to.
|
|
1360
|
+
* @returns {Request} A Request instance for chaining.
|
|
1039
1361
|
* @example
|
|
1040
1362
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1041
|
-
* const response = await api.patch('/users/1'
|
|
1363
|
+
* const response = await api.patch('/users/1').json();
|
|
1042
1364
|
*/
|
|
1043
|
-
patch(path
|
|
1044
|
-
return this.#createRequest("PATCH", path
|
|
1365
|
+
patch(path) {
|
|
1366
|
+
return this.#createRequest("PATCH", path);
|
|
1045
1367
|
}
|
|
1046
1368
|
/**
|
|
1047
1369
|
* Makes a DELETE request to the specified path
|
|
@@ -1077,8 +1399,9 @@ var Aspi2 = class {
|
|
|
1077
1399
|
return this.#createRequest("OPTIONS", path);
|
|
1078
1400
|
}
|
|
1079
1401
|
/**
|
|
1080
|
-
* Sets multiple headers for all API requests
|
|
1081
|
-
*
|
|
1402
|
+
* Sets multiple headers for all API requests. Existing headers are preserved
|
|
1403
|
+
* and new ones are merged, overriding any duplicate keys.
|
|
1404
|
+
* @param {HeadersInit} headers - An object containing header key-value pairs
|
|
1082
1405
|
* @returns {Aspi} The Aspi instance for chaining
|
|
1083
1406
|
* @example
|
|
1084
1407
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
@@ -1088,21 +1411,26 @@ var Aspi2 = class {
|
|
|
1088
1411
|
* });
|
|
1089
1412
|
*/
|
|
1090
1413
|
setHeaders(headers) {
|
|
1091
|
-
this.#globalRequestInit.headers =
|
|
1414
|
+
this.#globalRequestInit.headers = {
|
|
1415
|
+
...this.#globalRequestInit.headers ?? {},
|
|
1416
|
+
...headers
|
|
1417
|
+
};
|
|
1092
1418
|
return this;
|
|
1093
1419
|
}
|
|
1094
1420
|
/**
|
|
1095
|
-
* Sets a single header for all API requests
|
|
1096
|
-
*
|
|
1097
|
-
* @param
|
|
1098
|
-
* @
|
|
1421
|
+
* Sets a single header for all API requests.
|
|
1422
|
+
*
|
|
1423
|
+
* @param key - The header name.
|
|
1424
|
+
* @param value - The header value.
|
|
1425
|
+
* @returns This {@link Aspi} instance for chaining.
|
|
1426
|
+
*
|
|
1099
1427
|
* @example
|
|
1100
1428
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1101
1429
|
* api.setHeader('Content-Type', 'application/json');
|
|
1102
1430
|
*/
|
|
1103
1431
|
setHeader(key, value) {
|
|
1104
1432
|
this.#globalRequestInit.headers = {
|
|
1105
|
-
...this.#globalRequestInit.headers,
|
|
1433
|
+
...this.#globalRequestInit.headers ?? {},
|
|
1106
1434
|
[key]: value
|
|
1107
1435
|
};
|
|
1108
1436
|
return this;
|
|
@@ -1119,18 +1447,28 @@ var Aspi2 = class {
|
|
|
1119
1447
|
return this.setHeader("Authorization", `Bearer ${token}`);
|
|
1120
1448
|
}
|
|
1121
1449
|
/**
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1450
|
+
* Register a request‑transformer middleware.
|
|
1451
|
+
*
|
|
1452
|
+
* The supplied function receives the current request initialization object
|
|
1453
|
+
* (`T`) and must return a request initialization of type `U`. The middleware
|
|
1454
|
+
* is added to the internal middleware chain and will be applied to every
|
|
1455
|
+
* request created by this {@link Aspi} instance.
|
|
1456
|
+
*
|
|
1457
|
+
* @template T - The input request type, extending the current {@link Aspi} request init type.
|
|
1458
|
+
* @template U - The output request type after transformation.
|
|
1459
|
+
* @param {RequestTransformer<T, U>} fn - The middleware function that transforms a request configuration.
|
|
1460
|
+
* @returns {Aspi<U>} A new {@link Aspi} instance typed with the transformed request configuration.
|
|
1125
1461
|
* @example
|
|
1126
1462
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1127
|
-
* api.use((req) => {
|
|
1128
|
-
* // Add custom headers
|
|
1129
|
-
*
|
|
1130
|
-
* ...req
|
|
1131
|
-
*
|
|
1463
|
+
* const apiWithHeaders = api.use((req) => {
|
|
1464
|
+
* // Add custom headers to every request
|
|
1465
|
+
* return {
|
|
1466
|
+
* ...req,
|
|
1467
|
+
* headers: {
|
|
1468
|
+
* ...req.headers,
|
|
1469
|
+
* 'x-custom-header': 'custom-value',
|
|
1470
|
+
* },
|
|
1132
1471
|
* };
|
|
1133
|
-
* return req;
|
|
1134
1472
|
* });
|
|
1135
1473
|
*/
|
|
1136
1474
|
use(fn) {
|
|
@@ -1262,6 +1600,16 @@ var Aspi2 = class {
|
|
|
1262
1600
|
*/
|
|
1263
1601
|
throwable() {
|
|
1264
1602
|
this.#throwOnError = true;
|
|
1603
|
+
this.#shouldBeResult = false;
|
|
1604
|
+
return this;
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Configures the request to return a Result object instead of just the response body.
|
|
1608
|
+
* @returns The Aspi instance with result handling enabled.
|
|
1609
|
+
*/
|
|
1610
|
+
withResult() {
|
|
1611
|
+
this.#shouldBeResult = true;
|
|
1612
|
+
this.#throwOnError = false;
|
|
1265
1613
|
return this;
|
|
1266
1614
|
}
|
|
1267
1615
|
};
|