diff --git a/docs/guide/index.md b/docs/guide/index.md index 1059715..c57fd9e 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -65,6 +65,8 @@ widget.addEventListener("solve", function (e) { Alternatively, you can use `onsolve=""` directly within the widget or wrap the widget in a `
` (where Cap will automatically submit the token alongside other form data. for this, it'll create a hidden field with name set to its `data-cap-hidden-field-name` attribute or `cap-token`). +You can learn how to use the widget in more detail (such as the invisible mode) in the [widget guide](./widget.md). + ## Server-side Cap is fully self-hosted, so you'll need to start a server exposing an API for Cap's methods running at the same URL as specified in the `data-cap-api-endpoint` attribute. diff --git a/docs/guide/widget.md b/docs/guide/widget.md index 78ebcc1..880bfcc 100644 --- a/docs/guide/widget.md +++ b/docs/guide/widget.md @@ -4,7 +4,7 @@ > > **Requirements:** All modern browsers should be supported, but the build script specifically targets the last 10 versions of Chrome, Firefox, Safari and Edge. -`@cap.js/widget` is Cap's client-side library. It includes the `cap-widget` web component, the invisible mode and the Captcha solver. First, add it to your client-side code: +`@cap.js/widget` is Cap's client-side library. It includes the `cap-widget` web component, the invisible mode and the CAPTCHA solver. First, add it to your client-side code: ::: code-group @@ -24,6 +24,12 @@ ::: +You can also import it if you're using a bundler: + +```js +import Cap from '@cap.js/widget'; +``` + ::: warning We're using the latest version of the library here for simplicity, but you should optimally pin a specific version to avoid breaking changes in the future. @@ -59,6 +65,19 @@ widget.addEventListener("solve", function (e) { Alternatively, you can use `onsolve=""` directly within the widget or wrap the widget in a `
` (where Cap will automatically submit the token alongside other form data. for this, it'll create a hidden field with name set to its `data-cap-hidden-field-name` attribute or `cap-token`). +## Invisible mode +You can use `new Cap({ ... })` in your client-side JavaScript to create a new Cap instance and use the `solve()` method to solve the challenge. This is helpful for situations where you don't want the Cap widget to be visible but still want security, e.g. on a social media app when posting something. + +```js +import Cap from '@cap.js/widget'; + +const cap = new Cap({ + apiEndpoint: '/api/cap' +}); + +const token = await cap.solve(); +``` + ## Supported events The following custom events are supported: @@ -94,7 +113,8 @@ You can override the default browser fetch implementation by setting `window.CAP ```js window.CAP_CUSTOM_FETCH = function (url, options) { - // Custom fetch implementation + // … add your custom fetch implementation + return fetch(url, options); }; ``` @@ -133,3 +153,7 @@ cap-widget { --cap-opacity-hover: 0.8; } ``` + +## Types + +Cap's widget is fully typed. You can find the type definitions in the `cap.d.ts` file. \ No newline at end of file diff --git a/widget/src/cap.d.ts b/widget/src/cap.d.ts new file mode 100644 index 0000000..1405306 --- /dev/null +++ b/widget/src/cap.d.ts @@ -0,0 +1,155 @@ +declare global { + interface Window { + CAP_CUSTOM_FETCH?: typeof fetch; + CAP_CUSTOM_WASM_URL?: string; + CAP_CSS_NONCE?: string; + CAP_DONT_SKIP_REDEFINE?: boolean; + Cap: typeof Cap; + } +} + +interface CapProgressEventDetail { + progress: number; +} + +interface CapSolveEventDetail { + token: string; +} + +interface CapErrorEventDetail { + isCap: boolean; + message: string; +} + +interface CapProgressEvent extends CustomEvent { + detail: CapProgressEventDetail; +} + +interface CapSolveEvent extends CustomEvent { + detail: CapSolveEventDetail; +} + +interface CapErrorEvent extends CustomEvent { + detail: CapErrorEventDetail; +} + +interface CapResetEvent extends CustomEvent { + detail: Record; +} + +interface SolveResult { + success: boolean; + token: string; +} + +interface CapConfig { + apiEndpoint?: string; + "data-cap-api-endpoint"?: string; + "data-cap-worker-count"?: string; + "data-cap-hidden-field-name"?: string; + "data-cap-i18n-initial-state"?: string; + "data-cap-i18n-verifying-label"?: string; + "data-cap-i18n-solved-label"?: string; + "data-cap-i18n-error-label"?: string; + "data-cap-i18n-verify-aria-label"?: string; + "data-cap-i18n-verifying-aria-label"?: string; + "data-cap-i18n-verified-aria-label"?: string; + "data-cap-i18n-error-aria-label"?: string; + "data-cap-i18n-wasm-disabled"?: string; + onsolve?: string; + onprogress?: string; + onreset?: string; + onerror?: string; +} + +interface CapWidget extends HTMLElement { + readonly token: string | null; + readonly tokenValue: string | null; + + solve(): Promise; + reset(): void; + setWorkersCount(workers: number): void; + + addEventListener( + type: "progress", + listener: (event: CapProgressEvent) => void + ): void; + addEventListener( + type: "solve", + listener: (event: CapSolveEvent) => void + ): void; + addEventListener( + type: "error", + listener: (event: CapErrorEvent) => void + ): void; + addEventListener( + type: "reset", + listener: (event: CapResetEvent) => void + ): void; + addEventListener(type: string, listener: EventListener): void; + + removeEventListener( + type: "progress", + listener: (event: CapProgressEvent) => void + ): void; + removeEventListener( + type: "solve", + listener: (event: CapSolveEvent) => void + ): void; + removeEventListener( + type: "error", + listener: (event: CapErrorEvent) => void + ): void; + removeEventListener( + type: "reset", + listener: (event: CapResetEvent) => void + ): void; + removeEventListener(type: string, listener: EventListener): void; +} + +declare class Cap { + readonly widget: CapWidget; + readonly token: string | null; + + constructor(config?: CapConfig, el?: CapWidget); + + solve(): Promise; + reset(): void; + + addEventListener( + type: "progress", + listener: (event: CapProgressEvent) => void + ): void; + addEventListener( + type: "solve", + listener: (event: CapSolveEvent) => void + ): void; + addEventListener( + type: "error", + listener: (event: CapErrorEvent) => void + ): void; + addEventListener( + type: "reset", + listener: (event: CapResetEvent) => void + ): void; + addEventListener(type: string, listener: EventListener): void; +} + +declare global { + interface HTMLElementTagNameMap { + "cap-widget": CapWidget; + } +} + +export { + Cap, + type CapWidget, + type CapConfig, + type CapProgressEvent, + type CapSolveEvent, + type CapErrorEvent, + type CapResetEvent, + type SolveResult, +}; + +export default Cap; diff --git a/widget/src/package.json b/widget/src/package.json index 50e0c26..b8b2fac 100644 --- a/widget/src/package.json +++ b/widget/src/package.json @@ -1,6 +1,6 @@ { "name": "@cap.js/widget", - "version": "0.1.28", + "version": "0.1.29", "description": "Client-side widget for Cap, a lightweight, modern open-source CAPTCHA alternative designed using SHA-256 PoW.", "keywords": [ "security", @@ -49,7 +49,8 @@ "url": "git+https://github.com/tiagozip/cap.git" }, "license": "Apache-2.0", - "author": "Tiago Rangel", + "author": "Tiago", "type": "commonjs", - "main": "cap.min.js" + "main": "cap.min.js", + "types": "cap.d.ts" }