@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.
- package/README.md +19 -5
- package/dist/abbreviations.js +385 -0
- package/dist/constants.js +4 -0
- package/dist/filters.js +695 -0
- package/dist/helpers.js +15 -0
- package/dist/index.cjs +1 -0
- package/dist/index.js +12 -2
- package/dist/memes.js +52 -0
- package/dist/metaFilters.js +395 -0
- package/dist/related.js +135 -0
- package/dist/search.js +422 -0
- package/dist/shorthands.js +174 -0
- package/package.json +33 -6
package/dist/search.js
ADDED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setIdentifierToSetMappings,
|
|
3
|
+
setToSetIdentifierMappings
|
|
4
|
+
} from "@flesh-and-blood/types";
|
|
5
|
+
import Fuse from "fuse.js";
|
|
6
|
+
import { PUNCTUATION } from "./constants";
|
|
7
|
+
import { getKeywordsAndAppliedFiltersFromText } from "./filters";
|
|
8
|
+
import { memes } from "./memes";
|
|
9
|
+
import { getNormalizedText } from "./helpers";
|
|
10
|
+
import { FilterProperty } from "./metaFilters";
|
|
11
|
+
class Search {
|
|
12
|
+
constructor(cards, additionalHeroes = [], additionalSets = [], debug = false) {
|
|
13
|
+
this.log = (message, ...optionalParams) => {
|
|
14
|
+
if (this.debug) {
|
|
15
|
+
console.log(message, ...optionalParams);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
this.search = (text, includeMemes) => {
|
|
19
|
+
let results;
|
|
20
|
+
const { appliedFilters, attributes, keywords } = getKeywordsAndAppliedFiltersFromText(
|
|
21
|
+
text,
|
|
22
|
+
this.cards,
|
|
23
|
+
this.additionalHeroes,
|
|
24
|
+
this.additionalSets
|
|
25
|
+
);
|
|
26
|
+
const keyword = keywords.join(" ");
|
|
27
|
+
const matchingMemes = includeMemes ? memes.filter((meme) => meme.keyword === keyword) : [];
|
|
28
|
+
if (matchingMemes.length > 0) {
|
|
29
|
+
results = matchingMemes.map(({ card }) => card);
|
|
30
|
+
} else if (keywords.length) {
|
|
31
|
+
results = this.fuse.search(keyword).map((result) => result.item);
|
|
32
|
+
} else {
|
|
33
|
+
results = [...this.cards];
|
|
34
|
+
}
|
|
35
|
+
if (appliedFilters.length) {
|
|
36
|
+
results = results.filter(
|
|
37
|
+
(card) => card && filterCard(card, appliedFilters)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
if (keywords.length === 0) {
|
|
41
|
+
let setIdentifieToSortBy = "";
|
|
42
|
+
const shouldSortByRelease = attributes.releases.length === 1;
|
|
43
|
+
if (shouldSortByRelease) {
|
|
44
|
+
try {
|
|
45
|
+
const setToSort = attributes.releases[0];
|
|
46
|
+
const matchingSetIdentifiers = setToSetIdentifierMappings[setToSort];
|
|
47
|
+
setIdentifieToSortBy = matchingSetIdentifiers[0].toUpperCase();
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.error(`Error getting set identifier from search`, e);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const shouldSortByPrint = !setIdentifieToSortBy && attributes.prints.length === 1;
|
|
53
|
+
if (shouldSortByPrint) {
|
|
54
|
+
try {
|
|
55
|
+
const setToSort = attributes.prints[0];
|
|
56
|
+
const matchingSetIdentifiers = setIdentifierToSetMappings[setToSort];
|
|
57
|
+
if (matchingSetIdentifiers) {
|
|
58
|
+
setIdentifieToSortBy = setToSort.toUpperCase();
|
|
59
|
+
}
|
|
60
|
+
} catch (e) {
|
|
61
|
+
console.error(`Error getting set identifier from search`, e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (setIdentifieToSortBy) {
|
|
65
|
+
results.sort((c1, c2) => {
|
|
66
|
+
const c1SetNumber = c1.setIdentifiers.find((identifier) => identifier.includes(setIdentifieToSortBy))?.replace(setIdentifieToSortBy, "");
|
|
67
|
+
const c2SetNumber = c2.setIdentifiers.find((identifier) => identifier.includes(setIdentifieToSortBy))?.replace(setIdentifieToSortBy, "");
|
|
68
|
+
return c1SetNumber && c2SetNumber ? c1SetNumber.localeCompare(c2SetNumber) : -1;
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
results.sort(
|
|
72
|
+
(c1, c2) => c1.name === c2.name ? `${c1.pitch}`.localeCompare(`${c2.pitch}`) : c1.name.localeCompare(c2.name)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
const nameMatches = [];
|
|
77
|
+
const nonMatches = [];
|
|
78
|
+
const potentialCardName = keywords.map((keyword2) => keyword2.toLowerCase().replace(PUNCTUATION, "")).join(" ");
|
|
79
|
+
for (const card of results) {
|
|
80
|
+
if (card.name.toLowerCase().replace(PUNCTUATION, "") === potentialCardName) {
|
|
81
|
+
nameMatches.push(card);
|
|
82
|
+
} else {
|
|
83
|
+
nonMatches.push(card);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
results = [...nameMatches, ...nonMatches];
|
|
87
|
+
}
|
|
88
|
+
let searchResultsWithMatchingPrinting;
|
|
89
|
+
const {
|
|
90
|
+
artists,
|
|
91
|
+
isExpansionSlot,
|
|
92
|
+
foilings,
|
|
93
|
+
prints,
|
|
94
|
+
rarities,
|
|
95
|
+
releases,
|
|
96
|
+
treatments
|
|
97
|
+
} = attributes;
|
|
98
|
+
const shouldFindMatchingPrintings = artists.length > 0 || isExpansionSlot || foilings.length > 0 || prints.length > 0 || rarities.length > 0 || releases.length > 0 || treatments.length > 0;
|
|
99
|
+
if (shouldFindMatchingPrintings) {
|
|
100
|
+
searchResultsWithMatchingPrinting = results.map((card) => {
|
|
101
|
+
const matchingPrintings = card.printings.filter((printing) => {
|
|
102
|
+
const hasImage = !!printing.image;
|
|
103
|
+
const matchesArtist = artists.length === 0 || artists.some(
|
|
104
|
+
(attributeArtist) => printing.artists.find(
|
|
105
|
+
(artist) => artist.replace(PUNCTUATION, "").toLowerCase().includes(attributeArtist)
|
|
106
|
+
)
|
|
107
|
+
);
|
|
108
|
+
const matchesExpansionSlot = !isExpansionSlot || isExpansionSlot === printing.isExpansionSlot;
|
|
109
|
+
const matchesFoiling = foilings.length === 0 || foilings.includes(printing.foiling);
|
|
110
|
+
const matchesPrint = prints.length === 0 || prints.some(
|
|
111
|
+
(print) => printing.identifier.includes(print.toUpperCase())
|
|
112
|
+
);
|
|
113
|
+
const matchesRarity = rarities.length === 0 || rarities.includes(printing.rarity);
|
|
114
|
+
const matchesReleases = releases.length === 0 || releases.includes(printing.set);
|
|
115
|
+
const matchesTreatment = treatments.length === 0 || printing.treatments?.some(
|
|
116
|
+
(treatment) => treatments.includes(treatment)
|
|
117
|
+
);
|
|
118
|
+
const printMatches = hasImage && matchesArtist && matchesExpansionSlot && matchesFoiling && matchesPrint && matchesRarity && matchesReleases && matchesTreatment;
|
|
119
|
+
return printMatches;
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
...card,
|
|
123
|
+
matchingPrintings
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const searchResults = searchResultsWithMatchingPrinting?.length > 0 ? searchResultsWithMatchingPrinting : results;
|
|
128
|
+
return {
|
|
129
|
+
appliedFilters,
|
|
130
|
+
attributes,
|
|
131
|
+
keywords,
|
|
132
|
+
searchResults
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
const searchOptions = {
|
|
136
|
+
getFn: (obj, path) => {
|
|
137
|
+
const value = Fuse.config.getFn(obj, path);
|
|
138
|
+
if (!value) {
|
|
139
|
+
return value;
|
|
140
|
+
} else if (Array.isArray(value)) {
|
|
141
|
+
return value.map(
|
|
142
|
+
(val) => getNormalizedText(val.replace(PUNCTUATION, ""))
|
|
143
|
+
);
|
|
144
|
+
} else {
|
|
145
|
+
return getNormalizedText(value).replace(PUNCTUATION, "");
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
ignoreLocation: true,
|
|
149
|
+
includeScore: true,
|
|
150
|
+
keys: [
|
|
151
|
+
{ name: "name", weight: 10 },
|
|
152
|
+
{ name: "functionalText", weight: 6 },
|
|
153
|
+
{ name: "shorthands", weight: 4 },
|
|
154
|
+
{ name: "setIdentifiers", weight: 2 },
|
|
155
|
+
{ name: "traits", weight: 4 },
|
|
156
|
+
{ name: "typeText", weight: 6 }
|
|
157
|
+
],
|
|
158
|
+
threshold: 0.15,
|
|
159
|
+
useExtendedSearch: true
|
|
160
|
+
};
|
|
161
|
+
this.additionalHeroes = additionalHeroes;
|
|
162
|
+
this.additionalSets = additionalSets;
|
|
163
|
+
this.cards = [...cards];
|
|
164
|
+
this.debug = debug;
|
|
165
|
+
this.fuse = new Fuse([...cards], searchOptions);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
var search_default = Search;
|
|
169
|
+
const filterCard = (card, appliedFilters) => {
|
|
170
|
+
let doesCardMatchAllRequiredFilters = true;
|
|
171
|
+
let doesCardMatchAnyOptionalFilters = false;
|
|
172
|
+
for (const appliedFilter of appliedFilters) {
|
|
173
|
+
const isOptional = appliedFilter.isOptional;
|
|
174
|
+
const { isNumber, isString, isArray, isBoolean } = appliedFilter.filterToPropertyMapping;
|
|
175
|
+
if (isNumber) {
|
|
176
|
+
const cardMatchesNumericFilter = getDoesCardMatchNumericFilter(
|
|
177
|
+
card,
|
|
178
|
+
appliedFilter
|
|
179
|
+
);
|
|
180
|
+
if (isOptional) {
|
|
181
|
+
if (cardMatchesNumericFilter) {
|
|
182
|
+
doesCardMatchAnyOptionalFilters = true;
|
|
183
|
+
}
|
|
184
|
+
} else {
|
|
185
|
+
doesCardMatchAllRequiredFilters = doesCardMatchAllRequiredFilters && cardMatchesNumericFilter;
|
|
186
|
+
}
|
|
187
|
+
} else if (isString) {
|
|
188
|
+
const cardMatchesStringFilter = getDoesCardMatchStringFilter(
|
|
189
|
+
card,
|
|
190
|
+
appliedFilter
|
|
191
|
+
);
|
|
192
|
+
if (isOptional) {
|
|
193
|
+
if (cardMatchesStringFilter) {
|
|
194
|
+
doesCardMatchAnyOptionalFilters = true;
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
doesCardMatchAllRequiredFilters = doesCardMatchAllRequiredFilters && cardMatchesStringFilter;
|
|
198
|
+
}
|
|
199
|
+
} else if (isArray) {
|
|
200
|
+
const cardMatchesArrayFilter = getDoesCardMatchArrayFilter(
|
|
201
|
+
card,
|
|
202
|
+
appliedFilter,
|
|
203
|
+
appliedFilters
|
|
204
|
+
);
|
|
205
|
+
if (isOptional) {
|
|
206
|
+
if (cardMatchesArrayFilter) {
|
|
207
|
+
doesCardMatchAnyOptionalFilters = true;
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
doesCardMatchAllRequiredFilters = doesCardMatchAllRequiredFilters && cardMatchesArrayFilter;
|
|
211
|
+
}
|
|
212
|
+
} else if (isBoolean) {
|
|
213
|
+
const cardMatchesBooleanFilter = getDoesCardMatchBooleanFilter(
|
|
214
|
+
card,
|
|
215
|
+
appliedFilter
|
|
216
|
+
);
|
|
217
|
+
if (isOptional) {
|
|
218
|
+
if (cardMatchesBooleanFilter) {
|
|
219
|
+
doesCardMatchAnyOptionalFilters = true;
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
doesCardMatchAllRequiredFilters = doesCardMatchAllRequiredFilters && cardMatchesBooleanFilter;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return doesCardMatchAllRequiredFilters;
|
|
227
|
+
};
|
|
228
|
+
const getDoesCardMatchNumericFilter = (card, filter) => {
|
|
229
|
+
if (!doesFilterMatchCardType(filter, card)) {
|
|
230
|
+
return true;
|
|
231
|
+
} else {
|
|
232
|
+
const {
|
|
233
|
+
values,
|
|
234
|
+
modifier,
|
|
235
|
+
isExcluded: excluded,
|
|
236
|
+
filterToPropertyMapping: { partialMatch }
|
|
237
|
+
} = filter;
|
|
238
|
+
let cardValue = getCardValue(card, filter);
|
|
239
|
+
if (cardValue != null && !isNaN(cardValue)) {
|
|
240
|
+
cardValue = parseInt(cardValue);
|
|
241
|
+
if (modifier) {
|
|
242
|
+
switch (modifier) {
|
|
243
|
+
case ">=":
|
|
244
|
+
const isGreatherThanOrEqualTo = values?.some(
|
|
245
|
+
(filterValue) => cardValue >= parseInt(filterValue)
|
|
246
|
+
);
|
|
247
|
+
return excluded ? !isGreatherThanOrEqualTo : isGreatherThanOrEqualTo;
|
|
248
|
+
case ">":
|
|
249
|
+
const isGreatherThan = values?.some(
|
|
250
|
+
(filterValue) => cardValue > parseInt(filterValue)
|
|
251
|
+
);
|
|
252
|
+
return excluded ? !isGreatherThan : isGreatherThan;
|
|
253
|
+
case "<=":
|
|
254
|
+
const isLessThanOrEqualTo = values?.some(
|
|
255
|
+
(filterValue) => cardValue <= parseInt(filterValue)
|
|
256
|
+
);
|
|
257
|
+
return excluded ? !isLessThanOrEqualTo : isLessThanOrEqualTo;
|
|
258
|
+
case "<":
|
|
259
|
+
const isLessThan = values?.some(
|
|
260
|
+
(filterValue) => cardValue < parseInt(filterValue)
|
|
261
|
+
);
|
|
262
|
+
return excluded ? !isLessThan : isLessThan;
|
|
263
|
+
default:
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
const isEqualTo = values?.some(
|
|
268
|
+
(filterValue) => cardValue === parseInt(filterValue)
|
|
269
|
+
);
|
|
270
|
+
return excluded ? !isEqualTo : isEqualTo;
|
|
271
|
+
}
|
|
272
|
+
} else {
|
|
273
|
+
const cardSpecialValue = getCardSpecialValue(card, filter)?.toLowerCase();
|
|
274
|
+
const isMatch = partialMatch ? values?.some((filterValue) => cardSpecialValue?.includes(filterValue)) : values?.some((filterValue) => cardSpecialValue === filterValue);
|
|
275
|
+
return excluded ? !isMatch : isMatch;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
const getDoesCardMatchStringFilter = (card, filter) => {
|
|
280
|
+
if (!doesFilterMatchCardType(filter, card)) {
|
|
281
|
+
return true;
|
|
282
|
+
} else {
|
|
283
|
+
const {
|
|
284
|
+
values,
|
|
285
|
+
isAnd,
|
|
286
|
+
isExcluded: excluded,
|
|
287
|
+
filterToPropertyMapping: { partialMatch }
|
|
288
|
+
} = filter;
|
|
289
|
+
const cardValue = getCardValue(card, filter)?.replaceAll(
|
|
290
|
+
PUNCTUATION,
|
|
291
|
+
""
|
|
292
|
+
);
|
|
293
|
+
if (partialMatch) {
|
|
294
|
+
const isPartialMatch = isAnd ? values?.every(
|
|
295
|
+
(filterValue) => cardValue?.toLowerCase().includes(filterValue)
|
|
296
|
+
) : values?.some(
|
|
297
|
+
(filterValue) => cardValue?.toLowerCase().includes(filterValue)
|
|
298
|
+
);
|
|
299
|
+
return excluded ? !isPartialMatch : isPartialMatch;
|
|
300
|
+
} else {
|
|
301
|
+
const isFullMatch = isAnd ? values?.every(
|
|
302
|
+
(filterValue) => cardValue?.toLowerCase() === filterValue
|
|
303
|
+
) : values?.some(
|
|
304
|
+
(filterValue) => cardValue?.toLowerCase() === filterValue
|
|
305
|
+
);
|
|
306
|
+
return excluded ? !isFullMatch : isFullMatch;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
const getDoesCardMatchArrayFilter = (card, filter, filters) => {
|
|
311
|
+
if (!doesFilterMatchCardType(filter, card)) {
|
|
312
|
+
return true;
|
|
313
|
+
} else {
|
|
314
|
+
const {
|
|
315
|
+
values,
|
|
316
|
+
isAnd,
|
|
317
|
+
isExcluded,
|
|
318
|
+
filterToPropertyMapping: { partialMatch }
|
|
319
|
+
} = filter;
|
|
320
|
+
const cardValues = getCardValues(card, filter, filters).map(
|
|
321
|
+
(value) => value?.replaceAll(PUNCTUATION, "")
|
|
322
|
+
);
|
|
323
|
+
if (partialMatch) {
|
|
324
|
+
const isPartialMatch = isAnd ? values.every(
|
|
325
|
+
(filterValue) => cardValues?.some(
|
|
326
|
+
(cardValue) => cardValue?.toLowerCase().includes(filterValue)
|
|
327
|
+
)
|
|
328
|
+
) : values.some(
|
|
329
|
+
(filterValue) => cardValues?.some(
|
|
330
|
+
(cardValue) => cardValue?.toLowerCase().includes(filterValue)
|
|
331
|
+
)
|
|
332
|
+
);
|
|
333
|
+
const noValues = cardValues.length === 0;
|
|
334
|
+
return isExcluded ? !isPartialMatch || noValues : isPartialMatch;
|
|
335
|
+
} else {
|
|
336
|
+
const isFullMatch = isAnd ? values.every(
|
|
337
|
+
(filterValue) => cardValues?.some(
|
|
338
|
+
(cardValue) => cardValue?.toLowerCase() === filterValue
|
|
339
|
+
)
|
|
340
|
+
) : values.some(
|
|
341
|
+
(filterValue) => cardValues?.some(
|
|
342
|
+
(cardValue) => cardValue?.toLowerCase() === filterValue
|
|
343
|
+
)
|
|
344
|
+
);
|
|
345
|
+
return isExcluded ? !isFullMatch : isFullMatch;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
const getDoesCardMatchBooleanFilter = (card, filter) => {
|
|
350
|
+
if (!doesFilterMatchCardType(filter, card)) {
|
|
351
|
+
return true;
|
|
352
|
+
} else {
|
|
353
|
+
const { isExcluded } = filter;
|
|
354
|
+
const cardValue = getCardValue(card, filter);
|
|
355
|
+
return isExcluded ? !cardValue : cardValue;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
const getCardValue = (card, appliedFilter) => {
|
|
359
|
+
const { filterToPropertyMapping } = appliedFilter;
|
|
360
|
+
return card[filterToPropertyMapping.property];
|
|
361
|
+
};
|
|
362
|
+
const getCardValues = (card, filter, filters) => {
|
|
363
|
+
const {
|
|
364
|
+
filterToPropertyMapping: {
|
|
365
|
+
isNestedPropertyArray,
|
|
366
|
+
nestedProperty,
|
|
367
|
+
property
|
|
368
|
+
}
|
|
369
|
+
} = filter;
|
|
370
|
+
const propertyValues = card[property] || [];
|
|
371
|
+
let values = [];
|
|
372
|
+
const cardHasLegalOverrides = Object.keys(card.legalOverrides || {}).length > 0;
|
|
373
|
+
const isCheckingForLegalHeroes = filter.filterToPropertyMapping.property === FilterProperty.LegalHeroes;
|
|
374
|
+
const anotherFilterForLegalFormat = filters.find(
|
|
375
|
+
({ filterToPropertyMapping }) => filterToPropertyMapping.property === FilterProperty.LegalFormats
|
|
376
|
+
);
|
|
377
|
+
const shouldCheckForLegalOverrides = cardHasLegalOverrides && isCheckingForLegalHeroes && !!anotherFilterForLegalFormat;
|
|
378
|
+
if (shouldCheckForLegalOverrides) {
|
|
379
|
+
const valuesSet = /* @__PURE__ */ new Set();
|
|
380
|
+
for (const { format, heroes } of card.legalOverrides || []) {
|
|
381
|
+
if (anotherFilterForLegalFormat.values.includes(format.toLowerCase())) {
|
|
382
|
+
for (const hero of heroes) {
|
|
383
|
+
valuesSet.add(hero);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
values = Array.from(valuesSet);
|
|
388
|
+
}
|
|
389
|
+
if (values.length === 0) {
|
|
390
|
+
if (nestedProperty) {
|
|
391
|
+
const valuesSet = /* @__PURE__ */ new Set();
|
|
392
|
+
for (const rawValue of propertyValues) {
|
|
393
|
+
if (isNestedPropertyArray) {
|
|
394
|
+
const rawValues = rawValue[nestedProperty] || [];
|
|
395
|
+
for (const value of rawValues) {
|
|
396
|
+
valuesSet.add(value);
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
const value = rawValue[nestedProperty];
|
|
400
|
+
if (value) {
|
|
401
|
+
valuesSet.add(value);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
values = Array.from(valuesSet);
|
|
406
|
+
} else {
|
|
407
|
+
values = propertyValues;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return values;
|
|
411
|
+
};
|
|
412
|
+
const getCardSpecialValue = (card, appliedFilter) => {
|
|
413
|
+
const { filterToPropertyMapping } = appliedFilter;
|
|
414
|
+
return card[filterToPropertyMapping.specialProperty];
|
|
415
|
+
};
|
|
416
|
+
const doesFilterMatchCardType = ({ cardTypes }, { types, subtypes }) => !cardTypes || cardTypes?.some(
|
|
417
|
+
(cardType) => types.map((type) => type.toLowerCase()).includes(cardType.toLowerCase()) || subtypes.map((subtype) => subtype.toLowerCase()).includes(cardType.toLowerCase())
|
|
418
|
+
);
|
|
419
|
+
export {
|
|
420
|
+
search_default as default,
|
|
421
|
+
filterCard
|
|
422
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { Keyword, Subtype, Type } from "@flesh-and-blood/types";
|
|
2
|
+
const shorthands = [
|
|
3
|
+
{
|
|
4
|
+
description: "Attack actions",
|
|
5
|
+
expanded: ["st:attack"],
|
|
6
|
+
filters: {
|
|
7
|
+
subtypes: [Subtype.Attack]
|
|
8
|
+
},
|
|
9
|
+
isCardProperty: false,
|
|
10
|
+
shorthands: ["AA"]
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
description: "Arcane barrier",
|
|
14
|
+
expanded: ['k:"arcane barrier"'],
|
|
15
|
+
filters: {
|
|
16
|
+
keywords: [Keyword.ArcaneBarrier]
|
|
17
|
+
},
|
|
18
|
+
isCardProperty: false,
|
|
19
|
+
shorthands: ["AB"]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
description: "Attack reactions",
|
|
23
|
+
expanded: ['t:"attack reaction"'],
|
|
24
|
+
filters: {
|
|
25
|
+
types: [Type.AttackReaction]
|
|
26
|
+
},
|
|
27
|
+
isCardProperty: false,
|
|
28
|
+
shorthands: ["AR"]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
description: "Defense reactions",
|
|
32
|
+
expanded: ['t:"defense reaction"'],
|
|
33
|
+
filters: {
|
|
34
|
+
types: [Type.DefenseReaction]
|
|
35
|
+
},
|
|
36
|
+
isCardProperty: false,
|
|
37
|
+
shorthands: ["DR"]
|
|
38
|
+
},
|
|
39
|
+
// {
|
|
40
|
+
// description: "Flick daggers",
|
|
41
|
+
// expanded: ["dagger you control deal 1 damage"],
|
|
42
|
+
// filters: {
|
|
43
|
+
// functionalText: "dagger you control deal 1 damage",
|
|
44
|
+
// },
|
|
45
|
+
// isCardProperty: true,
|
|
46
|
+
// shorthands: ["Flick", "Hurl", "Throw"],
|
|
47
|
+
// },
|
|
48
|
+
{
|
|
49
|
+
description: "Gain life",
|
|
50
|
+
expanded: ["gain {h}"],
|
|
51
|
+
filters: {
|
|
52
|
+
functionalText: "gain {h}"
|
|
53
|
+
},
|
|
54
|
+
isCardProperty: false,
|
|
55
|
+
shorthands: ["Gain life", "Gains life"]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
description: "Go again",
|
|
59
|
+
expanded: ['k:"go again"'],
|
|
60
|
+
filters: {
|
|
61
|
+
keywords: [Keyword.GoAgain]
|
|
62
|
+
},
|
|
63
|
+
isCardProperty: false,
|
|
64
|
+
shorthands: ["GA"]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
description: "Non-attack actions",
|
|
68
|
+
expanded: ["t:action", "st:non-attack"],
|
|
69
|
+
filters: {
|
|
70
|
+
subtypes: [Subtype.NonAttack],
|
|
71
|
+
types: [Type.Action]
|
|
72
|
+
},
|
|
73
|
+
isCardProperty: false,
|
|
74
|
+
shorthands: ["NAA"]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
description: "Plus defense",
|
|
78
|
+
expanded: ["+ {d}"],
|
|
79
|
+
filters: {
|
|
80
|
+
functionalText: "+ {d}"
|
|
81
|
+
},
|
|
82
|
+
isCardProperty: false,
|
|
83
|
+
shorthands: [
|
|
84
|
+
"Pump defense",
|
|
85
|
+
"Pumps defense",
|
|
86
|
+
"Buff defense",
|
|
87
|
+
"Buffs defense"
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
// {
|
|
91
|
+
// description: "Plus power",
|
|
92
|
+
// expanded: ["+ {p}"],
|
|
93
|
+
// filters: {
|
|
94
|
+
// functionalText: "+ {p}",
|
|
95
|
+
// },
|
|
96
|
+
// isCardProperty: false,
|
|
97
|
+
// shorthands: [
|
|
98
|
+
// "Pumps",
|
|
99
|
+
// "Pump",
|
|
100
|
+
// "Buff",
|
|
101
|
+
// "Buffs",
|
|
102
|
+
// "Pump attack",
|
|
103
|
+
// "Pump attacks",
|
|
104
|
+
// "Pumps attack",
|
|
105
|
+
// "Pumps attacks",
|
|
106
|
+
// "Buff attack",
|
|
107
|
+
// "Buff attacks",
|
|
108
|
+
// "Buffs attack",
|
|
109
|
+
// "Buffs attacks",
|
|
110
|
+
// "Pump power",
|
|
111
|
+
// "Pumps power",
|
|
112
|
+
// "Buff power",
|
|
113
|
+
// "Buffs power",
|
|
114
|
+
// ],
|
|
115
|
+
// },
|
|
116
|
+
// {
|
|
117
|
+
// description: "Poppers",
|
|
118
|
+
// expanded: ["!c:illusionist", "st:attack", "pwr:>=6", "def:>=0"],
|
|
119
|
+
// filters: {
|
|
120
|
+
// defenseGreaterThanOrEqualTo: 0,
|
|
121
|
+
// notClass: [Class.Illusionist],
|
|
122
|
+
// powerGreaterThanOrEqualTo: 6,
|
|
123
|
+
// subtypes: [Subtype.Attack],
|
|
124
|
+
// },
|
|
125
|
+
// isCardProperty: false,
|
|
126
|
+
// helper:
|
|
127
|
+
// '6+ power non-Illusionist attacks that can "pop" phantasm attacks when defending',
|
|
128
|
+
// shorthands: ["Poppers", "Popper"],
|
|
129
|
+
// },
|
|
130
|
+
{
|
|
131
|
+
description: "Spellvoid",
|
|
132
|
+
expanded: ['k:"spellvoid"'],
|
|
133
|
+
filters: {
|
|
134
|
+
keywords: [Keyword.Spellvoid]
|
|
135
|
+
},
|
|
136
|
+
isCardProperty: false,
|
|
137
|
+
shorthands: ["SV"]
|
|
138
|
+
}
|
|
139
|
+
// {
|
|
140
|
+
// description: "Tap",
|
|
141
|
+
// expanded: ["{t}"],
|
|
142
|
+
// filters: {
|
|
143
|
+
// functionalText: "{t}",
|
|
144
|
+
// },
|
|
145
|
+
// isCardProperty: true,
|
|
146
|
+
// shorthands: ["Tap"],
|
|
147
|
+
// },
|
|
148
|
+
// {
|
|
149
|
+
// description: "Untap",
|
|
150
|
+
// expanded: ["{u}"],
|
|
151
|
+
// filters: {
|
|
152
|
+
// functionalText: "{u}",
|
|
153
|
+
// },
|
|
154
|
+
// isCardProperty: true,
|
|
155
|
+
// shorthands: ["Untap"],
|
|
156
|
+
// },
|
|
157
|
+
];
|
|
158
|
+
const multiWordShorthands = shorthands.filter(
|
|
159
|
+
({ shorthands: shorthands2 }) => shorthands2.some((shorthand) => shorthand.includes(" "))
|
|
160
|
+
).map((shorthand) => ({
|
|
161
|
+
...shorthand,
|
|
162
|
+
shorthands: shorthand.shorthands.filter((shorthand2) => shorthand2.includes(" ")).map((shorthand2) => shorthand2.toLowerCase()).sort((s1, s2) => s2.length - s1.length)
|
|
163
|
+
}));
|
|
164
|
+
const singleWordShorthands = shorthands.filter(
|
|
165
|
+
({ shorthands: shorthands2 }) => shorthands2.some((shorthand) => !shorthand.includes(" "))
|
|
166
|
+
).map((shorthand) => ({
|
|
167
|
+
...shorthand,
|
|
168
|
+
shorthands: shorthand.shorthands.filter((shorthand2) => !shorthand2.includes(" ")).map((shorthand2) => shorthand2.toLowerCase()).sort((s1, s2) => s2.length - s1.length)
|
|
169
|
+
}));
|
|
170
|
+
export {
|
|
171
|
+
multiWordShorthands,
|
|
172
|
+
shorthands,
|
|
173
|
+
singleWordShorthands
|
|
174
|
+
};
|
package/package.json
CHANGED
|
@@ -1,23 +1,50 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flesh-and-blood/search",
|
|
3
3
|
"description": "TypeScript search engine for Flesh and Blood cards",
|
|
4
|
-
"version": "
|
|
5
|
-
"
|
|
4
|
+
"version": "4.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "dist/index.cjs",
|
|
8
|
+
"module": "dist/index.js",
|
|
6
9
|
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./abbreviations": {
|
|
17
|
+
"types": "./dist/abbreviations.d.ts",
|
|
18
|
+
"import": "./dist/abbreviations.js"
|
|
19
|
+
},
|
|
20
|
+
"./shorthands": {
|
|
21
|
+
"types": "./dist/shorthands.d.ts",
|
|
22
|
+
"import": "./dist/shorthands.js"
|
|
23
|
+
},
|
|
24
|
+
"./memes": {
|
|
25
|
+
"types": "./dist/memes.d.ts",
|
|
26
|
+
"import": "./dist/memes.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
7
29
|
"scripts": {
|
|
8
30
|
"prebuild": "rm -rf dist",
|
|
9
|
-
"build": "
|
|
31
|
+
"build": "npm run build:esm && npm run build:cjs",
|
|
32
|
+
"build:esm": "esbuild src/*.ts --outdir=dist --format=esm --platform=neutral --target=es2020",
|
|
33
|
+
"build:cjs": "esbuild src/index.ts --outfile=dist/index.cjs --bundle --minify --format=cjs --platform=node --target=es2020 --external:@flesh-and-blood/types --external:fuse.js",
|
|
10
34
|
"postbuild": "tsc --declaration && npm test",
|
|
11
35
|
"test": "jest",
|
|
12
36
|
"test:specific": "jest -t 'Matching printing from treatment'",
|
|
13
37
|
"full": "npm i && npm run build"
|
|
14
38
|
},
|
|
15
39
|
"dependencies": {
|
|
16
|
-
"@flesh-and-blood/types": "^3.9.7",
|
|
17
40
|
"fuse.js": "^6.6.2"
|
|
18
41
|
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@flesh-and-blood/types": "^3.9.7"
|
|
44
|
+
},
|
|
19
45
|
"devDependencies": {
|
|
20
|
-
"@flesh-and-blood/cards": "^
|
|
46
|
+
"@flesh-and-blood/cards": "^4.0.0",
|
|
47
|
+
"@flesh-and-blood/types": "^4.0.0",
|
|
21
48
|
"@types/jest": "^29.5.11",
|
|
22
49
|
"@types/node": "^20.10.5",
|
|
23
50
|
"esbuild": "^0.19.10",
|
|
@@ -47,5 +74,5 @@
|
|
|
47
74
|
"FAB",
|
|
48
75
|
"FABTCG"
|
|
49
76
|
],
|
|
50
|
-
"gitHead": "
|
|
77
|
+
"gitHead": "9ded4ed228786d702f9c16ebe94d090dc4e73f23"
|
|
51
78
|
}
|