New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

solid-hcaptcha

Package Overview
Dependencies
Maintainers
0
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

solid-hcaptcha - npm Package Compare versions

Comparing version 0.4.0 to 1.0.0

41

dist/index.d.ts

@@ -44,3 +44,3 @@ /// <reference types="@hcaptcha/types" />

*/
theme?: "light" | "dark";
theme?: "light" | "dark" | "contrast" | object;
/**

@@ -60,13 +60,6 @@ * Set the tabindex of the widget and popup.

/**
* Advanced configuration for
* the hCaptcha component.
*/
config?: HCaptchaConfig;
}
interface HCaptchaConfig {
/**
* Disable drop-in replacement for reCAPTCHA with `false`
* to prevent hCaptcha from injecting into `window.grecaptcha`.
*/
recaptchacompat?: boolean;
reCaptchaCompat?: boolean;
/**

@@ -79,2 +72,4 @@ * When setting to `auto`, hCaptcha auto-detects language via the user's browser.

*/
languageOverride?: string;
/** @deprecated Use `languageOverride` instead. */
hl?: string;

@@ -87,4 +82,19 @@ apihost?: string;

reportapi?: string;
sentry?: string;
secureApi?: string;
scriptSource?: string;
custom?: boolean;
/**
* Set if the script should be loaded asynchronously.
* @default true
*/
loadAsync?: boolean;
/**
* Remove script tag after setup.
* @default true
*/
cleanup?: boolean;
/**
* Should enable Sentry reporting for hCaptcha.
*/
sentry?: boolean;
}

@@ -97,3 +107,3 @@ interface HCaptchaFunctions {

/** Manually remove the hCaptcha widget from the DOM. */
removeCaptcha(callback: () => unknown): void;
removeCaptcha(callback?: () => unknown): void;
/**

@@ -122,11 +132,4 @@ * Get the associated ekey (challenge ID) for a successful solve.

/** The name of the function that will be triggered when hCaptcha is loaded. */
declare const HCAPTCHA_ONLOAD_FUNCTION_NAME = "__hCaptchaOnLoad__";
declare global {
interface Window {
[HCAPTCHA_ONLOAD_FUNCTION_NAME]: () => void;
}
}
declare const HCaptcha: Component<HCaptchaProps>;
export { HCaptchaConfig, HCaptchaExecuteResponse, HCaptchaFunctions, HCaptchaProps, HCaptcha as default };
export { type HCaptchaExecuteResponse, type HCaptchaFunctions, type HCaptchaProps, HCaptcha as default };

@@ -1,1 +0,1 @@

import{effect as a,setAttribute as c,template as e,use as t}from"solid-js/web";import{createScriptLoader as o}from"@solid-primitives/script-loader";import{onMount as h,onCleanup as p}from"solid-js";import{createStore as n}from"solid-js/store";var r=e("<div>"),s="__hCaptchaOnLoad__",i=e=>{const i=()=>((a,c,e)=>{const t=new URL(e||"https://js.hcaptcha.com");t.pathname="/1/api.js";for(const[c,e]of Object.entries(a))e&&t.searchParams.set(encodeURIComponent(c),encodeURIComponent(e));return t.searchParams.set("render","explicit"),t.searchParams.set("onload",c),t.toString()})({assethost:e.config?.assethost,endpoint:e.config?.endpoint,hl:e.config?.hl,host:e.config?.host,imghost:e.config?.imghost,recaptchacompat:!1===e.config?.recaptchacompat?"off":null,reportapi:e.config?.reportapi,sentry:e.config?.sentry,custom:e.config?.custom},s,e.config?.apihost),d=()=>void 0!==window.hcaptcha,l=()=>{const{isRemoved:a}=f;return d()&&!a};let m;const[f,I]=n({isRemoved:!1,captchaId:null}),u=a=>{if(!m)return;const c=Object.assign({"open-callback":b,"close-callback":w,"error-callback":x,"chalexpired-callback":k,"expired-callback":v,callback:R},e.config,{sitekey:e.sitekey,tabindex:e.tabindex||0,theme:e.theme||"light",size:e.size||"normal"}),t=hcaptcha.render(m,c);I({isRemoved:!1,captchaId:t}),a&&a()},g={execute:async()=>{if(!l()||!f.captchaId)return;return await hcaptcha.execute(f.captchaId,{async:!0})},executeSync:()=>{if(l()&&f.captchaId)return hcaptcha.execute(f.captchaId,{async:!1})},getRespKey:()=>f.captchaId?hcaptcha.getRespKey(f.captchaId):null,getResponse:()=>f.captchaId?hcaptcha.getResponse(f.captchaId):null,removeCaptcha:a=>{l()&&f.captchaId&&(I({isRemoved:!0}),hcaptcha.remove(f.captchaId),a&&a())},renderCaptcha:u,resetCaptcha:()=>{l()&&f.captchaId&&hcaptcha.reset(f.captchaId)},setData:a=>{if(l()&&f.captchaId)return hcaptcha.setData(f.captchaId,a)}},y=()=>{window[s]=()=>{};u((()=>e.onLoad?.(g)))},R=()=>{if("undefined"==typeof hcaptcha||f.isRemoved||!f.captchaId)return;if(!e.onVerify)return;const a=hcaptcha.getResponse(f.captchaId),c=hcaptcha.getRespKey(f.captchaId);e.onVerify(a,c)},v=()=>{l()&&f.captchaId&&(hcaptcha.reset(f.captchaId),e.onExpire?.())},x=a=>{l()&&f.captchaId&&(hcaptcha.reset(f.captchaId),e.onError?.(a))},b=()=>{l()&&e.onOpen?.()},w=()=>{l()&&e.onClose?.()},k=()=>{l()&&e.onChallengeExpired?.()};return h((()=>{d()?y():(window[s]=y,o({src:i()}))})),p((()=>{l()&&f.captchaId&&(hcaptcha.reset(f.captchaId),hcaptcha.remove(f.captchaId),window.hcaptcha=void 0)})),(()=>{const o=r();return"function"==typeof m?t(m,o):m=o,a((()=>c(o,"id",e.id))),o})()};export{i as default};//# sourceMappingURL=index.js.map
import{effect as a,setAttribute as e,template as c,use as t}from"solid-js/web";import{onMount as r,onCleanup as p,createEffect as h,on as o}from"solid-js";import{createStore as d}from"solid-js/store";import{initSentry as s,hCaptchaLoader as n}from"@hcaptcha/loader";var i={key:"source",value:"solid-hcaptcha"},l="hCaptcha component mounted",u="hCaptcha expired",m="hCaptcha component unmounted",g="hCaptcha reset",I="hCaptcha removed",y=c("<div>"),v=c=>{let v=null,f=!1;const x=()=>void 0!==window.hcaptcha,b=()=>{const{isRemoved:a}=R;return x()&&!a};let C;const[R,k]=d({isRemoved:!1,captchaId:null}),E=a=>{if(!C)return;if(!x())return;const e=Object.assign({"open-callback":S,"close-callback":q,"error-callback":K,"chalexpired-callback":D,"expired-callback":A,callback:w},c,{hl:c.hl||c.languageOverride,languageOverride:void 0}),t=hcaptcha.render(C,e);k({isRemoved:!1,captchaId:t}),a?.()},O=a=>{b()&&R.captchaId&&(k({isRemoved:!0}),hcaptcha.remove(R.captchaId),a?.(),v?.addBreadcrumb({category:i.value,message:I}))},B=()=>{try{E((()=>{c.onLoad?.(j)}))}catch(a){v?.captureException(a)}},j={execute:async a=>{if(b()&&R.captchaId)try{return await hcaptcha.execute(R.captchaId,{async:!0,rqdata:a})}catch(a){v?.captureException(a)}},executeSync:a=>{if(b()&&R.captchaId)try{return hcaptcha.execute(R.captchaId,{async:!1,rqdata:a})}catch(a){v?.captureException(a)}},getRespKey:()=>R.captchaId?hcaptcha.getRespKey(R.captchaId):null,getResponse:()=>R.captchaId?hcaptcha.getResponse(R.captchaId):null,removeCaptcha:O,renderCaptcha:E,resetCaptcha:()=>{b()&&R.captchaId&&(hcaptcha.reset(R.captchaId),v?.addBreadcrumb({category:i.value,message:g}))},setData:a=>{if(b()&&R.captchaId)return hcaptcha.setData(R.captchaId,a)}},w=()=>{if(!x()||R.isRemoved||!R.captchaId)return;const a=hcaptcha.getResponse(R.captchaId),e=hcaptcha.getRespKey(R.captchaId);c.onVerify?.(a,e)},A=()=>{b()&&R.captchaId&&(hcaptcha.reset(R.captchaId),c.onExpire?.(),v?.addBreadcrumb({category:i.value,message:u}))},K=a=>{b()&&R.captchaId&&hcaptcha.reset(R.captchaId),v?.captureException(a),c.onError?.(a)},S=()=>{b()&&c.onOpen?.()},q=()=>{b()&&c.onClose?.()},D=()=>{b()&&c.onChallengeExpired?.()};return r((()=>{v=s(c.sentry??!0,i),v?.addBreadcrumb({category:i.value,message:l}),x()&&E(),(()=>{if(f)return;n({render:"explicit",apihost:c?.apihost,assethost:c?.assethost,endpoint:c?.endpoint,hl:c?.languageOverride,host:c?.host,imghost:c?.imghost,recaptchacompat:!1===c?.reCaptchaCompat?"off":null,reportapi:c?.reportapi,sentry:c?.sentry,custom:c?.custom,loadAsync:c?.loadAsync??!0,cleanup:c?.cleanup??!0,scriptSource:c?.scriptSource,secureApi:c?.secureApi}).then(B,K).catch(K),f=!0})()})),p((()=>{b()&&R.captchaId&&(hcaptcha.reset(R.captchaId),hcaptcha.remove(R.captchaId),v?.addBreadcrumb({category:i.value,message:m}))})),h(o([()=>c.sitekey,()=>c.size,()=>c.theme,()=>c.tabindex,()=>c.languageOverride,()=>c.endpoint],(()=>O((()=>E()))))),(()=>{const r=y();return"function"==typeof C?t(C,r):C=r,a((()=>e(r,"id",c.id))),r})()};export{v as default};//# sourceMappingURL=index.js.map
// src/hcaptcha.tsx
import { createScriptLoader } from "@solid-primitives/script-loader";
import { onCleanup, onMount } from "solid-js";
import { createEffect, on, onCleanup, onMount } from "solid-js";
import { createStore } from "solid-js/store";
import { hCaptchaLoader, initSentry } from "@hcaptcha/loader";
// src/utils.ts
var generateScriptUrl = (params, onLoadFunctionName, apihost) => {
const domain = apihost || "https://js.hcaptcha.com";
const url = new URL(domain);
url.pathname = "/1/api.js";
for (const [key, value] of Object.entries(params)) {
if (!value)
continue;
url.searchParams.set(encodeURIComponent(key), encodeURIComponent(value));
}
url.searchParams.set("render", "explicit");
url.searchParams.set("onload", onLoadFunctionName);
return url.toString();
// src/constants.ts
var scopeTag = {
key: "source",
value: "solid-hcaptcha"
};
var breadcrumbMessages = {
mounted: "hCaptcha component mounted",
expired: "hCaptcha expired",
unmounted: "hCaptcha component unmounted",
reset: "hCaptcha reset",
removed: "hCaptcha removed"
};
// src/hcaptcha.tsx
var HCAPTCHA_ONLOAD_FUNCTION_NAME = "__hCaptchaOnLoad__";
var HCaptcha = (props) => {
const script_url = () => generateScriptUrl({
assethost: props.config?.assethost,
endpoint: props.config?.endpoint,
hl: props.config?.hl,
host: props.config?.host,
imghost: props.config?.imghost,
recaptchacompat: props.config?.recaptchacompat === false ? "off" : null,
reportapi: props.config?.reportapi,
sentry: props.config?.sentry,
custom: props.config?.custom
}, HCAPTCHA_ONLOAD_FUNCTION_NAME, props.config?.apihost);
let sentryHub = null;
let apiScriptRequest = false;
const loadCaptcha = () => {
if (apiScriptRequest) return;
const mountParams = {
render: "explicit",
apihost: props?.apihost,
assethost: props?.assethost,
endpoint: props?.endpoint,
hl: props?.languageOverride,
host: props?.host,
imghost: props?.imghost,
recaptchacompat: props?.reCaptchaCompat === false ? "off" : null,
reportapi: props?.reportapi,
sentry: props?.sentry,
custom: props?.custom,
loadAsync: props?.loadAsync ?? true,
cleanup: props?.cleanup ?? true,
scriptSource: props?.scriptSource,
secureApi: props?.secureApi
};
hCaptchaLoader(mountParams).then(handleOnLoad, handleError).catch(handleError);
apiScriptRequest = true;
};
const isApiReady = () => typeof window.hcaptcha !== "undefined";

@@ -46,4 +56,4 @@ const isReady = () => {

const renderCaptcha = (onReady) => {
if (!captcha_ref)
return;
if (!captcha_ref) return;
if (!isApiReady()) return;
const renderParams = Object.assign({

@@ -56,49 +66,64 @@ "open-callback": handleOpen,

"callback": handleSubmit
}, props.config, {
"sitekey": props.sitekey,
"tabindex": props.tabindex || 0,
"theme": props.theme || "light",
"size": props.size || "normal"
}, props, {
hl: props.hl || props.languageOverride,
languageOverride: void 0
});
const captchaId = hcaptcha.render(captcha_ref, renderParams);
setState({ isRemoved: false, captchaId });
if (onReady)
onReady();
onReady?.();
};
const resetCaptcha = () => {
if (!isReady() || !state.captchaId)
return;
if (!isReady() || !state.captchaId) return;
hcaptcha.reset(state.captchaId);
sentryHub?.addBreadcrumb({
category: scopeTag.value,
message: breadcrumbMessages.reset
});
};
const removeCaptcha = (callback) => {
if (!isReady() || !state.captchaId)
return;
if (!isReady() || !state.captchaId) return;
setState({ isRemoved: true });
hcaptcha.remove(state.captchaId);
callback && callback();
callback?.();
sentryHub?.addBreadcrumb({
category: scopeTag.value,
message: breadcrumbMessages.removed
});
};
const executeSync = () => {
if (!isReady() || !state.captchaId)
return;
return hcaptcha.execute(state.captchaId, { async: false });
const handleOnLoad = () => {
try {
renderCaptcha(() => {
props.onLoad?.(hcaptcha_functions);
});
} catch (e) {
sentryHub?.captureException(e);
}
};
const execute = async () => {
if (!isReady() || !state.captchaId)
return;
const response = await hcaptcha.execute(state.captchaId, { async: true });
return response;
const executeSync = (rqdata) => {
if (!isReady() || !state.captchaId) return;
try {
return hcaptcha.execute(state.captchaId, { async: false, rqdata });
} catch (e) {
sentryHub?.captureException(e);
}
};
const execute = async (rqdata) => {
if (!isReady() || !state.captchaId) return;
try {
const response = await hcaptcha.execute(state.captchaId, { async: true, rqdata });
return response;
} catch (e) {
sentryHub?.captureException(e);
}
};
const setData = (data) => {
if (!isReady() || !state.captchaId)
return;
if (!isReady() || !state.captchaId) return;
return hcaptcha.setData(state.captchaId, data);
};
const getResponse = () => {
if (!state.captchaId)
return null;
if (!state.captchaId) return null;
return hcaptcha.getResponse(state.captchaId);
};
const getRespKey = () => {
if (!state.captchaId)
return null;
if (!state.captchaId) return null;
return hcaptcha.getRespKey(state.captchaId);

@@ -116,57 +141,64 @@ };

};
const handleOnLoad = () => {
window[HCAPTCHA_ONLOAD_FUNCTION_NAME] = () => void 0;
const onReady = () => props.onLoad?.(hcaptcha_functions);
renderCaptcha(onReady);
};
const handleSubmit = () => {
if (typeof hcaptcha === "undefined" || state.isRemoved || !state.captchaId)
return;
if (!props.onVerify)
return;
if (!isApiReady() || state.isRemoved || !state.captchaId) return;
const token = hcaptcha.getResponse(state.captchaId);
const ekey = hcaptcha.getRespKey(state.captchaId);
props.onVerify(token, ekey);
props.onVerify?.(token, ekey);
};
const handleExpire = () => {
if (!isReady() || !state.captchaId)
return;
if (!isReady() || !state.captchaId) return;
hcaptcha.reset(state.captchaId);
props.onExpire?.();
sentryHub?.addBreadcrumb({
category: scopeTag.value,
message: breadcrumbMessages.expired
});
};
const handleError = (event) => {
if (!isReady() || !state.captchaId)
return;
hcaptcha.reset(state.captchaId);
if (isReady() && state.captchaId) {
hcaptcha.reset(state.captchaId);
}
sentryHub?.captureException(event);
props.onError?.(event);
};
const handleOpen = () => {
if (!isReady())
return;
if (!isReady()) return;
props.onOpen?.();
};
const handleClose = () => {
if (!isReady())
return;
if (!isReady()) return;
props.onClose?.();
};
const handleChallengeExpired = () => {
if (!isReady())
return;
if (!isReady()) return;
props.onChallengeExpired?.();
};
onMount(() => {
if (!isApiReady()) {
window[HCAPTCHA_ONLOAD_FUNCTION_NAME] = handleOnLoad;
createScriptLoader({ src: script_url() });
} else
handleOnLoad();
sentryHub = initSentry(props.sentry ?? true, scopeTag);
sentryHub?.addBreadcrumb({
category: scopeTag.value,
message: breadcrumbMessages.mounted
});
if (isApiReady()) {
renderCaptcha();
}
loadCaptcha();
});
onCleanup(() => {
if (!isReady() || !state.captchaId)
return;
if (!isReady() || !state.captchaId) return;
hcaptcha.reset(state.captchaId);
hcaptcha.remove(state.captchaId);
window.hcaptcha = void 0;
sentryHub?.addBreadcrumb({
category: scopeTag.value,
message: breadcrumbMessages.unmounted
});
});
createEffect(on([
() => props.sitekey,
() => props.size,
() => props.theme,
() => props.tabindex,
() => props.languageOverride,
() => props.endpoint
], () => removeCaptcha(() => renderCaptcha())));
return <div

@@ -173,0 +205,0 @@ ref={captcha_ref}

{
"name": "solid-hcaptcha",
"description": "hCaptcha Component Library for Solid.",
"version": "0.4.0",
"version": "1.0.0",
"license": "MIT",

@@ -33,4 +33,4 @@ "type": "module",

"url": "https://github.com/Vexcited",
"email": "mikkel@milescode.dev",
"name": "Mikkel RINGAUD"
"email": "vexitofficial@gmail.com",
"name": "Mikkel ALMONTE--RINGAUD"
},

@@ -49,18 +49,18 @@ "repository": {

"@typescript-eslint/parser": "^6.7.4",
"eslint": "^8.50.0",
"eslint-plugin-solid": "^0.13.0",
"release-it": "^16.2.1",
"eslint": "^8.57.1",
"eslint-plugin-solid": "^0.14.3",
"release-it": "^17.8.2",
"rimraf": "^5.0.5",
"solid-js": "^1.7.12",
"solid-js": "^1.9.2",
"terser": "^5.21.0",
"tsup": "^7.2.0",
"tsup-preset-solid": "^2.1.0",
"tsup": "^8.3.0",
"tsup-preset-solid": "^2.2.0",
"typescript": "^5.2.2"
},
"peerDependencies": {
"solid-js": "^1.6.15 || ^1.7.12"
"solid-js": "^1.6.0 || ^1.7.0 || ^1.8.0 || ^1.9.0"
},
"dependencies": {
"@hcaptcha/types": "^1.0.3",
"@solid-primitives/script-loader": "^2.0.2"
"@hcaptcha/loader": "^1.2.4",
"@hcaptcha/types": "^1.0.4"
},

@@ -67,0 +67,0 @@ "browser": {},

@@ -106,24 +106,20 @@ # Solid hCaptcha Component Library

| `size` | `"normal" \| "compact" \| "invisible"` | No | `"normal"` | This specifies the "size" of the component. hCaptcha allows you to decide how big the component will appear on render, this always defaults to normal. |
| `theme` | `"light" \| "dark"` | No | `"light"` | hCaptcha supports both a light and dark theme. If no theme is inherently set, the captcha will always default to light. |
| `theme` | `"light" \| "dark" \| "constrast"` or `object` | No | `"light"` | hCaptcha supports both a light and dark theme. If no theme is inherently set, the captcha will always default to light. Takes `object` if custom theme is used. |
| `tabindex` | `number` | No | `0` | Set the tabindex of the widget and popup. When appropriate, this can make navigation of your site more intuitive. |
| `id` | `string` | No | `-` | Set an ID to the hCaptcha widget. Make sure each hCaptcha component generated on a single page has its own unique ID when using this prop. |
| `config` | [`HCaptchaConfig`](#advanced-configuration-hcaptchaconfig) | No | `{}` | Advanced configuration for the hCaptcha component. |
| `reCaptchaCompat` | `boolean` | No | `true` | Disable drop-in replacement for reCAPTCHA with `false` to prevent hCaptcha from injecting into `window.grecaptcha`. |
| `languageOverride` | `string` (ISO 639-2 code) | No | `auto` | hCaptcha auto-detects language via the user's browser. This overrides that to set a default UI language. See [language codes](https://hcaptcha.com/docs/languages). |
| `apihost` | `string` | No | `-` | See enterprise docs. |
| `assethost` | `string` | No | `-` | See enterprise docs. |
| `endpoint` | `string` | No | `-` | See enterprise docs. |
| `host` | `string` | No | `-` | See enterprise docs. |
| `imghost` | `string` | No | `-` | See enterprise docs. |
| `reportapi` | `string` | No | `-` | See enterprise docs. |
| `secureApi` | `boolean` | No |`-`| See enterprise docs. |
| `scriptSource` | `string` | No |`-`| See enterprise docs. |
| `sentry` | `boolean` | **Yes** | `-` | Should enable Sentry reporting for hCaptcha. |
| `custom` | `boolean` | No | `-` | Custom theme: see enterprise docs. |
| `cleanup` | `boolean` | No | `true` | Remove script tag after setup. |
| `loadAsync` | `boolean` | No | `true` | Set if the script should be loaded asynchronously. |
### Advanced Configuration (`HCaptchaConfig`)
All the parameters are optional.
| Name | Values/Type | Default | Description |
| ---- | ----------- | ------- | ----------- |
| `recaptchacompat` | `boolean` | `true` | Disable drop-in replacement for reCAPTCHA with `false` to prevent hCaptcha from injecting into `window.grecaptcha`. |
| `hl` | `string` (ISO 639-2 code) | `auto` | hCaptcha auto-detects language via the user's browser. This overrides that to set a default UI language. See [language codes](https://hcaptcha.com/docs/languages). |
| `apihost` | `string` | `-` | See enterprise docs. |
| `assethost` | `string` | `-` | See enterprise docs. |
| `endpoint` | `string` | `-` | See enterprise docs. |
| `host` | `string` | `-` | See enterprise docs. |
| `imghost` | `string` | `-` | See enterprise docs. |
| `reportapi` | `string` | `-` | See enterprise docs. |
| `sentry` | `string` | `-` | See enterprise docs. |
| `custom` | `boolean` |`-` | See enterprise docs. |
### Events Props

@@ -165,4 +161,9 @@

### Make sure you are using `recaptchacompat: false` if you have the reCAPTCHA JS loaded on the same page
### Make sure you are using `reCaptchaCompat={false}` if you have the reCAPTCHA JS loaded on the same page
parallel (they recommend only running hCaptcha) then please disable their compatibility mode.
The hCaptcha "compatibility mode" will interfere with reCAPTCHA, as it adds properties with the same name. If for any reason you are running both hCaptcha and reCAPTCHA in parallel (we recommend only running hCaptcha) then please disable our compatibility mode.
### Avoid conflicts with legacy Sentry package usage on solid-hcaptcha 1.0.0+
If you are using Sentry 7.x in your SolidJS app, this can conflict with the upstream `hcaptcha-loader` package's Sentry error tracing.
You can avoid this issue by setting the `sentry` prop to `false`.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc