release: WRNexusJS 0.4.0

This commit is contained in:
2026-07-27 12:42:18 +05:30
parent 8b728a3e5d
commit 30e5721e84
250 changed files with 10065 additions and 3923 deletions
+4 -2
View File
@@ -2,7 +2,9 @@ import type { CaptchaProvider } from "../types.ts";
export function defineCaptchaProvider<T extends CaptchaProvider>(provider: T): T {
if (!provider.name) throw new TypeError("Custom CAPTCHA provider requires a stable name");
if (!provider.client?.responseField) throw new TypeError("Custom CAPTCHA provider requires client.responseField");
if (typeof provider.verify !== "function") throw new TypeError("Custom CAPTCHA provider requires verify()");
if (!provider.client?.responseField)
throw new TypeError("Custom CAPTCHA provider requires client.responseField");
if (typeof provider.verify !== "function")
throw new TypeError("Custom CAPTCHA provider requires verify()");
return provider;
}
+11 -5
View File
@@ -32,10 +32,14 @@ export class ManagedCaptchaProvider implements CaptchaProvider {
}
async createChallenge(options: CreateCaptchaOptions): Promise<CaptchaChallenge> {
return this.request<CaptchaChallenge>("/v1/challenges", {
siteKey: this.options.siteKey,
...options,
}, false);
return this.request<CaptchaChallenge>(
"/v1/challenges",
{
siteKey: this.options.siteKey,
...options,
},
false,
);
}
async verify(input: VerifyCaptchaInput): Promise<CaptchaVerificationResult> {
@@ -64,6 +68,8 @@ export class ManagedCaptchaProvider implements CaptchaProvider {
}
}
export function managedCaptchaProvider(options: ManagedCaptchaProviderOptions): ManagedCaptchaProvider {
export function managedCaptchaProvider(
options: ManagedCaptchaProviderOptions,
): ManagedCaptchaProvider {
return new ManagedCaptchaProvider(options);
}
@@ -22,7 +22,10 @@ export class SelfHostedCaptchaProvider implements CaptchaProvider {
verify(input: VerifyCaptchaInput) {
return input.responseToken || input.providerToken
? this.engine.verifyResponseToken({ ...input, responseToken: input.responseToken ?? input.providerToken })
? this.engine.verifyResponseToken({
...input,
responseToken: input.responseToken ?? input.providerToken,
})
: this.engine.verify(input);
}
}
+44 -16
View File
@@ -61,8 +61,10 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
async verify(input: VerifyCaptchaInput): Promise<CaptchaVerificationResult> {
const token = input.providerToken ?? input.responseToken;
const action = input.action;
if (!token) return providerFailure(this.name, action, "missing-input", "Missing provider response token");
if (token.length > 4096) return providerFailure(this.name, action, "invalid-input", "Provider token is too long");
if (!token)
return providerFailure(this.name, action, "missing-input", "Missing provider response token");
if (token.length > 4096)
return providerFailure(this.name, action, "invalid-input", "Provider token is too long");
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs ?? 10_000);
const body = new URLSearchParams({ secret: this.options.secretKey, response: token });
@@ -79,7 +81,12 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
signal: controller.signal,
});
if (!response.ok) {
return providerFailure(this.name, action, "provider-error", `${this.name} verification returned HTTP ${response.status}`);
return providerFailure(
this.name,
action,
"provider-error",
`${this.name} verification returned HTTP ${response.status}`,
);
}
const data = (await response.json()) as SiteverifyPayload;
if (!data.success) {
@@ -98,26 +105,45 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
}
const expectedAction = this.options.expectedAction ?? action;
if (data.action && expectedAction && data.action !== expectedAction) {
return providerFailure(this.name, action, "action-mismatch", "Provider action does not match", {
receivedAction: data.action,
});
return providerFailure(
this.name,
action,
"action-mismatch",
"Provider action does not match",
{
receivedAction: data.action,
},
);
}
if (
this.options.expectedHostnames?.length &&
(!data.hostname || !this.options.expectedHostnames.includes(data.hostname))
) {
return providerFailure(this.name, action, "hostname-mismatch", "Provider hostname does not match", {
hostname: data.hostname,
});
return providerFailure(
this.name,
action,
"hostname-mismatch",
"Provider hostname does not match",
{
hostname: data.hostname,
},
);
}
if (this.options.minScore !== undefined && typeof data.score === "number") {
const rejected = this.preset.scoreDirection === "higher-is-risk"
? data.score >= this.options.minScore
: data.score < this.options.minScore;
const rejected =
this.preset.scoreDirection === "higher-is-risk"
? data.score >= this.options.minScore
: data.score < this.options.minScore;
if (rejected) {
return providerFailure(this.name, action, "risk-rejected", "Provider risk score did not pass", {
score: data.score,
});
return providerFailure(
this.name,
action,
"risk-rejected",
"Provider risk score did not pass",
{
score: data.score,
},
);
}
}
return {
@@ -135,7 +161,9 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
return providerFailure(
this.name,
action,
error instanceof DOMException && error.name === "AbortError" ? "network-error" : "provider-error",
error instanceof DOMException && error.name === "AbortError"
? "network-error"
: "provider-error",
error instanceof DOMException && error.name === "AbortError"
? `${this.name} verification timed out`
: `${this.name} verification failed`,