@blizzard-api/client 2.2.0 → 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.d.ts CHANGED
@@ -2,18 +2,7 @@ import { Locales, Origins, Resource, ResourceResponse, getBlizzardApi } from "@b
2
2
  import { Options } from "ky";
3
3
 
4
4
  //#region src/client/types.d.ts
5
- /**
6
- * An access token response from the Blizzard API.
7
- * @see https://develop.battle.net/documentation/guides/using-oauth
8
- * @see https://develop.battle.net/documentation/guides/using-oauth/client-credentials-flow¨
9
- * @example
10
- * const response: AccessToken = {
11
- * access_token: 'access-token',
12
- * token_type: 'bearer',
13
- * expires_in: 86399,
14
- * sub: 'client-id',
15
- * };
16
- */
5
+
17
6
  /**
18
7
  * An access token response from the Blizzard API.
19
8
  * @see https://develop.battle.net/documentation/guides/using-oauth
@@ -108,7 +97,8 @@ interface ValidateAccessTokenResponse {
108
97
  client_id: string;
109
98
  exp: number;
110
99
  scope: Array<string>;
111
- } //#endregion
100
+ }
101
+ //#endregion
112
102
  //#region src/client/client.d.ts
113
103
  /**
114
104
  * A Blizzard API client.
@@ -208,7 +198,6 @@ declare class BlizzardApiClient {
208
198
  */
209
199
  sendRequest<T, Protected extends boolean = false>(resource: Resource<T, object, Protected>, options?: Partial<ClientOptions>, headers?: Record<string, string>): ResourceResponse<AxiosCompatability<T>>;
210
200
  }
211
-
212
201
  //#endregion
213
202
  //#region src/client/create-client.d.ts
214
203
  /**
@@ -218,7 +207,6 @@ declare class BlizzardApiClient {
218
207
  * @returns A new Blizzard API client.
219
208
  */
220
209
  declare const createBlizzardApiClient: (options: ClientOptions, onTokenRefresh?: ((token: AccessToken) => void) | boolean) => Promise<BlizzardApiClient>;
221
-
222
210
  //#endregion
223
211
  export { AccessToken, AccessTokenRequestArguments, AxiosCompatability, ClientOptions, ValidateAccessTokenArguments, ValidateAccessTokenResponse, createBlizzardApiClient, createBlizzardApiClient as default };
224
212
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -5,29 +5,20 @@ 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 {
20
+ defaults;
21
+ ky;
31
22
  constructor(options) {
32
23
  const { locale, origin } = getBlizzardApi(options.origin, options.locale);
33
24
  this.defaults = {
@@ -40,29 +31,17 @@ var BlizzardApiClient = class {
40
31
  this.ky = ky.create(options.kyOptions);
41
32
  }
42
33
  /**
43
-
44
34
  * Get an access token.
45
-
46
35
  * @param options The access token request arguments. See {@link AccessTokenRequestArguments}.
47
-
48
36
  * @returns The access token. See {@link AccessToken}.
49
-
50
37
  * @example
51
-
52
38
  * const response = await client.getAccessToken();
53
-
54
39
  * const { access_token, token_type, expires_in, sub } = response;
55
-
56
40
  * console.log(access_token, token_type, expires_in, sub);
57
-
58
41
  * // => 'access'
59
-
60
42
  * // => 'bearer'
61
-
62
43
  * // => 86399
63
-
64
44
  * // => 'client-id'
65
-
66
45
  */
67
46
  getAccessToken = async (options) => {
68
47
  const { key, origin, secret } = {
@@ -83,39 +62,24 @@ var BlizzardApiClient = class {
83
62
  };
84
63
  };
85
64
  /**
86
-
87
65
  * Set the access token.
88
-
89
66
  * @param token The access token.
90
-
91
67
  */
92
68
  setAccessToken = (token) => {
93
69
  this.defaults.token = token;
94
70
  };
95
71
  /**
96
-
97
72
  * Refresh the access token.
98
-
99
73
  * @param options The access token request arguments. See {@link AccessTokenRequestArguments}.
100
-
101
74
  * @returns The access token. See {@link AccessToken}.
102
-
103
75
  * @example
104
-
105
76
  * const response = await client.refreshAccessToken();
106
-
107
77
  * const { access_token, token_type, expires_in, sub } = response;
108
-
109
78
  * console.log(access_token, token_type, expires_in, sub);
110
-
111
79
  * // => 'access'
112
-
113
80
  * // => 'bearer'
114
-
115
81
  * // => 86399
116
-
117
82
  * // => 'client-id'
118
-
119
83
  */
120
84
  refreshAccessToken = async (options) => {
121
85
  const response = await this.getAccessToken(options);
@@ -123,21 +87,13 @@ var BlizzardApiClient = class {
123
87
  return response;
124
88
  };
125
89
  /**
126
-
127
90
  * Validate an access token.
128
-
129
91
  * @param options The validate access token arguments. See {@link ValidateAccessTokenArguments}.
130
-
131
92
  * @returns The response from the Blizzard API. See {@link ValidateAccessTokenResponse}.
132
-
133
93
  * @example
134
-
135
94
  * const response = await client.validateAccessToken({ token: 'access' });
136
-
137
95
  * console.log(response.client_id);
138
-
139
96
  * // => 'client-id'
140
-
141
97
  */
142
98
  validateAccessToken = async (options) => {
143
99
  const { origin, token } = {
@@ -155,17 +111,11 @@ var BlizzardApiClient = class {
155
111
  };
156
112
  };
157
113
  /**
158
-
159
114
  * Get the request configuration.
160
-
161
115
  * @param resource The resource to fetch. See {@link Resource}.
162
-
163
116
  * @param options Client options. See {@link ClientOptions}.
164
-
165
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.
166
-
167
118
  * @returns The request configuration.
168
-
169
119
  */
170
120
  getRequestConfig(resource, options, headers) {
171
121
  const config = {
@@ -192,15 +142,10 @@ var BlizzardApiClient = class {
192
142
  };
193
143
  }
194
144
  /**
195
-
196
145
  * Get the request URL.
197
-
198
146
  * @param resource The resource to fetch. See {@link Resource}.
199
-
200
147
  * @param options Client options. See {@link ClientOptions}.
201
-
202
148
  * @returns The request URL.
203
-
204
149
  */
205
150
  getRequestUrl(resource, options) {
206
151
  const config = {
@@ -212,17 +157,11 @@ var BlizzardApiClient = class {
212
157
  return `${endpoint.hostname}${backslashSeparator}${resource.path}`;
213
158
  }
214
159
  /**
215
-
216
160
  * Send a request to the Blizzard API.
217
-
218
161
  * @param resource The resource to fetch. See {@link Resource}.
219
-
220
162
  * @param options Client options. See {@link ClientOptions}.
221
-
222
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.
223
-
224
164
  * @returns The response from the Blizzard API. See {@link ResourceResponse}.
225
-
226
165
  */
227
166
  async sendRequest(resource, options, headers) {
228
167
  const url = this.getRequestUrl(resource, options);
@@ -250,15 +189,10 @@ var BlizzardApiClient = class {
250
189
  //#region src/client/create-client.ts
251
190
  const getTokenExpiration = (expiresIn) => expiresIn * 1e3 - 6e4;
252
191
  /**
253
-
254
192
  * Create a new Blizzard API client.
255
-
256
193
  * @param options Client options, see {@link ClientOptions} & https://develop.battle.net/documentation/guides/getting-started
257
-
258
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.
259
-
260
195
  * @returns A new Blizzard API client.
261
-
262
196
  */
263
197
  const createBlizzardApiClient = async (options, onTokenRefresh = true) => {
264
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.0",
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.8.1"
43
+ "ky": "1.10.0"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "@blizzard-api/core": "2.1.1"
@@ -51,7 +51,7 @@
51
51
  "@blizzard-api/d3": "1.0.5",
52
52
  "@blizzard-api/hs": "1.0.5",
53
53
  "@blizzard-api/sc2": "1.0.5",
54
- "@blizzard-api/wow": "2.0.5"
54
+ "@blizzard-api/wow": "2.1.0"
55
55
  },
56
56
  "scripts": {
57
57
  "build": "tsdown",