@prosopo/user-access-policy 3.12.32 → 3.13.0

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 (80) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +12 -10
  2. package/.turbo/turbo-build$colon$tsc.log +14 -14
  3. package/.turbo/turbo-build.log +13 -11
  4. package/CHANGELOG.md +91 -0
  5. package/dist/.export.d.ts +2 -0
  6. package/dist/.export.d.ts.map +1 -1
  7. package/dist/.export.js +3 -1
  8. package/dist/.export.js.map +1 -1
  9. package/dist/cjs/.export.cjs +11 -0
  10. package/dist/cjs/classifyBrowser.cjs +44 -0
  11. package/dist/cjs/headerMatch.cjs +122 -0
  12. package/dist/cjs/mongoose/mongooseRuleSchema.cjs +20 -0
  13. package/dist/cjs/redis/reader/redisRulesReader.cjs +8 -0
  14. package/dist/cjs/redis/reader/redisRulesSplitQuery.cjs +3 -1
  15. package/dist/cjs/redis/redisRuleIndex.cjs +20 -0
  16. package/dist/cjs/ruleInput/userScopeInput.cjs +6 -1
  17. package/dist/cjs/ruleRecord.cjs +6 -1
  18. package/dist/classifyBrowser.d.ts +4 -0
  19. package/dist/classifyBrowser.d.ts.map +1 -0
  20. package/dist/classifyBrowser.js +43 -0
  21. package/dist/classifyBrowser.js.map +1 -0
  22. package/dist/headerMatch.d.ts +13 -0
  23. package/dist/headerMatch.d.ts.map +1 -0
  24. package/dist/headerMatch.js +116 -0
  25. package/dist/headerMatch.js.map +1 -0
  26. package/dist/mongoose/mongooseRuleSchema.d.ts.map +1 -1
  27. package/dist/mongoose/mongooseRuleSchema.js +20 -0
  28. package/dist/mongoose/mongooseRuleSchema.js.map +1 -1
  29. package/dist/redis/reader/redisRulesReader.d.ts.map +1 -1
  30. package/dist/redis/reader/redisRulesReader.js +8 -0
  31. package/dist/redis/reader/redisRulesReader.js.map +1 -1
  32. package/dist/redis/reader/redisRulesSplitQuery.d.ts.map +1 -1
  33. package/dist/redis/reader/redisRulesSplitQuery.js +3 -1
  34. package/dist/redis/reader/redisRulesSplitQuery.js.map +1 -1
  35. package/dist/redis/redisRuleIndex.d.ts.map +1 -1
  36. package/dist/redis/redisRuleIndex.js +20 -0
  37. package/dist/redis/redisRuleIndex.js.map +1 -1
  38. package/dist/rule.d.ts +5 -0
  39. package/dist/rule.d.ts.map +1 -1
  40. package/dist/ruleInput/ruleInput.d.ts +30 -0
  41. package/dist/ruleInput/ruleInput.d.ts.map +1 -1
  42. package/dist/ruleInput/userScopeInput.d.ts +40 -0
  43. package/dist/ruleInput/userScopeInput.d.ts.map +1 -1
  44. package/dist/ruleInput/userScopeInput.js +6 -1
  45. package/dist/ruleInput/userScopeInput.js.map +1 -1
  46. package/dist/ruleRecord.d.ts +2 -2
  47. package/dist/ruleRecord.d.ts.map +1 -1
  48. package/dist/ruleRecord.js +6 -1
  49. package/dist/ruleRecord.js.map +1 -1
  50. package/dist/tests/classifyBrowser.unit.test.d.ts +2 -0
  51. package/dist/tests/classifyBrowser.unit.test.d.ts.map +1 -0
  52. package/dist/tests/classifyBrowser.unit.test.js +99 -0
  53. package/dist/tests/classifyBrowser.unit.test.js.map +1 -0
  54. package/dist/tests/headerMatch.unit.test.d.ts +2 -0
  55. package/dist/tests/headerMatch.unit.test.d.ts.map +1 -0
  56. package/dist/tests/headerMatch.unit.test.js +202 -0
  57. package/dist/tests/headerMatch.unit.test.js.map +1 -0
  58. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js +3 -3
  59. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js.map +1 -1
  60. package/dist/tests/redis/reader/redisRulesSplitQuery.unit.test.js +9 -0
  61. package/dist/tests/redis/reader/redisRulesSplitQuery.unit.test.js.map +1 -1
  62. package/dist/tests/transformRule.unit.test.js +5 -0
  63. package/dist/tests/transformRule.unit.test.js.map +1 -1
  64. package/package.json +14 -14
  65. package/src/.export.ts +17 -0
  66. package/src/classifyBrowser.ts +77 -0
  67. package/src/headerMatch.ts +180 -0
  68. package/src/mongoose/mongooseRuleSchema.ts +5 -0
  69. package/src/redis/reader/redisRulesReader.ts +10 -0
  70. package/src/redis/reader/redisRulesSplitQuery.ts +2 -0
  71. package/src/redis/redisRuleIndex.ts +14 -0
  72. package/src/rule.ts +14 -0
  73. package/src/ruleInput/userScopeInput.ts +8 -0
  74. package/src/ruleRecord.ts +5 -0
  75. package/src/tests/classifyBrowser.unit.test.ts +138 -0
  76. package/src/tests/headerMatch.unit.test.ts +350 -0
  77. package/src/tests/redis/reader/redisRulesQuery.unit.test.ts +3 -3
  78. package/src/tests/redis/reader/redisRulesSplitQuery.unit.test.ts +17 -0
  79. package/src/tests/transformRule.unit.test.ts +5 -0
  80. package/tsconfig.tsbuildinfo +1 -1
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/user-access-policy@3.12.32 build:cjs
2
+ > @prosopo/user-access-policy@3.13.0 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -33,7 +33,7 @@ ViteCommonJSConfig: .
33
33
  vite v8.1.5 building ssr environment for production...
34
34
  Bundle build started
35
35
  
36
- ✓ 38 modules transformed.
36
+ ✓ 40 modules transformed.
37
37
  rendering chunks...
38
38
  computing gzip size...
39
39
  dist/cjs/api/accessRulesApiClient.cjs 0.00 kB │ gzip: 0.02 kB
@@ -47,32 +47,34 @@ dist/cjs/rule.cjs 0.45 kB │ gzip: 0.26 kB
47
47
  dist/cjs/api/.export.cjs 0.53 kB │ gzip: 0.21 kB
48
48
  dist/cjs/ruleInput/.export.cjs 0.57 kB │ gzip: 0.20 kB
49
49
  dist/cjs/api/delete/deleteAllRules.cjs 0.79 kB │ gzip: 0.41 kB
50
- dist/cjs/ruleRecord.cjs 0.83 kB │ gzip: 0.35 kB
50
+ dist/cjs/ruleRecord.cjs 0.90 kB │ gzip: 0.38 kB
51
51
  dist/cjs/_virtual/_rolldown/runtime.cjs 1.00 kB │ gzip: 0.50 kB
52
52
  dist/cjs/api/read/getMissingIds.cjs 1.12 kB │ gzip: 0.51 kB
53
53
  dist/cjs/api/read/fetchRules.cjs 1.22 kB │ gzip: 0.54 kB
54
54
  dist/cjs/redis/redisRulesStorage.cjs 1.41 kB │ gzip: 0.49 kB
55
55
  dist/cjs/api/delete/deleteRuleGroups.cjs 1.43 kB │ gzip: 0.65 kB
56
+ dist/cjs/classifyBrowser.cjs 1.45 kB │ gzip: 0.73 kB
56
57
  dist/cjs/api/write/rehashRules.cjs 1.48 kB │ gzip: 0.56 kB
57
58
  dist/cjs/api/delete/deleteRules.cjs 1.52 kB │ gzip: 0.65 kB
58
- dist/cjs/mongoose/mongooseRuleSchema.cjs 1.52 kB │ gzip: 0.42 kB
59
59
  dist/cjs/api/read/findRuleIds.cjs 1.58 kB │ gzip: 0.66 kB
60
- dist/cjs/.export.cjs 1.68 kB │ gzip: 0.42 kB
60
+ dist/cjs/mongoose/mongooseRuleSchema.cjs 1.79 kB │ gzip: 0.45 kB
61
61
  dist/cjs/redis/redisClient.cjs 1.92 kB │ gzip: 0.72 kB
62
62
  dist/cjs/classifyOs.cjs 2.04 kB │ gzip: 1.05 kB
63
63
  dist/cjs/ruleInput/policyInput.cjs 2.09 kB │ gzip: 0.79 kB
64
64
  dist/cjs/ruleInput/ruleInput.cjs 2.28 kB │ gzip: 0.69 kB
65
65
  dist/cjs/matchedRule.cjs 2.29 kB │ gzip: 1.03 kB
66
- dist/cjs/ruleInput/userScopeInput.cjs 2.44 kB │ gzip: 0.77 kB
66
+ dist/cjs/.export.cjs 2.44 kB │ gzip: 0.57 kB
67
67
  dist/cjs/api/rulesApiClient.cjs 2.53 kB │ gzip: 0.68 kB
68
68
  dist/cjs/redis/reader/redisAggregate.cjs 2.57 kB │ gzip: 1.04 kB
69
+ dist/cjs/ruleInput/userScopeInput.cjs 2.67 kB │ gzip: 0.81 kB
69
70
  dist/cjs/transformRule.cjs 2.90 kB │ gzip: 1.03 kB
70
- dist/cjs/redis/redisRuleIndex.cjs 3.12 kB │ gzip: 0.82 kB
71
71
  dist/cjs/api/write/insertRules.cjs 3.12 kB │ gzip: 1.09 kB
72
72
  dist/cjs/redis/redisRulesWriter.cjs 3.50 kB │ gzip: 1.15 kB
73
+ dist/cjs/redis/redisRuleIndex.cjs 3.55 kB │ gzip: 0.85 kB
73
74
  dist/cjs/api/ruleApiRoutes.cjs 4.14 kB │ gzip: 1.06 kB
74
75
  dist/cjs/redis/reader/redisRulesQuery.cjs 4.76 kB │ gzip: 1.58 kB
75
- dist/cjs/redis/reader/redisRulesSplitQuery.cjs 5.15 kB │ gzip: 2.16 kB
76
- dist/cjs/redis/reader/redisRulesReader.cjs 11.68 kB │ gzip: 2.84 kB
76
+ dist/cjs/redis/reader/redisRulesSplitQuery.cjs 5.17 kB │ gzip: 2.17 kB
77
+ dist/cjs/headerMatch.cjs 5.74 kB │ gzip: 2.25 kB
78
+ dist/cjs/redis/reader/redisRulesReader.cjs 11.85 kB │ gzip: 2.88 kB
77
79
 
78
- ✓ built in 90ms
80
+ ✓ built in 116ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/user-access-policy@3.12.32 build:tsc
2
+ > @prosopo/user-access-policy@3.13.0 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 1:56:23 PM - Projects in this build:
5
+ 1:15:14 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
  * ../api/tsconfig.json
16
16
  * tsconfig.json
17
17
 
18
- 1:56:23 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'
18
+ 1:15:14 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
- 1:56:23 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
20
+ 1:15:14 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
- 1:56:23 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
22
+ 1:15:14 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
- 1:56:23 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
24
+ 1:15:14 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
25
25
 
26
- 1:56:23 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'
26
+ 1:15:14 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'
27
27
 
28
- 1:56:23 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/index.ts' is older than output '../types/tsconfig.tsbuildinfo'
28
+ 1:15:14 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/types.test-d.ts' is older than output '../types/tsconfig.tsbuildinfo'
29
29
 
30
- 1:56:23 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
30
+ 1:15:14 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/utils/batches.ts' is older than output '../common/tsconfig.tsbuildinfo'
31
31
 
32
- 1:56:23 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/index.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
32
+ 1:15:14 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
33
33
 
34
- 1:56:23 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
34
+ 1:15:14 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
35
35
 
36
- 1:56:23 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
36
+ 1:15:14 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/tests/apiHarness.ts' is older than output '../api/tsconfig.tsbuildinfo'
37
37
 
38
- 1:56:23 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
38
+ 1:15:14 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
39
39
 
40
- 1:56:23 PM - Building project '/home/runner/work/captcha/captcha/packages/user-access-policy/tsconfig.json'...
40
+ 1:15:14 PM - Building project '/home/runner/work/captcha/captcha/packages/user-access-policy/tsconfig.json'...
41
41
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/user-access-policy@3.12.32 build
2
+ > @prosopo/user-access-policy@3.13.0 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/user-access-policy@3.12.32 build:cross-env
6
+ > @prosopo/user-access-policy@3.13.0 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -37,7 +37,7 @@ ViteEsmConfig: .
37
37
  vite v8.1.5 building ssr environment for production...
38
38
  Bundle build started
39
39
  
40
- ✓ 38 modules transformed.
40
+ ✓ 40 modules transformed.
41
41
  rendering chunks...
42
42
  computing gzip size...
43
43
  dist/_virtual/_rolldown/runtime.js 0.04 kB │ gzip: 0.06 kB
@@ -51,32 +51,34 @@ dist/rulesStorage.js 0.29 kB │ gzip: 0.19 kB
51
51
  dist/ruleInput/.export.js 0.30 kB │ gzip: 0.14 kB
52
52
  dist/api/.export.js 0.31 kB │ gzip: 0.16 kB
53
53
  dist/rule.js 0.36 kB │ gzip: 0.23 kB
54
- dist/ruleRecord.js 0.66 kB │ gzip: 0.33 kB
55
54
  dist/api/delete/deleteAllRules.js 0.73 kB │ gzip: 0.37 kB
56
- dist/.export.js 0.98 kB │ gzip: 0.31 kB
55
+ dist/ruleRecord.js 0.74 kB │ gzip: 0.35 kB
57
56
  dist/api/read/getMissingIds.js 1.02 kB │ gzip: 0.48 kB
58
57
  dist/api/read/fetchRules.js 1.07 kB │ gzip: 0.49 kB
59
58
  dist/redis/redisRulesStorage.js 1.22 kB │ gzip: 0.45 kB
60
59
  dist/api/delete/deleteRuleGroups.js 1.32 kB │ gzip: 0.61 kB
61
60
  dist/api/delete/deleteRules.js 1.36 kB │ gzip: 0.58 kB
61
+ dist/classifyBrowser.js 1.38 kB │ gzip: 0.71 kB
62
62
  dist/api/read/findRuleIds.js 1.39 kB │ gzip: 0.60 kB
63
63
  dist/api/write/rehashRules.js 1.42 kB │ gzip: 0.53 kB
64
- dist/mongoose/mongooseRuleSchema.js 1.47 kB │ gzip: 0.40 kB
64
+ dist/.export.js 1.43 kB │ gzip: 0.42 kB
65
+ dist/mongoose/mongooseRuleSchema.js 1.74 kB │ gzip: 0.43 kB
65
66
  dist/redis/redisClient.js 1.78 kB │ gzip: 0.68 kB
66
67
  dist/ruleInput/ruleInput.js 1.80 kB │ gzip: 0.63 kB
67
68
  dist/ruleInput/policyInput.js 1.84 kB │ gzip: 0.73 kB
68
69
  dist/classifyOs.js 1.98 kB │ gzip: 1.03 kB
69
- dist/ruleInput/userScopeInput.js 2.09 kB │ gzip: 0.67 kB
70
70
  dist/api/rulesApiClient.js 2.12 kB │ gzip: 0.62 kB
71
71
  dist/matchedRule.js 2.22 kB │ gzip: 1.00 kB
72
+ dist/ruleInput/userScopeInput.js 2.31 kB │ gzip: 0.70 kB
72
73
  dist/redis/reader/redisAggregate.js 2.35 kB │ gzip: 0.99 kB
73
74
  dist/transformRule.js 2.40 kB │ gzip: 0.94 kB
74
- dist/redis/redisRuleIndex.js 2.51 kB │ gzip: 0.74 kB
75
75
  dist/api/write/insertRules.js 2.87 kB │ gzip: 1.03 kB
76
+ dist/redis/redisRuleIndex.js 2.87 kB │ gzip: 0.77 kB
76
77
  dist/redis/redisRulesWriter.js 3.03 kB │ gzip: 1.07 kB
77
78
  dist/api/ruleApiRoutes.js 3.68 kB │ gzip: 0.99 kB
78
79
  dist/redis/reader/redisRulesQuery.js 4.53 kB │ gzip: 1.54 kB
79
- dist/redis/reader/redisRulesSplitQuery.js 4.96 kB │ gzip: 2.13 kB
80
- dist/redis/reader/redisRulesReader.js 10.27 kB │ gzip: 2.70 kB
80
+ dist/redis/reader/redisRulesSplitQuery.js 4.98 kB │ gzip: 2.14 kB
81
+ dist/headerMatch.js 5.51 kB │ gzip: 2.22 kB
82
+ dist/redis/reader/redisRulesReader.js 10.44 kB │ gzip: 2.74 kB
81
83
 
82
- ✓ built in 85ms
84
+ ✓ built in 149ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,96 @@
1
1
  # @prosopo/user-access-policy
2
2
 
3
+ ## 3.13.0
4
+ ### Minor Changes
5
+
6
+ - e22d5fb: Add an arbitrary-header match dimension to user access policies.
7
+
8
+ A `Block` or `Restrict` rule can now target a named request header with an `equals`, `contains`, `notEquals`, `notContains`, `notEqualsAny` or `notContainsAny` operator. The negated operators back the portal's allow-list mode — block unless the header matches; the `*Any` pair carries a list of accepted values in `headerValue` so an allow-list over several values of the same header fires only when the header matches none of them (separate single-value rules would each fire on the other's value and block everything). Because substring `contains` and per-rule operators can't be expressed as a Redis TAG query — and an allow-list rule must still fire on a request that omits the header — the header condition is carried on the rule as `headerName`/`headerValue`/`headerOperator` and evaluated in code against the raw request headers, while an indexed `headerMatch` sentinel makes every header rule a matching candidate for every request. Header rules contribute one point to rule specificity ranking, mirroring the other scalar dimensions.
9
+
10
+ The raw request headers are a **required** argument of `getPrioritisedAccessRule` / `CaptchaManager.getPrioritisedAccessPolicies`, with no default. A negated header operator treats a missing header as "does not match", so a lookup that quietly ran with an empty header map would fire every allow-list rule on every request. Every lookup now passes them: the request-time block middleware, the verify-path hard-block check, the `/frictionless` policy and dedup lookups, and the image / PoW / puzzle challenge endpoints.
11
+
12
+ `getPrioritisedAccessRule` now caches only the candidate fetch and ranks per request, because the header verdict depends on data that is not part of the cache key. This also closes a latent gap where `os` was never part of `hardBlockCacheKey`, so a cached ranked list could serve one operating system's verdict to a request from another.
13
+
14
+ ### Patch Changes
15
+
16
+ - 6f57ee9: chore(deps): bump yargs from 17.7.2 to 18.1.0
17
+ - 6f57ee9: chore(deps): bump the npm-minor-and-patch group across 1 directory with 3 updates
18
+ - Updated dependencies [6f57ee9]
19
+ - Updated dependencies [d288371]
20
+ - @prosopo/types@5.7.0
21
+ - @prosopo/util@3.3.9
22
+ - @prosopo/api@4.1.6
23
+ - @prosopo/api-route@2.6.58
24
+ - @prosopo/common@3.1.54
25
+ - @prosopo/logger@2.0.9
26
+ - @prosopo/redis-client@1.0.35
27
+
28
+ ## 3.12.33
29
+ ### Patch Changes
30
+
31
+ - 7fd6eb2: Add a `browser` match dimension to access rules.
32
+
33
+ Rules can now be scoped to the browser classified server-side from the request
34
+ User-Agent (`chrome`, `safari`, `firefox`, `edge`, `opera`, `samsung_internet`,
35
+ `wechat`, `facebook`, `instagram`, `ie`, `unknown`), mirroring the existing `os`
36
+ dimension. The classifier duplicates `@prosopo/decision-machines`' `uaClassify`
37
+ because the provider request path cannot depend on that package.
38
+
39
+ Also fixes `os` never getting its own probe on the Redis split-query hot path:
40
+ `SCALAR_USER_SCOPE_FIELDS` in `redisRulesSplitQuery` had not been updated when
41
+ the OS dimension landed, so an OS-only rule was reachable only via the
42
+ `no-user-scope` fall-through, competing for that probe's candidate budget
43
+ against genuine client-wide blocks.
44
+
45
+ The Redis index gains a `browser` TAG field. `createRedisIndex` hashes the index
46
+ definition and drops/recreates when the hash changes, so this needs no manual
47
+ migration — verified locally: the index came back carrying `browser` and the
48
+ stored hash moved to match.
49
+ - 89dd38a: chore(deps): batch the outstanding dependabot bumps into one upgrade
50
+
51
+ Rolls up dependabot PRs #3112, #3127-#3134 and #3159. Majors: `mongoose`
52
+ 8 -> 9, `bson` 6 -> 7, `@noble/curves` 1 -> 2, `@polkadot/util-crypto`
53
+ 13 -> 14, `@typegoose/auto-increment` 4 -> 5, `@babel/preset-env` 7 -> 8,
54
+ `@types/jsdom` 21 -> 30, `@types/bcrypt` 5 -> 6, `@actions/github` 6 -> 9,
55
+ `testcontainers` 11 -> 12. The rest are minor/patch.
56
+
57
+ Code changes the majors forced:
58
+ - `@noble/curves` v2 requires `.js` specifiers and renamed the point API,
59
+ so `secp256k1.ProjectivePoint.fromHex(...).toRawBytes()` becomes
60
+ `secp256k1.Point.fromBytes(...).toBytes()`, `RistrettoPoint` becomes
61
+ `ristretto255.Point`, and `abstract/utils` moves to `utils.js`.
62
+ - mongoose 9 drops `RootFilterQuery` (now `QueryFilter`), no longer sets
63
+ `background: true` on schema indexes by default, and no longer declares
64
+ `id` on `Document`, which un-hid a mismatch between
65
+ `updateDappUserCommitment`'s `Hash` parameter and the `string` `id` it
66
+ filters on.
67
+ - mongoose 9 rejects an aggregation-pipeline update (an array) unless the
68
+ call passes `updatePipeline: true`, so the six pipeline writes in
69
+ `ProviderDatabase` now opt in explicitly.
70
+ - mongoose 9's `castUpdate` throws on a `$setOnInsert` key inside `$set`.
71
+ `storeUserImageCaptchaSolution` passed its record straight in as the
72
+ update, and mongoose's `moveImmutableProperties` mutates that object on
73
+ an upsert -- adding the very `$setOnInsert` key the record then carried
74
+ into `CentralDbStreamer.streamImageRecord`. Image records stopped
75
+ reaching the central DB (the streamer is fire-and-forget, so it only
76
+ logged) and signup verification returned 500. The update is now an
77
+ explicit `$set` over a shallow copy.
78
+ - `@prosopo/database` moves from mongodb 6.20 to 7.5 to match the driver
79
+ mongoose 9 pulls, so bson 7 is the only copy resolvable in the package.
80
+ - `vitest`/`@vitest/coverage-v8` go to 4.1.11 alongside dependabot's
81
+ `@vitest/spy` bump; leaving them at 4.1.10 installed a second copy of
82
+ `@vitest/spy` and broke type inference in the provider test utils.
83
+ - Updated dependencies [89dd38a]
84
+ - Updated dependencies [80f73c1]
85
+ - Updated dependencies [8a670d3]
86
+ - @prosopo/api@4.1.5
87
+ - @prosopo/api-route@2.6.57
88
+ - @prosopo/common@3.1.53
89
+ - @prosopo/logger@2.0.8
90
+ - @prosopo/redis-client@1.0.34
91
+ - @prosopo/types@5.6.0
92
+ - @prosopo/util@3.3.8
93
+
3
94
  ## 3.12.32
4
95
  ### Patch Changes
5
96
 
package/dist/.export.d.ts CHANGED
@@ -3,6 +3,8 @@ export { makeAccessRuleHash, transformAccessRuleRecordIntoRule, transformAccessR
3
3
  export { type AccessRule, type AccessPolicy, type PolicyScope, type UserScope, AccessPolicyType, } from "./rule.js";
4
4
  export { describeMatchedRule } from "./matchedRule.js";
5
5
  export { classifyOs, OS_NAMES, type OsName } from "./classifyOs.js";
6
+ export { classifyBrowser, BROWSER_NAMES, type BrowserName, } from "./classifyBrowser.js";
7
+ export { HEADER_OPERATORS, type HeaderOperator, HEADER_RULE_MARKER, isHeaderOperator, evaluateHeaderCondition, accessRuleHeaderMatches, encodeHeaderValueList, decodeHeaderValueList, } from "./headerMatch.js";
6
8
  export { type AccessRulesFilter, type AccessRulesStorage, type AccessRuleEntry, FilterScopeMatch, } from "./rulesStorage.js";
7
9
  export { type AccessRuleRecord, type UserScopeRecord, type UserScopeRecordField, userScopeRecordFields, getUserScopeRecordFromAccessRuleRecord, } from "./ruleRecord.js";
8
10
  //# sourceMappingURL=.export.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":".export.d.ts","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":".export.d.ts","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EACN,eAAe,EACf,aAAa,EACb,KAAK,WAAW,GAChB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,gBAAgB,EAChB,KAAK,cAAc,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
package/dist/.export.js CHANGED
@@ -8,4 +8,6 @@ import { makeAccessRuleHash, transformAccessRuleIntoRecord, transformAccessRuleR
8
8
  import { getUserScopeRecordFromAccessRuleRecord, userScopeRecordFields } from "./ruleRecord.js";
9
9
  import { describeMatchedRule } from "./matchedRule.js";
10
10
  import { OS_NAMES, classifyOs } from "./classifyOs.js";
11
- export { AccessPolicyType, FilterScopeMatch, OS_NAMES, accessPolicyInput, accessRuleInput, classifyOs, describeMatchedRule, getUserScopeRecordFromAccessRuleRecord, makeAccessRuleHash, policyScopeInput, transformAccessRuleIntoRecord, transformAccessRuleRecordIntoRule, userScopeInput, userScopeRecordFields };
11
+ import { BROWSER_NAMES, classifyBrowser } from "./classifyBrowser.js";
12
+ import { HEADER_OPERATORS, HEADER_RULE_MARKER, accessRuleHeaderMatches, decodeHeaderValueList, encodeHeaderValueList, evaluateHeaderCondition, isHeaderOperator } from "./headerMatch.js";
13
+ export { AccessPolicyType, BROWSER_NAMES, FilterScopeMatch, HEADER_OPERATORS, HEADER_RULE_MARKER, OS_NAMES, accessPolicyInput, accessRuleHeaderMatches, accessRuleInput, classifyBrowser, classifyOs, decodeHeaderValueList, describeMatchedRule, encodeHeaderValueList, evaluateHeaderCondition, getUserScopeRecordFromAccessRuleRecord, isHeaderOperator, makeAccessRuleHash, policyScopeInput, transformAccessRuleIntoRecord, transformAccessRuleRecordIntoRule, userScopeInput, userScopeRecordFields };
@@ -1 +1 @@
1
- {"version":3,"file":".export.js","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAKN,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAIN,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAIN,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":".export.js","sourceRoot":"","sources":["../src/.export.ts"],"names":[],"mappings":"AAcA,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EACN,kBAAkB,EAClB,iCAAiC,EACjC,6BAA6B,GAC7B,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAKN,gBAAgB,GAChB,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAe,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EACN,eAAe,EACf,aAAa,GAEb,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACN,gBAAgB,EAEhB,kBAAkB,EAClB,gBAAgB,EAChB,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,GACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAIN,gBAAgB,GAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAIN,qBAAqB,EACrB,sCAAsC,GACtC,MAAM,iBAAiB,CAAC"}
@@ -8,14 +8,25 @@ const require_transformRule = require("./transformRule.cjs");
8
8
  const require_ruleRecord = require("./ruleRecord.cjs");
9
9
  const require_matchedRule = require("./matchedRule.cjs");
10
10
  const require_classifyOs = require("./classifyOs.cjs");
11
+ const require_classifyBrowser = require("./classifyBrowser.cjs");
12
+ const require_headerMatch = require("./headerMatch.cjs");
11
13
  exports.AccessPolicyType = require_rule.AccessPolicyType;
14
+ exports.BROWSER_NAMES = require_classifyBrowser.BROWSER_NAMES;
12
15
  exports.FilterScopeMatch = require_rulesStorage.FilterScopeMatch;
16
+ exports.HEADER_OPERATORS = require_headerMatch.HEADER_OPERATORS;
17
+ exports.HEADER_RULE_MARKER = require_headerMatch.HEADER_RULE_MARKER;
13
18
  exports.OS_NAMES = require_classifyOs.OS_NAMES;
14
19
  exports.accessPolicyInput = require_ruleInput_policyInput.accessPolicyInput;
20
+ exports.accessRuleHeaderMatches = require_headerMatch.accessRuleHeaderMatches;
15
21
  exports.accessRuleInput = require_ruleInput_ruleInput.accessRuleInput;
22
+ exports.classifyBrowser = require_classifyBrowser.classifyBrowser;
16
23
  exports.classifyOs = require_classifyOs.classifyOs;
24
+ exports.decodeHeaderValueList = require_headerMatch.decodeHeaderValueList;
17
25
  exports.describeMatchedRule = require_matchedRule.describeMatchedRule;
26
+ exports.encodeHeaderValueList = require_headerMatch.encodeHeaderValueList;
27
+ exports.evaluateHeaderCondition = require_headerMatch.evaluateHeaderCondition;
18
28
  exports.getUserScopeRecordFromAccessRuleRecord = require_ruleRecord.getUserScopeRecordFromAccessRuleRecord;
29
+ exports.isHeaderOperator = require_headerMatch.isHeaderOperator;
19
30
  exports.makeAccessRuleHash = require_transformRule.makeAccessRuleHash;
20
31
  exports.policyScopeInput = require_ruleInput_policyInput.policyScopeInput;
21
32
  exports.transformAccessRuleIntoRecord = require_transformRule.transformAccessRuleIntoRecord;
@@ -0,0 +1,44 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/classifyBrowser.ts
3
+ /**
4
+ * Kept in step with `@prosopo/decision-machines`' `uaClassify` — as `classifyOs`
5
+ * is — but defined here because the provider request path cannot depend on that
6
+ * package.
7
+ */
8
+ var BROWSER_NAMES = [
9
+ "chrome",
10
+ "safari",
11
+ "firefox",
12
+ "edge",
13
+ "opera",
14
+ "samsung_internet",
15
+ "wechat",
16
+ "facebook",
17
+ "instagram",
18
+ "ie",
19
+ "unknown"
20
+ ];
21
+ /**
22
+ * Branch order is load-bearing: every browser below Chrome also carries a
23
+ * `chrome/` token, and Safari's signature appears in nearly every WebKit UA.
24
+ *
25
+ * Reads the User-Agent rather than the `sec-ch-ua` client hint because a client
26
+ * can simply omit client hints, but stripping the UA breaks far more.
27
+ */
28
+ var classifyBrowser = (userAgent) => {
29
+ const ua = (userAgent || "").toLowerCase();
30
+ if (/\bedg(?:e|a|ios)?\//.test(ua)) return "edge";
31
+ if (/samsungbrowser/.test(ua)) return "samsung_internet";
32
+ if (/\bopr\/|opera/.test(ua)) return "opera";
33
+ if (/micromessenger/.test(ua)) return "wechat";
34
+ if (/fban|fbav|fbios/.test(ua)) return "facebook";
35
+ if (/instagram/.test(ua)) return "instagram";
36
+ if (/firefox|fxios/.test(ua)) return "firefox";
37
+ if (/msie|trident/.test(ua)) return "ie";
38
+ if (/\bchrome\/|\bcrios\//.test(ua)) return "chrome";
39
+ if (/safari\//.test(ua)) return "safari";
40
+ return "unknown";
41
+ };
42
+ //#endregion
43
+ exports.BROWSER_NAMES = BROWSER_NAMES;
44
+ exports.classifyBrowser = classifyBrowser;
@@ -0,0 +1,122 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region src/headerMatch.ts
3
+ /**
4
+ * Operators for an access rule's arbitrary-header condition.
5
+ *
6
+ * `equals` / `contains` are the deny-list operators (block when the request's
7
+ * header matches). `notEquals` / `notContains` are their allow-list negations
8
+ * (block when the request's header does NOT match — including when the header
9
+ * is absent), so an allow-list card that says "only let requests with header X
10
+ * through" desugars to `notEquals` / `notContains` Block rules, exactly the way
11
+ * the OS allow-list desugars to Block rules on the complement.
12
+ *
13
+ * `notEqualsAny` / `notContainsAny` are the multi-value form of those two: the
14
+ * rule's `headerValue` holds a list (see `encodeHeaderValueList`) and the rule
15
+ * blocks unless the request's header matches *one of* the listed values. They
16
+ * exist because an allow-list over several values of the SAME header cannot be
17
+ * expressed as several single-value rules — each rule fires independently, so
18
+ * `notEquals ios` + `notEquals android` blocks both an iOS and an Android
19
+ * request. One rule holding both values is the only way to get the "any of"
20
+ * semantics the allow-list needs.
21
+ */
22
+ var HEADER_OPERATORS = [
23
+ "equals",
24
+ "contains",
25
+ "notEquals",
26
+ "notContains",
27
+ "notEqualsAny",
28
+ "notContainsAny"
29
+ ];
30
+ var isHeaderOperator = (value) => value !== void 0 && HEADER_OPERATORS.includes(value);
31
+ /**
32
+ * Sentinel value carried both on every request's user scope (see
33
+ * `getRequestUserScope`) and on every header-restriction rule. Header rules
34
+ * match on data (`headerName` / `headerValue` / `headerOperator`) that Redis
35
+ * cannot evaluate — substring `contains` and per-rule operators aren't
36
+ * expressible as a TAG query, and an allow-list rule must still fire on a
37
+ * request that OMITS the target header, so candidate selection can't key off
38
+ * header presence. Redis returns header rules via the reader's
39
+ * `no-user-scope` fall-through probe (they constrain none of the indexed
40
+ * scope dimensions); this marker is what keeps them candidates through the
41
+ * JS-side `ruleApplies` scalar check, which would otherwise drop a rule
42
+ * carrying a scope field the request lacks. The concrete condition is then
43
+ * checked by `accessRuleHeaderMatches`.
44
+ */
45
+ var HEADER_RULE_MARKER = "1";
46
+ /**
47
+ * Encode the value list carried by a multi-value operator (`notEqualsAny` /
48
+ * `notContainsAny`) into the single `headerValue` string an access rule can
49
+ * store. JSON rather than a delimiter because header values are arbitrary
50
+ * text — any separator we picked could legitimately occur inside a value.
51
+ */
52
+ var encodeHeaderValueList = (values) => JSON.stringify(values);
53
+ /**
54
+ * Inverse of {@link encodeHeaderValueList}, tolerant of rules written by hand
55
+ * (e.g. via the access-policy CLI) that carry a single literal value instead of
56
+ * an encoded list.
57
+ *
58
+ * Returns `undefined` for a value that looks list-encoded but isn't usable — an
59
+ * empty list, a non-array, non-string members, or malformed JSON. Callers treat
60
+ * that as a malformed rule and decline to fire it, so a garbled list can't
61
+ * silently block all traffic.
62
+ */
63
+ var decodeHeaderValueList = (value) => {
64
+ if (!value.trimStart().startsWith("[")) return [value];
65
+ let parsed;
66
+ try {
67
+ parsed = JSON.parse(value);
68
+ } catch {
69
+ return;
70
+ }
71
+ if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every((entry) => typeof entry === "string")) return;
72
+ return parsed;
73
+ };
74
+ /**
75
+ * Evaluate a single header condition against a request's headers. Returns
76
+ * `true` when the condition is satisfied — i.e. when a Block rule carrying it
77
+ * should fire for this request.
78
+ *
79
+ * `headers` is expected to be keyed by lower-cased header name (HTTP header
80
+ * names are case-insensitive); `headerName` is lower-cased here defensively.
81
+ * The negated operators treat an absent header as "not matching the value",
82
+ * which is what makes an allow-list block a request that drops the header.
83
+ */
84
+ var evaluateHeaderCondition = (headerName, operator, value, headers) => {
85
+ const actual = headers[headerName.toLowerCase()];
86
+ switch (operator) {
87
+ case "equals": return actual !== void 0 && actual === value;
88
+ case "contains": return actual?.includes(value) ?? false;
89
+ case "notEquals": return actual === void 0 || actual !== value;
90
+ case "notContains": return actual === void 0 || !actual.includes(value);
91
+ case "notEqualsAny": {
92
+ const values = decodeHeaderValueList(value);
93
+ if (values === void 0) return false;
94
+ return actual === void 0 || !values.includes(actual);
95
+ }
96
+ case "notContainsAny": {
97
+ const values = decodeHeaderValueList(value);
98
+ if (values === void 0) return false;
99
+ return actual === void 0 || !values.some((candidate) => actual.includes(candidate));
100
+ }
101
+ }
102
+ };
103
+ /**
104
+ * Whether a rule's header condition (if any) is satisfied by the request's
105
+ * headers. A rule with no `headerName` carries no header constraint, so it is
106
+ * treated as satisfied and the rule's other dimensions decide the match. A
107
+ * rule with a `headerName` but an unrecognised operator is malformed and never
108
+ * matches (fail-safe: a garbled rule must not silently block traffic).
109
+ */
110
+ var accessRuleHeaderMatches = (rule, headers) => {
111
+ if (rule.headerName === void 0) return true;
112
+ if (!isHeaderOperator(rule.headerOperator)) return false;
113
+ return evaluateHeaderCondition(rule.headerName, rule.headerOperator, rule.headerValue ?? "", headers);
114
+ };
115
+ //#endregion
116
+ exports.HEADER_OPERATORS = HEADER_OPERATORS;
117
+ exports.HEADER_RULE_MARKER = HEADER_RULE_MARKER;
118
+ exports.accessRuleHeaderMatches = accessRuleHeaderMatches;
119
+ exports.decodeHeaderValueList = decodeHeaderValueList;
120
+ exports.encodeHeaderValueList = encodeHeaderValueList;
121
+ exports.evaluateHeaderCondition = evaluateHeaderCondition;
122
+ exports.isHeaderOperator = isHeaderOperator;
@@ -36,6 +36,26 @@ var userAttributesSchema = {
36
36
  os: {
37
37
  type: String,
38
38
  required: false
39
+ },
40
+ browser: {
41
+ type: String,
42
+ required: false
43
+ },
44
+ headerMatch: {
45
+ type: String,
46
+ required: false
47
+ },
48
+ headerName: {
49
+ type: String,
50
+ required: false
51
+ },
52
+ headerValue: {
53
+ type: String,
54
+ required: false
55
+ },
56
+ headerOperator: {
57
+ type: String,
58
+ required: false
39
59
  }
40
60
  };
41
61
  var userIpSchema = {
@@ -25,6 +25,8 @@ var SPECIFICITY_EXPR = [
25
25
  "exists(@countryCode)",
26
26
  "exists(@asn)",
27
27
  "exists(@os)",
28
+ "exists(@browser)",
29
+ "exists(@headerMatch)",
28
30
  "exists(@numericIp)",
29
31
  "exists(@numericIpMaskMin)"
30
32
  ].join(" + ");
@@ -52,6 +54,11 @@ var RULE_LOAD_FIELDS = [
52
54
  "@countryCode",
53
55
  "@asn",
54
56
  "@os",
57
+ "@browser",
58
+ "@headerMatch",
59
+ "@headerName",
60
+ "@headerValue",
61
+ "@headerOperator",
55
62
  "@numericIp",
56
63
  "@numericIpMaskMin",
57
64
  "@numericIpMaskMax"
@@ -68,6 +75,7 @@ var readerSpecificity = (rule) => {
68
75
  if (rule.countryCode !== void 0) score++;
69
76
  if (rule.asn !== void 0) score++;
70
77
  if (rule.os !== void 0) score++;
78
+ if (rule.browser !== void 0) score++;
71
79
  if (rule.numericIp !== void 0) score++;
72
80
  if (rule.numericIpMaskMin !== void 0) score++;
73
81
  return score;
@@ -13,7 +13,9 @@ var SCALAR_USER_SCOPE_FIELDS = [
13
13
  "headHash",
14
14
  "coords",
15
15
  "countryCode",
16
- "asn"
16
+ "asn",
17
+ "os",
18
+ "browser"
17
19
  ];
18
20
  var ALL_USER_SCOPE_FIELDS = [
19
21
  ...SCALAR_USER_SCOPE_FIELDS,
@@ -54,6 +54,26 @@ var userAttributesRedisSchema = {
54
54
  os: {
55
55
  type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
56
56
  INDEXMISSING: true
57
+ },
58
+ browser: {
59
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
60
+ INDEXMISSING: true
61
+ },
62
+ headerMatch: {
63
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
64
+ INDEXMISSING: true
65
+ },
66
+ headerName: {
67
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
68
+ INDEXMISSING: true
69
+ },
70
+ headerValue: {
71
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
72
+ INDEXMISSING: true
73
+ },
74
+ headerOperator: {
75
+ type: _redis_search.SCHEMA_FIELD_TYPE.TAG,
76
+ INDEXMISSING: true
57
77
  }
58
78
  };
59
79
  var userScopeRedisSchema = {
@@ -15,7 +15,12 @@ var userAttributesSchema = zod.z.object({
15
15
  coords: zod.z.coerce.string().optional(),
16
16
  countryCode: zod.z.coerce.string().optional(),
17
17
  asn: zod.z.coerce.number().int().nonnegative().optional(),
18
- os: zod.z.coerce.string().optional()
18
+ os: zod.z.coerce.string().optional(),
19
+ browser: zod.z.coerce.string().optional(),
20
+ headerMatch: zod.z.coerce.string().optional(),
21
+ headerName: zod.z.coerce.string().optional(),
22
+ headerValue: zod.z.coerce.string().optional(),
23
+ headerOperator: zod.z.coerce.string().optional()
19
24
  });
20
25
  var userAttributesInput = zod.z.object({
21
26
  ...userAttributesSchema.shape,
@@ -9,7 +9,12 @@ var userAttributesRecordFields = [
9
9
  "coords",
10
10
  "countryCode",
11
11
  "asn",
12
- "os"
12
+ "os",
13
+ "browser",
14
+ "headerMatch",
15
+ "headerName",
16
+ "headerValue",
17
+ "headerOperator"
13
18
  ];
14
19
  var userIpRecordFields = ["ip", "ipMask"];
15
20
  var userScopeRecordFields = [...userAttributesRecordFields, ...userIpRecordFields];
@@ -0,0 +1,4 @@
1
+ export declare const BROWSER_NAMES: readonly ["chrome", "safari", "firefox", "edge", "opera", "samsung_internet", "wechat", "facebook", "instagram", "ie", "unknown"];
2
+ export type BrowserName = (typeof BROWSER_NAMES)[number];
3
+ export declare const classifyBrowser: (userAgent: string | undefined) => BrowserName;
4
+ //# sourceMappingURL=classifyBrowser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifyBrowser.d.ts","sourceRoot":"","sources":["../src/classifyBrowser.ts"],"names":[],"mappings":"AAmBA,eAAO,MAAM,aAAa,mIAYhB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AASzD,eAAO,MAAM,eAAe,cAAe,MAAM,GAAG,SAAS,KAAG,WAkC/D,CAAC"}