clashofclans.js 2.5.2-dev.12b59ce → 2.6.0-dev.8cacb3e

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/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## 2.6.0
6
+
7
+ ## Features
8
+
9
+ - Replaced Keyv with customizable cache store ([#99](https://github.com/clashperk/clashofclans.js/pull/99))
10
+ - Guide for [Internal Caching](https://clashofclans.js.org/guide/internal-caching)
11
+
5
12
  ## 2.5.2 (2022-01-23)
6
13
 
7
14
  ### Bug Fixes
@@ -18,7 +18,7 @@ const struct_1 = require("../struct");
18
18
  class Client extends events_1.EventEmitter {
19
19
  constructor(options) {
20
20
  super();
21
- this.rest = new RESTManager_1.RESTManager(options);
21
+ this.rest = new RESTManager_1.RESTManager({ ...options, rejectIfNotValid: true });
22
22
  this.events = new EventManager_1.EventManager(this);
23
23
  }
24
24
  /** Contains various general-purpose utility methods. */
@@ -150,7 +150,10 @@ class Client extends events_1.EventEmitter {
150
150
  }
151
151
  /** Get info about clan war league. */
152
152
  async getClanWarLeagueGroup(clanTag, options) {
153
- const { data } = await this.rest.getClanWarLeagueGroup(clanTag, options);
153
+ const { data, status, path, maxAge } = await this.rest.getClanWarLeagueGroup(clanTag, options);
154
+ if (data.state === 'notInWar') {
155
+ throw new HTTPError_1.HTTPError(HTTPError_1.NotInWarError, status, path, maxAge);
156
+ }
154
157
  return new struct_1.ClanWarLeagueGroup(this, data);
155
158
  }
156
159
  /** Get info about a CWL round by WarTag. */
@@ -18,6 +18,7 @@ export declare class RequestHandler {
18
18
  private get _keys();
19
19
  private get _key();
20
20
  setKeys(keys: string[]): this;
21
+ private get creds();
21
22
  request<T>(path: string, options?: RequestOptions): Promise<Response<T>>;
22
23
  private exec;
23
24
  init(options: LoginOptions): Promise<string[]>;
@@ -50,6 +50,9 @@ class RequestHandler {
50
50
  this.keys = keys;
51
51
  return this;
52
52
  }
53
+ get creds() {
54
+ return Boolean(this.email && this.password);
55
+ }
53
56
  async request(path, options = {}) {
54
57
  const cached = (await this.cached?.get(path)) ?? null;
55
58
  if (cached && options.force !== true) {
@@ -76,8 +79,11 @@ class RequestHandler {
76
79
  const data = await res?.json().catch(() => null);
77
80
  if (!res && retries < (options.retryLimit ?? this.retryLimit))
78
81
  return this.exec(path, options, ++retries);
79
- if (res?.status === 403 && data?.reason === 'accessDenied.invalidIp' && this.email && this.password) {
80
- const keys = await this.reValidateKeys().then(() => this.login());
82
+ if (this.creds &&
83
+ res?.status === 403 &&
84
+ data?.reason === 'accessDenied.invalidIp' &&
85
+ retries < (options.retryLimit ?? this.retryLimit)) {
86
+ const keys = await this.reValidateKeys().then(() => () => this.login());
81
87
  if (keys.length)
82
88
  return this.exec(path, options, ++retries);
83
89
  }
@@ -114,7 +120,7 @@ class RequestHandler {
114
120
  if (res?.status === 403) {
115
121
  const index = this.keys.indexOf(key);
116
122
  this.keys.splice(index, 1);
117
- process.emitWarning(`Pre-defined key #${index + 1} is no longer valid. Removed from the key list.`);
123
+ process.emitWarning(`Key #${index + 1} is no longer valid. Removed from the key list.`);
118
124
  }
119
125
  }
120
126
  }
@@ -145,14 +151,14 @@ class RequestHandler {
145
151
  // Get all available keys from the developer site.
146
152
  const keys = (data.keys ?? []);
147
153
  // Revoke keys for specified key name but not matching current IP address.
148
- for (const key of keys.filter((key) => key.name === this.keyName && !key.cidrRanges.includes(ip))) {
154
+ for (const key of keys.filter((key) => key.name === this.keyName && !key.cidrRanges?.includes(ip))) {
149
155
  if (!(await this.revokeKey(key.id, cookie)))
150
156
  continue;
151
157
  const index = keys.findIndex(({ id }) => id === key.id);
152
158
  keys.splice(index, 1);
153
159
  }
154
160
  // Filter keys for current IP address and specified key name.
155
- for (const key of keys.filter((key) => key.name === this.keyName && key.cidrRanges.includes(ip))) {
161
+ for (const key of keys.filter((key) => key.name === this.keyName && key.cidrRanges?.includes(ip))) {
156
162
  if (this.keys.length >= this.keyCount)
157
163
  break;
158
164
  if (!this.keys.includes(key.key))
@@ -41,6 +41,7 @@ exports.ClanWarLeagueRound = ClanWarLeagueRound;
41
41
  class ClanWarLeagueGroup {
42
42
  constructor(client, data) {
43
43
  this.client = client;
44
+ // @ts-expect-error
44
45
  this.state = data.state;
45
46
  this.season = data.season;
46
47
  this.clans = data.clans.map((clan) => new ClanWarLeagueClan(client, clan));
@@ -139,7 +139,7 @@ export interface APIClanWarLog {
139
139
  }
140
140
  /** /clans/{clanTag}/currentwar/leaguegroup */
141
141
  export interface APIClanWarLeagueGroup {
142
- state: 'preparation' | 'inWar' | 'ended';
142
+ state: 'notInWar' | 'preparation' | 'inWar' | 'ended';
143
143
  season: string;
144
144
  clans: APIClanWarLeagueClan[];
145
145
  rounds: APIClanWarLeagueRound[];
@@ -67,7 +67,7 @@ export interface OverrideOptions {
67
67
  /** Whether to skip the cache check and request the API. */
68
68
  force?: boolean;
69
69
  /** How many times to retry on 5XX errors. */
70
- retryLimit?: string;
70
+ retryLimit?: number;
71
71
  /** Whether to ignore throttlers. */
72
72
  ignoreRateLimit?: boolean;
73
73
  /** Time to wait before cancelling a REST request, in milliseconds. */
@@ -3,7 +3,7 @@ export interface CacheOptions {
3
3
  /**
4
4
  * How frequently to remove data from cache that are older than the lifetime/ttl (in milliseconds, 0 for never)
5
5
  *
6
- * To prevent high CPU usage, set a higher value (30-60 seconds recommended)
6
+ * To prevent high CPU usage, set a higher value (>= 30 seconds)
7
7
  *
8
8
  * @default 120000 (2 minutes)
9
9
  */
@@ -15,9 +15,9 @@ export interface CacheOptions {
15
15
  */
16
16
  ttl?: number;
17
17
  }
18
- export declare class CacheStore<T = any> implements Store<T> {
18
+ export declare class CacheStore<T> implements Store<T> {
19
19
  private readonly ttl;
20
- private readonly sweepInterval?;
20
+ private readonly sweepInterval;
21
21
  private readonly store;
22
22
  constructor(options?: CacheOptions);
23
23
  private _sweep;
package/dist/util/Util.js CHANGED
@@ -28,7 +28,10 @@ class Util extends null {
28
28
  }
29
29
  /** @internal */
30
30
  static parseTag(tag) {
31
- return `#${tag.toUpperCase().replace(/O/g, '0').replace(/^#/g, '').replace(/\s/g, '')}`;
31
+ if (tag && typeof tag === 'string') {
32
+ return `#${tag.toUpperCase().replace(/O/g, '0').replace(/^#/g, '').replace(/\s/g, '')}`;
33
+ }
34
+ throw new TypeError('The "tag" argument must be of type string.');
32
35
  }
33
36
  /** Encodes a tag as a valid component of a URI. */
34
37
  static encodeURI(tag) {
@@ -47,7 +50,7 @@ class Util extends null {
47
50
  static encodeTag(tag) {
48
51
  const formatted = this.formatTag(tag).substring(1);
49
52
  if (!this.isValidTag(formatted)) {
50
- throw new Error(`Failed to encode tag ${formatted}. RegExp matching failed.`);
53
+ throw new TypeError(`Failed to encode tag ${formatted}. RegExp matching failed.`);
51
54
  }
52
55
  const result = formatted.split('').reduce((sum, char) => sum * BigInt(14) + BigInt(TAG_CHARACTERS.indexOf(char)), BigInt(0));
53
56
  return result.toString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clashofclans.js",
3
- "version": "2.5.2-dev.12b59ce",
3
+ "version": "2.6.0-dev.8cacb3e",
4
4
  "description": "JavaScript library for interacting with the Clash of Clans API",
5
5
  "author": "SUVAJIT <suvajit.me@gmail.com>",
6
6
  "license": "MIT",