@openora/core 0.4.0 → 0.4.1-canary.11

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 (427) hide show
  1. package/LICENSE-COMMERCIAL.md +2 -2
  2. package/README.md +8 -2
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/admin-console/__tests__/backoffice.service.test.js +1 -1
  5. package/dist/admin-console/__tests__/backoffice.service.test.js.map +1 -1
  6. package/dist/admin-console/contract/index.d.ts +14 -14
  7. package/dist/admin-console/contract/index.d.ts.map +1 -1
  8. package/dist/admin-console/contract/index.js +8 -8
  9. package/dist/admin-console/contract/index.js.map +1 -1
  10. package/dist/admin-console/router/index.d.ts +7 -7
  11. package/dist/admin-console/service/backoffice.service.d.ts +22 -49
  12. package/dist/admin-console/service/backoffice.service.d.ts.map +1 -1
  13. package/dist/admin-console/service/backoffice.service.js +16 -31
  14. package/dist/admin-console/service/backoffice.service.js.map +1 -1
  15. package/dist/audit/__tests__/audit.service.test.js +29 -2
  16. package/dist/audit/__tests__/audit.service.test.js.map +1 -1
  17. package/dist/audit/contract/index.d.ts.map +1 -1
  18. package/dist/audit/contract/index.js +2 -0
  19. package/dist/audit/contract/index.js.map +1 -1
  20. package/dist/audit/plugin.d.ts.map +1 -1
  21. package/dist/audit/plugin.js +54 -4
  22. package/dist/audit/plugin.js.map +1 -1
  23. package/dist/audit/service/audit.service.d.ts +1 -0
  24. package/dist/audit/service/audit.service.d.ts.map +1 -1
  25. package/dist/audit/service/audit.service.js +53 -25
  26. package/dist/audit/service/audit.service.js.map +1 -1
  27. package/dist/casino/gaming/contract/index.d.ts.map +1 -1
  28. package/dist/casino/gaming/contract/index.js +3 -3
  29. package/dist/casino/gaming/contract/index.js.map +1 -1
  30. package/dist/casino/gaming/drizzle/migrations/0001_easy_ben_grimm.sql +4 -0
  31. package/dist/casino/gaming/drizzle/migrations/meta/0001_snapshot.json +188 -0
  32. package/dist/casino/gaming/drizzle/migrations/meta/_journal.json +7 -0
  33. package/dist/casino/gaming/schema/index.js +2 -2
  34. package/dist/casino/gaming/schema/index.js.map +1 -1
  35. package/dist/casino/gaming/service/gaming.service.d.ts +11 -11
  36. package/dist/casino/gaming/service/gaming.service.d.ts.map +1 -1
  37. package/dist/casino/gaming/service/gaming.service.js +4 -14
  38. package/dist/casino/gaming/service/gaming.service.js.map +1 -1
  39. package/dist/cms/__tests__/cms.cache.test.js +1 -1
  40. package/dist/cms/__tests__/cms.cache.test.js.map +1 -1
  41. package/dist/cms/__tests__/cms.router.test.d.ts +2 -0
  42. package/dist/cms/__tests__/cms.router.test.d.ts.map +1 -0
  43. package/dist/cms/__tests__/cms.router.test.js +51 -0
  44. package/dist/cms/__tests__/cms.router.test.js.map +1 -0
  45. package/dist/cms/router/index.d.ts.map +1 -1
  46. package/dist/cms/router/index.js +12 -12
  47. package/dist/cms/router/index.js.map +1 -1
  48. package/dist/cms/service/cms.service.d.ts +7 -7
  49. package/dist/cms/service/cms.service.d.ts.map +1 -1
  50. package/dist/cms/service/cms.service.js +39 -23
  51. package/dist/cms/service/cms.service.js.map +1 -1
  52. package/dist/compliance/__tests__/compliance.service.test.js +2 -1
  53. package/dist/compliance/__tests__/compliance.service.test.js.map +1 -1
  54. package/dist/compliance/__tests__/kyc.service.test.js +12 -10
  55. package/dist/compliance/__tests__/kyc.service.test.js.map +1 -1
  56. package/dist/compliance/__tests__/rg-monitoring.service.test.js +17 -6
  57. package/dist/compliance/__tests__/rg-monitoring.service.test.js.map +1 -1
  58. package/dist/compliance/__tests__/rg.contract.test.js +18 -10
  59. package/dist/compliance/__tests__/rg.contract.test.js.map +1 -1
  60. package/dist/compliance/__tests__/rg.router.test.js +5 -4
  61. package/dist/compliance/__tests__/rg.router.test.js.map +1 -1
  62. package/dist/compliance/__tests__/rg.service.test.js +18 -12
  63. package/dist/compliance/__tests__/rg.service.test.js.map +1 -1
  64. package/dist/compliance/contract/index.d.ts +20 -14
  65. package/dist/compliance/contract/index.d.ts.map +1 -1
  66. package/dist/compliance/contract/limits.d.ts +9 -2
  67. package/dist/compliance/contract/limits.d.ts.map +1 -1
  68. package/dist/compliance/contract/limits.js +21 -3
  69. package/dist/compliance/contract/limits.js.map +1 -1
  70. package/dist/compliance/contract/rg.d.ts +28 -23
  71. package/dist/compliance/contract/rg.d.ts.map +1 -1
  72. package/dist/compliance/contract/rg.js +14 -9
  73. package/dist/compliance/contract/rg.js.map +1 -1
  74. package/dist/compliance/drizzle/migrations/0001_funny_vance_astro.sql +5 -0
  75. package/dist/compliance/drizzle/migrations/meta/0001_snapshot.json +660 -0
  76. package/dist/compliance/drizzle/migrations/meta/_journal.json +7 -0
  77. package/dist/compliance/plugin.d.ts.map +1 -1
  78. package/dist/compliance/plugin.js +26 -12
  79. package/dist/compliance/plugin.js.map +1 -1
  80. package/dist/compliance/router/index.d.ts +20 -14
  81. package/dist/compliance/router/index.d.ts.map +1 -1
  82. package/dist/compliance/router/index.js +2 -1
  83. package/dist/compliance/router/index.js.map +1 -1
  84. package/dist/compliance/schema/index.d.ts +31 -14
  85. package/dist/compliance/schema/index.d.ts.map +1 -1
  86. package/dist/compliance/schema/index.js +10 -7
  87. package/dist/compliance/schema/index.js.map +1 -1
  88. package/dist/compliance/service/compliance.service.d.ts +27 -22
  89. package/dist/compliance/service/compliance.service.d.ts.map +1 -1
  90. package/dist/compliance/service/compliance.service.js +9 -22
  91. package/dist/compliance/service/compliance.service.js.map +1 -1
  92. package/dist/compliance/service/kyc.service.d.ts +22 -19
  93. package/dist/compliance/service/kyc.service.d.ts.map +1 -1
  94. package/dist/compliance/service/kyc.service.js +19 -24
  95. package/dist/compliance/service/kyc.service.js.map +1 -1
  96. package/dist/compliance/service/re-kyc-trigger.d.ts +4 -4
  97. package/dist/compliance/service/re-kyc-trigger.d.ts.map +1 -1
  98. package/dist/compliance/service/re-kyc-trigger.js +10 -6
  99. package/dist/compliance/service/re-kyc-trigger.js.map +1 -1
  100. package/dist/compliance/service/rg-eval.d.ts.map +1 -1
  101. package/dist/compliance/service/rg-eval.js +4 -2
  102. package/dist/compliance/service/rg-eval.js.map +1 -1
  103. package/dist/compliance/service/rg-monitoring.service.d.ts +6 -5
  104. package/dist/compliance/service/rg-monitoring.service.d.ts.map +1 -1
  105. package/dist/compliance/service/rg-monitoring.service.js +62 -31
  106. package/dist/compliance/service/rg-monitoring.service.js.map +1 -1
  107. package/dist/compliance/service/rg.service.d.ts +28 -6
  108. package/dist/compliance/service/rg.service.d.ts.map +1 -1
  109. package/dist/compliance/service/rg.service.js +60 -34
  110. package/dist/compliance/service/rg.service.js.map +1 -1
  111. package/dist/contracts/adapters/admin-wallet-reporting.d.ts +5 -5
  112. package/dist/contracts/adapters/payment.d.ts +2 -2
  113. package/dist/contracts/adapters/wallet-commands.d.ts +5 -5
  114. package/dist/contracts/adapters/wallet-reader.d.ts +2 -2
  115. package/dist/contracts/adapters/wallet-reader.d.ts.map +1 -1
  116. package/dist/contracts/schemas/common.d.ts +2 -5
  117. package/dist/contracts/schemas/common.d.ts.map +1 -1
  118. package/dist/contracts/schemas/common.js +3 -4
  119. package/dist/contracts/schemas/common.js.map +1 -1
  120. package/dist/contracts/schemas/events.d.ts +35 -9
  121. package/dist/contracts/schemas/events.d.ts.map +1 -1
  122. package/dist/contracts/schemas/events.js +32 -7
  123. package/dist/contracts/schemas/events.js.map +1 -1
  124. package/dist/contracts/schemas/platform-config.d.ts +9 -9
  125. package/dist/contracts/schemas/platform-config.d.ts.map +1 -1
  126. package/dist/contracts/schemas/platform-config.js +6 -5
  127. package/dist/contracts/schemas/platform-config.js.map +1 -1
  128. package/dist/contracts/schemas/player.d.ts +2 -2
  129. package/dist/contracts/schemas/player.js +3 -3
  130. package/dist/contracts/schemas/player.js.map +1 -1
  131. package/dist/contracts/schemas/tag.d.ts +2 -2
  132. package/dist/contracts/schemas/tag.js +3 -3
  133. package/dist/contracts/schemas/tag.js.map +1 -1
  134. package/dist/engagement/chat/moderation/moderate.d.ts.map +1 -1
  135. package/dist/engagement/chat/moderation/moderate.js +2 -1
  136. package/dist/engagement/chat/moderation/moderate.js.map +1 -1
  137. package/dist/engagement/chat/react/use-chat-stream.d.ts +2 -3
  138. package/dist/engagement/chat/react/use-chat-stream.d.ts.map +1 -1
  139. package/dist/engagement/chat/react/use-chat-stream.js +4 -2
  140. package/dist/engagement/chat/react/use-chat-stream.js.map +1 -1
  141. package/dist/engagement/chat/router/index.d.ts.map +1 -1
  142. package/dist/engagement/chat/router/index.js +2 -1
  143. package/dist/engagement/chat/router/index.js.map +1 -1
  144. package/dist/engagement/chat/service/chat.service.d.ts.map +1 -1
  145. package/dist/engagement/chat/service/chat.service.js +22 -11
  146. package/dist/engagement/chat/service/chat.service.js.map +1 -1
  147. package/dist/engagement/notifications/__tests__/notifications.service.test.js +4 -3
  148. package/dist/engagement/notifications/__tests__/notifications.service.test.js.map +1 -1
  149. package/dist/engagement/notifications/contract/index.d.ts +24 -2
  150. package/dist/engagement/notifications/contract/index.d.ts.map +1 -1
  151. package/dist/engagement/notifications/contract/index.js +10 -1
  152. package/dist/engagement/notifications/contract/index.js.map +1 -1
  153. package/dist/engagement/notifications/index.d.ts +1 -1
  154. package/dist/engagement/notifications/index.d.ts.map +1 -1
  155. package/dist/engagement/notifications/plugin.d.ts.map +1 -1
  156. package/dist/engagement/notifications/plugin.js +6 -3
  157. package/dist/engagement/notifications/plugin.js.map +1 -1
  158. package/dist/engagement/notifications/router/index.d.ts +4 -1
  159. package/dist/engagement/notifications/router/index.d.ts.map +1 -1
  160. package/dist/engagement/notifications/router/index.js +15 -6
  161. package/dist/engagement/notifications/router/index.js.map +1 -1
  162. package/dist/engagement/notifications/service/notifications.service.d.ts +6 -13
  163. package/dist/engagement/notifications/service/notifications.service.d.ts.map +1 -1
  164. package/dist/engagement/notifications/service/notifications.service.js +6 -14
  165. package/dist/engagement/notifications/service/notifications.service.js.map +1 -1
  166. package/dist/iam/__tests__/iam.service.test.js +14 -9
  167. package/dist/iam/__tests__/iam.service.test.js.map +1 -1
  168. package/dist/iam/plugin.d.ts.map +1 -1
  169. package/dist/iam/plugin.js +4 -2
  170. package/dist/iam/plugin.js.map +1 -1
  171. package/dist/iam/seed/seed-default-roles.d.ts.map +1 -1
  172. package/dist/iam/seed/seed-default-roles.js +2 -1
  173. package/dist/iam/seed/seed-default-roles.js.map +1 -1
  174. package/dist/iam/service/iam.service.d.ts +19 -17
  175. package/dist/iam/service/iam.service.d.ts.map +1 -1
  176. package/dist/iam/service/iam.service.js +68 -34
  177. package/dist/iam/service/iam.service.js.map +1 -1
  178. package/dist/pam/identity/admin-user-directory.d.ts.map +1 -1
  179. package/dist/pam/identity/admin-user-directory.js +18 -9
  180. package/dist/pam/identity/admin-user-directory.js.map +1 -1
  181. package/dist/pam/identity/router/index.d.ts.map +1 -1
  182. package/dist/pam/identity/router/index.js +4 -2
  183. package/dist/pam/identity/router/index.js.map +1 -1
  184. package/dist/pam/identity/service/identity.service.d.ts +24 -2
  185. package/dist/pam/identity/service/identity.service.d.ts.map +1 -1
  186. package/dist/pam/identity/service/identity.service.js +51 -19
  187. package/dist/pam/identity/service/identity.service.js.map +1 -1
  188. package/dist/pam/identity/service/login-enforcement.service.d.ts +3 -3
  189. package/dist/pam/identity/service/login-enforcement.service.d.ts.map +1 -1
  190. package/dist/pam/identity/service/login-enforcement.service.js.map +1 -1
  191. package/dist/pam/identity/service/session.service.d.ts +5 -3
  192. package/dist/pam/identity/service/session.service.d.ts.map +1 -1
  193. package/dist/pam/identity/service/session.service.js +3 -3
  194. package/dist/pam/identity/service/session.service.js.map +1 -1
  195. package/dist/pam/player-management/__tests__/kyc-status-writer.test.js +2 -1
  196. package/dist/pam/player-management/__tests__/kyc-status-writer.test.js.map +1 -1
  197. package/dist/pam/player-management/__tests__/player.router.test.js +2 -2
  198. package/dist/pam/player-management/__tests__/player.router.test.js.map +1 -1
  199. package/dist/pam/player-management/__tests__/player.service.test.js +2 -1
  200. package/dist/pam/player-management/__tests__/player.service.test.js.map +1 -1
  201. package/dist/pam/player-management/contract/index.d.ts +10 -10
  202. package/dist/pam/player-management/router/index.d.ts +8 -8
  203. package/dist/pam/player-management/service/kyc-status-writer.d.ts.map +1 -1
  204. package/dist/pam/player-management/service/kyc-status-writer.js +2 -1
  205. package/dist/pam/player-management/service/kyc-status-writer.js.map +1 -1
  206. package/dist/pam/player-management/service/player.service.d.ts +15 -15
  207. package/dist/pam/player-management/service/player.service.d.ts.map +1 -1
  208. package/dist/pam/player-management/service/player.service.js +15 -12
  209. package/dist/pam/player-management/service/player.service.js.map +1 -1
  210. package/dist/pam/player-note/schema/index.d.ts +4 -4
  211. package/dist/pam/player-note/schema/index.js +6 -6
  212. package/dist/pam/player-note/schema/index.js.map +1 -1
  213. package/dist/pam/player-note/service/player-note.service.d.ts +11 -3
  214. package/dist/pam/player-note/service/player-note.service.d.ts.map +1 -1
  215. package/dist/pam/player-note/service/player-note.service.js +1 -1
  216. package/dist/pam/player-note/service/player-note.service.js.map +1 -1
  217. package/dist/pam/profile/__tests__/profile.service.test.js +2 -1
  218. package/dist/pam/profile/__tests__/profile.service.test.js.map +1 -1
  219. package/dist/pam/profile/contract/index.d.ts +4 -4
  220. package/dist/pam/profile/react/profile.d.ts +6 -7
  221. package/dist/pam/profile/react/profile.d.ts.map +1 -1
  222. package/dist/pam/profile/react/profile.js.map +1 -1
  223. package/dist/pam/profile/router/index.d.ts +4 -4
  224. package/dist/pam/profile/service/profile.service.d.ts +4 -4
  225. package/dist/pam/profile/service/profile.service.d.ts.map +1 -1
  226. package/dist/pam/profile/service/profile.service.js +2 -1
  227. package/dist/pam/profile/service/profile.service.js.map +1 -1
  228. package/dist/pam/shared/headers-mapper.d.ts.map +1 -1
  229. package/dist/pam/shared/headers-mapper.js +2 -1
  230. package/dist/pam/shared/headers-mapper.js.map +1 -1
  231. package/dist/pam/shared/player-mapper.d.ts +17 -2
  232. package/dist/pam/shared/player-mapper.d.ts.map +1 -1
  233. package/dist/pam/shared/player-mapper.js +2 -2
  234. package/dist/pam/shared/player-mapper.js.map +1 -1
  235. package/dist/pam/tag/__tests__/get-active-tag-keys.test.js +2 -1
  236. package/dist/pam/tag/__tests__/get-active-tag-keys.test.js.map +1 -1
  237. package/dist/pam/tag/__tests__/tag-evaluation.service.test.js +19 -18
  238. package/dist/pam/tag/__tests__/tag-evaluation.service.test.js.map +1 -1
  239. package/dist/pam/tag/__tests__/tag-mappers.test.js +6 -6
  240. package/dist/pam/tag/__tests__/tag-mappers.test.js.map +1 -1
  241. package/dist/pam/tag/__tests__/tag-rule.service.test.js +14 -14
  242. package/dist/pam/tag/__tests__/tag-rule.service.test.js.map +1 -1
  243. package/dist/pam/tag/__tests__/tag.router.test.d.ts +2 -0
  244. package/dist/pam/tag/__tests__/tag.router.test.d.ts.map +1 -0
  245. package/dist/pam/tag/__tests__/tag.router.test.js +38 -0
  246. package/dist/pam/tag/__tests__/tag.router.test.js.map +1 -0
  247. package/dist/pam/tag/contract/index.d.ts +3 -3
  248. package/dist/pam/tag/drizzle/migrations/0001_neat_iron_monger.sql +2 -0
  249. package/dist/pam/tag/drizzle/migrations/meta/0001_snapshot.json +295 -0
  250. package/dist/pam/tag/drizzle/migrations/meta/_journal.json +7 -0
  251. package/dist/pam/tag/router/index.d.ts +3 -3
  252. package/dist/pam/tag/schema/index.d.ts +16 -16
  253. package/dist/pam/tag/schema/index.d.ts.map +1 -1
  254. package/dist/pam/tag/schema/index.js +19 -19
  255. package/dist/pam/tag/schema/index.js.map +1 -1
  256. package/dist/pam/tag/seed/data/default-tag-rules.js +6 -6
  257. package/dist/pam/tag/seed/data/default-tag-rules.js.map +1 -1
  258. package/dist/pam/tag/seed/seed-default-tag-rules.d.ts.map +1 -1
  259. package/dist/pam/tag/seed/seed-default-tag-rules.js +5 -3
  260. package/dist/pam/tag/seed/seed-default-tag-rules.js.map +1 -1
  261. package/dist/pam/tag/service/tag-evaluation.service.d.ts.map +1 -1
  262. package/dist/pam/tag/service/tag-evaluation.service.js +32 -18
  263. package/dist/pam/tag/service/tag-evaluation.service.js.map +1 -1
  264. package/dist/pam/tag/service/tag-mappers.d.ts +8 -2
  265. package/dist/pam/tag/service/tag-mappers.d.ts.map +1 -1
  266. package/dist/pam/tag/service/tag-mappers.js +3 -8
  267. package/dist/pam/tag/service/tag-mappers.js.map +1 -1
  268. package/dist/pam/tag/service/tag-rule.service.d.ts +34 -4
  269. package/dist/pam/tag/service/tag-rule.service.d.ts.map +1 -1
  270. package/dist/pam/tag/service/tag-rule.service.js +9 -15
  271. package/dist/pam/tag/service/tag-rule.service.js.map +1 -1
  272. package/dist/pam/tag/service/tag.service.d.ts +51 -8
  273. package/dist/pam/tag/service/tag.service.d.ts.map +1 -1
  274. package/dist/pam/tag/service/tag.service.js +6 -11
  275. package/dist/pam/tag/service/tag.service.js.map +1 -1
  276. package/dist/react/context/api-client.d.ts.map +1 -1
  277. package/dist/react/context/api-client.js +14 -7
  278. package/dist/react/context/api-client.js.map +1 -1
  279. package/dist/react/hooks/use-event-stream.d.ts.map +1 -1
  280. package/dist/react/hooks/use-event-stream.js +6 -3
  281. package/dist/react/hooks/use-event-stream.js.map +1 -1
  282. package/dist/react/hooks/use-paginated-list.js +3 -3
  283. package/dist/react/hooks/use-paginated-list.js.map +1 -1
  284. package/dist/server/auth/admin-guard.d.ts +12 -0
  285. package/dist/server/auth/admin-guard.d.ts.map +1 -1
  286. package/dist/server/auth/admin-guard.js +14 -1
  287. package/dist/server/auth/admin-guard.js.map +1 -1
  288. package/dist/server/auth/auth.d.ts +9 -0
  289. package/dist/server/auth/auth.d.ts.map +1 -1
  290. package/dist/server/auth/auth.js +9 -1
  291. package/dist/server/auth/auth.js.map +1 -1
  292. package/dist/server/auth/permission-levels.d.ts.map +1 -1
  293. package/dist/server/auth/permission-levels.js +12 -6
  294. package/dist/server/auth/permission-levels.js.map +1 -1
  295. package/dist/server/db/drizzle.d.ts.map +1 -1
  296. package/dist/server/db/drizzle.js +2 -1
  297. package/dist/server/db/drizzle.js.map +1 -1
  298. package/dist/server/db/drizzle.service.d.ts.map +1 -1
  299. package/dist/server/db/drizzle.service.js +2 -1
  300. package/dist/server/db/drizzle.service.js.map +1 -1
  301. package/dist/server/db/index.d.ts +1 -1
  302. package/dist/server/db/index.d.ts.map +1 -1
  303. package/dist/server/db/index.js +1 -1
  304. package/dist/server/db/index.js.map +1 -1
  305. package/dist/server/db/migrate.d.ts.map +1 -1
  306. package/dist/server/db/migrate.js +2 -1
  307. package/dist/server/db/migrate.js.map +1 -1
  308. package/dist/server/db/outbox/__tests__/relay.test.js +4 -2
  309. package/dist/server/db/outbox/__tests__/relay.test.js.map +1 -1
  310. package/dist/server/db/outbox/relay.d.ts +15 -0
  311. package/dist/server/db/outbox/relay.d.ts.map +1 -1
  312. package/dist/server/db/outbox/relay.js +21 -14
  313. package/dist/server/db/outbox/relay.js.map +1 -1
  314. package/dist/server/db/query-helpers.d.ts +2 -0
  315. package/dist/server/db/query-helpers.d.ts.map +1 -1
  316. package/dist/server/db/query-helpers.js +28 -1
  317. package/dist/server/db/query-helpers.js.map +1 -1
  318. package/dist/server/kernel/__tests__/event-bus.test.js +10 -7
  319. package/dist/server/kernel/__tests__/event-bus.test.js.map +1 -1
  320. package/dist/server/kernel/__tests__/job-queue.test.js +5 -2
  321. package/dist/server/kernel/__tests__/job-queue.test.js.map +1 -1
  322. package/dist/server/kernel/__tests__/rate-limiter.test.js +10 -5
  323. package/dist/server/kernel/__tests__/rate-limiter.test.js.map +1 -1
  324. package/dist/server/kernel/__tests__/redis-cache.test.js +2 -1
  325. package/dist/server/kernel/__tests__/redis-cache.test.js.map +1 -1
  326. package/dist/server/kernel/bullmq-job-queue.d.ts +12 -0
  327. package/dist/server/kernel/bullmq-job-queue.d.ts.map +1 -1
  328. package/dist/server/kernel/bullmq-job-queue.js +21 -4
  329. package/dist/server/kernel/bullmq-job-queue.js.map +1 -1
  330. package/dist/server/kernel/cache.d.ts.map +1 -1
  331. package/dist/server/kernel/cache.js +14 -7
  332. package/dist/server/kernel/cache.js.map +1 -1
  333. package/dist/server/kernel/container.d.ts +10 -0
  334. package/dist/server/kernel/container.d.ts.map +1 -1
  335. package/dist/server/kernel/container.js +12 -1
  336. package/dist/server/kernel/container.js.map +1 -1
  337. package/dist/server/kernel/event-bus.d.ts +9 -0
  338. package/dist/server/kernel/event-bus.d.ts.map +1 -1
  339. package/dist/server/kernel/event-bus.js +9 -1
  340. package/dist/server/kernel/event-bus.js.map +1 -1
  341. package/dist/server/kernel/event-stream.d.ts.map +1 -1
  342. package/dist/server/kernel/event-stream.js +2 -1
  343. package/dist/server/kernel/event-stream.js.map +1 -1
  344. package/dist/server/kernel/job-queue.d.ts +12 -0
  345. package/dist/server/kernel/job-queue.d.ts.map +1 -1
  346. package/dist/server/kernel/job-queue.js +38 -15
  347. package/dist/server/kernel/job-queue.js.map +1 -1
  348. package/dist/server/kernel/ownership.d.ts.map +1 -1
  349. package/dist/server/kernel/ownership.js +2 -1
  350. package/dist/server/kernel/ownership.js.map +1 -1
  351. package/dist/server/kernel/platform-config-loader.d.ts.map +1 -1
  352. package/dist/server/kernel/platform-config-loader.js +34 -16
  353. package/dist/server/kernel/platform-config-loader.js.map +1 -1
  354. package/dist/server/kernel/rate-limiter.d.ts.map +1 -1
  355. package/dist/server/kernel/rate-limiter.js +6 -3
  356. package/dist/server/kernel/rate-limiter.js.map +1 -1
  357. package/dist/server/kernel/realtime-transport.d.ts.map +1 -1
  358. package/dist/server/kernel/realtime-transport.js +10 -5
  359. package/dist/server/kernel/realtime-transport.js.map +1 -1
  360. package/dist/server/kernel/redis-cache.d.ts.map +1 -1
  361. package/dist/server/kernel/redis-cache.js +10 -5
  362. package/dist/server/kernel/redis-cache.js.map +1 -1
  363. package/dist/server/kernel/redis-rate-limiter.d.ts +11 -0
  364. package/dist/server/kernel/redis-rate-limiter.d.ts.map +1 -1
  365. package/dist/server/kernel/redis-rate-limiter.js +13 -1
  366. package/dist/server/kernel/redis-rate-limiter.js.map +1 -1
  367. package/dist/server/kernel/serialize-row.d.ts.map +1 -1
  368. package/dist/server/kernel/serialize-row.js +4 -2
  369. package/dist/server/kernel/serialize-row.js.map +1 -1
  370. package/dist/server/plugin-host/load-extensions.js +2 -1
  371. package/dist/server/plugin-host/load-extensions.js.map +1 -1
  372. package/dist/server/plugin-host/load-plugins.d.ts.map +1 -1
  373. package/dist/server/plugin-host/load-plugins.js +4 -2
  374. package/dist/server/plugin-host/load-plugins.js.map +1 -1
  375. package/dist/server/plugin-host/service-manifest.d.ts.map +1 -1
  376. package/dist/server/plugin-host/service-manifest.js +4 -2
  377. package/dist/server/plugin-host/service-manifest.js.map +1 -1
  378. package/dist/server/runtime/create-app.d.ts +12 -0
  379. package/dist/server/runtime/create-app.d.ts.map +1 -1
  380. package/dist/server/runtime/create-app.js +27 -6
  381. package/dist/server/runtime/create-app.js.map +1 -1
  382. package/dist/wallet/__tests__/admin-reporting.test.js +2 -2
  383. package/dist/wallet/__tests__/admin-reporting.test.js.map +1 -1
  384. package/dist/wallet/__tests__/wallet-auto-rule.router.test.js +5 -5
  385. package/dist/wallet/__tests__/wallet-auto-rule.router.test.js.map +1 -1
  386. package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js +45 -34
  387. package/dist/wallet/__tests__/wallet-auto-withdrawal.test.js.map +1 -1
  388. package/dist/wallet/__tests__/wallet-commands.service.test.js +54 -37
  389. package/dist/wallet/__tests__/wallet-commands.service.test.js.map +1 -1
  390. package/dist/wallet/__tests__/wallet-kyc-gate.test.js +5 -5
  391. package/dist/wallet/__tests__/wallet-kyc-gate.test.js.map +1 -1
  392. package/dist/wallet/__tests__/wallet.rate-limit.test.js +2 -2
  393. package/dist/wallet/__tests__/wallet.rate-limit.test.js.map +1 -1
  394. package/dist/wallet/__tests__/wallet.service.test.js +62 -34
  395. package/dist/wallet/__tests__/wallet.service.test.js.map +1 -1
  396. package/dist/wallet/adapters/mock/mock-payment-adapter.d.ts +2 -2
  397. package/dist/wallet/adapters/wallet-reader.service.d.ts +1 -1
  398. package/dist/wallet/adapters/wallet-reader.service.js +1 -1
  399. package/dist/wallet/adapters/wallet-reader.service.js.map +1 -1
  400. package/dist/wallet/admin-reporting.d.ts +4 -4
  401. package/dist/wallet/admin-reporting.d.ts.map +1 -1
  402. package/dist/wallet/admin-reporting.js +8 -8
  403. package/dist/wallet/admin-reporting.js.map +1 -1
  404. package/dist/wallet/contract/index.d.ts +20 -20
  405. package/dist/wallet/contract/index.d.ts.map +1 -1
  406. package/dist/wallet/contract/index.js +14 -10
  407. package/dist/wallet/contract/index.js.map +1 -1
  408. package/dist/wallet/drizzle/migrations/0001_redundant_micromacro.sql +4 -0
  409. package/dist/wallet/drizzle/migrations/meta/0001_snapshot.json +397 -0
  410. package/dist/wallet/drizzle/migrations/meta/_journal.json +7 -0
  411. package/dist/wallet/plugin.d.ts.map +1 -1
  412. package/dist/wallet/plugin.js +5 -1
  413. package/dist/wallet/plugin.js.map +1 -1
  414. package/dist/wallet/router/index.d.ts +11 -11
  415. package/dist/wallet/schema/index.js +3 -3
  416. package/dist/wallet/schema/index.js.map +1 -1
  417. package/dist/wallet/service/wallet-commands.service.d.ts +1 -0
  418. package/dist/wallet/service/wallet-commands.service.d.ts.map +1 -1
  419. package/dist/wallet/service/wallet-commands.service.js +32 -24
  420. package/dist/wallet/service/wallet-commands.service.js.map +1 -1
  421. package/dist/wallet/service/wallet.service.d.ts +46 -18
  422. package/dist/wallet/service/wallet.service.d.ts.map +1 -1
  423. package/dist/wallet/service/wallet.service.js +152 -74
  424. package/dist/wallet/service/wallet.service.js.map +1 -1
  425. package/oxlint/oxlintrc.json +19 -3
  426. package/package.json +1 -1
  427. package/turbo-generators/src/config.ts +18 -6
@@ -1,4 +1,4 @@
1
- import { makeNotFoundError, makeConflictError, createDomainError, findOneOrThrow, pageToOffset, withAdvisoryXactLock, assertRateLimit, createLogger, } from '@openora/core/server';
1
+ import { makeNotFoundError, makeConflictError, createDomainError, findOneOrThrow, pageToOffset, withAdvisoryXactLock, assertRateLimit, createLogger, moneyToNumber, } from '@openora/core/server';
2
2
  import { eq, desc, sql, and, gte, lte, count, inArray } from 'drizzle-orm';
3
3
  import { createHash } from 'node:crypto';
4
4
  import { wallet, walletTransaction, autoWithdrawalRule, } from '../schema/index.js';
@@ -39,7 +39,8 @@ function namespacedIdempotencyKey(namespace, rawKey) {
39
39
  }
40
40
  // ponytail: a flat, currency-agnostic threshold - a starter heuristic for the review
41
41
  // queue, not a compliance risk engine. Revisit with a real risk service if/when one exists.
42
- const LARGE_WITHDRAWAL_THRESHOLD = 5000;
42
+ // moneyToNumber is the sanctioned JS conversion point for this heuristic comparison only.
43
+ const LARGE_WITHDRAWAL_THRESHOLD = '5000';
43
44
  // ponytail: >=3 withdrawals in a 24h window flags velocity; a flat count, not a per-tier rule.
44
45
  const HIGH_FREQUENCY_WINDOW_MS = 24 * 60 * 60 * 1000;
45
46
  const HIGH_FREQUENCY_MIN_COUNT = 3;
@@ -50,7 +51,7 @@ function toAutoWithdrawalRuleDto(row) {
50
51
  return {
51
52
  id: row.id,
52
53
  userId: row.userId,
53
- threshold: Number(row.threshold),
54
+ threshold: row.threshold,
54
55
  reason: row.reason,
55
56
  createdBy: row.createdBy,
56
57
  createdAt: row.createdAt.toISOString(),
@@ -62,6 +63,14 @@ function toAutoWithdrawalRuleDto(row) {
62
63
  function providerNameFor(rail) {
63
64
  return rail === 'crypto' ? 'fireblocks' : 'psp';
64
65
  }
66
+ /**
67
+ * Owns all wallet balance mutations and the withdrawal review/auto-approval
68
+ * pipeline. Every balance change is a transactional, idempotent-keyed ledger
69
+ * write (never a bare balance update) - `amount` is always a decimal string,
70
+ * mutated via SQL numeric arithmetic, never a JS float. Domain events emit only
71
+ * AFTER the owning transaction commits, and only for the non-replay branch of
72
+ * an idempotent call.
73
+ */
65
74
  export class WalletService {
66
75
  drizzle;
67
76
  events;
@@ -88,8 +97,9 @@ export class WalletService {
88
97
  }
89
98
  /** Fail-closed KYC gate: when enabled, a withdrawal needs a passing status (verified|manually_overridden). */
90
99
  async assertKycForWithdrawal(userId) {
91
- if (!this.platformConfig?.kyc?.gateWithdrawals)
100
+ if (!this.platformConfig?.kyc?.gateWithdrawals) {
92
101
  return;
102
+ }
93
103
  const status = await this.autoApprovalKycStatus(userId);
94
104
  if (!status || !KYC_PASS_STATUSES.has(status)) {
95
105
  throw new KycRequiredError();
@@ -98,13 +108,23 @@ export class WalletService {
98
108
  async getBalance(userId) {
99
109
  const [record] = await this.drizzle.db.select().from(wallet).where(eq(wallet.userId, userId));
100
110
  if (!record) {
101
- return { balance: 0, currency: 'USD' };
111
+ return { balance: '0', currency: 'USD' };
102
112
  }
103
113
  return {
104
- balance: Number(record.balance),
114
+ balance: record.balance,
105
115
  currency: record.currency,
106
116
  };
107
117
  }
118
+ /**
119
+ * `idempotencyKey` (when supplied) makes a retried deposit a no-op: a replay
120
+ * returns the original committed result WITHOUT calling the PSP again or
121
+ * crediting the balance twice, and rejects if the same key is reused with a
122
+ * different amount/currency (`IdempotencyKeyReuseError`). A GENUINE
123
+ * concurrent race (two first-attempts, same key, landing at once) can both
124
+ * reach the PSP, but the ledger insert's unique-index conflict still
125
+ * guarantees only one balance credit - the loser's write becomes a replay
126
+ * read of the winner's row.
127
+ */
108
128
  async deposit({ userId, amount, currency, provider, idempotencyKey, }) {
109
129
  await this.rateLimit(userId);
110
130
  // Short-circuit a replay BEFORE the PSP call - the common retry case (client
@@ -114,8 +134,9 @@ export class WalletService {
114
134
  let preResolvedWallet;
115
135
  if (idempotencyKey) {
116
136
  const found = await this.findDepositReplay({ userId, idempotencyKey, amount, currency });
117
- if (found.replay)
137
+ if (found.replay) {
118
138
  return found.replay;
139
+ }
119
140
  preResolvedWallet = found.walletRecord;
120
141
  }
121
142
  const psp = await this.payment.processDeposit(amount, currency, { userId, provider });
@@ -125,10 +146,7 @@ export class WalletService {
125
146
  [walletRecord] = await txn.select().from(wallet).where(eq(wallet.userId, userId));
126
147
  }
127
148
  if (!walletRecord) {
128
- [walletRecord] = await txn
129
- .insert(wallet)
130
- .values({ userId, balance: '0', currency })
131
- .returning();
149
+ walletRecord = findOneOrThrow(await txn.insert(wallet).values({ userId, balance: '0', currency }).returning(), new WalletNotFoundError(userId));
132
150
  }
133
151
  const { row, replayed } = await this.insertIdempotentTransaction(txn, {
134
152
  namespace: DEPOSIT_IDEMPOTENCY_NAMESPACE,
@@ -139,7 +157,7 @@ export class WalletService {
139
157
  values: {
140
158
  walletId: walletRecord.id,
141
159
  type: 'deposit',
142
- amount: amount.toString(),
160
+ amount,
143
161
  currency,
144
162
  status: 'completed',
145
163
  rail: railFor(currency),
@@ -150,13 +168,18 @@ export class WalletService {
150
168
  if (!replayed) {
151
169
  await txn
152
170
  .update(wallet)
153
- .set({ balance: sql `${wallet.balance} + ${amount}` })
171
+ .set({ balance: sql `${wallet.balance} + ${amount}::numeric` })
154
172
  .where(eq(wallet.id, walletRecord.id));
155
173
  }
156
174
  return { transactionId: row.id, replayed };
157
175
  });
158
176
  if (!replayed) {
159
- this.events.emit('wallet.deposit.completed', { userId, amount, currency, transactionId });
177
+ this.events.emit('wallet.deposit.completed', {
178
+ userId,
179
+ amount,
180
+ currency,
181
+ transactionId,
182
+ });
160
183
  }
161
184
  return { transactionId, status: 'completed' };
162
185
  }
@@ -167,16 +190,20 @@ export class WalletService {
167
190
  .select()
168
191
  .from(wallet)
169
192
  .where(eq(wallet.userId, userId));
170
- if (!walletRecord)
193
+ if (!walletRecord) {
171
194
  return { walletRecord: undefined, replay: undefined };
195
+ }
172
196
  const existing = await this.findByIdempotencyKey(this.drizzle.db, walletRecord.id, namespacedIdempotencyKey(DEPOSIT_IDEMPOTENCY_NAMESPACE, idempotencyKey));
173
- if (!existing)
197
+ if (!existing) {
174
198
  return { walletRecord, replay: undefined };
199
+ }
175
200
  this.assertReplayMatches(existing, amount, currency);
176
201
  return { walletRecord, replay: { transactionId: existing.id, status: existing.status } };
177
202
  }
203
+ // Replay comparison only (not a ledger write) - moneyToNumber avoids a false mismatch
204
+ // between an unnormalized caller string (eg "10") and the DB's fixed-scale value ("10.00").
178
205
  assertReplayMatches(existing, amount, currency) {
179
- if (Number(existing.amount) !== amount ||
206
+ if (moneyToNumber(existing.amount) !== moneyToNumber(amount) ||
180
207
  existing.currency.toUpperCase() !== currency.toUpperCase()) {
181
208
  throw new IdempotencyKeyReuseError();
182
209
  }
@@ -194,8 +221,9 @@ export class WalletService {
194
221
  const [row] = idempotencyKey
195
222
  ? await insertQuery.onConflictDoNothing().returning()
196
223
  : await insertQuery.returning();
197
- if (row)
224
+ if (row) {
198
225
  return { row, replayed: false };
226
+ }
199
227
  // Only the keyed (onConflictDoNothing) branch above can return an empty `returning()`
200
228
  // - the plain-insert branch always yields exactly one row.
201
229
  if (!idempotencyKey) {
@@ -208,7 +236,16 @@ export class WalletService {
208
236
  this.assertReplayMatches(winner, amount, currency);
209
237
  return { row: winner, replayed: true };
210
238
  }
211
- /** Creates a PENDING withdrawal and HOLDS the funds (balance debited at request time). Funds are returned on reject/PSP-failure. */
239
+ /**
240
+ * Creates a PENDING withdrawal and HOLDS the funds immediately (balance
241
+ * debited at request time, not on approval) - funds are returned on
242
+ * reject/PSP-failure. The debit is a conditional `UPDATE ... WHERE balance
243
+ * >= amount`, atomic with the row lock (`FOR UPDATE`) taken on the wallet,
244
+ * so two concurrent withdrawals can't both pass a balance check and
245
+ * double-spend. `idempotencyKey` replay semantics mirror `deposit()`. May
246
+ * return an already-`completed`/`processing` result if the request
247
+ * qualifies for (and passes) auto-approval - see `maybeAutoApprove`.
248
+ */
212
249
  async withdraw({ userId, amount, currency, idempotencyKey, }) {
213
250
  await this.rateLimit(userId);
214
251
  await this.assertKycForWithdrawal(userId);
@@ -242,7 +279,7 @@ export class WalletService {
242
279
  values: {
243
280
  walletId: current.id,
244
281
  type: 'withdrawal',
245
- amount: amount.toString(),
282
+ amount,
246
283
  currency,
247
284
  status: 'pending',
248
285
  rail: railFor(currency),
@@ -260,11 +297,11 @@ export class WalletService {
260
297
  // Guarded conditional debit: the WHERE makes balance>=amount atomic with the write; 0 rows back rolls back the whole tx.
261
298
  const debited = await txn
262
299
  .update(wallet)
263
- .set({ balance: sql `${wallet.balance} - ${amount}` })
264
- .where(and(eq(wallet.id, current.id), gte(wallet.balance, amount.toString())))
300
+ .set({ balance: sql `${wallet.balance} - ${amount}::numeric` })
301
+ .where(and(eq(wallet.id, current.id), gte(wallet.balance, amount)))
265
302
  .returning({ id: wallet.id });
266
303
  if (debited.length !== 1) {
267
- throw new InsufficientBalanceError(Number(current.balance), amount);
304
+ throw new InsufficientBalanceError(current.balance, amount);
268
305
  }
269
306
  return {
270
307
  transactionId: row.id,
@@ -274,9 +311,15 @@ export class WalletService {
274
311
  rail: railFor(currency),
275
312
  };
276
313
  });
277
- if (replayed)
314
+ if (replayed) {
278
315
  return { transactionId, status };
279
- this.events.emit('wallet.withdrawal.requested', { userId, amount, currency, transactionId });
316
+ }
317
+ this.events.emit('wallet.withdrawal.requested', {
318
+ userId,
319
+ amount,
320
+ currency,
321
+ transactionId,
322
+ });
280
323
  // Fail-closed post-step: decides who approves (system vs manual queue), never throws out of withdraw() - failure leaves the row pending.
281
324
  const auto = await this.maybeAutoApprove({
282
325
  userId,
@@ -292,17 +335,20 @@ export class WalletService {
292
335
  const db = this.drizzle.db;
293
336
  const { page, limit } = filters;
294
337
  const conditions = [eq(walletTransaction.type, 'withdrawal')];
295
- if (filters.status)
338
+ if (filters.status) {
296
339
  conditions.push(eq(walletTransaction.status, filters.status));
297
- if (filters.currency)
340
+ }
341
+ if (filters.currency) {
298
342
  conditions.push(eq(walletTransaction.currency, filters.currency));
299
- if (filters.rail)
343
+ }
344
+ if (filters.rail) {
300
345
  conditions.push(eq(walletTransaction.rail, filters.rail));
346
+ }
301
347
  if (filters.minAmount !== undefined) {
302
- conditions.push(gte(walletTransaction.amount, filters.minAmount.toString()));
348
+ conditions.push(gte(walletTransaction.amount, filters.minAmount));
303
349
  }
304
350
  if (filters.maxAmount !== undefined) {
305
- conditions.push(lte(walletTransaction.amount, filters.maxAmount.toString()));
351
+ conditions.push(lte(walletTransaction.amount, filters.maxAmount));
306
352
  }
307
353
  if (filters.dateFrom) {
308
354
  conditions.push(gte(walletTransaction.createdAt, new Date(filters.dateFrom)));
@@ -324,7 +370,7 @@ export class WalletService {
324
370
  const matching = filters.kycStatus
325
371
  ? rows.filter((r) => byUserId.get(r.userId)?.kycStatus === filters.kycStatus)
326
372
  : rows;
327
- const start = (page - 1) * limit;
373
+ const start = pageToOffset(page, limit);
328
374
  const pageRows = matching.slice(start, start + limit);
329
375
  // One batched velocity query for the page (no N+1); shares the window + threshold + query the auto-approval evaluator uses.
330
376
  const pageWalletIds = [...new Set(pageRows.map((r) => r.tx.walletId))];
@@ -332,15 +378,17 @@ export class WalletService {
332
378
  const items = pageRows.map((r) => {
333
379
  const summary = byUserId.get(r.userId);
334
380
  const riskTags = [];
335
- if (Number(r.tx.amount) >= LARGE_WITHDRAWAL_THRESHOLD)
381
+ if (moneyToNumber(r.tx.amount) >= moneyToNumber(LARGE_WITHDRAWAL_THRESHOLD)) {
336
382
  riskTags.push('large_amount');
337
- if (frequentWalletIds.has(r.tx.walletId))
383
+ }
384
+ if (frequentWalletIds.has(r.tx.walletId)) {
338
385
  riskTags.push('high_frequency');
386
+ }
339
387
  return {
340
388
  transactionId: r.tx.id,
341
389
  userId: r.userId,
342
390
  username: summary?.username ?? '',
343
- amount: Number(r.tx.amount),
391
+ amount: r.tx.amount,
344
392
  currency: r.tx.currency,
345
393
  rail: r.tx.rail ?? null,
346
394
  status: r.tx.status,
@@ -374,7 +422,7 @@ export class WalletService {
374
422
  if (current.status !== 'pending' || current.type !== 'withdrawal') {
375
423
  throw new WithdrawalNotPendingError();
376
424
  }
377
- const [updated] = await txn
425
+ return findOneOrThrow(await txn
378
426
  .update(walletTransaction)
379
427
  .set({
380
428
  status: 'processing',
@@ -383,14 +431,13 @@ export class WalletService {
383
431
  ...(reviewReason !== undefined ? { reviewReason } : {}),
384
432
  })
385
433
  .where(eq(walletTransaction.id, withdrawalId))
386
- .returning();
387
- return updated;
434
+ .returning(), new WithdrawalNotFoundError(withdrawalId));
388
435
  }
389
436
  // Phase two: settle a `processing` withdrawal via the PSP (Completed on success, Failed + refund on error).
390
437
  // Runs OUTSIDE the hold/flip transaction so the external PSP call never holds a DB lock.
391
438
  async settleApproved(tx, adminId) {
392
439
  const userId = await this.userIdForWallet(tx.walletId);
393
- const amount = Number(tx.amount);
440
+ const amount = tx.amount;
394
441
  // approved/failed are admin-attributed events (schema requires a uuid adminId); the system auto path
395
442
  // skips them - its trail is the AUDIT_WRITER entry plus the shared `completed` event below.
396
443
  if (adminId) {
@@ -420,7 +467,7 @@ export class WalletService {
420
467
  .where(eq(walletTransaction.id, tx.id));
421
468
  await txn
422
469
  .update(wallet)
423
- .set({ balance: sql `${wallet.balance} + ${amount}` })
470
+ .set({ balance: sql `${wallet.balance} + ${amount}::numeric` })
424
471
  .where(eq(wallet.id, tx.walletId));
425
472
  });
426
473
  if (adminId) {
@@ -454,22 +501,25 @@ export class WalletService {
454
501
  // leave the row pending for the manual queue. NEVER throws: any error fails closed to manual.
455
502
  async maybeAutoApprove(args) {
456
503
  // Cheap gates first (auto-approval off, or crypto - never auto-approved): skip opening a tx/lock.
457
- if (!this.platformConfig?.autoWithdrawal?.enabled || args.rail !== 'fiat')
504
+ if (!this.platformConfig?.autoWithdrawal?.enabled || args.rail !== 'fiat') {
458
505
  return undefined;
506
+ }
459
507
  try {
460
508
  const cfg = this.platformConfig.autoWithdrawal;
461
509
  // Threshold/KYC/risk/velocity gates run outside any lock; only the cap check below needs serializing.
462
510
  const gates = await this.evaluateAutoApproval(args);
463
- if (!gates)
511
+ if (!gates) {
464
512
  return undefined;
513
+ }
465
514
  // Serialize the cap-check-and-flip per user with an advisory lock: without it two concurrent
466
515
  // withdrawals read the same daily-cap usage and both auto-approve, bypassing the cap. The cap read
467
516
  // and the flip that writes the marker commit together, so a blocked caller re-reads current usage.
468
517
  // PSP settlement runs AFTER, outside the lock.
469
518
  const decided = await this.drizzle.db.transaction((txn) => withAdvisoryXactLock(txn, args.userId, async () => {
470
519
  const caps = await this.autoApprovalCaps({ walletId: args.walletId, amount: args.amount, cfg }, txn);
471
- if (caps.exceeded)
520
+ if (caps.exceeded) {
472
521
  return null;
522
+ }
473
523
  const tx = await this.flipToProcessing({
474
524
  txn,
475
525
  withdrawalId: args.transactionId,
@@ -478,8 +528,9 @@ export class WalletService {
478
528
  });
479
529
  return { tx, caps };
480
530
  }));
481
- if (!decided)
531
+ if (!decided) {
482
532
  return undefined;
533
+ }
483
534
  const decision = {
484
535
  ...gates,
485
536
  dailyCapAmount: cfg.dailyCapAmount ?? null,
@@ -494,7 +545,12 @@ export class WalletService {
494
545
  action: 'wallet.withdrawal.auto_approved',
495
546
  resourceType: 'wallet_transaction',
496
547
  resourceId: args.transactionId,
497
- after: { userId: args.userId, amount: args.amount, currency: args.currency, ...decision },
548
+ after: {
549
+ userId: args.userId,
550
+ amount: args.amount,
551
+ currency: args.currency,
552
+ ...decision,
553
+ },
498
554
  });
499
555
  }
500
556
  catch (err) {
@@ -518,29 +574,37 @@ export class WalletService {
518
574
  // route to manual. The daily-cap check is NOT here - it runs under the advisory lock (see maybeAutoApprove).
519
575
  async evaluateAutoApproval({ userId, amount, walletId, rail, }) {
520
576
  const cfg = this.platformConfig?.autoWithdrawal;
521
- if (!cfg?.enabled)
577
+ if (!cfg?.enabled) {
522
578
  return null;
579
+ }
523
580
  // Hard stop: crypto is irreversible and AML-sensitive - never auto-approved.
524
- if (rail !== 'fiat')
581
+ if (rail !== 'fiat') {
525
582
  return null;
583
+ }
526
584
  const threshold = await this.resolveAutoThreshold(userId);
527
- if (!threshold || threshold.value <= 0)
585
+ if (!threshold || moneyToNumber(threshold.value) <= 0) {
528
586
  return null;
529
- if (amount > threshold.value)
587
+ }
588
+ if (moneyToNumber(amount) > moneyToNumber(threshold.value)) {
530
589
  return null;
590
+ }
531
591
  // Independent of kyc.gateWithdrawals: auto-approval always demands a passing status; anything else fails closed.
532
592
  const kycStatus = await this.autoApprovalKycStatus(userId);
533
- if (!kycStatus || !KYC_PASS_STATUSES.has(kycStatus))
593
+ if (!kycStatus || !KYC_PASS_STATUSES.has(kycStatus)) {
534
594
  return null;
595
+ }
535
596
  const riskTags = await this.autoApprovalRiskTags(userId, cfg.excludeRiskFlags);
536
597
  // null = exclusions configured but the lookup port is unavailable => fail closed.
537
- if (riskTags === null)
598
+ if (riskTags === null) {
538
599
  return null;
539
- if (riskTags.some((t) => cfg.excludeRiskFlags.includes(t)))
600
+ }
601
+ if (riskTags.some((t) => cfg.excludeRiskFlags.includes(t))) {
540
602
  return null;
603
+ }
541
604
  const heuristics = await this.autoApprovalHeuristics({ walletId, amount });
542
- if (heuristics.largeAmount || heuristics.highFrequency)
605
+ if (heuristics.largeAmount || heuristics.highFrequency) {
543
606
  return null;
607
+ }
544
608
  return {
545
609
  threshold: threshold.value,
546
610
  thresholdSource: threshold.source,
@@ -554,33 +618,38 @@ export class WalletService {
554
618
  .select()
555
619
  .from(autoWithdrawalRule)
556
620
  .where(eq(autoWithdrawalRule.userId, userId));
557
- if (rule)
558
- return { value: Number(rule.threshold), source: 'per-player' };
621
+ if (rule) {
622
+ return { value: rule.threshold, source: 'per-player' };
623
+ }
559
624
  const global = this.platformConfig?.autoWithdrawal?.fiatThreshold;
560
- if (global !== undefined)
625
+ if (global !== undefined) {
561
626
  return { value: global, source: 'global' };
627
+ }
562
628
  return null;
563
629
  }
564
630
  async autoApprovalKycStatus(userId) {
565
631
  // No directory bound => cannot verify KYC => fail closed.
566
- if (!this.directory)
632
+ if (!this.directory) {
567
633
  return null;
634
+ }
568
635
  const [summary] = await this.directory.lookupPlayers([userId]);
569
636
  return summary?.kycStatus ?? null;
570
637
  }
571
638
  // Active tag keys; [] when no exclusions configured or none carried; null when configured but the port is unbound (fail closed).
572
639
  async autoApprovalRiskTags(userId, excludeRiskFlags) {
573
- if (excludeRiskFlags.length === 0)
640
+ if (excludeRiskFlags.length === 0) {
574
641
  return [];
575
- if (!this.riskTags)
642
+ }
643
+ if (!this.riskTags) {
576
644
  return null;
645
+ }
577
646
  const byUser = await this.riskTags.getActiveTagKeys([userId]);
578
647
  return byUser.get(userId) ?? [];
579
648
  }
580
649
  async autoApprovalHeuristics({ walletId, amount, }) {
581
650
  const frequent = await this.frequentWithdrawalWalletIds(this.drizzle.db, [walletId]);
582
651
  return {
583
- largeAmount: amount >= LARGE_WITHDRAWAL_THRESHOLD,
652
+ largeAmount: moneyToNumber(amount) >= moneyToNumber(LARGE_WITHDRAWAL_THRESHOLD),
584
653
  highFrequency: frequent.has(walletId),
585
654
  };
586
655
  }
@@ -588,8 +657,9 @@ export class WalletService {
588
657
  // Single source of the velocity check, shared by the review queue tag and the auto-approval heuristic.
589
658
  async frequentWithdrawalWalletIds(db, walletIds) {
590
659
  const frequent = new Set();
591
- if (walletIds.length === 0)
660
+ if (walletIds.length === 0) {
592
661
  return frequent;
662
+ }
593
663
  const since = new Date(Date.now() - HIGH_FREQUENCY_WINDOW_MS);
594
664
  const counts = await db
595
665
  .select({ walletId: walletTransaction.walletId, n: count() })
@@ -597,8 +667,9 @@ export class WalletService {
597
667
  .where(and(eq(walletTransaction.type, 'withdrawal'), gte(walletTransaction.createdAt, since), inArray(walletTransaction.walletId, walletIds)))
598
668
  .groupBy(walletTransaction.walletId);
599
669
  for (const row of counts) {
600
- if (Number(row.n) >= HIGH_FREQUENCY_MIN_COUNT)
670
+ if (Number(row.n) >= HIGH_FREQUENCY_MIN_COUNT) {
601
671
  frequent.add(row.walletId);
672
+ }
602
673
  }
603
674
  return frequent;
604
675
  }
@@ -607,24 +678,30 @@ export class WalletService {
607
678
  async autoApprovalCaps({ walletId, amount, cfg, }, db) {
608
679
  const since = new Date(Date.now() - DAILY_CAP_WINDOW_MS);
609
680
  const [row] = await db
610
- .select({ total: sql `coalesce(sum(${walletTransaction.amount}), 0)`, n: count() })
681
+ .select({
682
+ total: sql `coalesce(sum(${walletTransaction.amount}), 0)`,
683
+ n: count(),
684
+ })
611
685
  .from(walletTransaction)
612
686
  .where(and(eq(walletTransaction.walletId, walletId), eq(walletTransaction.type, 'withdrawal'), eq(walletTransaction.reviewReason, AUTO_APPROVED_REASON), gte(walletTransaction.createdAt, since)));
613
- const amountUsed = Number(row?.total ?? 0);
687
+ const amountUsed = row?.total ?? '0';
614
688
  const countUsed = Number(row?.n ?? 0);
615
- const amountExceeded = cfg.dailyCapAmount !== undefined && amountUsed + amount > cfg.dailyCapAmount;
689
+ // Cap comparison is a review-queue decision, not a ledger write - moneyToNumber is the
690
+ // documented single conversion point (see the helper's own doc comment).
691
+ const amountExceeded = cfg.dailyCapAmount !== undefined &&
692
+ moneyToNumber(amountUsed) + moneyToNumber(amount) > moneyToNumber(cfg.dailyCapAmount);
616
693
  const countExceeded = cfg.dailyCapCount !== undefined && countUsed + 1 > cfg.dailyCapCount;
617
694
  return { exceeded: amountExceeded || countExceeded, amountUsed, countUsed };
618
695
  }
619
696
  async setAutoWithdrawalRule({ userId, threshold, reason, createdBy, }) {
620
- const [row] = await this.drizzle.db
697
+ const row = findOneOrThrow(await this.drizzle.db
621
698
  .insert(autoWithdrawalRule)
622
- .values({ userId, threshold: threshold.toString(), reason, createdBy })
699
+ .values({ userId, threshold, reason, createdBy })
623
700
  .onConflictDoUpdate({
624
701
  target: autoWithdrawalRule.userId,
625
- set: { threshold: threshold.toString(), reason, createdBy },
702
+ set: { threshold, reason, createdBy },
626
703
  })
627
- .returning();
704
+ .returning(), new WalletNotFoundError(userId));
628
705
  return toAutoWithdrawalRuleDto(row);
629
706
  }
630
707
  async getAutoWithdrawalRule(userId) {
@@ -652,21 +729,21 @@ export class WalletService {
652
729
  if (current.status !== 'pending' || current.type !== 'withdrawal') {
653
730
  throw new WithdrawalNotPendingError();
654
731
  }
655
- const [updated] = await txn
732
+ const updated = findOneOrThrow(await txn
656
733
  .update(walletTransaction)
657
734
  .set({ status: 'rejected', reviewedBy: adminId, reviewedAt, reviewReason: reason })
658
735
  .where(eq(walletTransaction.id, withdrawalId))
659
- .returning();
736
+ .returning(), new WithdrawalNotFoundError(withdrawalId));
660
737
  await txn
661
738
  .update(wallet)
662
- .set({ balance: sql `${wallet.balance} + ${updated.amount}` })
739
+ .set({ balance: sql `${wallet.balance} + ${updated.amount}::numeric` })
663
740
  .where(eq(wallet.id, updated.walletId));
664
741
  return updated;
665
742
  });
666
743
  const userId = await this.userIdForWallet(tx.walletId);
667
744
  this.events.emit('wallet.withdrawal.rejected', {
668
745
  userId,
669
- amount: Number(tx.amount),
746
+ amount: tx.amount,
670
747
  currency: tx.currency,
671
748
  transactionId: tx.id,
672
749
  adminId,
@@ -677,8 +754,9 @@ export class WalletService {
677
754
  async getTransactions(userId, page, limit) {
678
755
  const db = this.drizzle.db;
679
756
  const [walletRecord] = await db.select().from(wallet).where(eq(wallet.userId, userId));
680
- if (!walletRecord)
757
+ if (!walletRecord) {
681
758
  return { items: [], total: 0, page, limit };
759
+ }
682
760
  const where = eq(walletTransaction.walletId, walletRecord.id);
683
761
  const [txs, [{ n }]] = await Promise.all([
684
762
  db
@@ -694,7 +772,7 @@ export class WalletService {
694
772
  items: txs.map((tx) => ({
695
773
  id: tx.id,
696
774
  type: tx.type,
697
- amount: Number(tx.amount),
775
+ amount: tx.amount,
698
776
  currency: tx.currency,
699
777
  status: tx.status,
700
778
  createdAt: tx.createdAt.toISOString(),