@prosopo/provider 4.15.13 → 4.15.15

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.
Files changed (236) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +106 -106
  2. package/.turbo/turbo-build$colon$tsc.log +24 -24
  3. package/.turbo/turbo-build.log +107 -104
  4. package/CHANGELOG.md +78 -0
  5. package/dist/_virtual/_rolldown/runtime.js +3 -0
  6. package/dist/api/admin/apiAdminRoutesProvider.js +27 -46
  7. package/dist/api/admin/apiClearAllCountersEndpoint.js +62 -57
  8. package/dist/api/admin/apiDnsEventEndpoint.js +79 -92
  9. package/dist/api/admin/apiGetAllDecisionMachinesEndpoint.js +43 -45
  10. package/dist/api/admin/apiGetDecisionMachineEndpoint.js +40 -40
  11. package/dist/api/admin/apiRegisterSiteKeyEndpoint.js +23 -22
  12. package/dist/api/admin/apiRegisterSiteKeysEndpoint.js +26 -30
  13. package/dist/api/admin/apiRemoveAllDecisionMachinesEndpoint.js +35 -37
  14. package/dist/api/admin/apiRemoveDecisionMachineEndpoint.js +40 -40
  15. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.d.ts.map +1 -1
  16. package/dist/api/admin/apiRemoveDetectorKeyEndpoint.js +29 -31
  17. package/dist/api/admin/apiRemoveSiteKeyEndpoint.js +33 -29
  18. package/dist/api/admin/apiRemoveSiteKeysEndpoint.js +32 -31
  19. package/dist/api/admin/apiToggleMaintenanceModeEndpoint.js +43 -35
  20. package/dist/api/admin/apiUpdateDecisionMachineEndpoint.js +43 -63
  21. package/dist/api/admin/apiUpdateDetectorKeyEndpoint.js +36 -35
  22. package/dist/api/admin/createApiAdminRoutesProvider.js +6 -8
  23. package/dist/api/blacklistRequestInspector.js +285 -377
  24. package/dist/api/block.js +17 -30
  25. package/dist/api/captcha/checkSpamEmail.js +88 -98
  26. package/dist/api/captcha/contextAwareValidation.js +21 -15
  27. package/dist/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.js +126 -140
  28. package/dist/api/captcha/getFrictionlessCaptchaChallenge/constants.js +11 -12
  29. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.js +236 -299
  30. package/dist/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.js +10 -13
  31. package/dist/api/captcha/getFrictionlessCaptchaChallenge/handler.js +336 -462
  32. package/dist/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.js +17 -14
  33. package/dist/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.js +27 -27
  34. package/dist/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.js +42 -63
  35. package/dist/api/captcha/getFrictionlessCaptchaChallenge.js +2 -4
  36. package/dist/api/captcha/getImageCaptchaChallenge.js +136 -207
  37. package/dist/api/captcha/getPoWCaptchaChallenge.js +128 -198
  38. package/dist/api/captcha/getPuzzleCaptchaChallenge.js +132 -201
  39. package/dist/api/captcha/maintenanceModeResponses.js +38 -47
  40. package/dist/api/captcha/submitImageCaptchaSolution.js +75 -101
  41. package/dist/api/captcha/submitPoWCaptchaSolution.js +137 -207
  42. package/dist/api/captcha/submitPuzzleCaptchaSolution.js +73 -105
  43. package/dist/api/captcha.js +76 -58
  44. package/dist/api/dnsEventUrl.js +20 -22
  45. package/dist/api/domainMiddleware.js +104 -115
  46. package/dist/api/forwardVerify.js +62 -48
  47. package/dist/api/handshakeTimingMiddleware.js +43 -62
  48. package/dist/api/hardBlockVerdictCache.js +114 -117
  49. package/dist/api/headerCheckMiddleware.js +34 -34
  50. package/dist/api/ignoreMiddleware.js +13 -15
  51. package/dist/api/ipInfoMiddleware.js +17 -19
  52. package/dist/api/ja4.js +149 -188
  53. package/dist/api/ja4Middleware.js +39 -54
  54. package/dist/api/metrics.js +245 -257
  55. package/dist/api/public.js +52 -54
  56. package/dist/api/robotsMiddleware.js +10 -10
  57. package/dist/api/startProviderApi.js +205 -232
  58. package/dist/api/testSiteKey.js +16 -16
  59. package/dist/api/validateAddress.js +23 -21
  60. package/dist/api/verify.js +334 -377
  61. package/dist/cjs/_virtual/_rolldown/runtime.cjs +23 -0
  62. package/dist/cjs/api/admin/apiAdminRoutesProvider.cjs +40 -59
  63. package/dist/cjs/api/admin/apiClearAllCountersEndpoint.cjs +63 -58
  64. package/dist/cjs/api/admin/apiDnsEventEndpoint.cjs +82 -93
  65. package/dist/cjs/api/admin/apiGetAllDecisionMachinesEndpoint.cjs +46 -48
  66. package/dist/cjs/api/admin/apiGetDecisionMachineEndpoint.cjs +41 -41
  67. package/dist/cjs/api/admin/apiRegisterSiteKeyEndpoint.cjs +25 -24
  68. package/dist/cjs/api/admin/apiRegisterSiteKeysEndpoint.cjs +28 -32
  69. package/dist/cjs/api/admin/apiRemoveAllDecisionMachinesEndpoint.cjs +38 -40
  70. package/dist/cjs/api/admin/apiRemoveDecisionMachineEndpoint.cjs +41 -41
  71. package/dist/cjs/api/admin/apiRemoveDetectorKeyEndpoint.cjs +31 -33
  72. package/dist/cjs/api/admin/apiRemoveSiteKeyEndpoint.cjs +35 -31
  73. package/dist/cjs/api/admin/apiRemoveSiteKeysEndpoint.cjs +34 -33
  74. package/dist/cjs/api/admin/apiToggleMaintenanceModeEndpoint.cjs +45 -35
  75. package/dist/cjs/api/admin/apiUpdateDecisionMachineEndpoint.cjs +45 -65
  76. package/dist/cjs/api/admin/apiUpdateDetectorKeyEndpoint.cjs +38 -37
  77. package/dist/cjs/api/admin/createApiAdminRoutesProvider.cjs +7 -8
  78. package/dist/cjs/api/blacklistRequestInspector.cjs +288 -376
  79. package/dist/cjs/api/block.cjs +18 -30
  80. package/dist/cjs/api/captcha/checkSpamEmail.cjs +90 -100
  81. package/dist/cjs/api/captcha/contextAwareValidation.cjs +21 -14
  82. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/accessPolicy.cjs +125 -140
  83. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/constants.cjs +10 -10
  84. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decisionMachine.cjs +238 -301
  85. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/decryptSimdReadings.cjs +9 -12
  86. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/handler.cjs +349 -473
  87. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/honeypotResponse.cjs +18 -14
  88. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/sessionDedup.cjs +26 -26
  89. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge/shortCircuit.cjs +42 -63
  90. package/dist/cjs/api/captcha/getFrictionlessCaptchaChallenge.cjs +2 -3
  91. package/dist/cjs/api/captcha/getImageCaptchaChallenge.cjs +138 -208
  92. package/dist/cjs/api/captcha/getPoWCaptchaChallenge.cjs +131 -200
  93. package/dist/cjs/api/captcha/getPuzzleCaptchaChallenge.cjs +135 -203
  94. package/dist/cjs/api/captcha/maintenanceModeResponses.cjs +38 -44
  95. package/dist/cjs/api/captcha/submitImageCaptchaSolution.cjs +77 -102
  96. package/dist/cjs/api/captcha/submitPoWCaptchaSolution.cjs +140 -209
  97. package/dist/cjs/api/captcha/submitPuzzleCaptchaSolution.cjs +76 -107
  98. package/dist/cjs/api/captcha.cjs +79 -58
  99. package/dist/cjs/api/dnsEventUrl.cjs +20 -21
  100. package/dist/cjs/api/domainMiddleware.cjs +106 -117
  101. package/dist/cjs/api/forwardVerify.cjs +63 -49
  102. package/dist/cjs/api/handshakeTimingMiddleware.cjs +43 -61
  103. package/dist/cjs/api/hardBlockVerdictCache.cjs +114 -114
  104. package/dist/cjs/api/headerCheckMiddleware.cjs +34 -34
  105. package/dist/cjs/api/ignoreMiddleware.cjs +12 -14
  106. package/dist/cjs/api/ipInfoMiddleware.cjs +16 -18
  107. package/dist/cjs/api/ja4.cjs +149 -187
  108. package/dist/cjs/api/ja4Middleware.cjs +40 -53
  109. package/dist/cjs/api/metrics.cjs +229 -228
  110. package/dist/cjs/api/public.cjs +57 -57
  111. package/dist/cjs/api/robotsMiddleware.cjs +9 -9
  112. package/dist/cjs/api/startProviderApi.cjs +219 -237
  113. package/dist/cjs/api/testSiteKey.cjs +15 -14
  114. package/dist/cjs/api/validateAddress.cjs +24 -21
  115. package/dist/cjs/api/verify.cjs +338 -379
  116. package/dist/cjs/compositeIpAddress.cjs +40 -47
  117. package/dist/cjs/index.cjs +65 -71
  118. package/dist/cjs/pairs.cjs +31 -35
  119. package/dist/cjs/rules/lang.cjs +10 -14
  120. package/dist/cjs/schedulers/captchaScheduler.cjs +26 -32
  121. package/dist/cjs/schedulers/getClientList.cjs +35 -33
  122. package/dist/cjs/schedulers/setClientEntropy.cjs +35 -33
  123. package/dist/cjs/schedulers/updateSpamEmailDomains.cjs +39 -43
  124. package/dist/cjs/services/ipComparison.cjs +122 -119
  125. package/dist/cjs/tasks/captchaManager.cjs +407 -486
  126. package/dist/cjs/tasks/client/clientTasks.cjs +347 -470
  127. package/dist/cjs/tasks/dataset/datasetTasks.cjs +25 -29
  128. package/dist/cjs/tasks/dataset/datasetTasksUtils.cjs +16 -32
  129. package/dist/cjs/tasks/decisionMachine/decisionMachineRunner.cjs +220 -307
  130. package/dist/cjs/tasks/detection/decodeBehavior.cjs +408 -317
  131. package/dist/cjs/tasks/detection/decodePayload.cjs +686 -468
  132. package/dist/cjs/tasks/detection/decodeSimd.cjs +462 -304
  133. package/dist/cjs/tasks/detection/getBotScore.cjs +37 -43
  134. package/dist/cjs/tasks/dnsEvent/enrichDnsEvent.cjs +88 -71
  135. package/dist/cjs/tasks/frictionless/frictionlessTasks.cjs +348 -486
  136. package/dist/cjs/tasks/frictionless/frictionlessTasksUtils.cjs +13 -30
  137. package/dist/cjs/tasks/frictionless/routingMachine.cjs +71 -55
  138. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +555 -806
  139. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasksUtils.cjs +22 -23
  140. package/dist/cjs/tasks/index.cjs +2 -4
  141. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +491 -687
  142. package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +17 -29
  143. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasks.cjs +422 -591
  144. package/dist/cjs/tasks/puzzleCaptcha/puzzleTasksUtils.cjs +8 -5
  145. package/dist/cjs/tasks/spam/checkSpamEmail.cjs +133 -141
  146. package/dist/cjs/tasks/spam/checkTrafficFilter.cjs +118 -76
  147. package/dist/cjs/tasks/spam/evaluateEmailSpamRules.cjs +75 -84
  148. package/dist/cjs/tasks/spam/updateSpamEmailDomains.cjs +40 -56
  149. package/dist/cjs/tasks/tasks.cjs +107 -158
  150. package/dist/cjs/util/usageCounters.cjs +220 -211
  151. package/dist/cjs/util.cjs +329 -359
  152. package/dist/cjs/utils/devicePlatform.cjs +14 -8
  153. package/dist/cjs/utils/dns.cjs +68 -93
  154. package/dist/cjs/utils/hashUserAgent.cjs +14 -7
  155. package/dist/cjs/utils/hashUserIp.cjs +14 -6
  156. package/dist/cjs/utils/honeypot/encoders.cjs +74 -80
  157. package/dist/cjs/utils/honeypot/phraseBank.cjs +51 -44
  158. package/dist/cjs/utils/normalizeRequestIp.cjs +33 -25
  159. package/dist/compositeIpAddress.js +38 -46
  160. package/dist/index.js +12 -56
  161. package/dist/pairs.js +31 -37
  162. package/dist/rules/lang.js +11 -15
  163. package/dist/schedulers/captchaScheduler.js +25 -31
  164. package/dist/schedulers/getClientList.js +34 -32
  165. package/dist/schedulers/setClientEntropy.js +34 -32
  166. package/dist/schedulers/updateSpamEmailDomains.js +38 -42
  167. package/dist/services/ipComparison.js +122 -119
  168. package/dist/tasks/captchaManager.js +403 -483
  169. package/dist/tasks/client/clientTasks.js +343 -467
  170. package/dist/tasks/dataset/datasetTasks.js +24 -28
  171. package/dist/tasks/dataset/datasetTasksUtils.js +15 -31
  172. package/dist/tasks/decisionMachine/decisionMachineRunner.js +217 -306
  173. package/dist/tasks/detection/getBotScore.js +37 -43
  174. package/dist/tasks/dnsEvent/enrichDnsEvent.d.ts.map +1 -1
  175. package/dist/tasks/dnsEvent/enrichDnsEvent.js +88 -74
  176. package/dist/tasks/dnsEvent/enrichDnsEvent.js.map +1 -1
  177. package/dist/tasks/frictionless/frictionlessTasks.js +342 -479
  178. package/dist/tasks/frictionless/frictionlessTasksUtils.js +14 -32
  179. package/dist/tasks/frictionless/routingMachine.js +72 -56
  180. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +550 -801
  181. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +21 -22
  182. package/dist/tasks/index.js +1 -3
  183. package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
  184. package/dist/tasks/powCaptcha/powTasks.js +486 -682
  185. package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
  186. package/dist/tasks/powCaptcha/powTasksUtils.js +15 -28
  187. package/dist/tasks/puzzleCaptcha/puzzleTasks.d.ts.map +1 -1
  188. package/dist/tasks/puzzleCaptcha/puzzleTasks.js +417 -586
  189. package/dist/tasks/puzzleCaptcha/puzzleTasks.js.map +1 -1
  190. package/dist/tasks/puzzleCaptcha/puzzleTasksUtils.js +9 -6
  191. package/dist/tasks/spam/checkSpamEmail.js +133 -141
  192. package/dist/tasks/spam/checkTrafficFilter.d.ts +1 -0
  193. package/dist/tasks/spam/checkTrafficFilter.d.ts.map +1 -1
  194. package/dist/tasks/spam/checkTrafficFilter.js +117 -77
  195. package/dist/tasks/spam/checkTrafficFilter.js.map +1 -1
  196. package/dist/tasks/spam/evaluateEmailSpamRules.js +75 -86
  197. package/dist/tasks/spam/updateSpamEmailDomains.js +36 -54
  198. package/dist/tasks/tasks.js +98 -150
  199. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js +4 -1
  200. package/dist/tests/integration/api/blacklistRequestInspector.integration.test.js.map +1 -1
  201. package/dist/tests/unit/api/block.unit.test.js +8 -4
  202. package/dist/tests/unit/api/block.unit.test.js.map +1 -1
  203. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js +8 -6
  204. package/dist/tests/unit/api/captcha/maintenanceModeShortCircuit.unit.test.js.map +1 -1
  205. package/dist/tests/unit/api/forwardVerify.unit.test.js +3 -1
  206. package/dist/tests/unit/api/forwardVerify.unit.test.js.map +1 -1
  207. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js +15 -7
  208. package/dist/tests/unit/api/getFrictionlessCaptchaChallenge.unit.test.js.map +1 -1
  209. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js +24 -18
  210. package/dist/tests/unit/schedulers/captchaScheduler.unit.test.js.map +1 -1
  211. package/dist/tests/unit/schedulers/getClientList.unit.test.js +19 -13
  212. package/dist/tests/unit/schedulers/getClientList.unit.test.js.map +1 -1
  213. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js +19 -13
  214. package/dist/tests/unit/schedulers/setClientEntropy.unit.test.js.map +1 -1
  215. package/dist/tests/unit/tasks/client/clientTasks.unit.test.js.map +1 -1
  216. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js +22 -16
  217. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.unit.test.js.map +1 -1
  218. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js +6 -0
  219. package/dist/tests/unit/tasks/powCaptcha/powTasks.unit.test.js.map +1 -1
  220. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js +60 -1
  221. package/dist/tests/unit/tasks/puzzleCaptcha/puzzleTasks.unit.test.js.map +1 -1
  222. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js +140 -0
  223. package/dist/tests/unit/tasks/spam/checkTrafficFilter.unit.test.js.map +1 -1
  224. package/dist/tests/unit/testUtils/mockProviderEnv.d.ts +7 -7
  225. package/dist/util/usageCounters.js +220 -212
  226. package/dist/util.d.ts.map +1 -1
  227. package/dist/util.js +327 -364
  228. package/dist/util.js.map +1 -1
  229. package/dist/utils/devicePlatform.js +15 -9
  230. package/dist/utils/dns.js +64 -77
  231. package/dist/utils/hashUserAgent.js +14 -7
  232. package/dist/utils/hashUserIp.js +14 -6
  233. package/dist/utils/honeypot/encoders.js +74 -80
  234. package/dist/utils/honeypot/phraseBank.js +49 -44
  235. package/dist/utils/normalizeRequestIp.js +34 -26
  236. package/package.json +24 -24
@@ -1,218 +1,227 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const types = require("@prosopo/types");
4
- const INCR_WITH_TTL_LUA = `local count = redis.call('INCR', KEYS[1])
1
+ let _prosopo_types = require("@prosopo/types");
2
+ //#region src/util/usageCounters.ts
3
+ /** Lua: INCR + EXPIRE only on first transition to 1. No TTL refresh. */
4
+ var INCR_WITH_TTL_LUA = `local count = redis.call('INCR', KEYS[1])
5
5
  if count == 1 then
6
6
  redis.call('EXPIRE', KEYS[1], tonumber(ARGV[1]))
7
7
  end
8
8
  return count`;
9
- class UsageCounters {
10
- constructor(connection, logger) {
11
- this.scriptShaPromise = null;
12
- this.connection = connection;
13
- this.logger = logger;
14
- }
15
- async getClient() {
16
- if (!this.connection.isReady()) {
17
- return null;
18
- }
19
- try {
20
- return await this.connection.getClient();
21
- } catch {
22
- return null;
23
- }
24
- }
25
- async getScriptSha(client) {
26
- if (!this.scriptShaPromise) {
27
- this.scriptShaPromise = client.scriptLoad(INCR_WITH_TTL_LUA).then(
28
- (sha) => sha,
29
- (error) => {
30
- this.scriptShaPromise = null;
31
- throw error;
32
- }
33
- );
34
- }
35
- return this.scriptShaPromise;
36
- }
37
- /**
38
- * Atomically increment a counter. Sets TTL only on the first write within
39
- * the window. Returns the post-increment value, or null on Redis failure.
40
- */
41
- async incr(dappAccount, spec, value) {
42
- const client = await this.getClient();
43
- if (!client) {
44
- return null;
45
- }
46
- const key = types.encodeCounterKey(dappAccount, spec, value);
47
- const ttlSeconds = types.COUNTER_WINDOW_SECONDS[spec.window];
48
- try {
49
- const sha = await this.getScriptSha(client);
50
- const result = await client.evalSha(sha, {
51
- keys: [key],
52
- arguments: [String(ttlSeconds)]
53
- });
54
- return Number(result);
55
- } catch (error) {
56
- this.logger.warn(() => ({
57
- msg: "Counter incr failed",
58
- err: error,
59
- data: { key }
60
- }));
61
- return null;
62
- }
63
- }
64
- /**
65
- * Fire-and-forget increment. Errors are logged but do not propagate.
66
- */
67
- incrAsync(dappAccount, spec, value) {
68
- this.incr(dappAccount, spec, value).catch((error) => {
69
- this.logger.error(() => ({
70
- msg: "Counter incrAsync rejected",
71
- err: error,
72
- data: { dappAccount, spec, value }
73
- }));
74
- });
75
- }
76
- /**
77
- * Fire-and-forget increment of multiple counters in parallel.
78
- */
79
- incrManyAsync(dappAccount, increments) {
80
- for (const { spec, value } of increments) {
81
- this.incrAsync(dappAccount, spec, value);
82
- }
83
- }
84
- /**
85
- * Delete every counter key matching the given dapp (or all counters when
86
- * dappAccount is omitted). Intended for admin manual-testing flows — not
87
- * for the hot path. Uses SCAN + DEL in chunks; returns the number of keys
88
- * deleted. Returns null on Redis failure.
89
- */
90
- async clearAll(dappAccount) {
91
- const client = await this.getClient();
92
- if (!client) {
93
- return null;
94
- }
95
- const pattern = dappAccount ? `cnt:${dappAccount}:*` : "cnt:*";
96
- let deleted = 0;
97
- try {
98
- const batchSize = 500;
99
- let batch = [];
100
- for await (const key of client.scanIterator({
101
- MATCH: pattern,
102
- COUNT: 200
103
- })) {
104
- if (Array.isArray(key)) {
105
- for (const k of key) batch.push(k);
106
- } else {
107
- batch.push(key);
108
- }
109
- if (batch.length >= batchSize) {
110
- deleted += Number(await client.del(batch));
111
- batch = [];
112
- }
113
- }
114
- if (batch.length > 0) {
115
- deleted += Number(await client.del(batch));
116
- }
117
- return deleted;
118
- } catch (error) {
119
- this.logger.warn(() => ({
120
- msg: "Counter clearAll failed",
121
- err: error,
122
- data: { pattern, deleted }
123
- }));
124
- return null;
125
- }
126
- }
127
- /**
128
- * Bulk read counters via MGET. Missing keys map to 0. Returns a record
129
- * keyed by encoded counter key. Returns null on Redis failure so callers
130
- * can fall back.
131
- */
132
- async batchGet(dappAccount, reads) {
133
- if (reads.length === 0) {
134
- return {};
135
- }
136
- const client = await this.getClient();
137
- if (!client) {
138
- return null;
139
- }
140
- const keys = reads.map(
141
- (r) => types.encodeCounterKey(dappAccount, r.spec, r.value)
142
- );
143
- try {
144
- const values = await client.mGet(keys);
145
- const out = {};
146
- for (let i = 0; i < keys.length; i++) {
147
- const raw = values[i];
148
- const key = keys[i];
149
- if (key === void 0) continue;
150
- out[key] = raw === null || raw === void 0 ? 0 : Number(raw);
151
- }
152
- return out;
153
- } catch (error) {
154
- this.logger.warn(() => ({
155
- msg: "Counter batchGet failed",
156
- err: error,
157
- data: { keyCount: keys.length }
158
- }));
159
- return null;
160
- }
161
- }
162
- }
163
- const buildAllWindowIncrements = (kind, captchaType, ip, userAccount, peerIp) => {
164
- const out = [];
165
- for (const window of [
166
- "1m",
167
- "10m",
168
- "1h",
169
- "3h",
170
- "6h",
171
- "24h"
172
- ]) {
173
- out.push(
174
- { spec: { kind, captchaType, dimension: "ip", window }, value: ip },
175
- {
176
- spec: { kind, captchaType, dimension: "userAccount", window },
177
- value: userAccount
178
- }
179
- );
180
- if (peerIp && peerIp !== ip) {
181
- out.push({
182
- spec: { kind, captchaType, dimension: "peerIp", window },
183
- value: peerIp
184
- });
185
- }
186
- if (captchaType !== "any") {
187
- out.push(
188
- {
189
- spec: { kind, captchaType: "any", dimension: "ip", window },
190
- value: ip
191
- },
192
- {
193
- spec: {
194
- kind,
195
- captchaType: "any",
196
- dimension: "userAccount",
197
- window
198
- },
199
- value: userAccount
200
- }
201
- );
202
- if (peerIp && peerIp !== ip) {
203
- out.push({
204
- spec: {
205
- kind,
206
- captchaType: "any",
207
- dimension: "peerIp",
208
- window
209
- },
210
- value: peerIp
211
- });
212
- }
213
- }
214
- }
215
- return out;
9
+ /**
10
+ * Per-sitekey usage counters backed by Redis. Writes are atomic via a Lua
11
+ * script that only sets TTL on first transition to 1 — subsequent INCRs
12
+ * within the same window do not refresh the TTL.
13
+ *
14
+ * All operations gracefully no-op (or return zeros) when Redis is unavailable
15
+ * so the caller can fall back to baseline behaviour.
16
+ */
17
+ var UsageCounters = class {
18
+ constructor(connection, logger) {
19
+ this.scriptShaPromise = null;
20
+ this.connection = connection;
21
+ this.logger = logger;
22
+ }
23
+ async getClient() {
24
+ if (!this.connection.isReady()) return null;
25
+ try {
26
+ return await this.connection.getClient();
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ async getScriptSha(client) {
32
+ if (!this.scriptShaPromise) this.scriptShaPromise = client.scriptLoad(INCR_WITH_TTL_LUA).then((sha) => sha, (error) => {
33
+ this.scriptShaPromise = null;
34
+ throw error;
35
+ });
36
+ return this.scriptShaPromise;
37
+ }
38
+ /**
39
+ * Atomically increment a counter. Sets TTL only on the first write within
40
+ * the window. Returns the post-increment value, or null on Redis failure.
41
+ */
42
+ async incr(dappAccount, spec, value) {
43
+ const client = await this.getClient();
44
+ if (!client) return null;
45
+ const key = (0, _prosopo_types.encodeCounterKey)(dappAccount, spec, value);
46
+ const ttlSeconds = _prosopo_types.COUNTER_WINDOW_SECONDS[spec.window];
47
+ try {
48
+ const sha = await this.getScriptSha(client);
49
+ const result = await client.evalSha(sha, {
50
+ keys: [key],
51
+ arguments: [String(ttlSeconds)]
52
+ });
53
+ return Number(result);
54
+ } catch (error) {
55
+ this.logger.warn(() => ({
56
+ msg: "Counter incr failed",
57
+ err: error,
58
+ data: { key }
59
+ }));
60
+ return null;
61
+ }
62
+ }
63
+ /**
64
+ * Fire-and-forget increment. Errors are logged but do not propagate.
65
+ */
66
+ incrAsync(dappAccount, spec, value) {
67
+ this.incr(dappAccount, spec, value).catch((error) => {
68
+ this.logger.error(() => ({
69
+ msg: "Counter incrAsync rejected",
70
+ err: error,
71
+ data: {
72
+ dappAccount,
73
+ spec,
74
+ value
75
+ }
76
+ }));
77
+ });
78
+ }
79
+ /**
80
+ * Fire-and-forget increment of multiple counters in parallel.
81
+ */
82
+ incrManyAsync(dappAccount, increments) {
83
+ for (const { spec, value } of increments) this.incrAsync(dappAccount, spec, value);
84
+ }
85
+ /**
86
+ * Delete every counter key matching the given dapp (or all counters when
87
+ * dappAccount is omitted). Intended for admin manual-testing flows — not
88
+ * for the hot path. Uses SCAN + DEL in chunks; returns the number of keys
89
+ * deleted. Returns null on Redis failure.
90
+ */
91
+ async clearAll(dappAccount) {
92
+ const client = await this.getClient();
93
+ if (!client) return null;
94
+ const pattern = dappAccount ? `cnt:${dappAccount}:*` : "cnt:*";
95
+ let deleted = 0;
96
+ try {
97
+ const batchSize = 500;
98
+ let batch = [];
99
+ for await (const key of client.scanIterator({
100
+ MATCH: pattern,
101
+ COUNT: 200
102
+ })) {
103
+ if (Array.isArray(key)) for (const k of key) batch.push(k);
104
+ else batch.push(key);
105
+ if (batch.length >= batchSize) {
106
+ deleted += Number(await client.del(batch));
107
+ batch = [];
108
+ }
109
+ }
110
+ if (batch.length > 0) deleted += Number(await client.del(batch));
111
+ return deleted;
112
+ } catch (error) {
113
+ this.logger.warn(() => ({
114
+ msg: "Counter clearAll failed",
115
+ err: error,
116
+ data: {
117
+ pattern,
118
+ deleted
119
+ }
120
+ }));
121
+ return null;
122
+ }
123
+ }
124
+ /**
125
+ * Bulk read counters via MGET. Missing keys map to 0. Returns a record
126
+ * keyed by encoded counter key. Returns null on Redis failure so callers
127
+ * can fall back.
128
+ */
129
+ async batchGet(dappAccount, reads) {
130
+ if (reads.length === 0) return {};
131
+ const client = await this.getClient();
132
+ if (!client) return null;
133
+ const keys = reads.map((r) => (0, _prosopo_types.encodeCounterKey)(dappAccount, r.spec, r.value));
134
+ try {
135
+ const values = await client.mGet(keys);
136
+ const out = {};
137
+ for (let i = 0; i < keys.length; i++) {
138
+ const raw = values[i];
139
+ const key = keys[i];
140
+ if (key === void 0) continue;
141
+ out[key] = raw === null || raw === void 0 ? 0 : Number(raw);
142
+ }
143
+ return out;
144
+ } catch (error) {
145
+ this.logger.warn(() => ({
146
+ msg: "Counter batchGet failed",
147
+ err: error,
148
+ data: { keyCount: keys.length }
149
+ }));
150
+ return null;
151
+ }
152
+ }
216
153
  };
154
+ /**
155
+ * Helper: produce the standard 24-counter served/solved write set for a given
156
+ * captcha challenge. Used by both served (post-session) and solved hooks.
157
+ */
158
+ var buildAllWindowIncrements = (kind, captchaType, ip, userAccount, peerIp) => {
159
+ const out = [];
160
+ for (const window of [
161
+ "1m",
162
+ "10m",
163
+ "1h",
164
+ "3h",
165
+ "6h",
166
+ "24h"
167
+ ]) {
168
+ out.push({
169
+ spec: {
170
+ kind,
171
+ captchaType,
172
+ dimension: "ip",
173
+ window
174
+ },
175
+ value: ip
176
+ }, {
177
+ spec: {
178
+ kind,
179
+ captchaType,
180
+ dimension: "userAccount",
181
+ window
182
+ },
183
+ value: userAccount
184
+ });
185
+ if (peerIp && peerIp !== ip) out.push({
186
+ spec: {
187
+ kind,
188
+ captchaType,
189
+ dimension: "peerIp",
190
+ window
191
+ },
192
+ value: peerIp
193
+ });
194
+ if (captchaType !== "any") {
195
+ out.push({
196
+ spec: {
197
+ kind,
198
+ captchaType: "any",
199
+ dimension: "ip",
200
+ window
201
+ },
202
+ value: ip
203
+ }, {
204
+ spec: {
205
+ kind,
206
+ captchaType: "any",
207
+ dimension: "userAccount",
208
+ window
209
+ },
210
+ value: userAccount
211
+ });
212
+ if (peerIp && peerIp !== ip) out.push({
213
+ spec: {
214
+ kind,
215
+ captchaType: "any",
216
+ dimension: "peerIp",
217
+ window
218
+ },
219
+ value: peerIp
220
+ });
221
+ }
222
+ }
223
+ return out;
224
+ };
225
+ //#endregion
217
226
  exports.UsageCounters = UsageCounters;
218
227
  exports.buildAllWindowIncrements = buildAllWindowIncrements;