worldstate-emitter 2.4.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,11 +1,13 @@
1
1
  import EventEmitter from "node:events";
2
2
  import { RSSItem } from "rss-feed-emitter";
3
3
  import WorldState from "warframe-worldstate-parser";
4
+ import { Logger } from "winston";
4
5
 
5
6
  //#region index.d.ts
6
7
  interface WorldstateEmitterOptions {
7
8
  locale?: string;
8
9
  features?: string[];
10
+ logger?: Logger;
9
11
  }
10
12
  interface RssFeedItem {
11
13
  url: string;
@@ -20,14 +22,16 @@ declare class WorldstateEmitter extends EventEmitter {
20
22
  #private;
21
23
  static make({
22
24
  locale,
23
- features
25
+ features,
26
+ logger: upLogger
24
27
  }?: WorldstateEmitterOptions): Promise<WorldstateEmitter>;
25
28
  /**
26
29
  * Pull in and instantiate emitters
27
30
  * @param options - Configuration options
28
31
  */
29
32
  constructor({
30
- locale
33
+ locale,
34
+ logger: uLogger
31
35
  }?: WorldstateEmitterOptions);
32
36
  /**
33
37
  * Set up internal logging
package/dist/index.d.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  import EventEmitter from "node:events";
2
2
  import { RSSItem } from "rss-feed-emitter";
3
+ import { Logger } from "winston";
3
4
  import WorldState from "warframe-worldstate-parser";
4
5
 
5
6
  //#region index.d.ts
6
7
  interface WorldstateEmitterOptions {
7
8
  locale?: string;
8
9
  features?: string[];
10
+ logger?: Logger;
9
11
  }
10
12
  interface RssFeedItem {
11
13
  url: string;
@@ -20,14 +22,16 @@ declare class WorldstateEmitter extends EventEmitter {
20
22
  #private;
21
23
  static make({
22
24
  locale,
23
- features
25
+ features,
26
+ logger: upLogger
24
27
  }?: WorldstateEmitterOptions): Promise<WorldstateEmitter>;
25
28
  /**
26
29
  * Pull in and instantiate emitters
27
30
  * @param options - Configuration options
28
31
  */
29
32
  constructor({
30
- locale
33
+ locale,
34
+ logger: uLogger
31
35
  }?: WorldstateEmitterOptions);
32
36
  /**
33
37
  * Set up internal logging
package/dist/index.js CHANGED
@@ -21,17 +21,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
  //#endregion
23
23
  let node_events = require("node:events");
24
- node_events = __toESM(node_events);
24
+ node_events = __toESM(node_events, 1);
25
25
  let rss_feed_emitter = require("rss-feed-emitter");
26
- rss_feed_emitter = __toESM(rss_feed_emitter);
26
+ rss_feed_emitter = __toESM(rss_feed_emitter, 1);
27
27
  let sanitize_html = require("sanitize-html");
28
- sanitize_html = __toESM(sanitize_html);
28
+ sanitize_html = __toESM(sanitize_html, 1);
29
29
  let winston = require("winston");
30
30
  let twitter = require("twitter");
31
- twitter = __toESM(twitter);
31
+ twitter = __toESM(twitter, 1);
32
32
  let warframe_worldstate_data = require("warframe-worldstate-data");
33
- warframe_worldstate_data = __toESM(warframe_worldstate_data);
33
+ warframe_worldstate_data = __toESM(warframe_worldstate_data, 1);
34
34
  let cron = require("cron");
35
+ let warframe_worldstate_parser = require("warframe-worldstate-parser");
36
+ warframe_worldstate_parser = __toESM(warframe_worldstate_parser, 1);
35
37
  //#region resources/rssFeeds.json
36
38
  var rssFeeds_default = [
37
39
  {
@@ -189,20 +191,51 @@ const twiClientInfo = {
189
191
  const TWITTER_TIMEOUT = Number(process?.env?.TWITTER_TIMEOUT) || 6e4;
190
192
  //#endregion
191
193
  //#region utilities/index.ts
192
- let tempLogger;
194
+ const levels = {
195
+ ["fatal"]: 0,
196
+ ["error"]: 1,
197
+ ["warn"]: 2,
198
+ ["info"]: 3,
199
+ ["debug"]: 4,
200
+ ["silly"]: 5
201
+ };
202
+ let loggerInstance;
193
203
  try {
194
204
  const { combine, label, printf, colorize } = winston.format;
195
205
  const transport = new winston.transports.Console();
196
206
  const logFormat = printf((info) => `[${info.label}] ${info.level}: ${info.message}`);
197
- tempLogger = (0, winston.createLogger)({
207
+ loggerInstance = (0, winston.createLogger)({
198
208
  format: combine(colorize(), label({ label: "WS" }), logFormat),
199
- transports: [transport]
209
+ transports: [transport],
210
+ level: LOG_LEVEL,
211
+ levels
200
212
  });
201
- tempLogger.level = LOG_LEVEL;
213
+ loggerInstance.level = LOG_LEVEL;
202
214
  } catch (_e) {
203
- tempLogger = (0, winston.createLogger)({ transports: [new winston.transports.Console()] });
215
+ loggerInstance = (0, winston.createLogger)({
216
+ transports: [new winston.transports.Console()],
217
+ level: LOG_LEVEL,
218
+ levels
219
+ });
204
220
  }
205
- const logger = tempLogger;
221
+ /**
222
+ * Replace the module-wide logger used by emitter internals.
223
+ * Call before constructing handlers when injecting a host-provided logger.
224
+ */
225
+ const setLogger = (uLogger) => {
226
+ loggerInstance = uLogger;
227
+ };
228
+ /** Global logger proxy so existing imports pick up setLogger() overrides. */
229
+ const logger = new Proxy({}, {
230
+ get(_target, prop) {
231
+ const value = Reflect.get(loggerInstance, prop, loggerInstance);
232
+ return typeof value === "function" ? value.bind(loggerInstance) : value;
233
+ },
234
+ set(_target, prop, value) {
235
+ Reflect.set(loggerInstance, prop, value);
236
+ return true;
237
+ }
238
+ });
206
239
  /**
207
240
  * Group an array by a field value
208
241
  * @param array - array of objects to group
@@ -470,6 +503,7 @@ const parseTweet = (tweets, watchable) => {
470
503
  */
471
504
  var TwitterCache = class {
472
505
  emitter;
506
+ logger;
473
507
  timeout;
474
508
  clientInfoValid;
475
509
  client;
@@ -487,9 +521,11 @@ var TwitterCache = class {
487
521
  * @param options.clientInfo - Custom Twitter client credentials
488
522
  * @param options.watchList - Custom list of Twitter accounts to watch
489
523
  * @param options.timeout - Polling interval in milliseconds
524
+ * @param options.logger - Custom logger instance (default: uses global logger)
490
525
  */
491
526
  constructor(eventEmitter, options = {}) {
492
527
  this.emitter = eventEmitter;
528
+ this.logger = options.logger || logger;
493
529
  this.timeout = options.timeout ?? TWITTER_TIMEOUT;
494
530
  this.lastUpdated = Date.now() - 6e4;
495
531
  this.disposed = false;
@@ -512,11 +548,11 @@ var TwitterCache = class {
512
548
  this.updateInterval = setInterval(() => this.update(), this.timeout);
513
549
  this.update();
514
550
  } else {
515
- logger.warn(`Twitter client not initialized... invalid token: ${clientInfo.bearer_token}`);
551
+ this.logger.warn(`Twitter client not initialized... invalid token: ${clientInfo.bearer_token}`);
516
552
  this.dispose();
517
553
  }
518
554
  } catch (err) {
519
- logger.error(err);
555
+ this.logger.error(err);
520
556
  this.dispose();
521
557
  }
522
558
  }
@@ -536,11 +572,11 @@ var TwitterCache = class {
536
572
  async update() {
537
573
  if (this.disposed || !this.clientInfoValid) return void 0;
538
574
  if (!this.toWatch || this.toWatch.length === 0) {
539
- logger.verbose("Not processing twitter, no data to watch.");
575
+ this.logger.debug("Not processing twitter, no data to watch.");
540
576
  return;
541
577
  }
542
578
  if (!this.client) {
543
- logger.verbose("Not processing twitter, no client to connect.");
579
+ this.logger.debug("Not processing twitter, no client to connect.");
544
580
  return;
545
581
  }
546
582
  this.updating = this.getParseableData();
@@ -551,7 +587,7 @@ var TwitterCache = class {
551
587
  * @returns Tweets
552
588
  */
553
589
  async getParseableData() {
554
- logger.silly("Starting Twitter update...");
590
+ this.logger.debug("Starting Twitter update...");
555
591
  const parsedData = [];
556
592
  try {
557
593
  await Promise.all((this.toWatch || []).map(async (watchable) => {
@@ -576,9 +612,9 @@ var TwitterCache = class {
576
612
  */
577
613
  onError(error) {
578
614
  if (Array.isArray(error) && error[0] && error[0].code === 32) {
579
- logger.info("wiping twitter client data, could not authenticate...");
615
+ this.logger.info("wiping twitter client data, could not authenticate...");
580
616
  this.dispose();
581
- } else logger.debug(JSON.stringify(error));
617
+ } else this.logger.debug(JSON.stringify(error));
582
618
  }
583
619
  /**
584
620
  * Get the current data or a promise with the current data
@@ -600,7 +636,7 @@ var TwitterCache = class {
600
636
  }
601
637
  this.client = void 0;
602
638
  this.clientInfoValid = false;
603
- logger.verbose("Twitter polling stopped and resources cleaned up");
639
+ this.logger.debug("Twitter polling stopped and resources cleaned up");
604
640
  }
605
641
  };
606
642
  //#endregion
@@ -913,8 +949,8 @@ var CronCache = class CronCache extends node_events.EventEmitter {
913
949
  * @param pattern - Optional cron pattern for update frequency
914
950
  * @returns Initialized CronCache instance
915
951
  */
916
- static async make(url, pattern) {
917
- const cache = new CronCache(url, pattern);
952
+ static async make(url, pattern, uLogger) {
953
+ const cache = new CronCache(url, pattern, uLogger);
918
954
  await cache.#update();
919
955
  return cache;
920
956
  }
@@ -922,11 +958,13 @@ var CronCache = class CronCache extends node_events.EventEmitter {
922
958
  * Create a new CronCache
923
959
  * @param url - The URL to fetch data from
924
960
  * @param pattern - Optional cron pattern for update frequency
961
+ * @param uLogger - Optional logger instance
925
962
  */
926
- constructor(url, pattern) {
963
+ constructor(url, pattern, uLogger) {
927
964
  super();
928
965
  this.#url = url;
929
966
  if (pattern) this.#pattern = pattern;
967
+ if (uLogger) this.#logger = uLogger;
930
968
  this.#job = new cron.CronJob(this.#pattern, () => void this.#update(), void 0, true);
931
969
  this.#job.start();
932
970
  }
@@ -957,12 +995,12 @@ var CronCache = class CronCache extends node_events.EventEmitter {
957
995
  * @private
958
996
  */
959
997
  async #fetch() {
960
- logger.silly(`fetching... ${this.#url}`);
998
+ this.#logger.silly(`fetching... ${this.#url}`);
961
999
  const response = await fetch(this.#url);
962
1000
  if (!response.ok) {
963
1001
  const responseText = await response.text();
964
1002
  const errorMessage = `Failed to fetch ${this.#url}: ${response.status} ${response.statusText}`;
965
- logger.error(errorMessage, { responseText });
1003
+ this.#logger.error(errorMessage, { responseText });
966
1004
  throw new Error(errorMessage);
967
1005
  }
968
1006
  this.#data = await response.text();
@@ -974,14 +1012,14 @@ var CronCache = class CronCache extends node_events.EventEmitter {
974
1012
  */
975
1013
  async get() {
976
1014
  if (this.#updating) {
977
- logger.silly("returning in-progress update promise");
1015
+ this.#logger.silly("returning in-progress update promise");
978
1016
  return this.#updating;
979
1017
  }
980
1018
  if (!this.#data) {
981
- logger.silly("returning new update promise");
1019
+ this.#logger.silly("returning new update promise");
982
1020
  return this.#update();
983
1021
  }
984
- logger.silly("returning cached data");
1022
+ this.#logger.silly("returning cached data");
985
1023
  return this.#data;
986
1024
  }
987
1025
  /**
@@ -1013,12 +1051,13 @@ var WSCache = class {
1013
1051
  * @param options.sentientCache - Cache of sentient outpost data, provided by Semlar
1014
1052
  * @param options.eventEmitter - Emitter to push new worldstate updates to
1015
1053
  */
1016
- constructor({ language, kuvaCache, sentientCache, eventEmitter }) {
1054
+ constructor({ language, kuvaCache, sentientCache, eventEmitter, logger: uLogger }) {
1017
1055
  this.#inner = void 0;
1018
1056
  this.#kuvaCache = kuvaCache;
1019
1057
  this.#sentientCache = sentientCache;
1020
1058
  this.#language = language;
1021
1059
  this.#emitter = eventEmitter;
1060
+ this.#logger = uLogger || logger;
1022
1061
  }
1023
1062
  /**
1024
1063
  * Update the current data with new data
@@ -1028,24 +1067,25 @@ var WSCache = class {
1028
1067
  #update = async (newData) => {
1029
1068
  const deps = {
1030
1069
  locale: this.#language,
1031
- kuvaData: {},
1032
- sentientData: {}
1070
+ kuvaData: [],
1071
+ sentientData: void 0,
1072
+ logger: { debug: (message) => this.#logger.debug(message) }
1033
1073
  };
1034
1074
  try {
1035
1075
  const kuvaRaw = await this.#kuvaCache.get();
1036
1076
  if (kuvaRaw) deps.kuvaData = JSON.parse(kuvaRaw);
1037
1077
  } catch (err) {
1038
- logger.debug(`Error parsing kuva data for ${this.#language}: ${err}`);
1078
+ this.#logger.debug(`Error parsing kuva data for ${this.#language}: ${err}`);
1039
1079
  }
1040
1080
  try {
1041
1081
  const sentientRaw = await this.#sentientCache.get();
1042
1082
  if (sentientRaw) deps.sentientData = JSON.parse(sentientRaw);
1043
1083
  } catch (err) {
1044
- logger.warn(`Error parsing sentient data for ${this.#language}: ${err}`);
1084
+ this.#logger.warn(`Error parsing sentient data for ${this.#language}: ${err}`);
1045
1085
  }
1046
1086
  let t;
1047
1087
  try {
1048
- t = await WorldState.build(newData, deps);
1088
+ t = await warframe_worldstate_parser.default.build(newData, deps);
1049
1089
  if (!t?.timestamp) return;
1050
1090
  } catch (err) {
1051
1091
  this.#logger.warn(`Error parsing worldstate data for ${this.#language}: ${err}`);
@@ -1070,7 +1110,7 @@ var WSCache = class {
1070
1110
  * @param newData - New string data to parse
1071
1111
  */
1072
1112
  set data(newData) {
1073
- logger.debug(`got new data for ${this.#language}, parsing...`);
1113
+ this.#logger.debug(`got new data for ${this.#language}, parsing...`);
1074
1114
  this.#update(newData);
1075
1115
  }
1076
1116
  /**
@@ -1096,6 +1136,7 @@ const debugEvents = [
1096
1136
  var Worldstate = class {
1097
1137
  #emitter;
1098
1138
  #locale;
1139
+ #logger;
1099
1140
  #worldStates = {};
1100
1141
  #wsRawCache;
1101
1142
  #kuvaCache;
@@ -1103,18 +1144,19 @@ var Worldstate = class {
1103
1144
  /**
1104
1145
  * Set up listening for specific platform and locale if provided.
1105
1146
  * @param eventEmitter - Emitter to push new worldstate events to
1106
- * @param locale - Locale (actually just language) to watch
1147
+ * @param options - Configuration options
1107
1148
  */
1108
- constructor(eventEmitter, locale) {
1149
+ constructor(eventEmitter, options = {}) {
1109
1150
  this.#emitter = eventEmitter;
1110
- this.#locale = locale;
1111
- logger.debug("starting up worldstate listener...");
1112
- if (locale) logger.debug(`only listening for ${locale}...`);
1151
+ this.#locale = options.locale;
1152
+ this.#logger = options.logger || logger;
1153
+ this.#logger.debug("starting up worldstate listener...");
1154
+ if (options.locale) this.#logger.debug(`only listening for ${options.locale}...`);
1113
1155
  }
1114
1156
  async init() {
1115
- this.#wsRawCache = await CronCache.make(worldstateUrl, worldstateCron);
1116
- this.#kuvaCache = await CronCache.make(kuvaUrl, externalCron);
1117
- this.#sentientCache = await CronCache.make(sentientUrl, externalCron);
1157
+ this.#wsRawCache = await CronCache.make(worldstateUrl, worldstateCron, this.#logger);
1158
+ this.#kuvaCache = await CronCache.make(kuvaUrl, externalCron, this.#logger);
1159
+ this.#sentientCache = await CronCache.make(sentientUrl, externalCron, this.#logger);
1118
1160
  await this.setUpRawEmitters();
1119
1161
  this.setupParsedEvents();
1120
1162
  }
@@ -1127,7 +1169,8 @@ var Worldstate = class {
1127
1169
  language: locale,
1128
1170
  kuvaCache: this.#kuvaCache,
1129
1171
  sentientCache: this.#sentientCache,
1130
- eventEmitter: this.#emitter
1172
+ eventEmitter: this.#emitter,
1173
+ logger: this.#logger
1131
1174
  });
1132
1175
  this.#wsRawCache.on("update", (dataStr) => {
1133
1176
  this.#emitter.emit("ws:update:raw", {
@@ -1136,7 +1179,7 @@ var Worldstate = class {
1136
1179
  });
1137
1180
  });
1138
1181
  this.#emitter.on("ws:update:raw", ({ data }) => {
1139
- logger.debug("ws:update:raw - updating locales data");
1182
+ this.#logger.debug("ws:update:raw - updating locales data");
1140
1183
  locales.forEach((locale) => {
1141
1184
  if (!this.#locale || this.#locale === locale) this.#worldStates[locale].data = data;
1142
1185
  });
@@ -1188,8 +1231,8 @@ var Worldstate = class {
1188
1231
  * @param packet - Data packet to emit
1189
1232
  */
1190
1233
  emit(id, packet) {
1191
- if (debugEvents.includes(packet.key)) logger.warn(packet.key);
1192
- logger.debug(`ws:update:event - emitting ${packet.id}`);
1234
+ if (debugEvents.includes(packet.key)) this.#logger.warn(packet.key);
1235
+ this.#logger.debug(`ws:update:event - emitting ${packet.id}`);
1193
1236
  delete packet.cycleStart;
1194
1237
  this.#emitter.emit(id, packet);
1195
1238
  }
@@ -1200,7 +1243,7 @@ var Worldstate = class {
1200
1243
  * @throws When the platform or locale aren't tracked and aren't updated
1201
1244
  */
1202
1245
  get(language = "en") {
1203
- logger.debug(`getting worldstate ${language}...`);
1246
+ this.#logger.debug(`getting worldstate ${language}...`);
1204
1247
  if (this.#worldStates?.[language]) return this.#worldStates?.[language]?.data;
1205
1248
  throw new Error(`Language (${language}) not tracked.\nEnsure that the parameters passed are correct`);
1206
1249
  }
@@ -1217,8 +1260,13 @@ var WorldstateEmitter = class WorldstateEmitter extends node_events.default {
1217
1260
  #worldstate;
1218
1261
  #twitter;
1219
1262
  #rss;
1220
- static async make({ locale, features } = {}) {
1221
- const emitter = new WorldstateEmitter({ locale });
1263
+ #logger;
1264
+ static async make({ locale, features, logger: upLogger } = {}) {
1265
+ if (upLogger) setLogger(upLogger);
1266
+ const emitter = new WorldstateEmitter({
1267
+ locale,
1268
+ logger: upLogger
1269
+ });
1222
1270
  await emitter.#init(features?.length ? features : FEATURES);
1223
1271
  return emitter;
1224
1272
  }
@@ -1226,18 +1274,22 @@ var WorldstateEmitter = class WorldstateEmitter extends node_events.default {
1226
1274
  * Pull in and instantiate emitters
1227
1275
  * @param options - Configuration options
1228
1276
  */
1229
- constructor({ locale } = {}) {
1277
+ constructor({ locale, logger: uLogger } = {}) {
1230
1278
  super();
1231
1279
  this.#locale = locale;
1280
+ this.#logger = uLogger || logger;
1232
1281
  }
1233
1282
  async #init(features) {
1234
- if (features.includes("rss")) this.#rss = new RSS(this);
1283
+ if (features.includes("rss")) this.#rss = new RSS(this, { logger: this.#logger });
1235
1284
  if (features.includes("worldstate")) {
1236
- this.#worldstate = new Worldstate(this, this.#locale);
1285
+ this.#worldstate = new Worldstate(this, {
1286
+ locale: this.#locale,
1287
+ logger: this.#logger
1288
+ });
1237
1289
  await this.#worldstate.init();
1238
1290
  }
1239
- if (features.includes("twitter")) this.#twitter = new TwitterCache(this);
1240
- logger.silly("hey look, i started up...");
1291
+ if (features.includes("twitter")) this.#twitter = new TwitterCache(this, { logger: this.#logger });
1292
+ this.#logger.silly("hey look, i started up...");
1241
1293
  this.setupLogging();
1242
1294
  }
1243
1295
  /**
@@ -1245,12 +1297,13 @@ var WorldstateEmitter = class WorldstateEmitter extends node_events.default {
1245
1297
  * @private
1246
1298
  */
1247
1299
  setupLogging() {
1248
- this.on("error", logger.error);
1249
- this.on("rss", (body) => logger.silly(`emitted: ${body.id}`));
1250
- this.on("ws:update:raw", (body) => logger.silly(`emitted raw: ${body.platform}`));
1251
- this.on("ws:update:parsed", (body) => logger.silly(`emitted parsed: ${body.platform} in ${body.language}`));
1252
- this.on("ws:update:event", (body) => logger.silly(`emitted event: ${body.id} ${body.platform} in ${body.language}`));
1253
- this.on("tweet", (body) => logger.silly(`emitted: ${body.id}`));
1300
+ const log = this.#logger;
1301
+ this.on("error", log.error.bind(log));
1302
+ this.on("rss", (body) => log.silly(`emitted: ${body.id}`));
1303
+ this.on("ws:update:raw", (body) => log.silly(`emitted raw: ${body.platform}`));
1304
+ this.on("ws:update:parsed", (body) => log.silly(`emitted parsed: ${body.platform} in ${body.language}`));
1305
+ this.on("ws:update:event", (body) => log.silly(`emitted event: ${body.id} ${body.platform} in ${body.language}`));
1306
+ this.on("tweet", (body) => log.silly(`emitted: ${body.id}`));
1254
1307
  }
1255
1308
  /**
1256
1309
  * Get current rss feed items
@@ -1299,6 +1352,7 @@ var WorldstateEmitter = class WorldstateEmitter extends node_events.default {
1299
1352
  this.#twitter.dispose();
1300
1353
  this.#twitter = void 0;
1301
1354
  }
1355
+ this.#logger.debug("Emitter destroyed");
1302
1356
  }
1303
1357
  };
1304
1358
  //#endregion
package/dist/index.mjs CHANGED
@@ -5,6 +5,7 @@ import { createLogger, format, transports } from "winston";
5
5
  import Twitter from "twitter";
6
6
  import wsData from "warframe-worldstate-data";
7
7
  import { CronJob } from "cron";
8
+ import WorldState from "warframe-worldstate-parser";
8
9
  //#region resources/rssFeeds.json
9
10
  var rssFeeds_default = [
10
11
  {
@@ -162,20 +163,51 @@ const twiClientInfo = {
162
163
  const TWITTER_TIMEOUT = Number(process?.env?.TWITTER_TIMEOUT) || 6e4;
163
164
  //#endregion
164
165
  //#region utilities/index.ts
165
- let tempLogger;
166
+ const levels = {
167
+ ["fatal"]: 0,
168
+ ["error"]: 1,
169
+ ["warn"]: 2,
170
+ ["info"]: 3,
171
+ ["debug"]: 4,
172
+ ["silly"]: 5
173
+ };
174
+ let loggerInstance;
166
175
  try {
167
176
  const { combine, label, printf, colorize } = format;
168
177
  const transport = new transports.Console();
169
178
  const logFormat = printf((info) => `[${info.label}] ${info.level}: ${info.message}`);
170
- tempLogger = createLogger({
179
+ loggerInstance = createLogger({
171
180
  format: combine(colorize(), label({ label: "WS" }), logFormat),
172
- transports: [transport]
181
+ transports: [transport],
182
+ level: LOG_LEVEL,
183
+ levels
173
184
  });
174
- tempLogger.level = LOG_LEVEL;
185
+ loggerInstance.level = LOG_LEVEL;
175
186
  } catch (_e) {
176
- tempLogger = createLogger({ transports: [new transports.Console()] });
187
+ loggerInstance = createLogger({
188
+ transports: [new transports.Console()],
189
+ level: LOG_LEVEL,
190
+ levels
191
+ });
177
192
  }
178
- const logger = tempLogger;
193
+ /**
194
+ * Replace the module-wide logger used by emitter internals.
195
+ * Call before constructing handlers when injecting a host-provided logger.
196
+ */
197
+ const setLogger = (uLogger) => {
198
+ loggerInstance = uLogger;
199
+ };
200
+ /** Global logger proxy so existing imports pick up setLogger() overrides. */
201
+ const logger = new Proxy({}, {
202
+ get(_target, prop) {
203
+ const value = Reflect.get(loggerInstance, prop, loggerInstance);
204
+ return typeof value === "function" ? value.bind(loggerInstance) : value;
205
+ },
206
+ set(_target, prop, value) {
207
+ Reflect.set(loggerInstance, prop, value);
208
+ return true;
209
+ }
210
+ });
179
211
  /**
180
212
  * Group an array by a field value
181
213
  * @param array - array of objects to group
@@ -443,6 +475,7 @@ const parseTweet = (tweets, watchable) => {
443
475
  */
444
476
  var TwitterCache = class {
445
477
  emitter;
478
+ logger;
446
479
  timeout;
447
480
  clientInfoValid;
448
481
  client;
@@ -460,9 +493,11 @@ var TwitterCache = class {
460
493
  * @param options.clientInfo - Custom Twitter client credentials
461
494
  * @param options.watchList - Custom list of Twitter accounts to watch
462
495
  * @param options.timeout - Polling interval in milliseconds
496
+ * @param options.logger - Custom logger instance (default: uses global logger)
463
497
  */
464
498
  constructor(eventEmitter, options = {}) {
465
499
  this.emitter = eventEmitter;
500
+ this.logger = options.logger || logger;
466
501
  this.timeout = options.timeout ?? TWITTER_TIMEOUT;
467
502
  this.lastUpdated = Date.now() - 6e4;
468
503
  this.disposed = false;
@@ -485,11 +520,11 @@ var TwitterCache = class {
485
520
  this.updateInterval = setInterval(() => this.update(), this.timeout);
486
521
  this.update();
487
522
  } else {
488
- logger.warn(`Twitter client not initialized... invalid token: ${clientInfo.bearer_token}`);
523
+ this.logger.warn(`Twitter client not initialized... invalid token: ${clientInfo.bearer_token}`);
489
524
  this.dispose();
490
525
  }
491
526
  } catch (err) {
492
- logger.error(err);
527
+ this.logger.error(err);
493
528
  this.dispose();
494
529
  }
495
530
  }
@@ -509,11 +544,11 @@ var TwitterCache = class {
509
544
  async update() {
510
545
  if (this.disposed || !this.clientInfoValid) return void 0;
511
546
  if (!this.toWatch || this.toWatch.length === 0) {
512
- logger.verbose("Not processing twitter, no data to watch.");
547
+ this.logger.debug("Not processing twitter, no data to watch.");
513
548
  return;
514
549
  }
515
550
  if (!this.client) {
516
- logger.verbose("Not processing twitter, no client to connect.");
551
+ this.logger.debug("Not processing twitter, no client to connect.");
517
552
  return;
518
553
  }
519
554
  this.updating = this.getParseableData();
@@ -524,7 +559,7 @@ var TwitterCache = class {
524
559
  * @returns Tweets
525
560
  */
526
561
  async getParseableData() {
527
- logger.silly("Starting Twitter update...");
562
+ this.logger.debug("Starting Twitter update...");
528
563
  const parsedData = [];
529
564
  try {
530
565
  await Promise.all((this.toWatch || []).map(async (watchable) => {
@@ -549,9 +584,9 @@ var TwitterCache = class {
549
584
  */
550
585
  onError(error) {
551
586
  if (Array.isArray(error) && error[0] && error[0].code === 32) {
552
- logger.info("wiping twitter client data, could not authenticate...");
587
+ this.logger.info("wiping twitter client data, could not authenticate...");
553
588
  this.dispose();
554
- } else logger.debug(JSON.stringify(error));
589
+ } else this.logger.debug(JSON.stringify(error));
555
590
  }
556
591
  /**
557
592
  * Get the current data or a promise with the current data
@@ -573,7 +608,7 @@ var TwitterCache = class {
573
608
  }
574
609
  this.client = void 0;
575
610
  this.clientInfoValid = false;
576
- logger.verbose("Twitter polling stopped and resources cleaned up");
611
+ this.logger.debug("Twitter polling stopped and resources cleaned up");
577
612
  }
578
613
  };
579
614
  //#endregion
@@ -886,8 +921,8 @@ var CronCache = class CronCache extends EventEmitter$1 {
886
921
  * @param pattern - Optional cron pattern for update frequency
887
922
  * @returns Initialized CronCache instance
888
923
  */
889
- static async make(url, pattern) {
890
- const cache = new CronCache(url, pattern);
924
+ static async make(url, pattern, uLogger) {
925
+ const cache = new CronCache(url, pattern, uLogger);
891
926
  await cache.#update();
892
927
  return cache;
893
928
  }
@@ -895,11 +930,13 @@ var CronCache = class CronCache extends EventEmitter$1 {
895
930
  * Create a new CronCache
896
931
  * @param url - The URL to fetch data from
897
932
  * @param pattern - Optional cron pattern for update frequency
933
+ * @param uLogger - Optional logger instance
898
934
  */
899
- constructor(url, pattern) {
935
+ constructor(url, pattern, uLogger) {
900
936
  super();
901
937
  this.#url = url;
902
938
  if (pattern) this.#pattern = pattern;
939
+ if (uLogger) this.#logger = uLogger;
903
940
  this.#job = new CronJob(this.#pattern, () => void this.#update(), void 0, true);
904
941
  this.#job.start();
905
942
  }
@@ -930,12 +967,12 @@ var CronCache = class CronCache extends EventEmitter$1 {
930
967
  * @private
931
968
  */
932
969
  async #fetch() {
933
- logger.silly(`fetching... ${this.#url}`);
970
+ this.#logger.silly(`fetching... ${this.#url}`);
934
971
  const response = await fetch(this.#url);
935
972
  if (!response.ok) {
936
973
  const responseText = await response.text();
937
974
  const errorMessage = `Failed to fetch ${this.#url}: ${response.status} ${response.statusText}`;
938
- logger.error(errorMessage, { responseText });
975
+ this.#logger.error(errorMessage, { responseText });
939
976
  throw new Error(errorMessage);
940
977
  }
941
978
  this.#data = await response.text();
@@ -947,14 +984,14 @@ var CronCache = class CronCache extends EventEmitter$1 {
947
984
  */
948
985
  async get() {
949
986
  if (this.#updating) {
950
- logger.silly("returning in-progress update promise");
987
+ this.#logger.silly("returning in-progress update promise");
951
988
  return this.#updating;
952
989
  }
953
990
  if (!this.#data) {
954
- logger.silly("returning new update promise");
991
+ this.#logger.silly("returning new update promise");
955
992
  return this.#update();
956
993
  }
957
- logger.silly("returning cached data");
994
+ this.#logger.silly("returning cached data");
958
995
  return this.#data;
959
996
  }
960
997
  /**
@@ -986,12 +1023,13 @@ var WSCache = class {
986
1023
  * @param options.sentientCache - Cache of sentient outpost data, provided by Semlar
987
1024
  * @param options.eventEmitter - Emitter to push new worldstate updates to
988
1025
  */
989
- constructor({ language, kuvaCache, sentientCache, eventEmitter }) {
1026
+ constructor({ language, kuvaCache, sentientCache, eventEmitter, logger: uLogger }) {
990
1027
  this.#inner = void 0;
991
1028
  this.#kuvaCache = kuvaCache;
992
1029
  this.#sentientCache = sentientCache;
993
1030
  this.#language = language;
994
1031
  this.#emitter = eventEmitter;
1032
+ this.#logger = uLogger || logger;
995
1033
  }
996
1034
  /**
997
1035
  * Update the current data with new data
@@ -1001,20 +1039,21 @@ var WSCache = class {
1001
1039
  #update = async (newData) => {
1002
1040
  const deps = {
1003
1041
  locale: this.#language,
1004
- kuvaData: {},
1005
- sentientData: {}
1042
+ kuvaData: [],
1043
+ sentientData: void 0,
1044
+ logger: { debug: (message) => this.#logger.debug(message) }
1006
1045
  };
1007
1046
  try {
1008
1047
  const kuvaRaw = await this.#kuvaCache.get();
1009
1048
  if (kuvaRaw) deps.kuvaData = JSON.parse(kuvaRaw);
1010
1049
  } catch (err) {
1011
- logger.debug(`Error parsing kuva data for ${this.#language}: ${err}`);
1050
+ this.#logger.debug(`Error parsing kuva data for ${this.#language}: ${err}`);
1012
1051
  }
1013
1052
  try {
1014
1053
  const sentientRaw = await this.#sentientCache.get();
1015
1054
  if (sentientRaw) deps.sentientData = JSON.parse(sentientRaw);
1016
1055
  } catch (err) {
1017
- logger.warn(`Error parsing sentient data for ${this.#language}: ${err}`);
1056
+ this.#logger.warn(`Error parsing sentient data for ${this.#language}: ${err}`);
1018
1057
  }
1019
1058
  let t;
1020
1059
  try {
@@ -1043,7 +1082,7 @@ var WSCache = class {
1043
1082
  * @param newData - New string data to parse
1044
1083
  */
1045
1084
  set data(newData) {
1046
- logger.debug(`got new data for ${this.#language}, parsing...`);
1085
+ this.#logger.debug(`got new data for ${this.#language}, parsing...`);
1047
1086
  this.#update(newData);
1048
1087
  }
1049
1088
  /**
@@ -1069,6 +1108,7 @@ const debugEvents = [
1069
1108
  var Worldstate = class {
1070
1109
  #emitter;
1071
1110
  #locale;
1111
+ #logger;
1072
1112
  #worldStates = {};
1073
1113
  #wsRawCache;
1074
1114
  #kuvaCache;
@@ -1076,18 +1116,19 @@ var Worldstate = class {
1076
1116
  /**
1077
1117
  * Set up listening for specific platform and locale if provided.
1078
1118
  * @param eventEmitter - Emitter to push new worldstate events to
1079
- * @param locale - Locale (actually just language) to watch
1119
+ * @param options - Configuration options
1080
1120
  */
1081
- constructor(eventEmitter, locale) {
1121
+ constructor(eventEmitter, options = {}) {
1082
1122
  this.#emitter = eventEmitter;
1083
- this.#locale = locale;
1084
- logger.debug("starting up worldstate listener...");
1085
- if (locale) logger.debug(`only listening for ${locale}...`);
1123
+ this.#locale = options.locale;
1124
+ this.#logger = options.logger || logger;
1125
+ this.#logger.debug("starting up worldstate listener...");
1126
+ if (options.locale) this.#logger.debug(`only listening for ${options.locale}...`);
1086
1127
  }
1087
1128
  async init() {
1088
- this.#wsRawCache = await CronCache.make(worldstateUrl, worldstateCron);
1089
- this.#kuvaCache = await CronCache.make(kuvaUrl, externalCron);
1090
- this.#sentientCache = await CronCache.make(sentientUrl, externalCron);
1129
+ this.#wsRawCache = await CronCache.make(worldstateUrl, worldstateCron, this.#logger);
1130
+ this.#kuvaCache = await CronCache.make(kuvaUrl, externalCron, this.#logger);
1131
+ this.#sentientCache = await CronCache.make(sentientUrl, externalCron, this.#logger);
1091
1132
  await this.setUpRawEmitters();
1092
1133
  this.setupParsedEvents();
1093
1134
  }
@@ -1100,7 +1141,8 @@ var Worldstate = class {
1100
1141
  language: locale,
1101
1142
  kuvaCache: this.#kuvaCache,
1102
1143
  sentientCache: this.#sentientCache,
1103
- eventEmitter: this.#emitter
1144
+ eventEmitter: this.#emitter,
1145
+ logger: this.#logger
1104
1146
  });
1105
1147
  this.#wsRawCache.on("update", (dataStr) => {
1106
1148
  this.#emitter.emit("ws:update:raw", {
@@ -1109,7 +1151,7 @@ var Worldstate = class {
1109
1151
  });
1110
1152
  });
1111
1153
  this.#emitter.on("ws:update:raw", ({ data }) => {
1112
- logger.debug("ws:update:raw - updating locales data");
1154
+ this.#logger.debug("ws:update:raw - updating locales data");
1113
1155
  locales.forEach((locale) => {
1114
1156
  if (!this.#locale || this.#locale === locale) this.#worldStates[locale].data = data;
1115
1157
  });
@@ -1161,8 +1203,8 @@ var Worldstate = class {
1161
1203
  * @param packet - Data packet to emit
1162
1204
  */
1163
1205
  emit(id, packet) {
1164
- if (debugEvents.includes(packet.key)) logger.warn(packet.key);
1165
- logger.debug(`ws:update:event - emitting ${packet.id}`);
1206
+ if (debugEvents.includes(packet.key)) this.#logger.warn(packet.key);
1207
+ this.#logger.debug(`ws:update:event - emitting ${packet.id}`);
1166
1208
  delete packet.cycleStart;
1167
1209
  this.#emitter.emit(id, packet);
1168
1210
  }
@@ -1173,7 +1215,7 @@ var Worldstate = class {
1173
1215
  * @throws When the platform or locale aren't tracked and aren't updated
1174
1216
  */
1175
1217
  get(language = "en") {
1176
- logger.debug(`getting worldstate ${language}...`);
1218
+ this.#logger.debug(`getting worldstate ${language}...`);
1177
1219
  if (this.#worldStates?.[language]) return this.#worldStates?.[language]?.data;
1178
1220
  throw new Error(`Language (${language}) not tracked.\nEnsure that the parameters passed are correct`);
1179
1221
  }
@@ -1190,8 +1232,13 @@ var WorldstateEmitter = class WorldstateEmitter extends EventEmitter {
1190
1232
  #worldstate;
1191
1233
  #twitter;
1192
1234
  #rss;
1193
- static async make({ locale, features } = {}) {
1194
- const emitter = new WorldstateEmitter({ locale });
1235
+ #logger;
1236
+ static async make({ locale, features, logger: upLogger } = {}) {
1237
+ if (upLogger) setLogger(upLogger);
1238
+ const emitter = new WorldstateEmitter({
1239
+ locale,
1240
+ logger: upLogger
1241
+ });
1195
1242
  await emitter.#init(features?.length ? features : FEATURES);
1196
1243
  return emitter;
1197
1244
  }
@@ -1199,18 +1246,22 @@ var WorldstateEmitter = class WorldstateEmitter extends EventEmitter {
1199
1246
  * Pull in and instantiate emitters
1200
1247
  * @param options - Configuration options
1201
1248
  */
1202
- constructor({ locale } = {}) {
1249
+ constructor({ locale, logger: uLogger } = {}) {
1203
1250
  super();
1204
1251
  this.#locale = locale;
1252
+ this.#logger = uLogger || logger;
1205
1253
  }
1206
1254
  async #init(features) {
1207
- if (features.includes("rss")) this.#rss = new RSS(this);
1255
+ if (features.includes("rss")) this.#rss = new RSS(this, { logger: this.#logger });
1208
1256
  if (features.includes("worldstate")) {
1209
- this.#worldstate = new Worldstate(this, this.#locale);
1257
+ this.#worldstate = new Worldstate(this, {
1258
+ locale: this.#locale,
1259
+ logger: this.#logger
1260
+ });
1210
1261
  await this.#worldstate.init();
1211
1262
  }
1212
- if (features.includes("twitter")) this.#twitter = new TwitterCache(this);
1213
- logger.silly("hey look, i started up...");
1263
+ if (features.includes("twitter")) this.#twitter = new TwitterCache(this, { logger: this.#logger });
1264
+ this.#logger.silly("hey look, i started up...");
1214
1265
  this.setupLogging();
1215
1266
  }
1216
1267
  /**
@@ -1218,12 +1269,13 @@ var WorldstateEmitter = class WorldstateEmitter extends EventEmitter {
1218
1269
  * @private
1219
1270
  */
1220
1271
  setupLogging() {
1221
- this.on("error", logger.error);
1222
- this.on("rss", (body) => logger.silly(`emitted: ${body.id}`));
1223
- this.on("ws:update:raw", (body) => logger.silly(`emitted raw: ${body.platform}`));
1224
- this.on("ws:update:parsed", (body) => logger.silly(`emitted parsed: ${body.platform} in ${body.language}`));
1225
- this.on("ws:update:event", (body) => logger.silly(`emitted event: ${body.id} ${body.platform} in ${body.language}`));
1226
- this.on("tweet", (body) => logger.silly(`emitted: ${body.id}`));
1272
+ const log = this.#logger;
1273
+ this.on("error", log.error.bind(log));
1274
+ this.on("rss", (body) => log.silly(`emitted: ${body.id}`));
1275
+ this.on("ws:update:raw", (body) => log.silly(`emitted raw: ${body.platform}`));
1276
+ this.on("ws:update:parsed", (body) => log.silly(`emitted parsed: ${body.platform} in ${body.language}`));
1277
+ this.on("ws:update:event", (body) => log.silly(`emitted event: ${body.id} ${body.platform} in ${body.language}`));
1278
+ this.on("tweet", (body) => log.silly(`emitted: ${body.id}`));
1227
1279
  }
1228
1280
  /**
1229
1281
  * Get current rss feed items
@@ -1272,6 +1324,7 @@ var WorldstateEmitter = class WorldstateEmitter extends EventEmitter {
1272
1324
  this.#twitter.dispose();
1273
1325
  this.#twitter = void 0;
1274
1326
  }
1327
+ this.#logger.debug("Emitter destroyed");
1275
1328
  }
1276
1329
  };
1277
1330
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "worldstate-emitter",
3
- "version": "2.4.4",
3
+ "version": "2.5.0",
4
4
  "description": "Event emitter for Warframe worldstate & other events - TypeScript support included",
5
5
  "keywords": [
6
6
  "warframe",
@@ -62,8 +62,8 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "@biomejs/biome": "^2.3.14",
65
- "@commitlint/cli": "^20.4.1",
66
- "@commitlint/config-conventional": "^20.4.1",
65
+ "@commitlint/cli": "^21.0.0",
66
+ "@commitlint/config-conventional": "^21.0.0",
67
67
  "@types/chai": "^5.2.3",
68
68
  "@types/mocha": "^10.0.10",
69
69
  "@types/node": "^25.2.2",
@@ -73,18 +73,19 @@
73
73
  "c8": "^11.0.0",
74
74
  "chai": "^6.2.2",
75
75
  "husky": "^9.1.7",
76
- "lint-staged": "^16.2.7",
76
+ "lint-staged": "^17.0.3",
77
77
  "mocha": "^11.7.5",
78
78
  "nodemon": "^3.1.11",
79
- "sinon": "^21.0.1",
80
- "tsdown": "^0.21.0",
79
+ "reflect-metadata": "^0.2.2",
80
+ "sinon": "^22.0.0",
81
+ "tsdown": "^0.22.0",
81
82
  "tsx": "^4.21.0",
82
83
  "typedoc": "^0.28.16",
83
84
  "typescript": "^6.0.2"
84
85
  },
85
86
  "peerDependencies": {
86
87
  "warframe-worldstate-data": "^3.x",
87
- "warframe-worldstate-parser": "^5.2.14",
88
+ "warframe-worldstate-parser": "^5.3.9",
88
89
  "winston": "^3.19.0"
89
90
  },
90
91
  "engines": {