blocket.js 1.2.1 → 1.3.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.
@@ -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,6 @@ 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;
59
52
  if (!query.query)
60
53
  throw new Error('Query string is required');
61
54
  const config = (0, config_1.getBaseConfig)();
@@ -72,31 +65,16 @@ function find(query, fetchOptions) {
72
65
  }
73
66
  const allAds = [...firstPageResponse.data];
74
67
  const totalPages = firstPageResponse.total_page_count;
75
- const pagePromises = Array.from({ length: totalPages - 1 }, (_, i) => i + 2).map((page) => {
68
+ // Optimized pagination: Direct page parameter requests without delay
69
+ for (let page = 2; page <= totalPages; page++) {
76
70
  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
- }
71
+ const response = yield (0, request_1.apiRequest)(config.apiBaseUrl, Object.assign({ query: pageParams }, fetchOptions));
72
+ if (response && response.data && Array.isArray(response.data)) {
73
+ allAds.push(...response.data);
92
74
  }
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);
75
+ else {
76
+ throw new Error(`Unexpected Blocket API response structure in paginated results, expected array of ads`);
98
77
  }
99
- finally { if (e_1) throw e_1.error; }
100
78
  }
101
79
  return allAds;
102
80
  });
@@ -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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"}
@@ -26,11 +26,16 @@ function apiRequest(url_1) {
26
26
  const config = (0, config_1.getBaseConfig)();
27
27
  const token = yield (0, token_1.fetchToken)();
28
28
  try {
29
- return yield (0, ofetch_1.ofetch)(url, Object.assign(Object.assign({}, options), { headers: Object.assign(Object.assign({}, options.headers), { Authorization: `Bearer ${token}` }) }));
29
+ const response = yield (0, ofetch_1.ofetch)(url, Object.assign(Object.assign({}, options), { headers: Object.assign(Object.assign({ 'Accept': 'application/json', 'Accept-Language': 'sv-SE,sv;q=0.9,en;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Referer': 'https://www.blocket.se/' }, options.headers), { Authorization: `Bearer ${token}` }) }));
30
+ return response;
30
31
  }
31
32
  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}).`);
33
+ // Enhanced error handling for HTTP status codes
34
+ if ((error === null || error === void 0 ? void 0 : error.status) >= 400) {
35
+ (0, config_1.logger)('error', `HTTP ${error.status}: ${error.statusText || 'Request failed'}`);
36
+ }
37
+ if (((error === null || error === void 0 ? void 0 : error.status) === 401 || ((_a = error === null || error === void 0 ? void 0 : error.data) === null || _a === void 0 ? void 0 : _a.status_code) === 401) && retryCount < config.retryAttempts) {
38
+ (0, config_1.logger)('info', `Token expired (${error.status}). Retrying request (${retryCount + 1}/${config.retryAttempts}).`);
34
39
  const newToken = yield (0, token_1.fetchToken)(true);
35
40
  (0, token_1.setCachedToken)(newToken);
36
41
  return apiRequest(url, options, retryCount + 1);
@@ -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":";;;;;;;;;;;AAYA,gCA0CC;AAtDD,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,MAAM,QAAQ,GAAG,MAAM,IAAA,eAAM,EAAI,GAAG,kCAC/B,OAAO,KACV,OAAO,gCACL,QAAQ,EAAE,kBAAkB,EAC5B,iBAAiB,EAAE,yBAAyB,EAC5C,iBAAiB,EAAE,mBAAmB,EACtC,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,yBAAyB,IACjC,OAAO,CAAC,OAAO,KAClB,aAAa,EAAE,UAAU,KAAK,EAAE,OAElC,CAAC;YAEH,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,gDAAgD;YAChD,IAAI,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,MAAM,KAAI,GAAG,EAAE,CAAC;gBACzB,IAAA,eAAM,EAAC,OAAO,EAAE,QAAQ,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAAC,CAAC;YACnF,CAAC;YAED,IAAI,CAAC,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,MAAM,MAAK,GAAG,IAAI,CAAA,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,IAAI,0CAAE,WAAW,MAAK,GAAG,CAAC,IAAI,UAAU,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;gBACrG,IAAA,eAAM,EACJ,MAAM,EACN,kBAAkB,KAAK,CAAC,MAAM,wBAAwB,UAAU,GAAG,CAAC,IAClE,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"}
@@ -37,7 +37,15 @@ const fetchToken = (...args_1) => __awaiter(void 0, [...args_1], void 0, functio
37
37
  return cachedToken;
38
38
  (0, config_1.logger)('debug', 'Fetching new Blocket API token.');
39
39
  const config = (0, config_1.getBaseConfig)();
40
- const tokenData = yield (0, ofetch_1.ofetch)(config.tokenEndpoint);
40
+ const tokenData = yield (0, ofetch_1.ofetch)(config.tokenEndpoint, {
41
+ headers: {
42
+ 'Accept': 'application/json',
43
+ 'Accept-Language': 'sv-SE,sv;q=0.9,en;q=0.8',
44
+ 'Accept-Encoding': 'gzip, deflate, br',
45
+ 'Connection': 'keep-alive',
46
+ 'Referer': 'https://www.blocket.se/',
47
+ },
48
+ });
41
49
  if (!tokenData || !tokenData.bearerToken) {
42
50
  throw new Error('Failed to retrieve Blocket API token.');
43
51
  }
@@ -1 +1 @@
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
+ {"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,EAAE;QACvE,OAAO,EAAE;YACP,QAAQ,EAAE,kBAAkB;YAC5B,iBAAiB,EAAE,yBAAyB;YAC5C,iBAAiB,EAAE,mBAAmB;YACtC,YAAY,EAAE,YAAY;YAC1B,SAAS,EAAE,yBAAyB;SACrC;KACF,CAAC,CAAC;IAEH,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;AAzBW,QAAA,UAAU,cAyBrB"}
@@ -43,7 +43,7 @@ export interface BlocketQueryConfig {
43
43
  /**
44
44
  * The sorting order of the results.
45
45
  */
46
- sort?: 'rel';
46
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
47
47
  /**
48
48
  * The type of listing to search for. `s` for selling, `b` for buying, `a` for all.
49
49
  * @default 's'
@@ -53,7 +53,7 @@ export interface BlocketQueryConfig {
53
53
  * The status of the ad. `active`, `inactive`, or `all`.
54
54
  * @default 'active'
55
55
  */
56
- status?: 'active' | 'deleted' | 'hidden_by_user';
56
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
57
57
  /**
58
58
  * The maximum distance in kilometers from the search location.
59
59
  */
@@ -62,6 +62,10 @@ export interface BlocketQueryConfig {
62
62
  * Additional filters or fields to include in the response.
63
63
  */
64
64
  include?: 'extend_with_shipping' | string;
65
+ /**
66
+ * Page number for pagination (starts from 1).
67
+ */
68
+ page?: number;
65
69
  }
66
70
  /**
67
71
  * Native Blocket query parameters for API requests.
@@ -69,9 +73,10 @@ export interface BlocketQueryConfig {
69
73
  export interface BlocketQueryParamsNative {
70
74
  q: string;
71
75
  lim?: number;
72
- sort?: 'rel';
76
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
73
77
  st?: 's' | 'b' | 'a';
74
- status?: 'active' | 'deleted' | 'hidden_by_user';
78
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
75
79
  gl?: number;
76
80
  include?: string;
81
+ page?: number;
77
82
  }
@@ -38,25 +38,46 @@ export interface BlocketAd {
38
38
  ad_id: string;
39
39
  ad_status: 'active' | 'inactive' | string;
40
40
  advertiser: {
41
- account_id: string;
42
- contact_methods: Record<string, any>;
41
+ account_id?: string;
42
+ contact_methods: {
43
+ phone: boolean;
44
+ sms: boolean;
45
+ };
43
46
  name: string;
44
- public_profile: Record<string, any>;
45
- type: 'private' | 'business';
47
+ public_profile?: Record<string, any>;
48
+ store_name?: string;
49
+ type: 'private' | 'business' | 'store';
46
50
  };
47
51
  body: string;
48
- category: Record<string, any>[];
49
- co2_text: string;
50
- images: Record<string, any>[];
52
+ category: Array<{
53
+ id: string;
54
+ name: string;
55
+ }>;
56
+ co2_text?: string;
57
+ images: Array<{
58
+ height: number;
59
+ type: string;
60
+ url: string;
61
+ width: number;
62
+ }>;
63
+ infopage?: {
64
+ text: string;
65
+ url: string;
66
+ };
51
67
  list_id: string;
52
68
  list_time: string;
53
- location: Record<string, any>[];
69
+ location: Array<{
70
+ id: string;
71
+ name: string;
72
+ parent_id?: string;
73
+ }>;
54
74
  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>;
75
+ parameter_groups?: Record<string, any>[];
76
+ parameters_raw?: {
77
+ is_shipping_buy_now_enabled?: Record<string, any>;
78
+ shipping_enabled?: Record<string, any>;
59
79
  };
80
+ partner_info?: any;
60
81
  price: {
61
82
  suffix: string;
62
83
  value: number;
@@ -29,6 +29,7 @@ function remapQueryParams(
29
29
  status: 'status',
30
30
  geolocation: 'gl',
31
31
  include: 'include',
32
+ page: 'page',
32
33
  };
33
34
 
34
35
  const remapped: Partial<BlocketQueryParamsNative> = {};
@@ -88,23 +89,17 @@ export async function find(
88
89
  const allAds = [...firstPageResponse.data];
89
90
  const totalPages = firstPageResponse.total_page_count;
90
91
 
91
- const pagePromises = Array.from(
92
- { length: totalPages - 1 },
93
- (_, i) => i + 2
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
- // Prevent rate limiting by adding a delay between requests
107
- await new Promise((resolve) => setTimeout(resolve, 200));
108
103
 
109
104
  if (response && response.data && Array.isArray(response.data)) {
110
105
  allAds.push(...response.data);
@@ -19,18 +19,30 @@ export async function apiRequest<T>(
19
19
  const token = await fetchToken();
20
20
 
21
21
  try {
22
- return await ofetch<T>(url, {
22
+ const response = await ofetch<T>(url, {
23
23
  ...options,
24
24
  headers: {
25
+ 'Accept': 'application/json',
26
+ 'Accept-Language': 'sv-SE,sv;q=0.9,en;q=0.8',
27
+ 'Accept-Encoding': 'gzip, deflate, br',
28
+ 'Connection': 'keep-alive',
29
+ 'Referer': 'https://www.blocket.se/',
25
30
  ...options.headers,
26
31
  Authorization: `Bearer ${token}`,
27
32
  },
28
33
  });
34
+
35
+ return response;
29
36
  } catch (error: any) {
30
- if (error?.data?.status_code === 401 && retryCount < config.retryAttempts) {
37
+ // Enhanced error handling for HTTP status codes
38
+ if (error?.status >= 400) {
39
+ logger('error', `HTTP ${error.status}: ${error.statusText || 'Request failed'}`);
40
+ }
41
+
42
+ if ((error?.status === 401 || error?.data?.status_code === 401) && retryCount < config.retryAttempts) {
31
43
  logger(
32
44
  'info',
33
- `Token expired. Retrying request (${retryCount + 1}/${
45
+ `Token expired (${error.status}). Retrying request (${retryCount + 1}/${
34
46
  config.retryAttempts
35
47
  }).`
36
48
  );
@@ -34,7 +34,16 @@ export const fetchToken = async (
34
34
 
35
35
  const config = getBaseConfig();
36
36
 
37
- const tokenData = await ofetch<BlocketAccessToken>(config.tokenEndpoint);
37
+ const tokenData = await ofetch<BlocketAccessToken>(config.tokenEndpoint, {
38
+ headers: {
39
+ 'Accept': 'application/json',
40
+ 'Accept-Language': 'sv-SE,sv;q=0.9,en;q=0.8',
41
+ 'Accept-Encoding': 'gzip, deflate, br',
42
+ 'Connection': 'keep-alive',
43
+ 'Referer': 'https://www.blocket.se/',
44
+ },
45
+ });
46
+
38
47
  if (!tokenData || !tokenData.bearerToken) {
39
48
  throw new Error('Failed to retrieve Blocket API token.');
40
49
  }
@@ -44,7 +44,7 @@ export interface BlocketQueryConfig {
44
44
  /**
45
45
  * The sorting order of the results.
46
46
  */
47
- sort?: 'rel';
47
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
48
48
  /**
49
49
  * The type of listing to search for. `s` for selling, `b` for buying, `a` for all.
50
50
  * @default 's'
@@ -54,7 +54,7 @@ export interface BlocketQueryConfig {
54
54
  * The status of the ad. `active`, `inactive`, or `all`.
55
55
  * @default 'active'
56
56
  */
57
- status?: 'active' | 'deleted' | 'hidden_by_user';
57
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
58
58
  /**
59
59
  * The maximum distance in kilometers from the search location.
60
60
  */
@@ -63,6 +63,10 @@ export interface BlocketQueryConfig {
63
63
  * Additional filters or fields to include in the response.
64
64
  */
65
65
  include?: 'extend_with_shipping' | string;
66
+ /**
67
+ * Page number for pagination (starts from 1).
68
+ */
69
+ page?: number;
66
70
  }
67
71
 
68
72
  /**
@@ -71,9 +75,10 @@ export interface BlocketQueryConfig {
71
75
  export interface BlocketQueryParamsNative {
72
76
  q: string;
73
77
  lim?: number;
74
- sort?: 'rel';
78
+ sort?: 'rel' | 'date' | 'price_asc' | 'price_desc';
75
79
  st?: 's' | 'b' | 'a';
76
- status?: 'active' | 'deleted' | 'hidden_by_user';
80
+ status?: 'active' | 'deleted' | 'hidden_by_user' | 'all';
77
81
  gl?: number;
78
82
  include?: string;
83
+ page?: number;
79
84
  }
@@ -42,25 +42,46 @@ export interface BlocketAd {
42
42
  ad_id: string;
43
43
  ad_status: 'active' | 'inactive' | string;
44
44
  advertiser: {
45
- account_id: string;
46
- contact_methods: Record<string, any>;
45
+ account_id?: string;
46
+ contact_methods: {
47
+ phone: boolean;
48
+ sms: boolean;
49
+ };
47
50
  name: string;
48
- public_profile: Record<string, any>;
49
- type: 'private' | 'business';
51
+ public_profile?: Record<string, any>;
52
+ store_name?: string; // For business listings
53
+ type: 'private' | 'business' | 'store';
50
54
  };
51
55
  body: string;
52
- category: Record<string, any>[];
53
- co2_text: string;
54
- images: Record<string, any>[];
56
+ category: Array<{
57
+ id: string;
58
+ name: string;
59
+ }>;
60
+ co2_text?: string;
61
+ images: Array<{
62
+ height: number;
63
+ type: string;
64
+ url: string;
65
+ width: number;
66
+ }>;
67
+ infopage?: {
68
+ text: string;
69
+ url: string;
70
+ }; // External links for businesses
55
71
  list_id: string;
56
72
  list_time: string; // ISO date string
57
- location: Record<string, any>[];
73
+ location: Array<{
74
+ id: string;
75
+ name: string;
76
+ parent_id?: string;
77
+ }>;
58
78
  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>;
79
+ parameter_groups?: Record<string, any>[];
80
+ parameters_raw?: {
81
+ is_shipping_buy_now_enabled?: Record<string, any>;
82
+ shipping_enabled?: Record<string, any>;
63
83
  };
84
+ partner_info?: any;
64
85
  price: {
65
86
  suffix: string;
66
87
  value: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blocket.js",
3
- "version": "1.2.1",
3
+ "version": "1.3.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",