@blizzard-api/client 2.0.3 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +23 -16
- package/dist/index.js +52 -44
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -33,8 +33,8 @@ const client = await createBlizzardApiClient({
|
|
|
33
33
|
//Response will automatically be typed with the appropriate values
|
|
34
34
|
const response = await client.sendRequest(wow.commodities());
|
|
35
35
|
|
|
36
|
-
console.log(response
|
|
37
|
-
|
|
36
|
+
console.log(response);
|
|
37
|
+
^ typeof AuctionHouseCommoditiesResponse
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Authentication
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AxiosResponse } from 'axios';
|
|
1
|
+
import { Locales, Origins, Resource, ResourceResponse } from '@blizzard-api/core';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* An access token response from the Blizzard API.
|
|
@@ -34,6 +33,14 @@ interface AccessTokenRequestArguments {
|
|
|
34
33
|
origin?: Origins;
|
|
35
34
|
secret?: string;
|
|
36
35
|
}
|
|
36
|
+
type AxiosCompatability<T> = T & {
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated
|
|
39
|
+
* This property is only here for backward compatibility, it will be removed in the next major version.
|
|
40
|
+
* All data should be accessed directly from the response object instead of through this property.
|
|
41
|
+
*/
|
|
42
|
+
data: T;
|
|
43
|
+
};
|
|
37
44
|
/**
|
|
38
45
|
* A client configuration object.
|
|
39
46
|
* @example
|
|
@@ -56,10 +63,10 @@ interface ClientOptions {
|
|
|
56
63
|
* A Blizzard API client.
|
|
57
64
|
*/
|
|
58
65
|
interface IBlizzardApiClient {
|
|
59
|
-
getAccessToken: (options: AccessTokenRequestArguments) => Promise<
|
|
60
|
-
refreshAccessToken: (options: AccessTokenRequestArguments) => Promise<
|
|
66
|
+
getAccessToken: (options: AccessTokenRequestArguments) => Promise<AxiosCompatability<AccessToken>>;
|
|
67
|
+
refreshAccessToken: (options: AccessTokenRequestArguments) => Promise<AxiosCompatability<AccessToken>>;
|
|
61
68
|
setAccessToken: (token: string) => void;
|
|
62
|
-
validateAccessToken: (options: ValidateAccessTokenArguments) => Promise<
|
|
69
|
+
validateAccessToken: (options: ValidateAccessTokenArguments) => Promise<AxiosCompatability<ValidateAccessTokenResponse>>;
|
|
63
70
|
}
|
|
64
71
|
/**
|
|
65
72
|
* Validate an access token.
|
|
@@ -116,21 +123,22 @@ declare class BlizzardApiClient implements IBlizzardApiClient {
|
|
|
116
123
|
secret: string;
|
|
117
124
|
token?: string;
|
|
118
125
|
};
|
|
119
|
-
private
|
|
126
|
+
private ky;
|
|
127
|
+
constructor(options: ClientOptions);
|
|
120
128
|
/**
|
|
121
129
|
* Get an access token.
|
|
122
130
|
* @param options The access token request arguments. See {@link AccessTokenRequestArguments}.
|
|
123
131
|
* @returns The access token. See {@link AccessToken}.
|
|
124
132
|
* @example
|
|
125
133
|
* const response = await client.getAccessToken();
|
|
126
|
-
* const { access_token, token_type, expires_in, sub } = response
|
|
134
|
+
* const { access_token, token_type, expires_in, sub } = response;
|
|
127
135
|
* console.log(access_token, token_type, expires_in, sub);
|
|
128
136
|
* // => 'access'
|
|
129
137
|
* // => 'bearer'
|
|
130
138
|
* // => 86399
|
|
131
139
|
* // => 'client-id'
|
|
132
140
|
*/
|
|
133
|
-
getAccessToken: (options?: AccessTokenRequestArguments) => Promise<
|
|
141
|
+
getAccessToken: (options?: AccessTokenRequestArguments) => Promise<AxiosCompatability<AccessToken>>;
|
|
134
142
|
/**
|
|
135
143
|
* Set the access token.
|
|
136
144
|
* @param token The access token.
|
|
@@ -142,25 +150,24 @@ declare class BlizzardApiClient implements IBlizzardApiClient {
|
|
|
142
150
|
* @returns The access token. See {@link AccessToken}.
|
|
143
151
|
* @example
|
|
144
152
|
* const response = await client.refreshAccessToken();
|
|
145
|
-
* const { access_token, token_type, expires_in, sub } = response
|
|
153
|
+
* const { access_token, token_type, expires_in, sub } = response;
|
|
146
154
|
* console.log(access_token, token_type, expires_in, sub);
|
|
147
155
|
* // => 'access'
|
|
148
156
|
* // => 'bearer'
|
|
149
157
|
* // => 86399
|
|
150
158
|
* // => 'client-id'
|
|
151
159
|
*/
|
|
152
|
-
refreshAccessToken: (options?: AccessTokenRequestArguments) => Promise<
|
|
160
|
+
refreshAccessToken: (options?: AccessTokenRequestArguments) => Promise<AxiosCompatability<AccessToken>>;
|
|
153
161
|
/**
|
|
154
162
|
* Validate an access token.
|
|
155
163
|
* @param options The validate access token arguments. See {@link ValidateAccessTokenArguments}.
|
|
156
164
|
* @returns The response from the Blizzard API. See {@link ValidateAccessTokenResponse}.
|
|
157
165
|
* @example
|
|
158
166
|
* const response = await client.validateAccessToken({ token: 'access' });
|
|
159
|
-
* console.log(response.
|
|
167
|
+
* console.log(response.client_id);
|
|
160
168
|
* // => 'client-id'
|
|
161
169
|
*/
|
|
162
|
-
|
|
163
|
-
validateAccessToken: (options?: ValidateAccessTokenArguments) => Promise<AxiosResponse<ValidateAccessTokenResponse>>;
|
|
170
|
+
validateAccessToken: (options?: ValidateAccessTokenArguments) => Promise<AxiosCompatability<ValidateAccessTokenResponse>>;
|
|
164
171
|
/**
|
|
165
172
|
* Get the request configuration.
|
|
166
173
|
* @param resource The resource to fetch. See {@link Resource}.
|
|
@@ -174,7 +181,7 @@ declare class BlizzardApiClient implements IBlizzardApiClient {
|
|
|
174
181
|
'Content-Type': string;
|
|
175
182
|
'Battlenet-Namespace'?: string | undefined;
|
|
176
183
|
};
|
|
177
|
-
|
|
184
|
+
searchParams: {
|
|
178
185
|
locale: "de_DE" | "en_GB" | "en_US" | "es_ES" | "es_MX" | "fr_FR" | "it_IT" | "ko_KR" | "multi" | "pt_BR" | "pt_PT" | "ru_RU" | "zh_CN" | "zh_TW";
|
|
179
186
|
};
|
|
180
187
|
};
|
|
@@ -192,7 +199,7 @@ declare class BlizzardApiClient implements IBlizzardApiClient {
|
|
|
192
199
|
* @param headers Additional headers to include in the request.
|
|
193
200
|
* @returns The response from the Blizzard API. See {@link ResourceResponse}.
|
|
194
201
|
*/
|
|
195
|
-
sendRequest<T, Protected extends boolean = false>(resource: Resource<T, object, Protected>, options?: Partial<ClientOptions>, headers?: Record<string, string>): ResourceResponse<
|
|
202
|
+
sendRequest<T, Protected extends boolean = false>(resource: Resource<T, object, Protected>, options?: Partial<ClientOptions>, headers?: Record<string, string>): ResourceResponse<AxiosCompatability<T>>;
|
|
196
203
|
}
|
|
197
204
|
|
|
198
205
|
/**
|
|
@@ -203,4 +210,4 @@ declare class BlizzardApiClient implements IBlizzardApiClient {
|
|
|
203
210
|
*/
|
|
204
211
|
declare const createBlizzardApiClient: (options: ClientOptions, onTokenRefresh?: ((token: AccessToken) => void) | boolean) => Promise<BlizzardApiClient>;
|
|
205
212
|
|
|
206
|
-
export { type AccessToken, type AccessTokenRequestArguments, type ClientOptions, type IBlizzardApiClient, type ValidateAccessTokenArguments, type ValidateAccessTokenResponse, createBlizzardApiClient, createBlizzardApiClient as default };
|
|
213
|
+
export { type AccessToken, type AccessTokenRequestArguments, type AxiosCompatability, type ClientOptions, type IBlizzardApiClient, type ValidateAccessTokenArguments, type ValidateAccessTokenResponse, createBlizzardApiClient, createBlizzardApiClient as default };
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
import { setTimeout } from 'node:timers';
|
|
2
2
|
import { stringify } from 'node:querystring';
|
|
3
3
|
import { getBlizzardApi } from '@blizzard-api/core';
|
|
4
|
-
import
|
|
4
|
+
import ky from 'ky';
|
|
5
5
|
|
|
6
6
|
// src/client/create-client.ts
|
|
7
7
|
var BlizzardApiClient = class {
|
|
8
8
|
defaults;
|
|
9
|
-
|
|
9
|
+
ky = ky.create();
|
|
10
|
+
constructor(options) {
|
|
11
|
+
const { locale, origin } = getBlizzardApi(options.origin, options.locale);
|
|
12
|
+
this.defaults = {
|
|
13
|
+
key: options.key,
|
|
14
|
+
locale,
|
|
15
|
+
origin,
|
|
16
|
+
secret: options.secret,
|
|
17
|
+
token: options.token
|
|
18
|
+
};
|
|
19
|
+
}
|
|
10
20
|
/**
|
|
11
21
|
* Get an access token.
|
|
12
22
|
* @param options The access token request arguments. See {@link AccessTokenRequestArguments}.
|
|
13
23
|
* @returns The access token. See {@link AccessToken}.
|
|
14
24
|
* @example
|
|
15
25
|
* const response = await client.getAccessToken();
|
|
16
|
-
* const { access_token, token_type, expires_in, sub } = response
|
|
26
|
+
* const { access_token, token_type, expires_in, sub } = response;
|
|
17
27
|
* console.log(access_token, token_type, expires_in, sub);
|
|
18
28
|
* // => 'access'
|
|
19
29
|
* // => 'bearer'
|
|
@@ -22,18 +32,20 @@ var BlizzardApiClient = class {
|
|
|
22
32
|
*/
|
|
23
33
|
getAccessToken = async (options) => {
|
|
24
34
|
const { key, origin, secret } = { ...this.defaults, ...options };
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
password: secret,
|
|
28
|
-
username: key
|
|
29
|
-
},
|
|
35
|
+
const basicAuth = Buffer.from(`${key}:${secret}`).toString("base64");
|
|
36
|
+
const response = await this.ky.post(`https://${origin}.battle.net/oauth/token`, {
|
|
30
37
|
headers: {
|
|
38
|
+
Authorization: `Basic ${basicAuth}`,
|
|
31
39
|
"Content-Type": "application/json"
|
|
32
40
|
},
|
|
33
|
-
|
|
41
|
+
searchParams: {
|
|
34
42
|
grant_type: "client_credentials"
|
|
35
43
|
}
|
|
36
|
-
});
|
|
44
|
+
}).json();
|
|
45
|
+
return {
|
|
46
|
+
data: response,
|
|
47
|
+
...response
|
|
48
|
+
};
|
|
37
49
|
};
|
|
38
50
|
/**
|
|
39
51
|
* Set the access token.
|
|
@@ -48,7 +60,7 @@ var BlizzardApiClient = class {
|
|
|
48
60
|
* @returns The access token. See {@link AccessToken}.
|
|
49
61
|
* @example
|
|
50
62
|
* const response = await client.refreshAccessToken();
|
|
51
|
-
* const { access_token, token_type, expires_in, sub } = response
|
|
63
|
+
* const { access_token, token_type, expires_in, sub } = response;
|
|
52
64
|
* console.log(access_token, token_type, expires_in, sub);
|
|
53
65
|
* // => 'access'
|
|
54
66
|
* // => 'bearer'
|
|
@@ -57,7 +69,7 @@ var BlizzardApiClient = class {
|
|
|
57
69
|
*/
|
|
58
70
|
refreshAccessToken = async (options) => {
|
|
59
71
|
const response = await this.getAccessToken(options);
|
|
60
|
-
this.setAccessToken(response.
|
|
72
|
+
this.setAccessToken(response.access_token);
|
|
61
73
|
return response;
|
|
62
74
|
};
|
|
63
75
|
/**
|
|
@@ -66,33 +78,24 @@ var BlizzardApiClient = class {
|
|
|
66
78
|
* @returns The response from the Blizzard API. See {@link ValidateAccessTokenResponse}.
|
|
67
79
|
* @example
|
|
68
80
|
* const response = await client.validateAccessToken({ token: 'access' });
|
|
69
|
-
* console.log(response.
|
|
81
|
+
* console.log(response.client_id);
|
|
70
82
|
* // => 'client-id'
|
|
71
83
|
*/
|
|
72
|
-
constructor(options) {
|
|
73
|
-
const { locale, origin } = getBlizzardApi(options.origin, options.locale);
|
|
74
|
-
this.defaults = {
|
|
75
|
-
key: options.key,
|
|
76
|
-
locale,
|
|
77
|
-
origin,
|
|
78
|
-
secret: options.secret,
|
|
79
|
-
token: options.token
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
84
|
validateAccessToken = async (options) => {
|
|
83
85
|
const { origin, token } = { ...this.defaults, ...options };
|
|
84
86
|
if (!token) {
|
|
85
87
|
throw new Error("No token has been set previously or been passed to the validateAccessToken method.");
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
headers: {
|
|
92
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
93
|
-
}
|
|
89
|
+
const response = await this.ky.post(`https://${origin}.battle.net/oauth/check_token`, {
|
|
90
|
+
body: stringify({ token }),
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
94
93
|
}
|
|
95
|
-
);
|
|
94
|
+
}).json();
|
|
95
|
+
return {
|
|
96
|
+
data: response,
|
|
97
|
+
...response
|
|
98
|
+
};
|
|
96
99
|
};
|
|
97
100
|
/**
|
|
98
101
|
* Get the request configuration.
|
|
@@ -105,6 +108,14 @@ var BlizzardApiClient = class {
|
|
|
105
108
|
const config = { ...this.defaults, ...options };
|
|
106
109
|
const endpoint = getBlizzardApi(config.origin, config.locale);
|
|
107
110
|
const namespace = resource.namespace ? { "Battlenet-Namespace": `${resource.namespace}-${endpoint.origin}` } : void 0;
|
|
111
|
+
const parameters = resource.parameters;
|
|
112
|
+
if (parameters) {
|
|
113
|
+
for (const key of Object.keys(parameters)) {
|
|
114
|
+
if (parameters[key] === void 0) {
|
|
115
|
+
delete parameters[key];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
108
119
|
return {
|
|
109
120
|
headers: {
|
|
110
121
|
...headers,
|
|
@@ -112,7 +123,7 @@ var BlizzardApiClient = class {
|
|
|
112
123
|
Authorization: `Bearer ${config.token}`,
|
|
113
124
|
"Content-Type": "application/json"
|
|
114
125
|
},
|
|
115
|
-
|
|
126
|
+
searchParams: {
|
|
116
127
|
locale: endpoint.locale,
|
|
117
128
|
...resource.parameters
|
|
118
129
|
}
|
|
@@ -140,14 +151,11 @@ var BlizzardApiClient = class {
|
|
|
140
151
|
async sendRequest(resource, options, headers) {
|
|
141
152
|
const url = this.getRequestUrl(resource, options);
|
|
142
153
|
const config = this.getRequestConfig(resource, options, headers);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
throw error;
|
|
150
|
-
}
|
|
154
|
+
const response = await this.ky.get(url, config).json();
|
|
155
|
+
return {
|
|
156
|
+
data: response,
|
|
157
|
+
...response
|
|
158
|
+
};
|
|
151
159
|
}
|
|
152
160
|
};
|
|
153
161
|
|
|
@@ -164,11 +172,11 @@ var createBlizzardApiClient = async (options, onTokenRefresh = true) => {
|
|
|
164
172
|
const client = new BlizzardApiClient(options);
|
|
165
173
|
const refreshToken = async () => {
|
|
166
174
|
const response = await client.getAccessToken();
|
|
167
|
-
client.setAccessToken(response.
|
|
175
|
+
client.setAccessToken(response.access_token);
|
|
168
176
|
if (typeof onTokenRefresh === "function") {
|
|
169
|
-
onTokenRefresh?.(response
|
|
177
|
+
onTokenRefresh?.(response);
|
|
170
178
|
}
|
|
171
|
-
const timeout = setTimeout(() => void refreshToken(), getTokenExpiration(response.
|
|
179
|
+
const timeout = setTimeout(() => void refreshToken(), getTokenExpiration(response.expires_in));
|
|
172
180
|
timeout.unref();
|
|
173
181
|
};
|
|
174
182
|
if (!onTokenRefresh) {
|
|
@@ -177,7 +185,7 @@ var createBlizzardApiClient = async (options, onTokenRefresh = true) => {
|
|
|
177
185
|
if (token) {
|
|
178
186
|
try {
|
|
179
187
|
const validatedToken = await client.validateAccessToken({ token });
|
|
180
|
-
const expiry = getTokenExpiration(validatedToken.
|
|
188
|
+
const expiry = getTokenExpiration(validatedToken.exp);
|
|
181
189
|
if (expiry - Date.now() < 6e4) {
|
|
182
190
|
await refreshToken();
|
|
183
191
|
} else {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client/client.ts","../src/client/create-client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { stringify } from 'node:querystring';\r\nimport { getBlizzardApi } from '@blizzard-api/core';\r\nimport type { Locales, Origins, Resource, ResourceResponse } from '@blizzard-api/core';\r\nimport type { AxiosResponse } from 'axios';\r\nimport axios, { AxiosError, isAxiosError } from 'axios';\r\nimport type {\r\n AccessToken,\r\n AccessTokenRequestArguments,\r\n ClientOptions,\r\n IBlizzardApiClient,\r\n ValidateAccessTokenArguments,\r\n ValidateAccessTokenResponse,\r\n} from './types';\r\n\r\n/**\r\n * A Blizzard API client.\r\n * @classdesc A client to interact with the Blizzard API.\r\n * @example\r\n * const client = new BlizzardApiClient({\r\n * key: 'client',\r\n * secret: 'secret',\r\n * origin: 'eu',\r\n * locale: 'en_GB',\r\n * token: 'access'\r\n * });\r\n */\r\nexport class BlizzardApiClient implements IBlizzardApiClient {\r\n public defaults: {\r\n key: string;\r\n locale: Locales;\r\n origin: Origins;\r\n secret: string;\r\n token?: string;\r\n };\r\n\r\n private axios = axios.create();\r\n\r\n /**\r\n * Get an access token.\r\n * @param options The access token request arguments. See {@link AccessTokenRequestArguments}.\r\n * @returns The access token. See {@link AccessToken}.\r\n * @example\r\n * const response = await client.getAccessToken();\r\n * const { access_token, token_type, expires_in, sub } = response.data;\r\n * console.log(access_token, token_type, expires_in, sub);\r\n * // => 'access'\r\n * // => 'bearer'\r\n * // => 86399\r\n * // => 'client-id'\r\n */\r\n public getAccessToken = async (options?: AccessTokenRequestArguments): Promise<AxiosResponse<AccessToken>> => {\r\n const { key, origin, secret } = { ...this.defaults, ...options };\r\n return this.axios.post<AccessToken>(`https://${origin}.battle.net/oauth/token`, undefined, {\r\n auth: {\r\n password: secret,\r\n username: key,\r\n },\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n },\r\n params: {\r\n grant_type: 'client_credentials',\r\n },\r\n });\r\n };\r\n\r\n /**\r\n * Set the access token.\r\n * @param token The access token.\r\n */\r\n public setAccessToken = (token: string): void => {\r\n this.defaults.token = token;\r\n };\r\n\r\n /**\r\n * Refresh the access token.\r\n * @param options The access token request arguments. See {@link AccessTokenRequestArguments}.\r\n * @returns The access token. See {@link AccessToken}.\r\n * @example\r\n * const response = await client.refreshAccessToken();\r\n * const { access_token, token_type, expires_in, sub } = response.data;\r\n * console.log(access_token, token_type, expires_in, sub);\r\n * // => 'access'\r\n * // => 'bearer'\r\n * // => 86399\r\n * // => 'client-id'\r\n */\r\n public refreshAccessToken = async (options?: AccessTokenRequestArguments): Promise<AxiosResponse<AccessToken>> => {\r\n const response = await this.getAccessToken(options);\r\n this.setAccessToken(response.data.access_token);\r\n return response;\r\n };\r\n\r\n /**\r\n * Validate an access token.\r\n * @param options The validate access token arguments. See {@link ValidateAccessTokenArguments}.\r\n * @returns The response from the Blizzard API. See {@link ValidateAccessTokenResponse}.\r\n * @example\r\n * const response = await client.validateAccessToken({ token: 'access' });\r\n * console.log(response.data.client_id);\r\n * // => 'client-id'\r\n */\r\n constructor(options: ClientOptions) {\r\n const { locale, origin } = getBlizzardApi(options.origin, options.locale);\r\n this.defaults = {\r\n key: options.key,\r\n locale: locale,\r\n origin: origin,\r\n secret: options.secret,\r\n token: options.token,\r\n };\r\n }\r\n\r\n public validateAccessToken = async (\r\n options?: ValidateAccessTokenArguments,\r\n ): Promise<AxiosResponse<ValidateAccessTokenResponse>> => {\r\n const { origin, token } = { ...this.defaults, ...options };\r\n\r\n if (!token) {\r\n throw new Error('No token has been set previously or been passed to the validateAccessToken method.');\r\n }\r\n\r\n return await this.axios.post<ValidateAccessTokenResponse>(\r\n `https://${origin}.battle.net/oauth/check_token`,\r\n stringify({ token }),\r\n {\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n },\r\n );\r\n };\r\n\r\n /**\r\n * Get the request configuration.\r\n * @param resource The resource to fetch. See {@link Resource}.\r\n * @param options Client options. See {@link ClientOptions}.\r\n * @param headers Additional headers to include in the request.\r\n * @returns The request configuration.\r\n */\r\n public getRequestConfig<T, Protected extends boolean = false>(\r\n resource: Resource<T, object, Protected>,\r\n options?: Partial<ClientOptions>,\r\n headers?: Record<string, string>,\r\n ) {\r\n const config = { ...this.defaults, ...options };\r\n const endpoint = getBlizzardApi(config.origin, config.locale);\r\n\r\n const namespace = resource.namespace\r\n ? { 'Battlenet-Namespace': `${resource.namespace}-${endpoint.origin}` }\r\n : undefined;\r\n\r\n return {\r\n headers: {\r\n ...headers,\r\n ...namespace,\r\n Authorization: `Bearer ${config.token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n params: {\r\n locale: endpoint.locale,\r\n ...resource.parameters,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Get the request URL.\r\n * @param resource The resource to fetch. See {@link Resource}.\r\n * @param options Client options. See {@link ClientOptions}.\r\n * @returns The request URL.\r\n */\r\n public getRequestUrl<T, Protected extends boolean = false>(\r\n resource: Resource<T, object, Protected>,\r\n options?: Partial<ClientOptions>,\r\n ) {\r\n const config = { ...this.defaults, ...options };\r\n const endpoint = getBlizzardApi(config.origin, config.locale);\r\n\r\n const backslashSeparator = resource.path.startsWith('/') ? '' : '/';\r\n\r\n return `${endpoint.hostname}${backslashSeparator}${resource.path}`;\r\n }\r\n\r\n /**\r\n * Send a request to the Blizzard API.\r\n * @param resource The resource to fetch. See {@link Resource}.\r\n * @param options Client options. See {@link ClientOptions}.\r\n * @param headers Additional headers to include in the request.\r\n * @returns The response from the Blizzard API. See {@link ResourceResponse}.\r\n */\r\n public async sendRequest<T, Protected extends boolean = false>(\r\n resource: Resource<T, object, Protected>,\r\n options?: Partial<ClientOptions>,\r\n headers?: Record<string, string>,\r\n ): ResourceResponse<AxiosResponse<T>> {\r\n const url = this.getRequestUrl(resource, options);\r\n const config = this.getRequestConfig(resource, options, headers);\r\n\r\n try {\r\n return await this.axios.get<T>(url, config);\r\n } catch (error) {\r\n if (isAxiosError(error)) {\r\n throw new AxiosError(error.message, error.code);\r\n }\r\n throw error;\r\n }\r\n }\r\n}\r\n","//We have to explicitly import setTimeout because of https://github.com/electron/electron/issues/21162#issuecomment-554792447\r\nimport { setTimeout } from 'node:timers';\r\nimport { BlizzardApiClient } from './client';\r\nimport type { AccessToken, ClientOptions } from './types';\r\n\r\nconst getTokenExpiration = (expiresIn: number) => expiresIn * 1000 - 60_000;\r\n\r\n/**\r\n * Create a new Blizzard API client.\r\n * @param options Client options, see {@link ClientOptions} & https://develop.battle.net/documentation/guides/getting-started\r\n * @param onTokenRefresh Callback function to handle token refresh. If set to `true`, the client will automatically refresh the token. If set to `false`, the client will not refresh the token. If set to a function, the function will be called with the new token.\r\n * @returns A new Blizzard API client.\r\n */\r\nexport const createBlizzardApiClient = async (\r\n options: ClientOptions,\r\n onTokenRefresh: ((token: AccessToken) => void) | boolean = true,\r\n): Promise<BlizzardApiClient> => {\r\n const { key, secret, token } = options;\r\n if (!key) {\r\n throw new Error(`Client missing 'key' parameter`);\r\n }\r\n if (!secret) {\r\n throw new Error(`Client missing 'secret' parameter`);\r\n }\r\n\r\n const client = new BlizzardApiClient(options);\r\n\r\n const refreshToken = async () => {\r\n const response = await client.getAccessToken();\r\n\r\n client.setAccessToken(response.data.access_token);\r\n\r\n if (typeof onTokenRefresh === 'function') {\r\n onTokenRefresh?.(response.data);\r\n }\r\n\r\n //Schedule a refresh of the token\r\n const timeout = setTimeout(() => void refreshToken(), getTokenExpiration(response.data.expires_in));\r\n timeout.unref();\r\n };\r\n\r\n //If tokenRefresh is false, return the client without refreshing the token\r\n if (!onTokenRefresh) {\r\n return client;\r\n }\r\n\r\n if (token) {\r\n try {\r\n //If token is set, validate the token\r\n const validatedToken = await client.validateAccessToken({ token });\r\n const expiry = getTokenExpiration(validatedToken.data.exp);\r\n //If token is expiring in less than 60 seconds, refresh the token\r\n if (expiry - Date.now() < 60_000) {\r\n await refreshToken();\r\n } else {\r\n //If token is not expiring, schedule a refresh\r\n const timeout = setTimeout(() => void refreshToken(), expiry - Date.now());\r\n //Unref the timeout so the process can exit\r\n timeout.unref();\r\n }\r\n } catch {\r\n //If token is invalid, refresh the token\r\n await refreshToken();\r\n }\r\n } else {\r\n //If token is not set, refresh the token\r\n await refreshToken();\r\n }\r\n\r\n return client;\r\n};\r\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/client/client.ts","../src/client/create-client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { stringify } from 'node:querystring';\r\nimport { getBlizzardApi } from '@blizzard-api/core';\r\nimport type { Locales, Origins, Resource, ResourceResponse } from '@blizzard-api/core';\r\nimport ky from 'ky';\r\nimport type {\r\n AccessToken,\r\n AccessTokenRequestArguments,\r\n AxiosCompatability,\r\n ClientOptions,\r\n IBlizzardApiClient,\r\n ValidateAccessTokenArguments,\r\n ValidateAccessTokenResponse,\r\n} from './types';\r\n\r\n/**\r\n * A Blizzard API client.\r\n * @classdesc A client to interact with the Blizzard API.\r\n * @example\r\n * const client = new BlizzardApiClient({\r\n * key: 'client',\r\n * secret: 'secret',\r\n * origin: 'eu',\r\n * locale: 'en_GB',\r\n * token: 'access'\r\n * });\r\n */\r\nexport class BlizzardApiClient implements IBlizzardApiClient {\r\n public defaults: {\r\n key: string;\r\n locale: Locales;\r\n origin: Origins;\r\n secret: string;\r\n token?: string;\r\n };\r\n\r\n private ky = ky.create();\r\n\r\n constructor(options: ClientOptions) {\r\n const { locale, origin } = getBlizzardApi(options.origin, options.locale);\r\n this.defaults = {\r\n key: options.key,\r\n locale: locale,\r\n origin: origin,\r\n secret: options.secret,\r\n token: options.token,\r\n };\r\n }\r\n\r\n /**\r\n * Get an access token.\r\n * @param options The access token request arguments. See {@link AccessTokenRequestArguments}.\r\n * @returns The access token. See {@link AccessToken}.\r\n * @example\r\n * const response = await client.getAccessToken();\r\n * const { access_token, token_type, expires_in, sub } = response;\r\n * console.log(access_token, token_type, expires_in, sub);\r\n * // => 'access'\r\n * // => 'bearer'\r\n * // => 86399\r\n * // => 'client-id'\r\n */\r\n public getAccessToken = async (options?: AccessTokenRequestArguments): Promise<AxiosCompatability<AccessToken>> => {\r\n const { key, origin, secret } = { ...this.defaults, ...options };\r\n const basicAuth = Buffer.from(`${key}:${secret}`).toString('base64');\r\n const response = await this.ky\r\n .post<AccessToken>(`https://${origin}.battle.net/oauth/token`, {\r\n headers: {\r\n Authorization: `Basic ${basicAuth}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n searchParams: {\r\n grant_type: 'client_credentials',\r\n },\r\n })\r\n .json();\r\n\r\n return {\r\n data: response,\r\n ...response,\r\n };\r\n };\r\n\r\n /**\r\n * Set the access token.\r\n * @param token The access token.\r\n */\r\n public setAccessToken = (token: string): void => {\r\n this.defaults.token = token;\r\n };\r\n\r\n /**\r\n * Refresh the access token.\r\n * @param options The access token request arguments. See {@link AccessTokenRequestArguments}.\r\n * @returns The access token. See {@link AccessToken}.\r\n * @example\r\n * const response = await client.refreshAccessToken();\r\n * const { access_token, token_type, expires_in, sub } = response;\r\n * console.log(access_token, token_type, expires_in, sub);\r\n * // => 'access'\r\n * // => 'bearer'\r\n * // => 86399\r\n * // => 'client-id'\r\n */\r\n public refreshAccessToken = async (\r\n options?: AccessTokenRequestArguments,\r\n ): Promise<AxiosCompatability<AccessToken>> => {\r\n const response = await this.getAccessToken(options);\r\n this.setAccessToken(response.access_token);\r\n return response;\r\n };\r\n\r\n /**\r\n * Validate an access token.\r\n * @param options The validate access token arguments. See {@link ValidateAccessTokenArguments}.\r\n * @returns The response from the Blizzard API. See {@link ValidateAccessTokenResponse}.\r\n * @example\r\n * const response = await client.validateAccessToken({ token: 'access' });\r\n * console.log(response.client_id);\r\n * // => 'client-id'\r\n */\r\n public validateAccessToken = async (\r\n options?: ValidateAccessTokenArguments,\r\n ): Promise<AxiosCompatability<ValidateAccessTokenResponse>> => {\r\n const { origin, token } = { ...this.defaults, ...options };\r\n\r\n if (!token) {\r\n throw new Error('No token has been set previously or been passed to the validateAccessToken method.');\r\n }\r\n\r\n const response = await this.ky\r\n .post<ValidateAccessTokenResponse>(`https://${origin}.battle.net/oauth/check_token`, {\r\n body: stringify({ token }),\r\n headers: {\r\n 'Content-Type': 'application/x-www-form-urlencoded',\r\n },\r\n })\r\n .json();\r\n\r\n return {\r\n data: response,\r\n ...response,\r\n };\r\n };\r\n\r\n /**\r\n * Get the request configuration.\r\n * @param resource The resource to fetch. See {@link Resource}.\r\n * @param options Client options. See {@link ClientOptions}.\r\n * @param headers Additional headers to include in the request.\r\n * @returns The request configuration.\r\n */\r\n public getRequestConfig<T, Protected extends boolean = false>(\r\n resource: Resource<T, object, Protected>,\r\n options?: Partial<ClientOptions>,\r\n headers?: Record<string, string>,\r\n ) {\r\n const config = { ...this.defaults, ...options };\r\n const endpoint = getBlizzardApi(config.origin, config.locale);\r\n\r\n const namespace = resource.namespace\r\n ? { 'Battlenet-Namespace': `${resource.namespace}-${endpoint.origin}` }\r\n : undefined;\r\n\r\n const parameters = resource.parameters as Record<string, unknown>;\r\n if (parameters) {\r\n for (const key of Object.keys(parameters)) {\r\n if (parameters[key] === undefined) {\r\n delete parameters[key];\r\n }\r\n }\r\n }\r\n\r\n return {\r\n headers: {\r\n ...headers,\r\n ...namespace,\r\n Authorization: `Bearer ${config.token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n searchParams: {\r\n locale: endpoint.locale,\r\n ...resource.parameters,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Get the request URL.\r\n * @param resource The resource to fetch. See {@link Resource}.\r\n * @param options Client options. See {@link ClientOptions}.\r\n * @returns The request URL.\r\n */\r\n public getRequestUrl<T, Protected extends boolean = false>(\r\n resource: Resource<T, object, Protected>,\r\n options?: Partial<ClientOptions>,\r\n ) {\r\n const config = { ...this.defaults, ...options };\r\n const endpoint = getBlizzardApi(config.origin, config.locale);\r\n\r\n const backslashSeparator = resource.path.startsWith('/') ? '' : '/';\r\n\r\n return `${endpoint.hostname}${backslashSeparator}${resource.path}`;\r\n }\r\n\r\n /**\r\n * Send a request to the Blizzard API.\r\n * @param resource The resource to fetch. See {@link Resource}.\r\n * @param options Client options. See {@link ClientOptions}.\r\n * @param headers Additional headers to include in the request.\r\n * @returns The response from the Blizzard API. See {@link ResourceResponse}.\r\n */\r\n public async sendRequest<T, Protected extends boolean = false>(\r\n resource: Resource<T, object, Protected>,\r\n options?: Partial<ClientOptions>,\r\n headers?: Record<string, string>,\r\n ): ResourceResponse<AxiosCompatability<T>> {\r\n const url = this.getRequestUrl(resource, options);\r\n const config = this.getRequestConfig(resource, options, headers);\r\n\r\n const response = await this.ky.get<T>(url, config).json();\r\n return {\r\n data: response,\r\n ...response,\r\n };\r\n }\r\n}\r\n","//We have to explicitly import setTimeout because of https://github.com/electron/electron/issues/21162#issuecomment-554792447\r\nimport { setTimeout } from 'node:timers';\r\nimport { BlizzardApiClient } from './client';\r\nimport type { AccessToken, ClientOptions } from './types';\r\n\r\nconst getTokenExpiration = (expiresIn: number) => expiresIn * 1000 - 60_000;\r\n\r\n/**\r\n * Create a new Blizzard API client.\r\n * @param options Client options, see {@link ClientOptions} & https://develop.battle.net/documentation/guides/getting-started\r\n * @param onTokenRefresh Callback function to handle token refresh. If set to `true`, the client will automatically refresh the token. If set to `false`, the client will not refresh the token. If set to a function, the function will be called with the new token.\r\n * @returns A new Blizzard API client.\r\n */\r\nexport const createBlizzardApiClient = async (\r\n options: ClientOptions,\r\n onTokenRefresh: ((token: AccessToken) => void) | boolean = true,\r\n): Promise<BlizzardApiClient> => {\r\n const { key, secret, token } = options;\r\n if (!key) {\r\n throw new Error(`Client missing 'key' parameter`);\r\n }\r\n if (!secret) {\r\n throw new Error(`Client missing 'secret' parameter`);\r\n }\r\n\r\n const client = new BlizzardApiClient(options);\r\n\r\n const refreshToken = async () => {\r\n const response = await client.getAccessToken();\r\n\r\n client.setAccessToken(response.access_token);\r\n\r\n if (typeof onTokenRefresh === 'function') {\r\n onTokenRefresh?.(response);\r\n }\r\n\r\n //Schedule a refresh of the token\r\n const timeout = setTimeout(() => void refreshToken(), getTokenExpiration(response.expires_in));\r\n timeout.unref();\r\n };\r\n\r\n //If tokenRefresh is false, return the client without refreshing the token\r\n if (!onTokenRefresh) {\r\n return client;\r\n }\r\n\r\n if (token) {\r\n try {\r\n //If token is set, validate the token\r\n const validatedToken = await client.validateAccessToken({ token });\r\n const expiry = getTokenExpiration(validatedToken.exp);\r\n //If token is expiring in less than 60 seconds, refresh the token\r\n if (expiry - Date.now() < 60_000) {\r\n await refreshToken();\r\n } else {\r\n //If token is not expiring, schedule a refresh\r\n const timeout = setTimeout(() => void refreshToken(), expiry - Date.now());\r\n //Unref the timeout so the process can exit\r\n timeout.unref();\r\n }\r\n } catch {\r\n //If token is invalid, refresh the token\r\n await refreshToken();\r\n }\r\n } else {\r\n //If token is not set, refresh the token\r\n await refreshToken();\r\n }\r\n\r\n return client;\r\n};\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blizzard-api/client",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Putro",
|
|
6
|
-
"description": "A node.js
|
|
6
|
+
"description": "A node.js client to integrate with the blizzard battle.net api.",
|
|
7
7
|
"repository": "https://github.com/Pewtro/blizzard-api/tree/main/packages/client",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"engines": {
|
|
@@ -40,18 +40,18 @@
|
|
|
40
40
|
"hearthstone"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"
|
|
43
|
+
"ky": "1.7.5"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@blizzard-api/core": "2.0
|
|
46
|
+
"@blizzard-api/core": "2.1.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@blizzard-api/classic-wow": "2.0
|
|
50
|
-
"@blizzard-api/
|
|
51
|
-
"@blizzard-api/
|
|
52
|
-
"@blizzard-api/
|
|
53
|
-
"@blizzard-api/
|
|
54
|
-
"@blizzard-api/wow": "2.0.
|
|
49
|
+
"@blizzard-api/classic-wow": "2.1.0",
|
|
50
|
+
"@blizzard-api/core": "2.1.0",
|
|
51
|
+
"@blizzard-api/d3": "1.0.4",
|
|
52
|
+
"@blizzard-api/hs": "1.0.4",
|
|
53
|
+
"@blizzard-api/sc2": "1.0.4",
|
|
54
|
+
"@blizzard-api/wow": "2.0.4"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
57
57
|
"build": "tsup",
|