llm-search-tools 1.1.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.
Files changed (126) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +244 -0
  3. package/dist/index.d.ts +18 -0
  4. package/dist/index.js +40 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/integration.test.d.ts +1 -0
  7. package/dist/integration.test.js +237 -0
  8. package/dist/modules/answerbox.test.d.ts +1 -0
  9. package/dist/modules/answerbox.test.js +105 -0
  10. package/dist/modules/autocomplete.d.ts +11 -0
  11. package/dist/modules/autocomplete.js +159 -0
  12. package/dist/modules/autocomplete.test.d.ts +1 -0
  13. package/dist/modules/autocomplete.test.js +188 -0
  14. package/dist/modules/common.d.ts +26 -0
  15. package/dist/modules/common.js +263 -0
  16. package/dist/modules/common.test.d.ts +1 -0
  17. package/dist/modules/common.test.js +87 -0
  18. package/dist/modules/crawl.d.ts +9 -0
  19. package/dist/modules/crawl.js +117 -0
  20. package/dist/modules/crawl.test.d.ts +1 -0
  21. package/dist/modules/crawl.test.js +48 -0
  22. package/dist/modules/events.d.ts +8 -0
  23. package/dist/modules/events.js +129 -0
  24. package/dist/modules/events.test.d.ts +1 -0
  25. package/dist/modules/events.test.js +104 -0
  26. package/dist/modules/finance.d.ts +10 -0
  27. package/dist/modules/finance.js +20 -0
  28. package/dist/modules/finance.test.d.ts +1 -0
  29. package/dist/modules/finance.test.js +77 -0
  30. package/dist/modules/flights.d.ts +8 -0
  31. package/dist/modules/flights.js +135 -0
  32. package/dist/modules/flights.test.d.ts +1 -0
  33. package/dist/modules/flights.test.js +128 -0
  34. package/dist/modules/hackernews.d.ts +8 -0
  35. package/dist/modules/hackernews.js +87 -0
  36. package/dist/modules/hackernews.js.map +1 -0
  37. package/dist/modules/images.test.d.ts +1 -0
  38. package/dist/modules/images.test.js +145 -0
  39. package/dist/modules/integrations.test.d.ts +1 -0
  40. package/dist/modules/integrations.test.js +93 -0
  41. package/dist/modules/media.d.ts +11 -0
  42. package/dist/modules/media.js +132 -0
  43. package/dist/modules/media.test.d.ts +1 -0
  44. package/dist/modules/media.test.js +186 -0
  45. package/dist/modules/news.d.ts +3 -0
  46. package/dist/modules/news.js +39 -0
  47. package/dist/modules/news.test.d.ts +1 -0
  48. package/dist/modules/news.test.js +88 -0
  49. package/dist/modules/parser.d.ts +19 -0
  50. package/dist/modules/parser.js +361 -0
  51. package/dist/modules/parser.test.d.ts +1 -0
  52. package/dist/modules/parser.test.js +151 -0
  53. package/dist/modules/reddit.d.ts +21 -0
  54. package/dist/modules/reddit.js +107 -0
  55. package/dist/modules/scrape.d.ts +16 -0
  56. package/dist/modules/scrape.js +272 -0
  57. package/dist/modules/scrape.test.d.ts +1 -0
  58. package/dist/modules/scrape.test.js +232 -0
  59. package/dist/modules/scraper.d.ts +12 -0
  60. package/dist/modules/scraper.js +640 -0
  61. package/dist/modules/scrapers/anidb.d.ts +8 -0
  62. package/dist/modules/scrapers/anidb.js +156 -0
  63. package/dist/modules/scrapers/duckduckgo.d.ts +6 -0
  64. package/dist/modules/scrapers/duckduckgo.js +284 -0
  65. package/dist/modules/scrapers/google-news.d.ts +2 -0
  66. package/dist/modules/scrapers/google-news.js +60 -0
  67. package/dist/modules/scrapers/google.d.ts +6 -0
  68. package/dist/modules/scrapers/google.js +211 -0
  69. package/dist/modules/scrapers/searxng.d.ts +2 -0
  70. package/dist/modules/scrapers/searxng.js +93 -0
  71. package/dist/modules/scrapers/thetvdb.d.ts +3 -0
  72. package/dist/modules/scrapers/thetvdb.js +147 -0
  73. package/dist/modules/scrapers/tmdb.d.ts +3 -0
  74. package/dist/modules/scrapers/tmdb.js +172 -0
  75. package/dist/modules/scrapers/yahoo-finance.d.ts +2 -0
  76. package/dist/modules/scrapers/yahoo-finance.js +33 -0
  77. package/dist/modules/search.d.ts +5 -0
  78. package/dist/modules/search.js +45 -0
  79. package/dist/modules/search.js.map +1 -0
  80. package/dist/modules/search.test.d.ts +1 -0
  81. package/dist/modules/search.test.js +219 -0
  82. package/dist/modules/urbandictionary.d.ts +12 -0
  83. package/dist/modules/urbandictionary.js +26 -0
  84. package/dist/modules/webpage.d.ts +4 -0
  85. package/dist/modules/webpage.js +150 -0
  86. package/dist/modules/webpage.js.map +1 -0
  87. package/dist/modules/wikipedia.d.ts +5 -0
  88. package/dist/modules/wikipedia.js +85 -0
  89. package/dist/modules/wikipedia.js.map +1 -0
  90. package/dist/scripts/interactive-search.d.ts +1 -0
  91. package/dist/scripts/interactive-search.js +98 -0
  92. package/dist/test.d.ts +1 -0
  93. package/dist/test.js +179 -0
  94. package/dist/test.js.map +1 -0
  95. package/dist/testBraveSearch.d.ts +1 -0
  96. package/dist/testBraveSearch.js +34 -0
  97. package/dist/testDuckDuckGo.d.ts +1 -0
  98. package/dist/testDuckDuckGo.js +52 -0
  99. package/dist/testEcosia.d.ts +1 -0
  100. package/dist/testEcosia.js +57 -0
  101. package/dist/testSearchModule.d.ts +1 -0
  102. package/dist/testSearchModule.js +95 -0
  103. package/dist/testwebpage.d.ts +1 -0
  104. package/dist/testwebpage.js +81 -0
  105. package/dist/types.d.ts +174 -0
  106. package/dist/types.js +3 -0
  107. package/dist/types.js.map +1 -0
  108. package/dist/utils/createTestDocx.d.ts +1 -0
  109. package/dist/utils/createTestDocx.js +58 -0
  110. package/dist/utils/htmlcleaner.d.ts +20 -0
  111. package/dist/utils/htmlcleaner.js +172 -0
  112. package/docs/README.md +275 -0
  113. package/docs/autocomplete.md +73 -0
  114. package/docs/crawling.md +88 -0
  115. package/docs/events.md +58 -0
  116. package/docs/examples.md +158 -0
  117. package/docs/finance.md +60 -0
  118. package/docs/flights.md +71 -0
  119. package/docs/hackernews.md +121 -0
  120. package/docs/media.md +87 -0
  121. package/docs/news.md +75 -0
  122. package/docs/parser.md +197 -0
  123. package/docs/scraper.md +347 -0
  124. package/docs/search.md +106 -0
  125. package/docs/wikipedia.md +91 -0
  126. package/package.json +97 -0
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchMedia = searchMedia;
4
+ exports.getMediaDetails = getMediaDetails;
5
+ const tmdb_1 = require("./scrapers/tmdb");
6
+ const thetvdb_1 = require("./scrapers/thetvdb");
7
+ const anidb_1 = require("./scrapers/anidb");
8
+ /**
9
+ * Unified Media Search
10
+ * Coordinates between TMDB, TheTVDB, and AniDB with fallback logic.
11
+ */
12
+ async function searchMedia(query, options = {}) {
13
+ const { type } = options;
14
+ let results = [];
15
+ const errors = [];
16
+ // Strategy Pattern based on media type
17
+ // 1. ANIME Specific Strategy
18
+ if (type === "anime") {
19
+ try {
20
+ // Try AniDB first for Anime
21
+ results = await (0, anidb_1.searchAniDB)(query, options);
22
+ if (results.length > 0)
23
+ return results;
24
+ }
25
+ catch (e) {
26
+ errors.push(e);
27
+ }
28
+ // Fallback to TMDB with anime/tv type
29
+ try {
30
+ results = await (0, tmdb_1.searchTMDB)(query, { ...options, type: "tv" }); // Anime is often under TV in TMDB
31
+ // Filter for animation genre if possible, but for now just return results
32
+ if (results.length > 0)
33
+ return results;
34
+ }
35
+ catch (e) {
36
+ errors.push(e);
37
+ }
38
+ }
39
+ // 2. TV SHOW Strategy
40
+ else if (type === "tv") {
41
+ try {
42
+ // TMDB is generally faster and better structured for general TV
43
+ results = await (0, tmdb_1.searchTMDB)(query, options);
44
+ if (results.length > 0)
45
+ return results;
46
+ }
47
+ catch (e) {
48
+ errors.push(e);
49
+ }
50
+ try {
51
+ // Fallback to TheTVDB
52
+ results = await (0, thetvdb_1.searchTheTVDB)(query, options);
53
+ if (results.length > 0)
54
+ return results;
55
+ }
56
+ catch (e) {
57
+ errors.push(e);
58
+ }
59
+ }
60
+ // 3. MOVIE or GENERAL Strategy
61
+ else {
62
+ try {
63
+ // TMDB is the best all-rounder
64
+ results = await (0, tmdb_1.searchTMDB)(query, options);
65
+ if (results.length > 0)
66
+ return results;
67
+ }
68
+ catch (e) {
69
+ errors.push(e);
70
+ }
71
+ // If generic search and TMDB failed or found nothing, maybe try TheTVDB?
72
+ // Only if type wasn't specified as "movie" (TheTVDB is mostly TV)
73
+ if (type !== "movie" && results.length === 0) {
74
+ try {
75
+ const tvdbResults = await (0, thetvdb_1.searchTheTVDB)(query, options);
76
+ if (tvdbResults.length > 0)
77
+ return tvdbResults;
78
+ }
79
+ catch (e) {
80
+ errors.push(e);
81
+ }
82
+ }
83
+ }
84
+ if (results.length === 0 && errors.length > 0) {
85
+ throw {
86
+ message: "Media search failed on all providers",
87
+ code: "MEDIA_SEARCH_FAILED",
88
+ originalError: errors[0], // Return the first error for context
89
+ };
90
+ }
91
+ return results;
92
+ }
93
+ /**
94
+ * Get detailed information for a media result.
95
+ * Automatically determines the source based on the URL or accepts an explicit source.
96
+ */
97
+ async function getMediaDetails(url, source, options = {}) {
98
+ // Infer source from URL if not provided
99
+ if (!source) {
100
+ if (url.includes("themoviedb.org"))
101
+ source = "tmdb";
102
+ else if (url.includes("thetvdb.com"))
103
+ source = "thetvdb";
104
+ else if (url.includes("anidb.net"))
105
+ source = "anidb";
106
+ else {
107
+ throw {
108
+ message: "Could not determine media source from URL",
109
+ code: "UNKNOWN_MEDIA_SOURCE",
110
+ };
111
+ }
112
+ }
113
+ try {
114
+ switch (source) {
115
+ case "tmdb":
116
+ return await (0, tmdb_1.getTMDBDetails)(url, options);
117
+ case "thetvdb":
118
+ return await (0, thetvdb_1.getTheTVDBDetails)(url, options);
119
+ case "anidb":
120
+ return await (0, anidb_1.getAniDBDetails)(url, options);
121
+ default:
122
+ return {};
123
+ }
124
+ }
125
+ catch (error) {
126
+ throw {
127
+ message: `Failed to get details from ${source}`,
128
+ code: "MEDIA_DETAILS_FAILED",
129
+ originalError: error,
130
+ };
131
+ }
132
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const vitest_1 = require("vitest");
37
+ const media_1 = require("./media");
38
+ const aniDB = __importStar(require("./scrapers/anidb"));
39
+ const tmdb = __importStar(require("./scrapers/tmdb"));
40
+ const theTVDB = __importStar(require("./scrapers/thetvdb"));
41
+ // Mock the scrapers
42
+ vitest_1.vi.mock("./scrapers/anidb");
43
+ vitest_1.vi.mock("./scrapers/tmdb");
44
+ vitest_1.vi.mock("./scrapers/thetvdb");
45
+ (0, vitest_1.describe)("Media Search Coordinator", () => {
46
+ const mockAnimeResult = {
47
+ title: "Test Anime",
48
+ url: "https://anidb.net/anime/1",
49
+ source: "anidb",
50
+ mediaType: "anime",
51
+ };
52
+ const mockTVResult = {
53
+ title: "Test TV Show",
54
+ url: "https://themoviedb.org/tv/1",
55
+ source: "tmdb",
56
+ mediaType: "tv",
57
+ };
58
+ const mockMovieResult = {
59
+ title: "Test Movie",
60
+ url: "https://themoviedb.org/movie/1",
61
+ source: "tmdb",
62
+ mediaType: "movie",
63
+ };
64
+ (0, vitest_1.beforeEach)(() => {
65
+ vitest_1.vi.resetAllMocks();
66
+ });
67
+ (0, vitest_1.describe)("searchMedia", () => {
68
+ (0, vitest_1.describe)("Anime Strategy", () => {
69
+ (0, vitest_1.it)("should try AniDB first for anime", async () => {
70
+ vitest_1.vi.mocked(aniDB.searchAniDB).mockResolvedValue([mockAnimeResult]);
71
+ const results = await (0, media_1.searchMedia)("Naruto", { type: "anime" });
72
+ (0, vitest_1.expect)(aniDB.searchAniDB).toHaveBeenCalledWith("Naruto", vitest_1.expect.objectContaining({ type: "anime" }));
73
+ (0, vitest_1.expect)(tmdb.searchTMDB).not.toHaveBeenCalled();
74
+ (0, vitest_1.expect)(results).toEqual([mockAnimeResult]);
75
+ });
76
+ (0, vitest_1.it)("should fall back to TMDB if AniDB fails", async () => {
77
+ vitest_1.vi.mocked(aniDB.searchAniDB).mockRejectedValue(new Error("AniDB down"));
78
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockResolvedValue([mockTVResult]);
79
+ const results = await (0, media_1.searchMedia)("Naruto", { type: "anime" });
80
+ (0, vitest_1.expect)(aniDB.searchAniDB).toHaveBeenCalled();
81
+ (0, vitest_1.expect)(tmdb.searchTMDB).toHaveBeenCalledWith("Naruto", vitest_1.expect.objectContaining({ type: "tv" }));
82
+ (0, vitest_1.expect)(results).toEqual([mockTVResult]);
83
+ });
84
+ (0, vitest_1.it)("should fall back to TMDB if AniDB returns empty", async () => {
85
+ vitest_1.vi.mocked(aniDB.searchAniDB).mockResolvedValue([]);
86
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockResolvedValue([mockTVResult]);
87
+ const results = await (0, media_1.searchMedia)("Naruto", { type: "anime" });
88
+ (0, vitest_1.expect)(aniDB.searchAniDB).toHaveBeenCalled();
89
+ (0, vitest_1.expect)(tmdb.searchTMDB).toHaveBeenCalled();
90
+ (0, vitest_1.expect)(results).toEqual([mockTVResult]);
91
+ });
92
+ });
93
+ (0, vitest_1.describe)("TV Strategy", () => {
94
+ (0, vitest_1.it)("should try TMDB first for TV", async () => {
95
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockResolvedValue([mockTVResult]);
96
+ const results = await (0, media_1.searchMedia)("Breaking Bad", { type: "tv" });
97
+ (0, vitest_1.expect)(tmdb.searchTMDB).toHaveBeenCalledWith("Breaking Bad", vitest_1.expect.objectContaining({ type: "tv" }));
98
+ (0, vitest_1.expect)(theTVDB.searchTheTVDB).not.toHaveBeenCalled();
99
+ (0, vitest_1.expect)(results).toEqual([mockTVResult]);
100
+ });
101
+ (0, vitest_1.it)("should fall back to TheTVDB if TMDB fails", async () => {
102
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockRejectedValue(new Error("TMDB down"));
103
+ vitest_1.vi.mocked(theTVDB.searchTheTVDB).mockResolvedValue([mockTVResult]);
104
+ const results = await (0, media_1.searchMedia)("Breaking Bad", { type: "tv" });
105
+ (0, vitest_1.expect)(tmdb.searchTMDB).toHaveBeenCalled();
106
+ (0, vitest_1.expect)(theTVDB.searchTheTVDB).toHaveBeenCalledWith("Breaking Bad", vitest_1.expect.objectContaining({ type: "tv" }));
107
+ (0, vitest_1.expect)(results).toEqual([mockTVResult]);
108
+ });
109
+ });
110
+ (0, vitest_1.describe)("General/Movie Strategy", () => {
111
+ (0, vitest_1.it)("should default to TMDB for unspecified type", async () => {
112
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockResolvedValue([mockMovieResult]);
113
+ const results = await (0, media_1.searchMedia)("Inception", {});
114
+ (0, vitest_1.expect)(tmdb.searchTMDB).toHaveBeenCalledWith("Inception", {});
115
+ (0, vitest_1.expect)(results).toEqual([mockMovieResult]);
116
+ });
117
+ (0, vitest_1.it)("should try TheTVDB if general TMDB search fails and type is not movie", async () => {
118
+ // TMDB fails/returns empty
119
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockResolvedValue([]);
120
+ vitest_1.vi.mocked(theTVDB.searchTheTVDB).mockResolvedValue([mockTVResult]);
121
+ // No type specified, so it could be a TV show
122
+ const results = await (0, media_1.searchMedia)("Unknown Show", {});
123
+ (0, vitest_1.expect)(tmdb.searchTMDB).toHaveBeenCalled();
124
+ (0, vitest_1.expect)(theTVDB.searchTheTVDB).toHaveBeenCalled();
125
+ (0, vitest_1.expect)(results).toEqual([mockTVResult]);
126
+ });
127
+ (0, vitest_1.it)("should NOT try TheTVDB if type is explicitly movie", async () => {
128
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockResolvedValue([]);
129
+ try {
130
+ await (0, media_1.searchMedia)("Unknown Movie", { type: "movie" });
131
+ }
132
+ catch {
133
+ // Expected to throw
134
+ }
135
+ (0, vitest_1.expect)(tmdb.searchTMDB).toHaveBeenCalled();
136
+ (0, vitest_1.expect)(theTVDB.searchTheTVDB).not.toHaveBeenCalled();
137
+ });
138
+ });
139
+ (0, vitest_1.describe)("Error Handling", () => {
140
+ (0, vitest_1.it)("should throw SearchError if all providers fail", async () => {
141
+ vitest_1.vi.mocked(aniDB.searchAniDB).mockRejectedValue(new Error("Fail 1"));
142
+ vitest_1.vi.mocked(tmdb.searchTMDB).mockRejectedValue(new Error("Fail 2"));
143
+ await (0, vitest_1.expect)((0, media_1.searchMedia)("Nothing", { type: "anime" })).rejects.toMatchObject({
144
+ code: "MEDIA_SEARCH_FAILED",
145
+ message: "Media search failed on all providers",
146
+ });
147
+ });
148
+ });
149
+ });
150
+ (0, vitest_1.describe)("getMediaDetails", () => {
151
+ const mockDetails = { genres: ["Action"], cast: ["Actor A"] };
152
+ (0, vitest_1.it)("should call getTMDBDetails for TMDB URLs", async () => {
153
+ vitest_1.vi.mocked(tmdb.getTMDBDetails).mockResolvedValue(mockDetails);
154
+ const url = "https://www.themoviedb.org/movie/123";
155
+ const result = await (0, media_1.getMediaDetails)(url);
156
+ (0, vitest_1.expect)(tmdb.getTMDBDetails).toHaveBeenCalledWith(url, vitest_1.expect.anything());
157
+ (0, vitest_1.expect)(result).toEqual(mockDetails);
158
+ });
159
+ (0, vitest_1.it)("should call getTheTVDBDetails for TheTVDB URLs", async () => {
160
+ vitest_1.vi.mocked(theTVDB.getTheTVDBDetails).mockResolvedValue(mockDetails);
161
+ const url = "https://thetvdb.com/series/breaking-bad";
162
+ const result = await (0, media_1.getMediaDetails)(url);
163
+ (0, vitest_1.expect)(theTVDB.getTheTVDBDetails).toHaveBeenCalledWith(url, vitest_1.expect.anything());
164
+ (0, vitest_1.expect)(result).toEqual(mockDetails);
165
+ });
166
+ (0, vitest_1.it)("should call getAniDBDetails for AniDB URLs", async () => {
167
+ vitest_1.vi.mocked(aniDB.getAniDBDetails).mockResolvedValue(mockDetails);
168
+ const url = "https://anidb.net/anime/123";
169
+ const result = await (0, media_1.getMediaDetails)(url);
170
+ (0, vitest_1.expect)(aniDB.getAniDBDetails).toHaveBeenCalledWith(url, vitest_1.expect.anything());
171
+ (0, vitest_1.expect)(result).toEqual(mockDetails);
172
+ });
173
+ (0, vitest_1.it)("should use explicit source if provided", async () => {
174
+ vitest_1.vi.mocked(tmdb.getTMDBDetails).mockResolvedValue(mockDetails);
175
+ const url = "https://some-mirror.com/movie/123";
176
+ await (0, media_1.getMediaDetails)(url, "tmdb");
177
+ (0, vitest_1.expect)(tmdb.getTMDBDetails).toHaveBeenCalledWith(url, vitest_1.expect.anything());
178
+ });
179
+ (0, vitest_1.it)("should throw error for unknown sources", async () => {
180
+ const url = "https://unknown.com/movie/123";
181
+ await (0, vitest_1.expect)((0, media_1.getMediaDetails)(url)).rejects.toMatchObject({
182
+ code: "UNKNOWN_MEDIA_SOURCE",
183
+ });
184
+ });
185
+ });
186
+ });
@@ -0,0 +1,3 @@
1
+ import { ScraperOptions, NewsResult } from "../types";
2
+ export { searchGoogleNews } from "./scrapers/google-news";
3
+ export declare function searchNews(query: string, options?: ScraperOptions): Promise<NewsResult[]>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchGoogleNews = void 0;
4
+ exports.searchNews = searchNews;
5
+ const google_news_1 = require("./scrapers/google-news");
6
+ const duckduckgo_1 = require("./scrapers/duckduckgo");
7
+ // Re-export specific news search functions
8
+ var google_news_2 = require("./scrapers/google-news");
9
+ Object.defineProperty(exports, "searchGoogleNews", { enumerable: true, get: function () { return google_news_2.searchGoogleNews; } });
10
+ // Unified news search that tries engines in sequence: Google News -> DuckDuckGo News
11
+ async function searchNews(query, options = {}) {
12
+ const errors = [];
13
+ // 1. Try Google News first (specialized news scraper)
14
+ try {
15
+ return await (0, google_news_1.searchGoogleNews)(query, options);
16
+ }
17
+ catch (err) {
18
+ errors.push(err);
19
+ }
20
+ // 2. Try DuckDuckGo with news category
21
+ try {
22
+ const ddgOptions = { ...options, category: "news" };
23
+ const results = await (0, duckduckgo_1.searchDuckDuckGo)(query, ddgOptions);
24
+ // Convert generic SearchResult to NewsResult
25
+ return results.map((result) => ({
26
+ ...result,
27
+ source: "duckduckgo-news",
28
+ }));
29
+ }
30
+ catch (err) {
31
+ errors.push(err);
32
+ }
33
+ // If all failed, throw error with details
34
+ throw {
35
+ message: "All news search engines failed",
36
+ code: "ALL_NEWS_ENGINES_FAILED",
37
+ errors,
38
+ };
39
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vitest_1 = require("vitest");
7
+ const news_1 = require("./news");
8
+ const google_news_1 = require("./scrapers/google-news");
9
+ const duckduckgo_1 = require("./scrapers/duckduckgo");
10
+ const google_news_scraper_1 = __importDefault(require("google-news-scraper"));
11
+ // Mock dependencies
12
+ vitest_1.vi.mock("google-news-scraper");
13
+ vitest_1.vi.mock("./scrapers/duckduckgo");
14
+ vitest_1.vi.mock("./scrapers/google-news", async () => {
15
+ const actual = await vitest_1.vi.importActual("./scrapers/google-news");
16
+ return {
17
+ ...actual,
18
+ // We want to test the real implementation of searchGoogleNews sometimes,
19
+ // but for searchNews orchestrator tests we might want to mock it.
20
+ // However, since searchGoogleNews is simple, we can mock the underlying library instead.
21
+ };
22
+ });
23
+ (0, vitest_1.describe)("News Module", () => {
24
+ (0, vitest_1.beforeEach)(() => {
25
+ vitest_1.vi.resetAllMocks();
26
+ });
27
+ (0, vitest_1.describe)("searchGoogleNews", () => {
28
+ (0, vitest_1.it)("should return formatted news results", async () => {
29
+ const mockArticles = [
30
+ {
31
+ title: "Test News",
32
+ link: "https://news.com/article",
33
+ image: "https://news.com/image.jpg",
34
+ source: "News Source",
35
+ time: "2 hours ago",
36
+ subtitle: "Snippet text",
37
+ },
38
+ ];
39
+ google_news_scraper_1.default.mockResolvedValue(mockArticles);
40
+ const results = await (0, google_news_1.searchGoogleNews)("test google news");
41
+ (0, vitest_1.expect)(results).toHaveLength(1);
42
+ (0, vitest_1.expect)(results[0]).toEqual({
43
+ title: "Test News",
44
+ url: "https://news.com/article",
45
+ imageUrl: "https://news.com/image.jpg",
46
+ source: "google-news",
47
+ sourceName: "News Source",
48
+ publishedAt: "2 hours ago",
49
+ snippet: "Snippet text",
50
+ });
51
+ });
52
+ (0, vitest_1.it)("should handle errors", async () => {
53
+ google_news_scraper_1.default.mockRejectedValue(new Error("Scrape failed"));
54
+ await (0, vitest_1.expect)((0, google_news_1.searchGoogleNews)("test error")).rejects.toMatchObject({
55
+ code: "GOOGLE_NEWS_SEARCH_ERROR",
56
+ });
57
+ });
58
+ });
59
+ (0, vitest_1.describe)("searchNews (Orchestrator)", () => {
60
+ (0, vitest_1.it)("should try Google News first", async () => {
61
+ const mockArticles = [{ title: "GNews", link: "url", source: "G" }];
62
+ google_news_scraper_1.default.mockResolvedValue(mockArticles);
63
+ const results = await (0, news_1.searchNews)("test orchestrator");
64
+ (0, vitest_1.expect)(results[0].source).toBe("google-news");
65
+ (0, vitest_1.expect)(google_news_scraper_1.default).toHaveBeenCalled();
66
+ (0, vitest_1.expect)(duckduckgo_1.searchDuckDuckGo).not.toHaveBeenCalled();
67
+ });
68
+ (0, vitest_1.it)("should fallback to DuckDuckGo if Google News fails", async () => {
69
+ // Mock Google News failure
70
+ google_news_scraper_1.default.mockRejectedValue(new Error("Fail"));
71
+ // Mock DDG success
72
+ duckduckgo_1.searchDuckDuckGo.mockResolvedValue([
73
+ { title: "DDG News", url: "url", snippet: "desc", source: "duckduckgo" },
74
+ ]);
75
+ const results = await (0, news_1.searchNews)("test fallback");
76
+ (0, vitest_1.expect)(results[0].source).toBe("duckduckgo-news");
77
+ (0, vitest_1.expect)(google_news_scraper_1.default).toHaveBeenCalled();
78
+ (0, vitest_1.expect)(duckduckgo_1.searchDuckDuckGo).toHaveBeenCalledWith("test fallback", vitest_1.expect.objectContaining({ category: "news" }));
79
+ });
80
+ (0, vitest_1.it)("should throw if all providers fail", async () => {
81
+ google_news_scraper_1.default.mockRejectedValue(new Error("Fail 1"));
82
+ duckduckgo_1.searchDuckDuckGo.mockRejectedValue(new Error("Fail 2"));
83
+ await (0, vitest_1.expect)((0, news_1.searchNews)("test all fail")).rejects.toMatchObject({
84
+ code: "ALL_NEWS_ENGINES_FAILED",
85
+ });
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,19 @@
1
+ export type FileType = "pdf" | "docx" | "csv" | "image" | "text" | "xml" | "json" | "unknown";
2
+ export interface ParseOptions {
3
+ language?: string;
4
+ csv?: {
5
+ delimiter?: string;
6
+ columns?: boolean;
7
+ };
8
+ xml?: {
9
+ ignoreAttributes?: boolean;
10
+ parseAttributeValue?: boolean;
11
+ };
12
+ }
13
+ export interface ParseResult {
14
+ type: FileType;
15
+ text: string;
16
+ metadata?: Record<string, unknown>;
17
+ data?: unknown;
18
+ }
19
+ export declare function parse(pathOrBuffer: string | Buffer, options?: ParseOptions, filename?: string): Promise<ParseResult>;