capjs-core 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capjs-core",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Stateless server-side challenge generator and verifier for Cap, a lightweight, modern open-source CAPTCHA alternative. No storage, no filesystem - bring your own.",
5
5
  "keywords": [
6
6
  "anti-abuse",
@@ -33,6 +33,13 @@
33
33
  "types": "./src/index.d.ts",
34
34
  "import": "./src/index.js",
35
35
  "default": "./src/index.js"
36
+ },
37
+ "./hashwx.wasm": "./src/hashwx.wasm"
38
+ },
39
+ "imports": {
40
+ "#hashwx-load": {
41
+ "workerd": "./src/hashwx-load.workerd.js",
42
+ "default": "./src/hashwx-load.js"
36
43
  }
37
44
  },
38
45
  "types": "src/index.d.ts",
@@ -46,6 +53,9 @@
46
53
  "src/rsw.js",
47
54
  "src/hashwx.js",
48
55
  "src/hashwx-wasm.js",
56
+ "src/hashwx-load.js",
57
+ "src/hashwx-load.workerd.js",
58
+ "src/hashwx.wasm",
49
59
  "README.md",
50
60
  "LICENSE"
51
61
  ],
@@ -0,0 +1,14 @@
1
+ import { HASHWX_WASM_BASE64 } from "./hashwx-wasm.js";
2
+
3
+ function decodeBase64(b64) {
4
+ if (typeof Buffer !== "undefined")
5
+ return new Uint8Array(Buffer.from(b64, "base64"));
6
+ const bin = atob(b64);
7
+ const out = new Uint8Array(bin.length);
8
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
9
+ return out;
10
+ }
11
+
12
+ export function loadHashwxModule() {
13
+ return WebAssembly.compile(decodeBase64(HASHWX_WASM_BASE64));
14
+ }
@@ -0,0 +1,5 @@
1
+ import hashwxModule from "./hashwx.wasm";
2
+
3
+ export async function loadHashwxModule() {
4
+ return hashwxModule;
5
+ }
package/src/hashwx.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import crypto from "node:crypto";
2
- import { HASHWX_WASM_BASE64 } from "./hashwx-wasm.js";
2
+ import { loadHashwxModule } from "#hashwx-load";
3
3
 
4
4
  export const HASHWX_SEED_SIZE = 32;
5
5
  export const HASHWX_CHALLENGE_SIZE = 32;
@@ -15,19 +15,23 @@ export const MAX_HASHWX_CHALLENGE_COUNT = 64;
15
15
  const U64_MAX = (1n << 64n) - 1n;
16
16
  const HASHWX_INTERPRETED = 0;
17
17
 
18
- function decodeBase64(b64) {
19
- if (typeof Buffer !== "undefined")
20
- return new Uint8Array(Buffer.from(b64, "base64"));
21
- const bin = atob(b64);
22
- const out = new Uint8Array(bin.length);
23
- for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
24
- return out;
25
- }
26
-
27
18
  let readyPromise = null;
19
+ let suppliedModule = null;
20
+
21
+ export function setHashwxModule(module) {
22
+ if (!(module instanceof WebAssembly.Module))
23
+ throw new TypeError(
24
+ "[capjs-core] setHashwxModule expects a WebAssembly.Module",
25
+ );
26
+ if (readyPromise)
27
+ throw new Error(
28
+ "[capjs-core] setHashwxModule must be called before HashWX initializes",
29
+ );
30
+ suppliedModule = module;
31
+ }
28
32
 
29
33
  async function init() {
30
- const mod = await WebAssembly.compile(decodeBase64(HASHWX_WASM_BASE64));
34
+ const mod = suppliedModule ?? (await loadHashwxModule());
31
35
  const instance = new WebAssembly.Instance(mod, {});
32
36
  const exports = instance.exports;
33
37
  if (typeof exports._initialize === "function") exports._initialize();
Binary file
package/src/index.d.ts CHANGED
@@ -250,6 +250,12 @@ export interface HashwxState {
250
250
  seedPtr: number;
251
251
  }
252
252
 
253
+ /** Supplies a precompiled HashWX module for runtimes that forbid compiling
254
+ * wasm at runtime. Cloudflare Workers pick up
255
+ * the bundled `hashwx.wasm` automatically. Must be called before the first
256
+ * verification or `hashwxReady()`. */
257
+ export function setHashwxModule(module: WebAssembly.Module): void;
258
+
253
259
  /** Compiles and initializes the embedded HashWX wasm. Called automatically on
254
260
  * first verification; await it at boot to move the cost off the first
255
261
  * request. Resolves to a state handle reusable with `hashwxHash`. */
package/src/index.js CHANGED
@@ -27,6 +27,7 @@ export {
27
27
  hashwxReady,
28
28
  hashwxSeed,
29
29
  hashwxTarget,
30
+ setHashwxModule,
30
31
  } from "./hashwx.js";
31
32
 
32
33
  import { fnv1a, fnv1aResume, prngFromHash } from "./prng.js";
@@ -240,9 +241,12 @@ export async function validateChallenge(secret, body, opts = {}) {
240
241
  }
241
242
 
242
243
  if (body.instr_blocked === true) {
243
- if (instrMeta.blockAutomatedBrowsers) {
244
- return fail("instr_automated_browser", { instr_error: true });
245
- }
244
+ return fail(
245
+ instrMeta.blockAutomatedBrowsers
246
+ ? "instr_automated_browser"
247
+ : "instr_failed",
248
+ { instr_error: true },
249
+ );
246
250
  } else if (body.instr_timeout === true) {
247
251
  return fail("instr_timeout", { instr_error: true });
248
252
  } else if (body.instr) {
@@ -434,9 +438,12 @@ async function validateChallengeV2(secret, body, payload, opts = {}) {
434
438
  return fail("instr_expired", { instr_error: true });
435
439
  }
436
440
  if (s.blocked === true) {
437
- if (meta.blockAutomatedBrowsers) {
438
- return fail("instr_automated_browser", { instr_error: true });
439
- }
441
+ return fail(
442
+ meta.blockAutomatedBrowsers
443
+ ? "instr_automated_browser"
444
+ : "instr_failed",
445
+ { instr_error: true },
446
+ );
440
447
  } else if (s.timeout === true) {
441
448
  return fail("instr_timeout", { instr_error: true });
442
449
  } else if (s.instr) {