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.cjs
CHANGED
|
@@ -103,7 +103,7 @@ var CustomError = class extends Error {
|
|
|
103
103
|
}
|
|
104
104
|
};
|
|
105
105
|
var isAspiError = (error) => {
|
|
106
|
-
return error instanceof AspiError;
|
|
106
|
+
return error instanceof AspiError && error.tag === "aspiError";
|
|
107
107
|
};
|
|
108
108
|
var isCustomError = (error) => {
|
|
109
109
|
return error instanceof CustomError;
|
|
@@ -315,19 +315,17 @@ var Request = class {
|
|
|
315
315
|
#shouldBeResult = false;
|
|
316
316
|
#bodySchemaIssues = [];
|
|
317
317
|
#throwOnError = false;
|
|
318
|
-
constructor(method, path, {
|
|
319
|
-
requestConfig,
|
|
320
|
-
retryConfig,
|
|
321
|
-
middlewares,
|
|
322
|
-
errorCbs,
|
|
323
|
-
throwOnError
|
|
324
|
-
}) {
|
|
318
|
+
constructor(method, path, requestOptions) {
|
|
325
319
|
this.#path = path;
|
|
326
|
-
this.#middlewares = middlewares || [];
|
|
327
|
-
this.#localRequestInit = {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
320
|
+
this.#middlewares = requestOptions.middlewares || [];
|
|
321
|
+
this.#localRequestInit = {
|
|
322
|
+
...requestOptions.requestConfig,
|
|
323
|
+
method
|
|
324
|
+
};
|
|
325
|
+
this.#retryConfig = requestOptions.retryConfig;
|
|
326
|
+
this.#customErrorCbs = requestOptions.errorCbs || {};
|
|
327
|
+
this.#throwOnError = requestOptions.throwOnError || false;
|
|
328
|
+
this.#shouldBeResult = requestOptions.shouldBeResult || false;
|
|
331
329
|
}
|
|
332
330
|
/**
|
|
333
331
|
* Sets the base URL for the request.
|
|
@@ -362,18 +360,25 @@ var Request = class {
|
|
|
362
360
|
return this;
|
|
363
361
|
}
|
|
364
362
|
/**
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
* @
|
|
363
|
+
* Merges the provided headers into the request configuration.
|
|
364
|
+
*
|
|
365
|
+
* @param {HeadersInit} headers - An object or iterable containing header name/value pairs.
|
|
366
|
+
* Existing headers are retained unless a key in this object overwrites them.
|
|
367
|
+
* @returns {this} The current {@link Request} instance for method chaining.
|
|
368
|
+
*
|
|
368
369
|
* @example
|
|
370
|
+
* // Set common JSON headers
|
|
369
371
|
* const request = new Request('/users', config);
|
|
370
372
|
* request.setHeaders({
|
|
371
373
|
* 'Content-Type': 'application/json',
|
|
372
|
-
* 'Accept': 'application/json'
|
|
374
|
+
* 'Accept': 'application/json',
|
|
373
375
|
* });
|
|
374
376
|
*/
|
|
375
377
|
setHeaders(headers) {
|
|
376
|
-
this.#localRequestInit.headers =
|
|
378
|
+
this.#localRequestInit.headers = {
|
|
379
|
+
...this.#localRequestInit.headers ?? {},
|
|
380
|
+
...headers
|
|
381
|
+
};
|
|
377
382
|
return this;
|
|
378
383
|
}
|
|
379
384
|
/**
|
|
@@ -387,7 +392,7 @@ var Request = class {
|
|
|
387
392
|
*/
|
|
388
393
|
setHeader(key, value) {
|
|
389
394
|
this.#localRequestInit.headers = {
|
|
390
|
-
...this.#localRequestInit.headers,
|
|
395
|
+
...this.#localRequestInit.headers ?? {},
|
|
391
396
|
[key]: value
|
|
392
397
|
};
|
|
393
398
|
return this;
|
|
@@ -449,6 +454,8 @@ var Request = class {
|
|
|
449
454
|
} else {
|
|
450
455
|
this.#localRequestInit.body = JSON.stringify(body);
|
|
451
456
|
}
|
|
457
|
+
} else {
|
|
458
|
+
this.#localRequestInit.body = JSON.stringify(body);
|
|
452
459
|
}
|
|
453
460
|
return this;
|
|
454
461
|
}
|
|
@@ -583,29 +590,54 @@ var Request = class {
|
|
|
583
590
|
return this.error("internalServerError", "INTERNAL_SERVER_ERROR", cb);
|
|
584
591
|
}
|
|
585
592
|
/**
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
*
|
|
593
|
+
* Register a custom error handler for a specific HTTP status code.
|
|
594
|
+
*
|
|
595
|
+
* When the response matches the provided `status`, the supplied callback `cb`
|
|
596
|
+
* is invoked and its return value is wrapped in a {@link CustomError} with the
|
|
597
|
+
* given `tag`. The method also augments the request's generic `Opts['error']`
|
|
598
|
+
* type so that the custom error is reflected in the resulting `Result`
|
|
599
|
+
* union.
|
|
600
|
+
*
|
|
601
|
+
* @template Tag - A string literal used as the error tag.
|
|
602
|
+
* @template A - The shape of the data returned by the callback.
|
|
603
|
+
*
|
|
604
|
+
* @param {Tag} tag
|
|
605
|
+
* A unique identifier for the custom error. This value becomes the `tag`
|
|
606
|
+
* property of the {@link CustomError} produced by the handler.
|
|
607
|
+
*
|
|
608
|
+
* @param {HttpErrorStatus} status
|
|
609
|
+
* The HTTP status code (e.g. `'BAD_REQUEST'`, `'NOT_FOUND'`) that should
|
|
610
|
+
* trigger the custom handler.
|
|
611
|
+
*
|
|
612
|
+
* @param {CustomErrorCb<TRequest, A>} cb
|
|
613
|
+
* A callback that receives the failing request and response objects and
|
|
614
|
+
* returns an object describing the error payload.
|
|
615
|
+
*
|
|
616
|
+
* @returns {Request<Method, TRequest, Merge<Omit<Opts, 'error'>, { error: { [K in Tag | keyof Opts['error']]: K extends Tag ? CustomError<Tag, A> : Opts['error'][K]; } }>>}
|
|
617
|
+
* The same {@link Request} instance, now typed with the newly added error
|
|
618
|
+
* variant, allowing method‑chaining.
|
|
619
|
+
*
|
|
591
620
|
* @example
|
|
621
|
+
* ```ts
|
|
592
622
|
* const request = new Request('/users', config);
|
|
623
|
+
*
|
|
624
|
+
* // Attach a custom handler for a 400 Bad Request response
|
|
593
625
|
* request
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
*
|
|
626
|
+
* withResult()
|
|
627
|
+
* .error('customError', 'BAD_REQUEST', (ctx) => {
|
|
628
|
+
* console.log('Bad request error:', ctx);
|
|
629
|
+
* return {
|
|
630
|
+
* message: 'Invalid input',
|
|
631
|
+
* details: ctx.response.responseData,
|
|
632
|
+
* };
|
|
633
|
+
* });
|
|
602
634
|
*
|
|
603
|
-
* // Later when
|
|
635
|
+
* // Later, when executing the request:
|
|
604
636
|
* const result = await request.json();
|
|
605
|
-
* if (Result.isErr(result)) {
|
|
606
|
-
* if(result.tag === 'customError') {
|
|
637
|
+
* if (Result.isErr(result) && result.tag === 'customError') {
|
|
607
638
|
* console.log(result.error.data.message); // 'Invalid input'
|
|
608
639
|
* }
|
|
640
|
+
* ```
|
|
609
641
|
*/
|
|
610
642
|
error(tag, status, cb) {
|
|
611
643
|
this.#customErrorCbs[httpErrors[status]] = {
|
|
@@ -615,58 +647,106 @@ var Request = class {
|
|
|
615
647
|
return this;
|
|
616
648
|
}
|
|
617
649
|
/**
|
|
618
|
-
* Sets query parameters for the request URL.
|
|
619
|
-
*
|
|
620
|
-
*
|
|
650
|
+
* Sets the query parameters for the request URL.
|
|
651
|
+
*
|
|
652
|
+
* Accepts any value that can be passed to the `URLSearchParams` constructor:
|
|
653
|
+
* - an object mapping keys to string values,
|
|
654
|
+
* - an iterable of `[key, value]` tuples,
|
|
655
|
+
* - a raw query string, or
|
|
656
|
+
* - an existing {@link URLSearchParams} instance.
|
|
657
|
+
*
|
|
658
|
+
* The supplied parameters replace any previously defined query parameters.
|
|
659
|
+
*
|
|
660
|
+
* @template T - The concrete type of the supplied parameters.
|
|
661
|
+
* @param {T} params - The query parameters to apply.
|
|
662
|
+
* @returns {this} The request instance for method‑chaining.
|
|
663
|
+
*
|
|
621
664
|
* @example
|
|
622
665
|
* const request = new Request('/users', config);
|
|
623
666
|
* request.setQueryParams({
|
|
624
667
|
* page: '1',
|
|
625
668
|
* limit: '10',
|
|
626
|
-
* sort: 'desc'
|
|
669
|
+
* sort: 'desc',
|
|
627
670
|
* });
|
|
671
|
+
*
|
|
672
|
+
* // Using a raw query string
|
|
673
|
+
* request.setQueryParams('page=1&limit=10');
|
|
674
|
+
*
|
|
675
|
+
* // Using an array of entries
|
|
676
|
+
* request.setQueryParams([['page', '1'], ['limit', '10']]);
|
|
677
|
+
*
|
|
678
|
+
* // Using an existing URLSearchParams instance
|
|
679
|
+
* const qp = new URLSearchParams({ page: '1' });
|
|
680
|
+
* request.setQueryParams(qp);
|
|
628
681
|
*/
|
|
629
682
|
setQueryParams(params) {
|
|
630
683
|
this.#queryParams = new URLSearchParams(params);
|
|
631
684
|
return this;
|
|
632
685
|
}
|
|
633
686
|
/**
|
|
634
|
-
* Sets
|
|
635
|
-
*
|
|
636
|
-
* @
|
|
687
|
+
* Sets a validation schema for the response data.
|
|
688
|
+
*
|
|
689
|
+
* The provided {@link StandardSchemaV1} schema will be used to validate the
|
|
690
|
+
* response payload when the request is executed. If validation fails, a
|
|
691
|
+
* `parseError` is added to the request's error type.
|
|
692
|
+
*
|
|
693
|
+
* @template TSchema - A type extending {@link StandardSchemaV1}
|
|
694
|
+
* @param schema - The schema used to validate the response data
|
|
695
|
+
* @returns The request instance for chaining with an updated generic type that
|
|
696
|
+
* includes the schema and a possible `parseError` in the error union
|
|
697
|
+
*
|
|
637
698
|
* @example
|
|
699
|
+
* ```ts
|
|
638
700
|
* import { z } from 'zod';
|
|
639
701
|
*
|
|
640
702
|
* const userSchema = z.object({
|
|
641
703
|
* id: z.number(),
|
|
642
704
|
* name: z.string(),
|
|
643
|
-
* email: z.string().email()
|
|
705
|
+
* email: z.string().email(),
|
|
644
706
|
* });
|
|
645
707
|
*
|
|
646
708
|
* const request = new Request('/users', config);
|
|
647
709
|
* const result = await request
|
|
648
710
|
* .withResult()
|
|
649
|
-
* .
|
|
711
|
+
* .schema(userSchema)
|
|
650
712
|
* .json();
|
|
651
713
|
*
|
|
652
714
|
* if (Result.isOk(result)) {
|
|
653
715
|
* const user = result.value; // Typed and validated user data
|
|
654
716
|
* }
|
|
717
|
+
* ```
|
|
655
718
|
*/
|
|
656
719
|
schema(schema) {
|
|
657
720
|
this.#schema = schema;
|
|
658
721
|
return this;
|
|
659
722
|
}
|
|
660
723
|
/**
|
|
661
|
-
*
|
|
662
|
-
*
|
|
724
|
+
* Configures the request to **throw** an exception when the response status
|
|
725
|
+
* indicates a failure (i.e., `!response.ok`). This disables the “Result”
|
|
726
|
+
* mode (`withResult`) and enables “throwable” mode, causing
|
|
727
|
+
* `await request.json()` (or other response helpers) to either resolve with
|
|
728
|
+
* the successful payload **or** reject with an `AspiError`/`CustomError`.
|
|
729
|
+
*
|
|
730
|
+
* Use this when you prefer traditional `try / catch` error handling over
|
|
731
|
+
* the explicit `Result` type returned by {@link withResult}.
|
|
732
|
+
*
|
|
733
|
+
* @returns This {@link Request} instance, now typed with `throwable: true` and
|
|
734
|
+
* `withResult: false` for proper chaining.
|
|
735
|
+
*
|
|
663
736
|
* @example
|
|
737
|
+
* ```ts
|
|
664
738
|
* const request = new Request('/users', config);
|
|
665
|
-
* const result = await request
|
|
666
|
-
* .withResult()
|
|
667
|
-
* .throwable()
|
|
668
|
-
* .json();
|
|
669
739
|
*
|
|
740
|
+
* try {
|
|
741
|
+
* const user = await request
|
|
742
|
+
* .throwable() // Enable throwing on HTTP errors
|
|
743
|
+
* .json<User>(); // Will throw if the response is not ok
|
|
744
|
+
* console.log(user);
|
|
745
|
+
* } catch (err) {
|
|
746
|
+
* // err is either AspiError or a CustomError returned by a custom handler
|
|
747
|
+
* console.error('Request failed:', err);
|
|
748
|
+
* }
|
|
749
|
+
* ```
|
|
670
750
|
*/
|
|
671
751
|
throwable() {
|
|
672
752
|
this.#shouldBeResult = false;
|
|
@@ -674,43 +754,100 @@ var Request = class {
|
|
|
674
754
|
return this;
|
|
675
755
|
}
|
|
676
756
|
/**
|
|
677
|
-
*
|
|
678
|
-
*
|
|
757
|
+
* Sends the request and parses the response body as JSON.
|
|
758
|
+
*
|
|
759
|
+
* The resolved value of the returned promise varies based on the request mode:
|
|
760
|
+
*
|
|
761
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} that
|
|
762
|
+
* contains either an {@link AspiResultOk} with the parsed payload or a union
|
|
763
|
+
* of possible error types (HTTP errors, custom errors, JSON‑parse errors,
|
|
764
|
+
* schema‑validation errors, etc.).
|
|
765
|
+
*
|
|
766
|
+
* - **Throwable mode** (`throwable()`): resolves directly to the successful
|
|
767
|
+
* payload (`AspiPlainResponse`) and throws a {@link AspiError} or
|
|
768
|
+
* {@link CustomError} on failure.
|
|
769
|
+
*
|
|
770
|
+
* - **Default mode** (no explicit mode): resolves to a tuple
|
|
771
|
+
* `[value, error]` where exactly one element is non‑null.
|
|
772
|
+
*
|
|
773
|
+
* @template T - The inferred output type of the response schema (if a schema
|
|
774
|
+
* was supplied via {@link schema}). When no schema is provided `T` defaults
|
|
775
|
+
* to `any`.
|
|
776
|
+
*
|
|
777
|
+
* @returns A promise whose shape depends on the selected mode (see description).
|
|
778
|
+
* In Result mode it is `Result<ResultOk<…>, …>`, in throwable mode it is
|
|
779
|
+
* `AspiPlainResponse<…>`, and otherwise a tuple
|
|
780
|
+
* `[AspiResultOk<…> | null, Error | null]`.
|
|
781
|
+
*
|
|
679
782
|
* @example
|
|
783
|
+
* // Using the Result API
|
|
680
784
|
* const request = new Request('/users', config);
|
|
681
785
|
* const result = await request
|
|
682
786
|
* .setQueryParams({ id: '123' })
|
|
683
|
-
* .
|
|
684
|
-
|
|
685
|
-
* .notFound((error) => ({ message: 'User not found' }))
|
|
787
|
+
* .withResult()
|
|
788
|
+
* .notFound(() => ({ message: 'User not found' }))
|
|
686
789
|
* .json<User>();
|
|
687
790
|
*
|
|
688
791
|
* if (Result.isOk(result)) {
|
|
689
|
-
*
|
|
792
|
+
* // `result.value` has type `User`
|
|
793
|
+
* console.log(result.value);
|
|
690
794
|
* } else {
|
|
691
|
-
* console.error(result.error);
|
|
795
|
+
* console.error(result.error);
|
|
692
796
|
* }
|
|
693
797
|
*/
|
|
694
798
|
async json() {
|
|
695
|
-
const output = await this.#makeRequest(
|
|
696
|
-
|
|
799
|
+
const output = await this.#makeRequest(async (response) => {
|
|
800
|
+
if (response.status === 204 || response.status >= 300 && response.status < 400) {
|
|
801
|
+
return null;
|
|
802
|
+
}
|
|
803
|
+
return response.json().catch(
|
|
697
804
|
(e) => new CustomError("jsonParseError", {
|
|
698
805
|
message: e instanceof Error ? e.message : "Failed to parse JSON"
|
|
699
806
|
})
|
|
700
|
-
)
|
|
701
|
-
|
|
702
|
-
);
|
|
807
|
+
);
|
|
808
|
+
}, true);
|
|
703
809
|
return this.#mapResponse(output);
|
|
704
810
|
}
|
|
705
811
|
/**
|
|
706
|
-
* Executes the request and returns the response as plain text.
|
|
707
|
-
*
|
|
812
|
+
* Executes the request and returns the response body as plain text.
|
|
813
|
+
*
|
|
814
|
+
* The method respects the request mode:
|
|
815
|
+
*
|
|
816
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result}
|
|
817
|
+
* containing either an {@link AspiResultOk} with the text payload or an
|
|
818
|
+
* error variant.
|
|
819
|
+
* - **Throwable mode** (`throwable()`): resolves directly to the text string
|
|
820
|
+
* and throws on error.
|
|
821
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one
|
|
822
|
+
* element is `null`.
|
|
823
|
+
*
|
|
824
|
+
* @returns {Promise<
|
|
825
|
+
* Opts['withResult'] extends true
|
|
826
|
+
* ? Result.Result<
|
|
827
|
+
* AspiResultOk<TRequest, string>,
|
|
828
|
+
* AspiError<TRequest> |
|
|
829
|
+
* (Opts extends { error: any } ? Opts['error'][keyof Opts['error']] : never)
|
|
830
|
+
* >
|
|
831
|
+
* : Opts['throwable'] extends true
|
|
832
|
+
* ? AspiPlainResponse<TRequest, string>
|
|
833
|
+
* : [
|
|
834
|
+
* AspiResultOk<TRequest, string> | null,
|
|
835
|
+
* (
|
|
836
|
+
* | AspiError<TRequest>
|
|
837
|
+
* | (Opts extends { error: any } ? Opts['error'][keyof Opts['error']] : never)
|
|
838
|
+
* | null
|
|
839
|
+
* )
|
|
840
|
+
* ]
|
|
841
|
+
* }>
|
|
842
|
+
* A promise that resolves according to the selected request mode.
|
|
843
|
+
*
|
|
708
844
|
* @example
|
|
845
|
+
* ```ts
|
|
709
846
|
* const request = new Request('/data.txt', config);
|
|
710
847
|
* const result = await request
|
|
711
848
|
* .setQueryParams({ version: '1' })
|
|
712
849
|
* .withResult()
|
|
713
|
-
* .notFound((
|
|
850
|
+
* .notFound(() => ({ message: 'Text file not found' }))
|
|
714
851
|
* .text();
|
|
715
852
|
*
|
|
716
853
|
* if (Result.isOk(result)) {
|
|
@@ -718,22 +855,58 @@ var Request = class {
|
|
|
718
855
|
* } else {
|
|
719
856
|
* console.error(result.error); // Error handling
|
|
720
857
|
* }
|
|
858
|
+
* ```
|
|
721
859
|
*/
|
|
722
860
|
async text() {
|
|
723
|
-
const output = await this.#makeRequest(
|
|
724
|
-
|
|
725
|
-
);
|
|
861
|
+
const output = await this.#makeRequest((response) => {
|
|
862
|
+
return response.text();
|
|
863
|
+
});
|
|
726
864
|
return this.#mapResponse(output);
|
|
727
865
|
}
|
|
728
866
|
/**
|
|
729
|
-
* Executes the request and returns the response as a Blob.
|
|
730
|
-
*
|
|
867
|
+
* Executes the request and returns the response body as a {@link Blob}.
|
|
868
|
+
*
|
|
869
|
+
* The shape of the returned {@link Promise} depends on the request mode:
|
|
870
|
+
*
|
|
871
|
+
* - **Result mode** (`withResult()`): resolves to a {@link Result.Result} containing
|
|
872
|
+
* either an {@link AspiResultOk} with `Blob` data or an error variant.
|
|
873
|
+
* - **Throwable mode** (`throwable()`): resolves directly to a {@link Blob}
|
|
874
|
+
* (wrapped in {@link AspiPlainResponse}) and throws on failure.
|
|
875
|
+
* - **Default mode**: resolves to a tuple `[value, error]` where exactly one element
|
|
876
|
+
* is `null`.
|
|
877
|
+
*
|
|
878
|
+
* @returns {Promise<
|
|
879
|
+
* Opts['withResult'] extends true
|
|
880
|
+
* ? Result.Result<
|
|
881
|
+
* AspiResultOk<TRequest, Blob>,
|
|
882
|
+
* | AspiError<TRequest>
|
|
883
|
+
* | (Opts extends { error: any }
|
|
884
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
885
|
+
* : never)
|
|
886
|
+
* >
|
|
887
|
+
* : Opts['throwable'] extends true
|
|
888
|
+
* ? AspiPlainResponse<TRequest, Blob>
|
|
889
|
+
* : [
|
|
890
|
+
* AspiResultOk<TRequest, Blob> | null,
|
|
891
|
+
* (
|
|
892
|
+
* | (
|
|
893
|
+
* | AspiError<TRequest>
|
|
894
|
+
* | (Opts extends { error: any }
|
|
895
|
+
* ? Opts['error'][keyof Opts['error']]
|
|
896
|
+
* : never)
|
|
897
|
+
* )
|
|
898
|
+
* | null
|
|
899
|
+
* ),
|
|
900
|
+
* ]
|
|
901
|
+
* }>
|
|
902
|
+
*
|
|
731
903
|
* @example
|
|
904
|
+
* ```ts
|
|
732
905
|
* const request = new Request('/image.jpg', config);
|
|
733
906
|
* const result = await request
|
|
734
907
|
* .setQueryParams({ size: 'large' })
|
|
735
908
|
* .withResult()
|
|
736
|
-
* .notFound((
|
|
909
|
+
* .notFound(() => ({ message: 'Image not found' }))
|
|
737
910
|
* .blob();
|
|
738
911
|
*
|
|
739
912
|
* if (Result.isOk(result)) {
|
|
@@ -741,43 +914,77 @@ var Request = class {
|
|
|
741
914
|
* } else {
|
|
742
915
|
* console.error(result.error); // Error handling
|
|
743
916
|
* }
|
|
917
|
+
* ```
|
|
744
918
|
*/
|
|
745
919
|
async blob() {
|
|
746
920
|
const output = await this.#makeRequest((response) => response.blob());
|
|
747
921
|
return this.#mapResponse(output);
|
|
748
922
|
}
|
|
749
923
|
#url() {
|
|
750
|
-
const
|
|
924
|
+
const passedBaseUrl = typeof this.#localRequestInit.baseUrl === "string" ? this.#localRequestInit.baseUrl : this.#localRequestInit.baseUrl.toString();
|
|
925
|
+
const baseUrl = passedBaseUrl.replace(/\/+$/, "") ?? "";
|
|
751
926
|
const path = this.#path.replace(/^\/+/, "/");
|
|
752
927
|
const queryString = this.#queryParams ? `?${this.#queryParams.toString()}` : "";
|
|
753
928
|
const url = [baseUrl, path, queryString].filter(Boolean).join("");
|
|
754
929
|
return url;
|
|
755
930
|
}
|
|
756
931
|
/**
|
|
757
|
-
* Returns the
|
|
758
|
-
*
|
|
932
|
+
* Returns the fully‑qualified URL that will be used for the request.
|
|
933
|
+
*
|
|
934
|
+
* The URL is constructed from the configured base URL, the request path,
|
|
935
|
+
* and any query parameters added via {@link setQueryParams}.
|
|
936
|
+
*
|
|
937
|
+
* @returns {string} The complete request URL.
|
|
938
|
+
*
|
|
759
939
|
* @example
|
|
940
|
+
* ```ts
|
|
760
941
|
* const request = new Request('/users', config);
|
|
761
942
|
* request.setBaseUrl('https://api.example.com');
|
|
762
943
|
* request.setQueryParams({ id: '123' });
|
|
763
|
-
*
|
|
944
|
+
*
|
|
945
|
+
* console.log(request.url());
|
|
946
|
+
* // => 'https://api.example.com/users?id=123'
|
|
947
|
+
* ```
|
|
764
948
|
*/
|
|
765
949
|
url() {
|
|
766
950
|
return this.#url();
|
|
767
951
|
}
|
|
768
952
|
/**
|
|
769
|
-
*
|
|
770
|
-
*
|
|
953
|
+
* Switches the request into **Result** mode.
|
|
954
|
+
*
|
|
955
|
+
* In Result mode the response helpers (`json`, `text`, `blob`, …) resolve to a
|
|
956
|
+
* {@link Result.Result} instance instead of the default tuple
|
|
957
|
+
* `[value, error]`. This allows callers to use pattern matching
|
|
958
|
+
* (`Result.isOk`, `Result.isErr`) to handle success and failure.
|
|
959
|
+
*
|
|
960
|
+
* Calling `withResult` disables the “throwable” behaviour (see {@link throwable}).
|
|
961
|
+
*
|
|
962
|
+
* @returns {Request<
|
|
963
|
+
* Method,
|
|
964
|
+
* TRequest,
|
|
965
|
+
* Merge<
|
|
966
|
+
* Omit<Opts, 'withResult' | 'throwable'>,
|
|
967
|
+
* {
|
|
968
|
+
* withResult: true;
|
|
969
|
+
* throwable: false;
|
|
970
|
+
* }
|
|
971
|
+
* >
|
|
972
|
+
* >} The same {@link Request} instance, now typed with `withResult: true` and
|
|
973
|
+
* `throwable: false` for fluent chaining.
|
|
974
|
+
*
|
|
771
975
|
* @example
|
|
976
|
+
* ```ts
|
|
772
977
|
* const request = new Request('/users', config);
|
|
978
|
+
*
|
|
773
979
|
* const result = await request
|
|
774
|
-
* .withResult()
|
|
980
|
+
* .withResult() // enable Result mode
|
|
775
981
|
* .json<User>();
|
|
776
982
|
*
|
|
777
|
-
* // Returns Result type instead of tuple
|
|
778
983
|
* if (Result.isOk(result)) {
|
|
779
|
-
*
|
|
984
|
+
* // `result.value` is of type `User`
|
|
985
|
+
* console.log(result.value);
|
|
780
986
|
* }
|
|
987
|
+
* ```
|
|
781
988
|
*/
|
|
782
989
|
withResult() {
|
|
783
990
|
this.#throwOnError = false;
|
|
@@ -797,6 +1004,9 @@ var Request = class {
|
|
|
797
1004
|
return [null, getErrorOrNull(value)];
|
|
798
1005
|
}
|
|
799
1006
|
}
|
|
1007
|
+
#isSuccessResponse(response) {
|
|
1008
|
+
return response.ok || response.status >= 300 && response.status < 400;
|
|
1009
|
+
}
|
|
800
1010
|
async #makeRequest(responseParser, isJson = false) {
|
|
801
1011
|
if (this.#bodySchemaIssues.length) {
|
|
802
1012
|
return err(new CustomError("parseError", this.#bodySchemaIssues));
|
|
@@ -807,20 +1017,23 @@ var Request = class {
|
|
|
807
1017
|
const requestInit = request.requestInit;
|
|
808
1018
|
const url = this.#url();
|
|
809
1019
|
let attempts = 1;
|
|
810
|
-
let response
|
|
811
|
-
|
|
1020
|
+
let response = new Response(null, {
|
|
1021
|
+
status: 500,
|
|
1022
|
+
statusText: "Internal Server Error"
|
|
1023
|
+
});
|
|
1024
|
+
let responseData = null;
|
|
812
1025
|
while (attempts <= retries) {
|
|
813
1026
|
try {
|
|
814
1027
|
response = await fetch(url, requestInit);
|
|
815
1028
|
responseData = await responseParser(response);
|
|
816
|
-
if (responseData instanceof
|
|
1029
|
+
if (responseData instanceof Error) {
|
|
817
1030
|
return err(responseData);
|
|
818
1031
|
}
|
|
819
1032
|
const retryWhileCondition = retryWhile ? await retryWhile(
|
|
820
1033
|
request,
|
|
821
1034
|
this.#makeResponse(response, responseData)
|
|
822
1035
|
) : false;
|
|
823
|
-
if (response
|
|
1036
|
+
if (this.#isSuccessResponse(response) || !retryOn.includes(response.status) && !retryWhileCondition) {
|
|
824
1037
|
break;
|
|
825
1038
|
}
|
|
826
1039
|
if (response.status in this.#customErrorCbs && attempts === retries) {
|
|
@@ -843,9 +1056,31 @@ var Request = class {
|
|
|
843
1056
|
request,
|
|
844
1057
|
this.#makeResponse(response, responseData)
|
|
845
1058
|
) : retryDelay;
|
|
846
|
-
await
|
|
1059
|
+
await this.#abortDelay(delay, request);
|
|
847
1060
|
}
|
|
848
1061
|
} catch (e) {
|
|
1062
|
+
if (e instanceof Error && e.name === "AbortError") {
|
|
1063
|
+
if (500 in this.#customErrorCbs) {
|
|
1064
|
+
const result = this.#customErrorCbs[response.status].cb({
|
|
1065
|
+
request,
|
|
1066
|
+
response: this.#makeResponse(response, responseData)
|
|
1067
|
+
});
|
|
1068
|
+
return err(
|
|
1069
|
+
new CustomError(
|
|
1070
|
+
// @ts-ignore
|
|
1071
|
+
this.#customErrorCbs[response.status].tag,
|
|
1072
|
+
result
|
|
1073
|
+
)
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
return err(
|
|
1077
|
+
new AspiError(
|
|
1078
|
+
e.message,
|
|
1079
|
+
this.#request(),
|
|
1080
|
+
this.#makeResponse(response, responseData)
|
|
1081
|
+
)
|
|
1082
|
+
);
|
|
1083
|
+
}
|
|
849
1084
|
if (attempts === retries) throw e;
|
|
850
1085
|
const delay = typeof retryDelay === "function" ? await retryDelay(
|
|
851
1086
|
retries - attempts - 1,
|
|
@@ -853,14 +1088,14 @@ var Request = class {
|
|
|
853
1088
|
request,
|
|
854
1089
|
this.#makeResponse(response, responseData)
|
|
855
1090
|
) : retryDelay;
|
|
856
|
-
await
|
|
1091
|
+
await this.#abortDelay(delay, request);
|
|
857
1092
|
}
|
|
858
1093
|
if (onRetry) {
|
|
859
1094
|
onRetry(request, this.#makeResponse(response, responseData));
|
|
860
1095
|
}
|
|
861
1096
|
attempts++;
|
|
862
1097
|
}
|
|
863
|
-
if (!response
|
|
1098
|
+
if (!this.#isSuccessResponse(response)) {
|
|
864
1099
|
if (response.status in this.#customErrorCbs) {
|
|
865
1100
|
const result = this.#customErrorCbs[response.status].cb({
|
|
866
1101
|
request,
|
|
@@ -930,17 +1165,38 @@ var Request = class {
|
|
|
930
1165
|
);
|
|
931
1166
|
}
|
|
932
1167
|
}
|
|
1168
|
+
#abortDelay(ms, request) {
|
|
1169
|
+
return new Promise((resolve, reject) => {
|
|
1170
|
+
const timer = setTimeout(resolve, ms);
|
|
1171
|
+
const signal = request.requestInit.signal;
|
|
1172
|
+
if (signal) {
|
|
1173
|
+
if (signal.aborted) {
|
|
1174
|
+
clearTimeout(timer);
|
|
1175
|
+
reject(new DOMException("The user aborted a request.", "AbortError"));
|
|
1176
|
+
} else {
|
|
1177
|
+
const abortHandler = () => {
|
|
1178
|
+
clearTimeout(timer);
|
|
1179
|
+
reject(
|
|
1180
|
+
new DOMException("The user aborted a request.", "AbortError")
|
|
1181
|
+
);
|
|
1182
|
+
};
|
|
1183
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
933
1188
|
#request() {
|
|
934
1189
|
let requestInit = this.#localRequestInit;
|
|
935
1190
|
for (const middleware of this.#middlewares) {
|
|
936
|
-
requestInit = middleware(
|
|
1191
|
+
requestInit = middleware(requestInit);
|
|
937
1192
|
}
|
|
938
1193
|
return {
|
|
939
|
-
requestInit
|
|
940
|
-
|
|
1194
|
+
requestInit: {
|
|
1195
|
+
...requestInit,
|
|
1196
|
+
retryConfig: this.#sanitisedRetryConfig()
|
|
1197
|
+
},
|
|
941
1198
|
path: this.#path,
|
|
942
|
-
queryParams: this.#queryParams || null
|
|
943
|
-
retryConfig: this.#sanitisedRetryConfig()
|
|
1199
|
+
queryParams: this.#queryParams || null
|
|
944
1200
|
};
|
|
945
1201
|
}
|
|
946
1202
|
#sanitisedRetryConfig() {
|
|
@@ -965,6 +1221,68 @@ var Request = class {
|
|
|
965
1221
|
responseData
|
|
966
1222
|
};
|
|
967
1223
|
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Returns the underlying {@link AspiRequest} object that will be used for the fetch call.
|
|
1226
|
+
*
|
|
1227
|
+
* This method does not perform any network activity; it simply builds and returns the
|
|
1228
|
+
* request configuration, including any applied middlewares, query parameters, etc.
|
|
1229
|
+
*
|
|
1230
|
+
* @returns {AspiRequest<TRequest>} The constructed request object.
|
|
1231
|
+
*/
|
|
1232
|
+
getRequest() {
|
|
1233
|
+
return this.#request();
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Retrieves the registry of custom error callbacks that have been
|
|
1237
|
+
* registered via {@link error}. The returned object maps HTTP status
|
|
1238
|
+
* codes to their corresponding callback functions and tags.
|
|
1239
|
+
*
|
|
1240
|
+
* @returns {ErrorCallbacks} A shallow copy of the internal error callback registry.
|
|
1241
|
+
*/
|
|
1242
|
+
getErrorCallbackRegistry() {
|
|
1243
|
+
return { ...this.#customErrorCbs };
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Returns whether the request is configured to return a {@link Result.Result}
|
|
1247
|
+
* instead of the default tuple or throwing.
|
|
1248
|
+
*
|
|
1249
|
+
* @returns {boolean} `true` when {@link withResult} has been called.
|
|
1250
|
+
*/
|
|
1251
|
+
isResult() {
|
|
1252
|
+
return this.#shouldBeResult;
|
|
1253
|
+
}
|
|
1254
|
+
/**
|
|
1255
|
+
* Returns whether the request is configured to throw on HTTP errors.
|
|
1256
|
+
*
|
|
1257
|
+
* @returns {boolean} `true` when {@link throwable} has been called.
|
|
1258
|
+
*/
|
|
1259
|
+
isThrowable() {
|
|
1260
|
+
return this.#throwOnError;
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Returns the effective retry configuration for this request, including defaulted values.
|
|
1264
|
+
*
|
|
1265
|
+
* The returned object contains:
|
|
1266
|
+
* - `retries` – number of retry attempts (default 1)
|
|
1267
|
+
* - `retryDelay` – delay between attempts in milliseconds or a function that returns a delay
|
|
1268
|
+
* - `retryOn` – array of HTTP status codes that should trigger a retry
|
|
1269
|
+
* - `retryWhile` – optional custom predicate executed after each response
|
|
1270
|
+
* - `onRetry` – optional callback invoked after a retry attempt
|
|
1271
|
+
*
|
|
1272
|
+
* A shallow copy is returned to avoid accidental mutation of the internal state.
|
|
1273
|
+
*
|
|
1274
|
+
* @returns {{
|
|
1275
|
+
* retries: number;
|
|
1276
|
+
* retryDelay: number | ((attempt: number, maxAttempts: number, request: AspiRequest<TRequest>, response: AspiResponse<any, true>) => number);
|
|
1277
|
+
* retryOn: number[];
|
|
1278
|
+
* retryWhile?: (request: AspiRequest<TRequest>, response: AspiResponse<any, true>) => boolean | Promise<boolean>;
|
|
1279
|
+
* onRetry?: (request: AspiRequest<TRequest>, response: AspiResponse<any, true>) => void;
|
|
1280
|
+
* }}
|
|
1281
|
+
*/
|
|
1282
|
+
getRetryConfig() {
|
|
1283
|
+
const cfg = this.#sanitisedRetryConfig();
|
|
1284
|
+
return { ...cfg };
|
|
1285
|
+
}
|
|
968
1286
|
};
|
|
969
1287
|
|
|
970
1288
|
// src/aspi.ts
|
|
@@ -974,15 +1292,20 @@ var Aspi2 = class {
|
|
|
974
1292
|
#retryConfig;
|
|
975
1293
|
#customErrorCbs = {};
|
|
976
1294
|
#throwOnError = false;
|
|
1295
|
+
#shouldBeResult = false;
|
|
977
1296
|
constructor(config) {
|
|
978
|
-
const { retryConfig, ...
|
|
979
|
-
this.#globalRequestInit =
|
|
1297
|
+
const { retryConfig, ...requestInit } = config;
|
|
1298
|
+
this.#globalRequestInit = requestInit;
|
|
980
1299
|
this.#retryConfig = retryConfig;
|
|
981
1300
|
}
|
|
982
1301
|
/**
|
|
983
|
-
* Sets the base URL for all API requests
|
|
984
|
-
*
|
|
985
|
-
*
|
|
1302
|
+
* Sets or overrides the base URL used for all subsequent API requests.
|
|
1303
|
+
*
|
|
1304
|
+
* Accepts either a string or a `URL` instance. If a `URL` object is provided,
|
|
1305
|
+
* it is converted to its string representation.
|
|
1306
|
+
*
|
|
1307
|
+
* @param url - The new base URL.
|
|
1308
|
+
* @returns The current {@link Aspi} instance for chaining.
|
|
986
1309
|
* @example
|
|
987
1310
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
988
1311
|
* api.setBaseUrl('https://api.newdomain.com');
|
|
@@ -1011,17 +1334,17 @@ var Aspi2 = class {
|
|
|
1011
1334
|
};
|
|
1012
1335
|
return this;
|
|
1013
1336
|
}
|
|
1014
|
-
#createRequest(method, path
|
|
1337
|
+
#createRequest(method, path) {
|
|
1015
1338
|
return new Request(method, path, {
|
|
1016
1339
|
requestConfig: {
|
|
1017
1340
|
...this.#globalRequestInit,
|
|
1018
|
-
method
|
|
1019
|
-
body
|
|
1341
|
+
method
|
|
1020
1342
|
},
|
|
1021
|
-
retryConfig: this.#retryConfig,
|
|
1022
1343
|
middlewares: this.#middlewares,
|
|
1023
1344
|
errorCbs: this.#customErrorCbs,
|
|
1024
|
-
throwOnError: this.#throwOnError
|
|
1345
|
+
throwOnError: this.#throwOnError,
|
|
1346
|
+
shouldBeResult: this.#shouldBeResult,
|
|
1347
|
+
retryConfig: this.#retryConfig
|
|
1025
1348
|
});
|
|
1026
1349
|
}
|
|
1027
1350
|
/**
|
|
@@ -1036,40 +1359,39 @@ var Aspi2 = class {
|
|
|
1036
1359
|
return this.#createRequest("GET", path);
|
|
1037
1360
|
}
|
|
1038
1361
|
/**
|
|
1039
|
-
* Makes a POST request to the specified path
|
|
1040
|
-
*
|
|
1041
|
-
* @param {
|
|
1042
|
-
* @returns {Request} A Request instance for chaining
|
|
1362
|
+
* Makes a POST request to the specified path.
|
|
1363
|
+
*
|
|
1364
|
+
* @param {string} path - The path to make the request to.
|
|
1365
|
+
* @returns {Request} A Request instance for chaining.
|
|
1366
|
+
*
|
|
1043
1367
|
* @example
|
|
1044
1368
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1045
|
-
* const response = await api.post('/users'
|
|
1369
|
+
* const response = await api.post('/users').json();
|
|
1046
1370
|
*/
|
|
1047
|
-
post(path
|
|
1048
|
-
return this.#createRequest("POST", path
|
|
1371
|
+
post(path) {
|
|
1372
|
+
return this.#createRequest("POST", path);
|
|
1049
1373
|
}
|
|
1050
1374
|
/**
|
|
1051
|
-
* Makes a PUT request to the specified path
|
|
1052
|
-
* @param {string} path - The path to make the request to
|
|
1053
|
-
* @
|
|
1054
|
-
* @returns {Request} A Request instance for chaining
|
|
1375
|
+
* Makes a PUT request to the specified path.
|
|
1376
|
+
* @param {string} path - The path to make the request to.
|
|
1377
|
+
* @returns {Request} A Request instance for chaining.
|
|
1055
1378
|
* @example
|
|
1056
1379
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1057
|
-
* const response = await api.put('/users/1'
|
|
1380
|
+
* const response = await api.put('/users/1').json();
|
|
1058
1381
|
*/
|
|
1059
|
-
put(path
|
|
1060
|
-
return this.#createRequest("PUT", path
|
|
1382
|
+
put(path) {
|
|
1383
|
+
return this.#createRequest("PUT", path);
|
|
1061
1384
|
}
|
|
1062
1385
|
/**
|
|
1063
|
-
* Makes a PATCH request to the specified path
|
|
1064
|
-
* @param {string} path - The path to make the request to
|
|
1065
|
-
* @
|
|
1066
|
-
* @returns {Request} A Request instance for chaining
|
|
1386
|
+
* Makes a PATCH request to the specified path.
|
|
1387
|
+
* @param {string} path - The path to make the request to.
|
|
1388
|
+
* @returns {Request} A Request instance for chaining.
|
|
1067
1389
|
* @example
|
|
1068
1390
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1069
|
-
* const response = await api.patch('/users/1'
|
|
1391
|
+
* const response = await api.patch('/users/1').json();
|
|
1070
1392
|
*/
|
|
1071
|
-
patch(path
|
|
1072
|
-
return this.#createRequest("PATCH", path
|
|
1393
|
+
patch(path) {
|
|
1394
|
+
return this.#createRequest("PATCH", path);
|
|
1073
1395
|
}
|
|
1074
1396
|
/**
|
|
1075
1397
|
* Makes a DELETE request to the specified path
|
|
@@ -1105,8 +1427,9 @@ var Aspi2 = class {
|
|
|
1105
1427
|
return this.#createRequest("OPTIONS", path);
|
|
1106
1428
|
}
|
|
1107
1429
|
/**
|
|
1108
|
-
* Sets multiple headers for all API requests
|
|
1109
|
-
*
|
|
1430
|
+
* Sets multiple headers for all API requests. Existing headers are preserved
|
|
1431
|
+
* and new ones are merged, overriding any duplicate keys.
|
|
1432
|
+
* @param {HeadersInit} headers - An object containing header key-value pairs
|
|
1110
1433
|
* @returns {Aspi} The Aspi instance for chaining
|
|
1111
1434
|
* @example
|
|
1112
1435
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
@@ -1116,21 +1439,26 @@ var Aspi2 = class {
|
|
|
1116
1439
|
* });
|
|
1117
1440
|
*/
|
|
1118
1441
|
setHeaders(headers) {
|
|
1119
|
-
this.#globalRequestInit.headers =
|
|
1442
|
+
this.#globalRequestInit.headers = {
|
|
1443
|
+
...this.#globalRequestInit.headers ?? {},
|
|
1444
|
+
...headers
|
|
1445
|
+
};
|
|
1120
1446
|
return this;
|
|
1121
1447
|
}
|
|
1122
1448
|
/**
|
|
1123
|
-
* Sets a single header for all API requests
|
|
1124
|
-
*
|
|
1125
|
-
* @param
|
|
1126
|
-
* @
|
|
1449
|
+
* Sets a single header for all API requests.
|
|
1450
|
+
*
|
|
1451
|
+
* @param key - The header name.
|
|
1452
|
+
* @param value - The header value.
|
|
1453
|
+
* @returns This {@link Aspi} instance for chaining.
|
|
1454
|
+
*
|
|
1127
1455
|
* @example
|
|
1128
1456
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1129
1457
|
* api.setHeader('Content-Type', 'application/json');
|
|
1130
1458
|
*/
|
|
1131
1459
|
setHeader(key, value) {
|
|
1132
1460
|
this.#globalRequestInit.headers = {
|
|
1133
|
-
...this.#globalRequestInit.headers,
|
|
1461
|
+
...this.#globalRequestInit.headers ?? {},
|
|
1134
1462
|
[key]: value
|
|
1135
1463
|
};
|
|
1136
1464
|
return this;
|
|
@@ -1147,18 +1475,28 @@ var Aspi2 = class {
|
|
|
1147
1475
|
return this.setHeader("Authorization", `Bearer ${token}`);
|
|
1148
1476
|
}
|
|
1149
1477
|
/**
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1478
|
+
* Register a request‑transformer middleware.
|
|
1479
|
+
*
|
|
1480
|
+
* The supplied function receives the current request initialization object
|
|
1481
|
+
* (`T`) and must return a request initialization of type `U`. The middleware
|
|
1482
|
+
* is added to the internal middleware chain and will be applied to every
|
|
1483
|
+
* request created by this {@link Aspi} instance.
|
|
1484
|
+
*
|
|
1485
|
+
* @template T - The input request type, extending the current {@link Aspi} request init type.
|
|
1486
|
+
* @template U - The output request type after transformation.
|
|
1487
|
+
* @param {RequestTransformer<T, U>} fn - The middleware function that transforms a request configuration.
|
|
1488
|
+
* @returns {Aspi<U>} A new {@link Aspi} instance typed with the transformed request configuration.
|
|
1153
1489
|
* @example
|
|
1154
1490
|
* const api = new Aspi({ baseUrl: 'https://api.example.com' });
|
|
1155
|
-
* api.use((req) => {
|
|
1156
|
-
* // Add custom headers
|
|
1157
|
-
*
|
|
1158
|
-
* ...req
|
|
1159
|
-
*
|
|
1491
|
+
* const apiWithHeaders = api.use((req) => {
|
|
1492
|
+
* // Add custom headers to every request
|
|
1493
|
+
* return {
|
|
1494
|
+
* ...req,
|
|
1495
|
+
* headers: {
|
|
1496
|
+
* ...req.headers,
|
|
1497
|
+
* 'x-custom-header': 'custom-value',
|
|
1498
|
+
* },
|
|
1160
1499
|
* };
|
|
1161
|
-
* return req;
|
|
1162
1500
|
* });
|
|
1163
1501
|
*/
|
|
1164
1502
|
use(fn) {
|
|
@@ -1290,6 +1628,16 @@ var Aspi2 = class {
|
|
|
1290
1628
|
*/
|
|
1291
1629
|
throwable() {
|
|
1292
1630
|
this.#throwOnError = true;
|
|
1631
|
+
this.#shouldBeResult = false;
|
|
1632
|
+
return this;
|
|
1633
|
+
}
|
|
1634
|
+
/**
|
|
1635
|
+
* Configures the request to return a Result object instead of just the response body.
|
|
1636
|
+
* @returns The Aspi instance with result handling enabled.
|
|
1637
|
+
*/
|
|
1638
|
+
withResult() {
|
|
1639
|
+
this.#shouldBeResult = true;
|
|
1640
|
+
this.#throwOnError = false;
|
|
1293
1641
|
return this;
|
|
1294
1642
|
}
|
|
1295
1643
|
};
|