Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ and `latest` stayed on the non-module 1.0.8.
unversioned CDN URLs get the ES module build. `latest` was moved from 1.0.8 to 1.1.11 on 2026-11-12
(`npm dist-tag add`). `module` stays on 1.1.11. A page that loads the non-module
1.0.8 with an unversioned script tag must pin it: https://unpkg.com/convertapi-js@1.0.8/lib/convertapi.js
- `ConvertApi.auth()` also takes the host as a URL: a leading `https://` or `http://` and trailing slashes
are removed, so `ConvertApi.auth('api-token', 'https://eu-v2.convertapi.com/')` sends requests to
`https://eu-v2.convertapi.com/convert/...`. Before, that form failed with "fetch failed", because the
library requested `https://https://eu-v2.convertapi.com//convert/...`. The README showed it from
2023-07-04 until https://github.com/ConvertAPI/convertapi-library-js/pull/34. Requests always use https.
- The type declarations (`dist/*.d.ts`) keep their JSDoc comments, so editors show them; the build used to
remove them. The `auth()` comment describes the API token and the host name instead of the retired
`secret` and `apiKey`/`token` object, and `Result.duration` is described as the conversion cost
(`ConversionCost`) rather than a duration in seconds.

## 1.1.11 - 2026-10-20

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"dist"
],
"scripts": {
"build": "tsc",
"build": "tsc && tsc --emitDeclarationOnly --removeComments false",
"test": "npm run build && node --test test/*.test.js",
"prepublishOnly": "echo 'convertapi-js is published by a GitHub Release, see RELEASING.md' && exit 1"
},
Expand Down
18 changes: 13 additions & 5 deletions src/convertapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,28 @@ import Result, {IResultErrorDto} from "./result.js";
export default class ConvertApi {

/**
* Returns ConvertApi object ready to use for conversions
* Returns a ConvertApi object ready to use for conversions.
*
* @param credentials - Your ConvertAPI credentials. Should be an `Object` with the `secret` or `apiKey` and `token` properties.
* @param host - ConvertApi server domain name
* @param credentials - Your API token (https://www.convertapi.com/a/authentication), sent in the `Authorization: Bearer` header.
* @param host - API host name, without `https://` or a trailing slash: `v2.convertapi.com` (the default) or a regional host such as `eu-v2.convertapi.com` (https://www.convertapi.com/docs/servers-location). A leading `https://` or `http://` and trailing slashes are removed; requests always use https.
* @returns - New ConvertApi object
*/
static auth(credentials: string, host?: string): ConvertApi {
return new ConvertApi(credentials, host)
}

/** API host name that requests go to, such as `v2.convertapi.com`, without `https://` or a trailing slash. */
public readonly host: string

constructor(
public readonly authCredentials: string,
public readonly host: string='v2.convertapi.com'
) {}
host: string = 'v2.convertapi.com'
) {
// Requests go to https://{host}/..., so a host given as a URL, such as 'https://eu-v2.convertapi.com/'
// (in the README until https://github.com/ConvertAPI/convertapi-library-js/pull/34), loses its scheme
// and trailing slashes instead of breaking every request.
this.host = host.replace(/^https?:\/\//i, '').replace(/\/+$/, '')
}

/**
* Returns initialized and ready to use new conversion parameters object.
Expand Down
2 changes: 1 addition & 1 deletion src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default class Result {
) {}

/**
* Returns conversion duration seconds
* Returns the conversion cost (`ConversionCost`), the amount deducted from your balance for this conversion.
*/
public get duration(): number {
return this.dto.ConversionCost
Expand Down
39 changes: 39 additions & 0 deletions test/convertapi.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { test } from 'node:test'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import { stubFetch } from './setup.js'
import ConvertApi from '../dist/convertapi.js'
import { FileValue } from '../dist/file-param.js'
Expand All @@ -16,6 +17,44 @@ test('auth() keeps the token and defaults to v2.convertapi.com', () => {
assert.equal(ConvertApi.auth('token-1', 'example.test').host, 'example.test')
})

test('auth() takes the host as a URL too: https://, http:// and trailing slashes are removed', () => {
const cases = [
['https://eu-v2.convertapi.com/', 'eu-v2.convertapi.com'],
['https://eu-v2.convertapi.com', 'eu-v2.convertapi.com'],
['HTTPS://eu-v2.convertapi.com/', 'eu-v2.convertapi.com'],
['http://eu-v2.convertapi.com/', 'eu-v2.convertapi.com'],
['eu-v2.convertapi.com//', 'eu-v2.convertapi.com'],
['eu-v2.convertapi.com', 'eu-v2.convertapi.com'],
['http://localhost:8080/', 'localhost:8080']
]
for (const [host, expected] of cases) {
assert.equal(ConvertApi.auth('token-1', host).host, expected, host)
}
assert.equal(new ConvertApi('token-1', 'https://example.test/').host, 'example.test')
})

test('a host given as a URL is used for uploads and conversions', async t => {
const calls = stubFetch(t, { body: { FileId: 'up-1' } }, { body: { FileId: 'up-2' } }, { body: result })
// The form the README showed until https://github.com/ConvertAPI/convertapi-library-js/pull/34
const api = ConvertApi.auth('token-1', 'https://example.test/')
const params = api.createParams()
params.add('File', new File(['%PDF'], 'a.pdf'))
params.add('OverlayFile', new URL('https://cdn.example.test/overlay.pdf'))

await api.convert('pdf', 'pdf-watermark', params)

assert.deepEqual(calls.map(c => c.url), [
'https://example.test/upload?filename=a.pdf',
'https://example.test/upload?url=https%3A%2F%2Fcdn.example.test%2Foverlay.pdf',
'https://example.test/convert/pdf/to/pdf-watermark?storefile=true'
])
})

test('the type declarations keep the JSDoc that editors show', async () => {
const dts = await readFile(new URL('../dist/convertapi.d.ts', import.meta.url), 'utf8')
assert.match(dts, /@param host - API host name, without `https:\/\/`/)
})

test('convert() posts the parameters with a Bearer token and returns the result', async t => {
const calls = stubFetch(t, { body: result })
const api = ConvertApi.auth('token-1', 'example.test')
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"noUnusedLocals": true,
"strictNullChecks": true,

// The JavaScript ships without comments. `npm run build` then emits the declarations a second time
// with --removeComments false, so dist/*.d.ts keep the JSDoc that editors show.
"removeComments": true,
"sourceMap": true,
"declaration": true,
Expand Down
Loading