@prosopo/server 2.10.36 → 2.10.37

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/server@2.10.36 build:cjs
2
+ > @prosopo/server@2.10.37 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -37,5 +37,5 @@ Build end
37
37
  rendering chunks...
38
38
  dist/cjs/index.cjs 0.57 kB
39
39
  dist/cjs/config.cjs 0.92 kB
40
- dist/cjs/server.cjs 4.43 kB
41
- ✓ built in 124ms
40
+ dist/cjs/server.cjs 6.26 kB
41
+ ✓ built in 172ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/server@2.10.36 build:tsc
2
+ > @prosopo/server@2.10.37 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 4:35:29 PM - Projects in this build:
5
+ 10:28:08 AM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../locale/tsconfig.json
8
8
  * ../util/tsconfig.json
@@ -15,27 +15,27 @@
15
15
  * ../load-balancer/tsconfig.json
16
16
  * tsconfig.json
17
17
 
18
- 4:35:29 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/index.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
18
+ 10:28:08 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/vite/NodejsPolarsNativeFilePlugin.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
19
19
 
20
- 4:35:29 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
20
+ 10:28:08 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
21
21
 
22
- 4:35:29 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
22
+ 10:28:08 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
23
23
 
24
- 4:35:29 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
24
+ 10:28:08 AM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
25
25
 
26
- 4:35:29 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/keyring/keyring/types.ts' is older than output '../types/tsconfig.tsbuildinfo'
26
+ 10:28:08 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
27
27
 
28
- 4:35:30 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
28
+ 10:28:08 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
29
29
 
30
- 4:35:30 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
30
+ 10:28:08 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
31
31
 
32
- 4:35:30 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
32
+ 10:28:08 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/tests/utils/batches.unit.test.ts' is older than output '../common/tsconfig.tsbuildinfo'
33
33
 
34
- 4:35:30 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
34
+ 10:28:08 AM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
35
35
 
36
- 4:35:30 PM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/balancer.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
36
+ 10:28:08 AM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/balancer.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
37
37
 
38
- 4:35:30 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
38
+ 10:28:08 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
39
39
 
40
- 4:35:30 PM - Building project '/home/runner/work/captcha/captcha/packages/server/tsconfig.json'...
40
+ 10:28:08 AM - Building project '/home/runner/work/captcha/captcha/packages/server/tsconfig.json'...
41
41
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/server@2.10.36 build
2
+ > @prosopo/server@2.10.37 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/server@2.10.36 build:cross-env
6
+ > @prosopo/server@2.10.37 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -41,5 +41,5 @@ Build end
41
41
  rendering chunks...
42
42
  dist/index.js 0.39 kB
43
43
  dist/config.js 0.81 kB
44
- dist/server.js 4.35 kB
45
- ✓ built in 125ms
44
+ dist/server.js 6.17 kB
45
+ ✓ built in 187ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @prosopo/server
2
2
 
3
+ ## 2.10.37
4
+ ### Patch Changes
5
+
6
+ - 0a4f902: fix(server): dispatch verify by captchaType so puzzle tokens hit the puzzle endpoint
7
+
8
+ Puzzle tokens were silently failing server-side verification. `ProsopoServer.verifyProvider` only had two branches — `challenge` present → PoW verify, absent → image verify — but puzzle tokens carry a challenge too, so they were routed to `/VerifyPowCaptchaSolution` and 404'd on the pow record lookup (`captchastorage.puzzlecaptchas.serverChecked` stayed 0/N in prod). Customers using the puzzle flow got `verified: false` on legitimate solvers.
9
+
10
+ Fix in two parts:
11
+
12
+ - `@prosopo/types`: adds `captchaType?: CaptchaType` to `ProcaptchaOutputSchema` and appends `Option(str)` to `ProcaptchaTokenCodec`. The pre-existing binary layout is preserved in a frozen `ProcaptchaTokenCodecV1`, and `decodeProcaptchaOutput` falls back to it for tokens minted by client bundles that predate this field.
13
+ - `@prosopo/server`: `verifyProvider` now dispatches on `captchaType` (puzzle → `submitPuzzleCaptchaVerify`, pow → `submitPowCaptchaVerify`, image → `verifyDappUser`) with per-type `cachedTimeout` recency checks. The legacy challenge heuristic is kept as a fallback for old tokens with a `warn`-level log so ops can see the tail-off.
14
+ - `@prosopo/procaptcha-pow` / `procaptcha-puzzle` / `procaptcha`: each Manager now sets the correct `captchaType` on the object passed to `encodeProcaptchaOutput`.
15
+
16
+ Backwards compatibility: pow and image tokens minted by any prior client bundle continue to verify. Puzzle tokens minted by old bundles still fall through to the pow branch and 404 — same behaviour as before — until the customer upgrades both the client bundle and `@prosopo/server` together.
17
+ - Updated dependencies [0a4f902]
18
+ - @prosopo/types@4.9.10
19
+ - @prosopo/api@3.5.17
20
+ - @prosopo/keyring@2.9.58
21
+ - @prosopo/load-balancer@2.10.12
22
+
3
23
  ## 2.10.36
4
24
  ### Patch Changes
5
25
 
@@ -26,8 +26,15 @@ class ProsopoServer {
26
26
  return new api.ProviderApi(providerUrl, this.dappAccount || "");
27
27
  }
28
28
  /**
29
- * Verify the user with the provider URL passed in. If a challenge is provided, we use the challenge to verify the
30
- * user. If not, we use the user, dapp, and optionally the commitmentID, to verify the user.
29
+ * Verify a token with the issuing provider. Dispatches to the correct
30
+ * verify endpoint by inspecting the token's declared captchaType:
31
+ * - puzzle → submitPuzzleCaptchaVerify
32
+ * - pow → submitPowCaptchaVerify
33
+ * - image → verifyDappUser
34
+ * When captchaType is absent (a legacy token minted before the field was
35
+ * added), fall back to the historical heuristic: presence of `challenge`
36
+ * routes to PoW, absence routes to image. Legacy puzzle tokens follow the
37
+ * PoW branch — same behaviour as before this dispatch was introduced.
31
38
  * @param token
32
39
  * @param timeouts
33
40
  * @param providerUrl
@@ -36,10 +43,11 @@ class ProsopoServer {
36
43
  * @param challenge
37
44
  * @param ip
38
45
  * @param email
46
+ * @param captchaType
39
47
  */
40
- async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email) {
48
+ async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email, captchaType) {
41
49
  this.logger.info(() => ({
42
- data: { providerUrl },
50
+ data: { providerUrl, captchaType: captchaType ?? "legacy" },
43
51
  msg: "Verifying with provider"
44
52
  }));
45
53
  const dappUserSignature = this.pair?.sign(timestamp.toString());
@@ -50,18 +58,54 @@ class ProsopoServer {
50
58
  }
51
59
  const signatureHex = util.u8aToHex(dappUserSignature);
52
60
  const providerApi = this.getProviderApi(providerUrl);
61
+ if (captchaType === types.CaptchaType.puzzle) {
62
+ const puzzleTimeout = this.config.timeouts.puzzle.cachedTimeout;
63
+ if (!this.isRecent(timestamp, puzzleTimeout, "Puzzle")) {
64
+ return this.notRecentResponse();
65
+ }
66
+ return await providerApi.submitPuzzleCaptchaVerify(
67
+ token,
68
+ signatureHex,
69
+ user,
70
+ ip,
71
+ email
72
+ );
73
+ }
74
+ if (captchaType === types.CaptchaType.pow) {
75
+ const powTimeout = this.config.timeouts.pow.cachedTimeout;
76
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) {
77
+ return this.notRecentResponse();
78
+ }
79
+ return await providerApi.submitPowCaptchaVerify(
80
+ token,
81
+ signatureHex,
82
+ user,
83
+ ip,
84
+ email
85
+ );
86
+ }
87
+ if (captchaType === types.CaptchaType.image) {
88
+ const imageTimeout2 = this.config.timeouts.image.cachedTimeout;
89
+ if (!this.isRecent(timestamp, imageTimeout2, "Image")) {
90
+ return this.notRecentResponse();
91
+ }
92
+ return await providerApi.verifyDappUser(
93
+ token,
94
+ signatureHex,
95
+ user,
96
+ timeouts.image.cachedTimeout,
97
+ ip,
98
+ email
99
+ );
100
+ }
101
+ this.logger.warn(() => ({
102
+ data: { challenge: Boolean(challenge) },
103
+ msg: "Verifying legacy token without captchaType field; using challenge heuristic"
104
+ }));
53
105
  if (challenge) {
54
106
  const powTimeout = this.config.timeouts.pow.cachedTimeout;
55
- const recent2 = timestamp ? Date.now() - timestamp < powTimeout : false;
56
- if (!recent2) {
57
- this.logger.error(() => ({
58
- data: { timestamp },
59
- msg: "PoW captcha is not recent"
60
- }));
61
- return {
62
- verified: false,
63
- status: i18n.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
64
- };
107
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) {
108
+ return this.notRecentResponse();
65
109
  }
66
110
  return await providerApi.submitPowCaptchaVerify(
67
111
  token,
@@ -72,16 +116,8 @@ class ProsopoServer {
72
116
  );
73
117
  }
74
118
  const imageTimeout = this.config.timeouts.image.cachedTimeout;
75
- const recent = timestamp ? Date.now() - timestamp < imageTimeout : false;
76
- if (!recent) {
77
- this.logger.error(() => ({
78
- data: { timestamp },
79
- msg: "Image captcha is not recent"
80
- }));
81
- return {
82
- verified: false,
83
- status: i18n.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
84
- };
119
+ if (!this.isRecent(timestamp, imageTimeout, "Image")) {
120
+ return this.notRecentResponse();
85
121
  }
86
122
  return await providerApi.verifyDappUser(
87
123
  token,
@@ -92,6 +128,23 @@ class ProsopoServer {
92
128
  email
93
129
  );
94
130
  }
131
+ isRecent(timestamp, timeoutMs, label) {
132
+ if (!timestamp) return false;
133
+ const recent = Date.now() - timestamp < timeoutMs;
134
+ if (!recent) {
135
+ this.logger.error(() => ({
136
+ data: { timestamp, timeoutMs },
137
+ msg: `${label} captcha is not recent`
138
+ }));
139
+ }
140
+ return recent;
141
+ }
142
+ notRecentResponse() {
143
+ return {
144
+ verified: false,
145
+ status: i18n.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
146
+ };
147
+ }
95
148
  /**
96
149
  * @param token
97
150
  * @param ip
@@ -100,7 +153,7 @@ class ProsopoServer {
100
153
  async isVerified(token, ip, email) {
101
154
  try {
102
155
  const payload = types.decodeProcaptchaOutput(token);
103
- const { providerUrl, challenge, timestamp, user } = types.ProcaptchaOutputSchema.parse(payload);
156
+ const { providerUrl, challenge, timestamp, user, captchaType } = types.ProcaptchaOutputSchema.parse(payload);
104
157
  const providers = await loadBalancer.loadBalancer(this.config.defaultEnvironment);
105
158
  const provider = providers.find((p) => p.url === providerUrl);
106
159
  if (!provider) {
@@ -121,7 +174,8 @@ class ProsopoServer {
121
174
  user,
122
175
  challenge,
123
176
  ip,
124
- email
177
+ email,
178
+ captchaType
125
179
  );
126
180
  this.logger.info(() => ({
127
181
  data: {
@@ -129,6 +183,7 @@ class ProsopoServer {
129
183
  providerUrl,
130
184
  user,
131
185
  challenge,
186
+ captchaType: captchaType ?? "legacy",
132
187
  siteKey: this.pair?.address
133
188
  }
134
189
  }));
package/dist/server.d.ts CHANGED
@@ -2,7 +2,7 @@ import { ProviderApi } from "@prosopo/api";
2
2
  import { Keyring } from "@prosopo/keyring";
3
3
  import { type Logger } from "@prosopo/logger";
4
4
  import type { KeyringPair } from "@prosopo/types";
5
- import { type CaptchaTimeoutOutput, type ProcaptchaToken, type ProsopoServerConfigOutput, type VerificationResponse } from "@prosopo/types";
5
+ import { type CaptchaTimeoutOutput, CaptchaType, type ProcaptchaToken, type ProsopoServerConfigOutput, type VerificationResponse } from "@prosopo/types";
6
6
  export declare class ProsopoServer {
7
7
  config: ProsopoServerConfigOutput;
8
8
  dappAccount: string | undefined;
@@ -12,7 +12,9 @@ export declare class ProsopoServer {
12
12
  pair: KeyringPair | undefined;
13
13
  constructor(config: ProsopoServerConfigOutput, pair?: KeyringPair);
14
14
  getProviderApi(providerUrl: string): ProviderApi;
15
- verifyProvider(token: string, timeouts: CaptchaTimeoutOutput, providerUrl: string, timestamp: number, user: string, challenge?: string, ip?: string, email?: string): Promise<VerificationResponse>;
15
+ verifyProvider(token: string, timeouts: CaptchaTimeoutOutput, providerUrl: string, timestamp: number, user: string, challenge?: string, ip?: string, email?: string, captchaType?: CaptchaType): Promise<VerificationResponse>;
16
+ private isRecent;
17
+ private notRecentResponse;
16
18
  isVerified(token: ProcaptchaToken, ip?: string, email?: string): Promise<VerificationResponse>;
17
19
  }
18
20
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAcA,OAAO,EAAa,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAAiB,KAAK,MAAM,EAAa,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACN,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EAEzB,MAAM,gBAAgB,CAAC;AAIxB,qBAAa,aAAa;IACzB,MAAM,EAAE,yBAAyB,CAAC;IAClC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;gBAElB,MAAM,EAAE,yBAAyB,EAAE,IAAI,CAAC,EAAE,WAAW;IAcjE,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW;IAgBnC,cAAc,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,oBAAoB,CAAC;IA8DnB,UAAU,CACtB,KAAK,EAAE,eAAe,EACtB,EAAE,CAAC,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,oBAAoB,CAAC;CAsDhC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAcA,OAAO,EAAa,WAAW,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAE3C,OAAO,EAAiB,KAAK,MAAM,EAAa,MAAM,iBAAiB,CAAC;AACxE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACN,KAAK,oBAAoB,EACzB,WAAW,EAEX,KAAK,eAAe,EACpB,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EAEzB,MAAM,gBAAgB,CAAC;AAIxB,qBAAa,aAAa;IACzB,MAAM,EAAE,yBAAyB,CAAC;IAClC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,WAAW,GAAG,SAAS,CAAC;gBAElB,MAAM,EAAE,yBAAyB,EAAE,IAAI,CAAC,EAAE,WAAW;IAcjE,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW;IAwBnC,cAAc,CAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,oBAAoB,EAC9B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,SAAS,CAAC,EAAE,MAAM,EAClB,EAAE,CAAC,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,WAAW,GACvB,OAAO,CAAC,oBAAoB,CAAC;IA+FhC,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,iBAAiB;IAYZ,UAAU,CACtB,KAAK,EAAE,eAAe,EACtB,EAAE,CAAC,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,oBAAoB,CAAC;CAwDhC"}
package/dist/server.js CHANGED
@@ -3,7 +3,7 @@ import { ProsopoContractError, ProsopoApiError } from "@prosopo/common";
3
3
  import { Keyring } from "@prosopo/keyring";
4
4
  import { loadBalancer } from "@prosopo/load-balancer";
5
5
  import { getLogger } from "@prosopo/logger";
6
- import { decodeProcaptchaOutput, ProcaptchaOutputSchema } from "@prosopo/types";
6
+ import { CaptchaType, decodeProcaptchaOutput, ProcaptchaOutputSchema } from "@prosopo/types";
7
7
  import { u8aToHex } from "@prosopo/util";
8
8
  import i18n from "i18next";
9
9
  class ProsopoServer {
@@ -24,8 +24,15 @@ class ProsopoServer {
24
24
  return new ProviderApi(providerUrl, this.dappAccount || "");
25
25
  }
26
26
  /**
27
- * Verify the user with the provider URL passed in. If a challenge is provided, we use the challenge to verify the
28
- * user. If not, we use the user, dapp, and optionally the commitmentID, to verify the user.
27
+ * Verify a token with the issuing provider. Dispatches to the correct
28
+ * verify endpoint by inspecting the token's declared captchaType:
29
+ * - puzzle → submitPuzzleCaptchaVerify
30
+ * - pow → submitPowCaptchaVerify
31
+ * - image → verifyDappUser
32
+ * When captchaType is absent (a legacy token minted before the field was
33
+ * added), fall back to the historical heuristic: presence of `challenge`
34
+ * routes to PoW, absence routes to image. Legacy puzzle tokens follow the
35
+ * PoW branch — same behaviour as before this dispatch was introduced.
29
36
  * @param token
30
37
  * @param timeouts
31
38
  * @param providerUrl
@@ -34,10 +41,11 @@ class ProsopoServer {
34
41
  * @param challenge
35
42
  * @param ip
36
43
  * @param email
44
+ * @param captchaType
37
45
  */
38
- async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email) {
46
+ async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email, captchaType) {
39
47
  this.logger.info(() => ({
40
- data: { providerUrl },
48
+ data: { providerUrl, captchaType: captchaType ?? "legacy" },
41
49
  msg: "Verifying with provider"
42
50
  }));
43
51
  const dappUserSignature = this.pair?.sign(timestamp.toString());
@@ -48,18 +56,54 @@ class ProsopoServer {
48
56
  }
49
57
  const signatureHex = u8aToHex(dappUserSignature);
50
58
  const providerApi = this.getProviderApi(providerUrl);
59
+ if (captchaType === CaptchaType.puzzle) {
60
+ const puzzleTimeout = this.config.timeouts.puzzle.cachedTimeout;
61
+ if (!this.isRecent(timestamp, puzzleTimeout, "Puzzle")) {
62
+ return this.notRecentResponse();
63
+ }
64
+ return await providerApi.submitPuzzleCaptchaVerify(
65
+ token,
66
+ signatureHex,
67
+ user,
68
+ ip,
69
+ email
70
+ );
71
+ }
72
+ if (captchaType === CaptchaType.pow) {
73
+ const powTimeout = this.config.timeouts.pow.cachedTimeout;
74
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) {
75
+ return this.notRecentResponse();
76
+ }
77
+ return await providerApi.submitPowCaptchaVerify(
78
+ token,
79
+ signatureHex,
80
+ user,
81
+ ip,
82
+ email
83
+ );
84
+ }
85
+ if (captchaType === CaptchaType.image) {
86
+ const imageTimeout2 = this.config.timeouts.image.cachedTimeout;
87
+ if (!this.isRecent(timestamp, imageTimeout2, "Image")) {
88
+ return this.notRecentResponse();
89
+ }
90
+ return await providerApi.verifyDappUser(
91
+ token,
92
+ signatureHex,
93
+ user,
94
+ timeouts.image.cachedTimeout,
95
+ ip,
96
+ email
97
+ );
98
+ }
99
+ this.logger.warn(() => ({
100
+ data: { challenge: Boolean(challenge) },
101
+ msg: "Verifying legacy token without captchaType field; using challenge heuristic"
102
+ }));
51
103
  if (challenge) {
52
104
  const powTimeout = this.config.timeouts.pow.cachedTimeout;
53
- const recent2 = timestamp ? Date.now() - timestamp < powTimeout : false;
54
- if (!recent2) {
55
- this.logger.error(() => ({
56
- data: { timestamp },
57
- msg: "PoW captcha is not recent"
58
- }));
59
- return {
60
- verified: false,
61
- status: i18n.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
62
- };
105
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) {
106
+ return this.notRecentResponse();
63
107
  }
64
108
  return await providerApi.submitPowCaptchaVerify(
65
109
  token,
@@ -70,16 +114,8 @@ class ProsopoServer {
70
114
  );
71
115
  }
72
116
  const imageTimeout = this.config.timeouts.image.cachedTimeout;
73
- const recent = timestamp ? Date.now() - timestamp < imageTimeout : false;
74
- if (!recent) {
75
- this.logger.error(() => ({
76
- data: { timestamp },
77
- msg: "Image captcha is not recent"
78
- }));
79
- return {
80
- verified: false,
81
- status: i18n.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
82
- };
117
+ if (!this.isRecent(timestamp, imageTimeout, "Image")) {
118
+ return this.notRecentResponse();
83
119
  }
84
120
  return await providerApi.verifyDappUser(
85
121
  token,
@@ -90,6 +126,23 @@ class ProsopoServer {
90
126
  email
91
127
  );
92
128
  }
129
+ isRecent(timestamp, timeoutMs, label) {
130
+ if (!timestamp) return false;
131
+ const recent = Date.now() - timestamp < timeoutMs;
132
+ if (!recent) {
133
+ this.logger.error(() => ({
134
+ data: { timestamp, timeoutMs },
135
+ msg: `${label} captcha is not recent`
136
+ }));
137
+ }
138
+ return recent;
139
+ }
140
+ notRecentResponse() {
141
+ return {
142
+ verified: false,
143
+ status: i18n.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
144
+ };
145
+ }
93
146
  /**
94
147
  * @param token
95
148
  * @param ip
@@ -98,7 +151,7 @@ class ProsopoServer {
98
151
  async isVerified(token, ip, email) {
99
152
  try {
100
153
  const payload = decodeProcaptchaOutput(token);
101
- const { providerUrl, challenge, timestamp, user } = ProcaptchaOutputSchema.parse(payload);
154
+ const { providerUrl, challenge, timestamp, user, captchaType } = ProcaptchaOutputSchema.parse(payload);
102
155
  const providers = await loadBalancer(this.config.defaultEnvironment);
103
156
  const provider = providers.find((p) => p.url === providerUrl);
104
157
  if (!provider) {
@@ -119,7 +172,8 @@ class ProsopoServer {
119
172
  user,
120
173
  challenge,
121
174
  ip,
122
- email
175
+ email,
176
+ captchaType
123
177
  );
124
178
  this.logger.info(() => ({
125
179
  data: {
@@ -127,6 +181,7 @@ class ProsopoServer {
127
181
  providerUrl,
128
182
  user,
129
183
  challenge,
184
+ captchaType: captchaType ?? "legacy",
130
185
  siteKey: this.pair?.address
131
186
  }
132
187
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACxE,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAA8B,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAEN,WAAW,EACX,sBAAsB,EAItB,sBAAsB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,IAAI,MAAM,SAAS,CAAC;AAE3B,MAAM,OAAO,aAAa;IAQzB,YAAY,MAAiC,EAAE,IAAkB;QAChE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC;QACzD,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;QAC/C,IAAI,CAAC,MAAM,GAAG,SAAS,CACtB,IAAI,CAAC,MAAM,CAAC,QAA+B,EAC3C,iBAAiB,CACjB,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC;YAC1B,IAAI,EAAE,SAAS;SACf,CAAC,CAAC;IACJ,CAAC;IAED,cAAc,CAAC,WAAmB;QACjC,OAAO,IAAI,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IAsBM,KAAK,CAAC,cAAc,CAC1B,KAAa,EACb,QAA8B,EAC9B,WAAmB,EACnB,SAAiB,EACjB,IAAY,EACZ,SAAkB,EAClB,EAAW,EACX,KAAc,EACd,WAAyB;QAEzB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,IAAI,QAAQ,EAAE;YAC3D,GAAG,EAAE,yBAAyB;SAC9B,CAAC,CAAC,CAAC;QACJ,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACxB,MAAM,IAAI,oBAAoB,CAAC,2BAA2B,EAAE;gBAC3D,OAAO,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE;aACzC,CAAC,CAAC;QACJ,CAAC;QACD,MAAM,YAAY,GAAG,QAAQ,CAAC,iBAAiB,CAAC,CAAC;QACjD,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;QAErD,IAAI,WAAW,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC;YACxC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC;YAChE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,CAAC;gBACxD,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACjC,CAAC;YACD,OAAO,MAAM,WAAW,CAAC,yBAAyB,CACjD,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,EAAE,EACF,KAAK,CACL,CAAC;QACH,CAAC;QAED,IAAI,WAAW,KAAK,WAAW,CAAC,GAAG,EAAE,CAAC;YACrC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;gBAClD,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACjC,CAAC;YACD,OAAO,MAAM,WAAW,CAAC,sBAAsB,CAC9C,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,EAAE,EACF,KAAK,CACL,CAAC;QACH,CAAC;QAED,IAAI,WAAW,KAAK,WAAW,CAAC,KAAK,EAAE,CAAC;YACvC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC;YAC9D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC;gBACtD,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACjC,CAAC;YACD,OAAO,MAAM,WAAW,CAAC,cAAc,CACtC,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,QAAQ,CAAC,KAAK,CAAC,aAAa,EAC5B,EAAE,EACF,KAAK,CACL,CAAC;QACH,CAAC;QASD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE;YACvC,GAAG,EAAE,6EAA6E;SAClF,CAAC,CAAC,CAAC;QACJ,IAAI,SAAS,EAAE,CAAC;YACf,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;gBAClD,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACjC,CAAC;YACD,OAAO,MAAM,WAAW,CAAC,sBAAsB,CAC9C,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,EAAE,EACF,KAAK,CACL,CAAC;QACH,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC;QAC9D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,CAAC;YACtD,OAAO,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,MAAM,WAAW,CAAC,cAAc,CACtC,KAAK,EACL,YAAY,EACZ,IAAI,EACJ,QAAQ,CAAC,KAAK,CAAC,aAAa,EAC5B,EAAE,EACF,KAAK,CACL,CAAC;IACH,CAAC;IAEO,QAAQ,CACf,SAAiB,EACjB,SAAiB,EACjB,KAAa;QAEb,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,SAAS,CAAC;QAClD,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;gBACxB,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE;gBAC9B,GAAG,EAAE,GAAG,KAAK,wBAAwB;aACrC,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC;IAEO,iBAAiB;QACxB,OAAO;YACN,QAAQ,EAAE,KAAK;YACf,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,oCAAoC,CAAC;SACpD,CAAC;IACH,CAAC;IAOM,KAAK,CAAC,UAAU,CACtB,KAAsB,EACtB,EAAW,EACX,KAAc;QAEd,IAAI,CAAC;YACJ,MAAM,OAAO,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;YAE9C,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,GAC7D,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAGvC,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAGrE,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC;YAG9D,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;oBACxB,IAAI,EAAE,EAAE,WAAW,EAAE;oBACrB,GAAG,EAAE,oBAAoB;iBACzB,CAAC,CAAC,CAAC;gBACJ,OAAO;oBACN,QAAQ,EAAE,KAAK;oBACf,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,uBAAuB,CAAC;iBACvC,CAAC;YACH,CAAC;YACD,MAAM,oBAAoB,GAAG,MAAM,IAAI,CAAC,cAAc,CACrD,KAAK,EACL,IAAI,CAAC,MAAM,CAAC,QAAQ,EACpB,QAAQ,CAAC,GAAG,EACZ,MAAM,CAAC,SAAS,CAAC,EACjB,IAAI,EACJ,SAAS,EACT,EAAE,EACF,KAAK,EACL,WAAW,CACX,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;gBACvB,IAAI,EAAE;oBACL,oBAAoB;oBACpB,WAAW;oBACX,IAAI;oBACJ,SAAS;oBACT,WAAW,EAAE,WAAW,IAAI,QAAQ;oBACpC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO;iBAC3B;aACD,CAAC,CAAC,CAAC;YAEJ,OAAO,oBAAoB,CAAC;QAC7B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;YACpD,MAAM,IAAI,GAAG,GAAG,YAAY,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;YACzD,MAAM,IAAI,eAAe,CAAC,iBAAiB,EAAE;gBAC5C,OAAO,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;aACxB,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;CACD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mintSites.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mintSites.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/mintSites.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { describe, expect, it } from "vitest";
4
+ const MINT_SITES = [
5
+ {
6
+ packageName: "@prosopo/procaptcha-pow",
7
+ relativePath: "../../../procaptcha-pow/src/services/Manager.ts",
8
+ expectedCaptchaType: "CaptchaType.pow",
9
+ },
10
+ {
11
+ packageName: "@prosopo/procaptcha-puzzle",
12
+ relativePath: "../../../procaptcha-puzzle/src/services/Manager.ts",
13
+ expectedCaptchaType: "CaptchaType.puzzle",
14
+ },
15
+ {
16
+ packageName: "@prosopo/procaptcha",
17
+ relativePath: "../../../procaptcha/src/modules/Manager.ts",
18
+ expectedCaptchaType: "CaptchaType.image",
19
+ },
20
+ ];
21
+ const readManagerSource = (relativePath) => {
22
+ const path = join(__dirname, relativePath);
23
+ return readFileSync(path, "utf8");
24
+ };
25
+ describe("client Manager mint sites — captchaType regression", () => {
26
+ for (const site of MINT_SITES) {
27
+ it(`${site.packageName} passes ${site.expectedCaptchaType} to encodeProcaptchaOutput`, () => {
28
+ const source = readManagerSource(site.relativePath);
29
+ expect(source).toContain("encodeProcaptchaOutput");
30
+ expect(source).toContain("[ApiParams.captchaType]:");
31
+ expect(source).toContain(site.expectedCaptchaType);
32
+ });
33
+ it(`${site.packageName} imports CaptchaType from @prosopo/types`, () => {
34
+ const source = readManagerSource(site.relativePath);
35
+ expect(source).toMatch(/from\s+["']@prosopo\/types["']/);
36
+ expect(source).toContain("CaptchaType");
37
+ });
38
+ }
39
+ });
40
+ //# sourceMappingURL=mintSites.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mintSites.unit.test.js","sourceRoot":"","sources":["../../src/tests/mintSites.unit.test.ts"],"names":[],"mappings":"AA0BA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAU9C,MAAM,UAAU,GAAe;IAC9B;QACC,WAAW,EAAE,yBAAyB;QACtC,YAAY,EAAE,iDAAiD;QAC/D,mBAAmB,EAAE,iBAAiB;KACtC;IACD;QACC,WAAW,EAAE,4BAA4B;QACzC,YAAY,EAAE,oDAAoD;QAClE,mBAAmB,EAAE,oBAAoB;KACzC;IACD;QACC,WAAW,EAAE,qBAAqB;QAClC,YAAY,EAAE,4CAA4C;QAC1D,mBAAmB,EAAE,mBAAmB;KACxC;CACD,CAAC;AAEF,MAAM,iBAAiB,GAAG,CAAC,YAAoB,EAAU,EAAE;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IAC3C,OAAO,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;IACnE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC/B,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,WAAW,IAAI,CAAC,mBAAmB,4BAA4B,EAAE,GAAG,EAAE;YAC3F,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAKpD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,wBAAwB,CAAC,CAAC;YACnD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,0BAA0B,CAAC,CAAC;YACrD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,0CAA0C,EAAE,GAAG,EAAE;YACtE,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAGpD,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;IACJ,CAAC;AACF,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/server.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,260 @@
1
+ import { ProviderApi } from "@prosopo/api";
2
+ import * as loadBalancerModule from "@prosopo/load-balancer";
3
+ import { ApiParams, CaptchaType, encodeProcaptchaOutput, } from "@prosopo/types";
4
+ import { afterEach, beforeEach, describe, expect, it, vi, } from "vitest";
5
+ import { ProsopoServer } from "../server.js";
6
+ const PROVIDER_URL = "https://pronode1.example";
7
+ const DAPP = "5C1cs9CfxYQfNi3ARtprMDzR7BFRWFVSoDaLb1JytkiXwq5m";
8
+ const USER = "5CFHA8d3S1XXkZuBwGqiuA6SECTzfoucq397YL34FuPAH89G";
9
+ const buildConfig = (powCachedMs, imageCachedMs, puzzleCachedMs) => ({
10
+ userAccountAddress: undefined,
11
+ web2: true,
12
+ solutionThreshold: 80,
13
+ dappName: "test",
14
+ serverUrl: undefined,
15
+ logLevel: "info",
16
+ defaultEnvironment: "development",
17
+ networks: {
18
+ development: {
19
+ endpoint: "wss://ignored",
20
+ dappContract: { address: DAPP },
21
+ procaptchaContract: { address: DAPP },
22
+ proxyContract: { address: DAPP },
23
+ },
24
+ },
25
+ account: { address: DAPP },
26
+ timeouts: {
27
+ image: {
28
+ challengeTimeout: 10_000,
29
+ solutionTimeout: 10_000,
30
+ verifiedTimeout: 10_000,
31
+ cachedTimeout: imageCachedMs,
32
+ },
33
+ pow: {
34
+ verifiedTimeout: 10_000,
35
+ solutionTimeout: 10_000,
36
+ cachedTimeout: powCachedMs,
37
+ },
38
+ puzzle: {
39
+ verifiedTimeout: 10_000,
40
+ solutionTimeout: 10_000,
41
+ cachedTimeout: puzzleCachedMs,
42
+ },
43
+ contract: { maxVerifiedTime: 10_000 },
44
+ },
45
+ });
46
+ const buildToken = (timestamp, overrides = {}) => {
47
+ const base = {
48
+ [ApiParams.providerUrl]: PROVIDER_URL,
49
+ [ApiParams.dapp]: DAPP,
50
+ [ApiParams.user]: USER,
51
+ [ApiParams.challenge]: "challenge-string",
52
+ [ApiParams.timestamp]: String(timestamp),
53
+ [ApiParams.signature]: {
54
+ [ApiParams.provider]: { [ApiParams.challenge]: "psig" },
55
+ [ApiParams.user]: { [ApiParams.timestamp]: "usig" },
56
+ },
57
+ };
58
+ return encodeProcaptchaOutput({ ...base, ...overrides });
59
+ };
60
+ const stubProviderList = () => [
61
+ {
62
+ address: DAPP,
63
+ url: PROVIDER_URL,
64
+ datasetId: "0xdatasetId",
65
+ weight: 1,
66
+ },
67
+ ];
68
+ const stubPair = () => ({
69
+ address: DAPP,
70
+ sign: (_) => new Uint8Array([1, 2, 3, 4]),
71
+ });
72
+ const installProviderApiSpies = () => {
73
+ const puzzle = vi
74
+ .spyOn(ProviderApi.prototype, "submitPuzzleCaptchaVerify")
75
+ .mockResolvedValue({ status: "ok", verified: true });
76
+ const pow = vi
77
+ .spyOn(ProviderApi.prototype, "submitPowCaptchaVerify")
78
+ .mockResolvedValue({ status: "ok", verified: true });
79
+ const image = vi
80
+ .spyOn(ProviderApi.prototype, "verifyDappUser")
81
+ .mockResolvedValue({ status: "ok", verified: true });
82
+ return { puzzle, pow, image };
83
+ };
84
+ const installLoadBalancer = () => {
85
+ vi.spyOn(loadBalancerModule, "loadBalancer").mockResolvedValue(stubProviderList());
86
+ };
87
+ describe("ProsopoServer.verifyProvider — captchaType dispatch", () => {
88
+ let spies;
89
+ beforeEach(() => {
90
+ spies = installProviderApiSpies();
91
+ installLoadBalancer();
92
+ });
93
+ afterEach(() => {
94
+ vi.restoreAllMocks();
95
+ });
96
+ it("routes puzzle tokens to submitPuzzleCaptchaVerify only", async () => {
97
+ const now = Date.now();
98
+ const token = buildToken(now, {
99
+ [ApiParams.captchaType]: CaptchaType.puzzle,
100
+ });
101
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
102
+ const result = await server.isVerified(token);
103
+ expect(result.verified).toBe(true);
104
+ expect(spies.puzzle).toHaveBeenCalledTimes(1);
105
+ expect(spies.pow).not.toHaveBeenCalled();
106
+ expect(spies.image).not.toHaveBeenCalled();
107
+ });
108
+ it("routes pow tokens to submitPowCaptchaVerify only", async () => {
109
+ const now = Date.now();
110
+ const token = buildToken(now, {
111
+ [ApiParams.captchaType]: CaptchaType.pow,
112
+ });
113
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
114
+ const result = await server.isVerified(token);
115
+ expect(result.verified).toBe(true);
116
+ expect(spies.pow).toHaveBeenCalledTimes(1);
117
+ expect(spies.puzzle).not.toHaveBeenCalled();
118
+ expect(spies.image).not.toHaveBeenCalled();
119
+ });
120
+ it("routes image tokens to verifyDappUser only", async () => {
121
+ const now = Date.now();
122
+ const token = buildToken(now, {
123
+ [ApiParams.captchaType]: CaptchaType.image,
124
+ [ApiParams.challenge]: undefined,
125
+ [ApiParams.commitmentId]: "0xdeadbeef",
126
+ [ApiParams.signature]: {
127
+ [ApiParams.provider]: { [ApiParams.requestHash]: "req" },
128
+ [ApiParams.user]: { [ApiParams.timestamp]: "usig" },
129
+ },
130
+ });
131
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
132
+ const result = await server.isVerified(token);
133
+ expect(result.verified).toBe(true);
134
+ expect(spies.image).toHaveBeenCalledTimes(1);
135
+ expect(spies.pow).not.toHaveBeenCalled();
136
+ expect(spies.puzzle).not.toHaveBeenCalled();
137
+ });
138
+ });
139
+ describe("ProsopoServer.verifyProvider — legacy tokens (no captchaType)", () => {
140
+ let spies;
141
+ beforeEach(() => {
142
+ spies = installProviderApiSpies();
143
+ installLoadBalancer();
144
+ });
145
+ afterEach(() => {
146
+ vi.restoreAllMocks();
147
+ });
148
+ it("legacy token with challenge routes to PoW (preserves historical behaviour)", async () => {
149
+ const now = Date.now();
150
+ const token = buildToken(now);
151
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
152
+ await server.isVerified(token);
153
+ expect(spies.pow).toHaveBeenCalledTimes(1);
154
+ expect(spies.image).not.toHaveBeenCalled();
155
+ expect(spies.puzzle).not.toHaveBeenCalled();
156
+ });
157
+ it("legacy token without challenge routes to image", async () => {
158
+ const now = Date.now();
159
+ const token = buildToken(now, {
160
+ [ApiParams.challenge]: undefined,
161
+ [ApiParams.commitmentId]: "0xdeadbeef",
162
+ [ApiParams.signature]: {
163
+ [ApiParams.provider]: { [ApiParams.requestHash]: "req" },
164
+ [ApiParams.user]: { [ApiParams.timestamp]: "usig" },
165
+ },
166
+ });
167
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
168
+ await server.isVerified(token);
169
+ expect(spies.image).toHaveBeenCalledTimes(1);
170
+ expect(spies.pow).not.toHaveBeenCalled();
171
+ expect(spies.puzzle).not.toHaveBeenCalled();
172
+ });
173
+ it("legacy puzzle-looking token routes to PoW (documents the unavoidable compromise)", async () => {
174
+ const now = Date.now();
175
+ const token = buildToken(now);
176
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
177
+ await server.isVerified(token);
178
+ expect(spies.pow).toHaveBeenCalledTimes(1);
179
+ expect(spies.puzzle).not.toHaveBeenCalled();
180
+ });
181
+ });
182
+ describe("ProsopoServer.verifyProvider — recency checks", () => {
183
+ let spies;
184
+ beforeEach(() => {
185
+ spies = installProviderApiSpies();
186
+ installLoadBalancer();
187
+ });
188
+ afterEach(() => {
189
+ vi.restoreAllMocks();
190
+ });
191
+ it("puzzle uses timeouts.puzzle.cachedTimeout — expired token short-circuits", async () => {
192
+ const puzzleCached = 1_000;
193
+ const staleTimestamp = Date.now() - (puzzleCached + 100);
194
+ const token = buildToken(staleTimestamp, {
195
+ [ApiParams.captchaType]: CaptchaType.puzzle,
196
+ });
197
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, puzzleCached), stubPair());
198
+ const result = await server.isVerified(token);
199
+ expect(result.verified).toBe(false);
200
+ expect(spies.puzzle).not.toHaveBeenCalled();
201
+ expect(spies.pow).not.toHaveBeenCalled();
202
+ });
203
+ it("pow uses timeouts.pow.cachedTimeout — expired token short-circuits", async () => {
204
+ const powCached = 1_000;
205
+ const staleTimestamp = Date.now() - (powCached + 100);
206
+ const token = buildToken(staleTimestamp, {
207
+ [ApiParams.captchaType]: CaptchaType.pow,
208
+ });
209
+ const server = new ProsopoServer(buildConfig(powCached, 60_000, 60_000), stubPair());
210
+ const result = await server.isVerified(token);
211
+ expect(result.verified).toBe(false);
212
+ expect(spies.pow).not.toHaveBeenCalled();
213
+ });
214
+ it("image uses timeouts.image.cachedTimeout — expired token short-circuits", async () => {
215
+ const imageCached = 1_000;
216
+ const staleTimestamp = Date.now() - (imageCached + 100);
217
+ const token = buildToken(staleTimestamp, {
218
+ [ApiParams.captchaType]: CaptchaType.image,
219
+ [ApiParams.challenge]: undefined,
220
+ [ApiParams.commitmentId]: "0xdeadbeef",
221
+ [ApiParams.signature]: {
222
+ [ApiParams.provider]: { [ApiParams.requestHash]: "req" },
223
+ [ApiParams.user]: { [ApiParams.timestamp]: "usig" },
224
+ },
225
+ });
226
+ const server = new ProsopoServer(buildConfig(60_000, imageCached, 60_000), stubPair());
227
+ const result = await server.isVerified(token);
228
+ expect(result.verified).toBe(false);
229
+ expect(spies.image).not.toHaveBeenCalled();
230
+ });
231
+ });
232
+ describe("ProsopoServer.isVerified — short-circuits", () => {
233
+ afterEach(() => {
234
+ vi.restoreAllMocks();
235
+ });
236
+ it("provider not in loadBalancer list returns USER_NOT_VERIFIED", async () => {
237
+ installProviderApiSpies();
238
+ vi.spyOn(loadBalancerModule, "loadBalancer").mockResolvedValue([
239
+ {
240
+ address: DAPP,
241
+ url: "https://a-different-provider.example",
242
+ datasetId: "0xother",
243
+ weight: 1,
244
+ },
245
+ ]);
246
+ const token = buildToken(Date.now(), {
247
+ [ApiParams.captchaType]: CaptchaType.puzzle,
248
+ });
249
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
250
+ const result = await server.isVerified(token);
251
+ expect(result.verified).toBe(false);
252
+ });
253
+ it("throws BAD_REQUEST for an unparseable token", async () => {
254
+ installProviderApiSpies();
255
+ installLoadBalancer();
256
+ const server = new ProsopoServer(buildConfig(60_000, 60_000, 60_000), stubPair());
257
+ await expect(server.isVerified("0xdeadbeef")).rejects.toThrow();
258
+ });
259
+ });
260
+ //# sourceMappingURL=server.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.unit.test.js","sourceRoot":"","sources":["../../src/tests/server.unit.test.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,kBAAkB,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EACN,SAAS,EACT,WAAW,EAGX,sBAAsB,GACtB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEN,SAAS,EACT,UAAU,EACV,QAAQ,EACR,MAAM,EACN,EAAE,EACF,EAAE,GACF,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAChD,MAAM,IAAI,GAAG,kDAAkD,CAAC;AAChE,MAAM,IAAI,GAAG,kDAAkD,CAAC;AAEhE,MAAM,WAAW,GAAG,CACnB,WAAmB,EACnB,aAAqB,EACrB,cAAsB,EACM,EAAE,CAC9B,CAAC;IACA,kBAAkB,EAAE,SAAS;IAC7B,IAAI,EAAE,IAAI;IACV,iBAAiB,EAAE,EAAE;IACrB,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,SAAS;IACpB,QAAQ,EAAE,MAAM;IAChB,kBAAkB,EAAE,aAAa;IACjC,QAAQ,EAAE;QACT,WAAW,EAAE;YACZ,QAAQ,EAAE,eAAe;YACzB,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;YAC/B,kBAAkB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;YACrC,aAAa,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;SAChC;KACD;IACD,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;IAC1B,QAAQ,EAAE;QACT,KAAK,EAAE;YACN,gBAAgB,EAAE,MAAM;YACxB,eAAe,EAAE,MAAM;YACvB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,aAAa;SAC5B;QACD,GAAG,EAAE;YACJ,eAAe,EAAE,MAAM;YACvB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,WAAW;SAC1B;QACD,MAAM,EAAE;YACP,eAAe,EAAE,MAAM;YACvB,eAAe,EAAE,MAAM;YACvB,aAAa,EAAE,cAAc;SAC7B;QACD,QAAQ,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE;KACrC;CAED,CAAQ,CAAC;AAEX,MAAM,UAAU,GAAG,CAClB,SAAiB,EACjB,YAAuC,EAAE,EAChC,EAAE;IACX,MAAM,IAAI,GAAqB;QAC9B,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,YAAY;QACrC,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI;QACtB,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,IAAI;QACtB,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,kBAAkB;QACzC,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,SAAS,CAAC;QACxC,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;YACtB,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE;YACvD,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE;SACnD;KACD,CAAC;IACF,OAAO,sBAAsB,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,SAAS,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,GAAwB,EAAE,CAAC;IACnD;QACC,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,YAAY;QACjB,SAAS,EAAE,aAAa;QACxB,MAAM,EAAE,CAAC;KACT;CACD,CAAC;AAKF,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,CAAC;IACvB,OAAO,EAAE,IAAI;IACb,IAAI,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;CACjD,CAAC,CAAC;AAQH,MAAM,uBAAuB,GAAG,GAAqB,EAAE;IACtD,MAAM,MAAM,GAAG,EAAE;SACf,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,2BAA2B,CAAC;SACzD,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,EAAE;SACZ,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,wBAAwB,CAAC;SACtD,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,EAAE;SACd,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,gBAAgB,CAAC;SAC9C,iBAAiB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,GAAG,EAAE;IAChC,EAAE,CAAC,KAAK,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC,iBAAiB,CAC7D,gBAAgB,EAAE,CAClB,CAAC;AACH,CAAC,CAAC;AAEF,QAAQ,CAAC,qDAAqD,EAAE,GAAG,EAAE;IACpE,IAAI,KAAuB,CAAC;IAE5B,UAAU,CAAC,GAAG,EAAE;QACf,KAAK,GAAG,uBAAuB,EAAE,CAAC;QAClC,mBAAmB,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM;SAC3C,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,GAAG;SACxC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,KAAK;YAC1C,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS;YAChC,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,YAAY;YACtC,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;gBACtB,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;gBACxD,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE;aACnD;SACD,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+DAA+D,EAAE,GAAG,EAAE;IAC9E,IAAI,KAAuB,CAAC;IAE5B,UAAU,CAAC,GAAG,EAAE;QACf,KAAK,GAAG,uBAAuB,EAAE,CAAC;QAClC,mBAAmB,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC3F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC7B,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS;YAChC,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,YAAY;YACtC,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;gBACtB,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;gBACxD,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE;aACnD;SACD,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACzC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;QAOjG,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,+CAA+C,EAAE,GAAG,EAAE;IAC9D,IAAI,KAAuB,CAAC;IAE5B,UAAU,CAAC,GAAG,EAAE;QACf,KAAK,GAAG,uBAAuB,EAAE,CAAC;QAClC,mBAAmB,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,YAAY,GAAG,KAAK,CAAC;QAC3B,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,EAAE;YACxC,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM;SAC3C,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAEzC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAC5C,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,SAAS,GAAG,KAAK,CAAC;QACxB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC;QACtD,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,EAAE;YACxC,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,GAAG;SACxC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAEtC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,WAAW,GAAG,KAAK,CAAC;QAC1B,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,UAAU,CAAC,cAAc,EAAE;YACxC,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,KAAK;YAC1C,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,SAAS;YAChC,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,YAAY;YACtC,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;gBACtB,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE;gBACxD,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE;aACnD;SACD,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,EAExC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IAC1D,SAAS,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,eAAe,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC5E,uBAAuB,EAAE,CAAC;QAC1B,EAAE,CAAC,KAAK,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC,iBAAiB,CAAC;YAC9D;gBACC,OAAO,EAAE,IAAI;gBACb,GAAG,EAAE,sCAAsC;gBAC3C,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,CAAC;aACT;SACD,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE;YACpC,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM;SAC3C,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC9C,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC5D,uBAAuB,EAAE,CAAC;QAC1B,mBAAmB,EAAE,CAAC;QACtB,MAAM,MAAM,GAAG,IAAI,aAAa,CAC/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAEnC,QAAQ,EAAS,CACjB,CAAC;QACF,MAAM,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IACjE,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=token.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/token.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,162 @@
1
+ import { hexToU8a } from "@polkadot/util";
2
+ import { ApiParams, CaptchaType, ProcaptchaOutputSchema, ProcaptchaTokenCodec, ProcaptchaTokenCodecV1, decodeProcaptchaOutput, encodeProcaptchaOutput, } from "@prosopo/types";
3
+ import { u8aToHex } from "@prosopo/util";
4
+ import { describe, expect, it } from "vitest";
5
+ import { ZodError } from "zod";
6
+ const baseOutput = () => ({
7
+ [ApiParams.providerUrl]: "https://pronode1.example",
8
+ [ApiParams.dapp]: "5C1cs9CfxYQfNi3ARtprMDzR7BFRWFVSoDaLb1JytkiXwq5m",
9
+ [ApiParams.user]: "5CFHA8d3S1XXkZuBwGqiuA6SECTzfoucq397YL34FuPAH89G",
10
+ [ApiParams.challenge]: "challenge-string",
11
+ [ApiParams.timestamp]: "1710000000000",
12
+ [ApiParams.signature]: {
13
+ [ApiParams.provider]: {
14
+ [ApiParams.challenge]: "provider-sig",
15
+ },
16
+ [ApiParams.user]: {
17
+ [ApiParams.timestamp]: "user-sig",
18
+ },
19
+ },
20
+ });
21
+ describe("procaptcha token codec — round trip", () => {
22
+ for (const captchaType of [
23
+ CaptchaType.pow,
24
+ CaptchaType.image,
25
+ CaptchaType.puzzle,
26
+ CaptchaType.frictionless,
27
+ ]) {
28
+ it(`preserves captchaType=${captchaType}`, () => {
29
+ const output = { ...baseOutput(), [ApiParams.captchaType]: captchaType };
30
+ const token = encodeProcaptchaOutput(output);
31
+ const decoded = decodeProcaptchaOutput(token);
32
+ expect(decoded[ApiParams.captchaType]).toBe(captchaType);
33
+ expect(decoded[ApiParams.dapp]).toBe(output[ApiParams.dapp]);
34
+ expect(decoded[ApiParams.user]).toBe(output[ApiParams.user]);
35
+ expect(decoded[ApiParams.challenge]).toBe(output[ApiParams.challenge]);
36
+ });
37
+ }
38
+ it("preserves captchaType=undefined (opt-out path)", () => {
39
+ const output = baseOutput();
40
+ const token = encodeProcaptchaOutput(output);
41
+ const decoded = decodeProcaptchaOutput(token);
42
+ expect(decoded[ApiParams.captchaType]).toBeUndefined();
43
+ });
44
+ it("image-shape token (no challenge, has commitmentId) round-trips with captchaType", () => {
45
+ const output = {
46
+ [ApiParams.providerUrl]: "https://pronode1.example",
47
+ [ApiParams.dapp]: "5C1cs9CfxYQfNi3ARtprMDzR7BFRWFVSoDaLb1JytkiXwq5m",
48
+ [ApiParams.user]: "5CFHA8d3S1XXkZuBwGqiuA6SECTzfoucq397YL34FuPAH89G",
49
+ [ApiParams.commitmentId]: "0xdeadbeef",
50
+ [ApiParams.timestamp]: "1710000000000",
51
+ [ApiParams.signature]: {
52
+ [ApiParams.provider]: {
53
+ [ApiParams.requestHash]: "req-hash",
54
+ },
55
+ [ApiParams.user]: {
56
+ [ApiParams.timestamp]: "user-sig",
57
+ },
58
+ },
59
+ [ApiParams.captchaType]: CaptchaType.image,
60
+ };
61
+ const decoded = decodeProcaptchaOutput(encodeProcaptchaOutput(output));
62
+ expect(decoded[ApiParams.captchaType]).toBe(CaptchaType.image);
63
+ expect(decoded[ApiParams.commitmentId]).toBe("0xdeadbeef");
64
+ expect(decoded[ApiParams.challenge]).toBeUndefined();
65
+ });
66
+ });
67
+ describe("procaptcha token codec — legacy v1 fallback", () => {
68
+ const encodeV1 = (output) => {
69
+ return u8aToHex(ProcaptchaTokenCodecV1.enc({
70
+ [ApiParams.commitmentId]: undefined,
71
+ [ApiParams.providerUrl]: undefined,
72
+ [ApiParams.challenge]: undefined,
73
+ [ApiParams.nonce]: undefined,
74
+ ...output,
75
+ signature: {
76
+ provider: {
77
+ challenge: output.signature.provider?.challenge || undefined,
78
+ requestHash: output.signature.provider?.requestHash || undefined,
79
+ },
80
+ user: {
81
+ timestamp: output.signature.user?.timestamp || undefined,
82
+ requestHash: output.signature.user?.requestHash || undefined,
83
+ },
84
+ },
85
+ }));
86
+ };
87
+ it("decodes a legacy v1-encoded pow-shape token as captchaType=undefined", () => {
88
+ const legacyToken = encodeV1(baseOutput());
89
+ const decoded = decodeProcaptchaOutput(legacyToken);
90
+ expect(decoded[ApiParams.captchaType]).toBeUndefined();
91
+ expect(decoded[ApiParams.challenge]).toBe("challenge-string");
92
+ expect(decoded[ApiParams.dapp]).toBe("5C1cs9CfxYQfNi3ARtprMDzR7BFRWFVSoDaLb1JytkiXwq5m");
93
+ });
94
+ it("decodes a legacy v1-encoded image-shape token as captchaType=undefined", () => {
95
+ const legacyToken = encodeV1({
96
+ [ApiParams.providerUrl]: "https://pronode1.example",
97
+ [ApiParams.dapp]: "5C1cs9CfxYQfNi3ARtprMDzR7BFRWFVSoDaLb1JytkiXwq5m",
98
+ [ApiParams.user]: "5CFHA8d3S1XXkZuBwGqiuA6SECTzfoucq397YL34FuPAH89G",
99
+ [ApiParams.commitmentId]: "0xcafebabe",
100
+ [ApiParams.timestamp]: "1710000000000",
101
+ [ApiParams.signature]: {
102
+ [ApiParams.provider]: {
103
+ [ApiParams.requestHash]: "req-hash",
104
+ },
105
+ [ApiParams.user]: {
106
+ [ApiParams.timestamp]: "user-sig",
107
+ },
108
+ },
109
+ });
110
+ const decoded = decodeProcaptchaOutput(legacyToken);
111
+ expect(decoded[ApiParams.captchaType]).toBeUndefined();
112
+ expect(decoded[ApiParams.challenge]).toBeUndefined();
113
+ expect(decoded[ApiParams.commitmentId]).toBe("0xcafebabe");
114
+ });
115
+ it("legacy tokens do not carry the captchaType byte on the wire", () => {
116
+ const output = baseOutput();
117
+ const v1Bytes = hexToU8a(encodeV1(output));
118
+ const v2Bytes = hexToU8a(encodeProcaptchaOutput(output));
119
+ expect(v2Bytes.length).toBe(v1Bytes.length + 1);
120
+ expect(Array.from(v2Bytes.slice(0, v1Bytes.length))).toEqual(Array.from(v1Bytes));
121
+ expect(v2Bytes[v2Bytes.length - 1]).toBe(0);
122
+ });
123
+ });
124
+ describe("procaptcha token codec — rejection paths", () => {
125
+ it("throws on truncated hex input", () => {
126
+ expect(() => decodeProcaptchaOutput("0x00")).toThrow();
127
+ });
128
+ it("throws on non-hex input", () => {
129
+ expect(() => decodeProcaptchaOutput("not-a-hex-token")).toThrow();
130
+ });
131
+ it("rejects unknown captchaType strings via zod", () => {
132
+ const bogusHex = u8aToHex(ProcaptchaTokenCodec.enc({
133
+ [ApiParams.commitmentId]: undefined,
134
+ [ApiParams.providerUrl]: "https://pronode1.example",
135
+ [ApiParams.challenge]: "c",
136
+ [ApiParams.nonce]: undefined,
137
+ [ApiParams.dapp]: "5C1cs9CfxYQfNi3ARtprMDzR7BFRWFVSoDaLb1JytkiXwq5m",
138
+ [ApiParams.user]: "5CFHA8d3S1XXkZuBwGqiuA6SECTzfoucq397YL34FuPAH89G",
139
+ [ApiParams.timestamp]: "1710000000000",
140
+ [ApiParams.signature]: {
141
+ provider: { challenge: "s", requestHash: undefined },
142
+ user: { timestamp: "s", requestHash: undefined },
143
+ },
144
+ [ApiParams.captchaType]: "bogus-captcha-type",
145
+ }));
146
+ let error;
147
+ try {
148
+ decodeProcaptchaOutput(bogusHex);
149
+ }
150
+ catch (e) {
151
+ error = e;
152
+ }
153
+ expect(error).toBeInstanceOf(ZodError);
154
+ });
155
+ it("ProcaptchaOutputSchema rejects unknown captchaType directly", () => {
156
+ expect(() => ProcaptchaOutputSchema.parse({
157
+ ...baseOutput(),
158
+ [ApiParams.captchaType]: "not-a-real-type",
159
+ })).toThrow(ZodError);
160
+ });
161
+ });
162
+ //# sourceMappingURL=token.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token.unit.test.js","sourceRoot":"","sources":["../../src/tests/token.unit.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/server",
3
- "version": "2.10.36",
3
+ "version": "2.10.37",
4
4
  "description": "NodeJS package for server side communication with the prosopo captcha client",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -22,7 +22,8 @@
22
22
  "build:cross-env": "vite build --config vite.esm.config.ts",
23
23
  "build:tsc": "tsc --build --verbose",
24
24
  "build:cjs": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV",
25
- "typecheck": "tsc --project tsconfig.types.json"
25
+ "typecheck": "tsc --project tsconfig.types.json",
26
+ "test": "NODE_ENV=${NODE_ENV:-test}; npx vitest run --config ./vite.test.config.ts"
26
27
  },
27
28
  "repository": {
28
29
  "type": "git",
@@ -37,16 +38,17 @@
37
38
  "homepage": "https://github.com/prosopo/captcha#readme",
38
39
  "sideEffects": false,
39
40
  "dependencies": {
40
- "@prosopo/api": "3.5.16",
41
+ "@prosopo/api": "3.5.17",
41
42
  "@prosopo/common": "3.1.46",
42
43
  "@prosopo/logger": "2.0.3",
43
- "@prosopo/keyring": "2.9.57",
44
- "@prosopo/load-balancer": "2.10.11",
45
- "@prosopo/types": "4.9.9",
44
+ "@prosopo/keyring": "2.9.58",
45
+ "@prosopo/load-balancer": "2.10.12",
46
+ "@prosopo/types": "4.9.10",
46
47
  "@prosopo/util": "3.3.4",
47
48
  "i18next": "24.1.0"
48
49
  },
49
50
  "devDependencies": {
51
+ "@polkadot/util": "13.5.7",
50
52
  "@prosopo/config": "3.3.4",
51
53
  "@types/node": "22.10.2",
52
54
  "@vitest/coverage-v8": "3.2.4",
@@ -57,6 +59,7 @@
57
59
  "tsx": "4.20.3",
58
60
  "typescript": "5.6.2",
59
61
  "vite": "6.4.1",
60
- "vitest": "3.2.4"
62
+ "vitest": "3.2.4",
63
+ "zod": "3.23.8"
61
64
  }
62
65
  }
@@ -0,0 +1,19 @@
1
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ import { ViteTestConfig } from "@prosopo/config";
16
+
17
+ process.env.NODE_ENV = "test";
18
+
19
+ export default ViteTestConfig();