bc-api-client 1.0.0-beta.3 → 1.0.0-beta.4
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/dist/index.d.mts +63 -7
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +83 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HTTPError, KyRequest, Options, TimeoutError } from "ky";
|
|
2
|
+
import { LimitFunction } from "p-limit";
|
|
2
3
|
|
|
3
4
|
//#region src/lib/common.d.ts
|
|
4
5
|
type ConcurrencyOptions = {
|
|
@@ -14,6 +15,11 @@ type ConcurrencyOptions = {
|
|
|
14
15
|
* response while below the configured max. Defaults to 1.
|
|
15
16
|
*/
|
|
16
17
|
backoffRecover?: ((concurrency: number) => number) | number;
|
|
18
|
+
/**
|
|
19
|
+
* A p-limit instance to reuse across calls. When provided, `batchStream` uses it instead of
|
|
20
|
+
* creating a new one, allowing callers to observe and react to live concurrency changes.
|
|
21
|
+
*/
|
|
22
|
+
pLimit?: LimitFunction;
|
|
17
23
|
};
|
|
18
24
|
/**
|
|
19
25
|
* Configuration options for the BigCommerce client.
|
|
@@ -576,6 +582,26 @@ type Err<E> = {
|
|
|
576
582
|
err: E;
|
|
577
583
|
};
|
|
578
584
|
type Result$1<T, E> = Ok<T> | Err<E>;
|
|
585
|
+
/**
|
|
586
|
+
* A {@link Result} extended with the zero-based index of the originating request in the input
|
|
587
|
+
* array passed to {@link BigCommerceClient.batchStream} or {@link BigCommerceClient.batchSafe}.
|
|
588
|
+
*
|
|
589
|
+
* Because concurrent requests complete out of insertion order, `index` is the only reliable way
|
|
590
|
+
* to correlate a result back to its input.
|
|
591
|
+
*
|
|
592
|
+
* @example
|
|
593
|
+
* ```ts
|
|
594
|
+
* const requests = ids.map(id => req.get(`catalog/products/${id}`));
|
|
595
|
+
* for await (const { index, err, data } of client.batchStream(requests)) {
|
|
596
|
+
* const originalId = ids[index];
|
|
597
|
+
* if (err) { console.error(originalId, err); continue; }
|
|
598
|
+
* console.log(originalId, data);
|
|
599
|
+
* }
|
|
600
|
+
* ```
|
|
601
|
+
*/
|
|
602
|
+
type BatchResult<T, E> = Result$1<T, E> & {
|
|
603
|
+
index: number;
|
|
604
|
+
};
|
|
579
605
|
/**
|
|
580
606
|
* Creates a successful {@link Result}. Check `result.ok` or `result.err` before accessing `data`.
|
|
581
607
|
* @param data - The success value.
|
|
@@ -729,6 +755,10 @@ declare class BigCommerceClient {
|
|
|
729
755
|
*
|
|
730
756
|
* Collects all results into an array. Use {@link queryStream} to process items lazily.
|
|
731
757
|
*
|
|
758
|
+
* **Sorting and concurrency:** when `concurrency > 1`, chunks complete out of order so
|
|
759
|
+
* sorted output is not preserved across the full result set. Pass `concurrency: false`
|
|
760
|
+
* if sort order matters.
|
|
761
|
+
*
|
|
732
762
|
* @param path - API path relative to the store's versioned base URL.
|
|
733
763
|
* @param options - Query options including `key`, `values`, pagination params, and concurrency
|
|
734
764
|
* controls.
|
|
@@ -770,6 +800,10 @@ declare class BigCommerceClient {
|
|
|
770
800
|
*
|
|
771
801
|
* Each yielded value is a {@link Result} — check `err` before using `data`.
|
|
772
802
|
*
|
|
803
|
+
* **Sorting and concurrency:** when `concurrency > 1`, chunks complete out of order so
|
|
804
|
+
* sorted output is not preserved across the full result set. Pass `concurrency: false`
|
|
805
|
+
* if sort order matters.
|
|
806
|
+
*
|
|
773
807
|
* @param path - API path relative to the store's versioned base URL.
|
|
774
808
|
* @param options - Query options including `key`, `values`, pagination params, and concurrency
|
|
775
809
|
* controls.
|
|
@@ -798,6 +832,10 @@ declare class BigCommerceClient {
|
|
|
798
832
|
*
|
|
799
833
|
* Use {@link stream} to process items lazily without buffering the full result set.
|
|
800
834
|
*
|
|
835
|
+
* **Sorting and concurrency:** the first page is fetched sequentially; remaining pages are
|
|
836
|
+
* fetched concurrently and may complete out of order. When `concurrency > 1`, sort order
|
|
837
|
+
* is not preserved across pages. Pass `concurrency: false` if sort order matters.
|
|
838
|
+
*
|
|
801
839
|
* @param path - API path relative to the store's versioned base URL.
|
|
802
840
|
* @param options - Ky options are forwarded to page requests.
|
|
803
841
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -838,6 +876,10 @@ declare class BigCommerceClient {
|
|
|
838
876
|
*
|
|
839
877
|
* Use {@link streamBlind} to process items lazily without buffering the full result set.
|
|
840
878
|
*
|
|
879
|
+
* **Sorting and concurrency:** pages within each batch are fetched concurrently and may
|
|
880
|
+
* complete out of order. When `concurrency > 1`, sort order is not preserved across pages.
|
|
881
|
+
* Pass `concurrency: false` if sort order matters.
|
|
882
|
+
*
|
|
841
883
|
* @param path - API path relative to the store's versioned base URL (always requests v2).
|
|
842
884
|
* @param options - Ky options are forwarded to page requests.
|
|
843
885
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -876,6 +918,10 @@ declare class BigCommerceClient {
|
|
|
876
918
|
*
|
|
877
919
|
* Use {@link collectBlind} to buffer all results into an array (throws on any error).
|
|
878
920
|
*
|
|
921
|
+
* **Sorting and concurrency:** pages within each batch are fetched concurrently and may
|
|
922
|
+
* complete out of order. When `concurrency > 1`, sort order is not preserved across pages.
|
|
923
|
+
* Pass `concurrency: false` if sort order matters.
|
|
924
|
+
*
|
|
879
925
|
* @param path - API path relative to the store's versioned base URL (always requests v2).
|
|
880
926
|
* @param options - Ky options are forwarded to page requests.
|
|
881
927
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -904,8 +950,11 @@ declare class BigCommerceClient {
|
|
|
904
950
|
*/
|
|
905
951
|
streamBlind<TItem = unknown, TQuery extends Query = Query>(path: string, options?: BlindOptions<TItem, TQuery>): AsyncGenerator<Result$1<TItem, BaseError>>;
|
|
906
952
|
/**
|
|
907
|
-
* Executes multiple requests concurrently and returns all results as {@link
|
|
908
|
-
* never throwing. Errors from individual requests are captured as `Err` results.
|
|
953
|
+
* Executes multiple requests concurrently and returns all results as {@link BatchResult}
|
|
954
|
+
* values, never throwing. Errors from individual requests are captured as `Err` results.
|
|
955
|
+
*
|
|
956
|
+
* Results arrive in completion order, not input order. Use the `index` field on each
|
|
957
|
+
* {@link BatchResult} to correlate a result back to its position in the `requests` array.
|
|
909
958
|
*
|
|
910
959
|
* Use {@link batchStream} to process results as they arrive rather than waiting for all.
|
|
911
960
|
*
|
|
@@ -919,9 +968,9 @@ declare class BigCommerceClient {
|
|
|
919
968
|
* @param options.backoffRecover - Amount (or function) added to concurrency per successful
|
|
920
969
|
* response. Defaults to `config.backoffRecover`, or 1 if not set on the client.
|
|
921
970
|
*
|
|
922
|
-
* @returns
|
|
971
|
+
* @returns {@link BatchResult} array in the order requests completed (not input order).
|
|
923
972
|
*/
|
|
924
|
-
batchSafe<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(requests: BatchRequestOptions<TBody, TRes, TQuery>[], options?: ConcurrencyOptions): Promise<
|
|
973
|
+
batchSafe<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(requests: BatchRequestOptions<TBody, TRes, TQuery>[], options?: ConcurrencyOptions): Promise<BatchResult<TRes, BaseError>[]>;
|
|
925
974
|
/**
|
|
926
975
|
* Streams all items from a v3 paginated endpoint, fetching the first page sequentially
|
|
927
976
|
* and remaining pages concurrently via {@link batchStream}.
|
|
@@ -929,6 +978,10 @@ declare class BigCommerceClient {
|
|
|
929
978
|
* Each yielded value is a {@link Result} — check `err` before using `data`. Use
|
|
930
979
|
* {@link collect} to gather all items into an array.
|
|
931
980
|
*
|
|
981
|
+
* **Sorting and concurrency:** the first page is fetched sequentially; remaining pages are
|
|
982
|
+
* fetched concurrently and may complete out of order. When `concurrency > 1`, sort order
|
|
983
|
+
* is not preserved across pages. Pass `concurrency: false` if sort order matters.
|
|
984
|
+
*
|
|
932
985
|
* @param path - API path relative to the store's versioned base URL.
|
|
933
986
|
* @param options - Ky options are forwarded to page requests.
|
|
934
987
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -953,9 +1006,12 @@ declare class BigCommerceClient {
|
|
|
953
1006
|
stream<TItem = unknown, TQuery extends Query = Query>(path: string, options?: CollectOptions<TItem, TQuery>): AsyncGenerator<Result$1<TItem, BaseError>>;
|
|
954
1007
|
/**
|
|
955
1008
|
* Executes multiple requests with configurable concurrency, yielding each result as a
|
|
956
|
-
* {@link
|
|
1009
|
+
* {@link BatchResult} as it completes. Errors from individual requests are yielded as `Err`
|
|
957
1010
|
* results rather than thrown.
|
|
958
1011
|
*
|
|
1012
|
+
* Results arrive in completion order, not input order. Use the `index` field on each
|
|
1013
|
+
* {@link BatchResult} to correlate a result back to its position in the `requests` array.
|
|
1014
|
+
*
|
|
959
1015
|
* Automatically adjusts concurrency up/down in response to rate-limit and error responses.
|
|
960
1016
|
* Use {@link batchSafe} to collect all results into an array.
|
|
961
1017
|
*
|
|
@@ -975,7 +1031,7 @@ declare class BigCommerceClient {
|
|
|
975
1031
|
* @param options.backoffRecover - Amount (or function) added to concurrency per successful
|
|
976
1032
|
* response. Defaults to `config.backoffRecover`, or 1 if not set on the client.
|
|
977
1033
|
*/
|
|
978
|
-
batchStream<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(requests: BatchRequestOptions<TBody, TRes, TQuery>[], options?: ConcurrencyOptions): AsyncGenerator<
|
|
1034
|
+
batchStream<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(requests: BatchRequestOptions<TBody, TRes, TQuery>[], options?: ConcurrencyOptions): AsyncGenerator<BatchResult<TRes, BaseError>>;
|
|
979
1035
|
private validatePaginatedItem;
|
|
980
1036
|
private assertPaginatedResponse;
|
|
981
1037
|
private validatePaginationOption;
|
|
@@ -1008,5 +1064,5 @@ type V3Resource<T> = {
|
|
|
1008
1064
|
};
|
|
1009
1065
|
};
|
|
1010
1066
|
//#endregion
|
|
1011
|
-
export { type ApiVersion, BCApiError, BCAuthInvalidJwtError, BCAuthInvalidRedirectUriError, BCAuthMissingParamError, BCAuthScopeMismatchError, BCClientError, BCCredentialsError, BCPaginatedItemValidationError, BCPaginatedOptionError, BCPaginatedResponseError, BCQueryValidationError, BCRateLimitDelayTooLongError, BCRateLimitNoHeadersError, BCRequestBodyValidationError, BCResponseParseError, BCResponseValidationError, BCSchemaValidationError, BCTimeoutError, BCUrlTooLongError, BaseError, type BatchRequestOptions, BigCommerceAuth, BigCommerceAuthConfig, BigCommerceAuthQuery, BigCommerceClient, Claims, type ClientConfig, type CollectOptions, type ConcurrencyOptions, type CountedCollectOptions, type DeleteOptions, Err, ErrorContext, FallbackLogger, type GetOptions, type HttpMethod, type LogLevel, type Logger, Ok, Pagination, type PostOptions, type PowertoolsLikeLogger, type PutOptions, type Query, type QueryOptions, type QueryValue, type RequestOptions, Result$1 as Result, type StandardSchemaV1, TokenResponse, User, V3Resource, fromAwsPowertoolsLogger, req };
|
|
1067
|
+
export { type ApiVersion, BCApiError, BCAuthInvalidJwtError, BCAuthInvalidRedirectUriError, BCAuthMissingParamError, BCAuthScopeMismatchError, BCClientError, BCCredentialsError, BCPaginatedItemValidationError, BCPaginatedOptionError, BCPaginatedResponseError, BCQueryValidationError, BCRateLimitDelayTooLongError, BCRateLimitNoHeadersError, BCRequestBodyValidationError, BCResponseParseError, BCResponseValidationError, BCSchemaValidationError, BCTimeoutError, BCUrlTooLongError, BaseError, type BatchRequestOptions, BatchResult, BigCommerceAuth, BigCommerceAuthConfig, BigCommerceAuthQuery, BigCommerceClient, Claims, type ClientConfig, type CollectOptions, type ConcurrencyOptions, type CountedCollectOptions, type DeleteOptions, Err, ErrorContext, FallbackLogger, type GetOptions, type HttpMethod, type LogLevel, type Logger, Ok, Pagination, type PostOptions, type PowertoolsLikeLogger, type PutOptions, type Query, type QueryOptions, type QueryValue, type RequestOptions, Result$1 as Result, type StandardSchemaV1, TokenResponse, User, V3Resource, fromAwsPowertoolsLogger, req };
|
|
1012
1068
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/lib/common.ts","../src/lib/logger.ts","../src/auth.ts","../src/lib/standard-schema.ts","../src/lib/request.ts","../src/lib/errors.ts","../src/lib/result.ts","../src/client.ts","../src/lib/pagination.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/lib/common.ts","../src/lib/logger.ts","../src/auth.ts","../src/lib/standard-schema.ts","../src/lib/request.ts","../src/lib/errors.ts","../src/lib/result.ts","../src/client.ts","../src/lib/pagination.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -845,6 +845,10 @@ var BigCommerceClient = class {
|
|
|
845
845
|
*
|
|
846
846
|
* Collects all results into an array. Use {@link queryStream} to process items lazily.
|
|
847
847
|
*
|
|
848
|
+
* **Sorting and concurrency:** when `concurrency > 1`, chunks complete out of order so
|
|
849
|
+
* sorted output is not preserved across the full result set. Pass `concurrency: false`
|
|
850
|
+
* if sort order matters.
|
|
851
|
+
*
|
|
848
852
|
* @param path - API path relative to the store's versioned base URL.
|
|
849
853
|
* @param options - Query options including `key`, `values`, pagination params, and concurrency
|
|
850
854
|
* controls.
|
|
@@ -891,6 +895,10 @@ var BigCommerceClient = class {
|
|
|
891
895
|
*
|
|
892
896
|
* Each yielded value is a {@link Result} — check `err` before using `data`.
|
|
893
897
|
*
|
|
898
|
+
* **Sorting and concurrency:** when `concurrency > 1`, chunks complete out of order so
|
|
899
|
+
* sorted output is not preserved across the full result set. Pass `concurrency: false`
|
|
900
|
+
* if sort order matters.
|
|
901
|
+
*
|
|
894
902
|
* @param path - API path relative to the store's versioned base URL.
|
|
895
903
|
* @param options - Query options including `key`, `values`, pagination params, and concurrency
|
|
896
904
|
* controls.
|
|
@@ -914,7 +922,7 @@ var BigCommerceClient = class {
|
|
|
914
922
|
* @throws {@link BCQueryValidationError} if `querySchema` validation fails.
|
|
915
923
|
*/
|
|
916
924
|
async *queryStream(path, options) {
|
|
917
|
-
const { key, values, query, querySchema, itemSchema, ...requestOptions } = options;
|
|
925
|
+
const { key, values, query, querySchema, itemSchema, concurrency, rateLimitBackoff, backoff, backoffRecover, ...requestOptions } = options;
|
|
918
926
|
const limit = this.validatePaginationOption(path, "limit", query?.limit ?? 250);
|
|
919
927
|
const newQuery = {
|
|
920
928
|
...await this.validate(query, querySchema, BCQueryValidationError, "GET", path, "Invalid query parameters"),
|
|
@@ -942,7 +950,12 @@ var BigCommerceClient = class {
|
|
|
942
950
|
[key]: chunk
|
|
943
951
|
}
|
|
944
952
|
}));
|
|
945
|
-
for await (const { err, data } of this.batchStream(requests,
|
|
953
|
+
for await (const { err, data } of this.batchStream(requests, {
|
|
954
|
+
concurrency,
|
|
955
|
+
rateLimitBackoff,
|
|
956
|
+
backoff,
|
|
957
|
+
backoffRecover
|
|
958
|
+
})) {
|
|
946
959
|
if (err) {
|
|
947
960
|
yield Err(err);
|
|
948
961
|
continue;
|
|
@@ -961,6 +974,10 @@ var BigCommerceClient = class {
|
|
|
961
974
|
*
|
|
962
975
|
* Use {@link stream} to process items lazily without buffering the full result set.
|
|
963
976
|
*
|
|
977
|
+
* **Sorting and concurrency:** the first page is fetched sequentially; remaining pages are
|
|
978
|
+
* fetched concurrently and may complete out of order. When `concurrency > 1`, sort order
|
|
979
|
+
* is not preserved across pages. Pass `concurrency: false` if sort order matters.
|
|
980
|
+
*
|
|
964
981
|
* @param path - API path relative to the store's versioned base URL.
|
|
965
982
|
* @param options - Ky options are forwarded to page requests.
|
|
966
983
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -1006,6 +1023,10 @@ var BigCommerceClient = class {
|
|
|
1006
1023
|
*
|
|
1007
1024
|
* Use {@link streamBlind} to process items lazily without buffering the full result set.
|
|
1008
1025
|
*
|
|
1026
|
+
* **Sorting and concurrency:** pages within each batch are fetched concurrently and may
|
|
1027
|
+
* complete out of order. When `concurrency > 1`, sort order is not preserved across pages.
|
|
1028
|
+
* Pass `concurrency: false` if sort order matters.
|
|
1029
|
+
*
|
|
1009
1030
|
* @param path - API path relative to the store's versioned base URL (always requests v2).
|
|
1010
1031
|
* @param options - Ky options are forwarded to page requests.
|
|
1011
1032
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -1049,6 +1070,10 @@ var BigCommerceClient = class {
|
|
|
1049
1070
|
*
|
|
1050
1071
|
* Use {@link collectBlind} to buffer all results into an array (throws on any error).
|
|
1051
1072
|
*
|
|
1073
|
+
* **Sorting and concurrency:** pages within each batch are fetched concurrently and may
|
|
1074
|
+
* complete out of order. When `concurrency > 1`, sort order is not preserved across pages.
|
|
1075
|
+
* Pass `concurrency: false` if sort order matters.
|
|
1076
|
+
*
|
|
1052
1077
|
* @param path - API path relative to the store's versioned base URL (always requests v2).
|
|
1053
1078
|
* @param options - Ky options are forwarded to page requests.
|
|
1054
1079
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -1076,14 +1101,19 @@ var BigCommerceClient = class {
|
|
|
1076
1101
|
* @yields `Err(error)` for non-terminating page errors (e.g. non-404/204 HTTP errors).
|
|
1077
1102
|
*/
|
|
1078
1103
|
async *streamBlind(path, options) {
|
|
1079
|
-
const { query: rawQuery, querySchema, itemSchema, maxPages: rawMaxPages, concurrency: rawConcurrency, rateLimitBackoff, backoff, backoffRecover, ...requestOptions } = options ?? {};
|
|
1104
|
+
const { query: rawQuery, querySchema, itemSchema, maxPages: rawMaxPages, concurrency: rawConcurrency, rateLimitBackoff, backoff, backoffRecover, pLimit: rawPLimit, ...requestOptions } = options ?? {};
|
|
1105
|
+
const concurrency = this.validateConcurrency(rawConcurrency ?? this.config.concurrency ?? 10);
|
|
1106
|
+
const limiter = rawPLimit ?? (concurrency ? pLimit({
|
|
1107
|
+
concurrency,
|
|
1108
|
+
rejectOnClear: true
|
|
1109
|
+
}) : void 0);
|
|
1080
1110
|
const concurrencyOptions = {
|
|
1081
1111
|
concurrency: rawConcurrency,
|
|
1082
1112
|
rateLimitBackoff,
|
|
1083
1113
|
backoff,
|
|
1084
|
-
backoffRecover
|
|
1114
|
+
backoffRecover,
|
|
1115
|
+
pLimit: limiter
|
|
1085
1116
|
};
|
|
1086
|
-
const concurrency = this.validateConcurrency(this.config.concurrency ?? rawConcurrency ?? 10);
|
|
1087
1117
|
const query = await this.validate(rawQuery, querySchema, BCQueryValidationError, "GET", path);
|
|
1088
1118
|
const page = this.validatePaginationOption(path, "page", query?.page ?? 1);
|
|
1089
1119
|
const limit = this.validatePaginationOption(path, "limit", query?.limit ?? 250);
|
|
@@ -1095,7 +1125,7 @@ var BigCommerceClient = class {
|
|
|
1095
1125
|
this.logger?.warn({ currentPage }, "Blind pagination reached maxPages before the end of the data");
|
|
1096
1126
|
break;
|
|
1097
1127
|
}
|
|
1098
|
-
const batchSize = concurrency || 1;
|
|
1128
|
+
const batchSize = (limiter?.concurrency ?? concurrency) || 1;
|
|
1099
1129
|
const pageRequests = Array.from({ length: batchSize }, (_, i) => currentPage + i).map((page) => req.get(path, {
|
|
1100
1130
|
...requestOptions,
|
|
1101
1131
|
version: "v2",
|
|
@@ -1123,8 +1153,11 @@ var BigCommerceClient = class {
|
|
|
1123
1153
|
} while (!done);
|
|
1124
1154
|
}
|
|
1125
1155
|
/**
|
|
1126
|
-
* Executes multiple requests concurrently and returns all results as {@link
|
|
1127
|
-
* never throwing. Errors from individual requests are captured as `Err` results.
|
|
1156
|
+
* Executes multiple requests concurrently and returns all results as {@link BatchResult}
|
|
1157
|
+
* values, never throwing. Errors from individual requests are captured as `Err` results.
|
|
1158
|
+
*
|
|
1159
|
+
* Results arrive in completion order, not input order. Use the `index` field on each
|
|
1160
|
+
* {@link BatchResult} to correlate a result back to its position in the `requests` array.
|
|
1128
1161
|
*
|
|
1129
1162
|
* Use {@link batchStream} to process results as they arrive rather than waiting for all.
|
|
1130
1163
|
*
|
|
@@ -1138,7 +1171,7 @@ var BigCommerceClient = class {
|
|
|
1138
1171
|
* @param options.backoffRecover - Amount (or function) added to concurrency per successful
|
|
1139
1172
|
* response. Defaults to `config.backoffRecover`, or 1 if not set on the client.
|
|
1140
1173
|
*
|
|
1141
|
-
* @returns
|
|
1174
|
+
* @returns {@link BatchResult} array in the order requests completed (not input order).
|
|
1142
1175
|
*/
|
|
1143
1176
|
async batchSafe(requests, options) {
|
|
1144
1177
|
const results = [];
|
|
@@ -1152,6 +1185,10 @@ var BigCommerceClient = class {
|
|
|
1152
1185
|
* Each yielded value is a {@link Result} — check `err` before using `data`. Use
|
|
1153
1186
|
* {@link collect} to gather all items into an array.
|
|
1154
1187
|
*
|
|
1188
|
+
* **Sorting and concurrency:** the first page is fetched sequentially; remaining pages are
|
|
1189
|
+
* fetched concurrently and may complete out of order. When `concurrency > 1`, sort order
|
|
1190
|
+
* is not preserved across pages. Pass `concurrency: false` if sort order matters.
|
|
1191
|
+
*
|
|
1155
1192
|
* @param path - API path relative to the store's versioned base URL.
|
|
1156
1193
|
* @param options - Ky options are forwarded to page requests.
|
|
1157
1194
|
* @param options.query - Query parameters. `query.limit` controls page size (default 250,
|
|
@@ -1174,7 +1211,7 @@ var BigCommerceClient = class {
|
|
|
1174
1211
|
* @throws {@link BCQueryValidationError} if `querySchema` validation fails.
|
|
1175
1212
|
*/
|
|
1176
1213
|
async *stream(path, options) {
|
|
1177
|
-
const { query, querySchema, itemSchema, ...requestOptions } = options ?? {};
|
|
1214
|
+
const { query, querySchema, itemSchema, concurrency, rateLimitBackoff, backoff, backoffRecover, ...requestOptions } = options ?? {};
|
|
1178
1215
|
let limit = this.validatePaginationOption(path, "limit", query?.limit ?? 250);
|
|
1179
1216
|
const page = this.validatePaginationOption(path, "page", query?.page ?? 1);
|
|
1180
1217
|
const validatedQuery = await this.validate(query, querySchema, BCQueryValidationError, "GET", path, "Invalid query parameters");
|
|
@@ -1214,7 +1251,12 @@ var BigCommerceClient = class {
|
|
|
1214
1251
|
page
|
|
1215
1252
|
}
|
|
1216
1253
|
}));
|
|
1217
|
-
for await (const pageRes of requests.length > 0 ? this.batchStream(requests,
|
|
1254
|
+
for await (const pageRes of requests.length > 0 ? this.batchStream(requests, {
|
|
1255
|
+
concurrency,
|
|
1256
|
+
rateLimitBackoff,
|
|
1257
|
+
backoff,
|
|
1258
|
+
backoffRecover
|
|
1259
|
+
}) : []) {
|
|
1218
1260
|
const { data: page, err } = pageRes;
|
|
1219
1261
|
if (err) {
|
|
1220
1262
|
yield Err(err);
|
|
@@ -1225,15 +1267,18 @@ var BigCommerceClient = class {
|
|
|
1225
1267
|
for (const item of data) yield this.validatePaginatedItem(path, item, itemSchema);
|
|
1226
1268
|
} catch (err) {
|
|
1227
1269
|
if (err instanceof BaseError) yield Err(err);
|
|
1228
|
-
else yield Err(new BCClientError("Unknown error
|
|
1270
|
+
else yield Err(new BCClientError("Unknown error occurred processing page", {}, { cause: err }));
|
|
1229
1271
|
}
|
|
1230
1272
|
}
|
|
1231
1273
|
}
|
|
1232
1274
|
/**
|
|
1233
1275
|
* Executes multiple requests with configurable concurrency, yielding each result as a
|
|
1234
|
-
* {@link
|
|
1276
|
+
* {@link BatchResult} as it completes. Errors from individual requests are yielded as `Err`
|
|
1235
1277
|
* results rather than thrown.
|
|
1236
1278
|
*
|
|
1279
|
+
* Results arrive in completion order, not input order. Use the `index` field on each
|
|
1280
|
+
* {@link BatchResult} to correlate a result back to its position in the `requests` array.
|
|
1281
|
+
*
|
|
1237
1282
|
* Automatically adjusts concurrency up/down in response to rate-limit and error responses.
|
|
1238
1283
|
* Use {@link batchSafe} to collect all results into an array.
|
|
1239
1284
|
*
|
|
@@ -1256,23 +1301,38 @@ var BigCommerceClient = class {
|
|
|
1256
1301
|
async *batchStream(requests, options) {
|
|
1257
1302
|
const resolved = this.resolveStreamOptions(options);
|
|
1258
1303
|
if (resolved.concurrency) {
|
|
1259
|
-
const limit = pLimit({
|
|
1304
|
+
const limit = resolved.pLimit ?? pLimit({
|
|
1260
1305
|
concurrency: resolved.concurrency,
|
|
1261
1306
|
rejectOnClear: true
|
|
1262
1307
|
});
|
|
1263
1308
|
const client = this.makeStreamClient(limit, resolved);
|
|
1264
1309
|
const channel = new AsyncChannel();
|
|
1265
1310
|
try {
|
|
1266
|
-
Promise.all(requests.map((req) => limit(() => this.request(req.path, req, client).then((val) => channel.push(
|
|
1311
|
+
Promise.all(requests.map((req, index) => limit(() => this.request(req.path, req, client).then((val) => channel.push({
|
|
1312
|
+
...Ok(val),
|
|
1313
|
+
index
|
|
1314
|
+
}), (err) => channel.push({
|
|
1315
|
+
...Err(err),
|
|
1316
|
+
index
|
|
1317
|
+
}))))).catch((err) => this.logger?.warn({ err }, "In-flight concurrent requests aborted")).finally(() => channel.close());
|
|
1267
1318
|
for await (const item of channel) yield item;
|
|
1268
1319
|
} finally {
|
|
1269
1320
|
limit.clearQueue();
|
|
1270
1321
|
}
|
|
1271
|
-
} else for (const request of requests) try {
|
|
1272
|
-
yield
|
|
1322
|
+
} else for (const [index, request] of requests.entries()) try {
|
|
1323
|
+
yield {
|
|
1324
|
+
...Ok(await this.request(request.path, request)),
|
|
1325
|
+
index
|
|
1326
|
+
};
|
|
1273
1327
|
} catch (err) {
|
|
1274
|
-
if (err instanceof BaseError) yield
|
|
1275
|
-
|
|
1328
|
+
if (err instanceof BaseError) yield {
|
|
1329
|
+
...Err(err),
|
|
1330
|
+
index
|
|
1331
|
+
};
|
|
1332
|
+
else yield {
|
|
1333
|
+
...Err(new BCClientError("Unknown error in batchStream", {}, { cause: err })),
|
|
1334
|
+
index
|
|
1335
|
+
};
|
|
1276
1336
|
}
|
|
1277
1337
|
}
|
|
1278
1338
|
async validatePaginatedItem(path, item, schema) {
|
|
@@ -1306,7 +1366,8 @@ var BigCommerceClient = class {
|
|
|
1306
1366
|
concurrency: options?.concurrency ?? this.config.concurrency ?? 10,
|
|
1307
1367
|
rateLimitBackoff: options?.rateLimitBackoff ?? this.config.rateLimitBackoff ?? 1,
|
|
1308
1368
|
backoff: options?.backoff ?? this.config.backoff ?? 2,
|
|
1309
|
-
backoffRecover: options?.backoffRecover ?? this.config.backoffRecover ?? 1
|
|
1369
|
+
backoffRecover: options?.backoffRecover ?? this.config.backoffRecover ?? 1,
|
|
1370
|
+
pLimit: options?.pLimit
|
|
1310
1371
|
};
|
|
1311
1372
|
}
|
|
1312
1373
|
makeStreamClient(limit, options) {
|
|
@@ -1339,9 +1400,9 @@ var BigCommerceClient = class {
|
|
|
1339
1400
|
}]
|
|
1340
1401
|
} });
|
|
1341
1402
|
}
|
|
1342
|
-
async request(
|
|
1403
|
+
async request(rawPath, options, client) {
|
|
1343
1404
|
const { version, query, body, bodySchema, querySchema, responseSchema, ...kyOptions } = options;
|
|
1344
|
-
const path = this.makePath(options.version ?? "v3",
|
|
1405
|
+
const path = this.makePath(options.version ?? "v3", rawPath);
|
|
1345
1406
|
const validQuery = await this.validate(query, querySchema, BCQueryValidationError, options.method, path, "Invalid query parameters");
|
|
1346
1407
|
const validBody = await this.validate(body, bodySchema, BCRequestBodyValidationError, options.method, path, `Invalid ${options.method} request body`);
|
|
1347
1408
|
let response;
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/common.ts","../src/lib/errors.ts","../src/lib/logger.ts","../src/auth.ts","../src/lib/util.ts","../src/lib/hooks.ts","../src/lib/request.ts","../src/lib/result.ts","../src/client.ts"],"sourcesContent":["import type { Options as KyOptions } from 'ky';\nimport type { Logger, LogLevel } from './logger';\n\nexport type { Logger, LogLevel };\n\nexport type ConcurrencyOptions = {\n /** Max concurrent requests. Must be 1–1000. `false` for sequential. Defaults to 10. */\n concurrency?: number | false;\n /**\n * Divisor (or `(concurrency, status) => number` function) applied to concurrency on\n * non-429 error responses. Defaults to 2.\n */\n backoff?: ((concurrency: number, status: number) => number) | number;\n /** Concurrency cap applied when a 429 response is received. Defaults to 1. */\n rateLimitBackoff?: number;\n /**\n * Amount (or `(concurrency) => number` function) added to concurrency per successful\n * response while below the configured max. Defaults to 1.\n */\n backoffRecover?: ((concurrency: number) => number) | number;\n};\n\n/** Maximum allowed concurrency value. */\nexport const MAX_CONCURRENCY = 1000;\n/** Default concurrency for batch/stream operations. */\nexport const DEFAULT_CONCURRENCY = 10;\n/** Default concurrency cap on 429 rate-limit responses. */\nexport const DEFAULT_RATE_LIMIT_BACKOFF = 1;\n/** Default divisor applied to concurrency on non-429 errors. */\nexport const DEFAULT_BACKOFF_RATE = 2;\n/** Default amount added to concurrency per successful response. */\nexport const DEFAULT_BACKOFF_RECOVER = 1;\n/** Default page size for paginated requests. */\nexport const DEFAULT_LIMIT = 250;\n/** Maximum allowed URL length before chunking is required. */\nexport const MAX_URL_LENGTH = 2048;\n/** Regex to strip leading slashes from API paths. */\nexport const LEADING_SLASHES = /^\\/+/;\n/** Maximum pages to fetch during blind pagination **/\nexport const DEFAULT_MAX_BLIND_PAGES = 500;\n\n/**\n * Configuration options for the BigCommerce client.\n */\nexport interface ClientConfig\n extends Omit<KyOptions, 'throwHttpErrors' | 'parseJson' | 'method' | 'body' | 'json' | 'searchParams'>,\n ConcurrencyOptions {\n storeHash: string;\n accessToken: string;\n logger?: Logger | LogLevel | boolean;\n}\n\n/**\n * Random positive jitter within 0-500 ms in increments of 100\n * @param {number} delay\n */\nexport const rateLimitJitter = (delay: number) => delay + Math.floor(Math.random() * 6) * 100;\n\n/**\n * HTTP header names used by the BigCommerce API.\n */\nexport const HEADERS = {\n AUTH_TOKEN: 'X-Auth-Token',\n ACCEPT: 'Accept',\n CONTENT_TYPE: 'Content-Type',\n RATE_LIMIT_LEFT: 'x-rate-limit-requests-left',\n RATE_LIMIT_RESET: 'x-rate-limit-time-reset-ms',\n RATE_LIMIT_QUOTA: 'x-rate-limit-requests-quota',\n RATE_LIMIT_WINDOW: 'x-rate-limit-time-window-ms',\n} as const;\n\n/**\n * Metadata extracted from rate-limit headers in API responses.\n */\nexport type RateLimitMeta = {\n /** Time in milliseconds until the rate limit resets. */\n resetIn: number;\n /** Number of requests remaining in the current window. */\n requestsLeft?: number;\n /** Total request quota for the current window. */\n quota?: number;\n /** Time window size in milliseconds. */\n window?: number;\n};\n\n/**\n * Default configuration for the underlying ky HTTP client.\n */\nexport const BASE_KY_CONFIG = {\n prefixUrl: 'https://api.bigcommerce.com',\n throwHttpErrors: true,\n // Some BC endpoints may take a while.\n // For example /catalog/product/options* endpoints may fully\n // recreate all variants in some cases\n timeout: 120e3,\n\n retry: {\n limit: 3,\n // BC uses PUT for many upsert operations, it's not guaranteed to be idempotent\n methods: ['GET', 'DELETE'],\n statusCodes: [429, 500, 502, 503, 504],\n // BC does not send standart Retry-After. We'll use custom beforeRetry hook\n afterStatusCodes: [],\n jitter: true,\n maxRetryAfter: 120e3,\n },\n\n headers: {\n [HEADERS.ACCEPT]: 'application/json',\n [HEADERS.CONTENT_TYPE]: 'application/json',\n },\n};\n\n/**\n * Concurrency options with all values resolved to their defaults.\n */\nexport type ResolvedConcurrencyOptions = Required<ConcurrencyOptions>;\n","import type { HTTPError, KyRequest, TimeoutError as KyTimeoutError } from 'ky';\nimport type { Query } from './request';\nimport type { StandardSchemaV1 } from './standard-schema';\n\nexport type ErrorContext = Record<string, unknown>;\n\n/**\n * Abstract base class for all library errors. Carries a typed `context` object with\n * structured diagnostic data and a machine-readable `code` string.\n *\n * Use `instanceof` checks against specific subclasses rather than this base class.\n */\nexport abstract class BaseError<TContext extends ErrorContext = ErrorContext> extends Error {\n /** Machine-readable error code. Unique per subclass. */\n abstract readonly code: string;\n\n constructor(\n message: string,\n readonly context: TContext,\n options?: ErrorOptions,\n ) {\n super(message, options);\n\n this.name = this.constructor.name;\n }\n\n /** @internal */\n toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n context: this.context,\n cause: this.cause,\n };\n }\n}\n\n/** Catch-all for unexpected client-side errors not covered by a more specific subclass. */\nexport class BCClientError extends BaseError<Record<string, unknown>> {\n code = 'BC_CLIENT_ERROR';\n\n constructor(message: string, context?: Record<string, unknown>, cause?: unknown) {\n super(message, context ?? {}, { cause });\n }\n}\n\n/** Thrown by the {@link BigCommerceClient} constructor when credentials or config are invalid. */\nexport class BCCredentialsError extends BaseError<{\n errors: string[];\n}> {\n code = 'BC_CLIENT_CREDENTIALS_ERROR';\n\n constructor(errors: string[]) {\n super('Failed to initialize BigCommerceClient', { errors });\n }\n}\n\n/** Thrown before a request is sent when the constructed URL exceeds 2048 characters. */\nexport class BCUrlTooLongError extends BaseError<{\n url: string;\n max: number;\n len: number;\n}> {\n code = 'BC_URL_TOO_LONG';\n\n constructor(url: string, max: number) {\n super(`Url length (${url.length}) exceeds max allowed length of ${max}`, { url, max, len: url.length });\n }\n}\n\n/**\n * Thrown during retry when a 429 response is received but the expected\n * `X-Rate-Limit-*` headers are absent, making it impossible to determine the backoff delay.\n */\nexport class BCRateLimitNoHeadersError extends BaseError<{\n url: string;\n method: string;\n attempts: number;\n}> {\n code = 'BC_RATE_LIMIT_NO_HEADERS';\n\n constructor(request: KyRequest, attempts: number) {\n super('Rate limit reached but the X-Rate-Limit-* headers were not returned. Unable to retry', {\n url: request.url,\n method: request.method,\n attempts,\n });\n }\n}\n\n/**\n * Thrown during retry when a 429 response specifies a reset window that exceeds\n * `config.retry.maxRetryAfter`, preventing an unbounded wait.\n */\nexport class BCRateLimitDelayTooLongError extends BaseError<{\n url: string;\n method: string;\n attempts: number;\n maxDelay: number;\n delay: number;\n}> {\n code = 'BC_RATE_LIMIT_DELAY_TOO_LONG';\n\n constructor(request: KyRequest, attempts: number, maxDelay: number, delay: number) {\n super('Rate limit reached, and the rate limit reset window is too high.', {\n url: request.url,\n method: request.method,\n attempts,\n maxDelay,\n delay,\n });\n }\n}\n\n/**\n * Abstract base for all StandardSchema validation errors. Carries the raw `data` that failed\n * validation and the schema `error` result. Use specific subclasses for `instanceof` checks.\n */\nexport abstract class BCSchemaValidationError extends BaseError<{\n method: string;\n path: string;\n data: unknown;\n error: StandardSchemaV1.FailureResult;\n}> {\n constructor(message: string, method: string, path: string, data: unknown, error: StandardSchemaV1.FailureResult) {\n super(message, { method, path, data, error });\n }\n}\n\n/** Thrown when `options.querySchema` validation fails before a request is sent. */\nexport class BCQueryValidationError extends BCSchemaValidationError {\n code = 'BC_QUERY_VALIDATION_FAILED';\n}\n\n/** Thrown when `options.bodySchema` validation fails before a request is sent. */\nexport class BCRequestBodyValidationError extends BCSchemaValidationError {\n code = 'BC_REQUEST_BODY_VALIDATION_FAILED';\n}\n\n/** Thrown when `options.responseSchema` validation fails after a response is received. */\nexport class BCResponseValidationError extends BCSchemaValidationError {\n code = 'BC_RESPONSE_VALIDATION_FAILED';\n}\n\n/** Thrown or yielded when `options.itemSchema` validation fails for an item in a page response. */\nexport class BCPaginatedItemValidationError extends BCSchemaValidationError {\n code = 'BC_PAGINATED_ITEM_VALIDATION_FAILED';\n}\n\n/**\n * Thrown when the BigCommerce API returns a non-2xx HTTP response.\n * `context.status` and `context.responseBody` are the most useful fields for debugging.\n */\nexport class BCApiError extends BaseError<{\n method: string;\n url: string;\n status: number;\n statusMessage: string;\n headers: Record<string, string>;\n requestBody: string;\n responseBody: string;\n}> {\n code = 'BC_API_ERROR';\n\n constructor(err: HTTPError, requestBody: string, responseBody: string) {\n const { request, response } = err;\n\n super('BigCommerce API request failed', {\n method: request.method,\n url: request.url,\n status: response.status,\n statusMessage: response.statusText,\n headers: Object.fromEntries(response.headers as unknown as Iterable<[string, string]>),\n requestBody,\n responseBody,\n });\n }\n}\n\n/** Thrown when a request exceeds the configured timeout (default 120 s). */\nexport class BCTimeoutError extends BaseError<{\n method: string;\n url: string;\n}> {\n code = 'BC_TIMEOUT_ERROR';\n\n constructor(err: KyTimeoutError) {\n super('BigCommerce API request timed out', {\n method: err.request.method,\n url: err.request.url,\n });\n }\n}\n\n/**\n * Thrown when the response body cannot be read or parsed as JSON.\n * `context.rawBody` contains the raw text that failed to parse (empty string if the body was empty).\n */\nexport class BCResponseParseError extends BaseError<{\n method: string;\n status: number;\n path: string;\n query?: Query;\n rawBody?: string;\n}> {\n code = 'BC_RESPONSE_PARSE_ERROR';\n\n constructor(method: string, path: string, status: number, cause: unknown, query?: Query, rawBody?: string) {\n super(\n 'Failed to parse BigCommerce API response',\n {\n status,\n method,\n path,\n query,\n rawBody,\n },\n { cause },\n );\n }\n}\n\n/**\n * Thrown when a pagination option (`limit`, `page`, or `count`) is not a positive number.\n * `context.option` names the offending field; `context.value` is the value that was passed.\n */\nexport class BCPaginatedOptionError extends BaseError<{ path: string; option: string; value: unknown }> {\n code = 'BC_PAGINATED_OPTION_ERROR';\n\n constructor(path: string, value: unknown, option: string) {\n super('The pagination option must be a positive number', { path, option, value });\n }\n}\n\n/**\n * Thrown or yielded when a paginated response is missing required v3 envelope fields\n * (`data`, `meta.pagination`, etc.). Usually means the path is not a v3 collection endpoint.\n */\nexport class BCPaginatedResponseError extends BaseError<{ path: string; data: unknown; reason: string }> {\n code = 'BC_PAGINATED_RESPONSE_ERROR';\n\n constructor(path: string, data: unknown, reason: string) {\n super('Paginated response structure is invalid', { path, data, reason });\n }\n}\n\n/** Thrown by {@link BigCommerceAuth} constructor when `config.redirectUri` is not a valid URL. */\nexport class BCAuthInvalidRedirectUriError extends BaseError<{ redirectUri: string }> {\n code = 'BC_AUTH_INVALID_REDIRECT_URI';\n\n constructor(redirectUri: string, cause: unknown) {\n super('Invalid redirect URI', { redirectUri }, { cause });\n }\n}\n\n/** Thrown by {@link BigCommerceAuth.requestToken} when a required OAuth callback param is absent. */\nexport class BCAuthMissingParamError extends BaseError<{ param: string }> {\n code = 'BC_AUTH_MISSING_PARAM';\n\n constructor(param: string) {\n super(`Missing required auth callback parameter: ${param}`, { param });\n }\n}\n\n/**\n * Thrown by {@link BigCommerceAuth.requestToken} when the scopes granted by BigCommerce\n * do not include all scopes listed in `config.scopes`.\n * `context.missing` lists the scopes that were expected but not granted.\n */\nexport class BCAuthScopeMismatchError extends BaseError<{\n granted: string[];\n expected: string[];\n missing: string[];\n}> {\n code = 'BC_AUTH_SCOPE_MISMATCH';\n\n constructor(granted: string[], expected: string[], missing: string[]) {\n super('Granted scopes do not match expected scopes', { granted, expected, missing });\n }\n}\n\n/** Thrown by {@link BigCommerceAuth.verify} when the JWT signature, audience, issuer, or subject is invalid. */\nexport class BCAuthInvalidJwtError extends BaseError<{ storeHash: string }> {\n code = 'BC_AUTH_INVALID_JWT';\n\n constructor(storeHash: string, cause: unknown) {\n super('Invalid JWT payload', { storeHash }, { cause });\n }\n}\n","import type { ClientConfig } from './common';\n\n/**\n * Logging interface for the BigCommerce client.\n *\n * Implement this interface to provide custom logging. The client passes context data\n * as the first argument, making it compatible with structured loggers.\n */\nexport interface Logger {\n debug(data: Record<string, unknown>, message?: string): void;\n info(data: Record<string, unknown>, message?: string): void;\n warn(data: Record<string, unknown>, message?: string): void;\n error(data: Record<string, unknown>, message?: string): void;\n}\n\nexport type PowertoolsLikeLogger = {\n debug(message: string, ...data: Record<string, unknown>[]): void;\n info(message: string, ...data: Record<string, unknown>[]): void;\n warn(message: string, ...data: Record<string, unknown>[]): void;\n error(message: string, ...data: Record<string, unknown>[]): void;\n};\n\n/** @internal */\nexport const LOG_LEVELS = ['debug', 'info', 'warn', 'error'] as const;\n\n/** Supported log levels. */\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\n/**\n * Adapts an AWS Lambda Powertools logger to the {@link Logger} interface expected by\n * {@link BigCommerceClient} and {@link BigCommerceAuth}.\n *\n * Powertools loggers use `(message, ...data)` argument order whereas this library uses\n * `(data, message)`. This adapter swaps the arguments.\n *\n * @param logger - An AWS Lambda Powertools (or any {@link PowertoolsLikeLogger}-compatible) logger.\n * @returns A {@link Logger} wrapper suitable for `config.logger`.\n */\nexport const fromAwsPowertoolsLogger = (logger: PowertoolsLikeLogger): Logger => ({\n debug: (data, message) => logger.debug(message ?? '', data),\n info: (data, message) => logger.info(message ?? '', data),\n warn: (data, message) => logger.warn(message ?? '', data),\n error: (data, message) => logger.error(message ?? '', data),\n});\n\n/**\n * Console-based {@link Logger} that filters messages below a minimum level.\n *\n * Used automatically when `config.logger` is `true`, `undefined`, or a {@link LogLevel} string.\n * Can also be instantiated directly for custom log level control.\n *\n * @example\n * ```ts\n * new BigCommerceClient({ ..., logger: new FallbackLogger('debug') });\n * ```\n */\nexport class FallbackLogger implements Logger {\n /**\n * @param level - Minimum level to output. Messages below this level are silently dropped.\n */\n constructor(public readonly level: LogLevel) {}\n\n debug(data: Record<string, unknown>, message?: string): void {\n this.log('debug', data, message);\n }\n\n info(data: Record<string, unknown>, message?: string): void {\n this.log('info', data, message);\n }\n\n warn(data: Record<string, unknown>, message?: string): void {\n this.log('warn', data, message);\n }\n\n error(data: Record<string, unknown>, message?: string): void {\n this.log('error', data, message);\n }\n\n private log(level: LogLevel, data: Record<string, unknown>, message?: string) {\n if (LOG_LEVELS.indexOf(level) < LOG_LEVELS.indexOf(this.level)) {\n return;\n }\n\n const fn = console[level];\n\n message !== undefined ? fn(message, data) : fn(data);\n }\n}\n\n/**\n * @internal\n */\nexport const initLogger = (logger: ClientConfig['logger']): Logger | undefined => {\n if (logger === false) {\n return;\n }\n\n if (logger === undefined || logger === true) {\n return new FallbackLogger('info');\n }\n\n if (typeof logger === 'string') {\n if (LOG_LEVELS.includes(logger)) {\n return new FallbackLogger(logger);\n } else {\n const logger = new FallbackLogger('info');\n\n logger.warn({ level: logger }, 'Unknown log level passed, using info');\n\n return logger;\n }\n }\n\n return logger;\n};\n","import * as jose from 'jose';\nimport ky, { isHTTPError, isTimeoutError } from 'ky';\nimport { BASE_KY_CONFIG } from './lib/common';\nimport {\n BCApiError,\n BCAuthInvalidJwtError,\n BCAuthInvalidRedirectUriError,\n BCAuthMissingParamError,\n BCAuthScopeMismatchError,\n BCClientError,\n BCTimeoutError,\n} from './lib/errors';\nimport { initLogger, type Logger, type LogLevel } from './lib/logger';\n\n/**\n * Configuration options for BigCommerce authentication\n */\nexport type BigCommerceAuthConfig = {\n /** The OAuth client ID from BigCommerce */\n clientId: string;\n /** The OAuth client secret from BigCommerce */\n secret: string;\n /** The redirect URI registered with BigCommerce */\n redirectUri: string;\n /** Optional array of scopes to validate during auth callback */\n scopes?: string[];\n /** Optional logger instance */\n logger?: Logger | LogLevel | boolean;\n};\n\nconst GRANT_TYPE = 'authorization_code';\nconst TOKEN_ENDPOINT = 'https://login.bigcommerce.com/oauth2/token';\nconst ISSUER = 'bc';\n\n/**\n * Query parameters received from BigCommerce auth callback\n */\nexport type BigCommerceAuthQuery = {\n /** The authorization code from BigCommerce */\n code: string;\n /** The granted OAuth scopes */\n scope: string;\n /** The store context */\n context: string;\n};\n\n/**\n * Request payload for token endpoint\n */\ntype TokenRequest = {\n client_id: string;\n client_secret: string;\n code: string;\n context: string;\n scope: string;\n grant_type: typeof GRANT_TYPE;\n redirect_uri: string;\n};\n\n/**\n * User information returned from BigCommerce\n */\nexport type User = {\n /** The user's ID */\n id: number;\n /** The user's username */\n username: string;\n /** The user's email address */\n email: string;\n};\n\n/**\n * Response from BigCommerce token endpoint\n */\nexport type TokenResponse = {\n /** The OAuth access token */\n access_token: string;\n /** The granted OAuth scopes */\n scope: string;\n /** Information about the authenticated user */\n user: User;\n /** Information about the store owner */\n owner: User;\n /** The store context */\n context: string;\n /** The BigCommerce account UUID */\n account_uuid: string;\n};\n\n/**\n * JWT claims from BigCommerce\n */\nexport type Claims = {\n /** JWT audience */\n aud: string;\n /** JWT issuer */\n iss: string;\n /** JWT issued at timestamp */\n iat: number;\n /** JWT not before timestamp */\n nbf: number;\n /** JWT expiration timestamp */\n exp: number;\n /** JWT unique identifier */\n jti: string;\n /** JWT subject */\n sub: string;\n /** Information about the authenticated user */\n user: {\n id: number;\n email: string;\n locale: string;\n };\n /** Information about the store owner */\n owner: {\n id: number;\n email: string;\n };\n /** The store URL */\n url: string;\n /** The channel ID (if applicable) */\n channel_id: number | null;\n};\n\n/**\n * Handles authentication with BigCommerce OAuth\n */\nexport class BigCommerceAuth {\n private readonly logger: Logger | undefined;\n private readonly client: ReturnType<typeof ky.create>;\n\n /**\n * Creates a new BigCommerceAuth instance for handling OAuth authentication\n * @param config - Configuration options for BigCommerce authentication\n * @param config.clientId - The OAuth client ID from BigCommerce\n * @param config.secret - The OAuth client secret from BigCommerce\n * @param config.redirectUri - The redirect URI registered with BigCommerce\n * @param config.scopes - Optional array of scopes to validate during auth callback\n * @param config.logger - Optional logger instance for debugging and error tracking\n * @throws {BCAuthInvalidRedirectUriError} If the redirect URI is invalid\n */\n constructor(private readonly config: BigCommerceAuthConfig) {\n try {\n new URL(this.config.redirectUri);\n } catch (error) {\n throw new BCAuthInvalidRedirectUriError(this.config.redirectUri, error);\n }\n\n this.logger = initLogger(config.logger);\n\n const { prefixUrl: _, ...authKyConfig } = BASE_KY_CONFIG;\n\n this.client = ky.create({\n ...authKyConfig,\n retry: {\n ...authKyConfig.retry,\n methods: ['POST'],\n },\n });\n }\n\n /**\n * Exchanges an OAuth authorization code for an access token.\n *\n * @param data - The auth callback payload: a raw query string, `URLSearchParams`, or a\n * pre-parsed object with `code`, `scope`, and `context`.\n * @returns The token response including `access_token`, `user`, and `context`.\n * @throws {@link BCAuthMissingParamError} if `code`, `scope`, or `context` are absent.\n * @throws {@link BCAuthScopeMismatchError} if the granted scopes don't include all `config.scopes`.\n * @throws {@link BCApiError} on HTTP error responses from the token endpoint.\n * @throws {@link BCTimeoutError} if the token request times out.\n * @throws {@link BCClientError} on any other error.\n */\n async requestToken(data: string | BigCommerceAuthQuery | URLSearchParams): Promise<TokenResponse> {\n const query = typeof data === 'string' || data instanceof URLSearchParams ? this.parseQueryString(data) : data;\n\n this.validateScopes(query.scope);\n\n const tokenRequest: TokenRequest = {\n client_id: this.config.clientId,\n client_secret: this.config.secret,\n ...query,\n grant_type: GRANT_TYPE,\n redirect_uri: this.config.redirectUri,\n };\n\n this.logger?.debug(\n {\n clientId: this.config.clientId,\n context: query.context,\n scopes: query.scope,\n },\n 'Requesting OAuth token',\n );\n\n let res: Response;\n\n try {\n res = await this.client(TOKEN_ENDPOINT, {\n method: 'POST',\n json: tokenRequest,\n });\n } catch (error) {\n if (isHTTPError(error)) {\n const requestBody = await error.request.text().catch(() => '');\n const responseBody = await error.response.text().catch(() => '');\n const err = new BCApiError(error, requestBody, responseBody);\n\n this.logger?.error(err.context, 'Failed to request token');\n\n throw err;\n }\n\n if (isTimeoutError(error)) {\n const err = new BCTimeoutError(error);\n\n this.logger?.error(err.context, 'Token request timed out');\n\n throw err;\n }\n\n throw new BCClientError('Failed to request token', {}, error);\n }\n\n return res.json() as Promise<TokenResponse>;\n }\n\n /**\n * Verifies a JWT payload from BigCommerce\n * @param jwtPayload - The JWT string to verify\n * @param storeHash - The store hash for the BigCommerce store\n * @returns Promise resolving to the verified JWT claims\n * @throws {BCAuthInvalidJwtError} If the JWT is invalid\n */\n async verify(jwtPayload: string, storeHash: string): Promise<Claims> {\n try {\n const secret = new TextEncoder().encode(this.config.secret);\n\n const { payload }: { payload: Claims } = await jose.jwtVerify(jwtPayload, secret, {\n audience: this.config.clientId,\n issuer: ISSUER,\n subject: `stores/${storeHash}`,\n });\n\n this.logger?.debug(\n {\n userId: payload.user?.id,\n storeHash: payload.sub.split('/')[1],\n },\n 'JWT verified successfully',\n );\n\n return payload;\n } catch (error) {\n const err = new BCAuthInvalidJwtError(storeHash, error);\n\n this.logger?.error(err.context, 'JWT verification failed');\n\n throw err;\n }\n }\n\n /**\n * Parses and validates a query string from BigCommerce auth callback\n * @param queryString - The query string to parse\n * @returns The parsed auth query parameters\n * @throws {BCAuthMissingParamError} If required parameters are missing\n */\n private parseQueryString(queryString: string | URLSearchParams): BigCommerceAuthQuery {\n const params = typeof queryString === 'string' ? new URLSearchParams(queryString) : queryString;\n\n const code = params.get('code');\n const scope = params.get('scope');\n const context = params.get('context');\n\n if (!code) {\n throw new BCAuthMissingParamError('code');\n }\n\n if (!scope) {\n throw new BCAuthMissingParamError('scope');\n } else if (this.config.scopes?.length) {\n this.validateScopes(scope);\n }\n\n if (!context) {\n throw new BCAuthMissingParamError('context');\n }\n\n return {\n code,\n scope,\n context,\n };\n }\n\n /**\n * Validates that the granted scopes match the expected scopes\n * @param scopes - Space-separated list of granted scopes\n * @throws {BCAuthScopeMismatchError} If the scopes don't match the expected scopes\n */\n private validateScopes(scopes: string) {\n if (!this.config.scopes) {\n return;\n }\n\n const granted = scopes.split(' ');\n const expected = this.config.scopes;\n const missing = expected.filter((scope) => !granted.includes(scope));\n\n if (missing.length) {\n throw new BCAuthScopeMismatchError(granted, expected, missing);\n }\n }\n}\n","import { HEADERS, type RateLimitMeta } from './common';\n\nexport function stripKeys<T extends object, K extends PropertyKey>(\n obj: T | undefined,\n keys: K[],\n): Omit<T, K> | undefined {\n if (!obj) {\n return obj;\n }\n\n const result = { ...obj } as Record<PropertyKey, unknown>;\n\n for (const key of keys) {\n delete result[key];\n }\n\n return result as Omit<T, K>;\n}\n\nconst parseIntHeader = (headers: Headers, key: string): number | undefined => {\n const value = Number.parseInt(headers.get(key) ?? '', 10);\n\n return Number.isNaN(value) ? undefined : value;\n};\n\nexport const extractRateLimitHeaders = (headers: Headers): RateLimitMeta | undefined => {\n const resetIn = parseIntHeader(headers, HEADERS.RATE_LIMIT_RESET);\n\n // Can't retry without this header - treat as unrecoverable\n if (resetIn === undefined) {\n return undefined;\n }\n\n return {\n resetIn,\n requestsLeft: parseIntHeader(headers, HEADERS.RATE_LIMIT_LEFT),\n quota: parseIntHeader(headers, HEADERS.RATE_LIMIT_QUOTA),\n window: parseIntHeader(headers, HEADERS.RATE_LIMIT_WINDOW),\n };\n};\n\nexport const chunkStrLength = (\n items: string[],\n options: {\n maxLength?: number;\n chunkLength?: number;\n offset?: number;\n separatorSize?: number;\n } = {},\n) => {\n const { maxLength = 2048, chunkLength = 250, offset = 0, separatorSize = 1 } = options;\n\n const chunks: string[][] = [];\n let currentStrLength = offset;\n let currentChunk: string[] = [];\n\n for (const item of items) {\n const itemLength = encodeURIComponent(item).length;\n const separatorLength = currentChunk.length > 0 ? separatorSize : 0;\n const totalItemLength = itemLength + separatorLength;\n\n const wouldExceedLength = currentStrLength + totalItemLength > maxLength;\n const wouldExceedCount = currentChunk.length >= chunkLength;\n\n if ((wouldExceedLength || wouldExceedCount) && currentChunk.length > 0) {\n chunks.push(currentChunk);\n currentChunk = [];\n currentStrLength = offset;\n }\n\n if (itemLength + offset > maxLength) {\n throw new Error(`Item too large: ${itemLength} exceeds maxLength ${maxLength}`);\n }\n\n currentChunk.push(item);\n currentStrLength += itemLength + (currentChunk.length > 1 ? separatorSize : 0);\n }\n\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n\n return chunks;\n};\n\nexport class AsyncChannel<T> {\n private readonly queue: T[] = [];\n private notify: (() => void) | null = null;\n private done = false;\n\n push(item: T) {\n this.queue.push(item);\n this.notify?.();\n this.notify = null;\n }\n\n close() {\n this.done = true;\n this.notify?.();\n this.notify = null;\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<T> {\n while (!this.done || this.queue.length > 0) {\n if (this.queue.length === 0) {\n await new Promise<void>((r) => {\n if (this.queue.length > 0) {\n return r();\n }\n\n this.notify = r;\n });\n }\n\n while (this.queue.length > 0) {\n const item = this.queue.shift();\n\n if (item !== undefined) {\n yield item;\n }\n }\n }\n }\n}\n","import { type BeforeRequestHook, type BeforeRetryHook, isHTTPError } from 'ky';\nimport { MAX_URL_LENGTH, rateLimitJitter } from './common';\nimport { BCRateLimitDelayTooLongError, BCRateLimitNoHeadersError, BCUrlTooLongError } from './errors';\nimport type { Logger } from './logger';\nimport { extractRateLimitHeaders } from './util';\n\nexport const validateUrlLength: BeforeRequestHook = (request) => {\n if (request.url.length > MAX_URL_LENGTH) {\n throw new BCUrlTooLongError(request.url, MAX_URL_LENGTH);\n }\n};\n\nexport const bcRateLimitRetry =\n (logger?: Logger): BeforeRetryHook =>\n async ({ request, options, error, retryCount }) => {\n if (isHTTPError(error) && error.response.status === 429) {\n const retryMeta = extractRateLimitHeaders(error.response.headers);\n\n if (!retryMeta) {\n throw new BCRateLimitNoHeadersError(request, retryCount);\n }\n\n if (options.retry.maxRetryAfter && retryMeta.resetIn > options.retry.maxRetryAfter) {\n throw new BCRateLimitDelayTooLongError(\n request,\n retryCount,\n options.retry.maxRetryAfter,\n retryMeta.resetIn,\n );\n }\n\n const delay =\n typeof options.retry.jitter === 'function'\n ? options.retry.jitter(retryMeta.resetIn)\n : rateLimitJitter(retryMeta.resetIn);\n\n logger?.warn(\n { attempt: retryCount, url: request.url, method: request.method, retryMeta },\n `Rate limit reached, retrying in ${delay} (with jitter)`,\n );\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n } else {\n logger?.warn({ url: request.url, method: request.method, attempt: retryCount }, 'Retrying request');\n }\n };\n","import type { Options as KyOptions } from 'ky';\nimport type { ConcurrencyOptions } from './common';\nimport type { StandardSchemaV1 } from './standard-schema';\n\n/** BigCommerce API versions supported by the client. */\nexport type ApiVersion = 'v3' | 'v2';\n\n/** Valid query parameter value types. */\nexport type QueryValue = string | number | Array<string | number>;\n\n/** Query parameter object for API requests. */\nexport type Query = Record<string, QueryValue>;\n\n/**\n * Converts a Query object to URLSearchParams.\n * Array values are joined with commas (e.g., `id:in=1,2,3`).\n */\nexport const toUrlSearchParams = (query?: Query): URLSearchParams | undefined => {\n if (!query) {\n return;\n }\n\n const params = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n params.append(key, value.map(String).join(','));\n } else {\n params.append(key, String(value));\n }\n }\n\n return params;\n};\n\n/** Supported HTTP methods for API requests. */\nexport type HttpMethod = 'POST' | 'GET' | 'PUT' | 'DELETE';\n\n/** @internal */\ntype BaseKyRequest = Omit<\n KyOptions,\n 'json' | 'method' | 'searchQueryParams' | 'body' | 'throwHttpErrors' | 'parseJson'\n>;\n\n/** @internal */\ntype QuerySchemaOptions<TQuery extends Query> =\n /** Query parameters to send with the request. */\n { query: TQuery; querySchema: StandardSchemaV1<TQuery> } | { query?: TQuery; querySchema?: never };\n\n/** @internal */\ntype BodySchemaOptions<TBody> =\n /** Request body, serialized as JSON. */\n { body: TBody; bodySchema: StandardSchemaV1<TBody> } | { body?: TBody; bodySchema?: never };\n\n/**\n * Full request options for direct API calls.\n * @see {@link GetOptions}, {@link PostOptions}, {@link PutOptions}, {@link DeleteOptions}\n */\nexport type RequestOptions<TBody, TRes, TQuery extends Query> = BaseKyRequest &\n QuerySchemaOptions<TQuery> &\n BodySchemaOptions<TBody> & {\n /** HTTP method for the request. */\n method: HttpMethod;\n /** API version to use. Defaults to `'v3'`. */\n version?: ApiVersion;\n /** Schema to validate the response body. */\n responseSchema?: StandardSchemaV1<TRes>;\n };\n\n/** Options for GET requests. */\nexport type GetOptions<TRes, TQuery extends Query> = Omit<\n RequestOptions<never, TRes, TQuery>,\n 'body' | 'bodySchema' | 'method'\n>;\n\n/** Options for POST requests. */\nexport type PostOptions<TBody, TRes, TQuery extends Query> = Omit<RequestOptions<TBody, TRes, TQuery>, 'method'>;\n\n/** Options for PUT requests. */\nexport type PutOptions<TBody, TRes, TQuery extends Query> = PostOptions<TBody, TRes, TQuery>;\n\n/** Options for DELETE requests. */\nexport type DeleteOptions<TQuery extends Query> = Omit<\n RequestOptions<never, never, TQuery>,\n 'body' | 'bodySchema' | 'method' | 'responseSchema'\n>;\n\n/**\n * Request descriptor for batch operations.\n * Use the {@link req} helpers to construct these.\n */\nexport type BatchRequestOptions<TBody, TRes, TQuery extends Query> = {\n path: string;\n} & RequestOptions<TBody, TRes, TQuery>;\n\n/**\n * Helpers for building typed request descriptors to pass to\n * {@link BigCommerceClient.batchSafe} or {@link BigCommerceClient.batchStream}.\n *\n * @example\n * ```ts\n * const results = await client.batchSafe([\n * req.get('catalog/products/1'),\n * req.post('catalog/products', { body: { name: 'Widget' } }),\n * ]);\n * ```\n */\nexport const req = {\n /**\n * Builds a GET request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional query params, schemas, and ky options.\n */\n get: <TRes, TQuery extends Query = Query>(\n path: string,\n options?: GetOptions<TRes, TQuery>,\n ): BatchRequestOptions<never, TRes, TQuery> =>\n ({ method: 'GET', path, ...options }) as BatchRequestOptions<never, TRes, TQuery>,\n\n /**\n * Builds a POST request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional body, query params, schemas, and ky options.\n */\n post: <TRes, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PostOptions<TBody, TRes, TQuery>,\n ): BatchRequestOptions<TBody, TRes, TQuery> =>\n ({ method: 'POST', path, ...options }) as BatchRequestOptions<TBody, TRes, TQuery>,\n\n /**\n * Builds a PUT request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional body, query params, schemas, and ky options.\n */\n put: <TRes, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PutOptions<TBody, TRes, TQuery>,\n ): BatchRequestOptions<TBody, TRes, TQuery> =>\n ({ method: 'PUT', path, ...options }) as BatchRequestOptions<TBody, TRes, TQuery>,\n\n /**\n * Builds a DELETE request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional query params and ky options.\n */\n delete: <TQuery extends Query = Query>(\n path: string,\n options?: DeleteOptions<TQuery>,\n ): BatchRequestOptions<never, never, TQuery> =>\n ({ method: 'DELETE', path, ...options }) as BatchRequestOptions<never, never, TQuery>,\n};\n\n/**\n * Options for v3 paginated collection operations ({@link BigCommerceClient.collect}, {@link BigCommerceClient.stream}).\n */\nexport type CollectOptions<TItem, TQuery extends Query> = ConcurrencyOptions &\n Omit<GetOptions<TItem, TQuery>, 'responseSchema' | 'version'> & {\n /** Schema to validate each item in the response. */\n itemSchema?: StandardSchemaV1<TItem>;\n };\n\nexport type BlindOptions<TItem, TQuery extends Query> = Omit<CollectOptions<TItem, TQuery>, 'version'> & {\n maxPages?: number;\n};\n\n/**\n * Options for v2 paginated operations with known count ({@link BigCommerceClient.collectCount}, {@link BigCommerceClient.streamCount}).\n */\nexport type CountedCollectOptions<TItem, TQuery extends Query> = CollectOptions<TItem, TQuery> & {\n /** Total number of items expected (for v2 endpoints without pagination metadata). */\n count?: number;\n};\n\n/**\n * Options for query-based filtering operations ({@link BigCommerceClient.query}, {@link BigCommerceClient.queryStream}).\n */\nexport type QueryOptions<TItem, TQuery extends Query> = CollectOptions<TItem, TQuery> & {\n /** Query parameter name for value filtering (e.g., `'id:in'`). */\n key: string;\n /** Values to filter by. Automatically chunked across multiple requests. */\n values: (string | number)[];\n};\n","export type Ok<T> = {\n ok: true;\n data: T;\n err: undefined;\n};\n\nexport type Err<E> = {\n ok: false;\n data: undefined;\n err: E;\n};\n\nexport type Result<T, E> = Ok<T> | Err<E>;\n\n/**\n * Creates a successful {@link Result}. Check `result.ok` or `result.err` before accessing `data`.\n * @param data - The success value.\n */\nexport const Ok = <T, E>(data: T): Result<T, E> => ({ ok: true, data, err: undefined });\n\n/**\n * Creates a failed {@link Result}. Check `result.ok` or `result.err` before accessing `err`.\n * @param err - The error value.\n */\nexport const Err = <T, E>(err: E): Result<T, E> => ({ ok: false, data: undefined, err });\n","import ky, { isHTTPError, isKyError, isTimeoutError, type KyInstance, type KyResponse } from 'ky';\nimport pLimit, { type LimitFunction } from 'p-limit';\nimport {\n BASE_KY_CONFIG,\n type ClientConfig,\n type ConcurrencyOptions,\n DEFAULT_BACKOFF_RATE,\n DEFAULT_BACKOFF_RECOVER,\n DEFAULT_CONCURRENCY,\n DEFAULT_LIMIT,\n DEFAULT_MAX_BLIND_PAGES,\n DEFAULT_RATE_LIMIT_BACKOFF,\n HEADERS,\n LEADING_SLASHES,\n type Logger,\n MAX_CONCURRENCY,\n MAX_URL_LENGTH,\n type ResolvedConcurrencyOptions,\n} from './lib/common';\nimport {\n BaseError,\n BCApiError,\n BCClientError,\n BCCredentialsError,\n BCPaginatedItemValidationError,\n BCPaginatedOptionError,\n BCPaginatedResponseError,\n BCQueryValidationError,\n BCRequestBodyValidationError,\n BCResponseParseError,\n BCResponseValidationError,\n type BCSchemaValidationError,\n BCTimeoutError,\n} from './lib/errors';\nimport { bcRateLimitRetry, validateUrlLength } from './lib/hooks';\nimport { initLogger } from './lib/logger';\nimport type { V3Resource } from './lib/pagination';\nimport {\n type ApiVersion,\n type BatchRequestOptions,\n type BlindOptions,\n type CollectOptions,\n type DeleteOptions,\n type GetOptions,\n type PostOptions,\n type PutOptions,\n type Query,\n type QueryOptions,\n type RequestOptions,\n req,\n toUrlSearchParams,\n} from './lib/request';\nimport { Err, Ok, type Result } from './lib/result';\nimport type { StandardSchemaV1 } from './lib/standard-schema';\nimport { AsyncChannel, chunkStrLength } from './lib/util';\n\nexport class BigCommerceClient {\n private readonly logger?: Logger;\n private readonly client: KyInstance;\n private readonly storeHash: string;\n\n /**\n * Creates a new BigCommerceClient.\n *\n * @param config - Client configuration. Ky options (e.g. `prefixUrl`, `timeout`, `retry`,\n * `hooks`) are forwarded to the underlying ky instance.\n * @param config.storeHash - BigCommerce store hash. Must be a non-empty string.\n * @param config.accessToken - BigCommerce API access token. Must be a non-empty string.\n * @param config.logger - A {@link Logger} instance, a log level string\n * (`'debug' | 'info' | 'warn' | 'error'`), `true` to enable console logging at `'info'`\n * level, or `false` to disable logging entirely. Omitting also defaults to `'info'` level.\n * @param config.concurrency - Default max concurrent requests for batch/stream operations.\n * Must be between 1 and 1000. Pass `false` to disable concurrency (sequential execution).\n * Defaults to 10.\n * @param config.rateLimitBackoff - Concurrency cap applied when a 429 response is received.\n * Defaults to 1.\n * @param config.backoff - Divisor (or `(concurrency, status) => number` function) applied to\n * concurrency on non-429 error responses. Defaults to 2.\n * @param config.backoffRecover - Amount (or `(concurrency) => number` function) added to\n * concurrency per successful response while below the configured max. Defaults to 1.\n *\n * @throws {@link BCCredentialsError} if `storeHash` or `accessToken` are missing.\n * @throws {@link BCClientError} if `prefixUrl` is not a valid URL or `concurrency` is out of range.\n */\n constructor(private readonly config: ClientConfig) {\n this.validateConfig();\n\n const { storeHash, accessToken, logger, concurrency: _, ...kyOptions } = config;\n\n this.logger = initLogger(logger);\n this.storeHash = storeHash;\n\n this.client = ky.create({\n ...BASE_KY_CONFIG,\n ...kyOptions,\n\n headers: {\n ...BASE_KY_CONFIG.headers,\n ...((kyOptions.headers ?? {}) as Record<string, string>),\n [HEADERS.AUTH_TOKEN]: accessToken,\n },\n\n hooks: {\n beforeRequest: [...(kyOptions.hooks?.beforeRequest ?? []), validateUrlLength],\n beforeRetry: [\n ({ error }) => {\n if (error instanceof BaseError) {\n throw error;\n }\n },\n bcRateLimitRetry(this.logger),\n ...(kyOptions.hooks?.beforeRetry ?? []),\n ],\n beforeError: [...(kyOptions.hooks?.beforeError ?? [])],\n afterResponse: [...(kyOptions.hooks?.afterResponse ?? [])],\n },\n });\n }\n\n /**\n * Sends a GET request to the given path.\n *\n * @param path - API path relative to the store's versioned base URL (e.g. `catalog/products`).\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n * @param options.responseSchema - StandardSchemaV1 schema to validate the parsed response body.\n *\n * @returns Parsed and optionally validated response body.\n *\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCResponseValidationError} if `responseSchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async get<TRes = unknown, TQuery extends Query = Query>(\n path: string,\n options?: GetOptions<TRes, TQuery>,\n ): Promise<TRes> {\n return this.request<never, TRes, TQuery>(path, {\n ...options,\n method: 'GET',\n } as RequestOptions<never, TRes, TQuery>);\n }\n\n /**\n * Sends a POST request to the given path.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.body - Request body, serialized as JSON.\n * @param options.bodySchema - StandardSchemaV1 schema to validate `body` before the request\n * is sent. Requires `body` to be provided.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n * @param options.responseSchema - StandardSchemaV1 schema to validate the parsed response body.\n *\n * @returns Parsed and optionally validated response body.\n *\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCRequestBodyValidationError} if `bodySchema` validation fails.\n * @throws {@link BCResponseValidationError} if `responseSchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async post<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PostOptions<TBody, TRes, TQuery>,\n ): Promise<TRes> {\n return this.request<TBody, TRes, TQuery>(path, {\n ...options,\n method: 'POST',\n } as RequestOptions<TBody, TRes, TQuery>);\n }\n\n /**\n * Sends a PUT request to the given path.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.body - Request body, serialized as JSON.\n * @param options.bodySchema - StandardSchemaV1 schema to validate `body` before the request\n * is sent. Requires `body` to be provided.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n * @param options.responseSchema - StandardSchemaV1 schema to validate the parsed response body.\n *\n * @returns Parsed and optionally validated response body.\n *\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCRequestBodyValidationError} if `bodySchema` validation fails.\n * @throws {@link BCResponseValidationError} if `responseSchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async put<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PutOptions<TBody, TRes, TQuery>,\n ): Promise<TRes> {\n return this.request<TBody, TRes, TQuery>(path, {\n ...options,\n method: 'PUT',\n } as RequestOptions<TBody, TRes, TQuery>);\n }\n\n /**\n * Sends a DELETE request to the given path.\n *\n * Silently suppresses 404 responses (resource already gone) and empty response bodies.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n *\n * @throws {@link BCApiError} on non-404 HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body is non-empty and cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async delete<TRes = never, TQuery extends Query = Query>(\n path: string,\n options?: DeleteOptions<TQuery>,\n ): Promise<void> {\n try {\n await this.request<never, TRes, TQuery>(path, {\n ...options,\n method: 'DELETE',\n } as RequestOptions<never, TRes, TQuery>);\n } catch (err) {\n if (err instanceof BCResponseParseError && err.context.rawBody === '') {\n return;\n }\n\n // Do not throw on delete for resources that are already gone.\n if (err instanceof BCApiError && err.context.status === 404) {\n this.logger?.warn({ err }, 'Attempted to delete the resource that is already gone');\n\n return;\n }\n\n throw err;\n }\n }\n\n /**\n * Fetches items from a v3 paginated endpoint by splitting `values` across multiple requests\n * using the given `key` query param, chunking to stay within URL length limits.\n *\n * Collects all results into an array. Use {@link queryStream} to process items lazily.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Query options including `key`, `values`, pagination params, and concurrency\n * controls.\n * @param options.key - Query parameter name used for value filtering (e.g. `'id:in'`).\n * @param options.values - Values to filter by. Automatically chunked across multiple requests\n * to keep each URL under 2048 characters.\n * @param options.query - Additional query parameters. `query.limit` controls page size\n * (default 250, must be > 0). If `options.key` is present in `query` it will be ignored.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent chunk requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n *\n * @returns All matching items across all chunked requests.\n * @throws {@link BCPaginatedOptionError} if `query.limit` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if a request times out.\n * @throws {@link BCResponseParseError} if a response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if a constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCPaginatedResponseError} if a page response has an unexpected shape.\n * @throws {@link BCPaginatedItemValidationError} if `itemSchema` validation fails for an item.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async query<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options: QueryOptions<TItem, TQuery>,\n ): Promise<TItem[]> {\n const results: TItem[] = [];\n\n for await (const { data, err } of this.queryStream(path, options)) {\n if (err) {\n throw err;\n } else {\n results.push(data);\n }\n }\n\n return results;\n }\n\n /**\n * Streaming variant of {@link query}. Yields each item individually as results arrive,\n * splitting `values` into URL-length-safe chunks across concurrent requests.\n *\n * Each yielded value is a {@link Result} — check `err` before using `data`.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Query options including `key`, `values`, pagination params, and concurrency\n * controls.\n * @param options.key - Query parameter name used for value filtering (e.g. `'id:in'`).\n * @param options.values - Values to filter by. Automatically chunked across multiple requests\n * to keep each URL under 2048 characters.\n * @param options.query - Additional query parameters. `query.limit` controls page size\n * (default 250, must be > 0). If `options.key` is present in `query` it will be ignored.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent chunk requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @throws {@link BCPaginatedOptionError} if `query.limit` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n */\n async *queryStream<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options: QueryOptions<TItem, TQuery>,\n ): AsyncGenerator<Result<TItem, BaseError>> {\n const { key, values, query, querySchema, itemSchema, ...requestOptions } = options;\n\n const limit = this.validatePaginationOption(path, 'limit', query?.limit ?? DEFAULT_LIMIT);\n\n const validatedQuery = await this.validate(\n query,\n querySchema,\n BCQueryValidationError,\n 'GET',\n path,\n 'Invalid query parameters',\n );\n\n const newQuery: Query = {\n ...validatedQuery,\n limit,\n };\n\n if (key in newQuery) {\n this.logger?.warn({ key }, 'The provided key is already in the query params, this param will be ignored');\n\n delete newQuery[key];\n }\n\n const url = this.config.prefixUrl ?? requestOptions.prefixUrl ?? BASE_KY_CONFIG.prefixUrl;\n const fullPath = this.makePath('v3', path);\n const fullQuery = toUrlSearchParams({ ...newQuery, page: 1 });\n const fullUrl = `${url}/${fullPath}?${fullQuery}`;\n const keyOverhead = encodeURIComponent(key).length + 2; // `&key=` or `key=` prefix\n\n const chunks = chunkStrLength(values.map(String), {\n chunkLength: limit,\n maxLength: MAX_URL_LENGTH,\n offset: fullUrl.length + keyOverhead,\n separatorSize: encodeURIComponent(',').length,\n });\n\n const requests = chunks.map((chunk) =>\n req.get(path, {\n ...requestOptions,\n query: {\n ...newQuery,\n page: 1,\n [key]: chunk,\n },\n }),\n );\n\n for await (const { err, data } of this.batchStream(requests, options)) {\n if (err) {\n yield Err(err);\n continue;\n }\n\n try {\n const { data: items } = this.assertPaginatedResponse(path, data);\n\n for (const item of items) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } catch (err) {\n if (err instanceof BaseError) {\n yield Err(err);\n } else {\n yield Err(new BCClientError('Unknown error occurred processing page', {}, { cause: err }));\n }\n }\n }\n }\n\n /**\n * Fetches all pages from a v3 paginated endpoint and collects items into an array.\n *\n * Use {@link stream} to process items lazily without buffering the full result set.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0).\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent page requests for pages after the first.\n * Must be 1–1000. `false` for sequential. Defaults to `config.concurrency`, or 10 if not\n * set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @returns All items across all pages.\n *\n * @throws {@link BCPaginatedOptionError} if `query.limit` or `query.page` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if a request times out.\n * @throws {@link BCResponseParseError} if a response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if a constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCPaginatedResponseError} if a page response has an unexpected shape.\n * @throws {@link BCPaginatedItemValidationError} if `itemSchema` validation fails for an item.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async collect<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: CollectOptions<TItem, TQuery>,\n ): Promise<TItem[]> {\n const items: TItem[] = [];\n\n for await (const { data, err } of this.stream(path, options)) {\n if (err) {\n throw err;\n } else {\n items.push(data);\n }\n }\n\n return items;\n }\n\n /**\n * Fetches all pages from a v2 flat-array endpoint and collects items into an array.\n *\n * Pagination is discovered dynamically — pages are fetched in batches until an empty page,\n * a 404, or a 204 response is received. No prior knowledge of total count is required.\n *\n * Use {@link streamBlind} to process items lazily without buffering the full result set.\n *\n * @param path - API path relative to the store's versioned base URL (always requests v2).\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0).\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.maxPages - Maximum number of pages to fetch before stopping (default 500,\n * must be > 0). A warning is logged if this limit is reached.\n * @param options.concurrency - Max concurrent page requests per batch. Must be 1–1000.\n * `false` for sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @returns All items across all pages.\n *\n * @throws {@link BCPaginatedOptionError} if `query.limit`, `query.page`, or `maxPages` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCPaginatedItemValidationError} if `itemSchema` validation fails for an item.\n * @throws {@link BCClientError} if a page returns a non-array response, or on any other error.\n * @throws {@link BCApiError} on non-terminating HTTP error responses.\n * @throws {@link BCTimeoutError} if a request times out.\n * @throws {@link BCResponseParseError} if a response body cannot be parsed.\n */\n async collectBlind<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: BlindOptions<TItem, TQuery>,\n ): Promise<TItem[]> {\n const results: TItem[] = [];\n\n for await (const { err, data } of this.streamBlind(path, options)) {\n if (err) {\n throw err;\n } else {\n results.push(data);\n }\n }\n\n return results;\n }\n\n /**\n * Lazily streams items from a v2 flat-array endpoint, page by page.\n *\n * Pagination is discovered dynamically — pages are fetched in concurrent batches until an\n * empty page, a 404, or a 204 response is received. No prior knowledge of total count is\n * required. Each item is yielded as a {@link Result}: `Ok(item)` on success or\n * `Err(error)` for item-level validation failures and non-terminating page errors.\n *\n * Use {@link collectBlind} to buffer all results into an array (throws on any error).\n *\n * @param path - API path relative to the store's versioned base URL (always requests v2).\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0).\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.maxPages - Maximum number of pages to fetch before stopping (default 500,\n * must be > 0). A warning is logged if this limit is reached.\n * @param options.concurrency - Max concurrent page requests per batch. Must be 1–1000.\n * `false` for sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n *\n * @throws {@link BCPaginatedOptionError} if `query.limit`, `query.page`, or `maxPages` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n *\n * @yields `Ok(item)` for each successfully fetched and validated item.\n * @yields `Err(BCPaginatedItemValidationError)` when `itemSchema` rejects an item.\n * @yields `Err(BCClientError)` when a page returns a non-array response.\n * @yields `Err(error)` for non-terminating page errors (e.g. non-404/204 HTTP errors).\n */\n async *streamBlind<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: BlindOptions<TItem, TQuery>,\n ): AsyncGenerator<Result<TItem, BaseError>> {\n const {\n query: rawQuery,\n querySchema,\n itemSchema,\n maxPages: rawMaxPages,\n concurrency: rawConcurrency,\n rateLimitBackoff,\n backoff,\n backoffRecover,\n ...requestOptions\n } = options ?? {};\n\n const concurrencyOptions = { concurrency: rawConcurrency, rateLimitBackoff, backoff, backoffRecover };\n\n const concurrency = this.validateConcurrency(this.config.concurrency ?? rawConcurrency ?? DEFAULT_CONCURRENCY);\n\n const query = await this.validate(rawQuery, querySchema, BCQueryValidationError, 'GET', path);\n const page = this.validatePaginationOption(path, 'page', query?.page ?? 1);\n const limit = this.validatePaginationOption(path, 'limit', query?.limit ?? DEFAULT_LIMIT);\n const maxPages = this.validatePaginationOption(path, 'maxPages', rawMaxPages ?? DEFAULT_MAX_BLIND_PAGES);\n\n let done = false;\n let currentPage = page;\n\n do {\n if (currentPage > maxPages) {\n this.logger?.warn({ currentPage }, 'Blind pagination reached maxPages before the end of the data');\n break;\n }\n\n const batchSize = concurrency || 1;\n const pageRequests = Array.from({ length: batchSize }, (_, i) => currentPage + i).map((page) =>\n req.get(path, {\n ...requestOptions,\n version: 'v2',\n query: {\n ...query,\n page,\n limit,\n },\n }),\n );\n\n currentPage += batchSize;\n\n const pages = await this.batchSafe(pageRequests, concurrencyOptions);\n\n for (const { err, data } of pages) {\n if (err) {\n done =\n (err instanceof BCApiError && err.context.status === 404) ||\n (err instanceof BCResponseParseError &&\n err.context.rawBody === '' &&\n err.context.status === 204);\n\n if (!done) {\n yield Err(err);\n }\n } else {\n if (Array.isArray(data)) {\n if (data.length === 0) {\n done = true;\n break;\n }\n\n for (const item of data) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } else {\n yield Err(\n new BCClientError('Received non array response from blind pagination page endpoint', {\n data,\n path,\n }),\n );\n }\n }\n }\n } while (!done);\n }\n\n /**\n * Executes multiple requests concurrently and returns all results as {@link Result} values,\n * never throwing. Errors from individual requests are captured as `Err` results.\n *\n * Use {@link batchStream} to process results as they arrive rather than waiting for all.\n *\n * @param requests - Array of request descriptors built with the {@link req} helpers.\n * @param options.concurrency - Max concurrent requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n *\n * @returns Results in the order requests complete (not necessarily input order).\n */\n async batchSafe<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n requests: BatchRequestOptions<TBody, TRes, TQuery>[],\n options?: ConcurrencyOptions,\n ): Promise<Result<TRes, BaseError>[]> {\n const results: Result<TRes, BaseError>[] = [];\n\n for await (const res of this.batchStream(requests, options)) {\n results.push(res);\n }\n\n return results;\n }\n\n /**\n * Streams all items from a v3 paginated endpoint, fetching the first page sequentially\n * and remaining pages concurrently via {@link batchStream}.\n *\n * Each yielded value is a {@link Result} — check `err` before using `data`. Use\n * {@link collect} to gather all items into an array.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0). If the API\n * enforces a different limit, the actual `per_page` from the first response is used for\n * subsequent pages.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent page requests for pages after the first.\n * Must be 1–1000. `false` for sequential. Defaults to `config.concurrency`, or 10 if not\n * set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @throws {@link BCPaginatedOptionError} if `query.limit` or `query.page` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n */\n async *stream<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: CollectOptions<TItem, TQuery>,\n ): AsyncGenerator<Result<TItem, BaseError>> {\n const { query, querySchema, itemSchema, ...requestOptions } = options ?? {};\n\n let limit = this.validatePaginationOption(path, 'limit', query?.limit ?? DEFAULT_LIMIT);\n const page = this.validatePaginationOption(path, 'page', query?.page ?? 1);\n\n const validatedQuery = await this.validate(\n query,\n querySchema,\n BCQueryValidationError,\n 'GET',\n path,\n 'Invalid query parameters',\n );\n\n let firstPageMeta: V3Resource<unknown[]>['meta'];\n\n try {\n const firstPage = await this.get(path, {\n ...requestOptions,\n query: {\n ...validatedQuery,\n page,\n limit,\n } as unknown as TQuery,\n });\n\n const { data, meta } = this.assertPaginatedResponse(path, firstPage);\n\n firstPageMeta = meta;\n\n // Validate and return the first page\n for (const item of data) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } catch (err) {\n if (err instanceof BaseError) {\n yield Err(err);\n } else {\n yield Err(new BCClientError('Unknown error occurred fetching first page', {}, { cause: err }));\n }\n\n return;\n }\n\n const { total_pages, per_page } = firstPageMeta.pagination;\n\n if (limit !== per_page) {\n this.logger?.warn({ limit, actual: per_page }, 'API enforces alternate limit on this endpoint');\n limit = per_page;\n }\n\n // Fetch other pages using batchStream\n const requests = Array.from({ length: total_pages - page }, (_, i) => i + page + 1).map((page) => ({\n method: 'GET' as const,\n path,\n ...requestOptions,\n query: {\n ...validatedQuery,\n limit,\n page,\n },\n }));\n\n for await (const pageRes of requests.length > 0 ? this.batchStream(requests, options) : []) {\n const { data: page, err } = pageRes;\n\n if (err) {\n yield Err(err);\n continue;\n }\n\n try {\n const { data } = this.assertPaginatedResponse(path, page);\n\n for (const item of data) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } catch (err) {\n if (err instanceof BaseError) {\n yield Err(err);\n } else {\n yield Err(new BCClientError('Unknown error occured processing page', {}, { cause: err }));\n }\n }\n }\n }\n\n /**\n * Executes multiple requests with configurable concurrency, yielding each result as a\n * {@link Result} as it completes. Errors from individual requests are yielded as `Err`\n * results rather than thrown.\n *\n * Automatically adjusts concurrency up/down in response to rate-limit and error responses.\n * Use {@link batchSafe} to collect all results into an array.\n *\n * **Caution:** the generator is making requests concurrently. As a consequence if a\n * request is mutating the remote (POST, DELETE) and `for await` loop is exited early,\n * the in-flight request may or may not commit the mutation, and the results of\n * these request WILL NOT be yielded. If you do intent to break the loop early and want to\n * get all the results, set `concurrency: false` to trade concurrency for deterministic behavior.\n *\n * @param requests - Array of request descriptors built with the {@link req} helpers.\n * @param options.concurrency - Max concurrent requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n */\n async *batchStream<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n requests: BatchRequestOptions<TBody, TRes, TQuery>[],\n options?: ConcurrencyOptions,\n ): AsyncGenerator<Result<TRes, BaseError>> {\n const resolved = this.resolveStreamOptions(options);\n\n if (resolved.concurrency) {\n const limit = pLimit({ concurrency: resolved.concurrency, rejectOnClear: true });\n const client = this.makeStreamClient(limit, resolved);\n const channel = new AsyncChannel<Result<TRes, BaseError>>();\n\n try {\n Promise.all(\n requests.map((req) =>\n limit(() =>\n this.request(req.path, req, client).then(\n (val) => channel.push(Ok(val)),\n (err) => channel.push(Err(err)),\n ),\n ),\n ),\n )\n .catch((err) => this.logger?.warn({ err }, 'In-flight concurrent requests aborted'))\n .finally(() => channel.close());\n\n for await (const item of channel) {\n yield item;\n }\n } finally {\n limit.clearQueue();\n }\n } else {\n for (const request of requests) {\n try {\n const res = await this.request(request.path, request);\n\n yield Ok(res);\n } catch (err) {\n if (err instanceof BaseError) {\n yield Err(err);\n } else {\n yield Err(new BCClientError('Unknown error in batchStream', {}, { cause: err }));\n }\n }\n }\n }\n }\n\n private async validatePaginatedItem<TItem>(\n path: string,\n item: unknown,\n schema?: StandardSchemaV1<TItem>,\n ): Promise<Result<TItem, BaseError>> {\n if (!schema) {\n return Ok(item as TItem);\n }\n\n const result = await schema['~standard'].validate(item);\n\n if (result.issues) {\n return Err(new BCPaginatedItemValidationError('Page item validation failed', 'GET', path, item, result));\n } else {\n return Ok(result.value);\n }\n }\n\n private assertPaginatedResponse(path: string, res: unknown): V3Resource<unknown[]> {\n if (typeof res !== 'object' || res === null) {\n throw new BCPaginatedResponseError(path, res, 'Response is invalid');\n }\n\n if (!('data' in res) || !Array.isArray(res.data)) {\n throw new BCPaginatedResponseError(\n path,\n res,\n 'response.data must be an array, ensure this endpoint returns a v3 collection',\n );\n }\n\n if (!('meta' in res) || typeof res.meta !== 'object' || res.meta === null || !('pagination' in res.meta)) {\n throw new BCPaginatedResponseError(path, res, 'response.meta is invalid unable to paginate');\n }\n\n const pagination = res.meta.pagination;\n\n if (typeof pagination !== 'object' || pagination === null) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination is invalid unable to paginate');\n }\n\n const requiredFields: Array<[string, (v: unknown) => boolean]> = [\n ['per_page', (v) => typeof v === 'number' && v > 0],\n ['total_pages', (v) => typeof v === 'number' && v >= 0],\n ];\n\n for (const [field, isValid] of requiredFields) {\n if (!(field in pagination) || !isValid(pagination[field as keyof typeof pagination])) {\n throw new BCPaginatedResponseError(\n path,\n res,\n `response.meta.pagination.${field} is missing or invalid`,\n );\n }\n }\n\n const { links } = pagination as { links?: unknown };\n\n if (typeof links !== 'object' || links === null) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination.links is missing or invalid');\n }\n\n const isNullableString = (v: unknown) => v === null || typeof v === 'string';\n\n if (!('current' in links) || typeof links.current !== 'string') {\n throw new BCPaginatedResponseError(\n path,\n res,\n 'response.meta.pagination.links.current is missing or invalid',\n );\n }\n\n if ('next' in links && !isNullableString(links.next)) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination.links.next is invalid');\n }\n\n if ('previous' in links && !isNullableString(links.previous)) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination.links.previous is invalid');\n }\n\n return res as V3Resource<unknown[]>;\n }\n\n private validatePaginationOption(path: string, key: string, value: unknown): number {\n if (typeof value !== 'number' || value <= 0) {\n throw new BCPaginatedOptionError(path, value, key);\n }\n\n return value;\n }\n\n private resolveStreamOptions(options?: ConcurrencyOptions): ResolvedConcurrencyOptions {\n return {\n concurrency: options?.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY,\n rateLimitBackoff: options?.rateLimitBackoff ?? this.config.rateLimitBackoff ?? DEFAULT_RATE_LIMIT_BACKOFF,\n backoff: options?.backoff ?? this.config.backoff ?? DEFAULT_BACKOFF_RATE,\n backoffRecover: options?.backoffRecover ?? this.config.backoffRecover ?? DEFAULT_BACKOFF_RECOVER,\n };\n }\n\n private makeStreamClient(limit: LimitFunction, options: ResolvedConcurrencyOptions): KyInstance {\n const { concurrency, rateLimitBackoff, backoff, backoffRecover } = options;\n\n if (concurrency === false) {\n return this.client;\n }\n\n return this.client.extend({\n hooks: {\n beforeRetry: [\n ({ error }) => {\n if (!isHTTPError(error)) {\n return;\n }\n\n const previousConcurrency = limit.concurrency;\n\n if (error.response.status === 429) {\n limit.concurrency = rateLimitBackoff;\n\n this.logger?.warn(\n { previousConcurrency, newConcurrency: limit.concurrency },\n 'Rate limit reached, limiting concurrency',\n );\n } else {\n const rate =\n typeof backoff === 'function'\n ? backoff(limit.concurrency, error.response.status)\n : backoff;\n\n limit.concurrency = Math.ceil(limit.concurrency / rate);\n\n this.logger?.warn(\n { previousConcurrency, newConcurrency: limit.concurrency },\n 'Intermittent errors, limiting concurrency to compensate',\n );\n }\n },\n ],\n afterResponse: [\n (_request, _options, response) => {\n if (response.ok && limit.concurrency < concurrency) {\n const recover =\n typeof backoffRecover === 'function'\n ? backoffRecover(limit.concurrency)\n : backoffRecover;\n\n limit.concurrency = Math.min(concurrency, limit.concurrency + recover);\n }\n },\n ],\n },\n });\n }\n\n private async request<TBody, TRes, TQuery extends Query = Query>(\n _path: string,\n options: RequestOptions<TBody, TRes, TQuery>,\n client?: KyInstance,\n ) {\n const { version, query, body, bodySchema, querySchema, responseSchema, ...kyOptions } = options;\n\n const path = this.makePath(options.version ?? 'v3', _path);\n const validQuery = await this.validate(\n query,\n querySchema,\n BCQueryValidationError,\n options.method,\n path,\n 'Invalid query parameters',\n );\n const validBody = await this.validate(\n body,\n bodySchema,\n BCRequestBodyValidationError,\n options.method,\n path,\n `Invalid ${options.method} request body`,\n );\n\n let response: KyResponse;\n\n try {\n response = await (client ?? this.client)(path, {\n ...kyOptions,\n method: options.method,\n searchParams: toUrlSearchParams(validQuery),\n json: validBody,\n });\n } catch (err) {\n if (err instanceof BaseError) {\n throw err;\n }\n\n if (isHTTPError(err)) {\n const requestBody = await err.request.text().catch(() => '');\n const responseBody = await err.response.text().catch(() => '');\n const error = new BCApiError(err, requestBody, responseBody);\n\n this.logger?.error(error.context, 'Request failed');\n\n throw error;\n }\n\n if (isTimeoutError(err)) {\n const error = new BCTimeoutError(err);\n\n this.logger?.error(error.context, 'Request timed out');\n\n throw error;\n }\n\n if (isKyError(err)) {\n throw new BCClientError('Client error', undefined, err);\n }\n\n throw new BCClientError('Unknown error', undefined, err);\n }\n\n let text: string;\n\n try {\n text = await response.text();\n } catch (err) {\n throw new BCResponseParseError(options.method, path, response.status, err, query, '');\n }\n\n let res: TRes;\n\n try {\n res = JSON.parse(text);\n } catch (err) {\n throw new BCResponseParseError(options.method, path, response.status, err, query, text);\n }\n\n this.logger?.debug(\n { method: options.method, url: response.url, status: response.status },\n 'Successful request',\n );\n\n return this.validate(\n res,\n responseSchema,\n BCResponseValidationError,\n options.method,\n path,\n 'Invalid API response',\n );\n }\n\n private async validate<T>(\n data: unknown,\n schema: StandardSchemaV1<T> | undefined,\n ErrorClass: new (\n message: string,\n method: string,\n path: string,\n data: unknown,\n error: StandardSchemaV1.FailureResult,\n ) => BCSchemaValidationError,\n method: string,\n path: string,\n message?: string,\n ): Promise<T> {\n if (!schema) {\n return data as T;\n }\n\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n throw new ErrorClass(message ?? 'Validation failed', method, path, data, result);\n }\n\n return result.value;\n }\n\n private makePath(version: ApiVersion, route: string): string {\n return `stores/${this.storeHash}/${version}/${route.replace(LEADING_SLASHES, '')}`;\n }\n\n private validateConfig() {\n const { accessToken, storeHash } = this.config;\n const errors: string[] = [];\n\n // Using reasonable assumptions about these credentials for validation.\n // This will not verify the credentials but at least guard against providing\n // something completely invalid like empty string\n if (typeof storeHash !== 'string' || storeHash.length <= 0) {\n errors.push('storeHash is empty');\n }\n\n if (typeof accessToken !== 'string' || accessToken.length <= 0) {\n errors.push('accessToken is empty');\n }\n\n if (errors.length > 0) {\n throw new BCCredentialsError(errors);\n }\n\n if (this.config.prefixUrl) {\n try {\n new URL(this.config.prefixUrl);\n } catch (err) {\n throw new BCClientError('Invalid prefixUrl', undefined, err);\n }\n }\n\n this.validateConcurrency(this.config.concurrency);\n }\n\n private validateConcurrency(concurrency: number | undefined | false) {\n if (concurrency === undefined) {\n return;\n }\n\n if (concurrency === false) {\n return concurrency;\n }\n\n if (concurrency <= 0 || concurrency > MAX_CONCURRENCY) {\n throw new BCClientError(`Invalid concurrency: allowed range (1:${MAX_CONCURRENCY})`, undefined);\n }\n\n return concurrency;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/common.ts","../src/lib/errors.ts","../src/lib/logger.ts","../src/auth.ts","../src/lib/util.ts","../src/lib/hooks.ts","../src/lib/request.ts","../src/lib/result.ts","../src/client.ts"],"sourcesContent":["import type { Options as KyOptions } from 'ky';\nimport type { LimitFunction } from 'p-limit';\nimport type { Logger, LogLevel } from './logger';\n\nexport type { Logger, LogLevel };\n\nexport type ConcurrencyOptions = {\n /** Max concurrent requests. Must be 1–1000. `false` for sequential. Defaults to 10. */\n concurrency?: number | false;\n /**\n * Divisor (or `(concurrency, status) => number` function) applied to concurrency on\n * non-429 error responses. Defaults to 2.\n */\n backoff?: ((concurrency: number, status: number) => number) | number;\n /** Concurrency cap applied when a 429 response is received. Defaults to 1. */\n rateLimitBackoff?: number;\n /**\n * Amount (or `(concurrency) => number` function) added to concurrency per successful\n * response while below the configured max. Defaults to 1.\n */\n backoffRecover?: ((concurrency: number) => number) | number;\n /**\n * A p-limit instance to reuse across calls. When provided, `batchStream` uses it instead of\n * creating a new one, allowing callers to observe and react to live concurrency changes.\n */\n pLimit?: LimitFunction;\n};\n\n/** Maximum allowed concurrency value. */\nexport const MAX_CONCURRENCY = 1000;\n/** Default concurrency for batch/stream operations. */\nexport const DEFAULT_CONCURRENCY = 10;\n/** Default concurrency cap on 429 rate-limit responses. */\nexport const DEFAULT_RATE_LIMIT_BACKOFF = 1;\n/** Default divisor applied to concurrency on non-429 errors. */\nexport const DEFAULT_BACKOFF_RATE = 2;\n/** Default amount added to concurrency per successful response. */\nexport const DEFAULT_BACKOFF_RECOVER = 1;\n/** Default page size for paginated requests. */\nexport const DEFAULT_LIMIT = 250;\n/** Maximum allowed URL length before chunking is required. */\nexport const MAX_URL_LENGTH = 2048;\n/** Regex to strip leading slashes from API paths. */\nexport const LEADING_SLASHES = /^\\/+/;\n/** Maximum pages to fetch during blind pagination **/\nexport const DEFAULT_MAX_BLIND_PAGES = 500;\n\n/**\n * Configuration options for the BigCommerce client.\n */\nexport interface ClientConfig\n extends Omit<KyOptions, 'throwHttpErrors' | 'parseJson' | 'method' | 'body' | 'json' | 'searchParams'>,\n ConcurrencyOptions {\n storeHash: string;\n accessToken: string;\n logger?: Logger | LogLevel | boolean;\n}\n\n/**\n * Random positive jitter within 0-500 ms in increments of 100\n * @param {number} delay\n */\nexport const rateLimitJitter = (delay: number) => delay + Math.floor(Math.random() * 6) * 100;\n\n/**\n * HTTP header names used by the BigCommerce API.\n */\nexport const HEADERS = {\n AUTH_TOKEN: 'X-Auth-Token',\n ACCEPT: 'Accept',\n CONTENT_TYPE: 'Content-Type',\n RATE_LIMIT_LEFT: 'x-rate-limit-requests-left',\n RATE_LIMIT_RESET: 'x-rate-limit-time-reset-ms',\n RATE_LIMIT_QUOTA: 'x-rate-limit-requests-quota',\n RATE_LIMIT_WINDOW: 'x-rate-limit-time-window-ms',\n} as const;\n\n/**\n * Metadata extracted from rate-limit headers in API responses.\n */\nexport type RateLimitMeta = {\n /** Time in milliseconds until the rate limit resets. */\n resetIn: number;\n /** Number of requests remaining in the current window. */\n requestsLeft?: number;\n /** Total request quota for the current window. */\n quota?: number;\n /** Time window size in milliseconds. */\n window?: number;\n};\n\n/**\n * Default configuration for the underlying ky HTTP client.\n */\nexport const BASE_KY_CONFIG = {\n prefixUrl: 'https://api.bigcommerce.com',\n throwHttpErrors: true,\n // Some BC endpoints may take a while.\n // For example /catalog/product/options* endpoints may fully\n // recreate all variants in some cases\n timeout: 120e3,\n\n retry: {\n limit: 3,\n // BC uses PUT for many upsert operations, it's not guaranteed to be idempotent\n methods: ['GET', 'DELETE'],\n statusCodes: [429, 500, 502, 503, 504],\n // BC does not send standart Retry-After. We'll use custom beforeRetry hook\n afterStatusCodes: [],\n jitter: true,\n maxRetryAfter: 120e3,\n },\n\n headers: {\n [HEADERS.ACCEPT]: 'application/json',\n [HEADERS.CONTENT_TYPE]: 'application/json',\n },\n};\n\n/**\n * Concurrency options with all values resolved to their defaults.\n */\nexport type ResolvedConcurrencyOptions = Required<Omit<ConcurrencyOptions, 'pLimit'>> &\n Pick<ConcurrencyOptions, 'pLimit'>;\n","import type { HTTPError, KyRequest, TimeoutError as KyTimeoutError } from 'ky';\nimport type { Query } from './request';\nimport type { StandardSchemaV1 } from './standard-schema';\n\nexport type ErrorContext = Record<string, unknown>;\n\n/**\n * Abstract base class for all library errors. Carries a typed `context` object with\n * structured diagnostic data and a machine-readable `code` string.\n *\n * Use `instanceof` checks against specific subclasses rather than this base class.\n */\nexport abstract class BaseError<TContext extends ErrorContext = ErrorContext> extends Error {\n /** Machine-readable error code. Unique per subclass. */\n abstract readonly code: string;\n\n constructor(\n message: string,\n readonly context: TContext,\n options?: ErrorOptions,\n ) {\n super(message, options);\n\n this.name = this.constructor.name;\n }\n\n /** @internal */\n toJSON() {\n return {\n name: this.name,\n code: this.code,\n message: this.message,\n context: this.context,\n cause: this.cause,\n };\n }\n}\n\n/** Catch-all for unexpected client-side errors not covered by a more specific subclass. */\nexport class BCClientError extends BaseError<Record<string, unknown>> {\n code = 'BC_CLIENT_ERROR';\n\n constructor(message: string, context?: Record<string, unknown>, cause?: unknown) {\n super(message, context ?? {}, { cause });\n }\n}\n\n/** Thrown by the {@link BigCommerceClient} constructor when credentials or config are invalid. */\nexport class BCCredentialsError extends BaseError<{\n errors: string[];\n}> {\n code = 'BC_CLIENT_CREDENTIALS_ERROR';\n\n constructor(errors: string[]) {\n super('Failed to initialize BigCommerceClient', { errors });\n }\n}\n\n/** Thrown before a request is sent when the constructed URL exceeds 2048 characters. */\nexport class BCUrlTooLongError extends BaseError<{\n url: string;\n max: number;\n len: number;\n}> {\n code = 'BC_URL_TOO_LONG';\n\n constructor(url: string, max: number) {\n super(`Url length (${url.length}) exceeds max allowed length of ${max}`, { url, max, len: url.length });\n }\n}\n\n/**\n * Thrown during retry when a 429 response is received but the expected\n * `X-Rate-Limit-*` headers are absent, making it impossible to determine the backoff delay.\n */\nexport class BCRateLimitNoHeadersError extends BaseError<{\n url: string;\n method: string;\n attempts: number;\n}> {\n code = 'BC_RATE_LIMIT_NO_HEADERS';\n\n constructor(request: KyRequest, attempts: number) {\n super('Rate limit reached but the X-Rate-Limit-* headers were not returned. Unable to retry', {\n url: request.url,\n method: request.method,\n attempts,\n });\n }\n}\n\n/**\n * Thrown during retry when a 429 response specifies a reset window that exceeds\n * `config.retry.maxRetryAfter`, preventing an unbounded wait.\n */\nexport class BCRateLimitDelayTooLongError extends BaseError<{\n url: string;\n method: string;\n attempts: number;\n maxDelay: number;\n delay: number;\n}> {\n code = 'BC_RATE_LIMIT_DELAY_TOO_LONG';\n\n constructor(request: KyRequest, attempts: number, maxDelay: number, delay: number) {\n super('Rate limit reached, and the rate limit reset window is too high.', {\n url: request.url,\n method: request.method,\n attempts,\n maxDelay,\n delay,\n });\n }\n}\n\n/**\n * Abstract base for all StandardSchema validation errors. Carries the raw `data` that failed\n * validation and the schema `error` result. Use specific subclasses for `instanceof` checks.\n */\nexport abstract class BCSchemaValidationError extends BaseError<{\n method: string;\n path: string;\n data: unknown;\n error: StandardSchemaV1.FailureResult;\n}> {\n constructor(message: string, method: string, path: string, data: unknown, error: StandardSchemaV1.FailureResult) {\n super(message, { method, path, data, error });\n }\n}\n\n/** Thrown when `options.querySchema` validation fails before a request is sent. */\nexport class BCQueryValidationError extends BCSchemaValidationError {\n code = 'BC_QUERY_VALIDATION_FAILED';\n}\n\n/** Thrown when `options.bodySchema` validation fails before a request is sent. */\nexport class BCRequestBodyValidationError extends BCSchemaValidationError {\n code = 'BC_REQUEST_BODY_VALIDATION_FAILED';\n}\n\n/** Thrown when `options.responseSchema` validation fails after a response is received. */\nexport class BCResponseValidationError extends BCSchemaValidationError {\n code = 'BC_RESPONSE_VALIDATION_FAILED';\n}\n\n/** Thrown or yielded when `options.itemSchema` validation fails for an item in a page response. */\nexport class BCPaginatedItemValidationError extends BCSchemaValidationError {\n code = 'BC_PAGINATED_ITEM_VALIDATION_FAILED';\n}\n\n/**\n * Thrown when the BigCommerce API returns a non-2xx HTTP response.\n * `context.status` and `context.responseBody` are the most useful fields for debugging.\n */\nexport class BCApiError extends BaseError<{\n method: string;\n url: string;\n status: number;\n statusMessage: string;\n headers: Record<string, string>;\n requestBody: string;\n responseBody: string;\n}> {\n code = 'BC_API_ERROR';\n\n constructor(err: HTTPError, requestBody: string, responseBody: string) {\n const { request, response } = err;\n\n super('BigCommerce API request failed', {\n method: request.method,\n url: request.url,\n status: response.status,\n statusMessage: response.statusText,\n headers: Object.fromEntries(response.headers as unknown as Iterable<[string, string]>),\n requestBody,\n responseBody,\n });\n }\n}\n\n/** Thrown when a request exceeds the configured timeout (default 120 s). */\nexport class BCTimeoutError extends BaseError<{\n method: string;\n url: string;\n}> {\n code = 'BC_TIMEOUT_ERROR';\n\n constructor(err: KyTimeoutError) {\n super('BigCommerce API request timed out', {\n method: err.request.method,\n url: err.request.url,\n });\n }\n}\n\n/**\n * Thrown when the response body cannot be read or parsed as JSON.\n * `context.rawBody` contains the raw text that failed to parse (empty string if the body was empty).\n */\nexport class BCResponseParseError extends BaseError<{\n method: string;\n status: number;\n path: string;\n query?: Query;\n rawBody?: string;\n}> {\n code = 'BC_RESPONSE_PARSE_ERROR';\n\n constructor(method: string, path: string, status: number, cause: unknown, query?: Query, rawBody?: string) {\n super(\n 'Failed to parse BigCommerce API response',\n {\n status,\n method,\n path,\n query,\n rawBody,\n },\n { cause },\n );\n }\n}\n\n/**\n * Thrown when a pagination option (`limit`, `page`, or `count`) is not a positive number.\n * `context.option` names the offending field; `context.value` is the value that was passed.\n */\nexport class BCPaginatedOptionError extends BaseError<{ path: string; option: string; value: unknown }> {\n code = 'BC_PAGINATED_OPTION_ERROR';\n\n constructor(path: string, value: unknown, option: string) {\n super('The pagination option must be a positive number', { path, option, value });\n }\n}\n\n/**\n * Thrown or yielded when a paginated response is missing required v3 envelope fields\n * (`data`, `meta.pagination`, etc.). Usually means the path is not a v3 collection endpoint.\n */\nexport class BCPaginatedResponseError extends BaseError<{ path: string; data: unknown; reason: string }> {\n code = 'BC_PAGINATED_RESPONSE_ERROR';\n\n constructor(path: string, data: unknown, reason: string) {\n super('Paginated response structure is invalid', { path, data, reason });\n }\n}\n\n/** Thrown by {@link BigCommerceAuth} constructor when `config.redirectUri` is not a valid URL. */\nexport class BCAuthInvalidRedirectUriError extends BaseError<{ redirectUri: string }> {\n code = 'BC_AUTH_INVALID_REDIRECT_URI';\n\n constructor(redirectUri: string, cause: unknown) {\n super('Invalid redirect URI', { redirectUri }, { cause });\n }\n}\n\n/** Thrown by {@link BigCommerceAuth.requestToken} when a required OAuth callback param is absent. */\nexport class BCAuthMissingParamError extends BaseError<{ param: string }> {\n code = 'BC_AUTH_MISSING_PARAM';\n\n constructor(param: string) {\n super(`Missing required auth callback parameter: ${param}`, { param });\n }\n}\n\n/**\n * Thrown by {@link BigCommerceAuth.requestToken} when the scopes granted by BigCommerce\n * do not include all scopes listed in `config.scopes`.\n * `context.missing` lists the scopes that were expected but not granted.\n */\nexport class BCAuthScopeMismatchError extends BaseError<{\n granted: string[];\n expected: string[];\n missing: string[];\n}> {\n code = 'BC_AUTH_SCOPE_MISMATCH';\n\n constructor(granted: string[], expected: string[], missing: string[]) {\n super('Granted scopes do not match expected scopes', { granted, expected, missing });\n }\n}\n\n/** Thrown by {@link BigCommerceAuth.verify} when the JWT signature, audience, issuer, or subject is invalid. */\nexport class BCAuthInvalidJwtError extends BaseError<{ storeHash: string }> {\n code = 'BC_AUTH_INVALID_JWT';\n\n constructor(storeHash: string, cause: unknown) {\n super('Invalid JWT payload', { storeHash }, { cause });\n }\n}\n","import type { ClientConfig } from './common';\n\n/**\n * Logging interface for the BigCommerce client.\n *\n * Implement this interface to provide custom logging. The client passes context data\n * as the first argument, making it compatible with structured loggers.\n */\nexport interface Logger {\n debug(data: Record<string, unknown>, message?: string): void;\n info(data: Record<string, unknown>, message?: string): void;\n warn(data: Record<string, unknown>, message?: string): void;\n error(data: Record<string, unknown>, message?: string): void;\n}\n\nexport type PowertoolsLikeLogger = {\n debug(message: string, ...data: Record<string, unknown>[]): void;\n info(message: string, ...data: Record<string, unknown>[]): void;\n warn(message: string, ...data: Record<string, unknown>[]): void;\n error(message: string, ...data: Record<string, unknown>[]): void;\n};\n\n/** @internal */\nexport const LOG_LEVELS = ['debug', 'info', 'warn', 'error'] as const;\n\n/** Supported log levels. */\nexport type LogLevel = (typeof LOG_LEVELS)[number];\n\n/**\n * Adapts an AWS Lambda Powertools logger to the {@link Logger} interface expected by\n * {@link BigCommerceClient} and {@link BigCommerceAuth}.\n *\n * Powertools loggers use `(message, ...data)` argument order whereas this library uses\n * `(data, message)`. This adapter swaps the arguments.\n *\n * @param logger - An AWS Lambda Powertools (or any {@link PowertoolsLikeLogger}-compatible) logger.\n * @returns A {@link Logger} wrapper suitable for `config.logger`.\n */\nexport const fromAwsPowertoolsLogger = (logger: PowertoolsLikeLogger): Logger => ({\n debug: (data, message) => logger.debug(message ?? '', data),\n info: (data, message) => logger.info(message ?? '', data),\n warn: (data, message) => logger.warn(message ?? '', data),\n error: (data, message) => logger.error(message ?? '', data),\n});\n\n/**\n * Console-based {@link Logger} that filters messages below a minimum level.\n *\n * Used automatically when `config.logger` is `true`, `undefined`, or a {@link LogLevel} string.\n * Can also be instantiated directly for custom log level control.\n *\n * @example\n * ```ts\n * new BigCommerceClient({ ..., logger: new FallbackLogger('debug') });\n * ```\n */\nexport class FallbackLogger implements Logger {\n /**\n * @param level - Minimum level to output. Messages below this level are silently dropped.\n */\n constructor(public readonly level: LogLevel) {}\n\n debug(data: Record<string, unknown>, message?: string): void {\n this.log('debug', data, message);\n }\n\n info(data: Record<string, unknown>, message?: string): void {\n this.log('info', data, message);\n }\n\n warn(data: Record<string, unknown>, message?: string): void {\n this.log('warn', data, message);\n }\n\n error(data: Record<string, unknown>, message?: string): void {\n this.log('error', data, message);\n }\n\n private log(level: LogLevel, data: Record<string, unknown>, message?: string) {\n if (LOG_LEVELS.indexOf(level) < LOG_LEVELS.indexOf(this.level)) {\n return;\n }\n\n const fn = console[level];\n\n message !== undefined ? fn(message, data) : fn(data);\n }\n}\n\n/**\n * @internal\n */\nexport const initLogger = (logger: ClientConfig['logger']): Logger | undefined => {\n if (logger === false) {\n return;\n }\n\n if (logger === undefined || logger === true) {\n return new FallbackLogger('info');\n }\n\n if (typeof logger === 'string') {\n if (LOG_LEVELS.includes(logger)) {\n return new FallbackLogger(logger);\n } else {\n const logger = new FallbackLogger('info');\n\n logger.warn({ level: logger }, 'Unknown log level passed, using info');\n\n return logger;\n }\n }\n\n return logger;\n};\n","import * as jose from 'jose';\nimport ky, { isHTTPError, isTimeoutError } from 'ky';\nimport { BASE_KY_CONFIG } from './lib/common';\nimport {\n BCApiError,\n BCAuthInvalidJwtError,\n BCAuthInvalidRedirectUriError,\n BCAuthMissingParamError,\n BCAuthScopeMismatchError,\n BCClientError,\n BCTimeoutError,\n} from './lib/errors';\nimport { initLogger, type Logger, type LogLevel } from './lib/logger';\n\n/**\n * Configuration options for BigCommerce authentication\n */\nexport type BigCommerceAuthConfig = {\n /** The OAuth client ID from BigCommerce */\n clientId: string;\n /** The OAuth client secret from BigCommerce */\n secret: string;\n /** The redirect URI registered with BigCommerce */\n redirectUri: string;\n /** Optional array of scopes to validate during auth callback */\n scopes?: string[];\n /** Optional logger instance */\n logger?: Logger | LogLevel | boolean;\n};\n\nconst GRANT_TYPE = 'authorization_code';\nconst TOKEN_ENDPOINT = 'https://login.bigcommerce.com/oauth2/token';\nconst ISSUER = 'bc';\n\n/**\n * Query parameters received from BigCommerce auth callback\n */\nexport type BigCommerceAuthQuery = {\n /** The authorization code from BigCommerce */\n code: string;\n /** The granted OAuth scopes */\n scope: string;\n /** The store context */\n context: string;\n};\n\n/**\n * Request payload for token endpoint\n */\ntype TokenRequest = {\n client_id: string;\n client_secret: string;\n code: string;\n context: string;\n scope: string;\n grant_type: typeof GRANT_TYPE;\n redirect_uri: string;\n};\n\n/**\n * User information returned from BigCommerce\n */\nexport type User = {\n /** The user's ID */\n id: number;\n /** The user's username */\n username: string;\n /** The user's email address */\n email: string;\n};\n\n/**\n * Response from BigCommerce token endpoint\n */\nexport type TokenResponse = {\n /** The OAuth access token */\n access_token: string;\n /** The granted OAuth scopes */\n scope: string;\n /** Information about the authenticated user */\n user: User;\n /** Information about the store owner */\n owner: User;\n /** The store context */\n context: string;\n /** The BigCommerce account UUID */\n account_uuid: string;\n};\n\n/**\n * JWT claims from BigCommerce\n */\nexport type Claims = {\n /** JWT audience */\n aud: string;\n /** JWT issuer */\n iss: string;\n /** JWT issued at timestamp */\n iat: number;\n /** JWT not before timestamp */\n nbf: number;\n /** JWT expiration timestamp */\n exp: number;\n /** JWT unique identifier */\n jti: string;\n /** JWT subject */\n sub: string;\n /** Information about the authenticated user */\n user: {\n id: number;\n email: string;\n locale: string;\n };\n /** Information about the store owner */\n owner: {\n id: number;\n email: string;\n };\n /** The store URL */\n url: string;\n /** The channel ID (if applicable) */\n channel_id: number | null;\n};\n\n/**\n * Handles authentication with BigCommerce OAuth\n */\nexport class BigCommerceAuth {\n private readonly logger: Logger | undefined;\n private readonly client: ReturnType<typeof ky.create>;\n\n /**\n * Creates a new BigCommerceAuth instance for handling OAuth authentication\n * @param config - Configuration options for BigCommerce authentication\n * @param config.clientId - The OAuth client ID from BigCommerce\n * @param config.secret - The OAuth client secret from BigCommerce\n * @param config.redirectUri - The redirect URI registered with BigCommerce\n * @param config.scopes - Optional array of scopes to validate during auth callback\n * @param config.logger - Optional logger instance for debugging and error tracking\n * @throws {BCAuthInvalidRedirectUriError} If the redirect URI is invalid\n */\n constructor(private readonly config: BigCommerceAuthConfig) {\n try {\n new URL(this.config.redirectUri);\n } catch (error) {\n throw new BCAuthInvalidRedirectUriError(this.config.redirectUri, error);\n }\n\n this.logger = initLogger(config.logger);\n\n const { prefixUrl: _, ...authKyConfig } = BASE_KY_CONFIG;\n\n this.client = ky.create({\n ...authKyConfig,\n retry: {\n ...authKyConfig.retry,\n methods: ['POST'],\n },\n });\n }\n\n /**\n * Exchanges an OAuth authorization code for an access token.\n *\n * @param data - The auth callback payload: a raw query string, `URLSearchParams`, or a\n * pre-parsed object with `code`, `scope`, and `context`.\n * @returns The token response including `access_token`, `user`, and `context`.\n * @throws {@link BCAuthMissingParamError} if `code`, `scope`, or `context` are absent.\n * @throws {@link BCAuthScopeMismatchError} if the granted scopes don't include all `config.scopes`.\n * @throws {@link BCApiError} on HTTP error responses from the token endpoint.\n * @throws {@link BCTimeoutError} if the token request times out.\n * @throws {@link BCClientError} on any other error.\n */\n async requestToken(data: string | BigCommerceAuthQuery | URLSearchParams): Promise<TokenResponse> {\n const query = typeof data === 'string' || data instanceof URLSearchParams ? this.parseQueryString(data) : data;\n\n this.validateScopes(query.scope);\n\n const tokenRequest: TokenRequest = {\n client_id: this.config.clientId,\n client_secret: this.config.secret,\n ...query,\n grant_type: GRANT_TYPE,\n redirect_uri: this.config.redirectUri,\n };\n\n this.logger?.debug(\n {\n clientId: this.config.clientId,\n context: query.context,\n scopes: query.scope,\n },\n 'Requesting OAuth token',\n );\n\n let res: Response;\n\n try {\n res = await this.client(TOKEN_ENDPOINT, {\n method: 'POST',\n json: tokenRequest,\n });\n } catch (error) {\n if (isHTTPError(error)) {\n const requestBody = await error.request.text().catch(() => '');\n const responseBody = await error.response.text().catch(() => '');\n const err = new BCApiError(error, requestBody, responseBody);\n\n this.logger?.error(err.context, 'Failed to request token');\n\n throw err;\n }\n\n if (isTimeoutError(error)) {\n const err = new BCTimeoutError(error);\n\n this.logger?.error(err.context, 'Token request timed out');\n\n throw err;\n }\n\n throw new BCClientError('Failed to request token', {}, error);\n }\n\n return res.json() as Promise<TokenResponse>;\n }\n\n /**\n * Verifies a JWT payload from BigCommerce\n * @param jwtPayload - The JWT string to verify\n * @param storeHash - The store hash for the BigCommerce store\n * @returns Promise resolving to the verified JWT claims\n * @throws {BCAuthInvalidJwtError} If the JWT is invalid\n */\n async verify(jwtPayload: string, storeHash: string): Promise<Claims> {\n try {\n const secret = new TextEncoder().encode(this.config.secret);\n\n const { payload }: { payload: Claims } = await jose.jwtVerify(jwtPayload, secret, {\n audience: this.config.clientId,\n issuer: ISSUER,\n subject: `stores/${storeHash}`,\n });\n\n this.logger?.debug(\n {\n userId: payload.user?.id,\n storeHash: payload.sub.split('/')[1],\n },\n 'JWT verified successfully',\n );\n\n return payload;\n } catch (error) {\n const err = new BCAuthInvalidJwtError(storeHash, error);\n\n this.logger?.error(err.context, 'JWT verification failed');\n\n throw err;\n }\n }\n\n /**\n * Parses and validates a query string from BigCommerce auth callback\n * @param queryString - The query string to parse\n * @returns The parsed auth query parameters\n * @throws {BCAuthMissingParamError} If required parameters are missing\n */\n private parseQueryString(queryString: string | URLSearchParams): BigCommerceAuthQuery {\n const params = typeof queryString === 'string' ? new URLSearchParams(queryString) : queryString;\n\n const code = params.get('code');\n const scope = params.get('scope');\n const context = params.get('context');\n\n if (!code) {\n throw new BCAuthMissingParamError('code');\n }\n\n if (!scope) {\n throw new BCAuthMissingParamError('scope');\n } else if (this.config.scopes?.length) {\n this.validateScopes(scope);\n }\n\n if (!context) {\n throw new BCAuthMissingParamError('context');\n }\n\n return {\n code,\n scope,\n context,\n };\n }\n\n /**\n * Validates that the granted scopes match the expected scopes\n * @param scopes - Space-separated list of granted scopes\n * @throws {BCAuthScopeMismatchError} If the scopes don't match the expected scopes\n */\n private validateScopes(scopes: string) {\n if (!this.config.scopes) {\n return;\n }\n\n const granted = scopes.split(' ');\n const expected = this.config.scopes;\n const missing = expected.filter((scope) => !granted.includes(scope));\n\n if (missing.length) {\n throw new BCAuthScopeMismatchError(granted, expected, missing);\n }\n }\n}\n","import { HEADERS, type RateLimitMeta } from './common';\n\nexport function stripKeys<T extends object, K extends PropertyKey>(\n obj: T | undefined,\n keys: K[],\n): Omit<T, K> | undefined {\n if (!obj) {\n return obj;\n }\n\n const result = { ...obj } as Record<PropertyKey, unknown>;\n\n for (const key of keys) {\n delete result[key];\n }\n\n return result as Omit<T, K>;\n}\n\nconst parseIntHeader = (headers: Headers, key: string): number | undefined => {\n const value = Number.parseInt(headers.get(key) ?? '', 10);\n\n return Number.isNaN(value) ? undefined : value;\n};\n\nexport const extractRateLimitHeaders = (headers: Headers): RateLimitMeta | undefined => {\n const resetIn = parseIntHeader(headers, HEADERS.RATE_LIMIT_RESET);\n\n // Can't retry without this header - treat as unrecoverable\n if (resetIn === undefined) {\n return undefined;\n }\n\n return {\n resetIn,\n requestsLeft: parseIntHeader(headers, HEADERS.RATE_LIMIT_LEFT),\n quota: parseIntHeader(headers, HEADERS.RATE_LIMIT_QUOTA),\n window: parseIntHeader(headers, HEADERS.RATE_LIMIT_WINDOW),\n };\n};\n\nexport const chunkStrLength = (\n items: string[],\n options: {\n maxLength?: number;\n chunkLength?: number;\n offset?: number;\n separatorSize?: number;\n } = {},\n) => {\n const { maxLength = 2048, chunkLength = 250, offset = 0, separatorSize = 1 } = options;\n\n const chunks: string[][] = [];\n let currentStrLength = offset;\n let currentChunk: string[] = [];\n\n for (const item of items) {\n const itemLength = encodeURIComponent(item).length;\n const separatorLength = currentChunk.length > 0 ? separatorSize : 0;\n const totalItemLength = itemLength + separatorLength;\n\n const wouldExceedLength = currentStrLength + totalItemLength > maxLength;\n const wouldExceedCount = currentChunk.length >= chunkLength;\n\n if ((wouldExceedLength || wouldExceedCount) && currentChunk.length > 0) {\n chunks.push(currentChunk);\n currentChunk = [];\n currentStrLength = offset;\n }\n\n if (itemLength + offset > maxLength) {\n throw new Error(`Item too large: ${itemLength} exceeds maxLength ${maxLength}`);\n }\n\n currentChunk.push(item);\n currentStrLength += itemLength + (currentChunk.length > 1 ? separatorSize : 0);\n }\n\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n\n return chunks;\n};\n\nexport class AsyncChannel<T> {\n private readonly queue: T[] = [];\n private notify: (() => void) | null = null;\n private done = false;\n\n push(item: T) {\n this.queue.push(item);\n this.notify?.();\n this.notify = null;\n }\n\n close() {\n this.done = true;\n this.notify?.();\n this.notify = null;\n }\n\n async *[Symbol.asyncIterator](): AsyncGenerator<T> {\n while (!this.done || this.queue.length > 0) {\n if (this.queue.length === 0) {\n await new Promise<void>((r) => {\n if (this.queue.length > 0) {\n return r();\n }\n\n this.notify = r;\n });\n }\n\n while (this.queue.length > 0) {\n const item = this.queue.shift();\n\n if (item !== undefined) {\n yield item;\n }\n }\n }\n }\n}\n","import { type BeforeRequestHook, type BeforeRetryHook, isHTTPError } from 'ky';\nimport { MAX_URL_LENGTH, rateLimitJitter } from './common';\nimport { BCRateLimitDelayTooLongError, BCRateLimitNoHeadersError, BCUrlTooLongError } from './errors';\nimport type { Logger } from './logger';\nimport { extractRateLimitHeaders } from './util';\n\nexport const validateUrlLength: BeforeRequestHook = (request) => {\n if (request.url.length > MAX_URL_LENGTH) {\n throw new BCUrlTooLongError(request.url, MAX_URL_LENGTH);\n }\n};\n\nexport const bcRateLimitRetry =\n (logger?: Logger): BeforeRetryHook =>\n async ({ request, options, error, retryCount }) => {\n if (isHTTPError(error) && error.response.status === 429) {\n const retryMeta = extractRateLimitHeaders(error.response.headers);\n\n if (!retryMeta) {\n throw new BCRateLimitNoHeadersError(request, retryCount);\n }\n\n if (options.retry.maxRetryAfter && retryMeta.resetIn > options.retry.maxRetryAfter) {\n throw new BCRateLimitDelayTooLongError(\n request,\n retryCount,\n options.retry.maxRetryAfter,\n retryMeta.resetIn,\n );\n }\n\n const delay =\n typeof options.retry.jitter === 'function'\n ? options.retry.jitter(retryMeta.resetIn)\n : rateLimitJitter(retryMeta.resetIn);\n\n logger?.warn(\n { attempt: retryCount, url: request.url, method: request.method, retryMeta },\n `Rate limit reached, retrying in ${delay} (with jitter)`,\n );\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n } else {\n logger?.warn({ url: request.url, method: request.method, attempt: retryCount }, 'Retrying request');\n }\n };\n","import type { Options as KyOptions } from 'ky';\nimport type { ConcurrencyOptions } from './common';\nimport type { StandardSchemaV1 } from './standard-schema';\n\n/** BigCommerce API versions supported by the client. */\nexport type ApiVersion = 'v3' | 'v2';\n\n/** Valid query parameter value types. */\nexport type QueryValue = string | number | Array<string | number>;\n\n/** Query parameter object for API requests. */\nexport type Query = Record<string, QueryValue>;\n\n/**\n * Converts a Query object to URLSearchParams.\n * Array values are joined with commas (e.g., `id:in=1,2,3`).\n */\nexport const toUrlSearchParams = (query?: Query): URLSearchParams | undefined => {\n if (!query) {\n return;\n }\n\n const params = new URLSearchParams();\n\n for (const [key, value] of Object.entries(query)) {\n if (Array.isArray(value)) {\n params.append(key, value.map(String).join(','));\n } else {\n params.append(key, String(value));\n }\n }\n\n return params;\n};\n\n/** Supported HTTP methods for API requests. */\nexport type HttpMethod = 'POST' | 'GET' | 'PUT' | 'DELETE';\n\n/** @internal */\ntype BaseKyRequest = Omit<\n KyOptions,\n 'json' | 'method' | 'searchQueryParams' | 'body' | 'throwHttpErrors' | 'parseJson'\n>;\n\n/** @internal */\ntype QuerySchemaOptions<TQuery extends Query> =\n /** Query parameters to send with the request. */\n { query: TQuery; querySchema: StandardSchemaV1<TQuery> } | { query?: TQuery; querySchema?: never };\n\n/** @internal */\ntype BodySchemaOptions<TBody> =\n /** Request body, serialized as JSON. */\n { body: TBody; bodySchema: StandardSchemaV1<TBody> } | { body?: TBody; bodySchema?: never };\n\n/**\n * Full request options for direct API calls.\n * @see {@link GetOptions}, {@link PostOptions}, {@link PutOptions}, {@link DeleteOptions}\n */\nexport type RequestOptions<TBody, TRes, TQuery extends Query> = BaseKyRequest &\n QuerySchemaOptions<TQuery> &\n BodySchemaOptions<TBody> & {\n /** HTTP method for the request. */\n method: HttpMethod;\n /** API version to use. Defaults to `'v3'`. */\n version?: ApiVersion;\n /** Schema to validate the response body. */\n responseSchema?: StandardSchemaV1<TRes>;\n };\n\n/** Options for GET requests. */\nexport type GetOptions<TRes, TQuery extends Query> = Omit<\n RequestOptions<never, TRes, TQuery>,\n 'body' | 'bodySchema' | 'method'\n>;\n\n/** Options for POST requests. */\nexport type PostOptions<TBody, TRes, TQuery extends Query> = Omit<RequestOptions<TBody, TRes, TQuery>, 'method'>;\n\n/** Options for PUT requests. */\nexport type PutOptions<TBody, TRes, TQuery extends Query> = PostOptions<TBody, TRes, TQuery>;\n\n/** Options for DELETE requests. */\nexport type DeleteOptions<TQuery extends Query> = Omit<\n RequestOptions<never, never, TQuery>,\n 'body' | 'bodySchema' | 'method' | 'responseSchema'\n>;\n\n/**\n * Request descriptor for batch operations.\n * Use the {@link req} helpers to construct these.\n */\nexport type BatchRequestOptions<TBody, TRes, TQuery extends Query> = {\n path: string;\n} & RequestOptions<TBody, TRes, TQuery>;\n\n/**\n * Helpers for building typed request descriptors to pass to\n * {@link BigCommerceClient.batchSafe} or {@link BigCommerceClient.batchStream}.\n *\n * @example\n * ```ts\n * const results = await client.batchSafe([\n * req.get('catalog/products/1'),\n * req.post('catalog/products', { body: { name: 'Widget' } }),\n * ]);\n * ```\n */\nexport const req = {\n /**\n * Builds a GET request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional query params, schemas, and ky options.\n */\n get: <TRes, TQuery extends Query = Query>(\n path: string,\n options?: GetOptions<TRes, TQuery>,\n ): BatchRequestOptions<never, TRes, TQuery> =>\n ({ method: 'GET', path, ...options }) as BatchRequestOptions<never, TRes, TQuery>,\n\n /**\n * Builds a POST request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional body, query params, schemas, and ky options.\n */\n post: <TRes, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PostOptions<TBody, TRes, TQuery>,\n ): BatchRequestOptions<TBody, TRes, TQuery> =>\n ({ method: 'POST', path, ...options }) as BatchRequestOptions<TBody, TRes, TQuery>,\n\n /**\n * Builds a PUT request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional body, query params, schemas, and ky options.\n */\n put: <TRes, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PutOptions<TBody, TRes, TQuery>,\n ): BatchRequestOptions<TBody, TRes, TQuery> =>\n ({ method: 'PUT', path, ...options }) as BatchRequestOptions<TBody, TRes, TQuery>,\n\n /**\n * Builds a DELETE request descriptor.\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Optional query params and ky options.\n */\n delete: <TQuery extends Query = Query>(\n path: string,\n options?: DeleteOptions<TQuery>,\n ): BatchRequestOptions<never, never, TQuery> =>\n ({ method: 'DELETE', path, ...options }) as BatchRequestOptions<never, never, TQuery>,\n};\n\n/**\n * Options for v3 paginated collection operations ({@link BigCommerceClient.collect}, {@link BigCommerceClient.stream}).\n */\nexport type CollectOptions<TItem, TQuery extends Query> = ConcurrencyOptions &\n Omit<GetOptions<TItem, TQuery>, 'responseSchema' | 'version'> & {\n /** Schema to validate each item in the response. */\n itemSchema?: StandardSchemaV1<TItem>;\n };\n\nexport type BlindOptions<TItem, TQuery extends Query> = Omit<CollectOptions<TItem, TQuery>, 'version'> & {\n maxPages?: number;\n};\n\n/**\n * Options for v2 paginated operations with known count ({@link BigCommerceClient.collectCount}, {@link BigCommerceClient.streamCount}).\n */\nexport type CountedCollectOptions<TItem, TQuery extends Query> = CollectOptions<TItem, TQuery> & {\n /** Total number of items expected (for v2 endpoints without pagination metadata). */\n count?: number;\n};\n\n/**\n * Options for query-based filtering operations ({@link BigCommerceClient.query}, {@link BigCommerceClient.queryStream}).\n */\nexport type QueryOptions<TItem, TQuery extends Query> = CollectOptions<TItem, TQuery> & {\n /** Query parameter name for value filtering (e.g., `'id:in'`). */\n key: string;\n /** Values to filter by. Automatically chunked across multiple requests. */\n values: (string | number)[];\n};\n","export type Ok<T> = {\n ok: true;\n data: T;\n err: undefined;\n};\n\nexport type Err<E> = {\n ok: false;\n data: undefined;\n err: E;\n};\n\nexport type Result<T, E> = Ok<T> | Err<E>;\n\n/**\n * A {@link Result} extended with the zero-based index of the originating request in the input\n * array passed to {@link BigCommerceClient.batchStream} or {@link BigCommerceClient.batchSafe}.\n *\n * Because concurrent requests complete out of insertion order, `index` is the only reliable way\n * to correlate a result back to its input.\n *\n * @example\n * ```ts\n * const requests = ids.map(id => req.get(`catalog/products/${id}`));\n * for await (const { index, err, data } of client.batchStream(requests)) {\n * const originalId = ids[index];\n * if (err) { console.error(originalId, err); continue; }\n * console.log(originalId, data);\n * }\n * ```\n */\nexport type BatchResult<T, E> = Result<T, E> & { index: number };\n\n/**\n * Creates a successful {@link Result}. Check `result.ok` or `result.err` before accessing `data`.\n * @param data - The success value.\n */\nexport const Ok = <T, E>(data: T): Result<T, E> => ({ ok: true, data, err: undefined });\n\n/**\n * Creates a failed {@link Result}. Check `result.ok` or `result.err` before accessing `err`.\n * @param err - The error value.\n */\nexport const Err = <T, E>(err: E): Result<T, E> => ({ ok: false, data: undefined, err });\n","import ky, { isHTTPError, isKyError, isTimeoutError, type KyInstance, type KyResponse } from 'ky';\nimport pLimit, { type LimitFunction } from 'p-limit';\nimport {\n BASE_KY_CONFIG,\n type ClientConfig,\n type ConcurrencyOptions,\n DEFAULT_BACKOFF_RATE,\n DEFAULT_BACKOFF_RECOVER,\n DEFAULT_CONCURRENCY,\n DEFAULT_LIMIT,\n DEFAULT_MAX_BLIND_PAGES,\n DEFAULT_RATE_LIMIT_BACKOFF,\n HEADERS,\n LEADING_SLASHES,\n type Logger,\n MAX_CONCURRENCY,\n MAX_URL_LENGTH,\n type ResolvedConcurrencyOptions,\n} from './lib/common';\nimport {\n BaseError,\n BCApiError,\n BCClientError,\n BCCredentialsError,\n BCPaginatedItemValidationError,\n BCPaginatedOptionError,\n BCPaginatedResponseError,\n BCQueryValidationError,\n BCRequestBodyValidationError,\n BCResponseParseError,\n BCResponseValidationError,\n type BCSchemaValidationError,\n BCTimeoutError,\n} from './lib/errors';\nimport { bcRateLimitRetry, validateUrlLength } from './lib/hooks';\nimport { initLogger } from './lib/logger';\nimport type { V3Resource } from './lib/pagination';\nimport {\n type ApiVersion,\n type BatchRequestOptions,\n type BlindOptions,\n type CollectOptions,\n type DeleteOptions,\n type GetOptions,\n type PostOptions,\n type PutOptions,\n type Query,\n type QueryOptions,\n type RequestOptions,\n req,\n toUrlSearchParams,\n} from './lib/request';\nimport { type BatchResult, Err, Ok, type Result } from './lib/result';\nimport type { StandardSchemaV1 } from './lib/standard-schema';\nimport { AsyncChannel, chunkStrLength } from './lib/util';\n\nexport class BigCommerceClient {\n private readonly logger?: Logger;\n private readonly client: KyInstance;\n private readonly storeHash: string;\n\n /**\n * Creates a new BigCommerceClient.\n *\n * @param config - Client configuration. Ky options (e.g. `prefixUrl`, `timeout`, `retry`,\n * `hooks`) are forwarded to the underlying ky instance.\n * @param config.storeHash - BigCommerce store hash. Must be a non-empty string.\n * @param config.accessToken - BigCommerce API access token. Must be a non-empty string.\n * @param config.logger - A {@link Logger} instance, a log level string\n * (`'debug' | 'info' | 'warn' | 'error'`), `true` to enable console logging at `'info'`\n * level, or `false` to disable logging entirely. Omitting also defaults to `'info'` level.\n * @param config.concurrency - Default max concurrent requests for batch/stream operations.\n * Must be between 1 and 1000. Pass `false` to disable concurrency (sequential execution).\n * Defaults to 10.\n * @param config.rateLimitBackoff - Concurrency cap applied when a 429 response is received.\n * Defaults to 1.\n * @param config.backoff - Divisor (or `(concurrency, status) => number` function) applied to\n * concurrency on non-429 error responses. Defaults to 2.\n * @param config.backoffRecover - Amount (or `(concurrency) => number` function) added to\n * concurrency per successful response while below the configured max. Defaults to 1.\n *\n * @throws {@link BCCredentialsError} if `storeHash` or `accessToken` are missing.\n * @throws {@link BCClientError} if `prefixUrl` is not a valid URL or `concurrency` is out of range.\n */\n constructor(private readonly config: ClientConfig) {\n this.validateConfig();\n\n const { storeHash, accessToken, logger, concurrency: _, ...kyOptions } = config;\n\n this.logger = initLogger(logger);\n this.storeHash = storeHash;\n\n this.client = ky.create({\n ...BASE_KY_CONFIG,\n ...kyOptions,\n\n headers: {\n ...BASE_KY_CONFIG.headers,\n ...((kyOptions.headers ?? {}) as Record<string, string>),\n [HEADERS.AUTH_TOKEN]: accessToken,\n },\n\n hooks: {\n beforeRequest: [...(kyOptions.hooks?.beforeRequest ?? []), validateUrlLength],\n beforeRetry: [\n ({ error }) => {\n if (error instanceof BaseError) {\n throw error;\n }\n },\n bcRateLimitRetry(this.logger),\n ...(kyOptions.hooks?.beforeRetry ?? []),\n ],\n beforeError: [...(kyOptions.hooks?.beforeError ?? [])],\n afterResponse: [...(kyOptions.hooks?.afterResponse ?? [])],\n },\n });\n }\n\n /**\n * Sends a GET request to the given path.\n *\n * @param path - API path relative to the store's versioned base URL (e.g. `catalog/products`).\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n * @param options.responseSchema - StandardSchemaV1 schema to validate the parsed response body.\n *\n * @returns Parsed and optionally validated response body.\n *\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCResponseValidationError} if `responseSchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async get<TRes = unknown, TQuery extends Query = Query>(\n path: string,\n options?: GetOptions<TRes, TQuery>,\n ): Promise<TRes> {\n return this.request<never, TRes, TQuery>(path, {\n ...options,\n method: 'GET',\n } as RequestOptions<never, TRes, TQuery>);\n }\n\n /**\n * Sends a POST request to the given path.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.body - Request body, serialized as JSON.\n * @param options.bodySchema - StandardSchemaV1 schema to validate `body` before the request\n * is sent. Requires `body` to be provided.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n * @param options.responseSchema - StandardSchemaV1 schema to validate the parsed response body.\n *\n * @returns Parsed and optionally validated response body.\n *\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCRequestBodyValidationError} if `bodySchema` validation fails.\n * @throws {@link BCResponseValidationError} if `responseSchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async post<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PostOptions<TBody, TRes, TQuery>,\n ): Promise<TRes> {\n return this.request<TBody, TRes, TQuery>(path, {\n ...options,\n method: 'POST',\n } as RequestOptions<TBody, TRes, TQuery>);\n }\n\n /**\n * Sends a PUT request to the given path.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.body - Request body, serialized as JSON.\n * @param options.bodySchema - StandardSchemaV1 schema to validate `body` before the request\n * is sent. Requires `body` to be provided.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n * @param options.responseSchema - StandardSchemaV1 schema to validate the parsed response body.\n *\n * @returns Parsed and optionally validated response body.\n *\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCRequestBodyValidationError} if `bodySchema` validation fails.\n * @throws {@link BCResponseValidationError} if `responseSchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async put<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n path: string,\n options?: PutOptions<TBody, TRes, TQuery>,\n ): Promise<TRes> {\n return this.request<TBody, TRes, TQuery>(path, {\n ...options,\n method: 'PUT',\n } as RequestOptions<TBody, TRes, TQuery>);\n }\n\n /**\n * Sends a DELETE request to the given path.\n *\n * Silently suppresses 404 responses (resource already gone) and empty response bodies.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to the underlying request.\n * @param options.version - API version segment inserted into the URL. Defaults to `'v3'`.\n * @param options.query - Query parameters to append to the URL.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query` before the request\n * is sent. Requires `query` to be provided.\n *\n * @throws {@link BCApiError} on non-404 HTTP error responses.\n * @throws {@link BCTimeoutError} if the request times out.\n * @throws {@link BCResponseParseError} if the response body is non-empty and cannot be parsed.\n * @throws {@link BCUrlTooLongError} if the constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async delete<TRes = never, TQuery extends Query = Query>(\n path: string,\n options?: DeleteOptions<TQuery>,\n ): Promise<void> {\n try {\n await this.request<never, TRes, TQuery>(path, {\n ...options,\n method: 'DELETE',\n } as RequestOptions<never, TRes, TQuery>);\n } catch (err) {\n if (err instanceof BCResponseParseError && err.context.rawBody === '') {\n return;\n }\n\n // Do not throw on delete for resources that are already gone.\n if (err instanceof BCApiError && err.context.status === 404) {\n this.logger?.warn({ err }, 'Attempted to delete the resource that is already gone');\n\n return;\n }\n\n throw err;\n }\n }\n\n /**\n * Fetches items from a v3 paginated endpoint by splitting `values` across multiple requests\n * using the given `key` query param, chunking to stay within URL length limits.\n *\n * Collects all results into an array. Use {@link queryStream} to process items lazily.\n *\n * **Sorting and concurrency:** when `concurrency > 1`, chunks complete out of order so\n * sorted output is not preserved across the full result set. Pass `concurrency: false`\n * if sort order matters.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Query options including `key`, `values`, pagination params, and concurrency\n * controls.\n * @param options.key - Query parameter name used for value filtering (e.g. `'id:in'`).\n * @param options.values - Values to filter by. Automatically chunked across multiple requests\n * to keep each URL under 2048 characters.\n * @param options.query - Additional query parameters. `query.limit` controls page size\n * (default 250, must be > 0). If `options.key` is present in `query` it will be ignored.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent chunk requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n *\n * @returns All matching items across all chunked requests.\n * @throws {@link BCPaginatedOptionError} if `query.limit` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if a request times out.\n * @throws {@link BCResponseParseError} if a response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if a constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCPaginatedResponseError} if a page response has an unexpected shape.\n * @throws {@link BCPaginatedItemValidationError} if `itemSchema` validation fails for an item.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async query<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options: QueryOptions<TItem, TQuery>,\n ): Promise<TItem[]> {\n const results: TItem[] = [];\n\n for await (const { data, err } of this.queryStream(path, options)) {\n if (err) {\n throw err;\n } else {\n results.push(data);\n }\n }\n\n return results;\n }\n\n /**\n * Streaming variant of {@link query}. Yields each item individually as results arrive,\n * splitting `values` into URL-length-safe chunks across concurrent requests.\n *\n * Each yielded value is a {@link Result} — check `err` before using `data`.\n *\n * **Sorting and concurrency:** when `concurrency > 1`, chunks complete out of order so\n * sorted output is not preserved across the full result set. Pass `concurrency: false`\n * if sort order matters.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Query options including `key`, `values`, pagination params, and concurrency\n * controls.\n * @param options.key - Query parameter name used for value filtering (e.g. `'id:in'`).\n * @param options.values - Values to filter by. Automatically chunked across multiple requests\n * to keep each URL under 2048 characters.\n * @param options.query - Additional query parameters. `query.limit` controls page size\n * (default 250, must be > 0). If `options.key` is present in `query` it will be ignored.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent chunk requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @throws {@link BCPaginatedOptionError} if `query.limit` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n */\n async *queryStream<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options: QueryOptions<TItem, TQuery>,\n ): AsyncGenerator<Result<TItem, BaseError>> {\n const {\n key,\n values,\n query,\n querySchema,\n itemSchema,\n concurrency,\n rateLimitBackoff,\n backoff,\n backoffRecover,\n ...requestOptions\n } = options;\n\n const limit = this.validatePaginationOption(path, 'limit', query?.limit ?? DEFAULT_LIMIT);\n\n const validatedQuery = await this.validate(\n query,\n querySchema,\n BCQueryValidationError,\n 'GET',\n path,\n 'Invalid query parameters',\n );\n\n const newQuery: Query = {\n ...validatedQuery,\n limit,\n };\n\n if (key in newQuery) {\n this.logger?.warn({ key }, 'The provided key is already in the query params, this param will be ignored');\n\n delete newQuery[key];\n }\n\n const url = this.config.prefixUrl ?? requestOptions.prefixUrl ?? BASE_KY_CONFIG.prefixUrl;\n const fullPath = this.makePath('v3', path);\n const fullQuery = toUrlSearchParams({ ...newQuery, page: 1 });\n const fullUrl = `${url}/${fullPath}?${fullQuery}`;\n const keyOverhead = encodeURIComponent(key).length + 2; // `&key=` or `key=` prefix\n\n const chunks = chunkStrLength(values.map(String), {\n chunkLength: limit,\n maxLength: MAX_URL_LENGTH,\n offset: fullUrl.length + keyOverhead,\n separatorSize: encodeURIComponent(',').length,\n });\n\n const requests = chunks.map((chunk) =>\n req.get(path, {\n ...requestOptions,\n query: {\n ...newQuery,\n page: 1,\n [key]: chunk,\n },\n }),\n );\n\n for await (const { err, data } of this.batchStream(requests, {\n concurrency,\n rateLimitBackoff,\n backoff,\n backoffRecover,\n })) {\n if (err) {\n yield Err(err);\n continue;\n }\n\n try {\n const { data: items } = this.assertPaginatedResponse(path, data);\n\n for (const item of items) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } catch (err) {\n if (err instanceof BaseError) {\n yield Err(err);\n } else {\n yield Err(new BCClientError('Unknown error occurred processing page', {}, { cause: err }));\n }\n }\n }\n }\n\n /**\n * Fetches all pages from a v3 paginated endpoint and collects items into an array.\n *\n * Use {@link stream} to process items lazily without buffering the full result set.\n *\n * **Sorting and concurrency:** the first page is fetched sequentially; remaining pages are\n * fetched concurrently and may complete out of order. When `concurrency > 1`, sort order\n * is not preserved across pages. Pass `concurrency: false` if sort order matters.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0).\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent page requests for pages after the first.\n * Must be 1–1000. `false` for sequential. Defaults to `config.concurrency`, or 10 if not\n * set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @returns All items across all pages.\n *\n * @throws {@link BCPaginatedOptionError} if `query.limit` or `query.page` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCApiError} on HTTP error responses.\n * @throws {@link BCTimeoutError} if a request times out.\n * @throws {@link BCResponseParseError} if a response body cannot be parsed.\n * @throws {@link BCUrlTooLongError} if a constructed URL exceeds 2048 characters.\n * @throws {@link BCRateLimitNoHeadersError} if a 429 is received without rate-limit headers.\n * @throws {@link BCRateLimitDelayTooLongError} if the rate-limit reset window exceeds\n * `config.retry.maxRetryAfter`.\n * @throws {@link BCPaginatedResponseError} if a page response has an unexpected shape.\n * @throws {@link BCPaginatedItemValidationError} if `itemSchema` validation fails for an item.\n * @throws {@link BCClientError} on any other ky or unknown error.\n */\n async collect<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: CollectOptions<TItem, TQuery>,\n ): Promise<TItem[]> {\n const items: TItem[] = [];\n\n for await (const { data, err } of this.stream(path, options)) {\n if (err) {\n throw err;\n } else {\n items.push(data);\n }\n }\n\n return items;\n }\n\n /**\n * Fetches all pages from a v2 flat-array endpoint and collects items into an array.\n *\n * Pagination is discovered dynamically — pages are fetched in batches until an empty page,\n * a 404, or a 204 response is received. No prior knowledge of total count is required.\n *\n * Use {@link streamBlind} to process items lazily without buffering the full result set.\n *\n * **Sorting and concurrency:** pages within each batch are fetched concurrently and may\n * complete out of order. When `concurrency > 1`, sort order is not preserved across pages.\n * Pass `concurrency: false` if sort order matters.\n *\n * @param path - API path relative to the store's versioned base URL (always requests v2).\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0).\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.maxPages - Maximum number of pages to fetch before stopping (default 500,\n * must be > 0). A warning is logged if this limit is reached.\n * @param options.concurrency - Max concurrent page requests per batch. Must be 1–1000.\n * `false` for sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @returns All items across all pages.\n *\n * @throws {@link BCPaginatedOptionError} if `query.limit`, `query.page`, or `maxPages` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n * @throws {@link BCPaginatedItemValidationError} if `itemSchema` validation fails for an item.\n * @throws {@link BCClientError} if a page returns a non-array response, or on any other error.\n * @throws {@link BCApiError} on non-terminating HTTP error responses.\n * @throws {@link BCTimeoutError} if a request times out.\n * @throws {@link BCResponseParseError} if a response body cannot be parsed.\n */\n async collectBlind<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: BlindOptions<TItem, TQuery>,\n ): Promise<TItem[]> {\n const results: TItem[] = [];\n\n for await (const { err, data } of this.streamBlind(path, options)) {\n if (err) {\n throw err;\n } else {\n results.push(data);\n }\n }\n\n return results;\n }\n\n /**\n * Lazily streams items from a v2 flat-array endpoint, page by page.\n *\n * Pagination is discovered dynamically — pages are fetched in concurrent batches until an\n * empty page, a 404, or a 204 response is received. No prior knowledge of total count is\n * required. Each item is yielded as a {@link Result}: `Ok(item)` on success or\n * `Err(error)` for item-level validation failures and non-terminating page errors.\n *\n * Use {@link collectBlind} to buffer all results into an array (throws on any error).\n *\n * **Sorting and concurrency:** pages within each batch are fetched concurrently and may\n * complete out of order. When `concurrency > 1`, sort order is not preserved across pages.\n * Pass `concurrency: false` if sort order matters.\n *\n * @param path - API path relative to the store's versioned base URL (always requests v2).\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0).\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.maxPages - Maximum number of pages to fetch before stopping (default 500,\n * must be > 0). A warning is logged if this limit is reached.\n * @param options.concurrency - Max concurrent page requests per batch. Must be 1–1000.\n * `false` for sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n *\n * @throws {@link BCPaginatedOptionError} if `query.limit`, `query.page`, or `maxPages` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n *\n * @yields `Ok(item)` for each successfully fetched and validated item.\n * @yields `Err(BCPaginatedItemValidationError)` when `itemSchema` rejects an item.\n * @yields `Err(BCClientError)` when a page returns a non-array response.\n * @yields `Err(error)` for non-terminating page errors (e.g. non-404/204 HTTP errors).\n */\n async *streamBlind<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: BlindOptions<TItem, TQuery>,\n ): AsyncGenerator<Result<TItem, BaseError>> {\n const {\n query: rawQuery,\n querySchema,\n itemSchema,\n maxPages: rawMaxPages,\n concurrency: rawConcurrency,\n rateLimitBackoff,\n backoff,\n backoffRecover,\n pLimit: rawPLimit,\n ...requestOptions\n } = options ?? {};\n\n const concurrency = this.validateConcurrency(rawConcurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY);\n const limiter = rawPLimit ?? (concurrency ? pLimit({ concurrency, rejectOnClear: true }) : undefined);\n\n const concurrencyOptions = {\n concurrency: rawConcurrency,\n rateLimitBackoff,\n backoff,\n backoffRecover,\n pLimit: limiter,\n };\n\n const query = await this.validate(rawQuery, querySchema, BCQueryValidationError, 'GET', path);\n const page = this.validatePaginationOption(path, 'page', query?.page ?? 1);\n const limit = this.validatePaginationOption(path, 'limit', query?.limit ?? DEFAULT_LIMIT);\n const maxPages = this.validatePaginationOption(path, 'maxPages', rawMaxPages ?? DEFAULT_MAX_BLIND_PAGES);\n\n let done = false;\n let currentPage = page;\n\n do {\n if (currentPage > maxPages) {\n this.logger?.warn({ currentPage }, 'Blind pagination reached maxPages before the end of the data');\n break;\n }\n\n const batchSize = (limiter?.concurrency ?? concurrency) || 1;\n const pageRequests = Array.from({ length: batchSize }, (_, i) => currentPage + i).map((page) =>\n req.get(path, {\n ...requestOptions,\n version: 'v2',\n query: {\n ...query,\n page,\n limit,\n },\n }),\n );\n\n currentPage += batchSize;\n\n const pages = await this.batchSafe(pageRequests, concurrencyOptions);\n\n for (const { err, data } of pages) {\n if (err) {\n done =\n (err instanceof BCApiError && err.context.status === 404) ||\n (err instanceof BCResponseParseError &&\n err.context.rawBody === '' &&\n err.context.status === 204);\n\n if (!done) {\n yield Err(err);\n }\n } else {\n if (Array.isArray(data)) {\n if (data.length === 0) {\n done = true;\n break;\n }\n\n for (const item of data) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } else {\n yield Err(\n new BCClientError('Received non array response from blind pagination page endpoint', {\n data,\n path,\n }),\n );\n }\n }\n }\n } while (!done);\n }\n\n /**\n * Executes multiple requests concurrently and returns all results as {@link BatchResult}\n * values, never throwing. Errors from individual requests are captured as `Err` results.\n *\n * Results arrive in completion order, not input order. Use the `index` field on each\n * {@link BatchResult} to correlate a result back to its position in the `requests` array.\n *\n * Use {@link batchStream} to process results as they arrive rather than waiting for all.\n *\n * @param requests - Array of request descriptors built with the {@link req} helpers.\n * @param options.concurrency - Max concurrent requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n *\n * @returns {@link BatchResult} array in the order requests completed (not input order).\n */\n async batchSafe<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n requests: BatchRequestOptions<TBody, TRes, TQuery>[],\n options?: ConcurrencyOptions,\n ): Promise<BatchResult<TRes, BaseError>[]> {\n const results: BatchResult<TRes, BaseError>[] = [];\n\n for await (const res of this.batchStream(requests, options)) {\n results.push(res);\n }\n\n return results;\n }\n\n /**\n * Streams all items from a v3 paginated endpoint, fetching the first page sequentially\n * and remaining pages concurrently via {@link batchStream}.\n *\n * Each yielded value is a {@link Result} — check `err` before using `data`. Use\n * {@link collect} to gather all items into an array.\n *\n * **Sorting and concurrency:** the first page is fetched sequentially; remaining pages are\n * fetched concurrently and may complete out of order. When `concurrency > 1`, sort order\n * is not preserved across pages. Pass `concurrency: false` if sort order matters.\n *\n * @param path - API path relative to the store's versioned base URL.\n * @param options - Ky options are forwarded to page requests.\n * @param options.query - Query parameters. `query.limit` controls page size (default 250,\n * must be > 0). `query.page` sets the starting page (default 1, must be > 0). If the API\n * enforces a different limit, the actual `per_page` from the first response is used for\n * subsequent pages.\n * @param options.querySchema - StandardSchemaV1 schema to validate `query`. Requires `query`\n * to be provided.\n * @param options.itemSchema - StandardSchemaV1 schema to validate each returned item.\n * @param options.concurrency - Max concurrent page requests for pages after the first.\n * Must be 1–1000. `false` for sequential. Defaults to `config.concurrency`, or 10 if not\n * set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n * @throws {@link BCPaginatedOptionError} if `query.limit` or `query.page` is not a positive number.\n * @throws {@link BCQueryValidationError} if `querySchema` validation fails.\n */\n async *stream<TItem = unknown, TQuery extends Query = Query>(\n path: string,\n options?: CollectOptions<TItem, TQuery>,\n ): AsyncGenerator<Result<TItem, BaseError>> {\n const {\n query,\n querySchema,\n itemSchema,\n concurrency,\n rateLimitBackoff,\n backoff,\n backoffRecover,\n ...requestOptions\n } = options ?? {};\n\n let limit = this.validatePaginationOption(path, 'limit', query?.limit ?? DEFAULT_LIMIT);\n const page = this.validatePaginationOption(path, 'page', query?.page ?? 1);\n\n const validatedQuery = await this.validate(\n query,\n querySchema,\n BCQueryValidationError,\n 'GET',\n path,\n 'Invalid query parameters',\n );\n\n let firstPageMeta: V3Resource<unknown[]>['meta'];\n\n try {\n const firstPage = await this.get(path, {\n ...requestOptions,\n query: {\n ...validatedQuery,\n page,\n limit,\n } as unknown as TQuery,\n });\n\n const { data, meta } = this.assertPaginatedResponse(path, firstPage);\n\n firstPageMeta = meta;\n\n // Validate and return the first page\n for (const item of data) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } catch (err) {\n if (err instanceof BaseError) {\n yield Err(err);\n } else {\n yield Err(new BCClientError('Unknown error occurred fetching first page', {}, { cause: err }));\n }\n\n return;\n }\n\n const { total_pages, per_page } = firstPageMeta.pagination;\n\n if (limit !== per_page) {\n this.logger?.warn({ limit, actual: per_page }, 'API enforces alternate limit on this endpoint');\n limit = per_page;\n }\n\n // Fetch other pages using batchStream\n const requests = Array.from({ length: total_pages - page }, (_, i) => i + page + 1).map((page) => ({\n method: 'GET' as const,\n path,\n ...requestOptions,\n query: {\n ...validatedQuery,\n limit,\n page,\n },\n }));\n\n for await (const pageRes of requests.length > 0\n ? this.batchStream(requests, { concurrency, rateLimitBackoff, backoff, backoffRecover })\n : []) {\n const { data: page, err } = pageRes;\n\n if (err) {\n yield Err(err);\n continue;\n }\n\n try {\n const { data } = this.assertPaginatedResponse(path, page);\n\n for (const item of data) {\n yield this.validatePaginatedItem(path, item, itemSchema);\n }\n } catch (err) {\n if (err instanceof BaseError) {\n yield Err(err);\n } else {\n yield Err(new BCClientError('Unknown error occurred processing page', {}, { cause: err }));\n }\n }\n }\n }\n\n /**\n * Executes multiple requests with configurable concurrency, yielding each result as a\n * {@link BatchResult} as it completes. Errors from individual requests are yielded as `Err`\n * results rather than thrown.\n *\n * Results arrive in completion order, not input order. Use the `index` field on each\n * {@link BatchResult} to correlate a result back to its position in the `requests` array.\n *\n * Automatically adjusts concurrency up/down in response to rate-limit and error responses.\n * Use {@link batchSafe} to collect all results into an array.\n *\n * **Caution:** the generator is making requests concurrently. As a consequence if a\n * request is mutating the remote (POST, DELETE) and `for await` loop is exited early,\n * the in-flight request may or may not commit the mutation, and the results of\n * these request WILL NOT be yielded. If you do intent to break the loop early and want to\n * get all the results, set `concurrency: false` to trade concurrency for deterministic behavior.\n *\n * @param requests - Array of request descriptors built with the {@link req} helpers.\n * @param options.concurrency - Max concurrent requests. Must be 1–1000. `false` for\n * sequential. Defaults to `config.concurrency`, or 10 if not set on the client.\n * @param options.rateLimitBackoff - Concurrency cap on 429 responses. Defaults to\n * `config.rateLimitBackoff`, or 1 if not set on the client.\n * @param options.backoff - Divisor (or function) applied to concurrency on error responses.\n * Defaults to `config.backoff`, or 2 if not set on the client.\n * @param options.backoffRecover - Amount (or function) added to concurrency per successful\n * response. Defaults to `config.backoffRecover`, or 1 if not set on the client.\n */\n async *batchStream<TRes = unknown, TBody = unknown, TQuery extends Query = Query>(\n requests: BatchRequestOptions<TBody, TRes, TQuery>[],\n options?: ConcurrencyOptions,\n ): AsyncGenerator<BatchResult<TRes, BaseError>> {\n const resolved = this.resolveStreamOptions(options);\n\n if (resolved.concurrency) {\n const limit = resolved.pLimit ?? pLimit({ concurrency: resolved.concurrency, rejectOnClear: true });\n const client = this.makeStreamClient(limit, resolved);\n const channel = new AsyncChannel<BatchResult<TRes, BaseError>>();\n\n try {\n Promise.all(\n requests.map((req, index) =>\n limit(() =>\n this.request(req.path, req, client).then(\n (val) => channel.push({ ...Ok(val), index }),\n (err) => channel.push({ ...Err(err), index }),\n ),\n ),\n ),\n )\n .catch((err) => this.logger?.warn({ err }, 'In-flight concurrent requests aborted'))\n .finally(() => channel.close());\n\n for await (const item of channel) {\n yield item;\n }\n } finally {\n limit.clearQueue();\n }\n } else {\n for (const [index, request] of requests.entries()) {\n try {\n const res = await this.request(request.path, request);\n\n yield { ...Ok(res), index };\n } catch (err) {\n if (err instanceof BaseError) {\n yield { ...Err(err), index };\n } else {\n yield { ...Err(new BCClientError('Unknown error in batchStream', {}, { cause: err })), index };\n }\n }\n }\n }\n }\n\n private async validatePaginatedItem<TItem>(\n path: string,\n item: unknown,\n schema?: StandardSchemaV1<TItem>,\n ): Promise<Result<TItem, BaseError>> {\n if (!schema) {\n return Ok(item as TItem);\n }\n\n const result = await schema['~standard'].validate(item);\n\n if (result.issues) {\n return Err(new BCPaginatedItemValidationError('Page item validation failed', 'GET', path, item, result));\n } else {\n return Ok(result.value);\n }\n }\n\n private assertPaginatedResponse(path: string, res: unknown): V3Resource<unknown[]> {\n if (typeof res !== 'object' || res === null) {\n throw new BCPaginatedResponseError(path, res, 'Response is invalid');\n }\n\n if (!('data' in res) || !Array.isArray(res.data)) {\n throw new BCPaginatedResponseError(\n path,\n res,\n 'response.data must be an array, ensure this endpoint returns a v3 collection',\n );\n }\n\n if (!('meta' in res) || typeof res.meta !== 'object' || res.meta === null || !('pagination' in res.meta)) {\n throw new BCPaginatedResponseError(path, res, 'response.meta is invalid unable to paginate');\n }\n\n const pagination = res.meta.pagination;\n\n if (typeof pagination !== 'object' || pagination === null) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination is invalid unable to paginate');\n }\n\n const requiredFields: Array<[string, (v: unknown) => boolean]> = [\n ['per_page', (v) => typeof v === 'number' && v > 0],\n ['total_pages', (v) => typeof v === 'number' && v >= 0],\n ];\n\n for (const [field, isValid] of requiredFields) {\n if (!(field in pagination) || !isValid(pagination[field as keyof typeof pagination])) {\n throw new BCPaginatedResponseError(\n path,\n res,\n `response.meta.pagination.${field} is missing or invalid`,\n );\n }\n }\n\n const { links } = pagination as { links?: unknown };\n\n if (typeof links !== 'object' || links === null) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination.links is missing or invalid');\n }\n\n const isNullableString = (v: unknown) => v === null || typeof v === 'string';\n\n if (!('current' in links) || typeof links.current !== 'string') {\n throw new BCPaginatedResponseError(\n path,\n res,\n 'response.meta.pagination.links.current is missing or invalid',\n );\n }\n\n if ('next' in links && !isNullableString(links.next)) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination.links.next is invalid');\n }\n\n if ('previous' in links && !isNullableString(links.previous)) {\n throw new BCPaginatedResponseError(path, res, 'response.meta.pagination.links.previous is invalid');\n }\n\n return res as V3Resource<unknown[]>;\n }\n\n private validatePaginationOption(path: string, key: string, value: unknown): number {\n if (typeof value !== 'number' || value <= 0) {\n throw new BCPaginatedOptionError(path, value, key);\n }\n\n return value;\n }\n\n private resolveStreamOptions(options?: ConcurrencyOptions): ResolvedConcurrencyOptions {\n return {\n concurrency: options?.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY,\n rateLimitBackoff: options?.rateLimitBackoff ?? this.config.rateLimitBackoff ?? DEFAULT_RATE_LIMIT_BACKOFF,\n backoff: options?.backoff ?? this.config.backoff ?? DEFAULT_BACKOFF_RATE,\n backoffRecover: options?.backoffRecover ?? this.config.backoffRecover ?? DEFAULT_BACKOFF_RECOVER,\n pLimit: options?.pLimit,\n };\n }\n\n private makeStreamClient(limit: LimitFunction, options: ResolvedConcurrencyOptions): KyInstance {\n const { concurrency, rateLimitBackoff, backoff, backoffRecover } = options;\n\n if (concurrency === false) {\n return this.client;\n }\n\n return this.client.extend({\n hooks: {\n beforeRetry: [\n ({ error }) => {\n if (!isHTTPError(error)) {\n return;\n }\n\n const previousConcurrency = limit.concurrency;\n\n if (error.response.status === 429) {\n limit.concurrency = rateLimitBackoff;\n\n this.logger?.warn(\n { previousConcurrency, newConcurrency: limit.concurrency },\n 'Rate limit reached, limiting concurrency',\n );\n } else {\n const rate =\n typeof backoff === 'function'\n ? backoff(limit.concurrency, error.response.status)\n : backoff;\n\n limit.concurrency = Math.ceil(limit.concurrency / rate);\n\n this.logger?.warn(\n { previousConcurrency, newConcurrency: limit.concurrency },\n 'Intermittent errors, limiting concurrency to compensate',\n );\n }\n },\n ],\n afterResponse: [\n (_request, _options, response) => {\n if (response.ok && limit.concurrency < concurrency) {\n const recover =\n typeof backoffRecover === 'function'\n ? backoffRecover(limit.concurrency)\n : backoffRecover;\n\n limit.concurrency = Math.min(concurrency, limit.concurrency + recover);\n }\n },\n ],\n },\n });\n }\n\n private async request<TBody, TRes, TQuery extends Query = Query>(\n rawPath: string,\n options: RequestOptions<TBody, TRes, TQuery>,\n client?: KyInstance,\n ) {\n const { version, query, body, bodySchema, querySchema, responseSchema, ...kyOptions } = options;\n\n const path = this.makePath(options.version ?? 'v3', rawPath);\n const validQuery = await this.validate(\n query,\n querySchema,\n BCQueryValidationError,\n options.method,\n path,\n 'Invalid query parameters',\n );\n const validBody = await this.validate(\n body,\n bodySchema,\n BCRequestBodyValidationError,\n options.method,\n path,\n `Invalid ${options.method} request body`,\n );\n\n let response: KyResponse;\n\n try {\n response = await (client ?? this.client)(path, {\n ...kyOptions,\n method: options.method,\n searchParams: toUrlSearchParams(validQuery),\n json: validBody,\n });\n } catch (err) {\n if (err instanceof BaseError) {\n throw err;\n }\n\n if (isHTTPError(err)) {\n const requestBody = await err.request.text().catch(() => '');\n const responseBody = await err.response.text().catch(() => '');\n const error = new BCApiError(err, requestBody, responseBody);\n\n this.logger?.error(error.context, 'Request failed');\n\n throw error;\n }\n\n if (isTimeoutError(err)) {\n const error = new BCTimeoutError(err);\n\n this.logger?.error(error.context, 'Request timed out');\n\n throw error;\n }\n\n if (isKyError(err)) {\n throw new BCClientError('Client error', undefined, err);\n }\n\n throw new BCClientError('Unknown error', undefined, err);\n }\n\n let text: string;\n\n try {\n text = await response.text();\n } catch (err) {\n throw new BCResponseParseError(options.method, path, response.status, err, query, '');\n }\n\n let res: TRes;\n\n try {\n res = JSON.parse(text);\n } catch (err) {\n throw new BCResponseParseError(options.method, path, response.status, err, query, text);\n }\n\n this.logger?.debug(\n { method: options.method, url: response.url, status: response.status },\n 'Successful request',\n );\n\n return this.validate(\n res,\n responseSchema,\n BCResponseValidationError,\n options.method,\n path,\n 'Invalid API response',\n );\n }\n\n private async validate<T>(\n data: unknown,\n schema: StandardSchemaV1<T> | undefined,\n ErrorClass: new (\n message: string,\n method: string,\n path: string,\n data: unknown,\n error: StandardSchemaV1.FailureResult,\n ) => BCSchemaValidationError,\n method: string,\n path: string,\n message?: string,\n ): Promise<T> {\n if (!schema) {\n return data as T;\n }\n\n const result = await schema['~standard'].validate(data);\n\n if (result.issues) {\n throw new ErrorClass(message ?? 'Validation failed', method, path, data, result);\n }\n\n return result.value;\n }\n\n private makePath(version: ApiVersion, route: string): string {\n return `stores/${this.storeHash}/${version}/${route.replace(LEADING_SLASHES, '')}`;\n }\n\n private validateConfig() {\n const { accessToken, storeHash } = this.config;\n const errors: string[] = [];\n\n // Using reasonable assumptions about these credentials for validation.\n // This will not verify the credentials but at least guard against providing\n // something completely invalid like empty string\n if (typeof storeHash !== 'string' || storeHash.length <= 0) {\n errors.push('storeHash is empty');\n }\n\n if (typeof accessToken !== 'string' || accessToken.length <= 0) {\n errors.push('accessToken is empty');\n }\n\n if (errors.length > 0) {\n throw new BCCredentialsError(errors);\n }\n\n if (this.config.prefixUrl) {\n try {\n new URL(this.config.prefixUrl);\n } catch (err) {\n throw new BCClientError('Invalid prefixUrl', undefined, err);\n }\n }\n\n this.validateConcurrency(this.config.concurrency);\n }\n\n private validateConcurrency(concurrency: number | undefined | false) {\n if (concurrency === undefined) {\n return;\n }\n\n if (concurrency === false) {\n return concurrency;\n }\n\n if (concurrency <= 0 || concurrency > MAX_CONCURRENCY) {\n throw new BCClientError(`Invalid concurrency: allowed range (1:${MAX_CONCURRENCY})`, undefined);\n }\n\n return concurrency;\n }\n}\n"],"mappings":"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"}
|