├── .gitattributes ├── test ├── snapshots │ ├── opts.js.snap │ ├── opts.mjs.snap │ ├── get-api-url.js.snap │ ├── get-api-url.mjs.snap │ ├── opts.js.md │ ├── opts.mjs.md │ ├── get-api-url.js.md │ └── get-api-url.mjs.md ├── clients.mjs ├── stream.mjs ├── error │ ├── client.mjs │ └── server.mjs ├── opts.mjs ├── get-api-url.mjs ├── build.mjs ├── node.test-d.ts ├── rules.mjs └── lightweight.test-d.ts ├── lightweight ├── package.json ├── index.d.ts └── index.umd.js ├── .npmrc ├── .github ├── dependabot.yml └── workflows │ ├── pull_request.yml │ └── main.yml ├── .editorconfig ├── src ├── node.js ├── node.d.ts ├── lightweight.js └── factory.js ├── .gitignore ├── README.md ├── LICENSE.md ├── rollup.config.js ├── index.html ├── package.json └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /test/snapshots/opts.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microlinkhq/mql/HEAD/test/snapshots/opts.js.snap -------------------------------------------------------------------------------- /test/snapshots/opts.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microlinkhq/mql/HEAD/test/snapshots/opts.mjs.snap -------------------------------------------------------------------------------- /lightweight/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "index.js", 3 | "type": "module", 4 | "types": "index.d.ts" 5 | } 6 | -------------------------------------------------------------------------------- /test/snapshots/get-api-url.js.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microlinkhq/mql/HEAD/test/snapshots/get-api-url.js.snap -------------------------------------------------------------------------------- /test/snapshots/get-api-url.mjs.snap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microlinkhq/mql/HEAD/test/snapshots/get-api-url.mjs.snap -------------------------------------------------------------------------------- /test/clients.mjs: -------------------------------------------------------------------------------- 1 | import mqlLightweight from '../lightweight/index.js' 2 | import mqlNode from '../src/node.mjs' 3 | 4 | export default [ 5 | { constructor: mqlNode, target: 'node' }, 6 | { constructor: mqlLightweight, target: 'lightweight' } 7 | ] 8 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | audit=false 2 | fund=false 3 | package-lock=false 4 | prefer-dedupe=true 5 | prefer-offline=false 6 | save-prefix=~ 7 | save=false 8 | strict-peer-dependencies=false 9 | unsafe-perm=true 10 | loglevel=error 11 | shamefully-hoist=true 12 | resolution-mode=highest 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | # Check for updates to GitHub Actions every weekday 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | max_line_length = 80 13 | indent_brace_style = 1TBS 14 | spaces_around_operators = true 15 | quote_type = auto 16 | 17 | [package.json] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | [*.md] 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /src/node.js: -------------------------------------------------------------------------------- 1 | const mql = require('./factory')('buffer')({ 2 | MicrolinkError: require('whoops')('MicrolinkError'), 3 | got: require('got').extend({ headers: { 'user-agent': undefined } }), 4 | flatten: require('flattie').flattie, 5 | VERSION: require('../package.json').version 6 | }) 7 | 8 | module.exports = mql 9 | module.exports.buffer = mql.extend({ responseType: 'buffer' }) 10 | module.exports.render = (input, { width = '650px' } = {}) => { 11 | if (input && input.url && input.type) { 12 | return `` 13 | } 14 | return input 15 | } 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ############################ 2 | # npm 3 | ############################ 4 | node_modules 5 | npm-debug.log 6 | .node_history 7 | yarn.lock 8 | package-lock.json 9 | 10 | ############################ 11 | # tmp, editor & OS files 12 | ############################ 13 | .tmp 14 | *.swo 15 | *.swp 16 | *.swn 17 | *.swm 18 | .DS_Store 19 | *# 20 | *~ 21 | .idea 22 | *sublime* 23 | nbproject 24 | 25 | ############################ 26 | # Tests 27 | ############################ 28 | testApp 29 | coverage 30 | .nyc_output 31 | 32 | ############################ 33 | # Other 34 | ############################ 35 | .env 36 | .envrc 37 | stats.html 38 | examples 39 | src/node.mjs 40 | lightweight/index.js 41 | lightweight/index.umd.js 42 | -------------------------------------------------------------------------------- /test/stream.mjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import test from 'ava' 4 | import toPromise from 'stream-to-promise' 5 | 6 | import clients from './clients.mjs' 7 | 8 | clients 9 | .filter(({ target }) => target === 'node') 10 | .forEach(({ constructor: mql, target }) => { 11 | test(`${target} » pass headers`, async t => { 12 | let headers 13 | 14 | const stream = mql.stream('https://cdn.microlink.io/logo/logo.png', { 15 | headers: { 16 | accept: 'image/webp' 17 | } 18 | }) 19 | 20 | stream.on('response', response => { 21 | headers = response.headers 22 | }) 23 | 24 | await toPromise(stream) 25 | 26 | t.true(['image/webp', 'image/png'].includes(headers['content-type'])) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | microlink logo 3 | microlink logo 4 |
5 | 6 | ###### [Documentation](https://microlink.io/mql) | [CLI](https://github.com/microlinkhq/cli) | [Playground](https://mql.microlink.io) 7 | 8 | ## License 9 | 10 | **microlink** © [Microlink](https://microlink.io), Released under the [MIT](https://github.com/microlinkhq/sdk/blob/master/LICENSE.md) License.
11 | Authored and maintained by Kiko Beats with help from [contributors](https://github.com/microlinkhq/sdk/contributors). 12 | 13 | > [microlink.io](https://microlink.io) · GitHub [@MicrolinkHQ](https://github.com/microlinkhq) · X [@microlinkhq](https://x.com/microlinkhq) 14 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: pull_request 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | test: 13 | if: github.ref != 'refs/heads/master' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v6 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v6 22 | with: 23 | node-version: lts/* 24 | - name: Setup PNPM 25 | uses: pnpm/action-setup@v4 26 | with: 27 | version: latest 28 | run_install: true 29 | - name: Test 30 | run: npm test 31 | - name: Report 32 | run: npx c8 report --reporter=text-lcov > coverage/lcov.info 33 | - name: Coverage 34 | uses: coverallsapp/github-action@main 35 | with: 36 | github-token: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /src/node.d.ts: -------------------------------------------------------------------------------- 1 | import { MqlPayload, MqlOptions } from '../lightweight'; 2 | 3 | export { 4 | ColorScheme, 5 | MicrolinkApiOptions, 6 | MicrolinkError, 7 | MqlError, 8 | MqlOptions, 9 | MqlPayload, 10 | MqlResponseData, 11 | version 12 | } from '../lightweight'; 13 | 14 | import { Response, Options as GotOpts } from 'got/dist/source/core' 15 | 16 | type HTTPResponseWithBody = Response & { body: MqlPayload }; 17 | 18 | export type HTTPResponseRaw = Response & { body: Buffer }; 19 | 20 | export type MqlResponse = MqlPayload & { response: HTTPResponseWithBody }; 21 | 22 | interface Mql { 23 | (url: string, opts?: MqlOptions & { stream: string }, gotOpts?: GotOpts): Promise; 24 | (url: string, opts?: MqlOptions, gotOpts?: GotOpts): Promise; 25 | extend: (gotOpts?: GotOpts) => Mql; 26 | stream: (url: string, gotOpts?: GotOpts) => NodeJS.ReadableStream; 27 | buffer: (url: string, opts?: MqlOptions, gotOpts?: GotOpts) => Promise; 28 | version: string; 29 | } 30 | 31 | declare const mql: Mql; 32 | 33 | export default mql; 34 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2019 Microlink (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. 22 | -------------------------------------------------------------------------------- /test/error/client.mjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import test from 'ava' 4 | 5 | import clients from '../clients.mjs' 6 | 7 | clients.forEach(({ constructor: mql, target }) => { 8 | test(`${target} » EINVALURLCLIENT`, async t => { 9 | const error = await t.throwsAsync(mql(), { instanceOf: mql.MicrolinkError }) 10 | 11 | t.true(error.url === '') 12 | t.true(error.code === 'EINVALURLCLIENT') 13 | t.true(error.status === 'fail') 14 | t.true(error.more === 'https://microlink.io/einvalurlclient') 15 | t.true(error.statusCode === undefined) 16 | t.true(!!error.data) 17 | t.true(!!error.message) 18 | t.true(!!error.description) 19 | }) 20 | 21 | test(`${target} » EFATALCLIENT`, async t => { 22 | let count = 0 23 | 24 | const hooks = { 25 | beforeRetry: [ 26 | () => ++count 27 | ] 28 | } 29 | 30 | const error = await t.throwsAsync( 31 | mql('https://example.com', { endpoint: 'https://notexist.dev' }, { retry: 2, hooks }), 32 | { instanceOf: mql.MicrolinkError } 33 | ) 34 | 35 | t.is(count, 2) 36 | t.true(error.url === 'https://notexist.dev?url=https%3A%2F%2Fexample.com') 37 | t.true(error.code === 'EFATALCLIENT') 38 | t.true(error.status === 'error') 39 | t.true(error.more === 'https://microlink.io/efatalclient') 40 | t.true(error.statusCode === undefined) 41 | t.true(!!error.data) 42 | t.true(!!error.message) 43 | t.true(!!error.description) 44 | }) 45 | }) 46 | -------------------------------------------------------------------------------- /test/opts.mjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import test from 'ava' 4 | 5 | import clients from './clients.mjs' 6 | 7 | clients.forEach(({ constructor: mql, target }) => { 8 | test(`${target} » url`, async t => { 9 | const { status, data, response } = await mql( 10 | 'https://kikobeats.com?ref=mql' 11 | ) 12 | const { date, ...restData } = data 13 | 14 | t.snapshot(status) 15 | t.snapshot(restData) 16 | t.snapshot(response.url) 17 | t.snapshot(response.statusCode) 18 | }) 19 | 20 | test(`${target} » stream`, async t => { 21 | const response = await mql( 22 | 'https://kikobeats.com?ref=mql', { 23 | screenshot: { optimizedForSpeed: true }, 24 | stream: 'screenshot' 25 | } 26 | ) 27 | 28 | t.is(typeof response.headers, 'object') 29 | t.is(typeof response.statusCode, 'number') 30 | t.is(typeof response.url, 'string') 31 | 32 | if (target === 'node') { 33 | t.true(Buffer.isBuffer(response.body)) 34 | t.is(typeof response.isFromCache, 'boolean') 35 | } else { 36 | t.true((response.body instanceof ArrayBuffer)) 37 | } 38 | }) 39 | 40 | if (target === 'node') { 41 | test('node » cache support', async t => { 42 | const cache = new Map() 43 | let data 44 | data = await mql('https://kikobeats.com?ref=mql', {}, { cache }) 45 | t.is(data.response.isFromCache, false) 46 | data = await mql('https://kikobeats.com?ref=mql', {}, { cache }) 47 | t.is(data.response.isFromCache, true) 48 | }) 49 | } 50 | }) 51 | -------------------------------------------------------------------------------- /test/get-api-url.mjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import test from 'ava' 4 | 5 | import clients from './clients.mjs' 6 | 7 | clients.forEach(({ constructor: mql, target }) => { 8 | test(`${target} » url without query params`, t => { 9 | t.snapshot(mql.getApiUrl('https://kikobeats.com')) 10 | }) 11 | 12 | test(`${target} » apiKey`, t => { 13 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { apiKey: 'foobar' })) 14 | }) 15 | 16 | test(`${target} » flatten options`, t => { 17 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { overlay: { browser: 'dark' } })) 18 | }) 19 | 20 | test(`${target} » don't pass null`, t => { 21 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { colorScheme: undefined })) 22 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { colorScheme: null })) 23 | }) 24 | 25 | test(`${target} » don't pass undefined`, t => { 26 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { colorScheme: undefined })) 27 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { colorScheme: null })) 28 | }) 29 | 30 | test(`${target} » timeout`, t => { 31 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { timeout: 15000 })) 32 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { timeout: 28000 })) 33 | }) 34 | 35 | test(`${target} » waitUntil`, t => { 36 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { waitUntil: 'load' })) 37 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { waitUntil: ['load', 'networkidle0'] })) 38 | }) 39 | 40 | test(`${target} » undefined`, t => { 41 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { waitUntil: undefined })) 42 | t.snapshot(mql.getApiUrl('https://kikobeats.com', { screenshot: { element: '#screenshot', type: undefined } })) 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /test/build.mjs: -------------------------------------------------------------------------------- 1 | import { createRequire } from 'module' 2 | import $ from 'tinyspawn' 3 | import test from 'ava' 4 | 5 | const pkg = createRequire(import.meta.url)('../package.json') 6 | 7 | const evalScript = (code, flags = []) => $('node', ['--eval', code, ...flags]).then(({ stdout }) => stdout) 8 | evalScript.esm = code => evalScript(code, ['--input-type', 'module']) 9 | 10 | const sort = array => array.sort((a, b) => a.localeCompare(b)) 11 | 12 | test('cjs', async t => { 13 | // eslint-disable-next-line no-template-curly-in-string 14 | t.is((await evalScript("console.log(`mql v${require('@microlink/mql').version}`)")), `mql v${pkg.version}`) 15 | 16 | const methods = sort(JSON.parse((await evalScript("console.log(JSON.stringify(Object.keys(require('@microlink/mql'))))")))) 17 | t.deepEqual(methods, 18 | [ 19 | 'buffer', 20 | 'extend', 21 | 'fetchFromApi', 22 | 'getApiUrl', 23 | 'mapRules', 24 | 'MicrolinkError', 25 | 'render', 26 | 'stream', 27 | 'version' 28 | ]) 29 | }) 30 | 31 | test('esm', async t => { 32 | // eslint-disable-next-line no-template-curly-in-string 33 | t.is((await evalScript.esm("import {version} from '@microlink/mql'; console.log(`mql v${version}`)")), `mql v${pkg.version}`) 34 | 35 | const methods = sort(JSON.parse((await evalScript('import("@microlink/mql").then(mql => console.log(JSON.stringify(Object.keys(mql))))')))) 36 | 37 | t.deepEqual(methods, 38 | [ 39 | 'arrayBuffer', 40 | 'default', 41 | 'extend', 42 | 'fetchFromApi', 43 | 'getApiUrl', 44 | 'mapRules', 45 | 'MicrolinkError', 46 | 'version' 47 | ] 48 | ) 49 | 50 | t.is((await evalScript.esm("import {getApiUrl} from '@microlink/mql'; console.log(typeof getApiUrl)")), 'function') 51 | }) 52 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | contributors: 10 | if: "${{ github.event.head_commit.message != 'build: contributors' }}" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v6 15 | with: 16 | fetch-depth: 0 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | - name: Setup Node.js 19 | uses: actions/setup-node@v6 20 | with: 21 | node-version: lts/* 22 | - name: Contributors 23 | run: | 24 | git config --global user.email ${{ secrets.GIT_EMAIL }} 25 | git config --global user.name ${{ secrets.GIT_USERNAME }} 26 | npm run contributors 27 | - name: Push changes 28 | run: | 29 | git push origin ${{ github.head_ref }} 30 | 31 | test: 32 | if: | 33 | !startsWith(github.event.head_commit.message, 'chore(release):') && 34 | !startsWith(github.event.head_commit.message, 'docs:') && 35 | !startsWith(github.event.head_commit.message, 'ci:') 36 | needs: [contributors] 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v6 41 | with: 42 | token: ${{ secrets.GITHUB_TOKEN }} 43 | - name: Setup Node.js 44 | uses: actions/setup-node@v6 45 | with: 46 | node-version: lts/* 47 | - name: Setup PNPM 48 | uses: pnpm/action-setup@v4 49 | with: 50 | version: latest 51 | run_install: true 52 | - name: Test 53 | run: npm test 54 | - name: Report 55 | run: npx c8 report --reporter=text-lcov > coverage/lcov.info 56 | - name: Coverage 57 | uses: coverallsapp/github-action@main 58 | with: 59 | github-token: ${{ secrets.GITHUB_TOKEN }} 60 | -------------------------------------------------------------------------------- /src/lightweight.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { flattie: flatten } = require('flattie') 4 | const { default: ky } = require('ky') 5 | 6 | const factory = require('./factory')('arrayBuffer') 7 | 8 | class MicrolinkError extends Error { 9 | constructor (props) { 10 | super() 11 | this.name = 'MicrolinkError' 12 | Object.assign(this, props) 13 | this.description = this.message 14 | this.message = this.code 15 | ? `${this.code}, ${this.description}` 16 | : this.description 17 | } 18 | } 19 | 20 | const got = async (url, { responseType, ...opts }) => { 21 | try { 22 | if (opts.timeout === undefined) opts.timeout = false 23 | const response = await ky(url, opts) 24 | const body = await response[responseType]() 25 | const { headers, status: statusCode } = response 26 | return { url: response.url, body, headers, statusCode } 27 | } catch (error) { 28 | if (error.response) { 29 | const { response } = error 30 | error.response = { 31 | ...response, 32 | headers: Array.from(response.headers.entries()).reduce( 33 | (acc, [key, value]) => { 34 | acc[key] = value 35 | return acc 36 | }, 37 | {} 38 | ), 39 | statusCode: response.status, 40 | body: await response.text() 41 | } 42 | } 43 | throw error 44 | } 45 | } 46 | 47 | got.stream = (...args) => ky(...args).then(res => res.body) 48 | 49 | const mql = factory({ 50 | MicrolinkError, 51 | got, 52 | flatten, 53 | VERSION: '__MQL_VERSION__' 54 | }) 55 | 56 | module.exports = mql 57 | module.exports.arrayBuffer = mql.extend({ responseType: 'arrayBuffer' }) 58 | module.exports.extend = mql.extend 59 | module.exports.fetchFromApi = mql.fetchFromApi 60 | module.exports.getApiUrl = mql.getApiUrl 61 | module.exports.mapRules = mql.mapRules 62 | module.exports.MicrolinkError = mql.MicrolinkError 63 | module.exports.version = mql.version 64 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import nodeResolve from '@rollup/plugin-node-resolve' 2 | import { visualizer } from 'rollup-plugin-visualizer' 3 | import commonjs from '@rollup/plugin-commonjs' 4 | import filesize from 'rollup-plugin-filesize' 5 | import replace from '@rollup/plugin-replace' 6 | import rewrite from 'rollup-plugin-rewrite' 7 | import terser from '@rollup/plugin-terser' 8 | 9 | const rewriteFlattie = () => 10 | rewrite({ 11 | find: /.* from 'flattie'/gm, 12 | replace: match => match[0].replace('import', 'import * as') 13 | }) 14 | 15 | const build = ({ input, output, plugins = [], compress }) => { 16 | return { 17 | input, 18 | output, 19 | plugins: [ 20 | replace({ 21 | values: { 22 | "require('../package.json').version": "'__MQL_VERSION__'", 23 | __MQL_VERSION__: require('./package.json').version 24 | } 25 | }), 26 | ...plugins, 27 | compress && 28 | terser({ 29 | format: { 30 | comments: false 31 | } 32 | }), 33 | filesize(), 34 | visualizer() 35 | ] 36 | } 37 | } 38 | 39 | const builds = [ 40 | /* This build is just for testing using ESM interface */ 41 | build({ 42 | input: './src/node.js', 43 | output: { file: 'src/node.mjs', format: 'es' }, 44 | plugins: [commonjs({ strictRequires: 'auto' }), rewriteFlattie()] 45 | }), 46 | build({ 47 | compress: false, 48 | input: 'src/lightweight.js', 49 | output: { file: 'lightweight/index.js', format: 'es' }, 50 | plugins: [ 51 | nodeResolve({ 52 | mainFields: ['browser', 'module', 'main'] 53 | }), 54 | commonjs({ strictRequires: 'auto' }) 55 | ] 56 | }), 57 | build({ 58 | compress: false, 59 | input: 'src/lightweight.js', 60 | output: { name: 'mql', file: 'lightweight/index.umd.js', format: 'umd' }, 61 | plugins: [ 62 | nodeResolve({ 63 | mainFields: ['browser', 'module', 'main'] 64 | }), 65 | commonjs({ strictRequires: 'auto' }) 66 | ] 67 | }) 68 | ] 69 | 70 | export default builds 71 | -------------------------------------------------------------------------------- /test/node.test-d.ts: -------------------------------------------------------------------------------- 1 | import mql, { MicrolinkError, version } from '../src/node' 2 | import type { MqlError } from '../lightweight' 3 | 4 | /** version */ 5 | 6 | ;(async () => { 7 | console.log(version) 8 | })() 9 | 10 | /** error */ 11 | 12 | ;({ 13 | status: 'error', 14 | data: { url: 'fetch failed' }, 15 | more: 'https://microlink.io/efatalclient', 16 | code: 'EFATALCLIENT', 17 | url: 'https://localhost.microlink.io?url=https%3A%2F%2Fexample.com%23t%3D1696503516588', 18 | statusCode: undefined, 19 | headers: {}, 20 | name: 'MicrolinkError', 21 | message: 'EFATALCLIENT, fetch failed', 22 | description: 'fetch failed' 23 | } as MqlError) 24 | 25 | ;(async () => { 26 | const error = new MicrolinkError({ 27 | status: 'fail', 28 | data: { url: 'something goes wrong' }, 29 | more: 'https://microlink.io/einvalurlclient', 30 | code: 'EINVALURLCLIENT', 31 | message: 'something goes wrong', 32 | url: 'https://example.com' 33 | }) 34 | 35 | console.log(error.status) 36 | console.log(error.data) 37 | console.log(error.more) 38 | console.log(error.code) 39 | console.log(error.url) 40 | console.log(error.description) 41 | })() 42 | 43 | /** mql */ 44 | 45 | ;(async () => { 46 | const result = await mql('https://example.com', { 47 | endpoint: 'https://pro.microlink.io', 48 | apiKey: '123', 49 | retry: 2, 50 | cache: new Map() 51 | }) 52 | 53 | console.log(result.status) 54 | console.log(result.data) 55 | console.log(result.headers) 56 | console.log(result.response) 57 | console.log(result.response.isFromCache) 58 | })() 59 | 60 | ;(async () => { 61 | const response = await mql('https://example.com', { 62 | stream: 'screenshot', 63 | screenshot: true 64 | }) 65 | console.log(response.url) 66 | console.log(response.body) 67 | console.log(response.headers) 68 | console.log(response.statusCode) 69 | })() 70 | 71 | /** got options */ 72 | 73 | await mql( 74 | 'https://example.com', 75 | { meta: true }, 76 | { 77 | timeout: 1000 78 | } 79 | ) 80 | 81 | /** stream */ 82 | 83 | mql.stream('https://cdn.microlink.io/logo/logo.png', { 84 | headers: { 85 | accept: 'image/webp' 86 | } 87 | }) 88 | -------------------------------------------------------------------------------- /test/snapshots/opts.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/opts.js` 2 | 3 | The actual snapshot is saved in `opts.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## node » url 8 | 9 | > Snapshot 1 10 | 11 | 'success' 12 | 13 | > Snapshot 2 14 | 15 | { 16 | author: 'kikobeats', 17 | description: 'A millennial doing stuff on internet that ships software every day and builds digital products.', 18 | image: { 19 | height: 1888, 20 | size: 620881, 21 | size_pretty: '621 kB', 22 | type: 'png', 23 | url: 'https://kikobeats.com/images/resume.png', 24 | width: 3838, 25 | }, 26 | lang: 'en', 27 | logo: { 28 | height: 500, 29 | size: 30285, 30 | size_pretty: '30.3 kB', 31 | type: 'jpg', 32 | url: 'https://kikobeats.com/images/avatar-glitch.jpg', 33 | width: 500, 34 | }, 35 | publisher: 'kikobeats', 36 | title: 'Kikobeats', 37 | url: 'https://kikobeats.com/', 38 | } 39 | 40 | > Snapshot 3 41 | 42 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com%3Fref%3Dmql' 43 | 44 | > Snapshot 4 45 | 46 | 200 47 | 48 | ## lightweight » url 49 | 50 | > Snapshot 1 51 | 52 | 'success' 53 | 54 | > Snapshot 2 55 | 56 | { 57 | author: 'kikobeats', 58 | description: 'A millennial doing stuff on internet that ships software every day and builds digital products.', 59 | image: { 60 | height: 1888, 61 | size: 620881, 62 | size_pretty: '621 kB', 63 | type: 'png', 64 | url: 'https://kikobeats.com/images/resume.png', 65 | width: 3838, 66 | }, 67 | lang: 'en', 68 | logo: { 69 | height: 500, 70 | size: 30285, 71 | size_pretty: '30.3 kB', 72 | type: 'jpg', 73 | url: 'https://kikobeats.com/images/avatar-glitch.jpg', 74 | width: 500, 75 | }, 76 | publisher: 'kikobeats', 77 | title: 'Kikobeats', 78 | url: 'https://kikobeats.com/', 79 | } 80 | 81 | > Snapshot 3 82 | 83 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com%3Fref%3Dmql' 84 | 85 | > Snapshot 4 86 | 87 | 200 88 | -------------------------------------------------------------------------------- /test/snapshots/opts.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/opts.mjs` 2 | 3 | The actual snapshot is saved in `opts.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## node » url 8 | 9 | > Snapshot 1 10 | 11 | 'success' 12 | 13 | > Snapshot 2 14 | 15 | { 16 | author: 'kikobeats', 17 | description: 'A millennial doing stuff on internet that ships software every day and builds digital products.', 18 | image: { 19 | height: 1888, 20 | size: 620881, 21 | size_pretty: '621 kB', 22 | type: 'png', 23 | url: 'https://kikobeats.com/images/resume.png', 24 | width: 3838, 25 | }, 26 | lang: 'en', 27 | logo: { 28 | height: 500, 29 | size: 30285, 30 | size_pretty: '30.3 kB', 31 | type: 'jpg', 32 | url: 'https://kikobeats.com/images/avatar-glitch.jpg', 33 | width: 500, 34 | }, 35 | publisher: 'kikobeats', 36 | title: 'Kikobeats', 37 | url: 'https://kikobeats.com/', 38 | } 39 | 40 | > Snapshot 3 41 | 42 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com%3Fref%3Dmql' 43 | 44 | > Snapshot 4 45 | 46 | 200 47 | 48 | ## lightweight » url 49 | 50 | > Snapshot 1 51 | 52 | 'success' 53 | 54 | > Snapshot 2 55 | 56 | { 57 | author: 'kikobeats', 58 | description: 'A millennial doing stuff on internet that ships software every day and builds digital products.', 59 | image: { 60 | height: 1888, 61 | size: 620881, 62 | size_pretty: '621 kB', 63 | type: 'png', 64 | url: 'https://kikobeats.com/images/resume.png', 65 | width: 3838, 66 | }, 67 | lang: 'en', 68 | logo: { 69 | height: 500, 70 | size: 30285, 71 | size_pretty: '30.3 kB', 72 | type: 'jpg', 73 | url: 'https://kikobeats.com/images/avatar-glitch.jpg', 74 | width: 500, 75 | }, 76 | publisher: 'kikobeats', 77 | title: 'Kikobeats', 78 | url: 'https://kikobeats.com/', 79 | } 80 | 81 | > Snapshot 3 82 | 83 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com%3Fref%3Dmql' 84 | 85 | > Snapshot 4 86 | 87 | 200 88 | -------------------------------------------------------------------------------- /test/error/server.mjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { listen } from 'async-listen' 4 | import http from 'http' 5 | import test from 'ava' 6 | 7 | import clients from '../clients.mjs' 8 | 9 | clients.forEach(({ constructor: mql, target }) => { 10 | test(`${target} » server side unexpected`, async t => { 11 | const server = http.createServer((req, res) => { 12 | res.statusCode = 503 13 | res.end('503 Service Unavailable') 14 | }) 15 | 16 | const endpoint = await listen(server) 17 | 18 | const error = await t.throwsAsync( 19 | mql('https://microlink.io', { endpoint, retry: 0 }), 20 | { 21 | instanceOf: mql.MicrolinkError 22 | } 23 | ) 24 | 25 | t.true(!!error.url) 26 | t.true(!!error.code) 27 | t.true(!!error.status) 28 | t.true(!!error.more) 29 | t.true(!!error.statusCode) 30 | t.true(!!error.data) 31 | t.true(!!error.message) 32 | t.true(!!error.description) 33 | }) 34 | 35 | test(`${target} » server side generic error`, async t => { 36 | const error = await t.throwsAsync( 37 | mql('https://microlink.io', { ttl: 100, retry: 0 }), 38 | { 39 | instanceOf: mql.MicrolinkError 40 | } 41 | ) 42 | 43 | t.true(!!error.url) 44 | t.true(!!error.code) 45 | t.true(!!error.status) 46 | t.true(!!error.more) 47 | t.true(!!error.statusCode) 48 | t.true(!!error.data) 49 | t.true(!!error.message) 50 | t.true(!!error.description) 51 | }) 52 | 53 | test(`${target} » server side timeout`, async t => { 54 | const error = await t.throwsAsync( 55 | mql('https://kikobeats.com', { 56 | timeout: 50, 57 | force: true, 58 | screenshot: true 59 | }, { retry: 0 }), 60 | { instanceOf: mql.MicrolinkError } 61 | ) 62 | 63 | t.true(!!error.url) 64 | t.true(!!error.code) 65 | t.true(!!error.status) 66 | t.true(!!error.more) 67 | t.true(!!error.statusCode) 68 | t.true(!!error.data) 69 | t.true(!!error.message) 70 | t.true(!!error.description) 71 | }) 72 | 73 | test(`${target} » EINVALURL`, async t => { 74 | const error = await t.throwsAsync(mql('https://invalid-link', {}), { 75 | instanceOf: mql.MicrolinkError 76 | }) 77 | 78 | t.true(!!error.url) 79 | t.true(!!error.code) 80 | t.true(!!error.status) 81 | t.true(!!error.more) 82 | t.true(!!error.statusCode) 83 | t.true(!!error.data) 84 | t.true(!!error.message) 85 | t.true(!!error.description) 86 | }) 87 | }) 88 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Microlink Query Language 9 | 10 | 11 | 37 | 38 | 39 | 40 |
41 | 69 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /test/rules.mjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import test from 'ava' 4 | 5 | import clients from './clients.mjs' 6 | 7 | clients.forEach(({ constructor: mql, target }) => { 8 | test(`${target} » no rules`, t => { 9 | t.deepEqual(mql.mapRules(), undefined) 10 | t.deepEqual(mql.mapRules(false), undefined) 11 | t.deepEqual(mql.mapRules('false'), undefined) 12 | t.deepEqual(mql.mapRules({}), {}) 13 | t.deepEqual(mql.mapRules([]), {}) 14 | }) 15 | 16 | test(`${target} » object selector`, t => { 17 | t.deepEqual( 18 | mql.mapRules({ 19 | avatar: { 20 | type: 'image', 21 | selectors: { 22 | selector: '#avatar', 23 | attr: 'src' 24 | } 25 | } 26 | }), 27 | { 28 | 'data.avatar.selectors.selector': '#avatar', 29 | 'data.avatar.selectors.attr': 'src', 30 | 'data.avatar.type': 'image' 31 | } 32 | ) 33 | }) 34 | 35 | test(`${target} » single selector`, t => { 36 | t.deepEqual( 37 | mql.mapRules({ 38 | avatar: { 39 | type: 'image', 40 | selectors: [ 41 | { 42 | selector: '#avatar', 43 | attr: 'src' 44 | } 45 | ] 46 | } 47 | }), 48 | { 49 | 'data.avatar.selectors.0.selector': '#avatar', 50 | 'data.avatar.selectors.0.attr': 'src', 51 | 'data.avatar.type': 'image' 52 | } 53 | ) 54 | }) 55 | 56 | test(`${target} » multiple selector`, t => { 57 | t.deepEqual( 58 | mql.mapRules({ 59 | avatar: { 60 | type: 'image', 61 | selectors: [ 62 | { 63 | selector: '#avatar', 64 | attr: 'src' 65 | }, 66 | { 67 | selector: '.avatar', 68 | attr: 'href' 69 | } 70 | ] 71 | } 72 | }), 73 | { 74 | 'data.avatar.selectors.0.selector': '#avatar', 75 | 'data.avatar.selectors.0.attr': 'src', 76 | 'data.avatar.selectors.1.selector': '.avatar', 77 | 'data.avatar.selectors.1.attr': 'href', 78 | 'data.avatar.type': 'image' 79 | } 80 | ) 81 | }) 82 | 83 | test(`${target} » multiple rules`, t => { 84 | t.deepEqual( 85 | mql.mapRules({ 86 | avatar: { 87 | type: 'image', 88 | selectors: [ 89 | { 90 | selector: '#avatar', 91 | attr: 'src' 92 | }, 93 | { 94 | selector: '.avatar', 95 | attr: 'href' 96 | } 97 | ] 98 | }, 99 | logo: { 100 | type: 'image', 101 | selectors: [ 102 | { 103 | selector: '#logo', 104 | attr: 'src' 105 | }, 106 | { 107 | selector: '.logo', 108 | attr: 'href' 109 | } 110 | ] 111 | } 112 | }), 113 | { 114 | 'data.avatar.selectors.0.selector': '#avatar', 115 | 'data.avatar.selectors.0.attr': 'src', 116 | 'data.avatar.selectors.1.selector': '.avatar', 117 | 'data.avatar.selectors.1.attr': 'href', 118 | 'data.avatar.type': 'image', 119 | 'data.logo.selectors.0.selector': '#logo', 120 | 'data.logo.selectors.0.attr': 'src', 121 | 'data.logo.selectors.1.selector': '.logo', 122 | 'data.logo.selectors.1.attr': 'href', 123 | 'data.logo.type': 'image' 124 | } 125 | ) 126 | }) 127 | }) 128 | -------------------------------------------------------------------------------- /src/factory.js: -------------------------------------------------------------------------------- 1 | const ENDPOINT = { 2 | FREE: 'https://api.microlink.io/', 3 | PRO: 'https://pro.microlink.io/' 4 | } 5 | 6 | const isObject = input => input !== null && typeof input === 'object' 7 | 8 | const isBuffer = input => 9 | input != null && 10 | input.constructor != null && 11 | typeof input.constructor.isBuffer === 'function' && 12 | input.constructor.isBuffer(input) 13 | 14 | const parseBody = (input, error, url) => { 15 | try { 16 | return JSON.parse(input) 17 | } catch (_) { 18 | const message = input || error.message 19 | 20 | return { 21 | status: 'error', 22 | data: { url: message }, 23 | more: 'https://microlink.io/efatalclient', 24 | code: 'EFATALCLIENT', 25 | message, 26 | url 27 | } 28 | } 29 | } 30 | 31 | const isURL = url => { 32 | try { 33 | return /^https?:\/\//i.test(new URL(url).href) 34 | } catch (_) { 35 | return false 36 | } 37 | } 38 | 39 | const factory = streamResponseType => ({ 40 | VERSION, 41 | MicrolinkError, 42 | got, 43 | flatten 44 | }) => { 45 | const assertUrl = (url = '') => { 46 | if (!isURL(url)) { 47 | const message = `The \`url\` as \`${url}\` is not valid. Ensure it has protocol (http or https) and hostname.` 48 | throw new MicrolinkError({ 49 | status: 'fail', 50 | data: { url: message }, 51 | more: 'https://microlink.io/einvalurlclient', 52 | code: 'EINVALURLCLIENT', 53 | message, 54 | url 55 | }) 56 | } 57 | } 58 | 59 | const mapRules = rules => { 60 | if (!isObject(rules)) return 61 | const flatRules = flatten(rules) 62 | return Object.keys(flatRules).reduce((acc, key) => { 63 | acc[`data.${key}`] = flatRules[key].toString() 64 | return acc 65 | }, {}) 66 | } 67 | 68 | const fetchFromApi = async (apiUrl, opts = {}) => { 69 | try { 70 | const response = await got(apiUrl, opts) 71 | return opts.responseType === streamResponseType 72 | ? response 73 | : { ...response.body, response } 74 | } catch (error) { 75 | const { response = {} } = error 76 | const { 77 | statusCode, 78 | body: rawBody, 79 | headers = {}, 80 | url: uri = apiUrl 81 | } = response 82 | const isBodyBuffer = isBuffer(rawBody) 83 | 84 | const body = 85 | isObject(rawBody) && !isBodyBuffer 86 | ? rawBody 87 | : parseBody(isBodyBuffer ? rawBody.toString() : rawBody, error, uri) 88 | 89 | throw new MicrolinkError({ 90 | ...body, 91 | message: body.message, 92 | url: uri, 93 | statusCode, 94 | headers 95 | }) 96 | } 97 | } 98 | 99 | const getApiUrl = ( 100 | url, 101 | { data, apiKey, endpoint, ...opts } = {}, 102 | { responseType = 'json', headers: gotHeaders, ...gotOpts } = {} 103 | ) => { 104 | const isPro = !!apiKey 105 | const apiEndpoint = endpoint || ENDPOINT[isPro ? 'PRO' : 'FREE'] 106 | 107 | const apiUrl = `${apiEndpoint}?${new URLSearchParams({ 108 | url, 109 | ...mapRules(data), 110 | ...flatten(opts) 111 | }).toString()}` 112 | 113 | const headers = isPro 114 | ? { ...gotHeaders, 'x-api-key': apiKey } 115 | : { ...gotHeaders } 116 | 117 | if (opts.stream) { 118 | responseType = streamResponseType 119 | } 120 | return [apiUrl, { ...gotOpts, responseType, headers }] 121 | } 122 | 123 | const createMql = defaultOpts => async (url, opts, gotOpts) => { 124 | assertUrl(url) 125 | const [apiUrl, fetchOpts] = getApiUrl(url, opts, { 126 | ...defaultOpts, 127 | ...gotOpts 128 | }) 129 | return fetchFromApi(apiUrl, fetchOpts) 130 | } 131 | 132 | const mql = createMql() 133 | mql.extend = createMql 134 | mql.MicrolinkError = MicrolinkError 135 | mql.getApiUrl = getApiUrl 136 | mql.fetchFromApi = fetchFromApi 137 | mql.mapRules = mapRules 138 | mql.version = VERSION 139 | mql.stream = got.stream 140 | 141 | return mql 142 | } 143 | 144 | module.exports = factory 145 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@microlink/mql", 3 | "description": "Microlink Query Language. The official HTTP client to interact with Microlink API for Node.js, browsers & Deno.", 4 | "homepage": "https://microlink.io/mql", 5 | "version": "0.14.0", 6 | "types": "lightweight/index.d.ts", 7 | "exports": { 8 | "require": "./src/node.js", 9 | "default": "./lightweight/index.js" 10 | }, 11 | "author": { 12 | "email": "josefrancisco.verdu@gmail.com", 13 | "name": "Kiko Beats", 14 | "url": "https://github.com/Kikobeats" 15 | }, 16 | "contributors": [ 17 | { 18 | "name": "Dani de la Cruz", 19 | "email": "5173869+delacruz-dev@users.noreply.github.com" 20 | }, 21 | { 22 | "name": "ndom91", 23 | "email": "yo@ndo.dev" 24 | }, 25 | { 26 | "name": "Askar Yusupov", 27 | "email": "devex.soft@gmail.com" 28 | }, 29 | { 30 | "name": "Gabe O'Leary", 31 | "email": "oleary.gabe@gmail.com" 32 | } 33 | ], 34 | "repository": { 35 | "type": "git", 36 | "url": "git+https://github.com/microlinkhq/mql.git" 37 | }, 38 | "bugs": { 39 | "url": "https://github.com/microlinkhq/mql/issues" 40 | }, 41 | "keywords": [ 42 | "api", 43 | "language", 44 | "microlink", 45 | "mql", 46 | "query" 47 | ], 48 | "dependencies": { 49 | "flattie": "~1.1.1", 50 | "got": "~11.8.6", 51 | "whoops": "~5.0.1" 52 | }, 53 | "devDependencies": { 54 | "@commitlint/cli": "latest", 55 | "@commitlint/config-conventional": "latest", 56 | "@ksmithut/prettier-standard": "latest", 57 | "@rollup/plugin-commonjs": "latest", 58 | "@rollup/plugin-node-resolve": "latest", 59 | "@rollup/plugin-replace": "latest", 60 | "@rollup/plugin-terser": "latest", 61 | "async-listen": "latest", 62 | "ava": "5", 63 | "c8": "latest", 64 | "ci-publish": "latest", 65 | "git-authors-cli": "latest", 66 | "github-generate-release": "latest", 67 | "ky": "latest", 68 | "nano-staged": "latest", 69 | "prettier-standard": "latest", 70 | "rollup": "latest", 71 | "rollup-plugin-filesize": "latest", 72 | "rollup-plugin-rewrite": "latest", 73 | "rollup-plugin-visualizer": "latest", 74 | "simple-git-hooks": "latest", 75 | "standard": "latest", 76 | "standard-markdown": "latest", 77 | "standard-version": "latest", 78 | "stream-to-promise": "latest", 79 | "tsd": "latest" 80 | }, 81 | "engines": { 82 | "node": ">= 18" 83 | }, 84 | "files": [ 85 | "lightweight", 86 | "src/factory.js", 87 | "src/node.js" 88 | ], 89 | "scripts": { 90 | "build": "rollup -c rollup.config.js --bundleConfigAsCjs", 91 | "clean": "rm -rf node_modules", 92 | "clean:build": "rm -rf lightweight/index.js", 93 | "contributors": "(npx git-authors-cli && npx finepack && git add package.json && git commit -m 'build: contributors' --no-verify) || true", 94 | "dev": "npm run build -- -w", 95 | "lint": "standard && tsd", 96 | "postrelease": "npm run release:tags && npm run release:github && (ci-publish || npm publish --access=public)", 97 | "prebuild": "npm run clean:build", 98 | "prepublishOnly": "npm run build", 99 | "pretest": "npm run lint && npm run build", 100 | "release": "standard-version -a", 101 | "release:github": "github-generate-release", 102 | "release:tags": "git push --follow-tags origin HEAD:master", 103 | "test": "c8 ava --verbose" 104 | }, 105 | "license": "MIT", 106 | "ava": { 107 | "files": [ 108 | "test/**/*", 109 | "!test/clients.mjs" 110 | ], 111 | "timeout": "1m" 112 | }, 113 | "commitlint": { 114 | "extends": [ 115 | "@commitlint/config-conventional" 116 | ], 117 | "rules": { 118 | "body-max-line-length": [ 119 | 0 120 | ] 121 | } 122 | }, 123 | "nano-staged": { 124 | "*.js": [ 125 | "prettier-standard", 126 | "standard --fix" 127 | ], 128 | "*.md": [ 129 | "standard-markdown" 130 | ], 131 | "package.json": [ 132 | "finepack" 133 | ] 134 | }, 135 | "simple-git-hooks": { 136 | "commit-msg": "npx commitlint --edit", 137 | "pre-commit": "npx nano-staged" 138 | }, 139 | "standard": { 140 | "ignore": [ 141 | "lightweight/index.js", 142 | "lightweight/index.umd.js", 143 | "src/node.mjs" 144 | ] 145 | }, 146 | "tsd": { 147 | "directory": "test" 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /test/snapshots/get-api-url.js.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/get-api-url.js` 2 | 3 | The actual snapshot is saved in `get-api-url.js.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## node » url without query params 8 | 9 | > Snapshot 1 10 | 11 | [ 12 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 13 | { 14 | cache: undefined, 15 | headers: {}, 16 | responseType: 'json', 17 | retry: undefined, 18 | }, 19 | ] 20 | 21 | ## node » apiKey 22 | 23 | > Snapshot 1 24 | 25 | [ 26 | 'https://pro.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 27 | { 28 | cache: undefined, 29 | headers: { 30 | 'x-api-key': 'foobar', 31 | }, 32 | responseType: 'json', 33 | retry: undefined, 34 | }, 35 | ] 36 | 37 | ## node » flatten options 38 | 39 | > Snapshot 1 40 | 41 | [ 42 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&overlay.browser=dark', 43 | { 44 | cache: undefined, 45 | headers: {}, 46 | responseType: 'json', 47 | retry: undefined, 48 | }, 49 | ] 50 | 51 | ## node » don't pass null 52 | 53 | > Snapshot 1 54 | 55 | [ 56 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 57 | { 58 | cache: undefined, 59 | headers: {}, 60 | responseType: 'json', 61 | retry: undefined, 62 | }, 63 | ] 64 | 65 | > Snapshot 2 66 | 67 | [ 68 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 69 | { 70 | cache: undefined, 71 | headers: {}, 72 | responseType: 'json', 73 | retry: undefined, 74 | }, 75 | ] 76 | 77 | ## node » don't pass undefined 78 | 79 | > Snapshot 1 80 | 81 | [ 82 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 83 | { 84 | cache: undefined, 85 | headers: {}, 86 | responseType: 'json', 87 | retry: undefined, 88 | }, 89 | ] 90 | 91 | > Snapshot 2 92 | 93 | [ 94 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 95 | { 96 | cache: undefined, 97 | headers: {}, 98 | responseType: 'json', 99 | retry: undefined, 100 | }, 101 | ] 102 | 103 | ## node » timeout 104 | 105 | > Snapshot 1 106 | 107 | [ 108 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=15000', 109 | { 110 | cache: undefined, 111 | headers: {}, 112 | responseType: 'json', 113 | retry: undefined, 114 | }, 115 | ] 116 | 117 | > Snapshot 2 118 | 119 | [ 120 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=28000', 121 | { 122 | cache: undefined, 123 | headers: {}, 124 | responseType: 'json', 125 | retry: undefined, 126 | }, 127 | ] 128 | 129 | ## lightweight » url without query params 130 | 131 | > Snapshot 1 132 | 133 | [ 134 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 135 | { 136 | cache: undefined, 137 | headers: {}, 138 | responseType: 'json', 139 | retry: undefined, 140 | }, 141 | ] 142 | 143 | ## lightweight » apiKey 144 | 145 | > Snapshot 1 146 | 147 | [ 148 | 'https://pro.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 149 | { 150 | cache: undefined, 151 | headers: { 152 | 'x-api-key': 'foobar', 153 | }, 154 | responseType: 'json', 155 | retry: undefined, 156 | }, 157 | ] 158 | 159 | ## lightweight » flatten options 160 | 161 | > Snapshot 1 162 | 163 | [ 164 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&overlay.browser=dark', 165 | { 166 | cache: undefined, 167 | headers: {}, 168 | responseType: 'json', 169 | retry: undefined, 170 | }, 171 | ] 172 | 173 | ## lightweight » don't pass null 174 | 175 | > Snapshot 1 176 | 177 | [ 178 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 179 | { 180 | cache: undefined, 181 | headers: {}, 182 | responseType: 'json', 183 | retry: undefined, 184 | }, 185 | ] 186 | 187 | > Snapshot 2 188 | 189 | [ 190 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 191 | { 192 | cache: undefined, 193 | headers: {}, 194 | responseType: 'json', 195 | retry: undefined, 196 | }, 197 | ] 198 | 199 | ## lightweight » don't pass undefined 200 | 201 | > Snapshot 1 202 | 203 | [ 204 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 205 | { 206 | cache: undefined, 207 | headers: {}, 208 | responseType: 'json', 209 | retry: undefined, 210 | }, 211 | ] 212 | 213 | > Snapshot 2 214 | 215 | [ 216 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 217 | { 218 | cache: undefined, 219 | headers: {}, 220 | responseType: 'json', 221 | retry: undefined, 222 | }, 223 | ] 224 | 225 | ## lightweight » timeout 226 | 227 | > Snapshot 1 228 | 229 | [ 230 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=15000', 231 | { 232 | cache: undefined, 233 | headers: {}, 234 | responseType: 'json', 235 | retry: undefined, 236 | }, 237 | ] 238 | 239 | > Snapshot 2 240 | 241 | [ 242 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=28000', 243 | { 244 | cache: undefined, 245 | headers: {}, 246 | responseType: 'json', 247 | retry: undefined, 248 | }, 249 | ] 250 | -------------------------------------------------------------------------------- /test/lightweight.test-d.ts: -------------------------------------------------------------------------------- 1 | import mql, { MicrolinkError, version } from '../lightweight' 2 | import type { MqlError } from '../lightweight' 3 | 4 | /** version */ 5 | 6 | ;(async () => { 7 | console.log(version) 8 | })() 9 | 10 | 11 | /** error */ 12 | 13 | ;(async () => { 14 | const error = new MicrolinkError({ 15 | status: 'fail', 16 | data: { url: 'something goes wrong' }, 17 | more: 'https://microlink.io/einvalurlclient', 18 | code: 'EINVALURLCLIENT', 19 | message: 'something goes wrong', 20 | url: 'https://example.com' 21 | }) 22 | 23 | console.log(error.status) 24 | console.log(error.data) 25 | console.log(error.more) 26 | console.log(error.code) 27 | console.log(error.url) 28 | console.log(error.description) 29 | })() 30 | 31 | /** mql */ 32 | 33 | ;(async () => { 34 | const result = await mql('https://example.com', { 35 | endpoint: 'https://pro.microlink.io', 36 | apiKey: '123', 37 | retry: 2, 38 | cache: new Map() 39 | }) 40 | 41 | console.log(result.status) 42 | console.log(result.data) 43 | console.log(result.headers) 44 | console.log(result.response) 45 | console.log(result.response.statusCode) 46 | })() 47 | 48 | ;(async () => { 49 | const response = await mql('https://example.com', { 50 | stream: 'screenshot', 51 | screenshot: true 52 | }) 53 | console.log(response.url) 54 | console.log(response.body) 55 | console.log(response.headers) 56 | console.log(response.statusCode) 57 | })() 58 | 59 | /** data */ 60 | 61 | mql('https://example.com', { 62 | data: { 63 | version: { 64 | evaluate: 'window.next.version', 65 | type: 'string' 66 | } 67 | } 68 | }) 69 | 70 | mql('https://github.com/microlinkhq', { 71 | data: { 72 | stats: { 73 | selector: '.application-main', 74 | attr: { 75 | followers: { 76 | selector: '.js-profile-editable-area a[href*="tab=followers"] span', 77 | type: 'number' 78 | }, 79 | following: { 80 | selector: '.js-profile-editable-area a[href*="tab=following"] span', 81 | type: 'number' 82 | }, 83 | stars: { 84 | selector: '.js-responsive-underlinenav a[data-tab-item="stars"] span', 85 | type: 'number' 86 | } 87 | } 88 | } 89 | } 90 | }) 91 | 92 | /** meta */ 93 | 94 | mql('https://example.com') 95 | mql('https://example.com', { meta: true }) 96 | mql('https://example.com', { meta: { logo: { square: true } } }) 97 | 98 | /** pdf */ 99 | 100 | mql('https://example.com', { pdf: true }) 101 | mql('https://example.com', { 102 | pdf: { 103 | format: 'A4', 104 | width: '480px', 105 | margin: { 106 | top: '4mm', 107 | bottom: '4mm', 108 | left: '4mm', 109 | right: '4mm' 110 | } 111 | } 112 | }) 113 | 114 | /** screenshot */ 115 | 116 | mql('https://example.com', { screenshot: true }) 117 | mql('https://example.com', { 118 | screenshot: { 119 | codeScheme: 'atom-dark', 120 | type: 'png', 121 | optimizeForSpeed: true, 122 | overlay: { 123 | background: '#000', 124 | browser: 'light' 125 | } 126 | } 127 | }) 128 | 129 | /** others */ 130 | 131 | mql('https://example.com', { click: ['div'] }) 132 | mql('https://example.com', { 133 | adblock: true, 134 | animations: false, 135 | audio: true, 136 | video: true 137 | }) 138 | 139 | console.log(MicrolinkError) 140 | console.log(mql.version) 141 | 142 | /** response */ 143 | 144 | const result = await mql('https://example.com', { meta: true }) 145 | console.log(result.status) 146 | console.log(result.data) 147 | console.log(result.statusCode) 148 | console.log(result.headers) 149 | 150 | /** error */ 151 | 152 | ;({ 153 | status: 'error', 154 | data: { url: 'fetch failed' }, 155 | more: 'https://microlink.io/efatalclient', 156 | code: 'EFATALCLIENT', 157 | url: 'https://localhost.microlink.io?url=https%3A%2F%2Fexample.com%23t%3D1696503516588', 158 | statusCode: undefined, 159 | headers: {}, 160 | name: 'MicrolinkError', 161 | message: 'EFATALCLIENT, fetch failed', 162 | description: 'fetch failed' 163 | } as MqlError) 164 | 165 | ;({ 166 | status: 'fail', 167 | code: 'EAUTH', 168 | more: 'https://microlink.io/eauth', 169 | url: 'https://pro.microlink.io?url=https%3A%2F%2Fexample.com%23t%3D1696503754740', 170 | statusCode: 403, 171 | headers: { 172 | 'alt-svc': 'h3=":443"; ma=86400', 173 | 'cf-cache-status': 'BYPASS', 174 | 'cf-ray': '81152c548f405e54-MAD', 175 | connection: 'keep-alive', 176 | 'content-encoding': 'br', 177 | 'content-type': 'application/json', 178 | date: 'Thu, 05 Oct 2023 11:02:35 GMT', 179 | nel: '{"success_fraction":0,"report_to":"cf-nel","max_age":604800}', 180 | 'report-to': 181 | '{"endpoints":[{"url":"https:\\/\\/a.nel.cloudflare.com\\/report\\/v3?s=6tEv%2Fk7XkC0so782muCCxAfbFeaMusFvyv839c8Xv74aKQFy1jD%2Fd8hRrldtfntrhuzCi5HG8W%2FlBxk1a9qKqxHObl79FhxBnK6pAOF6gGXc9Vi0wHnXb1hayCkTxolfpR7yoH89el9W34r1T8E%3D"}],"group":"cf-nel","max_age":604800}', 182 | server: 'cloudflare', 183 | 'transfer-encoding': 'chunked', 184 | vary: 'Accept-Encoding', 185 | via: '1.1 2d741086cf4a760a29245ab77d5fa70a.cloudfront.net (CloudFront)', 186 | 'x-amz-apigw-id': 'MUynzHpvIAMEtpA=', 187 | 'x-amz-cf-id': 'YPxW5fWYSiPgHuOiocQyYkCFoxWDsuV4MtRBh1Yiym3m1b361Q2fgQ==', 188 | 'x-amz-cf-pop': 'MAD56-P2', 189 | 'x-amzn-errortype': 'ForbiddenException', 190 | 'x-amzn-requestid': '7990f38f-d004-49cc-a406-f7cd0cb7df07', 191 | 'x-cache': 'Error from cloudfront' 192 | }, 193 | name: 'MicrolinkError', 194 | message: 195 | 'EAUTH, Invalid API key. Make sure you are attaching your API key as `x-api-key` header.', 196 | description: 197 | 'Invalid API key. Make sure you are attaching your API key as `x-api-key` header.' 198 | } as MqlError) 199 | 200 | /* extend */ 201 | 202 | mql.extend({ responseType: 'text' }) 203 | 204 | /* stream */ 205 | 206 | mql.stream('https://example.com', { headers: { 'user-agent': 'foo' } }) 207 | 208 | /* arrraBuffer */ 209 | 210 | { 211 | const response = await mql.arrayBuffer('https://example.com', { meta: false }) 212 | console.log(response.body) 213 | } 214 | 215 | /* redirects */ 216 | 217 | { 218 | const response = await mql('https://example.com', { meta: false }) 219 | console.log(response.redirects) 220 | } 221 | -------------------------------------------------------------------------------- /lightweight/index.d.ts: -------------------------------------------------------------------------------- 1 | export type ColorScheme = 'dark' | 'light' 2 | 3 | type WaitUntilEvent = 4 | | 'load' 5 | | 'domcontentloaded' 6 | | 'networkidle0' 7 | | 'networkidle2' 8 | 9 | type PixelUnit = string | number 10 | 11 | type ScreenshotOverlay = { 12 | background?: string 13 | browser?: 'dark' | 'light' 14 | } 15 | 16 | type PdfMargin = { 17 | bottom?: string | number 18 | left?: string | number 19 | right?: string | number 20 | top?: string | number 21 | } 22 | 23 | type PdfOptions = { 24 | format?: string 25 | height?: PixelUnit 26 | landscape?: string 27 | margin?: string | PdfMargin 28 | pageRanges?: string 29 | scale?: number 30 | width?: PixelUnit 31 | } 32 | 33 | type ScreenshotOptions = { 34 | codeScheme?: string 35 | element?: string 36 | fullPage?: boolean 37 | omitBackground?: boolean 38 | optimizeForSpeed?: boolean 39 | overlay?: ScreenshotOverlay 40 | type?: 'jpeg' | 'png' 41 | } 42 | 43 | type MqlClientOptions = { 44 | apiKey?: string 45 | cache?: Map 46 | endpoint?: string 47 | retry?: number 48 | } 49 | 50 | type MqlQuery = { 51 | [field: string]: MqlQueryOptions 52 | } 53 | 54 | type MqlQueryOptions = { 55 | attr?: string | string[] | MqlQuery 56 | evaluate?: string 57 | selector?: string | string[] 58 | selectorAll?: string | string[] 59 | type?: 60 | | 'audio' 61 | | 'author' 62 | | 'auto' 63 | | 'boolean' 64 | | 'date' 65 | | 'description' 66 | | 'email' 67 | | 'image' 68 | | 'ip' 69 | | 'lang' 70 | | 'logo' 71 | | 'number' 72 | | 'object' 73 | | 'publisher' 74 | | 'regexp' 75 | | 'string' 76 | | 'title' 77 | | 'url' 78 | | 'video' 79 | } 80 | 81 | export type MicrolinkApiOptions = { 82 | adblock?: boolean 83 | animations?: boolean 84 | audio?: boolean 85 | click?: string | string[] 86 | colorScheme?: ColorScheme 87 | data?: MqlQuery 88 | device?: string 89 | embed?: string 90 | filename?: string 91 | filter?: string 92 | force?: boolean 93 | function?: string 94 | headers?: Record 95 | iframe?: boolean | { maxWidth?: number; maxHeight?: number } 96 | insights?: boolean | { lighthouse?: boolean | object; technologies?: boolean } 97 | javascript?: boolean 98 | mediaType?: string 99 | meta?: boolean | { logo: { square: boolean } } 100 | modules?: string | string[] 101 | palette?: boolean 102 | pdf?: boolean | PdfOptions 103 | ping?: boolean | object 104 | prerender?: boolean | 'auto' 105 | proxy?: string | { countryCode?: string } 106 | retry?: number 107 | screenshot?: boolean | ScreenshotOptions 108 | scripts?: string | string[] 109 | scroll?: string 110 | staleTtl?: string | number 111 | stream?: string 112 | styles?: string | string[] 113 | timeout?: string | number 114 | ttl?: string | number 115 | video?: boolean 116 | viewport?: object 117 | waitForSelector?: string 118 | waitForTimeout?: string | number 119 | waitUntil?: WaitUntilEvent | WaitUntilEvent[] 120 | } 121 | 122 | type IframeInfo = { 123 | html: string 124 | scripts: Record 125 | } 126 | 127 | type MediaInfo = { 128 | alternative_color?: string 129 | background_color?: string 130 | color?: string 131 | duration_pretty?: string 132 | duration?: number 133 | height?: number 134 | palette?: string[] 135 | size_pretty?: string 136 | size?: number 137 | type?: string 138 | url: string 139 | width?: number 140 | } 141 | 142 | export type MqlResponseData = { 143 | audio?: MediaInfo | null 144 | author?: string | null 145 | date?: string | null 146 | description?: string | null 147 | function?: MqlFunctionResult 148 | iframe?: IframeInfo | null 149 | image?: MediaInfo | null 150 | lang?: string | null 151 | logo?: MediaInfo | null 152 | publisher?: string | null 153 | screenshot?: MediaInfo | null 154 | title?: string | null 155 | url?: string 156 | video?: MediaInfo | null 157 | } 158 | 159 | type MqlFunctionResult = { 160 | isFulfilled: boolean 161 | isRejected: boolean 162 | value: any 163 | } 164 | 165 | type MqlStatus = 'success' | 'fail' | 'error' 166 | 167 | export type MqlPayload = { 168 | status: MqlStatus 169 | data: MqlResponseData 170 | statusCode?: number 171 | redirects: { statusCode: number; url: string }[] 172 | headers: { [key: string]: string } 173 | } 174 | 175 | type HTTPResponse = { 176 | url: string 177 | statusCode: number 178 | headers: Headers 179 | } 180 | 181 | type HTTPResponseWithBody = HTTPResponse & { body: MqlPayload } 182 | 183 | export type HTTPResponseRaw = HTTPResponse & { body: ArrayBuffer } 184 | 185 | export type MqlResponse = MqlPayload & { response: HTTPResponseWithBody } 186 | 187 | export type MqlOptions = MqlClientOptions & MicrolinkApiOptions 188 | 189 | type MqlErrorGeneratedProps = { 190 | description: string 191 | name: string 192 | } 193 | 194 | export type MqlErrorProps = { 195 | code: string 196 | status: MqlStatus 197 | message: string 198 | data?: MqlResponseData 199 | headers?: { [key: string]: string } 200 | more?: string 201 | statusCode?: number 202 | url?: string 203 | } 204 | 205 | export type MqlError = MqlErrorProps & MqlErrorGeneratedProps 206 | 207 | export declare class MicrolinkError extends Error { 208 | constructor(props: MqlErrorProps) 209 | readonly code: string 210 | readonly status: MqlStatus 211 | readonly message: string 212 | readonly description: string 213 | readonly name: string 214 | readonly data?: MqlResponseData 215 | readonly headers?: { [key: string]: string } 216 | readonly more?: string 217 | readonly statusCode?: number 218 | readonly url?: string 219 | } 220 | 221 | export const version: string 222 | 223 | interface mql { 224 | ( 225 | url: string, 226 | opts?: MqlOptions & { stream: string }, 227 | gotOpts?: object 228 | ): Promise 229 | (url: string, opts?: MqlOptions, gotOpts?: object): Promise 230 | arrayBuffer: ( 231 | url: string, 232 | opts?: MqlOptions, 233 | gotOpts?: object 234 | ) => Promise 235 | extend: (gotOpts?: object) => mql 236 | stream: (input: RequestInfo, init?: RequestInit) => ReadableStream 237 | MicrolinkError: new (props: object) => MqlErrorProps 238 | version: string 239 | } 240 | 241 | declare const mql: mql 242 | 243 | export default mql 244 | -------------------------------------------------------------------------------- /test/snapshots/get-api-url.mjs.md: -------------------------------------------------------------------------------- 1 | # Snapshot report for `test/get-api-url.mjs` 2 | 3 | The actual snapshot is saved in `get-api-url.mjs.snap`. 4 | 5 | Generated by [AVA](https://avajs.dev). 6 | 7 | ## node » url without query params 8 | 9 | > Snapshot 1 10 | 11 | [ 12 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 13 | { 14 | headers: {}, 15 | responseType: 'json', 16 | }, 17 | ] 18 | 19 | ## node » apiKey 20 | 21 | > Snapshot 1 22 | 23 | [ 24 | 'https://pro.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 25 | { 26 | headers: { 27 | 'x-api-key': 'foobar', 28 | }, 29 | responseType: 'json', 30 | }, 31 | ] 32 | 33 | ## node » flatten options 34 | 35 | > Snapshot 1 36 | 37 | [ 38 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&overlay.browser=dark', 39 | { 40 | headers: {}, 41 | responseType: 'json', 42 | }, 43 | ] 44 | 45 | ## node » don't pass null 46 | 47 | > Snapshot 1 48 | 49 | [ 50 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 51 | { 52 | headers: {}, 53 | responseType: 'json', 54 | }, 55 | ] 56 | 57 | > Snapshot 2 58 | 59 | [ 60 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 61 | { 62 | headers: {}, 63 | responseType: 'json', 64 | }, 65 | ] 66 | 67 | ## node » don't pass undefined 68 | 69 | > Snapshot 1 70 | 71 | [ 72 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 73 | { 74 | headers: {}, 75 | responseType: 'json', 76 | }, 77 | ] 78 | 79 | > Snapshot 2 80 | 81 | [ 82 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 83 | { 84 | headers: {}, 85 | responseType: 'json', 86 | }, 87 | ] 88 | 89 | ## node » timeout 90 | 91 | > Snapshot 1 92 | 93 | [ 94 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=15000', 95 | { 96 | headers: {}, 97 | responseType: 'json', 98 | }, 99 | ] 100 | 101 | > Snapshot 2 102 | 103 | [ 104 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=28000', 105 | { 106 | headers: {}, 107 | responseType: 'json', 108 | }, 109 | ] 110 | 111 | ## node » waitUntil 112 | 113 | > Snapshot 1 114 | 115 | [ 116 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&waitUntil=load', 117 | { 118 | headers: {}, 119 | responseType: 'json', 120 | }, 121 | ] 122 | 123 | > Snapshot 2 124 | 125 | [ 126 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&waitUntil.0=load&waitUntil.1=networkidle0', 127 | { 128 | headers: {}, 129 | responseType: 'json', 130 | }, 131 | ] 132 | 133 | ## node » undefined 134 | 135 | > Snapshot 1 136 | 137 | [ 138 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 139 | { 140 | headers: {}, 141 | responseType: 'json', 142 | }, 143 | ] 144 | 145 | > Snapshot 2 146 | 147 | [ 148 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&screenshot.element=%23screenshot', 149 | { 150 | headers: {}, 151 | responseType: 'json', 152 | }, 153 | ] 154 | 155 | ## lightweight » url without query params 156 | 157 | > Snapshot 1 158 | 159 | [ 160 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 161 | { 162 | headers: {}, 163 | responseType: 'json', 164 | }, 165 | ] 166 | 167 | ## lightweight » apiKey 168 | 169 | > Snapshot 1 170 | 171 | [ 172 | 'https://pro.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 173 | { 174 | headers: { 175 | 'x-api-key': 'foobar', 176 | }, 177 | responseType: 'json', 178 | }, 179 | ] 180 | 181 | ## lightweight » flatten options 182 | 183 | > Snapshot 1 184 | 185 | [ 186 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&overlay.browser=dark', 187 | { 188 | headers: {}, 189 | responseType: 'json', 190 | }, 191 | ] 192 | 193 | ## lightweight » don't pass null 194 | 195 | > Snapshot 1 196 | 197 | [ 198 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 199 | { 200 | headers: {}, 201 | responseType: 'json', 202 | }, 203 | ] 204 | 205 | > Snapshot 2 206 | 207 | [ 208 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 209 | { 210 | headers: {}, 211 | responseType: 'json', 212 | }, 213 | ] 214 | 215 | ## lightweight » don't pass undefined 216 | 217 | > Snapshot 1 218 | 219 | [ 220 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 221 | { 222 | headers: {}, 223 | responseType: 'json', 224 | }, 225 | ] 226 | 227 | > Snapshot 2 228 | 229 | [ 230 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 231 | { 232 | headers: {}, 233 | responseType: 'json', 234 | }, 235 | ] 236 | 237 | ## lightweight » timeout 238 | 239 | > Snapshot 1 240 | 241 | [ 242 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=15000', 243 | { 244 | headers: {}, 245 | responseType: 'json', 246 | }, 247 | ] 248 | 249 | > Snapshot 2 250 | 251 | [ 252 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&timeout=28000', 253 | { 254 | headers: {}, 255 | responseType: 'json', 256 | }, 257 | ] 258 | 259 | ## lightweight » waitUntil 260 | 261 | > Snapshot 1 262 | 263 | [ 264 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&waitUntil=load', 265 | { 266 | headers: {}, 267 | responseType: 'json', 268 | }, 269 | ] 270 | 271 | > Snapshot 2 272 | 273 | [ 274 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&waitUntil.0=load&waitUntil.1=networkidle0', 275 | { 276 | headers: {}, 277 | responseType: 'json', 278 | }, 279 | ] 280 | 281 | ## lightweight » undefined 282 | 283 | > Snapshot 1 284 | 285 | [ 286 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com', 287 | { 288 | headers: {}, 289 | responseType: 'json', 290 | }, 291 | ] 292 | 293 | > Snapshot 2 294 | 295 | [ 296 | 'https://api.microlink.io/?url=https%3A%2F%2Fkikobeats.com&screenshot.element=%23screenshot', 297 | { 298 | headers: {}, 299 | responseType: 'json', 300 | }, 301 | ] 302 | -------------------------------------------------------------------------------- /lightweight/index.umd.js: -------------------------------------------------------------------------------- 1 | ;(function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' 3 | ? factory(exports) 4 | : typeof define === 'function' && define.amd 5 | ? define(['exports'], factory) 6 | : ((global = 7 | typeof globalThis !== 'undefined' ? globalThis : global || self), 8 | factory((global.mql = {}))) 9 | })(this, function (exports) { 10 | 'use strict' 11 | 12 | function getDefaultExportFromCjs (x) { 13 | return x && 14 | x.__esModule && 15 | Object.prototype.hasOwnProperty.call(x, 'default') 16 | ? x['default'] 17 | : x 18 | } 19 | 20 | function getAugmentedNamespace (n) { 21 | if (n.__esModule) return n 22 | var f = n.default 23 | if (typeof f == 'function') { 24 | var a = function a () { 25 | if (this instanceof a) { 26 | return Reflect.construct(f, arguments, this.constructor) 27 | } 28 | return f.apply(this, arguments) 29 | } 30 | a.prototype = f.prototype 31 | } else a = {} 32 | Object.defineProperty(a, '__esModule', { value: true }) 33 | Object.keys(n).forEach(function (k) { 34 | var d = Object.getOwnPropertyDescriptor(n, k) 35 | Object.defineProperty( 36 | a, 37 | k, 38 | d.get 39 | ? d 40 | : { 41 | enumerable: true, 42 | get: function () { 43 | return n[k] 44 | } 45 | } 46 | ) 47 | }) 48 | return a 49 | } 50 | 51 | var lightweight$1 = { exports: {} } 52 | 53 | var dist = {} 54 | 55 | function iter (output, nullish, sep, val, key) { 56 | var k, 57 | pfx = key ? key + sep : key 58 | 59 | if (val == null) { 60 | if (nullish) output[key] = val 61 | } else if (typeof val != 'object') { 62 | output[key] = val 63 | } else if (Array.isArray(val)) { 64 | for (k = 0; k < val.length; k++) { 65 | iter(output, nullish, sep, val[k], pfx + k) 66 | } 67 | } else { 68 | for (k in val) { 69 | iter(output, nullish, sep, val[k], pfx + k) 70 | } 71 | } 72 | } 73 | 74 | function flattie (input, glue, toNull) { 75 | var output = {} 76 | if (typeof input == 'object') { 77 | iter(output, !!toNull, glue || '.', input, '') 78 | } 79 | return output 80 | } 81 | 82 | dist.flattie = flattie 83 | 84 | class HTTPError extends Error { 85 | response 86 | request 87 | options 88 | constructor (response, request, options) { 89 | const code = 90 | response.status || response.status === 0 ? response.status : '' 91 | const title = response.statusText || '' 92 | const status = `${code} ${title}`.trim() 93 | const reason = status ? `status code ${status}` : 'an unknown error' 94 | super(`Request failed with ${reason}: ${request.method} ${request.url}`) 95 | this.name = 'HTTPError' 96 | this.response = response 97 | this.request = request 98 | this.options = options 99 | } 100 | } 101 | 102 | class TimeoutError extends Error { 103 | request 104 | constructor (request) { 105 | super(`Request timed out: ${request.method} ${request.url}`) 106 | this.name = 'TimeoutError' 107 | this.request = request 108 | } 109 | } 110 | 111 | // eslint-disable-next-line @typescript-eslint/ban-types 112 | const isObject$1 = value => value !== null && typeof value === 'object' 113 | 114 | const validateAndMerge = (...sources) => { 115 | for (const source of sources) { 116 | if ( 117 | (!isObject$1(source) || Array.isArray(source)) && 118 | source !== undefined 119 | ) { 120 | throw new TypeError('The `options` argument must be an object') 121 | } 122 | } 123 | return deepMerge({}, ...sources) 124 | } 125 | const mergeHeaders = (source1 = {}, source2 = {}) => { 126 | const result = new globalThis.Headers(source1) 127 | const isHeadersInstance = source2 instanceof globalThis.Headers 128 | const source = new globalThis.Headers(source2) 129 | for (const [key, value] of source.entries()) { 130 | if ((isHeadersInstance && value === 'undefined') || value === undefined) { 131 | result.delete(key) 132 | } else { 133 | result.set(key, value) 134 | } 135 | } 136 | return result 137 | } 138 | function newHookValue (original, incoming, property) { 139 | return Object.hasOwn(incoming, property) && incoming[property] === undefined 140 | ? [] 141 | : deepMerge(original[property] ?? [], incoming[property] ?? []) 142 | } 143 | const mergeHooks = (original = {}, incoming = {}) => ({ 144 | beforeRequest: newHookValue(original, incoming, 'beforeRequest'), 145 | beforeRetry: newHookValue(original, incoming, 'beforeRetry'), 146 | afterResponse: newHookValue(original, incoming, 'afterResponse'), 147 | beforeError: newHookValue(original, incoming, 'beforeError') 148 | }) 149 | // TODO: Make this strongly-typed (no `any`). 150 | const deepMerge = (...sources) => { 151 | let returnValue = {} 152 | let headers = {} 153 | let hooks = {} 154 | for (const source of sources) { 155 | if (Array.isArray(source)) { 156 | if (!Array.isArray(returnValue)) { 157 | returnValue = [] 158 | } 159 | returnValue = [...returnValue, ...source] 160 | } else if (isObject$1(source)) { 161 | for (let [key, value] of Object.entries(source)) { 162 | if (isObject$1(value) && key in returnValue) { 163 | value = deepMerge(returnValue[key], value) 164 | } 165 | returnValue = { ...returnValue, [key]: value } 166 | } 167 | if (isObject$1(source.hooks)) { 168 | hooks = mergeHooks(hooks, source.hooks) 169 | returnValue.hooks = hooks 170 | } 171 | if (isObject$1(source.headers)) { 172 | headers = mergeHeaders(headers, source.headers) 173 | returnValue.headers = headers 174 | } 175 | } 176 | } 177 | return returnValue 178 | } 179 | 180 | const supportsRequestStreams = (() => { 181 | let duplexAccessed = false 182 | let hasContentType = false 183 | const supportsReadableStream = 184 | typeof globalThis.ReadableStream === 'function' 185 | const supportsRequest = typeof globalThis.Request === 'function' 186 | if (supportsReadableStream && supportsRequest) { 187 | try { 188 | hasContentType = new globalThis.Request('https://empty.invalid', { 189 | body: new globalThis.ReadableStream(), 190 | method: 'POST', 191 | // @ts-expect-error - Types are outdated. 192 | get duplex () { 193 | duplexAccessed = true 194 | return 'half' 195 | } 196 | }).headers.has('Content-Type') 197 | } catch (error) { 198 | // QQBrowser on iOS throws "unsupported BodyInit type" error (see issue #581) 199 | if ( 200 | error instanceof Error && 201 | error.message === 'unsupported BodyInit type' 202 | ) { 203 | return false 204 | } 205 | throw error 206 | } 207 | } 208 | return duplexAccessed && !hasContentType 209 | })() 210 | const supportsAbortController = 211 | typeof globalThis.AbortController === 'function' 212 | const supportsResponseStreams = 213 | typeof globalThis.ReadableStream === 'function' 214 | const supportsFormData = typeof globalThis.FormData === 'function' 215 | const requestMethods = ['get', 'post', 'put', 'patch', 'head', 'delete'] 216 | const responseTypes = { 217 | json: 'application/json', 218 | text: 'text/*', 219 | formData: 'multipart/form-data', 220 | arrayBuffer: '*/*', 221 | blob: '*/*' 222 | } 223 | // The maximum value of a 32bit int (see issue #117) 224 | const maxSafeTimeout = 2_147_483_647 225 | const stop = Symbol('stop') 226 | const kyOptionKeys = { 227 | json: true, 228 | parseJson: true, 229 | stringifyJson: true, 230 | searchParams: true, 231 | prefixUrl: true, 232 | retry: true, 233 | timeout: true, 234 | hooks: true, 235 | throwHttpErrors: true, 236 | onDownloadProgress: true, 237 | fetch: true 238 | } 239 | const requestOptionsRegistry = { 240 | method: true, 241 | headers: true, 242 | body: true, 243 | mode: true, 244 | credentials: true, 245 | cache: true, 246 | redirect: true, 247 | referrer: true, 248 | referrerPolicy: true, 249 | integrity: true, 250 | keepalive: true, 251 | signal: true, 252 | window: true, 253 | dispatcher: true, 254 | duplex: true, 255 | priority: true 256 | } 257 | 258 | const normalizeRequestMethod = input => 259 | requestMethods.includes(input) ? input.toUpperCase() : input 260 | const retryMethods = ['get', 'put', 'head', 'delete', 'options', 'trace'] 261 | const retryStatusCodes = [408, 413, 429, 500, 502, 503, 504] 262 | const retryAfterStatusCodes = [413, 429, 503] 263 | const defaultRetryOptions = { 264 | limit: 2, 265 | methods: retryMethods, 266 | statusCodes: retryStatusCodes, 267 | afterStatusCodes: retryAfterStatusCodes, 268 | maxRetryAfter: Number.POSITIVE_INFINITY, 269 | backoffLimit: Number.POSITIVE_INFINITY, 270 | delay: attemptCount => 0.3 * 2 ** (attemptCount - 1) * 1000 271 | } 272 | const normalizeRetryOptions = (retry = {}) => { 273 | if (typeof retry === 'number') { 274 | return { 275 | ...defaultRetryOptions, 276 | limit: retry 277 | } 278 | } 279 | if (retry.methods && !Array.isArray(retry.methods)) { 280 | throw new Error('retry.methods must be an array') 281 | } 282 | if (retry.statusCodes && !Array.isArray(retry.statusCodes)) { 283 | throw new Error('retry.statusCodes must be an array') 284 | } 285 | return { 286 | ...defaultRetryOptions, 287 | ...retry 288 | } 289 | } 290 | 291 | // `Promise.race()` workaround (#91) 292 | async function timeout (request, init, abortController, options) { 293 | return new Promise((resolve, reject) => { 294 | const timeoutId = setTimeout(() => { 295 | if (abortController) { 296 | abortController.abort() 297 | } 298 | reject(new TimeoutError(request)) 299 | }, options.timeout) 300 | void options 301 | .fetch(request, init) 302 | .then(resolve) 303 | .catch(reject) 304 | .then(() => { 305 | clearTimeout(timeoutId) 306 | }) 307 | }) 308 | } 309 | 310 | // https://github.com/sindresorhus/delay/tree/ab98ae8dfcb38e1593286c94d934e70d14a4e111 311 | async function delay (ms, { signal }) { 312 | return new Promise((resolve, reject) => { 313 | if (signal) { 314 | signal.throwIfAborted() 315 | signal.addEventListener('abort', abortHandler, { once: true }) 316 | } 317 | function abortHandler () { 318 | clearTimeout(timeoutId) 319 | reject(signal.reason) 320 | } 321 | const timeoutId = setTimeout(() => { 322 | signal?.removeEventListener('abort', abortHandler) 323 | resolve() 324 | }, ms) 325 | }) 326 | } 327 | 328 | const findUnknownOptions = (request, options) => { 329 | const unknownOptions = {} 330 | for (const key in options) { 331 | if ( 332 | !(key in requestOptionsRegistry) && 333 | !(key in kyOptionKeys) && 334 | !(key in request) 335 | ) { 336 | unknownOptions[key] = options[key] 337 | } 338 | } 339 | return unknownOptions 340 | } 341 | 342 | class Ky { 343 | static create (input, options) { 344 | const ky = new Ky(input, options) 345 | const function_ = async () => { 346 | if ( 347 | typeof ky._options.timeout === 'number' && 348 | ky._options.timeout > maxSafeTimeout 349 | ) { 350 | throw new RangeError( 351 | `The \`timeout\` option cannot be greater than ${maxSafeTimeout}` 352 | ) 353 | } 354 | // Delay the fetch so that body method shortcuts can set the Accept header 355 | await Promise.resolve() 356 | let response = await ky._fetch() 357 | for (const hook of ky._options.hooks.afterResponse) { 358 | // eslint-disable-next-line no-await-in-loop 359 | const modifiedResponse = await hook( 360 | ky.request, 361 | ky._options, 362 | ky._decorateResponse(response.clone()) 363 | ) 364 | if (modifiedResponse instanceof globalThis.Response) { 365 | response = modifiedResponse 366 | } 367 | } 368 | ky._decorateResponse(response) 369 | if (!response.ok && ky._options.throwHttpErrors) { 370 | let error = new HTTPError(response, ky.request, ky._options) 371 | for (const hook of ky._options.hooks.beforeError) { 372 | // eslint-disable-next-line no-await-in-loop 373 | error = await hook(error) 374 | } 375 | throw error 376 | } 377 | // If `onDownloadProgress` is passed, it uses the stream API internally 378 | /* istanbul ignore next */ 379 | if (ky._options.onDownloadProgress) { 380 | if (typeof ky._options.onDownloadProgress !== 'function') { 381 | throw new TypeError( 382 | 'The `onDownloadProgress` option must be a function' 383 | ) 384 | } 385 | if (!supportsResponseStreams) { 386 | throw new Error( 387 | 'Streams are not supported in your environment. `ReadableStream` is missing.' 388 | ) 389 | } 390 | return ky._stream(response.clone(), ky._options.onDownloadProgress) 391 | } 392 | return response 393 | } 394 | const isRetriableMethod = ky._options.retry.methods.includes( 395 | ky.request.method.toLowerCase() 396 | ) 397 | const result = isRetriableMethod ? ky._retry(function_) : function_() 398 | for (const [type, mimeType] of Object.entries(responseTypes)) { 399 | result[type] = async () => { 400 | // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing 401 | ky.request.headers.set( 402 | 'accept', 403 | ky.request.headers.get('accept') || mimeType 404 | ) 405 | const awaitedResult = await result 406 | const response = awaitedResult.clone() 407 | if (type === 'json') { 408 | if (response.status === 204) { 409 | return '' 410 | } 411 | const arrayBuffer = await response.clone().arrayBuffer() 412 | const responseSize = arrayBuffer.byteLength 413 | if (responseSize === 0) { 414 | return '' 415 | } 416 | if (options.parseJson) { 417 | return options.parseJson(await response.text()) 418 | } 419 | } 420 | return response[type]() 421 | } 422 | } 423 | return result 424 | } 425 | request 426 | abortController 427 | _retryCount = 0 428 | _input 429 | _options 430 | // eslint-disable-next-line complexity 431 | constructor (input, options = {}) { 432 | this._input = input 433 | this._options = { 434 | ...options, 435 | headers: mergeHeaders(this._input.headers, options.headers), 436 | hooks: mergeHooks( 437 | { 438 | beforeRequest: [], 439 | beforeRetry: [], 440 | beforeError: [], 441 | afterResponse: [] 442 | }, 443 | options.hooks 444 | ), 445 | method: normalizeRequestMethod(options.method ?? this._input.method), 446 | // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing 447 | prefixUrl: String(options.prefixUrl || ''), 448 | retry: normalizeRetryOptions(options.retry), 449 | throwHttpErrors: options.throwHttpErrors !== false, 450 | timeout: options.timeout ?? 10_000, 451 | fetch: options.fetch ?? globalThis.fetch.bind(globalThis) 452 | } 453 | if ( 454 | typeof this._input !== 'string' && 455 | !( 456 | this._input instanceof URL || 457 | this._input instanceof globalThis.Request 458 | ) 459 | ) { 460 | throw new TypeError('`input` must be a string, URL, or Request') 461 | } 462 | if (this._options.prefixUrl && typeof this._input === 'string') { 463 | if (this._input.startsWith('/')) { 464 | throw new Error( 465 | '`input` must not begin with a slash when using `prefixUrl`' 466 | ) 467 | } 468 | if (!this._options.prefixUrl.endsWith('/')) { 469 | this._options.prefixUrl += '/' 470 | } 471 | this._input = this._options.prefixUrl + this._input 472 | } 473 | if (supportsAbortController) { 474 | this.abortController = new globalThis.AbortController() 475 | const originalSignal = this._options.signal ?? this._input.signal 476 | originalSignal?.addEventListener('abort', () => { 477 | this.abortController.abort(originalSignal.reason) 478 | }) 479 | this._options.signal = this.abortController.signal 480 | } 481 | if (supportsRequestStreams) { 482 | // @ts-expect-error - Types are outdated. 483 | this._options.duplex = 'half' 484 | } 485 | if (this._options.json !== undefined) { 486 | this._options.body = 487 | this._options.stringifyJson?.(this._options.json) ?? 488 | JSON.stringify(this._options.json) 489 | this._options.headers.set( 490 | 'content-type', 491 | this._options.headers.get('content-type') ?? 'application/json' 492 | ) 493 | } 494 | this.request = new globalThis.Request(this._input, this._options) 495 | if (this._options.searchParams) { 496 | // eslint-disable-next-line unicorn/prevent-abbreviations 497 | const textSearchParams = 498 | typeof this._options.searchParams === 'string' 499 | ? this._options.searchParams.replace(/^\?/, '') 500 | : new URLSearchParams(this._options.searchParams).toString() 501 | // eslint-disable-next-line unicorn/prevent-abbreviations 502 | const searchParams = '?' + textSearchParams 503 | const url = this.request.url.replace(/(?:\?.*?)?(?=#|$)/, searchParams) 504 | // To provide correct form boundary, Content-Type header should be deleted each time when new Request instantiated from another one 505 | if ( 506 | ((supportsFormData && 507 | this._options.body instanceof globalThis.FormData) || 508 | this._options.body instanceof URLSearchParams) && 509 | !(this._options.headers && this._options.headers['content-type']) 510 | ) { 511 | this.request.headers.delete('content-type') 512 | } 513 | // The spread of `this.request` is required as otherwise it misses the `duplex` option for some reason and throws. 514 | this.request = new globalThis.Request( 515 | new globalThis.Request(url, { ...this.request }), 516 | this._options 517 | ) 518 | } 519 | } 520 | _calculateRetryDelay (error) { 521 | this._retryCount++ 522 | if ( 523 | this._retryCount > this._options.retry.limit || 524 | error instanceof TimeoutError 525 | ) { 526 | throw error 527 | } 528 | if (error instanceof HTTPError) { 529 | if (!this._options.retry.statusCodes.includes(error.response.status)) { 530 | throw error 531 | } 532 | const retryAfter = 533 | error.response.headers.get('Retry-After') ?? 534 | error.response.headers.get('RateLimit-Reset') ?? 535 | error.response.headers.get('X-RateLimit-Reset') ?? // GitHub 536 | error.response.headers.get('X-Rate-Limit-Reset') // Twitter 537 | if ( 538 | retryAfter && 539 | this._options.retry.afterStatusCodes.includes(error.response.status) 540 | ) { 541 | let after = Number(retryAfter) * 1000 542 | if (Number.isNaN(after)) { 543 | after = Date.parse(retryAfter) - Date.now() 544 | } else if (after >= Date.parse('2024-01-01')) { 545 | // A large number is treated as a timestamp (fixed threshold protects against clock skew) 546 | after -= Date.now() 547 | } 548 | const max = this._options.retry.maxRetryAfter ?? after 549 | return after < max ? after : max 550 | } 551 | if (error.response.status === 413) { 552 | throw error 553 | } 554 | } 555 | const retryDelay = this._options.retry.delay(this._retryCount) 556 | return Math.min(this._options.retry.backoffLimit, retryDelay) 557 | } 558 | _decorateResponse (response) { 559 | if (this._options.parseJson) { 560 | response.json = async () => 561 | this._options.parseJson(await response.text()) 562 | } 563 | return response 564 | } 565 | async _retry (function_) { 566 | try { 567 | return await function_() 568 | } catch (error) { 569 | const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout) 570 | if (this._retryCount < 1) { 571 | throw error 572 | } 573 | await delay(ms, { signal: this._options.signal }) 574 | for (const hook of this._options.hooks.beforeRetry) { 575 | // eslint-disable-next-line no-await-in-loop 576 | const hookResult = await hook({ 577 | request: this.request, 578 | options: this._options, 579 | error: error, 580 | retryCount: this._retryCount 581 | }) 582 | // If `stop` is returned from the hook, the retry process is stopped 583 | if (hookResult === stop) { 584 | return 585 | } 586 | } 587 | return this._retry(function_) 588 | } 589 | } 590 | async _fetch () { 591 | for (const hook of this._options.hooks.beforeRequest) { 592 | // eslint-disable-next-line no-await-in-loop 593 | const result = await hook(this.request, this._options) 594 | if (result instanceof Request) { 595 | this.request = result 596 | break 597 | } 598 | if (result instanceof Response) { 599 | return result 600 | } 601 | } 602 | const nonRequestOptions = findUnknownOptions(this.request, this._options) 603 | // Cloning is done here to prepare in advance for retries 604 | const mainRequest = this.request 605 | this.request = mainRequest.clone() 606 | if (this._options.timeout === false) { 607 | return this._options.fetch(mainRequest, nonRequestOptions) 608 | } 609 | return timeout( 610 | mainRequest, 611 | nonRequestOptions, 612 | this.abortController, 613 | this._options 614 | ) 615 | } 616 | /* istanbul ignore next */ 617 | _stream (response, onDownloadProgress) { 618 | const totalBytes = Number(response.headers.get('content-length')) || 0 619 | let transferredBytes = 0 620 | if (response.status === 204) { 621 | if (onDownloadProgress) { 622 | onDownloadProgress( 623 | { percent: 1, totalBytes, transferredBytes }, 624 | new Uint8Array() 625 | ) 626 | } 627 | return new globalThis.Response(null, { 628 | status: response.status, 629 | statusText: response.statusText, 630 | headers: response.headers 631 | }) 632 | } 633 | return new globalThis.Response( 634 | new globalThis.ReadableStream({ 635 | async start (controller) { 636 | const reader = response.body.getReader() 637 | if (onDownloadProgress) { 638 | onDownloadProgress( 639 | { percent: 0, transferredBytes: 0, totalBytes }, 640 | new Uint8Array() 641 | ) 642 | } 643 | async function read () { 644 | const { done, value } = await reader.read() 645 | if (done) { 646 | controller.close() 647 | return 648 | } 649 | if (onDownloadProgress) { 650 | transferredBytes += value.byteLength 651 | const percent = 652 | totalBytes === 0 ? 0 : transferredBytes / totalBytes 653 | onDownloadProgress( 654 | { percent, transferredBytes, totalBytes }, 655 | value 656 | ) 657 | } 658 | controller.enqueue(value) 659 | await read() 660 | } 661 | await read() 662 | } 663 | }), 664 | { 665 | status: response.status, 666 | statusText: response.statusText, 667 | headers: response.headers 668 | } 669 | ) 670 | } 671 | } 672 | 673 | /*! MIT License © Sindre Sorhus */ 674 | const createInstance = defaults => { 675 | // eslint-disable-next-line @typescript-eslint/promise-function-async 676 | const ky = (input, options) => 677 | Ky.create(input, validateAndMerge(defaults, options)) 678 | for (const method of requestMethods) { 679 | // eslint-disable-next-line @typescript-eslint/promise-function-async 680 | ky[method] = (input, options) => 681 | Ky.create(input, validateAndMerge(defaults, options, { method })) 682 | } 683 | ky.create = newDefaults => createInstance(validateAndMerge(newDefaults)) 684 | ky.extend = newDefaults => { 685 | if (typeof newDefaults === 'function') { 686 | newDefaults = newDefaults(defaults ?? {}) 687 | } 688 | return createInstance(validateAndMerge(defaults, newDefaults)) 689 | } 690 | ky.stop = stop 691 | return ky 692 | } 693 | const ky$1 = createInstance() 694 | 695 | var distribution = /*#__PURE__*/ Object.freeze({ 696 | __proto__: null, 697 | HTTPError: HTTPError, 698 | TimeoutError: TimeoutError, 699 | default: ky$1 700 | }) 701 | 702 | var require$$1 = /*@__PURE__*/ getAugmentedNamespace(distribution) 703 | 704 | const ENDPOINT = { 705 | FREE: 'https://api.microlink.io/', 706 | PRO: 'https://pro.microlink.io/' 707 | } 708 | 709 | const isObject = input => input !== null && typeof input === 'object' 710 | 711 | const isBuffer = input => 712 | input != null && 713 | input.constructor != null && 714 | typeof input.constructor.isBuffer === 'function' && 715 | input.constructor.isBuffer(input) 716 | 717 | const parseBody = (input, error, url) => { 718 | try { 719 | return JSON.parse(input) 720 | } catch (_) { 721 | const message = input || error.message 722 | 723 | return { 724 | status: 'error', 725 | data: { url: message }, 726 | more: 'https://microlink.io/efatalclient', 727 | code: 'EFATALCLIENT', 728 | message, 729 | url 730 | } 731 | } 732 | } 733 | 734 | const isURL = url => { 735 | try { 736 | return /^https?:\/\//i.test(new URL(url).href) 737 | } catch (_) { 738 | return false 739 | } 740 | } 741 | 742 | const factory$1 = streamResponseType => ({ 743 | VERSION, 744 | MicrolinkError, 745 | got, 746 | flatten 747 | }) => { 748 | const assertUrl = (url = '') => { 749 | if (!isURL(url)) { 750 | const message = `The \`url\` as \`${url}\` is not valid. Ensure it has protocol (http or https) and hostname.` 751 | throw new MicrolinkError({ 752 | status: 'fail', 753 | data: { url: message }, 754 | more: 'https://microlink.io/einvalurlclient', 755 | code: 'EINVALURLCLIENT', 756 | message, 757 | url 758 | }) 759 | } 760 | } 761 | 762 | const mapRules = rules => { 763 | if (!isObject(rules)) return 764 | const flatRules = flatten(rules) 765 | return Object.keys(flatRules).reduce((acc, key) => { 766 | acc[`data.${key}`] = flatRules[key].toString() 767 | return acc 768 | }, {}) 769 | } 770 | 771 | const fetchFromApi = async (apiUrl, opts = {}) => { 772 | try { 773 | const response = await got(apiUrl, opts) 774 | return opts.responseType === streamResponseType 775 | ? response 776 | : { ...response.body, response } 777 | } catch (error) { 778 | const { response = {} } = error 779 | const { 780 | statusCode, 781 | body: rawBody, 782 | headers = {}, 783 | url: uri = apiUrl 784 | } = response 785 | const isBodyBuffer = isBuffer(rawBody) 786 | 787 | const body = 788 | isObject(rawBody) && !isBodyBuffer 789 | ? rawBody 790 | : parseBody(isBodyBuffer ? rawBody.toString() : rawBody, error, uri) 791 | 792 | throw new MicrolinkError({ 793 | ...body, 794 | message: body.message, 795 | url: uri, 796 | statusCode, 797 | headers 798 | }) 799 | } 800 | } 801 | 802 | const getApiUrl = ( 803 | url, 804 | { data, apiKey, endpoint, ...opts } = {}, 805 | { responseType = 'json', headers: gotHeaders, ...gotOpts } = {} 806 | ) => { 807 | const isPro = !!apiKey 808 | const apiEndpoint = endpoint || ENDPOINT[isPro ? 'PRO' : 'FREE'] 809 | 810 | const apiUrl = `${apiEndpoint}?${new URLSearchParams({ 811 | url, 812 | ...mapRules(data), 813 | ...flatten(opts) 814 | }).toString()}` 815 | 816 | const headers = isPro 817 | ? { ...gotHeaders, 'x-api-key': apiKey } 818 | : { ...gotHeaders } 819 | 820 | if (opts.stream) { 821 | responseType = streamResponseType 822 | } 823 | return [apiUrl, { ...gotOpts, responseType, headers }] 824 | } 825 | 826 | const createMql = defaultOpts => async (url, opts, gotOpts) => { 827 | assertUrl(url) 828 | const [apiUrl, fetchOpts] = getApiUrl(url, opts, { 829 | ...defaultOpts, 830 | ...gotOpts 831 | }) 832 | return fetchFromApi(apiUrl, fetchOpts) 833 | } 834 | 835 | const mql = createMql() 836 | mql.extend = createMql 837 | mql.MicrolinkError = MicrolinkError 838 | mql.getApiUrl = getApiUrl 839 | mql.fetchFromApi = fetchFromApi 840 | mql.mapRules = mapRules 841 | mql.version = VERSION 842 | mql.stream = got.stream 843 | 844 | return mql 845 | } 846 | 847 | var factory_1 = factory$1 848 | 849 | const { flattie: flatten } = dist 850 | const { default: ky } = require$$1 851 | 852 | const factory = factory_1('arrayBuffer') 853 | 854 | class MicrolinkError extends Error { 855 | constructor (props) { 856 | super() 857 | this.name = 'MicrolinkError' 858 | Object.assign(this, props) 859 | this.description = this.message 860 | this.message = this.code 861 | ? `${this.code}, ${this.description}` 862 | : this.description 863 | } 864 | } 865 | 866 | const got = async (url, { responseType, ...opts }) => { 867 | try { 868 | if (opts.timeout === undefined) opts.timeout = false 869 | const response = await ky(url, opts) 870 | const body = await response[responseType]() 871 | const { headers, status: statusCode } = response 872 | return { url: response.url, body, headers, statusCode } 873 | } catch (error) { 874 | if (error.response) { 875 | const { response } = error 876 | error.response = { 877 | ...response, 878 | headers: Array.from(response.headers.entries()).reduce( 879 | (acc, [key, value]) => { 880 | acc[key] = value 881 | return acc 882 | }, 883 | {} 884 | ), 885 | statusCode: response.status, 886 | body: await response.text() 887 | } 888 | } 889 | throw error 890 | } 891 | } 892 | 893 | got.stream = (...args) => ky(...args).then(res => res.body) 894 | 895 | const mql = factory({ 896 | MicrolinkError, 897 | got, 898 | flatten, 899 | VERSION: '0.13.20' 900 | }) 901 | 902 | lightweight$1.exports = mql 903 | var arrayBuffer = (lightweight$1.exports.arrayBuffer = mql.extend({ 904 | responseType: 'arrayBuffer' 905 | })) 906 | var extend = (lightweight$1.exports.extend = mql.extend) 907 | var fetchFromApi = (lightweight$1.exports.fetchFromApi = mql.fetchFromApi) 908 | var getApiUrl = (lightweight$1.exports.getApiUrl = mql.getApiUrl) 909 | var mapRules = (lightweight$1.exports.mapRules = mql.mapRules) 910 | var MicrolinkError_1 = (lightweight$1.exports.MicrolinkError = 911 | mql.MicrolinkError) 912 | var version = (lightweight$1.exports.version = mql.version) 913 | 914 | var lightweightExports = lightweight$1.exports 915 | var lightweight = /*@__PURE__*/ getDefaultExportFromCjs(lightweightExports) 916 | 917 | exports.MicrolinkError = MicrolinkError_1 918 | exports.arrayBuffer = arrayBuffer 919 | exports.default = lightweight 920 | exports.extend = extend 921 | exports.fetchFromApi = fetchFromApi 922 | exports.getApiUrl = getApiUrl 923 | exports.mapRules = mapRules 924 | exports.version = version 925 | 926 | Object.defineProperty(exports, '__esModule', { value: true }) 927 | }) 928 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [0.14.0](https://github.com/microlinkhq/mql/compare/v0.13.20...v0.14.0) (2025-06-04) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * retry as client parameter ([#163](https://github.com/microlinkhq/mql/issues/163)) ([36fbc5b](https://github.com/microlinkhq/mql/commit/36fbc5b1ac351eb619979d97c677be890b9daf38)) 11 | 12 | ### [0.13.20](https://github.com/microlinkhq/mql/compare/v0.13.19...v0.13.20) (2025-05-21) 13 | 14 | 15 | ### Bug Fixes 16 | 17 | * rename type ([55f1870](https://github.com/microlinkhq/mql/commit/55f18708a3d415d02f4082137f91ac8057a81eb7)) 18 | 19 | ### [0.13.19](https://github.com/microlinkhq/mql/compare/v0.13.18...v0.13.19) (2025-05-21) 20 | 21 | 22 | ### Bug Fixes 23 | 24 | * better error type ([aa44aa9](https://github.com/microlinkhq/mql/commit/aa44aa9d7b39fe7800a9405913e7cef28a9923dd)) 25 | 26 | ### [0.13.18](https://github.com/microlinkhq/mql/compare/v0.13.17...v0.13.18) (2025-05-21) 27 | 28 | 29 | ### Bug Fixes 30 | 31 | * export missing types ([b55df1d](https://github.com/microlinkhq/mql/commit/b55df1d64a06b64c2311e355f90c3f459c8ce6aa)) 32 | 33 | ### [0.13.17](https://github.com/microlinkhq/mql/compare/v0.13.16...v0.13.17) (2025-05-21) 34 | 35 | 36 | ### Bug Fixes 37 | 38 | * expose MicrolinkApiOptions for node ([fdad1b4](https://github.com/microlinkhq/mql/commit/fdad1b43ff5742f931258425bd80825c908d48e5)) 39 | 40 | ### [0.13.16](https://github.com/microlinkhq/mql/compare/v0.13.15...v0.13.16) (2025-05-21) 41 | 42 | 43 | ### Bug Fixes 44 | 45 | * timeout/waitForTimeout as string ([48000ac](https://github.com/microlinkhq/mql/commit/48000ace4ba61922d2228bd4682f18c56186b698)) 46 | 47 | ### [0.13.15](https://github.com/microlinkhq/mql/compare/v0.13.14...v0.13.15) (2025-05-21) 48 | 49 | ### [0.13.14](https://github.com/microlinkhq/mql/compare/v0.13.13...v0.13.14) (2025-01-28) 50 | 51 | ### [0.13.13](https://github.com/microlinkhq/mql/compare/v0.13.12...v0.13.13) (2025-01-28) 52 | 53 | ### [0.13.12](https://github.com/microlinkhq/mql/compare/v0.13.11...v0.13.12) (2024-12-08) 54 | 55 | 56 | ### Features 57 | 58 | * redirects ([#159](https://github.com/microlinkhq/mql/issues/159)) ([5b90fd7](https://github.com/microlinkhq/mql/commit/5b90fd7c5a9dc40d592e2709347cd9051a717c19)) 59 | 60 | ### [0.13.11](https://github.com/microlinkhq/mql/compare/v0.13.10...v0.13.11) (2024-11-26) 61 | 62 | 63 | ### Bug Fixes 64 | 65 | * **types:** expose MqlResponseData ([9bd3b66](https://github.com/microlinkhq/mql/commit/9bd3b66e53c21d49bfc862040535a6e7a5086ee1)) 66 | 67 | ### [0.13.10](https://github.com/microlinkhq/mql/compare/v0.13.9...v0.13.10) (2024-10-23) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * **types:** add size properties ([4e17fa4](https://github.com/microlinkhq/mql/commit/4e17fa4aee58abda929f051e27a2f7b74ce821a7)) 73 | 74 | ### [0.13.9](https://github.com/microlinkhq/mql/compare/v0.13.8...v0.13.9) (2024-10-01) 75 | 76 | ### [0.13.8](https://github.com/microlinkhq/mql/compare/v0.13.7...v0.13.8) (2024-10-01) 77 | 78 | 79 | ### Bug Fixes 80 | 81 | * **build:** ensure exports are there ([f64896a](https://github.com/microlinkhq/mql/commit/f64896a654b83aa544198b76e706709aab6b95ec)) 82 | 83 | ### [0.13.7](https://github.com/microlinkhq/mql/compare/v0.13.5...v0.13.7) (2024-09-30) 84 | 85 | 86 | ### Bug Fixes 87 | 88 | * **types:** statusCode can be null ([be503c7](https://github.com/microlinkhq/mql/commit/be503c7a6c97c3234586ddb906d85fe4419f8793)) 89 | 90 | ### [0.13.6](https://github.com/microlinkhq/mql/compare/v0.13.5...v0.13.6) (2024-04-23) 91 | 92 | 93 | ### Bug Fixes 94 | 95 | * **types:** statusCode can be null ([be503c7](https://github.com/microlinkhq/mql/commit/be503c7a6c97c3234586ddb906d85fe4419f8793)) 96 | 97 | ### [0.13.5](https://github.com/microlinkhq/mql/compare/v0.13.4...v0.13.5) (2024-04-04) 98 | 99 | 100 | ### Bug Fixes 101 | 102 | * update docs url ([a122de0](https://github.com/microlinkhq/mql/commit/a122de0f095913133fdb4563ee908233ab90d121)) 103 | 104 | ### [0.13.4](https://github.com/microlinkhq/mql/compare/v0.13.3...v0.13.4) (2024-03-17) 105 | 106 | 107 | ### Bug Fixes 108 | 109 | * types ([#154](https://github.com/microlinkhq/mql/issues/154)) ([e8cfe09](https://github.com/microlinkhq/mql/commit/e8cfe092a5aaf24ea007cd00292954e79d030742)) 110 | 111 | ### [0.13.3](https://github.com/microlinkhq/mql/compare/v0.13.2...v0.13.3) (2024-03-17) 112 | 113 | 114 | ### Bug Fixes 115 | 116 | * **types:** expose HTTPResponseRaw ([1ceff8f](https://github.com/microlinkhq/mql/commit/1ceff8f0091b5232043a01519b040764609a8cd6)) 117 | 118 | ### [0.13.2](https://github.com/microlinkhq/mql/compare/v0.13.1...v0.13.2) (2024-03-17) 119 | 120 | 121 | ### Bug Fixes 122 | 123 | * **types:** expose some methods ([#153](https://github.com/microlinkhq/mql/issues/153)) ([980787e](https://github.com/microlinkhq/mql/commit/980787e0a4a3aa099fbf79a55d778099f8b60483)) 124 | 125 | ### [0.13.1](https://github.com/microlinkhq/mql/compare/v0.13.0...v0.13.1) (2024-03-17) 126 | 127 | 128 | ### Bug Fixes 129 | 130 | * stream type ([b8a5f1b](https://github.com/microlinkhq/mql/commit/b8a5f1b6f71ad6ce6e862e79305b983ffcac2907)) 131 | 132 | ## [0.13.0](https://github.com/microlinkhq/mql/compare/v0.12.3...v0.13.0) (2024-03-17) 133 | 134 | 135 | ### Features 136 | 137 | * add `stream` query parameter ([#152](https://github.com/microlinkhq/mql/issues/152)) ([7b74413](https://github.com/microlinkhq/mql/commit/7b74413ffc9602a4ed310368ff76c41301ebe939)) 138 | 139 | ### [0.12.3](https://github.com/microlinkhq/mql/compare/v0.12.2...v0.12.3) (2024-03-01) 140 | 141 | ### [0.12.2](https://github.com/microlinkhq/mql/compare/v0.12.1...v0.12.2) (2024-01-07) 142 | 143 | ### [0.12.1](https://github.com/microlinkhq/mql/compare/v0.12.0...v0.12.1) (2023-11-30) 144 | 145 | 146 | ### Bug Fixes 147 | 148 | * **lightweight:** stream interface ([#144](https://github.com/microlinkhq/mql/issues/144)) ([9dcb280](https://github.com/microlinkhq/mql/commit/9dcb280a1bdac1fe7c3bee2fa1b735cd194abc28)) 149 | 150 | ## [0.12.0](https://github.com/microlinkhq/mql/compare/v0.11.10...v0.12.0) (2023-11-30) 151 | 152 | 153 | ### Features 154 | 155 | * add .extend and buffer/arrayBuffer methods ([#143](https://github.com/microlinkhq/mql/issues/143)) ([3e49bf2](https://github.com/microlinkhq/mql/commit/3e49bf2051775508e9a99c3f79e27b0308e379f3)) 156 | 157 | ### [0.11.10](https://github.com/microlinkhq/mql/compare/v0.11.9...v0.11.10) (2023-11-29) 158 | 159 | 160 | ### Bug Fixes 161 | 162 | * **esm:** expose fetchFromApi method ([#141](https://github.com/microlinkhq/mql/issues/141)) ([0ada023](https://github.com/microlinkhq/mql/commit/0ada0231b7e007b22d05c4d92cab53426d57463b)) 163 | 164 | ### [0.11.9](https://github.com/microlinkhq/mql/compare/v0.11.8...v0.11.9) (2023-11-06) 165 | 166 | 167 | ### Bug Fixes 168 | 169 | * export MqlResponse type ([831b669](https://github.com/microlinkhq/mql/commit/831b669156e7531a7c88b29a5019fbec9f3471f7)) 170 | 171 | ### [0.11.8](https://github.com/microlinkhq/mql/compare/v0.11.7...v0.11.8) (2023-11-05) 172 | 173 | ### [0.11.7](https://github.com/microlinkhq/mql/compare/v0.11.7-0...v0.11.7) (2023-11-03) 174 | 175 | 176 | ### Bug Fixes 177 | 178 | * sort fields using finepack ([061f3a8](https://github.com/microlinkhq/mql/commit/061f3a8c921e3c7c1aebdefda36619dccf34a2b9)) 179 | 180 | ### [0.11.7-0](https://github.com/microlinkhq/mql/compare/v0.11.5...v0.11.7-0) (2023-11-02) 181 | 182 | ### [0.11.6](https://github.com/microlinkhq/mql/compare/v0.11.5...v0.11.6) (2023-11-02) 183 | 184 | ### [0.11.5](https://github.com/microlinkhq/mql/compare/v0.11.4...v0.11.5) (2023-11-01) 185 | 186 | ### [0.11.4](https://github.com/microlinkhq/mql/compare/v0.11.3...v0.11.4) (2023-10-05) 187 | 188 | 189 | ### Bug Fixes 190 | 191 | * **error:** add missing fields to type ([3066841](https://github.com/microlinkhq/mql/commit/306684144dc11f86360cfc08f8278a5032eb7d19)) 192 | 193 | ### [0.11.3](https://github.com/microlinkhq/mql/compare/v0.11.2...v0.11.3) (2023-10-05) 194 | 195 | 196 | ### Bug Fixes 197 | 198 | * **types:** expose MqlError ([338cac7](https://github.com/microlinkhq/mql/commit/338cac70bc60895981299a167b04a2541dd89ed4)) 199 | 200 | ### [0.11.2](https://github.com/microlinkhq/mql/compare/v0.11.1...v0.11.2) (2023-09-06) 201 | 202 | 203 | ### Bug Fixes 204 | 205 | * declare main for old node versions ([79560af](https://github.com/microlinkhq/mql/commit/79560af2dec2d263f44588d41d3dd88bb2041cfe)) 206 | 207 | ### [0.11.1](https://github.com/microlinkhq/mql/compare/v0.11.0...v0.11.1) (2023-09-06) 208 | 209 | 210 | ### Bug Fixes 211 | 212 | * **esm:** ensure indvidual methods are exported ([#135](https://github.com/microlinkhq/mql/issues/135)) ([9212276](https://github.com/microlinkhq/mql/commit/9212276bae897dfed7279f7be6bb96a0572f6f8d)) 213 | 214 | ## [0.11.0](https://github.com/microlinkhq/mql/compare/v0.10.39...v0.11.0) (2023-09-06) 215 | 216 | ### [0.10.39](https://github.com/microlinkhq/mql/compare/v0.10.38...v0.10.39) (2023-09-04) 217 | 218 | 219 | ### Bug Fixes 220 | 221 | * prefer exports over main ([bab451c](https://github.com/microlinkhq/mql/commit/bab451cb1715117b0905984d2b43cd92ec242ac1)) 222 | 223 | ### [0.10.38](https://github.com/microlinkhq/mql/compare/v0.10.37...v0.10.38) (2023-09-03) 224 | 225 | 226 | ### Bug Fixes 227 | 228 | * add url type for media ([fb898dc](https://github.com/microlinkhq/mql/commit/fb898dcbb4df578657ec05266f8576e328f9e92c)) 229 | 230 | ### [0.10.37](https://github.com/microlinkhq/mql/compare/v0.10.36...v0.10.37) (2023-09-03) 231 | 232 | ### [0.10.36](https://github.com/microlinkhq/mql/compare/v0.10.35...v0.10.36) (2023-09-03) 233 | 234 | ### [0.10.35](https://github.com/microlinkhq/mql/compare/v0.10.34...v0.10.35) (2023-09-03) 235 | 236 | 237 | ### Bug Fixes 238 | 239 | * add missing types ([#132](https://github.com/microlinkhq/mql/issues/132)) ([55a58f4](https://github.com/microlinkhq/mql/commit/55a58f4ca5a64e113b0727de478ef9ad85f0aa40)) 240 | 241 | ### [0.10.34](https://github.com/microlinkhq/mql/compare/v0.10.33...v0.10.34) (2023-09-01) 242 | 243 | 244 | ### Bug Fixes 245 | 246 | * unify retry interface ([#130](https://github.com/microlinkhq/mql/issues/130)) ([64e4aff](https://github.com/microlinkhq/mql/commit/64e4aff438ece5f7c1ce3015fdc094950601f7b6)) 247 | 248 | ### [0.10.33](https://github.com/microlinkhq/mql/compare/v0.10.32...v0.10.33) (2023-04-06) 249 | 250 | ### [0.10.32](https://github.com/microlinkhq/mql/compare/v0.10.31...v0.10.32) (2023-01-02) 251 | 252 | ### [0.10.31](https://github.com/microlinkhq/mql/compare/v0.10.30...v0.10.31) (2022-11-27) 253 | 254 | 255 | ### Bug Fixes 256 | 257 | * remove module field ([00efd9c](https://github.com/microlinkhq/mql/commit/00efd9cf64c882b5cc8849f967ad9336ff7ea754)) 258 | 259 | ### [0.10.30](https://github.com/microlinkhq/mql/compare/v0.10.29...v0.10.30) (2022-11-13) 260 | 261 | 262 | ### Bug Fixes 263 | 264 | * avoid class field declaration ([070ef61](https://github.com/microlinkhq/mql/commit/070ef61cd6e40a41cf7b567fb36943984bc4a3a5)) 265 | 266 | ### [0.10.29](https://github.com/microlinkhq/mql/compare/v0.10.28...v0.10.29) (2022-11-13) 267 | 268 | 269 | ### Features 270 | 271 | * add ES bundle ([8f0935c](https://github.com/microlinkhq/mql/commit/8f0935cabcc447e5cce33a3a4d22743f00cd5156)) 272 | 273 | ### [0.10.28](https://github.com/microlinkhq/mql/compare/v0.10.27...v0.10.28) (2022-07-20) 274 | 275 | ### [0.10.27](https://github.com/microlinkhq/mql/compare/v0.10.26...v0.10.27) (2022-07-14) 276 | 277 | ### [0.10.26](https://github.com/microlinkhq/mql/compare/v0.10.25...v0.10.26) (2022-06-27) 278 | 279 | 280 | ### Bug Fixes 281 | 282 | * ensure headers is an object ([31fa807](https://github.com/microlinkhq/mql/commit/31fa8079015c4544a153e262a6a712587a3dfc26)) 283 | 284 | ### [0.10.25](https://github.com/microlinkhq/mql/compare/v0.10.24...v0.10.25) (2022-06-14) 285 | 286 | ### [0.10.24](https://github.com/microlinkhq/mql/compare/v0.10.23...v0.10.24) (2022-06-14) 287 | 288 | ### [0.10.23](https://github.com/microlinkhq/mql/compare/v0.10.22...v0.10.23) (2022-06-14) 289 | 290 | ### [0.10.22](https://github.com/microlinkhq/mql/compare/v0.10.21...v0.10.22) (2022-05-27) 291 | 292 | ### [0.10.21](https://github.com/microlinkhq/mql/compare/v0.10.20...v0.10.21) (2022-05-27) 293 | 294 | 295 | ### Bug Fixes 296 | 297 | * typecheck Buffer without using Buffer ([69ee222](https://github.com/microlinkhq/mql/commit/69ee2222c4f23cfb9f703369cac7e69bc6b79bef)) 298 | 299 | ### [0.10.20](https://github.com/microlinkhq/mql/compare/v0.10.19...v0.10.20) (2022-04-28) 300 | 301 | ### [0.10.19](https://github.com/microlinkhq/mql/compare/v0.10.18...v0.10.19) (2022-03-05) 302 | 303 | ### [0.10.18](https://github.com/microlinkhq/mql/compare/v0.10.17...v0.10.18) (2022-02-24) 304 | 305 | ### [0.10.17](https://github.com/microlinkhq/mql/compare/v0.10.15...v0.10.17) (2022-02-24) 306 | 307 | ### [0.10.16](https://github.com/microlinkhq/mql/compare/v0.10.15...v0.10.16) (2022-02-24) 308 | 309 | ### [0.10.15](https://github.com/microlinkhq/mql/compare/v0.10.14...v0.10.15) (2022-02-21) 310 | 311 | ### [0.10.14](https://github.com/microlinkhq/mql/compare/v0.10.13...v0.10.14) (2022-02-12) 312 | 313 | ### [0.10.13](https://github.com/microlinkhq/mql/compare/v0.10.12...v0.10.13) (2022-01-18) 314 | 315 | ### [0.10.12](https://github.com/microlinkhq/mql/compare/v0.10.11...v0.10.12) (2022-01-04) 316 | 317 | ### [0.10.11](https://github.com/microlinkhq/mql/compare/v0.10.10...v0.10.11) (2021-12-08) 318 | 319 | ### [0.10.10](https://github.com/microlinkhq/mql/compare/v0.10.9...v0.10.10) (2021-12-08) 320 | 321 | 322 | ### Bug Fixes 323 | 324 | * dependency name ([1b679a9](https://github.com/microlinkhq/mql/commit/1b679a96b2f90eefda521f7b3c64b37daafde58c)) 325 | 326 | ### [0.10.9](https://github.com/microlinkhq/mql/compare/v0.10.7...v0.10.9) (2021-12-08) 327 | 328 | ### [0.10.8](https://github.com/microlinkhq/mql/compare/v0.10.7...v0.10.8) (2021-12-08) 329 | 330 | ### [0.10.7](https://github.com/microlinkhq/mql/compare/v0.10.6...v0.10.7) (2021-12-08) 331 | 332 | 333 | ### Bug Fixes 334 | 335 | * ensure to parseBody input is a string ([45e037d](https://github.com/microlinkhq/mql/commit/45e037de0260ba6b5298cb5207c6cb1d8f063c46)) 336 | 337 | ### [0.10.6](https://github.com/microlinkhq/mql/compare/v0.10.5...v0.10.6) (2021-12-01) 338 | 339 | ### [0.10.5](https://github.com/microlinkhq/mql/compare/v0.10.4...v0.10.5) (2021-11-23) 340 | 341 | 342 | ### Features 343 | 344 | * expand types ([4be8564](https://github.com/microlinkhq/mql/commit/4be856405c683b077fefddf82d521ca5de71ff9a)) 345 | 346 | ### [0.10.4](https://github.com/microlinkhq/mql/compare/v0.10.3...v0.10.4) (2021-10-13) 347 | 348 | ### [0.10.3](https://github.com/microlinkhq/mql/compare/v0.10.2...v0.10.3) (2021-10-13) 349 | 350 | ### [0.10.2](https://github.com/microlinkhq/mql/compare/v0.10.1...v0.10.2) (2021-10-13) 351 | 352 | ### [0.10.1](https://github.com/microlinkhq/mql/compare/v0.10.0...v0.10.1) (2021-10-12) 353 | 354 | 355 | ### Bug Fixes 356 | 357 | * remove duplicate type ([da98fce](https://github.com/microlinkhq/mql/commit/da98fce145ec96c944857b87a05d17d159530431)) 358 | 359 | ## [0.10.0](https://github.com/microlinkhq/mql/compare/v0.9.13...v0.10.0) (2021-10-01) 360 | 361 | 362 | ### Features 363 | 364 | * add EFATALCLIENT error code ([2e77319](https://github.com/microlinkhq/mql/commit/2e77319df0864b2962e53113105c4eeb0ed07cf8)) 365 | 366 | ### [0.9.13](https://github.com/microlinkhq/mql/compare/v0.9.12...v0.9.13) (2021-09-19) 367 | 368 | ### [0.9.12](https://github.com/microlinkhq/mql/compare/v0.9.11...v0.9.12) (2021-09-18) 369 | 370 | 371 | ### Bug Fixes 372 | 373 | * serialize values properly ([74e2dbb](https://github.com/microlinkhq/mql/commit/74e2dbb9f376cf4dbe5ef49afe0a27714d7765ce)) 374 | 375 | ### [0.9.11](https://github.com/microlinkhq/mql/compare/v0.9.10...v0.9.11) (2021-09-12) 376 | 377 | ### [0.9.10](https://github.com/microlinkhq/mql/compare/v0.9.9...v0.9.10) (2021-09-12) 378 | 379 | ### [0.9.9](https://github.com/microlinkhq/mql/compare/v0.9.8...v0.9.9) (2021-08-09) 380 | 381 | ### [0.9.8](https://github.com/microlinkhq/mql/compare/v0.9.7...v0.9.8) (2021-06-28) 382 | 383 | 384 | ### Bug Fixes 385 | 386 | * ensure input exist before render ([7af11e1](https://github.com/microlinkhq/mql/commit/7af11e1eed5f73980b449c39f4a1f8cae8db09ec)) 387 | 388 | ### [0.9.7](https://github.com/microlinkhq/mql/compare/v0.9.5...v0.9.7) (2021-06-26) 389 | 390 | 391 | ### Features 392 | 393 | * add api param types ([00ff9b7](https://github.com/microlinkhq/mql/commit/00ff9b705928697543282b47894a0110ba1ed3cf)) 394 | * add types ([41cc61a](https://github.com/microlinkhq/mql/commit/41cc61ace94824d7b3814f93be34f8e1ccdb9c40)) 395 | 396 | 397 | ### Bug Fixes 398 | 399 | * **build:** add missing dependency ([89b16a3](https://github.com/microlinkhq/mql/commit/89b16a31e2896bea752726ca7caae2a0f5f14f07)) 400 | 401 | ### [0.9.5](https://github.com/microlinkhq/mql/compare/v0.9.4...v0.9.5) (2021-05-25) 402 | 403 | 404 | ### Bug Fixes 405 | 406 | * default parameters ([d5d788a](https://github.com/microlinkhq/mql/commit/d5d788ab486b070b41af0ed32491b65020657db2)) 407 | 408 | ### [0.9.4](https://github.com/microlinkhq/mql/compare/v0.9.3...v0.9.4) (2021-05-25) 409 | 410 | ### [0.9.3](https://github.com/microlinkhq/mql/compare/v0.9.2...v0.9.3) (2021-03-21) 411 | 412 | 413 | ### Bug Fixes 414 | 415 | * setup mql version properly ([e5f9016](https://github.com/microlinkhq/mql/commit/e5f9016167e1a5aeb1ecfb9b8d2a0cd42b972742)) 416 | 417 | ### [0.9.2](https://github.com/microlinkhq/mql/compare/v0.9.1...v0.9.2) (2021-03-20) 418 | 419 | 420 | ### Bug Fixes 421 | 422 | * browser dist file ([17aaecc](https://github.com/microlinkhq/mql/commit/17aaeccea629d3ca03f64139c8592fac32e9bd83)) 423 | 424 | ### [0.9.1](https://github.com/microlinkhq/mql/compare/v0.9.0...v0.9.1) (2021-03-20) 425 | 426 | 427 | ### Bug Fixes 428 | 429 | * scripts ([e883dd0](https://github.com/microlinkhq/mql/commit/e883dd0c42abe8e1e527dc24d8ab847ff83fd7c4)) 430 | 431 | ## [0.9.0](https://github.com/microlinkhq/mql/compare/v0.8.4...v0.9.0) (2021-03-20) 432 | 433 | ### [0.8.4](https://github.com/microlinkhq/mql/compare/v0.8.3...v0.8.4) (2021-03-20) 434 | 435 | ### [0.8.3](https://github.com/microlinkhq/mql/compare/v0.8.2...v0.8.3) (2021-03-03) 436 | 437 | ### [0.8.2](https://github.com/microlinkhq/mql/compare/v0.8.1...v0.8.2) (2021-02-24) 438 | 439 | ### [0.8.1](https://github.com/microlinkhq/mql/compare/v0.8.0...v0.8.1) (2021-01-17) 440 | 441 | ## [0.8.0](https://github.com/microlinkhq/mql/compare/v0.7.19...v0.8.0) (2020-12-18) 442 | 443 | ### [0.7.19](https://github.com/microlinkhq/mql/compare/v0.7.18...v0.7.19) (2020-11-29) 444 | 445 | ### [0.7.18](https://github.com/microlinkhq/mql/compare/v0.7.17...v0.7.18) (2020-11-29) 446 | 447 | ### [0.7.17](https://github.com/microlinkhq/mql/compare/v0.7.16...v0.7.17) (2020-11-29) 448 | 449 | ### [0.7.16](https://github.com/microlinkhq/mql/compare/v0.7.15...v0.7.16) (2020-11-16) 450 | 451 | ### [0.7.15](https://github.com/microlinkhq/mql/compare/v0.7.14...v0.7.15) (2020-10-31) 452 | 453 | 454 | ### Bug Fixes 455 | 456 | * linter ([031f98d](https://github.com/microlinkhq/mql/commit/031f98d561a056c175afdd659e517236867d39e4)) 457 | 458 | ### [0.7.14](https://github.com/microlinkhq/mql/compare/v0.7.13...v0.7.14) (2020-10-21) 459 | 460 | ### [0.7.13](https://github.com/microlinkhq/mql/compare/v0.7.12...v0.7.13) (2020-10-13) 461 | 462 | ### [0.7.12](https://github.com/microlinkhq/mql/compare/v0.7.11...v0.7.12) (2020-10-13) 463 | 464 | ### [0.7.11](https://github.com/microlinkhq/mql/compare/v0.7.10...v0.7.11) (2020-10-12) 465 | 466 | ### [0.7.10](https://github.com/microlinkhq/mql/compare/v0.7.9...v0.7.10) (2020-09-29) 467 | 468 | ### [0.7.9](https://github.com/microlinkhq/mql/compare/v0.7.8...v0.7.9) (2020-09-20) 469 | 470 | ### [0.7.8](https://github.com/microlinkhq/mql/compare/v0.7.7...v0.7.8) (2020-09-10) 471 | 472 | 473 | ### Bug Fixes 474 | 475 | * remove unused dependency ([7b87935](https://github.com/microlinkhq/mql/commit/7b879351587f85234a08e05cc199150219ebb3cf)) 476 | 477 | ### [0.7.7](https://github.com/microlinkhq/mql/compare/v0.7.6...v0.7.7) (2020-09-08) 478 | 479 | ### [0.7.6](https://github.com/microlinkhq/mql/compare/v0.7.5...v0.7.6) (2020-09-07) 480 | 481 | ### [0.7.5](https://github.com/microlinkhq/mql/compare/v0.7.4...v0.7.5) (2020-08-29) 482 | 483 | ### [0.7.4](https://github.com/microlinkhq/mql/compare/v0.7.3...v0.7.4) (2020-07-17) 484 | 485 | ### [0.7.3](https://github.com/microlinkhq/mql/compare/v0.7.2...v0.7.3) (2020-07-09) 486 | 487 | ### [0.7.2](https://github.com/microlinkhq/mql/compare/v0.7.1...v0.7.2) (2020-07-05) 488 | 489 | ### [0.7.1](https://github.com/microlinkhq/mql/compare/v0.7.0...v0.7.1) (2020-05-27) 490 | 491 | 492 | ### Bug Fixes 493 | 494 | * avoid undefined ([9feaec8](https://github.com/microlinkhq/mql/commit/9feaec8bdd1f826a6dd13064bf34b9564b999ef7)) 495 | 496 | ## [0.7.0](https://github.com/microlinkhq/mql/compare/v0.6.15...v0.7.0) (2020-05-25) 497 | 498 | 499 | ### Features 500 | 501 | * customize http client as third argument ([b9cf7c7](https://github.com/microlinkhq/mql/commit/b9cf7c71ebb05a3e667ebb1b421ea1e874b8f472)) 502 | 503 | ### [0.6.15](https://github.com/microlinkhq/mql/compare/v0.6.14...v0.6.15) (2020-05-19) 504 | 505 | 506 | ### Bug Fixes 507 | 508 | * ensure to parse buffer responses ([bf69ba9](https://github.com/microlinkhq/mql/commit/bf69ba9c1fd25a093442a1dab0431cd19be13bc6)) 509 | 510 | ### [0.6.14](https://github.com/microlinkhq/mql/compare/v0.6.13...v0.6.14) (2020-05-17) 511 | 512 | 513 | ### Bug Fixes 514 | 515 | * ensure to parse body ([6d66e43](https://github.com/microlinkhq/mql/commit/6d66e43128b62065cf2868d3614e62cb1668d56c)) 516 | 517 | ### [0.6.13](https://github.com/microlinkhq/mql/compare/v0.6.12...v0.6.13) (2020-05-04) 518 | 519 | ### [0.6.12](https://github.com/microlinkhq/mql/compare/v0.6.11...v0.6.12) (2020-04-21) 520 | 521 | ### [0.6.11](https://github.com/microlinkhq/mql/compare/v0.6.10...v0.6.11) (2020-04-03) 522 | 523 | 524 | ### Bug Fixes 525 | 526 | * client timeout ([ad4fadf](https://github.com/microlinkhq/mql/commit/ad4fadf1a699c9bc1bc1c8a7c435bc397a26758c)) 527 | 528 | ### [0.6.10](https://github.com/microlinkhq/mql/compare/v0.6.9...v0.6.10) (2020-03-30) 529 | 530 | ### [0.6.9](https://github.com/microlinkhq/mql/compare/v0.6.8...v0.6.9) (2020-03-25) 531 | 532 | ### [0.6.8](https://github.com/microlinkhq/mql/compare/v0.6.7...v0.6.8) (2020-03-23) 533 | 534 | ### [0.6.7](https://github.com/microlinkhq/mql/compare/v0.6.6...v0.6.7) (2020-03-23) 535 | 536 | ### [0.6.6](https://github.com/microlinkhq/mql/compare/v0.6.5...v0.6.6) (2020-03-23) 537 | 538 | ### [0.6.5](https://github.com/microlinkhq/mql/compare/v0.6.4...v0.6.5) (2020-03-11) 539 | 540 | 541 | ### Bug Fixes 542 | 543 | * remove module field ([56e17d7](https://github.com/microlinkhq/mql/commit/56e17d71bdbb91c2ff8f4b2dc1ae3212e990f01a)), closes [#50](https://github.com/microlinkhq/mql/issues/50) 544 | 545 | ### [0.6.4](https://github.com/microlinkhq/mql/compare/v0.6.3...v0.6.4) (2020-03-09) 546 | 547 | ### [0.6.3](https://github.com/microlinkhq/mql/compare/v0.6.2...v0.6.3) (2020-03-04) 548 | 549 | 550 | ### Bug Fixes 551 | 552 | * remove isStream prop ([afe5f6f](https://github.com/microlinkhq/mql/commit/afe5f6f802d5f947011a2829ae51c3dc79ac0fe6)) 553 | 554 | ### [0.6.2](https://github.com/microlinkhq/mql/compare/v0.6.1...v0.6.2) (2020-03-03) 555 | 556 | 557 | ### Bug Fixes 558 | 559 | * headers serialization on browser ([23fde59](https://github.com/microlinkhq/mql/commit/23fde599afbe700a94be9a522ab9dfdcfc6f31da)) 560 | 561 | ### [0.6.1](https://github.com/microlinkhq/mql/compare/v0.6.0...v0.6.1) (2020-03-01) 562 | 563 | 564 | ### Bug Fixes 565 | 566 | * streaming ([a3d97c6](https://github.com/microlinkhq/mql/commit/a3d97c684dae1e66bec5b6e29498a7d7c22d8207)) 567 | 568 | ## [0.6.0](https://github.com/microlinkhq/mql/compare/v0.5.23...v0.6.0) (2020-03-01) 569 | 570 | 571 | ### Features 572 | 573 | * better error handling ([17bee3b](https://github.com/microlinkhq/mql/commit/17bee3b051803f21ac761967439b2fcd4fe8d409)) 574 | 575 | ### [0.5.23](https://github.com/microlinkhq/mql/compare/v0.5.22...v0.5.23) (2020-02-28) 576 | 577 | ### [0.5.22](https://github.com/microlinkhq/mql/compare/v0.5.21...v0.5.22) (2020-02-20) 578 | 579 | ### [0.5.21](https://github.com/microlinkhq/mql/compare/v0.5.20...v0.5.21) (2020-02-12) 580 | 581 | ### [0.5.20](https://github.com/microlinkhq/mql/compare/v0.5.19...v0.5.20) (2020-02-07) 582 | 583 | ### [0.5.19](https://github.com/microlinkhq/mql/compare/v0.5.18...v0.5.19) (2020-02-05) 584 | 585 | ### [0.5.18](https://github.com/microlinkhq/mql/compare/v0.5.17...v0.5.18) (2020-02-05) 586 | 587 | 588 | ### Bug Fixes 589 | 590 | * ensure ky is loaded properly ([9f44ee2](https://github.com/microlinkhq/mql/commit/9f44ee20e2ac99e23d258b3651fec66290949e4f)) 591 | 592 | ### [0.5.17](https://github.com/microlinkhq/mql/compare/v0.5.16...v0.5.17) (2020-02-03) 593 | 594 | 595 | ### Features 596 | 597 | * update dependencies ([06e7ae2](https://github.com/microlinkhq/mql/commit/06e7ae2b8c195cbdb20e01f48d3ff3f551dd1cd6)) 598 | 599 | ### [0.5.16](https://github.com/microlinkhq/mql/compare/v0.5.15...v0.5.16) (2020-01-18) 600 | 601 | ### [0.5.15](https://github.com/microlinkhq/mql/compare/v0.5.14...v0.5.15) (2019-12-20) 602 | 603 | 604 | ### Bug Fixes 605 | 606 | * only flatten data objects ([1867815](https://github.com/microlinkhq/mql/commit/1867815b9804d02f3f5ba7bda51a4dcb22712faa)) 607 | 608 | ### [0.5.14](https://github.com/microlinkhq/mql/compare/v0.5.13...v0.5.14) (2019-12-04) 609 | 610 | ### [0.5.13](https://github.com/microlinkhq/mql/compare/v0.5.12...v0.5.13) (2019-11-25) 611 | 612 | 613 | ### Bug Fixes 614 | 615 | * ensure to flatten options passed ([3afeb3d](https://github.com/microlinkhq/mql/commit/3afeb3d930af220b312ca0be1a3048d0f2899114)) 616 | 617 | ### [0.5.12](https://github.com/microlinkhq/mql/compare/v0.5.11...v0.5.12) (2019-11-18) 618 | 619 | ### [0.5.11](https://github.com/microlinkhq/mql/compare/v0.5.10...v0.5.11) (2019-11-09) 620 | 621 | ### [0.5.10](https://github.com/microlinkhq/mql/compare/v0.5.9...v0.5.10) (2019-10-23) 622 | 623 | ### [0.5.9](https://github.com/microlinkhq/mql/compare/v0.5.8...v0.5.9) (2019-10-18) 624 | 625 | ### [0.5.8](https://github.com/microlinkhq/mql/compare/v0.5.7...v0.5.8) (2019-10-18) 626 | 627 | 628 | ### Bug Fixes 629 | 630 | * timeout no more undefined ([0f81606](https://github.com/microlinkhq/mql/commit/0f81606)) 631 | 632 | ### [0.5.7](https://github.com/microlinkhq/mql/compare/v0.5.6...v0.5.7) (2019-10-14) 633 | 634 | ### [0.5.6](https://github.com/microlinkhq/mql/compare/v0.5.5...v0.5.6) (2019-10-14) 635 | 636 | ### [0.5.5](https://github.com/microlinkhq/mql/compare/v0.5.4...v0.5.5) (2019-10-14) 637 | 638 | ### [0.5.4](https://github.com/microlinkhq/mql/compare/v0.5.3...v0.5.4) (2019-10-13) 639 | 640 | ### [0.5.3](https://github.com/microlinkhq/mql/compare/v0.5.2...v0.5.3) (2019-10-13) 641 | 642 | 643 | ### Bug Fixes 644 | 645 | * setup fetch default options ([81087d0](https://github.com/microlinkhq/mql/commit/81087d0)) 646 | 647 | ### [0.5.2](https://github.com/microlinkhq/mql/compare/v0.5.1...v0.5.2) (2019-10-13) 648 | 649 | 650 | ### Bug Fixes 651 | 652 | * print timeout ([e829a4a](https://github.com/microlinkhq/mql/commit/e829a4a)) 653 | 654 | ### [0.5.1](https://github.com/microlinkhq/mql/compare/v0.5.0...v0.5.1) (2019-10-13) 655 | 656 | ## [0.5.0](https://github.com/microlinkhq/mql/compare/v0.4.5...v0.5.0) (2019-10-08) 657 | 658 | ### [0.4.5](https://github.com/microlinkhq/mql/compare/v0.4.4...v0.4.5) (2019-09-28) 659 | 660 | ### [0.4.4](https://github.com/microlinkhq/mql/compare/v1.0.22...v0.4.4) (2019-09-26) 661 | 662 | 663 | ### Bug Fixes 664 | 665 | * pass headers ([d79f883](https://github.com/microlinkhq/mql/commit/d79f883)) 666 | 667 | ### [0.4.3](https://github.com/microlinkhq/mql/compare/v0.4.2...v0.4.3) (2019-09-10) 668 | 669 | ### [0.4.2](https://github.com/microlinkhq/mql/compare/v1.0.19...v0.4.2) (2019-09-08) 670 | 671 | ### [0.4.1](https://github.com/microlinkhq/mql/compare/v0.4.0...v0.4.1) (2019-08-24) 672 | 673 | ## [0.4.0](https://github.com/microlinkhq/mql/compare/v0.3.16...v0.4.0) (2019-08-24) 674 | 675 | 676 | ### Bug Fixes 677 | 678 | * linter ([5f62d0f](https://github.com/microlinkhq/mql/commit/5f62d0f)) 679 | 680 | ### [0.3.16](https://github.com/microlinkhq/mql/compare/v1.0.16...v0.3.16) (2019-08-13) 681 | 682 | ### [0.3.15](https://github.com/microlinkhq/mql/compare/v1.0.9...v0.3.15) (2019-07-31) 683 | 684 | 685 | ### Bug Fixes 686 | 687 | * linter ([f7f985b](https://github.com/microlinkhq/mql/commit/f7f985b)) 688 | 689 | ### [0.3.14](https://github.com/microlinkhq/mql/compare/v1.0.7...v0.3.14) (2019-07-10) 690 | 691 | 692 | ### Build System 693 | 694 | * migrate lint-staged ([698e8e0](https://github.com/microlinkhq/mql/commit/698e8e0)) 695 | * update dependencies ([d15b056](https://github.com/microlinkhq/mql/commit/d15b056)) 696 | 697 | 698 | 699 | ### [0.3.13](https://github.com/microlinkhq/mql/compare/v0.3.12...v0.3.13) (2019-06-12) 700 | 701 | 702 | ### Bug Fixes 703 | 704 | * handle buffer ([e62c782](https://github.com/microlinkhq/mql/commit/e62c782)) 705 | 706 | 707 | ### Build System 708 | 709 | * generate bundle ([2a6fa12](https://github.com/microlinkhq/mql/commit/2a6fa12)) 710 | 711 | 712 | 713 | ### [0.3.12](https://github.com/microlinkhq/mql/compare/v0.3.11...v0.3.12) (2019-06-09) 714 | 715 | 716 | ### Bug Fixes 717 | 718 | * require ([2eb2416](https://github.com/microlinkhq/mql/commit/2eb2416)) 719 | 720 | 721 | ### Build System 722 | 723 | * generate bundle ([37ec175](https://github.com/microlinkhq/mql/commit/37ec175)) 724 | 725 | 726 | 727 | ### [0.3.11](https://github.com/microlinkhq/mql/compare/v0.3.10...v0.3.11) (2019-06-09) 728 | 729 | 730 | ### Bug Fixes 731 | 732 | * setup umd main file properly ([4804c27](https://github.com/microlinkhq/mql/commit/4804c27)) 733 | * umd ([ac37de9](https://github.com/microlinkhq/mql/commit/ac37de9)) 734 | * use default constructor ([ee75e1c](https://github.com/microlinkhq/mql/commit/ee75e1c)) 735 | 736 | 737 | ### Build System 738 | 739 | * generate build after release ([4920073](https://github.com/microlinkhq/mql/commit/4920073)) 740 | * ignore stats.html ([c43e0cc](https://github.com/microlinkhq/mql/commit/c43e0cc)) 741 | 742 | 743 | 744 | ### [0.3.10](https://github.com/microlinkhq/mql/compare/v1.0.6...v0.3.10) (2019-06-08) 745 | 746 | 747 | ### Bug Fixes 748 | 749 | * just parse when the body is an string ([2612577](https://github.com/microlinkhq/mql/commit/2612577)) 750 | 751 | 752 | ### Build System 753 | 754 | * update dependencies ([9e1a31c](https://github.com/microlinkhq/mql/commit/9e1a31c)) 755 | 756 | 757 | ### Tests 758 | 759 | * update snapshot ([0caa920](https://github.com/microlinkhq/mql/commit/0caa920)) 760 | 761 | 762 | 763 | ### [0.3.9](https://github.com/microlinkhq/mql/compare/v1.0.5...v0.3.9) (2019-05-28) 764 | 765 | 766 | ### Build System 767 | 768 | * associate url with the error ([605f913](https://github.com/microlinkhq/mql/commit/605f913)) 769 | 770 | 771 | ### Tests 772 | 773 | * avoid date ([a21c21c](https://github.com/microlinkhq/mql/commit/a21c21c)) 774 | 775 | 776 | 777 | ### [0.3.8](https://github.com/microlinkhq/mql/compare/v1.0.4...v0.3.8) (2019-05-28) 778 | 779 | 780 | ### Build System 781 | 782 | * update dependencies ([5047672](https://github.com/microlinkhq/mql/commit/5047672)) 783 | 784 | 785 | 786 | ### [0.3.7](https://github.com/microlinkhq/mql/compare/v1.0.3...v0.3.7) (2019-05-20) 787 | 788 | 789 | ### Bug Fixes 790 | 791 | * **package:** update is-url-http to version 1.2.0 ([fb85fae](https://github.com/microlinkhq/mql/commit/fb85fae)) 792 | 793 | 794 | ### Tests 795 | 796 | * update snapshots ([101c989](https://github.com/microlinkhq/mql/commit/101c989)) 797 | 798 | 799 | 800 | ## [0.3.6](https://github.com/microlinkhq/mql/compare/v0.3.5...v0.3.6) (2019-04-25) 801 | 802 | 803 | 804 | ## [0.3.5](https://github.com/microlinkhq/mql/compare/v0.3.4...v0.3.5) (2019-04-25) 805 | 806 | 807 | ### Bug Fixes 808 | 809 | * **package:** update ky to version 0.10.0 ([9947a0e](https://github.com/microlinkhq/mql/commit/9947a0e)) 810 | * **package:** update ky-universal to version 0.2.0 ([5cd3d89](https://github.com/microlinkhq/mql/commit/5cd3d89)) 811 | 812 | 813 | 814 | 815 | ## [0.3.4](https://github.com/microlinkhq/mql/compare/v0.3.3...v0.3.4) (2019-04-08) 816 | 817 | 818 | 819 | 820 | ## [0.3.3](https://github.com/microlinkhq/mql/compare/v0.3.2...v0.3.3) (2019-03-29) 821 | 822 | 823 | 824 | 825 | ## [0.3.2](https://github.com/microlinkhq/mql/compare/v0.3.1...v0.3.2) (2019-03-29) 826 | 827 | 828 | 829 | 830 | ## [0.3.1](https://github.com/microlinkhq/mql/compare/v0.3.0...v0.3.1) (2019-03-29) 831 | 832 | 833 | 834 | 835 | # [0.3.0](https://github.com/microlinkhq/mql/compare/v0.2.12...v0.3.0) (2019-03-21) 836 | 837 | 838 | ### Features 839 | 840 | * add custom rules support ([f342dd3](https://github.com/microlinkhq/mql/commit/f342dd3)) 841 | 842 | 843 | 844 | 845 | ## [0.2.12](https://github.com/microlinkhq/mql/compare/v0.2.11...v0.2.12) (2019-03-11) 846 | 847 | 848 | 849 | 850 | ## [0.2.11](https://github.com/microlinkhq/mql/compare/v0.2.6...v0.2.11) (2019-03-11) 851 | 852 | 853 | ### Bug Fixes 854 | 855 | * add missing browser dependency ([ec1ed09](https://github.com/microlinkhq/mql/commit/ec1ed09)) 856 | 857 | 858 | 859 | 860 | ## 0.2.10 (2019-03-03) 861 | 862 | 863 | 864 | 865 | ## 0.2.9 (2019-03-03) 866 | 867 | 868 | 869 | 870 | ## 0.2.8 (2019-03-03) 871 | 872 | 873 | 874 | 875 | ## 0.2.7 (2019-03-03) 876 | 877 | 878 | 879 | 880 | ## 0.2.10 (2019-03-03) 881 | 882 | * build: update dependencies ([1998c50](https://github.com/microlinkhq/mql/commit/1998c50)) 883 | 884 | 885 | 886 | 887 | ## 0.2.9 (2019-03-03) 888 | 889 | * test: improve ([f89a41f](https://github.com/microlinkhq/mql/commit/f89a41f)) 890 | 891 | 892 | 893 | 894 | ## 0.2.8 (2019-03-03) 895 | 896 | * build: setup deps ([491e227](https://github.com/microlinkhq/mql/commit/491e227)) 897 | 898 | 899 | 900 | 901 | ## 0.2.7 (2019-03-03) 902 | 903 | * build: add ky-universal as dependecy ([8570d8e](https://github.com/microlinkhq/mql/commit/8570d8e)) 904 | * build: fix tests ([c9bddfd](https://github.com/microlinkhq/mql/commit/c9bddfd)) 905 | 906 | 907 | 908 | # Change Log 909 | 910 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 911 | 912 | 913 | ## [0.2.6](https://github.com/microlinkhq/mql/compare/v0.2.5...v0.2.6) (2019-02-24) 914 | 915 | 916 | 917 | 918 | ## [0.2.5](https://github.com/microlinkhq/mql/compare/v0.2.4...v0.2.5) (2019-02-24) 919 | 920 | 921 | 922 | 923 | ## [0.2.4](https://github.com/microlinkhq/mql/compare/v0.2.3...v0.2.4) (2019-02-24) 924 | 925 | 926 | 927 | 928 | ## [0.2.3](https://github.com/microlinkhq/mql/compare/v0.2.2...v0.2.3) (2019-02-24) 929 | 930 | 931 | 932 | 933 | ## [0.2.2](https://github.com/microlinkhq/mql/compare/v0.2.1...v0.2.2) (2019-02-24) 934 | 935 | 936 | 937 | 938 | ## [0.2.1](https://github.com/microlinkhq/mql/compare/v0.2.0...v0.2.1) (2019-02-24) 939 | 940 | 941 | 942 | 943 | # 0.2.0 (2019-02-24) 944 | 945 | 946 | ### Bug Fixes 947 | 948 | * linter ([92762fc](https://github.com/microlinkhq/mql/commit/92762fc)) 949 | * **package:** update is-url-http to version 1.1.1 ([7d0d33a](https://github.com/microlinkhq/mql/commit/7d0d33a)) 950 | 951 | 952 | ### Features 953 | 954 | * add browser support ([fdcfad5](https://github.com/microlinkhq/mql/commit/fdcfad5)) 955 | 956 | 957 | 958 | 959 | # 0.1.0 (2019-02-03) 960 | 961 | 962 | ### Bug Fixes 963 | 964 | * better docs ([ea72b17](https://github.com/microlinkhq/mql/commit/ea72b17)) 965 | 966 | 967 | 968 | 969 | # 0.1.0 (2019-02-03) 970 | 971 | * docs: add introduction ([2d30d5a](https://github.com/microlinkhq/mql/commit/2d30d5a)) 972 | * test: fix snapshots ([ec78c1d](https://github.com/microlinkhq/mql/commit/ec78c1d)) 973 | * build: first commit ([8cf91a9](https://github.com/microlinkhq/mql/commit/8cf91a9)) 974 | * build: tweak meta ([bbf383c](https://github.com/microlinkhq/mql/commit/bbf383c)) 975 | * fix: better docs ([ea72b17](https://github.com/microlinkhq/mql/commit/ea72b17)) 976 | * add cache support ([f7f2414](https://github.com/microlinkhq/mql/commit/f7f2414)) 977 | * add error handling ([7f56519](https://github.com/microlinkhq/mql/commit/7f56519)) 978 | --------------------------------------------------------------------------------