@prosopo/user-access-policy 3.5.19 → 3.5.27

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 (290) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/clover.xml +786 -0
  5. package/coverage/coverage-final.json +15 -0
  6. package/coverage/favicon.png +0 -0
  7. package/coverage/index.html +146 -0
  8. package/coverage/prettify.css +1 -0
  9. package/coverage/prettify.js +2 -0
  10. package/coverage/sort-arrow-sprite.png +0 -0
  11. package/coverage/sorter.js +210 -0
  12. package/coverage/src/accessPolicy.ts.html +457 -0
  13. package/coverage/src/accessPolicyResolver.ts.html +211 -0
  14. package/coverage/src/accessRules.ts.html +265 -0
  15. package/coverage/src/api/accessRuleApiRoutes.ts.html +379 -0
  16. package/coverage/src/api/accessRulesApiClient.ts.html +274 -0
  17. package/coverage/src/api/deleteAllRulesEndpoint.ts.html +229 -0
  18. package/coverage/src/api/deleteRulesEndpoint.ts.html +301 -0
  19. package/coverage/src/api/index.html +176 -0
  20. package/coverage/src/api/insertRulesEndpoint.ts.html +436 -0
  21. package/coverage/src/index.html +176 -0
  22. package/coverage/src/index.ts.html +277 -0
  23. package/coverage/src/redis/index.html +161 -0
  24. package/coverage/src/redis/redisRulesIndex.ts.html +769 -0
  25. package/coverage/src/redis/redisRulesReader.ts.html +652 -0
  26. package/coverage/src/redis/redisRulesStorage.ts.html +229 -0
  27. package/coverage/src/redis/redisRulesWriter.ts.html +424 -0
  28. package/coverage/src/util.ts.html +136 -0
  29. package/dist/.export.d.ts +6 -0
  30. package/dist/.export.d.ts.map +1 -0
  31. package/dist/.export.js +21 -0
  32. package/dist/.export.js.map +1 -0
  33. package/dist/accessPolicy.d.ts +169 -0
  34. package/dist/accessPolicy.d.ts.map +1 -0
  35. package/dist/accessPolicy.js.map +1 -0
  36. package/dist/accessPolicyResolver.d.ts +110 -0
  37. package/dist/accessPolicyResolver.d.ts.map +1 -0
  38. package/dist/accessPolicyResolver.js.map +1 -0
  39. package/dist/accessRules.d.ts +16 -0
  40. package/dist/accessRules.d.ts.map +1 -0
  41. package/dist/accessRules.js.map +1 -0
  42. package/dist/api/.export.d.ts +7 -0
  43. package/dist/api/.export.d.ts.map +1 -0
  44. package/dist/api/.export.js +11 -0
  45. package/dist/api/.export.js.map +1 -0
  46. package/dist/api/accessRuleApiRoutes.d.ts +27 -0
  47. package/dist/api/accessRuleApiRoutes.d.ts.map +1 -0
  48. package/dist/api/accessRuleApiRoutes.js.map +1 -0
  49. package/dist/api/accessRulesApiClient.d.ts +10 -0
  50. package/dist/api/accessRulesApiClient.d.ts.map +1 -0
  51. package/dist/api/accessRulesApiClient.js.map +1 -0
  52. package/dist/api/delete/.export.d.ts +2 -0
  53. package/dist/api/delete/.export.d.ts.map +1 -0
  54. package/dist/api/delete/.export.js +1 -0
  55. package/dist/api/delete/.export.js.map +1 -0
  56. package/dist/api/delete/deleteAllRules.d.ts +11 -0
  57. package/dist/api/delete/deleteAllRules.d.ts.map +1 -0
  58. package/dist/api/delete/deleteAllRules.js +25 -0
  59. package/dist/api/delete/deleteAllRules.js.map +1 -0
  60. package/dist/api/delete/deleteRuleGroups.d.ts +19 -0
  61. package/dist/api/delete/deleteRuleGroups.d.ts.map +1 -0
  62. package/dist/api/delete/deleteRuleGroups.js +52 -0
  63. package/dist/api/delete/deleteRuleGroups.js.map +1 -0
  64. package/dist/api/delete/deleteRules.d.ts +15 -0
  65. package/dist/api/delete/deleteRules.d.ts.map +1 -0
  66. package/dist/api/delete/deleteRules.js +43 -0
  67. package/dist/api/delete/deleteRules.js.map +1 -0
  68. package/dist/api/deleteAllRulesEndpoint.d.ts +12 -0
  69. package/dist/api/deleteAllRulesEndpoint.d.ts.map +1 -0
  70. package/dist/api/deleteAllRulesEndpoint.js.map +1 -0
  71. package/dist/api/deleteRulesEndpoint.d.ts +116 -0
  72. package/dist/api/deleteRulesEndpoint.d.ts.map +1 -0
  73. package/dist/api/deleteRulesEndpoint.js.map +1 -0
  74. package/dist/api/insertRulesEndpoint.d.ts +22 -0
  75. package/dist/api/insertRulesEndpoint.d.ts.map +1 -0
  76. package/dist/api/insertRulesEndpoint.js.map +1 -0
  77. package/dist/api/read/.export.d.ts +4 -0
  78. package/dist/api/read/.export.d.ts.map +1 -0
  79. package/dist/api/read/.export.js +1 -0
  80. package/dist/api/read/.export.js.map +1 -0
  81. package/dist/api/read/fetchRules.d.ts +53 -0
  82. package/dist/api/read/fetchRules.d.ts.map +1 -0
  83. package/dist/api/read/fetchRules.js +43 -0
  84. package/dist/api/read/fetchRules.js.map +1 -0
  85. package/dist/api/read/findRuleIds.d.ts +28 -0
  86. package/dist/api/read/findRuleIds.d.ts.map +1 -0
  87. package/dist/api/read/findRuleIds.js +50 -0
  88. package/dist/api/read/findRuleIds.js.map +1 -0
  89. package/dist/api/read/getMissingIds.d.ts +28 -0
  90. package/dist/api/read/getMissingIds.d.ts.map +1 -0
  91. package/dist/api/read/getMissingIds.js +41 -0
  92. package/dist/api/read/getMissingIds.js.map +1 -0
  93. package/dist/api/ruleApiRoutes.d.ts +43 -0
  94. package/dist/api/ruleApiRoutes.d.ts.map +1 -0
  95. package/dist/api/ruleApiRoutes.js +131 -0
  96. package/dist/api/ruleApiRoutes.js.map +1 -0
  97. package/dist/api/rulesApiClient.d.ts +20 -0
  98. package/dist/api/rulesApiClient.d.ts.map +1 -0
  99. package/dist/api/rulesApiClient.js +93 -0
  100. package/dist/api/rulesApiClient.js.map +1 -0
  101. package/dist/api/write/.export.d.ts +2 -0
  102. package/dist/api/write/.export.d.ts.map +1 -0
  103. package/dist/api/write/.export.js +1 -0
  104. package/dist/api/write/.export.js.map +1 -0
  105. package/dist/api/write/insertRules.d.ts +29 -0
  106. package/dist/api/write/insertRules.d.ts.map +1 -0
  107. package/dist/api/write/insertRules.js +102 -0
  108. package/dist/api/write/insertRules.js.map +1 -0
  109. package/dist/api/write/rehashRules.d.ts +11 -0
  110. package/dist/api/write/rehashRules.d.ts.map +1 -0
  111. package/dist/api/write/rehashRules.js +57 -0
  112. package/dist/api/write/rehashRules.js.map +1 -0
  113. package/dist/cjs/.export.cjs +21 -0
  114. package/dist/cjs/api/.export.cjs +11 -0
  115. package/dist/cjs/api/delete/.export.cjs +1 -0
  116. package/dist/cjs/api/delete/deleteAllRules.cjs +25 -0
  117. package/dist/cjs/api/delete/deleteRuleGroups.cjs +52 -0
  118. package/dist/cjs/api/delete/deleteRules.cjs +43 -0
  119. package/dist/cjs/api/read/.export.cjs +1 -0
  120. package/dist/cjs/api/read/fetchRules.cjs +43 -0
  121. package/dist/cjs/api/read/findRuleIds.cjs +50 -0
  122. package/dist/cjs/api/read/getMissingIds.cjs +41 -0
  123. package/dist/cjs/api/ruleApiRoutes.cjs +131 -0
  124. package/dist/cjs/api/rulesApiClient.cjs +93 -0
  125. package/dist/cjs/api/write/.export.cjs +1 -0
  126. package/dist/cjs/api/write/insertRules.cjs +102 -0
  127. package/dist/cjs/api/write/rehashRules.cjs +57 -0
  128. package/dist/cjs/index.cjs +4 -6
  129. package/dist/cjs/mongoose/.export.cjs +4 -0
  130. package/dist/cjs/mongoose/mongooseRuleSchema.cjs +36 -0
  131. package/dist/cjs/redis/.export.cjs +6 -0
  132. package/dist/cjs/redis/reader/redisAggregate.cjs +60 -0
  133. package/dist/cjs/redis/reader/redisRulesQuery.cjs +99 -0
  134. package/dist/cjs/redis/reader/redisRulesReader.cjs +230 -0
  135. package/dist/cjs/redis/{redisRulesReader.cjs → redisAccessRules.cjs} +38 -28
  136. package/dist/cjs/redis/{redisRulesIndex.cjs → redisAccessRulesIndex.cjs} +45 -12
  137. package/dist/cjs/redis/redisClient.cjs +67 -0
  138. package/dist/cjs/redis/redisIndex.cjs +22 -0
  139. package/dist/cjs/redis/redisRuleIndex.cjs +50 -0
  140. package/dist/cjs/redis/redisRulesStorage.cjs +22 -9
  141. package/dist/cjs/redis/redisRulesWriter.cjs +91 -64
  142. package/dist/cjs/rule.cjs +8 -0
  143. package/dist/cjs/ruleInput/.export.cjs +9 -0
  144. package/dist/cjs/ruleInput/policyInput.cjs +25 -0
  145. package/dist/cjs/ruleInput/ruleInput.cjs +50 -0
  146. package/dist/cjs/ruleInput/userScopeInput.cjs +55 -0
  147. package/dist/cjs/ruleRecord.cjs +23 -0
  148. package/dist/cjs/rulesStorage.cjs +8 -0
  149. package/dist/cjs/transformRule.cjs +77 -0
  150. package/dist/index.d.ts +15 -0
  151. package/dist/index.d.ts.map +1 -0
  152. package/dist/index.js.map +1 -0
  153. package/dist/mongoose/.export.d.ts +2 -0
  154. package/dist/mongoose/.export.d.ts.map +1 -0
  155. package/dist/mongoose/.export.js +4 -0
  156. package/dist/mongoose/.export.js.map +1 -0
  157. package/dist/mongoose/mongooseRuleSchema.d.ts +4 -0
  158. package/dist/mongoose/mongooseRuleSchema.d.ts.map +1 -0
  159. package/dist/mongoose/mongooseRuleSchema.js +36 -0
  160. package/dist/mongoose/mongooseRuleSchema.js.map +1 -0
  161. package/dist/redis/.export.d.ts +3 -0
  162. package/dist/redis/.export.d.ts.map +1 -0
  163. package/dist/redis/.export.js +6 -0
  164. package/dist/redis/.export.js.map +1 -0
  165. package/dist/redis/reader/redisAggregate.d.ts +4 -0
  166. package/dist/redis/reader/redisAggregate.d.ts.map +1 -0
  167. package/dist/redis/reader/redisAggregate.js +60 -0
  168. package/dist/redis/reader/redisAggregate.js.map +1 -0
  169. package/dist/redis/reader/redisRulesQuery.d.ts +4 -0
  170. package/dist/redis/reader/redisRulesQuery.d.ts.map +1 -0
  171. package/dist/redis/reader/redisRulesQuery.js +99 -0
  172. package/dist/redis/reader/redisRulesQuery.js.map +1 -0
  173. package/dist/redis/reader/redisRulesReader.d.ts +26 -0
  174. package/dist/redis/reader/redisRulesReader.d.ts.map +1 -0
  175. package/dist/redis/reader/redisRulesReader.js +213 -0
  176. package/dist/redis/reader/redisRulesReader.js.map +1 -0
  177. package/dist/redis/redisAccessRules.d.ts +7 -0
  178. package/dist/redis/redisAccessRules.d.ts.map +1 -0
  179. package/dist/redis/redisAccessRules.js +119 -0
  180. package/dist/redis/redisAccessRules.js.map +1 -0
  181. package/dist/redis/redisAccessRulesIndex.d.ts +13 -0
  182. package/dist/redis/redisAccessRulesIndex.d.ts.map +1 -0
  183. package/dist/redis/redisAccessRulesIndex.js +140 -0
  184. package/dist/redis/redisAccessRulesIndex.js.map +1 -0
  185. package/dist/redis/redisClient.d.ts +11 -0
  186. package/dist/redis/redisClient.d.ts.map +1 -0
  187. package/dist/redis/redisClient.js +67 -0
  188. package/dist/redis/redisClient.js.map +1 -0
  189. package/dist/redis/redisIndex.d.ts +9 -0
  190. package/dist/redis/redisIndex.d.ts.map +1 -0
  191. package/dist/redis/redisIndex.js +23 -0
  192. package/dist/redis/redisIndex.js.map +1 -0
  193. package/dist/redis/redisRuleIndex.d.ts +13 -0
  194. package/dist/redis/redisRuleIndex.d.ts.map +1 -0
  195. package/dist/redis/redisRuleIndex.js +50 -0
  196. package/dist/redis/redisRuleIndex.js.map +1 -0
  197. package/dist/redis/redisRulesIndex.d.ts +9 -0
  198. package/dist/redis/redisRulesIndex.d.ts.map +1 -0
  199. package/dist/redis/redisRulesIndex.js.map +1 -0
  200. package/dist/redis/redisRulesReader.d.ts +6 -0
  201. package/dist/redis/redisRulesReader.d.ts.map +1 -0
  202. package/dist/redis/redisRulesReader.js.map +1 -0
  203. package/dist/redis/redisRulesStorage.d.ts +5 -0
  204. package/dist/redis/redisRulesStorage.d.ts.map +1 -0
  205. package/dist/redis/redisRulesStorage.js +23 -10
  206. package/dist/redis/redisRulesStorage.js.map +1 -0
  207. package/dist/redis/redisRulesWriter.d.ts +22 -0
  208. package/dist/redis/redisRulesWriter.d.ts.map +1 -0
  209. package/dist/redis/redisRulesWriter.js +91 -64
  210. package/dist/redis/redisRulesWriter.js.map +1 -0
  211. package/dist/rule.d.ts +34 -0
  212. package/dist/rule.d.ts.map +1 -0
  213. package/dist/rule.js +8 -0
  214. package/dist/rule.js.map +1 -0
  215. package/dist/ruleInput/.export.d.ts +4 -0
  216. package/dist/ruleInput/.export.d.ts.map +1 -0
  217. package/dist/ruleInput/.export.js +9 -0
  218. package/dist/ruleInput/.export.js.map +1 -0
  219. package/dist/ruleInput/policyInput.d.ts +38 -0
  220. package/dist/ruleInput/policyInput.d.ts.map +1 -0
  221. package/dist/ruleInput/policyInput.js +25 -0
  222. package/dist/ruleInput/policyInput.js.map +1 -0
  223. package/dist/ruleInput/ruleInput.d.ts +145 -0
  224. package/dist/ruleInput/ruleInput.d.ts.map +1 -0
  225. package/dist/ruleInput/ruleInput.js +50 -0
  226. package/dist/ruleInput/ruleInput.js.map +1 -0
  227. package/dist/ruleInput/userScopeInput.d.ts +93 -0
  228. package/dist/ruleInput/userScopeInput.d.ts.map +1 -0
  229. package/dist/ruleInput/userScopeInput.js +55 -0
  230. package/dist/ruleInput/userScopeInput.js.map +1 -0
  231. package/dist/ruleRecord.d.ts +18 -0
  232. package/dist/ruleRecord.d.ts.map +1 -0
  233. package/dist/ruleRecord.js +23 -0
  234. package/dist/ruleRecord.js.map +1 -0
  235. package/dist/rulesStorage.d.ts +30 -0
  236. package/dist/rulesStorage.d.ts.map +1 -0
  237. package/dist/rulesStorage.js +8 -0
  238. package/dist/rulesStorage.js.map +1 -0
  239. package/dist/tests/accessPolicy.test.d.ts +2 -0
  240. package/dist/tests/accessPolicy.test.d.ts.map +1 -0
  241. package/dist/tests/accessPolicy.test.js +27 -0
  242. package/dist/tests/accessPolicy.test.js.map +1 -0
  243. package/dist/tests/redis/reader/redisRulesQuery.unit.test.d.ts +2 -0
  244. package/dist/tests/redis/reader/redisRulesQuery.unit.test.d.ts.map +1 -0
  245. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js +101 -0
  246. package/dist/tests/redis/reader/redisRulesQuery.unit.test.js.map +1 -0
  247. package/dist/tests/redis/redisAccessRules.integration.test.d.ts +2 -0
  248. package/dist/tests/redis/redisAccessRules.integration.test.d.ts.map +1 -0
  249. package/dist/tests/redis/redisAccessRules.integration.test.js +586 -0
  250. package/dist/tests/redis/redisAccessRules.integration.test.js.map +1 -0
  251. package/dist/tests/redis/redisAccessRules.unit.test.d.ts +2 -0
  252. package/dist/tests/redis/redisAccessRules.unit.test.d.ts.map +1 -0
  253. package/dist/tests/redis/redisAccessRules.unit.test.js +198 -0
  254. package/dist/tests/redis/redisAccessRules.unit.test.js.map +1 -0
  255. package/dist/tests/redis/redisIndex.integration.test.d.ts +2 -0
  256. package/dist/tests/redis/redisIndex.integration.test.d.ts.map +1 -0
  257. package/dist/tests/redis/redisIndex.integration.test.js +80 -0
  258. package/dist/tests/redis/redisIndex.integration.test.js.map +1 -0
  259. package/dist/tests/redis/redisRulesIndex.unit.test.d.ts +2 -0
  260. package/dist/tests/redis/redisRulesIndex.unit.test.d.ts.map +1 -0
  261. package/dist/tests/redis/redisRulesIndex.unit.test.js +101 -0
  262. package/dist/tests/redis/redisRulesIndex.unit.test.js.map +1 -0
  263. package/dist/tests/redis/redisRulesStorage.integration.test.d.ts +2 -0
  264. package/dist/tests/redis/redisRulesStorage.integration.test.d.ts.map +1 -0
  265. package/dist/tests/redis/redisRulesStorage.integration.test.js +611 -0
  266. package/dist/tests/redis/redisRulesStorage.integration.test.js.map +1 -0
  267. package/dist/tests/redis/testRedisClient.d.ts +3 -0
  268. package/dist/tests/redis/testRedisClient.d.ts.map +1 -0
  269. package/dist/tests/redis/testRedisClient.js +8 -0
  270. package/dist/tests/redis/testRedisClient.js.map +1 -0
  271. package/dist/tests/testLogger.d.ts +4 -0
  272. package/dist/tests/testLogger.d.ts.map +1 -0
  273. package/dist/tests/testLogger.js +22 -0
  274. package/dist/tests/testLogger.js.map +1 -0
  275. package/dist/tests/transformRule.unit.test.d.ts +2 -0
  276. package/dist/tests/transformRule.unit.test.d.ts.map +1 -0
  277. package/dist/tests/transformRule.unit.test.js +188 -0
  278. package/dist/tests/transformRule.unit.test.js.map +1 -0
  279. package/dist/transformRule.d.ts +7 -0
  280. package/dist/transformRule.d.ts.map +1 -0
  281. package/dist/transformRule.js +77 -0
  282. package/dist/transformRule.js.map +1 -0
  283. package/dist/util.d.ts +2 -0
  284. package/dist/util.d.ts.map +1 -0
  285. package/dist/util.js.map +1 -0
  286. package/entries.ts +20 -0
  287. package/package.json +34 -18
  288. package/vite.cjs.config.ts +4 -1
  289. package/vite.esm.config.ts +6 -1
  290. package/dist/cjs/api/accessRulesApiClient.cjs +0 -38
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const redisRulesQuery = require("./redisRulesQuery.cjs");
5
+ const redisClient = require("../redisClient.cjs");
6
+ const redisRuleIndex = require("../redisRuleIndex.cjs");
7
+ const aggregateRedisKeys = async (client, query, logger, batchHandler) => {
8
+ const keyField = "__key";
9
+ const recordSchema = zod.z.object({
10
+ // it's a reserved name for the record key
11
+ [keyField]: zod.z.string()
12
+ });
13
+ const foundKeys = [];
14
+ const addRecordKeys = async (records) => {
15
+ const parsedRecords = redisClient.parseRedisRecords(records, recordSchema, logger);
16
+ const recordKeys = parsedRecords.map((record) => record[keyField]);
17
+ if (batchHandler) {
18
+ await batchHandler(recordKeys);
19
+ } else {
20
+ foundKeys.push(...recordKeys);
21
+ logger.debug(() => ({
22
+ msg: "Processed aggregation batch",
23
+ data: {
24
+ size: recordKeys.length
25
+ }
26
+ }));
27
+ }
28
+ };
29
+ await executeAggregation(
30
+ client,
31
+ query,
32
+ {
33
+ // #2 is a required option when the 'ismissing()' function is in the query body
34
+ DIALECT: redisRulesQuery.REDIS_QUERY_DIALECT,
35
+ COUNT: redisClient.REDIS_BATCH_SIZE,
36
+ LOAD: `@${keyField}`
37
+ },
38
+ addRecordKeys
39
+ );
40
+ return foundKeys;
41
+ };
42
+ const executeAggregation = async (client, query, aggregateOptions, handleBatch) => {
43
+ const initialReply = await client.ft.aggregateWithCursor(
44
+ redisRuleIndex.ACCESS_RULES_REDIS_INDEX_NAME,
45
+ query,
46
+ aggregateOptions
47
+ );
48
+ await handleBatch(initialReply.results);
49
+ let cursor = initialReply.cursor;
50
+ while (0 !== cursor) {
51
+ const batchReply = await client.ft.cursorRead(
52
+ redisRuleIndex.ACCESS_RULES_REDIS_INDEX_NAME,
53
+ cursor,
54
+ { COUNT: aggregateOptions.COUNT }
55
+ );
56
+ await handleBatch(batchReply.results);
57
+ cursor = batchReply.cursor;
58
+ }
59
+ };
60
+ exports.aggregateRedisKeys = aggregateRedisKeys;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const userScopeInput = require("../../ruleInput/userScopeInput.cjs");
4
+ const rulesStorage = require("../../rulesStorage.cjs");
5
+ const REDIS_QUERY_DIALECT = 2;
6
+ const userIpQueries = {
7
+ numericIp: (value, scope) => {
8
+ if (void 0 !== value) {
9
+ return `( @numericIp:[${value} ${value}] | ( @numericIpMaskMin:[-inf ${value}] @numericIpMaskMax:[${value} +inf] ) )`;
10
+ }
11
+ if (scope.numericIpMaskMin === void 0 && scope.numericIpMaskMax === void 0) {
12
+ return "ismissing(@numericIp) ismissing(@numericIpMaskMin) ismissing(@numericIpMaskMax)";
13
+ }
14
+ return "";
15
+ },
16
+ numericIpMaskMin: (value, scope) => {
17
+ if (scope.numericIp !== void 0) {
18
+ return "";
19
+ }
20
+ return value !== void 0 ? `@numericIpMaskMin:[-inf ${value}]` : "ismissing(@numericIpMaskMin)";
21
+ },
22
+ numericIpMaskMax: (value, scope) => {
23
+ if (scope.numericIp !== void 0) {
24
+ return "";
25
+ }
26
+ return value !== void 0 ? `@numericIpMaskMax:[${value} +inf]` : "ismissing(@numericIpMaskMax)";
27
+ }
28
+ };
29
+ const getUserScopeQuery = (userScope, FilterScopeMatchType, matchingFieldsOnly) => {
30
+ let scopeEntries = Object.entries(userScope);
31
+ let scopeJoinType = " ";
32
+ if (FilterScopeMatchType === rulesStorage.FilterScopeMatch.Greedy) {
33
+ scopeEntries = scopeEntries.filter(
34
+ ([_, value]) => value !== void 0
35
+ );
36
+ scopeJoinType = " | ";
37
+ }
38
+ if (matchingFieldsOnly) {
39
+ const scopeMap = new Map(scopeEntries);
40
+ if (scopeMap.has("numericIp") && scopeMap.get("numericIp") === void 0) {
41
+ scopeMap.set("numericIpMaskMin", void 0);
42
+ scopeMap.set("numericIpMaskMax", void 0);
43
+ }
44
+ for (const name of Object.keys(userScopeInput.userScopeSchema.shape)) {
45
+ if (!scopeMap.has(name)) {
46
+ scopeMap.set(name, void 0);
47
+ }
48
+ }
49
+ scopeEntries = [...scopeMap.entries()];
50
+ }
51
+ const scopeObj = Object.fromEntries(scopeEntries);
52
+ return scopeEntries.map(
53
+ ([scopeFieldName, scopeFieldValue]) => getUserScopeFieldQuery(
54
+ scopeFieldName,
55
+ scopeFieldValue,
56
+ FilterScopeMatchType,
57
+ scopeObj
58
+ )
59
+ ).filter(Boolean).join(scopeJoinType);
60
+ };
61
+ const getUserScopeFieldQuery = (fieldName, fieldValue, scopeMatch, fullScope) => {
62
+ if (fieldName in userIpQueries) {
63
+ const queryBuilder = userIpQueries[fieldName];
64
+ return queryBuilder(fieldValue, fullScope);
65
+ }
66
+ return void 0 === fieldValue ? `ismissing(@${fieldName})` : `@${fieldName}:{${fieldValue}}`;
67
+ };
68
+ const getPolicyScopeQuery = (policyScope, scopeMatch) => {
69
+ const clientId = policyScope?.clientId;
70
+ if ("string" === typeof clientId) {
71
+ return rulesStorage.FilterScopeMatch.Exact === scopeMatch ? `@clientId:{${clientId}}` : `( @clientId:{${clientId}} | ismissing(@clientId) )`;
72
+ }
73
+ return rulesStorage.FilterScopeMatch.Exact === scopeMatch ? "ismissing(@clientId)" : "";
74
+ };
75
+ const getRulesRedisQuery = (filter, matchingFieldsOnly) => {
76
+ const { policyScope, userScope } = filter;
77
+ const queryParts = [];
78
+ if (filter.groupId) {
79
+ queryParts.push(`@groupId:{${filter.groupId}}`);
80
+ }
81
+ const policyScopeQuery = getPolicyScopeQuery(
82
+ policyScope,
83
+ filter.policyScopeMatch
84
+ );
85
+ if (policyScopeQuery) {
86
+ queryParts.push(policyScopeQuery);
87
+ }
88
+ if (userScope && Object.keys(userScope).length > 0) {
89
+ const userScopeFilter = getUserScopeQuery(
90
+ userScope,
91
+ filter.userScopeMatch,
92
+ matchingFieldsOnly
93
+ );
94
+ queryParts.push(`( ${userScopeFilter} )`);
95
+ }
96
+ return queryParts.length > 0 ? queryParts.join(" ") : "*";
97
+ };
98
+ exports.REDIS_QUERY_DIALECT = REDIS_QUERY_DIALECT;
99
+ exports.getRulesRedisQuery = getRulesRedisQuery;
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const util = require("node:util");
4
+ const common = require("@prosopo/common");
5
+ const redisRulesQuery = require("./redisRulesQuery.cjs");
6
+ const redisClient = require("../redisClient.cjs");
7
+ const redisRuleIndex = require("../redisRuleIndex.cjs");
8
+ const ruleInput = require("../../ruleInput/ruleInput.cjs");
9
+ const redisAggregate = require("./redisAggregate.cjs");
10
+ function _interopNamespaceDefault(e) {
11
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
12
+ if (e) {
13
+ for (const k in e) {
14
+ if (k !== "default") {
15
+ const d = Object.getOwnPropertyDescriptor(e, k);
16
+ Object.defineProperty(n, k, d.get ? d : {
17
+ enumerable: true,
18
+ get: () => e[k]
19
+ });
20
+ }
21
+ }
22
+ }
23
+ n.default = e;
24
+ return Object.freeze(n);
25
+ }
26
+ const util__namespace = /* @__PURE__ */ _interopNamespaceDefault(util);
27
+ class RedisRulesReader {
28
+ constructor(client, logger) {
29
+ this.client = client;
30
+ this.logger = logger;
31
+ }
32
+ async getMissingRuleIds(ruleIds) {
33
+ const ruleKeys = this.getRuleKeys(ruleIds);
34
+ const keyBatches = common.chunkIntoBatches(ruleKeys, redisClient.REDIS_BATCH_SIZE);
35
+ const missingKeyBatches = await common.executeBatchesSequentially(
36
+ keyBatches,
37
+ async (keysBatch) => redisClient.getMissingRedisKeys(this.client, keysBatch)
38
+ );
39
+ return missingKeyBatches.flat().map((ruleKey) => ruleKey.slice(redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX.length));
40
+ }
41
+ async fetchRules(ruleIds) {
42
+ const ruleKeys = this.getRuleKeys(ruleIds);
43
+ const keyBatches = common.chunkIntoBatches(ruleKeys, redisClient.REDIS_BATCH_SIZE);
44
+ const entryBatches = await common.executeBatchesSequentially(
45
+ keyBatches,
46
+ (keysBatch) => this.fetchRuleEntries(keysBatch)
47
+ );
48
+ return entryBatches.flat();
49
+ }
50
+ async findRules(filter, matchingFieldsOnly = false, skipEmptyUserScopes = true) {
51
+ const query = redisRulesQuery.getRulesRedisQuery(filter, matchingFieldsOnly);
52
+ if (skipEmptyUserScopes && query === "ismissing(@clientId)") {
53
+ return [];
54
+ }
55
+ let searchReply;
56
+ try {
57
+ searchReply = await this.client.ft.search(
58
+ redisRuleIndex.ACCESS_RULES_REDIS_INDEX_NAME,
59
+ query,
60
+ {
61
+ DIALECT: redisRulesQuery.REDIS_QUERY_DIALECT,
62
+ // FT.search doesn't support "unlimited" selects
63
+ LIMIT: {
64
+ from: 0,
65
+ size: redisClient.REDIS_BATCH_SIZE
66
+ }
67
+ }
68
+ );
69
+ if (searchReply.total > 0) {
70
+ this.logger.debug(() => ({
71
+ msg: "Executed search query",
72
+ data: {
73
+ inspect: util__namespace.inspect(
74
+ {
75
+ filter,
76
+ searchReply,
77
+ query
78
+ },
79
+ { depth: null }
80
+ )
81
+ }
82
+ }));
83
+ }
84
+ } catch (e) {
85
+ this.logger.error(() => ({
86
+ err: e,
87
+ data: {
88
+ inspect: util__namespace.inspect(
89
+ {
90
+ query,
91
+ filter
92
+ },
93
+ {
94
+ depth: null
95
+ }
96
+ )
97
+ },
98
+ msg: "failed to execute search query"
99
+ }));
100
+ return [];
101
+ }
102
+ const records = searchReply.documents.map(({ value }) => value);
103
+ return redisClient.parseRedisRecords(records, ruleInput.accessRuleInput, this.logger);
104
+ }
105
+ async findRuleIds(filter, matchingFieldsOnly = false) {
106
+ const query = redisRulesQuery.getRulesRedisQuery(filter, matchingFieldsOnly);
107
+ let ruleIds = [];
108
+ try {
109
+ const ruleKeys = await redisAggregate.aggregateRedisKeys(
110
+ this.client,
111
+ query,
112
+ this.logger
113
+ );
114
+ ruleIds = ruleKeys.map(
115
+ (ruleKey) => ruleKey.slice(redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX.length)
116
+ );
117
+ } catch (e) {
118
+ this.logger.error(() => ({
119
+ err: e,
120
+ data: {
121
+ inspect: util__namespace.inspect(
122
+ {
123
+ query,
124
+ filter
125
+ },
126
+ {
127
+ depth: null
128
+ }
129
+ )
130
+ },
131
+ msg: "Failed to execute search query for rule IDs"
132
+ }));
133
+ return [];
134
+ }
135
+ this.logger.debug(() => ({
136
+ msg: "Executed search query for rule IDs",
137
+ data: {
138
+ query,
139
+ foundCount: ruleIds.length,
140
+ foundIds: ruleIds
141
+ }
142
+ }));
143
+ return ruleIds;
144
+ }
145
+ async fetchAllRuleIds(batchHandler) {
146
+ const keysBatchHandler = async (keys) => {
147
+ const ids = keys.map(
148
+ (ruleKey) => ruleKey.slice(redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX.length)
149
+ );
150
+ await batchHandler(ids);
151
+ };
152
+ await redisAggregate.aggregateRedisKeys(this.client, "*", this.logger, keysBatchHandler);
153
+ }
154
+ async fetchRuleEntries(keys) {
155
+ const { records, expirations } = await redisClient.fetchRedisHashRecords(
156
+ this.client,
157
+ keys,
158
+ this.logger
159
+ );
160
+ const entries = [];
161
+ for (const [index, ruleData] of records.entries()) {
162
+ const isRulePresent = Object.keys(ruleData).length > 0;
163
+ if (isRulePresent) {
164
+ const rule = redisClient.parseRedisRecords(
165
+ [ruleData],
166
+ ruleInput.accessRuleInput,
167
+ this.logger
168
+ )[0];
169
+ if (rule) {
170
+ entries.push({
171
+ rule,
172
+ expiresUnixTimestamp: expirations[index]
173
+ });
174
+ }
175
+ }
176
+ }
177
+ return entries;
178
+ }
179
+ getRuleKeys(ruleIds) {
180
+ return ruleIds.map((id) => `${redisRuleIndex.ACCESS_RULE_REDIS_KEY_PREFIX}${id}`);
181
+ }
182
+ }
183
+ class DummyRedisRulesReader {
184
+ constructor(logger) {
185
+ this.logger = logger;
186
+ }
187
+ async getMissingRuleIds(ruleIds) {
188
+ this.logger.info(() => ({
189
+ msg: "Dummy getMissingRuleIds() has no effect (redis is not ready)",
190
+ data: {
191
+ ruleIds
192
+ }
193
+ }));
194
+ return [];
195
+ }
196
+ async fetchRules(ruleIds) {
197
+ this.logger.info(() => ({
198
+ msg: "Dummy fetchRule() has no effect (redis is not ready)",
199
+ data: {
200
+ ruleIds
201
+ }
202
+ }));
203
+ return [];
204
+ }
205
+ async findRules(filter, matchingFieldsOnly = false, skipEmptyUserScopes = true) {
206
+ this.logger.info(() => ({
207
+ msg: "Dummy findRules() has no effect (redis is not ready)",
208
+ data: {
209
+ filter
210
+ }
211
+ }));
212
+ return [];
213
+ }
214
+ async findRuleIds(filter, matchingFieldsOnly = false) {
215
+ this.logger.info(() => ({
216
+ msg: "Dummy findRuleIds() has no effect (redis is not ready)",
217
+ data: {
218
+ filter
219
+ }
220
+ }));
221
+ return [];
222
+ }
223
+ async fetchAllRuleIds(batchHandler) {
224
+ this.logger.info(() => ({
225
+ msg: "Dummy fetchAllRuleIds() has no effect (redis is not ready)"
226
+ }));
227
+ }
228
+ }
229
+ exports.DummyRedisRulesReader = DummyRedisRulesReader;
230
+ exports.RedisRulesReader = RedisRulesReader;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const util = require("node:util");
4
4
  const accessRules = require("../accessRules.cjs");
5
- const redisRulesIndex = require("./redisRulesIndex.cjs");
5
+ const redisAccessRulesIndex = require("./redisAccessRulesIndex.cjs");
6
6
  function _interopNamespaceDefault(e) {
7
7
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
8
8
  if (e) {
@@ -20,19 +20,19 @@ function _interopNamespaceDefault(e) {
20
20
  return Object.freeze(n);
21
21
  }
22
22
  const util__namespace = /* @__PURE__ */ _interopNamespaceDefault(util);
23
- const createRedisRulesReader = (client, logger) => {
23
+ const createRedisAccessRulesReader = (client, logger) => {
24
24
  return {
25
25
  findRules: async (filter, matchingFieldsOnly = false, skipEmptyUserScopes = true) => {
26
- const query = redisRulesIndex.getRedisRulesQuery(filter, matchingFieldsOnly);
26
+ const query = redisAccessRulesIndex.getRedisAccessRulesQuery(filter, matchingFieldsOnly);
27
27
  if (skipEmptyUserScopes && query === "ismissing(@clientId)") {
28
28
  return [];
29
29
  }
30
30
  let searchReply;
31
31
  try {
32
32
  searchReply = await client.ft.search(
33
- redisRulesIndex.redisRulesIndexName,
33
+ redisAccessRulesIndex.accessRulesRedisIndexName,
34
34
  query,
35
- redisRulesIndex.redisRulesSearchOptions
35
+ redisAccessRulesIndex.accessRulesRedisSearchOptions
36
36
  );
37
37
  if (searchReply.total > 0) {
38
38
  logger.debug(() => ({
@@ -67,16 +67,16 @@ const createRedisRulesReader = (client, logger) => {
67
67
  }));
68
68
  return [];
69
69
  }
70
- return extractRulesFromSearchReply(searchReply, logger);
70
+ return extractAccessRulesFromSearchReply(searchReply, logger);
71
71
  },
72
72
  findRuleIds: async (filter, matchingFieldsOnly = false) => {
73
- const query = redisRulesIndex.getRedisRulesQuery(filter, matchingFieldsOnly);
73
+ const query = redisAccessRulesIndex.getRedisAccessRulesQuery(filter, matchingFieldsOnly);
74
74
  let searchReply;
75
75
  try {
76
76
  searchReply = await client.ft.searchNoContent(
77
- redisRulesIndex.redisRulesIndexName,
77
+ redisAccessRulesIndex.accessRulesRedisIndexName,
78
78
  query,
79
- redisRulesIndex.redisRulesSearchOptions
79
+ redisAccessRulesIndex.accessRulesRedisSearchOptions
80
80
  );
81
81
  } catch (e) {
82
82
  logger.error(() => ({
@@ -100,29 +100,38 @@ const createRedisRulesReader = (client, logger) => {
100
100
  }
101
101
  };
102
102
  };
103
- const getDummyRedisRulesReader = (logger) => {
103
+ const createRedisAccessRulesWriter = (client) => {
104
104
  return {
105
- findRules: async (filter, matchingFieldsOnly = false, skipEmptyUserScopes = true) => {
106
- logger.info(() => ({
107
- msg: "Dummy findRules() has no effect (redis is not ready)",
108
- data: {
109
- filter
105
+ insertRule: async (rule, expirationTimestamp) => {
106
+ const ruleKey = redisAccessRulesIndex.getRedisAccessRuleKey(rule);
107
+ const ruleValue = redisAccessRulesIndex.getRedisAccessRuleValue(rule);
108
+ await client.hSet(ruleKey, ruleValue);
109
+ if (expirationTimestamp) {
110
+ const expiryDate = new Date(expirationTimestamp);
111
+ if (expiryDate.getUTCFullYear() === 1970) {
112
+ await client.expireAt(ruleKey, expirationTimestamp);
113
+ } else {
114
+ const timestampInSeconds = Math.floor(expirationTimestamp / 1e3);
115
+ await client.expireAt(ruleKey, timestampInSeconds);
110
116
  }
111
- }));
112
- return [];
117
+ }
118
+ return ruleKey;
113
119
  },
114
- findRuleIds: async (filter, matchingFieldsOnly = false) => {
115
- logger.info(() => ({
116
- msg: "Dummy findRuleIds() has no effect (redis is not ready)",
117
- data: {
118
- filter
119
- }
120
- }));
121
- return [];
120
+ deleteRules: async (ruleIds) => void await client.del(ruleIds),
121
+ deleteAllRules: async () => {
122
+ const keys = await client.keys(`${redisAccessRulesIndex.accessRuleRedisKeyPrefix}*`);
123
+ if (keys.length === 0) return 0;
124
+ return await client.del(keys);
122
125
  }
123
126
  };
124
127
  };
125
- const extractRulesFromSearchReply = (searchReply, logger) => {
128
+ const createRedisAccessRulesStorage = (client, logger) => {
129
+ return {
130
+ ...createRedisAccessRulesReader(client, logger),
131
+ ...createRedisAccessRulesWriter(client)
132
+ };
133
+ };
134
+ const extractAccessRulesFromSearchReply = (searchReply, logger) => {
126
135
  const accessRules$1 = [];
127
136
  searchReply.documents.map(({ id, value: document }) => {
128
137
  const parsedDocument = accessRules.accessRuleSchema.safeParse(document);
@@ -138,5 +147,6 @@ const extractRulesFromSearchReply = (searchReply, logger) => {
138
147
  });
139
148
  return accessRules$1;
140
149
  };
141
- exports.createRedisRulesReader = createRedisRulesReader;
142
- exports.getDummyRedisRulesReader = getDummyRedisRulesReader;
150
+ exports.createRedisAccessRulesReader = createRedisAccessRulesReader;
151
+ exports.createRedisAccessRulesStorage = createRedisAccessRulesStorage;
152
+ exports.createRedisAccessRulesWriter = createRedisAccessRulesWriter;
@@ -1,12 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const crypto = require("node:crypto");
3
4
  const search = require("@redis/search");
4
5
  const accessPolicy = require("../accessPolicy.cjs");
5
6
  const accessPolicyResolver = require("../accessPolicyResolver.cjs");
6
- const redisRulesIndexName = "index:user-access-rules";
7
- const redisRuleKeyPrefix = "uar:";
8
- const redisAccessRulesIndex = {
9
- name: redisRulesIndexName,
7
+ const redisIndex = require("./redisIndex.cjs");
8
+ const accessRulesRedisIndexName = "index:user-access-rules";
9
+ const accessRuleRedisKeyPrefix = "uar:";
10
+ const accessRuleContentHashAlgorithm = "md5";
11
+ const DEFAULT_SEARCH_LIMIT = 1e3;
12
+ const accessRulesIndex = {
13
+ name: accessRulesRedisIndexName,
10
14
  /**
11
15
  * Note on the field type decision
12
16
  *
@@ -32,9 +36,15 @@ const redisAccessRulesIndex = {
32
36
  // the satisfy statement is to guarantee that the keys are right
33
37
  options: {
34
38
  ON: "HASH",
35
- PREFIX: [redisRuleKeyPrefix]
39
+ PREFIX: [accessRuleRedisKeyPrefix]
36
40
  }
37
41
  };
42
+ const createRedisAccessRulesIndex = async (client, indexName) => {
43
+ if (indexName) {
44
+ accessRulesIndex.name = indexName;
45
+ }
46
+ return redisIndex.createRedisIndex(client, accessRulesIndex);
47
+ };
38
48
  const numericIndexFields = [
39
49
  "numericIp",
40
50
  "numericIpMaskMin",
@@ -63,11 +73,19 @@ const greedyFieldComparisons = {
63
73
  return value !== void 0 ? `@numericIpMaskMax:[${value} +inf]` : "ismissing(@numericIpMaskMax)";
64
74
  }
65
75
  };
66
- const redisRulesSearchOptions = {
76
+ const accessRulesRedisSearchOptions = {
67
77
  // #2 is a required option when the 'ismissing()' function is in the query body
68
78
  DIALECT: 2
69
79
  };
70
- const getRedisRulesQuery = (filter, matchingFieldsOnly) => {
80
+ const accessRulesRedisDeleteOptions = {
81
+ // #2 is a required option when the 'ismissing()' function is in the query body
82
+ DIALECT: 2,
83
+ LIMIT: {
84
+ from: 0,
85
+ size: DEFAULT_SEARCH_LIMIT
86
+ }
87
+ };
88
+ const getRedisAccessRulesQuery = (filter, matchingFieldsOnly) => {
71
89
  const { policyScope, userScope } = filter;
72
90
  const policyScopeFilter = getPolicyScopeQuery(
73
91
  policyScope,
@@ -131,8 +149,23 @@ const getUserScopeFieldQuery = (fieldName, fieldValue, matchType, fullScope) =>
131
149
  }
132
150
  return numericIndexFields.includes(fieldName) ? `@${fieldName}:[${fieldValue}]` : `@${fieldName}:{${fieldValue}}`;
133
151
  };
134
- exports.getRedisRulesQuery = getRedisRulesQuery;
135
- exports.redisAccessRulesIndex = redisAccessRulesIndex;
136
- exports.redisRuleKeyPrefix = redisRuleKeyPrefix;
137
- exports.redisRulesIndexName = redisRulesIndexName;
138
- exports.redisRulesSearchOptions = redisRulesSearchOptions;
152
+ const getRedisAccessRuleKey = (rule) => accessRuleRedisKeyPrefix + crypto.createHash(accessRuleContentHashAlgorithm).update(
153
+ JSON.stringify(
154
+ rule,
155
+ (key, value) => (
156
+ // JSON.stringify can't handle BigInt itself: throws "Do not know how to serialize a BigInt"
157
+ "bigint" === typeof value ? value.toString() : value
158
+ )
159
+ )
160
+ ).digest("hex");
161
+ const getRedisAccessRuleValue = (rule) => Object.fromEntries(
162
+ Object.entries(rule).map(([key, value]) => [key, String(value)])
163
+ );
164
+ exports.accessRuleRedisKeyPrefix = accessRuleRedisKeyPrefix;
165
+ exports.accessRulesRedisDeleteOptions = accessRulesRedisDeleteOptions;
166
+ exports.accessRulesRedisIndexName = accessRulesRedisIndexName;
167
+ exports.accessRulesRedisSearchOptions = accessRulesRedisSearchOptions;
168
+ exports.createRedisAccessRulesIndex = createRedisAccessRulesIndex;
169
+ exports.getRedisAccessRuleKey = getRedisAccessRuleKey;
170
+ exports.getRedisAccessRuleValue = getRedisAccessRuleValue;
171
+ exports.getRedisAccessRulesQuery = getRedisAccessRulesQuery;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const zod = require("zod");
4
+ const REDIS_BATCH_SIZE = 1e3;
5
+ const getMissingRedisKeys = async (client, keys) => {
6
+ const queries = client.multi();
7
+ keys.map((key) => {
8
+ queries.exists(key);
9
+ });
10
+ const records = await queries.exec();
11
+ const missingKeys = [];
12
+ records.map((exists, recordIndex) => {
13
+ if ("0" === String(exists)) {
14
+ const key = keys[recordIndex];
15
+ if (key) {
16
+ missingKeys.push(key);
17
+ }
18
+ }
19
+ });
20
+ return missingKeys;
21
+ };
22
+ const fetchRedisHashRecords = async (client, keys, logger) => {
23
+ const rulesPipe = client.multi();
24
+ const expirationPipe = client.multi();
25
+ for (const key of keys) {
26
+ rulesPipe.hGetAll(key);
27
+ expirationPipe.expireTime(key);
28
+ }
29
+ const records = await rulesPipe.exec();
30
+ const expirationRecords = await expirationPipe.exec();
31
+ return {
32
+ records,
33
+ expirations: parseExpirationRecords(expirationRecords, logger)
34
+ };
35
+ };
36
+ const parseRedisRecords = (records, recordSchema, logger) => records.flatMap((record) => {
37
+ const parseResult = recordSchema.safeParse(record);
38
+ if (parseResult.success) {
39
+ return [parseResult.data];
40
+ }
41
+ logger.error(() => ({
42
+ msg: "Failed to parse Redis record",
43
+ data: { record, error: parseResult.error }
44
+ }));
45
+ return [];
46
+ });
47
+ const expirationRecordSchema = zod.z.coerce.number();
48
+ const UNSET_EXPIRATION_VALUE = -1;
49
+ const parseExpirationRecords = (records, logger) => records.flatMap((record) => {
50
+ const parseResult = expirationRecordSchema.safeParse(record);
51
+ if (parseResult.success) {
52
+ const expiration = UNSET_EXPIRATION_VALUE === parseResult.data ? void 0 : parseResult.data;
53
+ return [expiration];
54
+ }
55
+ logger.error(() => ({
56
+ msg: "Failed to parse Redis expiration record",
57
+ data: {
58
+ record,
59
+ error: parseResult.error
60
+ }
61
+ }));
62
+ return [void 0];
63
+ });
64
+ exports.REDIS_BATCH_SIZE = REDIS_BATCH_SIZE;
65
+ exports.fetchRedisHashRecords = fetchRedisHashRecords;
66
+ exports.getMissingRedisKeys = getMissingRedisKeys;
67
+ exports.parseRedisRecords = parseRedisRecords;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const crypto = require("node:crypto");
4
+ const redisIndexHashesRecordKey = "_index_hashes";
5
+ const redisIndexHashAlgorithm = "sha256";
6
+ const createRedisIndex = async (client, index) => {
7
+ const indexHash = createIndexHash(index);
8
+ const existingIndexes = await client.ft._LIST();
9
+ if (existingIndexes.includes(index.name)) {
10
+ const existingIndexHash = await fetchIndexHash(client, index.name);
11
+ if (indexHash === existingIndexHash) {
12
+ return;
13
+ }
14
+ await client.ft.dropIndex(index.name);
15
+ }
16
+ await client.ft.create(index.name, index.schema, index.options);
17
+ await saveIndexHash(client, index.name, indexHash);
18
+ };
19
+ const createIndexHash = (index) => crypto.createHash(redisIndexHashAlgorithm).update(JSON.stringify(index)).digest("hex");
20
+ const fetchIndexHash = async (client, indexName) => client.hGet(redisIndexHashesRecordKey, indexName);
21
+ const saveIndexHash = async (client, indexName, indexHash) => client.hSet(redisIndexHashesRecordKey, indexName, indexHash);
22
+ exports.createRedisIndex = createRedisIndex;