From d4be87694b3b5af5c40961b09b9ba8a8f5073d74 Mon Sep 17 00:00:00 2001 From: Tomas Rutkauskas Date: Thu, 12 Nov 2026 15:19:56 +0200 Subject: [PATCH 1/2] Accept the ConvertApi.auth host as a URL and ship the JSDoc ConvertApi.auth('api-token', 'https://eu-v2.convertapi.com/') requested https://https://eu-v2.convertapi.com//convert/... and failed with "fetch failed" (ENOTFOUND https). The README showed that form from 2023-07-04 until https://github.com/ConvertAPI/convertapi-library-js/pull/34, and https://www.convertapi.com/llms.txt still does. The constructor now removes a leading https:// or http:// and trailing slashes and stores the bare host in this.host, which createParams() and the upload URLs use too. Requests always use https. The auth() JSDoc described the retired secret or apiKey and token object; it now describes the API token and the host name. The build also removed every JSDoc comment from dist/*.d.ts (removeComments), so editors never showed any of them. It now emits the declarations a second time with their comments; the JavaScript keeps none, as before. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- src/convertapi.ts | 18 +++++++++++++----- test/convertapi.test.js | 39 +++++++++++++++++++++++++++++++++++++++ tsconfig.json | 2 ++ 5 files changed, 63 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de85088..8a39e12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,14 @@ 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. ## 1.1.11 - 2026-10-20 diff --git a/package.json b/package.json index 4f7fa31..cc13143 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/src/convertapi.ts b/src/convertapi.ts index b7c78c5..9117d3e 100644 --- a/src/convertapi.ts +++ b/src/convertapi.ts @@ -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. diff --git a/test/convertapi.test.js b/test/convertapi.test.js index 533821c..1a402ce 100644 --- a/test/convertapi.test.js +++ b/test/convertapi.test.js @@ -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' @@ -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') diff --git a/tsconfig.json b/tsconfig.json index 5219eb6..cc90518 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -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, From 1ac9be3f8110c7b379709853f67653fb54b608cc Mon Sep 17 00:00:00 2001 From: Tomas Rutkauskas Date: Thu, 12 Nov 2026 15:24:08 +0200 Subject: [PATCH 2/2] Describe Result.duration as the conversion cost The previous commit makes dist/*.d.ts keep their JSDoc, so this comment now shows in editors. It said "conversion duration seconds", but the getter returns ConversionCost, which the API spec describes as the amount deducted from the balance after the conversion. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 3 ++- src/result.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a39e12..5ec1768 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ and `latest` stayed on the non-module 1.0.8. 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. + `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 diff --git a/src/result.ts b/src/result.ts index 7cb341d..afb63e1 100644 --- a/src/result.ts +++ b/src/result.ts @@ -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