hxdec 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Harun Eggleton
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # hxdec
2
+ A Hexidecimal Magic the Gathering Decklist format.
@@ -0,0 +1,13 @@
1
+ // @ts-check
2
+ import js from '@eslint/js';
3
+ import tseslint from 'typescript-eslint';
4
+
5
+ export default tseslint.config(
6
+ js.configs.recommended,
7
+ ...tseslint.configs.recommended,
8
+ {
9
+ rules: {
10
+ "@typescript-eslint/no-explicit-any": "off"
11
+ }
12
+ }
13
+ );
package/lib/index.js ADDED
@@ -0,0 +1,559 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const hxdec = {
4
+ setData: [], // to be populated with set data from scryfall on init
5
+ encode: (list, foil = false, tags = false, cat = false) => {
6
+ if (hxdec.setData.length === 0) {
7
+ hxdec.error("Set data not ready yet, cannot encode");
8
+ return;
9
+ }
10
+ const digestList = () => {
11
+ if (list.length === 0) {
12
+ hxdec.error("No deck list provided");
13
+ return [];
14
+ }
15
+ const sections = list.split("\n\n");
16
+ const listData = {
17
+ name: "",
18
+ targetSection: "",
19
+ commander: [],
20
+ mainboard: [],
21
+ sideboard: [],
22
+ maybeboard: [],
23
+ companion: [],
24
+ prefetch: [], // cards that need to be fetched from scryfall to get set and collector number info for encoding
25
+ };
26
+ let targetSection = "mainboard"; // default section target
27
+ sections.forEach((section) => {
28
+ const sec = section.split("\n");
29
+ sec.forEach((line, index) => {
30
+ line = line.trim();
31
+ // if line doesnt start with a number followed by " " or "x ", It is not a card line. Check to see if its a section title and then, assign the targetSection based on the title. If the title is "About" and it is the first line of the first section, check to see if the second line starts with "Name " and if so, capture the deck name from that line and assign it to listData.name. If the line is not a card line or a section header, skip it.
32
+ if (!/^\d+x?\s/.test(line)) {
33
+ const titleLine = line.toLowerCase().replace(":", "");
34
+ console.log(`Processing section title: ${titleLine}`);
35
+ if (titleLine === "commander" || titleLine === "companion" || titleLine === "sideboard" || titleLine === "maybeboard" || titleLine === "mainboard") {
36
+ targetSection = titleLine;
37
+ }
38
+ if (titleLine === "deck") {
39
+ targetSection = "mainboard";
40
+ }
41
+ if (titleLine === "about" && index === 0 && /^Name\s/.test(sec[1])) {
42
+ listData.name = sec[1].split("Name")[1].trim();
43
+ }
44
+ return;
45
+ }
46
+ else if (sec.length < 3 && index === 0 && listData.commander.length === 0) {
47
+ // 1-2 separate lines with no section headerde notes commanders for some formats
48
+ targetSection = "commander";
49
+ }
50
+ const newCard = hxdec.digestCard(line);
51
+ if (typeof newCard.hxnumber === "undefined" || typeof newCard.hxcode === "undefined") {
52
+ listData.prefetch = listData.prefetch || [];
53
+ listData.prefetch.push(Object.assign(Object.assign({}, newCard), { targetSection }));
54
+ return;
55
+ }
56
+ else if (typeof newCard.cat !== "undefined" && newCard.cat.includes("Commander")) {
57
+ listData.commander = listData.commander || [];
58
+ listData.commander.push(Object.assign(Object.assign({}, newCard), { targetSection }));
59
+ }
60
+ else {
61
+ listData[targetSection] = listData[targetSection] || [];
62
+ listData[targetSection].push(Object.assign(Object.assign({}, newCard), { targetSection }));
63
+ }
64
+ });
65
+ });
66
+ if (typeof listData.prefetch !== "undefined") {
67
+ hxdec.getCardData(listData.prefetch, ["set", "collector_number"], (cards) => {
68
+ cards.forEach((card) => {
69
+ var _a;
70
+ // add each card to its target section and update the hxcode and hxnumber based on the set and collector number
71
+ if (typeof card.set !== "undefined" && typeof card.collector_number !== "undefined" && typeof hxdec.setData !== "undefined") {
72
+ card.hxcode = ((_a = hxdec.setData.find(s => s.code.toLowerCase() === card.set.toLowerCase())) === null || _a === void 0 ? void 0 : _a.hxcode) || null;
73
+ if (card.hxcode) {
74
+ const collectorNumber = card.collector_number;
75
+ if (/^\d+$/.test(collectorNumber)) {
76
+ card.hxnumber = Number(collectorNumber).toString(16);
77
+ }
78
+ else {
79
+ card.hxnumber = `~${collectorNumber}~`;
80
+ }
81
+ }
82
+ else {
83
+ hxdec.warning(`No set data found for set: ${card.set} on card: ${card.name}`);
84
+ }
85
+ listData[card.targetSection] = listData[card.targetSection] || [];
86
+ listData[card.targetSection].push(card);
87
+ }
88
+ });
89
+ console.log("Decoded list data and fetched missing card info from scryfall:", listData);
90
+ buildHxdec(listData);
91
+ });
92
+ }
93
+ else {
94
+ console.log("Decoded list data:", listData);
95
+ buildHxdec(listData);
96
+ }
97
+ return;
98
+ };
99
+ const buildHxdec = (listData) => {
100
+ // build the hxdec string based on the listData and setData
101
+ const outputCards = (rawCards) => {
102
+ let newCards = "";
103
+ rawCards.forEach(card => {
104
+ const qtyChars = ["u", "v", "w", "x", "y", "z"];
105
+ let cardString = "";
106
+ if (card.qty >= 1 && card.qty <= 4) {
107
+ cardString += qtyChars[card.qty - 1];
108
+ }
109
+ else if (card.qty > 4 && card.qty < 16) {
110
+ const qtyHex = Number(card.qty).toString(16);
111
+ cardString += `y${qtyHex}`;
112
+ }
113
+ else if (card.qty >= 16 && card.qty < 256) {
114
+ const qtyHex = Number(card.qty).toString(16);
115
+ cardString += `z${qtyHex}`;
116
+ }
117
+ else {
118
+ cardString += "zff";
119
+ }
120
+ cardString += card.hxcode;
121
+ cardString += card.hxnumber;
122
+ if (foil && typeof card.foil !== "undefined") {
123
+ cardString += `*${card.foil}*`;
124
+ }
125
+ if (tags && typeof card.tags !== "undefined") {
126
+ cardString += `^${card.tags}^`;
127
+ }
128
+ if (cat && typeof card.cat !== "undefined") {
129
+ cardString += `[${card.cat}]`;
130
+ }
131
+ newCards += cardString;
132
+ });
133
+ return newCards;
134
+ };
135
+ if (typeof listData.mainboard === "undefined" || listData.mainboard.length === 0) {
136
+ hxdec.error("No mainboard cards found, cannot build HXDEC");
137
+ return;
138
+ }
139
+ // If this is a type of format that lists a commander as the first card in the mainboard section without a section header, move it to the commander section and remove it from the mainboard section
140
+ if (typeof listData.mainboard !== "undefined" && typeof listData.commander === "undefined") {
141
+ // count the number of cards in the mainboard
142
+ let mainboardCount = 0;
143
+ listData.mainboard.forEach((mainboardCard) => {
144
+ mainboardCount += mainboardCard.qty;
145
+ });
146
+ if (mainboardCount > 98) {
147
+ // Mainboard has more than 98 cards and no commander specified, treating first card as commander
148
+ listData.commander = [listData.mainboard[0]];
149
+ listData.mainboard = listData.mainboard.slice(1);
150
+ }
151
+ }
152
+ let deck = `h${outputCards(listData.mainboard)}`;
153
+ if (typeof listData.commander !== "undefined") {
154
+ deck += `k${outputCards(listData.commander)}`;
155
+ }
156
+ if (typeof listData.sideboard !== "undefined") {
157
+ deck += `s${outputCards(listData.sideboard)}`;
158
+ }
159
+ if (typeof listData.maybeboard !== "undefined") {
160
+ deck += `m${outputCards(listData.maybeboard)}`;
161
+ }
162
+ if (typeof listData.companion !== "undefined") {
163
+ deck += `p${outputCards(listData.companion)}`;
164
+ }
165
+ if (typeof listData.name !== "undefined") {
166
+ deck += `+${encodeURIComponent(listData.name)}+`;
167
+ }
168
+ hxdec.ready();
169
+ hxdec.encoded(deck);
170
+ return;
171
+ };
172
+ hxdec.loading("Processing deck list");
173
+ digestList();
174
+ },
175
+ decode: (list, format = "Archideckt") => {
176
+ if (hxdec.setData.length === 0) {
177
+ console.log("Set data not ready yet, cannot decode");
178
+ return;
179
+ }
180
+ hxdec.loading("Decoding HXDEC string");
181
+ // Decoding logic to be implemented
182
+ // remove values wrapped and store them in an array, replace them in the deck string with their index in the array wrapped in thier original wrap symbol so they can be reinserted after splitting the string into sections
183
+ const captureWrappedValues = (wrap1, wrap2, regex) => {
184
+ const capturedValues = [];
185
+ const matches = list.match(regex) || [];
186
+ // for each match push the value without the wrapSymbol to capturedValues and replace the match in list with a placeholder of the index wrapped
187
+ matches.forEach((match, index) => {
188
+ // console.log(`Captured value for ${wrap1}${wrap2}:`, match);
189
+ capturedValues.push(match.replace(wrap1, "").replace(wrap2, ""));
190
+ list = list.replace(match, `${wrap1}${index}${wrap2}`);
191
+ });
192
+ return capturedValues;
193
+ };
194
+ const categoryValues = captureWrappedValues("[", "]", /\[(.*?)\]/g);
195
+ const textSets = captureWrappedValues("~", "~", /~(.*?)~/g);
196
+ const foilValues = captureWrappedValues("*", "*", /\*(.*?)\*/g);
197
+ const tagValues = captureWrappedValues("^", "^", /\^(.*?)\^/g);
198
+ const nameValue = captureWrappedValues("+", "", /\+(.*)\+/g);
199
+ const decodedData = { name: "", prefetch: [] };
200
+ if (nameValue.length > 0) {
201
+ decodedData.name = decodeURIComponent(nameValue[0]);
202
+ }
203
+ list = list.replace(/\+(.*)\+/g, ""); // remove the name from the list string so it doesn't interfere with section splitting
204
+ const sections = list.split(/(?=[hksmp])/);
205
+ sections.forEach(section => {
206
+ const type = section[0];
207
+ const data = section.substring(1);
208
+ const cardCodes = data.split(/(?=[uvwxyz])/);
209
+ const targetSection = type === "h" ? "mainboard" : type === "k" ? "commander" : type === "s" ? "sideboard" : type === "m" ? "maybeboard" : type === "p" ? "companion" : null;
210
+ if (targetSection) {
211
+ const cards = [];
212
+ cardCodes.forEach(code => {
213
+ // get the first character and remove it
214
+ const qtyChar = code[0];
215
+ code = code.substring(1);
216
+ let qty = ["u", "v", "w", "x", "y", "z"].indexOf(qtyChar) + 1;
217
+ if (qtyChar === "y") {
218
+ qty = parseInt(code.substring(0, 1), 16);
219
+ code = code.substring(1);
220
+ }
221
+ if (qtyChar === "z") {
222
+ qty = parseInt(code.substring(0, 2), 16);
223
+ code = code.substring(2);
224
+ }
225
+ const setHx = code.substring(0, 3);
226
+ // if code contains * , extract the foil value and remove it from the code
227
+ let foil = null;
228
+ if (code.includes("*")) {
229
+ const foilMatch = code.match(/\*(.*?)\*/);
230
+ if (foilMatch) {
231
+ foil = foilValues[parseInt(foilMatch[1])];
232
+ code = code.replace(foilMatch[0], "");
233
+ }
234
+ }
235
+ // if code contains ^, extract the tag value and remove it from the code
236
+ let tags = null;
237
+ if (code.includes("^")) {
238
+ const tagMatch = code.match(/\^(.*?)\^/);
239
+ if (tagMatch) {
240
+ tags = tagValues[parseInt(tagMatch[1])];
241
+ code = code.replace(tagMatch[0], "");
242
+ }
243
+ }
244
+ // if code contains [ ], extract the category value and remove it from the code
245
+ let category = null;
246
+ if (code.includes("[")) {
247
+ const categoryMatch = code.match(/\[(.*?)\]/);
248
+ if (categoryMatch) {
249
+ category = categoryValues[parseInt(categoryMatch[1])];
250
+ code = code.replace(categoryMatch[0], "");
251
+ }
252
+ }
253
+ const numberHx = code.substring(3);
254
+ let number = Number(parseInt(numberHx, 16)).toString();
255
+ if (isNaN(parseInt(number)) && numberHx.includes("~")) {
256
+ number = textSets[parseInt(numberHx.replace("~", ""))];
257
+ }
258
+ else if (isNaN(parseInt(number))) {
259
+ console.log(`Invalid collector number hex: ${numberHx} for code: ${code}`);
260
+ }
261
+ let set = null;
262
+ // find set in set data
263
+ const setInfo = hxdec.setData.find(s => s.hxcode === setHx);
264
+ if (setInfo) {
265
+ set = setInfo.code;
266
+ }
267
+ const newCard = { code, qty, set, number, foil, tags, category, targetSection };
268
+ cards.push(newCard);
269
+ });
270
+ decodedData.prefetch.push(...cards);
271
+ return;
272
+ }
273
+ console.log(`Unknown section type: ${type} in section: ${section}`);
274
+ return;
275
+ });
276
+ hxdec.getCardData(decodedData.prefetch, ["name"], (cards) => {
277
+ cards.forEach(card => {
278
+ decodedData[card.targetSection] = decodedData[card.targetSection] || [];
279
+ decodedData[card.targetSection].push(card);
280
+ });
281
+ buildList(decodedData);
282
+ console.log("Decoded data with card names:", decodedData);
283
+ });
284
+ const buildList = (decodedData) => {
285
+ let formatOptions = {
286
+ mainboardTitle: "",
287
+ commanderTitle: "",
288
+ sideboardTitle: "",
289
+ maybeboardTitle: "",
290
+ companionTitle: "",
291
+ nameTitle: "",
292
+ name: false,
293
+ sets: false,
294
+ collectorNumber: false,
295
+ foil: false,
296
+ cat: false,
297
+ tags: false,
298
+ order: ["commander", "mainboard", "companion", "sideboard", "maybeboard"]
299
+ };
300
+ if (format === "Archideckt") {
301
+ formatOptions = Object.assign(Object.assign({}, formatOptions), { mainboardTitle: "\nMainboard\n", commanderTitle: "\nCommander\n", sideboardTitle: "\nSideboard\n", maybeboardTitle: "\nMaybeboard\n", companionTitle: "\nCompanion\n", sets: true, collectorNumber: true, foil: true, cat: true, tags: true });
302
+ }
303
+ if (format === "MTGO") {
304
+ formatOptions = Object.assign(Object.assign({}, formatOptions), { sideboardTitle: "\nSIDEBOARD:\n", commanderTitle: "\n", order: ["mainboard", "companion", "sideboard", "commander", "maybeboard"] });
305
+ }
306
+ if (format === "Arena") {
307
+ formatOptions = Object.assign(Object.assign({}, formatOptions), { mainboardTitle: "\nDeck\n", commanderTitle: "\nCommander\n", sideboardTitle: "\nSideboard\n", nameTitle: "About\nName ", name: true });
308
+ }
309
+ if (format === "Moxfield") {
310
+ formatOptions = Object.assign(Object.assign({}, formatOptions), { sideboardTitle: "\nSIDEBOARD:\n", sets: true, collectorNumber: true, foil: true });
311
+ }
312
+ let newList = "";
313
+ if (formatOptions.name && decodedData.name) {
314
+ newList += `${formatOptions.nameTitle}${decodedData.name}\n`;
315
+ }
316
+ const buildCardLine = (card) => {
317
+ let line = `${card.qty} ${card.name}`;
318
+ if (formatOptions.sets && card.set) {
319
+ line += ` (${card.set})`;
320
+ }
321
+ if (formatOptions.collectorNumber && card.number) {
322
+ line += ` ${card.number}`;
323
+ }
324
+ if (formatOptions.foil && card.foil) {
325
+ line += ` *${card.foil}*`;
326
+ }
327
+ if (formatOptions.cat && card.category) {
328
+ line += ` [${card.category}]`;
329
+ }
330
+ if (formatOptions.tags && card.tags) {
331
+ line += ` ^${card.tags}^`;
332
+ }
333
+ return line;
334
+ };
335
+ const renderSection = (section, title) => {
336
+ if (section && section.length > 0) {
337
+ newList += title;
338
+ section.forEach(card => {
339
+ newList += buildCardLine(card) + "\n";
340
+ });
341
+ }
342
+ };
343
+ formatOptions.order.forEach((sectionKey) => {
344
+ if (decodedData[sectionKey]) {
345
+ const title = formatOptions[`${sectionKey}Title`] || "";
346
+ renderSection(decodedData[sectionKey], title);
347
+ }
348
+ });
349
+ // trim the final newList and set it to the output
350
+ newList = newList.trim();
351
+ hxdec.ready();
352
+ hxdec.decoded(newList);
353
+ };
354
+ return;
355
+ },
356
+ init: () => {
357
+ // fetch all sets from scryfall and prepare them for initial firebase upload
358
+ if (typeof hxdec.setData !== "undefined") {
359
+ console.log("Set data already processed, skipping fetch");
360
+ return;
361
+ }
362
+ hxdec.loading("Fetching set data from scryfall");
363
+ fetch("https://api.scryfall.com/sets")
364
+ .then((resp) => resp.json())
365
+ .then((resp) => {
366
+ if (typeof resp.data !== "undefined" && resp.data.length > 0) {
367
+ const sets = [];
368
+ const initSetsData = resp.data;
369
+ // sort initSetsData by release date ascending
370
+ initSetsData.sort((a, b) => {
371
+ const dateA = new Date(a.released_at).getTime();
372
+ const dateB = new Date(b.released_at).getTime();
373
+ return dateA - dateB;
374
+ });
375
+ let setCounter = 1;
376
+ initSetsData.forEach((set) => {
377
+ if (set.set_type !== "token") {
378
+ const hxcode = Number(setCounter).toString(16).padStart(3, "0");
379
+ const newSet = {
380
+ date: new Date(set.released_at).getTime(),
381
+ code: set.code,
382
+ hxcode
383
+ };
384
+ sets.push(newSet);
385
+ setCounter++;
386
+ }
387
+ });
388
+ if (sets.length > 0) {
389
+ hxdec.setData = sets;
390
+ hxdec.ready();
391
+ }
392
+ else {
393
+ hxdec.error("No set data found from scryfall");
394
+ console.log("No set data found from scryfall", resp);
395
+ }
396
+ return;
397
+ }
398
+ hxdec.error("No set data found from scryfall");
399
+ console.log("No set data found from scryfall", resp);
400
+ return;
401
+ })
402
+ .catch((err) => {
403
+ hxdec.error("Error fetching set data from scryfall");
404
+ console.error("Error fetching set data from scryfall", err);
405
+ return;
406
+ });
407
+ },
408
+ getCardData: async (cards, properties, callback) => {
409
+ // fetch all cards from scryfall
410
+ const batchSize = 75; // scryfall allows up to 75 identifiers per request
411
+ const delay = 500; // delay in ms between requests to avoid hitting rate limits
412
+ const cardInfos = [];
413
+ //delay each request by the specified delay to avoid hitting rate limits
414
+ for (let i = 0; i < cards.length; i += batchSize) {
415
+ const chunk = cards.slice(i, i + batchSize);
416
+ const identifiers = chunk.map(card => {
417
+ if (card.set && card.number) {
418
+ return {
419
+ set: card.set,
420
+ collector_number: String(card.number)
421
+ };
422
+ }
423
+ else if (card.name) {
424
+ return {
425
+ name: card.name
426
+ };
427
+ }
428
+ else {
429
+ return null;
430
+ }
431
+ }).filter(id => id !== null);
432
+ if (identifiers.length === 0) {
433
+ continue;
434
+ }
435
+ fetch("https://api.scryfall.com/cards/collection", {
436
+ method: "POST",
437
+ headers: {
438
+ "Content-Type": "application/json"
439
+ },
440
+ body: JSON.stringify({ identifiers })
441
+ })
442
+ .then(resp => resp.json())
443
+ .then(resp => {
444
+ if (resp.data && resp.data.length > 0) {
445
+ cardInfos.push(...resp.data);
446
+ if (cardInfos.length >= cards.length) {
447
+ // all card info has been fetched, add the card names to the cards in the decoded data
448
+ cards.forEach((card, index) => {
449
+ let cardInfo = {};
450
+ if (typeof card.name !== "undefined" && (typeof card.set === "undefined" || typeof card.number === "undefined")) {
451
+ cardInfo = cardInfos.find(info => info.name.toLowerCase() === card.name.toLowerCase()) || {};
452
+ }
453
+ else if ((typeof card.set !== "undefined" && typeof card.number !== "undefined") && typeof card.name === "undefined") {
454
+ cardInfo = cardInfos.find(info => info.set.toLowerCase() === card.set.toLowerCase() && info.collector_number === String(card.number)) || {};
455
+ }
456
+ if (cardInfo && Object.keys(cardInfo).length !== 0) {
457
+ properties.forEach(prop => {
458
+ if (typeof cardInfo[prop] !== "undefined") {
459
+ cards[index][prop] = cardInfo[prop];
460
+ }
461
+ });
462
+ }
463
+ else {
464
+ console.log(`No card info found for card: ${card.name} with set: ${card.set} and number: ${card.number}`);
465
+ }
466
+ // console.log("card", card, cardInfo);
467
+ });
468
+ // if all batches have been processed, call the callback with the updated cards
469
+ if (cardInfos.length >= cards.length) {
470
+ callback(cards);
471
+ }
472
+ }
473
+ // console.log("Fetched card info from scryfall:", resp.data);
474
+ }
475
+ else {
476
+ console.log("No card info found from scryfall for identifiers:", identifiers, resp);
477
+ }
478
+ })
479
+ .catch(err => {
480
+ console.error("Error fetching card info from scryfall for identifiers:", identifiers, err);
481
+ });
482
+ // wait 500ms before next request
483
+ await new Promise(resolve => setTimeout(resolve, delay));
484
+ }
485
+ return;
486
+ },
487
+ digestCard: (line) => {
488
+ var _a;
489
+ const splitIndex = line.indexOf(" ");
490
+ let name = line.substring(splitIndex + 1).trim();
491
+ const newCard = {
492
+ name,
493
+ qty: Number(line.substring(0, splitIndex).trim().replace("x", "")),
494
+ set: "",
495
+ foil: "",
496
+ cat: "",
497
+ tags: "",
498
+ collectorNumber: "",
499
+ hxcode: "",
500
+ hxnumber: "",
501
+ };
502
+ const trimValue = (property, start, end) => {
503
+ const regex = new RegExp(`\\${start}([^\\${end}]*)\\${end}`);
504
+ const match = name.match(regex);
505
+ let replaceSymbol = "";
506
+ if (start === "(")
507
+ replaceSymbol = `::::`;
508
+ // get the matched value without the start and end symbols and assign it to newCard with the property name as the key
509
+ if (match && match[1]) {
510
+ newCard[property] = match[1].trim();
511
+ name = name.replace(regex, replaceSymbol);
512
+ // console.log('newCard:', newCard, match[1]);
513
+ }
514
+ };
515
+ trimValue("set", "(", ")");
516
+ trimValue("foil", "*", "*");
517
+ trimValue("cat", "[", "]");
518
+ trimValue("tags", "^", "^");
519
+ const nameSplit = name.split("::::");
520
+ name = nameSplit[0].trim();
521
+ if (nameSplit.length > 1) {
522
+ newCard.collectorNumber = nameSplit[1].trim();
523
+ }
524
+ if (typeof newCard.set !== "undefined") {
525
+ newCard.hxcode = ((_a = hxdec.setData.find(s => s.code.toLowerCase() === newCard.set.toLowerCase())) === null || _a === void 0 ? void 0 : _a.hxcode) || null;
526
+ }
527
+ if (typeof newCard.collectorNumber !== "undefined") {
528
+ // if collector number is just a number, convert to hex and pad to 3 digits
529
+ if (/^\d+$/.test(newCard.collectorNumber)) {
530
+ const hxnumber = Number(newCard.collectorNumber).toString(16);
531
+ newCard.hxnumber = hxnumber;
532
+ }
533
+ else {
534
+ newCard.hxnumber = `~${newCard.collectorNumber}~`;
535
+ }
536
+ }
537
+ return newCard;
538
+ },
539
+ encoded: (list) => {
540
+ console.log("hxdec.encoded: ", list);
541
+ },
542
+ decoded: (list) => {
543
+ console.log("hxdec.decoded: ", list);
544
+ },
545
+ ready: () => {
546
+ console.log("hxdec.ready: ready to encode and decode");
547
+ },
548
+ loading: (status) => {
549
+ console.log("hxdec.loading: ", status);
550
+ },
551
+ warning: (message) => {
552
+ console.log("HXDEC Warning:", message);
553
+ },
554
+ error: (message) => {
555
+ console.log("HXDEC Error:", message);
556
+ }
557
+ };
558
+ exports.default = hxdec;
559
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "hxdec",
3
+ "version": "1.0.0",
4
+ "description": "A Hexidecimal Magic the Gathering Decklist format.",
5
+ "main": "./lib/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "lint": "eslint --ext .js,.ts .",
9
+ "build": "tsc",
10
+ "build:watch": "tsc --watch"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "devDependencies": {
16
+ "@eslint/js": "^10.0.1",
17
+ "eslint": "^10.5.0",
18
+ "globals": "^17.6.0",
19
+ "typescript": "^6.0.3",
20
+ "typescript-eslint": "^8.61.0"
21
+ }
22
+ }
package/src/index.ts ADDED
@@ -0,0 +1,589 @@
1
+
2
+ type Obj = {
3
+ [key: string]: any;
4
+ }
5
+
6
+ const hxdec = {
7
+ setData: [] as Obj[], // to be populated with set data from scryfall on init
8
+ encode: (list: string, foil = false, tags = false, cat = false) => {
9
+ if (hxdec.setData.length === 0) {
10
+ hxdec.error("Set data not ready yet, cannot encode");
11
+ return;
12
+ }
13
+ const digestList = () => {
14
+ if (list.length === 0) {
15
+ hxdec.error("No deck list provided");
16
+ return [];
17
+ }
18
+ const sections = list.split("\n\n");
19
+ const listData = {
20
+ name: "",
21
+ targetSection: "",
22
+ commander: [] as Obj[],
23
+ mainboard: [] as Obj[],
24
+ sideboard: [] as Obj[],
25
+ maybeboard: [] as Obj[],
26
+ companion: [] as Obj[],
27
+ prefetch: [] as Obj[], // cards that need to be fetched from scryfall to get set and collector number info for encoding
28
+ } as Obj;
29
+ let targetSection = "mainboard"; // default section target
30
+ sections.forEach((section) => {
31
+ const sec = section.split("\n");
32
+ sec.forEach((line, index) => {
33
+ line = line.trim();
34
+
35
+ // if line doesnt start with a number followed by " " or "x ", It is not a card line. Check to see if its a section title and then, assign the targetSection based on the title. If the title is "About" and it is the first line of the first section, check to see if the second line starts with "Name " and if so, capture the deck name from that line and assign it to listData.name. If the line is not a card line or a section header, skip it.
36
+ if (!/^\d+x?\s/.test(line)) {
37
+ const titleLine = line.toLowerCase().replace(":", "");
38
+ console.log(`Processing section title: ${titleLine}`);
39
+ if (titleLine === "commander" || titleLine === "companion" || titleLine === "sideboard" || titleLine === "maybeboard" || titleLine === "mainboard") {
40
+ targetSection = titleLine;
41
+ }
42
+ if (titleLine === "deck") {
43
+ targetSection = "mainboard";
44
+ }
45
+ if (titleLine === "about" && index === 0 && /^Name\s/.test(sec[1])) {
46
+ listData.name = sec[1].split("Name")[1].trim();
47
+ }
48
+ return;
49
+ } else if (sec.length < 3 && index === 0 && listData.commander.length === 0) {
50
+ // 1-2 separate lines with no section headerde notes commanders for some formats
51
+ targetSection = "commander";
52
+ }
53
+ const newCard = hxdec.digestCard(line);
54
+ if (typeof newCard.hxnumber === "undefined" || typeof newCard.hxcode === "undefined") {
55
+ listData.prefetch = listData.prefetch || [];
56
+ listData.prefetch.push({ ...newCard, targetSection });
57
+ return;
58
+ } else if (typeof newCard.cat !== "undefined" && newCard.cat.includes("Commander")) {
59
+ listData.commander = listData.commander || [];
60
+ listData.commander.push({ ...newCard, targetSection });
61
+ } else {
62
+ listData[targetSection] = listData[targetSection] || [];
63
+ listData[targetSection].push({ ...newCard, targetSection });
64
+ }
65
+ });
66
+
67
+ });
68
+ if (typeof listData.prefetch !== "undefined") {
69
+ hxdec.getCardData(listData.prefetch, ["set", "collector_number"], (cards: Obj[]) => {
70
+ cards.forEach((card: Obj) => {
71
+ // add each card to its target section and update the hxcode and hxnumber based on the set and collector number
72
+ if (typeof card.set !== "undefined" && typeof card.collector_number !== "undefined" && typeof hxdec.setData !== "undefined") {
73
+ card.hxcode = hxdec.setData.find(s => s.code.toLowerCase() === card.set.toLowerCase())?.hxcode || null;
74
+ if (card.hxcode) {
75
+ const collectorNumber = card.collector_number;
76
+ if (/^\d+$/.test(collectorNumber)) {
77
+ card.hxnumber = Number(collectorNumber).toString(16);
78
+ } else {
79
+ card.hxnumber = `~${collectorNumber}~`;
80
+ }
81
+ } else {
82
+ hxdec.warning(`No set data found for set: ${card.set} on card: ${card.name}`);
83
+ }
84
+ listData[card.targetSection] = listData[card.targetSection] || [];
85
+ listData[card.targetSection].push(card);
86
+ }
87
+ });
88
+ console.log("Decoded list data and fetched missing card info from scryfall:", listData);
89
+ buildHxdec(listData);
90
+ });
91
+ } else {
92
+ console.log("Decoded list data:", listData);
93
+ buildHxdec(listData);
94
+ }
95
+ return;
96
+ }
97
+ const buildHxdec = (listData: Obj) => {
98
+ // build the hxdec string based on the listData and setData
99
+ const outputCards = (rawCards: Obj[]) => {
100
+ let newCards = ""
101
+ rawCards.forEach(card => {
102
+ const qtyChars = ["u", "v", "w", "x", "y", "z"];
103
+ let cardString = "";
104
+ if (card.qty >= 1 && card.qty <= 4) {
105
+ cardString += qtyChars[card.qty - 1];
106
+ } else if (card.qty > 4 && card.qty < 16) {
107
+ const qtyHex = Number(card.qty).toString(16);
108
+ cardString += `y${qtyHex}`;
109
+ } else if (card.qty >= 16 && card.qty < 256) {
110
+ const qtyHex = Number(card.qty).toString(16);
111
+ cardString += `z${qtyHex}`;
112
+ } else {
113
+ cardString += "zff";
114
+ }
115
+ cardString += card.hxcode;
116
+ cardString += card.hxnumber;
117
+ if (foil && typeof card.foil !== "undefined") {
118
+ cardString += `*${card.foil}*`;
119
+ }
120
+ if (tags && typeof card.tags !== "undefined") {
121
+ cardString += `^${card.tags}^`;
122
+ }
123
+ if (cat && typeof card.cat !== "undefined") {
124
+ cardString += `[${card.cat}]`;
125
+ }
126
+ newCards += cardString;
127
+ });
128
+ return newCards;
129
+ }
130
+ if (typeof listData.mainboard === "undefined" || listData.mainboard.length === 0) {
131
+ hxdec.error("No mainboard cards found, cannot build HXDEC");
132
+ return;
133
+ }
134
+ // If this is a type of format that lists a commander as the first card in the mainboard section without a section header, move it to the commander section and remove it from the mainboard section
135
+ if (typeof listData.mainboard !== "undefined" && typeof listData.commander === "undefined") {
136
+ // count the number of cards in the mainboard
137
+ let mainboardCount = 0;
138
+ listData.mainboard.forEach((mainboardCard: Obj) => {
139
+ mainboardCount += mainboardCard.qty;
140
+ });
141
+ if (mainboardCount > 98) {
142
+ // Mainboard has more than 98 cards and no commander specified, treating first card as commander
143
+ listData.commander = [listData.mainboard[0]];
144
+ listData.mainboard = listData.mainboard.slice(1);
145
+ }
146
+ }
147
+ let deck = `h${outputCards(listData.mainboard)}`;
148
+ if (typeof listData.commander !== "undefined") {
149
+ deck += `k${outputCards(listData.commander)}`;
150
+ }
151
+ if (typeof listData.sideboard !== "undefined") {
152
+ deck += `s${outputCards(listData.sideboard)}`;
153
+ }
154
+ if (typeof listData.maybeboard !== "undefined") {
155
+ deck += `m${outputCards(listData.maybeboard)}`;
156
+ }
157
+ if (typeof listData.companion !== "undefined") {
158
+ deck += `p${outputCards(listData.companion)}`;
159
+ }
160
+ if (typeof listData.name !== "undefined") {
161
+ deck += `+${encodeURIComponent(listData.name)}+`;
162
+ }
163
+ hxdec.ready();
164
+ hxdec.encoded(deck);
165
+ return;
166
+
167
+ }
168
+
169
+ hxdec.loading("Processing deck list");
170
+ digestList();
171
+ },
172
+ decode: (list: string, format = "Archideckt") => {
173
+ if (hxdec.setData.length === 0) {
174
+ console.log("Set data not ready yet, cannot decode");
175
+ return;
176
+ }
177
+ hxdec.loading("Decoding HXDEC string");
178
+ // Decoding logic to be implemented
179
+ // remove values wrapped and store them in an array, replace them in the deck string with their index in the array wrapped in thier original wrap symbol so they can be reinserted after splitting the string into sections
180
+ const captureWrappedValues = (wrap1: string, wrap2: string, regex: RegExp) => {
181
+ const capturedValues = [] as string[];
182
+ const matches = list.match(regex) || [];
183
+ // for each match push the value without the wrapSymbol to capturedValues and replace the match in list with a placeholder of the index wrapped
184
+ matches.forEach((match, index) => {
185
+ // console.log(`Captured value for ${wrap1}${wrap2}:`, match);
186
+ capturedValues.push(match.replace(wrap1, "").replace(wrap2, ""));
187
+ list = list.replace(match, `${wrap1}${index}${wrap2}`);
188
+ });
189
+ return capturedValues;
190
+ };
191
+ const categoryValues = captureWrappedValues("[", "]", /\[(.*?)\]/g);
192
+ const textSets = captureWrappedValues("~", "~", /~(.*?)~/g);
193
+ const foilValues = captureWrappedValues("*", "*", /\*(.*?)\*/g);
194
+ const tagValues = captureWrappedValues("^", "^", /\^(.*?)\^/g);
195
+ const nameValue = captureWrappedValues("+", "", /\+(.*)\+/g);
196
+ const decodedData = { name: "", prefetch: [] } as Obj;
197
+ if (nameValue.length > 0) {
198
+ decodedData.name = decodeURIComponent(nameValue[0]);
199
+ }
200
+ list = list.replace(/\+(.*)\+/g, ""); // remove the name from the list string so it doesn't interfere with section splitting
201
+
202
+ const sections = list.split(/(?=[hksmp])/);
203
+ sections.forEach(section => {
204
+ const type = section[0];
205
+ const data = section.substring(1);
206
+ const cardCodes = data.split(/(?=[uvwxyz])/);
207
+ const targetSection = type === "h" ? "mainboard" : type === "k" ? "commander" : type === "s" ? "sideboard" : type === "m" ? "maybeboard" : type === "p" ? "companion" : null;
208
+ if (targetSection) {
209
+ const cards = [] as Obj[];
210
+ cardCodes.forEach(code => {
211
+ // get the first character and remove it
212
+ const qtyChar = code[0];
213
+ code = code.substring(1);
214
+ let qty = ["u", "v", "w", "x", "y", "z"].indexOf(qtyChar) + 1;
215
+ if (qtyChar === "y") {
216
+ qty = parseInt(code.substring(0, 1), 16);
217
+ code = code.substring(1);
218
+ }
219
+ if (qtyChar === "z") {
220
+ qty = parseInt(code.substring(0, 2), 16);
221
+ code = code.substring(2);
222
+ }
223
+ const setHx = code.substring(0, 3);
224
+ // if code contains * , extract the foil value and remove it from the code
225
+ let foil = null;
226
+ if (code.includes("*")) {
227
+ const foilMatch = code.match(/\*(.*?)\*/);
228
+ if (foilMatch) {
229
+ foil = foilValues[parseInt(foilMatch[1])];
230
+ code = code.replace(foilMatch[0], "");
231
+ }
232
+ }
233
+ // if code contains ^, extract the tag value and remove it from the code
234
+ let tags = null;
235
+ if (code.includes("^")) {
236
+ const tagMatch = code.match(/\^(.*?)\^/);
237
+ if (tagMatch) {
238
+ tags = tagValues[parseInt(tagMatch[1])];
239
+ code = code.replace(tagMatch[0], "");
240
+ }
241
+ }
242
+ // if code contains [ ], extract the category value and remove it from the code
243
+ let category = null;
244
+ if (code.includes("[")) {
245
+ const categoryMatch = code.match(/\[(.*?)\]/);
246
+ if (categoryMatch) {
247
+ category = categoryValues[parseInt(categoryMatch[1])];
248
+ code = code.replace(categoryMatch[0], "");
249
+ }
250
+ }
251
+
252
+ const numberHx = code.substring(3);
253
+
254
+ let number: string = Number(parseInt(numberHx, 16)).toString();
255
+ if (isNaN(parseInt(number)) && numberHx.includes("~")) {
256
+ number = textSets[parseInt(numberHx.replace("~", ""))];
257
+ } else if (isNaN(parseInt(number))) {
258
+ console.log(`Invalid collector number hex: ${numberHx} for code: ${code}`);
259
+ }
260
+ let set = null;
261
+
262
+ // find set in set data
263
+ const setInfo = hxdec.setData.find(s => s.hxcode === setHx);
264
+ if (setInfo) {
265
+ set = setInfo.code;
266
+ }
267
+
268
+ const newCard = { code, qty, set, number, foil, tags, category, targetSection } as Obj;
269
+
270
+ cards.push(newCard);
271
+ });
272
+ decodedData.prefetch.push(...cards);
273
+ return;
274
+ }
275
+ console.log(`Unknown section type: ${type} in section: ${section}`);
276
+ return;
277
+ });
278
+ hxdec.getCardData(decodedData.prefetch, ["name"], (cards: Obj[]) => {
279
+ cards.forEach(card => {
280
+ decodedData[card.targetSection] = decodedData[card.targetSection] || [];
281
+ decodedData[card.targetSection].push(card);
282
+ });
283
+
284
+ buildList(decodedData);
285
+ console.log("Decoded data with card names:", decodedData);
286
+ });
287
+
288
+ const buildList = (decodedData: Obj) => {
289
+ let formatOptions = {
290
+ mainboardTitle: "",
291
+ commanderTitle: "",
292
+ sideboardTitle: "",
293
+ maybeboardTitle: "",
294
+ companionTitle: "",
295
+ nameTitle: "",
296
+ name: false,
297
+ sets: false,
298
+ collectorNumber: false,
299
+ foil: false,
300
+ cat: false,
301
+ tags: false,
302
+ order: ["commander", "mainboard", "companion", "sideboard", "maybeboard"]
303
+ } as Obj;
304
+ if (format === "Archideckt") {
305
+ formatOptions = {
306
+ ...formatOptions,
307
+ mainboardTitle: "\nMainboard\n",
308
+ commanderTitle: "\nCommander\n",
309
+ sideboardTitle: "\nSideboard\n",
310
+ maybeboardTitle: "\nMaybeboard\n",
311
+ companionTitle: "\nCompanion\n",
312
+ sets: true,
313
+ collectorNumber: true,
314
+ foil: true,
315
+ cat: true,
316
+ tags: true,
317
+ }
318
+ }
319
+ if (format === "MTGO") {
320
+ formatOptions = {
321
+ ...formatOptions,
322
+ sideboardTitle: "\nSIDEBOARD:\n",
323
+ commanderTitle: "\n",
324
+ order: ["mainboard", "companion", "sideboard", "commander", "maybeboard"]
325
+ }
326
+ }
327
+ if (format === "Arena") {
328
+ formatOptions = {
329
+ ...formatOptions,
330
+ mainboardTitle: "\nDeck\n",
331
+ commanderTitle: "\nCommander\n",
332
+ sideboardTitle: "\nSideboard\n",
333
+ nameTitle: "About\nName ",
334
+ name: true,
335
+ }
336
+ }
337
+ if (format === "Moxfield") {
338
+ formatOptions = {
339
+ ...formatOptions,
340
+ sideboardTitle: "\nSIDEBOARD:\n",
341
+ sets: true,
342
+ collectorNumber: true,
343
+ foil: true,
344
+ }
345
+ }
346
+
347
+ let newList = "";
348
+ if (formatOptions.name && decodedData.name) {
349
+ newList += `${formatOptions.nameTitle}${decodedData.name}\n`;
350
+ }
351
+ const buildCardLine = (card: Obj) => {
352
+ let line = `${card.qty} ${card.name}`;
353
+ if (formatOptions.sets && card.set) {
354
+ line += ` (${card.set})`;
355
+ }
356
+ if (formatOptions.collectorNumber && card.number) {
357
+ line += ` ${card.number}`;
358
+ }
359
+ if (formatOptions.foil && card.foil) {
360
+ line += ` *${card.foil}*`;
361
+ }
362
+ if (formatOptions.cat && card.category) {
363
+ line += ` [${card.category}]`;
364
+ }
365
+ if (formatOptions.tags && card.tags) {
366
+ line += ` ^${card.tags}^`;
367
+ }
368
+ return line;
369
+ }
370
+ const renderSection = (section: Obj[], title: string) => {
371
+ if (section && section.length > 0) {
372
+ newList += title;
373
+ section.forEach(card => {
374
+ newList += buildCardLine(card) + "\n";
375
+ });
376
+ }
377
+ }
378
+ formatOptions.order.forEach((sectionKey: string) => {
379
+ if (decodedData[sectionKey]) {
380
+ const title = formatOptions[`${sectionKey}Title`] || "";
381
+ renderSection(decodedData[sectionKey], title);
382
+ }
383
+ });
384
+
385
+ // trim the final newList and set it to the output
386
+ newList = newList.trim();
387
+ hxdec.ready();
388
+ hxdec.decoded(newList);
389
+ }
390
+
391
+ return;
392
+ },
393
+ init: () => {
394
+ // fetch all sets from scryfall and prepare them for initial firebase upload
395
+ if (typeof hxdec.setData !== "undefined") {
396
+ console.log("Set data already processed, skipping fetch");
397
+ return;
398
+ }
399
+ hxdec.loading("Fetching set data from scryfall");
400
+ fetch("https://api.scryfall.com/sets")
401
+ .then((resp) => resp.json())
402
+ .then((resp) => {
403
+ if (typeof resp.data !== "undefined" && resp.data.length > 0) {
404
+ const sets = [] as Obj[];
405
+ const initSetsData = resp.data;
406
+ // sort initSetsData by release date ascending
407
+ initSetsData.sort((a: Obj, b: Obj) => {
408
+ const dateA = new Date(a.released_at).getTime();
409
+ const dateB = new Date(b.released_at).getTime();
410
+ return dateA - dateB;
411
+ });
412
+ let setCounter = 1;
413
+ initSetsData.forEach((set: Obj) => {
414
+ if (set.set_type !== "token") {
415
+ const hxcode = Number(setCounter).toString(16).padStart(3, "0");
416
+ const newSet = {
417
+ date: new Date(set.released_at).getTime(),
418
+ code: set.code,
419
+ hxcode
420
+ };
421
+ sets.push(newSet);
422
+ setCounter++;
423
+ }
424
+ });
425
+ if (sets.length > 0) {
426
+ hxdec.setData = sets;
427
+ hxdec.ready();
428
+ } else {
429
+ hxdec.error("No set data found from scryfall");
430
+ console.log("No set data found from scryfall", resp);
431
+ }
432
+ return;
433
+ }
434
+ hxdec.error("No set data found from scryfall");
435
+ console.log("No set data found from scryfall", resp);
436
+ return;
437
+ })
438
+ .catch((err) => {
439
+ hxdec.error("Error fetching set data from scryfall");
440
+ console.error("Error fetching set data from scryfall", err);
441
+ return;
442
+ });
443
+ },
444
+ getCardData: async (cards: Obj[], properties: string[], callback: (cardInfos: Obj[]) => void) => {
445
+ // fetch all cards from scryfall
446
+ const batchSize = 75; // scryfall allows up to 75 identifiers per request
447
+ const delay = 500; // delay in ms between requests to avoid hitting rate limits
448
+ const cardInfos = [] as Obj[];
449
+ //delay each request by the specified delay to avoid hitting rate limits
450
+ for (let i = 0; i < cards.length; i += batchSize) {
451
+ const chunk = cards.slice(i, i + batchSize);
452
+ const identifiers = chunk.map(card => {
453
+ if (card.set && card.number) {
454
+ return {
455
+ set: card.set,
456
+ collector_number: String(card.number)
457
+ }
458
+ } else if (card.name) {
459
+ return {
460
+ name: card.name
461
+ };
462
+ } else {
463
+ return null;
464
+ }
465
+ }).filter(id => id !== null);
466
+ if (identifiers.length === 0) {
467
+ continue;
468
+ }
469
+ fetch("https://api.scryfall.com/cards/collection", {
470
+ method: "POST",
471
+ headers: {
472
+ "Content-Type": "application/json"
473
+ },
474
+ body: JSON.stringify({ identifiers })
475
+ })
476
+ .then(resp => resp.json())
477
+ .then(resp => {
478
+ if (resp.data && resp.data.length > 0) {
479
+ cardInfos.push(...resp.data);
480
+ if (cardInfos.length >= cards.length) {
481
+ // all card info has been fetched, add the card names to the cards in the decoded data
482
+ cards.forEach((card, index) => {
483
+ let cardInfo = {} as Obj;
484
+ if (typeof card.name !== "undefined" && (typeof card.set === "undefined" || typeof card.number === "undefined")) {
485
+ cardInfo = cardInfos.find(info => info.name.toLowerCase() === card.name.toLowerCase()) || {} as Obj;
486
+ } else if ((typeof card.set !== "undefined" && typeof card.number !== "undefined") && typeof card.name === "undefined") {
487
+ cardInfo = cardInfos.find(info => info.set.toLowerCase() === card.set.toLowerCase() && info.collector_number === String(card.number)) || {} as Obj;
488
+ }
489
+ if (cardInfo && Object.keys(cardInfo).length !== 0) {
490
+ properties.forEach(prop => {
491
+ if (typeof cardInfo[prop] !== "undefined") {
492
+ cards[index][prop] = cardInfo[prop];
493
+ }
494
+ });
495
+ } else {
496
+ console.log(`No card info found for card: ${card.name} with set: ${card.set} and number: ${card.number}`);
497
+ }
498
+
499
+ // console.log("card", card, cardInfo);
500
+ });
501
+ // if all batches have been processed, call the callback with the updated cards
502
+ if (cardInfos.length >= cards.length) {
503
+ callback(cards);
504
+ }
505
+
506
+ }
507
+ // console.log("Fetched card info from scryfall:", resp.data);
508
+ } else {
509
+ console.log("No card info found from scryfall for identifiers:", identifiers, resp);
510
+ }
511
+ })
512
+ .catch(err => {
513
+ console.error("Error fetching card info from scryfall for identifiers:", identifiers, err);
514
+ });
515
+ // wait 500ms before next request
516
+ await new Promise(resolve => setTimeout(resolve, delay));
517
+ }
518
+ return;
519
+ },
520
+ digestCard: (line: string) => { //split at first space
521
+ const splitIndex = line.indexOf(" ");
522
+ let name = line.substring(splitIndex + 1).trim();
523
+ const newCard = {
524
+ name,
525
+ qty: Number(line.substring(0, splitIndex).trim().replace("x", "")),
526
+ set: "",
527
+ foil: "",
528
+ cat: "",
529
+ tags: "",
530
+ collectorNumber: "",
531
+ hxcode: "",
532
+ hxnumber: "",
533
+ } as Obj;
534
+ const trimValue = (property: string, start: string, end: string) => {
535
+ const regex = new RegExp(`\\${start}([^\\${end}]*)\\${end}`);
536
+ const match = name.match(regex);
537
+ let replaceSymbol = "";
538
+ if (start === "(") replaceSymbol = `::::`;
539
+ // get the matched value without the start and end symbols and assign it to newCard with the property name as the key
540
+ if (match && match[1]) {
541
+ newCard[property] = match[1].trim();
542
+ name = name.replace(regex, replaceSymbol);
543
+ // console.log('newCard:', newCard, match[1]);
544
+ }
545
+ }
546
+ trimValue("set", "(", ")");
547
+ trimValue("foil", "*", "*");
548
+ trimValue("cat", "[", "]");
549
+ trimValue("tags", "^", "^");
550
+ const nameSplit = name.split("::::");
551
+ name = nameSplit[0].trim();
552
+ if (nameSplit.length > 1) {
553
+ newCard.collectorNumber = nameSplit[1].trim();
554
+ }
555
+ if (typeof newCard.set !== "undefined") {
556
+ newCard.hxcode = hxdec.setData.find(s => s.code.toLowerCase() === newCard.set.toLowerCase())?.hxcode || null;
557
+ }
558
+ if (typeof newCard.collectorNumber !== "undefined") {
559
+ // if collector number is just a number, convert to hex and pad to 3 digits
560
+ if (/^\d+$/.test(newCard.collectorNumber)) {
561
+ const hxnumber = Number(newCard.collectorNumber).toString(16);
562
+ newCard.hxnumber = hxnumber;
563
+ } else {
564
+ newCard.hxnumber = `~${newCard.collectorNumber}~`;
565
+ }
566
+ }
567
+ return newCard;
568
+ },
569
+ encoded: (list: string) => {
570
+ console.log("hxdec.encoded: ", list);
571
+ },
572
+ decoded: (list: string) => {
573
+ console.log("hxdec.decoded: ", list);
574
+ },
575
+ ready: () => {
576
+ console.log("hxdec.ready: ready to encode and decode");
577
+ },
578
+ loading: (status: string) => {
579
+ console.log("hxdec.loading: ", status);
580
+ },
581
+ warning: (message: string) => {
582
+ console.log("HXDEC Warning:", message);
583
+ },
584
+ error: (message: string) => {
585
+ console.log("HXDEC Error:", message);
586
+ }
587
+ };
588
+
589
+ export default hxdec;
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "noImplicitReturns": true,
5
+ "noUnusedLocals": true,
6
+ "outDir": "lib",
7
+ "rootDir": "./src",
8
+ "sourceMap": true,
9
+ "strict": true,
10
+ "target": "es2017"
11
+ },
12
+ "compileOnSave": true,
13
+ "include": [
14
+ "src"
15
+ ]
16
+ }