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.
- package/dist/client/index.d.ts +5 -1
- package/dist/client/index.js +29 -41
- package/dist/client/index.js.map +1 -1
- package/dist/client/request.d.ts +2 -3
- package/dist/client/request.js +5 -13
- package/dist/client/request.js.map +1 -1
- package/dist/config/index.js +1 -3
- package/dist/config/index.js.map +1 -1
- package/dist/types/config.d.ts +10 -15
- package/dist/types/index.d.ts +56 -58
- package/lib/client/index.ts +33 -24
- package/lib/client/request.ts +8 -18
- package/lib/config/index.ts +2 -4
- package/lib/types/config.ts +10 -15
- package/lib/types/index.ts +57 -59
- package/package.json +6 -2
- package/dist/client/token.d.ts +0 -16
- package/dist/client/token.js +0 -48
- package/dist/client/token.js.map +0 -1
- package/lib/client/token.ts +0 -44
package/dist/client/index.d.ts
CHANGED
|
@@ -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
|
|
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>;
|
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,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
|
|
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.
|
|
67
|
-
!Array.isArray(firstPageResponse.
|
|
68
|
-
throw new Error(`Unexpected Blocket API response structure, expected array of ads, got: ${typeof (firstPageResponse === null || firstPageResponse === void 0 ? void 0 : firstPageResponse.
|
|
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
|
-
|
|
71
|
-
|
|
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.
|
|
74
|
-
const totalPages =
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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
|
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.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { type FetchOptions } from 'ofetch';
|
|
2
2
|
/**
|
|
3
|
-
* Make an API request with
|
|
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
|
|
8
|
+
export declare function apiRequest<T>(url: string, options?: FetchOptions<'json', any>): Promise<T>;
|
package/dist/client/request.js
CHANGED
|
@@ -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
|
|
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 = {}
|
|
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(
|
|
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 ((
|
|
33
|
-
(0, config_1.logger)('
|
|
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":";;;;;;;;;;;
|
|
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"}
|
package/dist/config/index.js
CHANGED
|
@@ -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://
|
|
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
|
/**
|
package/dist/config/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/config/index.ts"],"names":[],"mappings":";;;AAEA;;GAEG;AACU,QAAA,aAAa,GAAkB;IAC1C,UAAU,
|
|
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"}
|
package/dist/types/config.d.ts
CHANGED
|
@@ -4,25 +4,15 @@
|
|
|
4
4
|
export interface BlocketConfig {
|
|
5
5
|
/**
|
|
6
6
|
* Base URL for the Blocket API.
|
|
7
|
-
* @default 'https://
|
|
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
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,74 +1,72 @@
|
|
|
1
1
|
export * from './config';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Blocket API response containing an array of ads with metadata.
|
|
4
4
|
*/
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
|
11
|
+
* Blocket API metadata containing pagination and search info.
|
|
18
12
|
*/
|
|
19
|
-
export interface
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
65
|
-
|
|
57
|
+
ad_type: number;
|
|
58
|
+
labels: Array<{
|
|
66
59
|
id: string;
|
|
67
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
subject: string;
|
|
72
|
-
type: string;
|
|
73
|
-
zipcode: string;
|
|
70
|
+
distance: number;
|
|
71
|
+
trade_type: string;
|
|
74
72
|
}
|
package/lib/client/index.ts
CHANGED
|
@@ -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.
|
|
77
|
-
!Array.isArray(firstPageResponse.
|
|
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?.
|
|
80
|
+
`Unexpected Blocket API response structure, expected array of ads, got: ${typeof firstPageResponse?.docs}`
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
const paging = firstPageResponse.metadata?.paging;
|
|
85
|
+
if (!paging || paging.last <= 1) {
|
|
86
|
+
return firstPageResponse.docs;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
const allAds = [...firstPageResponse.
|
|
89
|
-
const totalPages =
|
|
89
|
+
const allAds = [...firstPageResponse.docs];
|
|
90
|
+
const totalPages = paging.last;
|
|
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
|
-
if (response && response.
|
|
109
|
-
allAds.push(...response.
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
139
|
+
if (!response || !response.docs) {
|
|
136
140
|
return null;
|
|
137
141
|
}
|
|
138
142
|
|
|
139
|
-
|
|
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
|
}
|
package/lib/client/request.ts
CHANGED
|
@@ -1,42 +1,32 @@
|
|
|
1
1
|
import { ofetch, type FetchOptions } from 'ofetch';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { getBaseConfig, logger } from '../config';
|
|
3
|
+
import { logger } from '../config';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
|
-
* Make an API request with
|
|
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?.
|
|
25
|
+
if (error?.status >= 400) {
|
|
31
26
|
logger(
|
|
32
|
-
'
|
|
33
|
-
`
|
|
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
|
}
|
package/lib/config/index.ts
CHANGED
|
@@ -4,11 +4,9 @@ import { BlocketQueryConfig, BlocketConfig } from '../types';
|
|
|
4
4
|
* Default global configuration.
|
|
5
5
|
*/
|
|
6
6
|
export const defaultConfig: BlocketConfig = {
|
|
7
|
-
apiBaseUrl:
|
|
8
|
-
|
|
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 };
|
package/lib/types/config.ts
CHANGED
|
@@ -4,25 +4,15 @@
|
|
|
4
4
|
export interface BlocketConfig {
|
|
5
5
|
/**
|
|
6
6
|
* Base URL for the Blocket API.
|
|
7
|
-
* @default 'https://
|
|
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
|
}
|
package/lib/types/index.ts
CHANGED
|
@@ -1,78 +1,76 @@
|
|
|
1
1
|
export * from './config';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Blocket API response containing an array of ads with metadata.
|
|
5
5
|
*/
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
13
|
+
* Blocket API metadata containing pagination and search info.
|
|
21
14
|
*/
|
|
22
|
-
export interface
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
69
|
-
|
|
60
|
+
ad_type: number;
|
|
61
|
+
labels: Array<{
|
|
70
62
|
id: string;
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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.
|
|
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
|
-
"
|
|
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",
|
package/dist/client/token.d.ts
DELETED
|
@@ -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>;
|
package/dist/client/token.js
DELETED
|
@@ -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
|
package/dist/client/token.js.map
DELETED
|
@@ -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"}
|
package/lib/client/token.ts
DELETED
|
@@ -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
|
-
};
|