@blizzard-api/client 2.2.1 → 2.2.2
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/index.js +0 -68
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,27 +5,16 @@ import ky from "ky";
|
|
|
5
5
|
|
|
6
6
|
//#region src/client/client.ts
|
|
7
7
|
/**
|
|
8
|
-
|
|
9
8
|
* A Blizzard API client.
|
|
10
|
-
|
|
11
9
|
* @classdesc A client to interact with the Blizzard API.
|
|
12
|
-
|
|
13
10
|
* @example
|
|
14
|
-
|
|
15
11
|
* const client = new BlizzardApiClient({
|
|
16
|
-
|
|
17
12
|
* key: 'client',
|
|
18
|
-
|
|
19
13
|
* secret: 'secret',
|
|
20
|
-
|
|
21
14
|
* origin: 'eu',
|
|
22
|
-
|
|
23
15
|
* locale: 'en_GB',
|
|
24
|
-
|
|
25
16
|
* token: 'access'
|
|
26
|
-
|
|
27
17
|
* });
|
|
28
|
-
|
|
29
18
|
*/
|
|
30
19
|
var BlizzardApiClient = class {
|
|
31
20
|
defaults;
|
|
@@ -42,29 +31,17 @@ var BlizzardApiClient = class {
|
|
|
42
31
|
this.ky = ky.create(options.kyOptions);
|
|
43
32
|
}
|
|
44
33
|
/**
|
|
45
|
-
|
|
46
34
|
* Get an access token.
|
|
47
|
-
|
|
48
35
|
* @param options The access token request arguments. See {@link AccessTokenRequestArguments}.
|
|
49
|
-
|
|
50
36
|
* @returns The access token. See {@link AccessToken}.
|
|
51
|
-
|
|
52
37
|
* @example
|
|
53
|
-
|
|
54
38
|
* const response = await client.getAccessToken();
|
|
55
|
-
|
|
56
39
|
* const { access_token, token_type, expires_in, sub } = response;
|
|
57
|
-
|
|
58
40
|
* console.log(access_token, token_type, expires_in, sub);
|
|
59
|
-
|
|
60
41
|
* // => 'access'
|
|
61
|
-
|
|
62
42
|
* // => 'bearer'
|
|
63
|
-
|
|
64
43
|
* // => 86399
|
|
65
|
-
|
|
66
44
|
* // => 'client-id'
|
|
67
|
-
|
|
68
45
|
*/
|
|
69
46
|
getAccessToken = async (options) => {
|
|
70
47
|
const { key, origin, secret } = {
|
|
@@ -85,39 +62,24 @@ var BlizzardApiClient = class {
|
|
|
85
62
|
};
|
|
86
63
|
};
|
|
87
64
|
/**
|
|
88
|
-
|
|
89
65
|
* Set the access token.
|
|
90
|
-
|
|
91
66
|
* @param token The access token.
|
|
92
|
-
|
|
93
67
|
*/
|
|
94
68
|
setAccessToken = (token) => {
|
|
95
69
|
this.defaults.token = token;
|
|
96
70
|
};
|
|
97
71
|
/**
|
|
98
|
-
|
|
99
72
|
* Refresh the access token.
|
|
100
|
-
|
|
101
73
|
* @param options The access token request arguments. See {@link AccessTokenRequestArguments}.
|
|
102
|
-
|
|
103
74
|
* @returns The access token. See {@link AccessToken}.
|
|
104
|
-
|
|
105
75
|
* @example
|
|
106
|
-
|
|
107
76
|
* const response = await client.refreshAccessToken();
|
|
108
|
-
|
|
109
77
|
* const { access_token, token_type, expires_in, sub } = response;
|
|
110
|
-
|
|
111
78
|
* console.log(access_token, token_type, expires_in, sub);
|
|
112
|
-
|
|
113
79
|
* // => 'access'
|
|
114
|
-
|
|
115
80
|
* // => 'bearer'
|
|
116
|
-
|
|
117
81
|
* // => 86399
|
|
118
|
-
|
|
119
82
|
* // => 'client-id'
|
|
120
|
-
|
|
121
83
|
*/
|
|
122
84
|
refreshAccessToken = async (options) => {
|
|
123
85
|
const response = await this.getAccessToken(options);
|
|
@@ -125,21 +87,13 @@ var BlizzardApiClient = class {
|
|
|
125
87
|
return response;
|
|
126
88
|
};
|
|
127
89
|
/**
|
|
128
|
-
|
|
129
90
|
* Validate an access token.
|
|
130
|
-
|
|
131
91
|
* @param options The validate access token arguments. See {@link ValidateAccessTokenArguments}.
|
|
132
|
-
|
|
133
92
|
* @returns The response from the Blizzard API. See {@link ValidateAccessTokenResponse}.
|
|
134
|
-
|
|
135
93
|
* @example
|
|
136
|
-
|
|
137
94
|
* const response = await client.validateAccessToken({ token: 'access' });
|
|
138
|
-
|
|
139
95
|
* console.log(response.client_id);
|
|
140
|
-
|
|
141
96
|
* // => 'client-id'
|
|
142
|
-
|
|
143
97
|
*/
|
|
144
98
|
validateAccessToken = async (options) => {
|
|
145
99
|
const { origin, token } = {
|
|
@@ -157,17 +111,11 @@ var BlizzardApiClient = class {
|
|
|
157
111
|
};
|
|
158
112
|
};
|
|
159
113
|
/**
|
|
160
|
-
|
|
161
114
|
* Get the request configuration.
|
|
162
|
-
|
|
163
115
|
* @param resource The resource to fetch. See {@link Resource}.
|
|
164
|
-
|
|
165
116
|
* @param options Client options. See {@link ClientOptions}.
|
|
166
|
-
|
|
167
117
|
* @param headers Additional headers to include in the request. This is deprecated and should be passed into the kyOptions as part of the client options instead.
|
|
168
|
-
|
|
169
118
|
* @returns The request configuration.
|
|
170
|
-
|
|
171
119
|
*/
|
|
172
120
|
getRequestConfig(resource, options, headers) {
|
|
173
121
|
const config = {
|
|
@@ -194,15 +142,10 @@ var BlizzardApiClient = class {
|
|
|
194
142
|
};
|
|
195
143
|
}
|
|
196
144
|
/**
|
|
197
|
-
|
|
198
145
|
* Get the request URL.
|
|
199
|
-
|
|
200
146
|
* @param resource The resource to fetch. See {@link Resource}.
|
|
201
|
-
|
|
202
147
|
* @param options Client options. See {@link ClientOptions}.
|
|
203
|
-
|
|
204
148
|
* @returns The request URL.
|
|
205
|
-
|
|
206
149
|
*/
|
|
207
150
|
getRequestUrl(resource, options) {
|
|
208
151
|
const config = {
|
|
@@ -214,17 +157,11 @@ var BlizzardApiClient = class {
|
|
|
214
157
|
return `${endpoint.hostname}${backslashSeparator}${resource.path}`;
|
|
215
158
|
}
|
|
216
159
|
/**
|
|
217
|
-
|
|
218
160
|
* Send a request to the Blizzard API.
|
|
219
|
-
|
|
220
161
|
* @param resource The resource to fetch. See {@link Resource}.
|
|
221
|
-
|
|
222
162
|
* @param options Client options. See {@link ClientOptions}.
|
|
223
|
-
|
|
224
163
|
* @param headers Additional headers to include in the request. This is deprecated and should be passed into the kyOptions as part of the client options instead.
|
|
225
|
-
|
|
226
164
|
* @returns The response from the Blizzard API. See {@link ResourceResponse}.
|
|
227
|
-
|
|
228
165
|
*/
|
|
229
166
|
async sendRequest(resource, options, headers) {
|
|
230
167
|
const url = this.getRequestUrl(resource, options);
|
|
@@ -252,15 +189,10 @@ var BlizzardApiClient = class {
|
|
|
252
189
|
//#region src/client/create-client.ts
|
|
253
190
|
const getTokenExpiration = (expiresIn) => expiresIn * 1e3 - 6e4;
|
|
254
191
|
/**
|
|
255
|
-
|
|
256
192
|
* Create a new Blizzard API client.
|
|
257
|
-
|
|
258
193
|
* @param options Client options, see {@link ClientOptions} & https://develop.battle.net/documentation/guides/getting-started
|
|
259
|
-
|
|
260
194
|
* @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.
|
|
261
|
-
|
|
262
195
|
* @returns A new Blizzard API client.
|
|
263
|
-
|
|
264
196
|
*/
|
|
265
197
|
const createBlizzardApiClient = async (options, onTokenRefresh = true) => {
|
|
266
198
|
const { key, secret, token } = options;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["options: ClientOptions","options?: AccessTokenRequestArguments","token: string","options?: ValidateAccessTokenArguments","resource: Resource<T, object, Protected>","options?: Partial<ClientOptions>","headers?: Record<string, string>","expiresIn: number","options: ClientOptions","onTokenRefresh: ((token: AccessToken) => void) | boolean"],"sources":["../src/client/client.ts","../src/client/create-client.ts"],"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 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 {\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;\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 this.ky = ky.create(options.kyOptions);\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. This is deprecated and should be passed into the kyOptions as part of the client options instead.\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 headers: Record<string, string> & {\r\n Authorization: `Bearer ${string}`;\r\n 'Battlenet-Namespace'?: string;\r\n 'Content-Type': 'application/json';\r\n };\r\n searchParams: Record<string, unknown> & { locale: ReturnType<typeof getBlizzardApi>['locale'] };\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 ...parameters,\r\n locale: endpoint.locale,\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. This is deprecated and should be passed into the kyOptions as part of the client options instead.\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, {\r\n ...options?.kyOptions,\r\n headers: {\r\n ...config.headers,\r\n ...options?.kyOptions?.headers,\r\n },\r\n searchParams: { ...config.searchParams, ...Object.entries(options?.kyOptions?.searchParams ?? {}) },\r\n });\r\n const data = await response.json();\r\n\r\n return {\r\n data,\r\n ...data,\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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/client/client.ts","../src/client/create-client.ts"],"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 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 {\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;\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 this.ky = ky.create(options.kyOptions);\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. This is deprecated and should be passed into the kyOptions as part of the client options instead.\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 headers: Record<string, string> & {\r\n Authorization: `Bearer ${string}`;\r\n 'Battlenet-Namespace'?: string;\r\n 'Content-Type': 'application/json';\r\n };\r\n searchParams: Record<string, unknown> & { locale: ReturnType<typeof getBlizzardApi>['locale'] };\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 ...parameters,\r\n locale: endpoint.locale,\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. This is deprecated and should be passed into the kyOptions as part of the client options instead.\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, {\r\n ...options?.kyOptions,\r\n headers: {\r\n ...config.headers,\r\n ...options?.kyOptions?.headers,\r\n },\r\n searchParams: { ...config.searchParams, ...Object.entries(options?.kyOptions?.searchParams ?? {}) },\r\n });\r\n const data = await response.json();\r\n\r\n return {\r\n data,\r\n ...data,\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"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blizzard-api/client",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Putro",
|
|
6
6
|
"description": "A node.js client to integrate with the blizzard battle.net api.",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"hearthstone"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"ky": "1.
|
|
43
|
+
"ky": "1.10.0"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"@blizzard-api/core": "2.1.1"
|