@builtbyecho/public-api-finder 0.5.3 → 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 +49 -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,55 @@ 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);
|
|
622
|
+
|
|
623
|
+
export function getCuratedApis() {
|
|
624
|
+
return ENRICHED_CURATED_APIS.map(api => ({ ...api, tags: [...(api.tags || [])], domains: [...(api.domains || [])], useCases: [...(api.useCases || [])] }));
|
|
625
|
+
}
|
|
594
626
|
|
|
595
627
|
function compactName(value) { return String(value || '').toLowerCase().replace(/[^a-z0-9]+/g, ''); }
|
|
596
628
|
const KNOWN_BEST_NAMES = new Map(ENRICHED_CURATED_APIS.map(api => [compactName(api.name), 15]));
|
|
@@ -701,8 +733,10 @@ function applyQueryHints(args) {
|
|
|
701
733
|
if (!args.cors && /\b(cors|frontend-safe|browser-safe|frontend safe|browser safe)\b/.test(query)) args.cors = 'Yes';
|
|
702
734
|
}
|
|
703
735
|
|
|
736
|
+
const SEARCH_STOPWORDS = new Set(['a', 'an', 'and', 'api', 'apis', 'for', 'from', 'in', 'no', 'of', 'on', 'or', 'the', 'to', 'with']);
|
|
737
|
+
|
|
704
738
|
function tokenSet(text) {
|
|
705
|
-
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)) || []);
|
|
706
740
|
}
|
|
707
741
|
|
|
708
742
|
function intersectionCount(a, b) {
|
|
@@ -939,7 +973,8 @@ async function buildData() {
|
|
|
939
973
|
} else sourceStatus['apis-guru'] = `error: ${guru.reason.message}`;
|
|
940
974
|
sourceStatus.curated = ENRICHED_CURATED_APIS.length;
|
|
941
975
|
entries.push(...ENRICHED_CURATED_APIS);
|
|
942
|
-
|
|
976
|
+
const deduped = dedupe(entries).map(enrichApiMetadata);
|
|
977
|
+
return { dataVersion: DATA_VERSION, generatedAt: new Date().toISOString(), sourceStatus, entries: deduped };
|
|
943
978
|
}
|
|
944
979
|
|
|
945
980
|
function keyFor(entry) {
|
|
@@ -1002,7 +1037,7 @@ function dedupe(entries) {
|
|
|
1002
1037
|
async function loadData(refresh = false) {
|
|
1003
1038
|
if (!refresh && await cacheIsFresh()) {
|
|
1004
1039
|
const cached = JSON.parse(await readFile(CACHE_PATH, 'utf8'));
|
|
1005
|
-
if (cached.dataVersion === DATA_VERSION) return cached.entries || [];
|
|
1040
|
+
if (cached.dataVersion === DATA_VERSION) return (cached.entries || []).map(enrichApiMetadata);
|
|
1006
1041
|
}
|
|
1007
1042
|
const data = await buildData();
|
|
1008
1043
|
await mkdir(dirname(CACHE_PATH), { recursive: true });
|
|
@@ -1080,6 +1115,10 @@ async function checkRows(rows) {
|
|
|
1080
1115
|
return checked;
|
|
1081
1116
|
}
|
|
1082
1117
|
|
|
1118
|
+
export async function buildSearchIndex(options = {}) {
|
|
1119
|
+
return loadData(Boolean(options.refresh));
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1083
1122
|
export async function searchApis(options = {}) {
|
|
1084
1123
|
const args = {
|
|
1085
1124
|
query: options.query || '',
|