blocket.js 1.2.2 → 1.4.0

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.
@@ -10,8 +10,12 @@ import type { BlocketAd, BlocketQueryConfig } from '../types';
10
10
  export declare function find(query: BlocketQueryConfig, fetchOptions?: FetchOptions<'json', any>): Promise<BlocketAd[]>;
11
11
  /**
12
12
  * Get details of a specific ad by its ID.
13
+ *
14
+ * Note: The Blocket API no longer provides a public endpoint for single ad lookups.
15
+ * This method searches for the ad and returns it if found.
16
+ *
13
17
  * @param adId Advertisement ID.
14
18
  * @param fetchOptions Additional fetch options.
15
- * @returns {Promise<BlocketAd | null>} Blocket ad details or null if not found.
19
+ * @returns {Promise<BlocketAd | null>} Blocket ad or null if not found.
16
20
  */
17
21
  export declare function findById(adId: string, fetchOptions?: FetchOptions<'json', any>): Promise<BlocketAd | null>;
@@ -8,13 +8,6 @@ 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
- };
18
11
  Object.defineProperty(exports, "__esModule", { value: true });
19
12
  exports.find = find;
20
13
  exports.findById = findById;
@@ -34,6 +27,7 @@ function remapQueryParams(params) {
34
27
  status: 'status',
35
28
  geolocation: 'gl',
36
29
  include: 'include',
30
+ page: 'page',
37
31
  };
38
32
  const remapped = {};
39
33
  for (const key in params) {
@@ -55,7 +49,7 @@ function remapQueryParams(params) {
55
49
  */
56
50
  function find(query, fetchOptions) {
57
51
  return __awaiter(this, void 0, void 0, function* () {
58
- var _a, e_1, _b, _c;
52
+ var _a;
59
53
  if (!query.query)
60
54
  throw new Error('Query string is required');
61
55
  const config = (0, config_1.getBaseConfig)();
@@ -63,58 +57,52 @@ function find(query, fetchOptions) {
63
57
  const params = remapQueryParams(queryConfig);
64
58
  const firstPageResponse = yield (0, request_1.apiRequest)(config.apiBaseUrl, Object.assign({ query: params }, fetchOptions));
65
59
  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)}`);
60
+ !firstPageResponse.docs ||
61
+ !Array.isArray(firstPageResponse.docs)) {
62
+ throw new Error(`Unexpected Blocket API response structure, expected array of ads, got: ${typeof (firstPageResponse === null || firstPageResponse === void 0 ? void 0 : firstPageResponse.docs)}`);
69
63
  }
70
- if (firstPageResponse.total_page_count <= 1) {
71
- return firstPageResponse.data;
64
+ const paging = (_a = firstPageResponse.metadata) === null || _a === void 0 ? void 0 : _a.paging;
65
+ if (!paging || paging.last <= 1) {
66
+ return firstPageResponse.docs;
72
67
  }
73
- const allAds = [...firstPageResponse.data];
74
- const totalPages = firstPageResponse.total_page_count;
75
- const pagePromises = Array.from({ length: totalPages - 1 }, (_, i) => i + 1).map((page) => {
68
+ const allAds = [...firstPageResponse.docs];
69
+ const totalPages = paging.last;
70
+ // Optimized pagination: Direct page parameter requests without delay
71
+ for (let page = 2; page <= totalPages; page++) {
76
72
  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
- yield new Promise((resolve) => setTimeout(resolve, 150));
85
- if (response && response.data && Array.isArray(response.data)) {
86
- allAds.push(...response.data);
87
- }
88
- else {
89
- throw new Error(`Unexpected Blocket API response structure in paginated results, expected array of ads`);
90
- }
73
+ const response = yield (0, request_1.apiRequest)(config.apiBaseUrl, Object.assign({ query: pageParams }, fetchOptions));
74
+ if (response && response.docs && Array.isArray(response.docs)) {
75
+ allAds.push(...response.docs);
91
76
  }
92
- }
93
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
94
- finally {
95
- try {
96
- if (!_d && !_a && (_b = pagePromises_1.return)) yield _b.call(pagePromises_1);
77
+ else {
78
+ throw new Error(`Unexpected Blocket API response structure in paginated results, expected array of ads`);
97
79
  }
98
- finally { if (e_1) throw e_1.error; }
99
80
  }
100
81
  return allAds;
101
82
  });
102
83
  }
103
84
  /**
104
85
  * Get details of a specific ad by its ID.
86
+ *
87
+ * Note: The Blocket API no longer provides a public endpoint for single ad lookups.
88
+ * This method searches for the ad and returns it if found.
89
+ *
105
90
  * @param adId Advertisement ID.
106
91
  * @param fetchOptions Additional fetch options.
107
- * @returns {Promise<BlocketAd | null>} Blocket ad details or null if not found.
92
+ * @returns {Promise<BlocketAd | null>} Blocket ad or null if not found.
108
93
  */
109
94
  function findById(adId, fetchOptions) {
110
95
  return __awaiter(this, void 0, void 0, function* () {
111
96
  const config = (0, config_1.getBaseConfig)();
112
- const url = `${config.apiBaseUrl}/${adId}`;
113
- const ad = yield (0, request_1.apiRequest)(url, fetchOptions);
114
- if (!ad || !(ad === null || ad === void 0 ? void 0 : ad.data)) {
97
+ // Search with a generic query and filter by ID
98
+ // The API doesn't support direct ID lookup, so we use the ad ID as query
99
+ const response = yield (0, request_1.apiRequest)(config.apiBaseUrl, Object.assign({ query: { q: adId, lim: 100 } }, fetchOptions));
100
+ if (!response || !response.docs) {
115
101
  return null;
116
102
  }
117
- return ad.data;
103
+ // Find the exact ad by ID
104
+ const ad = response.docs.find((doc) => doc.id === adId || doc.ad_id === parseInt(adId, 10));
105
+ return ad || null;
118
106
  });
119
107
  }
120
108
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/client/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/client/index.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,8 @@
1
1
  import { type FetchOptions } from 'ofetch';
2
2
  /**
3
- * Make an API request with automatic token handling and retry on 401 errors.
3
+ * Make an API request with User-Agent authentication.
4
4
  * @param url URL to fetch.
5
5
  * @param options Fetch options.
6
- * @param retryCount Current retry count.
7
6
  * @returns Parsed response of type T.
8
7
  */
9
- export declare function apiRequest<T>(url: string, options?: FetchOptions<'json', any>, retryCount?: number): Promise<T>;
8
+ export declare function apiRequest<T>(url: string, options?: FetchOptions<'json', any>): Promise<T>;
@@ -11,29 +11,21 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.apiRequest = apiRequest;
13
13
  const ofetch_1 = require("ofetch");
14
- const token_1 = require("./token");
15
14
  const config_1 = require("../config");
16
15
  /**
17
- * Make an API request with automatic token handling and retry on 401 errors.
16
+ * Make an API request with User-Agent authentication.
18
17
  * @param url URL to fetch.
19
18
  * @param options Fetch options.
20
- * @param retryCount Current retry count.
21
19
  * @returns Parsed response of type T.
22
20
  */
23
21
  function apiRequest(url_1) {
24
- return __awaiter(this, arguments, void 0, function* (url, options = {}, retryCount = 0) {
25
- var _a;
26
- const config = (0, config_1.getBaseConfig)();
27
- const token = yield (0, token_1.fetchToken)();
22
+ return __awaiter(this, arguments, void 0, function* (url, options = {}) {
28
23
  try {
29
- return yield (0, ofetch_1.ofetch)(url, Object.assign(Object.assign({}, options), { headers: Object.assign(Object.assign({}, options.headers), { Authorization: `Bearer ${token}` }) }));
24
+ return yield (0, ofetch_1.ofetch)(url, Object.assign(Object.assign({}, options), { headers: Object.assign({ 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0' }, options.headers) }));
30
25
  }
31
26
  catch (error) {
32
- if (((_a = error === null || error === void 0 ? void 0 : error.data) === null || _a === void 0 ? void 0 : _a.status_code) === 401 && retryCount < config.retryAttempts) {
33
- (0, config_1.logger)('info', `Token expired. Retrying request (${retryCount + 1}/${config.retryAttempts}).`);
34
- const newToken = yield (0, token_1.fetchToken)(true);
35
- (0, token_1.setCachedToken)(newToken);
36
- return apiRequest(url, options, retryCount + 1);
27
+ if ((error === null || error === void 0 ? void 0 : error.status) >= 400) {
28
+ (0, config_1.logger)('error', `HTTP ${error.status}: ${error.statusText || 'Request failed'}`);
37
29
  }
38
30
  throw error;
39
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","sourceRoot":"","sources":["../../lib/client/request.ts"],"names":[],"mappings":";;;;;;;;;;;AAYA,gCA8BC;AA1CD,mCAAmD;AAEnD,mCAAqD;AACrD,sCAAkD;AAElD;;;;;;GAMG;AACH,SAAsB,UAAU;yDAC9B,GAAW,EACX,UAAqC,EAAE,EACvC,aAAqB,CAAC;;QAEtB,MAAM,MAAM,GAAG,IAAA,sBAAa,GAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,MAAM,IAAA,kBAAU,GAAE,CAAC;QAEjC,IAAI,CAAC;YACH,OAAO,MAAM,IAAA,eAAM,EAAI,GAAG,kCACrB,OAAO,KACV,OAAO,kCACF,OAAO,CAAC,OAAO,KAClB,aAAa,EAAE,UAAU,KAAK,EAAE,OAElC,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,CAAA,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,0CAAE,WAAW,MAAK,GAAG,IAAI,UAAU,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;gBAC1E,IAAA,eAAM,EACJ,MAAM,EACN,oCAAoC,UAAU,GAAG,CAAC,IAChD,MAAM,CAAC,aACT,IAAI,CACL,CAAC;gBACF,MAAM,QAAQ,GAAG,MAAM,IAAA,kBAAU,EAAC,IAAI,CAAC,CAAC;gBACxC,IAAA,sBAAc,EAAC,QAAQ,CAAC,CAAC;gBACzB,OAAO,UAAU,CAAI,GAAG,EAAE,OAAO,EAAE,UAAU,GAAG,CAAC,CAAC,CAAC;YACrD,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CAAA"}
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../../lib/client/request.ts"],"names":[],"mappings":";;;;;;;;;;;AAUA,gCAsBC;AAhCD,mCAAmD;AAEnD,sCAAmC;AAEnC;;;;;GAKG;AACH,SAAsB,UAAU;yDAC9B,GAAW,EACX,UAAqC,EAAE;QAEvC,IAAI,CAAC;YACH,OAAO,MAAM,IAAA,eAAM,EAAI,GAAG,kCACrB,OAAO,KACV,OAAO,kBACL,YAAY,EACV,wEAAwE,IACvE,OAAO,CAAC,OAAO,KAEpB,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,IAAI,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,MAAM,KAAI,GAAG,EAAE,CAAC;gBACzB,IAAA,eAAM,EACJ,OAAO,EACP,QAAQ,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAChE,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CAAA"}
@@ -5,10 +5,8 @@ exports.createQueryConfig = exports.defaultQueryConfig = exports.logger = export
5
5
  * Default global configuration.
6
6
  */
7
7
  exports.defaultConfig = {
8
- apiBaseUrl: 'https://api.blocket.se/search_bff/v2/content',
9
- tokenEndpoint: 'https://www.blocket.se/api/adout-api-route/refresh-token-and-validate-session',
8
+ apiBaseUrl: 'https://www.blocket.se/recommerce/forsale/search/api/search/SEARCH_ID_BAP_COMMON',
10
9
  logLevel: 'error',
11
- retryAttempts: 3,
12
10
  };
13
11
  let currentConfig = Object.assign({}, exports.defaultConfig);
14
12
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/config/index.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACU,QAAA,aAAa,GAAkB;IAC1C,UAAU,EAAE,8CAA8C;IAC1D,aAAa,EACX,+EAA+E;IACjF,QAAQ,EAAE,OAAO;IACjB,aAAa,EAAE,CAAC;CACjB,CAAC;AAEF,IAAI,aAAa,qBAAuB,qBAAa,CAAE,CAAC;AAExD;;;GAGG;AACI,MAAM,SAAS,GAAG,CAAC,MAA8B,EAAQ,EAAE;IAChE,aAAa,mCAAQ,aAAa,GAAK,MAAM,CAAE,CAAC;AAClD,CAAC,CAAC;AAFW,QAAA,SAAS,aAEpB;AAEF;;;GAGG;AACI,MAAM,aAAa,GAAG,GAAkB,EAAE,CAAC,aAAa,CAAC;AAAnD,QAAA,aAAa,iBAAsC;AAEhE;;;;GAIG;AACI,MAAM,MAAM,GAAG,CACpB,KAAiC,EACjC,OAAe,EACT,EAAE;IACR,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACxD,IAAI,MAAM,CAAC,IAAA,qBAAa,GAAE,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;AACH,CAAC,CAAC;AARW,QAAA,MAAM,UAQjB;AAEF;;GAEG;AACU,QAAA,kBAAkB,GAAsC;IACnE,WAAW,EAAE,GAAG;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF;;;;GAIG;AACI,MAAM,iBAAiB,GAAG,CAC/B,WAC4C,EACxB,EAAE;IACtB,uCAAY,0BAAkB,GAAK,WAAW,EAAG;AACnD,CAAC,CAAC;AALW,QAAA,iBAAiB,qBAK5B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/config/index.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACU,QAAA,aAAa,GAAkB;IAC1C,UAAU,EACR,kFAAkF;IACpF,QAAQ,EAAE,OAAO;CAClB,CAAC;AAEF,IAAI,aAAa,qBAAuB,qBAAa,CAAE,CAAC;AAExD;;;GAGG;AACI,MAAM,SAAS,GAAG,CAAC,MAA8B,EAAQ,EAAE;IAChE,aAAa,mCAAQ,aAAa,GAAK,MAAM,CAAE,CAAC;AAClD,CAAC,CAAC;AAFW,QAAA,SAAS,aAEpB;AAEF;;;GAGG;AACI,MAAM,aAAa,GAAG,GAAkB,EAAE,CAAC,aAAa,CAAC;AAAnD,QAAA,aAAa,iBAAsC;AAEhE;;;;GAIG;AACI,MAAM,MAAM,GAAG,CACpB,KAAiC,EACjC,OAAe,EACT,EAAE;IACR,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACxD,IAAI,MAAM,CAAC,IAAA,qBAAa,GAAE,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC;AACH,CAAC,CAAC;AARW,QAAA,MAAM,UAQjB;AAEF;;GAEG;AACU,QAAA,kBAAkB,GAAsC;IACnE,WAAW,EAAE,GAAG;IAChB,MAAM,EAAE,QAAQ;CACjB,CAAC;AAEF;;;;GAIG;AACI,MAAM,iBAAiB,GAAG,CAC/B,WAC4C,EACxB,EAAE;IACtB,uCAAY,0BAAkB,GAAK,WAAW,EAAG;AACnD,CAAC,CAAC;AALW,QAAA,iBAAiB,qBAK5B"}
@@ -4,25 +4,15 @@
4
4
  export interface BlocketConfig {
5
5
  /**
6
6
  * Base URL for the Blocket API.
7
- * @default 'https://api.blocket.se/search_bff/v2/content'
7
+ * @default 'https://www.blocket.se/recommerce/forsale/search/api/search/SEARCH_ID_BAP_COMMON'
8
8
  */
9
9
  apiBaseUrl: string;
10
- /**
11
- * Endpoint URL to fetch the token.
12
- * @default 'https://www.blocket.se/api/adout-api-route/refresh-token-and-validate-session'
13
- */
14
- tokenEndpoint: string;
15
10
  /**
16
11
  * Log level for debugging.
17
12
  * Options: 'none', 'error', 'info', 'debug'
18
13
  * @default 'error'
19
14
  */
20
15
  logLevel: 'none' | 'error' | 'info' | 'debug';
21
- /**
22
- * Maximum number of retry attempts on 401 error.
23
- * @default 3
24
- */
25
- retryAttempts: number;
26
16
  }
27
17
  /**
28
18
  * Blocket Query Configuration interface.
@@ -43,7 +33,7 @@ export interface BlocketQueryConfig {
43
33
  /**
44
34
  * The sorting order of the results.
45
35
  */
46
- sort?: 'rel';
36
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
47
37
  /**
48
38
  * The type of listing to search for. `s` for selling, `b` for buying, `a` for all.
49
39
  * @default 's'
@@ -53,7 +43,7 @@ export interface BlocketQueryConfig {
53
43
  * The status of the ad. `active`, `inactive`, or `all`.
54
44
  * @default 'active'
55
45
  */
56
- status?: 'active' | 'deleted' | 'hidden_by_user';
46
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
57
47
  /**
58
48
  * The maximum distance in kilometers from the search location.
59
49
  */
@@ -62,6 +52,10 @@ export interface BlocketQueryConfig {
62
52
  * Additional filters or fields to include in the response.
63
53
  */
64
54
  include?: 'extend_with_shipping' | string;
55
+ /**
56
+ * Page number for pagination (starts from 1).
57
+ */
58
+ page?: number;
65
59
  }
66
60
  /**
67
61
  * Native Blocket query parameters for API requests.
@@ -69,9 +63,10 @@ export interface BlocketQueryConfig {
69
63
  export interface BlocketQueryParamsNative {
70
64
  q: string;
71
65
  lim?: number;
72
- sort?: 'rel';
66
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
73
67
  st?: 's' | 'b' | 'a';
74
- status?: 'active' | 'deleted' | 'hidden_by_user';
68
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
75
69
  gl?: number;
76
70
  include?: string;
71
+ page?: number;
77
72
  }
@@ -1,74 +1,72 @@
1
1
  export * from './config';
2
2
  /**
3
- * Access token.
3
+ * Blocket API response containing an array of ads with metadata.
4
4
  */
5
- export type BlocketAccessToken = {
6
- user: null;
7
- isLoggedIn: false;
8
- bearerToken: string;
9
- };
10
- /**
11
- * Blocket API response containing a single ad.
12
- */
13
- export interface BlocketAdResponse {
14
- data: BlocketAd;
5
+ export interface BlocketApiResponse {
6
+ docs: BlocketAd[];
7
+ filters: unknown[];
8
+ metadata: BlocketMetadata;
15
9
  }
16
10
  /**
17
- * Blocket API response containing an array of ads with additional metadata.
11
+ * Blocket API metadata containing pagination and search info.
18
12
  */
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;
13
+ export interface BlocketMetadata {
14
+ params: Record<string, string[]>;
15
+ search_key: string;
16
+ selected_filters: unknown[];
17
+ num_results: number;
18
+ result_size: {
19
+ match_count: number;
20
+ group_count: number;
21
+ };
22
+ paging: {
23
+ param: string;
24
+ current: number;
25
+ last: number;
26
+ };
30
27
  title: string;
31
- total_count: number;
32
- total_page_count: number;
28
+ is_savable_search: boolean;
29
+ is_end_of_paging: boolean;
30
+ timestamp: number;
33
31
  }
34
32
  /**
35
33
  * Blocket advertisement object.
36
34
  */
37
35
  export interface BlocketAd {
38
- ad_id: string;
39
- ad_status: 'active' | 'inactive' | string;
40
- advertiser: {
41
- account_id: string;
42
- contact_methods: Record<string, any>;
43
- name: string;
44
- public_profile: Record<string, any>;
45
- type: 'private' | 'business';
46
- };
47
- body: string;
48
- category: Record<string, any>[];
49
- co2_text: string;
50
- images: Record<string, any>[];
51
- list_id: string;
52
- list_time: string;
53
- location: Record<string, any>[];
54
- map_url: string;
55
- parameter_groups: Record<string, any>[];
56
- parameters_raw: {
57
- is_shipping_buy_now_enabled: Record<string, any>;
58
- shipping_enabled: Record<string, any>;
59
- };
60
- price: {
61
- suffix: string;
62
- value: number;
36
+ type: string;
37
+ id: string;
38
+ ad_id: number;
39
+ main_search_key: string;
40
+ heading: string;
41
+ location: string;
42
+ image: {
43
+ url: string;
44
+ path: string;
45
+ height: number;
46
+ width: number;
47
+ aspect_ratio: number;
48
+ } | null;
49
+ image_urls: string[];
50
+ flags: string[];
51
+ timestamp: number;
52
+ coordinates?: {
53
+ lat: number;
54
+ lon: number;
55
+ accuracy: number;
63
56
  };
64
- price_badge?: {
65
- icon: Record<string, any>;
57
+ ad_type: number;
58
+ labels: Array<{
66
59
  id: string;
67
- label: string;
60
+ text: string;
61
+ type: 'PRIMARY' | 'SECONDARY' | string;
62
+ }>;
63
+ canonical_url: string;
64
+ extras: unknown[];
65
+ price: {
66
+ amount: number;
67
+ currency_code: string;
68
+ price_unit: string;
68
69
  };
69
- share_url: string;
70
- state_id: string;
71
- subject: string;
72
- type: string;
73
- zipcode: string;
70
+ distance: number;
71
+ trade_type: string;
74
72
  }
@@ -5,7 +5,6 @@ import type { FetchOptions } from 'ofetch';
5
5
  import type {
6
6
  BlocketQueryParamsNative,
7
7
  BlocketAd,
8
- BlocketAdResponse,
9
8
  BlocketQueryConfig,
10
9
  BlocketApiResponse,
11
10
  } from '../types';
@@ -29,6 +28,7 @@ function remapQueryParams(
29
28
  status: 'status',
30
29
  geolocation: 'gl',
31
30
  include: 'include',
31
+ page: 'page',
32
32
  };
33
33
 
34
34
  const remapped: Partial<BlocketQueryParamsNative> = {};
@@ -73,40 +73,36 @@ export async function find(
73
73
 
74
74
  if (
75
75
  !firstPageResponse ||
76
- !firstPageResponse.data ||
77
- !Array.isArray(firstPageResponse.data)
76
+ !firstPageResponse.docs ||
77
+ !Array.isArray(firstPageResponse.docs)
78
78
  ) {
79
79
  throw new Error(
80
- `Unexpected Blocket API response structure, expected array of ads, got: ${typeof firstPageResponse?.data}`
80
+ `Unexpected Blocket API response structure, expected array of ads, got: ${typeof firstPageResponse?.docs}`
81
81
  );
82
82
  }
83
83
 
84
- if (firstPageResponse.total_page_count <= 1) {
85
- return firstPageResponse.data;
84
+ const paging = firstPageResponse.metadata?.paging;
85
+ if (!paging || paging.last <= 1) {
86
+ return firstPageResponse.docs;
86
87
  }
87
88
 
88
- const allAds = [...firstPageResponse.data];
89
- const totalPages = firstPageResponse.total_page_count;
89
+ const allAds = [...firstPageResponse.docs];
90
+ const totalPages = paging.last;
90
91
 
91
- const pagePromises = Array.from(
92
- { length: totalPages - 1 },
93
- (_, i) => i + 1
94
- ).map((page) => {
92
+ // Optimized pagination: Direct page parameter requests without delay
93
+ for (let page = 2; page <= totalPages; page++) {
95
94
  const pageParams = {
96
95
  ...params,
97
96
  page,
98
97
  };
99
- return apiRequest<BlocketApiResponse>(config.apiBaseUrl, {
98
+
99
+ const response = await apiRequest<BlocketApiResponse>(config.apiBaseUrl, {
100
100
  query: pageParams,
101
101
  ...fetchOptions,
102
102
  });
103
- });
104
-
105
- for await (const response of pagePromises) {
106
- await new Promise((resolve) => setTimeout(resolve, 150));
107
103
 
108
- if (response && response.data && Array.isArray(response.data)) {
109
- allAds.push(...response.data);
104
+ if (response && response.docs && Array.isArray(response.docs)) {
105
+ allAds.push(...response.docs);
110
106
  } else {
111
107
  throw new Error(
112
108
  `Unexpected Blocket API response structure in paginated results, expected array of ads`
@@ -119,22 +115,35 @@ export async function find(
119
115
 
120
116
  /**
121
117
  * Get details of a specific ad by its ID.
118
+ *
119
+ * Note: The Blocket API no longer provides a public endpoint for single ad lookups.
120
+ * This method searches for the ad and returns it if found.
121
+ *
122
122
  * @param adId Advertisement ID.
123
123
  * @param fetchOptions Additional fetch options.
124
- * @returns {Promise<BlocketAd | null>} Blocket ad details or null if not found.
124
+ * @returns {Promise<BlocketAd | null>} Blocket ad or null if not found.
125
125
  */
126
126
  export async function findById(
127
127
  adId: string,
128
128
  fetchOptions?: FetchOptions<'json', any>
129
129
  ): Promise<BlocketAd | null> {
130
130
  const config = getBaseConfig();
131
- const url = `${config.apiBaseUrl}/${adId}`;
132
131
 
133
- const ad = await apiRequest<BlocketAdResponse>(url, fetchOptions);
132
+ // Search with a generic query and filter by ID
133
+ // The API doesn't support direct ID lookup, so we use the ad ID as query
134
+ const response = await apiRequest<BlocketApiResponse>(config.apiBaseUrl, {
135
+ query: { q: adId, lim: 100 },
136
+ ...fetchOptions,
137
+ });
134
138
 
135
- if (!ad || !ad?.data) {
139
+ if (!response || !response.docs) {
136
140
  return null;
137
141
  }
138
142
 
139
- return ad.data;
143
+ // Find the exact ad by ID
144
+ const ad = response.docs.find(
145
+ (doc) => doc.id === adId || doc.ad_id === parseInt(adId, 10)
146
+ );
147
+
148
+ return ad || null;
140
149
  }
@@ -1,42 +1,32 @@
1
1
  import { ofetch, type FetchOptions } from 'ofetch';
2
2
 
3
- import { fetchToken, setCachedToken } from './token';
4
- import { getBaseConfig, logger } from '../config';
3
+ import { logger } from '../config';
5
4
 
6
5
  /**
7
- * Make an API request with automatic token handling and retry on 401 errors.
6
+ * Make an API request with User-Agent authentication.
8
7
  * @param url URL to fetch.
9
8
  * @param options Fetch options.
10
- * @param retryCount Current retry count.
11
9
  * @returns Parsed response of type T.
12
10
  */
13
11
  export async function apiRequest<T>(
14
12
  url: string,
15
- options: FetchOptions<'json', any> = {},
16
- retryCount: number = 0
13
+ options: FetchOptions<'json', any> = {}
17
14
  ): Promise<T> {
18
- const config = getBaseConfig();
19
- const token = await fetchToken();
20
-
21
15
  try {
22
16
  return await ofetch<T>(url, {
23
17
  ...options,
24
18
  headers: {
19
+ 'User-Agent':
20
+ 'Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0',
25
21
  ...options.headers,
26
- Authorization: `Bearer ${token}`,
27
22
  },
28
23
  });
29
24
  } catch (error: any) {
30
- if (error?.data?.status_code === 401 && retryCount < config.retryAttempts) {
25
+ if (error?.status >= 400) {
31
26
  logger(
32
- 'info',
33
- `Token expired. Retrying request (${retryCount + 1}/${
34
- config.retryAttempts
35
- }).`
27
+ 'error',
28
+ `HTTP ${error.status}: ${error.statusText || 'Request failed'}`
36
29
  );
37
- const newToken = await fetchToken(true);
38
- setCachedToken(newToken);
39
- return apiRequest<T>(url, options, retryCount + 1);
40
30
  }
41
31
  throw error;
42
32
  }
@@ -4,11 +4,9 @@ import { BlocketQueryConfig, BlocketConfig } from '../types';
4
4
  * Default global configuration.
5
5
  */
6
6
  export const defaultConfig: BlocketConfig = {
7
- apiBaseUrl: 'https://api.blocket.se/search_bff/v2/content',
8
- tokenEndpoint:
9
- 'https://www.blocket.se/api/adout-api-route/refresh-token-and-validate-session',
7
+ apiBaseUrl:
8
+ 'https://www.blocket.se/recommerce/forsale/search/api/search/SEARCH_ID_BAP_COMMON',
10
9
  logLevel: 'error',
11
- retryAttempts: 3,
12
10
  };
13
11
 
14
12
  let currentConfig: BlocketConfig = { ...defaultConfig };
@@ -4,25 +4,15 @@
4
4
  export interface BlocketConfig {
5
5
  /**
6
6
  * Base URL for the Blocket API.
7
- * @default 'https://api.blocket.se/search_bff/v2/content'
7
+ * @default 'https://www.blocket.se/recommerce/forsale/search/api/search/SEARCH_ID_BAP_COMMON'
8
8
  */
9
9
  apiBaseUrl: string;
10
- /**
11
- * Endpoint URL to fetch the token.
12
- * @default 'https://www.blocket.se/api/adout-api-route/refresh-token-and-validate-session'
13
- */
14
- tokenEndpoint: string;
15
10
  /**
16
11
  * Log level for debugging.
17
12
  * Options: 'none', 'error', 'info', 'debug'
18
13
  * @default 'error'
19
14
  */
20
15
  logLevel: 'none' | 'error' | 'info' | 'debug';
21
- /**
22
- * Maximum number of retry attempts on 401 error.
23
- * @default 3
24
- */
25
- retryAttempts: number;
26
16
  }
27
17
 
28
18
  /**
@@ -44,7 +34,7 @@ export interface BlocketQueryConfig {
44
34
  /**
45
35
  * The sorting order of the results.
46
36
  */
47
- sort?: 'rel';
37
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
48
38
  /**
49
39
  * The type of listing to search for. `s` for selling, `b` for buying, `a` for all.
50
40
  * @default 's'
@@ -54,7 +44,7 @@ export interface BlocketQueryConfig {
54
44
  * The status of the ad. `active`, `inactive`, or `all`.
55
45
  * @default 'active'
56
46
  */
57
- status?: 'active' | 'deleted' | 'hidden_by_user';
47
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
58
48
  /**
59
49
  * The maximum distance in kilometers from the search location.
60
50
  */
@@ -63,6 +53,10 @@ export interface BlocketQueryConfig {
63
53
  * Additional filters or fields to include in the response.
64
54
  */
65
55
  include?: 'extend_with_shipping' | string;
56
+ /**
57
+ * Page number for pagination (starts from 1).
58
+ */
59
+ page?: number;
66
60
  }
67
61
 
68
62
  /**
@@ -71,9 +65,10 @@ export interface BlocketQueryConfig {
71
65
  export interface BlocketQueryParamsNative {
72
66
  q: string;
73
67
  lim?: number;
74
- sort?: 'rel';
68
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
75
69
  st?: 's' | 'b' | 'a';
76
- status?: 'active' | 'deleted' | 'hidden_by_user';
70
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
77
71
  gl?: number;
78
72
  include?: string;
73
+ page?: number;
79
74
  }
@@ -1,78 +1,76 @@
1
1
  export * from './config';
2
2
 
3
3
  /**
4
- * Access token.
4
+ * Blocket API response containing an array of ads with metadata.
5
5
  */
6
- export type BlocketAccessToken = {
7
- user: null;
8
- isLoggedIn: false;
9
- bearerToken: string;
10
- };
11
-
12
- /**
13
- * Blocket API response containing a single ad.
14
- */
15
- export interface BlocketAdResponse {
16
- data: BlocketAd;
6
+ export interface BlocketApiResponse {
7
+ docs: BlocketAd[];
8
+ filters: unknown[];
9
+ metadata: BlocketMetadata;
17
10
  }
18
11
 
19
12
  /**
20
- * Blocket API response containing an array of ads with additional metadata.
13
+ * Blocket API metadata containing pagination and search info.
21
14
  */
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;
15
+ export interface BlocketMetadata {
16
+ params: Record<string, string[]>;
17
+ search_key: string;
18
+ selected_filters: unknown[];
19
+ num_results: number;
20
+ result_size: {
21
+ match_count: number;
22
+ group_count: number;
23
+ };
24
+ paging: {
25
+ param: string;
26
+ current: number;
27
+ last: number;
28
+ };
33
29
  title: string;
34
- total_count: number;
35
- total_page_count: number;
30
+ is_savable_search: boolean;
31
+ is_end_of_paging: boolean;
32
+ timestamp: number;
36
33
  }
37
34
 
38
35
  /**
39
36
  * Blocket advertisement object.
40
37
  */
41
38
  export interface BlocketAd {
42
- ad_id: string;
43
- ad_status: 'active' | 'inactive' | string;
44
- advertiser: {
45
- account_id: string;
46
- contact_methods: Record<string, any>;
47
- name: string;
48
- public_profile: Record<string, any>;
49
- type: 'private' | 'business';
50
- };
51
- body: string;
52
- category: Record<string, any>[];
53
- co2_text: string;
54
- images: Record<string, any>[];
55
- list_id: string;
56
- list_time: string; // ISO date string
57
- location: Record<string, any>[];
58
- map_url: string;
59
- parameter_groups: Record<string, any>[];
60
- parameters_raw: {
61
- is_shipping_buy_now_enabled: Record<string, any>;
62
- shipping_enabled: Record<string, any>;
63
- };
64
- price: {
65
- suffix: string;
66
- value: number;
39
+ type: string;
40
+ id: string;
41
+ ad_id: number;
42
+ main_search_key: string;
43
+ heading: string;
44
+ location: string;
45
+ image: {
46
+ url: string;
47
+ path: string;
48
+ height: number;
49
+ width: number;
50
+ aspect_ratio: number;
51
+ } | null;
52
+ image_urls: string[];
53
+ flags: string[];
54
+ timestamp: number;
55
+ coordinates?: {
56
+ lat: number;
57
+ lon: number;
58
+ accuracy: number;
67
59
  };
68
- price_badge?: {
69
- icon: Record<string, any>;
60
+ ad_type: number;
61
+ labels: Array<{
70
62
  id: string;
71
- label: string;
63
+ text: string;
64
+ type: 'PRIMARY' | 'SECONDARY' | string;
65
+ }>;
66
+ canonical_url: string;
67
+ extras: unknown[];
68
+ price: {
69
+ amount: number;
70
+ currency_code: string;
71
+ price_unit: string;
72
72
  };
73
- share_url: string;
74
- state_id: string;
75
- subject: string;
76
- type: string;
77
- zipcode: string;
73
+ distance: number;
74
+ trade_type: string;
78
75
  }
76
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blocket.js",
3
- "version": "1.2.2",
3
+ "version": "1.4.0",
4
4
  "description": "A user-friendly js wrapper for blocket.se",
5
5
  "keywords": [
6
6
  "blocket",
@@ -40,7 +40,11 @@
40
40
  "build"
41
41
  ],
42
42
  "dependencies": {
43
- "ofetch": "^1.4.1"
43
+ "@playwright/test": "^1.55.0",
44
+ "@types/node": "^24.5.0",
45
+ "ofetch": "^1.4.1",
46
+ "playwright": "^1.55.0",
47
+ "ts-node": "^10.9.2"
44
48
  },
45
49
  "devDependencies": {
46
50
  "pre-push": "^0.1.4",
@@ -1,16 +0,0 @@
1
- /**
2
- * Get the cached token.
3
- * @returns Cached bearer token if available.
4
- */
5
- export declare const getCachedToken: () => string | null;
6
- /**
7
- * Set the cached token.
8
- * @param token Bearer token.
9
- */
10
- export declare const setCachedToken: (token: string) => void;
11
- /**
12
- * Fetch a new token from Blocket API.
13
- * @param forceRefresh If true, ignores cached token and fetches a new one.
14
- * @returns The bearer token.
15
- */
16
- export declare const fetchToken: (forceRefresh?: boolean) => Promise<string>;
@@ -1,48 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.fetchToken = exports.setCachedToken = exports.getCachedToken = void 0;
13
- const ofetch_1 = require("ofetch");
14
- const config_1 = require("../config");
15
- let cachedToken = null;
16
- /**
17
- * Get the cached token.
18
- * @returns Cached bearer token if available.
19
- */
20
- const getCachedToken = () => cachedToken;
21
- exports.getCachedToken = getCachedToken;
22
- /**
23
- * Set the cached token.
24
- * @param token Bearer token.
25
- */
26
- const setCachedToken = (token) => {
27
- cachedToken = token;
28
- };
29
- exports.setCachedToken = setCachedToken;
30
- /**
31
- * Fetch a new token from Blocket API.
32
- * @param forceRefresh If true, ignores cached token and fetches a new one.
33
- * @returns The bearer token.
34
- */
35
- const fetchToken = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (forceRefresh = false) {
36
- if (!forceRefresh && cachedToken)
37
- return cachedToken;
38
- (0, config_1.logger)('debug', 'Fetching new Blocket API token.');
39
- const config = (0, config_1.getBaseConfig)();
40
- const tokenData = yield (0, ofetch_1.ofetch)(config.tokenEndpoint);
41
- if (!tokenData || !tokenData.bearerToken) {
42
- throw new Error('Failed to retrieve Blocket API token.');
43
- }
44
- cachedToken = tokenData.bearerToken;
45
- return cachedToken;
46
- });
47
- exports.fetchToken = fetchToken;
48
- //# sourceMappingURL=token.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"token.js","sourceRoot":"","sources":["../../lib/client/token.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,mCAAgC;AAEhC,sCAAkD;AAIlD,IAAI,WAAW,GAAkB,IAAI,CAAC;AAEtC;;;GAGG;AACI,MAAM,cAAc,GAAG,GAAkB,EAAE,CAAC,WAAW,CAAC;AAAlD,QAAA,cAAc,kBAAoC;AAE/D;;;GAGG;AACI,MAAM,cAAc,GAAG,CAAC,KAAa,EAAQ,EAAE;IACpD,WAAW,GAAG,KAAK,CAAC;AACtB,CAAC,CAAC;AAFW,QAAA,cAAc,kBAEzB;AAEF;;;;GAIG;AACI,MAAM,UAAU,GAAG,YAEP,EAAE,mDADnB,eAAwB,KAAK;IAE7B,IAAI,CAAC,YAAY,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC;IAErD,IAAA,eAAM,EAAC,OAAO,EAAE,iCAAiC,CAAC,CAAC;IAEnD,MAAM,MAAM,GAAG,IAAA,sBAAa,GAAE,CAAC;IAE/B,MAAM,SAAS,GAAG,MAAM,IAAA,eAAM,EAAqB,MAAM,CAAC,aAAa,CAAC,CAAC;IACzE,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;QACzC,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;IACpC,OAAO,WAAW,CAAC;AACrB,CAAC,CAAA,CAAC;AAhBW,QAAA,UAAU,cAgBrB"}
@@ -1,44 +0,0 @@
1
- import { ofetch } from 'ofetch';
2
-
3
- import { getBaseConfig, logger } from '../config';
4
-
5
- import type { BlocketAccessToken } from '../types';
6
-
7
- let cachedToken: string | null = null;
8
-
9
- /**
10
- * Get the cached token.
11
- * @returns Cached bearer token if available.
12
- */
13
- export const getCachedToken = (): string | null => cachedToken;
14
-
15
- /**
16
- * Set the cached token.
17
- * @param token Bearer token.
18
- */
19
- export const setCachedToken = (token: string): void => {
20
- cachedToken = token;
21
- };
22
-
23
- /**
24
- * Fetch a new token from Blocket API.
25
- * @param forceRefresh If true, ignores cached token and fetches a new one.
26
- * @returns The bearer token.
27
- */
28
- export const fetchToken = async (
29
- forceRefresh: boolean = false
30
- ): Promise<string> => {
31
- if (!forceRefresh && cachedToken) return cachedToken;
32
-
33
- logger('debug', 'Fetching new Blocket API token.');
34
-
35
- const config = getBaseConfig();
36
-
37
- const tokenData = await ofetch<BlocketAccessToken>(config.tokenEndpoint);
38
- if (!tokenData || !tokenData.bearerToken) {
39
- throw new Error('Failed to retrieve Blocket API token.');
40
- }
41
-
42
- cachedToken = tokenData.bearerToken;
43
- return cachedToken;
44
- };