@microlink/mcp 1.0.1 → 1.1.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/README.md +128 -56
- package/package.json +14 -48
- package/scripts/postinstall.js +10 -3
- package/src/index.js +4 -1
- package/src/microlink-client.js +32 -219
- package/src/schemas.js +151 -24
- package/src/tools/audio.js +5 -5
- package/src/tools/audios.js +16 -0
- package/src/tools/emails.js +16 -0
- package/src/tools/embed.js +17 -0
- package/src/tools/extract.js +6 -8
- package/src/tools/function.js +16 -0
- package/src/tools/html.js +16 -0
- package/src/tools/images.js +16 -0
- package/src/tools/index.js +29 -9
- package/src/tools/lighthouse.js +16 -0
- package/src/tools/links.js +16 -0
- package/src/tools/logo.js +17 -0
- package/src/tools/markdown.js +4 -4
- package/src/tools/metadata.js +17 -0
- package/src/tools/pdf.js +6 -11
- package/src/tools/register.js +41 -51
- package/src/tools/screenshot.js +8 -11
- package/src/tools/search.js +17 -0
- package/src/tools/technologies.js +16 -0
- package/src/tools/text.js +5 -5
- package/src/tools/video.js +6 -6
- package/src/tools/videos.js +16 -0
- package/src/tools/insights.js +0 -21
- package/src/tools/meta.js +0 -17
- package/src/tools/palette.js +0 -17
package/src/microlink-client.js
CHANGED
|
@@ -1,239 +1,52 @@
|
|
|
1
|
-
import
|
|
1
|
+
import createClient, { MicrolinkError } from 'microlink.io'
|
|
2
2
|
|
|
3
|
-
const RESPONSE_HEADER_KEYS = [
|
|
4
|
-
'x-request-id',
|
|
5
|
-
'x-pricing-plan',
|
|
6
|
-
'x-cache-status',
|
|
7
|
-
'x-cache-ttl',
|
|
8
|
-
'cf-cache-status',
|
|
9
|
-
'cache-control',
|
|
10
|
-
'x-rate-limit-limit',
|
|
11
|
-
'x-rate-limit-remaining',
|
|
12
|
-
'x-rate-limit-reset',
|
|
13
|
-
'x-response-time',
|
|
14
|
-
'content-type',
|
|
15
|
-
'content-encoding'
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
const FREE_ENDPOINT_ORIGIN = 'https://api.microlink.io'
|
|
19
3
|
const FREE_QUOTA_EXCEEDED_HINT =
|
|
20
4
|
'Free daily quota reached (50 requests/day). Extend your limit by getting an API key at https://microlink.io/#pricing.'
|
|
21
|
-
const EMPTY_OBJECT_MEANS_TRUE_FLAGS = ['screenshot', 'pdf', 'insights']
|
|
22
|
-
|
|
23
|
-
function isPlainObject (value) {
|
|
24
|
-
return Object.prototype.toString.call(value) === '[object Object]'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function isObject (value) {
|
|
28
|
-
return value !== null && typeof value === 'object'
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function hasSerializableValue (value) {
|
|
32
|
-
if (value === undefined || value === null) {
|
|
33
|
-
return false
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (Array.isArray(value)) {
|
|
37
|
-
return value.length > 0
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (isPlainObject(value)) {
|
|
41
|
-
return Object.values(value).some(hasSerializableValue)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return true
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getHeaderValue (headers, headerName) {
|
|
48
|
-
if (!headers) {
|
|
49
|
-
return null
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (typeof headers.get === 'function') {
|
|
53
|
-
const value = headers.get(headerName)
|
|
54
|
-
return value ?? null
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (typeof headers !== 'object') {
|
|
58
|
-
return null
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const lookup = headerName.toLowerCase()
|
|
62
|
-
|
|
63
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
64
|
-
if (key.toLowerCase() === lookup) {
|
|
65
|
-
return value
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function pickResponseHeaders (headers) {
|
|
73
|
-
const output = {}
|
|
74
|
-
|
|
75
|
-
for (const headerName of RESPONSE_HEADER_KEYS) {
|
|
76
|
-
const value = getHeaderValue(headers, headerName)
|
|
77
|
-
if (value !== null) {
|
|
78
|
-
output[headerName] = value
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return output
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function withForcedFlags (opts, forcedFlags = {}) {
|
|
86
|
-
const nextOpts = { ...opts }
|
|
87
|
-
|
|
88
|
-
// Microlink treats empty objects for these toggles as enabled defaults.
|
|
89
|
-
// Normalize `{}` -> `true` so query serialization always keeps the flag.
|
|
90
|
-
for (const flagName of EMPTY_OBJECT_MEANS_TRUE_FLAGS) {
|
|
91
|
-
if (isPlainObject(nextOpts[flagName]) && !hasSerializableValue(nextOpts[flagName])) {
|
|
92
|
-
nextOpts[flagName] = true
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
5
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
nextOpts[flagName] = flagValue
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return nextOpts
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function getEndpointFromRequestUrl (requestUrl) {
|
|
106
|
-
try {
|
|
107
|
-
return new URL(requestUrl).origin
|
|
108
|
-
} catch (_) {
|
|
109
|
-
return requestUrl
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function stripResponseFromBody (body) {
|
|
114
|
-
if (!isPlainObject(body)) {
|
|
115
|
-
return body
|
|
116
|
-
}
|
|
6
|
+
// A single shared client; the per-request apiKey travels in the options bag.
|
|
7
|
+
export const client = createClient()
|
|
117
8
|
|
|
118
|
-
|
|
119
|
-
return payload
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function withFreeQuotaHint (message, { requestUrl, statusCode }) {
|
|
123
|
-
const endpoint = getEndpointFromRequestUrl(requestUrl)
|
|
124
|
-
|
|
125
|
-
if (endpoint !== FREE_ENDPOINT_ORIGIN || statusCode !== 429) {
|
|
126
|
-
return message
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (typeof message === 'string' && message.includes('50 requests/day')) {
|
|
130
|
-
return message
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (typeof message === 'string' && message.trim().length > 0) {
|
|
134
|
-
return `${message} ${FREE_QUOTA_EXCEEDED_HINT}`
|
|
135
|
-
}
|
|
9
|
+
export { MicrolinkError }
|
|
136
10
|
|
|
137
|
-
|
|
11
|
+
export function resolveApiKey (inputApiKey, headerApiKey) {
|
|
12
|
+
return (
|
|
13
|
+
inputApiKey || headerApiKey || process.env.MICROLINK_API_KEY || undefined
|
|
14
|
+
)
|
|
138
15
|
}
|
|
139
16
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const payload = {}
|
|
150
|
-
|
|
151
|
-
for (const key of ['status', 'data', 'more', 'code', 'id', 'report', 'message', 'url']) {
|
|
152
|
-
if (error[key] !== undefined) {
|
|
153
|
-
payload[key] = error[key]
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (payload.status === undefined) {
|
|
158
|
-
payload.status = 'error'
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (payload.message === undefined) {
|
|
162
|
-
payload.message = error.message || 'Microlink request failed.'
|
|
163
|
-
}
|
|
164
|
-
payload.message = withFreeQuotaHint(payload.message, { requestUrl, statusCode })
|
|
165
|
-
|
|
166
|
-
if (payload.url === undefined) {
|
|
167
|
-
payload.url = requestUrl
|
|
17
|
+
// Every tool returns the library's direct result (a string, array, or object).
|
|
18
|
+
// MCP `structuredContent` must be an object, so wrap the value under `data`.
|
|
19
|
+
export function asToolResult (value) {
|
|
20
|
+
const data = value ?? null
|
|
21
|
+
return {
|
|
22
|
+
isError: false,
|
|
23
|
+
structuredContent: { data },
|
|
24
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }]
|
|
168
25
|
}
|
|
169
|
-
|
|
170
|
-
return payload
|
|
171
26
|
}
|
|
172
27
|
|
|
173
|
-
export
|
|
174
|
-
const
|
|
175
|
-
const
|
|
176
|
-
const apiKey = requestApiKey || envApiKey
|
|
177
|
-
const opts = withForcedFlags(rawOpts, forcedFlags)
|
|
178
|
-
|
|
179
|
-
if (apiKey) {
|
|
180
|
-
opts.apiKey = apiKey
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
const [requestUrl, requestOpts] = mql.getApiUrl(url, opts, {
|
|
184
|
-
headers: {
|
|
185
|
-
accept: 'application/json'
|
|
186
|
-
}
|
|
187
|
-
})
|
|
188
|
-
const endpoint = getEndpointFromRequestUrl(requestUrl)
|
|
28
|
+
export function asErrorResult (error) {
|
|
29
|
+
const isMql = error instanceof MicrolinkError
|
|
30
|
+
const statusCode = isMql ? error.statusCode : undefined
|
|
189
31
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return {
|
|
195
|
-
ok: true,
|
|
196
|
-
statusCode: response?.statusCode ?? 200,
|
|
197
|
-
requestUrl,
|
|
198
|
-
finalUrl: response?.url ?? requestUrl,
|
|
199
|
-
endpoint,
|
|
200
|
-
headers: pickResponseHeaders(response?.headers),
|
|
201
|
-
body: stripResponseFromBody(body)
|
|
202
|
-
}
|
|
203
|
-
} catch (error) {
|
|
204
|
-
return {
|
|
205
|
-
ok: false,
|
|
206
|
-
statusCode: error?.statusCode ?? 500,
|
|
207
|
-
requestUrl,
|
|
208
|
-
finalUrl: error?.url ?? requestUrl,
|
|
209
|
-
endpoint,
|
|
210
|
-
headers: pickResponseHeaders(error?.headers),
|
|
211
|
-
body: toErrorBody(error, requestUrl, error?.statusCode ?? 500)
|
|
212
|
-
}
|
|
32
|
+
const payload = {
|
|
33
|
+
message:
|
|
34
|
+
(isMql ? error.description : undefined) || error?.message || String(error)
|
|
213
35
|
}
|
|
214
|
-
}
|
|
215
36
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
responseHeaders: result.headers,
|
|
223
|
-
microlink: result.body
|
|
37
|
+
if (isMql) {
|
|
38
|
+
if (error.code) payload.code = error.code
|
|
39
|
+
if (error.status) payload.status = error.status
|
|
40
|
+
if (statusCode) payload.statusCode = statusCode
|
|
41
|
+
if (error.url) payload.url = error.url
|
|
42
|
+
if (error.more) payload.more = error.more
|
|
224
43
|
}
|
|
225
44
|
|
|
226
|
-
|
|
227
|
-
const isError = !result.ok || isMicrolinkError
|
|
45
|
+
if (statusCode === 429) payload.hint = FREE_QUOTA_EXCEEDED_HINT
|
|
228
46
|
|
|
229
47
|
return {
|
|
230
|
-
isError,
|
|
231
|
-
structuredContent:
|
|
232
|
-
content: [
|
|
233
|
-
{
|
|
234
|
-
type: 'text',
|
|
235
|
-
text: JSON.stringify(output, null, 2)
|
|
236
|
-
}
|
|
237
|
-
]
|
|
48
|
+
isError: true,
|
|
49
|
+
structuredContent: { error: payload },
|
|
50
|
+
content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }]
|
|
238
51
|
}
|
|
239
52
|
}
|
package/src/schemas.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
2
|
|
|
3
|
-
const stringOrStringArraySchema = z.union([
|
|
3
|
+
const stringOrStringArraySchema = z.union([
|
|
4
|
+
z.string(),
|
|
5
|
+
z.array(z.string()).min(1)
|
|
6
|
+
])
|
|
4
7
|
const stringOrNumberSchema = z.union([z.string(), z.number()])
|
|
5
8
|
|
|
6
9
|
function coerceJsonObjectString (value) {
|
|
@@ -15,7 +18,11 @@ function coerceJsonObjectString (value) {
|
|
|
15
18
|
|
|
16
19
|
try {
|
|
17
20
|
const parsed = JSON.parse(input)
|
|
18
|
-
if (
|
|
21
|
+
if (
|
|
22
|
+
parsed !== null &&
|
|
23
|
+
typeof parsed === 'object' &&
|
|
24
|
+
!Array.isArray(parsed)
|
|
25
|
+
) {
|
|
19
26
|
return parsed
|
|
20
27
|
}
|
|
21
28
|
} catch {
|
|
@@ -37,17 +44,27 @@ function coerceBooleanString (value) {
|
|
|
37
44
|
|
|
38
45
|
const booleanSchema = z.preprocess(coerceBooleanString, z.boolean())
|
|
39
46
|
const objectLikeSchema = schema => z.preprocess(coerceJsonObjectString, schema)
|
|
40
|
-
const toggledObjectSchema = z.union([
|
|
47
|
+
const toggledObjectSchema = z.union([
|
|
48
|
+
booleanSchema,
|
|
49
|
+
objectLikeSchema(z.object({}).catchall(z.unknown()))
|
|
50
|
+
])
|
|
41
51
|
const proxySchema = objectLikeSchema(
|
|
42
52
|
z.union([z.string().min(1), z.object({}).catchall(z.unknown())])
|
|
43
53
|
)
|
|
44
54
|
|
|
55
|
+
// A CSS selector or an array of selectors (ordered fallbacks / unioned matches),
|
|
56
|
+
// matching what the Microlink API and the microlink.io library accept.
|
|
57
|
+
const selectorSchema = z.union([
|
|
58
|
+
z.string().min(1),
|
|
59
|
+
z.array(z.string().min(1)).min(1)
|
|
60
|
+
])
|
|
61
|
+
|
|
45
62
|
// A single data-extraction rule: CSS selector(s) + optional attr/type/evaluate/nested data.
|
|
46
63
|
const dataSingleRuleSchema = objectLikeSchema(
|
|
47
64
|
z
|
|
48
65
|
.object({
|
|
49
|
-
selector:
|
|
50
|
-
selectorAll:
|
|
66
|
+
selector: selectorSchema.optional(),
|
|
67
|
+
selectorAll: selectorSchema.optional(),
|
|
51
68
|
attr: z.string().min(1).optional(),
|
|
52
69
|
type: z.string().min(1).optional(),
|
|
53
70
|
evaluate: z.string().min(1).optional(),
|
|
@@ -57,9 +74,18 @@ const dataSingleRuleSchema = objectLikeSchema(
|
|
|
57
74
|
)
|
|
58
75
|
|
|
59
76
|
// A rule can also be an array of rules used as ordered fallback selectors.
|
|
60
|
-
const dataRuleSchema = z.union([
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
const dataRuleSchema = z.union([
|
|
78
|
+
dataSingleRuleSchema,
|
|
79
|
+
z.array(dataSingleRuleSchema).min(1)
|
|
80
|
+
])
|
|
81
|
+
|
|
82
|
+
const waitUntilEventSchema = z.enum([
|
|
83
|
+
'auto',
|
|
84
|
+
'load',
|
|
85
|
+
'domcontentloaded',
|
|
86
|
+
'networkidle0',
|
|
87
|
+
'networkidle2'
|
|
88
|
+
])
|
|
63
89
|
|
|
64
90
|
const viewportSchema = objectLikeSchema(
|
|
65
91
|
z
|
|
@@ -86,11 +112,14 @@ const screenshotOverlaySchema = objectLikeSchema(
|
|
|
86
112
|
export const screenshotConfigSchema = objectLikeSchema(
|
|
87
113
|
z
|
|
88
114
|
.object({
|
|
115
|
+
animated: booleanSchema.optional(),
|
|
89
116
|
codeScheme: z.string().min(1).optional(),
|
|
90
117
|
element: z.string().min(1).optional(),
|
|
91
118
|
fullPage: booleanSchema.optional(),
|
|
92
119
|
omitBackground: booleanSchema.optional(),
|
|
120
|
+
optimizeForSpeed: booleanSchema.optional(),
|
|
93
121
|
overlay: screenshotOverlaySchema.optional(),
|
|
122
|
+
palette: booleanSchema.optional(),
|
|
94
123
|
type: z.enum(['jpeg', 'png']).optional()
|
|
95
124
|
})
|
|
96
125
|
.strict()
|
|
@@ -113,7 +142,21 @@ const pdfMarginSchema = objectLikeSchema(
|
|
|
113
142
|
export const pdfConfigSchema = objectLikeSchema(
|
|
114
143
|
z
|
|
115
144
|
.object({
|
|
116
|
-
format: z
|
|
145
|
+
format: z
|
|
146
|
+
.enum([
|
|
147
|
+
'Letter',
|
|
148
|
+
'Legal',
|
|
149
|
+
'Tabloid',
|
|
150
|
+
'Ledger',
|
|
151
|
+
'A0',
|
|
152
|
+
'A1',
|
|
153
|
+
'A2',
|
|
154
|
+
'A3',
|
|
155
|
+
'A4',
|
|
156
|
+
'A5',
|
|
157
|
+
'A6'
|
|
158
|
+
])
|
|
159
|
+
.optional(),
|
|
117
160
|
height: z.string().min(1).optional(),
|
|
118
161
|
landscape: booleanSchema.optional(),
|
|
119
162
|
margin: pdfMarginSchema.optional(),
|
|
@@ -125,7 +168,15 @@ export const pdfConfigSchema = objectLikeSchema(
|
|
|
125
168
|
)
|
|
126
169
|
|
|
127
170
|
const lighthouseOutputSchema = z.enum(['json', 'html', 'csv'])
|
|
128
|
-
const lighthousePresetSchema = z.enum([
|
|
171
|
+
const lighthousePresetSchema = z.enum([
|
|
172
|
+
'default',
|
|
173
|
+
'desktop',
|
|
174
|
+
'experimental',
|
|
175
|
+
'full',
|
|
176
|
+
'lr-desktop',
|
|
177
|
+
'lr-mobile',
|
|
178
|
+
'perf'
|
|
179
|
+
])
|
|
129
180
|
|
|
130
181
|
const lighthouseConfigSchema = objectLikeSchema(
|
|
131
182
|
z
|
|
@@ -186,7 +237,9 @@ const visualSchema = {
|
|
|
186
237
|
filename: z.string().min(1).optional(),
|
|
187
238
|
filter: z.string().min(1).optional(),
|
|
188
239
|
force: booleanSchema.optional(),
|
|
189
|
-
headers: objectLikeSchema(
|
|
240
|
+
headers: objectLikeSchema(
|
|
241
|
+
z.record(z.string(), z.union([z.string(), z.number(), booleanSchema]))
|
|
242
|
+
).optional(),
|
|
190
243
|
javascript: booleanSchema.optional(),
|
|
191
244
|
mediaType: z.enum(['screen', 'print']).optional(),
|
|
192
245
|
modules: stringOrStringArraySchema.optional(),
|
|
@@ -202,7 +255,9 @@ const visualSchema = {
|
|
|
202
255
|
viewport: viewportSchema.optional(),
|
|
203
256
|
waitForSelector: z.string().min(1).optional(),
|
|
204
257
|
waitForTimeout: stringOrNumberSchema.optional(),
|
|
205
|
-
waitUntil: z
|
|
258
|
+
waitUntil: z
|
|
259
|
+
.union([waitUntilEventSchema, z.array(waitUntilEventSchema).min(1)])
|
|
260
|
+
.optional()
|
|
206
261
|
}
|
|
207
262
|
|
|
208
263
|
export const extractInputSchema = baseSchema
|
|
@@ -231,12 +286,6 @@ export const pdfInputSchema = baseSchema
|
|
|
231
286
|
})
|
|
232
287
|
.strict()
|
|
233
288
|
|
|
234
|
-
export const insightsInputSchema = baseSchema
|
|
235
|
-
.extend({
|
|
236
|
-
insights: z.union([booleanSchema, insightsConfigSchema]).optional()
|
|
237
|
-
})
|
|
238
|
-
.strict()
|
|
239
|
-
|
|
240
289
|
export const audioInputSchema = baseSchema
|
|
241
290
|
.extend({
|
|
242
291
|
proxy: proxySchema.optional(),
|
|
@@ -253,19 +302,97 @@ export const videoInputSchema = baseSchema
|
|
|
253
302
|
})
|
|
254
303
|
.strict()
|
|
255
304
|
|
|
256
|
-
export const
|
|
305
|
+
export const logoInputSchema = baseSchema
|
|
257
306
|
.extend({
|
|
258
|
-
|
|
259
|
-
palette: booleanSchema.optional()
|
|
307
|
+
square: booleanSchema.optional()
|
|
260
308
|
})
|
|
261
309
|
.strict()
|
|
262
310
|
|
|
263
|
-
export const
|
|
311
|
+
export const metadataInputSchema = baseSchema
|
|
264
312
|
.extend({
|
|
265
313
|
meta: z.union([booleanSchema, metaConfigSchema]).optional()
|
|
266
314
|
})
|
|
267
315
|
.strict()
|
|
268
316
|
|
|
269
|
-
|
|
317
|
+
// Content tools (markdown/html/text) accept a selector to scope the extraction.
|
|
318
|
+
const contentSchema = baseSchema
|
|
319
|
+
.extend({
|
|
320
|
+
selector: selectorSchema.optional(),
|
|
321
|
+
selectorAll: selectorSchema.optional(),
|
|
322
|
+
type: z.string().min(1).optional()
|
|
323
|
+
})
|
|
324
|
+
.strict()
|
|
325
|
+
|
|
326
|
+
// Collection tools accept selector/attr/type overrides for the data rule.
|
|
327
|
+
const collectionSchema = baseSchema
|
|
328
|
+
.extend({
|
|
329
|
+
selector: selectorSchema.optional(),
|
|
330
|
+
selectorAll: selectorSchema.optional(),
|
|
331
|
+
attr: z.string().min(1).optional(),
|
|
332
|
+
type: z.string().min(1).optional()
|
|
333
|
+
})
|
|
334
|
+
.strict()
|
|
335
|
+
|
|
336
|
+
export const markdownInputSchema = contentSchema
|
|
337
|
+
|
|
338
|
+
export const htmlInputSchema = contentSchema
|
|
339
|
+
|
|
340
|
+
export const textInputSchema = contentSchema
|
|
341
|
+
|
|
342
|
+
export const embedInputSchema = baseSchema
|
|
343
|
+
.extend({
|
|
344
|
+
maxWidth: z.coerce.number().int().positive().optional(),
|
|
345
|
+
maxHeight: z.coerce.number().int().positive().optional()
|
|
346
|
+
})
|
|
347
|
+
.strict()
|
|
348
|
+
|
|
349
|
+
export const linksInputSchema = collectionSchema
|
|
350
|
+
|
|
351
|
+
export const imagesInputSchema = collectionSchema
|
|
270
352
|
|
|
271
|
-
export const
|
|
353
|
+
export const videosInputSchema = collectionSchema
|
|
354
|
+
|
|
355
|
+
export const audiosInputSchema = collectionSchema
|
|
356
|
+
|
|
357
|
+
export const emailsInputSchema = collectionSchema
|
|
358
|
+
|
|
359
|
+
export const technologiesInputSchema = baseSchema.strict()
|
|
360
|
+
|
|
361
|
+
export const lighthouseInputSchema = baseSchema
|
|
362
|
+
.extend({
|
|
363
|
+
onlyCategories: z.array(z.string().min(1)).optional(),
|
|
364
|
+
onlyAudits: z.array(z.string().min(1)).optional(),
|
|
365
|
+
skipAudits: z.array(z.string().min(1)).optional(),
|
|
366
|
+
output: z.union([z.string().min(1), z.array(z.string().min(1))]).optional()
|
|
367
|
+
})
|
|
368
|
+
.strict()
|
|
369
|
+
|
|
370
|
+
export const searchInputSchema = z
|
|
371
|
+
.object({
|
|
372
|
+
query: z.string().min(1),
|
|
373
|
+
apiKey: z.string().min(1).optional(),
|
|
374
|
+
type: z
|
|
375
|
+
.enum([
|
|
376
|
+
'search',
|
|
377
|
+
'news',
|
|
378
|
+
'images',
|
|
379
|
+
'videos',
|
|
380
|
+
'places',
|
|
381
|
+
'maps',
|
|
382
|
+
'shopping',
|
|
383
|
+
'scholar',
|
|
384
|
+
'patents',
|
|
385
|
+
'autocomplete'
|
|
386
|
+
])
|
|
387
|
+
.optional(),
|
|
388
|
+
limit: z.coerce.number().int().positive().optional(),
|
|
389
|
+
location: z.string().min(1).optional(),
|
|
390
|
+
period: z.enum(['hour', 'day', 'week', 'month', 'year']).optional()
|
|
391
|
+
})
|
|
392
|
+
.strict()
|
|
393
|
+
|
|
394
|
+
export const functionInputSchema = baseSchema
|
|
395
|
+
.extend({
|
|
396
|
+
code: z.string().min(1)
|
|
397
|
+
})
|
|
398
|
+
.strict()
|
package/src/tools/audio.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { audioInputSchema } from '../schemas.js'
|
|
2
|
-
import { register } from './register.js'
|
|
2
|
+
import { register, urlMethod } from './register.js'
|
|
3
3
|
|
|
4
4
|
export function audio (server) {
|
|
5
5
|
register(
|
|
6
6
|
server,
|
|
7
7
|
'microlink_audio',
|
|
8
8
|
[
|
|
9
|
-
'Detect
|
|
9
|
+
'Detect the primary playable audio of any public URL via Microlink.',
|
|
10
10
|
'Works with SoundCloud, Spotify, Mixcloud, and other audio platforms.',
|
|
11
|
-
'
|
|
12
|
-
'
|
|
11
|
+
'Returns the audio asset: `url`, `type`, `duration`, `size`, `duration_pretty`, `size_pretty`.',
|
|
12
|
+
'Mirrors the `microlink.audio(url)` library method. For every audio on the page, use `microlink_audios`.'
|
|
13
13
|
].join(' '),
|
|
14
14
|
audioInputSchema,
|
|
15
|
-
|
|
15
|
+
urlMethod('audio')
|
|
16
16
|
)
|
|
17
17
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { audiosInputSchema } from '../schemas.js'
|
|
2
|
+
import { register, urlMethod } from './register.js'
|
|
3
|
+
|
|
4
|
+
export function audios (server) {
|
|
5
|
+
register(
|
|
6
|
+
server,
|
|
7
|
+
'microlink_audios',
|
|
8
|
+
[
|
|
9
|
+
'Collect every audio source (`<audio>`, `<source>`) from any public URL via Microlink.',
|
|
10
|
+
'Returns an array of absolute, deduped audio URLs.',
|
|
11
|
+
'Mirrors the `microlink.audios(url)` library method. For the primary playable audio, use `microlink_audio`.'
|
|
12
|
+
].join(' '),
|
|
13
|
+
audiosInputSchema,
|
|
14
|
+
urlMethod('audios')
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { emailsInputSchema } from '../schemas.js'
|
|
2
|
+
import { register, urlMethod } from './register.js'
|
|
3
|
+
|
|
4
|
+
export function emails (server) {
|
|
5
|
+
register(
|
|
6
|
+
server,
|
|
7
|
+
'microlink_emails',
|
|
8
|
+
[
|
|
9
|
+
'Collect every email address present on any public URL via Microlink.',
|
|
10
|
+
'Detects addresses from `mailto:` links and plain text alike; returns a deduped array.',
|
|
11
|
+
'Mirrors the `microlink.emails(url)` library method.'
|
|
12
|
+
].join(' '),
|
|
13
|
+
emailsInputSchema,
|
|
14
|
+
urlMethod('emails')
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { embedInputSchema } from '../schemas.js'
|
|
2
|
+
import { register, urlMethod } from './register.js'
|
|
3
|
+
|
|
4
|
+
export function embed (server) {
|
|
5
|
+
register(
|
|
6
|
+
server,
|
|
7
|
+
'microlink_embed',
|
|
8
|
+
[
|
|
9
|
+
'Get the oEmbed-style embeddable iframe for any public URL via Microlink (YouTube, Tweet, CodePen, ...).',
|
|
10
|
+
'Returns `{ html, scripts }` — the markup to embed plus the script URLs it needs.',
|
|
11
|
+
'Pass `maxWidth` / `maxHeight` to constrain the embed size.',
|
|
12
|
+
'Mirrors the `microlink.embed(url)` library method.'
|
|
13
|
+
].join(' '),
|
|
14
|
+
embedInputSchema,
|
|
15
|
+
urlMethod('embed')
|
|
16
|
+
)
|
|
17
|
+
}
|
package/src/tools/extract.js
CHANGED
|
@@ -6,15 +6,13 @@ export function extract (server) {
|
|
|
6
6
|
server,
|
|
7
7
|
'microlink_extract',
|
|
8
8
|
[
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'For `screenshot`, `pdf`, and `insights`, pass `true` to enable defaults or pass a config object with options; `{}` is treated as `true`.',
|
|
14
|
-
'Supports device emulation (`device`), custom headers, proxy, JavaScript injection (`scripts`, `modules`, `function`), interaction (`click`, `scroll`), and caching (`ttl`, `staleTtl`, `force`).',
|
|
15
|
-
'The CDN asset URL for any enabled media feature is in `data.<feature>.url` (e.g. `data.screenshot.url`).'
|
|
9
|
+
'Scrape custom fields from any public URL via Microlink using MQL data rules.',
|
|
10
|
+
'Pass `data` with the rules object (selector/selectorAll, attr, type, evaluate; nested rules and arrays supported); returns the extracted `data` object.',
|
|
11
|
+
'Also supports combining capabilities (`screenshot`, `pdf`, `iframe`, `insights`) and browser controls in the same request.',
|
|
12
|
+
'Mirrors the `microlink.extract(url, rules)` library method.'
|
|
16
13
|
].join(' '),
|
|
17
14
|
extractInputSchema,
|
|
18
|
-
{}
|
|
15
|
+
(client, { url, data, ...options }) =>
|
|
16
|
+
client.extract(url, data ?? {}, options)
|
|
19
17
|
)
|
|
20
18
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { functionInputSchema } from '../schemas.js'
|
|
2
|
+
import { register } from './register.js'
|
|
3
|
+
|
|
4
|
+
export function fn (server) {
|
|
5
|
+
register(
|
|
6
|
+
server,
|
|
7
|
+
'microlink_function',
|
|
8
|
+
[
|
|
9
|
+
'Run a JavaScript function against any public URL inside Microlink’s server-side browser sandbox.',
|
|
10
|
+
'Pass `code` as the function source (e.g. "async ({ page }) => page.title()"); it receives `{ page, response, ...args }` and its return value comes back in `value`.',
|
|
11
|
+
'Also returns `isFulfilled`, `profiling`, and `logging`. Mirrors the `microlink.function(url, code)` library method.'
|
|
12
|
+
].join(' '),
|
|
13
|
+
functionInputSchema,
|
|
14
|
+
(client, { url, code, ...options }) => client.function(url, code, options)
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { htmlInputSchema } from '../schemas.js'
|
|
2
|
+
import { register, urlMethod } from './register.js'
|
|
3
|
+
|
|
4
|
+
export function html (server) {
|
|
5
|
+
register(
|
|
6
|
+
server,
|
|
7
|
+
'microlink_html',
|
|
8
|
+
[
|
|
9
|
+
'Extract the HTML content of any public URL via Microlink.',
|
|
10
|
+
'Returns the page HTML as a string. Pass `selector` to scope it to part of the page.',
|
|
11
|
+
'Mirrors the `microlink.html(url)` library method.'
|
|
12
|
+
].join(' '),
|
|
13
|
+
htmlInputSchema,
|
|
14
|
+
urlMethod('html')
|
|
15
|
+
)
|
|
16
|
+
}
|