ubersearch 0.0.0-development

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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +374 -0
  3. package/package.json +76 -0
  4. package/src/app/index.ts +30 -0
  5. package/src/bootstrap/container.ts +157 -0
  6. package/src/cli.ts +380 -0
  7. package/src/config/defineConfig.ts +176 -0
  8. package/src/config/load.ts +368 -0
  9. package/src/config/types.ts +86 -0
  10. package/src/config/validation.ts +148 -0
  11. package/src/core/cache.ts +74 -0
  12. package/src/core/container.ts +268 -0
  13. package/src/core/credits/CreditManager.ts +158 -0
  14. package/src/core/credits/CreditStateProvider.ts +151 -0
  15. package/src/core/credits/FileCreditStateProvider.ts +137 -0
  16. package/src/core/credits/index.ts +3 -0
  17. package/src/core/docker/dockerComposeHelper.ts +177 -0
  18. package/src/core/docker/dockerLifecycleManager.ts +361 -0
  19. package/src/core/docker/index.ts +8 -0
  20. package/src/core/logger.ts +146 -0
  21. package/src/core/orchestrator.ts +103 -0
  22. package/src/core/paths.ts +157 -0
  23. package/src/core/provider/ILifecycleProvider.ts +120 -0
  24. package/src/core/provider/ProviderFactory.ts +120 -0
  25. package/src/core/provider.ts +61 -0
  26. package/src/core/serviceKeys.ts +45 -0
  27. package/src/core/strategy/AllProvidersStrategy.ts +245 -0
  28. package/src/core/strategy/FirstSuccessStrategy.ts +98 -0
  29. package/src/core/strategy/ISearchStrategy.ts +94 -0
  30. package/src/core/strategy/StrategyFactory.ts +204 -0
  31. package/src/core/strategy/index.ts +9 -0
  32. package/src/core/strategy/types.ts +56 -0
  33. package/src/core/types.ts +58 -0
  34. package/src/index.ts +1 -0
  35. package/src/plugin/PluginRegistry.ts +336 -0
  36. package/src/plugin/builtin.ts +130 -0
  37. package/src/plugin/index.ts +33 -0
  38. package/src/plugin/types.ts +212 -0
  39. package/src/providers/BaseProvider.ts +49 -0
  40. package/src/providers/brave.ts +66 -0
  41. package/src/providers/constants.ts +13 -0
  42. package/src/providers/helpers/index.ts +24 -0
  43. package/src/providers/helpers/lifecycleHelpers.ts +110 -0
  44. package/src/providers/helpers/resultMappers.ts +168 -0
  45. package/src/providers/index.ts +6 -0
  46. package/src/providers/linkup.ts +114 -0
  47. package/src/providers/retry.ts +95 -0
  48. package/src/providers/searchxng.ts +163 -0
  49. package/src/providers/tavily.ts +73 -0
  50. package/src/providers/types/index.ts +185 -0
  51. package/src/providers/utils.ts +182 -0
  52. package/src/tool/allSearchTool.ts +110 -0
  53. package/src/tool/interface.ts +71 -0
@@ -0,0 +1,245 @@
1
+ /**
2
+ * AllProvidersStrategy - Queries all available providers and combines their results
3
+ *
4
+ * This strategy executes search queries against all configured providers,
5
+ * collecting results from all successful providers. Supports both sequential
6
+ * and parallel execution modes. Failed providers are logged but don't stop
7
+ * the overall search.
8
+ */
9
+
10
+ import { createLogger } from "../logger";
11
+ import type { SearchProvider } from "../provider";
12
+ import type { EngineId, SearchResponse, SearchResultItem } from "../types";
13
+ import { SearchError } from "../types";
14
+ import type {
15
+ EngineAttempt,
16
+ ISearchStrategy,
17
+ AllSearchOptions,
18
+ StrategyContext,
19
+ StrategyResult,
20
+ } from "./ISearchStrategy";
21
+
22
+ const log = createLogger("AllProviders");
23
+
24
+ /**
25
+ * Result from a single engine search attempt
26
+ */
27
+ interface EngineSearchResult {
28
+ engineId: EngineId;
29
+ response?: SearchResponse;
30
+ error?: Error;
31
+ }
32
+
33
+ /**
34
+ * Strategy implementation that queries all providers and combines results
35
+ *
36
+ * Supports two execution modes:
37
+ * - Sequential (default): Providers are queried one at a time
38
+ * - Parallel: All providers are queried simultaneously using Promise.allSettled
39
+ */
40
+ export class AllProvidersStrategy implements ISearchStrategy {
41
+ /**
42
+ * Execute search against all providers and combine results
43
+ *
44
+ * @param query - The search query string
45
+ * @param engineIds - Ordered list of engine IDs to search
46
+ * @param options - Search options (limit, includeRaw, parallel, etc.)
47
+ * @param context - Strategy context with registry and credits
48
+ * @returns Combined results from all successful providers with attempt metadata
49
+ */
50
+ async execute(
51
+ query: string,
52
+ engineIds: EngineId[],
53
+ options: AllSearchOptions,
54
+ context: StrategyContext,
55
+ ): Promise<StrategyResult> {
56
+ // Choose execution mode based on options
57
+ if (options.parallel) {
58
+ return this.executeParallel(query, engineIds, options, context);
59
+ }
60
+ return this.executeSequential(query, engineIds, options, context);
61
+ }
62
+
63
+ /**
64
+ * Execute searches sequentially (original behavior)
65
+ */
66
+ private async executeSequential(
67
+ query: string,
68
+ engineIds: EngineId[],
69
+ options: AllSearchOptions,
70
+ context: StrategyContext,
71
+ ): Promise<StrategyResult> {
72
+ const results: SearchResultItem[] = [];
73
+ const attempts: EngineAttempt[] = [];
74
+
75
+ // Query each engine sequentially
76
+ for (const engineId of engineIds) {
77
+ const provider = context.providerRegistry.get(engineId);
78
+ if (!provider) {
79
+ attempts.push({ engineId, success: false, reason: "no_provider" });
80
+ continue;
81
+ }
82
+
83
+ // Check credit availability
84
+ if (!context.creditManager.hasSufficientCredits(engineId)) {
85
+ attempts.push({ engineId, success: false, reason: "out_of_credit" });
86
+ continue;
87
+ }
88
+
89
+ try {
90
+ // Execute search
91
+ const response = await provider.search({
92
+ query,
93
+ limit: options.limit,
94
+ includeRaw: options.includeRaw,
95
+ });
96
+
97
+ // Deduct credits
98
+ if (!context.creditManager.charge(engineId)) {
99
+ // This shouldn't happen because we checked above, but handle gracefully
100
+ attempts.push({ engineId, success: false, reason: "out_of_credit" });
101
+ continue;
102
+ }
103
+
104
+ // Record success
105
+ attempts.push({ engineId, success: true });
106
+
107
+ // Add results, applying limit if specified
108
+ if (options.limit !== undefined) {
109
+ results.push(...response.items.slice(0, options.limit - results.length));
110
+ } else {
111
+ results.push(...response.items);
112
+ }
113
+ } catch (error) {
114
+ // Record failure
115
+ if (error instanceof SearchError) {
116
+ attempts.push({ engineId, success: false, reason: error.reason });
117
+ } else {
118
+ attempts.push({ engineId, success: false, reason: "unknown" });
119
+ }
120
+
121
+ // Log warning but continue with other providers
122
+ log.warn(
123
+ `Search failed for ${engineId}: ${error instanceof Error ? error.message : String(error)}`,
124
+ );
125
+ }
126
+ }
127
+
128
+ // Apply final limit if specified
129
+ if (options.limit !== undefined && results.length > options.limit) {
130
+ results.splice(options.limit);
131
+ }
132
+
133
+ return { results, attempts };
134
+ }
135
+
136
+ /**
137
+ * Execute searches in parallel using Promise.allSettled
138
+ *
139
+ * This provides faster execution when querying multiple providers,
140
+ * as all requests are made simultaneously.
141
+ */
142
+ private async executeParallel(
143
+ query: string,
144
+ engineIds: EngineId[],
145
+ options: AllSearchOptions,
146
+ context: StrategyContext,
147
+ ): Promise<StrategyResult> {
148
+ const results: SearchResultItem[] = [];
149
+ const attempts: EngineAttempt[] = [];
150
+
151
+ // Filter engines that are available and have credits
152
+ const eligibleEngines: { engineId: EngineId; provider: SearchProvider }[] = [];
153
+ const ineligibleAttempts: EngineAttempt[] = [];
154
+
155
+ for (const engineId of engineIds) {
156
+ const provider = context.providerRegistry.get(engineId);
157
+ if (!provider) {
158
+ ineligibleAttempts.push({ engineId, success: false, reason: "no_provider" });
159
+ continue;
160
+ }
161
+
162
+ if (!context.creditManager.hasSufficientCredits(engineId)) {
163
+ ineligibleAttempts.push({ engineId, success: false, reason: "out_of_credit" });
164
+ continue;
165
+ }
166
+
167
+ eligibleEngines.push({ engineId, provider });
168
+ }
169
+
170
+ // Execute all eligible searches in parallel
171
+ const searchPromises = eligibleEngines.map(
172
+ async ({ engineId, provider }): Promise<EngineSearchResult> => {
173
+ try {
174
+ const response = await provider.search({
175
+ query,
176
+ limit: options.limit,
177
+ includeRaw: options.includeRaw,
178
+ });
179
+ return { engineId, response };
180
+ } catch (error) {
181
+ return { engineId, error: error instanceof Error ? error : new Error(String(error)) };
182
+ }
183
+ },
184
+ );
185
+
186
+ // Wait for all searches to complete
187
+ const searchResults = await Promise.allSettled(searchPromises);
188
+
189
+ // Process results in original order (maintain engine priority)
190
+ for (let i = 0; i < searchResults.length; i++) {
191
+ const settledResult = searchResults[i];
192
+ const { engineId } = eligibleEngines[i];
193
+
194
+ if (settledResult.status === "rejected") {
195
+ // Promise itself was rejected (shouldn't happen with our try/catch, but handle it)
196
+ attempts.push({ engineId, success: false, reason: "unknown" });
197
+ log.warn(`Search failed for ${engineId}: Promise rejected`);
198
+ continue;
199
+ }
200
+
201
+ const { response, error } = settledResult.value;
202
+
203
+ if (error) {
204
+ // Search threw an error
205
+ if (error instanceof SearchError) {
206
+ attempts.push({ engineId, success: false, reason: error.reason });
207
+ } else {
208
+ attempts.push({ engineId, success: false, reason: "unknown" });
209
+ }
210
+ log.warn(`Search failed for ${engineId}: ${error.message}`);
211
+ continue;
212
+ }
213
+
214
+ if (response) {
215
+ // Deduct credits
216
+ if (!context.creditManager.charge(engineId)) {
217
+ attempts.push({ engineId, success: false, reason: "out_of_credit" });
218
+ continue;
219
+ }
220
+
221
+ // Record success
222
+ attempts.push({ engineId, success: true });
223
+
224
+ // Add results
225
+ results.push(...response.items);
226
+ }
227
+ }
228
+
229
+ // Add ineligible attempts at the end (maintaining order within their category)
230
+ attempts.push(...ineligibleAttempts);
231
+
232
+ // Sort attempts to match original engine order
233
+ const engineOrder = new Map(engineIds.map((id, idx) => [id, idx]));
234
+ attempts.sort(
235
+ (a, b) => (engineOrder.get(a.engineId) ?? 0) - (engineOrder.get(b.engineId) ?? 0),
236
+ );
237
+
238
+ // Apply final limit if specified
239
+ if (options.limit !== undefined && results.length > options.limit) {
240
+ results.splice(options.limit);
241
+ }
242
+
243
+ return { results, attempts };
244
+ }
245
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * First Success Strategy - stops after first successful provider
3
+ *
4
+ * This strategy tries search providers in order and returns immediately
5
+ * with results from the first successful provider. It skips providers
6
+ * with insufficient credits and stops execution after the first success.
7
+ */
8
+
9
+ import { createLogger } from "../logger";
10
+ import type { EngineId } from "../types";
11
+ import { SearchError } from "../types";
12
+ import type {
13
+ EngineAttempt,
14
+ ISearchStrategy,
15
+ AllSearchOptions,
16
+ StrategyContext,
17
+ StrategyResult,
18
+ } from "./ISearchStrategy";
19
+
20
+ const log = createLogger("FirstSuccess");
21
+
22
+ /**
23
+ * First Success Strategy implementation
24
+ *
25
+ * Tries engines in order until first success, then returns immediately.
26
+ * Records all attempts up to and including the first successful one.
27
+ * Skips providers with insufficient credits.
28
+ */
29
+ export class FirstSuccessStrategy implements ISearchStrategy {
30
+ /**
31
+ * Execute search with first-success strategy
32
+ *
33
+ * @param query - The search query string
34
+ * @param engineIds - Ordered list of engine IDs to try
35
+ * @param options - Search options (limit, includeRaw, etc.)
36
+ * @param context - Strategy context with registry and credits
37
+ * @returns Promise resolving to strategy result with first successful results
38
+ */
39
+ async execute(
40
+ query: string,
41
+ engineIds: EngineId[],
42
+ options: AllSearchOptions,
43
+ context: StrategyContext,
44
+ ): Promise<StrategyResult> {
45
+ const attempts: EngineAttempt[] = [];
46
+
47
+ // Try each engine in order until one succeeds
48
+ for (const engineId of engineIds) {
49
+ const provider = context.providerRegistry.get(engineId);
50
+ if (!provider) {
51
+ attempts.push({ engineId, success: false, reason: "no_provider" });
52
+ continue;
53
+ }
54
+
55
+ // Check credit availability
56
+ if (!context.creditManager.hasSufficientCredits(engineId)) {
57
+ attempts.push({ engineId, success: false, reason: "out_of_credit" });
58
+ continue;
59
+ }
60
+
61
+ try {
62
+ // Execute search
63
+ const response = await provider.search({
64
+ query,
65
+ limit: options.limit,
66
+ includeRaw: options.includeRaw,
67
+ });
68
+
69
+ // Deduct credits
70
+ if (!context.creditManager.charge(engineId)) {
71
+ attempts.push({ engineId, success: false, reason: "out_of_credit" });
72
+ continue;
73
+ }
74
+
75
+ // Record success
76
+ attempts.push({ engineId, success: true });
77
+
78
+ // Return immediately with results from this provider
79
+ return { results: response.items, attempts };
80
+ } catch (error) {
81
+ // Record failure and continue to next provider
82
+ if (error instanceof SearchError) {
83
+ attempts.push({ engineId, success: false, reason: error.reason });
84
+ } else {
85
+ attempts.push({ engineId, success: false, reason: "unknown" });
86
+ }
87
+
88
+ // Log warning and continue
89
+ log.warn(
90
+ `Search failed for ${engineId}: ${error instanceof Error ? error.message : String(error)}`,
91
+ );
92
+ }
93
+ }
94
+
95
+ // No provider succeeded
96
+ return { results: [], attempts };
97
+ }
98
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Search Strategy Interface
3
+ *
4
+ * Defines the contract for all search strategies, enabling the Strategy pattern
5
+ * for different search execution approaches (all engines, first success, etc.)
6
+ */
7
+
8
+ import type { CreditManager } from "../credits";
9
+ import type { ProviderRegistry } from "../provider";
10
+ import type { EngineId, SearchResultItem } from "../types";
11
+
12
+ /**
13
+ * Context object providing dependencies to search strategies
14
+ */
15
+ export interface StrategyContext {
16
+ /** Registry of available search providers */
17
+ providerRegistry: ProviderRegistry;
18
+
19
+ /** Credit manager for tracking usage across providers */
20
+ creditManager: CreditManager;
21
+ }
22
+
23
+ /**
24
+ * Options for configuring search strategy behavior
25
+ */
26
+ export interface AllSearchOptions {
27
+ /** Override the default engine order */
28
+ engineOrderOverride?: EngineId[];
29
+
30
+ /** Maximum results per provider */
31
+ limit?: number;
32
+
33
+ /** Include raw provider responses */
34
+ includeRaw?: boolean;
35
+
36
+ /** Search strategy */
37
+ strategy?: "all" | "first-success";
38
+
39
+ /**
40
+ * Execute searches in parallel (only applies to 'all' strategy)
41
+ * When true, all providers are queried simultaneously using Promise.allSettled
42
+ * When false (default), providers are queried sequentially
43
+ */
44
+ parallel?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Metadata about a single engine attempt
49
+ */
50
+ export interface EngineAttempt {
51
+ /** Engine ID that was attempted */
52
+ engineId: EngineId;
53
+
54
+ /** Whether the attempt succeeded */
55
+ success: boolean;
56
+
57
+ /** Reason for failure (if success=false) */
58
+ reason?: string;
59
+ }
60
+
61
+ /**
62
+ * Result from executing a search strategy
63
+ */
64
+ export interface StrategyResult {
65
+ /** Combined results from all successful providers */
66
+ results: SearchResultItem[];
67
+
68
+ /** Metadata about which engines were tried and their outcomes */
69
+ attempts: EngineAttempt[];
70
+ }
71
+
72
+ /**
73
+ * Interface for implementing different search execution strategies
74
+ *
75
+ * This enables the Strategy pattern, allowing different approaches to
76
+ * coordinate multiple search providers while maintaining a consistent interface.
77
+ */
78
+ export interface ISearchStrategy {
79
+ /**
80
+ * Execute the search strategy with the given parameters
81
+ *
82
+ * @param query - The search query string
83
+ * @param engineIds - Ordered list of engine IDs to attempt
84
+ * @param options - Strategy configuration options
85
+ * @param context - Dependencies and services needed for execution
86
+ * @returns Promise resolving to strategy execution results
87
+ */
88
+ execute(
89
+ query: string,
90
+ engineIds: EngineId[],
91
+ options: AllSearchOptions,
92
+ context: StrategyContext,
93
+ ): Promise<StrategyResult>;
94
+ }
@@ -0,0 +1,204 @@
1
+ import { AllProvidersStrategy } from "./AllProvidersStrategy";
2
+ import { FirstSuccessStrategy } from "./FirstSuccessStrategy";
3
+ import type { ISearchStrategy } from "./ISearchStrategy";
4
+
5
+ /**
6
+ * Configuration options for search strategies
7
+ * @interface StrategyOptions
8
+ */
9
+ export interface StrategyOptions {
10
+ /**
11
+ * Timeout in milliseconds for individual provider requests
12
+ * @default 30000
13
+ */
14
+ timeout?: number;
15
+
16
+ /**
17
+ * Maximum number of concurrent requests
18
+ * @default 5
19
+ */
20
+ maxConcurrent?: number;
21
+
22
+ /**
23
+ * Retry configuration
24
+ */
25
+ retry?: {
26
+ /**
27
+ * Number of retry attempts
28
+ * @default 2
29
+ */
30
+ attempts?: number;
31
+ /**
32
+ * Delay between retries in milliseconds
33
+ * @default 1000
34
+ */
35
+ delay?: number;
36
+ };
37
+
38
+ /**
39
+ * Additional strategy-specific options
40
+ */
41
+ [key: string]: unknown;
42
+ }
43
+
44
+ /**
45
+ * Maps strategy names to their implementing classes
46
+ * @interface StrategyMap
47
+ */
48
+ interface StrategyMap {
49
+ [strategyName: string]: new (options?: StrategyOptions) => ISearchStrategy;
50
+ }
51
+
52
+ /**
53
+ * Factory for creating search strategy instances.
54
+ * Implements the Factory pattern to enable Open/Closed Principle -
55
+ * new strategies can be added without modifying existing code.
56
+ *
57
+ * @class StrategyFactory
58
+ * @example
59
+ * ```typescript
60
+ * // Create an all-providers strategy
61
+ * const allStrategy = StrategyFactory.createStrategy('all', {
62
+ * timeout: 30000,
63
+ * maxConcurrent: 3
64
+ * });
65
+ *
66
+ * // Create a first-success strategy
67
+ * const firstSuccessStrategy = StrategyFactory.createStrategy('first-success', {
68
+ * timeout: 15000
69
+ * });
70
+ *
71
+ * // Register a custom strategy
72
+ * StrategyFactory.registerStrategy('custom', CustomStrategy);
73
+ * ```
74
+ */
75
+ let strategies: StrategyMap = {
76
+ all: AllProvidersStrategy,
77
+ "first-success": FirstSuccessStrategy,
78
+ };
79
+
80
+ /**
81
+ * Creates a search strategy instance based on the provided name and options.
82
+ *
83
+ * @param {string} strategyName - The name of the strategy to create
84
+ * @param {StrategyOptions} [options] - Optional configuration for the strategy
85
+ * @returns {ISearchStrategy} An instance of the requested strategy
86
+ * @throws {Error} If the strategy name is not recognized
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * const strategy = StrategyFactory.createStrategy('all', {
91
+ * timeout: 20000,
92
+ * maxConcurrent: 5,
93
+ * retry: { attempts: 3, delay: 500 }
94
+ * });
95
+ * ```
96
+ */
97
+ function createStrategy(strategyName: string, options?: StrategyOptions): ISearchStrategy {
98
+ const StrategyClass = strategies[strategyName];
99
+
100
+ if (!StrategyClass) {
101
+ const availableStrategies = Object.keys(strategies).join(", ");
102
+ throw new Error(
103
+ `Unknown strategy: "${strategyName}". Available strategies: [${availableStrategies}]`,
104
+ );
105
+ }
106
+
107
+ return new StrategyClass(options);
108
+ }
109
+
110
+ /**
111
+ * Registers a new strategy in the factory.
112
+ * Enables the Open/Closed Principle by allowing new strategies to be added
113
+ * without modifying the factory code itself.
114
+ *
115
+ * @param {string} name - The name to register the strategy under
116
+ * @param strategyClass - The strategy class constructor
117
+ * @throws {Error} If a strategy with the same name already exists
118
+ *
119
+ * @example
120
+ * ```typescript
121
+ * class WeightedStrategy implements ISearchStrategy {
122
+ * constructor(private options?: StrategyOptions) {}
123
+ *
124
+ * async search(
125
+ * query: string,
126
+ * providers: ISearchProvider[],
127
+ * options?: SearchOptions
128
+ * ): Promise<SearchResult[]> {
129
+ * // Implementation
130
+ * }
131
+ * }
132
+ *
133
+ * StrategyFactory.registerStrategy('weighted', WeightedStrategy);
134
+ * ```
135
+ */
136
+ function registerStrategy(
137
+ name: string,
138
+ strategyClass: new (options?: StrategyOptions) => ISearchStrategy,
139
+ ): void {
140
+ if (strategies[name]) {
141
+ throw new Error(`Strategy "${name}" is already registered`);
142
+ }
143
+
144
+ strategies[name] = strategyClass;
145
+ }
146
+
147
+ /**
148
+ * Gets a list of all available strategy names.
149
+ *
150
+ * @returns {string[]} Array of available strategy names
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * const availableStrategies = StrategyFactory.getAvailableStrategies();
155
+ * console.log(availableStrategies); // ['all', 'first-success', 'custom']
156
+ * ```
157
+ */
158
+ function getAvailableStrategies(): string[] {
159
+ return Object.keys(strategies);
160
+ }
161
+
162
+ /**
163
+ * Checks if a strategy is registered.
164
+ *
165
+ * @param {string} strategyName - The name of the strategy to check
166
+ * @returns {boolean} True if the strategy is registered, false otherwise
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * if (StrategyFactory.hasStrategy('all')) {
171
+ * console.log('All providers strategy is available');
172
+ * }
173
+ * ```
174
+ */
175
+ function hasStrategy(strategyName: string): boolean {
176
+ return strategyName in strategies;
177
+ }
178
+
179
+ /**
180
+ * Resets the factory to its default state.
181
+ * This is primarily useful for testing to ensure test isolation.
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * // In test setup/teardown
186
+ * afterEach(() => {
187
+ * StrategyFactory.reset();
188
+ * });
189
+ * ```
190
+ */
191
+ function reset(): void {
192
+ strategies = {
193
+ all: AllProvidersStrategy,
194
+ "first-success": FirstSuccessStrategy,
195
+ };
196
+ }
197
+
198
+ export const StrategyFactory = {
199
+ createStrategy,
200
+ registerStrategy,
201
+ getAvailableStrategies,
202
+ hasStrategy,
203
+ reset,
204
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Search strategy implementations and interfaces
3
+ */
4
+
5
+ export * from "./AllProvidersStrategy";
6
+ export * from "./FirstSuccessStrategy";
7
+ export * from "./ISearchStrategy";
8
+ export * from "./StrategyFactory";
9
+ export * from "./types";
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Strategy interface and types for search execution patterns
3
+ */
4
+
5
+ import type { CreditManager } from "../credits";
6
+ import type { ProviderRegistry } from "../provider";
7
+ import type { EngineId, SearchQuery, SearchResultItem } from "../types";
8
+
9
+ /**
10
+ * Context passed to search strategies containing dependencies
11
+ */
12
+ export interface StrategyContext {
13
+ /** Provider registry for accessing search providers */
14
+ registry: ProviderRegistry;
15
+ /** Credit manager for checking and charging credits */
16
+ credits: CreditManager;
17
+ }
18
+
19
+ /**
20
+ * Result from a search strategy execution
21
+ */
22
+ export interface StrategyResult {
23
+ /** Search results from successful providers */
24
+ results: SearchResultItem[];
25
+ /** Metadata about engine attempts */
26
+ attempts: EngineAttempt[];
27
+ }
28
+
29
+ /**
30
+ * Engine attempt metadata
31
+ */
32
+ export interface EngineAttempt {
33
+ engineId: EngineId;
34
+ success: boolean;
35
+ reason?: string;
36
+ }
37
+
38
+ /**
39
+ * Interface for implementing different search execution strategies
40
+ */
41
+ export interface ISearchStrategy {
42
+ /**
43
+ * Execute search using this strategy
44
+ * @param query - The search query
45
+ * @param engineIds - Ordered list of engine IDs to try
46
+ * @param options - Search options (limit, includeRaw, etc.)
47
+ * @param context - Strategy context with dependencies
48
+ * @returns Promise resolving to strategy result
49
+ */
50
+ execute(
51
+ query: string,
52
+ engineIds: EngineId[],
53
+ options: SearchQuery,
54
+ context: StrategyContext,
55
+ ): Promise<StrategyResult>;
56
+ }