@wfcd/relics 2.0.29 → 2.0.31

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.
@@ -10,103 +10,132 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
10
10
  var __getProtoOf = Object.getPrototypeOf;
11
11
  var __hasOwnProp = Object.prototype.hasOwnProperty;
12
12
  var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
15
- key = keys[i];
16
- if (!__hasOwnProp.call(to, key) && key !== except) {
17
- __defProp(to, key, {
18
- get: ((k) => from[k]).bind(null, key),
19
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
20
- });
21
- }
22
- }
13
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
14
+ key = keys[i];
15
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
16
+ get: ((k) => from[k]).bind(null, key),
17
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
18
+ });
23
19
  }
24
20
  return to;
25
21
  };
26
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
27
23
  value: mod,
28
24
  enumerable: true
29
25
  }) : target, mod));
30
-
31
26
  //#endregion
27
+ let node_fetch = require("node-fetch");
28
+ node_fetch = __toESM(node_fetch);
32
29
  let node_fs_promises = require("node:fs/promises");
33
30
  node_fs_promises = __toESM(node_fs_promises);
34
31
  let node_path = require("node:path");
35
32
  node_path = __toESM(node_path);
36
- let node_fetch = require("node-fetch");
37
- node_fetch = __toESM(node_fetch);
38
33
  let node_console = require("node:console");
39
34
  node_console = __toESM(node_console);
40
-
41
35
  //#region src/Config.ts
42
36
  const Config = {
43
- warframeRelicDropUrl: "https://drops.warframestat.us/data/relics.json",
44
- warframeMarketItemUrl: "https://api.warframe.market/v2/items",
37
+ warframeItemByNameUrl: "https://api.warframestat.us/items/",
38
+ warframeItemsSparseUrl: "https://api.warframestat.us/items/?only=name,uniqueName",
45
39
  warframeItemsUrl: "https://api.warframestat.us/items/search/Relics?by=category",
40
+ warframeMarketItemUrl: "https://api.warframe.market/v2/items",
41
+ warframePatchlogsUrl: "https://raw.githubusercontent.com/WFCD/warframe-patchlogs/master/data/patchlogs.json",
46
42
  warframeRelicDropInfoUrl: "https://drops.warframestat.us/data/info.json",
47
- warframePatchlogsUrl: "https://raw.githubusercontent.com/WFCD/warframe-patchlogs/master/data/patchlogs.json"
43
+ warframeRelicDropUrl: "https://drops.warframestat.us/data/relics.json"
48
44
  };
49
-
45
+ //#endregion
46
+ //#region src/itemUniqueName.ts
47
+ const QTY_PREFIX = /^\d+X\s+/i;
48
+ const BLUEPRINT_SUFFIX = / Blueprint$/i;
49
+ /**
50
+ * Index sparse warframestat items by lowercase name, preferring higher-ranked uniqueNames.
51
+ * @param {Array<WFCDSparseItem>} items Sparse item list from warframestat
52
+ * @returns {Map<string, string>} Lowercase name → uniqueName
53
+ */
54
+ function buildItemUniqueNameIndex(items) {
55
+ const index = /* @__PURE__ */ new Map();
56
+ items.forEach((item) => {
57
+ if (!(item === null || item === void 0 ? void 0 : item.name) || !(item === null || item === void 0 ? void 0 : item.uniqueName)) return;
58
+ const key = item.name.toLowerCase();
59
+ const existing = index.get(key);
60
+ if (!existing || uniqueNameRank(item.uniqueName) > uniqueNameRank(existing)) index.set(key, item.uniqueName);
61
+ });
62
+ return index;
63
+ }
64
+ /**
65
+ * Strip drop quantity prefixes (e.g. "1200X Kuva" → "Kuva").
66
+ * @param {string} itemName Raw drop item name
67
+ * @returns {string} Name without quantity prefix
68
+ */
69
+ function normalizeRewardName(itemName) {
70
+ return itemName.replace(QTY_PREFIX, "").trim();
71
+ }
72
+ /**
73
+ * Resolve reward uniqueName from WFM gameRef or sparse name index.
74
+ * @param {string} itemName Raw drop item name
75
+ * @param {string|undefined} gameRef WFM gameRef when matched
76
+ * @param {Map<string, string>} itemUniqueNames Sparse (+ Blueprint) index
77
+ * @returns {string} Lotus uniqueName or empty string
78
+ */
79
+ function resolveRewardUniqueName(itemName, gameRef, itemUniqueNames) {
80
+ if (gameRef) return gameRef;
81
+ return itemUniqueNames.get(normalizeRewardName(itemName).toLowerCase()) ?? "";
82
+ }
83
+ /**
84
+ * Prefer in-world item/recipe paths over StoreItems, enemies, and other duplicates.
85
+ * @param {string} uniqueName Lotus path
86
+ * @returns {number} Higher is preferred
87
+ */
88
+ function uniqueNameRank(uniqueName) {
89
+ if (uniqueName.includes("/Enemies/")) return 0;
90
+ if (uniqueName.startsWith("/Lotus/Types/Items/") || uniqueName.startsWith("/Lotus/Types/Recipes/")) return 3;
91
+ if (uniqueName.startsWith("/Lotus/Types/")) return 2;
92
+ if (uniqueName.startsWith("/Lotus/StoreItems/")) return 1;
93
+ return 0;
94
+ }
50
95
  //#endregion
51
96
  //#region src/Logger.ts
52
- var LogLevel = /* @__PURE__ */ function(LogLevel) {
53
- LogLevel[LogLevel["FATAL"] = -1] = "FATAL";
54
- LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
55
- LogLevel[LogLevel["LOG"] = 1] = "LOG";
56
- LogLevel[LogLevel["DEBUG"] = 2] = "DEBUG";
57
- return LogLevel;
58
- }(LogLevel || {});
59
97
  const fromString = (logLevelIsh) => {
60
98
  switch (logLevelIsh === null || logLevelIsh === void 0 ? void 0 : logLevelIsh.toLowerCase()) {
61
- case "fatal": return LogLevel.FATAL;
62
- case "error":
63
- case "bad": return LogLevel.ERROR;
99
+ case "bad":
100
+ case "error": return 0;
101
+ case "debug": return 2;
102
+ case "fatal": return -1;
64
103
  case "info":
65
- case "log": return LogLevel.LOG;
66
- case "debug": return LogLevel.DEBUG;
67
- default: return LogLevel.FATAL;
104
+ case "log": return 1;
105
+ default: return -1;
68
106
  }
69
107
  };
70
108
  var Logger = class {
71
- logLevel = fromString(process.env.LOG_LEVEL || "fatal");
72
- log(message) {
73
- if (this.logLevel >= LogLevel.LOG) node_console.log(message);
109
+ logLevel = fromString(process.env.LOG_LEVEL ?? "fatal");
110
+ debug(message) {
111
+ if (this.logLevel === 2) node_console.debug(message);
74
112
  }
75
113
  error(message) {
76
- if (this.logLevel >= LogLevel.ERROR) node_console.error(message);
77
- }
78
- debug(message) {
79
- if (this.logLevel === LogLevel.DEBUG) node_console.debug(message);
114
+ if (this.logLevel >= 0) node_console.error(message);
80
115
  }
81
116
  fatal(message) {
82
- if (this.logLevel >= LogLevel.FATAL) {
117
+ if (this.logLevel >= -1) {
83
118
  node_console.error(`FATAL: ${message}`);
84
119
  throw new Error(message);
85
120
  }
86
121
  }
122
+ log(message) {
123
+ if (this.logLevel >= 1) node_console.log(message);
124
+ }
87
125
  };
88
126
  var Logger_default = new Logger();
89
-
90
127
  //#endregion
91
128
  //#region src/Generator.ts
92
129
  var Generator = class {
130
+ /** Lowercase item name → uniqueName from sparse warframestat index (+ Blueprint follow-ups) */
131
+ itemUniqueNames;
132
+ relics;
93
133
  relicsRaw;
94
134
  wfcdItems;
95
135
  wfmItems;
96
- relics;
97
136
  constructor() {
98
137
  this.relics = [];
99
- }
100
- /**
101
- * Main function to fetch and generate the relic data
102
- * @returns {Promise<Array<TitaniaRelic>>} The Relics data array
103
- */
104
- async generate() {
105
- Logger_default.log("Starting Generation");
106
- await this.fetchRawData();
107
- this.filterWFCDRelics();
108
- this.generateTitaniaRelics();
109
- return this.relics;
138
+ this.itemUniqueNames = /* @__PURE__ */ new Map();
110
139
  }
111
140
  /**
112
141
  * Fetches all required data from WFCD and WFM.
@@ -131,6 +160,25 @@ var Generator = class {
131
160
  return;
132
161
  }
133
162
  this.wfcdItems = await wfcdItemRequest.json();
163
+ const sparseRequest = await (0, node_fetch.default)(Config.warframeItemsSparseUrl);
164
+ if (!sparseRequest.ok) {
165
+ Logger_default.error("Failed to fetch sparse items from WFCD!");
166
+ return;
167
+ }
168
+ const sparseItems = await sparseRequest.json();
169
+ this.indexSparseItems(sparseItems);
170
+ }
171
+ /**
172
+ * Main function to fetch and generate the relic data
173
+ * @returns {Promise<Array<TitaniaRelic>>} The Relics data array
174
+ */
175
+ async generate() {
176
+ Logger_default.log("Starting Generation");
177
+ await this.fetchRawData();
178
+ this.filterWFCDRelics();
179
+ await this.resolveSpecialRewardUniqueNames();
180
+ this.generateTitaniaRelics();
181
+ return this.relics;
134
182
  }
135
183
  /**
136
184
  * Generates the relic data
@@ -167,17 +215,46 @@ var Generator = class {
167
215
  }
168
216
  }
169
217
  /**
218
+ * Fetch a single warframestat item by name with components for Blueprint resolution.
219
+ * @param {string} name Item display name
220
+ * @returns {Promise<WFCDSparseItem|undefined>} Item or undefined on failure
221
+ */
222
+ async fetchItemByName(name) {
223
+ const url = `${Config.warframeItemByNameUrl}${encodeURIComponent(name)}?only=name,uniqueName,components`;
224
+ const response = await (0, node_fetch.default)(url);
225
+ if (!response.ok) {
226
+ Logger_default.debug(`Failed to fetch item by name: ${name} (${response.status})`);
227
+ return;
228
+ }
229
+ const data = await response.json();
230
+ if ("error" in data && data.error) {
231
+ Logger_default.debug(`No item result for: ${name}`);
232
+ return;
233
+ }
234
+ return data;
235
+ }
236
+ /**
237
+ * Filters WFCD's relic data to only include Intact variants, since we just need the base.
238
+ */
239
+ filterWFCDRelics() {
240
+ var _this$relicsRaw;
241
+ var _this$relicsRaw2;
242
+ var _this$relicsRaw3;
243
+ const before = ((_this$relicsRaw = this.relicsRaw) === null || _this$relicsRaw === void 0 ? void 0 : _this$relicsRaw.length) ?? 0;
244
+ this.relicsRaw = (_this$relicsRaw2 = this.relicsRaw) === null || _this$relicsRaw2 === void 0 ? void 0 : _this$relicsRaw2.filter((x) => x.state === "Intact");
245
+ Logger_default.log(`Filtered relics to intact variants. Before: ${before} After: ${((_this$relicsRaw3 = this.relicsRaw) === null || _this$relicsRaw3 === void 0 ? void 0 : _this$relicsRaw3.length) ?? 0}`);
246
+ }
247
+ /**
170
248
  * Generates a single relic from all available data
171
249
  * @param {WFCDRelic} rawRelic relic to pull Titania data from
172
250
  * @returns {TitaniaRelicReward}
173
251
  */
174
252
  generateTitaniaRelic(rawRelic) {
175
- var _this$wfcdItems2;
253
+ var _this$wfcdItems;
176
254
  var _this$wfmItems2;
177
255
  const name = `${rawRelic.tier} ${rawRelic.relicName}`;
178
256
  const rewards = rawRelic.rewards.map((rawReward) => {
179
257
  var _this$wfmItems;
180
- var _this$wfcdItems;
181
258
  const { chance } = rawReward;
182
259
  const { rarity } = rawReward;
183
260
  const wfmInfo = (_this$wfmItems = this.wfmItems) === null || _this$wfmItems === void 0 ? void 0 : _this$wfmItems.data.find((x) => {
@@ -192,7 +269,7 @@ var Generator = class {
192
269
  if (!(wfmInfo || isSpecial)) Logger_default.debug(`Failed to find wfm item for ${rawReward.itemName}`);
193
270
  const item = {
194
271
  name: rawReward.itemName,
195
- uniqueName: ((_this$wfcdItems = this.wfcdItems) === null || _this$wfcdItems === void 0 || (_this$wfcdItems = _this$wfcdItems.find((x) => x.name.toLowerCase() === `${name.trim()} Intact`.toLowerCase())) === null || _this$wfcdItems === void 0 ? void 0 : _this$wfcdItems.uniqueName) || "",
272
+ uniqueName: resolveRewardUniqueName(rawReward.itemName, wfmInfo === null || wfmInfo === void 0 ? void 0 : wfmInfo.gameRef, this.itemUniqueNames),
196
273
  warframeMarket: void 0
197
274
  };
198
275
  if (wfmInfo) item.warframeMarket = {
@@ -200,19 +277,19 @@ var Generator = class {
200
277
  urlName: wfmInfo.slug
201
278
  };
202
279
  return {
203
- rarity,
204
280
  chance,
205
- item
281
+ item,
282
+ rarity
206
283
  };
207
284
  });
208
285
  let drops = [];
209
- const wfcdItem = (_this$wfcdItems2 = this.wfcdItems) === null || _this$wfcdItems2 === void 0 ? void 0 : _this$wfcdItems2.find((x) => x.name.toLowerCase() === `${name.trim()} Intact`.toLowerCase());
286
+ const wfcdItem = (_this$wfcdItems = this.wfcdItems) === null || _this$wfcdItems === void 0 ? void 0 : _this$wfcdItems.find((x) => x.name.toLowerCase() === `${name.trim()} Intact`.toLowerCase());
210
287
  if (!wfcdItem) Logger_default.error(`Failed to get WFCD item for relic: ${name}`);
211
- if (wfcdItem && wfcdItem.drops) drops = wfcdItem.drops.map((rawDrop) => {
288
+ if (wfcdItem === null || wfcdItem === void 0 ? void 0 : wfcdItem.drops) drops = wfcdItem.drops.map((rawDrop) => {
212
289
  return {
213
- rarity: rawDrop.rarity,
214
290
  chance: rawDrop.chance,
215
- location: rawDrop.location
291
+ location: rawDrop.location,
292
+ rarity: rawDrop.rarity
216
293
  };
217
294
  });
218
295
  const wfm = (_this$wfmItems2 = this.wfmItems) === null || _this$wfmItems2 === void 0 ? void 0 : _this$wfmItems2.data.find((x) => {
@@ -220,36 +297,71 @@ var Generator = class {
220
297
  });
221
298
  if (!wfm) Logger_default.error(`Failed to get relic item from wfm: ${name}`);
222
299
  return {
300
+ locations: drops,
223
301
  name,
224
302
  rewards,
225
- locations: drops,
226
- uniqueName: (wfcdItem === null || wfcdItem === void 0 ? void 0 : wfcdItem.uniqueName) || "",
303
+ uniqueName: (wfcdItem === null || wfcdItem === void 0 ? void 0 : wfcdItem.uniqueName) ?? "",
227
304
  vaultInfo: { vaulted: drops.length === 0 },
228
- ...(wfm === null || wfm === void 0 ? void 0 : wfm.id) && (wfm === null || wfm === void 0 ? void 0 : wfm.slug) && { warframeMarket: {
229
- id: wfm === null || wfm === void 0 ? void 0 : wfm.id,
230
- urlName: wfm === null || wfm === void 0 ? void 0 : wfm.slug
305
+ ...(wfm === null || wfm === void 0 ? void 0 : wfm.id) && wfm.slug && { warframeMarket: {
306
+ id: wfm.id,
307
+ urlName: wfm.slug
231
308
  } }
232
309
  };
233
310
  }
234
311
  /**
235
- * Filters WFCD's relic data to only include Intact variants, since we just need the base.
312
+ * Index sparse warframestat items by lowercase name, preferring /Lotus/Types/ paths.
313
+ * @param {Array<WFCDSparseItem>} items Sparse item list from warframestat
236
314
  */
237
- filterWFCDRelics() {
238
- var _this$relicsRaw;
239
- var _this$relicsRaw2;
240
- var _this$relicsRaw3;
241
- const before = ((_this$relicsRaw = this.relicsRaw) === null || _this$relicsRaw === void 0 ? void 0 : _this$relicsRaw.length) || 0;
242
- this.relicsRaw = (_this$relicsRaw2 = this.relicsRaw) === null || _this$relicsRaw2 === void 0 ? void 0 : _this$relicsRaw2.filter((x) => x.state === "Intact");
243
- Logger_default.log(`Filtered relics to intact variants. Before: ${before} After: ${((_this$relicsRaw3 = this.relicsRaw) === null || _this$relicsRaw3 === void 0 ? void 0 : _this$relicsRaw3.length) || 0}`);
315
+ indexSparseItems(items) {
316
+ this.itemUniqueNames = buildItemUniqueNameIndex(items);
317
+ Logger_default.debug(`Indexed ${this.itemUniqueNames.size} sparse item uniqueNames`);
318
+ }
319
+ /**
320
+ * For reward names missing WFM, resolve uniqueNames from sparse index.
321
+ * Blueprint-suffixed names need a parent item fetch for the Blueprint component.
322
+ */
323
+ async resolveSpecialRewardUniqueNames() {
324
+ if (!this.relicsRaw || !this.wfmItems) return;
325
+ const specialNames = /* @__PURE__ */ new Set();
326
+ this.relicsRaw.forEach((relic) => {
327
+ relic.rewards.forEach((reward) => {
328
+ var _this$wfmItems3;
329
+ if (!((_this$wfmItems3 = this.wfmItems) === null || _this$wfmItems3 === void 0 ? void 0 : _this$wfmItems3.data.some((x) => {
330
+ return x.i18n.en.name.toLowerCase() === reward.itemName.toLowerCase();
331
+ }))) specialNames.add(normalizeRewardName(reward.itemName));
332
+ });
333
+ });
334
+ const blueprintParents = /* @__PURE__ */ new Map();
335
+ Array.from(specialNames).forEach((name) => {
336
+ const key = name.toLowerCase();
337
+ if (this.itemUniqueNames.has(key)) return;
338
+ if (!BLUEPRINT_SUFFIX.test(name)) {
339
+ Logger_default.debug(`No uniqueName for special reward: ${name}`);
340
+ return;
341
+ }
342
+ const parent = name.replace(BLUEPRINT_SUFFIX, "").trim();
343
+ if (parent) blueprintParents.set(name, parent);
344
+ });
345
+ const parentCache = /* @__PURE__ */ new Map();
346
+ for (const [blueprintName, parent] of blueprintParents.entries()) {
347
+ var _parentItem$component;
348
+ let parentItem = parentCache.get(parent);
349
+ if (!parentCache.has(parent)) {
350
+ parentItem = await this.fetchItemByName(parent);
351
+ parentCache.set(parent, parentItem);
352
+ }
353
+ const blueprint = parentItem === null || parentItem === void 0 || (_parentItem$component = parentItem.components) === null || _parentItem$component === void 0 ? void 0 : _parentItem$component.find((c) => c.name.toLowerCase() === "blueprint");
354
+ if (blueprint === null || blueprint === void 0 ? void 0 : blueprint.uniqueName) this.itemUniqueNames.set(blueprintName.toLowerCase(), blueprint.uniqueName);
355
+ else Logger_default.debug(`Failed to resolve Blueprint uniqueName for: ${blueprintName}`);
356
+ }
244
357
  }
245
358
  };
246
-
247
359
  //#endregion
248
360
  //#region src/VersionManager.ts
249
361
  var VersionManager = class {
362
+ hashPath;
250
363
  versionPath;
251
364
  versionRawPath;
252
- hashPath;
253
365
  /**
254
366
  * Creates a new VersionManager instance.
255
367
  * @param {string} dataDir Folder to write version information to.
@@ -271,7 +383,7 @@ var VersionManager = class {
271
383
  try {
272
384
  await node_fs_promises.default.access(this.hashPath);
273
385
  return JSON.parse(await node_fs_promises.default.readFile(this.hashPath, "utf-8")).hash !== info.hash;
274
- } catch (ex) {
386
+ } catch {
275
387
  return true;
276
388
  }
277
389
  }
@@ -294,12 +406,12 @@ var VersionManager = class {
294
406
  }
295
407
  const hashInfo = await hashReq.json();
296
408
  const versionInfo = {
297
- version,
298
- title: patchlogs[0].name
409
+ title: patchlogs[0].name,
410
+ version
299
411
  };
300
412
  const hashFile = {
301
- hash: hashInfo.hash,
302
413
  deUpdated: hashInfo.modified,
414
+ hash: hashInfo.hash,
303
415
  timestamp
304
416
  };
305
417
  await node_fs_promises.default.writeFile(this.hashPath, JSON.stringify(hashFile, void 0, 2), "utf-8");
@@ -308,29 +420,28 @@ var VersionManager = class {
308
420
  Logger_default.debug("Finished writing version info");
309
421
  }
310
422
  };
311
-
312
423
  //#endregion
313
- Object.defineProperty(exports, 'Generator', {
314
- enumerable: true,
315
- get: function () {
316
- return Generator;
317
- }
424
+ Object.defineProperty(exports, "Generator", {
425
+ enumerable: true,
426
+ get: function() {
427
+ return Generator;
428
+ }
318
429
  });
319
- Object.defineProperty(exports, 'Logger_default', {
320
- enumerable: true,
321
- get: function () {
322
- return Logger_default;
323
- }
430
+ Object.defineProperty(exports, "Logger_default", {
431
+ enumerable: true,
432
+ get: function() {
433
+ return Logger_default;
434
+ }
324
435
  });
325
- Object.defineProperty(exports, 'VersionManager', {
326
- enumerable: true,
327
- get: function () {
328
- return VersionManager;
329
- }
436
+ Object.defineProperty(exports, "VersionManager", {
437
+ enumerable: true,
438
+ get: function() {
439
+ return VersionManager;
440
+ }
441
+ });
442
+ Object.defineProperty(exports, "__name", {
443
+ enumerable: true,
444
+ get: function() {
445
+ return __name;
446
+ }
330
447
  });
331
- Object.defineProperty(exports, '__name', {
332
- enumerable: true,
333
- get: function () {
334
- return __name;
335
- }
336
- });
package/dist/index.cjs CHANGED
@@ -1,5 +1,4 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_VersionManager = require('./VersionManager-DkHKfTUL.cjs');
3
-
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_VersionManager = require("./VersionManager-CDrWT0KK.cjs");
4
3
  exports.Generator = require_VersionManager.Generator;
5
- exports.VersionManager = require_VersionManager.VersionManager;
4
+ exports.VersionManager = require_VersionManager.VersionManager;