@flesh-and-blood/search 3.9.13 → 4.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.
@@ -0,0 +1,695 @@
1
+ import {
2
+ Foiling,
3
+ Meta,
4
+ Rarity,
5
+ Release,
6
+ Treatment,
7
+ Type,
8
+ setIdentifierToSetMappings,
9
+ setToSetIdentifierMappings
10
+ } from "@flesh-and-blood/types";
11
+ import { getAbbreviation } from "./abbreviations";
12
+ import { getExcludedMetaFilters, getMetaFilters } from "./metaFilters";
13
+ import { multiWordShorthands, singleWordShorthands } from "./shorthands";
14
+ import { PUNCTUATION } from "./constants";
15
+ import { getCardByName, getRelatedCardsByName } from ".";
16
+ const availableModifiers = [">=", ">", "<=", "<"];
17
+ const availableExclusions = ["!", "-"];
18
+ const arcaneFilter = {
19
+ property: "arcane",
20
+ specialProperty: "specialArcane",
21
+ isNumber: true,
22
+ partialMatch: true
23
+ };
24
+ const artistFilter = {
25
+ property: "artists",
26
+ isArray: true,
27
+ partialMatch: true
28
+ };
29
+ const bannedFilter = {
30
+ property: "n/a",
31
+ isMeta: true
32
+ };
33
+ const bondFilter = {
34
+ property: "bonds",
35
+ isArray: true
36
+ };
37
+ const chainFilter = {
38
+ property: "n/a"
39
+ };
40
+ const classFilter = {
41
+ property: "classes",
42
+ isArray: true,
43
+ partialMatch: true
44
+ };
45
+ const costFilter = {
46
+ property: "cost",
47
+ specialProperty: "specialCost",
48
+ isNumber: true,
49
+ partialMatch: true
50
+ };
51
+ const defenseFilter = {
52
+ property: "defense",
53
+ specialProperty: "specialDefense",
54
+ isNumber: true
55
+ };
56
+ const flowFilter = {
57
+ property: "flows",
58
+ isArray: true
59
+ };
60
+ const foilFilter = {
61
+ nestedProperty: "foiling",
62
+ property: "printings",
63
+ isArray: true
64
+ };
65
+ const fuseFilter = {
66
+ property: "fusions",
67
+ isArray: true
68
+ };
69
+ const intellectFilter = {
70
+ property: "intellect",
71
+ isNumber: true
72
+ };
73
+ const keywordFilter = {
74
+ property: "keywords",
75
+ isArray: true
76
+ // partialMatch: true,
77
+ };
78
+ const legalFilter = {
79
+ property: "n/a",
80
+ isMeta: true
81
+ };
82
+ const lifeFilter = {
83
+ property: "life",
84
+ specialProperty: "specialLife",
85
+ isNumber: true
86
+ };
87
+ const metaFilter = {
88
+ property: "meta",
89
+ isArray: true
90
+ };
91
+ const nameFilter = {
92
+ property: "name",
93
+ isString: true,
94
+ partialMatch: true
95
+ };
96
+ const pitchFilter = {
97
+ property: "pitch",
98
+ isNumber: true
99
+ };
100
+ const powerFilter = {
101
+ property: "power",
102
+ specialProperty: "specialPower",
103
+ isNumber: true
104
+ };
105
+ const setIdentifiersFilter = {
106
+ property: "setIdentifiers",
107
+ isArray: true,
108
+ partialMatch: true
109
+ };
110
+ const rarityFilter = {
111
+ property: "n/a",
112
+ isMeta: true
113
+ };
114
+ const referencedByFilter = {
115
+ property: "n/a"
116
+ };
117
+ const referencesFilter = {
118
+ property: "n/a"
119
+ };
120
+ const setFilter = {
121
+ property: "sets",
122
+ isArray: true,
123
+ partialMatch: true
124
+ };
125
+ const shorthandsFilter = {
126
+ property: "shorthands",
127
+ isArray: true,
128
+ partialMatch: true
129
+ };
130
+ const specializationsFilter = {
131
+ property: "specializations",
132
+ isArray: true,
133
+ partialMatch: true
134
+ };
135
+ const subtypeFilter = {
136
+ property: "subtypes",
137
+ isArray: true
138
+ };
139
+ const typeFilter = {
140
+ property: "types",
141
+ isArray: true
142
+ };
143
+ const talentFilter = {
144
+ property: "talents",
145
+ isArray: true
146
+ };
147
+ const textFilter = {
148
+ property: "functionalText",
149
+ isString: true,
150
+ partialMatch: true
151
+ };
152
+ const traitFilter = {
153
+ property: "traits",
154
+ isArray: true,
155
+ partialMatch: true
156
+ };
157
+ const typeTextFilter = {
158
+ property: "typeText",
159
+ isString: true,
160
+ partialMatch: true
161
+ };
162
+ const treatmentFilter = {
163
+ nestedProperty: "treatments",
164
+ property: "printings",
165
+ isArray: true,
166
+ isNestedPropertyArray: true
167
+ };
168
+ const filtersToCardPropertyMappings = {
169
+ arcane: arcaneFilter,
170
+ a: artistFilter,
171
+ artist: artistFilter,
172
+ art: artistFilter,
173
+ attack: powerFilter,
174
+ b: defenseFilter,
175
+ block: defenseFilter,
176
+ banned: bannedFilter,
177
+ bond: bondFilter,
178
+ bonds: bondFilter,
179
+ c: classFilter,
180
+ class: classFilter,
181
+ chain: chainFilter,
182
+ co: costFilter,
183
+ cost: costFilter,
184
+ color: pitchFilter,
185
+ d: defenseFilter,
186
+ def: defenseFilter,
187
+ defense: defenseFilter,
188
+ flow: flowFilter,
189
+ flows: flowFilter,
190
+ f: fuseFilter,
191
+ fusion: fuseFilter,
192
+ foil: foilFilter,
193
+ foiling: foilFilter,
194
+ i: intellectFilter,
195
+ intellect: intellectFilter,
196
+ is: metaFilter,
197
+ k: keywordFilter,
198
+ keyword: keywordFilter,
199
+ l: legalFilter,
200
+ legal: legalFilter,
201
+ hero: legalFilter,
202
+ li: lifeFilter,
203
+ life: lifeFilter,
204
+ meta: metaFilter,
205
+ n: nameFilter,
206
+ name: nameFilter,
207
+ p: pitchFilter,
208
+ pitch: pitchFilter,
209
+ pwr: powerFilter,
210
+ pow: powerFilter,
211
+ power: powerFilter,
212
+ print: setIdentifiersFilter,
213
+ r: rarityFilter,
214
+ rarity: rarityFilter,
215
+ referencedby: referencedByFilter,
216
+ references: referencesFilter,
217
+ rf: bannedFilter,
218
+ s: setFilter,
219
+ set: setFilter,
220
+ short: shorthandsFilter,
221
+ shorthand: shorthandsFilter,
222
+ shorthands: shorthandsFilter,
223
+ sp: specializationsFilter,
224
+ spec: specializationsFilter,
225
+ specialization: specializationsFilter,
226
+ specializations: specializationsFilter,
227
+ st: subtypeFilter,
228
+ subtype: subtypeFilter,
229
+ t: typeFilter,
230
+ type: typeFilter,
231
+ tal: talentFilter,
232
+ talent: talentFilter,
233
+ text: textFilter,
234
+ trait: traitFilter,
235
+ treat: treatmentFilter,
236
+ treatment: treatmentFilter,
237
+ var: treatmentFilter,
238
+ variation: treatmentFilter,
239
+ x: typeTextFilter
240
+ };
241
+ const punctuationOverrides = [
242
+ {
243
+ text: Release.ClassicBattlesRhinarDorinthea.toLowerCase(),
244
+ override: Release.ClassicBattlesRhinarDorinthea.toLowerCase().replaceAll(
245
+ PUNCTUATION,
246
+ ""
247
+ )
248
+ }
249
+ ];
250
+ const getSearchCriteria = (text) => {
251
+ const searchCriteria = [];
252
+ let rawSearchCriteria = text.replaceAll("\u201D", '"');
253
+ for (const { text: text2, override } of punctuationOverrides) {
254
+ if (rawSearchCriteria.includes(text2)) {
255
+ rawSearchCriteria = rawSearchCriteria.replace(text2, override);
256
+ }
257
+ }
258
+ const matchingAbbreviation = getAbbreviation(rawSearchCriteria)?.card;
259
+ if (matchingAbbreviation) {
260
+ searchCriteria.push(rawSearchCriteria);
261
+ } else {
262
+ const splitSearchCriteria = rawSearchCriteria.split(/[ ]+/);
263
+ let combinedSearchCriteria = "";
264
+ let combinedQuoteCount = 0;
265
+ for (const criteria of splitSearchCriteria) {
266
+ if (combinedQuoteCount === 2) {
267
+ searchCriteria.push(combinedSearchCriteria.trim().replaceAll('"', ""));
268
+ combinedSearchCriteria = "";
269
+ combinedQuoteCount = 0;
270
+ }
271
+ if (combinedQuoteCount < 2 && criteria.split('"').length === 2) {
272
+ combinedSearchCriteria += " " + criteria;
273
+ combinedQuoteCount++;
274
+ } else if (combinedQuoteCount === 0 && combinedSearchCriteria === "") {
275
+ searchCriteria.push(criteria);
276
+ } else if (combinedQuoteCount === 1) {
277
+ combinedSearchCriteria += " " + criteria;
278
+ }
279
+ }
280
+ if (combinedQuoteCount === 2) {
281
+ searchCriteria.push(combinedSearchCriteria.trim().replaceAll('"', ""));
282
+ combinedSearchCriteria = "";
283
+ combinedQuoteCount = 0;
284
+ }
285
+ }
286
+ return searchCriteria;
287
+ };
288
+ const getKeywordsAndAppliedFiltersFromText = (text, cards, additionalHeroes = [], additionalSets = []) => {
289
+ let expandedText = text.trim().toLowerCase();
290
+ for (const { expanded: filters, shorthands } of multiWordShorthands) {
291
+ for (const shorthand of shorthands) {
292
+ if (expandedText.includes(shorthand)) {
293
+ expandedText = expandedText.replace(shorthand, filters.join(" "));
294
+ break;
295
+ }
296
+ }
297
+ }
298
+ for (const [set, setIdentifiers] of Object.entries(
299
+ setToSetIdentifierMappings
300
+ )) {
301
+ if (expandedText.includes(set.toLowerCase())) {
302
+ expandedText = expandedText.replace(set.toLowerCase(), setIdentifiers[0]);
303
+ }
304
+ }
305
+ const rawSearchCriteria = getSearchCriteria(expandedText);
306
+ const searchCriteria = [];
307
+ for (const criteria of rawSearchCriteria) {
308
+ const expanded = singleWordShorthands.find(
309
+ ({ shorthands }) => shorthands.includes(criteria)
310
+ );
311
+ if (expanded && !expanded.isCardProperty) {
312
+ searchCriteria.push(...expanded.expanded);
313
+ } else {
314
+ searchCriteria.push(criteria);
315
+ }
316
+ }
317
+ let appliedFilters = [];
318
+ let artists = [];
319
+ let keywords = [];
320
+ let foilings = [];
321
+ let isExpansionSlot = false;
322
+ let prints = [];
323
+ let rarities = [];
324
+ let releases = [];
325
+ let treatments = [];
326
+ for (const criteria of searchCriteria) {
327
+ if (hasFilter(criteria)) {
328
+ const [unparsedFilterKey, unparsedFilterValue] = criteria.split(":");
329
+ let { modifier, values, isAnd, isOr } = getFilterValuesAndModifier(unparsedFilterValue);
330
+ let { filterKey, isExcluded, isOptional, isMeta } = getFilterKeyAndExcludedOrOptional(unparsedFilterKey);
331
+ if (isMeta) {
332
+ if (["rarity", "r"].includes(filterKey)) {
333
+ const rarityValues = getRarityValuesFromText(values);
334
+ if (!isExcluded) {
335
+ rarities = [...rarityValues];
336
+ }
337
+ values = rarityValues.map((s) => s.toLowerCase());
338
+ }
339
+ if (["legal", "l", "hero"].includes(filterKey)) {
340
+ }
341
+ appliedFilters.push(
342
+ ...getMetaFilters(
343
+ isExcluded,
344
+ isOptional,
345
+ filterKey,
346
+ values,
347
+ modifier,
348
+ additionalHeroes
349
+ )
350
+ );
351
+ } else {
352
+ if (["chain"].includes(filterKey)) {
353
+ const getName = (card) => card.name.toLowerCase().replaceAll(PUNCTUATION, "");
354
+ const relatedCardNames = values.map((name) => getCardByName(name, cards)).filter((card) => !!card).map(getName);
355
+ const names = new Set(relatedCardNames);
356
+ filterKey = "name";
357
+ isOr = true;
358
+ let limit = 20;
359
+ let counter = 0;
360
+ const addToSetAndRelated = (card) => {
361
+ if (!card.types.includes(Type.Hero)) {
362
+ const name = getName(card);
363
+ names.add(name);
364
+ if (!relatedCardNames.includes(name)) {
365
+ relatedCardNames.push(name);
366
+ }
367
+ }
368
+ };
369
+ for (const relatedCardName of relatedCardNames) {
370
+ if (counter > limit) {
371
+ break;
372
+ }
373
+ const { referencedBy, references } = getRelatedCardsByName(
374
+ relatedCardName,
375
+ cards
376
+ );
377
+ references.forEach(addToSetAndRelated);
378
+ if (counter === 0) {
379
+ referencedBy.forEach(addToSetAndRelated);
380
+ }
381
+ counter++;
382
+ }
383
+ values = Array.from(names);
384
+ } else if (["referencedby", "references"].includes(filterKey)) {
385
+ const originalKey = filterKey;
386
+ const relatedCardNames = [...values].filter((value) => !!value);
387
+ values = [];
388
+ filterKey = "name";
389
+ isOr = true;
390
+ for (const relatedCardName of relatedCardNames) {
391
+ const { referencedBy, references } = getRelatedCardsByName(
392
+ relatedCardName,
393
+ cards
394
+ );
395
+ if (["referencedby"].includes(originalKey)) {
396
+ values.push(
397
+ ...references.map(
398
+ ({ name }) => name.toLowerCase().replaceAll(PUNCTUATION, "")
399
+ )
400
+ );
401
+ } else if (["references"].includes(originalKey)) {
402
+ values.push(
403
+ ...referencedBy.map(
404
+ ({ name }) => name.toLowerCase().replaceAll(PUNCTUATION, "")
405
+ )
406
+ );
407
+ }
408
+ }
409
+ } else if (["art", "artist"].includes(filterKey)) {
410
+ artists = values;
411
+ } else if (["print", "prints", "printing", "printings"].includes(filterKey)) {
412
+ prints = values;
413
+ } else if (["is", "meta"].includes(filterKey)) {
414
+ const metaValues = getMetaValuesFromText(values);
415
+ values = metaValues.map(
416
+ (v) => v.toLowerCase().replaceAll(PUNCTUATION, "")
417
+ );
418
+ if (metaValues.includes(Meta.Expansion) && !isExcluded) {
419
+ isExpansionSlot = true;
420
+ }
421
+ } else if (["foiling", "foil"].includes(filterKey)) {
422
+ foilings = getFoilingValuesFromText(values);
423
+ values = foilings.map((f) => f.toLowerCase());
424
+ } else if (["treat", "treatment", "var", "variation"].includes(filterKey)) {
425
+ treatments = getTreatmentValuesFromText(values);
426
+ values = treatments.map((t) => t.toLowerCase());
427
+ } else if (["set", "s"].includes(filterKey)) {
428
+ releases = getReleasesFromRawValues(values, additionalSets);
429
+ values = releases.map(
430
+ (s) => s.toLowerCase().replaceAll(PUNCTUATION, "")
431
+ );
432
+ } else if (["pitch", "p", "color"].includes(filterKey)) {
433
+ values = getPitchValuesFromText(values);
434
+ }
435
+ if (filtersToCardPropertyMappings[filterKey]) {
436
+ appliedFilters.push({
437
+ filterToPropertyMapping: filtersToCardPropertyMappings[filterKey],
438
+ values,
439
+ isAnd,
440
+ isOr,
441
+ modifier,
442
+ isExcluded,
443
+ isOptional
444
+ });
445
+ }
446
+ }
447
+ } else if (criteria) {
448
+ const expanded = getAbbreviation(criteria)?.card;
449
+ const metaFilters = getExcludedMetaFilters(criteria);
450
+ if (expanded) {
451
+ keywords.push(`"${expanded.toLowerCase().replace(PUNCTUATION, "")}"`);
452
+ } else if (metaFilters && metaFilters.length > 0) {
453
+ appliedFilters.push(...metaFilters);
454
+ } else {
455
+ keywords.push(criteria.replace(PUNCTUATION, ""));
456
+ }
457
+ }
458
+ }
459
+ return {
460
+ appliedFilters,
461
+ attributes: {
462
+ artists,
463
+ foilings,
464
+ isExpansionSlot,
465
+ prints,
466
+ rarities,
467
+ releases,
468
+ treatments
469
+ },
470
+ keywords
471
+ };
472
+ };
473
+ const getReleasesFromRawValues = (rawValues, additionalSets = []) => {
474
+ const releases = [];
475
+ for (const rawValue of rawValues) {
476
+ releases.push(...getMatchingReleasesFromRawValue(rawValue, additionalSets));
477
+ }
478
+ return releases;
479
+ };
480
+ const getMatchingReleasesFromRawValue = (rawValue, additionalSets = []) => {
481
+ const releases = [];
482
+ const setFromValue = Object.values(Release).find(
483
+ (release) => release.toLowerCase().replaceAll(PUNCTUATION, "") === rawValue
484
+ );
485
+ if (setFromValue) {
486
+ releases.push(setFromValue);
487
+ }
488
+ if (releases.length === 0) {
489
+ const setFromSetIdentifier = setIdentifierToSetMappings[rawValue];
490
+ if (setFromSetIdentifier) {
491
+ releases.push(setFromSetIdentifier);
492
+ }
493
+ }
494
+ if (releases.length === 0) {
495
+ const setsFromPartialValue = Object.values(Release).filter(
496
+ (release) => release.toLowerCase().includes(rawValue)
497
+ );
498
+ if (setsFromPartialValue.length > 0) {
499
+ releases.push(...setsFromPartialValue);
500
+ }
501
+ }
502
+ if (releases.length === 0) {
503
+ const additionalSetFromValue = additionalSets.find(
504
+ (additionalSet) => additionalSet.toLowerCase().replaceAll(PUNCTUATION, "") === rawValue
505
+ );
506
+ if (additionalSetFromValue) {
507
+ releases.push(additionalSetFromValue);
508
+ }
509
+ }
510
+ return releases;
511
+ };
512
+ const pitchValuesMapping = {
513
+ blue: 3,
514
+ yellow: 2,
515
+ red: 1,
516
+ white: 0
517
+ };
518
+ const getPitchValuesFromText = (rawValues) => {
519
+ const values = [];
520
+ for (const rawValue of rawValues) {
521
+ if (pitchValuesMapping[rawValue] || pitchValuesMapping[rawValue] === 0) {
522
+ values.push(pitchValuesMapping[rawValue].toString());
523
+ } else {
524
+ values.push(rawValue);
525
+ }
526
+ }
527
+ return values;
528
+ };
529
+ const metaValuesMapping = {
530
+ exp: Meta.Expansion,
531
+ expansion: Meta.Expansion,
532
+ expansionSlot: Meta.Expansion,
533
+ rainbow: Meta.Rainbow
534
+ };
535
+ const getMetaValuesFromText = (rawValues) => {
536
+ const values = [];
537
+ for (const rawValue of rawValues) {
538
+ if (metaValuesMapping[rawValue]) {
539
+ values.push(metaValuesMapping[rawValue]);
540
+ }
541
+ }
542
+ return values;
543
+ };
544
+ const foilingValuesMapping = {
545
+ r: Foiling.Rainbow,
546
+ rf: Foiling.Rainbow,
547
+ rainbow: Foiling.Rainbow,
548
+ c: Foiling.Cold,
549
+ cf: Foiling.Cold,
550
+ cold: Foiling.Cold,
551
+ g: Foiling.Gold,
552
+ gf: Foiling.Gold,
553
+ gold: Foiling.Gold
554
+ };
555
+ const getFoilingValuesFromText = (rawValues) => {
556
+ const values = [];
557
+ for (const rawValue of rawValues) {
558
+ if (foilingValuesMapping[rawValue]) {
559
+ values.push(foilingValuesMapping[rawValue]);
560
+ }
561
+ }
562
+ return values;
563
+ };
564
+ const treatmentValuesMapping = {
565
+ ...Object.values(Treatment).reduce((obj, treatment) => {
566
+ obj[treatment.toLowerCase()] = treatment;
567
+ return obj;
568
+ }, {}),
569
+ ...{
570
+ aa: Treatment.AA,
571
+ alt: Treatment.AA,
572
+ "alt art": Treatment.AA,
573
+ ab: Treatment.AB,
574
+ "alt border": Treatment.AB,
575
+ at: Treatment.AT,
576
+ "alt text": Treatment.AT,
577
+ // ds: Treatment.DS,
578
+ // "double sided": Treatment.DS,
579
+ ea: Treatment.EA,
580
+ extended: Treatment.EA,
581
+ "extended art": Treatment.EA,
582
+ fa: Treatment.FA,
583
+ full: Treatment.FA,
584
+ "full art": Treatment.FA
585
+ }
586
+ };
587
+ const getTreatmentValuesFromText = (rawValues) => {
588
+ const values = [];
589
+ for (const rawValue of rawValues) {
590
+ if (treatmentValuesMapping[rawValue]) {
591
+ values.push(treatmentValuesMapping[rawValue]);
592
+ } else if (!!Treatment[rawValue.toUpperCase()]) {
593
+ values.push(Treatment[rawValue.toUpperCase()]);
594
+ }
595
+ }
596
+ return values;
597
+ };
598
+ const RARITY_VALUES_MAPPING = {
599
+ b: Rarity.Basic,
600
+ c: Rarity.Common,
601
+ f: Rarity.Fabled,
602
+ l: Rarity.Legendary,
603
+ m: Rarity.Majestic,
604
+ p: Rarity.Promo,
605
+ r: Rarity.Rare,
606
+ s: Rarity.SuperRare,
607
+ t: Rarity.Token,
608
+ v: Rarity.Marvel
609
+ };
610
+ const getRarityValuesFromText = (rawValues) => {
611
+ const values = [];
612
+ for (const rawValue of rawValues) {
613
+ if (RARITY_VALUES_MAPPING[rawValue]) {
614
+ values.push(RARITY_VALUES_MAPPING[rawValue]);
615
+ } else {
616
+ values.push(rawValue);
617
+ }
618
+ }
619
+ return values;
620
+ };
621
+ const getFilterValuesAndModifier = (unparsedFilterValue) => {
622
+ const values = [];
623
+ let isAnd, isOr;
624
+ const modifier = availableModifiers.find(
625
+ (modifier2) => unparsedFilterValue.includes(modifier2)
626
+ );
627
+ if (modifier) {
628
+ const [, rawValue] = unparsedFilterValue.split(modifier);
629
+ if (filterIsAnd(rawValue)) {
630
+ isAnd = true;
631
+ values.push(
632
+ ...rawValue.trim().split("+").map((value) => value.replace(PUNCTUATION, ""))
633
+ );
634
+ } else if (filterIsOr(rawValue)) {
635
+ isOr = true;
636
+ values.push(
637
+ ...rawValue.trim().split(",").map((value) => value.replace(PUNCTUATION, ""))
638
+ );
639
+ } else {
640
+ values.push(rawValue.trim().replace(PUNCTUATION, ""));
641
+ }
642
+ } else {
643
+ if (filterIsAnd(unparsedFilterValue)) {
644
+ isAnd = true;
645
+ values.push(
646
+ ...unparsedFilterValue.trim().split("+").map((value) => value.replace(PUNCTUATION, ""))
647
+ );
648
+ } else if (filterIsOr(unparsedFilterValue)) {
649
+ isOr = true;
650
+ values.push(
651
+ ...unparsedFilterValue.trim().split(",").map((value) => value.replace(PUNCTUATION, ""))
652
+ );
653
+ } else {
654
+ if (unparsedFilterValue.startsWith('"') && unparsedFilterValue.endsWith('"')) {
655
+ values.push(
656
+ unparsedFilterValue.trim().replaceAll('"', "").replace(PUNCTUATION, "")
657
+ );
658
+ } else {
659
+ values.push(unparsedFilterValue.trim().replace(PUNCTUATION, ""));
660
+ }
661
+ }
662
+ }
663
+ return { modifier, values, isAnd, isOr };
664
+ };
665
+ const getFilterKeyAndExcludedOrOptional = (unparsedFilterKey) => {
666
+ const exclusion = getExclusion(unparsedFilterKey);
667
+ if (exclusion) {
668
+ const [, filterKey] = unparsedFilterKey.split(exclusion);
669
+ return {
670
+ filterKey,
671
+ isExcluded: true,
672
+ isOptional: false,
673
+ isMeta: filterIsMeta(filterKey)
674
+ };
675
+ } else {
676
+ return {
677
+ filterKey: unparsedFilterKey,
678
+ isExcluded: false,
679
+ isOptional: false,
680
+ isMeta: filterIsMeta(unparsedFilterKey)
681
+ };
682
+ }
683
+ };
684
+ const hasFilter = (text) => text.indexOf(":") >= 0;
685
+ const filterIsAnd = (text) => text.indexOf("+") >= 0;
686
+ const filterIsOr = (text) => text.indexOf(",") >= 0;
687
+ const filterIsMeta = (filterKey) => !!filtersToCardPropertyMappings[filterKey]?.isMeta;
688
+ const getExclusion = (text) => availableExclusions.find((exclusion) => text.includes(exclusion))?.slice(0, 1);
689
+ export {
690
+ RARITY_VALUES_MAPPING,
691
+ availableExclusions,
692
+ availableModifiers,
693
+ filtersToCardPropertyMappings,
694
+ getKeywordsAndAppliedFiltersFromText
695
+ };
@@ -0,0 +1,15 @@
1
+ import { PUNCTUATION } from "./constants";
2
+ const getCardByName = (name, cards) => {
3
+ let card = cards.find((card2) => getCleanText(card2.name) === name);
4
+ if (!card) {
5
+ card = cards.find((card2) => getCleanText(card2.name).includes(name));
6
+ }
7
+ return card;
8
+ };
9
+ const getCleanText = (text) => getNormalizedText(text.toLowerCase().trim().replace(PUNCTUATION, ""));
10
+ const getNormalizedText = (text) => text.normalize("NFD").replace(/\p{Diacritic}/gu, "");
11
+ export {
12
+ getCardByName,
13
+ getCleanText,
14
+ getNormalizedText
15
+ };