search-web-api 1.0.13

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.
Files changed (111) hide show
  1. package/README.md +307 -0
  2. package/demo/index.ts +38 -0
  3. package/demo/openapi.ts +402 -0
  4. package/demo/routes/autocomplete.ts +124 -0
  5. package/demo/routes/search.ts +61 -0
  6. package/docs/AUTOCOMPLETE.md +296 -0
  7. package/docs/CATEGORY_SEARCH.md +265 -0
  8. package/package.json +32 -0
  9. package/src/autocomplete/autocomplete-ai-next-word-predictor.ts +47 -0
  10. package/src/autocomplete/autocomplete-search-engine-backends.ts +231 -0
  11. package/src/category-registry.ts +6 -0
  12. package/src/config/search-engine-constants.ts +216 -0
  13. package/src/constants.ts +6 -0
  14. package/src/engine-descriptions.ts +7 -0
  15. package/src/engine-status.ts +7 -0
  16. package/src/engine.ts +6 -0
  17. package/src/registry/search-engine-category-registry.ts +200 -0
  18. package/src/registry/search-engine-descriptions.ts +121 -0
  19. package/src/registry/search-engine-status-tracker.ts +240 -0
  20. package/src/result-container.ts +8 -0
  21. package/src/search/search-engines-registry-list.ts +205 -0
  22. package/src/search/search-query-executor.ts +168 -0
  23. package/src/search/search-result-container.ts +421 -0
  24. package/src/search-web-types.ts +7 -0
  25. package/src/search.ts +10 -0
  26. package/src/sources/academic/arxiv.ts +72 -0
  27. package/src/sources/academic/core.ts +65 -0
  28. package/src/sources/academic/crossref.ts +75 -0
  29. package/src/sources/academic/doaj.ts +63 -0
  30. package/src/sources/academic/google_scholar.ts +53 -0
  31. package/src/sources/academic/openalex.ts +75 -0
  32. package/src/sources/academic/pubmed.ts +96 -0
  33. package/src/sources/academic/semantic_scholar.ts +77 -0
  34. package/src/sources/academic/wikidata.ts +44 -0
  35. package/src/sources/general/baidu.ts +59 -0
  36. package/src/sources/general/bing.ts +30 -0
  37. package/src/sources/general/brave.ts +54 -0
  38. package/src/sources/general/duckduckgo.ts +49 -0
  39. package/src/sources/general/google.ts +68 -0
  40. package/src/sources/general/mojeek.ts +56 -0
  41. package/src/sources/general/qwant.ts +37 -0
  42. package/src/sources/general/startpage.ts +44 -0
  43. package/src/sources/general/yahoo.ts +41 -0
  44. package/src/sources/general/yandex.ts +56 -0
  45. package/src/sources/images/bing_images.ts +68 -0
  46. package/src/sources/images/deviantart.ts +71 -0
  47. package/src/sources/images/flickr.ts +132 -0
  48. package/src/sources/images/google_images.ts +101 -0
  49. package/src/sources/images/imgur.ts +58 -0
  50. package/src/sources/images/openclipart.ts +52 -0
  51. package/src/sources/images/pixabay.ts +56 -0
  52. package/src/sources/images/unsplash.ts +38 -0
  53. package/src/sources/images/wallhaven.ts +50 -0
  54. package/src/sources/it/crates.ts +41 -0
  55. package/src/sources/it/dockerhub.ts +47 -0
  56. package/src/sources/it/github.ts +37 -0
  57. package/src/sources/it/gitlab.ts +55 -0
  58. package/src/sources/it/npm.ts +36 -0
  59. package/src/sources/it/packagist.ts +43 -0
  60. package/src/sources/it/pypi.ts +30 -0
  61. package/src/sources/it/rubygems.ts +43 -0
  62. package/src/sources/it/stackoverflow.ts +39 -0
  63. package/src/sources/maps/apple_maps.ts +105 -0
  64. package/src/sources/maps/openstreetmap.ts +34 -0
  65. package/src/sources/maps/photon.ts +77 -0
  66. package/src/sources/news/bing_news.ts +95 -0
  67. package/src/sources/news/google_news.ts +80 -0
  68. package/src/sources/news/hackernews.ts +94 -0
  69. package/src/sources/news/yahoo_news.ts +77 -0
  70. package/src/sources/shopping/ebay.ts +96 -0
  71. package/src/sources/social/mastodon.ts +46 -0
  72. package/src/sources/social/medium.ts +52 -0
  73. package/src/sources/social/reddit.ts +48 -0
  74. package/src/sources/social/soundcloud.ts +64 -0
  75. package/src/sources/social/twitter.ts +56 -0
  76. package/src/sources/specialized/annas_archive.ts +97 -0
  77. package/src/sources/specialized/archive.ts +48 -0
  78. package/src/sources/specialized/genius.ts +43 -0
  79. package/src/sources/specialized/goodreads.ts +62 -0
  80. package/src/sources/specialized/imdb.ts +55 -0
  81. package/src/sources/specialized/openlibrary.ts +59 -0
  82. package/src/sources/specialized/wikipedia.ts +37 -0
  83. package/src/sources/specialized/wttr.ts +98 -0
  84. package/src/sources/torrents/1337x.ts +43 -0
  85. package/src/sources/torrents/eztv.ts +47 -0
  86. package/src/sources/torrents/kickass.ts +63 -0
  87. package/src/sources/torrents/nyaa.ts +53 -0
  88. package/src/sources/torrents/solidtorrents.ts +68 -0
  89. package/src/sources/torrents/thepiratebay.ts +57 -0
  90. package/src/sources/torrents/yts.ts +54 -0
  91. package/src/sources/videos/bing_videos.ts +91 -0
  92. package/src/sources/videos/dailymotion.ts +101 -0
  93. package/src/sources/videos/invidious.ts +86 -0
  94. package/src/sources/videos/peertube.ts +76 -0
  95. package/src/sources/videos/vimeo.ts +67 -0
  96. package/src/sources/videos/youtube.ts +78 -0
  97. package/src/suggest-next-words/autocomplete-ai.ts +38 -0
  98. package/src/suggest-next-words/autocomplete-search-engines.ts +384 -0
  99. package/src/suggest-next-words/misspelled-typos-8k.json +1 -0
  100. package/src/types/search-engine-interface.ts +27 -0
  101. package/src/types/search-result-types.ts +405 -0
  102. package/test/api.test.ts +128 -0
  103. package/test/autocomplete-ai.test.ts +20 -0
  104. package/test/autocomplete-engines.test.ts +131 -0
  105. package/test/engine-health-suite.test.ts +350 -0
  106. package/test/search.test.ts +69 -0
  107. package/test/sources-unit.test.ts +1152 -0
  108. package/test/sources.test.ts +182 -0
  109. package/test/test-utils.ts +81 -0
  110. package/tsconfig.json +16 -0
  111. package/vitest.config.ts +20 -0
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Unit tests for all search-web/sources engines
3
+ *
4
+ * Tests each engine function directly to verify it returns
5
+ * valid JSON results with the expected structure.
6
+ */
7
+
8
+ import { describe, it, expect } from "vitest";
9
+ import { EngineResult } from "../src/search-web-types.js";
10
+ import { extractResponseData } from "../src/engine.js";
11
+ import grab from "grab-url";
12
+
13
+ grab("", {
14
+ setDefaults: true,
15
+ headers: {
16
+ "User-Agent":
17
+ "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
18
+ },
19
+ });
20
+
21
+ // Import all engines from the exported list
22
+ import { ALL_ENGINES } from "../src/search.js";
23
+
24
+ /**
25
+ * Validates that results are a non-empty array of objects with
26
+ * at minimum title and content strings.
27
+ */
28
+ function assertValidResults(results: EngineResult[], engineName: string) {
29
+ expect(Array.isArray(results), `${engineName} should return an array`).toBe(
30
+ true,
31
+ );
32
+ expect(
33
+ results.length,
34
+ `${engineName} should return at least 1 result`,
35
+ ).toBeGreaterThan(0);
36
+
37
+ for (const r of results) {
38
+ expect(typeof r.title, `${engineName} result title should be string`).toBe(
39
+ "string",
40
+ );
41
+ expect(
42
+ r.title.length,
43
+ `${engineName} result title should not be empty`,
44
+ ).toBeGreaterThan(0);
45
+ expect(
46
+ typeof r.content,
47
+ `${engineName} result content should be string`,
48
+ ).toBe("string");
49
+ if (r.url) {
50
+ expect(typeof r.url).toBe("string");
51
+ }
52
+ if (r.engine) {
53
+ expect(typeof r.engine).toBe("string");
54
+ }
55
+ }
56
+ }
57
+
58
+ const DEFAULT_QUERIES: Record<string, string> = {
59
+ general: "typescript",
60
+ it: "typescript",
61
+ images: "sunset",
62
+ videos: "tutorial",
63
+ news: "technology",
64
+ academic: "machine%20learning",
65
+ torrents: "ubuntu",
66
+ social: "programming",
67
+ maps: "New%20York",
68
+ shopping: "laptop",
69
+ specialized: "books",
70
+ };
71
+
72
+ const SPECIFIC_QUERIES: Record<string, string> = {
73
+ npm: "express",
74
+ crates: "serde",
75
+ dockerhub: "nginx",
76
+ pypi: "requests",
77
+ packagist: "laravel",
78
+ rubygems: "rails",
79
+ imgur: "cats",
80
+ pixabay: "mountains",
81
+ wallhaven: "landscape",
82
+ deviantart: "art",
83
+ openclipart: "arrow",
84
+ vimeo: "documentary",
85
+ dailymotion: "music",
86
+ peertube: "linux",
87
+ arxiv: "neural%20networks",
88
+ wikidata: "python",
89
+ semantic_scholar: "deep%20learning",
90
+ crossref: "quantum%20computing",
91
+ pubmed: "cancer",
92
+ nyaa: "one%20piece",
93
+ yts: "inception",
94
+ eztv: "breaking%20bad",
95
+ medium: "javascript",
96
+ soundcloud: "lofi",
97
+ photon: "Paris",
98
+ apple_maps: "Tokyo",
99
+ wikipedia: "javascript",
100
+ imdb: "inception",
101
+ genius: "bohemian%20rhapsody",
102
+ archive: "books",
103
+ openlibrary: "tolkien",
104
+ wttr: "London",
105
+ annas_archive: "python",
106
+ goodreads: "dune",
107
+ };
108
+
109
+ // All engines with their query and category for organized testing
110
+ const engines = ALL_ENGINES.map((eng) => ({
111
+ name: eng.name,
112
+ fn: eng.fn,
113
+ category: eng.categories[0] || "general",
114
+ query:
115
+ SPECIFIC_QUERIES[eng.name] ||
116
+ DEFAULT_QUERIES[eng.categories[0] || "general"] ||
117
+ "test",
118
+ }));
119
+
120
+ // Group engines by category for organized describe blocks
121
+ const byCategory = engines.reduce(
122
+ (acc, e) => {
123
+ (acc[e.category] ??= []).push(e);
124
+ return acc;
125
+ },
126
+ {} as Record<string, typeof engines>,
127
+ );
128
+
129
+ // Track results across all tests for summary
130
+ const testResults: Array<{
131
+ name: string;
132
+ category: string;
133
+ status: "pass" | "fail";
134
+ resultCount: number;
135
+ timeMs: number;
136
+ error?: string;
137
+ }> = [];
138
+
139
+ describe("Source Engine Unit Tests", () => {
140
+ for (const [category, categoryEngines] of Object.entries(byCategory)) {
141
+ describe(`${category}`, () => {
142
+ for (const eng of categoryEngines) {
143
+ it(`${eng.name} returns valid JSON results`, async () => {
144
+ const start = Date.now();
145
+ let results: EngineResult[];
146
+
147
+ try {
148
+ let rawResults = await eng.fn(eng.query, 1);
149
+ results = extractResponseData(rawResults) as EngineResult[];
150
+ } catch (error) {
151
+ const errMsg =
152
+ error instanceof Error ? error.message : String(error);
153
+ testResults.push({
154
+ name: eng.name,
155
+ category: eng.category,
156
+ status: "fail",
157
+ resultCount: 0,
158
+ timeMs: Date.now() - start,
159
+ error: errMsg,
160
+ });
161
+ throw error;
162
+ }
163
+
164
+ const timeMs = Date.now() - start;
165
+
166
+ // Allow null/undefined to be treated as empty array
167
+ if (!results) results = [];
168
+
169
+ testResults.push({
170
+ name: eng.name,
171
+ category: eng.category,
172
+ status: results.length > 0 ? "pass" : "fail",
173
+ resultCount: results.length,
174
+ timeMs,
175
+ });
176
+
177
+ assertValidResults(results, eng.name);
178
+ }, 20000);
179
+ }
180
+ });
181
+ }
182
+ });
@@ -0,0 +1,81 @@
1
+ import { EngineResult } from "../src/engine";
2
+
3
+ /**
4
+ * Mock HTML response helper
5
+ */
6
+ export function createMockHtml(content: string): string {
7
+ return content;
8
+ }
9
+
10
+ /**
11
+ * Mock JSON response helper
12
+ */
13
+ export function createMockJson(data: any): any {
14
+ return data;
15
+ }
16
+
17
+ /**
18
+ * Validate engine result structure
19
+ */
20
+ export function validateEngineResult(result: EngineResult): boolean {
21
+ return (
22
+ typeof result.title === "string" &&
23
+ result.title.length > 0 &&
24
+ typeof result.content === "string" &&
25
+ (result.url === undefined || typeof result.url === "string") &&
26
+ (result.link === undefined || typeof result.link === "string") &&
27
+ (result.thumbnail === undefined || typeof result.thumbnail === "string") &&
28
+ (result.engine === undefined || typeof result.engine === "string")
29
+ );
30
+ }
31
+
32
+ /**
33
+ * Validate all results in an array
34
+ */
35
+ export function validateEngineResults(results: EngineResult[]): boolean {
36
+ if (!Array.isArray(results)) {
37
+ return false;
38
+ }
39
+ return results.every(validateEngineResult);
40
+ }
41
+
42
+ /**
43
+ * Common test queries for different categories
44
+ */
45
+ export const TEST_QUERIES = {
46
+ general: ["javascript", "python", "linux", "machine learning"],
47
+ code: ["react hooks", "typescript generics", "nodejs express"],
48
+ torrent: ["ubuntu", "debian", "linux mint"],
49
+ anime: ["one piece", "naruto", "attack on titan"],
50
+ movies: ["inception", "interstellar", "the matrix"],
51
+ tv: ["breaking bad", "game of thrones", "the office"],
52
+ music: ["lofi hip hop", "jazz", "classical music"],
53
+ social: ["javascript news", "tech trends", "programming"],
54
+ academic: ["quantum computing", "neural networks", "cryptography"],
55
+ };
56
+
57
+ /**
58
+ * Wait helper for rate limiting
59
+ */
60
+ export async function wait(ms: number): Promise<void> {
61
+ return new Promise((resolve) => setTimeout(resolve, ms));
62
+ }
63
+
64
+ /**
65
+ * Retry helper for flaky network requests
66
+ */
67
+ export async function retry<T>(
68
+ fn: () => Promise<T>,
69
+ retries: number = 3,
70
+ delay: number = 1000
71
+ ): Promise<T> {
72
+ try {
73
+ return await fn();
74
+ } catch (error) {
75
+ if (retries <= 0) {
76
+ throw error;
77
+ }
78
+ await wait(delay);
79
+ return retry(fn, retries - 1, delay * 2);
80
+ }
81
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "outDir": "./dist",
9
+ "esModuleInterop": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ },
12
+ "include": [
13
+ "src/**/*",
14
+ "test/**/*"
15
+ ]
16
+ }
@@ -0,0 +1,20 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ globals: true,
6
+ environment: 'node',
7
+ coverage: {
8
+ provider: 'v8',
9
+ reporter: ['text', 'json', 'html'],
10
+ exclude: [
11
+ 'node_modules/**',
12
+ 'dist/**',
13
+ '**/*.test.ts',
14
+ '**/__tests__/**'
15
+ ]
16
+ },
17
+ testTimeout: 30000, // 30 seconds for network requests
18
+ hookTimeout: 30000
19
+ }
20
+ });