bc-api-client 0.1.0-beta.6 → 0.1.0-beta.8

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/auth.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Logger } from './core';
1
2
  /**
2
3
  * Configuration options for BigCommerce authentication
3
4
  */
@@ -12,6 +13,8 @@ type Config = {
12
13
  storeHash: string;
13
14
  /** Optional array of scopes to validate during auth callback */
14
15
  scopes?: string[];
16
+ /** Optional logger instance */
17
+ logger?: Logger;
15
18
  };
16
19
  /**
17
20
  * Query parameters received from BigCommerce auth callback
package/dist/client.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { Logger } from './core';
1
2
  import { RateLimitOptions, RequestOptions, StoreOptions } from './net';
2
3
  /**
3
4
  * Options for GET requests to the BigCommerce API
@@ -36,7 +37,10 @@ export type QueryOptions = Omit<GetOptions, 'version'> & ConcurrencyOptions & {
36
37
  /**
37
38
  * Configuration options for the BigCommerce client
38
39
  */
39
- export type Config = StoreOptions & RateLimitOptions & ConcurrencyOptions;
40
+ export type Config = StoreOptions & RateLimitOptions & ConcurrencyOptions & {
41
+ /** Logger instance */
42
+ logger?: Logger;
43
+ };
40
44
  /**
41
45
  * Client for interacting with the BigCommerce API
42
46
  *
package/dist/core.d.ts CHANGED
@@ -16,3 +16,12 @@ export type V3Resource<T> = {
16
16
  pagination: Pagination;
17
17
  };
18
18
  };
19
+ /**
20
+ * Logger interface for logging messages and data, Pino compatible by default
21
+ */
22
+ export interface Logger {
23
+ debug: (data: unknown, message?: string) => void;
24
+ info: (data: unknown, message?: string) => void;
25
+ warn: (data: unknown, message?: string) => void;
26
+ error: (data: unknown, message?: string) => void;
27
+ }
package/dist/index.js CHANGED
@@ -40,7 +40,7 @@ var RequestError = class extends Error {
40
40
  }
41
41
  };
42
42
  var request = async (options) => {
43
- const { maxDelay = CONFIG.DEFAULT_MAX_DELAY, maxRetries = CONFIG.DEFAULT_MAX_RETRIES } = options;
43
+ const { maxDelay = CONFIG.DEFAULT_MAX_DELAY, maxRetries = CONFIG.DEFAULT_MAX_RETRIES, logger } = options;
44
44
  let retries = 0;
45
45
  let lastError = null;
46
46
  while (retries < maxRetries) {
@@ -52,6 +52,11 @@ var request = async (options) => {
52
52
  if (err.status === 429 && typeof err.data === "object" && err.data !== null && "headers" in err.data) {
53
53
  const headers = err.data.headers;
54
54
  const retryAfter = Number.parseInt(headers[CONFIG.HEADERS.RETRY_AFTER]);
55
+ logger?.debug({
56
+ retryAfter,
57
+ retries,
58
+ remaining: headers[CONFIG.HEADERS.REQUESTS_LEFT]
59
+ }, "Rate limit hit, retrying");
55
60
  if (Number.isNaN(retryAfter)) {
56
61
  throw new RequestError(
57
62
  err.status,
@@ -61,6 +66,10 @@ var request = async (options) => {
61
66
  );
62
67
  }
63
68
  if (retryAfter > maxDelay) {
69
+ logger?.warn({
70
+ retryAfter,
71
+ maxDelay
72
+ }, "Rate limit delay exceeds maximum allowed delay");
64
73
  throw new RequestError(
65
74
  err.status,
66
75
  `Rate limit exceeded: ${retryAfter}ms, ${err.message}`,
@@ -75,9 +84,14 @@ var request = async (options) => {
75
84
  throw err;
76
85
  }
77
86
  }
87
+ logger?.error({
88
+ retries,
89
+ error: lastError
90
+ }, "Request failed after maximum retries");
78
91
  throw lastError ?? new RequestError(500, "Failed to make request", "Too many retries after rate limit");
79
92
  };
80
93
  var safeRequest = async (options) => {
94
+ const { logger } = options;
81
95
  let res;
82
96
  try {
83
97
  res = await call(options);
@@ -86,6 +100,12 @@ var safeRequest = async (options) => {
86
100
  throw error;
87
101
  }
88
102
  if (!(error instanceof HTTPError)) {
103
+ logger?.error({
104
+ error: error instanceof Error ? {
105
+ name: error.name,
106
+ message: error.message
107
+ } : error
108
+ }, "Unexpected error during request");
89
109
  throw error;
90
110
  }
91
111
  let data;
@@ -102,6 +122,10 @@ var safeRequest = async (options) => {
102
122
  } catch {
103
123
  data = "Failed to read error response";
104
124
  }
125
+ logger?.error({
126
+ status: error?.response?.status,
127
+ errorMessage
128
+ }, "HTTP error during request");
105
129
  throw new RequestError(
106
130
  error?.response?.status ?? 500,
107
131
  errorMessage,
@@ -119,6 +143,13 @@ var safeRequest = async (options) => {
119
143
  try {
120
144
  return JSON.parse(text);
121
145
  } catch (error) {
146
+ logger?.error({
147
+ status: res.status,
148
+ error: error instanceof Error ? {
149
+ name: error.name,
150
+ message: error.message
151
+ } : error
152
+ }, "Failed to parse response");
122
153
  throw new RequestError(
123
154
  res.status,
124
155
  `Failed to parse response: ${text}`,
@@ -127,12 +158,16 @@ var safeRequest = async (options) => {
127
158
  );
128
159
  }
129
160
  };
130
- var call = (options) => {
131
- const { storeHash, accessToken, endpoint, method = "GET", body, version = CONFIG.DEFAULT_VERSION, query } = options;
161
+ var call = async (options) => {
162
+ const { storeHash, accessToken, endpoint, method = "GET", body, version = CONFIG.DEFAULT_VERSION, query, logger } = options;
132
163
  const url = `${CONFIG.BASE_URL}${storeHash}/${version}/${endpoint.replace(/^\//, "")}`;
133
164
  const searchParams = query ? new URLSearchParams(query).toString() : "";
134
165
  const fullUrl = searchParams ? `${url}?${searchParams}` : url;
135
166
  if (fullUrl.length > CONFIG.MAX_URL_LENGTH) {
167
+ logger?.error({
168
+ urlLength: fullUrl.length,
169
+ maxLength: CONFIG.MAX_URL_LENGTH
170
+ }, "URL length exceeds maximum allowed length");
136
171
  throw new RequestError(
137
172
  400,
138
173
  "URL too long",
@@ -148,12 +183,10 @@ var call = (options) => {
148
183
  },
149
184
  json: body
150
185
  };
151
- return ky(fullUrl, request2);
186
+ const response = await ky(fullUrl, request2);
187
+ return response;
152
188
  };
153
189
 
154
- // src/client.ts
155
- import { chunk, range } from "remeda";
156
-
157
190
  // src/util.ts
158
191
  var chunkStrLength = (items, options = {}) => {
159
192
  const { maxLength = 2048, chunkLength = 250, offset = 0, separatorSize = 1 } = options;
@@ -187,6 +220,15 @@ var chunkStrLength = (items, options = {}) => {
187
220
  // src/client.ts
188
221
  var MAX_PAGE_SIZE = 250;
189
222
  var DEFAULT_CONCURRENCY = 10;
223
+ function chunkArray(array, size) {
224
+ return Array.from(
225
+ { length: Math.ceil(array.length / size) },
226
+ (_, i) => array.slice(i * size, i * size + size)
227
+ );
228
+ }
229
+ function rangeArray(start, end) {
230
+ return Array.from({ length: end - start + 1 }, (_, i) => start + i);
231
+ }
190
232
  var BigCommerceClient = class {
191
233
  /**
192
234
  * Creates a new BigCommerce client instance
@@ -269,12 +311,18 @@ var BigCommerceClient = class {
269
311
  * @returns Promise resolving to array of response data
270
312
  */
271
313
  async concurrent(requests, options) {
272
- const chunks = chunk(requests, options?.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY);
314
+ const chunkSize = options?.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY;
315
+ const chunks = chunkArray(requests, chunkSize);
273
316
  const skipErrors = options?.skipErrors ?? this.config.skipErrors ?? false;
274
317
  const results = [];
275
- for (const chunk2 of chunks) {
318
+ this.config.logger?.debug({
319
+ totalRequests: requests.length,
320
+ chunkSize,
321
+ chunks: chunks.length
322
+ }, "Starting concurrent requests");
323
+ for (const chunk of chunks) {
276
324
  const responses = await Promise.allSettled(
277
- chunk2.map(
325
+ chunk.map(
278
326
  (opt) => request({
279
327
  ...opt,
280
328
  ...this.config
@@ -288,7 +336,12 @@ var BigCommerceClient = class {
288
336
  if (!skipErrors) {
289
337
  throw response.reason;
290
338
  } else {
291
- console.warn(`Error in concurrent request: ${response.reason}`);
339
+ this.config.logger?.warn({
340
+ error: response.reason instanceof Error ? {
341
+ name: response.reason.name,
342
+ message: response.reason.message
343
+ } : response.reason
344
+ }, "Error in concurrent request");
292
345
  }
293
346
  }
294
347
  });
@@ -318,16 +371,26 @@ var BigCommerceClient = class {
318
371
  }
319
372
  const results = [...first.data];
320
373
  const pages = first.meta.pagination.total_pages;
321
- const remainingPages = range(2, pages + 1);
322
- const requests = remainingPages.map((page) => ({
323
- ...options,
324
- endpoint,
325
- query: { ...options.query, page: page.toString() }
326
- }));
327
- const responses = await this.concurrent(requests, options);
328
- responses.forEach((response) => {
329
- results.push(...response.data);
330
- });
374
+ if (pages > 1) {
375
+ this.config.logger?.debug({
376
+ totalPages: pages,
377
+ itemsPerPage: first.data.length
378
+ }, "Collecting remaining pages");
379
+ const pageRequests = rangeArray(2, pages).map((page) => ({
380
+ endpoint,
381
+ method: "GET",
382
+ query: {
383
+ ...options.query,
384
+ page: page.toString()
385
+ }
386
+ }));
387
+ const remainingPages = await this.concurrent(pageRequests, options);
388
+ remainingPages.forEach((page) => {
389
+ if (Array.isArray(page.data)) {
390
+ results.push(...page.data);
391
+ }
392
+ });
393
+ }
331
394
  return results;
332
395
  }
333
396
  /**
@@ -352,7 +415,7 @@ var BigCommerceClient = class {
352
415
  let page = 1;
353
416
  const concurrency = options.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY;
354
417
  while (!done) {
355
- const pages = range(page, page + concurrency);
418
+ const pages = rangeArray(page, page + concurrency);
356
419
  page += concurrency;
357
420
  const requests = pages.map((page2) => ({
358
421
  ...options,
@@ -375,7 +438,12 @@ var BigCommerceClient = class {
375
438
  if (!(options.skipErrors ?? this.config.skipErrors ?? false)) {
376
439
  throw response.reason;
377
440
  } else {
378
- console.warn(`Error in collectV2: ${response.reason}`);
441
+ this.config.logger?.warn({
442
+ error: response.reason instanceof Error ? {
443
+ name: response.reason.name,
444
+ message: response.reason.message
445
+ } : response.reason
446
+ }, "Error in collectV2");
379
447
  }
380
448
  }
381
449
  }
@@ -411,10 +479,15 @@ var BigCommerceClient = class {
411
479
  offset: fullUrl.length,
412
480
  chunkLength: Number.parseInt(options.query?.limit) || MAX_PAGE_SIZE
413
481
  });
414
- const requests = chunks.map((chunk2) => ({
482
+ this.config.logger?.debug({
483
+ totalValues: options.values.length,
484
+ chunks: chunks.length,
485
+ valuesPerChunk: chunks[0]?.length
486
+ }, "Querying with chunked values");
487
+ const requests = chunks.map((chunk) => ({
415
488
  ...options,
416
489
  endpoint,
417
- query: { ...options.query, [options.key]: chunk2.join(",") }
490
+ query: { ...options.query, [options.key]: chunk.join(",") }
418
491
  }));
419
492
  const responses = await this.concurrent(requests, options);
420
493
  return responses.flatMap((response) => response.data);
@@ -424,7 +497,6 @@ var BigCommerceClient = class {
424
497
  // src/auth.ts
425
498
  import ky2 from "ky";
426
499
  import * as jose from "jose";
427
- import { intersection } from "remeda";
428
500
  var GRANT_TYPE = "authorization_code";
429
501
  var TOKEN_ENDPOINT = "https://login.bigcommerce.com/oauth2/token";
430
502
  var ISSUER = "bc";
@@ -456,6 +528,11 @@ var BigCommerceAuth = class {
456
528
  grant_type: GRANT_TYPE,
457
529
  redirect_uri: this.config.redirectUri
458
530
  };
531
+ this.config.logger?.debug({
532
+ clientId: this.config.clientId,
533
+ context: query.context,
534
+ scopes: query.scope
535
+ }, "Requesting OAuth token");
459
536
  const res = await ky2(TOKEN_ENDPOINT, {
460
537
  method: "POST",
461
538
  json: tokenRequest
@@ -476,8 +553,18 @@ var BigCommerceAuth = class {
476
553
  issuer: ISSUER,
477
554
  subject: `stores/${this.config.storeHash}`
478
555
  });
556
+ this.config.logger?.debug({
557
+ userId: payload.user?.id,
558
+ storeHash: this.config.storeHash
559
+ }, "JWT verified successfully");
479
560
  return payload;
480
561
  } catch (error) {
562
+ this.config.logger?.error({
563
+ error: error instanceof Error ? {
564
+ name: error.name,
565
+ message: error.message
566
+ } : error
567
+ });
481
568
  throw new Error("Invalid JWT payload", { cause: error });
482
569
  }
483
570
  }
@@ -520,12 +607,14 @@ var BigCommerceAuth = class {
520
607
  * @throws {Error} If the scopes don't match the expected scopes
521
608
  */
522
609
  validateScopes(scopes) {
523
- const scopesArray = scopes.split(" ");
524
- if (this.config.scopes?.length) {
525
- const int = intersection(scopesArray, this.config.scopes);
526
- if (int.length !== scopesArray.length) {
527
- throw new Error(`Scope mismatch: ${scopes}; expected: ${this.config.scopes.join(" ")}`);
528
- }
610
+ if (!this.config.scopes) {
611
+ return;
612
+ }
613
+ const grantedScopes = scopes.split(" ");
614
+ const requiredScopes = this.config.scopes;
615
+ const missingScopes = requiredScopes.filter((scope) => !grantedScopes.includes(scope));
616
+ if (missingScopes.length) {
617
+ throw new Error(`Scope mismatch: ${scopes}; expected: ${this.config.scopes.join(" ")}`);
529
618
  }
530
619
  }
531
620
  };
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/net.ts", "../src/client.ts", "../src/util.ts", "../src/auth.ts"],
4
- "sourcesContent": ["/**\n * Network utilities for interacting with the BigCommerce API.\n * Provides rate-limited request handling, error management, and type-safe API calls.\n */\n\nimport ky, { ResponsePromise, KyResponse, HTTPError } from 'ky';\n\n/** HTTP methods supported by the API */\nexport type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\nexport const Methods: Record<string, Method> = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n DELETE: 'DELETE',\n} as const;\n\nexport const BASE_URL = 'https://api.bigcommerce.com/stores/';\n\n/** Configuration for the BigCommerce API client */\nconst CONFIG = {\n /** Base URL for BigCommerce API */\n BASE_URL,\n /** Default API version to use */\n DEFAULT_VERSION: 'v3',\n /** Maximum delay in milliseconds for rate limit retries */\n DEFAULT_MAX_DELAY: 60e3,\n /** Maximum allowed URL length */\n MAX_URL_LENGTH: 2048,\n /** Default maximum number of retries for rate-limited requests */\n DEFAULT_MAX_RETRIES: 5,\n /** Rate limit header names */\n HEADERS: {\n /** Time window for rate limiting in milliseconds */\n WINDOW: 'x-rate-limit-time-window-ms',\n /** Time to wait before retrying after rate limit in milliseconds */\n RETRY_AFTER: 'x-rate-limit-time-reset-ms',\n /** Total request quota for the time window */\n REQUEST_QUOTA: 'x-rate-limit-requests-quota',\n /** Number of requests remaining in the current window */\n REQUESTS_LEFT: 'x-rate-limit-requests-left',\n }\n} as const;\n\n/** Supported BigCommerce API versions */\nexport type ApiVersion = 'v3' | 'v2';\n\n/**\n * Options for making API requests\n * @template T - Type of the request body\n */\nexport type RequestOptions<T> = {\n /** API endpoint to call */\n endpoint: string;\n /** HTTP method to use */\n method?: Method;\n /** Request body data */\n body?: T;\n /** API version to use */\n version?: ApiVersion;\n /** Query parameters to append to the URL */\n query?: Record<string, string>;\n};\n\nexport type StoreOptions = {\n /** BigCommerce store hash */\n storeHash: string;\n /** API access token */\n accessToken: string;\n}\n\n/**\n * Options for rate limit handling\n */\nexport type RateLimitOptions = {\n /** Maximum delay in milliseconds before giving up on rate-limited requests */\n maxDelay?: number;\n /** Maximum number of retries for rate-limited requests */\n maxRetries?: number;\n};\n\n/**\n * Custom error class for API request failures\n * @template T - Type of the error data\n */\nexport class RequestError<T> extends Error {\n constructor(\n public status: number,\n public message: string,\n public data: T | string,\n public cause?: unknown,\n ) {\n super(message, { cause });\n }\n}\n\n/**\n * Makes an API request with rate limit handling\n * @template T - Type of the request body and response\n * @param options - Request options including rate limit settings\n * @returns Promise resolving to the API response\n * @throws {RequestError} If the request fails or rate limit is exceeded\n */\nexport const request = async <T, R>(options: RequestOptions<T> & RateLimitOptions & StoreOptions): Promise<R> => {\n const { maxDelay = CONFIG.DEFAULT_MAX_DELAY, maxRetries = CONFIG.DEFAULT_MAX_RETRIES } = options;\n\n let retries = 0;\n let lastError: RequestError<T> | null = null;\n\n while (retries < maxRetries) {\n try {\n return await safeRequest<T, R>(options);\n } catch (error) {\n const err = error as RequestError<T>;\n lastError = err;\n\n if (err.status === 429 && typeof err.data === 'object' && err.data !== null && 'headers' in err.data) {\n const headers = err.data.headers as Record<string, string>;\n\n const retryAfter = Number.parseInt(headers[CONFIG.HEADERS.RETRY_AFTER]);\n\n if (Number.isNaN(retryAfter)) {\n throw new RequestError(\n err.status,\n `Failed to parse retry after: ${headers[CONFIG.HEADERS.RETRY_AFTER]}, ${err.message}`,\n err.data,\n err.cause,\n );\n }\n\n if (retryAfter > maxDelay) {\n throw new RequestError(\n err.status,\n `Rate limit exceeded: ${retryAfter}ms, ${err.message}`,\n err.data,\n err.cause,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n retries++;\n continue;\n }\n\n throw err;\n }\n }\n\n throw lastError ?? new RequestError(500, 'Failed to make request', 'Too many retries after rate limit');\n};\n\n/**\n * Makes a single API request with error handling\n * @template T - Type of the request body and response\n * @param options - Request options\n * @returns Promise resolving to the API response\n * @throws {RequestError} If the request fails\n */ \nconst safeRequest = async <T, R>(options: RequestOptions<T> & StoreOptions): Promise<R> => {\n let res: KyResponse<T>;\n\n try {\n res = await call<T, R>(options);\n } catch (error) {\n if(error instanceof RequestError) {\n throw error;\n }\n\n if(!(error instanceof HTTPError)) {\n throw error;\n }\n\n let data: unknown;\n let errorMessage = error.message;\n\n try {\n data = await error.response.text();\n try {\n data = JSON.parse(data as string);\n\n if (typeof data === 'object' && data !== null && 'message' in data) {\n errorMessage = data.message as string;\n }\n } catch {\n // If JSON parsing fails, keep the text response\n }\n } catch {\n data = 'Failed to read error response';\n }\n\n throw new RequestError(\n error?.response?.status ?? 500,\n errorMessage,\n {\n data,\n headers: Object.fromEntries(error?.response?.headers?.entries() ?? []),\n },\n error,\n );\n }\n\n const text = await res.text();\n\n\n if(res.status === 204) {\n return undefined as unknown as R;\n }\n\n try {\n return JSON.parse(text);\n } catch (error) {\n throw new RequestError(\n res.status,\n `Failed to parse response: ${text}`,\n text,\n error\n );\n }\n};\n\n/**\n * Internal function to make the actual HTTP request\n * @template T - Type of the request body and response\n * @param options - Request options\n * @returns Promise resolving to the raw response\n * @throws {RequestError} If the URL is too long or request fails\n */\nconst call = <T, R>(options: RequestOptions<T> & StoreOptions): ResponsePromise<R> => {\n const { storeHash, accessToken, endpoint, method = 'GET', body, version = CONFIG.DEFAULT_VERSION, query } = options;\n\n const url = `${CONFIG.BASE_URL}${storeHash}/${version}/${endpoint.replace(/^\\//, '')}`;\n \n // Check URL length including search params\n const searchParams = query ? new URLSearchParams(query).toString() : '';\n const fullUrl = searchParams ? `${url}?${searchParams}` : url;\n \n if (fullUrl.length > CONFIG.MAX_URL_LENGTH) {\n throw new RequestError(\n 400,\n 'URL too long',\n `URL length ${fullUrl.length} exceeds maximum allowed length of ${CONFIG.MAX_URL_LENGTH}`,\n );\n }\n\n const request = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'X-Auth-Token': accessToken,\n },\n json: body,\n };\n\n return ky<R>(fullUrl, request);\n};\n", "import { V3Resource } from './core';\nimport { BASE_URL, RateLimitOptions, RequestError, RequestOptions, StoreOptions, request } from './net';\nimport { chunk, range } from 'remeda';\nimport { chunkStrLength } from './util';\n\nconst MAX_PAGE_SIZE = 250;\nconst DEFAULT_CONCURRENCY = 10;\n\n/**\n * Options for GET requests to the BigCommerce API\n */\nexport type GetOptions = {\n /** Query parameters to include in the request */\n query?: Record<string, string>;\n /** API version to use (v2 or v3) */\n version?: 'v2' | 'v3';\n};\n\n/**\n * Options for POST/PUT requests to the BigCommerce API\n */\nexport type PostOptions<T> = GetOptions & {\n /** Request body data */\n body: T;\n};\n\n/**\n * Options for controlling concurrent request behavior\n */\nexport type ConcurrencyOptions = {\n /** Maximum number of concurrent requests (default: 10) */\n concurrency?: number;\n /** Whether to skip errors and continue processing (default: false) */\n skipErrors?: boolean;\n};\n\n/**\n * Options for querying multiple values against a single filter field\n */\nexport type QueryOptions = Omit<GetOptions, 'version'> & ConcurrencyOptions & {\n /** The field name to query against */\n key: string;\n /** Array of values to query for */\n values: (string | number)[];\n};\n\n/**\n * Configuration options for the BigCommerce client\n */\nexport type Config = StoreOptions & RateLimitOptions & ConcurrencyOptions;\n\n/**\n * Client for interacting with the BigCommerce API\n * \n * This client provides methods for making HTTP requests to the BigCommerce API,\n * with support for both v2 and v3 endpoints, pagination, and concurrent requests.\n */\nexport class BigCommerceClient {\n /**\n * Creates a new BigCommerce client instance\n * @param config.storeHash - The store hash to use for the client\n * @param config.accessToken - The API access token to use for the client\n * @param config.maxRetries - (default: 5) The maximum number of retries for rate limit errors\n * @param config.maxDelay - (default: 60e3 - 1 minute) Maximum time to wait to retry in case of rate limit errors. If `X-Rate-Limit-Time-Reset-Ms` header is higher than `maxDelay`, the request will fail immediately.\n * @param config.concurrency - (default: 10) The default concurrency for concurrent methods\n * @param config.skipErrors - (default: false) Whether to skip errors during concurrent requests\n */\n constructor(private readonly config: Config) {}\n\n /**\n * Makes a GET request to the BigCommerce API\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @returns Promise resolving to the response data of type `R`\n */\n async get<R>(endpoint: string, options?: GetOptions): Promise<R> {\n return request<never, R>({\n endpoint,\n method: 'GET',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Makes a POST request to the BigCommerce API\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @param options.body - Request body data of type `T`\n * @returns Promise resolving to the response data of type `R`\n */\n async post<T, R>(endpoint: string, options?: PostOptions<T>): Promise<R> {\n return request<T, R>({\n endpoint,\n method: 'POST',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Makes a PUT request to the BigCommerce API\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @param options.body - Request body data of type `T`\n * @returns Promise resolving to the response data of type `R`\n */\n async put<T, R>(endpoint: string, options?: PostOptions<T>): Promise<R> {\n return request<T, R>({\n endpoint,\n method: 'PUT',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Makes a DELETE request to the BigCommerce API\n * @param endpoint - The API endpoint to delete\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @returns Promise resolving to void\n */\n async delete<R>(endpoint: string, options?: Pick<GetOptions, 'version'>): Promise<void> {\n await request<never, R>({\n endpoint,\n method: 'DELETE',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Executes multiple requests concurrently with controlled concurrency\n * @param requests - Array of request options to execute\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of response data\n */\n async concurrent<T, R>(requests: RequestOptions<T>[], options?: ConcurrencyOptions): Promise<R[]> {\n const chunks = chunk(requests, options?.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY);\n const skipErrors = options?.skipErrors ?? this.config.skipErrors ?? false;\n\n const results: R[] = [];\n\n for (const chunk of chunks) {\n const responses = await Promise.allSettled(\n chunk.map((opt) =>\n request<T, R>({\n ...opt,\n ...this.config,\n }),\n ),\n );\n\n responses.forEach((response) => {\n if (response.status === 'fulfilled') {\n results.push(response.value);\n } else {\n if (!skipErrors) {\n throw response.reason;\n } else {\n console.warn(`Error in concurrent request: ${response.reason}`);\n }\n }\n });\n }\n\n return results;\n }\n\n /**\n * Collects all pages of data from a paginated v3 API endpoint.\n * This method pulls the first page and uses pagination meta to collect the remaining pages concurrently.\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of all items across all pages\n */\n async collect<T>(endpoint: string, options: Omit<GetOptions, 'version'> & ConcurrencyOptions): Promise<T[]> {\n if (options.query) {\n if (!options.query.limit) {\n options.query.limit = MAX_PAGE_SIZE.toString();\n }\n } else {\n options.query = { limit: MAX_PAGE_SIZE.toString() };\n }\n\n const first = await this.get<V3Resource<T[]>>(endpoint, options);\n\n if (!Array.isArray(first.data) || !first?.meta?.pagination?.total_pages) {\n return first.data;\n }\n\n const results: T[] = [...first.data];\n const pages = first.meta.pagination.total_pages;\n\n const remainingPages = range(2, pages + 1);\n\n const requests = remainingPages.map((page) => ({\n ...options,\n endpoint,\n query: { ...options.query, page: page.toString() },\n }));\n\n const responses = await this.concurrent<never, V3Resource<T[]>>(requests, options);\n\n responses.forEach((response) => {\n results.push(...response.data);\n });\n\n return results;\n }\n\n /**\n * Collects all pages of data from a paginated v2 API endpoint.\n * This method simply pulls all pages concurrently until a 204 is returned in a batch.\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of all items across all pages\n */\n async collectV2<T>(endpoint: string, options: Omit<GetOptions, 'version'> & ConcurrencyOptions): Promise<T[]> {\n if (options.query) {\n if (!options.query.limit) {\n options.query.limit = MAX_PAGE_SIZE.toString();\n }\n } else {\n options.query = { limit: MAX_PAGE_SIZE.toString() };\n }\n\n let done = false;\n const results: T[] = [];\n let page = 1;\n const concurrency = options.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY;\n\n while (!done) {\n const pages = range(page, page + concurrency);\n page += concurrency;\n\n const requests = pages.map((page) => ({\n ...options,\n endpoint,\n version: 'v2' as const,\n query: { ...options.query, page: page.toString() },\n }));\n\n const responses = await Promise.allSettled(requests.map((request) => this.get<T[]>(endpoint, request)));\n\n responses.forEach((response) => {\n if (response.status === 'fulfilled') {\n if (response.value) {\n results.push(...response.value);\n } else {\n done = true;\n }\n } else {\n if (response.reason instanceof RequestError && response.reason.status === 404) {\n done = true;\n } else {\n if (!(options.skipErrors ?? this.config.skipErrors ?? false)) {\n throw response.reason;\n } else {\n console.warn(`Error in collectV2: ${response.reason}`);\n }\n }\n }\n });\n }\n\n return results;\n }\n\n /**\n * Queries multiple values against a single field using the v3 API. \n * If the url + query params are too long, the query will be chunked. Otherwise, this method acts like `collect`.\n * This method does not check for uniqueness of the `values` array.\n * \n * @param endpoint - The API endpoint to request\n * @param options.key - The field name to query against e.g. `sku:in`\n * @param options.values - Array of values to query for e.g. `['123', '456', ...]`\n * @param options.query - Additional query parameters\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of matching items\n */\n async query<T>(endpoint: string, options: QueryOptions): Promise<T[]> {\n if(options.query) {\n if(!options.query.limit) {\n options.query.limit = MAX_PAGE_SIZE.toString();\n }\n } else {\n options.query = { limit: MAX_PAGE_SIZE.toString() };\n }\n\n const {limit:_, ...restQuery} = options.query;\n // Only needed to calculate the offset for chunking\n const fullUrl = `${BASE_URL}${this.config.storeHash}/v3/${endpoint}?${new URLSearchParams(restQuery).toString()}`;\n\n const queryStr = options.values.map((value) => `${value}`)\n const chunks = chunkStrLength(queryStr, {\n offset: fullUrl.length,\n chunkLength: Number.parseInt(options.query?.limit) || MAX_PAGE_SIZE,\n });\n\n const requests = chunks.map((chunk) => ({\n ...options,\n endpoint,\n query: { ...options.query, [options.key]: chunk.join(',') },\n }));\n\n const responses = await this.concurrent<never, V3Resource<T[]>>(requests, options);\n\n return responses.flatMap((response) => response.data);\n }\n}\n", "/**\n * Split an array of strings into chunks by following logic\n *\n * 1. add length of each string + separatorSize to offset\n * 2. if result is greater than `maxLength`, start a new chunk\n * 3. otherwise, add the string to the current chunk until the chunk is of `chunkLength`\n *\n * This function to be used for splitting query params to avoid url length limit\n *\n * @param items array of strings\n * @param options\n * @param options.maxLength max length of the combined strings\n * @param options.chunkLength max length of each chunk\n * @param options.offset offset of the first chunk\n * @param options.separatorSize size of the separator\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 = item.length + separatorSize;\n\n const newCurrentStrLength = currentStrLength + itemLength;\n // Check if adding this item would exceed maxLength\n if (newCurrentStrLength > maxLength) {\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n currentChunk = [];\n currentStrLength = offset;\n }\n }\n\n // Check if current chunk is full\n if (currentChunk.length === chunkLength) {\n chunks.push(currentChunk);\n currentChunk = [];\n currentStrLength = offset;\n }\n\n currentChunk.push(item);\n currentStrLength += itemLength;\n }\n\n // Add the last chunk if it's not empty\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n\n return chunks;\n};\n", "import ky from 'ky';\nimport * as jose from 'jose';\nimport { intersection } from 'remeda';\n\n/**\n * Configuration options for BigCommerce authentication\n */\ntype Config = {\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 /** The store hash for the BigCommerce store */\n storeHash: string;\n /** Optional array of scopes to validate during auth callback */\n scopes?: string[];\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 */\ntype AuthQuery = {\n /** The BigCommerce account UUID */\n account_uuid: string;\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 /**\n * Creates a new BigCommerceAuth instance\n * @param config - Configuration options for BigCommerce authentication\n * @throws {Error} If the redirect URI is invalid\n */\n constructor(private readonly config: Config) {\n try {\n new URL(this.config.redirectUri);\n } catch (error) {\n throw new Error('Invalid redirect URI', { cause: error });\n }\n }\n\n /**\n * Requests an access token from BigCommerce\n * @param data - Either a query string or AuthQuery object containing auth callback data\n * @returns Promise resolving to the token response\n */\n async requestToken(data: string | AuthQuery) {\n const query = typeof data === 'string' ? this.parseQueryString(data) : data;\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 const res = await ky(TOKEN_ENDPOINT, {\n method: 'POST',\n json: tokenRequest,\n });\n\n return res.json<TokenResponse>();\n }\n\n /**\n * Verifies a JWT payload from BigCommerce\n * @param jwtPayload - The JWT string to verify\n * @returns Promise resolving to the verified JWT claims\n * @throws {Error} If the JWT is invalid\n */\n async verify(jwtPayload: string) {\n try {\n const secret = new TextEncoder().encode(this.config.secret);\n\n const { payload } = await jose.jwtVerify(jwtPayload, secret, {\n audience: this.config.clientId,\n issuer: ISSUER,\n subject: `stores/${this.config.storeHash}`,\n });\n\n return payload as Claims;\n } catch (error) {\n throw new Error('Invalid JWT payload', { cause: error });\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 {Error} If required parameters are missing or scopes are invalid\n */\n private parseQueryString(queryString: string): AuthQuery {\n const params = new URLSearchParams(queryString);\n\n // Get required parameters\n const code = params.get('code');\n const scope = params.get('scope');\n const context = params.get('context');\n const account_uuid = params.get('account_uuid');\n\n // Validate required parameters\n if (!code) {\n throw new Error('No code found in query string');\n }\n\n if (!scope) {\n throw new Error('No scope found in query string');\n } else if (this.config.scopes?.length) {\n this.validateScopes(scope);\n }\n\n if (!context) {\n throw new Error('No context found in query string');\n }\n\n if (!account_uuid) {\n throw new Error('No account UUID found in query string');\n }\n\n return {\n account_uuid,\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 {Error} If the scopes don't match the expected scopes\n */\n private validateScopes(scopes: string) {\n const scopesArray = scopes.split(' ');\n\n if (this.config.scopes?.length) {\n const int = intersection(scopesArray, this.config.scopes);\n\n if (int.length !== scopesArray.length) {\n throw new Error(`Scope mismatch: ${scopes}; expected: ${this.config.scopes.join(' ')}`);\n }\n }\n }\n}\n"],
5
- "mappings": ";AAKA,OAAO,MAAmC,iBAAiB;AAKpD,IAAM,UAAkC;AAAA,EAC3C,KAAK;AAAA,EACL,MAAM;AAAA,EACN,KAAK;AAAA,EACL,QAAQ;AACZ;AAEO,IAAM,WAAW;AAGxB,IAAM,SAAS;AAAA;AAAA,EAEX;AAAA;AAAA,EAEA,iBAAiB;AAAA;AAAA,EAEjB,mBAAmB;AAAA;AAAA,EAEnB,gBAAgB;AAAA;AAAA,EAEhB,qBAAqB;AAAA;AAAA,EAErB,SAAS;AAAA;AAAA,IAEL,QAAQ;AAAA;AAAA,IAER,aAAa;AAAA;AAAA,IAEb,eAAe;AAAA;AAAA,IAEf,eAAe;AAAA,EACnB;AACJ;AA2CO,IAAM,eAAN,cAA8B,MAAM;AAAA,EACvC,YACW,QACA,SACA,MACA,OACT;AACE,UAAM,SAAS,EAAE,MAAM,CAAC;AALjB;AACA;AACA;AACA;AAAA,EAGX;AACJ;AASO,IAAM,UAAU,OAAa,YAA6E;AAC7G,QAAM,EAAE,WAAW,OAAO,mBAAmB,aAAa,OAAO,oBAAoB,IAAI;AAEzF,MAAI,UAAU;AACd,MAAI,YAAoC;AAExC,SAAO,UAAU,YAAY;AACzB,QAAI;AACA,aAAO,MAAM,YAAkB,OAAO;AAAA,IAC1C,SAAS,OAAO;AACZ,YAAM,MAAM;AACZ,kBAAY;AAEZ,UAAI,IAAI,WAAW,OAAO,OAAO,IAAI,SAAS,YAAY,IAAI,SAAS,QAAQ,aAAa,IAAI,MAAM;AAClG,cAAM,UAAU,IAAI,KAAK;AAEzB,cAAM,aAAa,OAAO,SAAS,QAAQ,OAAO,QAAQ,WAAW,CAAC;AAEtE,YAAI,OAAO,MAAM,UAAU,GAAG;AAC1B,gBAAM,IAAI;AAAA,YACN,IAAI;AAAA,YACJ,gCAAgC,QAAQ,OAAO,QAAQ,WAAW,CAAC,KAAK,IAAI,OAAO;AAAA,YACnF,IAAI;AAAA,YACJ,IAAI;AAAA,UACR;AAAA,QACJ;AAEA,YAAI,aAAa,UAAU;AACvB,gBAAM,IAAI;AAAA,YACN,IAAI;AAAA,YACJ,wBAAwB,UAAU,OAAO,IAAI,OAAO;AAAA,YACpD,IAAI;AAAA,YACJ,IAAI;AAAA,UACR;AAAA,QACJ;AAEA,cAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAC9D;AACA;AAAA,MACJ;AAEA,YAAM;AAAA,IACV;AAAA,EACJ;AAEA,QAAM,aAAa,IAAI,aAAa,KAAK,0BAA0B,mCAAmC;AAC1G;AASA,IAAM,cAAc,OAAa,YAA0D;AACvF,MAAI;AAEJ,MAAI;AACA,UAAM,MAAM,KAAW,OAAO;AAAA,EAClC,SAAS,OAAO;AACZ,QAAG,iBAAiB,cAAc;AAC9B,YAAM;AAAA,IACV;AAEA,QAAG,EAAE,iBAAiB,YAAY;AAC9B,YAAM;AAAA,IACV;AAEA,QAAI;AACJ,QAAI,eAAe,MAAM;AAEzB,QAAI;AACA,aAAO,MAAM,MAAM,SAAS,KAAK;AACjC,UAAI;AACA,eAAO,KAAK,MAAM,IAAc;AAEhC,YAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,aAAa,MAAM;AAChE,yBAAe,KAAK;AAAA,QACxB;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACJ,QAAQ;AACJ,aAAO;AAAA,IACX;AAEA,UAAM,IAAI;AAAA,MACN,OAAO,UAAU,UAAU;AAAA,MAC3B;AAAA,MACA;AAAA,QACI;AAAA,QACA,SAAS,OAAO,YAAY,OAAO,UAAU,SAAS,QAAQ,KAAK,CAAC,CAAC;AAAA,MACzE;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK;AAG5B,MAAG,IAAI,WAAW,KAAK;AACnB,WAAO;AAAA,EACX;AAEA,MAAI;AACA,WAAO,KAAK,MAAM,IAAI;AAAA,EAC1B,SAAS,OAAO;AACZ,UAAM,IAAI;AAAA,MACN,IAAI;AAAA,MACJ,6BAA6B,IAAI;AAAA,MACjC;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AACJ;AASA,IAAM,OAAO,CAAO,YAAkE;AAClF,QAAM,EAAE,WAAW,aAAa,UAAU,SAAS,OAAO,MAAM,UAAU,OAAO,iBAAiB,MAAM,IAAI;AAE5G,QAAM,MAAM,GAAG,OAAO,QAAQ,GAAG,SAAS,IAAI,OAAO,IAAI,SAAS,QAAQ,OAAO,EAAE,CAAC;AAGpF,QAAM,eAAe,QAAQ,IAAI,gBAAgB,KAAK,EAAE,SAAS,IAAI;AACrE,QAAM,UAAU,eAAe,GAAG,GAAG,IAAI,YAAY,KAAK;AAE1D,MAAI,QAAQ,SAAS,OAAO,gBAAgB;AACxC,UAAM,IAAI;AAAA,MACN;AAAA,MACA;AAAA,MACA,cAAc,QAAQ,MAAM,sCAAsC,OAAO,cAAc;AAAA,IAC3F;AAAA,EACJ;AAEA,QAAMA,WAAU;AAAA,IACZ;AAAA,IACA,SAAS;AAAA,MACL,gBAAgB;AAAA,MAChB,UAAU;AAAA,MACV,gBAAgB;AAAA,IACpB;AAAA,IACA,MAAM;AAAA,EACV;AAEA,SAAO,GAAM,SAASA,QAAO;AACjC;;;AC7PA,SAAS,OAAO,aAAa;;;ACctB,IAAM,iBAAiB,CAC1B,OACA,UAKI,CAAC,MACJ;AACD,QAAM,EAAE,YAAY,MAAM,cAAc,KAAK,SAAS,GAAG,gBAAgB,EAAE,IAAI;AAE/E,QAAM,SAAqB,CAAC;AAC5B,MAAI,mBAAmB;AACvB,MAAI,eAAyB,CAAC;AAE9B,aAAW,QAAQ,OAAO;AACtB,UAAM,aAAa,KAAK,SAAS;AAEjC,UAAM,sBAAsB,mBAAmB;AAE/C,QAAI,sBAAsB,WAAW;AACjC,UAAI,aAAa,SAAS,GAAG;AACzB,eAAO,KAAK,YAAY;AACxB,uBAAe,CAAC;AAChB,2BAAmB;AAAA,MACvB;AAAA,IACJ;AAGA,QAAI,aAAa,WAAW,aAAa;AACrC,aAAO,KAAK,YAAY;AACxB,qBAAe,CAAC;AAChB,yBAAmB;AAAA,IACvB;AAEA,iBAAa,KAAK,IAAI;AACtB,wBAAoB;AAAA,EACxB;AAGA,MAAI,aAAa,SAAS,GAAG;AACzB,WAAO,KAAK,YAAY;AAAA,EAC5B;AAEA,SAAO;AACX;;;ADxDA,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAmDrB,IAAM,oBAAN,MAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU3B,YAA6B,QAAgB;AAAhB;AAAA,EAAiB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS9C,MAAM,IAAO,UAAkB,SAAkC;AAC7D,WAAO,QAAkB;AAAA,MACrB;AAAA,MACA,QAAQ;AAAA,MACR,GAAG;AAAA,MACH,GAAG,KAAK;AAAA,IACZ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,KAAW,UAAkB,SAAsC;AACrE,WAAO,QAAc;AAAA,MACjB;AAAA,MACA,QAAQ;AAAA,MACR,GAAG;AAAA,MACH,GAAG,KAAK;AAAA,IACZ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,IAAU,UAAkB,SAAsC;AACpE,WAAO,QAAc;AAAA,MACjB;AAAA,MACA,QAAQ;AAAA,MACR,GAAG;AAAA,MACH,GAAG,KAAK;AAAA,IACZ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAU,UAAkB,SAAsD;AACpF,UAAM,QAAkB;AAAA,MACpB;AAAA,MACA,QAAQ;AAAA,MACR,GAAG;AAAA,MACH,GAAG,KAAK;AAAA,IACZ,CAAC;AAAA,EACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAAiB,UAA+B,SAA4C;AAC9F,UAAM,SAAS,MAAM,UAAU,SAAS,eAAe,KAAK,OAAO,eAAe,mBAAmB;AACrG,UAAM,aAAa,SAAS,cAAc,KAAK,OAAO,cAAc;AAEpE,UAAM,UAAe,CAAC;AAEtB,eAAWC,UAAS,QAAQ;AACxB,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC5BA,OAAM;AAAA,UAAI,CAAC,QACP,QAAc;AAAA,YACV,GAAG;AAAA,YACH,GAAG,KAAK;AAAA,UACZ,CAAC;AAAA,QACL;AAAA,MACJ;AAEA,gBAAU,QAAQ,CAAC,aAAa;AAC5B,YAAI,SAAS,WAAW,aAAa;AACjC,kBAAQ,KAAK,SAAS,KAAK;AAAA,QAC/B,OAAO;AACH,cAAI,CAAC,YAAY;AACb,kBAAM,SAAS;AAAA,UACnB,OAAO;AACH,oBAAQ,KAAK,gCAAgC,SAAS,MAAM,EAAE;AAAA,UAClE;AAAA,QACJ;AAAA,MACJ,CAAC;AAAA,IACL;AAEA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,QAAW,UAAkB,SAAyE;AACxG,QAAI,QAAQ,OAAO;AACf,UAAI,CAAC,QAAQ,MAAM,OAAO;AACtB,gBAAQ,MAAM,QAAQ,cAAc,SAAS;AAAA,MACjD;AAAA,IACJ,OAAO;AACH,cAAQ,QAAQ,EAAE,OAAO,cAAc,SAAS,EAAE;AAAA,IACtD;AAEA,UAAM,QAAQ,MAAM,KAAK,IAAqB,UAAU,OAAO;AAE/D,QAAI,CAAC,MAAM,QAAQ,MAAM,IAAI,KAAK,CAAC,OAAO,MAAM,YAAY,aAAa;AACrE,aAAO,MAAM;AAAA,IACjB;AAEA,UAAM,UAAe,CAAC,GAAG,MAAM,IAAI;AACnC,UAAM,QAAQ,MAAM,KAAK,WAAW;AAEpC,UAAM,iBAAiB,MAAM,GAAG,QAAQ,CAAC;AAEzC,UAAM,WAAW,eAAe,IAAI,CAAC,UAAU;AAAA,MAC3C,GAAG;AAAA,MACH;AAAA,MACA,OAAO,EAAE,GAAG,QAAQ,OAAO,MAAM,KAAK,SAAS,EAAE;AAAA,IACrD,EAAE;AAEF,UAAM,YAAY,MAAM,KAAK,WAAmC,UAAU,OAAO;AAEjF,cAAU,QAAQ,CAAC,aAAa;AAC5B,cAAQ,KAAK,GAAG,SAAS,IAAI;AAAA,IACjC,CAAC;AAED,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,UAAa,UAAkB,SAAyE;AAC1G,QAAI,QAAQ,OAAO;AACf,UAAI,CAAC,QAAQ,MAAM,OAAO;AACtB,gBAAQ,MAAM,QAAQ,cAAc,SAAS;AAAA,MACjD;AAAA,IACJ,OAAO;AACH,cAAQ,QAAQ,EAAE,OAAO,cAAc,SAAS,EAAE;AAAA,IACtD;AAEA,QAAI,OAAO;AACX,UAAM,UAAe,CAAC;AACtB,QAAI,OAAO;AACX,UAAM,cAAc,QAAQ,eAAe,KAAK,OAAO,eAAe;AAEtE,WAAO,CAAC,MAAM;AACV,YAAM,QAAQ,MAAM,MAAM,OAAO,WAAW;AAC5C,cAAQ;AAER,YAAM,WAAW,MAAM,IAAI,CAACC,WAAU;AAAA,QAClC,GAAG;AAAA,QACH;AAAA,QACA,SAAS;AAAA,QACT,OAAO,EAAE,GAAG,QAAQ,OAAO,MAAMA,MAAK,SAAS,EAAE;AAAA,MACrD,EAAE;AAEF,YAAM,YAAY,MAAM,QAAQ,WAAW,SAAS,IAAI,CAACC,aAAY,KAAK,IAAS,UAAUA,QAAO,CAAC,CAAC;AAEtG,gBAAU,QAAQ,CAAC,aAAa;AAC5B,YAAI,SAAS,WAAW,aAAa;AACjC,cAAI,SAAS,OAAO;AAChB,oBAAQ,KAAK,GAAG,SAAS,KAAK;AAAA,UAClC,OAAO;AACH,mBAAO;AAAA,UACX;AAAA,QACJ,OAAO;AACH,cAAI,SAAS,kBAAkB,gBAAgB,SAAS,OAAO,WAAW,KAAK;AAC3E,mBAAO;AAAA,UACX,OAAO;AACH,gBAAI,EAAE,QAAQ,cAAc,KAAK,OAAO,cAAc,QAAQ;AAC1D,oBAAM,SAAS;AAAA,YACnB,OAAO;AACH,sBAAQ,KAAK,uBAAuB,SAAS,MAAM,EAAE;AAAA,YACzD;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ,CAAC;AAAA,IACL;AAEA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,MAAS,UAAkB,SAAqC;AAClE,QAAG,QAAQ,OAAO;AACd,UAAG,CAAC,QAAQ,MAAM,OAAO;AACrB,gBAAQ,MAAM,QAAQ,cAAc,SAAS;AAAA,MACjD;AAAA,IACJ,OAAO;AACH,cAAQ,QAAQ,EAAE,OAAO,cAAc,SAAS,EAAE;AAAA,IACtD;AAEA,UAAM,EAAC,OAAM,GAAG,GAAG,UAAS,IAAI,QAAQ;AAExC,UAAM,UAAU,GAAG,QAAQ,GAAG,KAAK,OAAO,SAAS,OAAO,QAAQ,IAAI,IAAI,gBAAgB,SAAS,EAAE,SAAS,CAAC;AAE/G,UAAM,WAAW,QAAQ,OAAO,IAAI,CAAC,UAAU,GAAG,KAAK,EAAE;AACzD,UAAM,SAAS,eAAe,UAAU;AAAA,MACpC,QAAQ,QAAQ;AAAA,MAChB,aAAa,OAAO,SAAS,QAAQ,OAAO,KAAK,KAAK;AAAA,IAC1D,CAAC;AAED,UAAM,WAAW,OAAO,IAAI,CAACF,YAAW;AAAA,MACpC,GAAG;AAAA,MACH;AAAA,MACA,OAAO,EAAE,GAAG,QAAQ,OAAO,CAAC,QAAQ,GAAG,GAAGA,OAAM,KAAK,GAAG,EAAE;AAAA,IAC9D,EAAE;AAEF,UAAM,YAAY,MAAM,KAAK,WAAmC,UAAU,OAAO;AAEjF,WAAO,UAAU,QAAQ,CAAC,aAAa,SAAS,IAAI;AAAA,EACxD;AACJ;;;AE/TA,OAAOG,SAAQ;AACf,YAAY,UAAU;AACtB,SAAS,oBAAoB;AAkB7B,IAAM,aAAa;AACnB,IAAM,iBAAiB;AACvB,IAAM,SAAS;AAiGR,IAAM,kBAAN,MAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMzB,YAA6B,QAAgB;AAAhB;AACzB,QAAI;AACA,UAAI,IAAI,KAAK,OAAO,WAAW;AAAA,IACnC,SAAS,OAAO;AACZ,YAAM,IAAI,MAAM,wBAAwB,EAAE,OAAO,MAAM,CAAC;AAAA,IAC5D;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAAa,MAA0B;AACzC,UAAM,QAAQ,OAAO,SAAS,WAAW,KAAK,iBAAiB,IAAI,IAAI;AAEvE,UAAM,eAA6B;AAAA,MAC/B,WAAW,KAAK,OAAO;AAAA,MACvB,eAAe,KAAK,OAAO;AAAA,MAC3B,GAAG;AAAA,MACH,YAAY;AAAA,MACZ,cAAc,KAAK,OAAO;AAAA,IAC9B;AAEA,UAAM,MAAM,MAAMA,IAAG,gBAAgB;AAAA,MACjC,QAAQ;AAAA,MACR,MAAM;AAAA,IACV,CAAC;AAED,WAAO,IAAI,KAAoB;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,OAAO,YAAoB;AAC7B,QAAI;AACA,YAAM,SAAS,IAAI,YAAY,EAAE,OAAO,KAAK,OAAO,MAAM;AAE1D,YAAM,EAAE,QAAQ,IAAI,MAAW,eAAU,YAAY,QAAQ;AAAA,QACzD,UAAU,KAAK,OAAO;AAAA,QACtB,QAAQ;AAAA,QACR,SAAS,UAAU,KAAK,OAAO,SAAS;AAAA,MAC5C,CAAC;AAED,aAAO;AAAA,IACX,SAAS,OAAO;AACZ,YAAM,IAAI,MAAM,uBAAuB,EAAE,OAAO,MAAM,CAAC;AAAA,IAC3D;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,iBAAiB,aAAgC;AACrD,UAAM,SAAS,IAAI,gBAAgB,WAAW;AAG9C,UAAM,OAAO,OAAO,IAAI,MAAM;AAC9B,UAAM,QAAQ,OAAO,IAAI,OAAO;AAChC,UAAM,UAAU,OAAO,IAAI,SAAS;AACpC,UAAM,eAAe,OAAO,IAAI,cAAc;AAG9C,QAAI,CAAC,MAAM;AACP,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACnD;AAEA,QAAI,CAAC,OAAO;AACR,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD,WAAW,KAAK,OAAO,QAAQ,QAAQ;AACnC,WAAK,eAAe,KAAK;AAAA,IAC7B;AAEA,QAAI,CAAC,SAAS;AACV,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACtD;AAEA,QAAI,CAAC,cAAc;AACf,YAAM,IAAI,MAAM,uCAAuC;AAAA,IAC3D;AAEA,WAAO;AAAA,MACH;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,eAAe,QAAgB;AACnC,UAAM,cAAc,OAAO,MAAM,GAAG;AAEpC,QAAI,KAAK,OAAO,QAAQ,QAAQ;AAC5B,YAAM,MAAM,aAAa,aAAa,KAAK,OAAO,MAAM;AAExD,UAAI,IAAI,WAAW,YAAY,QAAQ;AACnC,cAAM,IAAI,MAAM,mBAAmB,MAAM,eAAe,KAAK,OAAO,OAAO,KAAK,GAAG,CAAC,EAAE;AAAA,MAC1F;AAAA,IACJ;AAAA,EACJ;AACJ;",
6
- "names": ["request", "chunk", "page", "request", "ky"]
3
+ "sources": ["../src/net.ts", "../src/util.ts", "../src/client.ts", "../src/auth.ts"],
4
+ "sourcesContent": ["/**\n * Network utilities for interacting with the BigCommerce API.\n * Provides rate-limited request handling, error management, and type-safe API calls.\n */\n\nimport ky, { KyResponse, HTTPError } from 'ky';\nimport { Logger } from './core';\n\n/** HTTP methods supported by the API */\nexport type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\nexport const Methods: Record<string, Method> = {\n GET: 'GET',\n POST: 'POST',\n PUT: 'PUT',\n DELETE: 'DELETE',\n} as const;\n\nexport const BASE_URL = 'https://api.bigcommerce.com/stores/';\n\n/** Configuration for the BigCommerce API client */\nconst CONFIG = {\n /** Base URL for BigCommerce API */\n BASE_URL,\n /** Default API version to use */\n DEFAULT_VERSION: 'v3',\n /** Maximum delay in milliseconds for rate limit retries */\n DEFAULT_MAX_DELAY: 60e3,\n /** Maximum allowed URL length */\n MAX_URL_LENGTH: 2048,\n /** Default maximum number of retries for rate-limited requests */\n DEFAULT_MAX_RETRIES: 5,\n /** Rate limit header names */\n HEADERS: {\n /** Time window for rate limiting in milliseconds */\n WINDOW: 'x-rate-limit-time-window-ms',\n /** Time to wait before retrying after rate limit in milliseconds */\n RETRY_AFTER: 'x-rate-limit-time-reset-ms',\n /** Total request quota for the time window */\n REQUEST_QUOTA: 'x-rate-limit-requests-quota',\n /** Number of requests remaining in the current window */\n REQUESTS_LEFT: 'x-rate-limit-requests-left',\n }\n} as const;\n\n/** Supported BigCommerce API versions */\nexport type ApiVersion = 'v3' | 'v2';\n\n/**\n * Options for making API requests\n * @template T - Type of the request body\n */\nexport type RequestOptions<T> = {\n /** API endpoint to call */\n endpoint: string;\n /** HTTP method to use */\n method?: Method;\n /** Request body data */\n body?: T;\n /** API version to use */\n version?: ApiVersion;\n /** Query parameters to append to the URL */\n query?: Record<string, string>;\n};\n\nexport type StoreOptions = {\n /** BigCommerce store hash */\n storeHash: string;\n /** API access token */\n accessToken: string;\n}\n\n/**\n * Options for rate limit handling\n */\nexport type RateLimitOptions = {\n /** Maximum delay in milliseconds before giving up on rate-limited requests */\n maxDelay?: number;\n /** Maximum number of retries for rate-limited requests */\n maxRetries?: number;\n};\n\n/**\n * Custom error class for API request failures\n * @template T - Type of the error data\n */\nexport class RequestError<T> extends Error {\n constructor(\n public status: number,\n public message: string,\n public data: T | string,\n public cause?: unknown,\n ) {\n super(message, { cause });\n }\n}\n\n/**\n * Makes an API request with rate limit handling\n * @template T - Type of the request body and response\n * @param options - Request options including rate limit settings\n * @returns Promise resolving to the API response\n * @throws {RequestError} If the request fails or rate limit is exceeded\n */\nexport const request = async <T, R>(options: RequestOptions<T> & RateLimitOptions & StoreOptions & {\n logger?: Logger;\n}): Promise<R> => {\n const { maxDelay = CONFIG.DEFAULT_MAX_DELAY, maxRetries = CONFIG.DEFAULT_MAX_RETRIES, logger } = options;\n\n let retries = 0;\n let lastError: RequestError<T> | null = null;\n\n while (retries < maxRetries) {\n try {\n return await safeRequest<T, R>(options);\n } catch (error) {\n const err = error as RequestError<T>;\n lastError = err;\n\n if (err.status === 429 && typeof err.data === 'object' && err.data !== null && 'headers' in err.data) {\n const headers = err.data.headers as Record<string, string>;\n const retryAfter = Number.parseInt(headers[CONFIG.HEADERS.RETRY_AFTER]);\n\n logger?.debug({\n retryAfter,\n retries,\n remaining: headers[CONFIG.HEADERS.REQUESTS_LEFT]\n }, 'Rate limit hit, retrying');\n\n if (Number.isNaN(retryAfter)) {\n throw new RequestError(\n err.status,\n `Failed to parse retry after: ${headers[CONFIG.HEADERS.RETRY_AFTER]}, ${err.message}`,\n err.data,\n err.cause,\n );\n }\n\n if (retryAfter > maxDelay) {\n logger?.warn({\n retryAfter,\n maxDelay\n }, 'Rate limit delay exceeds maximum allowed delay');\n throw new RequestError(\n err.status,\n `Rate limit exceeded: ${retryAfter}ms, ${err.message}`,\n err.data,\n err.cause,\n );\n }\n\n await new Promise((resolve) => setTimeout(resolve, retryAfter));\n retries++;\n continue;\n }\n\n throw err;\n }\n }\n\n logger?.error({\n retries,\n error: lastError\n }, 'Request failed after maximum retries');\n\n throw lastError ?? new RequestError(500, 'Failed to make request', 'Too many retries after rate limit');\n};\n\n/**\n * Makes a single API request with error handling\n * @template T - Type of the request body and response\n * @param options - Request options\n * @returns Promise resolving to the API response\n * @throws {RequestError} If the request fails\n */ \nconst safeRequest = async <T, R>(options: RequestOptions<T> & StoreOptions & {\n logger?: Logger;\n}): Promise<R> => {\n const { logger } = options;\n let res: KyResponse<T>;\n\n try {\n res = await call<T, R>(options);\n } catch (error) {\n if(error instanceof RequestError) {\n throw error;\n }\n\n if(!(error instanceof HTTPError)) {\n logger?.error({\n error: error instanceof Error ? {\n name: error.name,\n message: error.message\n } : error\n }, 'Unexpected error during request');\n throw error;\n }\n\n let data: unknown;\n let errorMessage = error.message;\n\n try {\n data = await error.response.text();\n try {\n data = JSON.parse(data as string);\n\n if (typeof data === 'object' && data !== null && 'message' in data) {\n errorMessage = data.message as string;\n }\n } catch {\n // If JSON parsing fails, keep the text response\n }\n } catch {\n data = 'Failed to read error response';\n }\n\n logger?.error({\n status: error?.response?.status,\n errorMessage\n }, 'HTTP error during request');\n\n throw new RequestError(\n error?.response?.status ?? 500,\n errorMessage,\n {\n data,\n headers: Object.fromEntries(error?.response?.headers?.entries() ?? []),\n },\n error,\n );\n }\n\n const text = await res.text();\n\n if(res.status === 204) {\n return undefined as unknown as R;\n }\n\n try {\n return JSON.parse(text);\n } catch (error) {\n logger?.error({\n status: res.status,\n error: error instanceof Error ? {\n name: error.name,\n message: error.message\n } : error\n }, 'Failed to parse response');\n throw new RequestError(\n res.status,\n `Failed to parse response: ${text}`,\n text,\n error\n );\n }\n};\n\n/**\n * Internal function to make the actual HTTP request\n * @template T - Type of the request body and response\n * @param options - Request options\n * @returns Promise resolving to the raw response\n * @throws {RequestError} If the URL is too long or request fails\n */\nconst call = async <T, R>(options: RequestOptions<T> & StoreOptions & {\n logger?: Logger;\n}): Promise<KyResponse<R>> => {\n const { storeHash, accessToken, endpoint, method = 'GET', body, version = CONFIG.DEFAULT_VERSION, query, logger } = options;\n\n const url = `${CONFIG.BASE_URL}${storeHash}/${version}/${endpoint.replace(/^\\//, '')}`;\n \n // Check URL length including search params\n const searchParams = query ? new URLSearchParams(query).toString() : '';\n const fullUrl = searchParams ? `${url}?${searchParams}` : url;\n \n if (fullUrl.length > CONFIG.MAX_URL_LENGTH) {\n logger?.error({\n urlLength: fullUrl.length,\n maxLength: CONFIG.MAX_URL_LENGTH\n }, 'URL length exceeds maximum allowed length');\n throw new RequestError(\n 400,\n 'URL too long',\n `URL length ${fullUrl.length} exceeds maximum allowed length of ${CONFIG.MAX_URL_LENGTH}`,\n );\n }\n\n const request = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n 'Accept': 'application/json',\n 'X-Auth-Token': accessToken,\n },\n json: body,\n };\n\n const response = await ky<R>(fullUrl, request);\n return response;\n};\n", "/**\n * Split an array of strings into chunks by following logic\n *\n * 1. add length of each string + separatorSize to offset\n * 2. if result is greater than `maxLength`, start a new chunk\n * 3. otherwise, add the string to the current chunk until the chunk is of `chunkLength`\n *\n * This function to be used for splitting query params to avoid url length limit\n *\n * @param items array of strings\n * @param options\n * @param options.maxLength max length of the combined strings\n * @param options.chunkLength max length of each chunk\n * @param options.offset offset of the first chunk\n * @param options.separatorSize size of the separator\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 = item.length + separatorSize;\n\n const newCurrentStrLength = currentStrLength + itemLength;\n // Check if adding this item would exceed maxLength\n if (newCurrentStrLength > maxLength) {\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n currentChunk = [];\n currentStrLength = offset;\n }\n }\n\n // Check if current chunk is full\n if (currentChunk.length === chunkLength) {\n chunks.push(currentChunk);\n currentChunk = [];\n currentStrLength = offset;\n }\n\n currentChunk.push(item);\n currentStrLength += itemLength;\n }\n\n // Add the last chunk if it's not empty\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n\n return chunks;\n};\n", "import { V3Resource, Logger } from './core';\nimport { BASE_URL, RateLimitOptions, RequestError, RequestOptions, StoreOptions, request } from './net';\nimport { chunkStrLength } from './util';\n\nconst MAX_PAGE_SIZE = 250;\nconst DEFAULT_CONCURRENCY = 10;\n\n// Helper function to chunk array into smaller arrays\nfunction chunkArray<T>(array: T[], size: number): T[][] {\n return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>\n array.slice(i * size, i * size + size)\n );\n}\n\n// Helper function to create range array\nfunction rangeArray(start: number, end: number): number[] {\n return Array.from({ length: end - start + 1 }, (_, i) => start + i);\n}\n\n/**\n * Options for GET requests to the BigCommerce API\n */\nexport type GetOptions = {\n /** Query parameters to include in the request */\n query?: Record<string, string>;\n /** API version to use (v2 or v3) */\n version?: 'v2' | 'v3';\n};\n\n/**\n * Options for POST/PUT requests to the BigCommerce API\n */\nexport type PostOptions<T> = GetOptions & {\n /** Request body data */\n body: T;\n};\n\n/**\n * Options for controlling concurrent request behavior\n */\nexport type ConcurrencyOptions = {\n /** Maximum number of concurrent requests (default: 10) */\n concurrency?: number;\n /** Whether to skip errors and continue processing (default: false) */\n skipErrors?: boolean;\n};\n\n/**\n * Options for querying multiple values against a single filter field\n */\nexport type QueryOptions = Omit<GetOptions, 'version'> & ConcurrencyOptions & {\n /** The field name to query against */\n key: string;\n /** Array of values to query for */\n values: (string | number)[];\n};\n\n/**\n * Configuration options for the BigCommerce client\n */\nexport type Config = StoreOptions & RateLimitOptions & ConcurrencyOptions & {\n /** Logger instance */\n logger?: Logger;\n};\n\n/**\n * Client for interacting with the BigCommerce API\n * \n * This client provides methods for making HTTP requests to the BigCommerce API,\n * with support for both v2 and v3 endpoints, pagination, and concurrent requests.\n */\nexport class BigCommerceClient {\n /**\n * Creates a new BigCommerce client instance\n * @param config.storeHash - The store hash to use for the client\n * @param config.accessToken - The API access token to use for the client\n * @param config.maxRetries - (default: 5) The maximum number of retries for rate limit errors\n * @param config.maxDelay - (default: 60e3 - 1 minute) Maximum time to wait to retry in case of rate limit errors. If `X-Rate-Limit-Time-Reset-Ms` header is higher than `maxDelay`, the request will fail immediately.\n * @param config.concurrency - (default: 10) The default concurrency for concurrent methods\n * @param config.skipErrors - (default: false) Whether to skip errors during concurrent requests\n */\n constructor(private readonly config: Config) {}\n\n /**\n * Makes a GET request to the BigCommerce API\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @returns Promise resolving to the response data of type `R`\n */\n async get<R>(endpoint: string, options?: GetOptions): Promise<R> {\n return request<never, R>({\n endpoint,\n method: 'GET',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Makes a POST request to the BigCommerce API\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @param options.body - Request body data of type `T`\n * @returns Promise resolving to the response data of type `R`\n */\n async post<T, R>(endpoint: string, options?: PostOptions<T>): Promise<R> {\n return request<T, R>({\n endpoint,\n method: 'POST',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Makes a PUT request to the BigCommerce API\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @param options.body - Request body data of type `T`\n * @returns Promise resolving to the response data of type `R`\n */\n async put<T, R>(endpoint: string, options?: PostOptions<T>): Promise<R> {\n return request<T, R>({\n endpoint,\n method: 'PUT',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Makes a DELETE request to the BigCommerce API\n * @param endpoint - The API endpoint to delete\n * @param options.version - API version to use (v2 or v3) (default: v3)\n * @returns Promise resolving to void\n */\n async delete<R>(endpoint: string, options?: Pick<GetOptions, 'version'>): Promise<void> {\n await request<never, R>({\n endpoint,\n method: 'DELETE',\n ...options,\n ...this.config,\n });\n }\n\n /**\n * Executes multiple requests concurrently with controlled concurrency\n * @param requests - Array of request options to execute\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of response data\n */\n async concurrent<T, R>(requests: RequestOptions<T>[], options?: ConcurrencyOptions): Promise<R[]> {\n const chunkSize = options?.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY;\n const chunks = chunkArray(requests, chunkSize);\n const skipErrors = options?.skipErrors ?? this.config.skipErrors ?? false;\n\n const results: R[] = [];\n\n this.config.logger?.debug({\n totalRequests: requests.length,\n chunkSize,\n chunks: chunks.length\n }, 'Starting concurrent requests');\n\n for (const chunk of chunks) {\n const responses = await Promise.allSettled(\n chunk.map((opt) =>\n request<T, R>({\n ...opt,\n ...this.config,\n }),\n ),\n );\n\n responses.forEach((response) => {\n if (response.status === 'fulfilled') {\n results.push(response.value);\n } else {\n if (!skipErrors) {\n throw response.reason;\n } else {\n this.config.logger?.warn({\n error: response.reason instanceof Error ? {\n name: response.reason.name,\n message: response.reason.message\n } : response.reason\n }, 'Error in concurrent request');\n }\n }\n });\n }\n\n return results;\n }\n\n /**\n * Collects all pages of data from a paginated v3 API endpoint.\n * This method pulls the first page and uses pagination meta to collect the remaining pages concurrently.\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of all items across all pages\n */\n async collect<T>(endpoint: string, options: Omit<GetOptions, 'version'> & ConcurrencyOptions): Promise<T[]> {\n if (options.query) {\n if (!options.query.limit) {\n options.query.limit = MAX_PAGE_SIZE.toString();\n }\n } else {\n options.query = { limit: MAX_PAGE_SIZE.toString() };\n }\n\n const first = await this.get<V3Resource<T[]>>(endpoint, options);\n\n if (!Array.isArray(first.data) || !first?.meta?.pagination?.total_pages) {\n return first.data;\n }\n\n const results: T[] = [...first.data];\n const pages = first.meta.pagination.total_pages;\n\n if (pages > 1) {\n this.config.logger?.debug({\n totalPages: pages,\n itemsPerPage: first.data.length\n }, 'Collecting remaining pages');\n\n const pageRequests = rangeArray(2, pages).map((page) => ({\n endpoint,\n method: 'GET' as const,\n query: {\n ...options.query,\n page: page.toString(),\n },\n }));\n\n const remainingPages = await this.concurrent<never, V3Resource<T[]>>(pageRequests, options);\n\n remainingPages.forEach((page) => {\n if (Array.isArray(page.data)) {\n results.push(...page.data);\n }\n });\n }\n\n return results;\n }\n\n /**\n * Collects all pages of data from a paginated v2 API endpoint.\n * This method simply pulls all pages concurrently until a 204 is returned in a batch.\n * @param endpoint - The API endpoint to request\n * @param options.query - Query parameters to include in the request\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of all items across all pages\n */\n async collectV2<T>(endpoint: string, options: Omit<GetOptions, 'version'> & ConcurrencyOptions): Promise<T[]> {\n if (options.query) {\n if (!options.query.limit) {\n options.query.limit = MAX_PAGE_SIZE.toString();\n }\n } else {\n options.query = { limit: MAX_PAGE_SIZE.toString() };\n }\n\n let done = false;\n const results: T[] = [];\n let page = 1;\n const concurrency = options.concurrency ?? this.config.concurrency ?? DEFAULT_CONCURRENCY;\n\n while (!done) {\n const pages = rangeArray(page, page + concurrency);\n page += concurrency;\n\n const requests = pages.map((page) => ({\n ...options,\n endpoint,\n version: 'v2' as const,\n query: { ...options.query, page: page.toString() },\n }));\n\n const responses = await Promise.allSettled(requests.map((request) => this.get<T[]>(endpoint, request)));\n\n responses.forEach((response) => {\n if (response.status === 'fulfilled') {\n if (response.value) {\n results.push(...response.value);\n } else {\n done = true;\n }\n } else {\n if (response.reason instanceof RequestError && response.reason.status === 404) {\n done = true;\n } else {\n if (!(options.skipErrors ?? this.config.skipErrors ?? false)) {\n throw response.reason;\n } else {\n this.config.logger?.warn({\n error: response.reason instanceof Error ? {\n name: response.reason.name,\n message: response.reason.message\n } : response.reason\n }, 'Error in collectV2');\n }\n }\n }\n });\n }\n\n return results;\n }\n\n /**\n * Queries multiple values against a single field using the v3 API. \n * If the url + query params are too long, the query will be chunked. Otherwise, this method acts like `collect`.\n * This method does not check for uniqueness of the `values` array.\n * \n * @param endpoint - The API endpoint to request\n * @param options.key - The field name to query against e.g. `sku:in`\n * @param options.values - Array of values to query for e.g. `['123', '456', ...]`\n * @param options.query - Additional query parameters\n * @param options.concurrency - Maximum number of concurrent requests, overrides the client's concurrency setting (default: 10)\n * @param options.skipErrors - Whether to skip errors and continue processing, overrides the client's skipErrors setting (default: false)\n * @returns Promise resolving to array of matching items\n */\n async query<T>(endpoint: string, options: QueryOptions): Promise<T[]> {\n if(options.query) {\n if(!options.query.limit) {\n options.query.limit = MAX_PAGE_SIZE.toString();\n }\n } else {\n options.query = { limit: MAX_PAGE_SIZE.toString() };\n }\n\n const {limit:_, ...restQuery} = options.query;\n const fullUrl = `${BASE_URL}${this.config.storeHash}/v3/${endpoint}?${new URLSearchParams(restQuery).toString()}`;\n\n const queryStr = options.values.map((value) => `${value}`)\n const chunks = chunkStrLength(queryStr, {\n offset: fullUrl.length,\n chunkLength: Number.parseInt(options.query?.limit) || MAX_PAGE_SIZE,\n });\n\n this.config.logger?.debug({\n totalValues: options.values.length,\n chunks: chunks.length,\n valuesPerChunk: chunks[0]?.length\n }, 'Querying with chunked values');\n\n const requests = chunks.map((chunk) => ({\n ...options,\n endpoint,\n query: { ...options.query, [options.key]: chunk.join(',') },\n }));\n\n const responses = await this.concurrent<never, V3Resource<T[]>>(requests, options);\n\n return responses.flatMap((response) => response.data);\n }\n}\n", "import ky from 'ky';\nimport * as jose from 'jose';\nimport { Logger } from './core';\n\n/**\n * Configuration options for BigCommerce authentication\n */\ntype Config = {\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 /** The store hash for the BigCommerce store */\n storeHash: string;\n /** Optional array of scopes to validate during auth callback */\n scopes?: string[];\n /** Optional logger instance */\n logger?: Logger;\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 */\ntype AuthQuery = {\n /** The BigCommerce account UUID */\n account_uuid: string;\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 /**\n * Creates a new BigCommerceAuth instance\n * @param config - Configuration options for BigCommerce authentication\n * @throws {Error} If the redirect URI is invalid\n */\n constructor(private readonly config: Config) {\n try {\n new URL(this.config.redirectUri);\n } catch (error) {\n throw new Error('Invalid redirect URI', { cause: error });\n }\n }\n\n /**\n * Requests an access token from BigCommerce\n * @param data - Either a query string or AuthQuery object containing auth callback data\n * @returns Promise resolving to the token response\n */\n async requestToken(data: string | AuthQuery) {\n const query = typeof data === 'string' ? this.parseQueryString(data) : data;\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.config.logger?.debug({\n clientId: this.config.clientId,\n context: query.context,\n scopes: query.scope\n }, 'Requesting OAuth token');\n\n const res = await ky(TOKEN_ENDPOINT, {\n method: 'POST',\n json: tokenRequest,\n });\n\n return res.json<TokenResponse>();\n }\n\n /**\n * Verifies a JWT payload from BigCommerce\n * @param jwtPayload - The JWT string to verify\n * @returns Promise resolving to the verified JWT claims\n * @throws {Error} If the JWT is invalid\n */\n async verify(jwtPayload: string) {\n try {\n const secret = new TextEncoder().encode(this.config.secret);\n\n const { payload } = await jose.jwtVerify(jwtPayload, secret, {\n audience: this.config.clientId,\n issuer: ISSUER,\n subject: `stores/${this.config.storeHash}`,\n });\n\n this.config.logger?.debug({\n userId: (payload as Claims).user?.id,\n storeHash: this.config.storeHash\n }, 'JWT verified successfully');\n\n return payload as Claims;\n } catch (error) {\n this.config.logger?.error({\n error: error instanceof Error ? {\n name: error.name,\n message: error.message\n } : error\n });\n throw new Error('Invalid JWT payload', { cause: error });\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 {Error} If required parameters are missing or scopes are invalid\n */\n private parseQueryString(queryString: string): AuthQuery {\n const params = new URLSearchParams(queryString);\n\n // Get required parameters\n const code = params.get('code');\n const scope = params.get('scope');\n const context = params.get('context');\n const account_uuid = params.get('account_uuid');\n\n // Validate required parameters\n if (!code) {\n throw new Error('No code found in query string');\n }\n\n if (!scope) {\n throw new Error('No scope found in query string');\n } else if (this.config.scopes?.length) {\n this.validateScopes(scope);\n }\n\n if (!context) {\n throw new Error('No context found in query string');\n }\n\n if (!account_uuid) {\n throw new Error('No account UUID found in query string');\n }\n\n return {\n account_uuid,\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 {Error} 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 grantedScopes = scopes.split(' ');\n const requiredScopes = this.config.scopes;\n const missingScopes = requiredScopes.filter(scope => !grantedScopes.includes(scope));\n\n if (missingScopes.length) {\n throw new Error(`Scope mismatch: ${scopes}; expected: ${this.config.scopes.join(' ')}`);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["request", "page", "request", "ky"]
7
7
  }
package/dist/net.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * Network utilities for interacting with the BigCommerce API.
3
3
  * Provides rate-limited request handling, error management, and type-safe API calls.
4
4
  */
5
+ import { Logger } from './core';
5
6
  /** HTTP methods supported by the API */
6
7
  export type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';
7
8
  export declare const Methods: Record<string, Method>;
@@ -57,4 +58,6 @@ export declare class RequestError<T> extends Error {
57
58
  * @returns Promise resolving to the API response
58
59
  * @throws {RequestError} If the request fails or rate limit is exceeded
59
60
  */
60
- export declare const request: <T, R>(options: RequestOptions<T> & RateLimitOptions & StoreOptions) => Promise<R>;
61
+ export declare const request: <T, R>(options: RequestOptions<T> & RateLimitOptions & StoreOptions & {
62
+ logger?: Logger;
63
+ }) => Promise<R>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bc-api-client",
3
- "version": "0.1.0-beta.6",
3
+ "version": "0.1.0-beta.8",
4
4
  "description": "A client for the BigCommerce management API and app authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -48,15 +48,15 @@
48
48
  "esbuild-plugin-d.ts": "^1.3.1",
49
49
  "eslint": "^9.25.1",
50
50
  "eslint-config-prettier": "^10.1.2",
51
+ "pino": "^9.6.0",
51
52
  "prettier": "^3.5.3",
52
53
  "typescript": "^5.8.3",
53
54
  "typescript-eslint": "^8.31.1",
54
55
  "vitest": "^3.1.2"
55
56
  },
56
57
  "dependencies": {
57
- "jose": "^6.0.11",
58
- "ky": "^1.8.1",
59
- "remeda": "^2.21.3"
58
+ "jose": "^5.2.2",
59
+ "ky": "^1.1.0"
60
60
  },
61
61
  "scripts": {
62
62
  "test": "vitest",