@flesh-and-blood/search 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/filters.d.ts CHANGED
@@ -1,7 +1,14 @@
1
- import { Card, Foiling, Hero, Rarity, Release, Treatment } from "@flesh-and-blood/types";
1
+ import { Foiling, Hero, Rarity, Release, Treatment } from "@flesh-and-blood/types";
2
+ import { SearchIndex } from "./searchIndex.js";
2
3
  export interface AppliedFilter {
3
4
  filterToPropertyMapping: FilterToPropertyMapping;
4
5
  values: string[];
6
+ /**
7
+ * The same strings as `values`, for filters whose match is exact membership
8
+ * rather than a comparison, so a card costs one lookup instead of a scan.
9
+ * Consumers read `values`; this is the matcher's copy.
10
+ */
11
+ valuesSet?: Set<string>;
5
12
  isAnd?: boolean;
6
13
  isOr?: boolean;
7
14
  modifier?: Modifier;
@@ -25,6 +32,12 @@ export interface FilterToPropertyMapping {
25
32
  isBoolean?: boolean;
26
33
  isDate?: boolean;
27
34
  isMeta?: boolean;
35
+ /**
36
+ * The card stores this property the way a filter value is written, so the
37
+ * matcher compares it as stored instead of stripping punctuation and case
38
+ * from it first.
39
+ */
40
+ isNormalized?: boolean;
28
41
  modifier?: Modifier;
29
42
  partialMatch?: boolean;
30
43
  specialProperty?: string;
@@ -103,7 +116,7 @@ export declare const filtersToCardPropertyMappings: {
103
116
  x: FilterToPropertyMapping;
104
117
  year: FilterToPropertyMapping;
105
118
  };
106
- export declare const getKeywordsAndAppliedFiltersFromText: (text: string, cards: Card[], additionalHeroes?: Hero[], additionalSets?: Release[], today?: string) => {
119
+ export declare const getKeywordsAndAppliedFiltersFromText: (text: string, index: SearchIndex, additionalHeroes?: Hero[], additionalSets?: Release[], today?: string) => {
107
120
  appliedFilters: AppliedFilter[];
108
121
  attributes: {
109
122
  artists: string[];
package/dist/filters.js CHANGED
@@ -12,11 +12,11 @@ import { getAbbreviation } from "./abbreviations.js";
12
12
  import { getExcludedMetaFilters, getMetaFilters } from "./metaFilters.js";
13
13
  import { multiWordShorthands, singleWordShorthands } from "./shorthands.js";
14
14
  import { PUNCTUATION } from "./constants.js";
15
- import { getCardByName } from "./helpers.js";
16
15
  import {
17
- getCardsByReferencedCardIdentifier,
18
- getReferencedCards
19
- } from "./related.js";
16
+ getCardsByName,
17
+ getCardsReferencedBy,
18
+ getCardsReferencing
19
+ } from "./searchIndex.js";
20
20
  const availableModifiers = [">=", ">", "<=", "<"];
21
21
  const availableExclusions = ["!", "-"];
22
22
  const arcaneFilter = {
@@ -38,6 +38,26 @@ const bondFilter = {
38
38
  property: "bonds",
39
39
  isArray: true
40
40
  };
41
+ const cardIdentifierFilter = {
42
+ property: "cardIdentifier",
43
+ isString: true,
44
+ isNormalized: true
45
+ };
46
+ const getRelationAppliedFilter = (cardIdentifiers, {
47
+ isAnd,
48
+ isExcluded,
49
+ isOptional,
50
+ modifier
51
+ }) => ({
52
+ filterToPropertyMapping: cardIdentifierFilter,
53
+ values: [...cardIdentifiers],
54
+ valuesSet: cardIdentifiers,
55
+ isAnd,
56
+ isOr: true,
57
+ modifier,
58
+ isExcluded,
59
+ isOptional
60
+ });
41
61
  const chainFilter = {
42
62
  property: "n/a"
43
63
  };
@@ -299,7 +319,8 @@ const getSearchCriteria = (text) => {
299
319
  }
300
320
  return searchCriteria;
301
321
  };
302
- const getKeywordsAndAppliedFiltersFromText = (text, cards, additionalHeroes = [], additionalSets = [], today = getTodayAsReleaseDate()) => {
322
+ const CHAIN_EXPANSION_LIMIT = 20;
323
+ const getKeywordsAndAppliedFiltersFromText = (text, index, additionalHeroes = [], additionalSets = [], today = getTodayAsReleaseDate()) => {
303
324
  let expandedText = text.trim().toLowerCase();
304
325
  for (const { expanded: filters, shorthands } of multiWordShorthands) {
305
326
  for (const shorthand of shorthands) {
@@ -341,7 +362,7 @@ const getKeywordsAndAppliedFiltersFromText = (text, cards, additionalHeroes = []
341
362
  if (hasFilter(criteria)) {
342
363
  const [unparsedFilterKey, unparsedFilterValue] = criteria.split(":");
343
364
  let { modifier, values, isAnd, isOr } = getFilterValuesAndModifier(unparsedFilterValue);
344
- let { filterKey, isExcluded, isOptional, isMeta } = getFilterKeyAndExcludedOrOptional(unparsedFilterKey);
365
+ const { filterKey, isExcluded, isOptional, isMeta } = getFilterKeyAndExcludedOrOptional(unparsedFilterKey);
345
366
  let areValuesAlreadyApplied = false;
346
367
  if (isMeta) {
347
368
  if (["rarity", "r"].includes(filterKey)) {
@@ -365,71 +386,81 @@ const getKeywordsAndAppliedFiltersFromText = (text, cards, additionalHeroes = []
365
386
  );
366
387
  } else {
367
388
  if (["chain"].includes(filterKey)) {
368
- const getName = (card) => card.name.toLowerCase().replaceAll(PUNCTUATION, "");
369
- const relatedCardNames = values.map((name) => getCardByName(name, cards)).filter((card) => !!card).map(getName);
370
- const names = new Set(relatedCardNames);
371
- filterKey = "name";
372
- isOr = true;
373
- const limit = 20;
374
- let counter = 0;
375
- const addToSetAndRelated = (card) => {
389
+ const chainedCardIdentifiers = /* @__PURE__ */ new Set();
390
+ const cardsToExpand = [];
391
+ const namesToExpand = /* @__PURE__ */ new Set();
392
+ const addToChain = (card) => {
393
+ chainedCardIdentifiers.add(card.cardIdentifier);
394
+ if (!namesToExpand.has(card.name)) {
395
+ namesToExpand.add(card.name);
396
+ cardsToExpand.push(card);
397
+ }
398
+ };
399
+ const addRelatedCardToChain = (card) => {
376
400
  if (!card.types.includes(Type.Hero)) {
377
- const name = getName(card);
378
- names.add(name);
379
- if (!relatedCardNames.includes(name)) {
380
- relatedCardNames.push(name);
381
- }
401
+ addToChain(card);
382
402
  }
383
403
  };
384
- const cardsByReferencedCardIdentifier = getCardsByReferencedCardIdentifier(cards);
385
- for (const relatedCardName of relatedCardNames) {
386
- if (counter > limit) {
404
+ for (const value of values) {
405
+ if (value) {
406
+ for (const seedCard of getCardsByName(index, value)) {
407
+ addToChain(seedCard);
408
+ }
409
+ }
410
+ }
411
+ let expansions = 0;
412
+ for (const cardToExpand of cardsToExpand) {
413
+ if (expansions > CHAIN_EXPANSION_LIMIT) {
387
414
  break;
388
415
  }
389
- const relatedCard = getCardByName(relatedCardName, cards);
390
- getReferencedCards(relatedCard, cards).forEach(addToSetAndRelated);
391
- if (counter === 0 && relatedCard) {
392
- const referencingCards = cardsByReferencedCardIdentifier.get(
393
- relatedCard.cardIdentifier
394
- ) || [];
395
- referencingCards.forEach(addToSetAndRelated);
416
+ for (const referencedCard of getCardsReferencedBy(
417
+ index,
418
+ cardToExpand
419
+ )) {
420
+ addRelatedCardToChain(referencedCard);
396
421
  }
397
- counter++;
422
+ const isSeed = expansions === 0;
423
+ if (isSeed) {
424
+ for (const referencingCard of getCardsReferencing(
425
+ index,
426
+ cardToExpand
427
+ )) {
428
+ addRelatedCardToChain(referencingCard);
429
+ }
430
+ }
431
+ expansions++;
398
432
  }
399
- values = Array.from(names);
433
+ appliedFilters.push(
434
+ getRelationAppliedFilter(chainedCardIdentifiers, {
435
+ isAnd,
436
+ isExcluded,
437
+ isOptional,
438
+ modifier
439
+ })
440
+ );
441
+ areValuesAlreadyApplied = true;
400
442
  } else if (["referencedby", "references"].includes(filterKey)) {
401
443
  const isNamedByFilter = ["referencedby"].includes(filterKey);
402
- const relatedCardNames = [...values].filter((value) => !!value);
403
- values = [];
404
- filterKey = "name";
405
- isOr = true;
406
- const relatedCards = [];
407
- if (isNamedByFilter) {
408
- for (const relatedCardName of relatedCardNames) {
409
- relatedCards.push(
410
- ...getReferencedCards(
411
- getCardByName(relatedCardName, cards),
412
- cards
413
- )
414
- );
415
- }
416
- } else {
417
- const cardsByReferencedCardIdentifier = getCardsByReferencedCardIdentifier(cards);
418
- for (const relatedCardName of relatedCardNames) {
419
- const relatedCard = getCardByName(relatedCardName, cards);
420
- if (relatedCard) {
421
- const referencingCards = cardsByReferencedCardIdentifier.get(
422
- relatedCard.cardIdentifier
423
- ) || [];
424
- relatedCards.push(...referencingCards);
444
+ const relatedCardIdentifiers = /* @__PURE__ */ new Set();
445
+ for (const value of values) {
446
+ if (value) {
447
+ for (const namedCard of getCardsByName(index, value)) {
448
+ const cardsInRelation = isNamedByFilter ? getCardsReferencedBy(index, namedCard) : getCardsReferencing(index, namedCard);
449
+ for (const relatedCard of cardsInRelation) {
450
+ relatedCardIdentifiers.add(relatedCard.cardIdentifier);
451
+ }
425
452
  }
426
453
  }
427
454
  }
428
- values.push(
429
- ...relatedCards.map(
430
- ({ name }) => name.toLowerCase().replaceAll(PUNCTUATION, "")
431
- )
455
+ appliedFilters.push(
456
+ getRelationAppliedFilter(relatedCardIdentifiers, {
457
+ isAnd,
458
+ isExcluded,
459
+ isOptional,
460
+ modifier
461
+ })
432
462
  );
463
+ areValuesAlreadyApplied = true;
433
464
  } else if (["art", "artist"].includes(filterKey)) {
434
465
  artists = values;
435
466
  } else if (["print", "prints", "printing", "printings"].includes(filterKey)) {
@@ -507,9 +538,10 @@ const getKeywordsAndAppliedFiltersFromText = (text, cards, additionalHeroes = []
507
538
  } else if (["pitch", "p", "color"].includes(filterKey)) {
508
539
  values = getPitchValuesFromText(values);
509
540
  }
510
- if (filtersToCardPropertyMappings[filterKey] && !areValuesAlreadyApplied) {
541
+ const filterToPropertyMapping = filtersToCardPropertyMappings[filterKey];
542
+ if (filterToPropertyMapping && !areValuesAlreadyApplied) {
511
543
  appliedFilters.push({
512
- filterToPropertyMapping: filtersToCardPropertyMappings[filterKey],
544
+ filterToPropertyMapping,
513
545
  values,
514
546
  isAnd,
515
547
  isOr,
package/dist/helpers.d.ts CHANGED
@@ -1,4 +1,2 @@
1
- import { Card } from "@flesh-and-blood/types";
2
- export declare const getCardByName: (name: string, cards: Card[]) => Card;
3
1
  export declare const getCleanText: (text: string) => string;
4
2
  export declare const getNormalizedText: (text: string) => string;
package/dist/helpers.js CHANGED
@@ -1,15 +1,7 @@
1
1
  import { PUNCTUATION } from "./constants.js";
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
2
  const getCleanText = (text) => getNormalizedText(text.toLowerCase().trim().replace(PUNCTUATION, ""));
10
3
  const getNormalizedText = (text) => text.normalize("NFD").replace(/\p{Diacritic}/gu, "");
11
4
  export {
12
- getCardByName,
13
5
  getCleanText,
14
6
  getNormalizedText
15
7
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- var sr=Object.create;var Y=Object.defineProperty;var or=Object.getOwnPropertyDescriptor;var nr=Object.getOwnPropertyNames;var lr=Object.getPrototypeOf,cr=Object.prototype.hasOwnProperty;var pr=(e,r)=>{for(var t in r)Y(e,t,{get:r[t],enumerable:!0})},we=(e,r,t,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of nr(r))!cr.call(e,a)&&a!==t&&Y(e,a,{get:()=>r[a],enumerable:!(i=or(r,a))||i.enumerable});return e};var dr=(e,r,t)=>(t=e!=null?sr(lr(e)):{},we(r||!e||!e.__esModule?Y(t,"default",{value:e,enumerable:!0}):t,e)),ur=e=>we(Y({},"__esModule",{value:!0}),e);var lt={};pr(lt,{FilterProperty:()=>Se,PUNCTUATION:()=>u,RARITY_VALUES_MAPPING:()=>Ae,abbreviations:()=>pe,availableExclusions:()=>er,availableModifiers:()=>Xe,default:()=>ir,filterCard:()=>ar,filtersToCardPropertyMappings:()=>oe,getAbbreviation:()=>J,getAbbreviationByCard:()=>fr,getCardByName:()=>j,getCardsByReferencedCardIdentifier:()=>te,getCleanText:()=>be,getExcludedMetaFilters:()=>ue,getKeywordsAndAppliedFiltersFromText:()=>Te,getMetaFilters:()=>de,getNormalizedText:()=>z,getOtherPitches:()=>wr,getReferencedCards:()=>re,getTokensReferencedByCards:()=>Sr,multiWordShorthands:()=>ge,shorthands:()=>fe,singleWordShorthands:()=>he});module.exports=ur(lt);var ne=require("@flesh-and-blood/types"),Pe=dr(require("fuse.js"),1);var u=/[!"#$%&'’(),./:;<=>?@[\]^_`|~]/g;var l=require("@flesh-and-blood/types");var J=e=>pe.find(({abbreviations:r})=>r.find(t=>t.toLowerCase()===e)),fr=e=>pe.find(({card:r})=>r.toLowerCase()===e.name.toLowerCase()),pe=[{abbreviations:["10k"],card:"10,000 Year Reunion"},{abbreviations:["Pajamas","PJs"],card:"Alluvion Constellas"},{abbreviations:["Slippy","Arakni Slipped Through the Cracks","Arakni, Slipped Through the Cracks"],card:"Arakni, 5L!p3d 7hRu 7h3 cR4X"},{abbreviations:["Dullcap"],card:"Arcanite Skullcap"},{abbreviations:["ALS"],card:"Arc Light Sentinel"},{abbreviations:["AoW"],card:"Art of War"},{abbreviations:["BBD"],card:"Barraging Beatdown"},{abbreviations:["BBS"],card:"Big Blue Sky"},{abbreviations:["Starvo"],card:"Bravo, Star of the Show"},{abbreviations:["BEB"],card:"Bull's Eye Bracers"},{abbreviations:["BLW"],card:"Be Like Water"},{abbreviations:["BoJ"],card:"Balance of Justice"},{abbreviations:["BOHH"],card:"Blood on Her Hands"},{abbreviations:["BRB"],card:"Bloodrush Bellow"},{abbreviations:["Breezies"],card:"Breeze Rider Boots"},{abbreviations:["BTA"],card:"Burn Them All"},{abbreviations:["CStrike","C-Strike","C Strike"],card:"Celestial Cataclysm"},{abbreviations:["CBelly"],card:"Cerebellum Processor"},{abbreviations:["CLF"],card:"Channel Lake Frigid"},{abbreviations:["CLV"],card:"Channel Lightning Valley"},{abbreviations:["CMH"],card:"Channel Mount Heroic"},{abbreviations:["CMI"],card:"Channel Mount Isen"},{abbreviations:["CMT"],card:"Channel the Millennium Tree"},{abbreviations:["CnC","C&C"],card:"Command and Conquer"},{abbreviations:["Sea and Sea"],card:"Conqueror of the High Seas"},{abbreviations:["CYB"],card:"Count Your Blessings"},{abbreviations:["Cat","Kitty"],card:"Crouching Tiger"},{abbreviations:["CoD"],card:"Crown of Dominion"},{abbreviations:["CoP"],card:"Crown of Providence"},{abbreviations:["CtW"],card:"Crush the Weak"},{abbreviations:["DD"],card:"Death Dealer"},{abbreviations:["DnD"],card:"Devotion Never Dies"},{abbreviations:["DIO"],card:"Dash I/O"},{abbreviations:["EBTT"],card:"Even Bigger Than That!"},{abbreviations:["NewNigma"],card:"Enigma, New Moon"},{abbreviations:["EPot","E Pot"],card:"Energy Potion"},{abbreviations:["EStrike","E-Strike","E Strike"],card:"Enlightened Strike"},{abbreviations:["FFS"],card:"Fyendal's Fighting Spirit"},{abbreviations:["FoN"],card:"Force of Nature"},{abbreviations:["Frosty","\u{1F976}","\u{1F9CA}","\u2744\uFE0F"],card:"Frostbite"},{abbreviations:["Yum yum"],card:"Fruits of the Forest"},{abbreviations:["GnT"],card:"Give and Take"},{abbreviations:["Habibi"],card:"Hanabi Blaster"},{abbreviations:["HMH"],card:"Hope Merchant's Hood"},{abbreviations:["HoI"],card:"Heart of Ice"},{abbreviations:["Pumpkin"],card:"Jack-o'-lantern"},{abbreviations:["RKO","Cheato"],card:"Kayo, Underhanded Cheat"},{abbreviations:["KKBB"],card:"Knick Knack Bric-a-brac"},{abbreviations:["BStrike"],card:"Levels of Enlightenment"},{abbreviations:["LAG","Twominaris"],card:"Luminaris, Angel's Glow"},{abbreviations:["LCF","Newminaris"],card:"Luminaris, Celestial Fury"},{abbreviations:["LDE"],card:"Last Ditch Effort"},{abbreviations:["LtC"],card:"Lead the Charge"},{abbreviations:["LNW"],card:"Leave No Witnesses"},{abbreviations:["LFaL","L4aL"],card:"Life for a Life"},{abbreviations:["LiT"],card:"Lost in Thought"},{abbreviations:["MMB"],card:"Mage Master Boots"},{abbreviations:["MaxV"],card:"Maximum Velocity"},{abbreviations:["MoM"],card:"Mask of Momentum"},{abbreviations:["MoPL"],card:"Mask of the Pouncing Lynx"},{abbreviations:["MnG"],card:"Meat and Greet"},{abbreviations:["Cake","\u{1F382}"],card:"Ninth Blade of the Blood Oath"},{abbreviations:["PoM"],card:"Peace of Mind"},{abbreviations:["P-Bone"],card:"Performance Bonus"},{abbreviations:["PF","\u{1F525}"],card:"Phoenix Flame"},{abbreviations:["PtW"],card:"Poison the Well"},{abbreviations:["Thanos","Infinity Gauntlet"],card:"Polarity Reversal Script"},{abbreviations:["DPot","D Pot"],card:"Potion of D\xE9j\xE0 Vu"},{abbreviations:["Ponder Run"],card:"Premeditate"},{abbreviations:["Qi Unbound"],card:"Qi Unleashed"},{abbreviations:["RitL"],card:"Red in the Ledger"},{abbreviations:["Cats","Ghost cat","Ghost cats"],card:"Restless Coalescence"},{abbreviations:["Eugene"],card:"Rhinar, Reckless Rampage",isHidden:!0},{abbreviations:["SSGB"],card:"Sandscour Greatbow"},{abbreviations:["SSP"],card:"Sand Sketched Plan"},{abbreviations:["SFaS","S4aS"],card:"Scar for a Scar"},{abbreviations:["Tyler"],card:"Scurv, Stowaway",isHidden:!0},{abbreviations:["SWOMB"],card:"Shifting Winds of the Mystic Beast"},{abbreviations:["Snaps"],card:"Snapdragon Scalers"},{abbreviations:["SWK"],card:"Spinning Wheel Kick"},{abbreviations:["SFTL"],card:"Swing Fist, Think Later"},{abbreviations:["TTT"],card:"Take the Tempo"},{abbreviations:["Ultron"],card:"Teklovossen, the Mechropotent"},{abbreviations:["ToS"],card:"Test of Strength"},{abbreviations:["TAYG"],card:"That All You Got?"},{abbreviations:["TROM"],card:"This Round's on Me"},{abbreviations:["3oak"],card:"Three of a Kind"},{abbreviations:["Pox Malone","Post Malone"],card:"Virulent Touch"},{abbreviations:["Cast Homes"],card:"Visit Goldmane Estate"},{abbreviations:["Frosty Hammer"],card:"Winter's Wail"},{abbreviations:["Wreckless Wing"],card:"War Cry of Bellona"},{abbreviations:["ZTS"],card:"Zero to Sixty"}];var x=require("@flesh-and-blood/types");var Se=(i=>(i.BannedFormats="bannedFormats",i.LegalFormats="legalFormats",i.LegalHeroes="legalHeroes",i))(Se||{}),ee=Array.from(Array(50).keys()).map(e=>`${e}`),gr=[{format:x.Format.ClassicConstructed,nicknames:["cc","classic"]},{format:x.Format.LivingLegend,nicknames:["cc ll","classic constructed ll","ll cc","ll","living legend"]},{format:x.Format.SilverAge,nicknames:["sage"]},{format:x.Format.GoldenAge,nicknames:["gage"]},{format:x.Format.UltimatePitFight,nicknames:["upf"]}],hr=Object.values(x.Format).map(e=>{let r=gr.find(({format:i})=>i===e),t=e.toLowerCase().replaceAll(u,"");return r?{...r,format:t}:{format:t}}),br=[{hero:x.Hero.DataDoll,nicknames:["data","datadoll"]},{hero:x.Hero.Dorinthea,nicknames:["dori"]},{hero:x.Hero.Genis,nicknames:["genis"]},{hero:x.Hero.GravyBones,nicknames:["gravy"]},{hero:x.Hero.Iyslander,nicknames:["islander"]}],mr=Object.values(x.Hero).map(e=>{let r=br.find(({hero:i})=>i===e),t=e.toLowerCase().replaceAll(u,"");return r?{...r,hero:t}:{hero:t}}),Q=["common","rare","super rare","majestic","legendary","fabled"],yr=(e,r,t,i)=>{let a=[];if(!r)a.push(...e);else for(let s of e)switch(r){case">=":{let o=!1;for(let n of Q)o?a.push(n):n===s&&(o=!0,a.push(n));break}case">":{let o=!1;for(let n of Q)o?a.push(n):n===s&&(o=!0);break}case"<=":{let o=!1;for(let n of Q.slice().reverse())o?a.push(n):n===s&&(o=!0,a.push(n));break}case"<":{let o=!1;for(let n of Q.slice().reverse())o?a.push(n):n===s&&(o=!0);break}default:break}return{filterToPropertyMapping:{nestedProperty:"rarity",property:"printings",isArray:!0},isExcluded:t,isOptional:i,isOr:!0,values:a}},ke=(e,r,t,i,a)=>{let s=i.map(g=>({hero:g.toLowerCase().replaceAll(u,"")})),o=[],n=[],c=[];for(let g of e){let f=hr.find(({format:C,nicknames:M})=>C===g||!!M&&M.includes(g));if(f)n.push(f.format);else{let C=mr.find(({hero:M,nicknames:A})=>M===g||!!A&&A.includes(g))||s.find(({hero:M})=>M===g);C&&c.push(C.hero)}}let b=a||"legalFormats";return n.length>0&&o.push({filterToPropertyMapping:{property:b,isArray:!0},values:n,isOr:!0,isExcluded:r,isOptional:t}),c.length>0&&o.push({filterToPropertyMapping:{property:"legalHeroes",isArray:!0},values:c,isOr:!0,isExcluded:r,isOptional:t}),o},Cr=(e,r,t,i)=>ke(e,r,t,i,"bannedFormats"),de=(e,r,t,i,a,s)=>{let o=[];return vr(t)?o.push(...ke(i,e,r,s)):Tr(t)?o.push(...Cr(i,e,r,s)):xr(t)&&o.push(yr(i,a,e,r)),o},K=[{filterToPropertyMapping:{property:"cost",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"specialCost",isString:!0,partialMatch:!0},isExcluded:!0,values:["*","x"]},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["equipment","hero","placeholder","token","weapon"]}],I=[{filterToPropertyMapping:{property:"defense",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"specialDefense",isString:!0,partialMatch:!0},isExcluded:!0,values:["*","x"]},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["hero","placeholder","token","weapon"]}],Z=[{filterToPropertyMapping:{property:"pitch",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["equipment","hero","placeholder","token","weapon"]},{filterToPropertyMapping:{property:"isCardBack",isBoolean:!0},isExcluded:!0,values:["true"]}],V=[{filterToPropertyMapping:{property:"power",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"specialPower",isString:!0,partialMatch:!0},isExcluded:!0,values:["*","x"]},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["equipment","hero","placeholder","token"]}],X=[{filterToPropertyMapping:{property:"talents",isArray:!0},isExcluded:!0,values:Object.values(x.Talent).map(e=>e.toLowerCase())}],Fr={"!co":K,"-co":K,"!cost":K,"-cost":K,"!color":K,"-color":K,"!b":I,"-b":I,"!block":I,"-block":I,"!d":I,"-d":I,"!def":I,"-def":I,"!defense":I,"-defense":I,"!pitch":Z,"-pitch":Z,"!p":Z,"-p":Z,"!attack":V,"-attack":V,"!power":V,"-power":V,"!pwr":V,"-pwr":V,"!pow":V,"-pow":V,"!talents":X,"-talents":X,"!tal":X,"-tal":X},ue=e=>{let r=[],t=Fr[e];return t&&r.push(...t),r},Mr=["l","legal","hero"],vr=e=>Mr.includes(e),Ar=["banned"],Tr=e=>Ar.includes(e),Pr=["r","rarity"],xr=e=>Pr.includes(e);var N=require("@flesh-and-blood/types"),fe=[{description:"Attack actions",expanded:["st:attack"],filters:{subtypes:[N.Subtype.Attack]},isCardProperty:!1,shorthands:["AA"]},{description:"Arcane barrier",expanded:['k:"arcane barrier"'],filters:{keywords:[N.Keyword.ArcaneBarrier]},isCardProperty:!1,shorthands:["AB"]},{description:"Attack reactions",expanded:['t:"attack reaction"'],filters:{types:[N.Type.AttackReaction]},isCardProperty:!1,shorthands:["AR"]},{description:"Defense reactions",expanded:['t:"defense reaction"'],filters:{types:[N.Type.DefenseReaction]},isCardProperty:!1,shorthands:["DR"]},{description:"Gain life",expanded:["gain {h}"],filters:{functionalText:"gain {h}"},isCardProperty:!1,shorthands:["Gain life","Gains life"]},{description:"Go again",expanded:['k:"go again"'],filters:{keywords:[N.Keyword.GoAgain]},isCardProperty:!1,shorthands:["GA"]},{description:"Non-attack actions",expanded:["t:action","st:non-attack"],filters:{subtypes:[N.Subtype.NonAttack],types:[N.Type.Action]},isCardProperty:!1,shorthands:["NAA"]},{description:"Plus defense",expanded:["+ {d}"],filters:{functionalText:"+ {d}"},isCardProperty:!1,shorthands:["Pump defense","Pumps defense","Buff defense","Buffs defense"]},{description:"Spellvoid",expanded:['k:"spellvoid"'],filters:{keywords:[N.Keyword.Spellvoid]},isCardProperty:!1,shorthands:["SV"]}],ge=fe.filter(({shorthands:e})=>e.some(r=>r.includes(" "))).map(e=>({...e,shorthands:e.shorthands.filter(r=>r.includes(" ")).map(r=>r.toLowerCase()).sort((r,t)=>t.length-r.length)})),he=fe.filter(({shorthands:e})=>e.some(r=>!r.includes(" "))).map(e=>({...e,shorthands:e.shorthands.filter(r=>!r.includes(" ")).map(r=>r.toLowerCase()).sort((r,t)=>t.length-r.length)}));var j=(e,r)=>{let t=r.find(i=>be(i.name)===e);return t||(t=r.find(i=>be(i.name).includes(e))),t},be=e=>z(e.toLowerCase().trim().replace(u,"")),z=e=>e.normalize("NFD").replace(/\p{Diacritic}/gu,"");var wr=(e,r)=>{let t=[];if(e)for(let i of r){let a=i.name===e.name,s=i.cardIdentifier!==e.cardIdentifier,o=i.pitch!==e.pitch;a&&s&&o&&t.push(i)}return t},re=(e,r)=>{let t=new Set(e?.referencedCards),i=[];for(let a of r)t.has(a.cardIdentifier)&&i.push(a);return i},te=e=>{let r=new Map;for(let t of e)for(let i of t.referencedCards||[]){let a=r.get(i);a?a.push(t):r.set(i,[t])}return r},Sr=(e,r)=>{let t=new Set;for(let a of e)for(let s of a.createdExtras||[])t.add(s);let i=[];for(let a of r)t.has(a.cardIdentifier)&&i.push(a);return i};var Xe=[">=",">","<=","<"],er=["!","-"],kr={property:"arcane",specialProperty:"specialArcane",isNumber:!0,partialMatch:!0},me={property:"artists",isArray:!0,partialMatch:!0},Le={property:"n/a",isMeta:!0},Re={property:"bonds",isArray:!0},Lr={property:"n/a"},Ee={property:"classes",isArray:!0,partialMatch:!0},Be={property:"cost",specialProperty:"specialCost",isNumber:!0,partialMatch:!0},$={property:"defense",specialProperty:"specialDefense",isNumber:!0},Ne={property:"flows",isArray:!0},Ie={nestedProperty:"foiling",property:"printings",isArray:!0},De={property:"fusions",isArray:!0},Oe={property:"intellect",isNumber:!0},He={property:"keywords",isArray:!0},ye={property:"n/a",isMeta:!0},Ve={property:"life",specialProperty:"specialLife",isNumber:!0},ve={property:"meta",isArray:!0},Ge={property:"name",isString:!0,partialMatch:!0},Ce={property:"pitch",isNumber:!0},We={property:"firstReleaseDate",isDate:!0},ie={property:"power",specialProperty:"specialPower",isNumber:!0},Rr={property:"setIdentifiers",isArray:!0,partialMatch:!0},Ue={property:"n/a",isMeta:!0},Er={property:"n/a"},Br={property:"n/a"},Ke={property:"sets",isArray:!0,partialMatch:!0},Fe={property:"shorthands",isArray:!0,partialMatch:!0},ae={property:"specializations",isArray:!0,partialMatch:!0},je={property:"subtypes",isArray:!0},qe={property:"types",isArray:!0},ze={property:"talents",isArray:!0},Nr={property:"functionalText",isString:!0,partialMatch:!0},Ir={property:"traits",isArray:!0,partialMatch:!0},Dr={property:"typeText",isString:!0,partialMatch:!0},se={nestedProperty:"treatments",property:"printings",isArray:!0,isNestedPropertyArray:!0},Or={property:"firstReleaseDate",isString:!0,partialMatch:!0},oe={arcane:kr,a:me,artist:me,art:me,attack:ie,b:$,block:$,banned:Le,bond:Re,bonds:Re,c:Ee,class:Ee,chain:Lr,co:Be,cost:Be,color:Ce,d:$,def:$,defense:$,flow:Ne,flows:Ne,f:De,fusion:De,foil:Ie,foiling:Ie,i:Oe,intellect:Oe,is:ve,k:He,keyword:He,l:ye,legal:ye,hero:ye,li:Ve,life:Ve,meta:ve,n:Ge,name:Ge,p:Ce,pitch:Ce,pwr:ie,pow:ie,power:ie,print:Rr,r:Ue,rarity:Ue,referencedby:Er,references:Br,rf:Le,s:Ke,set:Ke,short:Fe,shorthand:Fe,shorthands:Fe,sp:ae,spec:ae,specialization:ae,specializations:ae,st:je,subtype:je,t:qe,type:qe,tal:ze,talent:ze,text:Nr,trait:Ir,treat:se,treatment:se,var:se,variation:se,x:Dr,year:Or},Hr=[{text:l.Release.ClassicBattlesRhinarDorinthea.toLowerCase(),override:l.Release.ClassicBattlesRhinarDorinthea.toLowerCase().replaceAll(u,"")}],Vr=e=>{let r=[],t=e.replaceAll("\u201D",'"');for(let{text:a,override:s}of Hr)t.includes(a)&&(t=t.replace(a,s));if(J(t)?.card)r.push(t);else{let a=t.split(/[ ]+/),s="",o=0;for(let n of a)o===2&&(r.push(s.trim().replaceAll('"',"")),s="",o=0),o<2&&n.split('"').length===2?(s+=" "+n,o++):o===0&&s===""?r.push(n):o===1&&(s+=" "+n);o===2&&(r.push(s.trim().replaceAll('"',"")),s="",o=0)}return r},Te=(e,r,t=[],i=[],a=zr())=>{let s=e.trim().toLowerCase();for(let{expanded:w,shorthands:k}of ge)for(let p of k)if(s.includes(p)){s=s.replace(p,w.join(" "));break}for(let[w,k]of Object.entries(l.setToSetIdentifierMappings))s.includes(w.toLowerCase())&&(s=s.replace(w.toLowerCase(),k[0]));let o=Vr(s),n=[];for(let w of o){let k=he.find(({shorthands:p})=>p.includes(w));k&&!k.isCardProperty?n.push(...k.expanded):n.push(w)}let c=[],b=[],g=[],f=[],C=!1,M=[],A=[],R=[],G=[];for(let w of n)if(Xr(w)){let[k,p]=w.split(":"),{modifier:D,values:d,isAnd:h,isOr:T}=Qr(p),{filterKey:y,isExcluded:E,isOptional:B,isMeta:ce}=Zr(k),q=!1;if(ce){if(["rarity","r"].includes(y)){let v=Jr(d);E||(A=[...v]),d=v.map(L=>L.toLowerCase())}["legal","l","hero"].includes(y),c.push(...de(E,B,y,d,D,t))}else{if(["chain"].includes(y)){let v=F=>F.name.toLowerCase().replaceAll(u,""),L=d.map(F=>j(F,r)).filter(F=>!!F).map(v),S=new Set(L);y="name",T=!0;let P=20,O=0,H=F=>{if(!F.types.includes(l.Type.Hero)){let W=v(F);S.add(W),L.includes(W)||L.push(W)}},U=te(r);for(let F of L){if(O>P)break;let W=j(F,r);re(W,r).forEach(H),O===0&&W&&(U.get(W.cardIdentifier)||[]).forEach(H),O++}d=Array.from(S)}else if(["referencedby","references"].includes(y)){let v=["referencedby"].includes(y),L=[...d].filter(P=>!!P);d=[],y="name",T=!0;let S=[];if(v)for(let P of L)S.push(...re(j(P,r),r));else{let P=te(r);for(let O of L){let H=j(O,r);if(H){let U=P.get(H.cardIdentifier)||[];S.push(...U)}}}d.push(...S.map(({name:P})=>P.toLowerCase().replaceAll(u,"")))}else if(["art","artist"].includes(y))b=d;else if(["print","prints","printing","printings"].includes(y))M=d;else if(["is","meta"].includes(y)){let v=[],L=[],S=[],P=[];for(let F of d)Kr.includes(F)?v.push(F):jr.includes(F)?L.push(F):qr.includes(F)?S.push(F):P.push(F);v.length>0&&(c.push({filterToPropertyMapping:ve,values:[l.Meta.Reprint.toLowerCase().replaceAll(u,"")],isAnd:h,isOr:T,isExcluded:!E,isOptional:B}),q=P.length===0);let O=L.length>0;O&&c.push({filterToPropertyMapping:We,values:[a],isAnd:h,isOr:T,isExcluded:E,isOptional:B});let H=S.length>0;H&&c.push({filterToPropertyMapping:We,values:[a],isAnd:h,isOr:T,isExcluded:!E,isOptional:B}),(O||H)&&(q=P.length===0);let U=$r(P);d=U.map(F=>F.toLowerCase().replaceAll(u,"")),U.includes(l.Meta.Expansion)&&!E&&(C=!0)}else["foiling","foil"].includes(y)?(f=_r(d),d=f.map(v=>v.toLowerCase())):["treat","treatment","var","variation"].includes(y)?(G=Yr(d),d=G.map(v=>v.toLowerCase())):["set","s"].includes(y)?(R=Gr(d,i),d=R.map(v=>v.toLowerCase().replaceAll(u,""))):["pitch","p","color"].includes(y)&&(d=Ur(d));oe[y]&&!q&&c.push({filterToPropertyMapping:oe[y],values:d,isAnd:h,isOr:T,modifier:D,isExcluded:E,isOptional:B})}}else if(w){let k=J(w)?.card,p=ue(w);k?g.push(`"${k.toLowerCase().replace(u,"")}"`):p&&p.length>0?c.push(...p):g.push(w.replace(u,""))}return{appliedFilters:c,attributes:{artists:b,foilings:f,isExpansionSlot:C,prints:M,rarities:A,releases:R,treatments:G},keywords:g}},Gr=(e,r=[])=>{let t=[];for(let i of e)t.push(...Wr(i,r));return t},Wr=(e,r=[])=>{let t=[],i=Object.values(l.Release).find(a=>a.toLowerCase().replaceAll(u,"")===e);if(i&&t.push(i),t.length===0){let a=l.setIdentifierToSetMappings[e];a&&t.push(a)}if(t.length===0){let a=Object.values(l.Release).filter(s=>s.toLowerCase().includes(e));a.length>0&&t.push(...a)}if(t.length===0){let a=r.find(s=>s.toLowerCase().replaceAll(u,"")===e);a&&t.push(a)}return t},Me={purple:4,blue:3,yellow:2,red:1,white:0},Ur=e=>{let r=[];for(let t of e)Me[t]||Me[t]===0?r.push(Me[t].toString()):r.push(t);return r},Kr=["unique"],jr=["preview","spoiler","unreleased"],qr=["released"],zr=()=>{let e=new Date,r=`${e.getMonth()+1}`.padStart(2,"0"),t=`${e.getDate()}`.padStart(2,"0");return`${e.getFullYear()}-${r}-${t}`},$e={dual:l.Meta.DualClass,exp:l.Meta.Expansion,expansion:l.Meta.Expansion,expansionSlot:l.Meta.Expansion,rainbow:l.Meta.Rainbow,reprint:l.Meta.Reprint,reprints:l.Meta.Reprint},$r=e=>{let r=[];for(let t of e)$e[t]?r.push($e[t]):l.Meta[t]&&r.push(l.Meta[t]);if(e.length>0&&r.length===0){for(let t of Object.values(l.Meta))for(let i of e)if(t.toLowerCase().includes(i)){r.push(t);break}}return r},_e={r:l.Foiling.Rainbow,rf:l.Foiling.Rainbow,rainbow:l.Foiling.Rainbow,c:l.Foiling.Cold,cf:l.Foiling.Cold,cold:l.Foiling.Cold,g:l.Foiling.Gold,gf:l.Foiling.Gold,gold:l.Foiling.Gold},_r=e=>{let r=[];for(let t of e)_e[t]&&r.push(_e[t]);return r},Ye={...Object.values(l.Treatment).reduce((e,r)=>(e[r.toLowerCase()]=r,e),{}),aa:l.Treatment.AA,alt:l.Treatment.AA,"alt art":l.Treatment.AA,ab:l.Treatment.AB,"alt border":l.Treatment.AB,at:l.Treatment.AT,"alt text":l.Treatment.AT,ea:l.Treatment.EA,extended:l.Treatment.EA,"extended art":l.Treatment.EA,fa:l.Treatment.FA,full:l.Treatment.FA,"full art":l.Treatment.FA},Yr=e=>{let r=[];for(let t of e)Ye[t]?r.push(Ye[t]):l.Treatment[t.toUpperCase()]&&r.push(l.Treatment[t.toUpperCase()]);return r},Ae={b:l.Rarity.Basic,c:l.Rarity.Common,f:l.Rarity.Fabled,l:l.Rarity.Legendary,m:l.Rarity.Majestic,p:l.Rarity.Promo,r:l.Rarity.Rare,s:l.Rarity.SuperRare,t:l.Rarity.Token,v:l.Rarity.Marvel},Jr=e=>{let r=[];for(let t of e)Ae[t]?r.push(Ae[t]):r.push(t);return r},Qr=e=>{let r=[],t,i,a=Xe.find(s=>e.includes(s));if(a){let[,s]=e.split(a);Je(s)?(t=!0,r.push(...s.trim().split("+").map(o=>o.replace(u,"")))):Qe(s)?(i=!0,r.push(...s.trim().split(",").map(o=>o.replace(u,"")))):r.push(s.trim().replace(u,""))}else Je(e)?(t=!0,r.push(...e.trim().split("+").map(s=>s.replace(u,"")))):Qe(e)?(i=!0,r.push(...e.trim().split(",").map(s=>s.replace(u,"")))):e.startsWith('"')&&e.endsWith('"')?r.push(e.trim().replaceAll('"',"").replace(u,"")):r.push(e.trim().replace(u,""));return{modifier:a,values:r,isAnd:t,isOr:i}},Zr=e=>{let r=et(e);if(r){let[,t]=e.split(r);return{filterKey:t,isExcluded:!0,isOptional:!1,isMeta:Ze(t)}}else return{filterKey:e,isExcluded:!1,isOptional:!1,isMeta:Ze(e)}},Xr=e=>e.indexOf(":")>=0,Je=e=>e.indexOf("+")>=0,Qe=e=>e.indexOf(",")>=0,Ze=e=>!!oe[e]?.isMeta,et=e=>er.find(r=>e.includes(r))?.slice(0,1);var m=require("@flesh-and-blood/types"),rr={artists:["Hoodwill"],cardIdentifier:"fangs-a-lot-blue",classes:[m.Class.Generic],defaultImage:"FNG000",firstReleaseDate:"2022-06-02",functionalText:"If Fangs A Lot is put into your banished zone from your graveyard, instead put it into your hand.",legalFormats:[],legalHeroes:[m.Hero.Kayo,m.Hero.Levia,m.Hero.Rhinar],printings:[{artists:["Hoodwill"],identifier:"FNG000",image:"FNG000",print:"FNG000",rarity:m.Rarity.Rare,set:m.Release.Promos},{artists:["Hoodwill"],identifier:"FNG000",image:"FNG000_Marvel",print:`FNG000-${m.Treatment.FA}`,rarity:m.Rarity.Marvel,set:m.Release.Promos,treatment:m.Treatment.FA}],name:"Fangs A Lot",rarities:[m.Rarity.Rare,m.Rarity.Marvel],rarity:m.Rarity.Rare,sets:[m.Release.Promos],setIdentifiers:["FNG000"],specialImage:"FNG000_Marvel",subtypes:[m.Subtype.Attack],types:[m.Type.Action],typeText:"Generic Action - Attack"},tr=[{keyword:rr.name.toLowerCase(),card:rr}];var xe=class{constructor(r,t=[],i=[],a=!1){this.log=(r,...t)=>{this.debug&&console.log(r,...t)};this.search=(r,t)=>{let i,{appliedFilters:a,attributes:s,keywords:o}=Te(r,this.cards,this.additionalHeroes,this.additionalSets),n=o.join(" "),c=t?tr.filter(p=>p.keyword===n):[];if(c.length>0?i=c.map(({card:p})=>p):o.length?i=this.fuse.search(n).map(p=>p.item):i=[...this.cards],a.length&&(i=i.filter(p=>p&&ar(p,a))),o.length===0){let p="";if(s.releases.length===1)try{let h=s.releases[0];p=ne.setToSetIdentifierMappings[h][0].toUpperCase()}catch(h){console.error("Error getting set identifier from search",h)}if(!p&&s.prints.length===1)try{let h=s.prints[0];ne.setIdentifierToSetMappings[h]&&(p=h.toUpperCase())}catch(h){console.error("Error getting set identifier from search",h)}p?i.sort((h,T)=>{let y=h.setIdentifiers.find(B=>B.includes(p))?.replace(p,""),E=T.setIdentifiers.find(B=>B.includes(p))?.replace(p,"");return y&&E?y.localeCompare(E):-1}):i.sort((h,T)=>h.name===T.name?`${h.pitch}`.localeCompare(`${T.pitch}`):h.name.localeCompare(T.name))}else{let p=[],D=[],d=o.map(h=>h.toLowerCase().replace(u,"")).join(" ");for(let h of i)h.name.toLowerCase().replace(u,"")===d?p.push(h):D.push(h);i=[...p,...D]}let b,{artists:g,isExpansionSlot:f,foilings:C,prints:M,rarities:A,releases:R,treatments:G}=s;(g.length>0||f||C.length>0||M.length>0||A.length>0||R.length>0||G.length>0)&&(b=i.map(p=>{let D=p.printings.filter(d=>{let h=!!d.image,T=g.length===0||g.some(S=>d.artists.find(P=>P.replace(u,"").toLowerCase().includes(S))),y=!f||f===d.isExpansionSlot,E=C.length===0||C.includes(d.foiling),B=M.length===0||M.some(S=>d.identifier.includes(S.toUpperCase())),ce=A.length===0||A.includes(d.rarity),q=R.length===0||R.includes(d.set),v=G.length===0||d.treatments?.some(S=>G.includes(S));return h&&T&&y&&E&&B&&ce&&q&&v});return{...p,matchingPrintings:D}}));let k=b?.length>0?b:i;return{appliedFilters:a,attributes:s,keywords:o,searchResults:k}};let s={getFn:(o,n)=>{let c=Pe.default.config.getFn(o,n);return c&&(Array.isArray(c)?c.map(b=>z(b.replace(u,""))):z(c).replace(u,""))},ignoreLocation:!0,includeScore:!0,keys:[{name:"name",weight:10},{name:"functionalText",weight:6},{name:"shorthands",weight:4},{name:"setIdentifiers",weight:2},{name:"traits",weight:4},{name:"typeText",weight:6}],threshold:.15,useExtendedSearch:!0};this.additionalHeroes=t,this.additionalSets=i,this.cards=[...r],this.debug=a,this.fuse=new Pe.default([...r],s)}},ir=xe,ar=(e,r)=>{let t=!0,i=!1;for(let a of r){let s=a.isOptional,{isNumber:o,isString:n,isArray:c,isBoolean:b,isDate:g}=a.filterToPropertyMapping;if(o){let f=rt(e,a);s?f&&(i=!0):t=t&&f}else if(n){let f=tt(e,a);s?f&&(i=!0):t=t&&f}else if(c){let f=it(e,a,r);s?f&&(i=!0):t=t&&f}else if(b){let f=at(e,a);s?f&&(i=!0):t=t&&f}else if(g){let f=st(e,a);s?f&&(i=!0):t=t&&f}}return t},rt=(e,r)=>{if(_(r,e)){let{values:t,modifier:i,isExcluded:a,filterToPropertyMapping:{partialMatch:s}}=r,o=le(e,r);if(o!=null&&!isNaN(o))if(o=parseInt(o),i)switch(i){case">=":{let n=t?.some(c=>o>=parseInt(c));return a?!n:n}case">":{let n=t?.some(c=>o>parseInt(c));return a?!n:n}case"<=":{let n=t?.some(c=>o<=parseInt(c));return a?!n:n}case"<":{let n=t?.some(c=>o<parseInt(c));return a?!n:n}default:return!1}else{let n=t?.some(c=>o===parseInt(c));return a?!n:n}else{let n=nt(e,r)?.toLowerCase(),c=s?t?.some(b=>n?.includes(b)):t?.some(b=>n===b);return a?!c:c}}else return!0},tt=(e,r)=>{if(_(r,e)){let{values:t,isAnd:i,isExcluded:a,filterToPropertyMapping:{partialMatch:s}}=r,o=le(e,r)?.replaceAll(u,"");if(s){let n=i?t?.every(c=>o?.toLowerCase().includes(c)):t?.some(c=>o?.toLowerCase().includes(c));return a?!n:n}else{let n=i?t?.every(c=>o?.toLowerCase()===c):t?.some(c=>o?.toLowerCase()===c);return a?!n:n}}else return!0},it=(e,r,t)=>{if(_(r,e)){let{values:i,isAnd:a,isExcluded:s,filterToPropertyMapping:{partialMatch:o}}=r,n=ot(e,r,t).map(c=>c?.replaceAll(u,""));if(o){let c=a?i.every(g=>n?.some(f=>f?.toLowerCase().includes(g))):i.some(g=>n?.some(f=>f?.toLowerCase().includes(g))),b=n.length===0;return s?!c||b:c}else{let c=a?i.every(b=>n?.some(g=>g?.toLowerCase()===b)):i.some(b=>n?.some(g=>g?.toLowerCase()===b));return s?!c:c}}else return!0},at=(e,r)=>{if(_(r,e)){let{isExcluded:t}=r,i=le(e,r);return t?!i:i}else return!0},st=(e,r)=>{if(_(r,e)){let{values:t,isExcluded:i}=r,a=le(e,r),s=t?.some(o=>a>o);return i?!s:s}else return!0},le=(e,r)=>{let{filterToPropertyMapping:t}=r;return e[t.property]},ot=(e,r,t)=>{let{filterToPropertyMapping:{isNestedPropertyArray:i,nestedProperty:a,property:s}}=r,o=e[s]||[],n=[],c=Object.keys(e.legalOverrides||{}).length>0,b=r.filterToPropertyMapping.property==="legalHeroes",g=t.find(({filterToPropertyMapping:C})=>C.property==="legalFormats");if(c&&b&&!!g){let C=new Set;for(let{format:M,heroes:A}of e.legalOverrides||[])if(g.values.includes(M.toLowerCase()))for(let R of A)C.add(R);n=Array.from(C)}if(n.length===0)if(a){let C=new Set;for(let M of o)if(i){let A=M[a]||[];for(let R of A)C.add(R)}else{let A=M[a];A&&C.add(A)}n=Array.from(C)}else n=o;return n},nt=(e,r)=>{let{filterToPropertyMapping:t}=r;return e[t.specialProperty]},_=({cardTypes:e},{types:r,subtypes:t})=>!e||e?.some(i=>r.map(a=>a.toLowerCase()).includes(i.toLowerCase())||t.map(a=>a.toLowerCase()).includes(i.toLowerCase()));0&&(module.exports={FilterProperty,PUNCTUATION,RARITY_VALUES_MAPPING,abbreviations,availableExclusions,availableModifiers,filterCard,filtersToCardPropertyMappings,getAbbreviation,getAbbreviationByCard,getCardByName,getCardsByReferencedCardIdentifier,getCleanText,getExcludedMetaFilters,getKeywordsAndAppliedFiltersFromText,getMetaFilters,getNormalizedText,getOtherPitches,getReferencedCards,getTokensReferencedByCards,multiWordShorthands,shorthands,singleWordShorthands});
1
+ var dr=Object.create;var Y=Object.defineProperty;var ur=Object.getOwnPropertyDescriptor;var fr=Object.getOwnPropertyNames;var gr=Object.getPrototypeOf,hr=Object.prototype.hasOwnProperty;var br=(e,r)=>{for(var t in r)Y(e,t,{get:r[t],enumerable:!0})},Be=(e,r,t,a)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of fr(r))!hr.call(e,i)&&i!==t&&Y(e,i,{get:()=>r[i],enumerable:!(a=ur(r,i))||a.enumerable});return e};var yr=(e,r,t)=>(t=e!=null?dr(gr(e)):{},Be(r||!e||!e.__esModule?Y(t,"default",{value:e,enumerable:!0}):t,e)),mr=e=>Be(Y({},"__esModule",{value:!0}),e);var yt={};br(yt,{FilterProperty:()=>Ie,PUNCTUATION:()=>f,RARITY_VALUES_MAPPING:()=>Pe,abbreviations:()=>de,availableExclusions:()=>or,availableModifiers:()=>sr,buildSearchIndex:()=>me,default:()=>cr,filterCard:()=>pr,filtersToCardPropertyMappings:()=>xe,getAbbreviation:()=>J,getAbbreviationByCard:()=>Cr,getCardsByName:()=>re,getCardsByReferencedCardIdentifier:()=>ye,getCardsReferencedBy:()=>ie,getCardsReferencing:()=>te,getCleanText:()=>z,getExcludedMetaFilters:()=>fe,getKeywordsAndAppliedFiltersFromText:()=>we,getMetaFilters:()=>ue,getNormalizedText:()=>q,getOtherPitches:()=>Rr,getReferencedCards:()=>Er,getTokensReferencedByCards:()=>Nr,multiWordShorthands:()=>he,shorthands:()=>ge,singleWordShorthands:()=>be});module.exports=mr(yt);var ne=require("@flesh-and-blood/types"),ke=yr(require("fuse.js"),1);var f=/[!"#$%&'’(),./:;<=>?@[\]^_`|~]/g;var l=require("@flesh-and-blood/types");var J=e=>de.find(({abbreviations:r})=>r.find(t=>t.toLowerCase()===e)),Cr=e=>de.find(({card:r})=>r.toLowerCase()===e.name.toLowerCase()),de=[{abbreviations:["10k"],card:"10,000 Year Reunion"},{abbreviations:["Pajamas","PJs"],card:"Alluvion Constellas"},{abbreviations:["Slippy","Arakni Slipped Through the Cracks","Arakni, Slipped Through the Cracks"],card:"Arakni, 5L!p3d 7hRu 7h3 cR4X"},{abbreviations:["Dullcap"],card:"Arcanite Skullcap"},{abbreviations:["ALS"],card:"Arc Light Sentinel"},{abbreviations:["AoW"],card:"Art of War"},{abbreviations:["BBD"],card:"Barraging Beatdown"},{abbreviations:["BBS"],card:"Big Blue Sky"},{abbreviations:["Starvo"],card:"Bravo, Star of the Show"},{abbreviations:["BEB"],card:"Bull's Eye Bracers"},{abbreviations:["BLW"],card:"Be Like Water"},{abbreviations:["BoJ"],card:"Balance of Justice"},{abbreviations:["BOHH"],card:"Blood on Her Hands"},{abbreviations:["BRB"],card:"Bloodrush Bellow"},{abbreviations:["Breezies"],card:"Breeze Rider Boots"},{abbreviations:["BTA"],card:"Burn Them All"},{abbreviations:["CStrike","C-Strike","C Strike"],card:"Celestial Cataclysm"},{abbreviations:["CBelly"],card:"Cerebellum Processor"},{abbreviations:["CLF"],card:"Channel Lake Frigid"},{abbreviations:["CLV"],card:"Channel Lightning Valley"},{abbreviations:["CMH"],card:"Channel Mount Heroic"},{abbreviations:["CMI"],card:"Channel Mount Isen"},{abbreviations:["CMT"],card:"Channel the Millennium Tree"},{abbreviations:["CnC","C&C"],card:"Command and Conquer"},{abbreviations:["Sea and Sea"],card:"Conqueror of the High Seas"},{abbreviations:["CYB"],card:"Count Your Blessings"},{abbreviations:["Cat","Kitty"],card:"Crouching Tiger"},{abbreviations:["CoD"],card:"Crown of Dominion"},{abbreviations:["CoP"],card:"Crown of Providence"},{abbreviations:["CtW"],card:"Crush the Weak"},{abbreviations:["DD"],card:"Death Dealer"},{abbreviations:["DnD"],card:"Devotion Never Dies"},{abbreviations:["DIO"],card:"Dash I/O"},{abbreviations:["EBTT"],card:"Even Bigger Than That!"},{abbreviations:["NewNigma"],card:"Enigma, New Moon"},{abbreviations:["EPot","E Pot"],card:"Energy Potion"},{abbreviations:["EStrike","E-Strike","E Strike"],card:"Enlightened Strike"},{abbreviations:["FFS"],card:"Fyendal's Fighting Spirit"},{abbreviations:["FoN"],card:"Force of Nature"},{abbreviations:["Frosty","\u{1F976}","\u{1F9CA}","\u2744\uFE0F"],card:"Frostbite"},{abbreviations:["Yum yum"],card:"Fruits of the Forest"},{abbreviations:["GnT"],card:"Give and Take"},{abbreviations:["Habibi"],card:"Hanabi Blaster"},{abbreviations:["HMH"],card:"Hope Merchant's Hood"},{abbreviations:["HoI"],card:"Heart of Ice"},{abbreviations:["Pumpkin"],card:"Jack-o'-lantern"},{abbreviations:["RKO","Cheato"],card:"Kayo, Underhanded Cheat"},{abbreviations:["KKBB"],card:"Knick Knack Bric-a-brac"},{abbreviations:["BStrike"],card:"Levels of Enlightenment"},{abbreviations:["LAG","Twominaris"],card:"Luminaris, Angel's Glow"},{abbreviations:["LCF","Newminaris"],card:"Luminaris, Celestial Fury"},{abbreviations:["LDE"],card:"Last Ditch Effort"},{abbreviations:["LtC"],card:"Lead the Charge"},{abbreviations:["LNW"],card:"Leave No Witnesses"},{abbreviations:["LFaL","L4aL"],card:"Life for a Life"},{abbreviations:["LiT"],card:"Lost in Thought"},{abbreviations:["MMB"],card:"Mage Master Boots"},{abbreviations:["MaxV"],card:"Maximum Velocity"},{abbreviations:["MoM"],card:"Mask of Momentum"},{abbreviations:["MoPL"],card:"Mask of the Pouncing Lynx"},{abbreviations:["MnG"],card:"Meat and Greet"},{abbreviations:["Cake","\u{1F382}"],card:"Ninth Blade of the Blood Oath"},{abbreviations:["PoM"],card:"Peace of Mind"},{abbreviations:["P-Bone"],card:"Performance Bonus"},{abbreviations:["PF","\u{1F525}"],card:"Phoenix Flame"},{abbreviations:["PtW"],card:"Poison the Well"},{abbreviations:["Thanos","Infinity Gauntlet"],card:"Polarity Reversal Script"},{abbreviations:["DPot","D Pot"],card:"Potion of D\xE9j\xE0 Vu"},{abbreviations:["Ponder Run"],card:"Premeditate"},{abbreviations:["Qi Unbound"],card:"Qi Unleashed"},{abbreviations:["RitL"],card:"Red in the Ledger"},{abbreviations:["Cats","Ghost cat","Ghost cats"],card:"Restless Coalescence"},{abbreviations:["Eugene"],card:"Rhinar, Reckless Rampage",isHidden:!0},{abbreviations:["SSGB"],card:"Sandscour Greatbow"},{abbreviations:["SSP"],card:"Sand Sketched Plan"},{abbreviations:["SFaS","S4aS"],card:"Scar for a Scar"},{abbreviations:["Tyler"],card:"Scurv, Stowaway",isHidden:!0},{abbreviations:["SWOMB"],card:"Shifting Winds of the Mystic Beast"},{abbreviations:["Snaps"],card:"Snapdragon Scalers"},{abbreviations:["SWK"],card:"Spinning Wheel Kick"},{abbreviations:["SFTL"],card:"Swing Fist, Think Later"},{abbreviations:["TTT"],card:"Take the Tempo"},{abbreviations:["Ultron"],card:"Teklovossen, the Mechropotent"},{abbreviations:["ToS"],card:"Test of Strength"},{abbreviations:["TAYG"],card:"That All You Got?"},{abbreviations:["TROM"],card:"This Round's on Me"},{abbreviations:["3oak"],card:"Three of a Kind"},{abbreviations:["Pox Malone","Post Malone"],card:"Virulent Touch"},{abbreviations:["Cast Homes"],card:"Visit Goldmane Estate"},{abbreviations:["Frosty Hammer"],card:"Winter's Wail"},{abbreviations:["Wreckless Wing"],card:"War Cry of Bellona"},{abbreviations:["ZTS"],card:"Zero to Sixty"}];var T=require("@flesh-and-blood/types");var Ie=(a=>(a.BannedFormats="bannedFormats",a.LegalFormats="legalFormats",a.LegalHeroes="legalHeroes",a))(Ie||{}),ee=Array.from(Array(50).keys()).map(e=>`${e}`),Fr=[{format:T.Format.ClassicConstructed,nicknames:["cc","classic"]},{format:T.Format.LivingLegend,nicknames:["cc ll","classic constructed ll","ll cc","ll","living legend"]},{format:T.Format.SilverAge,nicknames:["sage"]},{format:T.Format.GoldenAge,nicknames:["gage"]},{format:T.Format.UltimatePitFight,nicknames:["upf"]}],Mr=Object.values(T.Format).map(e=>{let r=Fr.find(({format:a})=>a===e),t=e.toLowerCase().replaceAll(f,"");return r?{...r,format:t}:{format:t}}),vr=[{hero:T.Hero.DataDoll,nicknames:["data","datadoll"]},{hero:T.Hero.Dorinthea,nicknames:["dori"]},{hero:T.Hero.Genis,nicknames:["genis"]},{hero:T.Hero.GravyBones,nicknames:["gravy"]},{hero:T.Hero.Iyslander,nicknames:["islander"]}],Ar=Object.values(T.Hero).map(e=>{let r=vr.find(({hero:a})=>a===e),t=e.toLowerCase().replaceAll(f,"");return r?{...r,hero:t}:{hero:t}}),Q=["common","rare","super rare","majestic","legendary","fabled"],Tr=(e,r,t,a)=>{let i=[];if(!r)i.push(...e);else for(let s of e)switch(r){case">=":{let o=!1;for(let n of Q)o?i.push(n):n===s&&(o=!0,i.push(n));break}case">":{let o=!1;for(let n of Q)o?i.push(n):n===s&&(o=!0);break}case"<=":{let o=!1;for(let n of Q.slice().reverse())o?i.push(n):n===s&&(o=!0,i.push(n));break}case"<":{let o=!1;for(let n of Q.slice().reverse())o?i.push(n):n===s&&(o=!0);break}default:break}return{filterToPropertyMapping:{nestedProperty:"rarity",property:"printings",isArray:!0},isExcluded:t,isOptional:a,isOr:!0,values:i}},Re=(e,r,t,a,i)=>{let s=a.map(d=>({hero:d.toLowerCase().replaceAll(f,"")})),o=[],n=[],c=[];for(let d of e){let p=Mr.find(({format:m,nicknames:F})=>m===d||!!F&&F.includes(d));if(p)n.push(p.format);else{let m=Ar.find(({hero:F,nicknames:M})=>F===d||!!M&&M.includes(d))||s.find(({hero:F})=>F===d);m&&c.push(m.hero)}}let g=i||"legalFormats";return n.length>0&&o.push({filterToPropertyMapping:{property:g,isArray:!0},values:n,isOr:!0,isExcluded:r,isOptional:t}),c.length>0&&o.push({filterToPropertyMapping:{property:"legalHeroes",isArray:!0},values:c,isOr:!0,isExcluded:r,isOptional:t}),o},Sr=(e,r,t,a)=>Re(e,r,t,a,"bannedFormats"),ue=(e,r,t,a,i,s)=>{let o=[];return wr(t)?o.push(...Re(a,e,r,s)):Lr(t)?o.push(...Sr(a,e,r,s)):Ir(t)&&o.push(Tr(a,i,e,r)),o},j=[{filterToPropertyMapping:{property:"cost",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"specialCost",isString:!0,partialMatch:!0},isExcluded:!0,values:["*","x"]},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["equipment","hero","placeholder","token","weapon"]}],O=[{filterToPropertyMapping:{property:"defense",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"specialDefense",isString:!0,partialMatch:!0},isExcluded:!0,values:["*","x"]},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["hero","placeholder","token","weapon"]}],X=[{filterToPropertyMapping:{property:"pitch",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["equipment","hero","placeholder","token","weapon"]},{filterToPropertyMapping:{property:"isCardBack",isBoolean:!0},isExcluded:!0,values:["true"]}],V=[{filterToPropertyMapping:{property:"power",isNumber:!0},isExcluded:!0,values:ee},{filterToPropertyMapping:{property:"specialPower",isString:!0,partialMatch:!0},isExcluded:!0,values:["*","x"]},{filterToPropertyMapping:{property:"types",isArray:!0,partialMatch:!0},isExcluded:!0,values:["equipment","hero","placeholder","token"]}],Z=[{filterToPropertyMapping:{property:"talents",isArray:!0},isExcluded:!0,values:Object.values(T.Talent).map(e=>e.toLowerCase())}],Pr={"!co":j,"-co":j,"!cost":j,"-cost":j,"!color":j,"-color":j,"!b":O,"-b":O,"!block":O,"-block":O,"!d":O,"-d":O,"!def":O,"-def":O,"!defense":O,"-defense":O,"!pitch":X,"-pitch":X,"!p":X,"-p":X,"!attack":V,"-attack":V,"!power":V,"-power":V,"!pwr":V,"-pwr":V,"!pow":V,"-pow":V,"!talents":Z,"-talents":Z,"!tal":Z,"-tal":Z},fe=e=>{let r=[],t=Pr[e];return t&&r.push(...t),r},xr=["l","legal","hero"],wr=e=>xr.includes(e),kr=["banned"],Lr=e=>kr.includes(e),Br=["r","rarity"],Ir=e=>Br.includes(e);var R=require("@flesh-and-blood/types"),ge=[{description:"Attack actions",expanded:["st:attack"],filters:{subtypes:[R.Subtype.Attack]},isCardProperty:!1,shorthands:["AA"]},{description:"Arcane barrier",expanded:['k:"arcane barrier"'],filters:{keywords:[R.Keyword.ArcaneBarrier]},isCardProperty:!1,shorthands:["AB"]},{description:"Attack reactions",expanded:['t:"attack reaction"'],filters:{types:[R.Type.AttackReaction]},isCardProperty:!1,shorthands:["AR"]},{description:"Defense reactions",expanded:['t:"defense reaction"'],filters:{types:[R.Type.DefenseReaction]},isCardProperty:!1,shorthands:["DR"]},{description:"Gain life",expanded:["gain {h}"],filters:{functionalText:"gain {h}"},isCardProperty:!1,shorthands:["Gain life","Gains life"]},{description:"Go again",expanded:['k:"go again"'],filters:{keywords:[R.Keyword.GoAgain]},isCardProperty:!1,shorthands:["GA"]},{description:"Non-attack actions",expanded:["t:action","st:non-attack"],filters:{subtypes:[R.Subtype.NonAttack],types:[R.Type.Action]},isCardProperty:!1,shorthands:["NAA"]},{description:"Plus defense",expanded:["+ {d}"],filters:{functionalText:"+ {d}"},isCardProperty:!1,shorthands:["Pump defense","Pumps defense","Buff defense","Buffs defense"]},{description:"Spellvoid",expanded:['k:"spellvoid"'],filters:{keywords:[R.Keyword.Spellvoid]},isCardProperty:!1,shorthands:["SV"]}],he=ge.filter(({shorthands:e})=>e.some(r=>r.includes(" "))).map(e=>({...e,shorthands:e.shorthands.filter(r=>r.includes(" ")).map(r=>r.toLowerCase()).sort((r,t)=>t.length-r.length)})),be=ge.filter(({shorthands:e})=>e.some(r=>!r.includes(" "))).map(e=>({...e,shorthands:e.shorthands.filter(r=>!r.includes(" ")).map(r=>r.toLowerCase()).sort((r,t)=>t.length-r.length)}));var z=e=>q(e.toLowerCase().trim().replace(f,"")),q=e=>e.normalize("NFD").replace(/\p{Diacritic}/gu,"");var Rr=(e,r)=>{let t=[];if(e)for(let a of r){let i=a.name===e.name,s=a.cardIdentifier!==e.cardIdentifier,o=a.pitch!==e.pitch;i&&s&&o&&t.push(a)}return t},Er=(e,r)=>{let t=new Set(e?.referencedCards),a=[];for(let i of r)t.has(i.cardIdentifier)&&a.push(i);return a},ye=e=>{let r=new Map;for(let t of e)for(let a of t.referencedCards||[]){let i=r.get(a);i?i.push(t):r.set(a,[t])}return r},Nr=(e,r)=>{let t=new Set;for(let i of e)for(let s of i.createdExtras||[])t.add(s);let a=[];for(let i of r)t.has(i.cardIdentifier)&&a.push(i);return a};var me=e=>{let r=new Map,t=new Map,a=[],i=new Map,s=0;for(let o of e){r.set(o.cardIdentifier,o),t.set(o.cardIdentifier,s),s++;let n=z(o.name),c=i.get(n);c?c.push(o):(i.set(n,[o]),a.push(n))}return{cards:e,cardByCardIdentifier:r,corpusPositionByCardIdentifier:t,cleanedNames:a,pitchCycleByCleanedName:i,referencingCardsByCardIdentifier:ye(e)}},Ce=(e,r)=>e.pitchCycleByCleanedName.get(z(r.name))||[],re=(e,r)=>{let t=z(r),a=e.pitchCycleByCleanedName.get(t);if(!a){let i=e.cleanedNames.find(s=>s.includes(t));i&&(a=e.pitchCycleByCleanedName.get(i))}return a||[]},Ee=(e,r)=>{let t=new Map;for(let i of r)for(let s of Ce(e,i))t.set(s.cardIdentifier,s);let a=({cardIdentifier:i})=>e.corpusPositionByCardIdentifier.get(i)||0;return[...t.values()].sort((i,s)=>a(i)-a(s))},te=(e,r)=>{let t=[];for(let a of Ce(e,r))t.push(...e.referencingCardsByCardIdentifier.get(a.cardIdentifier)||[]);return Ee(e,t)},ie=(e,r)=>{let t=[];for(let a of Ce(e,r))for(let i of a.referencedCards||[]){let s=e.cardByCardIdentifier.get(i);s&&t.push(s)}return Ee(e,t)};var sr=[">=",">","<=","<"],or=["!","-"],Dr={property:"arcane",specialProperty:"specialArcane",isNumber:!0,partialMatch:!0},Fe={property:"artists",isArray:!0,partialMatch:!0},Ne={property:"n/a",isMeta:!0},De={property:"bonds",isArray:!0},Or={property:"cardIdentifier",isString:!0,isNormalized:!0},Oe=(e,{isAnd:r,isExcluded:t,isOptional:a,modifier:i})=>({filterToPropertyMapping:Or,values:[...e],valuesSet:e,isAnd:r,isOr:!0,modifier:i,isExcluded:t,isOptional:a}),Hr={property:"n/a"},He={property:"classes",isArray:!0,partialMatch:!0},Ve={property:"cost",specialProperty:"specialCost",isNumber:!0,partialMatch:!0},_={property:"defense",specialProperty:"specialDefense",isNumber:!0},Ge={property:"flows",isArray:!0},We={nestedProperty:"foiling",property:"printings",isArray:!0},Ue={property:"fusions",isArray:!0},Ke={property:"intellect",isNumber:!0},je={property:"keywords",isArray:!0},Me={property:"n/a",isMeta:!0},ze={property:"life",specialProperty:"specialLife",isNumber:!0},Se={property:"meta",isArray:!0},qe={property:"name",isString:!0,partialMatch:!0},ve={property:"pitch",isNumber:!0},_e={property:"firstReleaseDate",isDate:!0},ae={property:"power",specialProperty:"specialPower",isNumber:!0},Vr={property:"setIdentifiers",isArray:!0,partialMatch:!0},$e={property:"n/a",isMeta:!0},Gr={property:"n/a"},Wr={property:"n/a"},Ye={property:"sets",isArray:!0,partialMatch:!0},Ae={property:"shorthands",isArray:!0,partialMatch:!0},se={property:"specializations",isArray:!0,partialMatch:!0},Je={property:"subtypes",isArray:!0},Qe={property:"types",isArray:!0},Xe={property:"talents",isArray:!0},Ur={property:"functionalText",isString:!0,partialMatch:!0},Kr={property:"traits",isArray:!0,partialMatch:!0},jr={property:"typeText",isString:!0,partialMatch:!0},oe={nestedProperty:"treatments",property:"printings",isArray:!0,isNestedPropertyArray:!0},zr={property:"firstReleaseDate",isString:!0,partialMatch:!0},xe={arcane:Dr,a:Fe,artist:Fe,art:Fe,attack:ae,b:_,block:_,banned:Ne,bond:De,bonds:De,c:He,class:He,chain:Hr,co:Ve,cost:Ve,color:ve,d:_,def:_,defense:_,flow:Ge,flows:Ge,f:Ue,fusion:Ue,foil:We,foiling:We,i:Ke,intellect:Ke,is:Se,k:je,keyword:je,l:Me,legal:Me,hero:Me,li:ze,life:ze,meta:Se,n:qe,name:qe,p:ve,pitch:ve,pwr:ae,pow:ae,power:ae,print:Vr,r:$e,rarity:$e,referencedby:Gr,references:Wr,rf:Ne,s:Ye,set:Ye,short:Ae,shorthand:Ae,shorthands:Ae,sp:se,spec:se,specialization:se,specializations:se,st:Je,subtype:Je,t:Qe,type:Qe,tal:Xe,talent:Xe,text:Ur,trait:Kr,treat:oe,treatment:oe,var:oe,variation:oe,x:jr,year:zr},qr=[{text:l.Release.ClassicBattlesRhinarDorinthea.toLowerCase(),override:l.Release.ClassicBattlesRhinarDorinthea.toLowerCase().replaceAll(f,"")}],_r=e=>{let r=[],t=e.replaceAll("\u201D",'"');for(let{text:i,override:s}of qr)t.includes(i)&&(t=t.replace(i,s));if(J(t)?.card)r.push(t);else{let i=t.split(/[ ]+/),s="",o=0;for(let n of i)o===2&&(r.push(s.trim().replaceAll('"',"")),s="",o=0),o<2&&n.split('"').length===2?(s+=" "+n,o++):o===0&&s===""?r.push(n):o===1&&(s+=" "+n);o===2&&(r.push(s.trim().replaceAll('"',"")),s="",o=0)}return r},$r=20,we=(e,r,t=[],a=[],i=rt())=>{let s=e.trim().toLowerCase();for(let{expanded:S,shorthands:P}of he)for(let u of P)if(s.includes(u)){s=s.replace(u,S.join(" "));break}for(let[S,P]of Object.entries(l.setToSetIdentifierMappings))s.includes(S.toLowerCase())&&(s=s.replace(S.toLowerCase(),P[0]));let o=_r(s),n=[];for(let S of o){let P=be.find(({shorthands:u})=>u.includes(S));P&&!P.isCardProperty?n.push(...P.expanded):n.push(S)}let c=[],g=[],d=[],p=[],m=!1,F=[],M=[],B=[],G=[];for(let S of n)if(lt(S)){let[P,u]=S.split(":"),{modifier:E,values:h,isAnd:b,isOr:x}=ot(u),{filterKey:C,isExcluded:w,isOptional:L,isMeta:ce}=nt(P),W=!1;if(ce){if(["rarity","r"].includes(C)){let U=st(h);w||(M=[...U]),h=U.map(v=>v.toLowerCase())}["legal","l","hero"].includes(C),c.push(...ue(w,L,C,h,E,t))}else{if(["chain"].includes(C)){let v=new Set,k=[],N=new Set,D=A=>{v.add(A.cardIdentifier),N.has(A.name)||(N.add(A.name),k.push(A))},K=A=>{A.types.includes(l.Type.Hero)||D(A)};for(let A of h)if(A)for(let I of re(r,A))D(I);let H=0;for(let A of k){if(H>$r)break;for(let pe of ie(r,A))K(pe);if(H===0)for(let pe of te(r,A))K(pe);H++}c.push(Oe(v,{isAnd:b,isExcluded:w,isOptional:L,modifier:E})),W=!0}else if(["referencedby","references"].includes(C)){let v=["referencedby"].includes(C),k=new Set;for(let N of h)if(N)for(let D of re(r,N)){let K=v?ie(r,D):te(r,D);for(let H of K)k.add(H.cardIdentifier)}c.push(Oe(k,{isAnd:b,isExcluded:w,isOptional:L,modifier:E})),W=!0}else if(["art","artist"].includes(C))g=h;else if(["print","prints","printing","printings"].includes(C))F=h;else if(["is","meta"].includes(C)){let v=[],k=[],N=[],D=[];for(let I of h)Xr.includes(I)?v.push(I):Zr.includes(I)?k.push(I):et.includes(I)?N.push(I):D.push(I);v.length>0&&(c.push({filterToPropertyMapping:Se,values:[l.Meta.Reprint.toLowerCase().replaceAll(f,"")],isAnd:b,isOr:x,isExcluded:!w,isOptional:L}),W=D.length===0);let K=k.length>0;K&&c.push({filterToPropertyMapping:_e,values:[i],isAnd:b,isOr:x,isExcluded:w,isOptional:L});let H=N.length>0;H&&c.push({filterToPropertyMapping:_e,values:[i],isAnd:b,isOr:x,isExcluded:!w,isOptional:L}),(K||H)&&(W=D.length===0);let A=tt(D);h=A.map(I=>I.toLowerCase().replaceAll(f,"")),A.includes(l.Meta.Expansion)&&!w&&(m=!0)}else["foiling","foil"].includes(C)?(p=it(h),h=p.map(v=>v.toLowerCase())):["treat","treatment","var","variation"].includes(C)?(G=at(h),h=G.map(v=>v.toLowerCase())):["set","s"].includes(C)?(B=Yr(h,a),h=B.map(v=>v.toLowerCase().replaceAll(f,""))):["pitch","p","color"].includes(C)&&(h=Qr(h));let U=xe[C];U&&!W&&c.push({filterToPropertyMapping:U,values:h,isAnd:b,isOr:x,modifier:E,isExcluded:w,isOptional:L})}}else if(S){let P=J(S)?.card,u=fe(S);P?d.push(`"${P.toLowerCase().replace(f,"")}"`):u&&u.length>0?c.push(...u):d.push(S.replace(f,""))}return{appliedFilters:c,attributes:{artists:g,foilings:p,isExpansionSlot:m,prints:F,rarities:M,releases:B,treatments:G},keywords:d}},Yr=(e,r=[])=>{let t=[];for(let a of e)t.push(...Jr(a,r));return t},Jr=(e,r=[])=>{let t=[],a=Object.values(l.Release).find(i=>i.toLowerCase().replaceAll(f,"")===e);if(a&&t.push(a),t.length===0){let i=l.setIdentifierToSetMappings[e];i&&t.push(i)}if(t.length===0){let i=Object.values(l.Release).filter(s=>s.toLowerCase().includes(e));i.length>0&&t.push(...i)}if(t.length===0){let i=r.find(s=>s.toLowerCase().replaceAll(f,"")===e);i&&t.push(i)}return t},Te={purple:4,blue:3,yellow:2,red:1,white:0},Qr=e=>{let r=[];for(let t of e)Te[t]||Te[t]===0?r.push(Te[t].toString()):r.push(t);return r},Xr=["unique"],Zr=["preview","spoiler","unreleased"],et=["released"],rt=()=>{let e=new Date,r=`${e.getMonth()+1}`.padStart(2,"0"),t=`${e.getDate()}`.padStart(2,"0");return`${e.getFullYear()}-${r}-${t}`},Ze={dual:l.Meta.DualClass,exp:l.Meta.Expansion,expansion:l.Meta.Expansion,expansionSlot:l.Meta.Expansion,rainbow:l.Meta.Rainbow,reprint:l.Meta.Reprint,reprints:l.Meta.Reprint},tt=e=>{let r=[];for(let t of e)Ze[t]?r.push(Ze[t]):l.Meta[t]&&r.push(l.Meta[t]);if(e.length>0&&r.length===0){for(let t of Object.values(l.Meta))for(let a of e)if(t.toLowerCase().includes(a)){r.push(t);break}}return r},er={r:l.Foiling.Rainbow,rf:l.Foiling.Rainbow,rainbow:l.Foiling.Rainbow,c:l.Foiling.Cold,cf:l.Foiling.Cold,cold:l.Foiling.Cold,g:l.Foiling.Gold,gf:l.Foiling.Gold,gold:l.Foiling.Gold},it=e=>{let r=[];for(let t of e)er[t]&&r.push(er[t]);return r},rr={...Object.values(l.Treatment).reduce((e,r)=>(e[r.toLowerCase()]=r,e),{}),aa:l.Treatment.AA,alt:l.Treatment.AA,"alt art":l.Treatment.AA,ab:l.Treatment.AB,"alt border":l.Treatment.AB,at:l.Treatment.AT,"alt text":l.Treatment.AT,ea:l.Treatment.EA,extended:l.Treatment.EA,"extended art":l.Treatment.EA,fa:l.Treatment.FA,full:l.Treatment.FA,"full art":l.Treatment.FA},at=e=>{let r=[];for(let t of e)rr[t]?r.push(rr[t]):l.Treatment[t.toUpperCase()]&&r.push(l.Treatment[t.toUpperCase()]);return r},Pe={b:l.Rarity.Basic,c:l.Rarity.Common,f:l.Rarity.Fabled,l:l.Rarity.Legendary,m:l.Rarity.Majestic,p:l.Rarity.Promo,r:l.Rarity.Rare,s:l.Rarity.SuperRare,t:l.Rarity.Token,v:l.Rarity.Marvel},st=e=>{let r=[];for(let t of e)Pe[t]?r.push(Pe[t]):r.push(t);return r},ot=e=>{let r=[],t,a,i=sr.find(s=>e.includes(s));if(i){let[,s]=e.split(i);tr(s)?(t=!0,r.push(...s.trim().split("+").map(o=>o.replace(f,"")))):ir(s)?(a=!0,r.push(...s.trim().split(",").map(o=>o.replace(f,"")))):r.push(s.trim().replace(f,""))}else tr(e)?(t=!0,r.push(...e.trim().split("+").map(s=>s.replace(f,"")))):ir(e)?(a=!0,r.push(...e.trim().split(",").map(s=>s.replace(f,"")))):e.startsWith('"')&&e.endsWith('"')?r.push(e.trim().replaceAll('"',"").replace(f,"")):r.push(e.trim().replace(f,""));return{modifier:i,values:r,isAnd:t,isOr:a}},nt=e=>{let r=ct(e);if(r){let[,t]=e.split(r);return{filterKey:t,isExcluded:!0,isOptional:!1,isMeta:ar(t)}}else return{filterKey:e,isExcluded:!1,isOptional:!1,isMeta:ar(e)}},lt=e=>e.indexOf(":")>=0,tr=e=>e.indexOf("+")>=0,ir=e=>e.indexOf(",")>=0,ar=e=>!!xe[e]?.isMeta,ct=e=>or.find(r=>e.includes(r))?.slice(0,1);var y=require("@flesh-and-blood/types"),nr={artists:["Hoodwill"],cardIdentifier:"fangs-a-lot-blue",classes:[y.Class.Generic],defaultImage:"FNG000",firstReleaseDate:"2022-06-02",functionalText:"If Fangs A Lot is put into your banished zone from your graveyard, instead put it into your hand.",legalFormats:[],legalHeroes:[y.Hero.Kayo,y.Hero.Levia,y.Hero.Rhinar],printings:[{artists:["Hoodwill"],identifier:"FNG000",image:"FNG000",print:"FNG000",rarity:y.Rarity.Rare,set:y.Release.Promos},{artists:["Hoodwill"],identifier:"FNG000",image:"FNG000_Marvel",print:`FNG000-${y.Treatment.FA}`,rarity:y.Rarity.Marvel,set:y.Release.Promos,treatment:y.Treatment.FA}],name:"Fangs A Lot",rarities:[y.Rarity.Rare,y.Rarity.Marvel],rarity:y.Rarity.Rare,sets:[y.Release.Promos],setIdentifiers:["FNG000"],specialImage:"FNG000_Marvel",subtypes:[y.Subtype.Attack],types:[y.Type.Action],typeText:"Generic Action - Attack"},lr=[{keyword:nr.name.toLowerCase(),card:nr}];var Le=class{constructor(r,t=[],a=[],i=!1){this.log=(r,...t)=>{this.debug&&console.log(r,...t)};this.search=(r,t)=>{let a,{appliedFilters:i,attributes:s,keywords:o}=we(r,this.index,this.additionalHeroes,this.additionalSets),n=o.join(" "),c=t?lr.filter(u=>u.keyword===n):[];if(c.length>0?a=c.map(({card:u})=>u):o.length?a=this.fuse.search(n).map(u=>u.item):a=[...this.cards],i.length&&(a=a.filter(u=>u&&pr(u,i))),o.length===0){let u="";if(s.releases.length===1)try{let b=s.releases[0];u=ne.setToSetIdentifierMappings[b][0].toUpperCase()}catch(b){console.error("Error getting set identifier from search",b)}if(!u&&s.prints.length===1)try{let b=s.prints[0];ne.setIdentifierToSetMappings[b]&&(u=b.toUpperCase())}catch(b){console.error("Error getting set identifier from search",b)}u?a.sort((b,x)=>{let C=b.setIdentifiers.find(L=>L.includes(u))?.replace(u,""),w=x.setIdentifiers.find(L=>L.includes(u))?.replace(u,"");return C&&w?C.localeCompare(w):-1}):a.sort((b,x)=>b.name===x.name?`${b.pitch}`.localeCompare(`${x.pitch}`):b.name.localeCompare(x.name))}else{let u=[],E=[],h=o.map(b=>b.toLowerCase().replace(f,"")).join(" ");for(let b of a)b.name.toLowerCase().replace(f,"")===h?u.push(b):E.push(b);a=[...u,...E]}let g,{artists:d,isExpansionSlot:p,foilings:m,prints:F,rarities:M,releases:B,treatments:G}=s;(d.length>0||p||m.length>0||F.length>0||M.length>0||B.length>0||G.length>0)&&(g=a.map(u=>{let E=u.printings.filter(h=>{let b=!!h.image,x=d.length===0||d.some(k=>h.artists.find(N=>N.replace(f,"").toLowerCase().includes(k))),C=!p||p===h.isExpansionSlot,w=m.length===0||m.includes(h.foiling),L=F.length===0||F.some(k=>h.identifier.includes(k.toUpperCase())),ce=M.length===0||M.includes(h.rarity),W=B.length===0||B.includes(h.set),U=G.length===0||h.treatments?.some(k=>G.includes(k));return b&&x&&C&&w&&L&&ce&&W&&U});return{...u,matchingPrintings:E}}));let P=g?.length>0?g:a;return{appliedFilters:i,attributes:s,keywords:o,searchResults:P}};let s={getFn:(o,n)=>{let c=ke.default.config.getFn(o,n);return c&&(Array.isArray(c)?c.map(g=>q(g.replace(f,""))):q(c).replace(f,""))},ignoreLocation:!0,includeScore:!0,keys:[{name:"name",weight:10},{name:"functionalText",weight:6},{name:"shorthands",weight:4},{name:"setIdentifiers",weight:2},{name:"traits",weight:4},{name:"typeText",weight:6}],threshold:.15,useExtendedSearch:!0};this.additionalHeroes=t,this.additionalSets=a,this.cards=[...r],this.debug=i,this.fuse=new ke.default([...r],s),this.index=me(this.cards)}},cr=Le,pr=(e,r)=>{let t=!0,a=!1;for(let i of r){let s=i.isOptional,{isNumber:o,isString:n,isArray:c,isBoolean:g,isDate:d}=i.filterToPropertyMapping;if(o){let p=pt(e,i);s?p&&(a=!0):t=t&&p}else if(n){let p=dt(e,i);s?p&&(a=!0):t=t&&p}else if(c){let p=ut(e,i,r);s?p&&(a=!0):t=t&&p}else if(g){let p=ft(e,i);s?p&&(a=!0):t=t&&p}else if(d){let p=gt(e,i);s?p&&(a=!0):t=t&&p}}return t},pt=(e,r)=>{if($(r,e)){let{values:t,modifier:a,isExcluded:i,filterToPropertyMapping:{partialMatch:s}}=r,o=le(e,r);if(o!=null&&!isNaN(o))if(o=parseInt(o),a)switch(a){case">=":{let n=t?.some(c=>o>=parseInt(c));return i?!n:n}case">":{let n=t?.some(c=>o>parseInt(c));return i?!n:n}case"<=":{let n=t?.some(c=>o<=parseInt(c));return i?!n:n}case"<":{let n=t?.some(c=>o<parseInt(c));return i?!n:n}default:return!1}else{let n=t?.some(c=>o===parseInt(c));return i?!n:n}else{let n=bt(e,r)?.toLowerCase(),c=s?t?.some(g=>n?.includes(g)):t?.some(g=>n===g);return i?!c:c}}else return!0},dt=(e,r)=>{if($(r,e)){let{values:t,valuesSet:a,isAnd:i,isExcluded:s,filterToPropertyMapping:{isNormalized:o,partialMatch:n}}=r,c=le(e,r),g=o?c:c?.replaceAll(f,"").toLowerCase();if(n){let d=i?t?.every(p=>g?.includes(p)):t?.some(p=>g?.includes(p));return s?!d:d}else{let d;return i?d=t?.every(p=>g===p):a?d=a.has(g):d=t?.some(p=>g===p),s?!d:d}}else return!0},ut=(e,r,t)=>{if($(r,e)){let{values:a,isAnd:i,isExcluded:s,filterToPropertyMapping:{partialMatch:o}}=r,n=ht(e,r,t).map(c=>c?.replaceAll(f,""));if(o){let c=i?a.every(d=>n?.some(p=>p?.toLowerCase().includes(d))):a.some(d=>n?.some(p=>p?.toLowerCase().includes(d))),g=n.length===0;return s?!c||g:c}else{let c=i?a.every(g=>n?.some(d=>d?.toLowerCase()===g)):a.some(g=>n?.some(d=>d?.toLowerCase()===g));return s?!c:c}}else return!0},ft=(e,r)=>{if($(r,e)){let{isExcluded:t}=r,a=le(e,r);return t?!a:a}else return!0},gt=(e,r)=>{if($(r,e)){let{values:t,isExcluded:a}=r,i=le(e,r),s=t?.some(o=>i>o);return a?!s:s}else return!0},le=(e,r)=>{let{filterToPropertyMapping:t}=r;return e[t.property]},ht=(e,r,t)=>{let{filterToPropertyMapping:{isNestedPropertyArray:a,nestedProperty:i,property:s}}=r,o=e[s]||[],n=[],c=Object.keys(e.legalOverrides||{}).length>0,g=r.filterToPropertyMapping.property==="legalHeroes",d=t.find(({filterToPropertyMapping:m})=>m.property==="legalFormats");if(c&&g&&!!d){let m=new Set;for(let{format:F,heroes:M}of e.legalOverrides||[])if(d.values.includes(F.toLowerCase()))for(let B of M)m.add(B);n=Array.from(m)}if(n.length===0)if(i){let m=new Set;for(let F of o)if(a){let M=F[i]||[];for(let B of M)m.add(B)}else{let M=F[i];M&&m.add(M)}n=Array.from(m)}else n=o;return n},bt=(e,r)=>{let{filterToPropertyMapping:t}=r;return e[t.specialProperty]},$=({cardTypes:e},{types:r,subtypes:t})=>!e||e?.some(a=>r.map(i=>i.toLowerCase()).includes(a.toLowerCase())||t.map(i=>i.toLowerCase()).includes(a.toLowerCase()));0&&(module.exports={FilterProperty,PUNCTUATION,RARITY_VALUES_MAPPING,abbreviations,availableExclusions,availableModifiers,buildSearchIndex,filterCard,filtersToCardPropertyMappings,getAbbreviation,getAbbreviationByCard,getCardsByName,getCardsByReferencedCardIdentifier,getCardsReferencedBy,getCardsReferencing,getCleanText,getExcludedMetaFilters,getKeywordsAndAppliedFiltersFromText,getMetaFilters,getNormalizedText,getOtherPitches,getReferencedCards,getTokensReferencedByCards,multiWordShorthands,shorthands,singleWordShorthands});
package/dist/index.d.ts CHANGED
@@ -6,4 +6,5 @@ export * from "./helpers.js";
6
6
  export * from "./metaFilters.js";
7
7
  export * from "./related.js";
8
8
  export * from "./search.js";
9
+ export * from "./searchIndex.js";
9
10
  export * from "./shorthands.js";
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ export * from "./helpers.js";
6
6
  export * from "./metaFilters.js";
7
7
  export * from "./related.js";
8
8
  export * from "./search.js";
9
+ export * from "./searchIndex.js";
9
10
  export * from "./shorthands.js";
10
11
  export {
11
12
  default2 as default
package/dist/related.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare const getReferencedCards: (card: Card | undefined, cards: Card[])
8
8
  * answers the reverse relation for every card in the corpus, so a page or a
9
9
  * filter asking about several of them builds this once.
10
10
  */
11
- export declare const getCardsByReferencedCardIdentifier: (cards: Card[]) => Map<string, Card[]>;
11
+ export declare const getCardsByReferencedCardIdentifier: <CardType extends Card>(cards: CardType[]) => Map<string, CardType[]>;
12
12
  /**
13
13
  * The extras a set of cards brings, out of the ones available to them. A card
14
14
  * carries what it creates, so the hero's own card has to be among the cards for
package/dist/search.d.ts CHANGED
@@ -21,6 +21,7 @@ declare class Search {
21
21
  private cards;
22
22
  private debug;
23
23
  private fuse;
24
+ private index;
24
25
  constructor(cards: DoubleSidedCard[], additionalHeroes?: Hero[], additionalSets?: Release[], debug?: boolean);
25
26
  log: (message?: any, ...optionalParams: any[]) => void;
26
27
  search: (text: string, includeMemes?: boolean) => SearchResults;
package/dist/search.js CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  import { memes } from "./memes.js";
11
11
  import { getNormalizedText } from "./helpers.js";
12
12
  import { FilterProperty } from "./metaFilters.js";
13
+ import { buildSearchIndex } from "./searchIndex.js";
13
14
  class Search {
14
15
  constructor(cards, additionalHeroes = [], additionalSets = [], debug = false) {
15
16
  this.log = (message, ...optionalParams) => {
@@ -21,7 +22,7 @@ class Search {
21
22
  let results;
22
23
  const { appliedFilters, attributes, keywords } = getKeywordsAndAppliedFiltersFromText(
23
24
  text,
24
- this.cards,
25
+ this.index,
25
26
  this.additionalHeroes,
26
27
  this.additionalSets
27
28
  );
@@ -165,6 +166,7 @@ class Search {
165
166
  this.cards = [...cards];
166
167
  this.debug = debug;
167
168
  this.fuse = new Fuse([...cards], searchOptions);
169
+ this.index = buildSearchIndex(this.cards);
168
170
  }
169
171
  }
170
172
  var search_default = Search;
@@ -300,27 +302,25 @@ const getDoesCardMatchStringFilter = (card, filter) => {
300
302
  } else {
301
303
  const {
302
304
  values,
305
+ valuesSet,
303
306
  isAnd,
304
307
  isExcluded: excluded,
305
- filterToPropertyMapping: { partialMatch }
308
+ filterToPropertyMapping: { isNormalized, partialMatch }
306
309
  } = filter;
307
- const cardValue = getCardValue(card, filter)?.replaceAll(
308
- PUNCTUATION,
309
- ""
310
- );
310
+ const storedValue = getCardValue(card, filter);
311
+ const cardValue = isNormalized ? storedValue : storedValue?.replaceAll(PUNCTUATION, "").toLowerCase();
311
312
  if (partialMatch) {
312
- const isPartialMatch = isAnd ? values?.every(
313
- (filterValue) => cardValue?.toLowerCase().includes(filterValue)
314
- ) : values?.some(
315
- (filterValue) => cardValue?.toLowerCase().includes(filterValue)
316
- );
313
+ const isPartialMatch = isAnd ? values?.every((filterValue) => cardValue?.includes(filterValue)) : values?.some((filterValue) => cardValue?.includes(filterValue));
317
314
  return excluded ? !isPartialMatch : isPartialMatch;
318
315
  } else {
319
- const isFullMatch = isAnd ? values?.every(
320
- (filterValue) => cardValue?.toLowerCase() === filterValue
321
- ) : values?.some(
322
- (filterValue) => cardValue?.toLowerCase() === filterValue
323
- );
316
+ let isFullMatch;
317
+ if (isAnd) {
318
+ isFullMatch = values?.every((filterValue) => cardValue === filterValue);
319
+ } else if (valuesSet) {
320
+ isFullMatch = valuesSet.has(cardValue);
321
+ } else {
322
+ isFullMatch = values?.some((filterValue) => cardValue === filterValue);
323
+ }
324
324
  return excluded ? !isFullMatch : isFullMatch;
325
325
  }
326
326
  }
@@ -0,0 +1,26 @@
1
+ import { Card, DoubleSidedCard } from "@flesh-and-blood/types";
2
+ /**
3
+ * Lookups over a corpus, built once per `Search` instance. Every relation is
4
+ * keyed at the name level: a reference is between Cards, so a reference to or
5
+ * from one pitch counts for every pitch of that card.
6
+ */
7
+ export interface SearchIndex {
8
+ cards: DoubleSidedCard[];
9
+ cardByCardIdentifier: Map<string, DoubleSidedCard>;
10
+ /** Corpus order, for restoring it after a lookup returns cards out of order. */
11
+ corpusPositionByCardIdentifier: Map<string, number>;
12
+ /** Distinct cleaned names, in the order the corpus first carries them. */
13
+ cleanedNames: string[];
14
+ pitchCycleByCleanedName: Map<string, DoubleSidedCard[]>;
15
+ referencingCardsByCardIdentifier: Map<string, DoubleSidedCard[]>;
16
+ }
17
+ export declare const buildSearchIndex: (cards: DoubleSidedCard[]) => SearchIndex;
18
+ /**
19
+ * Every pitch of the named card. An exact name wins; failing that the first
20
+ * name containing the text answers, so a fragment resolves to one card.
21
+ */
22
+ export declare const getCardsByName: (index: SearchIndex, name: string) => DoubleSidedCard[];
23
+ /** The cards naming the card, at every pitch of both. */
24
+ export declare const getCardsReferencing: (index: SearchIndex, card: Card) => DoubleSidedCard[];
25
+ /** The cards the card names, at every pitch of both. */
26
+ export declare const getCardsReferencedBy: (index: SearchIndex, card: Card) => DoubleSidedCard[];
@@ -0,0 +1,85 @@
1
+ import { getCleanText } from "./helpers.js";
2
+ import { getCardsByReferencedCardIdentifier } from "./related.js";
3
+ const buildSearchIndex = (cards) => {
4
+ const cardByCardIdentifier = /* @__PURE__ */ new Map();
5
+ const corpusPositionByCardIdentifier = /* @__PURE__ */ new Map();
6
+ const cleanedNames = [];
7
+ const pitchCycleByCleanedName = /* @__PURE__ */ new Map();
8
+ let corpusPosition = 0;
9
+ for (const card of cards) {
10
+ cardByCardIdentifier.set(card.cardIdentifier, card);
11
+ corpusPositionByCardIdentifier.set(card.cardIdentifier, corpusPosition);
12
+ corpusPosition++;
13
+ const cleanedName = getCleanText(card.name);
14
+ const pitchCycle = pitchCycleByCleanedName.get(cleanedName);
15
+ if (pitchCycle) {
16
+ pitchCycle.push(card);
17
+ } else {
18
+ pitchCycleByCleanedName.set(cleanedName, [card]);
19
+ cleanedNames.push(cleanedName);
20
+ }
21
+ }
22
+ return {
23
+ cards,
24
+ cardByCardIdentifier,
25
+ corpusPositionByCardIdentifier,
26
+ cleanedNames,
27
+ pitchCycleByCleanedName,
28
+ referencingCardsByCardIdentifier: getCardsByReferencedCardIdentifier(cards)
29
+ };
30
+ };
31
+ const getPitchCycle = (index, card) => index.pitchCycleByCleanedName.get(getCleanText(card.name)) || [];
32
+ const getCardsByName = (index, name) => {
33
+ const cleanedName = getCleanText(name);
34
+ let pitchCycle = index.pitchCycleByCleanedName.get(cleanedName);
35
+ if (!pitchCycle) {
36
+ const containingName = index.cleanedNames.find(
37
+ (candidate) => candidate.includes(cleanedName)
38
+ );
39
+ if (containingName) {
40
+ pitchCycle = index.pitchCycleByCleanedName.get(containingName);
41
+ }
42
+ }
43
+ return pitchCycle || [];
44
+ };
45
+ const getCardsWithPitchSiblings = (index, cards) => {
46
+ const cardByCardIdentifier = /* @__PURE__ */ new Map();
47
+ for (const card of cards) {
48
+ for (const pitch of getPitchCycle(index, card)) {
49
+ cardByCardIdentifier.set(pitch.cardIdentifier, pitch);
50
+ }
51
+ }
52
+ const getCorpusPosition = ({ cardIdentifier }) => index.corpusPositionByCardIdentifier.get(cardIdentifier) || 0;
53
+ return [...cardByCardIdentifier.values()].sort(
54
+ (first, second) => getCorpusPosition(first) - getCorpusPosition(second)
55
+ );
56
+ };
57
+ const getCardsReferencing = (index, card) => {
58
+ const referencingCards = [];
59
+ for (const pitch of getPitchCycle(index, card)) {
60
+ referencingCards.push(
61
+ ...index.referencingCardsByCardIdentifier.get(pitch.cardIdentifier) || []
62
+ );
63
+ }
64
+ return getCardsWithPitchSiblings(index, referencingCards);
65
+ };
66
+ const getCardsReferencedBy = (index, card) => {
67
+ const referencedCards = [];
68
+ for (const pitch of getPitchCycle(index, card)) {
69
+ for (const referencedCardIdentifier of pitch.referencedCards || []) {
70
+ const referencedCard = index.cardByCardIdentifier.get(
71
+ referencedCardIdentifier
72
+ );
73
+ if (referencedCard) {
74
+ referencedCards.push(referencedCard);
75
+ }
76
+ }
77
+ }
78
+ return getCardsWithPitchSiblings(index, referencedCards);
79
+ };
80
+ export {
81
+ buildSearchIndex,
82
+ getCardsByName,
83
+ getCardsReferencedBy,
84
+ getCardsReferencing
85
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@flesh-and-blood/search",
3
3
  "description": "TypeScript search engine for Flesh and Blood cards",
4
- "version": "4.1.0",
4
+ "version": "4.2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "main": "dist/index.cjs",
@@ -46,8 +46,8 @@
46
46
  "@flesh-and-blood/types": "^4.0.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@flesh-and-blood/cards": "^4.1.0",
50
- "@flesh-and-blood/types": "^4.1.0",
49
+ "@flesh-and-blood/cards": "^4.2.0",
50
+ "@flesh-and-blood/types": "^4.1.1",
51
51
  "@types/jest": "^29.5.11",
52
52
  "@types/node": "^20.10.5",
53
53
  "esbuild": "^0.19.10",
@@ -77,5 +77,5 @@
77
77
  "FAB",
78
78
  "FABTCG"
79
79
  ],
80
- "gitHead": "bb392bb60feaad216eb7bfd340e2de705b989e9e"
80
+ "gitHead": "41a6b8eda11e89d0f75d56941a9d3a106abf0aa5"
81
81
  }