blocket.js 1.2.2 → 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.
- package/dist/client/index.js +8 -29
- package/dist/client/index.js.map +1 -1
- package/dist/client/request.js +8 -3
- package/dist/client/request.js.map +1 -1
- package/dist/client/token.js +9 -1
- package/dist/client/token.js.map +1 -1
- package/dist/types/config.d.ts +9 -4
- package/dist/types/index.d.ts +33 -12
- package/lib/client/index.ts +5 -9
- package/lib/client/request.ts +15 -3
- package/lib/client/token.ts +10 -1
- package/lib/types/config.ts +9 -4
- package/lib/types/index.ts +33 -12
- package/package.json +6 -2
package/dist/client/index.js
CHANGED
|
@@ -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,30 +65,16 @@ function find(query, fetchOptions) {
|
|
|
72
65
|
}
|
|
73
66
|
const allAds = [...firstPageResponse.data];
|
|
74
67
|
const totalPages = firstPageResponse.total_page_count;
|
|
75
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
}
|
|
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);
|
|
91
74
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
finally {
|
|
95
|
-
try {
|
|
96
|
-
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`);
|
|
97
77
|
}
|
|
98
|
-
finally { if (e_1) throw e_1.error; }
|
|
99
78
|
}
|
|
100
79
|
return allAds;
|
|
101
80
|
});
|
package/dist/client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/client/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/client/index.ts"],"names":[],"mappings":"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"}
|
package/dist/client/request.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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,
|
|
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"}
|
package/dist/client/token.js
CHANGED
|
@@ -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
|
}
|
package/dist/client/token.js.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
|
42
|
-
contact_methods:
|
|
41
|
+
account_id?: string;
|
|
42
|
+
contact_methods: {
|
|
43
|
+
phone: boolean;
|
|
44
|
+
sms: boolean;
|
|
45
|
+
};
|
|
43
46
|
name: string;
|
|
44
|
-
public_profile
|
|
45
|
-
|
|
47
|
+
public_profile?: Record<string, any>;
|
|
48
|
+
store_name?: string;
|
|
49
|
+
type: 'private' | 'business' | 'store';
|
|
46
50
|
};
|
|
47
51
|
body: string;
|
|
48
|
-
category:
|
|
49
|
-
|
|
50
|
-
|
|
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:
|
|
69
|
+
location: Array<{
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
parent_id?: string;
|
|
73
|
+
}>;
|
|
54
74
|
map_url: string;
|
|
55
|
-
parameter_groups
|
|
56
|
-
parameters_raw
|
|
57
|
-
is_shipping_buy_now_enabled
|
|
58
|
-
shipping_enabled
|
|
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;
|
package/lib/client/index.ts
CHANGED
|
@@ -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,22 +89,17 @@ export async function find(
|
|
|
88
89
|
const allAds = [...firstPageResponse.data];
|
|
89
90
|
const totalPages = firstPageResponse.total_page_count;
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
104
|
if (response && response.data && Array.isArray(response.data)) {
|
|
109
105
|
allAds.push(...response.data);
|
package/lib/client/request.ts
CHANGED
|
@@ -19,18 +19,30 @@ export async function apiRequest<T>(
|
|
|
19
19
|
const token = await fetchToken();
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
|
-
|
|
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
|
-
|
|
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
|
);
|
package/lib/client/token.ts
CHANGED
|
@@ -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
|
}
|
package/lib/types/config.ts
CHANGED
|
@@ -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
|
}
|
package/lib/types/index.ts
CHANGED
|
@@ -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
|
|
46
|
-
contact_methods:
|
|
45
|
+
account_id?: string;
|
|
46
|
+
contact_methods: {
|
|
47
|
+
phone: boolean;
|
|
48
|
+
sms: boolean;
|
|
49
|
+
};
|
|
47
50
|
name: string;
|
|
48
|
-
public_profile
|
|
49
|
-
|
|
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:
|
|
53
|
-
|
|
54
|
-
|
|
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:
|
|
73
|
+
location: Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
name: string;
|
|
76
|
+
parent_id?: string;
|
|
77
|
+
}>;
|
|
58
78
|
map_url: string;
|
|
59
|
-
parameter_groups
|
|
60
|
-
parameters_raw
|
|
61
|
-
is_shipping_buy_now_enabled
|
|
62
|
-
shipping_enabled
|
|
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.
|
|
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
|
-
"
|
|
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",
|