@prosopo/server 2.10.39 → 2.10.41

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.39 build:cjs
2
+ > @prosopo/server@2.10.41 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -29,13 +29,16 @@ ViteCommonJSConfig: .
29
29
  '@prosopo/load-balancer'
30
30
  ]
31
31
  }
32
- vite v6.4.1 building SSR bundle for production...
32
+ The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
33
+ vite v8.1.5 building ssr environment for production...
33
34
  Bundle build started
34
- transforming...
35
- Build end
36
- ✓ 3 modules transformed.
35
+ 
36
+ ✓ 4 modules transformed.
37
37
  rendering chunks...
38
- dist/cjs/index.cjs 0.57 kB
39
- dist/cjs/config.cjs 0.92 kB
40
- dist/cjs/server.cjs 6.26 kB
41
- ✓ built in 158ms
38
+ computing gzip size...
39
+ dist/cjs/index.cjs 0.65 kB │ gzip: 0.31 kB
40
+ dist/cjs/config.cjs 0.82 kB │ gzip: 0.41 kB
41
+ dist/cjs/_virtual/_rolldown/runtime.cjs 1.00 kB │ gzip: 0.50 kB
42
+ dist/cjs/server.cjs 5.73 kB │ gzip: 1.78 kB
43
+
44
+ ✓ built in 46ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/server@2.10.39 build:tsc
2
+ > @prosopo/server@2.10.41 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 12:17:21 PM - Projects in this build:
5
+ 8:59:18 PM - 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
- 12:17:21 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
+ 8:59:18 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
19
19
 
20
- 12:17:21 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
20
+ 8:59:18 PM - 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
- 12:17:21 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
22
+ 8:59:18 PM - 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
- 12:17:21 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
+ 8:59:18 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'
25
25
 
26
- 12:17:21 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
26
+ 8:59:18 PM - 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
- 12:17:21 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
28
+ 8:59:18 PM - 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
- 12:17:21 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
30
+ 8:59:18 PM - 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
- 12:17:21 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
32
+ 8:59:18 PM - 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
- 12:17:21 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
34
+ 8:59:18 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/keyring/keyring.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
35
35
 
36
- 12:17:21 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
+ 8:59:18 PM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/tests/providers.unit.test.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
37
37
 
38
- 12:17:21 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
38
+ 8:59:18 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
39
39
 
40
- 12:17:21 PM - Building project '/home/runner/work/captcha/captcha/packages/server/tsconfig.json'...
40
+ 8:59:18 PM - Building project '/home/runner/work/captcha/captcha/packages/server/tsconfig.json'...
41
41
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/server@2.10.39 build
2
+ > @prosopo/server@2.10.41 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/server@2.10.39 build:cross-env
6
+ > @prosopo/server@2.10.41 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -33,13 +33,16 @@ ViteEsmConfig: .
33
33
  '@prosopo/load-balancer'
34
34
  ]
35
35
  }
36
- vite v6.4.1 building SSR bundle for production...
36
+ The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
37
+ vite v8.1.5 building ssr environment for production...
37
38
  Bundle build started
38
- transforming...
39
- Build end
40
- ✓ 3 modules transformed.
39
+ 
40
+ ✓ 4 modules transformed.
41
41
  rendering chunks...
42
- dist/index.js 0.39 kB
43
- dist/config.js 0.81 kB
44
- dist/server.js 6.17 kB
45
- ✓ built in 182ms
42
+ computing gzip size...
43
+ dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
44
+ dist/index.js 0.43 kB │ gzip: 0.23 kB
45
+ dist/config.js 0.78 kB │ gzip: 0.40 kB
46
+ dist/server.js 5.39 kB │ gzip: 1.68 kB
47
+
48
+ ✓ built in 50ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # @prosopo/server
2
2
 
3
+ ## 2.10.41
4
+ ### Patch Changes
5
+
6
+ - e14fce6: chore(deps): bump vite to 6.4.3 and mongoose to 8.24.1, and adjust types for the mongoose 8.24 Document/ObjectId changes
7
+ - Updated dependencies [2c47bb7]
8
+ - Updated dependencies [0e1171c]
9
+ - Updated dependencies [103318c]
10
+ - Updated dependencies [270a8d8]
11
+ - Updated dependencies [e14fce6]
12
+ - @prosopo/util@3.3.5
13
+ - @prosopo/types@4.10.0
14
+ - @prosopo/api@3.5.20
15
+ - @prosopo/common@3.1.48
16
+ - @prosopo/keyring@2.9.61
17
+ - @prosopo/load-balancer@2.10.16
18
+ - @prosopo/logger@2.0.4
19
+
20
+ ## 2.10.40
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [a0cb39e]
24
+ - @prosopo/types@4.9.12
25
+ - @prosopo/api@3.5.19
26
+ - @prosopo/keyring@2.9.60
27
+ - @prosopo/load-balancer@2.10.15
28
+
3
29
  ## 2.10.39
4
30
  ### Patch Changes
5
31
 
@@ -0,0 +1,3 @@
1
+ import "node:module";
2
+ //#endregion
3
+ export {};
@@ -0,0 +1,23 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ exports.__toESM = __toESM;
@@ -1,25 +1,22 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const types = require("@prosopo/types");
4
- const getServerConfig = () => types.ProsopoServerConfigSchema.parse({
5
- defaultEnvironment: process.env.PROSOPO_DEFAULT_ENVIRONMENT,
6
- // environmental variables
7
- serverUrl: getServerUrl(),
8
- dappName: process.env.PROSOPO_DAPP_NAME || "client-example-server",
9
- account: {
10
- password: "",
11
- address: process.env.PROSOPO_SITE_KEY || "",
12
- secret: process.env.PROSOPO_SITE_PRIVATE_KEY || ""
13
- }
1
+ let _prosopo_types = require("@prosopo/types");
2
+ //#region src/config.ts
3
+ var getServerConfig = () => _prosopo_types.ProsopoServerConfigSchema.parse({
4
+ defaultEnvironment: process.env.PROSOPO_DEFAULT_ENVIRONMENT,
5
+ serverUrl: getServerUrl(),
6
+ dappName: process.env.PROSOPO_DAPP_NAME || "client-example-server",
7
+ account: {
8
+ password: "",
9
+ address: process.env.PROSOPO_SITE_KEY || "",
10
+ secret: process.env.PROSOPO_SITE_PRIVATE_KEY || ""
11
+ }
14
12
  });
15
- const getServerUrl = () => {
16
- if (process.env.PROSOPO_SERVER_URL) {
17
- if (process.env.PROSOPO_SERVER_URL.match(/:\d+/)) {
18
- return process.env.PROSOPO_SERVER_URL;
19
- }
20
- return `${process.env.PROSOPO_SERVER_URL}:${process.env.PROSOPO_SERVER_PORT || 9228}`;
21
- }
22
- return "https://localhost:9228";
13
+ var getServerUrl = () => {
14
+ if (process.env.PROSOPO_SERVER_URL) {
15
+ if (process.env.PROSOPO_SERVER_URL.match(/:\d+/)) return process.env.PROSOPO_SERVER_URL;
16
+ return `${process.env.PROSOPO_SERVER_URL}:${process.env.PROSOPO_SERVER_PORT || 9228}`;
17
+ }
18
+ return "https://localhost:9228";
23
19
  };
20
+ //#endregion
24
21
  exports.getServerConfig = getServerConfig;
25
22
  exports.getServerUrl = getServerUrl;
@@ -1,13 +1,14 @@
1
- "use strict";
2
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const server = require("./server.cjs");
4
- const config = require("./config.cjs");
5
- const keyring = require("@prosopo/keyring");
6
- const PublicProsopoServer = async (config2) => {
7
- const pair = keyring.getPair(void 0, config2.account.address);
8
- return new server.ProsopoServer(config2, pair);
2
+ const require_server = require("./server.cjs");
3
+ const require_config = require("./config.cjs");
4
+ let _prosopo_keyring = require("@prosopo/keyring");
5
+ //#region src/index.ts
6
+ var PublicProsopoServer = async (config) => {
7
+ const pair = (0, _prosopo_keyring.getPair)(void 0, config.account.address);
8
+ return new require_server.ProsopoServer(config, pair);
9
9
  };
10
- exports.ProsopoServer = server.ProsopoServer;
11
- exports.getServerConfig = config.getServerConfig;
12
- exports.getServerUrl = config.getServerUrl;
10
+ //#endregion
11
+ exports.ProsopoServer = require_server.ProsopoServer;
13
12
  exports.PublicProsopoServer = PublicProsopoServer;
13
+ exports.getServerConfig = require_config.getServerConfig;
14
+ exports.getServerUrl = require_config.getServerUrl;
@@ -1,200 +1,146 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const api = require("@prosopo/api");
4
- const common = require("@prosopo/common");
5
- const keyring = require("@prosopo/keyring");
6
- const loadBalancer = require("@prosopo/load-balancer");
7
- const logger = require("@prosopo/logger");
8
- const types = require("@prosopo/types");
9
- const util = require("@prosopo/util");
10
- const i18n = require("i18next");
11
- class ProsopoServer {
12
- constructor(config, pair) {
13
- this.config = config;
14
- this.pair = pair;
15
- this.defaultEnvironment = this.config.defaultEnvironment;
16
- this.dappAccount = this.config.account.address;
17
- this.logger = logger.getLogger(
18
- this.config.logLevel,
19
- "@prosopo/server"
20
- );
21
- this.keyring = new keyring.Keyring({
22
- type: "sr25519"
23
- });
24
- }
25
- getProviderApi(providerUrl) {
26
- return new api.ProviderApi(providerUrl, this.dappAccount || "");
27
- }
28
- /**
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.
38
- * @param token
39
- * @param timeouts
40
- * @param providerUrl
41
- * @param timestamp
42
- * @param user
43
- * @param challenge
44
- * @param ip
45
- * @param email
46
- * @param captchaType
47
- */
48
- async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email, captchaType) {
49
- this.logger.info(() => ({
50
- data: { providerUrl, captchaType: captchaType ?? "legacy" },
51
- msg: "Verifying with provider"
52
- }));
53
- const dappUserSignature = this.pair?.sign(timestamp.toString());
54
- if (!dappUserSignature) {
55
- throw new common.ProsopoContractError("CAPTCHA.INVALID_TIMESTAMP", {
56
- context: { error: "Timestamp not found" }
57
- });
58
- }
59
- const signatureHex = util.u8aToHex(dappUserSignature);
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
- }));
105
- if (challenge) {
106
- const powTimeout = this.config.timeouts.pow.cachedTimeout;
107
- if (!this.isRecent(timestamp, powTimeout, "PoW")) {
108
- return this.notRecentResponse();
109
- }
110
- return await providerApi.submitPowCaptchaVerify(
111
- token,
112
- signatureHex,
113
- user,
114
- ip,
115
- email
116
- );
117
- }
118
- const imageTimeout = this.config.timeouts.image.cachedTimeout;
119
- if (!this.isRecent(timestamp, imageTimeout, "Image")) {
120
- return this.notRecentResponse();
121
- }
122
- return await providerApi.verifyDappUser(
123
- token,
124
- signatureHex,
125
- user,
126
- timeouts.image.cachedTimeout,
127
- ip,
128
- email
129
- );
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
- }
148
- /**
149
- * @param token
150
- * @param ip
151
- * @param email
152
- */
153
- async isVerified(token, ip, email) {
154
- try {
155
- const payload = types.decodeProcaptchaOutput(token);
156
- const { providerUrl, challenge, timestamp, user, captchaType } = types.ProcaptchaOutputSchema.parse(payload);
157
- const providers = await loadBalancer.loadBalancer(this.config.defaultEnvironment);
158
- const provider = providers.find((p) => p.url === providerUrl);
159
- if (!provider) {
160
- this.logger.error(() => ({
161
- data: { providerUrl },
162
- msg: "Provider not found"
163
- }));
164
- return {
165
- verified: false,
166
- status: i18n.t("API.USER_NOT_VERIFIED")
167
- };
168
- }
169
- const verificationResponse = await this.verifyProvider(
170
- token,
171
- this.config.timeouts,
172
- provider.url,
173
- Number(timestamp),
174
- user,
175
- challenge,
176
- ip,
177
- email,
178
- captchaType
179
- );
180
- this.logger.info(() => ({
181
- data: {
182
- verificationResponse,
183
- providerUrl,
184
- user,
185
- challenge,
186
- captchaType: captchaType ?? "legacy",
187
- siteKey: this.pair?.address
188
- }
189
- }));
190
- return verificationResponse;
191
- } catch (err) {
192
- this.logger.error(() => ({ err, data: { token } }));
193
- const code = err instanceof api.HttpError ? err.status : 500;
194
- throw new common.ProsopoApiError("API.BAD_REQUEST", {
195
- context: { code, token }
196
- });
197
- }
198
- }
199
- }
1
+ const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
2
+ let _prosopo_api = require("@prosopo/api");
3
+ let _prosopo_common = require("@prosopo/common");
4
+ let _prosopo_keyring = require("@prosopo/keyring");
5
+ let _prosopo_load_balancer = require("@prosopo/load-balancer");
6
+ let _prosopo_logger = require("@prosopo/logger");
7
+ let _prosopo_types = require("@prosopo/types");
8
+ let _prosopo_util = require("@prosopo/util");
9
+ let i18next = require("i18next");
10
+ i18next = require_runtime.__toESM(i18next, 1);
11
+ //#region src/server.ts
12
+ var ProsopoServer = class {
13
+ constructor(config, pair) {
14
+ this.config = config;
15
+ this.pair = pair;
16
+ this.defaultEnvironment = this.config.defaultEnvironment;
17
+ this.dappAccount = this.config.account.address;
18
+ this.logger = (0, _prosopo_logger.getLogger)(this.config.logLevel, "@prosopo/server");
19
+ this.keyring = new _prosopo_keyring.Keyring({ type: "sr25519" });
20
+ }
21
+ getProviderApi(providerUrl) {
22
+ return new _prosopo_api.ProviderApi(providerUrl, this.dappAccount || "");
23
+ }
24
+ /**
25
+ * Verify a token with the issuing provider. Dispatches to the correct
26
+ * verify endpoint by inspecting the token's declared captchaType:
27
+ * - puzzle → submitPuzzleCaptchaVerify
28
+ * - pow → submitPowCaptchaVerify
29
+ * - image → verifyDappUser
30
+ * When captchaType is absent (a legacy token minted before the field was
31
+ * added), fall back to the historical heuristic: presence of `challenge`
32
+ * routes to PoW, absence routes to image. Legacy puzzle tokens follow the
33
+ * PoW branch — same behaviour as before this dispatch was introduced.
34
+ * @param token
35
+ * @param timeouts
36
+ * @param providerUrl
37
+ * @param timestamp
38
+ * @param user
39
+ * @param challenge
40
+ * @param ip
41
+ * @param email
42
+ * @param captchaType
43
+ */
44
+ async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email, captchaType) {
45
+ this.logger.info(() => ({
46
+ data: {
47
+ providerUrl,
48
+ captchaType: captchaType ?? "legacy"
49
+ },
50
+ msg: "Verifying with provider"
51
+ }));
52
+ const dappUserSignature = this.pair?.sign(timestamp.toString());
53
+ if (!dappUserSignature) throw new _prosopo_common.ProsopoContractError("CAPTCHA.INVALID_TIMESTAMP", { context: { error: "Timestamp not found" } });
54
+ const signatureHex = (0, _prosopo_util.u8aToHex)(dappUserSignature);
55
+ const providerApi = this.getProviderApi(providerUrl);
56
+ if (captchaType === _prosopo_types.CaptchaType.puzzle) {
57
+ const puzzleTimeout = this.config.timeouts.puzzle.cachedTimeout;
58
+ if (!this.isRecent(timestamp, puzzleTimeout, "Puzzle")) return this.notRecentResponse();
59
+ return await providerApi.submitPuzzleCaptchaVerify(token, signatureHex, user, ip, email);
60
+ }
61
+ if (captchaType === _prosopo_types.CaptchaType.pow) {
62
+ const powTimeout = this.config.timeouts.pow.cachedTimeout;
63
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) return this.notRecentResponse();
64
+ return await providerApi.submitPowCaptchaVerify(token, signatureHex, user, ip, email);
65
+ }
66
+ if (captchaType === _prosopo_types.CaptchaType.image) {
67
+ const imageTimeout = this.config.timeouts.image.cachedTimeout;
68
+ if (!this.isRecent(timestamp, imageTimeout, "Image")) return this.notRecentResponse();
69
+ return await providerApi.verifyDappUser(token, signatureHex, user, timeouts.image.cachedTimeout, ip, email);
70
+ }
71
+ this.logger.warn(() => ({
72
+ data: { challenge: Boolean(challenge) },
73
+ msg: "Verifying legacy token without captchaType field; using challenge heuristic"
74
+ }));
75
+ if (challenge) {
76
+ const powTimeout = this.config.timeouts.pow.cachedTimeout;
77
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) return this.notRecentResponse();
78
+ return await providerApi.submitPowCaptchaVerify(token, signatureHex, user, ip, email);
79
+ }
80
+ const imageTimeout = this.config.timeouts.image.cachedTimeout;
81
+ if (!this.isRecent(timestamp, imageTimeout, "Image")) return this.notRecentResponse();
82
+ return await providerApi.verifyDappUser(token, signatureHex, user, timeouts.image.cachedTimeout, ip, email);
83
+ }
84
+ isRecent(timestamp, timeoutMs, label) {
85
+ if (!timestamp) return false;
86
+ const recent = Date.now() - timestamp < timeoutMs;
87
+ if (!recent) this.logger.error(() => ({
88
+ data: {
89
+ timestamp,
90
+ timeoutMs
91
+ },
92
+ msg: `${label} captcha is not recent`
93
+ }));
94
+ return recent;
95
+ }
96
+ notRecentResponse() {
97
+ return {
98
+ verified: false,
99
+ status: i18next.default.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
100
+ };
101
+ }
102
+ /**
103
+ * @param token
104
+ * @param ip
105
+ * @param email
106
+ */
107
+ async isVerified(token, ip, email) {
108
+ try {
109
+ const payload = (0, _prosopo_types.decodeProcaptchaOutput)(token);
110
+ const { providerUrl, challenge, timestamp, user, captchaType } = _prosopo_types.ProcaptchaOutputSchema.parse(payload);
111
+ const provider = (await (0, _prosopo_load_balancer.loadBalancer)(this.config.defaultEnvironment)).find((p) => p.url === providerUrl);
112
+ if (!provider) {
113
+ this.logger.error(() => ({
114
+ data: { providerUrl },
115
+ msg: "Provider not found"
116
+ }));
117
+ return {
118
+ verified: false,
119
+ status: i18next.default.t("API.USER_NOT_VERIFIED")
120
+ };
121
+ }
122
+ const verificationResponse = await this.verifyProvider(token, this.config.timeouts, provider.url, Number(timestamp), user, challenge, ip, email, captchaType);
123
+ this.logger.info(() => ({ data: {
124
+ verificationResponse,
125
+ providerUrl,
126
+ user,
127
+ challenge,
128
+ captchaType: captchaType ?? "legacy",
129
+ siteKey: this.pair?.address
130
+ } }));
131
+ return verificationResponse;
132
+ } catch (err) {
133
+ this.logger.error(() => ({
134
+ err,
135
+ data: { token }
136
+ }));
137
+ const code = err instanceof _prosopo_api.HttpError ? err.status : 500;
138
+ throw new _prosopo_common.ProsopoApiError("API.BAD_REQUEST", { context: {
139
+ code,
140
+ token
141
+ } });
142
+ }
143
+ }
144
+ };
145
+ //#endregion
200
146
  exports.ProsopoServer = ProsopoServer;
package/dist/config.js CHANGED
@@ -1,25 +1,21 @@
1
1
  import { ProsopoServerConfigSchema } from "@prosopo/types";
2
- const getServerConfig = () => ProsopoServerConfigSchema.parse({
3
- defaultEnvironment: process.env.PROSOPO_DEFAULT_ENVIRONMENT,
4
- // environmental variables
5
- serverUrl: getServerUrl(),
6
- dappName: process.env.PROSOPO_DAPP_NAME || "client-example-server",
7
- account: {
8
- password: "",
9
- address: process.env.PROSOPO_SITE_KEY || "",
10
- secret: process.env.PROSOPO_SITE_PRIVATE_KEY || ""
11
- }
2
+ //#region src/config.ts
3
+ var getServerConfig = () => ProsopoServerConfigSchema.parse({
4
+ defaultEnvironment: process.env.PROSOPO_DEFAULT_ENVIRONMENT,
5
+ serverUrl: getServerUrl(),
6
+ dappName: process.env.PROSOPO_DAPP_NAME || "client-example-server",
7
+ account: {
8
+ password: "",
9
+ address: process.env.PROSOPO_SITE_KEY || "",
10
+ secret: process.env.PROSOPO_SITE_PRIVATE_KEY || ""
11
+ }
12
12
  });
13
- const getServerUrl = () => {
14
- if (process.env.PROSOPO_SERVER_URL) {
15
- if (process.env.PROSOPO_SERVER_URL.match(/:\d+/)) {
16
- return process.env.PROSOPO_SERVER_URL;
17
- }
18
- return `${process.env.PROSOPO_SERVER_URL}:${process.env.PROSOPO_SERVER_PORT || 9228}`;
19
- }
20
- return "https://localhost:9228";
21
- };
22
- export {
23
- getServerConfig,
24
- getServerUrl
13
+ var getServerUrl = () => {
14
+ if (process.env.PROSOPO_SERVER_URL) {
15
+ if (process.env.PROSOPO_SERVER_URL.match(/:\d+/)) return process.env.PROSOPO_SERVER_URL;
16
+ return `${process.env.PROSOPO_SERVER_URL}:${process.env.PROSOPO_SERVER_PORT || 9228}`;
17
+ }
18
+ return "https://localhost:9228";
25
19
  };
20
+ //#endregion
21
+ export { getServerConfig, getServerUrl };
package/dist/index.js CHANGED
@@ -1,13 +1,10 @@
1
+ import "./_virtual/_rolldown/runtime.js";
1
2
  import { ProsopoServer } from "./server.js";
2
3
  import { getServerConfig, getServerUrl } from "./config.js";
3
4
  import { getPair } from "@prosopo/keyring";
4
- const PublicProsopoServer = async (config) => {
5
- const pair = getPair(void 0, config.account.address);
6
- return new ProsopoServer(config, pair);
7
- };
8
- export {
9
- ProsopoServer,
10
- PublicProsopoServer,
11
- getServerConfig,
12
- getServerUrl
5
+ //#region src/index.ts
6
+ var PublicProsopoServer = async (config) => {
7
+ return new ProsopoServer(config, getPair(void 0, config.account.address));
13
8
  };
9
+ //#endregion
10
+ export { ProsopoServer, PublicProsopoServer, getServerConfig, getServerUrl };
package/dist/server.js CHANGED
@@ -1,200 +1,143 @@
1
- import { ProviderApi, HttpError } from "@prosopo/api";
2
- import { ProsopoContractError, ProsopoApiError } from "@prosopo/common";
1
+ import { HttpError, ProviderApi } from "@prosopo/api";
2
+ import { ProsopoApiError, ProsopoContractError } 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 { CaptchaType, decodeProcaptchaOutput, ProcaptchaOutputSchema } from "@prosopo/types";
6
+ import { CaptchaType, ProcaptchaOutputSchema, decodeProcaptchaOutput } from "@prosopo/types";
7
7
  import { u8aToHex } from "@prosopo/util";
8
8
  import i18n from "i18next";
9
- class ProsopoServer {
10
- constructor(config, pair) {
11
- this.config = config;
12
- this.pair = pair;
13
- this.defaultEnvironment = this.config.defaultEnvironment;
14
- this.dappAccount = this.config.account.address;
15
- this.logger = getLogger(
16
- this.config.logLevel,
17
- "@prosopo/server"
18
- );
19
- this.keyring = new Keyring({
20
- type: "sr25519"
21
- });
22
- }
23
- getProviderApi(providerUrl) {
24
- return new ProviderApi(providerUrl, this.dappAccount || "");
25
- }
26
- /**
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.
36
- * @param token
37
- * @param timeouts
38
- * @param providerUrl
39
- * @param timestamp
40
- * @param user
41
- * @param challenge
42
- * @param ip
43
- * @param email
44
- * @param captchaType
45
- */
46
- async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email, captchaType) {
47
- this.logger.info(() => ({
48
- data: { providerUrl, captchaType: captchaType ?? "legacy" },
49
- msg: "Verifying with provider"
50
- }));
51
- const dappUserSignature = this.pair?.sign(timestamp.toString());
52
- if (!dappUserSignature) {
53
- throw new ProsopoContractError("CAPTCHA.INVALID_TIMESTAMP", {
54
- context: { error: "Timestamp not found" }
55
- });
56
- }
57
- const signatureHex = u8aToHex(dappUserSignature);
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
- }));
103
- if (challenge) {
104
- const powTimeout = this.config.timeouts.pow.cachedTimeout;
105
- if (!this.isRecent(timestamp, powTimeout, "PoW")) {
106
- return this.notRecentResponse();
107
- }
108
- return await providerApi.submitPowCaptchaVerify(
109
- token,
110
- signatureHex,
111
- user,
112
- ip,
113
- email
114
- );
115
- }
116
- const imageTimeout = this.config.timeouts.image.cachedTimeout;
117
- if (!this.isRecent(timestamp, imageTimeout, "Image")) {
118
- return this.notRecentResponse();
119
- }
120
- return await providerApi.verifyDappUser(
121
- token,
122
- signatureHex,
123
- user,
124
- timeouts.image.cachedTimeout,
125
- ip,
126
- email
127
- );
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
- }
146
- /**
147
- * @param token
148
- * @param ip
149
- * @param email
150
- */
151
- async isVerified(token, ip, email) {
152
- try {
153
- const payload = decodeProcaptchaOutput(token);
154
- const { providerUrl, challenge, timestamp, user, captchaType } = ProcaptchaOutputSchema.parse(payload);
155
- const providers = await loadBalancer(this.config.defaultEnvironment);
156
- const provider = providers.find((p) => p.url === providerUrl);
157
- if (!provider) {
158
- this.logger.error(() => ({
159
- data: { providerUrl },
160
- msg: "Provider not found"
161
- }));
162
- return {
163
- verified: false,
164
- status: i18n.t("API.USER_NOT_VERIFIED")
165
- };
166
- }
167
- const verificationResponse = await this.verifyProvider(
168
- token,
169
- this.config.timeouts,
170
- provider.url,
171
- Number(timestamp),
172
- user,
173
- challenge,
174
- ip,
175
- email,
176
- captchaType
177
- );
178
- this.logger.info(() => ({
179
- data: {
180
- verificationResponse,
181
- providerUrl,
182
- user,
183
- challenge,
184
- captchaType: captchaType ?? "legacy",
185
- siteKey: this.pair?.address
186
- }
187
- }));
188
- return verificationResponse;
189
- } catch (err) {
190
- this.logger.error(() => ({ err, data: { token } }));
191
- const code = err instanceof HttpError ? err.status : 500;
192
- throw new ProsopoApiError("API.BAD_REQUEST", {
193
- context: { code, token }
194
- });
195
- }
196
- }
197
- }
198
- export {
199
- ProsopoServer
9
+ //#region src/server.ts
10
+ var ProsopoServer = class {
11
+ constructor(config, pair) {
12
+ this.config = config;
13
+ this.pair = pair;
14
+ this.defaultEnvironment = this.config.defaultEnvironment;
15
+ this.dappAccount = this.config.account.address;
16
+ this.logger = getLogger(this.config.logLevel, "@prosopo/server");
17
+ this.keyring = new Keyring({ type: "sr25519" });
18
+ }
19
+ getProviderApi(providerUrl) {
20
+ return new ProviderApi(providerUrl, this.dappAccount || "");
21
+ }
22
+ /**
23
+ * Verify a token with the issuing provider. Dispatches to the correct
24
+ * verify endpoint by inspecting the token's declared captchaType:
25
+ * - puzzle → submitPuzzleCaptchaVerify
26
+ * - pow → submitPowCaptchaVerify
27
+ * - image → verifyDappUser
28
+ * When captchaType is absent (a legacy token minted before the field was
29
+ * added), fall back to the historical heuristic: presence of `challenge`
30
+ * routes to PoW, absence routes to image. Legacy puzzle tokens follow the
31
+ * PoW branch — same behaviour as before this dispatch was introduced.
32
+ * @param token
33
+ * @param timeouts
34
+ * @param providerUrl
35
+ * @param timestamp
36
+ * @param user
37
+ * @param challenge
38
+ * @param ip
39
+ * @param email
40
+ * @param captchaType
41
+ */
42
+ async verifyProvider(token, timeouts, providerUrl, timestamp, user, challenge, ip, email, captchaType) {
43
+ this.logger.info(() => ({
44
+ data: {
45
+ providerUrl,
46
+ captchaType: captchaType ?? "legacy"
47
+ },
48
+ msg: "Verifying with provider"
49
+ }));
50
+ const dappUserSignature = this.pair?.sign(timestamp.toString());
51
+ if (!dappUserSignature) throw new ProsopoContractError("CAPTCHA.INVALID_TIMESTAMP", { context: { error: "Timestamp not found" } });
52
+ const signatureHex = u8aToHex(dappUserSignature);
53
+ const providerApi = this.getProviderApi(providerUrl);
54
+ if (captchaType === CaptchaType.puzzle) {
55
+ const puzzleTimeout = this.config.timeouts.puzzle.cachedTimeout;
56
+ if (!this.isRecent(timestamp, puzzleTimeout, "Puzzle")) return this.notRecentResponse();
57
+ return await providerApi.submitPuzzleCaptchaVerify(token, signatureHex, user, ip, email);
58
+ }
59
+ if (captchaType === CaptchaType.pow) {
60
+ const powTimeout = this.config.timeouts.pow.cachedTimeout;
61
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) return this.notRecentResponse();
62
+ return await providerApi.submitPowCaptchaVerify(token, signatureHex, user, ip, email);
63
+ }
64
+ if (captchaType === CaptchaType.image) {
65
+ const imageTimeout = this.config.timeouts.image.cachedTimeout;
66
+ if (!this.isRecent(timestamp, imageTimeout, "Image")) return this.notRecentResponse();
67
+ return await providerApi.verifyDappUser(token, signatureHex, user, timeouts.image.cachedTimeout, ip, email);
68
+ }
69
+ this.logger.warn(() => ({
70
+ data: { challenge: Boolean(challenge) },
71
+ msg: "Verifying legacy token without captchaType field; using challenge heuristic"
72
+ }));
73
+ if (challenge) {
74
+ const powTimeout = this.config.timeouts.pow.cachedTimeout;
75
+ if (!this.isRecent(timestamp, powTimeout, "PoW")) return this.notRecentResponse();
76
+ return await providerApi.submitPowCaptchaVerify(token, signatureHex, user, ip, email);
77
+ }
78
+ const imageTimeout = this.config.timeouts.image.cachedTimeout;
79
+ if (!this.isRecent(timestamp, imageTimeout, "Image")) return this.notRecentResponse();
80
+ return await providerApi.verifyDappUser(token, signatureHex, user, timeouts.image.cachedTimeout, ip, email);
81
+ }
82
+ isRecent(timestamp, timeoutMs, label) {
83
+ if (!timestamp) return false;
84
+ const recent = Date.now() - timestamp < timeoutMs;
85
+ if (!recent) this.logger.error(() => ({
86
+ data: {
87
+ timestamp,
88
+ timeoutMs
89
+ },
90
+ msg: `${label} captcha is not recent`
91
+ }));
92
+ return recent;
93
+ }
94
+ notRecentResponse() {
95
+ return {
96
+ verified: false,
97
+ status: i18n.t("API.USER_NOT_VERIFIED_TIME_EXPIRED")
98
+ };
99
+ }
100
+ /**
101
+ * @param token
102
+ * @param ip
103
+ * @param email
104
+ */
105
+ async isVerified(token, ip, email) {
106
+ try {
107
+ const payload = decodeProcaptchaOutput(token);
108
+ const { providerUrl, challenge, timestamp, user, captchaType } = ProcaptchaOutputSchema.parse(payload);
109
+ const provider = (await loadBalancer(this.config.defaultEnvironment)).find((p) => p.url === providerUrl);
110
+ if (!provider) {
111
+ this.logger.error(() => ({
112
+ data: { providerUrl },
113
+ msg: "Provider not found"
114
+ }));
115
+ return {
116
+ verified: false,
117
+ status: i18n.t("API.USER_NOT_VERIFIED")
118
+ };
119
+ }
120
+ const verificationResponse = await this.verifyProvider(token, this.config.timeouts, provider.url, Number(timestamp), user, challenge, ip, email, captchaType);
121
+ this.logger.info(() => ({ data: {
122
+ verificationResponse,
123
+ providerUrl,
124
+ user,
125
+ challenge,
126
+ captchaType: captchaType ?? "legacy",
127
+ siteKey: this.pair?.address
128
+ } }));
129
+ return verificationResponse;
130
+ } catch (err) {
131
+ this.logger.error(() => ({
132
+ err,
133
+ data: { token }
134
+ }));
135
+ throw new ProsopoApiError("API.BAD_REQUEST", { context: {
136
+ code: err instanceof HttpError ? err.status : 500,
137
+ token
138
+ } });
139
+ }
140
+ }
200
141
  };
142
+ //#endregion
143
+ export { ProsopoServer };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/server",
3
- "version": "2.10.39",
3
+ "version": "2.10.41",
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",
@@ -38,28 +38,28 @@
38
38
  "homepage": "https://github.com/prosopo/captcha#readme",
39
39
  "sideEffects": false,
40
40
  "dependencies": {
41
- "@prosopo/api": "3.5.18",
42
- "@prosopo/common": "3.1.47",
43
- "@prosopo/logger": "2.0.3",
44
- "@prosopo/keyring": "2.9.59",
45
- "@prosopo/load-balancer": "2.10.14",
46
- "@prosopo/types": "4.9.11",
47
- "@prosopo/util": "3.3.4",
41
+ "@prosopo/api": "3.5.20",
42
+ "@prosopo/common": "3.1.48",
43
+ "@prosopo/logger": "2.0.4",
44
+ "@prosopo/keyring": "2.9.61",
45
+ "@prosopo/load-balancer": "2.10.16",
46
+ "@prosopo/types": "4.10.0",
47
+ "@prosopo/util": "3.3.5",
48
48
  "i18next": "24.1.0"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@polkadot/util": "13.5.7",
52
- "@prosopo/config": "3.3.4",
52
+ "@prosopo/config": "3.3.5",
53
53
  "@types/node": "22.10.2",
54
- "@vitest/coverage-v8": "3.2.4",
54
+ "@vitest/coverage-v8": "4.1.10",
55
55
  "concurrently": "9.0.1",
56
56
  "del-cli": "6.0.0",
57
57
  "npm-run-all": "4.1.5",
58
58
  "tslib": "2.7.0",
59
59
  "tsx": "4.20.3",
60
60
  "typescript": "5.6.2",
61
- "vite": "6.4.1",
62
- "vitest": "3.2.4",
61
+ "vite": "8.1.5",
62
+ "vitest": "4.1.10",
63
63
  "zod": "3.23.8"
64
64
  }
65
65
  }