@transcend-io/cli 10.0.1 → 10.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 (412) hide show
  1. package/README.md +16 -5
  2. package/dist/{RequestDataSilo-_Iv44M9u.mjs → RequestDataSilo-Rrc2dL9g.mjs} +4 -1
  3. package/dist/RequestDataSilo-Rrc2dL9g.mjs.map +1 -0
  4. package/dist/{app-BfTrk2nc.mjs → app-Cx8-4u8K.mjs} +21 -21
  5. package/dist/{app-BfTrk2nc.mjs.map → app-Cx8-4u8K.mjs.map} +1 -1
  6. package/dist/approvePrivacyRequests-Bjq5cPSI.mjs +2 -0
  7. package/dist/approvePrivacyRequests-Bjq5cPSI.mjs.map +1 -0
  8. package/dist/bin/bash-complete.mjs +1 -1
  9. package/dist/bin/cli.mjs +1 -1
  10. package/dist/bin/deprecated-command.mjs +1 -1
  11. package/dist/buildXdiSyncEndpoint-DWs9ImOw.mjs +9 -0
  12. package/dist/buildXdiSyncEndpoint-DWs9ImOw.mjs.map +1 -0
  13. package/dist/bulkRestartRequests-sie3tM3W.mjs +2 -0
  14. package/dist/bulkRestartRequests-sie3tM3W.mjs.map +1 -0
  15. package/dist/bulkRetryEnrichers-C1RrxiTR.mjs +2 -0
  16. package/dist/bulkRetryEnrichers-C1RrxiTR.mjs.map +1 -0
  17. package/dist/cancelPrivacyRequests-DmvFijq_.mjs +2 -0
  18. package/dist/cancelPrivacyRequests-DmvFijq_.mjs.map +1 -0
  19. package/dist/{codecs-BE3Wmoh8.mjs → codecs-CeDPaLYa.mjs} +1 -1
  20. package/dist/{codecs-BE3Wmoh8.mjs.map → codecs-CeDPaLYa.mjs.map} +1 -1
  21. package/dist/collectCsvFilesOrExit-D-csvd13.mjs +2 -0
  22. package/dist/collectCsvFilesOrExit-D-csvd13.mjs.map +1 -0
  23. package/dist/collectParquetFilesOrExit-C8qT5_57.mjs +2 -0
  24. package/dist/collectParquetFilesOrExit-C8qT5_57.mjs.map +1 -0
  25. package/dist/{command-BXxoAjFo.mjs → command-rzZKmlky.mjs} +2 -2
  26. package/dist/{command-BXxoAjFo.mjs.map → command-rzZKmlky.mjs.map} +1 -1
  27. package/dist/commands/admin/chunk-csv/worker.d.mts +48 -0
  28. package/dist/commands/admin/chunk-csv/worker.d.mts.map +1 -0
  29. package/dist/commands/admin/chunk-csv/worker.mjs +2 -0
  30. package/dist/commands/admin/chunk-csv/worker.mjs.map +1 -0
  31. package/dist/commands/admin/parquet-to-csv/worker.d.mts +25 -0
  32. package/dist/commands/admin/parquet-to-csv/worker.d.mts.map +1 -0
  33. package/dist/commands/admin/parquet-to-csv/worker.mjs +2 -0
  34. package/dist/commands/admin/parquet-to-csv/worker.mjs.map +1 -0
  35. package/dist/{consentManagersToBusinessEntities-BDgOFga7.mjs → consentManagersToBusinessEntities-D1bdBgnA.mjs} +2 -2
  36. package/dist/{consentManagersToBusinessEntities-BDgOFga7.mjs.map → consentManagersToBusinessEntities-D1bdBgnA.mjs.map} +1 -1
  37. package/dist/{constants-lIvXgkdp.mjs → constants-DYbzl8QH.mjs} +1 -1
  38. package/dist/{constants-lIvXgkdp.mjs.map → constants-DYbzl8QH.mjs.map} +1 -1
  39. package/dist/constants-XOsAW1__.mjs +2 -0
  40. package/dist/constants-XOsAW1__.mjs.map +1 -0
  41. package/dist/{constants-AFtS5Nad.mjs → constants-mjLYTIJm.mjs} +2 -2
  42. package/dist/{constants-AFtS5Nad.mjs.map → constants-mjLYTIJm.mjs.map} +1 -1
  43. package/dist/{context-CdSyuBlf.mjs → context-bkKpii_t.mjs} +1 -1
  44. package/dist/{context-CdSyuBlf.mjs.map → context-bkKpii_t.mjs.map} +1 -1
  45. package/dist/createExtraKeyHandler-Jp5XpTJi.mjs +14 -0
  46. package/dist/createExtraKeyHandler-Jp5XpTJi.mjs.map +1 -0
  47. package/dist/{dataFlowsToDataSilos-NhvBw1iy.mjs → dataFlowsToDataSilos-DUj1NhOt.mjs} +1 -1
  48. package/dist/dataFlowsToDataSilos-DUj1NhOt.mjs.map +1 -0
  49. package/dist/{dataSilo-DrFetFXw.mjs → dataSilo-Dvi8-PkH.mjs} +1 -1
  50. package/dist/{dataSilo-DrFetFXw.mjs.map → dataSilo-Dvi8-PkH.mjs.map} +1 -1
  51. package/dist/{dataSubject-y_aXI0pa.mjs → dataSubject-CF784Ug0.mjs} +1 -1
  52. package/dist/{dataSubject-y_aXI0pa.mjs.map → dataSubject-CF784Ug0.mjs.map} +1 -1
  53. package/dist/{done-input-validation-DLR0-MJ7.mjs → done-input-validation-C5rgR0Wr.mjs} +1 -1
  54. package/dist/{done-input-validation-DLR0-MJ7.mjs.map → done-input-validation-C5rgR0Wr.mjs.map} +1 -1
  55. package/dist/downloadPrivacyRequestFiles-GUbd_PRc.mjs +2 -0
  56. package/dist/downloadPrivacyRequestFiles-GUbd_PRc.mjs.map +1 -0
  57. package/dist/{extractClientError-DPjv09EH.mjs → extractClientError-X9wJVqGq.mjs} +1 -1
  58. package/dist/{extractClientError-DPjv09EH.mjs.map → extractClientError-X9wJVqGq.mjs.map} +1 -1
  59. package/dist/{fetchAllRequestEnrichers-CK-kk5eg.mjs → fetchAllRequestEnrichers-Bt97Bb7F.mjs} +5 -5
  60. package/dist/fetchAllRequestEnrichers-Bt97Bb7F.mjs.map +1 -0
  61. package/dist/fetchAllRequestIdentifiers-BXx3rSee.mjs +10 -0
  62. package/dist/fetchAllRequestIdentifiers-BXx3rSee.mjs.map +1 -0
  63. package/dist/fetchAllRequests-xGgt_STo.mjs +2 -0
  64. package/dist/fetchAllRequests-xGgt_STo.mjs.map +1 -0
  65. package/dist/fetchRequestDataSilo-0UvyeL60.mjs +2 -0
  66. package/dist/fetchRequestDataSilo-0UvyeL60.mjs.map +1 -0
  67. package/dist/{fetchRequestFilesForRequest-BbxrEKFK.mjs → fetchRequestFilesForRequest-CJH2iB-P.mjs} +4 -4
  68. package/dist/fetchRequestFilesForRequest-CJH2iB-P.mjs.map +1 -0
  69. package/dist/generateCrossAccountApiKeys-DztJoLQS.mjs +2 -0
  70. package/dist/generateCrossAccountApiKeys-DztJoLQS.mjs.map +1 -0
  71. package/dist/impl-B-PzeHxN.mjs +2 -0
  72. package/dist/impl-B-PzeHxN.mjs.map +1 -0
  73. package/dist/impl-B6TXE2oE.mjs +4 -0
  74. package/dist/impl-B6TXE2oE.mjs.map +1 -0
  75. package/dist/impl-BBKJIP0Q.mjs +2 -0
  76. package/dist/impl-BBKJIP0Q.mjs.map +1 -0
  77. package/dist/impl-BBnnC5xq.mjs +2 -0
  78. package/dist/impl-BBnnC5xq.mjs.map +1 -0
  79. package/dist/impl-BGGm947r2.mjs +2 -0
  80. package/dist/impl-BGGm947r2.mjs.map +1 -0
  81. package/dist/{impl-fqOKTw5J.mjs → impl-BKrNGF2F.mjs} +2 -2
  82. package/dist/{impl-fqOKTw5J.mjs.map → impl-BKrNGF2F.mjs.map} +1 -1
  83. package/dist/{impl-DGiPB5Vq2.mjs → impl-BMnXA_Vd.mjs} +2 -2
  84. package/dist/impl-BMnXA_Vd.mjs.map +1 -0
  85. package/dist/{impl-P_NDC3cX.mjs → impl-BRiRfzgu.mjs} +2 -2
  86. package/dist/{impl-P_NDC3cX.mjs.map → impl-BRiRfzgu.mjs.map} +1 -1
  87. package/dist/{impl-DGuwD_qz.mjs → impl-BSKl6rC6.mjs} +2 -2
  88. package/dist/{impl-DGuwD_qz.mjs.map → impl-BSKl6rC6.mjs.map} +1 -1
  89. package/dist/impl-BVHfSIVG.mjs +2 -0
  90. package/dist/{impl-CMmyv1cl.mjs.map → impl-BVHfSIVG.mjs.map} +1 -1
  91. package/dist/impl-BVnfUDUm.mjs +2 -0
  92. package/dist/impl-BVnfUDUm.mjs.map +1 -0
  93. package/dist/impl-BfeWet_F2.mjs +2 -0
  94. package/dist/impl-BfeWet_F2.mjs.map +1 -0
  95. package/dist/{impl-CSChmq_t2.mjs → impl-BffzTHKU.mjs} +2 -2
  96. package/dist/impl-BffzTHKU.mjs.map +1 -0
  97. package/dist/impl-BxOydpyJ.mjs +2 -0
  98. package/dist/impl-BxOydpyJ.mjs.map +1 -0
  99. package/dist/{impl-BUC4ZelU.mjs → impl-C-u5h8We.mjs} +2 -2
  100. package/dist/{impl-BUC4ZelU.mjs.map → impl-C-u5h8We.mjs.map} +1 -1
  101. package/dist/{impl-KDuBh4bu2.mjs → impl-C3DXXn8M.mjs} +2 -2
  102. package/dist/impl-C3DXXn8M.mjs.map +1 -0
  103. package/dist/{impl-BOUm7wly2.mjs → impl-CC0rkA9s.mjs} +2 -2
  104. package/dist/impl-CC0rkA9s.mjs.map +1 -0
  105. package/dist/impl-CODwodEc.mjs +7 -0
  106. package/dist/impl-CODwodEc.mjs.map +1 -0
  107. package/dist/impl-CPIMsZg-.mjs +2 -0
  108. package/dist/{impl-DEWXA_QC.mjs.map → impl-CPIMsZg-.mjs.map} +1 -1
  109. package/dist/{impl-c7rUQYDc2.mjs → impl-CZsYoSZQ.mjs} +2 -2
  110. package/dist/impl-CZsYoSZQ.mjs.map +1 -0
  111. package/dist/impl-CnHiD4zU.mjs +2 -0
  112. package/dist/impl-CnHiD4zU.mjs.map +1 -0
  113. package/dist/impl-CpJljZV2.mjs +2 -0
  114. package/dist/impl-CpJljZV2.mjs.map +1 -0
  115. package/dist/impl-Cpndlxar.mjs +4 -0
  116. package/dist/impl-Cpndlxar.mjs.map +1 -0
  117. package/dist/{impl-CNez1OAw.mjs → impl-CqH3YYuv.mjs} +2 -2
  118. package/dist/{impl-CNez1OAw.mjs.map → impl-CqH3YYuv.mjs.map} +1 -1
  119. package/dist/{impl-MpkLBntW.mjs → impl-CvJtt8H2.mjs} +2 -2
  120. package/dist/{impl-MpkLBntW.mjs.map → impl-CvJtt8H2.mjs.map} +1 -1
  121. package/dist/impl-Cw10WeUv.mjs +2 -0
  122. package/dist/impl-Cw10WeUv.mjs.map +1 -0
  123. package/dist/{impl-CCUsnhoW2.mjs → impl-Cy8-6_Oo2.mjs} +2 -2
  124. package/dist/{impl-CCUsnhoW2.mjs.map → impl-Cy8-6_Oo2.mjs.map} +1 -1
  125. package/dist/{impl-CNykdy3e2.mjs → impl-DJ4VCAcc.mjs} +2 -2
  126. package/dist/impl-DJ4VCAcc.mjs.map +1 -0
  127. package/dist/impl-DKAV-8XC.mjs +3 -0
  128. package/dist/impl-DKAV-8XC.mjs.map +1 -0
  129. package/dist/{impl-JThkrXiI2.mjs → impl-D_AxguFh2.mjs} +2 -2
  130. package/dist/{impl-JThkrXiI2.mjs.map → impl-D_AxguFh2.mjs.map} +1 -1
  131. package/dist/{impl-D-cp0CYr.mjs → impl-DaK9UOwL.mjs} +2 -2
  132. package/dist/{impl-D-cp0CYr.mjs.map → impl-DaK9UOwL.mjs.map} +1 -1
  133. package/dist/{impl-Cgg_bv7j.mjs → impl-DfVep2mE.mjs} +2 -2
  134. package/dist/{impl-Cgg_bv7j.mjs.map → impl-DfVep2mE.mjs.map} +1 -1
  135. package/dist/impl-DhXQb3bm.mjs +2 -0
  136. package/dist/impl-DhXQb3bm.mjs.map +1 -0
  137. package/dist/impl-DpGVNllB.mjs +2 -0
  138. package/dist/impl-DpGVNllB.mjs.map +1 -0
  139. package/dist/impl-DpwyYsfg.mjs +2 -0
  140. package/dist/impl-DpwyYsfg.mjs.map +1 -0
  141. package/dist/impl-DvrSuAJv.mjs +12 -0
  142. package/dist/impl-DvrSuAJv.mjs.map +1 -0
  143. package/dist/{impl-CqXFyvgV2.mjs → impl-Dw9uW5zy2.mjs} +2 -2
  144. package/dist/{impl-CqXFyvgV2.mjs.map → impl-Dw9uW5zy2.mjs.map} +1 -1
  145. package/dist/impl-PdIU1pLr2.mjs +2 -0
  146. package/dist/impl-PdIU1pLr2.mjs.map +1 -0
  147. package/dist/{impl-D9NjIwEi2.mjs → impl-StdJMCiM.mjs} +2 -2
  148. package/dist/impl-StdJMCiM.mjs.map +1 -0
  149. package/dist/impl-daUiLV3c.mjs +2 -0
  150. package/dist/impl-daUiLV3c.mjs.map +1 -0
  151. package/dist/{impl-Rt3C_fDF.mjs → impl-iGMjSniP.mjs} +2 -2
  152. package/dist/{impl-Rt3C_fDF.mjs.map → impl-iGMjSniP.mjs.map} +1 -1
  153. package/dist/impl-ogUHfunr.mjs +2 -0
  154. package/dist/impl-ogUHfunr.mjs.map +1 -0
  155. package/dist/impl-uwkj-RbF.mjs +2 -0
  156. package/dist/impl-uwkj-RbF.mjs.map +1 -0
  157. package/dist/{impl-DGzvE8aJ.mjs → impl-yvc0y1uO.mjs} +2 -2
  158. package/dist/{impl-DGzvE8aJ.mjs.map → impl-yvc0y1uO.mjs.map} +1 -1
  159. package/dist/index.d.mts +664 -3851
  160. package/dist/index.d.mts.map +1 -1
  161. package/dist/index.mjs +4 -78
  162. package/dist/index.mjs.map +1 -1
  163. package/dist/{inquirer-BgNcicZ4.mjs → inquirer-DyRwhvoh.mjs} +2 -2
  164. package/dist/{inquirer-BgNcicZ4.mjs.map → inquirer-DyRwhvoh.mjs.map} +1 -1
  165. package/dist/{listFiles-qzyQMaYH.mjs → listFiles-Odj7j2E1.mjs} +1 -1
  166. package/dist/{listFiles-qzyQMaYH.mjs.map → listFiles-Odj7j2E1.mjs.map} +1 -1
  167. package/dist/{logger-B-LXIf3U.mjs → logger-Bj782ZYD.mjs} +1 -1
  168. package/dist/{logger-B-LXIf3U.mjs.map → logger-Bj782ZYD.mjs.map} +1 -1
  169. package/dist/markRequestDataSiloIdsCompleted-DJSICILv.mjs +2 -0
  170. package/dist/markRequestDataSiloIdsCompleted-DJSICILv.mjs.map +1 -0
  171. package/dist/markSilentPrivacyRequests-ytCzpUkY.mjs +2 -0
  172. package/dist/markSilentPrivacyRequests-ytCzpUkY.mjs.map +1 -0
  173. package/dist/notifyPrivacyRequestsAdditionalTime-D8v68eAg.mjs +2 -0
  174. package/dist/notifyPrivacyRequestsAdditionalTime-D8v68eAg.mjs.map +1 -0
  175. package/dist/parquetToCsvOneFile-bgEgRoAi.mjs +6 -0
  176. package/dist/parquetToCsvOneFile-bgEgRoAi.mjs.map +1 -0
  177. package/dist/parseAttributesFromString-B8h4DudO.mjs +2 -0
  178. package/dist/{parseAttributesFromString-CZStzJc0.mjs.map → parseAttributesFromString-B8h4DudO.mjs.map} +1 -1
  179. package/dist/parseVariablesFromString-CvoeZZ75.mjs +23 -0
  180. package/dist/parseVariablesFromString-CvoeZZ75.mjs.map +1 -0
  181. package/dist/pullAllDatapoints-CqgqXRbp.mjs +45 -0
  182. package/dist/pullAllDatapoints-CqgqXRbp.mjs.map +1 -0
  183. package/dist/pullChunkedCustomSiloOutstandingIdentifiers-DaYEDZ66.mjs +2 -0
  184. package/dist/pullChunkedCustomSiloOutstandingIdentifiers-DaYEDZ66.mjs.map +1 -0
  185. package/dist/pullConsentManagerMetrics-BO0hYPDG.mjs +2 -0
  186. package/dist/pullConsentManagerMetrics-BO0hYPDG.mjs.map +1 -0
  187. package/dist/pullManualEnrichmentIdentifiersToCsv-BNuhsG20.mjs +2 -0
  188. package/dist/pullManualEnrichmentIdentifiersToCsv-BNuhsG20.mjs.map +1 -0
  189. package/dist/pullTranscendConfiguration-DSyMRyPe.mjs +58 -0
  190. package/dist/pullTranscendConfiguration-DSyMRyPe.mjs.map +1 -0
  191. package/dist/{pullUnstructuredSubDataPointRecommendations-DZd2q6S2.mjs → pullUnstructuredSubDataPointRecommendations-jE-tdoVK.mjs} +4 -4
  192. package/dist/pullUnstructuredSubDataPointRecommendations-jE-tdoVK.mjs.map +1 -0
  193. package/dist/pushCronIdentifiersFromCsv-D9Hzna0W.mjs +2 -0
  194. package/dist/pushCronIdentifiersFromCsv-D9Hzna0W.mjs.map +1 -0
  195. package/dist/pushManualEnrichmentIdentifiersFromCsv-BiR7PS_d.mjs +2 -0
  196. package/dist/pushManualEnrichmentIdentifiersFromCsv-BiR7PS_d.mjs.map +1 -0
  197. package/dist/{readCsv-CyOL7eCc.mjs → readCsv-0PIlJQCN.mjs} +1 -1
  198. package/dist/{readCsv-CyOL7eCc.mjs.map → readCsv-0PIlJQCN.mjs.map} +1 -1
  199. package/dist/{readTranscendYaml-D-J1ilS0.mjs → readTranscendYaml-DVkQL2SC.mjs} +2 -2
  200. package/dist/{readTranscendYaml-D-J1ilS0.mjs.map → readTranscendYaml-DVkQL2SC.mjs.map} +1 -1
  201. package/dist/removeUnverifiedRequestIdentifiers-B0Gx09XN.mjs +35 -0
  202. package/dist/removeUnverifiedRequestIdentifiers-B0Gx09XN.mjs.map +1 -0
  203. package/dist/{request-CAsR6CMY.mjs → request-SLqRySNU.mjs} +1 -1
  204. package/dist/{request-CAsR6CMY.mjs.map → request-SLqRySNU.mjs.map} +1 -1
  205. package/dist/retryRequestDataSilos-DFjFhhC0.mjs +2 -0
  206. package/dist/retryRequestDataSilos-DFjFhhC0.mjs.map +1 -0
  207. package/dist/skipPreflightJobs-Bm8lZZk-.mjs +2 -0
  208. package/dist/skipPreflightJobs-Bm8lZZk-.mjs.map +1 -0
  209. package/dist/skipRequestDataSilos-B5FByYTj.mjs +2 -0
  210. package/dist/skipRequestDataSilos-B5FByYTj.mjs.map +1 -0
  211. package/dist/streamPrivacyRequestsToCsv-CBzh80oQ.mjs +2 -0
  212. package/dist/streamPrivacyRequestsToCsv-CBzh80oQ.mjs.map +1 -0
  213. package/dist/syncCodePackages-BOS5foh6.mjs +2 -0
  214. package/dist/syncCodePackages-BOS5foh6.mjs.map +1 -0
  215. package/dist/syncEnrichers-C9HcWCrs.mjs +3 -0
  216. package/dist/syncEnrichers-C9HcWCrs.mjs.map +1 -0
  217. package/dist/updateConsentManagerVersionToLatest-X1HAM_IX.mjs +2 -0
  218. package/dist/updateConsentManagerVersionToLatest-X1HAM_IX.mjs.map +1 -0
  219. package/dist/uploadConsents-BP5XILuw.mjs +2 -0
  220. package/dist/uploadConsents-BP5XILuw.mjs.map +1 -0
  221. package/dist/uploadCookiesFromCsv-B42cZgYW.mjs +2 -0
  222. package/dist/uploadCookiesFromCsv-B42cZgYW.mjs.map +1 -0
  223. package/dist/uploadDataFlowsFromCsv-D2V567pP.mjs +2 -0
  224. package/dist/uploadDataFlowsFromCsv-D2V567pP.mjs.map +1 -0
  225. package/dist/uploadPrivacyRequestsFromCsv-Czc3vGfJ.mjs +2 -0
  226. package/dist/uploadPrivacyRequestsFromCsv-Czc3vGfJ.mjs.map +1 -0
  227. package/dist/{validateTranscendAuth-1W1IylqE.mjs → validateTranscendAuth-DCwAtgvh.mjs} +2 -2
  228. package/dist/{validateTranscendAuth-1W1IylqE.mjs.map → validateTranscendAuth-DCwAtgvh.mjs.map} +1 -1
  229. package/dist/{writeCsv-B51ulrVl.mjs → writeCsv-Da8NUe1V.mjs} +1 -1
  230. package/dist/{writeCsv-B51ulrVl.mjs.map → writeCsv-Da8NUe1V.mjs.map} +1 -1
  231. package/package.json +10 -7
  232. package/dist/RateCounter-DFL_mnk2.mjs +0 -2
  233. package/dist/RateCounter-DFL_mnk2.mjs.map +0 -1
  234. package/dist/RequestDataSilo-_Iv44M9u.mjs.map +0 -1
  235. package/dist/approvePrivacyRequests-CWGZR2N6.mjs +0 -2
  236. package/dist/approvePrivacyRequests-CWGZR2N6.mjs.map +0 -1
  237. package/dist/assessment-BDywVaGR.mjs +0 -284
  238. package/dist/assessment-BDywVaGR.mjs.map +0 -1
  239. package/dist/bluebird-CUitXgsY.mjs +0 -2
  240. package/dist/bluebird-CUitXgsY.mjs.map +0 -1
  241. package/dist/buildXdiSyncEndpoint-Cb-pvpak.mjs +0 -9
  242. package/dist/buildXdiSyncEndpoint-Cb-pvpak.mjs.map +0 -1
  243. package/dist/bulkRestartRequests-CKF_xpN0.mjs +0 -2
  244. package/dist/bulkRestartRequests-CKF_xpN0.mjs.map +0 -1
  245. package/dist/bulkRetryEnrichers-B-Szmin-.mjs +0 -2
  246. package/dist/bulkRetryEnrichers-B-Szmin-.mjs.map +0 -1
  247. package/dist/cancelPrivacyRequests-DNiL13E_.mjs +0 -2
  248. package/dist/cancelPrivacyRequests-DNiL13E_.mjs.map +0 -1
  249. package/dist/codecs-Dx_vGxsl.mjs +0 -2
  250. package/dist/codecs-Dx_vGxsl.mjs.map +0 -1
  251. package/dist/constants-CeMiHaHx.mjs +0 -2
  252. package/dist/constants-CeMiHaHx.mjs.map +0 -1
  253. package/dist/createExtraKeyHandler-tubeaEjA.mjs +0 -23
  254. package/dist/createExtraKeyHandler-tubeaEjA.mjs.map +0 -1
  255. package/dist/createPreferenceAccessTokens-DqmFctn3.mjs +0 -10
  256. package/dist/createPreferenceAccessTokens-DqmFctn3.mjs.map +0 -1
  257. package/dist/createSombraGotInstance-D1Il9zUE.mjs +0 -10
  258. package/dist/createSombraGotInstance-D1Il9zUE.mjs.map +0 -1
  259. package/dist/dataFlowsToDataSilos-NhvBw1iy.mjs.map +0 -1
  260. package/dist/downloadPrivacyRequestFiles-DlpgxqHF.mjs +0 -2
  261. package/dist/downloadPrivacyRequestFiles-DlpgxqHF.mjs.map +0 -1
  262. package/dist/extractErrorMessage-CPnTsT1S.mjs +0 -2
  263. package/dist/extractErrorMessage-CPnTsT1S.mjs.map +0 -1
  264. package/dist/fetchAllActions-BJsPdnxy.mjs +0 -832
  265. package/dist/fetchAllActions-BJsPdnxy.mjs.map +0 -1
  266. package/dist/fetchAllDataFlows-D248lO6_.mjs +0 -2
  267. package/dist/fetchAllDataFlows-D248lO6_.mjs.map +0 -1
  268. package/dist/fetchAllPreferenceTopics-ForE9GpZ.mjs +0 -36
  269. package/dist/fetchAllPreferenceTopics-ForE9GpZ.mjs.map +0 -1
  270. package/dist/fetchAllPurposes-ZdkO2fMp.mjs +0 -29
  271. package/dist/fetchAllPurposes-ZdkO2fMp.mjs.map +0 -1
  272. package/dist/fetchAllPurposesAndPreferences-DD6OyA5t.mjs +0 -2
  273. package/dist/fetchAllPurposesAndPreferences-DD6OyA5t.mjs.map +0 -1
  274. package/dist/fetchAllRequestEnrichers-CK-kk5eg.mjs.map +0 -1
  275. package/dist/fetchAllRequestIdentifiers-DrFFOt0m.mjs +0 -10
  276. package/dist/fetchAllRequestIdentifiers-DrFFOt0m.mjs.map +0 -1
  277. package/dist/fetchAllRequests-DNQQsY4s.mjs +0 -2
  278. package/dist/fetchAllRequests-DNQQsY4s.mjs.map +0 -1
  279. package/dist/fetchApiKeys-DjOr44xA.mjs +0 -33
  280. package/dist/fetchApiKeys-DjOr44xA.mjs.map +0 -1
  281. package/dist/fetchCatalogs-BM4FCbcS.mjs +0 -12
  282. package/dist/fetchCatalogs-BM4FCbcS.mjs.map +0 -1
  283. package/dist/fetchConsentManagerId-CFkg3-RS.mjs +0 -321
  284. package/dist/fetchConsentManagerId-CFkg3-RS.mjs.map +0 -1
  285. package/dist/fetchIdentifiers-pjQV4vUg.mjs +0 -54
  286. package/dist/fetchIdentifiers-pjQV4vUg.mjs.map +0 -1
  287. package/dist/fetchRequestDataSilo-P4yA7Lyc.mjs +0 -2
  288. package/dist/fetchRequestDataSilo-P4yA7Lyc.mjs.map +0 -1
  289. package/dist/fetchRequestFilesForRequest-BbxrEKFK.mjs.map +0 -1
  290. package/dist/generateCrossAccountApiKeys-Bxc_dzMG.mjs +0 -33
  291. package/dist/generateCrossAccountApiKeys-Bxc_dzMG.mjs.map +0 -1
  292. package/dist/impl-4ltdSmpl2.mjs +0 -4
  293. package/dist/impl-4ltdSmpl2.mjs.map +0 -1
  294. package/dist/impl-B19fH75P.mjs +0 -12
  295. package/dist/impl-B19fH75P.mjs.map +0 -1
  296. package/dist/impl-BBMjv5YQ.mjs +0 -2
  297. package/dist/impl-BBMjv5YQ.mjs.map +0 -1
  298. package/dist/impl-BKH3QRLi.mjs +0 -3
  299. package/dist/impl-BKH3QRLi.mjs.map +0 -1
  300. package/dist/impl-BOUm7wly2.mjs.map +0 -1
  301. package/dist/impl-BhTCp0kg.mjs +0 -2
  302. package/dist/impl-BhTCp0kg.mjs.map +0 -1
  303. package/dist/impl-BlHU1bbJ2.mjs +0 -2
  304. package/dist/impl-BlHU1bbJ2.mjs.map +0 -1
  305. package/dist/impl-BwjguKHC.mjs +0 -4
  306. package/dist/impl-BwjguKHC.mjs.map +0 -1
  307. package/dist/impl-C2o0eDzJ.mjs +0 -2
  308. package/dist/impl-C2o0eDzJ.mjs.map +0 -1
  309. package/dist/impl-C8HKnjw82.mjs +0 -2
  310. package/dist/impl-C8HKnjw82.mjs.map +0 -1
  311. package/dist/impl-CCc-wXqD.mjs +0 -2
  312. package/dist/impl-CCc-wXqD.mjs.map +0 -1
  313. package/dist/impl-CMmyv1cl.mjs +0 -2
  314. package/dist/impl-CNykdy3e2.mjs.map +0 -1
  315. package/dist/impl-CSChmq_t2.mjs.map +0 -1
  316. package/dist/impl-Ce9K4OCp.mjs +0 -2
  317. package/dist/impl-Ce9K4OCp.mjs.map +0 -1
  318. package/dist/impl-ChCqHkOc2.mjs +0 -2
  319. package/dist/impl-ChCqHkOc2.mjs.map +0 -1
  320. package/dist/impl-CqEwwWeD.mjs +0 -2
  321. package/dist/impl-CqEwwWeD.mjs.map +0 -1
  322. package/dist/impl-CxLSJk2P.mjs +0 -2
  323. package/dist/impl-CxLSJk2P.mjs.map +0 -1
  324. package/dist/impl-CzU9WTiW.mjs +0 -2
  325. package/dist/impl-CzU9WTiW.mjs.map +0 -1
  326. package/dist/impl-D9NjIwEi2.mjs.map +0 -1
  327. package/dist/impl-DEWXA_QC.mjs +0 -2
  328. package/dist/impl-DGiPB5Vq2.mjs.map +0 -1
  329. package/dist/impl-DTp9OQIZ.mjs +0 -7
  330. package/dist/impl-DTp9OQIZ.mjs.map +0 -1
  331. package/dist/impl-DhscnXSw.mjs +0 -2
  332. package/dist/impl-DhscnXSw.mjs.map +0 -1
  333. package/dist/impl-Dk7MdX-1.mjs +0 -2
  334. package/dist/impl-Dk7MdX-1.mjs.map +0 -1
  335. package/dist/impl-DsNPvet4.mjs +0 -2
  336. package/dist/impl-DsNPvet4.mjs.map +0 -1
  337. package/dist/impl-DxUFb0vv.mjs +0 -2
  338. package/dist/impl-DxUFb0vv.mjs.map +0 -1
  339. package/dist/impl-KDuBh4bu2.mjs.map +0 -1
  340. package/dist/impl-c7rUQYDc2.mjs.map +0 -1
  341. package/dist/impl-oiBTZqQS2.mjs +0 -2
  342. package/dist/impl-oiBTZqQS2.mjs.map +0 -1
  343. package/dist/impl-tbGnvKFm.mjs +0 -2
  344. package/dist/impl-tbGnvKFm.mjs.map +0 -1
  345. package/dist/makeGraphQLRequest-Cq26A_Lq.mjs +0 -2
  346. package/dist/makeGraphQLRequest-Cq26A_Lq.mjs.map +0 -1
  347. package/dist/markRequestDataSiloIdsCompleted-DzqJ5MNY.mjs +0 -2
  348. package/dist/markRequestDataSiloIdsCompleted-DzqJ5MNY.mjs.map +0 -1
  349. package/dist/markSilentPrivacyRequests-BKQUu6Ep.mjs +0 -2
  350. package/dist/markSilentPrivacyRequests-BKQUu6Ep.mjs.map +0 -1
  351. package/dist/mergeTranscendInputs-DGC4xUGu.mjs +0 -2
  352. package/dist/mergeTranscendInputs-DGC4xUGu.mjs.map +0 -1
  353. package/dist/notifyPrivacyRequestsAdditionalTime-TEHAJe4C.mjs +0 -2
  354. package/dist/notifyPrivacyRequestsAdditionalTime-TEHAJe4C.mjs.map +0 -1
  355. package/dist/package-C4J38oR1.mjs +0 -2
  356. package/dist/package-C4J38oR1.mjs.map +0 -1
  357. package/dist/parquetToCsvOneFile-DZVKXrjn.mjs +0 -6
  358. package/dist/parquetToCsvOneFile-DZVKXrjn.mjs.map +0 -1
  359. package/dist/parseAttributesFromString-CZStzJc0.mjs +0 -2
  360. package/dist/pullAllDatapoints-Cntwuzw7.mjs +0 -45
  361. package/dist/pullAllDatapoints-Cntwuzw7.mjs.map +0 -1
  362. package/dist/pullChunkedCustomSiloOutstandingIdentifiers-BT-GZpT1.mjs +0 -2
  363. package/dist/pullChunkedCustomSiloOutstandingIdentifiers-BT-GZpT1.mjs.map +0 -1
  364. package/dist/pullConsentManagerMetrics-FnhPEszu.mjs +0 -2
  365. package/dist/pullConsentManagerMetrics-FnhPEszu.mjs.map +0 -1
  366. package/dist/pullManualEnrichmentIdentifiersToCsv-B_4REnga.mjs +0 -2
  367. package/dist/pullManualEnrichmentIdentifiersToCsv-B_4REnga.mjs.map +0 -1
  368. package/dist/pullTranscendConfiguration-CqsgEf9A.mjs +0 -80
  369. package/dist/pullTranscendConfiguration-CqsgEf9A.mjs.map +0 -1
  370. package/dist/pullUnstructuredSubDataPointRecommendations-DZd2q6S2.mjs.map +0 -1
  371. package/dist/pushCronIdentifiersFromCsv-D2saGR5i.mjs +0 -2
  372. package/dist/pushCronIdentifiersFromCsv-D2saGR5i.mjs.map +0 -1
  373. package/dist/pushManualEnrichmentIdentifiersFromCsv-DOvAzMyt.mjs +0 -2
  374. package/dist/pushManualEnrichmentIdentifiersFromCsv-DOvAzMyt.mjs.map +0 -1
  375. package/dist/removeUnverifiedRequestIdentifiers-ChlwRmhd.mjs +0 -35
  376. package/dist/removeUnverifiedRequestIdentifiers-ChlwRmhd.mjs.map +0 -1
  377. package/dist/retryRequestDataSilos-DnwXA1YZ.mjs +0 -2
  378. package/dist/retryRequestDataSilos-DnwXA1YZ.mjs.map +0 -1
  379. package/dist/skipPreflightJobs-jK5lNlmv.mjs +0 -2
  380. package/dist/skipPreflightJobs-jK5lNlmv.mjs.map +0 -1
  381. package/dist/skipRequestDataSilos-DQGroOos.mjs +0 -2
  382. package/dist/skipRequestDataSilos-DQGroOos.mjs.map +0 -1
  383. package/dist/splitCsvToList-BRq_CIfd.mjs +0 -2
  384. package/dist/splitCsvToList-BRq_CIfd.mjs.map +0 -1
  385. package/dist/streamPrivacyRequestsToCsv-BK07Bm-T.mjs +0 -2
  386. package/dist/streamPrivacyRequestsToCsv-BK07Bm-T.mjs.map +0 -1
  387. package/dist/syncCodePackages-F-97FNjo.mjs +0 -232
  388. package/dist/syncCodePackages-F-97FNjo.mjs.map +0 -1
  389. package/dist/syncCookies-BxY36BeJ.mjs +0 -2
  390. package/dist/syncCookies-BxY36BeJ.mjs.map +0 -1
  391. package/dist/syncDataFlows-Cx5LZCen.mjs +0 -2
  392. package/dist/syncDataFlows-Cx5LZCen.mjs.map +0 -1
  393. package/dist/syncTemplates-BrH7Yr0V.mjs +0 -23
  394. package/dist/syncTemplates-BrH7Yr0V.mjs.map +0 -1
  395. package/dist/time-Bl_c3W8U.mjs +0 -2
  396. package/dist/time-Bl_c3W8U.mjs.map +0 -1
  397. package/dist/types-B4CVJCpj.mjs +0 -2
  398. package/dist/types-B4CVJCpj.mjs.map +0 -1
  399. package/dist/updateConsentManagerVersionToLatest-C221vAAw.mjs +0 -2
  400. package/dist/updateConsentManagerVersionToLatest-C221vAAw.mjs.map +0 -1
  401. package/dist/uploadConsents-BbR7_sSt.mjs +0 -2
  402. package/dist/uploadConsents-BbR7_sSt.mjs.map +0 -1
  403. package/dist/uploadCookiesFromCsv-roHWekOP.mjs +0 -2
  404. package/dist/uploadCookiesFromCsv-roHWekOP.mjs.map +0 -1
  405. package/dist/uploadDataFlowsFromCsv-DcTbrsv2.mjs +0 -2
  406. package/dist/uploadDataFlowsFromCsv-DcTbrsv2.mjs.map +0 -1
  407. package/dist/uploadPrivacyRequestsFromCsv-BUGTS-pY.mjs +0 -17
  408. package/dist/uploadPrivacyRequestsFromCsv-BUGTS-pY.mjs.map +0 -1
  409. package/dist/uploadSiloDiscoveryResults-D2fK92WR.mjs +0 -20
  410. package/dist/uploadSiloDiscoveryResults-D2fK92WR.mjs.map +0 -1
  411. package/dist/withPreferenceRetry-xLMZyTq9.mjs +0 -2
  412. package/dist/withPreferenceRetry-xLMZyTq9.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncCodePackages-BOS5foh6.mjs","names":[],"sources":["../src/lib/graphql/syncCodePackages.ts"],"sourcesContent":["import { CodePackageType } from '@transcend-io/privacy-types';\nimport {\n CREATE_CODE_PACKAGE,\n UPDATE_CODE_PACKAGES,\n fetchAllCodePackages,\n type CodePackage,\n makeGraphQLRequest,\n syncRepositories,\n syncSoftwareDevelopmentKits,\n} from '@transcend-io/sdk';\nimport { map, mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, uniq, keyBy, uniqBy } from 'lodash-es';\n\nimport { CodePackageInput, RepositoryInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\n\nconst CHUNK_SIZE = 100;\n\nconst LOOKUP_SPLIT_KEY = '%%%%';\n\n/**\n * Create a new code package\n *\n * @param client - GraphQL client\n * @param input - Code package input\n * @returns Code package ID\n */\nexport async function createCodePackage(\n client: GraphQLClient,\n input: {\n /** Name of package */\n name: string;\n /** Description of package */\n description?: string;\n /** Type of package */\n type: CodePackageType;\n /** Relative path to package */\n relativePath: string;\n /** Repository ID */\n repositoryId?: string;\n /** Name of repository */\n repositoryName?: string;\n /** IDs of SDKs */\n softwareDevelopmentKitIds?: string[];\n /** IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** IDs of teams */\n teamIds?: string[];\n /** Names of teams */\n teamNames?: string[];\n },\n): Promise<CodePackage> {\n const {\n createCodePackage: { codePackage },\n } = await makeGraphQLRequest<{\n /** createCodePackage mutation */\n createCodePackage: {\n /** Code package */\n codePackage: CodePackage;\n };\n }>(client, CREATE_CODE_PACKAGE, {\n variables: { input },\n logger,\n });\n logger.info(colors.green(`Successfully created code package \"${input.name}\"!`));\n return codePackage;\n}\n\n/**\n * Update an existing code package\n *\n * @param client - GraphQL client\n * @param inputs - Code package input\n * @returns Code packages that were updated\n */\nexport async function updateCodePackages(\n client: GraphQLClient,\n inputs: {\n /** ID of code package */\n id: string;\n /** Name of package */\n name: string;\n /** Description of package */\n description?: string;\n /** Type of package */\n type: CodePackageType;\n /** Relative path to package */\n relativePath: string;\n /** Repository ID */\n repositoryId?: string;\n /** Name of repository */\n repositoryName?: string;\n /** IDs of SDKs */\n softwareDevelopmentKitIds?: string[];\n /** IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** IDs of teams */\n teamIds?: string[];\n /** Names of teams */\n teamNames?: string[];\n }[],\n): Promise<CodePackage[]> {\n const {\n updateCodePackages: { codePackages },\n } = await makeGraphQLRequest<{\n /** updateCodePackages mutation */\n updateCodePackages: {\n /** Code packages */\n codePackages: CodePackage[];\n };\n }>(client, UPDATE_CODE_PACKAGES, {\n variables: {\n input: {\n codePackages: inputs,\n },\n },\n logger,\n });\n logger.info(colors.green(`Successfully updated ${inputs.length} code packages!`));\n return codePackages;\n}\n\n/**\n * Uploads silo discovery results for Transcend to classify\n *\n * @param client - GraphQL Client\n * @param codePackages - Packages to upload\n * @param concurrency - How many concurrent requests to make\n * @returns True if successful, false if any updates failed, or an error occurs\n */\nexport async function syncCodePackages(\n client: GraphQLClient,\n codePackages: CodePackageInput[],\n concurrency = 20,\n): Promise<boolean> {\n let encounteredError = false;\n const [existingCodePackages, { softwareDevelopmentKits: existingSoftwareDevelopmentKits }] =\n await Promise.all([\n // fetch all code packages\n fetchAllCodePackages(client, { logger }),\n // make sure all SDKs exist\n syncSoftwareDevelopmentKits(\n client,\n uniqBy(\n codePackages\n .map(({ type, softwareDevelopmentKits = [] }) =>\n softwareDevelopmentKits.map(({ name }) => ({\n name,\n codePackageType: type,\n })),\n )\n .flat(),\n ({ name, codePackageType }) => `${name}${LOOKUP_SPLIT_KEY}${codePackageType}`,\n ),\n { logger, concurrency },\n ),\n // make sure all Repositories exist\n syncRepositories(\n client,\n uniqBy(codePackages, 'repositoryName').map(\n ({ repositoryName }) =>\n ({\n name: repositoryName,\n url: `https://github.com/${repositoryName}`,\n }) as RepositoryInput,\n ),\n { logger },\n ),\n ]);\n\n const softwareDevelopmentKitLookup = keyBy(\n existingSoftwareDevelopmentKits,\n ({ name, codePackageType }) => `${name}${LOOKUP_SPLIT_KEY}${codePackageType}`,\n );\n const codePackagesLookup = keyBy(\n existingCodePackages,\n ({ name, type }) => `${name}${LOOKUP_SPLIT_KEY}${type}`,\n );\n\n // Determine which codePackages are new vs existing\n const mapCodePackagesToExisting = codePackages.map((codePackageInput) => [\n codePackageInput,\n codePackagesLookup[`${codePackageInput.name}${LOOKUP_SPLIT_KEY}${codePackageInput.type}`]?.id,\n ]);\n\n // Create the new codePackages\n const newCodePackages = mapCodePackagesToExisting\n .filter(([, existing]) => !existing)\n .map(([codePackageInput]) => codePackageInput as CodePackageInput);\n try {\n logger.info(colors.magenta(`Creating \"${newCodePackages.length}\" new code packages...`));\n await map(\n newCodePackages,\n async ({ softwareDevelopmentKits, ...codePackage }) => {\n await createCodePackage(client, {\n ...codePackage,\n ...(softwareDevelopmentKits\n ? {\n softwareDevelopmentKitIds: uniq(\n softwareDevelopmentKits.map(({ name }) => {\n const sdk =\n softwareDevelopmentKitLookup[`${name}${LOOKUP_SPLIT_KEY}${codePackage.type}`];\n if (!sdk) {\n throw new Error(`Failed to find SDK with name: \"${name}\"`);\n }\n return sdk.id;\n }),\n ),\n }\n : {}),\n });\n },\n {\n concurrency,\n },\n );\n logger.info(colors.green(`Successfully synced ${newCodePackages.length} code packages!`));\n } catch (err) {\n encounteredError = true;\n logger.error(colors.red(`Failed to create code packages! - ${err.message}`));\n }\n\n // Update existing codePackages\n const existingCodePackageInputs = mapCodePackagesToExisting.filter(\n (x): x is [CodePackageInput, string] => !!x[1],\n );\n logger.info(colors.magenta(`Updating \"${existingCodePackageInputs.length}\" code packages...`));\n const chunks = chunk(existingCodePackageInputs, CHUNK_SIZE);\n\n await mapSeries(chunks, async (chunk) => {\n try {\n await updateCodePackages(\n client,\n chunk.map(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ([{ softwareDevelopmentKits, repositoryName, ...input }, id]) => ({\n ...input,\n ...(softwareDevelopmentKits\n ? {\n softwareDevelopmentKitIds: uniq(\n softwareDevelopmentKits.map(({ name }) => {\n const sdk =\n softwareDevelopmentKitLookup[`${name}${LOOKUP_SPLIT_KEY}${input.type}`];\n if (!sdk) {\n throw new Error(`Failed to find SDK with name: \"${name}\"`);\n }\n return sdk.id;\n }),\n ),\n }\n : {}),\n id,\n }),\n ),\n );\n logger.info(colors.green(`Successfully updated \"${chunk.length}\" code packages!`));\n } catch (err) {\n encounteredError = true;\n logger.error(colors.red(`Failed to update code packages! - ${err.message}`));\n }\n });\n\n logger.info(colors.green(`Synced \"${codePackages.length}\" code packages!`));\n return !encounteredError;\n}\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import{t as e}from"./logger-Bj782ZYD.mjs";import{a as t,d as n,f as r,l as i,n as a,r as o,s,t as c}from"./dataSilo-Dvi8-PkH.mjs";import{a as l,c as u,n as d,o as f,t as p}from"./dataSubject-CF784Ug0.mjs";import{EnricherType as m,RequestAction as h}from"@transcend-io/privacy-types";import{chunk as g,difference as _,flatten as v,keyBy as y,sortBy as b,uniq as x}from"lodash-es";import{apply as S}from"@transcend-io/type-utils";import C from"colors";import{makeGraphQLRequest as w}from"@transcend-io/sdk";import{map as T,mapSeries as E}from"@transcend-io/utils";import D from"cli-progress";async function O(t){let{internalSubjects:n}=await w(t,d,{logger:e});return n}async function k({"data-silos":t=[],"data-subjects":n=[],"processing-activities":r=[],enrichers:i=[]},a,o=!1){let s=x([...v(t.map(e=>e[`data-subjects`]||[])||[]),...v(r.map(({dataSubjectTypes:e})=>e??[])??[]),...v(i.map(e=>e[`data-subjects`]||[])||[]),...n.map(e=>e.type)]);if(s.length===0&&!o)return{};let c=await O(a),l=y(c,`type`),u=_(s,c.map(({type:e})=>e));return u.length>0&&(e.info(C.magenta(`Creating ${u.length} new data subjects...`)),await E(u,async t=>{e.info(C.magenta(`Creating data subject ${t}...`));let{createSubject:n}=await w(a,p,{variables:{type:t,skipPublish:!0},logger:e});e.info(C.green(`Created data subject ${t}!`)),l[t]=n.subject})),l}function A(e,t){return e.forEach(e=>{if(!t[e])throw Error(`Expected to find data subject definition: ${e}`)}),Object.values(t).filter(t=>!e.includes(t.type)).map(({id:e})=>e)}function j(e,t){return e.forEach(e=>{if(!t[e])throw Error(`Expected to find data subject definition: ${e}`)}),Object.values(t).filter(t=>!e.includes(t.type)).map(({type:e})=>e)}async function M(t,{titles:n,pageSize:r,ids:i=[],gql:o=a,integrationNames:s=[]}){e.info(C.magenta(`Fetching ${i.length===0?`all`:i.length} Data Silos...`));let c=[],l=0,u=!1;do{let{dataSilos:{nodes:a}}=await w(t,o,{variables:{filterBy:{ids:i.length>0?i:void 0,type:s.length>0?s:void 0,titles:n},first:r,offset:l},logger:e});c.push(...a),l+=r,u=a.length===r}while(u);return e.info(C.green(`Found a total of ${c.length} data silo${i.length>0?` matching IDs ${i.join(`,`)}`:``}s${s.length>0?` matching integrationNames ${s.join(`,`)}`:``}`)),c.sort((e,t)=>e.title.localeCompare(t.title))}async function N(t,r,{debug:a,includeGuessedCategories:o,pageSize:s}){let c=[],l=0,u=!1;do try{a&&e.log(C.magenta(`Pulling in subdatapoints for offset ${l}`));let{subDataPoints:{nodes:d}}=await w(t,o?n:i,{variables:{first:s,filterBy:{dataPoints:[r]},offset:l},logger:e});c.push(...d),l+=s,u=d.length===s,a&&e.log(C.green(`Pulled in subdatapoints for offset ${l} for dataPointId=${r}`))}catch(t){throw e.error(C.red(`An error fetching subdatapoints for offset ${l} for dataPointId=${r}`)),t}while(u);return b(c,`name`)}async function P(t,n,{debug:r,pageSize:i,skipSubDatapoints:a,includeGuessedCategories:o}){let c=[],l=0,u=!1;do{r&&e.info(C.magenta(`Fetching datapoints with offset: ${l}`));let{dataPoints:{nodes:d}}=await w(t,s,{variables:{first:i,filterBy:{dataSilos:[n]},offset:l},logger:e});r&&e.info(C.magenta(`Fetched ${d.length} datapoints at offset: ${l}`)),a||(await T(d,async n=>{try{r&&e.info(C.magenta(`Fetching subdatapoints for ${n.name} for datapoint offset ${l}`));let i=await N(t,n.id,{pageSize:1e3,debug:r,includeGuessedCategories:o});c.push({...n,subDataPoints:i.sort((e,t)=>e.name.localeCompare(t.name))}),r&&e.info(C.green(`Successfully fetched subdatapoints for ${n.name}`))}catch(t){throw e.error(C.red(`An error fetching subdatapoints for ${n.name} datapoint offset ${l}`)),t}},{concurrency:5}),r&&e.info(C.green(`Fetched all subdatapoints for page of datapoints at offset: ${l}`))),l+=i,u=d.length===i}while(u);return c.sort((e,t)=>e.name.localeCompare(t.name))}async function F(t,{ids:n,pageSize:r,titles:i,debug:a,skipDatapoints:s,skipSubDatapoints:c,includeGuessedCategories:l,integrationNames:u}){let d=[],f=await M(t,{titles:i,ids:n,integrationNames:u,pageSize:r,gql:o});return s||await E(f,async(n,i)=>{e.info(C.magenta(`[${i+1}/${f.length}] Fetching data silo - ${n.title}`));let o=await P(t,n.id,{debug:a,pageSize:r,skipSubDatapoints:c,includeGuessedCategories:l});a&&e.info(C.green(`[${i+1}/${f.length}] Successfully fetched datapoint for - ${n.title}`)),d.push([n,o])}),e.info(C.green(`Successfully fetched all ${f.length} data silo configurations`)),d}async function I(n,i,{pageSize:a,dataSubjectsByName:o,apiKeysByTitle:s}){let l=!1,u=new Date().getTime();e.info(C.magenta(`Syncing "${n.length}" data silos...`));let d=y(await M(i,{titles:n.map(({title:e})=>e),pageSize:a}),`title`),f=n.filter(({title:e})=>!d[e]);f.length>0&&(e.info(C.magenta(`Creating "${f.length}" data silos that did not exist...`)),await E(g(f,20),async t=>{let{createDataSilos:{dataSilos:n}}=await w(i,c,{variables:{input:t.map(e=>({name:e[`outer-type`]||e.integrationName,title:e.title,country:e.country,countrySubDivision:e.countrySubDivision}))},logger:e});n.forEach(e=>{d[e.title]=e})}),e.info(C.green(`Successfully created "${f.length}" data silos!`)));let p=g(n,20);await E(p,async(n,r)=>{e.info(C.magenta(`[Batch ${r+1}/${p.length}] Syncing "${n.length}" data silos`)),await w(i,t,{variables:{input:{dataSilos:n.map(e=>({id:d[e.title].id,country:e.country,countrySubDivision:e.countrySubDivision,url:e.url,headers:e.headers,description:e.description,identifiers:e[`identity-keys`],isLive:!e.disabled,ownerEmails:e.owners,teamNames:e.teams,dependedOnDataSiloTitles:e[`deletion-dependencies`]?void 0:[],apiKeyId:e[`api-key-title`]?s[e[`api-key-title`]].id:void 0,dataSubjectBlockListIds:e[`data-subjects`]?A(e[`data-subjects`],o):void 0,attributes:e.attributes,businessEntityTitles:e.businessEntityTitles,notifyEmailAddress:e[`email-settings`]?.[`notify-email-address`],promptAVendorEmailSendFrequency:e[`email-settings`]?.[`send-frequency`],promptAVendorEmailSendType:e[`email-settings`]?.[`send-type`],promptAVendorEmailIncludeIdentifiersAttachment:e[`email-settings`]?.[`include-identifiers-attachment`],promptAVendorEmailCompletionLinkType:e[`email-settings`]?.[`completion-link-type`],manualWorkRetryFrequency:e[`email-settings`]?.[`manual-work-retry-frequency`]}))}},logger:e}),e.info(C.green(`[Batch ${r+1}/${p.length}] Synced "${n.length}" data silos!`))});let m=new D.SingleBar({},D.Presets.shades_classic),h=n.filter(({datapoints:e=[]})=>e.length>0),_=n.map(({datapoints:e=[]})=>e.length).reduce((e,t)=>e+t,0);e.info(C.magenta(`Syncing "${_}" datapoints from "${h.length}" data silos...`)),m.start(_,0);let v=0;await T(h,async({datapoints:t,title:n})=>{t&&await E(t,async t=>{let a=t.fields?t.fields.map(({key:e,description:t,categories:n,purposes:r,attributes:i,...a})=>({name:e,description:t,categories:n?n.map(e=>({...e,name:e.name||`Other`})):void 0,purposes:r?r.map(e=>({...e,name:e.name||`Other`})):void 0,attributes:i,accessRequestVisibilityEnabled:a[`access-request-visibility-enabled`],erasureRequestRedactionEnabled:a[`erasure-request-redaction-enabled`]})):void 0,o={dataSiloId:d[n].id,path:t.path,name:t.key,title:t.title,description:t.description,...t.owners?{ownerEmails:t.owners}:{},...t.teams?{teamNames:t.teams}:{},...t[`data-collection-tag`]?{dataCollectionTag:t[`data-collection-tag`]}:{},querySuggestions:t[`privacy-action-queries`]?Object.entries(t[`privacy-action-queries`]).map(([e,t])=>({requestType:e,suggestedQuery:t})):void 0,enabledActions:t[`privacy-actions`]||[],subDataPoints:a},s=(o.subDataPoints||[]).map(({name:e})=>e),c=s.filter((e,t)=>s.indexOf(e)!==t);if(c.length>0)e.info(C.red(`\nCannot update datapoint "${t.key}" as it has duplicate sub-datapoints with the same name: \n${c.join(`
2
+ `)}`)),l=!0;else try{await w(i,r,{variables:o,logger:e})}catch(r){e.info(C.red(`\nFailed to update datapoint "${t.key}" for data silo "${n}"! - \n${r.message}`)),l=!0}v+=1,m.update(v)})},{concurrency:10}),m.stop();let b=new Date().getTime()-u;return e.info(C.green(`Synced "${n.length}" data silos and "${_}" datapoints in "${b/1e3}" seconds!`)),{success:!l,dataSiloTitleToId:S(d,({id:e})=>e)}}async function L(n,r){let i=!1;return e.info(C.magenta(`Syncing "${r.length}" data silo dependencies...`)),await E(g(r,20),async(r,a)=>{e.info(C.magenta(`[Batch ${a}/${r.length}] Updating "${r.length}" data silos...`));try{await w(n,t,{variables:{input:{dataSilos:r.map(([e,t])=>({id:e,dependedOnDataSiloTitles:t}))}},logger:e}),e.info(C.green(`[Batch ${a+1}/${r.length}] Synced "${r.length}" data silos!`))}catch(t){i=!0,e.info(C.red(`[Batch ${a+1}/${r.length}] Failed to update "${r.length}" silos! - ${t.message}`))}}),!i}async function R(t,n){let r=[],i=0,a=!1;do{let{enrichers:{nodes:o}}=await w(t,f,{variables:{first:20,offset:i,title:n},logger:e});r.push(...o),i+=20,a=o.length===20}while(a);return r.sort((e,t)=>e.title.localeCompare(t.title))}async function z(t,{enricher:n,identifierByName:r,dataSubjectsByName:i}){let a=(await R(t,n.title)).find(({title:e})=>e===n.title),o=n[`data-subjects`]?.map(e=>{let t=i[e];if(!t)throw Error(`Failed to find a data subject with name: ${e}`);return t.id}),s=n[`input-identifier`],c=n[`privacy-actions`]||Object.values(h);a?await w(t,u,{variables:{input:{id:a.id,title:n.title,url:n.url,headers:n.headers,testRegex:n.testRegex,lookerQueryTitle:n.lookerQueryTitle,expirationDuration:typeof n.expirationDuration==`number`?n.expirationDuration.toString():void 0,transitionRequestStatus:n.transitionRequestStatus,phoneNumbers:n.phoneNumbers,regionList:n.regionList,dataSubjectIds:o,description:n.description||``,inputIdentifier:s?r[s].id:void 0,identifiers:n[`output-identifiers`].map(e=>r[e].id),...a.type===m.Sombra?{}:{actions:c}}},logger:e}):s&&await w(t,l,{variables:{input:{title:n.title,url:n.url,type:n.type||m.Server,headers:n.headers,testRegex:n.testRegex,lookerQueryTitle:n.lookerQueryTitle,expirationDuration:typeof n.expirationDuration==`number`?n.expirationDuration.toString():void 0,transitionRequestStatus:n.transitionRequestStatus,phoneNumbers:n.phoneNumbers,dataSubjectIds:o,regionList:n.regionList,description:n.description||``,inputIdentifier:r[s].id,identifiers:n[`output-identifiers`].map(e=>r[e].id),actions:c}},logger:e})}export{N as a,I as c,k as d,O as f,M as i,j as l,z as n,F as o,P as r,L as s,R as t,A as u};
3
+ //# sourceMappingURL=syncEnrichers-C9HcWCrs.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncEnrichers-C9HcWCrs.mjs","names":[],"sources":["../src/lib/graphql/fetchDataSubjects.ts","../src/lib/graphql/syncDataSilos.ts","../src/lib/graphql/syncEnrichers.ts"],"sourcesContent":["import { RequestActionObjectResolver } from '@transcend-io/privacy-types';\nimport { makeGraphQLRequest } from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport { keyBy, flatten, uniq, difference } from 'lodash-es';\n\nimport { TranscendInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\nimport { CREATE_DATA_SUBJECT, DATA_SUBJECTS } from './gqls/index.js';\n\nexport interface DataSubject {\n /** ID of data subject */\n id: string;\n /** Type of data subject */\n type: string;\n /** Whether active */\n active: boolean;\n /** Title of data subject */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** Whether silent mode is enabled by default */\n adminDashboardDefaultSilentMode: boolean;\n /** Enabled actions */\n actions: {\n /** Type of action */\n type: RequestActionObjectResolver;\n }[];\n}\n\n/**\n * Fetch all data subjects in an organization\n *\n * @param client - GraphQL client\n * @returns List of data subject configurations\n */\nexport async function fetchAllDataSubjects(client: GraphQLClient): Promise<DataSubject[]> {\n // Fetch all data subjects in the organization\n const { internalSubjects } = await makeGraphQLRequest<{\n /** Query response */\n internalSubjects: DataSubject[];\n }>(client, DATA_SUBJECTS, { logger });\n return internalSubjects;\n}\n\n/**\n * Fetch all of the data subjects in the organization\n *\n * @param input - Input to fetch\n * @param client - GraphQL client\n * @param fetchAll - When true, always fetch all subjects\n * @returns The list of data subjects\n */\nexport async function ensureAllDataSubjectsExist(\n {\n 'data-silos': dataSilos = [],\n 'data-subjects': dataSubjects = [],\n 'processing-activities': processingActivities = [],\n enrichers = [],\n }: TranscendInput,\n client: GraphQLClient,\n fetchAll = false,\n): Promise<{ [type in string]: DataSubject }> {\n // Only need to fetch data subjects if specified in config\n const expectedDataSubjects = uniq([\n ...flatten(dataSilos.map((silo) => silo['data-subjects'] || []) || []),\n ...flatten(processingActivities.map(({ dataSubjectTypes }) => dataSubjectTypes ?? []) ?? []),\n ...flatten(enrichers.map((enricher) => enricher['data-subjects'] || []) || []),\n ...dataSubjects.map((subject) => subject.type),\n ]);\n if (expectedDataSubjects.length === 0 && !fetchAll) {\n return {};\n }\n\n // Fetch all data subjects in the organization\n const internalSubjects = await fetchAllDataSubjects(client);\n const dataSubjectByName = keyBy(internalSubjects, 'type');\n\n // Determine expected set of data subjects to create\n const missingDataSubjects = difference(\n expectedDataSubjects,\n internalSubjects.map(({ type }) => type),\n );\n\n // If there are missing data subjects, create new ones\n if (missingDataSubjects.length > 0) {\n logger.info(colors.magenta(`Creating ${missingDataSubjects.length} new data subjects...`));\n await mapSeries(missingDataSubjects, async (dataSubject) => {\n logger.info(colors.magenta(`Creating data subject ${dataSubject}...`));\n const { createSubject } = await makeGraphQLRequest<{\n /** Create Subject Response */\n createSubject: {\n /** Created Data Subject */\n subject: DataSubject;\n };\n }>(client, CREATE_DATA_SUBJECT, {\n variables: { type: dataSubject, skipPublish: true },\n logger,\n });\n logger.info(colors.green(`Created data subject ${dataSubject}!`));\n\n dataSubjectByName[dataSubject] = createSubject.subject;\n });\n }\n\n return dataSubjectByName;\n}\n\n/**\n * Convert a list of data subject types into the block list of IDs to assign to the data silo\n *\n * @param dataSubjectTypes - The list of data subject types that the data silo should be for\n * @param allDataSubjects - All data subjects in the organization\n * @returns The block list of data subject ids to not process against this data silo\n */\nexport function convertToDataSubjectBlockList(\n dataSubjectTypes: string[],\n allDataSubjects: { [type in string]: DataSubject },\n): string[] {\n dataSubjectTypes.forEach((type) => {\n if (!allDataSubjects[type]) {\n throw new Error(`Expected to find data subject definition: ${type}`);\n }\n });\n\n return Object.values(allDataSubjects)\n .filter((silo) => !dataSubjectTypes.includes(silo.type))\n .map(({ id }) => id);\n}\n\n/**\n * Convert a list of data subject types into the allow list of types\n *\n * @param dataSubjectTypes - The list of data subject types that the data silo should be for\n * @param allDataSubjects - All data subjects in the organization\n * @returns The allow list of data subjects for that silo\n */\nexport function convertToDataSubjectAllowlist(\n dataSubjectTypes: string[],\n allDataSubjects: { [type in string]: DataSubject },\n): string[] {\n dataSubjectTypes.forEach((type) => {\n if (!allDataSubjects[type]) {\n throw new Error(`Expected to find data subject definition: ${type}`);\n }\n });\n\n return Object.values(allDataSubjects)\n .filter((silo) => !dataSubjectTypes.includes(silo.type))\n .map(({ type }) => type);\n}\n","import {\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n PromptAVendorEmailCompletionLinkType,\n PromptAVendorEmailSendType,\n ConfidenceLabel,\n RequestActionObjectResolver,\n SubDataPointDataSubCategoryGuessStatus,\n} from '@transcend-io/privacy-types';\nimport { makeGraphQLRequest, ApiKey } from '@transcend-io/sdk';\nimport { apply } from '@transcend-io/type-utils';\nimport { mapSeries, map } from '@transcend-io/utils';\n/* eslint-disable max-lines */\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport { sortBy, chunk, keyBy } from 'lodash-es';\n\nimport { DataCategoryInput, DataSiloInput, ProcessingPurposeInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\nimport { convertToDataSubjectBlockList, DataSubject } from './fetchDataSubjects.js';\nimport {\n DATA_SILOS,\n CREATE_DATA_SILOS,\n UPDATE_OR_CREATE_DATA_POINT,\n DATA_POINTS,\n SUB_DATA_POINTS,\n UPDATE_DATA_SILOS,\n DATA_SILOS_ENRICHED,\n SUB_DATA_POINTS_WITH_GUESSES,\n} from './gqls/index.js';\n\nexport interface DataSiloAttributeValue {\n /** Key associated to value */\n attributeKey: {\n /** Name of key */\n name: string;\n };\n /** Name of value */\n name: string;\n}\nexport interface DataSilo {\n /** ID of dataSilo */\n id: string;\n /** Title of dataSilo */\n title: string;\n /** Type of silo */\n type: string;\n /** The link to the data silo */\n link: string;\n /** Attribute labels */\n attributeValues: DataSiloAttributeValue[];\n /** description */\n description: string;\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n}\n\nconst BATCH_SILOS_LIMIT = 20;\n\n/**\n * Fetch all dataSilos in the organization\n *\n * @param client - GraphQL client\n * @param title - Filter by title\n * @returns All dataSilos in the organization\n */\nexport async function fetchAllDataSilos<TDataSilo extends DataSilo>(\n client: GraphQLClient,\n {\n titles,\n pageSize,\n ids = [],\n gql = DATA_SILOS,\n integrationNames = [],\n }: {\n /** Page size to fetch datapoints in */\n pageSize: number;\n /** Title */\n titles?: string[];\n /** IDs */\n ids?: string[];\n /** Set of integration names to fetch */\n integrationNames?: string[];\n /** GQL query for data silos */\n gql?: string;\n },\n): Promise<TDataSilo[]> {\n logger.info(colors.magenta(`Fetching ${ids.length === 0 ? 'all' : ids.length} Data Silos...`));\n\n const dataSilos: TDataSilo[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n dataSilos: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n dataSilos: {\n /** List of matches */\n nodes: TDataSilo[];\n };\n }>(client, gql, {\n variables: {\n filterBy: {\n ids: ids.length > 0 ? ids : undefined,\n type: integrationNames.length > 0 ? integrationNames : undefined,\n titles,\n },\n first: pageSize,\n offset,\n },\n logger,\n });\n dataSilos.push(...nodes);\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n } while (shouldContinue);\n logger.info(\n colors.green(\n `Found a total of ${dataSilos.length} data silo${\n ids.length > 0 ? ` matching IDs ${ids.join(',')}` : ''\n }s${\n integrationNames.length > 0\n ? ` matching integrationNames ${integrationNames.join(',')}`\n : ''\n }`,\n ),\n );\n\n return dataSilos.sort((a, b) => a.title.localeCompare(b.title));\n}\n\nexport interface SubDataPoint {\n /** Name (or key) of the subdatapoint */\n name: string;\n /** The description */\n description?: string;\n /** Personal data category */\n categories: DataCategoryInput[];\n /** The processing purpose for this sub datapoint */\n purposes: ProcessingPurposeInput[];\n /**\n * When true, this subdatapoint should be revealed in a data access request.\n * When false, this field should be redacted\n */\n accessRequestVisibilityEnabled: boolean;\n /**\n * When true, this subdatapoint should be redacted during an erasure request.\n * There normally is a choice of enabling hard deletion or redaction at the\n * datapoint level, but if redaction is enabled, this column can be used\n * to define which fields should be redacted.\n */\n erasureRequestRedactionEnabled: boolean;\n /** Attribute attached to subdatapoint */\n attributeValues: DataSiloAttributeValue[];\n /** Data category guesses that are output by the classifier */\n pendingCategoryGuesses?: {\n /** Data category being guessed */\n category: DataCategoryInput;\n /** Status of guess */\n status: SubDataPointDataSubCategoryGuessStatus;\n /** Confidence level of guess */\n confidence: number;\n /** Confidence label */\n confidenceLabel: ConfidenceLabel;\n /** classifier version that produced the guess */\n classifierVersion: number;\n }[];\n}\n\ninterface DataPoint {\n /** ID of dataPoint */\n id: string;\n /** Title of dataPoint */\n title: {\n /** Default message */\n defaultMessage: string;\n };\n /** The path to this data point */\n path: string[];\n /** Description */\n description: {\n /** Default message */\n defaultMessage: string;\n };\n /** Name */\n name: string;\n /** Global actions */\n actionSettings: {\n /** Action type */\n type: RequestActionObjectResolver;\n /** Is enabled */\n active: boolean;\n }[];\n /** Data collection tag for privacy request download zip labeling */\n dataCollection?: {\n /** Title of data collection */\n title: {\n /** Default message (since message can be translated) */\n defaultMessage: string;\n };\n };\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n /** Owners of the datapoint */\n owners: {\n /** Email address of the owner */\n email: string;\n }[];\n /** Teams that own the datapoint */\n teams: {\n /** Name of the team */\n name: string;\n }[];\n /** Database integration queries */\n dbIntegrationQueries: {\n /** Approved query */\n query: string | null;\n /** Suggested query */\n suggestedQuery: string | null;\n /** Request action */\n requestType: RequestActionObjectResolver;\n }[];\n}\n\ninterface DataPointWithSubDataPoint extends DataPoint {\n /** The associated subdatapoints */\n subDataPoints: SubDataPoint[];\n}\n\n/**\n * Helper to fetch all subdatapoints for a given datapoint\n *\n * @param client - The GraphQL client\n * @param dataPointId - The datapoint ID\n * @param options - Options\n * @returns The list of subdatapoints\n */\nexport async function fetchAllSubDataPoints(\n client: GraphQLClient,\n dataPointId: string,\n {\n debug,\n includeGuessedCategories,\n pageSize,\n }: {\n /** Debug logs */\n debug: boolean;\n /** Page size */\n pageSize: number;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n },\n): Promise<SubDataPoint[]> {\n const subDataPoints: SubDataPoint[] = [];\n\n let offset = 0;\n\n let shouldContinue = false;\n do {\n try {\n if (debug) {\n logger.log(colors.magenta(`Pulling in subdatapoints for offset ${offset}`));\n }\n const {\n subDataPoints: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n subDataPoints: {\n /** List of matches */\n nodes: SubDataPoint[];\n };\n }>(client, includeGuessedCategories ? SUB_DATA_POINTS_WITH_GUESSES : SUB_DATA_POINTS, {\n variables: {\n first: pageSize,\n filterBy: {\n dataPoints: [dataPointId],\n },\n offset,\n },\n logger,\n });\n\n subDataPoints.push(...nodes);\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n\n if (debug) {\n logger.log(\n colors.green(\n `Pulled in subdatapoints for offset ${offset} for dataPointId=${dataPointId}`,\n ),\n );\n }\n } catch (err) {\n logger.error(\n colors.red(\n `An error fetching subdatapoints for offset ${offset} for dataPointId=${dataPointId}`,\n ),\n );\n throw err;\n }\n } while (shouldContinue);\n return sortBy(subDataPoints, 'name');\n}\n\n/**\n * Fetch all datapoints for a data silo\n *\n * @param client - GraphQL client\n * @param dataSiloId - Data silo ID\n * @param options - Options\n * @returns List of datapoints\n */\nexport async function fetchAllDataPoints(\n client: GraphQLClient,\n dataSiloId: string,\n {\n debug,\n pageSize,\n skipSubDatapoints,\n includeGuessedCategories,\n }: {\n /** Debug logs */\n debug: boolean;\n /** Page size */\n pageSize: number;\n /** Skip fetching of subdatapoints */\n skipSubDatapoints?: boolean;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n },\n): Promise<DataPointWithSubDataPoint[]> {\n const dataPoints: DataPointWithSubDataPoint[] = [];\n\n // TODO: https://transcend.height.app/T-40481 - add cursor pagination\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n if (debug) {\n logger.info(colors.magenta(`Fetching datapoints with offset: ${offset}`));\n }\n\n const {\n dataPoints: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n dataPoints: {\n /** List of matches */\n nodes: DataPoint[];\n };\n }>(client, DATA_POINTS, {\n variables: {\n first: pageSize,\n filterBy: {\n dataSilos: [dataSiloId],\n },\n offset,\n },\n logger,\n });\n\n if (debug) {\n logger.info(colors.magenta(`Fetched ${nodes.length} datapoints at offset: ${offset}`));\n }\n\n if (!skipSubDatapoints) {\n await map(\n nodes,\n /* eslint-disable no-loop-func */\n async (node) => {\n try {\n if (debug) {\n logger.info(\n colors.magenta(\n `Fetching subdatapoints for ${node.name} for datapoint offset ${offset}`,\n ),\n );\n }\n\n const subDataPoints = await fetchAllSubDataPoints(client, node.id, {\n pageSize: 1000, // max page size\n debug,\n includeGuessedCategories,\n });\n dataPoints.push({\n ...node,\n subDataPoints: subDataPoints.sort((a, b) => a.name.localeCompare(b.name)),\n });\n\n if (debug) {\n logger.info(colors.green(`Successfully fetched subdatapoints for ${node.name}`));\n }\n } catch (err) {\n logger.error(\n colors.red(\n `An error fetching subdatapoints for ${node.name} datapoint offset ${offset}`,\n ),\n );\n throw err;\n }\n },\n /* eslint-enable no-loop-func */\n {\n concurrency: 5,\n },\n );\n\n if (debug) {\n logger.info(\n colors.green(`Fetched all subdatapoints for page of datapoints at offset: ${offset}`),\n );\n }\n }\n\n offset += pageSize;\n shouldContinue = nodes.length === pageSize;\n } while (shouldContinue);\n return dataPoints.sort((a, b) => a.name.localeCompare(b.name));\n}\n\nexport interface DataSiloEnriched {\n /** ID of dataSilo */\n id: string;\n /** Title of dataSilo */\n title: string;\n /** Type of silo */\n type: string;\n /** Link to silo */\n link: string;\n /** Outer type of silo */\n outerType: string;\n /** Description of data silo */\n description: string;\n /** Webhook URL */\n url?: string;\n /** Email address of user to notify for prompt a person use case */\n notifyEmailAddress?: string;\n /** Associated API keys */\n apiKeys: {\n /** Title */\n title: string;\n }[];\n /** Data subject block list */\n subjectBlocklist: {\n /** Type of data subject */\n type: string;\n }[];\n /** Identifiers */\n identifiers: {\n /** Name of identifier */\n name: string;\n /** True if identifier is wired */\n isConnected: boolean;\n }[];\n /** Dependent data silos */\n dependentDataSilos: {\n /** Title of silo */\n title: string;\n }[];\n /** Silo owners */\n owners: {\n /** Email owners */\n email: string;\n }[];\n /** The teams assigned to this data silo */\n teams: {\n /** Name of the team assigned to this data silo */\n name: string;\n }[];\n /** Metadata for this data silo */\n catalog: {\n /** Whether the data silo supports automated vendor coordination */\n hasAvcFunctionality: boolean;\n };\n /** Silo is live */\n isLive: boolean;\n /** Hosting country of data silo */\n country?: IsoCountryCode;\n /** Hosting subdivision data silo */\n countrySubDivision?: IsoCountrySubdivisionCode;\n /**\n * The frequency with which we should be sending emails for this data silo, in milliseconds.\n */\n promptAVendorEmailSendFrequency: number;\n /**\n * The type of emails to send for this data silo, i.e. send an email for each DSR, across all open DSRs,\n * or per profile in a DSR.\n */\n promptAVendorEmailSendType: PromptAVendorEmailSendType;\n /**\n * Indicates whether prompt-a-vendor emails should include a list of identifiers\n * in addition to a link to the bulk processing UI.\n */\n promptAVendorEmailIncludeIdentifiersAttachment: boolean;\n /**\n * Indicates what kind of link to generate as part of the emails sent out for this Prompt-a-Vendor silo.\n */\n promptAVendorEmailCompletionLinkType: PromptAVendorEmailCompletionLinkType;\n /**\n * The frequency with which we should retry sending emails for this data silo, in milliseconds.\n * Needs to be a string because the number can be larger than the MAX_INT\n */\n manualWorkRetryFrequency: string;\n /** Attribute values tagged to data silo */\n attributeValues: DataSiloAttributeValue[];\n /**\n * The data silos that discovered this particular data silo\n */\n discoveredBy: {\n /** Title of data silo */\n title: string;\n }[];\n /**\n * The business entities assigned directly to this data silo\n */\n businessEntities: {\n /** Title of business entity */\n title: string;\n }[];\n}\n\n/**\n * Fetch all dataSilos with additional metadata\n *\n * @param client - GraphQL client\n * @param options - Filter options\n * @returns All dataSilos in the organization\n */\nexport async function fetchEnrichedDataSilos(\n client: GraphQLClient,\n {\n ids,\n pageSize,\n titles,\n debug,\n skipDatapoints,\n skipSubDatapoints,\n includeGuessedCategories,\n integrationNames,\n }: {\n /** Page size */\n pageSize: number;\n /** Filter by IDs */\n ids?: string[];\n /** Enable debug logs */\n debug: boolean;\n /** Filter by title */\n titles?: string[];\n /** Integration names */\n integrationNames?: string[];\n /** Skip fetching of datapoints */\n skipDatapoints?: boolean;\n /** Skip fetching of subdatapoints */\n skipSubDatapoints?: boolean;\n /** When true, metadata around guessed data categories should be included */\n includeGuessedCategories?: boolean;\n },\n): Promise<[DataSiloEnriched, DataPointWithSubDataPoint[]][]> {\n const dataSilos: [DataSiloEnriched, DataPointWithSubDataPoint[]][] = [];\n\n // Grab silos\n const silos = await fetchAllDataSilos<DataSiloEnriched>(client, {\n titles,\n ids,\n integrationNames,\n pageSize,\n gql: DATA_SILOS_ENRICHED,\n });\n\n // Graph datapoints for each silo\n if (!skipDatapoints) {\n await mapSeries(silos, async (silo, index) => {\n logger.info(\n colors.magenta(`[${index + 1}/${silos.length}] Fetching data silo - ${silo.title}`),\n );\n\n const dataPoints = await fetchAllDataPoints(client, silo.id, {\n debug,\n pageSize,\n skipSubDatapoints,\n includeGuessedCategories,\n });\n\n if (debug) {\n logger.info(\n colors.green(\n `[${index + 1}/${silos.length}] Successfully fetched datapoint for - ${silo.title}`,\n ),\n );\n }\n\n dataSilos.push([silo, dataPoints]);\n });\n }\n\n logger.info(colors.green(`Successfully fetched all ${silos.length} data silo configurations`));\n\n return dataSilos;\n}\n\n/**\n * Sync a data silo configuration\n *\n * @param dataSilos - Data silos to sync\n * @param client - GraphQL client\n * @param options - Options\n * @returns Data silo info\n */\nexport async function syncDataSilos(\n dataSilos: DataSiloInput[],\n client: GraphQLClient,\n {\n pageSize,\n dataSubjectsByName,\n apiKeysByTitle,\n }: {\n /** Page size */\n pageSize: number;\n /** The data subjects in the organization */\n dataSubjectsByName: { [type in string]: DataSubject };\n /** API key title to API key */\n apiKeysByTitle: { [title in string]: ApiKey };\n },\n): Promise<{\n /** Whether successfully updated */\n success: boolean;\n /** A mapping between data silo title to data silo ID */\n dataSiloTitleToId: { [k in string]: string };\n}> {\n let encounteredError = false;\n\n // Time duration\n const t0 = new Date().getTime();\n logger.info(colors.magenta(`Syncing \"${dataSilos.length}\" data silos...`));\n\n // Determine the set of data silos that already exist\n const existingDataSilos = await fetchAllDataSilos(client, {\n titles: dataSilos.map(({ title }) => title),\n pageSize,\n });\n\n // Create a mapping of title -> existing silo, if it exists\n const existingDataSiloByTitle = keyBy<Pick<DataSilo, 'id' | 'title'>>(existingDataSilos, 'title');\n\n // Create new silos that do not exist\n const newDataSiloInputs = dataSilos.filter(({ title }) => !existingDataSiloByTitle[title]);\n if (newDataSiloInputs.length > 0) {\n logger.info(\n colors.magenta(`Creating \"${newDataSiloInputs.length}\" data silos that did not exist...`),\n );\n\n // Batch the creation\n const chunked = chunk(newDataSiloInputs, BATCH_SILOS_LIMIT);\n await mapSeries(chunked, async (dependencyUpdateChunk) => {\n const {\n createDataSilos: { dataSilos },\n } = await makeGraphQLRequest<{\n /** Mutation result */\n createDataSilos: {\n /** New data silos */\n dataSilos: Pick<DataSilo, 'id' | 'title'>[];\n };\n }>(client, CREATE_DATA_SILOS, {\n variables: {\n input: dependencyUpdateChunk.map((input) => ({\n name: input['outer-type'] || input.integrationName,\n title: input.title,\n country: input.country,\n countrySubDivision: input.countrySubDivision,\n })),\n },\n logger,\n });\n\n // save mapping of title and id\n dataSilos.forEach((silo) => {\n existingDataSiloByTitle[silo.title] = silo;\n });\n });\n\n logger.info(colors.green(`Successfully created \"${newDataSiloInputs.length}\" data silos!`));\n }\n\n // Batch the updates\n const chunkedUpdates = chunk(dataSilos, BATCH_SILOS_LIMIT);\n await mapSeries(chunkedUpdates, async (dataSiloUpdateChunk, ind) => {\n logger.info(\n colors.magenta(\n `[Batch ${ind + 1}/${chunkedUpdates.length}] Syncing \"${\n dataSiloUpdateChunk.length\n }\" data silos`,\n ),\n );\n await makeGraphQLRequest<{\n /** Mutation result */\n updateDataSilos: {\n /** New data silos */\n dataSilos: Pick<DataSilo, 'id' | 'title'>[];\n };\n }>(client, UPDATE_DATA_SILOS, {\n variables: {\n input: {\n dataSilos: dataSiloUpdateChunk.map((input) => ({\n id: existingDataSiloByTitle[input.title].id,\n country: input.country,\n countrySubDivision: input.countrySubDivision,\n url: input.url,\n headers: input.headers,\n description: input.description,\n identifiers: input['identity-keys'],\n isLive: !input.disabled,\n ownerEmails: input.owners,\n teamNames: input.teams,\n // clear out if not specified, otherwise the update needs to be applied after\n // all data silos are created\n dependedOnDataSiloTitles: input['deletion-dependencies'] ? undefined : [],\n apiKeyId: input['api-key-title']\n ? apiKeysByTitle[input['api-key-title']].id\n : undefined,\n dataSubjectBlockListIds: input['data-subjects']\n ? convertToDataSubjectBlockList(input['data-subjects'], dataSubjectsByName)\n : undefined,\n attributes: input.attributes,\n businessEntityTitles: input.businessEntityTitles,\n // AVC settings\n notifyEmailAddress: input['email-settings']?.['notify-email-address'],\n promptAVendorEmailSendFrequency: input['email-settings']?.['send-frequency'],\n promptAVendorEmailSendType: input['email-settings']?.['send-type'],\n promptAVendorEmailIncludeIdentifiersAttachment:\n input['email-settings']?.['include-identifiers-attachment'],\n promptAVendorEmailCompletionLinkType: input['email-settings']?.['completion-link-type'],\n manualWorkRetryFrequency: input['email-settings']?.['manual-work-retry-frequency'],\n })),\n },\n },\n logger,\n });\n logger.info(\n colors.green(\n `[Batch ${ind + 1}/${chunkedUpdates.length}] Synced \"${\n dataSiloUpdateChunk.length\n }\" data silos!`,\n ),\n );\n });\n\n // Sync datapoints\n\n // create a new progress bar instance and use shades_classic theme\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n const dataSilosWithDataPoints = dataSilos.filter(({ datapoints = [] }) => datapoints.length > 0);\n const totalDataPoints = dataSilos\n .map(({ datapoints = [] }) => datapoints.length)\n .reduce((acc, count) => acc + count, 0);\n logger.info(\n colors.magenta(\n `Syncing \"${totalDataPoints}\" datapoints from \"${dataSilosWithDataPoints.length}\" data silos...`,\n ),\n );\n progressBar.start(totalDataPoints, 0);\n let total = 0;\n\n await map(\n dataSilosWithDataPoints,\n async ({ datapoints, title }) => {\n if (datapoints) {\n await mapSeries(datapoints, async (datapoint) => {\n const fields = datapoint.fields\n ? datapoint.fields.map(\n ({ key, description, categories, purposes, attributes, ...rest }) =>\n // TODO: Support setting title separately from the 'key/name'\n ({\n name: key,\n description,\n categories: !categories\n ? undefined\n : categories.map((category) => ({\n ...category,\n name: category.name || 'Other',\n })),\n purposes: !purposes\n ? undefined\n : purposes.map((purpose) => ({\n ...purpose,\n name: purpose.name || 'Other',\n })),\n attributes,\n accessRequestVisibilityEnabled: rest['access-request-visibility-enabled'],\n erasureRequestRedactionEnabled: rest['erasure-request-redaction-enabled'],\n }),\n )\n : undefined;\n\n const payload = {\n dataSiloId: existingDataSiloByTitle[title].id,\n path: datapoint.path,\n name: datapoint.key,\n title: datapoint.title,\n description: datapoint.description,\n ...(datapoint.owners\n ? {\n ownerEmails: datapoint.owners,\n }\n : {}),\n ...(datapoint.teams\n ? {\n teamNames: datapoint.teams,\n }\n : {}),\n ...(datapoint['data-collection-tag']\n ? { dataCollectionTag: datapoint['data-collection-tag'] }\n : {}),\n querySuggestions: !datapoint['privacy-action-queries']\n ? undefined\n : Object.entries(datapoint['privacy-action-queries']).map(([key, value]) => ({\n requestType: key,\n suggestedQuery: value,\n })),\n enabledActions: datapoint['privacy-actions'] || [], // clear out when not specified\n subDataPoints: fields,\n };\n\n // Ensure no duplicate sub-datapoints are provided\n const subDataPointsToUpdate = (payload.subDataPoints || []).map(({ name }) => name);\n const duplicateDataPoints = subDataPointsToUpdate.filter(\n (name, index) => subDataPointsToUpdate.indexOf(name) !== index,\n );\n if (duplicateDataPoints.length > 0) {\n logger.info(\n colors.red(\n `\\nCannot update datapoint \"${\n datapoint.key\n }\" as it has duplicate sub-datapoints with the same name: \\n${duplicateDataPoints.join(\n '\\n',\n )}`,\n ),\n );\n encounteredError = true;\n } else {\n try {\n await makeGraphQLRequest(client, UPDATE_OR_CREATE_DATA_POINT, {\n variables: payload,\n logger,\n });\n } catch (err) {\n logger.info(\n colors.red(\n `\\nFailed to update datapoint \"${datapoint.key}\" for data silo \"${title}\"! - \\n${err.message}`,\n ),\n );\n encounteredError = true;\n }\n }\n total += 1;\n progressBar.update(total);\n });\n }\n },\n {\n concurrency: 10,\n },\n );\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Synced \"${dataSilos.length}\" data silos and \"${totalDataPoints}\" datapoints in \"${\n totalTime / 1000\n }\" seconds!`,\n ),\n );\n return {\n success: !encounteredError,\n dataSiloTitleToId: apply(existingDataSiloByTitle, ({ id }) => id),\n };\n}\n\n/**\n * Sync data silo dependencies\n *\n * @param client - GraphQL client\n * @param dependencyUpdates - Mapping from [data silo ID, dependency titles]\n * @returns True upon success\n */\nexport async function syncDataSiloDependencies(\n client: GraphQLClient,\n dependencyUpdates: [string, string[]][],\n): Promise<boolean> {\n let encounteredError = false;\n logger.info(colors.magenta(`Syncing \"${dependencyUpdates.length}\" data silo dependencies...`));\n\n // Batch the updates\n const chunkedUpdates = chunk(dependencyUpdates, BATCH_SILOS_LIMIT);\n await mapSeries(chunkedUpdates, async (dependencyUpdateChunk, ind) => {\n logger.info(\n colors.magenta(\n `[Batch ${ind}/${dependencyUpdateChunk.length}] Updating \"${dependencyUpdateChunk.length}\" data silos...`,\n ),\n );\n try {\n await makeGraphQLRequest<{\n /** Mutation result */\n updateDataSilos: {\n /** New data silos */\n dataSilos: Pick<DataSilo, 'id' | 'title'>[];\n };\n }>(client, UPDATE_DATA_SILOS, {\n variables: {\n input: {\n dataSilos: dependencyUpdateChunk.map(([id, dependedOnDataSiloTitles]) => ({\n id,\n dependedOnDataSiloTitles,\n })),\n },\n },\n logger,\n });\n logger.info(\n colors.green(\n `[Batch ${ind + 1}/${dependencyUpdateChunk.length}] ` +\n `Synced \"${dependencyUpdateChunk.length}\" data silos!`,\n ),\n );\n } catch (err) {\n encounteredError = true;\n logger.info(\n colors.red(\n `[Batch ${ind + 1}/${dependencyUpdateChunk.length}] ` +\n `Failed to update \"${dependencyUpdateChunk.length}\" silos! - ${err.message}`,\n ),\n );\n }\n });\n return !encounteredError;\n}\n/* eslint-enable max-lines */\n","import {\n EnricherType,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n PreflightRequestStatus,\n RequestAction,\n} from '@transcend-io/privacy-types';\nimport { makeGraphQLRequest, type Identifier } from '@transcend-io/sdk';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { EnricherInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\nimport { DataSubject } from './fetchDataSubjects.js';\nimport { ENRICHERS, CREATE_ENRICHER, UPDATE_ENRICHER } from './gqls/index.js';\n\nexport interface Enricher {\n /** ID of enricher */\n id: string;\n /** Title of enricher */\n title: string;\n /** URL of enricher */\n url: string;\n /** Server silo */\n type: EnricherType;\n /** Input identifier */\n inputIdentifier: {\n /** Identifier name */\n name: string;\n };\n /** The selected actions */\n actions: RequestAction[];\n /** Output identifiers */\n identifiers: {\n /** Identifier name */\n name: string;\n }[];\n /** Data subjects that the preflight check is configured for */\n dataSubjects: {\n /** Data subject type */\n type: string;\n }[];\n /** The duration (in ms) that the enricher should take to execute. - BigInt */\n expirationDuration: string;\n /** Looker query title */\n lookerQueryTitle?: string;\n /** A regular expression that can be used to match on for cancelation */\n testRegex?: string;\n /** The status that the enricher should transfer to when condition is met. */\n transitionRequestStatus?: PreflightRequestStatus;\n /** The twilio phone number to send from */\n phoneNumbers: string[];\n /**\n * The list of regions that should trigger the enrichment condition\n */\n regionList: (IsoCountryCode | IsoCountrySubdivisionCode)[];\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all enrichers in the organization\n *\n * @param client - GraphQL client\n * @param title - Filter by title\n * @returns All enrichers in the organization\n */\nexport async function fetchAllEnrichers(\n client: GraphQLClient,\n title?: string,\n): Promise<Enricher[]> {\n const enrichers: Enricher[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n enrichers: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n enrichers: {\n /** List of matches */\n nodes: Enricher[];\n };\n }>(client, ENRICHERS, {\n variables: { first: PAGE_SIZE, offset, title },\n logger,\n });\n enrichers.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return enrichers.sort((a, b) => a.title.localeCompare(b.title));\n}\n\n/**\n * Sync an enricher configuration\n *\n * @param client - GraphQL client\n * @param options - Options\n */\nexport async function syncEnricher(\n client: GraphQLClient,\n {\n enricher,\n identifierByName,\n dataSubjectsByName,\n }: {\n /** The enricher input */\n enricher: EnricherInput;\n /** Index of identifiers in the organization */\n identifierByName: { [name in string]: Identifier };\n /** Lookup data subject by name */\n dataSubjectsByName: { [name in string]: DataSubject };\n },\n): Promise<void> {\n // Whether to continue looping\n const matches = await fetchAllEnrichers(client, enricher.title);\n const existingEnricher = matches.find(({ title }) => title === enricher.title);\n\n // Map to data subject Ids\n const dataSubjectIds = enricher['data-subjects']?.map((subject) => {\n const existing = dataSubjectsByName[subject];\n if (!existing) {\n throw new Error(`Failed to find a data subject with name: ${subject}`);\n }\n return existing.id;\n });\n\n // If enricher exists, update it, else create new\n const inputIdentifier = enricher['input-identifier'];\n const actionUpdates = enricher['privacy-actions'] || Object.values(RequestAction);\n if (existingEnricher) {\n await makeGraphQLRequest(client, UPDATE_ENRICHER, {\n variables: {\n input: {\n id: existingEnricher.id,\n title: enricher.title,\n url: enricher.url,\n headers: enricher.headers,\n testRegex: enricher.testRegex,\n lookerQueryTitle: enricher.lookerQueryTitle,\n expirationDuration:\n typeof enricher.expirationDuration === 'number'\n ? enricher.expirationDuration.toString()\n : undefined,\n transitionRequestStatus: enricher.transitionRequestStatus,\n phoneNumbers: enricher.phoneNumbers,\n regionList: enricher.regionList,\n dataSubjectIds,\n description: enricher.description || '',\n inputIdentifier: inputIdentifier ? identifierByName[inputIdentifier].id : undefined,\n identifiers: enricher['output-identifiers'].map((id) => identifierByName[id].id),\n ...(existingEnricher.type === EnricherType.Sombra ? {} : { actions: actionUpdates }),\n },\n },\n logger,\n });\n } else if (inputIdentifier) {\n await makeGraphQLRequest(client, CREATE_ENRICHER, {\n variables: {\n input: {\n title: enricher.title,\n url: enricher.url,\n type: enricher.type || EnricherType.Server,\n headers: enricher.headers,\n testRegex: enricher.testRegex,\n lookerQueryTitle: enricher.lookerQueryTitle,\n expirationDuration:\n typeof enricher.expirationDuration === 'number'\n ? enricher.expirationDuration.toString()\n : undefined,\n transitionRequestStatus: enricher.transitionRequestStatus,\n phoneNumbers: enricher.phoneNumbers,\n dataSubjectIds,\n regionList: enricher.regionList,\n description: enricher.description || '',\n inputIdentifier: identifierByName[inputIdentifier].id,\n identifiers: enricher['output-identifiers'].map((id) => identifierByName[id].id),\n actions: actionUpdates,\n },\n },\n logger,\n });\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{a as e}from"./constants-XOsAW1__.mjs";import{t}from"./logger-Bj782ZYD.mjs";import{ConsentBundleType as n}from"@transcend-io/privacy-types";import r from"colors";import{buildTranscendGraphQLClient as i,deployConsentManager as a,fetchConsentManagerId as o,updateConsentManagerToLatest as s}from"@transcend-io/sdk";import{mapSeries as c}from"@transcend-io/utils";async function l({auth:l,deploy:u=!1,transcendUrl:d=e,bundleTypes:f=Object.values(n)}){let p=i(d,l),m=await o(p,{logger:t});await c(f,async e=>{t.info(r.magenta(`Update Consent Manager bundle with ID "${m}" and type "${e}" to latest version...`)),await s(p,{id:m,bundleType:e},{logger:t}),t.info(r.green(`Updated Consent Manager bundle with ID "${m}" and type "${e}" to latest version!`))}),u&&await c(f,async e=>{t.info(r.magenta(`Deploying Consent Manager bundle with ID "${m}" and type "${e}"...`)),await a(p,{id:m,bundleType:e},{logger:t}),t.info(r.green(`Deployed Consent Manager bundle with ID "${m}" and type "${e}"!`))})}export{l as t};
2
+ //# sourceMappingURL=updateConsentManagerVersionToLatest-X1HAM_IX.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"updateConsentManagerVersionToLatest-X1HAM_IX.mjs","names":[],"sources":["../src/lib/consent-manager/updateConsentManagerVersionToLatest.ts"],"sourcesContent":["import { ConsentBundleType } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n deployConsentManager,\n fetchConsentManagerId,\n updateConsentManagerToLatest,\n} from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\n\n/**\n * Update the consent manager to latest version\n *\n * @param options - Options\n */\nexport async function updateConsentManagerVersionToLatest({\n auth,\n deploy = false,\n transcendUrl = DEFAULT_TRANSCEND_API,\n bundleTypes = Object.values(ConsentBundleType),\n}: {\n /** Transcend API key authentication */\n auth: string;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Deploy consent manager with this update */\n deploy?: boolean;\n /** The bundle types to update and deploy */\n bundleTypes?: ConsentBundleType[];\n}): Promise<void> {\n // Find all requests made before createdAt that are in a removing data state\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Grab Consent Manager ID\n const consentManagerId = await fetchConsentManagerId(client, { logger });\n\n // Update each bundle type to latest version\n await mapSeries(bundleTypes, async (bundleType) => {\n logger.info(\n colors.magenta(\n `Update Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\" to latest version...`,\n ),\n );\n await updateConsentManagerToLatest(\n client,\n {\n id: consentManagerId,\n bundleType,\n },\n { logger },\n );\n logger.info(\n colors.green(\n `Updated Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\" to latest version!`,\n ),\n );\n });\n\n // deploy Consent Managers\n if (deploy) {\n // Update each bundle type to latest version\n await mapSeries(bundleTypes, async (bundleType) => {\n logger.info(\n colors.magenta(\n `Deploying Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\"...`,\n ),\n );\n await deployConsentManager(\n client,\n {\n id: consentManagerId,\n bundleType,\n },\n { logger },\n );\n logger.info(\n colors.green(\n `Deployed Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\"!`,\n ),\n );\n });\n }\n}\n"],"mappings":"+WAkBA,eAAsB,EAAoC,CACxD,OACA,SAAS,GACT,eAAe,EACf,cAAc,OAAO,OAAO,EAAkB,EAU9B,CAEhB,IAAM,EAAS,EAA4B,EAAc,EAAK,CAGxD,EAAmB,MAAM,EAAsB,EAAQ,CAAE,SAAQ,CAAC,CAGxE,MAAM,EAAU,EAAa,KAAO,IAAe,CACjD,EAAO,KACL,EAAO,QACL,0CAA0C,EAAiB,cAAc,EAAW,wBACrF,CACF,CACD,MAAM,EACJ,EACA,CACE,GAAI,EACJ,aACD,CACD,CAAE,SAAQ,CACX,CACD,EAAO,KACL,EAAO,MACL,2CAA2C,EAAiB,cAAc,EAAW,sBACtF,CACF,EACD,CAGE,GAEF,MAAM,EAAU,EAAa,KAAO,IAAe,CACjD,EAAO,KACL,EAAO,QACL,6CAA6C,EAAiB,cAAc,EAAW,MACxF,CACF,CACD,MAAM,EACJ,EACA,CACE,GAAI,EACJ,aACD,CACD,CAAE,SAAQ,CACX,CACD,EAAO,KACL,EAAO,MACL,4CAA4C,EAAiB,cAAc,EAAW,IACvF,CACF,EACD"}
@@ -0,0 +1,2 @@
1
+ import{o as e}from"./constants-XOsAW1__.mjs";import{t}from"./logger-Bj782ZYD.mjs";import{decodeCodec as n}from"@transcend-io/type-utils";import r from"colors";import*as i from"io-ts";import{createTranscendConsentGotInstance as a}from"@transcend-io/sdk";import{map as o}from"@transcend-io/utils";import s from"cli-progress";import*as c from"crypto";import*as l from"jsonwebtoken";function u(e,t,n){let r=Buffer.from(n,`base64`),i=Buffer.from(t,`base64`),a=Buffer.from(`A65959A6`,`hex`),o=c.createCipheriv(`id-aes256-wrap-pad`,i,a),s={encryptedIdentifier:Buffer.concat([o.update(e),o.final()]).toString(`base64`)};return l.sign(s,r,{algorithm:`HS384`})}const d=/^[0-9][Y|N]([Y|N])[Y|N]$/,f=i.record(i.string,i.union([i.boolean,i.literal(`Auto`)]));async function p({base64EncryptionKey:i,base64SigningKey:c,preferences:l,partition:p,concurrency:m=100,transcendUrl:h=e}){let g=a(h),_=l.filter(e=>e.usp&&!d.test(e.usp));if(_.length>0)throw Error(`Received invalid usp strings: ${JSON.stringify(_,null,2)}`);let v=l.map((e,t)=>[e,t]).filter(([e])=>{if(!e.purposes)return!1;try{return n(f,e.purposes),!1}catch{return!0}});if(v.length>0)throw Error(`Received invalid purpose maps: ${JSON.stringify(v,null,2)}`);let y=l.filter(e=>!e.usp&&!e.purposes);if(y.length>0)throw Error(`Received invalid inputs, expected either purposes or usp to be defined: ${JSON.stringify(y,null,2)}`);t.info(r.magenta(`Uploading ${l.length} user preferences to partition ${p}`));let b=new Date().getTime(),x=new s.SingleBar({},s.Presets.shades_classic),S=0;x.start(l.length,0),await o(l,async({userId:e,confirmed:a=`true`,updated:o,prompted:s,purposes:l,...m})=>{let h=u(e,i,c),[,_]=m.usp&&d.exec(m.usp)||[],v={token:h,partition:p,consent:{confirmed:a===`true`,purposes:l?n(f,l):m.usp?{SaleOfInfo:_===`Y`}:{},...o?{updated:o===`true`}:{},...s?{prompted:s===`true`}:{},...m}};try{await g.post(`sync`,{json:v}).json()}catch(e){try{let n=JSON.parse(e?.response?.body||`{}`);n.error&&t.error(r.red(`Error: ${n.error}`))}catch{}throw Error(`Received an error from server: ${e?.response?.body||e?.message}`)}S+=1,x.update(S)},{concurrency:m}),x.stop();let C=new Date().getTime()-b;t.info(r.green(`Successfully uploaded ${l.length} user preferences to partition ${p} in "${C/1e3}" seconds!`))}export{u as i,d as n,p as r,f as t};
2
+ //# sourceMappingURL=uploadConsents-BP5XILuw.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadConsents-BP5XILuw.mjs","names":[],"sources":["../src/lib/consent-manager/createConsentToken.ts","../src/lib/consent-manager/uploadConsents.ts"],"sourcesContent":["import * as crypto from 'crypto';\n\nimport * as jwt from 'jsonwebtoken';\n\n/**\n * Function to create a consent manager token\n *\n * @see https://docs.transcend.io/docs/consent/reference/managed-consent-database\n * @param userId - User ID\n * @param base64EncryptionKey - Encryption key\n * @param base64SigningKey - Signing key\n * @returns Token\n */\nexport function createConsentToken(\n userId: string,\n base64EncryptionKey: string,\n base64SigningKey: string,\n): string {\n // Read on for where to find these keys\n const signingKey = Buffer.from(base64SigningKey, 'base64');\n const encryptionKey = Buffer.from(base64EncryptionKey, 'base64');\n\n // NIST's AES-KWP implementation { aes 48 } - see https://tools.ietf.org/html/rfc5649\n const encryptionAlgorithm = 'id-aes256-wrap-pad';\n // Initial Value for AES-KWP integrity check - see https://tools.ietf.org/html/rfc5649#section-3\n const iv = Buffer.from('A65959A6', 'hex');\n // Set up encryption algorithm\n const cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, iv);\n\n // Encrypt the userId and base64-encode the result\n const encryptedIdentifier = Buffer.concat([cipher.update(userId), cipher.final()]).toString(\n 'base64',\n );\n\n // Create the JWT content - jwt.sign will add a 'iat' (issued at) field to the payload\n // If you wanted to add something manually, consider\n // const issued: Date = new Date();\n // const isoDate = issued.toISOString();\n const jwtPayload = {\n encryptedIdentifier,\n };\n\n // Create a JSON web token and HMAC it with SHA-384\n const consentToken = jwt.sign(jwtPayload, signingKey, {\n algorithm: 'HS384',\n });\n\n return consentToken;\n}\n","import { ConsentPreferencesBody } from '@transcend-io/airgap.js-types';\nimport { createTranscendConsentGotInstance } from '@transcend-io/sdk';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport * as t from 'io-ts';\n\nimport { DEFAULT_TRANSCEND_CONSENT_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { createConsentToken } from './createConsentToken.js';\nimport type { ConsentPreferenceUpload } from './types.js';\n\nexport const USP_STRING_REGEX = /^[0-9][Y|N]([Y|N])[Y|N]$/;\n\nexport const PurposeMap = t.record(t.string, t.union([t.boolean, t.literal('Auto')]));\n\n/**\n * Upload a set of consent preferences\n *\n * @param options - Options\n */\nexport async function uploadConsents({\n base64EncryptionKey,\n base64SigningKey,\n preferences,\n partition,\n concurrency = 100,\n transcendUrl = DEFAULT_TRANSCEND_CONSENT_API,\n}: {\n /** base64 encryption key */\n base64EncryptionKey: string;\n /** base64 signing key */\n base64SigningKey: string;\n /** Partition key */\n partition: string;\n /** Sombra API key authentication */\n preferences: ConsentPreferenceUpload[];\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Concurrency limit for approving */\n concurrency?: number;\n}): Promise<void> {\n // Create connection to API\n const transcendConsentApi = createTranscendConsentGotInstance(transcendUrl);\n\n // Ensure usp strings are valid\n const invalidUspStrings = preferences.filter(\n (pref) => pref.usp && !USP_STRING_REGEX.test(pref.usp),\n );\n if (invalidUspStrings.length > 0) {\n throw new Error(`Received invalid usp strings: ${JSON.stringify(invalidUspStrings, null, 2)}`);\n }\n\n // Ensure purpose maps are valid\n const invalidPurposeMaps = preferences\n .map((pref, ind) => [pref, ind] as [ConsentPreferenceUpload, number])\n .filter(([pref]) => {\n if (!pref.purposes) {\n return false;\n }\n try {\n decodeCodec(PurposeMap, pref.purposes);\n return false;\n } catch {\n return true;\n }\n });\n if (invalidPurposeMaps.length > 0) {\n throw new Error(\n `Received invalid purpose maps: ${JSON.stringify(invalidPurposeMaps, null, 2)}`,\n );\n }\n\n // Ensure usp or preferences are provided\n const invalidInputs = preferences.filter((pref) => !pref.usp && !pref.purposes);\n if (invalidInputs.length > 0) {\n throw new Error(\n `Received invalid inputs, expected either purposes or usp to be defined: ${JSON.stringify(\n invalidInputs,\n null,\n 2,\n )}`,\n );\n }\n\n logger.info(\n colors.magenta(`Uploading ${preferences.length} user preferences to partition ${partition}`),\n );\n\n // Time duration\n const t0 = new Date().getTime();\n // create a new progress bar instance and use shades_classic theme\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n // Build a GraphQL client\n let total = 0;\n progressBar.start(preferences.length, 0);\n await map(\n preferences,\n async ({ userId, confirmed = 'true', updated, prompted, purposes, ...consent }) => {\n const token = createConsentToken(userId, base64EncryptionKey, base64SigningKey);\n\n // parse usp string\n const [, saleStatus] = consent.usp ? USP_STRING_REGEX.exec(consent.usp) || [] : [];\n\n const input = {\n token,\n partition,\n consent: {\n confirmed: confirmed === 'true',\n purposes: purposes\n ? decodeCodec(PurposeMap, purposes)\n : consent.usp\n ? { SaleOfInfo: saleStatus === 'Y' }\n : {},\n ...(updated ? { updated: updated === 'true' } : {}),\n ...(prompted ? { prompted: prompted === 'true' } : {}),\n ...consent,\n },\n } as ConsentPreferencesBody;\n\n // Make the request\n try {\n await transcendConsentApi\n .post('sync', {\n json: input,\n })\n .json();\n } catch (err) {\n try {\n const parsed = JSON.parse(err?.response?.body || '{}');\n if (parsed.error) {\n logger.error(colors.red(`Error: ${parsed.error}`));\n }\n } catch {\n // continue\n }\n throw new Error(`Received an error from server: ${err?.response?.body || err?.message}`);\n }\n\n total += 1;\n progressBar.update(total);\n },\n { concurrency },\n );\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Successfully uploaded ${preferences.length} user preferences to partition ${partition} in \"${\n totalTime / 1000\n }\" seconds!`,\n ),\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{a as e}from"./constants-XOsAW1__.mjs";import{t}from"./logger-Bj782ZYD.mjs";import{A as n}from"./codecs-CeDPaLYa.mjs";import{t as r}from"./readCsv-0PIlJQCN.mjs";import i from"colors";import{buildTranscendGraphQLClient as a,syncCookies as o}from"@transcend-io/sdk";import{splitCsvToList as s}from"@transcend-io/utils";const c=[`ID`,`Activity`,`Encounters`,`Last Seen At`,`Has Native Do Not Sell/Share Support`,`IAB USP API Support`,`Service Description`,`Website URL`,`Categories of Recipients`];async function l({auth:l,trackerStatus:u,file:d,transcendUrl:f=e}){let p=a(f,l);t.info(i.magenta(`Reading "${d}" from disk`)),await o(p,r(d,n).map(({"Is Regex?":e,Notes:t,Service:n,Purpose:r,Status:i,Owners:a,Teams:o,Name:l,...d})=>({...typeof e==`string`?{isRegex:e.toLowerCase()===`true`}:{},name:l,description:t,trackingPurposes:s(r),status:i||u,owners:a?s(a):void 0,teams:o?s(o):void 0,attributes:Object.entries(d).filter(([e])=>!c.includes(e)).map(([e,t])=>({key:e,values:s(t)}))})),{logger:t})||(t.error(i.red(`Encountered error(s) syncing cookies from CSV, see logs above for more info. `)),process.exit(1))}export{l as t};
2
+ //# sourceMappingURL=uploadCookiesFromCsv-B42cZgYW.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadCookiesFromCsv-B42cZgYW.mjs","names":[],"sources":["../src/lib/consent-manager/uploadCookiesFromCsv.ts"],"sourcesContent":["import { ConsentTrackerStatus } from '@transcend-io/privacy-types';\nimport { buildTranscendGraphQLClient, syncCookies } from '@transcend-io/sdk';\nimport { splitCsvToList } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { CookieInput, CookieCsvInput } from '../../codecs.js';\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { readCsv } from '../requests/readCsv.js';\n\nconst OMIT_COLUMNS = [\n 'ID',\n 'Activity',\n 'Encounters',\n 'Last Seen At',\n 'Has Native Do Not Sell/Share Support',\n 'IAB USP API Support',\n 'Service Description',\n 'Website URL',\n 'Categories of Recipients',\n];\n\n/**\n * Upload a set of cookies from CSV\n *\n * @param options - Options\n */\nexport async function uploadCookiesFromCsv({\n auth,\n trackerStatus,\n file,\n transcendUrl = DEFAULT_TRANSCEND_API,\n}: {\n /** CSV file path */\n file: string;\n /** Transcend API key authentication */\n auth: string;\n /** Sombra API key authentication */\n trackerStatus: ConsentTrackerStatus;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n}): Promise<void> {\n // Build a GraphQL client\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Read from CSV the set of cookie inputs\n logger.info(colors.magenta(`Reading \"${file}\" from disk`));\n const cookieInputs = readCsv(file, CookieCsvInput);\n\n // Convert these inputs into a format that the other function can use\n const validatedCookieInputs = cookieInputs.map(\n ({\n 'Is Regex?': isRegex,\n Notes,\n // TODO: https://transcend.height.app/T-26391 - export in CSV\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n Service,\n Purpose,\n Status,\n Owners,\n Teams,\n Name,\n ...rest\n }): CookieInput => ({\n ...(typeof isRegex === 'string' ? { isRegex: isRegex.toLowerCase() === 'true' } : {}),\n name: Name,\n description: Notes,\n trackingPurposes: splitCsvToList(Purpose),\n // TODO: https://transcend.height.app/T-26391\n // service: Service,\n // Apply the trackerStatus to all values in the CSV -> allows for customer to define tracker status\n // on a row by row basis if needed\n status: Status || trackerStatus,\n owners: Owners ? splitCsvToList(Owners) : undefined,\n teams: Teams ? splitCsvToList(Teams) : undefined,\n // all remaining options are attribute\n attributes: Object.entries(rest)\n // filter out native columns that are exported from the admin dashboard\n // but not custom attributes\n .filter(([key]) => !OMIT_COLUMNS.includes(key))\n .map(([key, value]) => ({\n key,\n values: splitCsvToList(value),\n })),\n }),\n );\n\n // Upload the cookies into Transcend dashboard\n const syncedCookies = await syncCookies(client, validatedCookieInputs, { logger });\n\n // Log errors\n if (!syncedCookies) {\n logger.error(\n colors.red('Encountered error(s) syncing cookies from CSV, see logs above for more info. '),\n );\n process.exit(1);\n }\n}\n"],"mappings":"mUAUA,MAAM,EAAe,CACnB,KACA,WACA,aACA,eACA,uCACA,sBACA,sBACA,cACA,2BACD,CAOD,eAAsB,EAAqB,CACzC,OACA,gBACA,OACA,eAAe,GAUC,CAEhB,IAAM,EAAS,EAA4B,EAAc,EAAK,CAG9D,EAAO,KAAK,EAAO,QAAQ,YAAY,EAAK,aAAa,CAAC,CA0CpC,MAAM,EAAY,EAzCnB,EAAQ,EAAM,EAAe,CAGP,KACxC,CACC,YAAa,EACb,QAGA,UACA,UACA,SACA,SACA,QACA,OACA,GAAG,MACe,CAClB,GAAI,OAAO,GAAY,SAAW,CAAE,QAAS,EAAQ,aAAa,GAAK,OAAQ,CAAG,EAAE,CACpF,KAAM,EACN,YAAa,EACb,iBAAkB,EAAe,EAAQ,CAKzC,OAAQ,GAAU,EAClB,OAAQ,EAAS,EAAe,EAAO,CAAG,IAAA,GAC1C,MAAO,EAAQ,EAAe,EAAM,CAAG,IAAA,GAEvC,WAAY,OAAO,QAAQ,EAAK,CAG7B,QAAQ,CAAC,KAAS,CAAC,EAAa,SAAS,EAAI,CAAC,CAC9C,KAAK,CAAC,EAAK,MAAY,CACtB,MACA,OAAQ,EAAe,EAAM,CAC9B,EAAE,CACN,EACF,CAGsE,CAAE,SAAQ,CAAC,GAIhF,EAAO,MACL,EAAO,IAAI,gFAAgF,CAC5F,CACD,QAAQ,KAAK,EAAE"}
@@ -0,0 +1,2 @@
1
+ import{a as e}from"./constants-XOsAW1__.mjs";import{t}from"./logger-Bj782ZYD.mjs";import{I as n}from"./codecs-CeDPaLYa.mjs";import{t as r}from"./readCsv-0PIlJQCN.mjs";import i from"colors";import{buildTranscendGraphQLClient as a,syncDataFlows as o}from"@transcend-io/sdk";import{splitCsvToList as s}from"@transcend-io/utils";const c=[`ID`,`Activity`,`Encounters`,`Last Seen At`,`Has Native Do Not Sell/Share Support`,`IAB USP API Support`,`Service Description`,`Website URL`,`Categories of Recipients`];async function l({auth:l,trackerStatus:u,file:d,classifyService:f=!1,transcendUrl:p=e}){let m=a(p,l);t.info(i.magenta(`Reading "${d}" from disk`)),await o(m,r(d,n).map(({Type:e,Notes:t,Service:n,Purpose:r,Status:i,Owners:a,Teams:o,"Connections Made To":l,...d})=>({value:l,type:e,description:t,trackingPurposes:s(r),status:i||u,owners:a?s(a):void 0,teams:o?s(o):void 0,attributes:Object.entries(d).filter(([e])=>!c.includes(e)).map(([e,t])=>({key:e,values:s(t)}))})),f,{logger:t})||(t.error(i.red(`Encountered error(s) syncing data flows from CSV, see logs above for more info. `)),process.exit(1))}export{l as t};
2
+ //# sourceMappingURL=uploadDataFlowsFromCsv-D2V567pP.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadDataFlowsFromCsv-D2V567pP.mjs","names":[],"sources":["../src/lib/consent-manager/uploadDataFlowsFromCsv.ts"],"sourcesContent":["import { ConsentTrackerStatus } from '@transcend-io/privacy-types';\nimport { buildTranscendGraphQLClient, syncDataFlows } from '@transcend-io/sdk';\nimport { splitCsvToList } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { DataFlowInput, DataFlowCsvInput } from '../../codecs.js';\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { readCsv } from '../requests/readCsv.js';\n\nconst OMIT_COLUMNS = [\n 'ID',\n 'Activity',\n 'Encounters',\n 'Last Seen At',\n 'Has Native Do Not Sell/Share Support',\n 'IAB USP API Support',\n 'Service Description',\n 'Website URL',\n 'Categories of Recipients',\n];\n\n/**\n * Upload a set of data flows from CSV\n *\n * @param options - Options\n */\nexport async function uploadDataFlowsFromCsv({\n auth,\n trackerStatus,\n file,\n classifyService = false,\n transcendUrl = DEFAULT_TRANSCEND_API,\n}: {\n /** CSV file path */\n file: string;\n /** Transcend API key authentication */\n auth: string;\n /** Sombra API key authentication */\n trackerStatus: ConsentTrackerStatus;\n /** classify data flow service if missing */\n classifyService?: boolean;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n}): Promise<void> {\n // Build a GraphQL client\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Read from CSV the set of data flow inputs\n logger.info(colors.magenta(`Reading \"${file}\" from disk`));\n const dataFlowInputs = readCsv(file, DataFlowCsvInput);\n\n // Convert these data flow inputs into a format that the other function can use\n const validatedDataFlowInputs = dataFlowInputs.map(\n ({\n Type,\n Notes,\n // TODO: https://transcend.height.app/T-26391 - export in CSV\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n Service,\n Purpose,\n Status,\n Owners,\n Teams,\n 'Connections Made To': value,\n ...rest\n }): DataFlowInput => ({\n value,\n type: Type,\n description: Notes,\n trackingPurposes: splitCsvToList(Purpose),\n // TODO: https://transcend.height.app/T-26391\n // service: Service,\n // Apply the trackerStatus to all values in the CSV -> allows for customer to define tracker status\n // on a row by row basis if needed\n status: Status || trackerStatus,\n owners: Owners ? splitCsvToList(Owners) : undefined,\n teams: Teams ? splitCsvToList(Teams) : undefined,\n // all remaining options are attribute\n attributes: Object.entries(rest)\n // filter out native columns that are exported from the admin dashboard\n // but not custom attributes\n .filter(([key]) => !OMIT_COLUMNS.includes(key))\n .map(([key, value]) => ({\n key,\n values: splitCsvToList(value),\n })),\n }),\n );\n\n // Upload the data flows into Transcend dashboard\n const syncedDataFlows = await syncDataFlows(client, validatedDataFlowInputs, classifyService, {\n logger,\n });\n\n // Log errors\n if (!syncedDataFlows) {\n logger.error(\n colors.red(\n 'Encountered error(s) syncing data flows from CSV, see logs above for more info. ',\n ),\n );\n process.exit(1);\n }\n}\n"],"mappings":"qUAUA,MAAM,EAAe,CACnB,KACA,WACA,aACA,eACA,uCACA,sBACA,sBACA,cACA,2BACD,CAOD,eAAsB,EAAuB,CAC3C,OACA,gBACA,OACA,kBAAkB,GAClB,eAAe,GAYC,CAEhB,IAAM,EAAS,EAA4B,EAAc,EAAK,CAG9D,EAAO,KAAK,EAAO,QAAQ,YAAY,EAAK,aAAa,CAAC,CA0ClC,MAAM,EAAc,EAzCrB,EAAQ,EAAM,EAAiB,CAGP,KAC5C,CACC,OACA,QAGA,UACA,UACA,SACA,SACA,QACA,sBAAuB,EACvB,GAAG,MACiB,CACpB,QACA,KAAM,EACN,YAAa,EACb,iBAAkB,EAAe,EAAQ,CAKzC,OAAQ,GAAU,EAClB,OAAQ,EAAS,EAAe,EAAO,CAAG,IAAA,GAC1C,MAAO,EAAQ,EAAe,EAAM,CAAG,IAAA,GAEvC,WAAY,OAAO,QAAQ,EAAK,CAG7B,QAAQ,CAAC,KAAS,CAAC,EAAa,SAAS,EAAI,CAAC,CAC9C,KAAK,CAAC,EAAK,MAAY,CACtB,MACA,OAAQ,EAAe,EAAM,CAC9B,EAAE,CACN,EACF,CAG4E,EAAiB,CAC5F,SACD,CAAC,GAIA,EAAO,MACL,EAAO,IACL,mFACD,CACF,CACD,QAAQ,KAAK,EAAE"}
@@ -0,0 +1,2 @@
1
+ import{a as e}from"./constants-XOsAW1__.mjs";import{t}from"./logger-Bj782ZYD.mjs";import{n,s as r}from"./dataSubject-CF784Ug0.mjs";import{a as i,c as a,i as o,l as s,o as c,r as l,s as u,t as d}from"./constants-DYbzl8QH.mjs";import{i as f,n as p,r as m,t as h}from"./parseAttributesFromString-B8h4DudO.mjs";import{t as g}from"./readCsv-0PIlJQCN.mjs";import{r as _,t as v}from"./extractClientError-X9wJVqGq.mjs";import{CompletedRequestStatus as y,IdentifierType as b,IsoCountryCode as x,IsoCountrySubdivisionCode as S,NORMALIZE_PHONE_NUMBER as C,RequestAction as w}from"@transcend-io/privacy-types";import{startCase as T,uniq as E}from"lodash-es";import{apply as D,getEntries as O,getValues as k,valuesOf as A}from"@transcend-io/type-utils";import{join as ee}from"node:path";import j from"colors";import{LOCALE_KEY as M}from"@transcend-io/internationalization";import*as N from"io-ts";import{ATTRIBUTE_KEYS_REQUESTS as P,buildTranscendGraphQLClient as te,createSombraGotInstance as ne,makeGraphQLRequest as F}from"@transcend-io/sdk";import{map as re,splitCsvToList as I}from"@transcend-io/utils";import L from"cli-progress";import R from"inquirer";import z from"inquirer-autocomplete-prompt";import{DateFromISOString as B}from"io-ts-types";import{PersistedState as V}from"@transcend-io/persisted-state";async function H(e,t,n){R.registerPrompt(`autocomplete`,z);let r=e.map(e=>e||`<blank>`).filter(e=>!n[e]);if(r.length===0)return n;let i=await R.prompt(r.map(e=>({name:e,message:`Map value of: ${e}`,type:`autocomplete`,default:t.find(t=>f(e,t)),source:(e,n)=>n?t.filter(e=>typeof e==`string`&&f(n,e)):t})));return{...n,...D(i,e=>typeof e==`string`?e:Object.values(e)[0])}}function U(e,t){return E(e.map(e=>e[t]||``).flat())}async function W(e){let n=E(e.map(e=>Object.keys(e)).flat()),r=e,i=!0;for(;i;){let{filterColumnName:e}=await R.prompt([{name:`filterColumnName`,message:`If you need to filter the list of requests to import, choose the column to filter on. Currently ${r.length} rows.`,type:`list`,default:n,choices:[s,...n]}]);if(i=s!==e,i){let t=U(r,e),{valuesToKeep:i}=await R.prompt([{name:`valuesToKeep`,message:`Keep rows matching this value`,type:`checkbox`,default:n,choices:t}]);r=r.filter(t=>i.includes(t[e]))}}return t.info(j.magenta(`Importing ${r.length} requests`)),r}async function G(e){let n=[],r=0,i=!1;do{let{attributeKeys:{nodes:a}}=await F(e,P,{variables:{first:20,offset:r},logger:t});n.push(...a),r+=20,i=a.length===20}while(i);return n.sort((e,t)=>e.name.localeCompare(t.name))}async function K(e,t){let n=k(c).filter(e=>!t.getValue(`columnNames`,e)),r=n.length===0?{}:await R.prompt(n.map(t=>{let n=T(t.replace(`ColumnName`,``)),r=m(e,n,a[t],!!l[t]);return{name:t,message:`Choose the column that will be used to map in the field: ${n}`,type:`list`,default:r[0],choices:r}}));return await Promise.all(O(r).map(([e,n])=>t.setValue(n,`columnNames`,e))),r}async function q(e,r,{state:i,columnNameMap:a}){let o=e=>i.getValue(`columnNames`,e)||a[e],{internalSubjects:l}=await F(e,n,{logger:t});t.info(j.magenta(`Determining mapping of columns for request action`));let u=await H(U(r,o(c.RequestType)),Object.values(w),i.getValue(`requestTypeToRequestAction`));await i.setValue(u,`requestTypeToRequestAction`),t.info(j.magenta(`Determining mapping of columns for subject`));let d=await H(U(r,o(c.SubjectType)),l.map(({type:e})=>e),i.getValue(`subjectTypeToSubjectName`));await i.setValue(d,`subjectTypeToSubjectName`),t.info(j.magenta(`Determining mapping of columns for locale`));let f=await H(U(r,o(c.Locale)),Object.values(M),i.getValue(`languageToLocale`));await i.setValue(f,`languageToLocale`),t.info(j.magenta(`Determining mapping of columns for request status`)),t.info(j.magenta(`Determining mapping of columns for request status`));let p=o(c.RequestStatus),m=p===`[NONE]`?{}:await H(U(r,p),[...Object.values(y),s],i.getValue(`statusToRequestStatus`));await i.setValue(m,`statusToRequestStatus`),t.info(j.magenta(`Determining mapping of columns for country`));let h=o(c.Country),g=h===`[NONE]`?{}:await H(U(r,h),[...Object.values(x),s],i.getValue(`regionToCountry`));await i.setValue(g,`regionToCountry`),t.info(j.magenta(`Determining mapping of columns for country sub division`));let _=o(c.CountrySubDivision),v=_===`[NONE]`?{}:await H(U(r,_),[...Object.values(S),s],i.getValue(`regionToCountrySubDivision`));await i.setValue(v,`regionToCountrySubDivision`)}const J=N.record(N.string,N.array(N.intersection([N.type({value:N.string}),N.partial({name:N.string})]))),Y=N.intersection([N.type({email:N.string,attestedExtraIdentifiers:J,coreIdentifier:N.string,requestType:A(w),subjectType:N.string}),N.partial({country:A(x),countrySubDivision:A(S),attributes:N.array(h),status:A(y),createdAt:B,dataSiloIds:N.array(N.string),locale:A(M)})]);function X(e,t,n){if(t===b.Email)return e.toLowerCase();if(t===b.Phone){let t=e.replace(C,``).replace(/[()]/g,``).replace(/[–]/g,``).replace(/[:]/g,``).replace(/[‭‬]/g,``).replace(/[A-Za-z]/g,``);return t?t.startsWith(`+`)?t:`+${n}${t}`:``}return e}function Z(e,t,{columnNameMap:n,identifierNameMap:r,attributeNameMap:i,requestAttributeKeys:a,defaultPhoneCountryCode:o=`1`}){let l=e=>t.getValue(`columnNames`,e)||n[e];return e.map(e=>{let n={};Object.entries(r).filter(([,e])=>e!==s).forEach(([t,r])=>{let i=Object.values(b).includes(t)?t:b.Custom,a=e[r];if(a){let e=X(a,i,o);e&&(n[i]||(n[i]=[]),n[i].push({value:e,name:t}))}});let u=[];Object.entries(i).filter(([,e])=>e!==s).forEach(([t,n])=>{let r=e[n];if(r){let e=a.find(e=>e.name===t)?.type===`MULTI_SELECT`;u.push({values:e?I(r):r,key:t})}});let f=l(c.RequestType),p=l(c.SubjectType);return[e,{email:e[l(c.Email)],attestedExtraIdentifiers:n,attributes:u,coreIdentifier:e[l(c.CoreIdentifier)],requestType:f===`[APPLY VALUE TO ALL ROWS]`?t.getValue(`requestTypeToRequestAction`,d):t.getValue(`requestTypeToRequestAction`,e[f]),subjectType:p===`[APPLY VALUE TO ALL ROWS]`?t.getValue(`subjectTypeToSubjectName`,d):t.getValue(`subjectTypeToSubjectName`,e[p]),...l(c.Locale)!==`[NONE]`&&e[l(c.Locale)]?{locale:t.getValue(`languageToLocale`,e[l(c.Locale)])}:{},...l(c.Country)!==`[NONE]`&&e[l(c.Country)]?{country:t.getValue(`regionToCountry`,e[l(c.Country)])}:{},...l(c.CountrySubDivision)!==`[NONE]`&&e[l(c.CountrySubDivision)]?{countrySubDivision:t.getValue(`regionToCountrySubDivision`,e[l(c.CountrySubDivision)])}:{},...l(c.RequestStatus)!==`[NONE]`&&t.getValue(`statusToRequestStatus`,e[l(c.RequestStatus)])!==`[NONE]`&&e[l(c.RequestStatus)]?{status:t.getValue(`statusToRequestStatus`,e[l(c.RequestStatus)])}:{},...l(c.CreatedAt)!==`[NONE]`&&e[l(c.CreatedAt)]?{createdAt:new Date(e[l(c.CreatedAt)])}:{},...l(c.DataSiloIds)!==`[NONE]`&&e[l(c.DataSiloIds)]?{dataSiloIds:I(e[l(c.DataSiloIds)])}:{}}]})}async function Q(e,n,i){let{initializer:a}=await F(e,r,{logger:t}),o=a.identifiers.filter(({name:e})=>!i.getValue(`identifierNames`,e)&&!u.includes(e)),s=o.length===0?{}:await R.prompt(o.map(({name:e})=>{let t=m(n,e,!1);return{name:e,message:`Choose the column that will be used to map in the identifier: ${e}`,type:`list`,default:t[0],choices:t}}));return await Promise.all(Object.entries(s).map(([e,t])=>i.setValue(t,`identifierNames`,e))),{...i.getValue(`identifierNames`),...s}}async function $(e,t,n,r){let i=r.filter(({name:e})=>!n.getValue(`attributeNames`,e)),a=i.length===0?{}:await R.prompt(i.map(({name:e})=>{let n=m(t,e,!1);return{name:e,message:`Choose the column that will be used to map in the attribute: ${e}`,type:`list`,default:n[0],choices:n}}));return await Promise.all(Object.entries(a).map(([e,t])=>n.setValue(t,`attributeNames`,e))),{...n.getValue(`attributeNames`),...a}}async function ie({cacheFilepath:n,requestReceiptFolder:r,file:a,auth:s,sombraAuth:c,concurrency:l=100,defaultPhoneCountryCode:u=`1`,transcendUrl:d=e,attributes:f=[],emailIsVerified:m=!0,skipFilterStep:h=!1,skipSendingReceipt:y=!0,isTest:b=!1,isSilent:x=!0,debug:S=!1,dryRun:C=!1}){let w=new Date().getTime(),T=new L.SingleBar({},L.Presets.shades_classic),D=p(f),O=new V(n,o,{columnNames:{},requestTypeToRequestAction:{},subjectTypeToSubjectName:{},languageToLocale:{},statusToRequestStatus:{},identifierNames:{},attributeNames:{},regionToCountrySubDivision:{},regionToCountry:{}}),k=ee(r,`tr-request-upload-${new Date().toISOString()}-${a.split(`/`).pop()}`.replace(`.csv`,`.json`)),A=new V(k,i,{successfulRequests:[],duplicateRequests:[],failingRequests:[]}),M=await ne(d,s,{logger:t,sombraApiKey:c,sombraUrl:process.env.SOMBRA_URL}),P=g(a,N.record(N.string,N.string)),F=E(P.map(e=>Object.keys(e)).flat());if(P.length===0)throw Error(`No Requests found in list! Ensure the first row of the CSV is a header and the rest are requests.`);if(S){let e=P[0];t.info(j.magenta(`First request: ${JSON.stringify(e,null,2)}`))}let I=h?P:await W(P),R=te(d,s),z=await G(R),B=await K(F,O),H=await Q(R,F,O),U=await $(R,F,O,z);await q(R,I,{state:O,columnNameMap:B});let J=Z(I,O,{defaultPhoneCountryCode:u,columnNameMap:B,identifierNameMap:H,attributeNameMap:U,requestAttributeKeys:z});S||T.start(J.length,0);let Y=0;await re(J,async([e,n],r)=>{let i=S?`email:${n.email} | coreIdentifier:${n.coreIdentifier}`:`row:${r.toString()}`;if(S&&t.info(j.magenta(`[${r+1}/${J.length}] Importing: ${JSON.stringify(n,null,2)}`)),C){t.info(j.magenta(`Bailing out on dry run because dryRun is set`));return}try{let a=await _(M,n,{details:`Uploaded by Transcend Cli: "tr-request-upload" : ${JSON.stringify(e,null,2)}`,isTest:b,emailIsVerified:m,skipSendingReceipt:y,isSilent:x,additionalAttributes:D});S&&(t.info(j.green(`[${r+1}/${J.length}] Successfully submitted the test data subject request: "${i}"`)),t.info(j.green(`[${r+1}/${J.length}] View it at: "${a.link}"`)));let o=A.getValue(`successfulRequests`);o.push({id:a.id,link:a.link,rowIndex:r,coreIdentifier:a.coreIdentifier,attemptedAt:new Date().toISOString()}),await A.setValue(o,`successfulRequests`)}catch(e){let a=`${e.message} - ${JSON.stringify(e.response?.body,null,2)}`,o=v(a);if(o===`Client error: You have already made this request.`){S&&t.info(j.yellow(`[${r+1}/${J.length}] Skipping request as it is a duplicate`));let e=A.getValue(`duplicateRequests`);e.push({coreIdentifier:n.coreIdentifier,rowIndex:r,attemptedAt:new Date().toISOString()}),await A.setValue(e,`duplicateRequests`)}else{let e=A.getValue(`failingRequests`);e.push({...n,rowIndex:r,error:o||a,attemptedAt:new Date().toISOString()}),await A.setValue(e,`failingRequests`),S&&(t.error(j.red(o||a)),t.error(j.red(`[${r+1}/${J.length}] Failed to submit request for: "${i}"`)))}}Y+=1,S||T.update(Y)},{concurrency:l}),T.stop();let X=new Date().getTime()-w;t.info(j.green(`Completed upload in "${X/1e3}" seconds.`)),A.getValue(`duplicateRequests`).length>0&&t.info(j.yellow(`Encountered "${A.getValue(`duplicateRequests`).length}" duplicate requests. See "${k}" to review the core identifiers for these requests.`)),A.getValue(`failingRequests`).length>0&&(t.error(j.red(`Encountered "${A.getValue(`failingRequests`).length}" errors. See "${k}" to review the error messages and inputs.`)),process.exit(1))}export{Y as a,q as c,W as d,U as f,J as i,K as l,$ as n,Z as o,H as p,Q as r,X as s,ie as t,G as u};
2
+ //# sourceMappingURL=uploadPrivacyRequestsFromCsv-Czc3vGfJ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadPrivacyRequestsFromCsv-Czc3vGfJ.mjs","names":[],"sources":["../src/lib/requests/mapEnumValues.ts","../src/lib/requests/getUniqueValuesForColumn.ts","../src/lib/requests/filterRows.ts","../src/lib/graphql/fetchAllAttributeKeys.ts","../src/lib/requests/mapCsvColumnsToApi.ts","../src/lib/requests/mapRequestEnumValues.ts","../src/lib/requests/mapCsvRowsToRequestInputs.ts","../src/lib/requests/mapColumnsToIdentifiers.ts","../src/lib/requests/mapColumnsToAttributes.ts","../src/lib/requests/uploadPrivacyRequestsFromCsv.ts"],"sourcesContent":["import { apply, ObjByString } from '@transcend-io/type-utils';\nimport inquirer from 'inquirer';\nimport autoCompletePrompt from 'inquirer-autocomplete-prompt';\n\nimport { fuzzySearch } from './fuzzyMatchColumns.js';\n\n/**\n * Map a set of inputs to a set of outputs\n *\n * @param csvInputs - Input list\n * @param expectedOutputs - Output list\n * @param cache - Cache\n * @returns Mapping from row to enum value\n */\nexport async function mapEnumValues<TValue extends string>(\n csvInputs: string[],\n expectedOutputs: TValue[],\n cache: { [k in string]: TValue },\n): Promise<{ [k in string]: TValue }> {\n inquirer.registerPrompt('autocomplete', autoCompletePrompt);\n\n const inputs = csvInputs.map((item) => item || '<blank>').filter((value) => !cache[value]);\n if (inputs.length === 0) {\n return cache;\n }\n const result = await inquirer.prompt<{ [k in string]: TValue }>(\n inputs.map((value) => ({\n name: value,\n message: `Map value of: ${value}`,\n type: 'autocomplete',\n default: expectedOutputs.find((x) => fuzzySearch(value, x)),\n source: (answersSoFar: ObjByString, input: string) =>\n !input\n ? expectedOutputs\n : expectedOutputs.filter((x) => typeof x === 'string' && fuzzySearch(input, x)),\n })),\n );\n return {\n ...cache,\n ...apply(result, (r) =>\n typeof r === 'string' ? (r as TValue) : (Object.values(r)[0] as TValue),\n ),\n };\n}\n","import { ObjByString } from '@transcend-io/type-utils';\nimport { uniq } from 'lodash-es';\n\n/**\n * Return the unique set of values for a column in a CSV\n *\n * @param rows - Rows to look up\n * @param columnName - Name of column to grab values for\n * @returns Unique set of values in that column\n */\nexport function getUniqueValuesForColumn(rows: ObjByString[], columnName: string): string[] {\n return uniq(rows.map((row) => row[columnName] || '').flat());\n}\n","import { ObjByString } from '@transcend-io/type-utils';\nimport colors from 'colors';\nimport inquirer from 'inquirer';\nimport { uniq } from 'lodash-es';\n\nimport { logger } from '../../logger.js';\nimport { NONE } from './constants.js';\nimport { getUniqueValuesForColumn } from './getUniqueValuesForColumn.js';\n\n/**\n * Filter a list of CSV rows by column values\n * Choose columns that contain metadata to filter the requests\n *\n * @param rows - Rows to filter\n * @returns Filtered rows\n */\nexport async function filterRows(rows: ObjByString[]): Promise<ObjByString[]> {\n // Determine set of column names\n const columnNames = uniq(rows.map((x) => Object.keys(x)).flat());\n\n // update these variables recursively\n let filteredRows = rows;\n let keepFiltering = true;\n\n // loop over\n while (keepFiltering) {\n // Prompt user for column to filter on\n\n const { filterColumnName } = await inquirer.prompt<{\n /** Name of column to filter on */\n filterColumnName: string;\n }>([\n {\n name: 'filterColumnName',\n // eslint-disable-next-line max-len\n message: `If you need to filter the list of requests to import, choose the column to filter on. Currently ${filteredRows.length} rows.`,\n type: 'list',\n default: columnNames,\n choices: [NONE, ...columnNames],\n },\n ]);\n\n // Determine if filtering should continue, or loop should be exited\n keepFiltering = NONE !== filterColumnName;\n if (keepFiltering) {\n const options = getUniqueValuesForColumn(filteredRows, filterColumnName);\n\n const { valuesToKeep } = await inquirer.prompt<{\n /** Values to keep */\n valuesToKeep: string[];\n }>([\n {\n name: 'valuesToKeep',\n message: 'Keep rows matching this value',\n type: 'checkbox',\n default: columnNames,\n choices: options,\n },\n ]);\n\n filteredRows = filteredRows.filter((request) =>\n valuesToKeep.includes(request[filterColumnName]),\n );\n }\n }\n\n logger.info(colors.magenta(`Importing ${filteredRows.length} requests`));\n return filteredRows;\n}\n","import { makeGraphQLRequest, ATTRIBUTE_KEYS_REQUESTS } from '@transcend-io/sdk';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { logger } from '../../logger.js';\n\nexport interface AttributeKey {\n /** ID of attribute key */\n id: string;\n /** Name of attribute key */\n name: string;\n /** Attribute key type */\n type: string;\n}\n\nconst PAGE_SIZE = 20;\n\n/**\n * Fetch all attribute keys enabled for privacy requests\n *\n * @param client - GraphQL client\n * @returns All attribute keys in the organization\n */\nexport async function fetchAllRequestAttributeKeys(client: GraphQLClient): Promise<AttributeKey[]> {\n const attributeKeys: AttributeKey[] = [];\n let offset = 0;\n\n // Whether to continue looping\n let shouldContinue = false;\n do {\n const {\n attributeKeys: { nodes },\n } = await makeGraphQLRequest<{\n /** Query response */\n attributeKeys: {\n /** List of matches */\n nodes: AttributeKey[];\n };\n }>(client, ATTRIBUTE_KEYS_REQUESTS, {\n variables: { first: PAGE_SIZE, offset },\n logger,\n });\n attributeKeys.push(...nodes);\n offset += PAGE_SIZE;\n shouldContinue = nodes.length === PAGE_SIZE;\n } while (shouldContinue);\n\n return attributeKeys.sort((a, b) => a.name.localeCompare(b.name));\n}\n","import type { PersistedState } from '@transcend-io/persisted-state';\nimport { getValues, getEntries } from '@transcend-io/type-utils';\nimport inquirer from 'inquirer';\nimport { startCase } from 'lodash-es';\n\nimport { ColumnName, CachedFileState, IS_REQUIRED, CAN_APPLY_IN_BULK } from './constants.js';\nimport { fuzzyMatchColumns } from './fuzzyMatchColumns.js';\n\n/**\n * Mapping from column name to request input parameter\n */\nexport type ColumnNameMap = {\n [k in ColumnName]?: string;\n};\n\n/**\n * Determine the mapping between columns in CSV\n *\n * @param columnNames - The set of column names\n * @param state - The cached file state used to map DSR inputs\n * @returns The column name mapping\n */\nexport async function mapCsvColumnsToApi(\n columnNames: string[],\n state: PersistedState<typeof CachedFileState>,\n): Promise<ColumnNameMap> {\n // Determine the columns that should be mapped\n const columnQuestions = getValues(ColumnName).filter(\n (name) => !state.getValue('columnNames', name),\n );\n\n // Skip mapping when everything is mapped\n const columnNameMap =\n columnQuestions.length === 0\n ? {}\n : // prompt questions to map columns\n await inquirer.prompt<{\n [k in ColumnName]?: string;\n }>(\n columnQuestions.map((name) => {\n const field = startCase(name.replace('ColumnName', ''));\n const matches = fuzzyMatchColumns(\n columnNames,\n field,\n IS_REQUIRED[name],\n !!CAN_APPLY_IN_BULK[name],\n );\n return {\n name,\n message: `Choose the column that will be used to map in the field: ${field}`,\n type: 'list',\n default: matches[0],\n choices: matches,\n };\n }),\n );\n\n await Promise.all(getEntries(columnNameMap).map(([k, v]) => state.setValue(v, 'columnNames', k)));\n return columnNameMap;\n}\n","import { LOCALE_KEY, type LocaleValue } from '@transcend-io/internationalization';\nimport type { PersistedState } from '@transcend-io/persisted-state';\nimport {\n CompletedRequestStatus,\n RequestAction,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n} from '@transcend-io/privacy-types';\nimport { makeGraphQLRequest } from '@transcend-io/sdk';\nimport { ObjByString } from '@transcend-io/type-utils';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { logger } from '../../logger.js';\nimport { DataSubject, DATA_SUBJECTS } from '../graphql/index.js';\nimport { CachedFileState, NONE, ColumnName } from './constants.js';\nimport { getUniqueValuesForColumn } from './getUniqueValuesForColumn.js';\nimport { ColumnNameMap } from './mapCsvColumnsToApi.js';\nimport { mapEnumValues } from './mapEnumValues.js';\n\n/**\n * Map the values in a CSV to the enum values in Transcend\n *\n * @param client - GraphQL client\n * @param requests - Set of privacy requests\n * @param options - Options\n */\nexport async function mapRequestEnumValues(\n client: GraphQLClient,\n requests: ObjByString[],\n {\n state,\n columnNameMap,\n }: {\n /** State value to write cache to */\n state: PersistedState<typeof CachedFileState>;\n /** Mapping of column names */\n columnNameMap: ColumnNameMap;\n },\n): Promise<void> {\n // Get mapped value\n const getMappedName = (attribute: ColumnName): string =>\n state.getValue('columnNames', attribute) || columnNameMap[attribute]!;\n\n // Fetch all data subjects in the organization\n const { internalSubjects } = await makeGraphQLRequest<{\n /** Query response */\n internalSubjects: DataSubject[];\n }>(client, DATA_SUBJECTS, { logger });\n\n // Map RequestAction\n logger.info(colors.magenta('Determining mapping of columns for request action'));\n const requestTypeToRequestAction: { [k in string]: RequestAction } = await mapEnumValues(\n getUniqueValuesForColumn(requests, getMappedName(ColumnName.RequestType)),\n Object.values(RequestAction),\n state.getValue('requestTypeToRequestAction'),\n );\n await state.setValue(requestTypeToRequestAction, 'requestTypeToRequestAction');\n\n // Map data subject type\n logger.info(colors.magenta('Determining mapping of columns for subject'));\n const subjectTypeToSubjectName: { [k in string]: string } = await mapEnumValues(\n getUniqueValuesForColumn(requests, getMappedName(ColumnName.SubjectType)),\n internalSubjects.map(({ type }) => type),\n state.getValue('subjectTypeToSubjectName'),\n );\n await state.setValue(subjectTypeToSubjectName, 'subjectTypeToSubjectName');\n\n // Map locale\n logger.info(colors.magenta('Determining mapping of columns for locale'));\n const languageToLocale: { [k in string]: LocaleValue } = await mapEnumValues(\n getUniqueValuesForColumn(requests, getMappedName(ColumnName.Locale)),\n Object.values(LOCALE_KEY),\n state.getValue('languageToLocale'),\n );\n await state.setValue(languageToLocale, 'languageToLocale');\n logger.info(colors.magenta('Determining mapping of columns for request status'));\n\n // Map request status\n logger.info(colors.magenta('Determining mapping of columns for request status'));\n const requestStatusColumn = getMappedName(ColumnName.RequestStatus);\n const statusToRequestStatus: {\n [k in string]: CompletedRequestStatus | typeof NONE;\n } =\n requestStatusColumn === NONE\n ? {}\n : await mapEnumValues(\n getUniqueValuesForColumn(requests, requestStatusColumn),\n [...Object.values(CompletedRequestStatus), NONE],\n state.getValue('statusToRequestStatus'),\n );\n await state.setValue(statusToRequestStatus, 'statusToRequestStatus');\n\n // Map country\n logger.info(colors.magenta('Determining mapping of columns for country'));\n const countryColumn = getMappedName(ColumnName.Country);\n const regionToCountry: {\n [k in string]: IsoCountryCode | typeof NONE;\n } =\n countryColumn === NONE\n ? {}\n : await mapEnumValues(\n getUniqueValuesForColumn(requests, countryColumn),\n [...Object.values(IsoCountryCode), NONE],\n state.getValue('regionToCountry'),\n );\n await state.setValue(regionToCountry, 'regionToCountry');\n\n // Map country sub division\n logger.info(colors.magenta('Determining mapping of columns for country sub division'));\n const countrySubDivisionColumn = getMappedName(ColumnName.CountrySubDivision);\n const regionToCountrySubDivision: {\n [k in string]: IsoCountrySubdivisionCode | typeof NONE;\n } =\n countrySubDivisionColumn === NONE\n ? {}\n : await mapEnumValues(\n getUniqueValuesForColumn(requests, countrySubDivisionColumn),\n [...Object.values(IsoCountrySubdivisionCode), NONE],\n state.getValue('regionToCountrySubDivision'),\n );\n await state.setValue(regionToCountrySubDivision, 'regionToCountrySubDivision');\n}\n","import { LOCALE_KEY } from '@transcend-io/internationalization';\nimport type { PersistedState } from '@transcend-io/persisted-state';\nimport {\n NORMALIZE_PHONE_NUMBER,\n CompletedRequestStatus,\n RequestAction,\n IdentifierType,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n} from '@transcend-io/privacy-types';\nimport { ObjByString, valuesOf } from '@transcend-io/type-utils';\nimport { splitCsvToList } from '@transcend-io/utils';\nimport * as t from 'io-ts';\nimport { DateFromISOString } from 'io-ts-types';\n\nimport { AttributeKey } from '../graphql/index.js';\nimport { CachedFileState, BLANK, BULK_APPLY, ColumnName, NONE } from './constants.js';\nimport { AttributeNameMap } from './mapColumnsToAttributes.js';\nimport { IdentifierNameMap } from './mapColumnsToIdentifiers.js';\nimport { ColumnNameMap } from './mapCsvColumnsToApi.js';\nimport { ParsedAttributeInput } from './parseAttributesFromString.js';\n\n/**\n * Shape of additional identifiers\n *\n * key of object is IdentifierType\n */\nexport const AttestedExtraIdentifiers = t.record(\n t.string,\n t.array(\n t.intersection([\n t.type({\n /** Value of identifier */\n value: t.string,\n }),\n t.partial({\n /** Name of identifier - option for non-custom identifier types */\n name: t.string,\n }),\n ]),\n ),\n);\n\n/** Type override */\nexport type AttestedExtraIdentifiers = t.TypeOf<typeof AttestedExtraIdentifiers>;\n\nexport const PrivacyRequestInput = t.intersection([\n t.type({\n /** Email of user */\n email: t.string,\n /** Extra identifiers */\n attestedExtraIdentifiers: AttestedExtraIdentifiers,\n /** Core identifier for user */\n coreIdentifier: t.string,\n /** Action type being submitted */\n requestType: valuesOf(RequestAction),\n /** Type of data subject */\n subjectType: t.string,\n }),\n t.partial({\n /** Country */\n country: valuesOf(IsoCountryCode),\n /** Country sub division */\n countrySubDivision: valuesOf(IsoCountrySubdivisionCode),\n /** Attribute inputs */\n attributes: t.array(ParsedAttributeInput),\n /** The status that the request should be created as */\n status: valuesOf(CompletedRequestStatus),\n /** The time that the request was created */\n createdAt: DateFromISOString,\n /** Data silo IDs to submit for */\n dataSiloIds: t.array(t.string),\n /** Language key to map to */\n locale: valuesOf(LOCALE_KEY),\n }),\n]);\n\n/** Type override */\nexport type PrivacyRequestInput = t.TypeOf<typeof PrivacyRequestInput>;\n\n/**\n * Transform the identifier value based on type\n *\n * @param identifierValue - Value of identifier\n * @param identifierType - Type of identifier\n * @param defaultPhoneCountryCode - Default country code for phone numbers\n * @returns Post-processed identifier\n */\nexport function normalizeIdentifierValue(\n identifierValue: string,\n identifierType: IdentifierType,\n defaultPhoneCountryCode: string,\n): string {\n // Lowercase email\n if (identifierType === IdentifierType.Email) {\n return identifierValue.toLowerCase();\n }\n\n // Normalize phone number\n if (identifierType === IdentifierType.Phone) {\n const normalized = identifierValue\n .replace(NORMALIZE_PHONE_NUMBER, '')\n .replace(/[()]/g, '')\n .replace(/[–]/g, '')\n .replace(/[:]/g, '')\n .replace(/[‭‬]/g, '')\n .replace(/[A-Za-z]/g, '');\n return !normalized\n ? ''\n : normalized.startsWith('+')\n ? normalized\n : `+${defaultPhoneCountryCode}${normalized}`;\n }\n return identifierValue;\n}\n\n/**\n * Take the raw rows in a CSV upload, and map those rows to the request\n * input shape that can be passed to the Transcend API to submit a privacy\n * request.\n *\n * @param requestInputs - CSV of requests to be uploaded\n * @param state - The cached set of mapping values\n * @param options - Options\n * @returns [raw input, request input] list\n */\nexport function mapCsvRowsToRequestInputs(\n requestInputs: ObjByString[],\n state: PersistedState<typeof CachedFileState>,\n {\n columnNameMap,\n identifierNameMap,\n attributeNameMap,\n requestAttributeKeys,\n defaultPhoneCountryCode = '1', // US\n }: {\n /** Default country code */\n defaultPhoneCountryCode?: string;\n /** Mapping of column names */\n columnNameMap: ColumnNameMap;\n /** Mapping of identifier names */\n identifierNameMap: IdentifierNameMap;\n /** Mapping of attribute names */\n attributeNameMap: AttributeNameMap;\n /** Request attribute keys */\n requestAttributeKeys: AttributeKey[];\n },\n): [Record<string, string>, PrivacyRequestInput][] {\n // map the CSV to request input\n const getMappedName = (attribute: ColumnName): string =>\n state.getValue('columnNames', attribute) || columnNameMap[attribute]!;\n return requestInputs.map((input): [Record<string, string>, PrivacyRequestInput] => {\n // The extra identifiers to upload for this request\n const attestedExtraIdentifiers: AttestedExtraIdentifiers = {};\n Object.entries(identifierNameMap)\n // filter out skipped identifiers\n .filter(([, columnName]) => columnName !== NONE)\n .forEach(([identifierName, columnName]) => {\n // Determine the identifier type being specified\n const identifierType = Object.values(IdentifierType).includes(\n identifierName as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n )\n ? (identifierName as IdentifierType)\n : IdentifierType.Custom;\n\n // Only add the identifier if the value exists\n const identifierValue = input[columnName];\n if (identifierValue) {\n const normalized = normalizeIdentifierValue(\n identifierValue,\n identifierType,\n defaultPhoneCountryCode,\n );\n if (normalized) {\n // Initialize\n if (!attestedExtraIdentifiers[identifierType]) {\n attestedExtraIdentifiers[identifierType] = [];\n }\n\n // Add the identifier\n attestedExtraIdentifiers[identifierType]!.push({\n value: normalized,\n name: identifierName,\n });\n }\n }\n });\n\n // The extra attributes to upload for this request\n const attributes: ParsedAttributeInput[] = [];\n Object.entries(attributeNameMap)\n // filter out skipped attributes\n .filter(([, columnName]) => columnName !== NONE)\n .forEach(([attributeName, columnName]) => {\n // Only add the identifier if the value exists\n const attributeValueString = input[columnName];\n if (attributeValueString) {\n // Add the attribute\n const isMulti =\n requestAttributeKeys.find((attr) => attr.name === attributeName)?.type ===\n 'MULTI_SELECT';\n attributes.push({\n values: isMulti ? splitCsvToList(attributeValueString) : attributeValueString,\n key: attributeName,\n });\n }\n });\n\n const requestTypeColumn = getMappedName(ColumnName.RequestType);\n const dataSubjectTypeColumn = getMappedName(ColumnName.SubjectType);\n return [\n input,\n {\n email: input[getMappedName(ColumnName.Email)],\n attestedExtraIdentifiers,\n attributes,\n coreIdentifier: input[getMappedName(ColumnName.CoreIdentifier)],\n requestType:\n requestTypeColumn === BULK_APPLY\n ? state.getValue('requestTypeToRequestAction', BLANK)\n : state.getValue('requestTypeToRequestAction', input[requestTypeColumn]),\n subjectType:\n dataSubjectTypeColumn === BULK_APPLY\n ? state.getValue('subjectTypeToSubjectName', BLANK)\n : state.getValue('subjectTypeToSubjectName', input[dataSubjectTypeColumn]),\n ...(getMappedName(ColumnName.Locale) !== NONE && input[getMappedName(ColumnName.Locale)]\n ? {\n locale: state.getValue('languageToLocale', input[getMappedName(ColumnName.Locale)]),\n }\n : {}),\n ...(getMappedName(ColumnName.Country) !== NONE && input[getMappedName(ColumnName.Country)]\n ? {\n country: state.getValue(\n 'regionToCountry',\n input[getMappedName(ColumnName.Country)],\n ) as IsoCountryCode,\n }\n : {}),\n ...(getMappedName(ColumnName.CountrySubDivision) !== NONE &&\n input[getMappedName(ColumnName.CountrySubDivision)]\n ? {\n countrySubDivision: state.getValue(\n 'regionToCountrySubDivision',\n input[getMappedName(ColumnName.CountrySubDivision)],\n ) as IsoCountrySubdivisionCode,\n }\n : {}),\n ...(getMappedName(ColumnName.RequestStatus) !== NONE &&\n state.getValue('statusToRequestStatus', input[getMappedName(ColumnName.RequestStatus)]) !==\n NONE &&\n input[getMappedName(ColumnName.RequestStatus)]\n ? {\n status: state.getValue(\n 'statusToRequestStatus',\n input[getMappedName(ColumnName.RequestStatus)],\n ) as CompletedRequestStatus,\n }\n : {}),\n ...(getMappedName(ColumnName.CreatedAt) !== NONE &&\n input[getMappedName(ColumnName.CreatedAt)]\n ? {\n createdAt: new Date(input[getMappedName(ColumnName.CreatedAt)]),\n }\n : {}),\n ...(getMappedName(ColumnName.DataSiloIds) !== NONE &&\n input[getMappedName(ColumnName.DataSiloIds)]\n ? {\n dataSiloIds: splitCsvToList(input[getMappedName(ColumnName.DataSiloIds)]),\n }\n : {}),\n },\n ];\n });\n}\n","import type { PersistedState } from '@transcend-io/persisted-state';\nimport { makeGraphQLRequest } from '@transcend-io/sdk';\nimport type { GraphQLClient } from 'graphql-request';\nimport inquirer from 'inquirer';\n\nimport { logger } from '../../logger.js';\nimport { INITIALIZER, Initializer } from '../graphql/index.js';\nimport { CachedFileState, IDENTIFIER_BLOCK_LIST } from './constants.js';\nimport { fuzzyMatchColumns } from './fuzzyMatchColumns.js';\n\n/**\n * Mapping from identifier name to request input parameter\n */\nexport type IdentifierNameMap = {\n [k in string]: string;\n};\n\n/**\n * Create a mapping from the identifier names that can be included\n * at request submission, to the names of the columns that map to those\n * identifiers.\n *\n * @param client - GraphQL client\n * @param columnNames - The set of all column names\n * @param state - Cached state of this mapping\n * @returns Mapping from identifier name to column name\n */\nexport async function mapColumnsToIdentifiers(\n client: GraphQLClient,\n columnNames: string[],\n state: PersistedState<typeof CachedFileState>,\n): Promise<IdentifierNameMap> {\n // Grab the initializer\n const { initializer } = await makeGraphQLRequest<{\n /** Query response */\n initializer: Initializer;\n }>(client, INITIALIZER, { logger });\n\n // Determine the columns that should be mapped\n const columnQuestions = initializer.identifiers.filter(\n ({ name }) => !state.getValue('identifierNames', name) && !IDENTIFIER_BLOCK_LIST.includes(name),\n );\n\n // Skip mapping when everything is mapped\n const identifierNameMap =\n columnQuestions.length === 0\n ? {}\n : // prompt questions to map columns\n await inquirer.prompt<{\n [k in string]: string;\n }>(\n columnQuestions.map(({ name }) => {\n const matches = fuzzyMatchColumns(columnNames, name, false);\n return {\n name,\n message: `Choose the column that will be used to map in the identifier: ${name}`,\n type: 'list',\n default: matches[0],\n choices: matches,\n };\n }),\n );\n await Promise.all(\n Object.entries(identifierNameMap).map(([k, v]) => state.setValue(v, 'identifierNames', k)),\n );\n\n return {\n ...state.getValue('identifierNames'),\n ...identifierNameMap,\n };\n}\n","import type { PersistedState } from '@transcend-io/persisted-state';\nimport type { GraphQLClient } from 'graphql-request';\nimport inquirer from 'inquirer';\n\nimport { AttributeKey } from '../graphql/index.js';\nimport { CachedFileState } from './constants.js';\nimport { fuzzyMatchColumns } from './fuzzyMatchColumns.js';\n\n/**\n * Mapping from attribute name to request input parameter\n */\nexport type AttributeNameMap = {\n [k in string]: string;\n};\n\n/**\n * Create a mapping from the attributes names that can be included\n * at request submission, to the names of the columns that map to those\n * attributes.\n *\n * @param client - GraphQL client\n * @param columnNames - The set of all column names\n * @param state - Cached state of this mapping\n * @param requestAttributeKeys - Attribute keys to map\n * @returns Mapping from attributes name to column name\n */\nexport async function mapColumnsToAttributes(\n client: GraphQLClient,\n columnNames: string[],\n state: PersistedState<typeof CachedFileState>,\n requestAttributeKeys: AttributeKey[],\n): Promise<AttributeNameMap> {\n // Determine the columns that should be mapped\n const columnQuestions = requestAttributeKeys.filter(\n ({ name }) => !state.getValue('attributeNames', name),\n );\n\n // Skip mapping when everything is mapped\n const attributeNameMap =\n columnQuestions.length === 0\n ? {}\n : // prompt questions to map columns\n await inquirer.prompt<{\n [k in string]: string;\n }>(\n columnQuestions.map(({ name }) => {\n const matches = fuzzyMatchColumns(columnNames, name, false);\n return {\n name,\n message: `Choose the column that will be used to map in the attribute: ${name}`,\n type: 'list',\n default: matches[0],\n choices: matches,\n };\n }),\n );\n await Promise.all(\n Object.entries(attributeNameMap).map(([k, v]) => state.setValue(v, 'attributeNames', k)),\n );\n\n return {\n ...state.getValue('attributeNames'),\n ...attributeNameMap,\n };\n}\n","import { join } from 'node:path';\n\nimport { PersistedState } from '@transcend-io/persisted-state';\nimport { buildTranscendGraphQLClient, createSombraGotInstance } from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\n/* eslint-disable max-lines */\nimport colors from 'colors';\nimport * as t from 'io-ts';\nimport { uniq } from 'lodash-es';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchAllRequestAttributeKeys } from '../graphql/index.js';\nimport { CachedRequestState, CachedFileState } from './constants.js';\nimport { extractClientError } from './extractClientError.js';\nimport { filterRows } from './filterRows.js';\nimport { mapColumnsToAttributes } from './mapColumnsToAttributes.js';\nimport { mapColumnsToIdentifiers } from './mapColumnsToIdentifiers.js';\nimport { mapCsvColumnsToApi } from './mapCsvColumnsToApi.js';\nimport { mapCsvRowsToRequestInputs } from './mapCsvRowsToRequestInputs.js';\nimport { mapRequestEnumValues } from './mapRequestEnumValues.js';\nimport { parseAttributesFromString } from './parseAttributesFromString.js';\nimport { readCsv } from './readCsv.js';\nimport { submitPrivacyRequest } from './submitPrivacyRequest.js';\n\n/**\n * Upload a set of privacy requests from CSV\n *\n * @param options - Options\n */\nexport async function uploadPrivacyRequestsFromCsv({\n cacheFilepath,\n requestReceiptFolder,\n file,\n auth,\n sombraAuth,\n concurrency = 100,\n defaultPhoneCountryCode = '1', // USA\n transcendUrl = DEFAULT_TRANSCEND_API,\n attributes = [],\n emailIsVerified = true,\n skipFilterStep = false,\n skipSendingReceipt = true,\n isTest = false,\n isSilent = true,\n debug = false,\n dryRun = false,\n}: {\n /** File to cache metadata about mapping of CSV shape to script */\n cacheFilepath: string;\n /** File where request receipts are stored */\n requestReceiptFolder: string;\n /** CSV file path */\n file: string;\n /** Transcend API key authentication */\n auth: string;\n /** Default country code for phone numbers */\n defaultPhoneCountryCode?: string;\n /** Concurrency to upload in */\n concurrency?: number;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** Include debug logs */\n debug?: boolean;\n /** Skip the step where requests are filtered */\n skipFilterStep?: boolean;\n /** Whether test requests are being uploaded */\n isTest?: boolean;\n /** Whether requests are uploaded in silent mode */\n isSilent?: boolean;\n /** Whether to send the email receipt */\n skipSendingReceipt?: boolean;\n /** Whether the email was verified up front */\n emailIsVerified?: boolean;\n /** Attributes string pre-parse */\n attributes?: string[];\n /** Whether a dry run is happening */\n dryRun?: boolean;\n}): Promise<void> {\n // Time duration\n const t0 = new Date().getTime();\n // create a new progress bar instance and use shades_classic theme\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n // Parse out the extra attributes to apply to all requests uploaded\n const parsedAttributes = parseAttributesFromString(attributes);\n\n // Create a new state to persist the metadata that\n // maps the request inputs to the Transcend API shape\n const state = new PersistedState(cacheFilepath, CachedFileState, {\n columnNames: {},\n requestTypeToRequestAction: {},\n subjectTypeToSubjectName: {},\n languageToLocale: {},\n statusToRequestStatus: {},\n identifierNames: {},\n attributeNames: {},\n regionToCountrySubDivision: {},\n regionToCountry: {},\n });\n\n // Create a new state file to store the requests from this run\n const requestCacheFile = join(\n requestReceiptFolder,\n `tr-request-upload-${new Date().toISOString()}-${file.split('/').pop()}`.replace(\n '.csv',\n '.json',\n ),\n );\n const requestState = new PersistedState(requestCacheFile, CachedRequestState, {\n successfulRequests: [],\n duplicateRequests: [],\n failingRequests: [],\n });\n\n // Create sombra instance to communicate with\n const sombra = await createSombraGotInstance(transcendUrl, auth, {\n logger,\n sombraApiKey: sombraAuth,\n sombraUrl: process.env.SOMBRA_URL,\n });\n\n // Read in the list of integration requests\n const requestsList = readCsv(file, t.record(t.string, t.string));\n const columnNames = uniq(requestsList.map((x) => Object.keys(x)).flat());\n\n // Log out an example request\n if (requestsList.length === 0) {\n throw new Error(\n 'No Requests found in list! Ensure the first row of the CSV is a header and the rest are requests.',\n );\n }\n if (debug) {\n const firstRequest = requestsList[0];\n logger.info(colors.magenta(`First request: ${JSON.stringify(firstRequest, null, 2)}`));\n }\n // Determine what rows in the CSV should be imported\n // Choose columns that contain metadata to filter the requests\n const filteredRequestList = skipFilterStep ? requestsList : await filterRows(requestsList);\n\n // Build a GraphQL client\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n // Grab the request attributes\n const requestAttributeKeys = await fetchAllRequestAttributeKeys(client);\n // Determine the columns that should be mapped\n const columnNameMap = await mapCsvColumnsToApi(columnNames, state);\n const identifierNameMap = await mapColumnsToIdentifiers(client, columnNames, state);\n const attributeNameMap = await mapColumnsToAttributes(\n client,\n columnNames,\n state,\n requestAttributeKeys,\n );\n await mapRequestEnumValues(client, filteredRequestList, {\n state,\n columnNameMap,\n });\n\n // map the CSV to request input\n const requestInputs = mapCsvRowsToRequestInputs(filteredRequestList, state, {\n defaultPhoneCountryCode,\n columnNameMap,\n identifierNameMap,\n attributeNameMap,\n requestAttributeKeys,\n });\n\n // start the progress bar with a total value of 200 and start value of 0\n if (!debug) {\n progressBar.start(requestInputs.length, 0);\n }\n let total = 0;\n // Submit each request\n await map(\n requestInputs,\n async ([rawRow, requestInput], ind) => {\n // The identifier to log, only include personal data if debug mode is on\n const requestLogId = debug\n ? `email:${requestInput.email} | coreIdentifier:${requestInput.coreIdentifier}`\n : `row:${ind.toString()}`;\n\n if (debug) {\n logger.info(\n colors.magenta(\n `[${ind + 1}/${requestInputs.length}] Importing: ${JSON.stringify(\n requestInput,\n null,\n 2,\n )}`,\n ),\n );\n }\n\n // Skip on dry run\n if (dryRun) {\n logger.info(colors.magenta('Bailing out on dry run because dryRun is set'));\n return;\n }\n\n try {\n // Make the GraphQL request to submit the privacy request\n const requestResponse = await submitPrivacyRequest(sombra, requestInput, {\n details: `Uploaded by Transcend Cli: \"tr-request-upload\" : ${JSON.stringify(\n rawRow,\n null,\n 2,\n )}`,\n isTest,\n emailIsVerified,\n skipSendingReceipt,\n isSilent,\n additionalAttributes: parsedAttributes,\n });\n\n // Log success\n if (debug) {\n logger.info(\n colors.green(\n `[${ind + 1}/${\n requestInputs.length\n }] Successfully submitted the test data subject request: \"${requestLogId}\"`,\n ),\n );\n logger.info(\n colors.green(\n `[${ind + 1}/${requestInputs.length}] View it at: \"${requestResponse.link}\"`,\n ),\n );\n }\n\n // Cache successful upload\n const successfulRequests = requestState.getValue('successfulRequests');\n successfulRequests.push({\n id: requestResponse.id,\n link: requestResponse.link,\n rowIndex: ind,\n coreIdentifier: requestResponse.coreIdentifier,\n attemptedAt: new Date().toISOString(),\n });\n await requestState.setValue(successfulRequests, 'successfulRequests');\n } catch (err) {\n const msg = `${err.message} - ${JSON.stringify(err.response?.body, null, 2)}`;\n const clientError = extractClientError(msg);\n\n if (clientError === 'Client error: You have already made this request.') {\n if (debug) {\n logger.info(\n colors.yellow(\n `[${ind + 1}/${requestInputs.length}] Skipping request as it is a duplicate`,\n ),\n );\n }\n const duplicateRequests = requestState.getValue('duplicateRequests');\n duplicateRequests.push({\n coreIdentifier: requestInput.coreIdentifier,\n rowIndex: ind,\n attemptedAt: new Date().toISOString(),\n });\n await requestState.setValue(duplicateRequests, 'duplicateRequests');\n } else {\n const failingRequests = requestState.getValue('failingRequests');\n failingRequests.push({\n ...requestInput,\n rowIndex: ind,\n error: clientError || msg,\n attemptedAt: new Date().toISOString(),\n });\n await requestState.setValue(failingRequests, 'failingRequests');\n if (debug) {\n logger.error(colors.red(clientError || msg));\n logger.error(\n colors.red(\n `[${ind + 1}/${\n requestInputs.length\n }] Failed to submit request for: \"${requestLogId}\"`,\n ),\n );\n }\n }\n }\n\n total += 1;\n if (!debug) {\n progressBar.update(total);\n }\n },\n {\n concurrency,\n },\n );\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n // Log completion time\n logger.info(colors.green(`Completed upload in \"${totalTime / 1000}\" seconds.`));\n\n // Log duplicates\n if (requestState.getValue('duplicateRequests').length > 0) {\n logger.info(\n colors.yellow(\n `Encountered \"${requestState.getValue('duplicateRequests').length}\" duplicate requests. ` +\n `See \"${requestCacheFile}\" to review the core identifiers for these requests.`,\n ),\n );\n }\n\n // Log errors\n if (requestState.getValue('failingRequests').length > 0) {\n logger.error(\n colors.red(\n `Encountered \"${requestState.getValue('failingRequests').length}\" errors. ` +\n `See \"${requestCacheFile}\" to review the error messages and inputs.`,\n ),\n );\n process.exit(1);\n }\n}\n/* eslint-enable max-lines */\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{t as e}from"./logger-B-LXIf3U.mjs";import{mt as t}from"./codecs-BE3Wmoh8.mjs";import{decodeCodec as n}from"@transcend-io/type-utils";import{existsSync as r,readFileSync as i}from"node:fs";import a from"colors";import*as o from"io-ts";function s(s){return s||(e.error(a.red(`A Transcend API key must be provided. You can specify using --auth=$TRANSCEND_API_KEY`)),process.exit(1)),r(s)?n(o.array(t),i(s,`utf-8`)):s}export{s as t};
2
- //# sourceMappingURL=validateTranscendAuth-1W1IylqE.mjs.map
1
+ import{t as e}from"./logger-Bj782ZYD.mjs";import{mt as t}from"./codecs-CeDPaLYa.mjs";import{decodeCodec as n}from"@transcend-io/type-utils";import{existsSync as r,readFileSync as i}from"node:fs";import a from"colors";import*as o from"io-ts";function s(s){return s||(e.error(a.red(`A Transcend API key must be provided. You can specify using --auth=$TRANSCEND_API_KEY`)),process.exit(1)),r(s)?n(o.array(t),i(s,`utf-8`)):s}export{s as t};
2
+ //# sourceMappingURL=validateTranscendAuth-DCwAtgvh.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"validateTranscendAuth-1W1IylqE.mjs","names":[],"sources":["../src/lib/api-keys/validateTranscendAuth.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\n\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport colors from 'colors';\nimport * as t from 'io-ts';\n\nimport { StoredApiKey } from '../../codecs.js';\nimport { logger } from '../../logger.js';\n\n/**\n * Determine if the `--auth` parameter is an API key or a path to a JSON\n * file containing a list of API keys.\n *\n * @param auth - Raw auth parameter\n * @returns The API key or the list API keys\n */\nexport function validateTranscendAuth(auth: string): string | StoredApiKey[] {\n // Ensure auth is passed\n if (!auth) {\n logger.error(\n colors.red(\n 'A Transcend API key must be provided. You can specify using --auth=$TRANSCEND_API_KEY',\n ),\n );\n process.exit(1);\n }\n\n // Read from disk\n if (existsSync(auth)) {\n // validate that file is a list of API keys\n return decodeCodec(t.array(StoredApiKey), readFileSync(auth, 'utf-8'));\n }\n\n // Return as single API key\n return auth;\n}\n"],"mappings":"iPAgBA,SAAgB,EAAsB,EAAuC,CAkB3E,OAhBK,IACH,EAAO,MACL,EAAO,IACL,wFACD,CACF,CACD,QAAQ,KAAK,EAAE,EAIb,EAAW,EAAK,CAEX,EAAY,EAAE,MAAM,EAAa,CAAE,EAAa,EAAM,QAAQ,CAAC,CAIjE"}
1
+ {"version":3,"file":"validateTranscendAuth-DCwAtgvh.mjs","names":[],"sources":["../src/lib/api-keys/validateTranscendAuth.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\n\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport colors from 'colors';\nimport * as t from 'io-ts';\n\nimport { StoredApiKey } from '../../codecs.js';\nimport { logger } from '../../logger.js';\n\n/**\n * Determine if the `--auth` parameter is an API key or a path to a JSON\n * file containing a list of API keys.\n *\n * @param auth - Raw auth parameter\n * @returns The API key or the list API keys\n */\nexport function validateTranscendAuth(auth: string): string | StoredApiKey[] {\n // Ensure auth is passed\n if (!auth) {\n logger.error(\n colors.red(\n 'A Transcend API key must be provided. You can specify using --auth=$TRANSCEND_API_KEY',\n ),\n );\n process.exit(1);\n }\n\n // Read from disk\n if (existsSync(auth)) {\n // validate that file is a list of API keys\n return decodeCodec(t.array(StoredApiKey), readFileSync(auth, 'utf-8'));\n }\n\n // Return as single API key\n return auth;\n}\n"],"mappings":"iPAgBA,SAAgB,EAAsB,EAAuC,CAkB3E,OAhBK,IACH,EAAO,MACL,EAAO,IACL,wFACD,CACF,CACD,QAAQ,KAAK,EAAE,EAIb,EAAW,EAAK,CAEX,EAAY,EAAE,MAAM,EAAa,CAAE,EAAa,EAAM,QAAQ,CAAC,CAIjE"}
@@ -3,4 +3,4 @@ import{appendFileSync as e,createWriteStream as t,writeFileSync as n}from"node:f
3
3
  `))}function o(e,t){if(!t||t.length===0){n(e,``);return}n(e,`${t.map(i).join(`,`)}\n`)}function s(t,n,r){n.length&&e(t,`${n.map(e=>r.map(t=>{let n=e[t];return n==null?``:String(n)}).map(i).join(`,`)).join(`
4
4
  `)}\n`)}function c(t,n){e(t,`\n${n.map(e=>Object.values(e)).map(e=>e.map(i).join(`,`)).join(`
5
5
  `)}`)}async function l(e,n,i=!0){let a=t(e);await new Promise((e,t)=>{try{let o=r.write(n,{headers:i,objectMode:!0}).on(`error`,t);a.on(`error`,t),a.on(`finish`,()=>e()),o.pipe(a)}catch(e){t(e)}})}function u(e){let t=e.lastIndexOf(`.`);return{baseName:t===-1?e:e.substring(0,t),extension:t===-1?`.csv`:e.substring(t)}}function d(e,t){let n={};for(let r of t)n[r]=e[r];return n}function f(e){return new Promise(t=>{e.once(`drain`,t)})}async function p(e,n,i=!0){let a;a=Array.isArray(i)?i:i===!0?n.length>0?Object.keys(n[0]):[]:!1;let o=t(e),s=r.format({headers:a||void 0,objectMode:!0}),c=s.pipe(o),l=new Promise((e,t)=>{c.on(`finish`,()=>e()),c.on(`error`,t),s.on(`error`,t),o.on(`error`,t)});for(let e of n){let t=a?d(e,a):e;s.write(t)||await f(s)}return s.end(),await l,[e]}export{l as a,u as i,c as n,a as o,o as r,p as s,s as t};
6
- //# sourceMappingURL=writeCsv-B51ulrVl.mjs.map
6
+ //# sourceMappingURL=writeCsv-Da8NUe1V.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"writeCsv-B51ulrVl.mjs","names":[],"sources":["../src/lib/helpers/writeCsv.ts"],"sourcesContent":["import { createWriteStream, writeFileSync, appendFileSync } from 'node:fs';\n\nimport { ObjByString } from '@transcend-io/type-utils';\nimport * as fastcsv from 'fast-csv';\n\n/**\n * Escape a CSV value\n *\n * @param value - Value to escape\n * @returns Escaped value\n */\nfunction escapeCsvValue(value: string): string {\n if (value.includes('\"') || value.includes(',') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\n/**\n * Write a csv to file synchronously, overwriting any existing content\n *\n * @param filePath - File to write out to\n * @param data - Data to write\n * @param headers - Headers. If true, use object keys as headers. If array, use provided headers.\n */\nexport function writeCsvSync(filePath: string, data: ObjByString[], headers: string[]): void {\n const rows: string[][] = [];\n\n rows.push(headers);\n rows.push(...data.map((row) => Object.values(row)));\n\n // Build CSV content with proper escaping\n const csvContent = rows.map((row) => row.map(escapeCsvValue).join(',')).join('\\n');\n\n // Write to file, overwriting existing content\n writeFileSync(filePath, csvContent);\n}\n\n/**\n * Initialize a CSV file by writing only the header row (or an empty file if no headers).\n *\n * @param filePath - CSV path\n * @param headers - Ordered list of column names; if empty, creates/empties the file\n */\nexport function initCsvFile(filePath: string, headers: string[]): void {\n if (!headers || headers.length === 0) {\n writeFileSync(filePath, '');\n return;\n }\n const headerLine = headers.map(escapeCsvValue).join(',');\n writeFileSync(filePath, `${headerLine}\\n`);\n}\n\n/**\n * Append rows to CSV using an explicit header order (no header line).\n * Values are written in the order of `headerOrder`.\n *\n * @param filePath - CSV path\n * @param data - Row objects\n * @param headerOrder - Column order to apply\n */\nexport function appendCsvRowsOrdered(\n filePath: string,\n data: ObjByString[],\n headerOrder: string[],\n): void {\n if (!data.length) return;\n\n const lines = data.map((row) => {\n const vals = headerOrder.map((key) => {\n const v = row[key];\n return v == null ? '' : String(v);\n });\n return vals.map(escapeCsvValue).join(',');\n });\n\n appendFileSync(filePath, `${lines.join('\\n')}\\n`);\n}\n\n/**\n * Append data to an existing csv file synchronously (legacy, uses Object.values order).\n * Prefer appendCsvRowsOrdered for deterministic column order.\n *\n * @param filePath - File to append to\n * @param data - Data to append\n */\nexport function appendCsvSync(filePath: string, data: ObjByString[]): void {\n // Convert data to CSV rows\n const rows = data.map((row) => Object.values(row));\n\n // Build CSV content with proper escaping\n const csvContent = rows.map((row) => row.map(escapeCsvValue).join(',')).join('\\n');\n\n // Append to file with leading newline\n appendFileSync(filePath, `\\n${csvContent}`);\n}\n\n/**\n * Write a csv to file asynchronously\n *\n * @param filePath - File to write out to\n * @param data - Data to write\n * @param headers - Headers\n */\nexport async function writeCsv(\n filePath: string,\n data: ObjByString[],\n headers: boolean | string[] = true,\n): Promise<void> {\n const ws = createWriteStream(filePath);\n await new Promise<void>((resolve, reject) => {\n try {\n const stream = fastcsv.write(data, { headers, objectMode: true }).on('error', reject);\n\n ws.on('error', reject);\n ws.on('finish', () => resolve());\n\n stream.pipe(ws);\n } catch (err) {\n reject(err);\n }\n });\n}\n\n/**\n * Parse a file path into a base name and extension\n *\n * @param filePath - File path to parse\n * @returns Base name and extension\n */\nexport function parseFilePath(filePath: string): {\n /** Base name of the file */\n baseName: string;\n /** Extension of the file */\n extension: string;\n} {\n const lastDotIndex = filePath.lastIndexOf('.');\n return {\n baseName: lastDotIndex !== -1 ? filePath.substring(0, lastDotIndex) : filePath,\n extension: lastDotIndex !== -1 ? filePath.substring(lastDotIndex) : '.csv',\n };\n}\n\n/**\n * Convert an object row into values aligned to header order\n *\n * @param row - Row object\n * @param headerOrder - Header order\n * @returns Aligned row object\n */\nfunction rowToValues(row: ObjByString, headerOrder: string[]): Record<string, unknown> {\n // fast-csv with objectMode expects objects; we ensure consistent key ordering\n // by building a new object with keys in headerOrder.\n const ordered: Record<string, unknown> = {};\n for (const key of headerOrder) {\n // Preserve undefined -> becomes empty cell in CSV\n ordered[key] = row[key];\n }\n return ordered;\n}\n\n/**\n * Await the 'drain' event when backpressure indicates buffering\n *\n * @param stream - Writable stream\n * @returns Promise that resolves on 'drain'\n */\nfunction waitForDrain(stream: NodeJS.WritableStream): Promise<void> {\n return new Promise((resolve) => {\n stream.once('drain', resolve);\n });\n}\n\n/**\n * Stream a large CSV dataset to a single file with proper backpressure handling.\n * (Kept for completeness; not used by the incremental write path.)\n *\n * @param filePath - File to write out to\n * @param data - Data to write (iterated without buffering the entire file content)\n * @param headers - If true, infer from first row; if string[], use provided; if false, omit header row\n * @returns Array with a single written file path\n */\nexport async function writeLargeCsv(\n filePath: string,\n data: ObjByString[],\n headers: boolean | string[] = true,\n): Promise<string[]> {\n // Determine header order\n let headerOrder: string[] | false;\n if (Array.isArray(headers)) {\n headerOrder = headers;\n } else if (headers === true) {\n headerOrder = data.length > 0 ? Object.keys(data[0]) : [];\n } else {\n headerOrder = false;\n }\n\n const ws = createWriteStream(filePath);\n const csvStream = fastcsv.format<ObjByString, ObjByString>({\n headers: headerOrder || undefined,\n objectMode: true,\n });\n\n // Pipe CSV stream into file write stream\n const piping = csvStream.pipe(ws);\n\n const completion = new Promise<void>((resolve, reject) => {\n piping.on('finish', () => resolve());\n piping.on('error', reject);\n csvStream.on('error', reject);\n ws.on('error', reject);\n });\n\n // Stream rows with backpressure handling\n for (const row of data) {\n const toWrite = headerOrder ? rowToValues(row, headerOrder) : row;\n const ok = csvStream.write(toWrite);\n if (!ok) {\n // Respect backpressure: wait until the internal buffer drains\n await waitForDrain(csvStream);\n }\n }\n\n // Signal end of input and wait for finish\n csvStream.end();\n await completion;\n\n return [filePath];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"writeCsv-Da8NUe1V.mjs","names":[],"sources":["../src/lib/helpers/writeCsv.ts"],"sourcesContent":["import { createWriteStream, writeFileSync, appendFileSync } from 'node:fs';\n\nimport { ObjByString } from '@transcend-io/type-utils';\nimport * as fastcsv from 'fast-csv';\n\n/**\n * Escape a CSV value\n *\n * @param value - Value to escape\n * @returns Escaped value\n */\nfunction escapeCsvValue(value: string): string {\n if (value.includes('\"') || value.includes(',') || value.includes('\\n')) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\n/**\n * Write a csv to file synchronously, overwriting any existing content\n *\n * @param filePath - File to write out to\n * @param data - Data to write\n * @param headers - Headers. If true, use object keys as headers. If array, use provided headers.\n */\nexport function writeCsvSync(filePath: string, data: ObjByString[], headers: string[]): void {\n const rows: string[][] = [];\n\n rows.push(headers);\n rows.push(...data.map((row) => Object.values(row)));\n\n // Build CSV content with proper escaping\n const csvContent = rows.map((row) => row.map(escapeCsvValue).join(',')).join('\\n');\n\n // Write to file, overwriting existing content\n writeFileSync(filePath, csvContent);\n}\n\n/**\n * Initialize a CSV file by writing only the header row (or an empty file if no headers).\n *\n * @param filePath - CSV path\n * @param headers - Ordered list of column names; if empty, creates/empties the file\n */\nexport function initCsvFile(filePath: string, headers: string[]): void {\n if (!headers || headers.length === 0) {\n writeFileSync(filePath, '');\n return;\n }\n const headerLine = headers.map(escapeCsvValue).join(',');\n writeFileSync(filePath, `${headerLine}\\n`);\n}\n\n/**\n * Append rows to CSV using an explicit header order (no header line).\n * Values are written in the order of `headerOrder`.\n *\n * @param filePath - CSV path\n * @param data - Row objects\n * @param headerOrder - Column order to apply\n */\nexport function appendCsvRowsOrdered(\n filePath: string,\n data: ObjByString[],\n headerOrder: string[],\n): void {\n if (!data.length) return;\n\n const lines = data.map((row) => {\n const vals = headerOrder.map((key) => {\n const v = row[key];\n return v == null ? '' : String(v);\n });\n return vals.map(escapeCsvValue).join(',');\n });\n\n appendFileSync(filePath, `${lines.join('\\n')}\\n`);\n}\n\n/**\n * Append data to an existing csv file synchronously (legacy, uses Object.values order).\n * Prefer appendCsvRowsOrdered for deterministic column order.\n *\n * @param filePath - File to append to\n * @param data - Data to append\n */\nexport function appendCsvSync(filePath: string, data: ObjByString[]): void {\n // Convert data to CSV rows\n const rows = data.map((row) => Object.values(row));\n\n // Build CSV content with proper escaping\n const csvContent = rows.map((row) => row.map(escapeCsvValue).join(',')).join('\\n');\n\n // Append to file with leading newline\n appendFileSync(filePath, `\\n${csvContent}`);\n}\n\n/**\n * Write a csv to file asynchronously\n *\n * @param filePath - File to write out to\n * @param data - Data to write\n * @param headers - Headers\n */\nexport async function writeCsv(\n filePath: string,\n data: ObjByString[],\n headers: boolean | string[] = true,\n): Promise<void> {\n const ws = createWriteStream(filePath);\n await new Promise<void>((resolve, reject) => {\n try {\n const stream = fastcsv.write(data, { headers, objectMode: true }).on('error', reject);\n\n ws.on('error', reject);\n ws.on('finish', () => resolve());\n\n stream.pipe(ws);\n } catch (err) {\n reject(err);\n }\n });\n}\n\n/**\n * Parse a file path into a base name and extension\n *\n * @param filePath - File path to parse\n * @returns Base name and extension\n */\nexport function parseFilePath(filePath: string): {\n /** Base name of the file */\n baseName: string;\n /** Extension of the file */\n extension: string;\n} {\n const lastDotIndex = filePath.lastIndexOf('.');\n return {\n baseName: lastDotIndex !== -1 ? filePath.substring(0, lastDotIndex) : filePath,\n extension: lastDotIndex !== -1 ? filePath.substring(lastDotIndex) : '.csv',\n };\n}\n\n/**\n * Convert an object row into values aligned to header order\n *\n * @param row - Row object\n * @param headerOrder - Header order\n * @returns Aligned row object\n */\nfunction rowToValues(row: ObjByString, headerOrder: string[]): Record<string, unknown> {\n // fast-csv with objectMode expects objects; we ensure consistent key ordering\n // by building a new object with keys in headerOrder.\n const ordered: Record<string, unknown> = {};\n for (const key of headerOrder) {\n // Preserve undefined -> becomes empty cell in CSV\n ordered[key] = row[key];\n }\n return ordered;\n}\n\n/**\n * Await the 'drain' event when backpressure indicates buffering\n *\n * @param stream - Writable stream\n * @returns Promise that resolves on 'drain'\n */\nfunction waitForDrain(stream: NodeJS.WritableStream): Promise<void> {\n return new Promise((resolve) => {\n stream.once('drain', resolve);\n });\n}\n\n/**\n * Stream a large CSV dataset to a single file with proper backpressure handling.\n * (Kept for completeness; not used by the incremental write path.)\n *\n * @param filePath - File to write out to\n * @param data - Data to write (iterated without buffering the entire file content)\n * @param headers - If true, infer from first row; if string[], use provided; if false, omit header row\n * @returns Array with a single written file path\n */\nexport async function writeLargeCsv(\n filePath: string,\n data: ObjByString[],\n headers: boolean | string[] = true,\n): Promise<string[]> {\n // Determine header order\n let headerOrder: string[] | false;\n if (Array.isArray(headers)) {\n headerOrder = headers;\n } else if (headers === true) {\n headerOrder = data.length > 0 ? Object.keys(data[0]) : [];\n } else {\n headerOrder = false;\n }\n\n const ws = createWriteStream(filePath);\n const csvStream = fastcsv.format<ObjByString, ObjByString>({\n headers: headerOrder || undefined,\n objectMode: true,\n });\n\n // Pipe CSV stream into file write stream\n const piping = csvStream.pipe(ws);\n\n const completion = new Promise<void>((resolve, reject) => {\n piping.on('finish', () => resolve());\n piping.on('error', reject);\n csvStream.on('error', reject);\n ws.on('error', reject);\n });\n\n // Stream rows with backpressure handling\n for (const row of data) {\n const toWrite = headerOrder ? rowToValues(row, headerOrder) : row;\n const ok = csvStream.write(toWrite);\n if (!ok) {\n // Respect backpressure: wait until the internal buffer drains\n await waitForDrain(csvStream);\n }\n }\n\n // Signal end of input and wait for finish\n csvStream.end();\n await completion;\n\n return [filePath];\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transcend-io/cli",
3
- "version": "10.0.1",
3
+ "version": "10.1.0",
4
4
  "description": "A command line interface for programmatic operations across Transcend.",
5
5
  "homepage": "https://github.com/transcend-io/tools/tree/main/packages/cli",
6
6
  "license": "Apache-2.0",
@@ -106,7 +106,9 @@
106
106
  "semver": "^7.6.0",
107
107
  "undici": "^5.22.1",
108
108
  "yargs-parser": "^21.1.1",
109
- "@transcend-io/privacy-types": "5.0.0"
109
+ "@transcend-io/privacy-types": "5.0.1",
110
+ "@transcend-io/sdk": "0.1.0",
111
+ "@transcend-io/utils": "0.1.0"
110
112
  },
111
113
  "devDependencies": {
112
114
  "@arethetypeswrong/cli": "^0.18.2",
@@ -128,22 +130,23 @@
128
130
  "date-fns": "^4.1.0",
129
131
  "doctoc": "^2.2.1",
130
132
  "fdir": "^6.4.6",
131
- "shellcheck": "^3.1.0",
133
+ "publint": "^0.3.18",
132
134
  "tsdown": "^0.21.2",
133
135
  "tsx": "^4.20.3",
134
136
  "typescript": "^5.9.3",
135
137
  "vitest": "^4.0.18"
136
138
  },
137
139
  "engines": {
138
- "node": ">=22.0.0"
140
+ "node": ">=22.12.0"
139
141
  },
140
142
  "scripts": {
141
- "start": "./dist/bin/cli.mjs",
142
143
  "build": "tsdown",
143
144
  "build:watch": "tsdown --watch",
144
- "typecheck": "tsc -p tsconfig.json --noEmit",
145
+ "start": "./dist/bin/cli.mjs",
145
146
  "test": "vitest run",
146
- "check-exports": "attw --pack . --ignore-rules cjs-resolves-to-esm",
147
+ "typecheck": "tsc -p tsconfig.json --noEmit",
148
+ "check:exports": "attw --pack . --ignore-rules cjs-resolves-to-esm",
149
+ "check:publint": "publint --level warning --strict --pack pnpm",
147
150
  "script:transcend-json-schema": "NODE_OPTIONS=\"--conditions=@transcend-io/source\" tsx scripts/buildTranscendJsonSchema.ts && oxfmt --write ./schema/transcend-yml-schema-*.json",
148
151
  "script:pathfinder-json-schema": "NODE_OPTIONS=\"--conditions=@transcend-io/source\" tsx scripts/buildPathfinderJsonSchema.ts && oxfmt --write ./schema/pathfinder-policy-yml-schema.json",
149
152
  "docgen": "NODE_OPTIONS=\"--conditions=@transcend-io/source\" tsx scripts/buildReadmeDocs.ts",
@@ -1,2 +0,0 @@
1
- var e=class{buckets=[];add(e){let t=Date.now();this.buckets.push({t,n:e});let n=t-12e4;for(;this.buckets.length&&this.buckets[0].t<n;)this.buckets.shift()}rate(e){let t=Date.now()-e,n=0;for(let e=this.buckets.length-1;e>=0;--e){let r=this.buckets[e];if(r.t<t)break;n+=r.n}return n/(e/1e3)}};export{e as t};
2
- //# sourceMappingURL=RateCounter-DFL_mnk2.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RateCounter-DFL_mnk2.mjs","names":[],"sources":["../src/lib/helpers/RateCounter.ts"],"sourcesContent":["/**\n * Tracks counts over time and calculates rates within a specified time window.\n *\n * This class maintains a rolling window of count \"buckets\" (timestamped values)\n * and provides methods to add new counts and compute the rate of events over a\n * configurable time window.\n *\n * Example usage:\n * ```typescript\n * const counter = new RateCounter();\n * counter.add(5); // Add 5 events\n * const rate = counter.rate(60_000); // Get rate over last 60 seconds\n * ```\n */\nexport class RateCounter {\n private buckets: Array<{\n /** Timestamp of the bucket */\n t: number;\n /** Number of events in the bucket */\n n: number;\n }> = [];\n\n /**\n * Adds a new count to the counter.\n *\n * @param n - The number of events to add to the counter.\n */\n add(n: number): void {\n const now = Date.now();\n this.buckets.push({ t: now, n });\n // keep last 2 minutes of buckets\n const cutoff = now - 120_000;\n while (this.buckets.length && this.buckets[0].t < cutoff) {\n this.buckets.shift();\n }\n }\n\n /**\n * rate over the last `windowMs` milliseconds\n *\n * @param windowMs - The time window in milliseconds to calculate the rate over.\n * @returns The average rate of events per second over the specified time window.\n */\n rate(windowMs: number): number {\n const now = Date.now();\n const cutoff = now - windowMs;\n let sum = 0;\n for (let i = this.buckets.length - 1; i >= 0; i -= 1) {\n const b = this.buckets[i];\n if (b.t < cutoff) break;\n sum += b.n;\n }\n return sum / (windowMs / 1000);\n }\n}\n"],"mappings":"AAcA,IAAa,EAAb,KAAyB,CACvB,QAKK,EAAE,CAOP,IAAI,EAAiB,CACnB,IAAM,EAAM,KAAK,KAAK,CACtB,KAAK,QAAQ,KAAK,CAAK,EAAK,IAAG,CAAC,CAEhC,IAAM,EAAS,EAAM,KACrB,KAAO,KAAK,QAAQ,QAAU,KAAK,QAAQ,GAAG,EAAI,GAChD,KAAK,QAAQ,OAAO,CAUxB,KAAK,EAA0B,CAE7B,IAAM,EADM,KAAK,KAAK,CACD,EACjB,EAAM,EACV,IAAK,IAAI,EAAI,KAAK,QAAQ,OAAS,EAAG,GAAK,EAAG,IAAQ,CACpD,IAAM,EAAI,KAAK,QAAQ,GACvB,GAAI,EAAE,EAAI,EAAQ,MAClB,GAAO,EAAE,EAEX,OAAO,GAAO,EAAW"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"RequestDataSilo-_Iv44M9u.mjs","names":[],"sources":["../src/lib/graphql/gqls/RequestDataSilo.ts"],"sourcesContent":["import { gql } from 'graphql-request';\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\nexport const REQUEST_DATA_SILOS = gql`\n query TranscendCliRequestDataSilos(\n $first: Int!\n $offset: Int!\n $filterBy: RequestDataSiloFiltersInput!\n ) {\n requestDataSilos(\n filterBy: $filterBy\n first: $first\n offset: $offset\n useMaster: false\n orderBy: [\n { field: createdAt, direction: DESC }\n { field: title, direction: ASC, model: dataSilo }\n ]\n ) {\n nodes {\n id\n }\n totalCount\n }\n }\n`;\n\nexport const CHANGE_REQUEST_DATA_SILO_STATUS = gql`\n mutation TranscendCliMarkRequestDataSiloCompleted(\n $requestDataSiloId: ID!\n $status: UpdateRequestDataSiloStatus!\n ) {\n changeRequestDataSiloStatus(input: { id: $requestDataSiloId, status: $status }) {\n requestDataSilo {\n id\n }\n }\n }\n`;\n\nexport const RETRY_REQUEST_DATA_SILO = gql`\n mutation TranscendCliRetryRequestDataSilo($requestDataSiloId: ID!) {\n retryRequestDataSilo(id: $requestDataSiloId) {\n requestDataSilo {\n id\n }\n }\n }\n`;\n\n// TODO: https://transcend.height.app/T-27909 - enable optimizations\n// isExportCsv: true\n// useMaster: false\n// orderBy: [\n// { field: createdAt, direction: DESC }\n// { field: title, direction: ASC, model: dataSilo }\n// ]\nexport const REDUCED_REQUESTS_FOR_DATA_SILO_COUNT = gql`\n query TranscendCliListReducedRequestsForDataSiloCount(\n $input: BulkCompletionReducedRequestInput!\n ) {\n listReducedRequestsForDataSilo(input: $input) {\n totalCount\n }\n }\n`;\n"],"mappings":"sCAIA,MAAa,EAAqB,CAAG;;;;;;;;;;;;;;;;;;;;;;EAwBxB,EAAkC,CAAG;;;;;;;;;;;EAarC,EAA0B,CAAG;;;;;;;;EAiB7B,EAAuC,CAAG"}
@@ -1,2 +0,0 @@
1
- import{a as e}from"./constants-CeMiHaHx.mjs";import{t}from"./logger-B-LXIf3U.mjs";import{t as n}from"./bluebird-CUitXgsY.mjs";import{o as r,t as i}from"./request-CAsR6CMY.mjs";import{r as a,t as o}from"./makeGraphQLRequest-Cq26A_Lq.mjs";import{r as s}from"./fetchAllRequests-DNQQsY4s.mjs";import{RequestStatus as c}from"@transcend-io/privacy-types";import l from"colors";import u from"cli-progress";async function d({requestActions:d,requestOrigins:f,auth:p,silentModeBefore:m,createdAtAfter:h,createdAtBefore:g,updatedAtBefore:_,updatedAtAfter:v,concurrency:y=50,transcendUrl:b=e}){let x=a(b,p),S=new Date().getTime(),C=new u.SingleBar({},u.Presets.shades_classic),w=await s(x,{actions:d,statuses:[c.Approving],createdAtAfter:h,createdAtBefore:g,updatedAtBefore:_,updatedAtAfter:v,origins:f});t.info(l.magenta(`Approving "${w.length}" requests.`));let T=0,E=0;C.start(w.length,0),await n(w,async e=>{m&&new Date(m)>new Date(e.createdAt)&&await o(x,r,{input:{id:e.id,isSilent:!0}});try{await o(x,i,{input:{requestId:e.id}})}catch(e){e.message.includes(`Request must be in an approving state,`)&&(E+=1)}T+=1,C.update(T)},{concurrency:y}),C.stop();let D=new Date().getTime()-S;return E>0&&t.info(l.yellow(`${E} requests were skipped.`)),t.info(l.green(`Successfully approved ${T} requests in "${D/1e3}" seconds!`)),w.length}export{d as t};
2
- //# sourceMappingURL=approvePrivacyRequests-CWGZR2N6.mjs.map