blocket.js 1.1.3 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,6 +14,7 @@ blocket.js is a lightweight and easy-to-use npm package that provides a TypeScri
14
14
  - **Configurable**: Global and per-request configuration options let you override API endpoints, logging preferences, retry attempts, and more.
15
15
  - **TypeScript Support**: Fully typed interfaces for query parameters, API responses, and advertisements.
16
16
  - **Robust Error Handling & Logging**: Automatic retries on token expiry with configurable logging to assist in debugging.
17
+ - **Automatic Pagination**: Seamlessly fetches all results across multiple pages without any additional configuration.
17
18
 
18
19
  ## Installation
19
20
 
@@ -40,8 +41,9 @@ import client from 'blocket.js';
40
41
 
41
42
  (async () => {
42
43
  try {
44
+ // This automatically fetches all results across all pages
43
45
  const ads = await client.find({ query: 'macbook air' });
44
- console.log(ads);
46
+ console.log(`Found ${ads.length} total listings`);
45
47
  } catch (error) {
46
48
  console.error('Error fetching ads:', error);
47
49
  }
@@ -99,19 +101,19 @@ import client from 'blocket.js';
99
101
 
100
102
  `client.find(query: BlocketQueryConfig, fetchOptions?: FetchOptions<'json', any>): Promise<BlocketAd[]>`
101
103
 
102
- Searches for ads on Blocket based on the provided query parameters.
104
+ Searches for ads on Blocket based on the provided query parameters. Automatically handles pagination to return all matching listings across all pages.
103
105
 
104
106
  - Parameters:
105
107
  - `query`: An object conforming to the `BlocketQueryConfig` interface:
106
108
  - `query` (string): The search query (e.g., `'macbook air'`).
107
- - `limit` (number, optional): Maximum number of results to return (default: 20).
109
+ - `limit` (number, optional): Maximum number of results to return per page (default: 20).
108
110
  - `sort` (string, optional): Sorting order (default: `'rel'`).
109
111
  - `listingType` (string, optional): Listing type; `'s'` for selling, `'b'` for buying (default: `'s'`).
110
112
  - `status` (string, optional): Ad status (`'active'` or `'inactive'`, default: `'active'`).
111
113
  - `geolocation` (number, optional): Maximum distance in kilometers.
112
114
  - `include` (string, optional): Additional filters or fields to include (e.g., 'extend_with_shipping').
113
115
  - `fetchOptions` (optional): Additional options to pass to the underlying fetch request.
114
- - Returns: A promise that resolves to an array of `BlocketAd` objects.
116
+ - Returns: A promise that resolves to an array of `BlocketAd` objects from all available pages.
115
117
 
116
118
  `client.findById(adId: string, fetchOptions?: FetchOptions<'json', any>): Promise<BlocketAd>`
117
119
 
@@ -2,9 +2,10 @@ import type { FetchOptions } from 'ofetch';
2
2
  import type { BlocketAd, BlocketQueryConfig } from '../types';
3
3
  /**
4
4
  * Find ads on Blocket based on query parameters.
5
+ * Automatically handles pagination if the API returns multiple pages of results.
5
6
  * @param query Blocket query parameters.
6
7
  * @param fetchOptions Additional fetch options.
7
- * @returns Array of Blocket ads.
8
+ * @returns Array of Blocket ads from all available pages.
8
9
  */
9
10
  export declare function find(query: BlocketQueryConfig, fetchOptions?: FetchOptions<'json', any>): Promise<BlocketAd[]>;
10
11
  /**
@@ -8,6 +8,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
12
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
+ var m = o[Symbol.asyncIterator], i;
14
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
+ };
11
18
  Object.defineProperty(exports, "__esModule", { value: true });
12
19
  exports.find = find;
13
20
  exports.findById = findById;
@@ -41,21 +48,57 @@ function remapQueryParams(params) {
41
48
  }
42
49
  /**
43
50
  * Find ads on Blocket based on query parameters.
51
+ * Automatically handles pagination if the API returns multiple pages of results.
44
52
  * @param query Blocket query parameters.
45
53
  * @param fetchOptions Additional fetch options.
46
- * @returns Array of Blocket ads.
54
+ * @returns Array of Blocket ads from all available pages.
47
55
  */
48
56
  function find(query, fetchOptions) {
49
57
  return __awaiter(this, void 0, void 0, function* () {
58
+ var _a, e_1, _b, _c;
50
59
  if (!query.query)
51
60
  throw new Error('Query string is required');
52
61
  const config = (0, config_1.getBaseConfig)();
53
62
  const queryConfig = (0, config_1.createQueryConfig)(query);
54
- const response = yield (0, request_1.apiRequest)(config.apiBaseUrl, Object.assign({ query: remapQueryParams(queryConfig) }, fetchOptions));
55
- if (!response || !response.data || !Array.isArray(response.data)) {
56
- throw new Error(`Unexpected Blocket API response structure, expected array of ads, got: ${typeof (response === null || response === void 0 ? void 0 : response.data)}`);
63
+ const params = remapQueryParams(queryConfig);
64
+ const firstPageResponse = yield (0, request_1.apiRequest)(config.apiBaseUrl, Object.assign({ query: params }, fetchOptions));
65
+ if (!firstPageResponse ||
66
+ !firstPageResponse.data ||
67
+ !Array.isArray(firstPageResponse.data)) {
68
+ throw new Error(`Unexpected Blocket API response structure, expected array of ads, got: ${typeof (firstPageResponse === null || firstPageResponse === void 0 ? void 0 : firstPageResponse.data)}`);
69
+ }
70
+ if (firstPageResponse.total_page_count <= 1) {
71
+ return firstPageResponse.data;
72
+ }
73
+ const allAds = [...firstPageResponse.data];
74
+ const totalPages = firstPageResponse.total_page_count;
75
+ const pagePromises = Array.from({ length: totalPages - 1 }, (_, i) => i + 2).map((page) => {
76
+ const pageParams = Object.assign(Object.assign({}, params), { page });
77
+ return (0, request_1.apiRequest)(config.apiBaseUrl, Object.assign({ query: pageParams }, fetchOptions));
78
+ });
79
+ try {
80
+ for (var _d = true, pagePromises_1 = __asyncValues(pagePromises), pagePromises_1_1; pagePromises_1_1 = yield pagePromises_1.next(), _a = pagePromises_1_1.done, !_a; _d = true) {
81
+ _c = pagePromises_1_1.value;
82
+ _d = false;
83
+ const response = _c;
84
+ // Prevent rate limiting by adding a delay between requests
85
+ yield new Promise((resolve) => setTimeout(resolve, 200));
86
+ if (response && response.data && Array.isArray(response.data)) {
87
+ allAds.push(...response.data);
88
+ }
89
+ else {
90
+ throw new Error(`Unexpected Blocket API response structure in paginated results, expected array of ads`);
91
+ }
92
+ }
93
+ }
94
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
95
+ finally {
96
+ try {
97
+ if (!_d && !_a && (_b = pagePromises_1.return)) yield _b.call(pagePromises_1);
98
+ }
99
+ finally { if (e_1) throw e_1.error; }
57
100
  }
58
- return response.data;
101
+ return allAds;
59
102
  });
60
103
  }
61
104
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/client/index.ts"],"names":[],"mappings":";;;;;;;;;;;AAqDA,oBAwBC;AAQD,4BAcC;AAnGD,uCAAuC;AACvC,sCAA6D;AAW7D;;;;GAIG;AACH,SAAS,gBAAgB,CACvB,MAA0B;IAE1B,MAAM,OAAO,GAGT;QACF,KAAK,EAAE,GAAG;QACV,KAAK,EAAE,KAAK;QACZ,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,IAAI;QACjB,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,IAAI;QACjB,OAAO,EAAE,SAAS;KACnB,CAAC;IAEF,MAAM,QAAQ,GAAsC,EAAE,CAAC;IAEvD,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,GAA+B,CAAC,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,GAA+B,CAAC,CAAC;YACxD,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE;gBACtB,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,GAA+B,CAAC;aAClD,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAoC,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,SAAsB,IAAI,CACxB,KAAyB,EACzB,YAAwC;;QAExC,IAAI,CAAC,KAAK,CAAC,KAAK;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAE9D,MAAM,MAAM,GAAG,IAAA,sBAAa,GAAE,CAAC;QAC/B,MAAM,WAAW,GAAG,IAAA,0BAAiB,EAAC,KAAK,CAAC,CAAC;QAE7C,MAAM,QAAQ,GAAG,MAAM,IAAA,oBAAU,EAC/B,MAAM,CAAC,UAAU,kBAEf,KAAK,EAAE,gBAAgB,CAAC,WAAW,CAAC,IACjC,YAAY,EAElB,CAAC;QAEF,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CACb,0EAA0E,OAAO,CAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI,CAAA,EAAE,CAClG,CAAC;QACJ,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC;IACvB,CAAC;CAAA;AAED;;;;;GAKG;AACH,SAAsB,QAAQ,CAC5B,IAAY,EACZ,YAAwC;;QAExC,MAAM,MAAM,GAAG,IAAA,sBAAa,GAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;QAE3C,MAAM,EAAE,GAAG,MAAM,IAAA,oBAAU,EAAoB,GAAG,EAAE,YAAY,CAAC,CAAC;QAElE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAA,EAAE,aAAF,EAAE,uBAAF,EAAE,CAAE,IAAI,CAAA,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,EAAE,CAAC,IAAI,CAAC;IACjB,CAAC;CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/client/index.ts"],"names":[],"mappings":"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"}
@@ -7,18 +7,30 @@ export type BlocketAccessToken = {
7
7
  isLoggedIn: false;
8
8
  bearerToken: string;
9
9
  };
10
- /**
11
- * Blocket API response containing an array of ads.
12
- */
13
- export interface BlocketAdSearchResponse {
14
- data: BlocketAd[];
15
- }
16
10
  /**
17
11
  * Blocket API response containing a single ad.
18
12
  */
19
13
  export interface BlocketAdResponse {
20
14
  data: BlocketAd;
21
15
  }
16
+ /**
17
+ * Blocket API response containing an array of ads with additional metadata.
18
+ */
19
+ export interface BlocketApiResponse {
20
+ data: BlocketAd[];
21
+ gallery: unknown[];
22
+ inventory: Record<string, unknown>;
23
+ next_scroll_block: number;
24
+ next_scroll_id: string;
25
+ non_shipping_count: number;
26
+ query_signature: string;
27
+ saveable: boolean;
28
+ selected_values: string;
29
+ share_url: string;
30
+ title: string;
31
+ total_count: number;
32
+ total_page_count: number;
33
+ }
22
34
  /**
23
35
  * Blocket advertisement object.
24
36
  */
@@ -6,8 +6,8 @@ import type {
6
6
  BlocketQueryParamsNative,
7
7
  BlocketAd,
8
8
  BlocketAdResponse,
9
- BlocketAdSearchResponse,
10
9
  BlocketQueryConfig,
10
+ BlocketApiResponse,
11
11
  } from '../types';
12
12
 
13
13
  /**
@@ -47,9 +47,10 @@ function remapQueryParams(
47
47
 
48
48
  /**
49
49
  * Find ads on Blocket based on query parameters.
50
+ * Automatically handles pagination if the API returns multiple pages of results.
50
51
  * @param query Blocket query parameters.
51
52
  * @param fetchOptions Additional fetch options.
52
- * @returns Array of Blocket ads.
53
+ * @returns Array of Blocket ads from all available pages.
53
54
  */
54
55
  export async function find(
55
56
  query: BlocketQueryConfig,
@@ -60,21 +61,61 @@ export async function find(
60
61
  const config = getBaseConfig();
61
62
  const queryConfig = createQueryConfig(query);
62
63
 
63
- const response = await apiRequest<BlocketAdSearchResponse>(
64
+ const params = remapQueryParams(queryConfig);
65
+
66
+ const firstPageResponse = await apiRequest<BlocketApiResponse>(
64
67
  config.apiBaseUrl,
65
68
  {
66
- query: remapQueryParams(queryConfig),
69
+ query: params,
67
70
  ...fetchOptions,
68
71
  }
69
72
  );
70
73
 
71
- if (!response || !response.data || !Array.isArray(response.data)) {
74
+ if (
75
+ !firstPageResponse ||
76
+ !firstPageResponse.data ||
77
+ !Array.isArray(firstPageResponse.data)
78
+ ) {
72
79
  throw new Error(
73
- `Unexpected Blocket API response structure, expected array of ads, got: ${typeof response?.data}`
80
+ `Unexpected Blocket API response structure, expected array of ads, got: ${typeof firstPageResponse?.data}`
74
81
  );
75
82
  }
76
83
 
77
- return response.data;
84
+ if (firstPageResponse.total_page_count <= 1) {
85
+ return firstPageResponse.data;
86
+ }
87
+
88
+ const allAds = [...firstPageResponse.data];
89
+ const totalPages = firstPageResponse.total_page_count;
90
+
91
+ const pagePromises = Array.from(
92
+ { length: totalPages - 1 },
93
+ (_, i) => i + 2
94
+ ).map((page) => {
95
+ const pageParams = {
96
+ ...params,
97
+ page,
98
+ };
99
+ return apiRequest<BlocketApiResponse>(config.apiBaseUrl, {
100
+ query: pageParams,
101
+ ...fetchOptions,
102
+ });
103
+ });
104
+
105
+ for await (const response of pagePromises) {
106
+ // Prevent rate limiting by adding a delay between requests
107
+ await new Promise((resolve) => setTimeout(resolve, 200));
108
+
109
+ if (response && response.data && Array.isArray(response.data)) {
110
+ allAds.push(...response.data);
111
+ } else {
112
+ throw new Error(
113
+ `Unexpected Blocket API response structure in paginated results, expected array of ads`
114
+ );
115
+ }
116
+ }
117
+
118
+ return allAds;
78
119
  }
79
120
 
80
121
  /**
@@ -10,17 +10,29 @@ export type BlocketAccessToken = {
10
10
  };
11
11
 
12
12
  /**
13
- * Blocket API response containing an array of ads.
13
+ * Blocket API response containing a single ad.
14
14
  */
15
- export interface BlocketAdSearchResponse {
16
- data: BlocketAd[];
15
+ export interface BlocketAdResponse {
16
+ data: BlocketAd;
17
17
  }
18
18
 
19
19
  /**
20
- * Blocket API response containing a single ad.
20
+ * Blocket API response containing an array of ads with additional metadata.
21
21
  */
22
- export interface BlocketAdResponse {
23
- data: BlocketAd;
22
+ export interface BlocketApiResponse {
23
+ data: BlocketAd[];
24
+ gallery: unknown[];
25
+ inventory: Record<string, unknown>;
26
+ next_scroll_block: number;
27
+ next_scroll_id: string;
28
+ non_shipping_count: number;
29
+ query_signature: string;
30
+ saveable: boolean;
31
+ selected_values: string;
32
+ share_url: string;
33
+ title: string;
34
+ total_count: number;
35
+ total_page_count: number;
24
36
  }
25
37
 
26
38
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blocket.js",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
4
4
  "description": "A user-friendly js wrapper for blocket.se",
5
5
  "keywords": [
6
6
  "blocket",