From 270420c9c5bae2eab211e1f2314a1e20f186d071 Mon Sep 17 00:00:00 2001 From: e271828- Date: Tue, 13 Feb 2024 17:16:33 -0500 Subject: [PATCH 1/4] add secureApi and scriptSource (#22) --- README.md | 2 ++ lib/__test__/script.test.ts | 16 ++++++++++++++++ lib/src/script.ts | 14 ++++++++++++-- lib/src/types.ts | 2 ++ package.json | 2 +- 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b923c22..98d2717 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,9 @@ const { response } = await hcaptcha.execute({ async: true }); | `loadAsync` | Boolean | No | `true` | Set if the script should be loaded asynchronously. | | `cleanup` | Boolean | No | `true` | Remove script tag after setup. | | `crossOrigin` | String | No | `-` | Set script cross origin attribute such as "anonymous". | +| `scriptSource` | String | No | `https://js.hcaptcha.com/1/api.js` | Set script source URI. Takes precedence over `secureApi`. | | `scriptLocation` | HTMLElement | No | `document.head` | Location of where to append the script tag. Make sure to add it to an area that will persist to prevent loading multiple times in the same document view. | +| `secureApi` | Boolean | No | `false` | See enterprise docs. | | `apihost` | String | No | `-` | See enterprise docs. | | `assethost` | String | No | `-` | See enterprise docs. | | `endpoint` | String | No | `-` | See enterprise docs. | diff --git a/lib/__test__/script.test.ts b/lib/__test__/script.test.ts index 7ac5701..c60ca31 100644 --- a/lib/__test__/script.test.ts +++ b/lib/__test__/script.test.ts @@ -143,6 +143,22 @@ describe('fetchScript', () => { const [script] = nodes; expect(script.src).toMatch(`${apihost}/1/api.js?onload=${HCAPTCHA_LOAD_FN_NAME}`); }); + + it('should change hCaptcha JS API if secureApi is specified', async () => { + const secureApi = true; + await fetchScript({ secureApi }); + + const [script] = nodes; + expect(script.src).toMatch(`https://js.hcaptcha.com/1/secure-api.js?onload=${HCAPTCHA_LOAD_FN_NAME}`); + }); + + it('should change hCaptcha JS API if scriptSource is specified', async () => { + const scriptSource = 'hcaptcha.com/1/api.js'; + await fetchScript({ scriptSource }); + + const [script] = nodes; + expect(script.src).toMatch(`${scriptSource}?onload=${HCAPTCHA_LOAD_FN_NAME}`); + }); }); describe('cleanup', () => { diff --git a/lib/src/script.ts b/lib/src/script.ts index 65a9922..4e75055 100644 --- a/lib/src/script.ts +++ b/lib/src/script.ts @@ -9,7 +9,9 @@ export function fetchScript({ loadAsync = true, crossOrigin, apihost = 'https://js.hcaptcha.com', - cleanup = true + cleanup = true, + secureApi = false, + scriptSource = '' }: IScriptParams = {}) { const element = getMountElement(scriptLocation); const frame: any = getFrame(element); @@ -18,7 +20,15 @@ export function fetchScript({ const script = frame.document.createElement('script'); script.id = SCRIPT_ID; - script.src = `${apihost}/1/api.js?onload=${HCAPTCHA_LOAD_FN_NAME}`; + if (scriptSource) { + script.src = `${scriptSource}?onload=${HCAPTCHA_LOAD_FN_NAME}`; + } else { + if (secureApi) { + script.src = `${apihost}/1/secure-api.js?onload=${HCAPTCHA_LOAD_FN_NAME}`; + } else { + script.src = `${apihost}/1/api.js?onload=${HCAPTCHA_LOAD_FN_NAME}`; + } + } script.crossOrigin = crossOrigin; script.async = loadAsync; diff --git a/lib/src/types.ts b/lib/src/types.ts index ceab92d..d8d8d70 100644 --- a/lib/src/types.ts +++ b/lib/src/types.ts @@ -1,5 +1,7 @@ export interface IScriptParams { scriptLocation?: HTMLElement; + secureApi?: boolean; + scriptSource?: string; apihost?: string; loadAsync?: boolean; cleanup?: boolean; diff --git a/package.json b/package.json index 2d5d858..3c878f0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@hcaptcha/loader", "description": "This is a JavaScript library to easily configure the loading of the hCaptcha JS client SDK with built-in error handling.", - "version": "1.1.3", + "version": "1.2.0", "author": "hCaptcha team and contributors", "license": "MIT", "keywords": [ From bb0abf3a3f8f4ebbc53acf84902a871d06f68c83 Mon Sep 17 00:00:00 2001 From: Brad Peters Date: Wed, 14 Feb 2024 14:51:46 -0800 Subject: [PATCH 2/4] fix(secure-api): Do not clean up secure api script (#23) --- lib/src/script.ts | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/script.ts b/lib/src/script.ts index 4e75055..f1cc955 100644 --- a/lib/src/script.ts +++ b/lib/src/script.ts @@ -34,7 +34,7 @@ export function fetchScript({ const onComplete = (event, callback) => { try { - if (cleanup) { + if (!secureApi && cleanup) { element.removeChild(script); } callback(event); diff --git a/package.json b/package.json index 3c878f0..e84012b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@hcaptcha/loader", "description": "This is a JavaScript library to easily configure the loading of the hCaptcha JS client SDK with built-in error handling.", - "version": "1.2.0", + "version": "1.2.1", "author": "hCaptcha team and contributors", "license": "MIT", "keywords": [ From 16f8cb45b1da3ea573c696b0120f9897adc0b684 Mon Sep 17 00:00:00 2001 From: Faris Mahmutovic <115102863+faris-imi@users.noreply.github.com> Date: Thu, 15 Feb 2024 21:43:10 +0100 Subject: [PATCH 3/4] fix(legacy): use swc to transform esm to es5 (#20) Co-authored-by: Brad Peters Co-authored-by: e271828- --- README.md | 99 +++++++++++++++++++++++++++++++++-------- lib/esbuild.config.js | 52 +++++++++++++++++++++- lib/package.json | 3 +- lib/src/loader.ts | 2 +- lib/src/polyfills.ts | 4 ++ lib/tsconfig.types.json | 3 ++ package.json | 2 +- pnpm-lock.yaml | 12 +++++ 8 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 lib/src/polyfills.ts diff --git a/README.md b/README.md index 98d2717..f8fea21 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This is a JavaScript library to easily configure the loading of the [hCaptcha](h 1. [Installation](#installation) 2. [Implementation](#implementation) 3. [Props](#props) +3. [Legacy Support](#legacy-support) ### Installation ``` @@ -30,21 +31,83 @@ const { response } = await hcaptcha.execute({ async: true }); ``` ### Props -| Name | Values/Type | Required | Default | Description | -|-------------------|-------------|----------|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| -| `loadAsync` | Boolean | No | `true` | Set if the script should be loaded asynchronously. | -| `cleanup` | Boolean | No | `true` | Remove script tag after setup. | -| `crossOrigin` | String | No | `-` | Set script cross origin attribute such as "anonymous". | -| `scriptSource` | String | No | `https://js.hcaptcha.com/1/api.js` | Set script source URI. Takes precedence over `secureApi`. | -| `scriptLocation` | HTMLElement | No | `document.head` | Location of where to append the script tag. Make sure to add it to an area that will persist to prevent loading multiple times in the same document view. | -| `secureApi` | Boolean | No | `false` | See enterprise docs. | -| `apihost` | String | No | `-` | See enterprise docs. | -| `assethost` | String | No | `-` | See enterprise docs. | -| `endpoint` | String | No | `-` | See enterprise docs. | -| `hl` | String | No | `-` | See enterprise docs. | -| `host` | String | No | `-` | See enterprise docs. | -| `imghost` | String | No | `-` | See enterprise docs. | -| `recaptchacompat` | String | No | `-` | See enterprise docs. | -| `reportapi` | String | No | `-` | See enterprise docs. | -| `sentry` | Boolean | No | `-` | See enterprise docs. | -| `custom` | Boolean | No | `-` | See enterprise docs. | +| Name | Values/Type | Required | Default | Description | +|-------------------|-------------|----------|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `loadAsync` | Boolean | No | `true` | Set if the script should be loaded asynchronously. | +| `cleanup` | Boolean | No | `true` | Remove script tag after setup. | +| `crossOrigin` | String | No | `-` | Set script cross origin attribute such as "anonymous". | +| `scriptSource` | String | No | `https://js.hcaptcha.com/1/api.js` | Set script source URI. Takes precedence over `secureApi`. | +| `scriptLocation` | HTMLElement | No | `document.head` | Location of where to append the script tag. Make sure to add it to an area that will persist to prevent loading multiple times in the same document view. | +| `secureApi` | Boolean | No | `false` | See enterprise docs. | +| `apihost` | String | No | `-` | See enterprise docs. | +| `assethost` | String | No | `-` | See enterprise docs. | +| `endpoint` | String | No | `-` | See enterprise docs. | +| `hl` | String | No | `-` | See enterprise docs. | +| `host` | String | No | `-` | See enterprise docs. | +| `imghost` | String | No | `-` | See enterprise docs. | +| `recaptchacompat` | String | No | `-` | See enterprise docs. | +| `reportapi` | String | No | `-` | See enterprise docs. | +| `sentry` | Boolean | No | `-` | See enterprise docs. | +| `custom` | Boolean | No | `-` | See enterprise docs. | + + + +## Legacy Support +In order to support older browsers, a separate bundle is generated in which all ES6 code is compiled down to ES5 along with an optional polyfill bundle. + +- `polyfills.js`: Provides polyfills for features not supported in older browsers. +- `index.es5.js`: **@hcaptcha/loader** package compiled for ES5 environments. + +### Import Bundle(s) +Both bundles generated use IIFE format rather than a more modern import syntax such as `require` or `esm`. + +```js +// Optional polyfill import +import '@hCaptcha/loader/dist/polyfills.js'; +// ES5 version of hCaptcha Loader +import '@hCaptcha/loader/dist/index.es5.js'; + +hCaptchaLoader().then(function(hcaptcha) { + var element = document.createElement('div'); + // hCaptcha API is ready + hcaptcha.render(element, { + sitekey: 'YOUR_SITE_KEY', + // Additional options here + }); +}); + +``` +### TypeScript +To handle typescript with ES5 version, use the following statement. +```ts +declare global { + interface Window { + hCaptchaLoader: any; + } +} +``` + +### CDN +The hCaptcha Loader targeted for older browsers can also be imported via CDN by using UNPKG](https://www.unpkg.com/), see example below. + + +```html + + + + + + +
+ + + +``` diff --git a/lib/esbuild.config.js b/lib/esbuild.config.js index 571b433..3f06f13 100644 --- a/lib/esbuild.config.js +++ b/lib/esbuild.config.js @@ -3,6 +3,7 @@ import { fileURLToPath } from 'url'; import { build, context, analyzeMetafile } from 'esbuild'; import * as dotenv from 'dotenv'; +import swc from '@swc/core'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -44,6 +45,14 @@ const config = { sourcemap: BUILD === 'development', }; +const swcOptions = { + minify: true, + sourceMaps: BUILD === 'development', + jsc: { + target: 'es5', + }, +}; + if (WATCH) { const ctx = await context({ @@ -57,6 +66,7 @@ if (WATCH) { }); await ctx.watch(); } else { + // Transpile TypeScript to ESM const resultESM = await build({ ...config, format: 'esm', @@ -67,11 +77,46 @@ if (WATCH) { ] }); + // Transpile TypeScript to CommonJS const resultCJS = await build({ ...config, format: 'cjs', outfile: resolve(DIST, 'index.cjs'), - treeShaking: true + treeShaking: true, + }); + + // Transform to ES5 + const transformedESM = await swc.transformFile(resolve(DIST, 'index.mjs'), swcOptions); + + // Build ES5 bundle + const resultES5 = await build({ + ...config, + entryPoints: undefined, + globalName: 'hCaptchaLoaderPkg', + stdin: { + contents: transformedESM.code, + resolveDir: DIST, + sourcefile: 'index.es5.js', + }, + outfile: resolve(DIST, 'index.es5.js'), + footer: { + js: 'window.hCaptchaLoader = hCaptchaLoaderPkg.hCaptchaLoader;', + }, + treeShaking: true, + target: [ + 'es5', + ] + }); + + // Add Polyfills + await build({ + ...config, + entryPoints: [resolve(SRC, 'polyfills.ts')], + outfile: resolve(DIST, 'polyfills.js'), + treeShaking: true, + target: [ + 'es5', + ] }); if (DEBUG) { @@ -81,7 +126,12 @@ if (WATCH) { const analyzeCJS = await analyzeMetafile(resultCJS.metafile, { verbose: false }); + const analyzeES5 = await analyzeMetafile(resultES5.metafile, { + verbose: false + }); + console.log(analyzeESM); console.log(analyzeCJS); + console.log(analyzeES5); } } diff --git a/lib/package.json b/lib/package.json index 8cef576..baea3df 100644 --- a/lib/package.json +++ b/lib/package.json @@ -12,7 +12,8 @@ "test:unit": "jest" }, "dependencies": { - "@sentry/browser": "^7.73.0" + "@sentry/browser": "^7.73.0", + "core-js": "^3.35.1" }, "devDependencies": { "@hcaptcha/types": "^1.0.3", diff --git a/lib/src/loader.ts b/lib/src/loader.ts index 0e4b02f..83bad76 100644 --- a/lib/src/loader.ts +++ b/lib/src/loader.ts @@ -125,6 +125,6 @@ export async function loadScript(params, retries = 0) { } -export async function hCaptchaLoader(params) { +export async function hCaptchaLoader(params = {}) { return await loadScript(params); } diff --git a/lib/src/polyfills.ts b/lib/src/polyfills.ts new file mode 100644 index 0000000..b46d37b --- /dev/null +++ b/lib/src/polyfills.ts @@ -0,0 +1,4 @@ +import 'core-js/es/array/find'; +import 'core-js/es/object/assign'; +import 'core-js/es/object/entries'; +import 'core-js/es/object/get-own-property-descriptors'; diff --git a/lib/tsconfig.types.json b/lib/tsconfig.types.json index 35f4d01..8cf2a7b 100644 --- a/lib/tsconfig.types.json +++ b/lib/tsconfig.types.json @@ -13,5 +13,8 @@ "include": [ "src/**/*" + ], + "exclude": [ + "src/polyfills.ts" ] } diff --git a/package.json b/package.json index e84012b..89b6320 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@hcaptcha/loader", "description": "This is a JavaScript library to easily configure the loading of the hCaptcha JS client SDK with built-in error handling.", - "version": "1.2.1", + "version": "1.2.2", "author": "hCaptcha team and contributors", "license": "MIT", "keywords": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2433bb8..5065438 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + importers: .: @@ -43,6 +47,9 @@ importers: '@sentry/browser': specifier: ^7.73.0 version: 7.73.0 + core-js: + specifier: ^3.35.1 + version: 3.35.1 devDependencies: '@hcaptcha/types': specifier: ^1.0.3 @@ -2171,6 +2178,11 @@ packages: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} dev: true + /core-js@3.35.1: + resolution: {integrity: sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw==} + requiresBuild: true + dev: false + /corser@2.0.1: resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} engines: {node: '>= 0.4.0'} From 19238d8e6592871d56058c80dd8e998cb406d4ff Mon Sep 17 00:00:00 2001 From: Brad Peters Date: Thu, 15 Feb 2024 13:02:19 -0800 Subject: [PATCH 4/4] fix(readme): Missed bracket for link (#24) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f8fea21..da45b23 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ declare global { ``` ### CDN -The hCaptcha Loader targeted for older browsers can also be imported via CDN by using UNPKG](https://www.unpkg.com/), see example below. +The hCaptcha Loader targeted for older browsers can also be imported via CDN by using [UNPKG](https://www.unpkg.com/), see example below. ```html