@ambiten/core 1.0.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 (304) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +247 -0
  3. package/bin/bootstrap.js +26 -0
  4. package/dist/ambiten-cache/ambitenCache.d.ts +64 -0
  5. package/dist/ambiten-cache/ambitenCache.d.ts.map +1 -0
  6. package/dist/ambiten-cache/ambitenCache.js +127 -0
  7. package/dist/ambiten-cache/index.d.ts +2 -0
  8. package/dist/ambiten-cache/index.d.ts.map +1 -0
  9. package/dist/ambiten-cache/index.js +4 -0
  10. package/dist/browser.d.ts +32 -0
  11. package/dist/browser.d.ts.map +1 -0
  12. package/dist/browser.js +37 -0
  13. package/dist/cli/ambiten-core-cli.d.ts +4 -0
  14. package/dist/cli/ambiten-core-cli.d.ts.map +1 -0
  15. package/dist/cli/ambiten-core-cli.js +41 -0
  16. package/dist/cli/buildInteractiveConfig.d.ts +4 -0
  17. package/dist/cli/buildInteractiveConfig.d.ts.map +1 -0
  18. package/dist/cli/buildInteractiveConfig.js +198 -0
  19. package/dist/cli/index.d.ts +5 -0
  20. package/dist/cli/index.d.ts.map +1 -0
  21. package/dist/cli/index.js +7 -0
  22. package/dist/cli/prompt.d.ts +10 -0
  23. package/dist/cli/prompt.d.ts.map +1 -0
  24. package/dist/cli/prompt.js +48 -0
  25. package/dist/cli/types.d.ts +11 -0
  26. package/dist/cli/types.d.ts.map +1 -0
  27. package/dist/cli/types.js +2 -0
  28. package/dist/config/ambiten.config.schema.json +278 -0
  29. package/dist/config/index.d.ts +2 -0
  30. package/dist/config/index.d.ts.map +1 -0
  31. package/dist/config/index.js +4 -0
  32. package/dist/config/loadAmbitenConfig.d.ts +3 -0
  33. package/dist/config/loadAmbitenConfig.d.ts.map +1 -0
  34. package/dist/config/loadAmbitenConfig.js +131 -0
  35. package/dist/context/ambitenContext.d.ts +35 -0
  36. package/dist/context/ambitenContext.d.ts.map +1 -0
  37. package/dist/context/ambitenContext.js +147 -0
  38. package/dist/context/helpers/runManualTransaction.d.ts +3 -0
  39. package/dist/context/helpers/runManualTransaction.d.ts.map +1 -0
  40. package/dist/context/helpers/runManualTransaction.js +22 -0
  41. package/dist/context/helpers/transactionSession.d.ts +9 -0
  42. package/dist/context/helpers/transactionSession.d.ts.map +1 -0
  43. package/dist/context/helpers/transactionSession.js +8 -0
  44. package/dist/context/index.d.ts +4 -0
  45. package/dist/context/index.d.ts.map +1 -0
  46. package/dist/context/index.js +6 -0
  47. package/dist/debug/debugLog.d.ts +2 -0
  48. package/dist/debug/debugLog.d.ts.map +1 -0
  49. package/dist/debug/debugLog.js +23 -0
  50. package/dist/debug/index.d.ts +2 -0
  51. package/dist/debug/index.d.ts.map +1 -0
  52. package/dist/debug/index.js +4 -0
  53. package/dist/gc/ambitenGC.d.ts +49 -0
  54. package/dist/gc/ambitenGC.d.ts.map +1 -0
  55. package/dist/gc/ambitenGC.js +138 -0
  56. package/dist/gc/gcCron.browser.d.ts +2 -0
  57. package/dist/gc/gcCron.browser.d.ts.map +1 -0
  58. package/dist/gc/gcCron.browser.js +9 -0
  59. package/dist/gc/gcCron.node.d.ts +19 -0
  60. package/dist/gc/gcCron.node.d.ts.map +1 -0
  61. package/dist/gc/gcCron.node.js +47 -0
  62. package/dist/gc/gcManager.d.ts +37 -0
  63. package/dist/gc/gcManager.d.ts.map +1 -0
  64. package/dist/gc/gcManager.js +62 -0
  65. package/dist/gc/index.d.ts +5 -0
  66. package/dist/gc/index.d.ts.map +1 -0
  67. package/dist/gc/index.js +7 -0
  68. package/dist/graphql/ambitenGraphQL.d.ts +24 -0
  69. package/dist/graphql/ambitenGraphQL.d.ts.map +1 -0
  70. package/dist/graphql/ambitenGraphQL.js +215 -0
  71. package/dist/graphql/index.d.ts +2 -0
  72. package/dist/graphql/index.d.ts.map +1 -0
  73. package/dist/graphql/index.js +4 -0
  74. package/dist/index-browser.js +25 -0
  75. package/dist/index-browser.js.map +1 -0
  76. package/dist/index-cli.js +26 -0
  77. package/dist/index-cli.js.map +1 -0
  78. package/dist/index.d.ts +30 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +25 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/init-cli/generate.project.d.ts +9 -0
  83. package/dist/init-cli/generate.project.d.ts.map +1 -0
  84. package/dist/init-cli/generate.project.js +38 -0
  85. package/dist/instrumentation/index.d.ts +3 -0
  86. package/dist/instrumentation/index.d.ts.map +1 -0
  87. package/dist/instrumentation/index.js +5 -0
  88. package/dist/instrumentation/measureQuery.browser.d.ts +3 -0
  89. package/dist/instrumentation/measureQuery.browser.d.ts.map +1 -0
  90. package/dist/instrumentation/measureQuery.browser.js +6 -0
  91. package/dist/instrumentation/measureQueryWithErrors.d.ts +11 -0
  92. package/dist/instrumentation/measureQueryWithErrors.d.ts.map +1 -0
  93. package/dist/instrumentation/measureQueryWithErrors.js +90 -0
  94. package/dist/lib-core/ambitenClient.d.ts +115 -0
  95. package/dist/lib-core/ambitenClient.d.ts.map +1 -0
  96. package/dist/lib-core/ambitenClient.js +537 -0
  97. package/dist/lib-core/ambitenModelFactory.d.ts +979 -0
  98. package/dist/lib-core/ambitenModelFactory.d.ts.map +1 -0
  99. package/dist/lib-core/ambitenModelFactory.js +3984 -0
  100. package/dist/lib-core/ambitenSchema.d.ts +126 -0
  101. package/dist/lib-core/ambitenSchema.d.ts.map +1 -0
  102. package/dist/lib-core/ambitenSchema.js +247 -0
  103. package/dist/lib-core/bootstrap/ambitenBootstrap.d.ts +38 -0
  104. package/dist/lib-core/bootstrap/ambitenBootstrap.d.ts.map +1 -0
  105. package/dist/lib-core/bootstrap/ambitenBootstrap.js +381 -0
  106. package/dist/lib-core/bootstrap/index.d.ts +2 -0
  107. package/dist/lib-core/bootstrap/index.d.ts.map +1 -0
  108. package/dist/lib-core/bootstrap/index.js +4 -0
  109. package/dist/lib-core/index.d.ts +5 -0
  110. package/dist/lib-core/index.d.ts.map +1 -0
  111. package/dist/lib-core/index.js +7 -0
  112. package/dist/middleware/authorization/auth.d.ts +22 -0
  113. package/dist/middleware/authorization/auth.d.ts.map +1 -0
  114. package/dist/middleware/authorization/auth.js +37 -0
  115. package/dist/middleware/index.d.ts +4 -0
  116. package/dist/middleware/index.d.ts.map +1 -0
  117. package/dist/middleware/index.js +6 -0
  118. package/dist/middleware/rbac/rbacMiddleware.d.ts +103 -0
  119. package/dist/middleware/rbac/rbacMiddleware.d.ts.map +1 -0
  120. package/dist/middleware/rbac/rbacMiddleware.js +246 -0
  121. package/dist/middleware/rbac/rbacTypes.d.ts +4 -0
  122. package/dist/middleware/rbac/rbacTypes.d.ts.map +1 -0
  123. package/dist/middleware/rbac/rbacTypes.js +8 -0
  124. package/dist/plugins/index.d.ts +3 -0
  125. package/dist/plugins/index.d.ts.map +1 -0
  126. package/dist/plugins/index.js +5 -0
  127. package/dist/plugins/softDelete/applySoftDelete.d.ts +5 -0
  128. package/dist/plugins/softDelete/applySoftDelete.d.ts.map +1 -0
  129. package/dist/plugins/softDelete/applySoftDelete.js +139 -0
  130. package/dist/plugins/softDelete/types.d.ts +14 -0
  131. package/dist/plugins/softDelete/types.d.ts.map +1 -0
  132. package/dist/plugins/softDelete/types.js +2 -0
  133. package/dist/redis-manager/index.d.ts +2 -0
  134. package/dist/redis-manager/index.d.ts.map +1 -0
  135. package/dist/redis-manager/index.js +4 -0
  136. package/dist/redis-manager/redisClient.d.ts +32 -0
  137. package/dist/redis-manager/redisClient.d.ts.map +1 -0
  138. package/dist/redis-manager/redisClient.js +146 -0
  139. package/dist/tanancy/MultiTenantManager.d.ts +98 -0
  140. package/dist/tanancy/MultiTenantManager.d.ts.map +1 -0
  141. package/dist/tanancy/MultiTenantManager.js +190 -0
  142. package/dist/tanancy/TenantContext.d.ts +31 -0
  143. package/dist/tanancy/TenantContext.d.ts.map +1 -0
  144. package/dist/tanancy/TenantContext.js +59 -0
  145. package/dist/tanancy/TenantModelResolver.d.ts +10 -0
  146. package/dist/tanancy/TenantModelResolver.d.ts.map +1 -0
  147. package/dist/tanancy/TenantModelResolver.js +114 -0
  148. package/dist/tanancy/index.d.ts +5 -0
  149. package/dist/tanancy/index.d.ts.map +1 -0
  150. package/dist/tanancy/index.js +7 -0
  151. package/dist/tanancy/init/initMultiTenancy.d.ts +29 -0
  152. package/dist/tanancy/init/initMultiTenancy.d.ts.map +1 -0
  153. package/dist/tanancy/init/initMultiTenancy.js +62 -0
  154. package/dist/templates/generateDefaultConfigContent.d.ts +10 -0
  155. package/dist/templates/generateDefaultConfigContent.d.ts.map +1 -0
  156. package/dist/templates/generateDefaultConfigContent.js +108 -0
  157. package/dist/templates/generateGCManagerFile.d.ts +2 -0
  158. package/dist/templates/generateGCManagerFile.d.ts.map +1 -0
  159. package/dist/templates/generateGCManagerFile.js +39 -0
  160. package/dist/templates/generateGCRunnerFile.d.ts +2 -0
  161. package/dist/templates/generateGCRunnerFile.d.ts.map +1 -0
  162. package/dist/templates/generateGCRunnerFile.js +25 -0
  163. package/dist/templates/generateMainEntry.d.ts +3 -0
  164. package/dist/templates/generateMainEntry.d.ts.map +1 -0
  165. package/dist/templates/generateMainEntry.js +24 -0
  166. package/dist/templates/index.d.ts +6 -0
  167. package/dist/templates/index.d.ts.map +1 -0
  168. package/dist/templates/index.js +8 -0
  169. package/dist/templates/scaffold/structureGenerator.d.ts +4 -0
  170. package/dist/templates/scaffold/structureGenerator.d.ts.map +1 -0
  171. package/dist/templates/scaffold/structureGenerator.js +193 -0
  172. package/dist/types/GCConfig.d.ts +7 -0
  173. package/dist/types/GCConfig.d.ts.map +1 -0
  174. package/dist/types/GCConfig.js +2 -0
  175. package/dist/types/ambiten-runtime-type.d.ts +24 -0
  176. package/dist/types/ambiten-runtime-type.d.ts.map +1 -0
  177. package/dist/types/ambiten-runtime-type.js +2 -0
  178. package/dist/types/ambiten.client.type.d.ts +136 -0
  179. package/dist/types/ambiten.client.type.d.ts.map +1 -0
  180. package/dist/types/ambiten.client.type.js +3 -0
  181. package/dist/types/ambiten.model.type.d.ts +135 -0
  182. package/dist/types/ambiten.model.type.d.ts.map +1 -0
  183. package/dist/types/ambiten.model.type.js +4 -0
  184. package/dist/types/ambitenConfig.d.ts +103 -0
  185. package/dist/types/ambitenConfig.d.ts.map +1 -0
  186. package/dist/types/ambitenConfig.js +3 -0
  187. package/dist/types/bootstrapClient.type.d.ts +11 -0
  188. package/dist/types/bootstrapClient.type.d.ts.map +1 -0
  189. package/dist/types/bootstrapClient.type.js +2 -0
  190. package/dist/types/db.provider.d.ts +66 -0
  191. package/dist/types/db.provider.d.ts.map +1 -0
  192. package/dist/types/db.provider.js +2 -0
  193. package/dist/types/document.d.ts +7 -0
  194. package/dist/types/document.d.ts.map +1 -0
  195. package/dist/types/document.js +2 -0
  196. package/dist/types/event.type.d.ts +6 -0
  197. package/dist/types/event.type.d.ts.map +1 -0
  198. package/dist/types/event.type.js +27 -0
  199. package/dist/types/graphql.type.d.ts +22 -0
  200. package/dist/types/graphql.type.d.ts.map +1 -0
  201. package/dist/types/graphql.type.js +2 -0
  202. package/dist/types/index.d.ts +15 -0
  203. package/dist/types/index.d.ts.map +1 -0
  204. package/dist/types/index.js +17 -0
  205. package/dist/types/logger.types.d.ts +15 -0
  206. package/dist/types/logger.types.d.ts.map +1 -0
  207. package/dist/types/logger.types.js +3 -0
  208. package/dist/types/middleware/types.d.ts +28 -0
  209. package/dist/types/middleware/types.d.ts.map +1 -0
  210. package/dist/types/middleware/types.js +2 -0
  211. package/dist/types/result.utils.types.d.ts +7 -0
  212. package/dist/types/result.utils.types.d.ts.map +1 -0
  213. package/dist/types/result.utils.types.js +2 -0
  214. package/dist/types/schema.type.d.ts +23 -0
  215. package/dist/types/schema.type.d.ts.map +1 -0
  216. package/dist/types/schema.type.js +10 -0
  217. package/dist/types/user.type.d.ts +11 -0
  218. package/dist/types/user.type.d.ts.map +1 -0
  219. package/dist/types/user.type.js +2 -0
  220. package/dist/utils/AuthUtils.d.ts +15 -0
  221. package/dist/utils/AuthUtils.d.ts.map +1 -0
  222. package/dist/utils/AuthUtils.js +26 -0
  223. package/dist/utils/ModelRegistry.d.ts +15 -0
  224. package/dist/utils/ModelRegistry.d.ts.map +1 -0
  225. package/dist/utils/ModelRegistry.js +35 -0
  226. package/dist/utils/banner.d.ts +2 -0
  227. package/dist/utils/banner.d.ts.map +1 -0
  228. package/dist/utils/banner.js +20 -0
  229. package/dist/utils/builders/createModel.d.ts +37 -0
  230. package/dist/utils/builders/createModel.d.ts.map +1 -0
  231. package/dist/utils/builders/createModel.js +38 -0
  232. package/dist/utils/builders/getTenantDb.d.ts +12 -0
  233. package/dist/utils/builders/getTenantDb.d.ts.map +1 -0
  234. package/dist/utils/builders/getTenantDb.js +25 -0
  235. package/dist/utils/builders/index.d.ts +4 -0
  236. package/dist/utils/builders/index.d.ts.map +1 -0
  237. package/dist/utils/builders/index.js +6 -0
  238. package/dist/utils/builders/schema.d.ts +22 -0
  239. package/dist/utils/builders/schema.d.ts.map +1 -0
  240. package/dist/utils/builders/schema.js +26 -0
  241. package/dist/utils/cacheWithRedis.d.ts +27 -0
  242. package/dist/utils/cacheWithRedis.d.ts.map +1 -0
  243. package/dist/utils/cacheWithRedis.js +22 -0
  244. package/dist/utils/castId.d.ts +5 -0
  245. package/dist/utils/castId.d.ts.map +1 -0
  246. package/dist/utils/castId.js +52 -0
  247. package/dist/utils/color-palatte.d.ts +4 -0
  248. package/dist/utils/color-palatte.d.ts.map +1 -0
  249. package/dist/utils/color-palatte.js +24 -0
  250. package/dist/utils/configureAmbitenContext.d.ts +13 -0
  251. package/dist/utils/configureAmbitenContext.d.ts.map +1 -0
  252. package/dist/utils/configureAmbitenContext.js +35 -0
  253. package/dist/utils/detect-package-manager.d.ts +5 -0
  254. package/dist/utils/detect-package-manager.d.ts.map +1 -0
  255. package/dist/utils/detect-package-manager.js +59 -0
  256. package/dist/utils/ensureModelNameSafe.d.ts +12 -0
  257. package/dist/utils/ensureModelNameSafe.d.ts.map +1 -0
  258. package/dist/utils/ensureModelNameSafe.js +28 -0
  259. package/dist/utils/ensureRedis.d.ts +7 -0
  260. package/dist/utils/ensureRedis.d.ts.map +1 -0
  261. package/dist/utils/ensureRedis.js +40 -0
  262. package/dist/utils/error/ambitenError-handler.d.ts +24 -0
  263. package/dist/utils/error/ambitenError-handler.d.ts.map +1 -0
  264. package/dist/utils/error/ambitenError-handler.js +39 -0
  265. package/dist/utils/error/errorTypes.d.ts +27 -0
  266. package/dist/utils/error/errorTypes.d.ts.map +1 -0
  267. package/dist/utils/error/errorTypes.js +30 -0
  268. package/dist/utils/error/index.d.ts +3 -0
  269. package/dist/utils/error/index.d.ts.map +1 -0
  270. package/dist/utils/error/index.js +5 -0
  271. package/dist/utils/eventOptions.d.ts +38 -0
  272. package/dist/utils/eventOptions.d.ts.map +1 -0
  273. package/dist/utils/eventOptions.js +130 -0
  274. package/dist/utils/events.d.ts +5 -0
  275. package/dist/utils/events.d.ts.map +1 -0
  276. package/dist/utils/events.js +7 -0
  277. package/dist/utils/index.d.ts +23 -0
  278. package/dist/utils/index.d.ts.map +1 -0
  279. package/dist/utils/index.js +25 -0
  280. package/dist/utils/invalidateTenantCache.d.ts +18 -0
  281. package/dist/utils/invalidateTenantCache.d.ts.map +1 -0
  282. package/dist/utils/invalidateTenantCache.js +27 -0
  283. package/dist/utils/logger.d.ts +3 -0
  284. package/dist/utils/logger.d.ts.map +1 -0
  285. package/dist/utils/logger.js +27 -0
  286. package/dist/utils/modelLoader.d.ts +3 -0
  287. package/dist/utils/modelLoader.d.ts.map +1 -0
  288. package/dist/utils/modelLoader.js +41 -0
  289. package/dist/utils/resolveAmbitenOption.d.ts +2 -0
  290. package/dist/utils/resolveAmbitenOption.d.ts.map +1 -0
  291. package/dist/utils/resolveAmbitenOption.js +10 -0
  292. package/dist/utils/resolveRuntimeConfig.d.ts +22 -0
  293. package/dist/utils/resolveRuntimeConfig.d.ts.map +1 -0
  294. package/dist/utils/resolveRuntimeConfig.js +33 -0
  295. package/dist/utils/scaffoldLoger.d.ts +11 -0
  296. package/dist/utils/scaffoldLoger.d.ts.map +1 -0
  297. package/dist/utils/scaffoldLoger.js +68 -0
  298. package/dist/utils/symbols.d.ts +4 -0
  299. package/dist/utils/symbols.d.ts.map +1 -0
  300. package/dist/utils/symbols.js +6 -0
  301. package/dist/utils/time.d.ts +5 -0
  302. package/dist/utils/time.d.ts.map +1 -0
  303. package/dist/utils/time.js +39 -0
  304. package/package.json +180 -0
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkPermission = checkPermission;
4
+ exports.getCachedData = getCachedData;
5
+ exports.setCachedData = setCachedData;
6
+ exports.invalidateTenantCache = invalidateTenantCache;
7
+ exports.enforceRBAC = enforceRBAC;
8
+ exports.getRBACAction = getRBACAction;
9
+ exports.getOriginalResolver = getOriginalResolver;
10
+ const redis_manager_1 = require("../../redis-manager");
11
+ const rbacTypes_1 = require("./rbacTypes");
12
+ function buildCacheKey(role, key) {
13
+ if (Array.isArray(role)) {
14
+ return `${role.join(',')}:${key}`;
15
+ }
16
+ if (role && typeof role === 'object') {
17
+ if ('tenantId' in role)
18
+ return `${role.tenantId}:${key}`;
19
+ // If role is actually a role object or other shape, fallback to JSON-safe string
20
+ try {
21
+ const convertCredentials = JSON.parse(JSON.stringify(role));
22
+ return `${convertCredentials}:${key}`;
23
+ }
24
+ catch (err) {
25
+ return `${String(role)}:${key}`;
26
+ }
27
+ }
28
+ return `${role}:${key}`;
29
+ }
30
+ ;
31
+ // const tenantCache: Map<string, any> = new Map();
32
+ /**
33
+ * Check if a user has a specific permission
34
+ * @param role The role of the user
35
+ * @param permission The permission to check
36
+ * @returns A promise that resolves to true if the user has the permission, false otherwise
37
+ * @example
38
+ * const hasPermission = await checkPermission('admin', 'createUser');
39
+ * if (hasPermission) {
40
+ * // User has permission to create a user
41
+ * } else {
42
+ * // User does not have permission to create a user
43
+ * }
44
+ */
45
+ async function checkPermission(role, permission) {
46
+ const cacheKey = buildCacheKey(role, 'permissions');
47
+ let allowedActions = await getCachedData(role, 'permissions');
48
+ if (!allowedActions) {
49
+ console.info(`Cache miss for role "${role}". Fetching permissions...`);
50
+ // Fetch permissions from the rolePermissions object
51
+ allowedActions = rbacTypes_1.rolePermissions[role] || [];
52
+ if (allowedActions.length > 0) {
53
+ console.info(`Permissions found for role "${role}": ${allowedActions.join(', ')}`);
54
+ }
55
+ else {
56
+ console.warn(`No permissions found for role "${role}"`);
57
+ }
58
+ }
59
+ ;
60
+ const cacheExists = await redis_manager_1.redis.exists(cacheKey);
61
+ if (cacheExists) {
62
+ console.info(`Cache hit for role "${role}"`);
63
+ }
64
+ // If allowedActions is not already cached, set it
65
+ if (allowedActions) {
66
+ allowedActions = Promise.resolve(rbacTypes_1.rolePermissions[role] || []);
67
+ await setCachedData(role, 'permissions', await allowedActions, 500); // Cache for 500s
68
+ }
69
+ return Promise.resolve(rbacTypes_1.rolePermissions[role] || []).then(permissions => {
70
+ if (permissions.includes(permission)) {
71
+ console.info(`Permission granted for role "${role}" to perform action "${permission}"`);
72
+ return true;
73
+ }
74
+ else {
75
+ console.warn(`Permission denied for role "${role}" to perform action "${permission}"`);
76
+ return false;
77
+ }
78
+ });
79
+ }
80
+ /**
81
+ * Get cached data for a specific role and key
82
+ * @param role The role or tenant object
83
+ * @param key The key to retrieve from the cache
84
+ * @return {Promise<any>} A promise that resolves to the cached data or null if not found
85
+ * This function retrieves cached data from Redis based on the provided role and key.
86
+ * It constructs a unique cache key using the role and key, checks if the data exists in the cache,
87
+ * and returns the parsed data if found. If the data is not found, it logs a cache miss and returns null.
88
+ * @throws {Error} If there is an issue with the Redis operation
89
+ * @example
90
+ * // Get cached permissions for role 'admin'
91
+ * const cachedPermissions = await getCachedData('admin', 'permissions');
92
+ * if (cachedPermissions) {
93
+ * console.log('Cached permissions:', cachedPermissions);
94
+ * } else {
95
+ * console.log('No cached permissions found');
96
+ * }
97
+ */
98
+ async function getCachedData(role, key) {
99
+ const cacheKey = buildCacheKey(role, key);
100
+ const cachedData = await redis_manager_1.redis.get(cacheKey);
101
+ if (cachedData) {
102
+ console.info(`Cache hit for key "${cacheKey}"`);
103
+ }
104
+ else {
105
+ console.warn(`Cache miss for key "${cacheKey}"`);
106
+ }
107
+ try {
108
+ return cachedData ? JSON.parse(JSON.stringify(cachedData)) : null;
109
+ }
110
+ catch (err) {
111
+ console.warn(`Failed to parse cached data for key ${cacheKey}:`, err);
112
+ // If parsing fails, return raw value to avoid crashing
113
+ return cachedData;
114
+ }
115
+ }
116
+ ;
117
+ async function setCachedData(role, key, data, ttl) {
118
+ // Build a consistent cache key and store JSON-stringified data with TTL
119
+ const cacheKey = buildCacheKey(role, key);
120
+ try {
121
+ await redis_manager_1.redis?.set(cacheKey, JSON.stringify(data), { EX: ttl });
122
+ console.info(`Cache set for ${cacheKey}:`, data);
123
+ }
124
+ catch (err) {
125
+ console.error(`Failed to set cache for ${cacheKey}:`, err);
126
+ }
127
+ // Only log permissions if role is a string or array with values
128
+ let roleKey;
129
+ if (typeof role === 'string') {
130
+ roleKey = role;
131
+ }
132
+ else if (Array.isArray(role) && role.length > 0) {
133
+ roleKey = role[0];
134
+ }
135
+ else if (role && typeof role === 'object' && 'tenantId' in role) {
136
+ roleKey = role.tenantId;
137
+ }
138
+ if (roleKey) {
139
+ const permissions = rbacTypes_1.rolePermissions[roleKey] || [];
140
+ if (permissions.length > 0) {
141
+ console.info(`Permissions cached for role "${roleKey}": ${permissions.join(', ')}`);
142
+ }
143
+ else {
144
+ console.warn(`No permissions found for role "${roleKey}" to cache`);
145
+ }
146
+ }
147
+ else {
148
+ console.warn(`Role key is not a string, cannot cache permissions`);
149
+ }
150
+ return;
151
+ }
152
+ /**
153
+ * Invalidate the cache for a specific tenant and role
154
+ * @param tenantId The ID of the tenant
155
+ * @param role The role to invalidate cache for
156
+ * @return {Promise<void>}
157
+ * This function removes the cached permissions for the specified tenant and role.
158
+ * It is useful when permissions change and you want to ensure the cache reflects the latest state.
159
+ * It logs the invalidation action and deletes the cache entry from Redis.
160
+ * This is particularly important in multi-tenant applications where each tenant may have different permissions.
161
+ * @throws {Error} If there is an issue with the Redis operation
162
+ * @example
163
+ * // Invalidate cache for tenant 'tenant123' with role 'admin'
164
+ * await invalidateTenantCache('tenant123', 'admin');
165
+ */
166
+ async function invalidateTenantCache(tenantId, role) {
167
+ try {
168
+ const cacheKey = `${tenantId}:permissions:${role}`;
169
+ const cacheExists = await redis_manager_1.redis.exists(cacheKey);
170
+ if (cacheExists) {
171
+ await redis_manager_1.redis.del(cacheKey);
172
+ console.info(`Cache invalidated for tenant ${tenantId} and role ${role}`);
173
+ }
174
+ else {
175
+ console.warn(`No cache found for tenant ${tenantId} and role ${role}`);
176
+ }
177
+ }
178
+ catch (error) {
179
+ console.error(`Error invalidating cache for tenant ${tenantId} and role ${role}:`, error);
180
+ throw new Error(`Failed to invalidate cache for tenant ${tenantId} and role ${role}`);
181
+ }
182
+ return;
183
+ }
184
+ /**
185
+ * Middleware to enforce RBAC (Role-Based Access Control) on GraphQL resolvers
186
+ * @param resolver The original resolver function
187
+ * @param permission The required permission for the action
188
+ * @returns A wrapped resolver function that checks permissions
189
+ * @throws {Error} If the user does not have the required permission
190
+ * @example
191
+ * // Wrap a resolver with RBAC enforcement
192
+ * const securedResolver = enforceRBAC(originalResolver, 'createUser');
193
+ *
194
+ * // Use the secured resolver in your GraphQL schema
195
+ * const resolvers = {
196
+ * Mutation: {
197
+ * createUser: securedResolver,
198
+ * }, };
199
+ *
200
+ * In this example, the `enforceRBAC` function wraps the `originalResolver` and checks if the user has the 'createUser' permission before allowing access to the resolver. If the user lacks the required permission, an error is thrown, preventing unauthorized access.
201
+ * * This middleware is essential for implementing fine-grained access control in GraphQL APIs, ensuring that only authorized users can perform specific actions based on their roles and permissions.
202
+ * @remarks This function is designed to be used in GraphQL resolvers to enforce role-based access control (RBAC). It checks if the user has the necessary permissions before allowing access to the resolver function. If the user lacks the required permission, an error is thrown, preventing unauthorized access.
203
+ * * @see {@link checkPermission} for checking user permissions
204
+ * * @see {@link Role} and {@link Permission} for role and permission types
205
+ * * @see {@link rolePermissions} for the mapping of roles to permissions
206
+ * * @see {@link getCachedData} and {@link setCachedData} for caching permissions
207
+ */
208
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
209
+ function enforceRBAC(resolver, permission) {
210
+ const wrapped = async (parent, args, context, info) => {
211
+ const { user } = context;
212
+ // Ensure we pass the user's role (string) and await the async permission check
213
+ if (!user || !(await checkPermission(user.role, permission))) {
214
+ throw new Error('Unauthorized: You do not have permission to perform this action.');
215
+ }
216
+ return await resolver(parent, args, context, info);
217
+ };
218
+ // Preserve function name (useful for logging, debugging)
219
+ Object.defineProperty(wrapped, 'name', {
220
+ value: resolver.name || 'anonymous',
221
+ writable: false,
222
+ configurable: true,
223
+ });
224
+ // Attach metadata
225
+ wrapped.__originalResolver = resolver;
226
+ wrapped.__rbacAction = permission;
227
+ return wrapped;
228
+ }
229
+ /**
230
+ * Get the RBAC action from a wrapped resolver
231
+ * @param resolver The wrapped resolver function
232
+ * @returns The RBAC action
233
+ */
234
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
235
+ function getRBACAction(resolver) {
236
+ return resolver.__rbacAction || null;
237
+ }
238
+ /**
239
+ * Get the original resolver function from a wrapped resolver
240
+ * @param resolver The wrapped resolver function
241
+ * @returns The original resolver function
242
+ */
243
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
244
+ function getOriginalResolver(resolver) {
245
+ return resolver.__originalResolver || resolver;
246
+ }
@@ -0,0 +1,4 @@
1
+ export type Role = 'admin' | 'user' | 'viewer';
2
+ export type Permission = 'create' | 'read' | 'update' | 'delete';
3
+ export declare const rolePermissions: Record<Role, Permission[]>;
4
+ //# sourceMappingURL=rbacTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbacTypes.d.ts","sourceRoot":"","sources":["../../../src/middleware/rbac/rbacTypes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAC/C,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,CAItD,CAAC"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rolePermissions = void 0;
4
+ exports.rolePermissions = {
5
+ admin: ['create', 'read', 'update', 'delete'],
6
+ user: ['create', 'read', 'update'],
7
+ viewer: ['read'],
8
+ };
@@ -0,0 +1,3 @@
1
+ export * from './softDelete/applySoftDelete';
2
+ export * from './softDelete/types';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./softDelete/applySoftDelete"), exports);
5
+ tslib_1.__exportStar(require("./softDelete/types"), exports);
@@ -0,0 +1,5 @@
1
+ import type { Document } from 'mongodb';
2
+ import type { AmbitenModel } from '../../lib-core';
3
+ import type { SoftDeleteOptions } from './types';
4
+ export declare function applySoftDelete<T extends Document>(model: AmbitenModel<T>, opts?: SoftDeleteOptions): AmbitenModel<T>;
5
+ //# sourceMappingURL=applySoftDelete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"applySoftDelete.d.ts","sourceRoot":"","sources":["../../../src/plugins/softDelete/applySoftDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAwB,MAAM,SAAS,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAmEjD,wBAAgB,eAAe,CAAC,CAAC,SAAS,QAAQ,EAChD,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EACtB,IAAI,GAAE,iBAAsB,GAC3B,YAAY,CAAC,CAAC,CAAC,CA+GjB"}
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applySoftDelete = applySoftDelete;
4
+ const DEFAULTS = {
5
+ field: 'deletedAt',
6
+ deletedAtField: 'deletedAt',
7
+ deletedByField: 'deletedBy',
8
+ isDeletedField: 'isDeleted',
9
+ enabled: true,
10
+ excludeDeletedByDefault: true
11
+ };
12
+ function buildNotDeletedFilter(options) {
13
+ return {
14
+ $and: [
15
+ {
16
+ $or: [
17
+ { [options.deletedAtField]: { $exists: false } },
18
+ { [options.deletedAtField]: null }
19
+ ]
20
+ },
21
+ {
22
+ $or: [
23
+ { [options.isDeletedField]: { $exists: false } },
24
+ { [options.isDeletedField]: false }
25
+ ]
26
+ }
27
+ ]
28
+ };
29
+ }
30
+ function buildOnlyDeletedFilter(options) {
31
+ return {
32
+ $or: [
33
+ { [options.deletedAtField]: { $ne: null } },
34
+ { [options.isDeletedField]: true }
35
+ ]
36
+ };
37
+ }
38
+ function mergeFilter(original, extra) {
39
+ if (!original || Object.keys(original).length === 0) {
40
+ return extra;
41
+ }
42
+ return {
43
+ $and: [original, extra]
44
+ };
45
+ }
46
+ function buildSoftDeleteUpdate(options) {
47
+ return {
48
+ $set: {
49
+ [options.deletedAtField]: new Date(),
50
+ [options.isDeletedField]: true
51
+ }
52
+ };
53
+ }
54
+ function applySoftDelete(model, opts = {}) {
55
+ const options = { ...DEFAULTS, ...opts };
56
+ if (!options.enabled) {
57
+ return model;
58
+ }
59
+ const applyReadFilter = (ctx) => {
60
+ if (ctx.withDeleted === true) {
61
+ return;
62
+ }
63
+ if (ctx.onlyDeleted === true) {
64
+ ctx.filter = mergeFilter(ctx.filter, buildOnlyDeletedFilter(options));
65
+ return;
66
+ }
67
+ if (options.excludeDeletedByDefault) {
68
+ ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
69
+ }
70
+ };
71
+ model.beforeFind((ctx) => {
72
+ applyReadFilter(ctx);
73
+ });
74
+ model.beforeFindOne((ctx) => {
75
+ applyReadFilter(ctx);
76
+ });
77
+ model.beforeFindOneAndUpdate((ctx) => {
78
+ applyReadFilter(ctx);
79
+ });
80
+ model.beforeFindOneAndReplace((ctx) => {
81
+ applyReadFilter(ctx);
82
+ });
83
+ model.beforeFindOneAndUpsert((ctx) => {
84
+ applyReadFilter(ctx);
85
+ });
86
+ model.beforeAggregate((ctx) => {
87
+ if (ctx.withDeleted === true) {
88
+ return;
89
+ }
90
+ const stage = ctx.onlyDeleted === true
91
+ ? { $match: buildOnlyDeletedFilter(options) }
92
+ : options.excludeDeletedByDefault
93
+ ? { $match: buildNotDeletedFilter(options) }
94
+ : null;
95
+ if (!stage) {
96
+ return;
97
+ }
98
+ ctx.pipeline = [stage, ...(ctx.pipeline ?? [])];
99
+ });
100
+ model.beforeDeleteOne((ctx) => {
101
+ if (ctx.hardDelete === true) {
102
+ return;
103
+ }
104
+ ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
105
+ ctx.update = buildSoftDeleteUpdate(options);
106
+ ctx.meta = {
107
+ ...(ctx.meta ?? {}),
108
+ softDelete: true
109
+ };
110
+ });
111
+ model.beforeDeleteMany((ctx) => {
112
+ if (ctx.hardDelete === true) {
113
+ return;
114
+ }
115
+ ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
116
+ ctx.update = buildSoftDeleteUpdate(options);
117
+ ctx.meta = {
118
+ ...(ctx.meta ?? {}),
119
+ softDelete: true
120
+ };
121
+ });
122
+ model.beforeFindOneAndDelete((ctx) => {
123
+ ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
124
+ if (ctx.hardDelete === true) {
125
+ return;
126
+ }
127
+ ctx.update = buildSoftDeleteUpdate(options);
128
+ ctx.meta = {
129
+ ...(ctx.meta ?? {}),
130
+ softDelete: true
131
+ };
132
+ });
133
+ model.setSoftDeleteConfig({
134
+ deletedAtField: options.deletedAtField,
135
+ isDeletedField: options.isDeletedField
136
+ });
137
+ return model;
138
+ }
139
+ ;
@@ -0,0 +1,14 @@
1
+ export interface SoftDeleteOptions {
2
+ field?: string;
3
+ deletedAtField?: string;
4
+ deletedByField?: string;
5
+ isDeletedField?: string;
6
+ enabled?: boolean;
7
+ excludeDeletedByDefault?: boolean;
8
+ }
9
+ export interface SoftDeletableDocument extends Document {
10
+ deletedAt?: Date | null;
11
+ isDeleted?: boolean;
12
+ deletedBy?: string | null;
13
+ }
14
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/plugins/softDelete/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,qBAAsB,SAAQ,QAAQ;IACrD,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export * from './redisClient';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/redis-manager/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./redisClient"), exports);
@@ -0,0 +1,32 @@
1
+ import 'dotenv/config';
2
+ type AmbitenRedisClient = {
3
+ isOpen: boolean;
4
+ connect(): Promise<any>;
5
+ disconnect(): Promise<any>;
6
+ quit?: () => Promise<any>;
7
+ publish(channel: string, message: string): Promise<any>;
8
+ subscribe(channel: string, listener: any): Promise<any>;
9
+ duplicate(): AmbitenRedisClient;
10
+ get(key: string): Promise<string | null>;
11
+ set(key: string, value: any, options?: any): Promise<any>;
12
+ del(...keys: string[]): Promise<number>;
13
+ exists(key: string): Promise<number>;
14
+ scan(cursor?: number | string, options?: any): Promise<any>;
15
+ multi(): any;
16
+ };
17
+ type RedisLike = AmbitenRedisClient & {
18
+ isOpen: boolean;
19
+ scan?: (...args: any[]) => Promise<any>;
20
+ };
21
+ export declare const redis: any;
22
+ export declare class RedisService {
23
+ static getInstance(): RedisService;
24
+ connect(url?: string): Promise<void>;
25
+ getClient(): Promise<RedisLike>;
26
+ close(): Promise<void>;
27
+ }
28
+ export declare function connectRedis(): Promise<{
29
+ connect: (url?: string) => Promise<any>;
30
+ }>;
31
+ export {};
32
+ //# sourceMappingURL=redisClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redisClient.d.ts","sourceRoot":"","sources":["../../src/redis-manager/redisClient.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAIvB,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IACxB,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACxD,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACxD,SAAS,IAAI,kBAAkB,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1D,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5D,KAAK,IAAI,GAAG,CAAC;CACd,CAAC;AASF,KAAK,SAAS,GAAG,kBAAkB,GAAG;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACzC,CAAC;AAiCF,eAAO,MAAM,KAAK,EAAE,GAkHnB,CAAC;AAEF,qBAAa,YAAY;IACvB,MAAM,CAAC,WAAW,IAAI,YAAY;IAI5B,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM;IAI1B,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC;IAIzB,KAAK;CAGZ;AAED,wBAAsB,YAAY;oBAER,MAAM;GAE/B"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RedisService = exports.redis = void 0;
4
+ exports.connectRedis = connectRedis;
5
+ require("dotenv/config");
6
+ const redis_1 = require("redis");
7
+ const DEFAULT_RECONNECT = (retries) => Math.min(retries * 50, 1000);
8
+ const redisUrl = process.env.REDIS_URI;
9
+ let currentClient = null;
10
+ const createStub = () => ({
11
+ isOpen: false,
12
+ connect: async () => null,
13
+ disconnect: async () => null,
14
+ publish: async () => 0,
15
+ subscribe: async () => null,
16
+ duplicate: () => createStub(),
17
+ get: async () => null,
18
+ set: async () => 'OK',
19
+ del: async () => 0,
20
+ exists: async () => 0,
21
+ scan: async () => ({
22
+ cursor: 0,
23
+ keys: [],
24
+ }),
25
+ multi: () => ({
26
+ incr() {
27
+ return this;
28
+ },
29
+ expire() {
30
+ return this;
31
+ },
32
+ exec: async () => [],
33
+ }),
34
+ });
35
+ exports.redis = {
36
+ async getClient(uri) {
37
+ if (currentClient && currentClient.isOpen) {
38
+ return currentClient;
39
+ }
40
+ const url = uri || redisUrl;
41
+ if (!url) {
42
+ return createStub();
43
+ }
44
+ try {
45
+ const client = (0, redis_1.createClient)({
46
+ url,
47
+ socket: {
48
+ reconnectStrategy: DEFAULT_RECONNECT,
49
+ },
50
+ });
51
+ await client.connect();
52
+ currentClient = client;
53
+ return client;
54
+ }
55
+ catch {
56
+ return createStub();
57
+ }
58
+ },
59
+ async disconnect() {
60
+ if (currentClient) {
61
+ try {
62
+ if (currentClient.isOpen &&
63
+ typeof currentClient.disconnect === 'function') {
64
+ await currentClient.disconnect();
65
+ }
66
+ else if (currentClient.isOpen &&
67
+ typeof currentClient.quit === 'function') {
68
+ await currentClient.quit();
69
+ }
70
+ }
71
+ catch {
72
+ // swallow disconnect errors
73
+ }
74
+ }
75
+ currentClient = null;
76
+ },
77
+ get isOpen() {
78
+ return !!currentClient?.isOpen;
79
+ },
80
+ async get(key) {
81
+ const client = await this.getClient();
82
+ return client.get?.(key);
83
+ },
84
+ async set(key, value, options) {
85
+ const client = await this.getClient();
86
+ return client.set?.(key, value, options);
87
+ },
88
+ async del(...keys) {
89
+ const client = await this.getClient();
90
+ return client.del?.(keys);
91
+ },
92
+ async exists(key) {
93
+ const client = await this.getClient();
94
+ return client.exists?.(key);
95
+ },
96
+ async scan(cursor = 0, options) {
97
+ const client = await this.getClient();
98
+ if (typeof client.scan !== 'function') {
99
+ return {
100
+ cursor: 0,
101
+ keys: [],
102
+ };
103
+ }
104
+ return client.scan(cursor, options);
105
+ },
106
+ async publish(channel, message) {
107
+ const client = await this.getClient();
108
+ return client.publish?.(channel, message);
109
+ },
110
+ async subscribe(channel, listener) {
111
+ const client = await this.getClient();
112
+ return client.subscribe?.(channel, listener);
113
+ },
114
+ duplicate() {
115
+ if (currentClient?.duplicate) {
116
+ return currentClient.duplicate();
117
+ }
118
+ return createStub();
119
+ },
120
+ multi() {
121
+ if (currentClient?.multi) {
122
+ return currentClient.multi();
123
+ }
124
+ return createStub().multi?.();
125
+ },
126
+ };
127
+ class RedisService {
128
+ static getInstance() {
129
+ return new RedisService();
130
+ }
131
+ async connect(url) {
132
+ await exports.redis.getClient(url);
133
+ }
134
+ getClient() {
135
+ return exports.redis.getClient();
136
+ }
137
+ async close() {
138
+ await exports.redis.disconnect();
139
+ }
140
+ }
141
+ exports.RedisService = RedisService;
142
+ async function connectRedis() {
143
+ return {
144
+ connect: async (url) => exports.redis.getClient(url),
145
+ };
146
+ }