microlink.io 0.0.0 → 0.0.1
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/LICENSE.md +21 -0
- package/bin/help.txt +47 -0
- package/bin/index.js +81 -0
- package/package.json +79 -10
- package/src/index.d.ts +178 -0
- package/src/index.js +195 -0
- package/src/main.mjs +5 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright © 2026 microlink.io <hello@microlink.io> (microlink.io)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/bin/help.txt
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Usage
|
|
2
|
+
$ microlink <product> <url|query> [options]
|
|
3
|
+
|
|
4
|
+
Products
|
|
5
|
+
metadata Unified metadata (title, description, image, ...)
|
|
6
|
+
logo Brand logo of the site (--square prefers the square variant)
|
|
7
|
+
markdown Page content as Markdown
|
|
8
|
+
html Page content as HTML
|
|
9
|
+
text Page content as plain text
|
|
10
|
+
video Primary video of the page (returns the asset object)
|
|
11
|
+
audio Primary audio of the page (returns the asset object)
|
|
12
|
+
emails Every email address present on the page
|
|
13
|
+
links Every absolute link URL on the page
|
|
14
|
+
images Every absolute image URL on the page
|
|
15
|
+
videos Every absolute video URL on the page
|
|
16
|
+
audios Every absolute audio URL on the page
|
|
17
|
+
extract Custom MQL data rules (--data '{"field":{...}}')
|
|
18
|
+
screenshot Take a screenshot (returns the asset object)
|
|
19
|
+
pdf Generate a PDF (returns the asset object)
|
|
20
|
+
embed oEmbed-style embeddable iframe ({ html, scripts })
|
|
21
|
+
technologies Detect the tech stack behind the site
|
|
22
|
+
lighthouse Run a Lighthouse report
|
|
23
|
+
search Google as structured data (query instead of url); --type
|
|
24
|
+
routes to news, images, videos, places, maps, shopping,
|
|
25
|
+
scholar, patents or autocomplete
|
|
26
|
+
function Run code remotely with browser access (--file ./fn.js);
|
|
27
|
+
extra flags are injected as variables in the function scope
|
|
28
|
+
|
|
29
|
+
Options
|
|
30
|
+
--api-key Microlink API key (defaults to MICROLINK_API_KEY env)
|
|
31
|
+
--header, -H Extra request header as 'Name: value' (repeatable)
|
|
32
|
+
--data JSON data rules for the extract command
|
|
33
|
+
--file Path to the code file for the function command
|
|
34
|
+
--help Show this help
|
|
35
|
+
|
|
36
|
+
Any other flag is passed as an option to the product, e.g. --fullPage,
|
|
37
|
+
--device 'iPhone 11', --waitUntil networkidle0, --selector article.
|
|
38
|
+
|
|
39
|
+
Examples
|
|
40
|
+
$ microlink markdown https://example.com
|
|
41
|
+
$ microlink screenshot https://example.com --fullPage
|
|
42
|
+
$ microlink logo https://github.com --square
|
|
43
|
+
$ microlink links https://example.com
|
|
44
|
+
$ microlink search "best coffee" --limit 10 --location es
|
|
45
|
+
$ microlink search "open source llm" --type news --period week
|
|
46
|
+
$ microlink extract https://microlink.io --data '{"image":{"selector":"meta[property=og:image]","attr":"content","type":"image"}}'
|
|
47
|
+
$ microlink function https://example.com --file ./fn.js
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
|
|
4
|
+
const { readFileSync } = require('fs')
|
|
5
|
+
const path = require('path')
|
|
6
|
+
const jsome = require('jsome')
|
|
7
|
+
const mri = require('mri')
|
|
8
|
+
|
|
9
|
+
const create = require('../src')
|
|
10
|
+
|
|
11
|
+
const showHelp = () => {
|
|
12
|
+
console.log(readFileSync(path.join(__dirname, 'help.txt'), 'utf8'))
|
|
13
|
+
process.exit(0)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const parseHeaders = input => {
|
|
17
|
+
const headers = {}
|
|
18
|
+
for (const item of [].concat(input ?? [])) {
|
|
19
|
+
const index = String(item).indexOf(':')
|
|
20
|
+
if (index === -1) continue
|
|
21
|
+
headers[String(item).slice(0, index).trim().toLowerCase()] = String(item)
|
|
22
|
+
.slice(index + 1)
|
|
23
|
+
.trim()
|
|
24
|
+
}
|
|
25
|
+
return headers
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const argv = mri(process.argv.slice(2), {
|
|
29
|
+
alias: { H: 'header' },
|
|
30
|
+
string: ['header', 'api-key', 'data', 'file']
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const {
|
|
34
|
+
_: [command, target],
|
|
35
|
+
header,
|
|
36
|
+
help,
|
|
37
|
+
data,
|
|
38
|
+
file,
|
|
39
|
+
'api-key': apiKeyFlag,
|
|
40
|
+
apiKey: apiKeyCamel,
|
|
41
|
+
...flags
|
|
42
|
+
} = argv
|
|
43
|
+
|
|
44
|
+
if (help || !command) showHelp()
|
|
45
|
+
|
|
46
|
+
const apiKey = apiKeyFlag || apiKeyCamel || process.env.MICROLINK_API_KEY
|
|
47
|
+
const client = create(apiKey ? { apiKey } : {})
|
|
48
|
+
|
|
49
|
+
if (typeof client[command] !== 'function') {
|
|
50
|
+
console.error(
|
|
51
|
+
`Unknown command \`${command}\`. Run \`microlink --help\` to see the available commands.`
|
|
52
|
+
)
|
|
53
|
+
process.exit(1)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const options = { ...flags }
|
|
57
|
+
const headers = parseHeaders(header)
|
|
58
|
+
if (Object.keys(headers).length > 0) options.headers = headers
|
|
59
|
+
|
|
60
|
+
const invoke = () => {
|
|
61
|
+
if (command === 'extract') {
|
|
62
|
+
return client.extract(target, JSON.parse(data), options)
|
|
63
|
+
}
|
|
64
|
+
if (command === 'function' || command === 'run') {
|
|
65
|
+
const code = readFileSync(path.resolve(file), 'utf8')
|
|
66
|
+
return client.function(target, code, options)
|
|
67
|
+
}
|
|
68
|
+
return client[command](target, options)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Promise.resolve()
|
|
72
|
+
.then(invoke)
|
|
73
|
+
.then(result => {
|
|
74
|
+
if (typeof result === 'string') console.log(result)
|
|
75
|
+
else jsome(result)
|
|
76
|
+
process.exit(0)
|
|
77
|
+
})
|
|
78
|
+
.catch(error => {
|
|
79
|
+
console.error(error.message)
|
|
80
|
+
process.exit(1)
|
|
81
|
+
})
|
package/package.json
CHANGED
|
@@ -1,13 +1,82 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "microlink.io",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
3
|
+
"description": "The Microlink API organized into products, each returning a direct result",
|
|
4
|
+
"homepage": "https://github.com/microlinkhq/microlink",
|
|
5
|
+
"version": "0.0.1",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"main": "./src/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./src/index.d.ts",
|
|
11
|
+
"require": "./src/index.js",
|
|
12
|
+
"import": "./src/main.mjs",
|
|
13
|
+
"default": "./src/main.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"bin": {
|
|
17
|
+
"microlink": "bin/index.js"
|
|
18
|
+
},
|
|
19
|
+
"author": {
|
|
20
|
+
"email": "hello@microlink.io",
|
|
21
|
+
"name": "microlink.io",
|
|
22
|
+
"url": "https://microlink.io"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"directory": "packages/core",
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "git+https://github.com/microlinkhq/microlink.git"
|
|
28
|
+
},
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/microlinkhq/microlink/issues"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"api",
|
|
34
|
+
"extract",
|
|
35
|
+
"lighthouse",
|
|
36
|
+
"markdown",
|
|
37
|
+
"metadata",
|
|
38
|
+
"microlink",
|
|
39
|
+
"mql",
|
|
40
|
+
"pdf",
|
|
41
|
+
"scraper",
|
|
42
|
+
"screenshot",
|
|
43
|
+
"seo"
|
|
44
|
+
],
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@microlink/function": "0.2.6",
|
|
47
|
+
"@microlink/google": "1.0.4",
|
|
48
|
+
"@microlink/mql": "0.17.0",
|
|
49
|
+
"jsome": "~2.5.0",
|
|
50
|
+
"mri": "~1.2.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"ava": "latest",
|
|
54
|
+
"proxyquire": "latest",
|
|
55
|
+
"tinyspawn": "latest",
|
|
56
|
+
"tsd": "latest"
|
|
57
|
+
},
|
|
58
|
+
"engines": {
|
|
59
|
+
"node": ">= 24"
|
|
60
|
+
},
|
|
61
|
+
"files": [
|
|
62
|
+
"bin",
|
|
63
|
+
"src"
|
|
64
|
+
],
|
|
10
65
|
"scripts": {
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
}
|
|
66
|
+
"lint": "tsd",
|
|
67
|
+
"test": "ava"
|
|
68
|
+
},
|
|
69
|
+
"preferGlobal": true,
|
|
70
|
+
"license": "MIT",
|
|
71
|
+
"ava": {
|
|
72
|
+
"timeout": "2m"
|
|
73
|
+
},
|
|
74
|
+
"tsd": {
|
|
75
|
+
"compilerOptions": {
|
|
76
|
+
"module": "node16",
|
|
77
|
+
"moduleResolution": "node16"
|
|
78
|
+
},
|
|
79
|
+
"directory": "test"
|
|
80
|
+
},
|
|
81
|
+
"gitHead": "b28aa7d6434ac1bb43b084d7e329efec97dab36a"
|
|
82
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import createGoogleClient from '@microlink/google'
|
|
2
|
+
|
|
3
|
+
type GoogleClient = ReturnType<typeof createGoogleClient>
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Transport & top-level API query params. Unknown keys fall through
|
|
7
|
+
* to the API query string, so an index signature is provided.
|
|
8
|
+
*/
|
|
9
|
+
interface Options {
|
|
10
|
+
apiKey?: string
|
|
11
|
+
endpoint?: string
|
|
12
|
+
headers?: Record<string, string>
|
|
13
|
+
adblock?: boolean
|
|
14
|
+
animations?: boolean
|
|
15
|
+
audio?: boolean
|
|
16
|
+
cacheKey?: string
|
|
17
|
+
click?: string | string[]
|
|
18
|
+
colorScheme?: 'light' | 'dark' | 'no-preference'
|
|
19
|
+
device?: string
|
|
20
|
+
filename?: string
|
|
21
|
+
force?: boolean
|
|
22
|
+
javascript?: boolean
|
|
23
|
+
meta?: boolean
|
|
24
|
+
modules?: string | string[]
|
|
25
|
+
prerender?: boolean | 'auto'
|
|
26
|
+
proxy?: string
|
|
27
|
+
retry?: number
|
|
28
|
+
scripts?: string | string[]
|
|
29
|
+
staleTtl?: string | number
|
|
30
|
+
styles?: string | string[]
|
|
31
|
+
timeout?: number
|
|
32
|
+
ttl?: string | number
|
|
33
|
+
video?: boolean
|
|
34
|
+
viewport?: Record<string, unknown>
|
|
35
|
+
waitForTimeout?: number
|
|
36
|
+
waitUntil?: string | string[]
|
|
37
|
+
[key: string]: unknown
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ContentOptions extends Options {
|
|
41
|
+
selector?: string
|
|
42
|
+
selectorAll?: string | string[]
|
|
43
|
+
type?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface CollectionOptions extends Options {
|
|
47
|
+
selector?: string
|
|
48
|
+
selectorAll?: string | string[]
|
|
49
|
+
attr?: string
|
|
50
|
+
type?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ScreenshotOptions extends Options {
|
|
54
|
+
fullPage?: boolean
|
|
55
|
+
type?: 'jpeg' | 'png'
|
|
56
|
+
overlay?: Record<string, unknown>
|
|
57
|
+
element?: string
|
|
58
|
+
omitBackground?: boolean
|
|
59
|
+
optimizeForSpeed?: boolean
|
|
60
|
+
codeScheme?: string
|
|
61
|
+
animated?: boolean
|
|
62
|
+
palette?: boolean
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface PdfOptions extends Options {
|
|
66
|
+
format?: string
|
|
67
|
+
margin?: string | Record<string, string | number>
|
|
68
|
+
scale?: number
|
|
69
|
+
landscape?: boolean
|
|
70
|
+
pageRanges?: string
|
|
71
|
+
width?: string | number
|
|
72
|
+
height?: string | number
|
|
73
|
+
printBackground?: boolean
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface EmbedOptions extends Options {
|
|
77
|
+
maxWidth?: number
|
|
78
|
+
maxHeight?: number
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface LogoOptions extends Options {
|
|
82
|
+
square?: boolean
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface LighthouseOptions extends Options {
|
|
86
|
+
onlyCategories?: string[]
|
|
87
|
+
onlyAudits?: string[]
|
|
88
|
+
skipAudits?: string[]
|
|
89
|
+
output?: string | string[]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Rich asset object (screenshot, pdf, logo, image rules, ...). */
|
|
93
|
+
interface Asset {
|
|
94
|
+
url: string
|
|
95
|
+
type?: string
|
|
96
|
+
size?: number
|
|
97
|
+
size_pretty?: string
|
|
98
|
+
width?: number
|
|
99
|
+
height?: number
|
|
100
|
+
[key: string]: unknown
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface Metadata {
|
|
104
|
+
title?: string | null
|
|
105
|
+
description?: string | null
|
|
106
|
+
url?: string | null
|
|
107
|
+
[key: string]: unknown
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface Embed {
|
|
111
|
+
html: string
|
|
112
|
+
scripts?: unknown[]
|
|
113
|
+
[key: string]: unknown
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
type FunctionInput = string | ((args: Record<string, unknown>) => unknown)
|
|
117
|
+
|
|
118
|
+
interface FunctionResult<T = unknown> {
|
|
119
|
+
isFulfilled: boolean
|
|
120
|
+
value: T
|
|
121
|
+
profiling: Record<string, unknown>
|
|
122
|
+
logging: Record<string, unknown>
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export declare class MicrolinkError extends Error {
|
|
126
|
+
status?: string
|
|
127
|
+
code?: string
|
|
128
|
+
statusCode?: number
|
|
129
|
+
description?: string
|
|
130
|
+
url?: string
|
|
131
|
+
headers?: Record<string, string>
|
|
132
|
+
more?: string
|
|
133
|
+
constructor (props: Record<string, unknown>)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface MicrolinkClient {
|
|
137
|
+
metadata (url: string, options?: Options): Promise<Metadata>
|
|
138
|
+
logo (url: string, options?: LogoOptions): Promise<Asset>
|
|
139
|
+
markdown (url: string, options?: ContentOptions): Promise<string>
|
|
140
|
+
html (url: string, options?: ContentOptions): Promise<string>
|
|
141
|
+
text (url: string, options?: ContentOptions): Promise<string>
|
|
142
|
+
video (url: string, options?: Options): Promise<Asset>
|
|
143
|
+
audio (url: string, options?: Options): Promise<Asset>
|
|
144
|
+
emails (url: string, options?: Options): Promise<string[]>
|
|
145
|
+
links (url: string, options?: CollectionOptions): Promise<string[]>
|
|
146
|
+
images (url: string, options?: CollectionOptions): Promise<string[]>
|
|
147
|
+
videos (url: string, options?: CollectionOptions): Promise<string[]>
|
|
148
|
+
audios (url: string, options?: CollectionOptions): Promise<string[]>
|
|
149
|
+
extract (
|
|
150
|
+
url: string,
|
|
151
|
+
rules: Record<string, unknown>,
|
|
152
|
+
options?: Options
|
|
153
|
+
): Promise<Record<string, unknown>>
|
|
154
|
+
screenshot (url: string, options?: ScreenshotOptions): Promise<Asset>
|
|
155
|
+
pdf (url: string, options?: PdfOptions): Promise<Asset>
|
|
156
|
+
embed (url: string, options?: EmbedOptions): Promise<Embed>
|
|
157
|
+
technologies (url: string, options?: Options): Promise<unknown[]>
|
|
158
|
+
lighthouse (
|
|
159
|
+
url: string,
|
|
160
|
+
options?: LighthouseOptions
|
|
161
|
+
): Promise<Record<string, unknown>>
|
|
162
|
+
search: GoogleClient
|
|
163
|
+
function<T = unknown> (
|
|
164
|
+
url: string,
|
|
165
|
+
code: FunctionInput,
|
|
166
|
+
options?: Options
|
|
167
|
+
): Promise<FunctionResult<T>>
|
|
168
|
+
run: MicrolinkClient['function']
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
interface create {
|
|
172
|
+
(options?: Options): MicrolinkClient
|
|
173
|
+
MicrolinkError: typeof MicrolinkError
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
declare const create: create
|
|
177
|
+
|
|
178
|
+
export default create
|
package/src/index.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const createGoogleClient = require('@microlink/google')
|
|
4
|
+
const fn = require('@microlink/function')
|
|
5
|
+
const mql = require('@microlink/mql')
|
|
6
|
+
|
|
7
|
+
const CONTENT_KEYS = ['selector', 'selectorAll', 'type']
|
|
8
|
+
|
|
9
|
+
const COLLECTION_KEYS = ['selector', 'selectorAll', 'attr', 'type']
|
|
10
|
+
|
|
11
|
+
const SCREENSHOT_KEYS = [
|
|
12
|
+
'fullPage',
|
|
13
|
+
'type',
|
|
14
|
+
'overlay',
|
|
15
|
+
'element',
|
|
16
|
+
'omitBackground',
|
|
17
|
+
'optimizeForSpeed',
|
|
18
|
+
'codeScheme',
|
|
19
|
+
'animated',
|
|
20
|
+
'palette'
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
const PDF_KEYS = [
|
|
24
|
+
'format',
|
|
25
|
+
'margin',
|
|
26
|
+
'scale',
|
|
27
|
+
'landscape',
|
|
28
|
+
'pageRanges',
|
|
29
|
+
'width',
|
|
30
|
+
'height',
|
|
31
|
+
'printBackground'
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
const EMBED_KEYS = ['maxWidth', 'maxHeight']
|
|
35
|
+
|
|
36
|
+
const LOGO_KEYS = ['square']
|
|
37
|
+
|
|
38
|
+
const LIGHTHOUSE_KEYS = ['onlyCategories', 'onlyAudits', 'skipAudits', 'output']
|
|
39
|
+
|
|
40
|
+
const isEmpty = obj => Object.keys(obj).length === 0
|
|
41
|
+
|
|
42
|
+
const create = (ctx = {}) => {
|
|
43
|
+
/**
|
|
44
|
+
* Split the single options bag into the three destinations mql has:
|
|
45
|
+
* `got.headers` (HTTP layer, 3rd arg), `sub` (capability nested keys)
|
|
46
|
+
* and `top` (top-level query params, 2nd arg). Client context is
|
|
47
|
+
* merged first so per-call options can override it.
|
|
48
|
+
*/
|
|
49
|
+
const route = (opts, nested = []) => {
|
|
50
|
+
const top = {}
|
|
51
|
+
const sub = {}
|
|
52
|
+
let got
|
|
53
|
+
for (const [key, value] of Object.entries({ ...ctx, ...opts })) {
|
|
54
|
+
if (key === 'headers') got = { headers: value }
|
|
55
|
+
else if (nested.includes(key)) sub[key] = value
|
|
56
|
+
else top[key] = value
|
|
57
|
+
}
|
|
58
|
+
return { top, sub, got }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const content = field => (url, options) => {
|
|
62
|
+
const { top, sub, got } = route(options, CONTENT_KEYS)
|
|
63
|
+
return mql(
|
|
64
|
+
url,
|
|
65
|
+
{ ...top, meta: false, data: { [field]: { attr: field, ...sub } } },
|
|
66
|
+
got
|
|
67
|
+
).then(({ data }) => data[field])
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const collection = (field, rule) => (url, options) => {
|
|
71
|
+
const { top, sub, got } = route(options, COLLECTION_KEYS)
|
|
72
|
+
return mql(
|
|
73
|
+
url,
|
|
74
|
+
{ ...top, meta: false, data: { [field]: { ...rule, ...sub } } },
|
|
75
|
+
got
|
|
76
|
+
).then(({ data }) => data[field] ?? [])
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const capability = (field, nested) => (url, options) => {
|
|
80
|
+
const { top, sub, got } = route(options, nested)
|
|
81
|
+
return mql(
|
|
82
|
+
url,
|
|
83
|
+
{ ...top, meta: false, [field]: isEmpty(sub) ? true : sub },
|
|
84
|
+
got
|
|
85
|
+
).then(({ data }) => data[field])
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Primary media detection (`data.video` / `data.audio`). */
|
|
89
|
+
const media = field => (url, options) => {
|
|
90
|
+
const { top, got } = route(options)
|
|
91
|
+
return mql(url, { ...top, meta: false, [field]: true }, got).then(
|
|
92
|
+
({ data }) => data[field]
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const googleClient = createGoogleClient()
|
|
97
|
+
|
|
98
|
+
const run = (url, code, options) => {
|
|
99
|
+
const { top, got } = route(options)
|
|
100
|
+
return fn(code, top, got)(url)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
metadata: (url, options) => {
|
|
105
|
+
const { top, got } = route(options)
|
|
106
|
+
return mql(url, top, got).then(({ data }) => data)
|
|
107
|
+
},
|
|
108
|
+
logo: (url, options) => {
|
|
109
|
+
const { top, sub, got } = route(options, LOGO_KEYS)
|
|
110
|
+
return mql(
|
|
111
|
+
url,
|
|
112
|
+
{ ...top, meta: isEmpty(sub) ? true : { logo: sub } },
|
|
113
|
+
got
|
|
114
|
+
).then(({ data }) => data.logo)
|
|
115
|
+
},
|
|
116
|
+
markdown: content('markdown'),
|
|
117
|
+
html: content('html'),
|
|
118
|
+
text: content('text'),
|
|
119
|
+
video: media('video'),
|
|
120
|
+
audio: media('audio'),
|
|
121
|
+
links: collection('links', { selectorAll: 'a', attr: 'href', type: 'url' }),
|
|
122
|
+
images: collection('images', {
|
|
123
|
+
selectorAll: 'img',
|
|
124
|
+
attr: 'src',
|
|
125
|
+
type: 'url'
|
|
126
|
+
}),
|
|
127
|
+
videos: collection('videos', {
|
|
128
|
+
selectorAll: ['video[src]', 'video source[src]'],
|
|
129
|
+
attr: 'src',
|
|
130
|
+
type: 'url'
|
|
131
|
+
}),
|
|
132
|
+
audios: collection('audios', {
|
|
133
|
+
selectorAll: ['audio[src]', 'audio source[src]'],
|
|
134
|
+
attr: 'src',
|
|
135
|
+
type: 'url'
|
|
136
|
+
}),
|
|
137
|
+
emails: collection('emails', {
|
|
138
|
+
selector: 'html',
|
|
139
|
+
attr: 'html',
|
|
140
|
+
type: 'email'
|
|
141
|
+
}),
|
|
142
|
+
extract: (url, rules, options) => {
|
|
143
|
+
const { top, got } = route(options)
|
|
144
|
+
return mql(url, { ...top, meta: false, data: rules }, got).then(
|
|
145
|
+
({ data }) => data
|
|
146
|
+
)
|
|
147
|
+
},
|
|
148
|
+
screenshot: capability('screenshot', SCREENSHOT_KEYS),
|
|
149
|
+
pdf: capability('pdf', PDF_KEYS),
|
|
150
|
+
embed: (url, options) => {
|
|
151
|
+
const { top, sub, got } = route(options, EMBED_KEYS)
|
|
152
|
+
return mql(
|
|
153
|
+
url,
|
|
154
|
+
{ ...top, meta: false, iframe: isEmpty(sub) ? true : sub },
|
|
155
|
+
got
|
|
156
|
+
).then(({ data }) => data.iframe)
|
|
157
|
+
},
|
|
158
|
+
technologies: (url, options) => {
|
|
159
|
+
const { top, got } = route(options)
|
|
160
|
+
return mql(
|
|
161
|
+
url,
|
|
162
|
+
{
|
|
163
|
+
...top,
|
|
164
|
+
meta: false,
|
|
165
|
+
insights: { technologies: true, lighthouse: false }
|
|
166
|
+
},
|
|
167
|
+
got
|
|
168
|
+
).then(({ data }) => data.insights.technologies)
|
|
169
|
+
},
|
|
170
|
+
lighthouse: (url, options) => {
|
|
171
|
+
const { top, sub, got } = route(options, LIGHTHOUSE_KEYS)
|
|
172
|
+
return mql(
|
|
173
|
+
url,
|
|
174
|
+
{
|
|
175
|
+
...top,
|
|
176
|
+
meta: false,
|
|
177
|
+
insights: {
|
|
178
|
+
technologies: false,
|
|
179
|
+
lighthouse: isEmpty(sub) ? true : sub
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
got
|
|
183
|
+
).then(({ data }) => data.insights.lighthouse)
|
|
184
|
+
},
|
|
185
|
+
search: (query, options) => {
|
|
186
|
+
const { top } = route(options)
|
|
187
|
+
return googleClient(query, top)
|
|
188
|
+
},
|
|
189
|
+
function: run,
|
|
190
|
+
run
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = create
|
|
195
|
+
module.exports.MicrolinkError = mql.MicrolinkError
|