@memberjunction/web-search-engine 0.0.1-seed.2 → 6.2.0-edge.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 (47) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +343 -3
  3. package/dist/BaseWebSearchProvider.d.ts +89 -0
  4. package/dist/BaseWebSearchProvider.d.ts.map +1 -0
  5. package/dist/BaseWebSearchProvider.js +130 -0
  6. package/dist/BaseWebSearchProvider.js.map +1 -0
  7. package/dist/WebSearchEngine.d.ts +107 -0
  8. package/dist/WebSearchEngine.d.ts.map +1 -0
  9. package/dist/WebSearchEngine.js +396 -0
  10. package/dist/WebSearchEngine.js.map +1 -0
  11. package/dist/index.d.ts +28 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +43 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/operations/WebSearchQueryOperation.d.ts +8 -0
  16. package/dist/operations/WebSearchQueryOperation.d.ts.map +1 -0
  17. package/dist/operations/WebSearchQueryOperation.js +72 -0
  18. package/dist/operations/WebSearchQueryOperation.js.map +1 -0
  19. package/dist/providers/BraveWebSearchProvider.d.ts +41 -0
  20. package/dist/providers/BraveWebSearchProvider.d.ts.map +1 -0
  21. package/dist/providers/BraveWebSearchProvider.js +169 -0
  22. package/dist/providers/BraveWebSearchProvider.js.map +1 -0
  23. package/dist/providers/DuckDuckGoWebSearchProvider.d.ts +45 -0
  24. package/dist/providers/DuckDuckGoWebSearchProvider.d.ts.map +1 -0
  25. package/dist/providers/DuckDuckGoWebSearchProvider.js +116 -0
  26. package/dist/providers/DuckDuckGoWebSearchProvider.js.map +1 -0
  27. package/dist/providers/GoogleCustomSearchWebSearchProvider.d.ts +36 -0
  28. package/dist/providers/GoogleCustomSearchWebSearchProvider.d.ts.map +1 -0
  29. package/dist/providers/GoogleCustomSearchWebSearchProvider.js +125 -0
  30. package/dist/providers/GoogleCustomSearchWebSearchProvider.js.map +1 -0
  31. package/dist/providers/PerplexityWebSearchProvider.d.ts +40 -0
  32. package/dist/providers/PerplexityWebSearchProvider.d.ts.map +1 -0
  33. package/dist/providers/PerplexityWebSearchProvider.js +148 -0
  34. package/dist/providers/PerplexityWebSearchProvider.js.map +1 -0
  35. package/dist/providers/TavilyWebSearchProvider.d.ts +30 -0
  36. package/dist/providers/TavilyWebSearchProvider.d.ts.map +1 -0
  37. package/dist/providers/TavilyWebSearchProvider.js +126 -0
  38. package/dist/providers/TavilyWebSearchProvider.js.map +1 -0
  39. package/dist/providers/httpFailure.d.ts +29 -0
  40. package/dist/providers/httpFailure.d.ts.map +1 -0
  41. package/dist/providers/httpFailure.js +136 -0
  42. package/dist/providers/httpFailure.js.map +1 -0
  43. package/dist/types.d.ts +214 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +17 -0
  46. package/dist/types.js.map +1 -0
  47. package/package.json +28 -5
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @fileoverview Public surface of the MemberJunction web search engine.
3
+ *
4
+ * Importing this module runs every driver's `@RegisterClass` decorator as a side effect, which
5
+ * is what makes them resolvable by `DriverClass`. That is why the providers are exported here
6
+ * rather than left for consumers to import individually.
7
+ *
8
+ * @module @memberjunction/web-search-engine
9
+ */
10
+ export * from './types.js';
11
+ export * from './BaseWebSearchProvider.js';
12
+ export * from './WebSearchEngine.js';
13
+ export * from './providers/BraveWebSearchProvider.js';
14
+ export * from './providers/TavilyWebSearchProvider.js';
15
+ export * from './providers/PerplexityWebSearchProvider.js';
16
+ export * from './providers/GoogleCustomSearchWebSearchProvider.js';
17
+ export * from './providers/DuckDuckGoWebSearchProvider.js';
18
+ export * from './providers/httpFailure.js';
19
+ export * from './operations/WebSearchQueryOperation.js';
20
+ /**
21
+ * Keep every driver registration alive through tree-shaking.
22
+ *
23
+ * Bundlers drop a module whose exports are unused, and a dropped module never runs its
24
+ * decorator — so the driver silently vanishes from `ClassFactory` and the engine reports
25
+ * "no registered driver" for a `DriverClass` that plainly exists in the source.
26
+ */
27
+ export declare function LoadWebSearchProviders(): void;
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,iDAAiD,CAAC;AAChE,cAAc,yCAAyC,CAAC;AACxD,cAAc,yBAAyB,CAAC;AACxC,cAAc,sCAAsC,CAAC;AASrD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAO7C"}
package/dist/index.js ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * @fileoverview Public surface of the MemberJunction web search engine.
3
+ *
4
+ * Importing this module runs every driver's `@RegisterClass` decorator as a side effect, which
5
+ * is what makes them resolvable by `DriverClass`. That is why the providers are exported here
6
+ * rather than left for consumers to import individually.
7
+ *
8
+ * @module @memberjunction/web-search-engine
9
+ */
10
+ export * from './types.js';
11
+ export * from './BaseWebSearchProvider.js';
12
+ export * from './WebSearchEngine.js';
13
+ // Drivers — exported for their registration side effect as much as for their types.
14
+ export * from './providers/BraveWebSearchProvider.js';
15
+ export * from './providers/TavilyWebSearchProvider.js';
16
+ export * from './providers/PerplexityWebSearchProvider.js';
17
+ export * from './providers/GoogleCustomSearchWebSearchProvider.js';
18
+ export * from './providers/DuckDuckGoWebSearchProvider.js';
19
+ export * from './providers/httpFailure.js';
20
+ export * from './operations/WebSearchQueryOperation.js';
21
+ import { LoadBraveWebSearchProvider } from './providers/BraveWebSearchProvider.js';
22
+ import { LoadTavilyWebSearchProvider } from './providers/TavilyWebSearchProvider.js';
23
+ import { LoadPerplexityWebSearchProvider } from './providers/PerplexityWebSearchProvider.js';
24
+ import { LoadGoogleCustomSearchWebSearchProvider } from './providers/GoogleCustomSearchWebSearchProvider.js';
25
+ import { LoadDuckDuckGoWebSearchProvider } from './providers/DuckDuckGoWebSearchProvider.js';
26
+ import { LoadWebSearchOperations } from './operations/WebSearchQueryOperation.js';
27
+ /**
28
+ * Keep every driver registration alive through tree-shaking.
29
+ *
30
+ * Bundlers drop a module whose exports are unused, and a dropped module never runs its
31
+ * decorator — so the driver silently vanishes from `ClassFactory` and the engine reports
32
+ * "no registered driver" for a `DriverClass` that plainly exists in the source.
33
+ */
34
+ export function LoadWebSearchProviders() {
35
+ LoadBraveWebSearchProvider();
36
+ LoadTavilyWebSearchProvider();
37
+ LoadPerplexityWebSearchProvider();
38
+ LoadGoogleCustomSearchWebSearchProvider();
39
+ LoadDuckDuckGoWebSearchProvider();
40
+ LoadWebSearchOperations();
41
+ }
42
+ LoadWebSearchProviders();
43
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAElC,oFAAoF;AACpF,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,iDAAiD,CAAC;AAChE,cAAc,yCAAyC,CAAC;AACxD,cAAc,yBAAyB,CAAC;AACxC,cAAc,sCAAsC,CAAC;AAErD,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,+BAA+B,EAAE,MAAM,yCAAyC,CAAC;AAC1F,OAAO,EAAE,uCAAuC,EAAE,MAAM,iDAAiD,CAAC;AAC1G,OAAO,EAAE,+BAA+B,EAAE,MAAM,yCAAyC,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAE/E;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB;IAClC,0BAA0B,EAAE,CAAC;IAC7B,2BAA2B,EAAE,CAAC;IAC9B,+BAA+B,EAAE,CAAC;IAClC,uCAAuC,EAAE,CAAC;IAC1C,+BAA+B,EAAE,CAAC;IAClC,uBAAuB,EAAE,CAAC;AAC9B,CAAC;AAED,sBAAsB,EAAE,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { IMetadataProvider, UserInfo } from '@memberjunction/core';
2
+ import { WebSearchQueryOperation, type WebSearchQueryInput, type WebSearchQueryOutput } from '@memberjunction/core-entities';
3
+ export declare class WebSearchQueryServerOperation extends WebSearchQueryOperation {
4
+ protected InternalExecute(input: WebSearchQueryInput, provider: IMetadataProvider, user: UserInfo): Promise<WebSearchQueryOutput>;
5
+ }
6
+ /** Keep the registration from being tree-shaken out of a bundled host. */
7
+ export declare function LoadWebSearchOperations(): void;
8
+ //# sourceMappingURL=WebSearchQueryOperation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSearchQueryOperation.d.ts","sourceRoot":"","sources":["../../src/operations/WebSearchQueryOperation.ts"],"names":[],"mappings":"AASA,OAAO,EAA0B,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC3F,OAAO,EACH,uBAAuB,EACvB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EAC5B,MAAM,+BAA+B,CAAC;AAIvC,qBACa,6BAA8B,SAAQ,uBAAuB;cACtD,eAAe,CAC3B,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,QAAQ,GACf,OAAO,CAAC,oBAAoB,CAAC;CA+CnC;AAED,0EAA0E;AAC1E,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
@@ -0,0 +1,72 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ /**
8
+ * @fileoverview `WebSearch.Query` — server implementation of the Web Search remote operation.
9
+ *
10
+ * Exposes WebSearchEngine to client applications through MemberJunction's Remote Operations
11
+ * RPC gateway with `websearch:execute` API scope enforcement.
12
+ *
13
+ * @module @memberjunction/web-search-engine
14
+ */
15
+ import { RegisterClass } from '@memberjunction/global';
16
+ import { BaseRemotableOperation } from '@memberjunction/core';
17
+ import { WebSearchQueryOperation, } from '@memberjunction/core-entities';
18
+ import { WebSearchEngine } from '../WebSearchEngine.js';
19
+ let WebSearchQueryServerOperation = class WebSearchQueryServerOperation extends WebSearchQueryOperation {
20
+ async InternalExecute(input, provider, user) {
21
+ const query = input?.query?.trim();
22
+ if (!query) {
23
+ throw new Error('query is required');
24
+ }
25
+ await WebSearchEngine.Instance.Config(false, user, provider);
26
+ const searchParams = {
27
+ Query: query,
28
+ MaxResults: input.maxResults,
29
+ Provider: input.provider,
30
+ IncludeDomains: input.includeDomains,
31
+ ExcludeDomains: input.excludeDomains,
32
+ Freshness: input.freshness,
33
+ Country: input.country,
34
+ Language: input.language,
35
+ SafeSearch: input.safeSearch,
36
+ IncludeAnswer: input.includeAnswer,
37
+ };
38
+ const result = await WebSearchEngine.Instance.Search(searchParams, user);
39
+ if (!result.Success) {
40
+ throw new Error(`${result.ResultCode}: ${result.ErrorMessage ?? 'Search failed'}`);
41
+ }
42
+ return {
43
+ hits: (result.Hits ?? []).map((hit) => ({
44
+ title: hit.Title,
45
+ url: hit.URL,
46
+ snippet: hit.Snippet,
47
+ displayUrl: hit.DisplayURL,
48
+ publishedAt: hit.PublishedAt instanceof Date ? hit.PublishedAt.toISOString() : undefined,
49
+ score: hit.Score,
50
+ })),
51
+ answer: result.Answer,
52
+ providerUsed: result.ProviderUsed ?? '',
53
+ attempts: (result.Attempts ?? []).map((attempt) => ({
54
+ providerName: attempt.ProviderName,
55
+ succeeded: attempt.Succeeded,
56
+ durationMs: attempt.DurationMs,
57
+ hitCount: attempt.HitCount,
58
+ failureKind: attempt.FailureKind,
59
+ errorMessage: attempt.ErrorMessage,
60
+ })),
61
+ };
62
+ }
63
+ };
64
+ WebSearchQueryServerOperation = __decorate([
65
+ RegisterClass(BaseRemotableOperation, 'WebSearch.Query')
66
+ ], WebSearchQueryServerOperation);
67
+ export { WebSearchQueryServerOperation };
68
+ /** Keep the registration from being tree-shaken out of a bundled host. */
69
+ export function LoadWebSearchOperations() {
70
+ void WebSearchQueryServerOperation;
71
+ }
72
+ //# sourceMappingURL=WebSearchQueryOperation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSearchQueryOperation.js","sourceRoot":"","sources":["../../src/operations/WebSearchQueryOperation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @fileoverview Brave Search driver.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ import { UserInfo } from '@memberjunction/core';
6
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
7
+ import { WebSearchCapabilities, WebSearchParams, WebSearchProviderResponse } from '../types.js';
8
+ /**
9
+ * Searches the web through the Brave Search API.
10
+ *
11
+ * Brave is the recommended default for a structural reason rather than a qualitative one: it
12
+ * serves from **its own index** rather than reselling Google's or Bing's. Every SERP-proxy
13
+ * vendor, and the Google Custom Search JSON API that this engine exists to replace, depends on
14
+ * a search engine continuing to permit that access. Brave does not, so it fails independently
15
+ * of every other driver here — which is the entire point of having a provider set.
16
+ *
17
+ * Credential key `apiKey`; environment fallback `BRAVE_SEARCH_API_KEY`.
18
+ */
19
+ export declare class BraveWebSearchProvider extends BaseWebSearchProvider {
20
+ private static readonly ENDPOINT;
21
+ /** Brave rejects queries longer than this outright. */
22
+ private static readonly MAX_QUERY_CHARS;
23
+ /** Brave's cap on the pagination offset, in pages. */
24
+ private static readonly MAX_OFFSET;
25
+ readonly Capabilities: WebSearchCapabilities;
26
+ private apiKey;
27
+ CheckAvailability(_contextUser: UserInfo): Promise<void>;
28
+ ExecuteSearch(params: WebSearchParams, _contextUser: UserInfo): Promise<WebSearchProviderResponse>;
29
+ /**
30
+ * Brave has no domain-filter request parameter — it expresses domain scoping through query
31
+ * operators, so that is how the capability is honoured. Includes become an OR-group of
32
+ * `site:` terms; excludes become `-site:` terms.
33
+ */
34
+ private applyDomainFilters;
35
+ /** Brave expresses relative windows as pd/pw/pm/py and ranges as `YYYY-MM-DDtoYYYY-MM-DD`. */
36
+ private mapFreshness;
37
+ private toHit;
38
+ }
39
+ /** Anti-tree-shaking anchor — importing this module is what runs the @RegisterClass decorator. */
40
+ export declare function LoadBraveWebSearchProvider(): void;
41
+ //# sourceMappingURL=BraveWebSearchProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BraveWebSearchProvider.d.ts","sourceRoot":"","sources":["../../src/providers/BraveWebSearchProvider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EACH,qBAAqB,EAErB,eAAe,EACf,yBAAyB,EAC5B,MAAM,UAAU,CAAC;AAkBlB;;;;;;;;;;GAUG;AACH,qBACa,sBAAuB,SAAQ,qBAAqB;IAC7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAoD;IACpF,uDAAuD;IACvD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAO;IAC9C,sDAAsD;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAK;IAEvC,SAAgB,YAAY,EAAE,qBAAqB,CAMjD;IAEF,OAAO,CAAC,MAAM,CAAqB;IAEtB,iBAAiB,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxD,aAAa,CACtB,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,yBAAyB,CAAC;IA4DrC;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;IAgB1B,8FAA8F;IAC9F,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,KAAK;CA2BhB;AAED,kGAAkG;AAClG,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * @fileoverview Brave Search driver.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var BraveWebSearchProvider_1;
12
+ import { RegisterClass } from '@memberjunction/global';
13
+ import { HttpGet } from '@memberjunction/network-utils';
14
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
15
+ import { classifyHttpFailure, failure } from './httpFailure.js';
16
+ /**
17
+ * Searches the web through the Brave Search API.
18
+ *
19
+ * Brave is the recommended default for a structural reason rather than a qualitative one: it
20
+ * serves from **its own index** rather than reselling Google's or Bing's. Every SERP-proxy
21
+ * vendor, and the Google Custom Search JSON API that this engine exists to replace, depends on
22
+ * a search engine continuing to permit that access. Brave does not, so it fails independently
23
+ * of every other driver here — which is the entire point of having a provider set.
24
+ *
25
+ * Credential key `apiKey`; environment fallback `BRAVE_SEARCH_API_KEY`.
26
+ */
27
+ let BraveWebSearchProvider = class BraveWebSearchProvider extends BaseWebSearchProvider {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.Capabilities = {
31
+ Answer: false,
32
+ DomainFilter: true,
33
+ Freshness: true,
34
+ Region: true,
35
+ MaxResultsCap: 20,
36
+ };
37
+ }
38
+ static { BraveWebSearchProvider_1 = this; }
39
+ static { this.ENDPOINT = 'https://api.search.brave.com/res/v1/web/search'; }
40
+ /** Brave rejects queries longer than this outright. */
41
+ static { this.MAX_QUERY_CHARS = 400; }
42
+ /** Brave's cap on the pagination offset, in pages. */
43
+ static { this.MAX_OFFSET = 9; }
44
+ async CheckAvailability(_contextUser) {
45
+ this.apiKey = this.GetSecret('apiKey', 'BRAVE_SEARCH_API_KEY');
46
+ if (this.apiKey) {
47
+ this.MarkAvailable();
48
+ }
49
+ else {
50
+ this.MarkUnavailable('No Brave API key. Set it on the linked Credential (key `apiKey`) or as BRAVE_SEARCH_API_KEY.');
51
+ }
52
+ }
53
+ async ExecuteSearch(params, _contextUser) {
54
+ // Rejected rather than truncated: silently cutting a query changes what was asked and
55
+ // returns confident results for a different question. Permanent — no other provider
56
+ // will accept it either.
57
+ if (params.Query.length > BraveWebSearchProvider_1.MAX_QUERY_CHARS) {
58
+ return failure('permanent', `Query is ${params.Query.length} characters; Brave's limit is ${BraveWebSearchProvider_1.MAX_QUERY_CHARS}.`);
59
+ }
60
+ const q = this.applyDomainFilters(params);
61
+ if (q.length > BraveWebSearchProvider_1.MAX_QUERY_CHARS) {
62
+ return failure('permanent', `Query plus domain filters is ${q.length} characters; Brave's limit is ${BraveWebSearchProvider_1.MAX_QUERY_CHARS}.`);
63
+ }
64
+ const query = {
65
+ q,
66
+ count: this.ResolveMaxResults(params.MaxResults),
67
+ offset: 0,
68
+ safesearch: params.SafeSearch ?? 'moderate',
69
+ };
70
+ const freshness = this.mapFreshness(params);
71
+ if (freshness) {
72
+ query.freshness = freshness;
73
+ }
74
+ if (params.Country) {
75
+ query.country = params.Country.toUpperCase();
76
+ }
77
+ if (params.Language) {
78
+ query.search_lang = params.Language.toLowerCase();
79
+ }
80
+ try {
81
+ const response = await HttpGet(BraveWebSearchProvider_1.ENDPOINT, {
82
+ Query: query,
83
+ Headers: {
84
+ 'X-Subscription-Token': this.apiKey ?? '',
85
+ Accept: 'application/json',
86
+ },
87
+ Timeout: 15000,
88
+ });
89
+ if (!response.Data) {
90
+ return failure('transient', 'Empty response from Brave Search.');
91
+ }
92
+ return {
93
+ Success: true,
94
+ Hits: (response.Data.web?.results ?? []).map((item) => this.toHit(item)),
95
+ };
96
+ }
97
+ catch (e) {
98
+ return classifyHttpFailure(e, 'Brave');
99
+ }
100
+ }
101
+ /**
102
+ * Brave has no domain-filter request parameter — it expresses domain scoping through query
103
+ * operators, so that is how the capability is honoured. Includes become an OR-group of
104
+ * `site:` terms; excludes become `-site:` terms.
105
+ */
106
+ applyDomainFilters(params) {
107
+ const parts = [params.Query.trim()];
108
+ const include = (params.IncludeDomains ?? []).map((d) => d.trim()).filter(Boolean);
109
+ const exclude = (params.ExcludeDomains ?? []).map((d) => d.trim()).filter(Boolean);
110
+ if (include.length === 1) {
111
+ parts.push(`site:${include[0]}`);
112
+ }
113
+ else if (include.length > 1) {
114
+ parts.push(`(${include.map((d) => `site:${d}`).join(' OR ')})`);
115
+ }
116
+ for (const domain of exclude) {
117
+ parts.push(`-site:${domain}`);
118
+ }
119
+ return parts.join(' ');
120
+ }
121
+ /** Brave expresses relative windows as pd/pw/pm/py and ranges as `YYYY-MM-DDtoYYYY-MM-DD`. */
122
+ mapFreshness(params) {
123
+ const freshness = params.Freshness;
124
+ if (!freshness) {
125
+ return undefined;
126
+ }
127
+ if (typeof freshness === 'string') {
128
+ return { day: 'pd', week: 'pw', month: 'pm', year: 'py' }[freshness];
129
+ }
130
+ const iso = (d) => d.toISOString().slice(0, 10);
131
+ return `${iso(freshness.From)}to${iso(freshness.To)}`;
132
+ }
133
+ toHit(item) {
134
+ const hit = {
135
+ Title: item.title ?? '',
136
+ URL: item.url ?? '',
137
+ Snippet: item.description ?? '',
138
+ DisplayURL: item.meta_url?.netloc ?? item.meta_url?.hostname ?? undefined,
139
+ };
140
+ if (item.page_age) {
141
+ const parsed = new Date(item.page_age);
142
+ if (!isNaN(parsed.getTime())) {
143
+ hit.PublishedAt = parsed;
144
+ }
145
+ }
146
+ // Brave's relative age string and extra snippets have no slot in the normalised shape,
147
+ // and they are part of why Brave is worth having — so they ride along here.
148
+ const extras = {};
149
+ if (item.age) {
150
+ extras.age = item.age;
151
+ }
152
+ if (item.extra_snippets?.length) {
153
+ extras.extraSnippets = item.extra_snippets;
154
+ }
155
+ if (Object.keys(extras).length > 0) {
156
+ hit.ProviderExtras = extras;
157
+ }
158
+ return hit;
159
+ }
160
+ };
161
+ BraveWebSearchProvider = BraveWebSearchProvider_1 = __decorate([
162
+ RegisterClass(BaseWebSearchProvider, 'BraveWebSearchProvider')
163
+ ], BraveWebSearchProvider);
164
+ export { BraveWebSearchProvider };
165
+ /** Anti-tree-shaking anchor — importing this module is what runs the @RegisterClass decorator. */
166
+ export function LoadBraveWebSearchProvider() {
167
+ void BraveWebSearchProvider;
168
+ }
169
+ //# sourceMappingURL=BraveWebSearchProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BraveWebSearchProvider.js","sourceRoot":"","sources":["../../src/providers/BraveWebSearchProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fileoverview DuckDuckGo driver — keyless last resort.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ import { UserInfo } from '@memberjunction/core';
6
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
7
+ import { WebSearchCapabilities, WebSearchParams, WebSearchProviderResponse } from '../types.js';
8
+ /**
9
+ * Searches through DuckDuckGo's Instant Answer API.
10
+ *
11
+ * **This is a last resort, and it is ordered last for good reasons.** The Instant Answer API is
12
+ * not a web search API: it returns disambiguation topics and an abstract, not a ranked result
13
+ * set, so it answers only a minority of queries and returns nothing at all for many ordinary
14
+ * ones. It also rate-limits aggressively and without documentation.
15
+ *
16
+ * It earns its place because it needs **no credential**, so a host that has configured nothing
17
+ * still gets some web capability rather than a hard failure. Treat a deployment that relies on
18
+ * it as unconfigured, not as working — configure Brave or Tavily for real coverage.
19
+ *
20
+ * ## Why there is no HTML fallback
21
+ *
22
+ * An earlier version fell back to fetching and regex-parsing DuckDuckGo's HTML results page,
23
+ * which answered far more queries. It was removed deliberately: running regular expressions over
24
+ * remote HTML is running them over **attacker-influenceable input**, and the parser carried two
25
+ * high-severity findings — polynomial backtracking in the result patterns (the `[^"]*…[^"]*`
26
+ * shape) and incomplete sanitization in its tag-stripping.
27
+ *
28
+ * Hardening those patterns would have narrowed the surface; deleting the parser removes it. For a
29
+ * fallback *inside* a last-resort provider that this engine already tells operators not to rely
30
+ * on, the capability was not worth the exposure — and "the regex looks safe now" is exactly the
31
+ * kind of claim that is true when written and wrong later. Anything needing real web coverage
32
+ * should configure a provider with an API.
33
+ */
34
+ export declare class DuckDuckGoWebSearchProvider extends BaseWebSearchProvider {
35
+ private static readonly ENDPOINT;
36
+ readonly Capabilities: WebSearchCapabilities;
37
+ /** Keyless, so always available — which is the entire reason it is the fallback. */
38
+ CheckAvailability(_contextUser: UserInfo): Promise<void>;
39
+ ExecuteSearch(params: WebSearchParams, _contextUser: UserInfo): Promise<WebSearchProviderResponse>;
40
+ /** RelatedTopics nests one level for disambiguation groups; flatten to a single list. */
41
+ private flattenTopics;
42
+ }
43
+ /** Anti-tree-shaking anchor. */
44
+ export declare function LoadDuckDuckGoWebSearchProvider(): void;
45
+ //# sourceMappingURL=DuckDuckGoWebSearchProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuckDuckGoWebSearchProvider.d.ts","sourceRoot":"","sources":["../../src/providers/DuckDuckGoWebSearchProvider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAgB,eAAe,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAgB3G;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBACa,2BAA4B,SAAQ,qBAAqB;IAClE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAEjE,SAAgB,YAAY,EAAE,qBAAqB,CAMjD;IAEF,oFAAoF;IACvE,iBAAiB,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxD,aAAa,CACtB,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,yBAAyB,CAAC;IAgCrC,yFAAyF;IACzF,OAAO,CAAC,aAAa;CAmBxB;AAED,gCAAgC;AAChC,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * @fileoverview DuckDuckGo driver — keyless last resort.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
6
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
7
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
8
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
9
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
10
+ };
11
+ var DuckDuckGoWebSearchProvider_1;
12
+ import { RegisterClass } from '@memberjunction/global';
13
+ import { HttpGet } from '@memberjunction/network-utils';
14
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
15
+ import { classifyHttpFailure, failure } from './httpFailure.js';
16
+ /**
17
+ * Searches through DuckDuckGo's Instant Answer API.
18
+ *
19
+ * **This is a last resort, and it is ordered last for good reasons.** The Instant Answer API is
20
+ * not a web search API: it returns disambiguation topics and an abstract, not a ranked result
21
+ * set, so it answers only a minority of queries and returns nothing at all for many ordinary
22
+ * ones. It also rate-limits aggressively and without documentation.
23
+ *
24
+ * It earns its place because it needs **no credential**, so a host that has configured nothing
25
+ * still gets some web capability rather than a hard failure. Treat a deployment that relies on
26
+ * it as unconfigured, not as working — configure Brave or Tavily for real coverage.
27
+ *
28
+ * ## Why there is no HTML fallback
29
+ *
30
+ * An earlier version fell back to fetching and regex-parsing DuckDuckGo's HTML results page,
31
+ * which answered far more queries. It was removed deliberately: running regular expressions over
32
+ * remote HTML is running them over **attacker-influenceable input**, and the parser carried two
33
+ * high-severity findings — polynomial backtracking in the result patterns (the `[^"]*…[^"]*`
34
+ * shape) and incomplete sanitization in its tag-stripping.
35
+ *
36
+ * Hardening those patterns would have narrowed the surface; deleting the parser removes it. For a
37
+ * fallback *inside* a last-resort provider that this engine already tells operators not to rely
38
+ * on, the capability was not worth the exposure — and "the regex looks safe now" is exactly the
39
+ * kind of claim that is true when written and wrong later. Anything needing real web coverage
40
+ * should configure a provider with an API.
41
+ */
42
+ let DuckDuckGoWebSearchProvider = class DuckDuckGoWebSearchProvider extends BaseWebSearchProvider {
43
+ constructor() {
44
+ super(...arguments);
45
+ this.Capabilities = {
46
+ Answer: false,
47
+ DomainFilter: false,
48
+ Freshness: false,
49
+ Region: false,
50
+ MaxResultsCap: 30,
51
+ };
52
+ }
53
+ static { DuckDuckGoWebSearchProvider_1 = this; }
54
+ static { this.ENDPOINT = 'https://api.duckduckgo.com/'; }
55
+ /** Keyless, so always available — which is the entire reason it is the fallback. */
56
+ async CheckAvailability(_contextUser) {
57
+ this.MarkAvailable();
58
+ }
59
+ async ExecuteSearch(params, _contextUser) {
60
+ try {
61
+ const response = await HttpGet(DuckDuckGoWebSearchProvider_1.ENDPOINT, {
62
+ Query: { q: params.Query, format: 'json', no_html: 1, skip_disambig: 1 },
63
+ Timeout: 15000,
64
+ });
65
+ if (!response.Data) {
66
+ return failure('transient', 'Empty response from DuckDuckGo.');
67
+ }
68
+ const maxResults = this.ResolveMaxResults(params.MaxResults);
69
+ const hits = this.flattenTopics(response.Data.RelatedTopics ?? []);
70
+ // The abstract, when present, is the most relevant thing DDG returns — it leads.
71
+ if (response.Data.AbstractText && response.Data.AbstractURL) {
72
+ hits.unshift({
73
+ Title: response.Data.Heading ?? params.Query,
74
+ URL: response.Data.AbstractURL,
75
+ Snippet: response.Data.AbstractText,
76
+ });
77
+ }
78
+ // Zero hits is common here and is reported honestly rather than escalated: the engine
79
+ // treats it as a successful empty answer, so a caller that needs coverage sees the
80
+ // gap instead of a failure it might retry.
81
+ return { Success: true, Hits: hits.slice(0, maxResults) };
82
+ }
83
+ catch (e) {
84
+ return classifyHttpFailure(e, 'DuckDuckGo');
85
+ }
86
+ }
87
+ /** RelatedTopics nests one level for disambiguation groups; flatten to a single list. */
88
+ flattenTopics(topics) {
89
+ const hits = [];
90
+ for (const topic of topics) {
91
+ if (topic.Topics?.length) {
92
+ hits.push(...this.flattenTopics(topic.Topics));
93
+ continue;
94
+ }
95
+ if (topic.FirstURL && topic.Text) {
96
+ // DDG puts "Title - description" in one string; split on the first separator only.
97
+ const separator = topic.Text.indexOf(' - ');
98
+ hits.push({
99
+ Title: separator > 0 ? topic.Text.slice(0, separator) : topic.Text,
100
+ URL: topic.FirstURL,
101
+ Snippet: separator > 0 ? topic.Text.slice(separator + 3) : topic.Text,
102
+ });
103
+ }
104
+ }
105
+ return hits;
106
+ }
107
+ };
108
+ DuckDuckGoWebSearchProvider = DuckDuckGoWebSearchProvider_1 = __decorate([
109
+ RegisterClass(BaseWebSearchProvider, 'DuckDuckGoWebSearchProvider')
110
+ ], DuckDuckGoWebSearchProvider);
111
+ export { DuckDuckGoWebSearchProvider };
112
+ /** Anti-tree-shaking anchor. */
113
+ export function LoadDuckDuckGoWebSearchProvider() {
114
+ void DuckDuckGoWebSearchProvider;
115
+ }
116
+ //# sourceMappingURL=DuckDuckGoWebSearchProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DuckDuckGoWebSearchProvider.js","sourceRoot":"","sources":["../../src/providers/DuckDuckGoWebSearchProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @fileoverview Google Custom Search driver — retiring 2027-01-01.
3
+ * @module @memberjunction/web-search-engine
4
+ */
5
+ import { UserInfo } from '@memberjunction/core';
6
+ import { BaseWebSearchProvider } from '../BaseWebSearchProvider.js';
7
+ import { WebSearchCapabilities, WebSearchParams, WebSearchProviderResponse } from '../types.js';
8
+ /**
9
+ * Searches through the Google Custom Search JSON API.
10
+ *
11
+ * **This provider has an end date.** Google closed the API to new customers during 2025 and
12
+ * discontinues it on **2027-01-01**. It ships so that hosts already holding a key keep working
13
+ * through the transition, and it is ordered behind Brave and Tavily deliberately.
14
+ *
15
+ * It needs no dated code change to retire: once Google stops serving, calls fail, the engine
16
+ * fails over, and removing the metadata row takes it out of rotation entirely. A host that
17
+ * never had a key sees `CheckAvailability` self-disable it on the first config, which is the
18
+ * ordinary "not configured here" path rather than an error.
19
+ *
20
+ * Credential keys `apiKey` and `cx`; environment fallbacks `GOOGLE_CUSTOM_SEARCH_API_KEY`
21
+ * and `GOOGLE_CUSTOM_SEARCH_CX`.
22
+ */
23
+ export declare class GoogleCustomSearchWebSearchProvider extends BaseWebSearchProvider {
24
+ private static readonly ENDPOINT;
25
+ readonly Capabilities: WebSearchCapabilities;
26
+ private apiKey;
27
+ private cx;
28
+ CheckAvailability(_contextUser: UserInfo): Promise<void>;
29
+ ExecuteSearch(params: WebSearchParams, _contextUser: UserInfo): Promise<WebSearchProviderResponse>;
30
+ /** Google expresses recency as `d7`, `w2`, `m3`, `y1`. */
31
+ private mapFreshness;
32
+ private toHit;
33
+ }
34
+ /** Anti-tree-shaking anchor. */
35
+ export declare function LoadGoogleCustomSearchWebSearchProvider(): void;
36
+ //# sourceMappingURL=GoogleCustomSearchWebSearchProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GoogleCustomSearchWebSearchProvider.d.ts","sourceRoot":"","sources":["../../src/providers/GoogleCustomSearchWebSearchProvider.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAgB,eAAe,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAe3G;;;;;;;;;;;;;;GAcG;AACH,qBACa,mCAAoC,SAAQ,qBAAqB;IAC1E,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAgD;IAEhF,SAAgB,YAAY,EAAE,qBAAqB,CAOjD;IAEF,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,EAAE,CAAqB;IAElB,iBAAiB,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxD,aAAa,CACtB,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,QAAQ,GACvB,OAAO,CAAC,yBAAyB,CAAC;IA4CrC,0DAA0D;IAC1D,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,KAAK;CAQhB;AAED,gCAAgC;AAChC,wBAAgB,uCAAuC,IAAI,IAAI,CAE9D"}