@wfcd/relics 2.0.30 → 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.
- package/CHANGELOG.md +7 -0
- package/dist/Build.cjs +1 -1
- package/dist/Build.d.cts +1 -1
- package/dist/Build.d.mts +1 -1
- package/dist/Build.mjs +1 -1
- package/dist/{VersionManager-W7wuuPML.mjs → VersionManager-BckuXhbh.mjs} +180 -52
- package/dist/{VersionManager-ZP_iui8x.cjs → VersionManager-CDrWT0KK.cjs} +182 -54
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +146 -114
- package/dist/index.d.mts +146 -115
- package/dist/index.mjs +1 -1
- package/package.json +16 -13
|
@@ -19,79 +19,123 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
19
|
}
|
|
20
20
|
return to;
|
|
21
21
|
};
|
|
22
|
-
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", {
|
|
23
23
|
value: mod,
|
|
24
24
|
enumerable: true
|
|
25
25
|
}) : target, mod));
|
|
26
26
|
//#endregion
|
|
27
|
+
let node_fetch = require("node-fetch");
|
|
28
|
+
node_fetch = __toESM(node_fetch);
|
|
27
29
|
let node_fs_promises = require("node:fs/promises");
|
|
28
30
|
node_fs_promises = __toESM(node_fs_promises);
|
|
29
31
|
let node_path = require("node:path");
|
|
30
32
|
node_path = __toESM(node_path);
|
|
31
|
-
let node_fetch = require("node-fetch");
|
|
32
|
-
node_fetch = __toESM(node_fetch);
|
|
33
33
|
let node_console = require("node:console");
|
|
34
34
|
node_console = __toESM(node_console);
|
|
35
35
|
//#region src/Config.ts
|
|
36
36
|
const Config = {
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
warframeItemByNameUrl: "https://api.warframestat.us/items/",
|
|
38
|
+
warframeItemsSparseUrl: "https://api.warframestat.us/items/?only=name,uniqueName",
|
|
39
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",
|
|
40
42
|
warframeRelicDropInfoUrl: "https://drops.warframestat.us/data/info.json",
|
|
41
|
-
|
|
43
|
+
warframeRelicDropUrl: "https://drops.warframestat.us/data/relics.json"
|
|
42
44
|
};
|
|
43
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
|
+
}
|
|
95
|
+
//#endregion
|
|
44
96
|
//#region src/Logger.ts
|
|
45
97
|
const fromString = (logLevelIsh) => {
|
|
46
98
|
switch (logLevelIsh === null || logLevelIsh === void 0 ? void 0 : logLevelIsh.toLowerCase()) {
|
|
99
|
+
case "bad":
|
|
100
|
+
case "error": return 0;
|
|
101
|
+
case "debug": return 2;
|
|
47
102
|
case "fatal": return -1;
|
|
48
|
-
case "error":
|
|
49
|
-
case "bad": return 0;
|
|
50
103
|
case "info":
|
|
51
104
|
case "log": return 1;
|
|
52
|
-
case "debug": return 2;
|
|
53
105
|
default: return -1;
|
|
54
106
|
}
|
|
55
107
|
};
|
|
56
108
|
var Logger = class {
|
|
57
|
-
logLevel = fromString(process.env.LOG_LEVEL
|
|
58
|
-
|
|
59
|
-
if (this.logLevel
|
|
109
|
+
logLevel = fromString(process.env.LOG_LEVEL ?? "fatal");
|
|
110
|
+
debug(message) {
|
|
111
|
+
if (this.logLevel === 2) node_console.debug(message);
|
|
60
112
|
}
|
|
61
113
|
error(message) {
|
|
62
114
|
if (this.logLevel >= 0) node_console.error(message);
|
|
63
115
|
}
|
|
64
|
-
debug(message) {
|
|
65
|
-
if (this.logLevel === 2) node_console.debug(message);
|
|
66
|
-
}
|
|
67
116
|
fatal(message) {
|
|
68
117
|
if (this.logLevel >= -1) {
|
|
69
118
|
node_console.error(`FATAL: ${message}`);
|
|
70
119
|
throw new Error(message);
|
|
71
120
|
}
|
|
72
121
|
}
|
|
122
|
+
log(message) {
|
|
123
|
+
if (this.logLevel >= 1) node_console.log(message);
|
|
124
|
+
}
|
|
73
125
|
};
|
|
74
126
|
var Logger_default = new Logger();
|
|
75
127
|
//#endregion
|
|
76
128
|
//#region src/Generator.ts
|
|
77
129
|
var Generator = class {
|
|
130
|
+
/** Lowercase item name → uniqueName from sparse warframestat index (+ Blueprint follow-ups) */
|
|
131
|
+
itemUniqueNames;
|
|
132
|
+
relics;
|
|
78
133
|
relicsRaw;
|
|
79
134
|
wfcdItems;
|
|
80
135
|
wfmItems;
|
|
81
|
-
relics;
|
|
82
136
|
constructor() {
|
|
83
137
|
this.relics = [];
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Main function to fetch and generate the relic data
|
|
87
|
-
* @returns {Promise<Array<TitaniaRelic>>} The Relics data array
|
|
88
|
-
*/
|
|
89
|
-
async generate() {
|
|
90
|
-
Logger_default.log("Starting Generation");
|
|
91
|
-
await this.fetchRawData();
|
|
92
|
-
this.filterWFCDRelics();
|
|
93
|
-
this.generateTitaniaRelics();
|
|
94
|
-
return this.relics;
|
|
138
|
+
this.itemUniqueNames = /* @__PURE__ */ new Map();
|
|
95
139
|
}
|
|
96
140
|
/**
|
|
97
141
|
* Fetches all required data from WFCD and WFM.
|
|
@@ -116,6 +160,25 @@ var Generator = class {
|
|
|
116
160
|
return;
|
|
117
161
|
}
|
|
118
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;
|
|
119
182
|
}
|
|
120
183
|
/**
|
|
121
184
|
* Generates the relic data
|
|
@@ -152,17 +215,46 @@ var Generator = class {
|
|
|
152
215
|
}
|
|
153
216
|
}
|
|
154
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
|
+
/**
|
|
155
248
|
* Generates a single relic from all available data
|
|
156
249
|
* @param {WFCDRelic} rawRelic relic to pull Titania data from
|
|
157
250
|
* @returns {TitaniaRelicReward}
|
|
158
251
|
*/
|
|
159
252
|
generateTitaniaRelic(rawRelic) {
|
|
160
|
-
var _this$
|
|
253
|
+
var _this$wfcdItems;
|
|
161
254
|
var _this$wfmItems2;
|
|
162
255
|
const name = `${rawRelic.tier} ${rawRelic.relicName}`;
|
|
163
256
|
const rewards = rawRelic.rewards.map((rawReward) => {
|
|
164
257
|
var _this$wfmItems;
|
|
165
|
-
var _this$wfcdItems;
|
|
166
258
|
const { chance } = rawReward;
|
|
167
259
|
const { rarity } = rawReward;
|
|
168
260
|
const wfmInfo = (_this$wfmItems = this.wfmItems) === null || _this$wfmItems === void 0 ? void 0 : _this$wfmItems.data.find((x) => {
|
|
@@ -177,7 +269,7 @@ var Generator = class {
|
|
|
177
269
|
if (!(wfmInfo || isSpecial)) Logger_default.debug(`Failed to find wfm item for ${rawReward.itemName}`);
|
|
178
270
|
const item = {
|
|
179
271
|
name: rawReward.itemName,
|
|
180
|
-
uniqueName: (
|
|
272
|
+
uniqueName: resolveRewardUniqueName(rawReward.itemName, wfmInfo === null || wfmInfo === void 0 ? void 0 : wfmInfo.gameRef, this.itemUniqueNames),
|
|
181
273
|
warframeMarket: void 0
|
|
182
274
|
};
|
|
183
275
|
if (wfmInfo) item.warframeMarket = {
|
|
@@ -185,19 +277,19 @@ var Generator = class {
|
|
|
185
277
|
urlName: wfmInfo.slug
|
|
186
278
|
};
|
|
187
279
|
return {
|
|
188
|
-
rarity,
|
|
189
280
|
chance,
|
|
190
|
-
item
|
|
281
|
+
item,
|
|
282
|
+
rarity
|
|
191
283
|
};
|
|
192
284
|
});
|
|
193
285
|
let drops = [];
|
|
194
|
-
const wfcdItem = (_this$
|
|
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());
|
|
195
287
|
if (!wfcdItem) Logger_default.error(`Failed to get WFCD item for relic: ${name}`);
|
|
196
|
-
if (wfcdItem
|
|
288
|
+
if (wfcdItem === null || wfcdItem === void 0 ? void 0 : wfcdItem.drops) drops = wfcdItem.drops.map((rawDrop) => {
|
|
197
289
|
return {
|
|
198
|
-
rarity: rawDrop.rarity,
|
|
199
290
|
chance: rawDrop.chance,
|
|
200
|
-
location: rawDrop.location
|
|
291
|
+
location: rawDrop.location,
|
|
292
|
+
rarity: rawDrop.rarity
|
|
201
293
|
};
|
|
202
294
|
});
|
|
203
295
|
const wfm = (_this$wfmItems2 = this.wfmItems) === null || _this$wfmItems2 === void 0 ? void 0 : _this$wfmItems2.data.find((x) => {
|
|
@@ -205,35 +297,71 @@ var Generator = class {
|
|
|
205
297
|
});
|
|
206
298
|
if (!wfm) Logger_default.error(`Failed to get relic item from wfm: ${name}`);
|
|
207
299
|
return {
|
|
300
|
+
locations: drops,
|
|
208
301
|
name,
|
|
209
302
|
rewards,
|
|
210
|
-
|
|
211
|
-
uniqueName: (wfcdItem === null || wfcdItem === void 0 ? void 0 : wfcdItem.uniqueName) || "",
|
|
303
|
+
uniqueName: (wfcdItem === null || wfcdItem === void 0 ? void 0 : wfcdItem.uniqueName) ?? "",
|
|
212
304
|
vaultInfo: { vaulted: drops.length === 0 },
|
|
213
|
-
...(wfm === null || wfm === void 0 ? void 0 : wfm.id) &&
|
|
214
|
-
id: wfm
|
|
215
|
-
urlName: wfm
|
|
305
|
+
...(wfm === null || wfm === void 0 ? void 0 : wfm.id) && wfm.slug && { warframeMarket: {
|
|
306
|
+
id: wfm.id,
|
|
307
|
+
urlName: wfm.slug
|
|
216
308
|
} }
|
|
217
309
|
};
|
|
218
310
|
}
|
|
219
311
|
/**
|
|
220
|
-
*
|
|
312
|
+
* Index sparse warframestat items by lowercase name, preferring /Lotus/Types/ paths.
|
|
313
|
+
* @param {Array<WFCDSparseItem>} items Sparse item list from warframestat
|
|
221
314
|
*/
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
+
}
|
|
229
357
|
}
|
|
230
358
|
};
|
|
231
359
|
//#endregion
|
|
232
360
|
//#region src/VersionManager.ts
|
|
233
361
|
var VersionManager = class {
|
|
362
|
+
hashPath;
|
|
234
363
|
versionPath;
|
|
235
364
|
versionRawPath;
|
|
236
|
-
hashPath;
|
|
237
365
|
/**
|
|
238
366
|
* Creates a new VersionManager instance.
|
|
239
367
|
* @param {string} dataDir Folder to write version information to.
|
|
@@ -255,7 +383,7 @@ var VersionManager = class {
|
|
|
255
383
|
try {
|
|
256
384
|
await node_fs_promises.default.access(this.hashPath);
|
|
257
385
|
return JSON.parse(await node_fs_promises.default.readFile(this.hashPath, "utf-8")).hash !== info.hash;
|
|
258
|
-
} catch
|
|
386
|
+
} catch {
|
|
259
387
|
return true;
|
|
260
388
|
}
|
|
261
389
|
}
|
|
@@ -278,12 +406,12 @@ var VersionManager = class {
|
|
|
278
406
|
}
|
|
279
407
|
const hashInfo = await hashReq.json();
|
|
280
408
|
const versionInfo = {
|
|
281
|
-
|
|
282
|
-
|
|
409
|
+
title: patchlogs[0].name,
|
|
410
|
+
version
|
|
283
411
|
};
|
|
284
412
|
const hashFile = {
|
|
285
|
-
hash: hashInfo.hash,
|
|
286
413
|
deUpdated: hashInfo.modified,
|
|
414
|
+
hash: hashInfo.hash,
|
|
287
415
|
timestamp
|
|
288
416
|
};
|
|
289
417
|
await node_fs_promises.default.writeFile(this.hashPath, JSON.stringify(hashFile, void 0, 2), "utf-8");
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_VersionManager = require("./VersionManager-
|
|
2
|
+
const require_VersionManager = require("./VersionManager-CDrWT0KK.cjs");
|
|
3
3
|
exports.Generator = require_VersionManager.Generator;
|
|
4
4
|
exports.VersionManager = require_VersionManager.VersionManager;
|