@mojaloop/central-ledger 19.14.0 → 20.1.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 (394) hide show
  1. package/.grype.yaml +3 -0
  2. package/.license-scanner.json +16 -0
  3. package/.ncurc.yaml +19 -13
  4. package/.nvmrc +1 -1
  5. package/.nycrc.yml +14 -2
  6. package/CHANGELOG.md +51 -0
  7. package/Dockerfile +13 -10
  8. package/README.md +39 -89
  9. package/audit-ci.jsonc +1 -1
  10. package/config/default.json +60 -3
  11. package/config/deprecated.default-settlement.json +160 -0
  12. package/docker/ml-api-adapter/default.json +28 -0
  13. package/docker-compose.yml +6 -0
  14. package/nodemon.json +6 -0
  15. package/package.json +128 -64
  16. package/{sbom-v19.13.2.csv → sbom-v20.0.0.csv} +886 -736
  17. package/scripts/_wait4_all.js +5 -5
  18. package/scripts/transferSettlementTemp/interchangeFeeCalculation.js +51 -0
  19. package/src/api/index.ts +30 -0
  20. package/src/api/interface/swagger.json +2249 -2054
  21. package/src/api/participants/routes.js +1 -1
  22. package/src/api/root/handler.int.ts +55 -0
  23. package/src/api/routes.js +7 -6
  24. package/src/api/server.ts +27 -0
  25. package/src/api/server.unit.ts +16 -0
  26. package/src/api/settlementModels/routes.js +4 -4
  27. package/src/domain/participant/index.d.ts +47 -0
  28. package/src/domain/participant/index.int.ts +724 -0
  29. package/src/domain/participant/index.js +4 -5
  30. package/src/domain/position/fx-prepare.js +1 -1
  31. package/src/domain/position/fx-timeout-reserved.js +6 -0
  32. package/src/domain/position/prepare.js +1 -1
  33. package/src/handlers/admin/handler.js +1 -1
  34. package/src/handlers/index.js +30 -0
  35. package/src/handlers/positions/handler.js +0 -2
  36. package/src/handlers/positions/handlerBatch.js +5 -5
  37. package/src/handlers/register.js +44 -29
  38. package/src/handlers/timeouts/handler.js +1 -4
  39. package/src/handlers/transfers/fx-abort.int.ts +209 -0
  40. package/src/handlers/transfers/fx-handlers.int.ts +275 -0
  41. package/src/handlers/transfers/fx-timeout.int.ts +284 -0
  42. package/src/handlers/transfers/handler.js +1 -3
  43. package/src/handlers/transfers/handlers.int.ts +538 -0
  44. package/src/handlers/transfers/prepare.int.ts +1283 -0
  45. package/src/handlers/transfers/prepare.js +2 -2
  46. package/src/knexfile.js +19 -0
  47. package/src/lib/cache.js +1 -1
  48. package/src/lib/config/index.ts +60 -0
  49. package/src/lib/config/resolver.ts +231 -0
  50. package/src/lib/config/resolver.unit.ts +72 -0
  51. package/src/lib/config/types.ts +372 -0
  52. package/src/lib/config/util.ts +523 -0
  53. package/src/lib/config/util.unit.ts +825 -0
  54. package/src/lib/migrator.js +3 -3
  55. package/src/lib/proxyCache.js +3 -3
  56. package/{migrations → src/migrations}/100100_event.js +1 -1
  57. package/{migrations → src/migrations}/110100_contactType.js +1 -1
  58. package/{migrations → src/migrations}/110200_currency.js +1 -1
  59. package/{migrations → src/migrations}/110201_currency-scale.js +1 -1
  60. package/{migrations → src/migrations}/110300_endpointType.js +1 -1
  61. package/{migrations → src/migrations}/110400_ledgerEntryType.js +1 -1
  62. package/{migrations → src/migrations}/110450_ledgerAccountType.js +1 -1
  63. package/{migrations → src/migrations}/110500_participantLimitType.js +1 -1
  64. package/{migrations → src/migrations}/110600_transferParticipantRoleType.js +1 -1
  65. package/{migrations → src/migrations}/110700_transferState.js +1 -1
  66. package/{migrations → src/migrations}/110900_settlementState.js +1 -1
  67. package/{migrations → src/migrations}/112000_bulkTransferState.js +1 -1
  68. package/{migrations → src/migrations}/112100_bulkProcessingState.js +1 -1
  69. package/{migrations → src/migrations}/200100_participant.js +1 -1
  70. package/{migrations → src/migrations}/200200_participantContact.js +1 -1
  71. package/{migrations → src/migrations}/200300_participantEndpoint.js +1 -1
  72. package/{migrations → src/migrations}/200400_participantParty.js +1 -1
  73. package/{migrations → src/migrations}/200600_token.js +1 -1
  74. package/{migrations → src/migrations}/200601_token-indexes.js +1 -1
  75. package/{migrations → src/migrations}/300100_transferDuplicateCheck.js +2 -2
  76. package/{migrations → src/migrations}/300150_bulkTransferDuplicateCheck.js +2 -2
  77. package/{migrations → src/migrations}/300200_transfer.js +2 -2
  78. package/{migrations → src/migrations}/300202_transfer-decimal184.js +1 -1
  79. package/{migrations → src/migrations}/300250_bulkTransfer.js +2 -3
  80. package/{migrations → src/migrations}/300275_bulkTransferAssociation.js +2 -3
  81. package/{migrations → src/migrations}/300300_ilpPacket.js +1 -1
  82. package/{migrations → src/migrations}/300400_transferStateChange.js +1 -1
  83. package/{migrations → src/migrations}/300450_bulkTransferStateChange.js +1 -1
  84. package/{migrations → src/migrations}/300500_segment.js +1 -1
  85. package/{migrations → src/migrations}/310100_participantCurrency.js +1 -1
  86. package/{migrations → src/migrations}/310200_transferParticipant.js +1 -1
  87. package/{migrations → src/migrations}/310202_transferParticipant-decimal184.js +1 -1
  88. package/{migrations → src/migrations}/310204_transferParticipant-participantId.js +2 -2
  89. package/{migrations → src/migrations}/310300_participantPosition.js +1 -1
  90. package/{migrations → src/migrations}/310302_participantPosition-decimal184.js +1 -1
  91. package/{migrations → src/migrations}/310400_participantPositionChange.js +1 -1
  92. package/{migrations → src/migrations}/310402_participantPositionChange-decimal184.js +1 -1
  93. package/{migrations → src/migrations}/310403_participantPositionChange-participantCurrencyId.js +2 -2
  94. package/{migrations → src/migrations}/310404_participantPositionChange-change.js +2 -2
  95. package/{migrations → src/migrations}/310500_participantLimit.js +1 -1
  96. package/{migrations → src/migrations}/310502_participantLimit-decimal184.js +1 -1
  97. package/{migrations → src/migrations}/400100_settlementWindow.js +1 -1
  98. package/{migrations → src/migrations}/400200_settlement.js +1 -1
  99. package/{migrations → src/migrations}/400300_settlementSettlementWindow.js +1 -1
  100. package/{migrations → src/migrations}/400301_settlementSettlementWindow-indexes.js +0 -1
  101. package/{migrations → src/migrations}/400400_settlementWindowStateChange.js +1 -1
  102. package/{migrations → src/migrations}/400500_settlementTransferParticipant.js +1 -1
  103. package/{migrations → src/migrations}/400502_settlementTransferParticipant-decimal184.js +1 -1
  104. package/{migrations → src/migrations}/400600_settlementParticipantCurrency.js +1 -1
  105. package/{migrations → src/migrations}/400602_settlementParticipantCurrency-decimal184.js +1 -1
  106. package/{migrations → src/migrations}/400700_settlementParticipantCurrencyStateChange.js +1 -1
  107. package/{migrations → src/migrations}/400800_settlementStateChange.js +1 -1
  108. package/{migrations → src/migrations}/400900_settlementWindowContent.js +1 -1
  109. package/{migrations → src/migrations}/401000_settlementWindowContentStateChange.js +1 -1
  110. package/{migrations → src/migrations}/401001_settlementWindowContentStateChange-indexes.js +6 -6
  111. package/{migrations → src/migrations}/401200_settlementContentAggregation.js +1 -1
  112. package/{migrations → src/migrations}/401300_settlementModel.js +1 -1
  113. package/{migrations → src/migrations}/410100_transferFulfilmentDuplicateCheck.js +1 -1
  114. package/{migrations → src/migrations}/410102_transferFulfilmentDuplicateCheck-dropForeignTransferId.js +1 -1
  115. package/{migrations → src/migrations}/410103_transferFulfilmentDuplicateCheck-hashNullable.js +1 -1
  116. package/{migrations → src/migrations}/410200_transferFulfilment.js +1 -1
  117. package/{migrations → src/migrations}/410202_transferFulfilment-ilpFulfilment.js +1 -1
  118. package/{migrations → src/migrations}/410203_transferFulfilment-foreignTransferFulfilmentId.js +1 -1
  119. package/{migrations → src/migrations}/410240_bulkTransferFulfilmentDuplicateCheck.js +1 -1
  120. package/{migrations → src/migrations}/410250_bulkTransferFulfilment.js +1 -1
  121. package/{migrations → src/migrations}/410300_transferExtension.js +1 -1
  122. package/{migrations → src/migrations}/410302_transferExtension-transferErrorId.js +1 -1
  123. package/{migrations → src/migrations}/410350_bulkTransferExtension.js +1 -1
  124. package/{migrations → src/migrations}/410400_transferTimeout.js +1 -1
  125. package/{migrations → src/migrations}/410450_transferErrorDuplicateCheck.js +1 -1
  126. package/{migrations → src/migrations}/410500_transferError.js +1 -1
  127. package/{migrations → src/migrations}/410502_transferError-foreignTransferErrorDuplicateCheckId.js +1 -1
  128. package/{migrations → src/migrations}/410550_bulkTransferError.js +1 -1
  129. package/{migrations → src/migrations}/410600_expiringTransfer.js +1 -1
  130. package/{migrations → src/migrations}/500401_quote-hotfix-2719.js +8 -8
  131. package/{migrations → src/migrations}/500501_feature-fixSubIdRef.js +12 -12
  132. package/{migrations → src/migrations}/500601_party-2480.js +3 -3
  133. package/{migrations → src/migrations}/501001_quoteExtension-quote-service-174.js +1 -2
  134. package/{migrations → src/migrations}/501002_quoteExtension-2522.js +3 -3
  135. package/{migrations → src/migrations}/600010_fxTransferType.js +1 -1
  136. package/{migrations → src/migrations}/600012_fxParticipantCurrencyType.js +1 -1
  137. package/{migrations → src/migrations}/600100_fxTransferDuplicateCheck.js +2 -2
  138. package/{migrations → src/migrations}/600110_fxTransferErrorDuplicateCheck.js.js +1 -1
  139. package/{migrations → src/migrations}/600200_fxTransfer.js +2 -2
  140. package/{migrations → src/migrations}/600400_fxTransferStateChange.js +1 -1
  141. package/{migrations → src/migrations}/600501_fxWatchList.js +2 -2
  142. package/{migrations → src/migrations}/600600_fxTransferFulfilmentDuplicateCheck.js +1 -1
  143. package/{migrations → src/migrations}/600700_fxTransferFulfilment.js +1 -1
  144. package/{migrations → src/migrations}/600800_fxTransferExtension.js +1 -1
  145. package/{migrations → src/migrations}/601400_fxTransferTimeout.js +1 -1
  146. package/{migrations → src/migrations}/601500_fxTransferError.js +1 -1
  147. package/{migrations → src/migrations}/610200_fxTransferParticipant.js +1 -1
  148. package/{migrations → src/migrations}/610202_fxTransferParticipant-participantId.js +2 -2
  149. package/{migrations → src/migrations}/610403_participantPositionChange-fxTransfer.js +1 -1
  150. package/{migrations → src/migrations}/800100_quotePartyView.js +1 -1
  151. package/{migrations → src/migrations}/800101_feature-fixSubIdRef.js +1 -1
  152. package/{migrations → src/migrations}/800200_quoteView.js +1 -1
  153. package/{migrations → src/migrations}/800300_quoteResponseView.js +1 -1
  154. package/{migrations → src/migrations}/900100_foreign-keys.js +18 -18
  155. package/{migrations → src/migrations}/900101_foreign-keys.js +6 -6
  156. package/{migrations → src/migrations}/910100_feature904DDL.js +7 -7
  157. package/{migrations → src/migrations}/910101_feature904DataMigration.js +1 -1
  158. package/{migrations → src/migrations}/910102_feature949DataMigration.js +1 -1
  159. package/{migrations → src/migrations}/910103_feature1093SettlementByCurrency.js +3 -3
  160. package/{migrations → src/migrations}/940101_quotePartyIdInfoExtension-2522.js +25 -25
  161. package/{migrations → src/migrations}/950101_transferParticipantStateChange.js +2 -2
  162. package/{migrations → src/migrations}/950102_settlementModel-adjustPosition.js +2 -2
  163. package/{migrations → src/migrations}/950103_dropTransferParticipantStateChange.js +3 -3
  164. package/{migrations → src/migrations}/950104_settlementModel-settlementAccountTypeId.js +3 -3
  165. package/src/migrations/950107_settlementWindowContent_foreign_settlementModel.js +63 -0
  166. package/{migrations → src/migrations}/950108_participantProxy.js +1 -2
  167. package/{migrations → src/migrations}/950120_fxQuoteConversionTerms_sourceAmount_nullable.js +1 -1
  168. package/{migrations → src/migrations}/960100_create_externalParticipant.js +2 -2
  169. package/{migrations → src/migrations}/960110_alter_transferParticipant__addFiled_externalParticipantId.js +2 -2
  170. package/{migrations → src/migrations}/961001_transferForwarded.js +1 -1
  171. package/{migrations → src/migrations}/961002_fxTransferForwarded.js +1 -1
  172. package/src/models/participant/externalParticipant.int.ts +85 -0
  173. package/src/models/participant/facade.js +4 -2
  174. package/src/models/participant/participantCurrencyCached.js +1 -1
  175. package/src/models/position/facade.js +8 -2
  176. package/src/models/transfer/facade.int.ts +139 -0
  177. package/src/models/transfer/facade.js +8 -3
  178. package/src/models/transfer/transferDuplicateCheck.js +2 -2
  179. package/src/models/transfer/transferError.int.ts +112 -0
  180. package/src/models/transfer/transferExtension.int.ts +137 -0
  181. package/src/models/transfer/transferStateChange.int.ts +117 -0
  182. package/{seeds → src/seeds}/participant.js +1 -1
  183. package/{seeds → src/seeds}/settlementInterchange.js +1 -0
  184. package/src/settlement/api/handlerRoutes.js +71 -0
  185. package/src/settlement/api/handlers/health.js +10 -0
  186. package/src/settlement/api/handlers/index.js +57 -0
  187. package/src/settlement/api/handlers/settlementWindows/{id}.js +103 -0
  188. package/src/settlement/api/handlers/settlementWindows.js +77 -0
  189. package/src/settlement/api/handlers/settlements/{id}.js +133 -0
  190. package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}/accounts/{aid}.js +127 -0
  191. package/src/settlement/api/handlers/settlements/{sid}/participants/{pid}.js +127 -0
  192. package/src/settlement/api/handlers/settlements.js +114 -0
  193. package/src/settlement/api/openapiRouting.js +87 -0
  194. package/src/settlement/api/routes.js +176 -0
  195. package/src/settlement/domain/rules/index.js +46 -0
  196. package/src/settlement/domain/settlement/index.js +405 -0
  197. package/src/settlement/domain/settlementWindow/index.js +117 -0
  198. package/src/settlement/domain/transactions/index.js +83 -0
  199. package/src/settlement/domain/transferSettlement/index.js +57 -0
  200. package/src/settlement/handlers/deferredSettlement/handler.js +180 -0
  201. package/src/settlement/handlers/grossSettlement/handler.js +180 -0
  202. package/src/settlement/handlers/index.js +101 -0
  203. package/src/settlement/handlers/register.js +92 -0
  204. package/src/settlement/handlers/rules/handler.js +164 -0
  205. package/src/settlement/interface/swagger-handler.json +34 -0
  206. package/src/settlement/interface/swagger.json +1560 -0
  207. package/src/settlement/lib/db.js +32 -0
  208. package/src/settlement/lib/kafka/index.js +41 -0
  209. package/src/settlement/lib/kafka/producer.js +142 -0
  210. package/src/settlement/lib/scriptEngine.js +99 -0
  211. package/src/settlement/lib/scriptsLoader.js +175 -0
  212. package/src/settlement/lib/utility.js +243 -0
  213. package/src/settlement/models/ilpPackets/ilpPacket.js +42 -0
  214. package/src/settlement/models/lib/enums.js +192 -0
  215. package/src/settlement/models/misc/migrationLock.js +53 -0
  216. package/src/settlement/models/settlement/facade.js +1652 -0
  217. package/src/settlement/models/settlement/index.js +55 -0
  218. package/src/settlement/models/settlement/participantCurrency.js +48 -0
  219. package/src/settlement/models/settlement/settlement.js +49 -0
  220. package/src/settlement/models/settlement/settlementModel.js +44 -0
  221. package/src/settlement/models/settlement/settlementParticipantCurrency.js +61 -0
  222. package/src/settlement/models/settlement/settlementStateChange.js +44 -0
  223. package/src/settlement/models/settlement/settlementTransferParticipant.js +46 -0
  224. package/src/settlement/models/settlementWindow/facade.js +345 -0
  225. package/src/settlement/models/settlementWindow/index.js +47 -0
  226. package/src/settlement/models/settlementWindow/settlementWindowStateChange.js +55 -0
  227. package/src/settlement/models/settlementWindowContent/facade.js +79 -0
  228. package/src/settlement/models/settlementWindowContent/index.js +40 -0
  229. package/src/settlement/models/settlementWindowContent/settlementWindowContentStateChange.js +53 -0
  230. package/src/settlement/models/transferSettlement/facade.js +363 -0
  231. package/src/settlement/models/transferSettlement/index.js +42 -0
  232. package/src/settlement/shared/logger/index.js +7 -0
  233. package/src/settlement/shared/plugins.js +88 -0
  234. package/src/settlement/shared/setup.js +224 -0
  235. package/src/settlement/utils/truthyProperty.js +37 -0
  236. package/src/shared/legacyUtilShim.js +20 -0
  237. package/src/shared/plugins.js +3 -3
  238. package/src/shared/results.ts +22 -0
  239. package/src/shared/results.unit.ts +38 -0
  240. package/src/shared/setup.js +14 -1
  241. package/src/testing/api-helpers.ts +1222 -0
  242. package/src/testing/exec-async.ts +67 -0
  243. package/src/testing/harness.snapshot.sql +3895 -0
  244. package/src/testing/harness.test.ts +15 -0
  245. package/src/testing/harness.ts +1160 -0
  246. package/src/testing/kafka.ts +221 -0
  247. package/src/testing/prng.ts +82 -0
  248. package/src/testing/rdkafka.test.ts +263 -0
  249. package/src/testing/run.ts +559 -0
  250. package/src/testing/snapshot.test.ts +92 -0
  251. package/src/testing/snapshot.ts +549 -0
  252. package/src/testing/tap-stream.ts +164 -0
  253. package/src/testing/types.ts +95 -0
  254. package/src/testing/util.ts +333 -0
  255. package/src/types/central-services-error-handling.d.ts +72 -0
  256. package/src/types/central-services-stream.d.ts +148 -0
  257. package/test-integration.Dockerfile +0 -2
  258. package/test.Dockerfile +0 -2
  259. package/tsconfig.json +40 -0
  260. package/config/knexfile.js +0 -19
  261. package/migrations/950107_settlementWindowContent_foreign_settlementModel.js +0 -63
  262. package/src/api/index.js +0 -16
  263. package/src/lib/config.js +0 -81
  264. /package/{migrations → src/migrations}/110101_contactType-indexes.js +0 -0
  265. /package/{migrations → src/migrations}/110301_endpointType-indexes.js +0 -0
  266. /package/{migrations → src/migrations}/110401_ledgerEntryType-indexes.js +0 -0
  267. /package/{migrations → src/migrations}/110451_ledgerAccountType-indexes.js +0 -0
  268. /package/{migrations → src/migrations}/110501_participantLimitType-indexes.js +0 -0
  269. /package/{migrations → src/migrations}/110601_transferParticipantRoleType-indexes.js +0 -0
  270. /package/{migrations → src/migrations}/110800_settlementWindowState.js +0 -0
  271. /package/{migrations → src/migrations}/111000_amountType.js +0 -0
  272. /package/{migrations → src/migrations}/111001_amountType-indexes.js +0 -0
  273. /package/{migrations → src/migrations}/111100_balanceOfPayments.js +0 -0
  274. /package/{migrations → src/migrations}/111101_balanceOfPayments-indexes.js +0 -0
  275. /package/{migrations → src/migrations}/111200_partyIdentifierType.js +0 -0
  276. /package/{migrations → src/migrations}/111201_partyIdentifierType-indexes.js +0 -0
  277. /package/{migrations → src/migrations}/111300_partyType.js +0 -0
  278. /package/{migrations → src/migrations}/111301_partyType-indexes.js +0 -0
  279. /package/{migrations → src/migrations}/111400_quoteDuplicateCheck.js +0 -0
  280. /package/{migrations → src/migrations}/111500_transactionInitiator.js +0 -0
  281. /package/{migrations → src/migrations}/111501_transactionInitiator-indexes.js +0 -0
  282. /package/{migrations → src/migrations}/111600_transactionInitiatorType.js +0 -0
  283. /package/{migrations → src/migrations}/111601_transactionInitiatorType-indexes.js +0 -0
  284. /package/{migrations → src/migrations}/111700_settlementDelay.js +0 -0
  285. /package/{migrations → src/migrations}/111701_settlementDelay-indexes.js +0 -0
  286. /package/{migrations → src/migrations}/111800_settlementInterchange.js +0 -0
  287. /package/{migrations → src/migrations}/111801_settlementInterchange-indexes.js +0 -0
  288. /package/{migrations → src/migrations}/111900_settlementGranularity.js +0 -0
  289. /package/{migrations → src/migrations}/111901_settlementGranularity-indexes.js +0 -0
  290. /package/{migrations → src/migrations}/112101_bulkProcessingState-indexes.js +0 -0
  291. /package/{migrations → src/migrations}/200101_participant-indexes.js +0 -0
  292. /package/{migrations → src/migrations}/200201_participantContact-indexes.js +0 -0
  293. /package/{migrations → src/migrations}/200301_participantEndpoint-indexes.js +0 -0
  294. /package/{migrations → src/migrations}/200401_participantParty-indexes.js +0 -0
  295. /package/{migrations → src/migrations}/300201_transfer-indexes.js +0 -0
  296. /package/{migrations → src/migrations}/300251_bulkTransfer-indexes.js +0 -0
  297. /package/{migrations → src/migrations}/300276_bulkTransferAssociation-indexes.js +0 -0
  298. /package/{migrations → src/migrations}/300401_transferStateChange-indexes.js +0 -0
  299. /package/{migrations → src/migrations}/300451_bulkTransferStateChange-indexes.js +0 -0
  300. /package/{migrations → src/migrations}/300501_segment-indexes.js +0 -0
  301. /package/{migrations → src/migrations}/310101_participantCurrency-indexes.js +0 -0
  302. /package/{migrations → src/migrations}/310201_transferParticipant-indexes.js +0 -0
  303. /package/{migrations → src/migrations}/310203_transferParticipant-indexes.js +0 -0
  304. /package/{migrations → src/migrations}/310301_participantPosition-indexes.js +0 -0
  305. /package/{migrations → src/migrations}/310401_participantPositionChange-indexes.js +0 -0
  306. /package/{migrations → src/migrations}/310405_participantPositionChange-positionChange.js +0 -0
  307. /package/{migrations → src/migrations}/310406_participantPositionChange-positionChange-revert.js +0 -0
  308. /package/{migrations → src/migrations}/310501_participantLimit-indexes.js +0 -0
  309. /package/{migrations → src/migrations}/310503_participantLimit-indexes-composite.js +0 -0
  310. /package/{migrations → src/migrations}/400401_settlmentWindowStateChange-indexes.js +0 -0
  311. /package/{migrations → src/migrations}/400501_settlementTransferParticipant-indexes.js +0 -0
  312. /package/{migrations → src/migrations}/400601_settlementParticipantCurrency-indexes.js +0 -0
  313. /package/{migrations → src/migrations}/400701_settlementParticipantCurrencyStateChange-indexes.js +0 -0
  314. /package/{migrations → src/migrations}/400801_settlementStateChange-indexes.js +0 -0
  315. /package/{migrations → src/migrations}/401002_settlementWindowContent-indexes.js +0 -0
  316. /package/{migrations → src/migrations}/401201_settlementContentAggregation-indexes.js +0 -0
  317. /package/{migrations → src/migrations}/401301_settlementModel-indexes.js +0 -0
  318. /package/{migrations → src/migrations}/410101_transferFulfilmentDuplicateCheck-indexes.js +0 -0
  319. /package/{migrations → src/migrations}/410201_transferFulfilment-indexes.js +0 -0
  320. /package/{migrations → src/migrations}/410301_transferExtension-indexes.js +0 -0
  321. /package/{migrations → src/migrations}/410351_bulkTransferExtension-indexes.js +0 -0
  322. /package/{migrations → src/migrations}/410401_transferTimeout-indexes.js +0 -0
  323. /package/{migrations → src/migrations}/410501_transferError-indexes.js +0 -0
  324. /package/{migrations → src/migrations}/410551_bulkTransferError-indexes.js +0 -0
  325. /package/{migrations → src/migrations}/410601_expiringTransfer-indexes.js +0 -0
  326. /package/{migrations → src/migrations}/500100_transactionReference.js +0 -0
  327. /package/{migrations → src/migrations}/500101_transactionReference-indexes.js +0 -0
  328. /package/{migrations → src/migrations}/500200_transactionScenario.js +0 -0
  329. /package/{migrations → src/migrations}/500201_transactionScenario-indexes.js +0 -0
  330. /package/{migrations → src/migrations}/500300_transactionSubScenario.js +0 -0
  331. /package/{migrations → src/migrations}/500400_quote.js +0 -0
  332. /package/{migrations → src/migrations}/500500_quoteParty.js +0 -0
  333. /package/{migrations → src/migrations}/500600_party.js +0 -0
  334. /package/{migrations → src/migrations}/500700_quoteResponse.js +0 -0
  335. /package/{migrations → src/migrations}/500800_quoteResponseIlpPacket.js +0 -0
  336. /package/{migrations → src/migrations}/500900_geoCode.js +0 -0
  337. /package/{migrations → src/migrations}/501000_quoteExtension.js +0 -0
  338. /package/{migrations → src/migrations}/501100_quoteResponseDuplicateCheck.js +0 -0
  339. /package/{migrations → src/migrations}/501200_quoteError.js +0 -0
  340. /package/{migrations → src/migrations}/501300_transferRules.js +0 -0
  341. /package/{migrations → src/migrations}/600011_fxTransferType-indexes.js +0 -0
  342. /package/{migrations → src/migrations}/600013_fxParticipantCurrencyType-indexes.js +0 -0
  343. /package/{migrations → src/migrations}/600201_fxTransfer-indexes.js +0 -0
  344. /package/{migrations → src/migrations}/600401_fxTransferStateChange-indexes.js +0 -0
  345. /package/{migrations → src/migrations}/600502_fxWatchList-indexes.js +0 -0
  346. /package/{migrations → src/migrations}/600601_fxTransferFulfilmentDuplicateCheck-indexes.js +0 -0
  347. /package/{migrations → src/migrations}/600701_fxTransferFulfilment-indexes.js +0 -0
  348. /package/{migrations → src/migrations}/601401_fxTransferTimeout-indexes.js +0 -0
  349. /package/{migrations → src/migrations}/601501_fxTransferError-indexes.js +0 -0
  350. /package/{migrations → src/migrations}/610201_fxTransferParticipant-indexes.js +0 -0
  351. /package/{migrations → src/migrations}/910102_hotfix954.js +0 -0
  352. /package/{migrations → src/migrations}/940100_quotePartyIdInfoExtension.js +0 -0
  353. /package/{migrations → src/migrations}/950100_settlementContentAggregation-decimal184.js +0 -0
  354. /package/{migrations → src/migrations}/950106_unique_settlement_model_ledger_account_currency.js +0 -0
  355. /package/{migrations → src/migrations}/950109_fxQuote.js +0 -0
  356. /package/{migrations → src/migrations}/950110_fxQuoteResponse.js +0 -0
  357. /package/{migrations → src/migrations}/950111_fxQuoteError.js +0 -0
  358. /package/{migrations → src/migrations}/950113_fxQuoteDuplicateCheck.js +0 -0
  359. /package/{migrations → src/migrations}/950114_fxQuoteResponseDuplicateCheck.js +0 -0
  360. /package/{migrations → src/migrations}/950115_fxQuoteConversionTerms.js +0 -0
  361. /package/{migrations → src/migrations}/950116_fxQuoteConversionTermsExtension.js +0 -0
  362. /package/{migrations → src/migrations}/950117_fxQuoteResponseConversionTerms.js +0 -0
  363. /package/{migrations → src/migrations}/950118_fxQuoteResponseConversionTermsExtension.js +0 -0
  364. /package/{migrations → src/migrations}/950119_fxCharge.js +0 -0
  365. /package/{migrations → src/migrations}/960111_alter_fxTransferParticipant__addFiled_externalParticipantId.js +0 -0
  366. /package/{migrations → src/migrations}/960112_fxTransfer-indexes2.js +0 -0
  367. /package/{migrations → src/migrations}/960113_fxTransferExtension-indexes.js +0 -0
  368. /package/{migrations → src/migrations}/960114_fxTransferTimeout-indexes2.js +0 -0
  369. /package/{migrations → src/migrations}/961000_participantEndpoint_unique.js +0 -0
  370. /package/{migrations → src/migrations}/migration.template +0 -0
  371. /package/{seeds → src/seeds}/amountType.js +0 -0
  372. /package/{seeds → src/seeds}/balanceOfPayments.js +0 -0
  373. /package/{seeds → src/seeds}/bulkProcessingState.js +0 -0
  374. /package/{seeds → src/seeds}/bulkTransferState.js +0 -0
  375. /package/{seeds → src/seeds}/currency.js +0 -0
  376. /package/{seeds → src/seeds}/endpointType.js +0 -0
  377. /package/{seeds → src/seeds}/fxParticipantCurrencyType.js +0 -0
  378. /package/{seeds → src/seeds}/fxTransferType.js +0 -0
  379. /package/{seeds → src/seeds}/ledgerAccountType.js +0 -0
  380. /package/{seeds → src/seeds}/ledgerEntryType.js +0 -0
  381. /package/{seeds → src/seeds}/participantLimitType.js +0 -0
  382. /package/{seeds → src/seeds}/partyIdentifierType.js +0 -0
  383. /package/{seeds → src/seeds}/partyType.js +0 -0
  384. /package/{seeds → src/seeds}/settlementDelay.js +0 -0
  385. /package/{seeds → src/seeds}/settlementGranularity.js +0 -0
  386. /package/{seeds → src/seeds}/settlementState.js +0 -0
  387. /package/{seeds → src/seeds}/settlementWindow1State.js +0 -0
  388. /package/{seeds → src/seeds}/settlementWindow2Open.js +0 -0
  389. /package/{seeds → src/seeds}/transactionInitiator.js +0 -0
  390. /package/{seeds → src/seeds}/transactionInitiatorType.js +0 -0
  391. /package/{seeds → src/seeds}/transactionScenario.js +0 -0
  392. /package/{seeds → src/seeds}/transferParticipantRoleType.js +0 -0
  393. /package/{seeds → src/seeds}/transferState.js +0 -0
  394. /package/{seeds → src/seeds}/z1000_settlementModel-deprecated.js- +0 -0
@@ -0,0 +1,549 @@
1
+ import assert from "node:assert"
2
+ import fs from "node:fs"
3
+
4
+ const BG_YELLOW = '\x1b[43m'
5
+ const BLACK = '\x1b[30m'
6
+ const RESET = '\x1b[0m'
7
+ const BG_RED = '\x1b[41m'
8
+
9
+ export enum SnapshotResultType {
10
+ MATCH = 'MATCH',
11
+ MISMATCH = 'MISMATCH',
12
+ }
13
+
14
+ export interface SnapshotMatch<T> {
15
+ type: SnapshotResultType.MATCH
16
+ actual: T
17
+ snapshot: T
18
+ }
19
+
20
+ export interface SnapshotMismatch<T> {
21
+ type: SnapshotResultType.MISMATCH
22
+ actual: T
23
+ snapshot: T
24
+ diff: string
25
+ /**
26
+ * The updated string to use for updating the inline snapshots.
27
+ */
28
+ update: string
29
+ }
30
+
31
+ export interface SnapshotRange {
32
+ start: number,
33
+ end: number,
34
+ indent: number
35
+ }
36
+
37
+ export type SnapshotResult<T> = SnapshotMatch<T> | SnapshotMismatch<T>
38
+
39
+ /**
40
+ * @class Snapshot
41
+ * @description A simple inline snapshot testing library, inspired by
42
+ * https://tigerbeetle.com/blog/2024-05-14-snapshot-testing-for-the-masses/
43
+ *
44
+ * Motivation:
45
+ * 1. The built in nodejs snapshot features store the files separately, which makes tests harder to
46
+ * read and the snapshots more brittle. It also doesn't support features such as ":ignore", which
47
+ * we have implemented here.
48
+ *
49
+ * 2. Jest-based property matchers are very tricky to write, and are _almost_ snapshots but harder
50
+ * to update and reason about. Sometimes we just want to _see_ exactly what the response from
51
+ * some API was.
52
+ *
53
+ * @example
54
+ *
55
+ * import { describe, it } from "node:test";
56
+ * import { getCallerLocation, Snapshot } from "./snapshot";
57
+ *
58
+ * describe('snapshot smoke test', () => {
59
+ * it('updates the snapshot', () => {
60
+ * const result = {
61
+ * a: true,
62
+ * b: {
63
+ * icecream: 'chocolate'
64
+ * },
65
+ * c: new Date()
66
+ * }
67
+ *
68
+ * Snapshot.from(getCallerLocation(), `{
69
+ * "a": true,
70
+ * "b": {
71
+ * "icecream": "vanilla"
72
+ * },
73
+ * "c": ":ignore"
74
+ * }`).checkStringUnwrap(JSON.stringify(result, null, 2))
75
+ * })
76
+ * })
77
+ *
78
+ * This test ^^ will fail, as the line with "icecream" doesn't match.
79
+ * You can rerun the tests with `SNAP_UPDATE=1` and this library will try and auto-update the
80
+ * snapshot.
81
+ */
82
+ export class Snapshot {
83
+ sourceLocation: CallerLocation
84
+ text: any
85
+ updateable: boolean
86
+
87
+ private constructor(sourceLocation: CallerLocation, text: string, updateable: boolean) {
88
+ this.sourceLocation = sourceLocation
89
+ this.text = text
90
+ this.updateable = updateable
91
+ }
92
+
93
+ public static from(
94
+ text: string,
95
+ options: {
96
+ stripWhitespace: boolean, // Whether or not we should display the update hint. Default: true.
97
+ updateable: boolean // Whether or not we should display the update hint. Default: true.
98
+ } = {
99
+ stripWhitespace: true,
100
+ updateable: true,
101
+ }) {
102
+ const sourceLocation = getCallerLocation(2)
103
+ if (options.stripWhitespace) {
104
+ text = this.stripLeadingWhitespace(text)
105
+ }
106
+ return new Snapshot(sourceLocation, text, options.updateable)
107
+ }
108
+
109
+ /**
110
+ * @description Format the text to remove indents, so we can write prettier inline snapshots:
111
+ *
112
+ * E.g. if we are indented 2 layers deep, this:
113
+ * const snap = Snapshot.from(getCallerLocation(), `{
114
+ * "a": true,
115
+ * }
116
+ * `)
117
+ *
118
+ * is much nicer to write than this:
119
+ * const snap = Snapshot.from(getCallerLocation(), `{
120
+ * "a": true,
121
+ * }
122
+ * `)
123
+ *
124
+ * @param text
125
+ */
126
+ private static stripLeadingWhitespace(text: string): string {
127
+ const lines = text.split('\n')
128
+ // Check the last line for indented whitespace, otherwise the penultimate line.
129
+ let lastLine = lines.pop()
130
+ assert(lastLine !== undefined, 'stripLeadingWhitespace(), text was empty.')
131
+ if (lastLine.length === 0) {
132
+ lastLine = lines.pop()
133
+ }
134
+ assert(lastLine)
135
+
136
+ const indent = lastLine.match(/^\s+/)
137
+ if (!indent) {
138
+ return text
139
+ }
140
+ assert(indent[0].length % 2 === 0, 'Malformatted string. Expected indent to be a multiple of 2.')
141
+ const leadingSpaceRe = new RegExp(String.raw`^\s{${indent[0].length}}`, "gm");
142
+ return text.replaceAll(leadingSpaceRe, '')
143
+ }
144
+
145
+ public checkString(have: string): SnapshotResult<string> {
146
+ return checkSnapshotString(have, this.text)
147
+ }
148
+
149
+ /**
150
+ * Check a `have` of any against the snapshot string.
151
+ */
152
+ public checkUnwrap(have: any): void {
153
+ const haveString = JSON.stringify(have, null, 2);
154
+ return this.checkStringUnwrap(haveString)
155
+ }
156
+
157
+ public checkStringUnwrap(have: string): void {
158
+ const result = checkSnapshotString(have, this.text)
159
+
160
+ if (result.type === SnapshotResultType.MATCH) {
161
+ return
162
+ }
163
+
164
+ console.log(`Snapshot mismatch:\n${result.diff}`)
165
+
166
+ if (!this.updateable) {
167
+ throw new Error(`Snapshot mismatch.`)
168
+ }
169
+
170
+ if (process.env.SNAP_UPDATE === undefined) {
171
+ throw new Error(`Snapshot mismatch. Rerun with SNAP_UPDATE=1 to update the snapshot.`)
172
+ }
173
+ const updated = this._replaceSnapshot('`' + result.update + '`')
174
+ fs.writeFileSync(this.sourceLocation.file, updated)
175
+ throw new Error(`Snapshot updated.`)
176
+ }
177
+
178
+ /**
179
+ * Read the current file, replace the old snapshot with the new snapshot.
180
+ */
181
+ public _replaceSnapshot(update: string): string {
182
+ assert(update[0] === '`', 'Replacement should start with a tick.')
183
+ assert(update.endsWith('`'), 'Replacement should end with a tick.')
184
+ const contents = fs.readFileSync(this.sourceLocation.file).toString('utf-8')
185
+ const range = Snapshot._snapshotRange(contents, this.sourceLocation.line)
186
+ return Snapshot._replaceSnapshot(contents, range, update)
187
+ }
188
+
189
+ /**
190
+ * Replace the old snapshot in the file with a new snapshot.
191
+ */
192
+ public static _replaceSnapshot(contents: string, range: SnapshotRange, update: string): string {
193
+ let newFile = contents.slice(0, range.start - 1)
194
+ const updateLines = update.split('\n')
195
+ updateLines.forEach((line, idx) => {
196
+ // Don't indent the first line
197
+ if (idx === 0) {
198
+ newFile += line + '\n'
199
+ return
200
+ }
201
+ // Skip newline on last line.
202
+ if (idx === updateLines.length - 1) {
203
+ newFile += ' '.repeat(range.indent) + line
204
+ return
205
+ }
206
+ newFile += ' '.repeat(range.indent) + line + '\n'
207
+ })
208
+ newFile += contents.slice(range.end)
209
+
210
+ return newFile;
211
+ }
212
+
213
+ /**
214
+ * Find the snapshot string from ``, inclusive of start and end ticks to be updated.
215
+ */
216
+ public static _snapshotRange(contents: string, lineNum: number, ): SnapshotRange {
217
+ assert(lineNum > 0)
218
+
219
+ const lines = contents.split('\n')
220
+ const lineIdx = lineNum - 1
221
+ let start = 0
222
+ let range = 0
223
+ let indent = 0
224
+ lines.forEach((line, idx) => {
225
+ if (idx < lineIdx) {
226
+ // Don't forget newlines!
227
+ start += line.length + 1
228
+ }
229
+ if (idx === lineIdx) {
230
+ const openingTickIdx = line.indexOf('`')
231
+ if (openingTickIdx === -1) {
232
+ throw new Error(`Couldn't find opening '\`' in snapshot string.`)
233
+ }
234
+ start += openingTickIdx + 1
235
+ }
236
+ })
237
+
238
+ const tail = contents.slice(start)
239
+ const tailLines = tail.split('\n')
240
+ let foundEnd: boolean = false
241
+ let lastLine = ''
242
+ tailLines.forEach((line, idx) => {
243
+ if (foundEnd) {
244
+ return
245
+ }
246
+
247
+ const closingTickIdx = line.indexOf('\`')
248
+ if (closingTickIdx === -1) {
249
+ range += line.length + 1
250
+ return
251
+ }
252
+ range += closingTickIdx + 1
253
+ lastLine = line
254
+ foundEnd = true
255
+ })
256
+ if (!foundEnd) {
257
+ throw new Error(`Snapshot end not found. Searched string:\n${tail}`)
258
+ }
259
+
260
+ // We can find the indent based on the last line before the `.
261
+ // If there are spaces before `
262
+ const indentMatch = lastLine.match(/^\s*/)
263
+ if (indentMatch) {
264
+ indent = indentMatch[0].length
265
+ }
266
+
267
+ return {
268
+ start,
269
+ end: start + range,
270
+ indent
271
+ }
272
+ }
273
+ }
274
+
275
+ export function unwrapSnapshot<T>(result: SnapshotResult<T>): void {
276
+ if (result.type === SnapshotResultType.MATCH) {
277
+ return
278
+ }
279
+
280
+ console.log(`snapshot mismatch:\n${result.diff}`)
281
+
282
+ if (process.env.SNAP_UPDATE === undefined) {
283
+ throw new Error(`Snapshot mismatch. Rerun with SNAP_UPDATE=1 to update the snapshot.`)
284
+ }
285
+
286
+ throw new Error(`Snapshot updated.`)
287
+ }
288
+
289
+ enum SpecialToken {
290
+ IGNORE = 'IGNORE',
291
+ MATCH_STRING = 'MATCH_STRING',
292
+ MATCH_INTEGER = 'MATCH_INTEGER',
293
+ MATCH_DATE = 'MATCH_DATE',
294
+ MATCH_BIGINT = 'MATCH_BIGINT',
295
+ NONE = 'NONE',
296
+ }
297
+
298
+ /**
299
+ * Search the line for special tokens.
300
+ */
301
+ const matchSpecialToken = (line: string): {
302
+ token: SpecialToken,
303
+ index: number,
304
+ literal: string
305
+ } => {
306
+
307
+ const matchers: Array<[string, SpecialToken]> = [
308
+ ['\":ignore', SpecialToken.IGNORE],
309
+ ['\':ignore', SpecialToken.IGNORE],
310
+ [':ignore', SpecialToken.IGNORE],
311
+ ['\":string', SpecialToken.MATCH_STRING],
312
+ ['\':string', SpecialToken.MATCH_STRING],
313
+ [':string', SpecialToken.MATCH_STRING],
314
+ ['":integer', SpecialToken.MATCH_INTEGER],
315
+ ['\':integer', SpecialToken.MATCH_INTEGER],
316
+ [':integer', SpecialToken.MATCH_INTEGER],
317
+ ['":int', SpecialToken.MATCH_INTEGER],
318
+ ['\':int', SpecialToken.MATCH_INTEGER],
319
+ [':int', SpecialToken.MATCH_INTEGER],
320
+ ['":date', SpecialToken.MATCH_DATE],
321
+ ['\':date', SpecialToken.MATCH_DATE],
322
+ [':date', SpecialToken.MATCH_DATE],
323
+ [':bigint', SpecialToken.MATCH_BIGINT],
324
+ ]
325
+
326
+ for (const matcher of matchers) {
327
+ let index = line.indexOf(matcher[0])
328
+ if (index > -1) {
329
+ return {
330
+ token: matcher[1],
331
+ index,
332
+ literal: line.slice(index)
333
+ }
334
+ }
335
+ }
336
+
337
+ // No match.
338
+ return {
339
+ token: SpecialToken.NONE,
340
+ index: 0,
341
+ literal: ''
342
+ }
343
+ }
344
+
345
+ export function checkSnapshotString(actual: string, snapshot: string): SnapshotResult<string> {
346
+ assert(actual)
347
+ assert(typeof actual === 'string')
348
+ assert(snapshot)
349
+ assert(typeof snapshot === 'string')
350
+
351
+ const actualLines = actual.split('\n')
352
+ const snapshotLines = snapshot.split('\n')
353
+ const maxLines = Math.max(actualLines.length, snapshotLines.length)
354
+ const mismatchedLines: Array<number> = []
355
+ // Start with a reasonable number.
356
+ let maxColumnLengthLeft = 45
357
+
358
+ const specialTokens: Array<{ token: SpecialToken, line: number, col: number, literal: string }> = []
359
+
360
+ for (let lineIdx = 0; lineIdx < maxLines; lineIdx++) {
361
+ const left = actualLines[lineIdx] || ''
362
+ const right = snapshotLines[lineIdx] || ''
363
+ if (left.length > maxColumnLengthLeft) {
364
+ maxColumnLengthLeft = left.length
365
+ }
366
+
367
+ if ((!left && right) || (left && !right)) {
368
+ mismatchedLines.push(lineIdx)
369
+ continue
370
+ }
371
+
372
+ const specialToken = matchSpecialToken(right)
373
+ specialTokens.push({ token: specialToken.token, line: lineIdx, col: specialToken.index, literal: specialToken.literal })
374
+ switch (specialToken.token) {
375
+ /**
376
+ * We ignore the rest of the line after the index of the `:ignore`
377
+ */
378
+ case SpecialToken.IGNORE: {
379
+ // if we found a skip token, then only match the line up to the token
380
+ if (left.length < specialToken.index) {
381
+ mismatchedLines.push(lineIdx)
382
+ continue;
383
+ }
384
+
385
+ const leftTruncated = left.substring(0, specialToken.index)
386
+ const rightTruncated = right.substring(0, specialToken.index)
387
+ assert(leftTruncated.length === rightTruncated.length)
388
+ if (leftTruncated !== rightTruncated) {
389
+ mismatchedLines.push(lineIdx)
390
+ continue;
391
+ }
392
+ break;
393
+ }
394
+ // We expect the left side to be a string
395
+ case SpecialToken.MATCH_STRING: {
396
+ if (left.length < specialToken.index) {
397
+ mismatchedLines.push(lineIdx)
398
+ continue;
399
+ }
400
+
401
+ // Ensure the left side is a string.
402
+ const leftCandidate = left.substring(specialToken.index)
403
+ .replace(',', '') // Workaround for trailing commas
404
+ if (!leftCandidate || leftCandidate.length === 0) {
405
+ mismatchedLines.push(lineIdx)
406
+ continue
407
+ }
408
+
409
+ // Parse and see what it might be.
410
+ try {
411
+ const leftParsed = JSON.parse(leftCandidate)
412
+ if (typeof leftParsed !== 'string') {
413
+ mismatchedLines.push(lineIdx)
414
+ }
415
+
416
+ } catch (err) {
417
+ mismatchedLines.push(lineIdx)
418
+ }
419
+
420
+ break;
421
+ }
422
+
423
+ // We expect the left side to be an integer.
424
+ case SpecialToken.MATCH_INTEGER: {
425
+ if (left.length < specialToken.index) {
426
+ mismatchedLines.push(lineIdx)
427
+ continue;
428
+ }
429
+
430
+ const leftCandidate = left.substring(specialToken.index)
431
+ .replace(',', '') // Strip off trailing commas, a little hacky but it works!
432
+ if (!leftCandidate || leftCandidate.length === 0) {
433
+ mismatchedLines.push(lineIdx)
434
+ continue
435
+ }
436
+
437
+ // Parse and see what it might be.
438
+ try {
439
+ const leftParsed = JSON.parse(leftCandidate)
440
+ if (typeof leftParsed !== 'number') {
441
+ mismatchedLines.push(lineIdx)
442
+ }
443
+ } catch (err) {
444
+ mismatchedLines.push(lineIdx)
445
+ }
446
+ break;
447
+ }
448
+ case SpecialToken.NONE: {
449
+ if (left !== right) {
450
+ mismatchedLines.push(lineIdx)
451
+ continue;
452
+ }
453
+ break;
454
+ }
455
+ default: {
456
+ throw new Error(`${specialToken.token} not yet implemented!`)
457
+ }
458
+ }
459
+ }
460
+
461
+ if (mismatchedLines.length === 0) {
462
+ return {
463
+ type: SnapshotResultType.MATCH,
464
+ actual: actual,
465
+ snapshot: snapshot
466
+ }
467
+ }
468
+
469
+ let diff = `${RESET}\n`
470
+ maxColumnLengthLeft = Math.min(85, maxColumnLengthLeft)
471
+ diff += `${'Actual:'.padEnd(maxColumnLengthLeft)} | Snapshot:\n`
472
+ for (let index = 0; index < maxLines; index++) {
473
+ let left = actualLines[index] || ''
474
+ let right = snapshotLines[index] || ''
475
+ // Truncate very long strings. Doesn't affect the matching.
476
+ if (left.length > maxColumnLengthLeft) {
477
+ left = left.slice(0, maxColumnLengthLeft - 20) + '...(truncated)'
478
+ }
479
+ if (right.length > maxColumnLengthLeft) {
480
+ right = right.slice(0, maxColumnLengthLeft - 20) + '...(truncated)'
481
+ }
482
+ if (mismatchedLines.indexOf(index) < 0) {
483
+ diff += `${left.padEnd(maxColumnLengthLeft)} | ${right}\n`
484
+ continue
485
+ }
486
+ diff += `${BG_YELLOW}${left.padEnd(maxColumnLengthLeft)} | ${right}${RESET}\n`
487
+ }
488
+
489
+ return {
490
+ type: SnapshotResultType.MISMATCH,
491
+ actual: actual,
492
+ snapshot: snapshot,
493
+ diff,
494
+ update: updateStringForActualAndTokens(actual, specialTokens)
495
+ }
496
+ }
497
+
498
+ /**
499
+ * @description Take the actual string, and interpolate the special tokens.
500
+ */
501
+ function updateStringForActualAndTokens(
502
+ input: string,
503
+ tokens: Array<{ token: SpecialToken, line: number, col: number, literal: string }>
504
+ ): string {
505
+ let out = ""
506
+ const lines = input.split('\n')
507
+ if (lines.length !== tokens.length) {
508
+ console.warn(`Couldn't update snapshot, as the number of lines don't match.`)
509
+ return input
510
+ }
511
+ lines.forEach((line, idx) => {
512
+ const token = tokens[idx]
513
+ if (token.token === SpecialToken.NONE) {
514
+ out += line + (idx !== lines.length -1 ? '\n' : '')
515
+ return
516
+ }
517
+
518
+ const mergedLine = line.slice(0, token.col) + token.literal
519
+ out += mergedLine + (idx !== lines.length -1 ? '\n' : '')
520
+ })
521
+
522
+ return out
523
+ }
524
+
525
+ type CallerLocation = {
526
+ file: string,
527
+ line: number,
528
+ col: number,
529
+ }
530
+
531
+ /**
532
+ * @description Gets the source file location of the caller.
533
+ */
534
+ export function getCallerLocation(depth: number = 1): CallerLocation {
535
+ const error = new Error()
536
+ assert(error.stack)
537
+ const line = error.stack?.split('\n')[1 + depth]
538
+ if (!line) {
539
+ throw new Error(`getCallerLocation() - line was undefined`);
540
+ }
541
+ const match = line.match(/\((.+):(\d+):(\d+)\)/) || line.match(/at (.+):(\d+):(\d+)/)
542
+ assert(match !== null)
543
+ assert(match!.length === 4)
544
+ return {
545
+ file: match![1],
546
+ line: Number.parseInt(match![2]),
547
+ col: Number.parseInt(match![3])
548
+ }
549
+ }
@@ -0,0 +1,164 @@
1
+ /*****
2
+ License
3
+ --------------
4
+ Copyright © 2020-2026 Mojaloop Foundation
5
+ The Mojaloop files are made available by the Mojaloop Foundation under the Apache License, Version 2.0 (the "License") and you may not use these files except in compliance with the License. You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, the Mojaloop files are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
10
+
11
+ Contributors
12
+ --------------
13
+ This is the official list of the Mojaloop project contributors for this file.
14
+ Names of the original copyright holders (individuals or organizations)
15
+ should be listed with a '*' in the first column. People who have
16
+ contributed from an organization can be listed under the organization
17
+ that actually holds the copyright for their contributions (see the
18
+ Mojaloop Foundation for an example). Those individuals should have
19
+ their names indented and be marked with a '-'. Email address can be added
20
+ optionally within square brackets <email>.
21
+
22
+ * TigerBeetle
23
+ - Lewis Daly <lewis@tigerbeetle.com>
24
+ --------------
25
+
26
+ ******/
27
+
28
+ import { ParsedTap } from './types'
29
+
30
+ /**
31
+ * @function parseTestResult
32
+ * @description Parse a test result line and update counters.
33
+ */
34
+ function parseTestResult(testMatch: RegExpMatchArray, result: ParsedTap): void {
35
+ const ok = testMatch[1] === 'ok'
36
+ const number = Number.parseInt(testMatch[2], 10)
37
+ let description = (testMatch[3] || '').replace(/^ - |^ /, '')
38
+ let directive: string | undefined
39
+
40
+ // Check for directives.
41
+ const directiveMatch = description.match(/# ?(SKIP|TODO)\b/i)
42
+ if (directiveMatch) {
43
+ directive = directiveMatch[1].toUpperCase()
44
+ description = description.replace(directiveMatch[0], '').trim()
45
+ }
46
+
47
+ result.tests.push({ number, ok, description, directive })
48
+ result.total++
49
+
50
+ if (directive === 'SKIP') {
51
+ result.skip++
52
+ } else if (directive === 'TODO') {
53
+ result.todo++
54
+ } else if (ok) {
55
+ result.pass++
56
+ } else {
57
+ result.fail++
58
+ result.ok = false
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @function parseTap
64
+ * @description Parse a TAP output string into a structured format.
65
+ */
66
+ export function parseTap(tap: string): ParsedTap {
67
+ const lines = tap.split('\n')
68
+ const result: ParsedTap = {
69
+ version: undefined,
70
+ plan: null,
71
+ tests: [],
72
+ ok: true,
73
+ total: 0,
74
+ pass: 0,
75
+ fail: 0,
76
+ skip: 0,
77
+ todo: 0
78
+ }
79
+
80
+ for (const line of lines) {
81
+ // Parse TAP version.
82
+ const versionMatch = line.match(/^TAP version (\d+)/)
83
+ if (versionMatch) {
84
+ result.version = Number.parseInt(versionMatch[1], 10)
85
+ continue
86
+ }
87
+
88
+ // Parse plan (1..N).
89
+ const planMatch = line.match(/^(\d+)\.\.(\d+)/)
90
+ if (planMatch) {
91
+ result.plan = {
92
+ start: Number.parseInt(planMatch[1], 10),
93
+ end: Number.parseInt(planMatch[2], 10)
94
+ }
95
+ continue
96
+ }
97
+
98
+ // Parse test result (ok/not ok).
99
+ const testMatch = line.match(/^(ok|not ok) +(\d+)\b(.*)$/)
100
+ if (testMatch) {
101
+ parseTestResult(testMatch, result)
102
+ }
103
+ }
104
+
105
+ return result
106
+ }
107
+
108
+ /**
109
+ * @function mergeTapStreams
110
+ * @description Merge two TAP streams into a single TAP output. Tests from the second stream are
111
+ * renumbered to follow the first.
112
+ */
113
+ export function mergeTapStreams(legacy: string, native: string): string {
114
+ const legacyParsed = parseTap(legacy)
115
+ const nativeParsed = parseTap(native)
116
+
117
+ const legacyCount = legacyParsed.total
118
+ const totalCount = legacyCount + nativeParsed.total
119
+
120
+ const lines: string[] = []
121
+
122
+ lines.push('TAP version 13', `1..${totalCount}`)
123
+
124
+ // Add legacy tests (keep original numbering).
125
+ for (const test of legacyParsed.tests) {
126
+ const status = test.ok ? 'ok' : 'not ok'
127
+ let line = `${status} ${test.number} - ${test.description}`
128
+ if (test.directive) {
129
+ line += ` # ${test.directive}`
130
+ }
131
+ lines.push(line)
132
+ }
133
+
134
+ // Add native tests (renumber starting after legacy).
135
+ for (const test of nativeParsed.tests) {
136
+ const newNumber = legacyCount + test.number
137
+ const status = test.ok ? 'ok' : 'not ok'
138
+ let line = `${status} ${newNumber} - ${test.description}`
139
+ if (test.directive) {
140
+ line += ` # ${test.directive}`
141
+ }
142
+ lines.push(line)
143
+ }
144
+
145
+ // Add summary comments.
146
+ const totalPass = legacyParsed.pass + nativeParsed.pass
147
+ const totalFail = legacyParsed.fail + nativeParsed.fail
148
+ const totalSkip = legacyParsed.skip + nativeParsed.skip
149
+
150
+ lines.push('',
151
+ `# tests ${totalCount}`,
152
+ `# pass ${totalPass}`,
153
+ `# fail ${totalFail}`,
154
+ )
155
+ if (totalSkip > 0) {
156
+ lines.push(`# skip ${totalSkip}`)
157
+ }
158
+
159
+ if (totalFail === 0) {
160
+ lines.push('', '# ok')
161
+ }
162
+
163
+ return lines.join('\n')
164
+ }