@youdotcom-oss/api 0.1.0 → 0.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdotcom-oss/api",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "You.com API client with bundled CLI for agents supporting Agent Skills",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -63,6 +63,6 @@
63
63
  "test:watch": "bun test --watch"
64
64
  },
65
65
  "dependencies": {
66
- "zod": "^4.3.5"
66
+ "zod": "^4.3.6"
67
67
  }
68
68
  }
package/src/cli.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * Commands:
6
6
  * search <query> - Search the web with You.com
7
- * express <input> - Get AI answers with web context
7
+ * deep-search <query> - Perform deep research with comprehensive answers
8
8
  * contents <url> [url...] - Extract content from URLs
9
9
  *
10
10
  * Options:
@@ -14,10 +14,17 @@
14
14
  * --help, -h - Show help
15
15
  */
16
16
  import { parseArgs } from 'node:util'
17
+ import type * as z from 'zod'
17
18
  import packageJson from '../package.json' with { type: 'json' }
18
- import { contentsCommand } from './commands/contents.ts'
19
- import { expressCommand } from './commands/express.ts'
20
- import { searchCommand } from './commands/search.ts'
19
+ import { ContentsQuerySchema } from './contents/contents.schemas.ts'
20
+ import { fetchContents } from './contents/contents.utils.ts'
21
+ import { DeepSearchQuerySchema } from './deep-search/deep-search.schemas.ts'
22
+ import { callDeepSearch } from './deep-search/deep-search.utils.ts'
23
+ import { SearchQuerySchema } from './search/search.schemas.ts'
24
+ import { fetchSearchResults } from './search/search.utils.ts'
25
+ import type { GetUserAgent } from './shared/api.types.ts'
26
+ import { type CommandConfig, runCommand } from './shared/command-runner.ts'
27
+ import { buildContentsRequest, buildDeepSearchRequest, buildSearchRequest } from './shared/dry-run-utils.ts'
21
28
  import { generateErrorReportLink } from './shared/generate-error-report-link.ts'
22
29
  import { useGetUserAgent } from './shared/use-get-user-agents.ts'
23
30
 
@@ -35,7 +42,7 @@ Usage: ydc <command> --json <json> [options]
35
42
 
36
43
  Commands:
37
44
  search Search the web with You.com
38
- express Get AI answers with web context
45
+ deep-search Perform deep research with comprehensive answers
39
46
  contents Extract content from URLs
40
47
 
41
48
  Global Options:
@@ -43,6 +50,7 @@ Global Options:
43
50
  --api-key <key> You.com API key (overrides YDC_API_KEY)
44
51
  --client <name> Client name for tracking and debugging
45
52
  --schema Output JSON schema for what can be passed to --json
53
+ --dry-run Show request details without making API call
46
54
  --help, -h Show this help
47
55
 
48
56
  Environment Variables:
@@ -55,9 +63,10 @@ Output Format:
55
63
 
56
64
  Examples:
57
65
  ydc search --json '{"query":"AI developments"}' --client Openclaw
58
- ydc express --json '{"input":"What happened today?","tools":[{"type":"web_search"}]}' --client MyAgent
66
+ ydc deep-search --json '{"query":"What are the latest breakthroughs in AI?","search_effort":"high"}' --client MyAgent
59
67
  ydc contents --json '{"urls":["https://example.com"],"formats":["markdown"]}'
60
68
  ydc search --schema # Get JSON schema for search --json input
69
+ ydc search --json '{"query":"AI"}' --dry-run # Inspect request without API call
61
70
  ydc search --json '{"query":"AI"}' | jq '.data.results.web[0].title'
62
71
 
63
72
  More info: https://github.com/youdotcom-oss/dx-toolkit/tree/main/packages/api
@@ -65,22 +74,84 @@ More info: https://github.com/youdotcom-oss/dx-toolkit/tree/main/packages/api
65
74
  process.exit(command ? 0 : 2)
66
75
  }
67
76
 
77
+ // Command configuration map
78
+ const commands = {
79
+ search: {
80
+ schema: SearchQuerySchema,
81
+ handler: ({
82
+ input,
83
+ YDC_API_KEY,
84
+ getUserAgent,
85
+ }: {
86
+ input: z.infer<typeof SearchQuerySchema>
87
+ YDC_API_KEY: string
88
+ getUserAgent: GetUserAgent
89
+ }) => fetchSearchResults({ searchQuery: input, YDC_API_KEY, getUserAgent }),
90
+ dryRunHandler: ({
91
+ input,
92
+ YDC_API_KEY,
93
+ getUserAgent,
94
+ }: {
95
+ input: z.infer<typeof SearchQuerySchema>
96
+ YDC_API_KEY: string
97
+ getUserAgent: GetUserAgent
98
+ }) => buildSearchRequest({ searchQuery: input, YDC_API_KEY, getUserAgent }),
99
+ },
100
+ 'deep-search': {
101
+ schema: DeepSearchQuerySchema,
102
+ handler: ({
103
+ input,
104
+ YDC_API_KEY,
105
+ getUserAgent,
106
+ }: {
107
+ input: z.infer<typeof DeepSearchQuerySchema>
108
+ YDC_API_KEY: string
109
+ getUserAgent: GetUserAgent
110
+ }) => callDeepSearch({ deepSearchQuery: input, YDC_API_KEY, getUserAgent }),
111
+ dryRunHandler: ({
112
+ input,
113
+ YDC_API_KEY,
114
+ getUserAgent,
115
+ }: {
116
+ input: z.infer<typeof DeepSearchQuerySchema>
117
+ YDC_API_KEY: string
118
+ getUserAgent: GetUserAgent
119
+ }) => buildDeepSearchRequest({ deepSearchQuery: input, YDC_API_KEY, getUserAgent }),
120
+ },
121
+ contents: {
122
+ schema: ContentsQuerySchema,
123
+ handler: ({
124
+ input,
125
+ YDC_API_KEY,
126
+ getUserAgent,
127
+ }: {
128
+ input: z.infer<typeof ContentsQuerySchema>
129
+ YDC_API_KEY: string
130
+ getUserAgent: GetUserAgent
131
+ }) => fetchContents({ contentsQuery: input, YDC_API_KEY, getUserAgent }),
132
+ dryRunHandler: ({
133
+ input,
134
+ YDC_API_KEY,
135
+ getUserAgent,
136
+ }: {
137
+ input: z.infer<typeof ContentsQuerySchema>
138
+ YDC_API_KEY: string
139
+ getUserAgent: GetUserAgent
140
+ }) => buildContentsRequest({ contentsQuery: input, YDC_API_KEY, getUserAgent }),
141
+ },
142
+ }
143
+
144
+ // Validate command
145
+ if (!(command in commands)) {
146
+ console.error(`Unknown command: ${command}`)
147
+ console.error(`Run 'ydc --help' for usage`)
148
+ process.exit(2)
149
+ }
150
+
151
+ // Execute command
68
152
  try {
69
- switch (command) {
70
- case 'search':
71
- await searchCommand(args)
72
- break
73
- case 'express':
74
- await expressCommand(args)
75
- break
76
- case 'contents':
77
- await contentsCommand(args)
78
- break
79
- default:
80
- console.error(`Unknown command: ${command}`)
81
- console.error(`Run 'ydc --help' for usage`)
82
- process.exit(2)
83
- }
153
+ // Type assertion is safe because we validated command exists above
154
+ await runCommand(args, commands[command as keyof typeof commands] as CommandConfig<unknown, unknown>)
84
155
  process.exit(0)
85
156
  } catch (error) {
86
157
  console.error(error)
@@ -21,20 +21,21 @@ export type ContentsQuery = z.infer<typeof ContentsQuerySchema>
21
21
 
22
22
  /**
23
23
  * Schema for a single content item in the API response
24
+ * Based on OpenAPI spec: https://you.com/specs/openapi_contents.yaml
24
25
  */
25
26
  const ContentsItemSchema = z.object({
26
27
  url: z.string().describe('URL'),
27
- title: z.string().optional().describe('Title'),
28
- html: z.string().optional().describe('HTML content'),
29
- markdown: z.string().optional().describe('Markdown content'),
28
+ title: z.string().optional().describe('Title (optional in actual API responses)'),
29
+ html: z.string().nullable().optional().describe('HTML content'),
30
+ markdown: z.string().nullable().optional().describe('Markdown content'),
30
31
  metadata: z
31
32
  .object({
32
- jsonld: z.array(z.record(z.string(), z.unknown())).optional().describe('JSON-LD structured data (Schema.org)'),
33
- opengraph: z.record(z.string(), z.string()).optional().describe('OpenGraph meta tags'),
34
- twitter: z.record(z.string(), z.string()).optional().describe('Twitter Card metadata'),
33
+ site_name: z.string().nullable().optional().describe('OpenGraph site name'),
34
+ favicon_url: z.string().describe('Favicon URL'),
35
35
  })
36
+ .nullable()
36
37
  .optional()
37
- .describe('Structured metadata when available'),
38
+ .describe('Page metadata (only when metadata format requested)'),
38
39
  })
39
40
 
40
41
  /**
@@ -0,0 +1,109 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { CONTENTS_API_URL } from '../../shared/api.constants.ts'
3
+ import { buildContentsRequest } from '../../shared/dry-run-utils.ts'
4
+
5
+ describe('buildContentsRequest', () => {
6
+ const getUserAgent = () => 'test-agent'
7
+ const YDC_API_KEY = 'test-key'
8
+
9
+ test('builds basic contents request with markdown format', () => {
10
+ const request = buildContentsRequest({
11
+ contentsQuery: { urls: ['https://example.com'] },
12
+ YDC_API_KEY,
13
+ getUserAgent,
14
+ })
15
+
16
+ expect(request.url).toBe(CONTENTS_API_URL)
17
+ expect(request.method).toBe('POST')
18
+ expect(request.headers['X-API-Key']).toBe('test-key')
19
+ expect(request.headers['Content-Type']).toBe('application/json')
20
+ expect(request.headers['User-Agent']).toBe('test-agent')
21
+
22
+ const body = JSON.parse(request.body!)
23
+ expect(body.urls).toEqual(['https://example.com'])
24
+ expect(body.formats).toEqual(['markdown'])
25
+ })
26
+
27
+ test('builds request with multiple URLs', () => {
28
+ const request = buildContentsRequest({
29
+ contentsQuery: {
30
+ urls: ['https://a.com', 'https://b.com', 'https://c.com'],
31
+ },
32
+ YDC_API_KEY,
33
+ getUserAgent,
34
+ })
35
+
36
+ const body = JSON.parse(request.body!)
37
+ expect(body.urls).toEqual(['https://a.com', 'https://b.com', 'https://c.com'])
38
+ })
39
+
40
+ test('builds request with multiple formats', () => {
41
+ const request = buildContentsRequest({
42
+ contentsQuery: {
43
+ urls: ['https://example.com'],
44
+ formats: ['html', 'markdown', 'metadata'],
45
+ },
46
+ YDC_API_KEY,
47
+ getUserAgent,
48
+ })
49
+
50
+ const body = JSON.parse(request.body!)
51
+ expect(body.formats).toEqual(['html', 'markdown', 'metadata'])
52
+ })
53
+
54
+ test('builds request with deprecated format parameter', () => {
55
+ const request = buildContentsRequest({
56
+ contentsQuery: {
57
+ urls: ['https://example.com'],
58
+ format: 'html',
59
+ },
60
+ YDC_API_KEY,
61
+ getUserAgent,
62
+ })
63
+
64
+ const body = JSON.parse(request.body!)
65
+ expect(body.formats).toEqual(['html'])
66
+ })
67
+
68
+ test('prefers formats array over deprecated format parameter', () => {
69
+ const request = buildContentsRequest({
70
+ contentsQuery: {
71
+ urls: ['https://example.com'],
72
+ formats: ['markdown', 'metadata'],
73
+ format: 'html',
74
+ },
75
+ YDC_API_KEY,
76
+ getUserAgent,
77
+ })
78
+
79
+ const body = JSON.parse(request.body!)
80
+ expect(body.formats).toEqual(['markdown', 'metadata'])
81
+ })
82
+
83
+ test('includes crawl_timeout when provided', () => {
84
+ const request = buildContentsRequest({
85
+ contentsQuery: {
86
+ urls: ['https://example.com'],
87
+ crawl_timeout: 30,
88
+ },
89
+ YDC_API_KEY,
90
+ getUserAgent,
91
+ })
92
+
93
+ const body = JSON.parse(request.body!)
94
+ expect(body.crawl_timeout).toBe(30)
95
+ })
96
+
97
+ test('omits crawl_timeout when not provided', () => {
98
+ const request = buildContentsRequest({
99
+ contentsQuery: {
100
+ urls: ['https://example.com'],
101
+ },
102
+ YDC_API_KEY,
103
+ getUserAgent,
104
+ })
105
+
106
+ const body = JSON.parse(request.body!)
107
+ expect(body.crawl_timeout).toBeUndefined()
108
+ })
109
+ })
@@ -0,0 +1,75 @@
1
+ import { describe, expect, test } from 'bun:test'
2
+ import { ContentsQuerySchema } from '../contents.schemas.ts'
3
+
4
+ describe('ContentsQuerySchema OpenAPI validation', () => {
5
+ test('accepts valid contents queries', () => {
6
+ const validQueries = [
7
+ { urls: ['https://example.com'] },
8
+ { urls: ['https://example.com'], formats: ['markdown'] },
9
+ { urls: ['https://example.com'], formats: ['html', 'markdown'] },
10
+ { urls: ['https://example.com'], formats: ['markdown', 'metadata'] },
11
+ { urls: ['https://example.com'], formats: ['html', 'markdown', 'metadata'] },
12
+ { urls: ['https://example.com'], format: 'html' }, // Deprecated but still supported
13
+ { urls: ['https://example.com'], crawl_timeout: 30 },
14
+ { urls: ['https://a.com', 'https://b.com', 'https://c.com'], formats: ['markdown'] },
15
+ ]
16
+
17
+ for (const validQuery of validQueries) {
18
+ expect(() => ContentsQuerySchema.parse(validQuery)).not.toThrow()
19
+ }
20
+ })
21
+
22
+ test('rejects invalid contents queries', () => {
23
+ const invalidQueries = [
24
+ {}, // Missing urls
25
+ { urls: [] }, // Empty urls array
26
+ { urls: ['not-a-url'] }, // Invalid URL
27
+ { urls: ['https://example.com'], formats: ['invalid'] }, // Invalid format
28
+ { urls: ['https://example.com'], crawl_timeout: 0 }, // Timeout too low
29
+ { urls: ['https://example.com'], crawl_timeout: 61 }, // Timeout too high
30
+ ]
31
+
32
+ for (const invalidQuery of invalidQueries) {
33
+ expect(() => ContentsQuerySchema.parse(invalidQuery)).toThrow()
34
+ }
35
+ })
36
+
37
+ test('accepts metadata format', () => {
38
+ const query = {
39
+ urls: ['https://example.com'],
40
+ formats: ['metadata'],
41
+ }
42
+
43
+ expect(() => ContentsQuerySchema.parse(query)).not.toThrow()
44
+ })
45
+
46
+ test('accepts all format combinations', () => {
47
+ const formatCombinations = [
48
+ ['html'],
49
+ ['markdown'],
50
+ ['metadata'],
51
+ ['html', 'markdown'],
52
+ ['html', 'metadata'],
53
+ ['markdown', 'metadata'],
54
+ ['html', 'markdown', 'metadata'],
55
+ ]
56
+
57
+ for (const formats of formatCombinations) {
58
+ expect(() => ContentsQuerySchema.parse({ urls: ['https://example.com'], formats })).not.toThrow()
59
+ }
60
+ })
61
+
62
+ test('crawl_timeout validation', () => {
63
+ // Valid timeouts (1-60)
64
+ const validTimeouts = [1, 30, 60]
65
+ for (const timeout of validTimeouts) {
66
+ expect(() => ContentsQuerySchema.parse({ urls: ['https://example.com'], crawl_timeout: timeout })).not.toThrow()
67
+ }
68
+
69
+ // Invalid timeouts
70
+ const invalidTimeouts = [0, -1, 61, 100]
71
+ for (const timeout of invalidTimeouts) {
72
+ expect(() => ContentsQuerySchema.parse({ urls: ['https://example.com'], crawl_timeout: timeout })).toThrow()
73
+ }
74
+ })
75
+ })
@@ -0,0 +1,48 @@
1
+ import * as z from 'zod'
2
+
3
+ /**
4
+ * Search effort levels for deep-search API
5
+ * Controls computation budget and response time
6
+ */
7
+ export const SearchEffortSchema = z.enum(['low', 'medium', 'high']).describe('Search effort level')
8
+
9
+ /**
10
+ * Input schema for deep-search API
11
+ * Based on OpenAPI spec: https://docs.you.com/api-reference/deep-search/v1-deep_search
12
+ *
13
+ * @public
14
+ */
15
+ export const DeepSearchQuerySchema = z.object({
16
+ query: z
17
+ .string()
18
+ .min(1, 'Query is required')
19
+ .describe('The research question or complex query requiring in-depth investigation and multi-step reasoning'),
20
+ search_effort: SearchEffortSchema.optional()
21
+ .default('medium')
22
+ .describe('Computation budget: low (<30s), medium (<60s, default), high (<300s)'),
23
+ })
24
+
25
+ export type DeepSearchQuery = z.infer<typeof DeepSearchQuerySchema>
26
+
27
+ /**
28
+ * Schema for a single source in the deep-search response
29
+ *
30
+ * @public
31
+ */
32
+ const DeepSearchSourceSchema = z.object({
33
+ url: z.string().describe('Source webpage URL'),
34
+ title: z.string().describe('Source webpage title'),
35
+ snippets: z.array(z.string()).describe('Relevant excerpts from the source page used in generating the answer'),
36
+ })
37
+
38
+ /**
39
+ * Response schema for deep-search API
40
+ *
41
+ * @public
42
+ */
43
+ export const DeepSearchResponseSchema = z.object({
44
+ answer: z.string().describe('Comprehensive response with inline citations, formatted in Markdown'),
45
+ results: z.array(DeepSearchSourceSchema).describe('List of web sources used to generate the answer'),
46
+ })
47
+
48
+ export type DeepSearchResponse = z.infer<typeof DeepSearchResponseSchema>
@@ -0,0 +1,75 @@
1
+ import type * as z from 'zod'
2
+ import { DEEP_SEARCH_API_URL } from '../shared/api.constants.ts'
3
+ import type { GetUserAgent } from '../shared/api.types.ts'
4
+ import { checkResponseForErrors } from '../shared/check-response-for-errors.ts'
5
+ import { type DeepSearchQuery, DeepSearchResponseSchema } from './deep-search.schemas.ts'
6
+
7
+ /**
8
+ * Perform deep research using You.com Deep Search API
9
+ *
10
+ * @param params - Deep search query parameters
11
+ * @returns Deep search response with comprehensive answer and sources
12
+ *
13
+ * @public
14
+ */
15
+ export const callDeepSearch = async ({
16
+ deepSearchQuery,
17
+ YDC_API_KEY,
18
+ getUserAgent,
19
+ }: {
20
+ deepSearchQuery: DeepSearchQuery
21
+ YDC_API_KEY: string
22
+ getUserAgent: GetUserAgent
23
+ }) => {
24
+ const response = await fetch(DEEP_SEARCH_API_URL, {
25
+ method: 'POST',
26
+ headers: new Headers({
27
+ 'X-API-Key': YDC_API_KEY || '',
28
+ 'Content-Type': 'application/json',
29
+ 'User-Agent': getUserAgent(),
30
+ }),
31
+ body: JSON.stringify(deepSearchQuery),
32
+ })
33
+
34
+ await checkResponseForErrors(response)
35
+ const data = await response.json()
36
+
37
+ return DeepSearchResponseSchema.parse(data)
38
+ }
39
+
40
+ /**
41
+ * Format deep-search response for display
42
+ * Returns markdown-formatted text with answer and sources
43
+ *
44
+ * @param response - Deep search API response
45
+ * @returns Formatted markdown string
46
+ *
47
+ * @public
48
+ */
49
+ export const formatDeepSearchResponse = (response: z.infer<typeof DeepSearchResponseSchema>): string => {
50
+ const parts: string[] = []
51
+
52
+ // Add the comprehensive answer
53
+ parts.push('# Answer\n')
54
+ parts.push(response.answer)
55
+ parts.push('\n')
56
+
57
+ // Add sources section
58
+ if (response.results && response.results.length > 0) {
59
+ parts.push('\n## Sources\n')
60
+
61
+ for (const [index, source] of response.results.entries()) {
62
+ parts.push(`\n### ${index + 1}. ${source.title}\n`)
63
+ parts.push(`**URL:** ${source.url}\n`)
64
+
65
+ if (source.snippets && source.snippets.length > 0) {
66
+ parts.push('\n**Key Excerpts:**\n')
67
+ for (const snippet of source.snippets) {
68
+ parts.push(`> ${snippet}\n`)
69
+ }
70
+ }
71
+ }
72
+ }
73
+
74
+ return parts.join('\n')
75
+ }
package/src/main.ts CHANGED
@@ -10,13 +10,14 @@
10
10
  // Contents
11
11
  export * from './contents/contents.schemas.ts'
12
12
  export * from './contents/contents.utils.ts'
13
- // Express
14
- export * from './express/express.schemas.ts'
15
- export * from './express/express.utils.ts'
13
+ // Deep-Search
14
+ export * from './deep-search/deep-search.schemas.ts'
15
+ export * from './deep-search/deep-search.utils.ts'
16
16
  // Search
17
17
  export * from './search/search.schemas.ts'
18
18
  export * from './search/search.utils.ts'
19
19
  // Shared
20
20
  export * from './shared/api.constants.ts'
21
21
  export * from './shared/api.types.ts'
22
+ export * from './shared/dry-run-utils.ts'
22
23
  export * from './shared/generate-error-report-link.ts'
@@ -1,7 +1,70 @@
1
1
  import * as z from 'zod'
2
2
 
3
+ /**
4
+ * Language codes supported by You.com Search API (BCP 47 format)
5
+ * Based on OpenAPI spec: https://you.com/specs/openapi_search_v1.yaml
6
+ */
7
+ export const LanguageSchema = z.enum([
8
+ 'AR',
9
+ 'EU',
10
+ 'BN',
11
+ 'BG',
12
+ 'CA',
13
+ 'ZH-HANS',
14
+ 'ZH-HANT',
15
+ 'HR',
16
+ 'CS',
17
+ 'DA',
18
+ 'NL',
19
+ 'EN',
20
+ 'EN-GB',
21
+ 'ET',
22
+ 'FI',
23
+ 'FR',
24
+ 'GL',
25
+ 'DE',
26
+ 'EL',
27
+ 'GU',
28
+ 'HE',
29
+ 'HI',
30
+ 'HU',
31
+ 'IS',
32
+ 'IT',
33
+ 'JP',
34
+ 'KN',
35
+ 'KO',
36
+ 'LV',
37
+ 'LT',
38
+ 'MS',
39
+ 'ML',
40
+ 'MR',
41
+ 'NB',
42
+ 'PL',
43
+ 'PT-BR',
44
+ 'PT-PT',
45
+ 'PA',
46
+ 'RO',
47
+ 'RU',
48
+ 'SR',
49
+ 'SK',
50
+ 'SL',
51
+ 'ES',
52
+ 'SV',
53
+ 'TA',
54
+ 'TE',
55
+ 'TH',
56
+ 'TR',
57
+ 'UK',
58
+ 'VI',
59
+ ])
60
+
3
61
  export const SearchQuerySchema = z.object({
4
- query: z.string().min(1, 'Query is required').describe('Search query (supports +, -, site:, filetype:, lang:)'),
62
+ query: z
63
+ .string()
64
+ .min(1, 'Query is required')
65
+ .describe(
66
+ 'Search query. Supports operators: site:domain.com (domain filter), filetype:pdf (file type), +term (include), -term (exclude), AND/OR/NOT (boolean logic), lang:en (language). Example: "machine learning (Python OR PyTorch) -TensorFlow filetype:pdf"',
67
+ ),
5
68
  count: z.number().int().min(1).max(100).optional().describe('Max results per section'),
6
69
  freshness: z.string().optional().describe('day/week/month/year or YYYY-MM-DDtoYYYY-MM-DD'),
7
70
  offset: z.number().int().min(0).max(9).optional().describe('Pagination offset'),
@@ -32,6 +95,7 @@ export const SearchQuerySchema = z.object({
32
95
  'CN',
33
96
  'PL',
34
97
  'PT',
98
+ 'PT-BR',
35
99
  'PH',
36
100
  'RU',
37
101
  'SA',
@@ -47,11 +111,6 @@ export const SearchQuerySchema = z.object({
47
111
  .optional()
48
112
  .describe('Country code'),
49
113
  safesearch: z.enum(['off', 'moderate', 'strict']).optional().describe('Filter level'),
50
- site: z.string().optional().describe('Specific domain'),
51
- fileType: z.string().optional().describe('File type'),
52
- language: z.string().optional().describe('ISO 639-1 language code'),
53
- excludeTerms: z.string().optional().describe('Terms to exclude (pipe-separated)'),
54
- exactTerms: z.string().optional().describe('Exact terms (pipe-separated)'),
55
114
  livecrawl: z.enum(['web', 'news', 'all']).optional().describe('Live-crawl sections for full content'),
56
115
  livecrawl_formats: z.enum(['html', 'markdown']).optional().describe('Format for crawled content'),
57
116
  })
@@ -5,7 +5,7 @@ import { type SearchQuery, SearchResponseSchema } from './search.schemas.ts'
5
5
 
6
6
  export const fetchSearchResults = async ({
7
7
  YDC_API_KEY = process.env.YDC_API_KEY,
8
- searchQuery: { query, site, fileType, language, exactTerms, excludeTerms, ...rest },
8
+ searchQuery,
9
9
  getUserAgent,
10
10
  }: {
11
11
  searchQuery: SearchQuery
@@ -16,33 +16,11 @@ export const fetchSearchResults = async ({
16
16
 
17
17
  const searchParams = new URLSearchParams()
18
18
 
19
- // Build Query Param
20
- const searchQuery = [query]
21
- site && searchQuery.push(`site:${site}`)
22
- fileType && searchQuery.push(`filetype:${fileType}`)
23
- language && searchQuery.push(`lang:${language}`)
24
- if (exactTerms && excludeTerms) {
25
- throw new Error('Cannot specify both exactTerms and excludeTerms - please use only one')
26
- }
27
- exactTerms &&
28
- searchQuery.push(
29
- exactTerms
30
- .split('|')
31
- .map((term) => `+${term}`)
32
- .join(' AND '),
33
- )
34
- excludeTerms &&
35
- searchQuery.push(
36
- excludeTerms
37
- .split('|')
38
- .map((term) => `-${term}`)
39
- .join(' AND '),
40
- )
41
- searchParams.append('query', searchQuery.join(' '))
42
-
43
- // Append additional advanced Params
44
- for (const [name, value] of Object.entries(rest)) {
45
- if (value) searchParams.append(name, `${value}`)
19
+ // Append all query parameters
20
+ for (const [name, value] of Object.entries(searchQuery)) {
21
+ if (value !== undefined && value !== null) {
22
+ searchParams.append(name, `${value}`)
23
+ }
46
24
  }
47
25
 
48
26
  url.search = searchParams.toString()