@microlink/google 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,30 @@
4
4
  [![Coverage Status](https://img.shields.io/coveralls/microlinkhq/microlink-google.svg?style=flat-square)](https://coveralls.io/github/microlinkhq/microlink-google)
5
5
  [![NPM Status](https://img.shields.io/npm/dm/@microlink/google.svg?style=flat-square)](https://www.npmjs.org/package/@microlink/google)
6
6
 
7
- > Google search results via [Microlink API](https://microlink.io).
7
+ > Turn Google into a structured API.
8
+ > Query Search, News, Images, Videos, Places, Maps, Shopping, Scholar, Patents, and Autocomplete — and get normalized data ready for code.
9
+
10
+ ## Highlights
11
+
12
+ - **10 Google verticals in one API**
13
+ Search, News, Images, Videos, Places, Maps, Shopping, Scholar, Patents, and Autocomplete.
14
+
15
+ - **Normalized data**
16
+ Dates → ISO 8601
17
+ Prices → `{ symbol, amount }`
18
+ Ratings → `{ score, total, reviews }`
19
+
20
+ - **Lazy HTML fetching**
21
+ Any result with a `url` exposes `.html()` to fetch the page HTML on demand.
22
+
23
+ - **Built-in pagination**
24
+ Just call `.next()` to fetch the next page.
25
+
26
+ - **Fast**
27
+ ~1 second response time per request with full parallelization support.
28
+
29
+ - **Full TypeScript support**
30
+ Type-specific inference out of the box.
8
31
 
9
32
  ## Install
10
33
 
@@ -12,152 +35,246 @@
12
35
  npm install @microlink/google
13
36
  ```
14
37
 
15
- ## Usage
38
+ ## Quick start
16
39
 
17
- Create a client by passing your configuration, then call it with a search query:
40
+ ### Your first query
41
+
42
+ Initialize `@microlink/google`. The only prerequisite is a [Microlink API key](https://microlink.io/#pricing):
18
43
 
19
44
  ```js
20
45
  const google = require('@microlink/google')({
21
- apiKey: 'YOUR_API_KEY',
46
+ apiKey: process.env.MICROLINK_API_KEY
22
47
  })
48
+ ```
23
49
 
50
+ Make your first query:
51
+
52
+ ```js
24
53
  const page = await google('Lotus Elise S2')
25
54
 
26
- for (const result of page.results) {
27
- console.log(result.title) // "Lotus Elise - Wikipedia"
28
- console.log(result.url) // "https://en.wikipedia.org/wiki/Lotus_Elise"
29
- console.log(result.description) // "The Lotus Elise is a two-seat, ..."
30
- }
55
+ console.log(page.results)
56
+ // [
57
+ // {
58
+ // title: 'Lotus Elise - Wikipedia',
59
+ // url: 'https://en.wikipedia.org/wiki/Lotus_Elise',
60
+ // description: 'The Lotus Elise is a two-seat, rear-wheel-drive...'
61
+ // }
62
+ // ]
31
63
  ```
32
64
 
33
- Each result has a lazy `.html()` method that fetches the full page HTML on demand:
65
+ Use [Google search operators](https://ahrefs.com/blog/google-advanced-search-operators/) to refine queries:
34
66
 
35
67
  ```js
36
- const html = await page.results[0].html()
68
+ const page = await google('Lotus Elise S2 filetype:pdf')
37
69
  ```
38
70
 
39
- The SERP page HTML is also available:
71
+ Localize results using `location` or filter by time with `period`:
40
72
 
41
73
  ```js
42
- const serpHtml = await page.html()
74
+ await google('recetas de pasta', {
75
+ location: 'es',
76
+ period: 'week'
77
+ })
78
+ ```
79
+
80
+ ### Get HTML markup
81
+
82
+ Any result containing a `url` exposes a lazy `.html()` method:
83
+
84
+ ```js
85
+ const { results } = await google('node.js frameworks')
86
+
87
+ for (const result of results) {
88
+ const html = await result.html()
89
+ console.log(html)
90
+ }
43
91
  ```
44
92
 
45
93
  ### Pagination
46
94
 
47
- Results are paginated. Call `.next()` to get the next page:
95
+ Pages chain naturally:
48
96
 
49
97
  ```js
50
- const firstPage = await google('node.js frameworks')
51
- const secondPage = await firstPage.next()
52
- const thirdPage = await secondPage.next()
98
+ const page1 = await google('node.js frameworks')
99
+ const page2 = await page1.next()
100
+ const page3 = await page2.next()
53
101
  ```
54
102
 
55
- The offset is calculated automatically from the number of results in each page.
103
+ You can also iterate:
56
104
 
57
- ## API
105
+ ```js
106
+ let page = await google('node.js frameworks')
58
107
 
59
- ### google(query, [options])
108
+ while (page) {
109
+ for (const result of page.results) {
110
+ console.log(result.title)
111
+ }
60
112
 
61
- #### query
113
+ page = await page.next()
114
+ }
115
+ ```
62
116
 
63
- *Required*<br>
64
- Type: `string`
117
+ # Google products
118
+
119
+ | Type | Product | Example |
120
+ | -------------- | ------------------- | ----------------------------------------------------------- |
121
+ | `search` | Google Search | `google('Lotus Elise S2')` |
122
+ | `news` | Google News | `google('artificial intelligence', { type: 'news' })` |
123
+ | `images` | Google Images | `google('northern lights', { type: 'images' })` |
124
+ | `videos` | Google Videos | `google('cooking tutorial', { type: 'videos' })` |
125
+ | `places` | Google Places | `google('coffee shops denver', { type: 'places' })` |
126
+ | `maps` | Google Maps | `google('apple store new york', { type: 'maps' })` |
127
+ | `shopping` | Google Shopping | `google('macbook pro', { type: 'shopping' })` |
128
+ | `scholar` | Google Scholar | `google('transformer architecture', { type: 'scholar' })` |
129
+ | `patents` | Google Patents | `google('touchscreen gestures apple', { type: 'patents' })` |
130
+ | `autocomplete` | Google Autocomplete | `google('how to', { type: 'autocomplete' })` |
131
+
132
+ ---
133
+
134
+ ## Google Search
65
135
 
66
- The search query. Spaces are supported naturally. You can use [Google search operators](https://support.google.com/websearch/answer/2466433) for advanced filtering:
136
+ Web results with knowledge graph, related questions, and related searches.
67
137
 
68
138
  ```js
69
- // Only PDF files
70
- const page = await google('annual report filetype:pdf')
139
+ const page = await google('Lotus Elise S2')
140
+
141
+ page.results[0]
71
142
 
72
- // Results from a specific site
73
- const page = await google('security updates site:github.com')
143
+ page.knowledgeGraph
144
+ page.peopleAlsoAsk
145
+ page.relatedSearches
146
+ ```
74
147
 
75
- // Exclude a site
76
- const page = await google('node.js tutorials -site:w3schools.com')
148
+ ## Google News
77
149
 
78
- // Exact phrase
79
- const page = await google('"machine learning" site:arxiv.org')
150
+ Recent articles with publisher, date, and thumbnail.
80
151
 
81
- // Combine operators
82
- const page = await google('site:reddit.com intitle:best "mechanical keyboard"')
152
+ ```js
153
+ const page = await google('artificial intelligence', { type: 'news' })
83
154
  ```
84
155
 
85
- #### options
156
+ ## Google Images
86
157
 
87
- ##### limit
158
+ Full-resolution image URLs with dimensions.
88
159
 
89
- Type: `number`
160
+ ```js
161
+ const page = await google('northern lights', { type: 'images' })
162
+ ```
90
163
 
91
- Maximum number of results to return per page.
164
+ ## Google Videos
92
165
 
93
- ##### lang
166
+ Video metadata with duration in milliseconds.
94
167
 
95
- Type: `string`
168
+ ```js
169
+ const page = await google('cooking tutorial', { type: 'videos' })
170
+ ```
171
+
172
+ ## Google Places
96
173
 
97
- Country perspective for ranking results ([ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) code, e.g. `'us'`, `'de'`, `'jp'`).
174
+ Local business listings with coordinates and contact info.
98
175
 
99
176
  ```js
100
- const page = await google('recetas de pasta', { lang: 'es' })
177
+ const page = await google('coffee shops denver', { type: 'places' })
101
178
  ```
102
179
 
103
- ##### device
180
+ ## Google Maps
104
181
 
105
- Type: `string`<br>
106
- Values: `'desktop'`, `'mobile'`
107
- Default: `'desktop'`
182
+ Detailed place data with ratings, hours, and pricing.
108
183
 
109
- Device type for SERP layout. Affects how Google renders and ranks results.
184
+ ```js
185
+ const page = await google('apple store new york', { type: 'maps' })
186
+ ```
187
+
188
+ ## Google Shopping
189
+
190
+ Product listings with parsed prices and structured ratings.
110
191
 
111
192
  ```js
112
- const page = await google('best restaurants', { device: 'mobile' })
193
+ const page = await google('macbook pro', { type: 'shopping' })
113
194
  ```
114
195
 
115
- ##### period
196
+ ## Google Scholar
116
197
 
117
- Type: `string`<br>
118
- Values: `'hour'`, `'day'`, `'week'`, `'month'`, `'year'`
198
+ Academic papers with citation counts and PDF links.
199
+
200
+ ```js
201
+ const page = await google('transformer architecture', { type: 'scholar' })
202
+ ```
203
+
204
+ ## Google Patents
119
205
 
120
- Time-based filter for results.
206
+ Patent filings with ISO 8601 dates and metadata.
121
207
 
122
208
  ```js
123
- const page = await google('tech news', { period: 'week' })
209
+ const page = await google('touchscreen gestures apple', { type: 'patents' })
124
210
  ```
125
211
 
126
- ##### domain
212
+ ## Google Autocomplete
213
+
214
+ Search suggestions as you type.
215
+
216
+ ```js
217
+ const page = await google('how to', { type: 'autocomplete' })
218
+ ```
219
+
220
+ # API
221
+
222
+ ## google(query, options?)
223
+
224
+ ### query
225
+
226
+ **Required**
227
+ Type: `string`
228
+
229
+ The search query. Supports [Google search operators](https://support.google.com/websearch/answer/2466433).
230
+
231
+ ```js
232
+ await google('annual report filetype:pdf')
233
+ await google('security updates site:github.com')
234
+ await google('"machine learning" site:arxiv.org')
235
+ ```
236
+
237
+ ### options
238
+
239
+ #### type
127
240
 
128
241
  Type: `string`<br>
129
- Default: `'google.com'`
242
+ Default: `'search'`<br>
243
+ Values: `'search'` | `'news'` | `'images'` | `'videos'` | `'places'` | `'maps'` | `'shopping'` | `'scholar'` | `'patents'` | `'autocomplete'`
130
244
 
131
- Regional Google domain to query (e.g. `'google.de'`, `'google.co.uk'`, `'google.com.tr'`).
245
+ Selects which Google product to query.
132
246
 
133
247
  ```js
134
- const page = await google('restaurants', { domain: 'google.co.uk' })
248
+ await google('artificial intelligence', { type: 'news' })
135
249
  ```
136
250
 
137
- ### Page object
251
+ #### location
138
252
 
139
- The return value from a search call is a page object with the following properties:
253
+ Type: `string`<br>
254
+ Default: `'us'`<br>
255
+ Values: [`Location`](https://github.com/microlinkhq/google/blob/master/src/index.d.ts#L28)
256
+
257
+ Controls result geolocation using a country code ([ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)). This influences ranking, language, and local intent.
258
+
259
+ ```js
260
+ await google('recetas de pasta', { location: 'es' })
261
+ ```
140
262
 
141
- | Property | Type | Description |
142
- |---|---|---|
143
- | `results` | `Array<Result>` | Array of search results |
144
- | `html()` | `() => Promise<string>` | Returns the raw SERP HTML |
145
- | `next()` | `() => Promise<Page>` | Fetches the next page of results |
263
+ #### period
146
264
 
147
- ### Result object
265
+ Type: `string`<br>
266
+ Default: `undefined`<br>
267
+ Values: `hour` | `day` | `week` | `month` | `year`
148
268
 
149
- Each result in the `results` array has:
269
+ Limits results to a recent time window. Useful for news monitoring and freshness-sensitive queries.
150
270
 
151
- | Property | Type | Description |
152
- |---|---|---|
153
- | `title` | `string` | Page title |
154
- | `url` | `string` | Direct URL to the page |
155
- | `description` | `string` | Result snippet text |
156
- | `html()` | `() => Promise<string>` | Fetches the full HTML of this result's URL via Microlink |
271
+ ```js
272
+ await google('tech news', { period: 'week' })
273
+ ```
157
274
 
158
275
  ## License
159
276
 
160
- **@microlink/google** © [microlink.io](https://microlink.io), released under the [MIT](https://github.com/microlinkhq/microlink-google/blob/master/LICENSE.md) License.<br>
161
- Authored and maintained by [microlink.io](https://microlink.io) with help from [contributors](https://github.com/microlinkhq/microlink-google/contributors).
277
+ **@microlink/google** © [Microlink](https://microlink.io), released under the [MIT](https://github.com/microlinkhq/google/blob/master/LICENSE.md) License.<br>
278
+ Authored and maintained by [Kiko Beats](https://kikobeats.com) with help from [contributors](https://github.com/microlinkhq/google/contributors).
162
279
 
163
- > [microlink.io](https://microlink.io) · GitHub [microlink.io](https://github.com/microlinkhq) · Twitter [@microlinkhq](https://twitter.com/microlinkhq)
280
+ > [microlink.io](https://microlink.io) · GitHub [microlinkhq](https://github.com/microlinkhq) · X [@microlinkhq](https://x.com/microlinkhq)
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@microlink/google",
3
- "description": "Google search integration for Microlink",
3
+ "description": "Structured Google data from 10 verticals through Microlink API",
4
4
  "homepage": "https://github.com/microlinkhq/microlink-google",
5
- "version": "0.0.0",
5
+ "version": "0.1.0",
6
6
  "exports": {
7
- ".": "./src/index.js"
7
+ "types": "./src/index.d.ts",
8
+ "require": "./src/index.js",
9
+ "import": "./src/main.mjs",
10
+ "default": "./src/main.mjs"
8
11
  },
9
12
  "author": {
10
13
  "email": "hello@microlink.io",
@@ -42,7 +45,9 @@
42
45
  "simple-git-hooks": "latest",
43
46
  "standard": "latest",
44
47
  "standard-markdown": "latest",
45
- "standard-version": "latest"
48
+ "standard-version": "latest",
49
+ "proxyquire": "latest",
50
+ "tinyspawn": "latest"
46
51
  },
47
52
  "engines": {
48
53
  "node": ">= 24"
package/src/index.d.ts ADDED
@@ -0,0 +1,308 @@
1
+ type Period = 'hour' | 'day' | 'week' | 'month' | 'year'
2
+
3
+ type Type =
4
+ | 'search'
5
+ | 'news'
6
+ | 'images'
7
+ | 'videos'
8
+ | 'places'
9
+ | 'maps'
10
+ | 'shopping'
11
+ | 'scholar'
12
+ | 'patents'
13
+ | 'autocomplete'
14
+
15
+ interface TypeToPage {
16
+ search: SearchPage
17
+ news: NewsPage
18
+ images: ImagesPage
19
+ videos: VideosPage
20
+ places: PlacesPage
21
+ maps: MapsPage
22
+ shopping: ShoppingPage
23
+ scholar: ScholarPage
24
+ patents: PatentsPage
25
+ autocomplete: AutocompletePage
26
+ }
27
+
28
+ type Location =
29
+ | 'af' | 'al' | 'dz' | 'as' | 'ad' | 'ao' | 'ai' | 'aq' | 'ag' | 'ar'
30
+ | 'am' | 'aw' | 'au' | 'at' | 'az' | 'bs' | 'bh' | 'bd' | 'bb' | 'by'
31
+ | 'be' | 'bz' | 'bj' | 'bm' | 'bt' | 'bo' | 'ba' | 'bw' | 'bv' | 'br'
32
+ | 'io' | 'bn' | 'bg' | 'bf' | 'bi' | 'kh' | 'cm' | 'ca' | 'cv' | 'ky'
33
+ | 'cf' | 'td' | 'cl' | 'cn' | 'cx' | 'cc' | 'co' | 'km' | 'cg' | 'cd'
34
+ | 'ck' | 'cr' | 'ci' | 'hr' | 'cu' | 'cy' | 'cz' | 'dk' | 'dj' | 'dm'
35
+ | 'do' | 'ec' | 'eg' | 'sv' | 'gq' | 'er' | 'ee' | 'et' | 'fk' | 'fo'
36
+ | 'fj' | 'fi' | 'fr' | 'gf' | 'pf' | 'tf' | 'ga' | 'gm' | 'ge' | 'de'
37
+ | 'gh' | 'gi' | 'gr' | 'gl' | 'gd' | 'gp' | 'gu' | 'gt' | 'gn' | 'gw'
38
+ | 'gy' | 'ht' | 'hm' | 'va' | 'hn' | 'hk' | 'hu' | 'is' | 'in' | 'id'
39
+ | 'ir' | 'iq' | 'ie' | 'il' | 'it' | 'jm' | 'jp' | 'jo' | 'kz' | 'ke'
40
+ | 'ki' | 'kp' | 'kr' | 'kw' | 'kg' | 'la' | 'lv' | 'lb' | 'ls' | 'lr'
41
+ | 'ly' | 'li' | 'lt' | 'lu' | 'mo' | 'mk' | 'mg' | 'mw' | 'my' | 'mv'
42
+ | 'ml' | 'mt' | 'mh' | 'mq' | 'mr' | 'mu' | 'yt' | 'mx' | 'fm' | 'md'
43
+ | 'mc' | 'mn' | 'ms' | 'ma' | 'mz' | 'mm' | 'na' | 'nr' | 'np' | 'nl'
44
+ | 'an' | 'nc' | 'nz' | 'ni' | 'ne' | 'ng' | 'nu' | 'nf' | 'mp' | 'no'
45
+ | 'om' | 'pk' | 'pw' | 'ps' | 'pa' | 'pg' | 'py' | 'pe' | 'ph' | 'pn'
46
+ | 'pl' | 'pt' | 'pr' | 'qa' | 're' | 'ro' | 'ru' | 'rw' | 'sh' | 'kn'
47
+ | 'lc' | 'pm' | 'vc' | 'ws' | 'sm' | 'st' | 'sa' | 'sn' | 'rs' | 'sc'
48
+ | 'sl' | 'sg' | 'sk' | 'si' | 'sb' | 'so' | 'za' | 'gs' | 'es' | 'lk'
49
+ | 'sd' | 'sr' | 'sj' | 'sz' | 'se' | 'ch' | 'sy' | 'tw' | 'tj' | 'tz'
50
+ | 'th' | 'tl' | 'tg' | 'tk' | 'to' | 'tt' | 'tn' | 'tr' | 'tm' | 'tc'
51
+ | 'tv' | 'ug' | 'ua' | 'ae' | 'gb' | 'us' | 'um' | 'uy' | 'uz' | 'vu'
52
+ | 've' | 'vn' | 'vg' | 'vi' | 'wf' | 'eh' | 'ye' | 'zm' | 'zw'
53
+
54
+ interface Options {
55
+ type?: Type
56
+ limit?: number
57
+ location?: Location
58
+ period?: Period
59
+ }
60
+
61
+ interface Sitelink {
62
+ title: string
63
+ link: string
64
+ }
65
+
66
+ interface KnowledgeGraph {
67
+ title?: string
68
+ type?: string
69
+ website?: string
70
+ image?: { url: string }
71
+ description?: string
72
+ descriptionSource?: string
73
+ descriptionLink?: string
74
+ attributes?: Record<string, string>
75
+ }
76
+
77
+ interface PeopleAlsoAsk {
78
+ question: string
79
+ snippet: string
80
+ title: string
81
+ link: string
82
+ }
83
+
84
+ interface RelatedSearch {
85
+ query: string
86
+ }
87
+
88
+ interface SearchResult {
89
+ title: string
90
+ url: string
91
+ description: string
92
+ html(): Promise<string>
93
+ }
94
+
95
+ interface SearchPage {
96
+ results: SearchResult[]
97
+ knowledgeGraph?: KnowledgeGraph
98
+ peopleAlsoAsk?: PeopleAlsoAsk[]
99
+ relatedSearches?: RelatedSearch[]
100
+ html(): Promise<string>
101
+ next(): Promise<SearchPage>
102
+ }
103
+
104
+ interface NewsResult {
105
+ title: string
106
+ url: string
107
+ description: string
108
+ date: string
109
+ publisher: string
110
+ image?: { url: string }
111
+ html(): Promise<string>
112
+ }
113
+
114
+ interface NewsPage {
115
+ results: NewsResult[]
116
+ html(): Promise<string>
117
+ next(): Promise<NewsPage>
118
+ }
119
+
120
+ interface ImageResult {
121
+ title: string
122
+ url: string
123
+ image: { url: string; width: number; height: number }
124
+ thumbnail: { url: string; width: number; height: number }
125
+ google?: { url: string }
126
+ creator?: string
127
+ credit?: string
128
+ html(): Promise<string>
129
+ }
130
+
131
+ interface ImagesPage {
132
+ results: ImageResult[]
133
+ html(): Promise<string>
134
+ next(): Promise<ImagesPage>
135
+ }
136
+
137
+ interface VideoResult {
138
+ title: string
139
+ url: string
140
+ description: string
141
+ image?: { url: string }
142
+ video?: { url: string }
143
+ duration?: number
144
+ duration_pretty?: string
145
+ publisher?: string
146
+ channel?: string
147
+ date?: string
148
+ html(): Promise<string>
149
+ }
150
+
151
+ interface VideosPage {
152
+ results: VideoResult[]
153
+ html(): Promise<string>
154
+ next(): Promise<VideosPage>
155
+ }
156
+
157
+ interface PlaceResult {
158
+ title: string
159
+ address: string
160
+ latitude: number
161
+ longitude: number
162
+ phone?: { number: string }
163
+ url?: string
164
+ cid: string
165
+ html(): Promise<string>
166
+ }
167
+
168
+ interface PlacesPage {
169
+ results: PlaceResult[]
170
+ html(): Promise<string>
171
+ next(): Promise<PlacesPage>
172
+ }
173
+
174
+ interface MapPlaceResult {
175
+ title: string
176
+ address: string
177
+ latitude: number
178
+ longitude: number
179
+ rating?: number
180
+ ratingCount?: number
181
+ price?: { level: string }
182
+ type?: string
183
+ types?: string[]
184
+ url?: string
185
+ phone?: { number: string }
186
+ description?: string
187
+ opening?: { hours: Record<string, string> }
188
+ thumbnail?: { url: string }
189
+ cid: string
190
+ fid?: string
191
+ place?: { id: string }
192
+ html(): Promise<string>
193
+ }
194
+
195
+ interface MapsPage {
196
+ results: MapPlaceResult[]
197
+ html(): Promise<string>
198
+ next(): Promise<MapsPage>
199
+ }
200
+
201
+ interface ShoppingResult {
202
+ title: string
203
+ url: string
204
+ publisher: string
205
+ price: { symbol: string; amount: number }
206
+ image?: { url: string }
207
+ rating?: { score: number; total: number; reviews?: number }
208
+ id?: string
209
+ html(): Promise<string>
210
+ }
211
+
212
+ interface ShoppingPage {
213
+ results: ShoppingResult[]
214
+ html(): Promise<string>
215
+ next(): Promise<ShoppingPage>
216
+ }
217
+
218
+ interface ScholarResult {
219
+ title: string
220
+ url: string
221
+ description: string
222
+ publisher: string
223
+ year: number
224
+ citations: number
225
+ pdf?: { url: string }
226
+ id: string
227
+ html(): Promise<string>
228
+ }
229
+
230
+ interface ScholarPage {
231
+ results: ScholarResult[]
232
+ html(): Promise<string>
233
+ next(): Promise<ScholarPage>
234
+ }
235
+
236
+ interface PatentResult {
237
+ title: string
238
+ description: string
239
+ url: string
240
+ priority: { date: string }
241
+ filing: { date: string }
242
+ grant?: { date: string }
243
+ publication: { date: string; number: string }
244
+ inventor: string
245
+ assignee: string
246
+ language: string
247
+ pdf?: { url: string }
248
+ thumbnail?: { url: string }
249
+ figures?: Array<{ image: { url: string }; thumbnail: { url: string } }>
250
+ id?: string
251
+ html(): Promise<string>
252
+ }
253
+
254
+ interface PatentsPage {
255
+ results: PatentResult[]
256
+ html(): Promise<string>
257
+ next(): Promise<PatentsPage>
258
+ }
259
+
260
+ interface SuggestionResult {
261
+ value: string
262
+ }
263
+
264
+ interface AutocompletePage {
265
+ results: SuggestionResult[]
266
+ html(): Promise<string>
267
+ next(): Promise<AutocompletePage>
268
+ }
269
+
270
+ interface GoogleClient {
271
+ <K extends Type>(
272
+ query: string,
273
+ options: Omit<Options, 'type'> & { type: K }
274
+ ): Promise<TypeToPage[K]>
275
+ (query: string, options?: Options): Promise<SearchPage>
276
+ }
277
+
278
+ interface ContextOptions {
279
+ apiKey?: string
280
+ endpoint?: string
281
+ timeout?: number
282
+ }
283
+
284
+ export declare const DOMAIN: string
285
+
286
+ export declare function buildPath(
287
+ query: string,
288
+ limit?: number,
289
+ location?: string
290
+ ): string
291
+
292
+ export declare function buildUrl(
293
+ query: string,
294
+ options?: {
295
+ limit?: number
296
+ location?: string
297
+ type?: Type
298
+ period?: Period
299
+ }
300
+ ): URL
301
+
302
+ interface createGoogleClient {
303
+ (options?: ContextOptions): GoogleClient
304
+ }
305
+
306
+ declare const createGoogleClient: createGoogleClient
307
+
308
+ export default createGoogleClient
package/src/index.js CHANGED
@@ -4,51 +4,70 @@ const mql = require('@microlink/mql')
4
4
 
5
5
  const DOMAIN = 'microlink.google'
6
6
 
7
- const buildPath = (query, limit, lang) =>
8
- [encodeURIComponent(query).replace(/%20/g, '+'), limit, lang]
7
+ const buildPath = (query, limit, location) =>
8
+ [encodeURIComponent(query).replace(/%20/g, '+'), limit, location]
9
9
  .filter(v => v !== undefined)
10
10
  .join('/')
11
11
 
12
- const buildUrl = (query, { limit, lang, device, period, domain } = {}) => {
13
- const url = new URL(`https://${DOMAIN}/${buildPath(query, limit, lang)}`)
14
- if (device) url.searchParams.set('device', device)
12
+ const buildUrl = (query, { limit, location, type, period } = {}) => {
13
+ const url = new URL(`https://${DOMAIN}/${buildPath(query, limit, location)}`)
14
+ if (type) url.searchParams.set('type', type)
15
15
  if (period) url.searchParams.set('period', period)
16
- if (domain) url.searchParams.set('domain', domain)
17
16
  return url
18
17
  }
19
18
 
20
- const fetchPage = async (url, mqlOpts, offset) => {
19
+ const resultUrl = result => result.url
20
+
21
+ const fetchPage = async (url, mqlOpts, offset, query) => {
21
22
  url.searchParams.set('start', String(offset))
22
23
  const { data } = await mql(url.toString(), mqlOpts)
23
- const { results, html } = data
24
+ const { results, ...extra } = data
24
25
 
25
26
  return {
26
- html: () => Promise.resolve(html),
27
- results: results.map(result => ({
28
- ...result,
29
- html: async () => {
30
- const { data } = await mql(result.url, {
27
+ ...extra,
28
+ html: async () => {
29
+ const { data } = await mql(
30
+ `https://www.google.com/search?q=${encodeURIComponent(query)}`,
31
+ {
31
32
  ...mqlOpts,
32
33
  data: { content: { attr: 'html' } }
34
+ }
35
+ )
36
+ return data.content
37
+ },
38
+ results: results.map(result => {
39
+ const url = resultUrl(result)
40
+ return {
41
+ ...result,
42
+ ...(url && {
43
+ html: async () => {
44
+ const { data } = await mql(url, {
45
+ ...mqlOpts,
46
+ data: { content: { attr: 'html' } }
47
+ })
48
+ return data.content
49
+ }
33
50
  })
34
- return data.content
35
51
  }
36
- })),
52
+ }),
37
53
  next: () =>
38
- fetchPage(new URL(url.toString()), mqlOpts, offset + results.length)
54
+ fetchPage(
55
+ new URL(url.toString()),
56
+ mqlOpts,
57
+ offset + results.length,
58
+ query
59
+ )
39
60
  }
40
61
  }
41
62
 
42
- module.exports = ctxOpts => {
43
- return async (
44
- query,
45
- { limit, lang, device, period, domain, ...opts } = {}
46
- ) => {
47
- const url = buildUrl(query, { limit, lang, device, period, domain })
48
- return fetchPage(url, { ...ctxOpts, ...opts }, 0)
63
+ const createGoogleClient = ctxOpts => {
64
+ return async (query, { limit, location, type, period, ...opts } = {}) => {
65
+ const url = buildUrl(query, { limit, location, type, period })
66
+ return fetchPage(url, { ...ctxOpts, ...opts }, 0, query)
49
67
  }
50
68
  }
51
69
 
70
+ module.exports = createGoogleClient
52
71
  module.exports.buildPath = buildPath
53
72
  module.exports.buildUrl = buildUrl
54
73
  module.exports.DOMAIN = DOMAIN
package/src/main.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import createGoogleClient from './index.js'
2
+
3
+ export const buildPath = createGoogleClient.buildPath
4
+ export const buildUrl = createGoogleClient.buildUrl
5
+ export const DOMAIN = createGoogleClient.DOMAIN
6
+
7
+ export default createGoogleClient