@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/README.md +36 -36
- package/bin/cli.js +233 -403
- package/package.json +2 -2
- package/src/cli.ts +92 -21
- package/src/contents/contents.schemas.ts +8 -7
- package/src/contents/tests/contents.request.spec.ts +109 -0
- package/src/contents/tests/contents.schema-validation.spec.ts +75 -0
- package/src/deep-search/deep-search.schemas.ts +48 -0
- package/src/deep-search/deep-search.utils.ts +75 -0
- package/src/main.ts +4 -3
- package/src/search/search.schemas.ts +65 -6
- package/src/search/search.utils.ts +6 -28
- package/src/search/tests/search.request.spec.ts +122 -0
- package/src/search/tests/search.schema-validation.spec.ts +152 -0
- package/src/search/tests/{search.utils.spec.ts → search.utils.docker.ts} +0 -10
- package/src/shared/api.constants.ts +1 -1
- package/src/shared/check-response-for-errors.ts +1 -1
- package/src/shared/command-runner.ts +95 -0
- package/src/shared/dry-run-utils.ts +141 -0
- package/src/shared/tests/command-runner.spec.ts +210 -0
- package/src/shared/use-get-user-agents.ts +1 -1
- package/src/commands/contents.ts +0 -52
- package/src/commands/express.ts +0 -52
- package/src/commands/search.ts +0 -52
- package/src/express/express.schemas.ts +0 -85
- package/src/express/express.utils.ts +0 -113
- package/src/express/tests/express.utils.spec.ts +0 -83
- /package/src/contents/tests/{contents.utils.spec.ts → contents.utils.docker.ts} +0 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { SEARCH_API_URL } from '../../shared/api.constants.ts'
|
|
3
|
+
import { buildSearchRequest } from '../../shared/dry-run-utils.ts'
|
|
4
|
+
|
|
5
|
+
describe('buildSearchRequest', () => {
|
|
6
|
+
const getUserAgent = () => 'test-agent'
|
|
7
|
+
const YDC_API_KEY = 'test-key'
|
|
8
|
+
|
|
9
|
+
test('builds basic search request', () => {
|
|
10
|
+
const request = buildSearchRequest({
|
|
11
|
+
searchQuery: { query: 'AI' },
|
|
12
|
+
YDC_API_KEY,
|
|
13
|
+
getUserAgent,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
expect(request.url).toBe(SEARCH_API_URL)
|
|
17
|
+
expect(request.method).toBe('GET')
|
|
18
|
+
expect(request.headers['X-API-Key']).toBe('test-key')
|
|
19
|
+
expect(request.headers['User-Agent']).toBe('test-agent')
|
|
20
|
+
expect(request.queryParams?.query).toBe('AI')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('passes query with site: operator directly', () => {
|
|
24
|
+
const request = buildSearchRequest({
|
|
25
|
+
searchQuery: { query: 'AI site:you.com' },
|
|
26
|
+
YDC_API_KEY,
|
|
27
|
+
getUserAgent,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
expect(request.queryParams?.query).toBe('AI site:you.com')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('passes query with filetype: operator directly', () => {
|
|
34
|
+
const request = buildSearchRequest({
|
|
35
|
+
searchQuery: { query: 'tutorial filetype:pdf' },
|
|
36
|
+
YDC_API_KEY,
|
|
37
|
+
getUserAgent,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
expect(request.queryParams?.query).toBe('tutorial filetype:pdf')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('passes query with lang: operator directly', () => {
|
|
44
|
+
const request = buildSearchRequest({
|
|
45
|
+
searchQuery: { query: 'search lang:en' },
|
|
46
|
+
YDC_API_KEY,
|
|
47
|
+
getUserAgent,
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
expect(request.queryParams?.query).toBe('search lang:en')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('passes query with +term inclusion operator directly', () => {
|
|
54
|
+
const request = buildSearchRequest({
|
|
55
|
+
searchQuery: { query: 'search +machine +learning' },
|
|
56
|
+
YDC_API_KEY,
|
|
57
|
+
getUserAgent,
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
expect(request.queryParams?.query).toBe('search +machine +learning')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test('passes query with -term exclusion operator directly', () => {
|
|
64
|
+
const request = buildSearchRequest({
|
|
65
|
+
searchQuery: { query: 'python -django -flask' },
|
|
66
|
+
YDC_API_KEY,
|
|
67
|
+
getUserAgent,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
expect(request.queryParams?.query).toBe('python -django -flask')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('passes query with boolean operators directly', () => {
|
|
74
|
+
const request = buildSearchRequest({
|
|
75
|
+
searchQuery: { query: '(Python OR JavaScript) AND tutorial -deprecated' },
|
|
76
|
+
YDC_API_KEY,
|
|
77
|
+
getUserAgent,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
expect(request.queryParams?.query).toBe('(Python OR JavaScript) AND tutorial -deprecated')
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test('includes advanced search parameters', () => {
|
|
84
|
+
const request = buildSearchRequest({
|
|
85
|
+
searchQuery: {
|
|
86
|
+
query: 'AI',
|
|
87
|
+
count: 10,
|
|
88
|
+
freshness: 'week',
|
|
89
|
+
offset: 5,
|
|
90
|
+
country: 'US',
|
|
91
|
+
safesearch: 'moderate',
|
|
92
|
+
livecrawl: 'web',
|
|
93
|
+
livecrawl_formats: 'markdown',
|
|
94
|
+
},
|
|
95
|
+
YDC_API_KEY,
|
|
96
|
+
getUserAgent,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
expect(request.queryParams?.query).toBe('AI')
|
|
100
|
+
expect(request.queryParams?.count).toBe('10')
|
|
101
|
+
expect(request.queryParams?.freshness).toBe('week')
|
|
102
|
+
expect(request.queryParams?.offset).toBe('5')
|
|
103
|
+
expect(request.queryParams?.country).toBe('US')
|
|
104
|
+
expect(request.queryParams?.safesearch).toBe('moderate')
|
|
105
|
+
expect(request.queryParams?.livecrawl).toBe('web')
|
|
106
|
+
expect(request.queryParams?.livecrawl_formats).toBe('markdown')
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
test('combines multiple operators in query string', () => {
|
|
110
|
+
const request = buildSearchRequest({
|
|
111
|
+
searchQuery: {
|
|
112
|
+
query: 'machine learning best practices (Python OR PyTorch) -TensorFlow filetype:pdf',
|
|
113
|
+
},
|
|
114
|
+
YDC_API_KEY,
|
|
115
|
+
getUserAgent,
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
expect(request.queryParams?.query).toBe(
|
|
119
|
+
'machine learning best practices (Python OR PyTorch) -TensorFlow filetype:pdf',
|
|
120
|
+
)
|
|
121
|
+
})
|
|
122
|
+
})
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { LanguageSchema, SearchQuerySchema } from '../search.schemas.ts'
|
|
3
|
+
|
|
4
|
+
describe('SearchQuerySchema OpenAPI validation', () => {
|
|
5
|
+
test('accepts valid query parameters', () => {
|
|
6
|
+
const validQueries = [
|
|
7
|
+
{ query: 'AI' },
|
|
8
|
+
{ query: 'test', count: 10, freshness: 'week' },
|
|
9
|
+
{ query: 'search', country: 'US', safesearch: 'moderate' },
|
|
10
|
+
{ query: 'livecrawl test', livecrawl: 'web', livecrawl_formats: 'markdown' },
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
for (const validQuery of validQueries) {
|
|
14
|
+
expect(() => SearchQuerySchema.parse(validQuery)).not.toThrow()
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test('rejects invalid query parameters', () => {
|
|
19
|
+
const invalidQueries = [
|
|
20
|
+
{}, // Missing query
|
|
21
|
+
{ query: '' }, // Empty query
|
|
22
|
+
{ query: 'test', count: 0 }, // Count too low
|
|
23
|
+
{ query: 'test', count: 101 }, // Count too high
|
|
24
|
+
{ query: 'test', offset: -1 }, // Negative offset
|
|
25
|
+
{ query: 'test', offset: 10 }, // Offset too high
|
|
26
|
+
{ query: 'test', safesearch: 'invalid' }, // Invalid safesearch
|
|
27
|
+
{ query: 'test', livecrawl: 'invalid' }, // Invalid livecrawl
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
for (const invalidQuery of invalidQueries) {
|
|
31
|
+
expect(() => SearchQuerySchema.parse(invalidQuery)).toThrow()
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('LanguageSchema includes all 51 BCP 47 codes', () => {
|
|
36
|
+
// Test a sample of language codes from the OpenAPI spec
|
|
37
|
+
const languageCodes = [
|
|
38
|
+
'AR',
|
|
39
|
+
'EU',
|
|
40
|
+
'BN',
|
|
41
|
+
'BG',
|
|
42
|
+
'CA',
|
|
43
|
+
'ZH-HANS',
|
|
44
|
+
'ZH-HANT',
|
|
45
|
+
'HR',
|
|
46
|
+
'CS',
|
|
47
|
+
'DA',
|
|
48
|
+
'NL',
|
|
49
|
+
'EN',
|
|
50
|
+
'EN-GB',
|
|
51
|
+
'ET',
|
|
52
|
+
'FI',
|
|
53
|
+
'FR',
|
|
54
|
+
'GL',
|
|
55
|
+
'DE',
|
|
56
|
+
'EL',
|
|
57
|
+
'GU',
|
|
58
|
+
'HE',
|
|
59
|
+
'HI',
|
|
60
|
+
'HU',
|
|
61
|
+
'IS',
|
|
62
|
+
'IT',
|
|
63
|
+
'JP',
|
|
64
|
+
'KN',
|
|
65
|
+
'KO',
|
|
66
|
+
'LV',
|
|
67
|
+
'LT',
|
|
68
|
+
'MS',
|
|
69
|
+
'ML',
|
|
70
|
+
'MR',
|
|
71
|
+
'NB',
|
|
72
|
+
'PL',
|
|
73
|
+
'PT-BR',
|
|
74
|
+
'PT-PT',
|
|
75
|
+
'PA',
|
|
76
|
+
'RO',
|
|
77
|
+
'RU',
|
|
78
|
+
'SR',
|
|
79
|
+
'SK',
|
|
80
|
+
'SL',
|
|
81
|
+
'ES',
|
|
82
|
+
'SV',
|
|
83
|
+
'TA',
|
|
84
|
+
'TE',
|
|
85
|
+
'TH',
|
|
86
|
+
'TR',
|
|
87
|
+
'UK',
|
|
88
|
+
'VI',
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
for (const code of languageCodes) {
|
|
92
|
+
expect(() => LanguageSchema.parse(code)).not.toThrow()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Test that the schema has exactly 51 values
|
|
96
|
+
expect(LanguageSchema.options.length).toBe(51)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('rejects invalid language codes', () => {
|
|
100
|
+
const invalidCodes = ['XX', 'INVALID', 'en', 'zh', '123']
|
|
101
|
+
|
|
102
|
+
for (const code of invalidCodes) {
|
|
103
|
+
expect(() => LanguageSchema.parse(code)).toThrow()
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('accepts all country codes from OpenAPI spec', () => {
|
|
108
|
+
const countryCodes = [
|
|
109
|
+
'AR',
|
|
110
|
+
'AU',
|
|
111
|
+
'AT',
|
|
112
|
+
'BE',
|
|
113
|
+
'BR',
|
|
114
|
+
'CA',
|
|
115
|
+
'CL',
|
|
116
|
+
'DK',
|
|
117
|
+
'FI',
|
|
118
|
+
'FR',
|
|
119
|
+
'DE',
|
|
120
|
+
'HK',
|
|
121
|
+
'IN',
|
|
122
|
+
'ID',
|
|
123
|
+
'IT',
|
|
124
|
+
'JP',
|
|
125
|
+
'KR',
|
|
126
|
+
'MY',
|
|
127
|
+
'MX',
|
|
128
|
+
'NL',
|
|
129
|
+
'NZ',
|
|
130
|
+
'NO',
|
|
131
|
+
'CN',
|
|
132
|
+
'PL',
|
|
133
|
+
'PT',
|
|
134
|
+
'PT-BR',
|
|
135
|
+
'PH',
|
|
136
|
+
'RU',
|
|
137
|
+
'SA',
|
|
138
|
+
'ZA',
|
|
139
|
+
'ES',
|
|
140
|
+
'SE',
|
|
141
|
+
'CH',
|
|
142
|
+
'TW',
|
|
143
|
+
'TR',
|
|
144
|
+
'GB',
|
|
145
|
+
'US',
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
for (const code of countryCodes) {
|
|
149
|
+
expect(() => SearchQuerySchema.parse({ query: 'test', country: code })).not.toThrow()
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
})
|
|
@@ -15,9 +15,7 @@ describe('fetchSearchResults', () => {
|
|
|
15
15
|
expect(result).toHaveProperty('results')
|
|
16
16
|
expect(result).toHaveProperty('metadata')
|
|
17
17
|
expect(result.results).toHaveProperty('web')
|
|
18
|
-
// expect(result.results).toHaveProperty('news');
|
|
19
18
|
expect(Array.isArray(result.results.web)).toBe(true)
|
|
20
|
-
// expect(Array.isArray(result.results.news)).toBe(true);
|
|
21
19
|
|
|
22
20
|
// Assert required metadata fields
|
|
23
21
|
expect(typeof result.metadata?.query).toBe('string')
|
|
@@ -57,7 +55,6 @@ describe('fetchSearchResults', () => {
|
|
|
57
55
|
})
|
|
58
56
|
|
|
59
57
|
// Test that web results have required properties
|
|
60
|
-
// biome-ignore lint/style/noNonNullAssertion: Test
|
|
61
58
|
const webResult = result.results.web![0]
|
|
62
59
|
|
|
63
60
|
expect(webResult).toHaveProperty('url')
|
|
@@ -65,13 +62,6 @@ describe('fetchSearchResults', () => {
|
|
|
65
62
|
expect(webResult).toHaveProperty('description')
|
|
66
63
|
expect(webResult).toHaveProperty('snippets')
|
|
67
64
|
expect(Array.isArray(webResult?.snippets)).toBe(true)
|
|
68
|
-
|
|
69
|
-
// Test that news results have required properties
|
|
70
|
-
// const newsResult = result.results.news![0];
|
|
71
|
-
// expect(newsResult).toHaveProperty('url');
|
|
72
|
-
// expect(newsResult).toHaveProperty('title');
|
|
73
|
-
// expect(newsResult).toHaveProperty('description');
|
|
74
|
-
// expect(newsResult).toHaveProperty('page_age');
|
|
75
65
|
},
|
|
76
66
|
{ retry: 2 },
|
|
77
67
|
)
|
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
export const SEARCH_API_URL = 'https://ydc-index.io/v1/search'
|
|
9
|
-
export const
|
|
9
|
+
export const DEEP_SEARCH_API_URL = 'https://api.you.com/v1/deep_search'
|
|
10
10
|
export const CONTENTS_API_URL = 'https://ydc-index.io/v1/contents'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Checks if a response object contains an error field and throws if found
|
|
3
3
|
* Handles API responses that return 200 status but contain error messages
|
|
4
|
-
* Used by
|
|
4
|
+
* Used by search, deep-search, and contents utilities
|
|
5
5
|
*/
|
|
6
6
|
export const checkResponseForErrors = (responseData: unknown) => {
|
|
7
7
|
if (typeof responseData === 'object' && responseData !== null && 'error' in responseData) {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared command infrastructure for CLI commands
|
|
3
|
+
* Handles flag parsing, validation, and execution
|
|
4
|
+
*
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { parseArgs } from 'node:util'
|
|
9
|
+
import * as z from 'zod'
|
|
10
|
+
import type { GetUserAgent } from './api.types.ts'
|
|
11
|
+
import type { DryRunResult } from './dry-run-utils.ts'
|
|
12
|
+
import { useGetUserAgent } from './use-get-user-agents.ts'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configuration for a command
|
|
16
|
+
*
|
|
17
|
+
* @typeParam TInput - Zod-inferred input type
|
|
18
|
+
* @typeParam TOutput - Command output type
|
|
19
|
+
*/
|
|
20
|
+
export type CommandConfig<TInput, TOutput> = {
|
|
21
|
+
schema: z.ZodType<TInput>
|
|
22
|
+
handler: (params: { input: TInput; YDC_API_KEY: string; getUserAgent: GetUserAgent }) => Promise<TOutput>
|
|
23
|
+
dryRunHandler?: (params: { input: TInput; YDC_API_KEY: string; getUserAgent: GetUserAgent }) => DryRunResult
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Run a command with standardized flag parsing and validation
|
|
28
|
+
* Handles --schema, --json, --api-key, --client, and --dry-run flags
|
|
29
|
+
*
|
|
30
|
+
* @param args - Command line arguments
|
|
31
|
+
* @param config - Command configuration with schema and handler
|
|
32
|
+
*
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
export const runCommand = async <TInput, TOutput>(args: string[], config: CommandConfig<TInput, TOutput>) => {
|
|
36
|
+
// Handle --schema flag
|
|
37
|
+
if (args.includes('--schema')) {
|
|
38
|
+
console.log(JSON.stringify(z.toJSONSchema(config.schema)))
|
|
39
|
+
process.exit(0)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Parse flags with Node's built-in parseArgs
|
|
43
|
+
const { values } = parseArgs({
|
|
44
|
+
args,
|
|
45
|
+
options: {
|
|
46
|
+
json: { type: 'string' },
|
|
47
|
+
'api-key': { type: 'string' },
|
|
48
|
+
client: { type: 'string' },
|
|
49
|
+
'dry-run': { type: 'boolean' },
|
|
50
|
+
},
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// --json is required
|
|
54
|
+
if (!values.json) {
|
|
55
|
+
throw new Error('--json flag is required')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Parse JSON input
|
|
59
|
+
const input = JSON.parse(values.json)
|
|
60
|
+
const apiKey = values['api-key']
|
|
61
|
+
const client = values.client || process.env.YDC_CLIENT
|
|
62
|
+
|
|
63
|
+
// Get API key from options or environment
|
|
64
|
+
const YDC_API_KEY = apiKey || process.env.YDC_API_KEY
|
|
65
|
+
if (!YDC_API_KEY) {
|
|
66
|
+
throw new Error('YDC_API_KEY environment variable is required')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate with schema
|
|
70
|
+
const validatedInput = config.schema.parse(input)
|
|
71
|
+
|
|
72
|
+
// Create getUserAgent function
|
|
73
|
+
const getUserAgent = useGetUserAgent(client)
|
|
74
|
+
|
|
75
|
+
// Handle --dry-run flag
|
|
76
|
+
if (values['dry-run'] && config.dryRunHandler) {
|
|
77
|
+
const dryRunResult = config.dryRunHandler({
|
|
78
|
+
input: validatedInput,
|
|
79
|
+
YDC_API_KEY,
|
|
80
|
+
getUserAgent,
|
|
81
|
+
})
|
|
82
|
+
console.log(JSON.stringify(dryRunResult))
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Execute handler
|
|
87
|
+
const response = await config.handler({
|
|
88
|
+
input: validatedInput,
|
|
89
|
+
YDC_API_KEY,
|
|
90
|
+
getUserAgent,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// Output response to stdout (success)
|
|
94
|
+
console.log(JSON.stringify(response))
|
|
95
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dry-run utilities for testing request construction without API calls
|
|
3
|
+
* These functions build request details (URL, headers, body) that can be inspected
|
|
4
|
+
* without making actual API calls.
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ContentsQuery } from '../contents/contents.schemas.ts'
|
|
10
|
+
import type { DeepSearchQuery } from '../deep-search/deep-search.schemas.ts'
|
|
11
|
+
import type { SearchQuery } from '../search/search.schemas.ts'
|
|
12
|
+
import { CONTENTS_API_URL, DEEP_SEARCH_API_URL, SEARCH_API_URL } from './api.constants.ts'
|
|
13
|
+
import type { GetUserAgent } from './api.types.ts'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Result structure for dry-run request inspection
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export type DryRunResult = {
|
|
21
|
+
url: string
|
|
22
|
+
method: 'GET' | 'POST'
|
|
23
|
+
headers: Record<string, string>
|
|
24
|
+
body?: string
|
|
25
|
+
queryParams?: Record<string, string>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build search request details without making API call
|
|
30
|
+
* Useful for testing and debugging query construction
|
|
31
|
+
*
|
|
32
|
+
* @param params - Search query parameters
|
|
33
|
+
* @returns Request details including URL, headers, and query params
|
|
34
|
+
*
|
|
35
|
+
* @public
|
|
36
|
+
*/
|
|
37
|
+
export const buildSearchRequest = ({
|
|
38
|
+
searchQuery,
|
|
39
|
+
YDC_API_KEY,
|
|
40
|
+
getUserAgent,
|
|
41
|
+
}: {
|
|
42
|
+
searchQuery: SearchQuery
|
|
43
|
+
YDC_API_KEY: string
|
|
44
|
+
getUserAgent: GetUserAgent
|
|
45
|
+
}): DryRunResult => {
|
|
46
|
+
// Convert all search query params to query string parameters
|
|
47
|
+
const queryParams: Record<string, string> = {}
|
|
48
|
+
|
|
49
|
+
for (const [name, value] of Object.entries(searchQuery)) {
|
|
50
|
+
if (value !== undefined && value !== null) {
|
|
51
|
+
queryParams[name] = `${value}`
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
url: SEARCH_API_URL,
|
|
57
|
+
method: 'GET',
|
|
58
|
+
headers: {
|
|
59
|
+
'X-API-Key': YDC_API_KEY,
|
|
60
|
+
'User-Agent': getUserAgent(),
|
|
61
|
+
},
|
|
62
|
+
queryParams,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Build contents request details without making API call
|
|
68
|
+
* Useful for testing and debugging POST body construction
|
|
69
|
+
*
|
|
70
|
+
* @param params - Contents query parameters
|
|
71
|
+
* @returns Request details including URL, headers, and POST body
|
|
72
|
+
*
|
|
73
|
+
* @public
|
|
74
|
+
*/
|
|
75
|
+
export const buildContentsRequest = ({
|
|
76
|
+
contentsQuery: { urls, formats, format, crawl_timeout },
|
|
77
|
+
YDC_API_KEY,
|
|
78
|
+
getUserAgent,
|
|
79
|
+
}: {
|
|
80
|
+
contentsQuery: ContentsQuery
|
|
81
|
+
YDC_API_KEY: string
|
|
82
|
+
getUserAgent: GetUserAgent
|
|
83
|
+
}): DryRunResult => {
|
|
84
|
+
// Handle backward compatibility: prefer formats array, fallback to format string, default to ['markdown']
|
|
85
|
+
const requestFormats = formats || (format ? [format] : ['markdown'])
|
|
86
|
+
|
|
87
|
+
// Build request body
|
|
88
|
+
const requestBody: {
|
|
89
|
+
urls: string[]
|
|
90
|
+
formats: string[]
|
|
91
|
+
crawl_timeout?: number
|
|
92
|
+
} = {
|
|
93
|
+
urls,
|
|
94
|
+
formats: requestFormats,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (crawl_timeout !== undefined) {
|
|
98
|
+
requestBody.crawl_timeout = crawl_timeout
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
url: CONTENTS_API_URL,
|
|
103
|
+
method: 'POST',
|
|
104
|
+
headers: {
|
|
105
|
+
'X-API-Key': YDC_API_KEY,
|
|
106
|
+
'Content-Type': 'application/json',
|
|
107
|
+
'User-Agent': getUserAgent(),
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify(requestBody),
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Build deep-search request details without making API call
|
|
115
|
+
* Useful for testing and debugging POST body construction
|
|
116
|
+
*
|
|
117
|
+
* @param params - Deep-search query parameters
|
|
118
|
+
* @returns Request details including URL, headers, and POST body
|
|
119
|
+
*
|
|
120
|
+
* @public
|
|
121
|
+
*/
|
|
122
|
+
export const buildDeepSearchRequest = ({
|
|
123
|
+
deepSearchQuery,
|
|
124
|
+
YDC_API_KEY,
|
|
125
|
+
getUserAgent,
|
|
126
|
+
}: {
|
|
127
|
+
deepSearchQuery: DeepSearchQuery
|
|
128
|
+
YDC_API_KEY: string
|
|
129
|
+
getUserAgent: GetUserAgent
|
|
130
|
+
}): DryRunResult => {
|
|
131
|
+
return {
|
|
132
|
+
url: DEEP_SEARCH_API_URL,
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: {
|
|
135
|
+
'X-API-Key': YDC_API_KEY,
|
|
136
|
+
'Content-Type': 'application/json',
|
|
137
|
+
'User-Agent': getUserAgent(),
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify(deepSearchQuery),
|
|
140
|
+
}
|
|
141
|
+
}
|