@builtbyecho/public-api-finder 0.5.4 → 0.5.5
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/package.json +1 -1
- package/src/cli.js +45 -10
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ const SOURCES = {
|
|
|
12
12
|
};
|
|
13
13
|
const CACHE_PATH = process.env.PUBLIC_API_FINDER_CACHE || join(homedir(), '.cache', 'public-api-finder', 'all.json');
|
|
14
14
|
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
15
|
-
const DATA_VERSION =
|
|
15
|
+
const DATA_VERSION = 14;
|
|
16
16
|
|
|
17
17
|
const ENRICHMENT_FIELDS = [
|
|
18
18
|
'tags',
|
|
@@ -574,23 +574,51 @@ function normalizeAuthType(auth) {
|
|
|
574
574
|
return value && value !== 'unknown' ? value : 'unknown';
|
|
575
575
|
}
|
|
576
576
|
|
|
577
|
-
function
|
|
577
|
+
function wordsFrom(value) {
|
|
578
|
+
return String(value || '')
|
|
579
|
+
.replace(/&/g, ' and ')
|
|
580
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
581
|
+
.toLowerCase()
|
|
582
|
+
.match(/[a-z0-9]+/g) || [];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function categoryTags(category) {
|
|
586
|
+
const words = wordsFrom(category).filter(word => !['and', 'api', 'apis', 'openapi', 'unknown'].includes(word));
|
|
587
|
+
const tags = [];
|
|
588
|
+
const categoryText = String(category || '').toLowerCase();
|
|
589
|
+
if (categoryText && !['unknown', 'openapi'].includes(categoryText)) tags.push(categoryText.replace(/\s+/g, ' '));
|
|
590
|
+
for (const word of words) tags.push(word);
|
|
591
|
+
return tags;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function inferTags(api, specific = {}) {
|
|
595
|
+
const text = `${api.name || ''} ${api.category || ''} ${api.description || ''} ${api.url || ''} ${api.provider || ''} ${(specific.tags || []).join(' ')}`;
|
|
596
|
+
return INTENT_TAG_RULES.filter(([pattern]) => pattern.test(text)).map(([, tag]) => tag);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function enrichApiMetadata(api) {
|
|
578
600
|
const categoryBase = CURATED_CATEGORY_ENRICHMENTS[api.category] || {};
|
|
579
601
|
const specific = CURATED_API_ENRICHMENTS[api.name] || {};
|
|
580
|
-
const text = `${api.name || ''} ${api.description || ''} ${(specific.tags || []).join(' ')}`;
|
|
581
|
-
const inferredTags = INTENT_TAG_RULES.filter(([pattern]) => pattern.test(text)).map(([, tag]) => tag);
|
|
582
602
|
const merged = { ...categoryBase, ...specific, ...api };
|
|
603
|
+
const tags = uniqueStrings([
|
|
604
|
+
...(categoryBase.tags || []),
|
|
605
|
+
...(specific.tags || []),
|
|
606
|
+
...inferTags(api, specific),
|
|
607
|
+
...(api.tags || []),
|
|
608
|
+
...categoryTags(api.category),
|
|
609
|
+
]);
|
|
610
|
+
|
|
583
611
|
merged.authType = api.authType || specific.authType || categoryBase.authType || normalizeAuthType(api.auth);
|
|
584
612
|
merged.providerType = api.providerType || specific.providerType || categoryBase.providerType || 'public-api';
|
|
585
|
-
merged.tags =
|
|
586
|
-
merged.domains = uniqueStrings([...(categoryBase.domains || []), ...(specific.domains || []), ...(api.domains || [])]);
|
|
613
|
+
merged.tags = tags.length ? tags : ['public api'];
|
|
614
|
+
merged.domains = uniqueStrings([...(categoryBase.domains || []), ...(specific.domains || []), ...(api.domains || []), ...categoryTags(api.category).slice(0, 2)]);
|
|
587
615
|
merged.useCases = uniqueStrings([...(categoryBase.useCases || []), ...(specific.useCases || []), ...(api.useCases || [])]);
|
|
588
616
|
merged.caveats = uniqueStrings([...(categoryBase.caveats || []), ...(specific.caveats || []), ...(api.caveats || [])]);
|
|
589
617
|
merged.bestFor = api.bestFor || specific.bestFor || categoryBase.bestFor;
|
|
590
618
|
return merged;
|
|
591
619
|
}
|
|
592
620
|
|
|
593
|
-
const ENRICHED_CURATED_APIS = CURATED_APIS.map(
|
|
621
|
+
const ENRICHED_CURATED_APIS = CURATED_APIS.map(enrichApiMetadata);
|
|
594
622
|
|
|
595
623
|
export function getCuratedApis() {
|
|
596
624
|
return ENRICHED_CURATED_APIS.map(api => ({ ...api, tags: [...(api.tags || [])], domains: [...(api.domains || [])], useCases: [...(api.useCases || [])] }));
|
|
@@ -705,8 +733,10 @@ function applyQueryHints(args) {
|
|
|
705
733
|
if (!args.cors && /\b(cors|frontend-safe|browser-safe|frontend safe|browser safe)\b/.test(query)) args.cors = 'Yes';
|
|
706
734
|
}
|
|
707
735
|
|
|
736
|
+
const SEARCH_STOPWORDS = new Set(['a', 'an', 'and', 'api', 'apis', 'for', 'from', 'in', 'no', 'of', 'on', 'or', 'the', 'to', 'with']);
|
|
737
|
+
|
|
708
738
|
function tokenSet(text) {
|
|
709
|
-
return new Set(String(text).toLowerCase().match(/[a-z0-9]+/g)?.filter(t => t.length > 1) || []);
|
|
739
|
+
return new Set(String(text).toLowerCase().match(/[a-z0-9]+/g)?.filter(t => t.length > 1 && !SEARCH_STOPWORDS.has(t)) || []);
|
|
710
740
|
}
|
|
711
741
|
|
|
712
742
|
function intersectionCount(a, b) {
|
|
@@ -943,7 +973,8 @@ async function buildData() {
|
|
|
943
973
|
} else sourceStatus['apis-guru'] = `error: ${guru.reason.message}`;
|
|
944
974
|
sourceStatus.curated = ENRICHED_CURATED_APIS.length;
|
|
945
975
|
entries.push(...ENRICHED_CURATED_APIS);
|
|
946
|
-
|
|
976
|
+
const deduped = dedupe(entries).map(enrichApiMetadata);
|
|
977
|
+
return { dataVersion: DATA_VERSION, generatedAt: new Date().toISOString(), sourceStatus, entries: deduped };
|
|
947
978
|
}
|
|
948
979
|
|
|
949
980
|
function keyFor(entry) {
|
|
@@ -1006,7 +1037,7 @@ function dedupe(entries) {
|
|
|
1006
1037
|
async function loadData(refresh = false) {
|
|
1007
1038
|
if (!refresh && await cacheIsFresh()) {
|
|
1008
1039
|
const cached = JSON.parse(await readFile(CACHE_PATH, 'utf8'));
|
|
1009
|
-
if (cached.dataVersion === DATA_VERSION) return cached.entries || [];
|
|
1040
|
+
if (cached.dataVersion === DATA_VERSION) return (cached.entries || []).map(enrichApiMetadata);
|
|
1010
1041
|
}
|
|
1011
1042
|
const data = await buildData();
|
|
1012
1043
|
await mkdir(dirname(CACHE_PATH), { recursive: true });
|
|
@@ -1084,6 +1115,10 @@ async function checkRows(rows) {
|
|
|
1084
1115
|
return checked;
|
|
1085
1116
|
}
|
|
1086
1117
|
|
|
1118
|
+
export async function buildSearchIndex(options = {}) {
|
|
1119
|
+
return loadData(Boolean(options.refresh));
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1087
1122
|
export async function searchApis(options = {}) {
|
|
1088
1123
|
const args = {
|
|
1089
1124
|
query: options.query || '',
|