@transcend-io/cli 13.0.5 → 13.0.6

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 (156) hide show
  1. package/dist/{app-DQ5b5mys.mjs → app-CgHCDGve.mjs} +16 -16
  2. package/dist/{app-DQ5b5mys.mjs.map → app-CgHCDGve.mjs.map} +1 -1
  3. package/dist/{approvePrivacyRequests-O70eyhdH.mjs → approvePrivacyRequests-BrnJ-CVF.mjs} +2 -2
  4. package/dist/{approvePrivacyRequests-O70eyhdH.mjs.map → approvePrivacyRequests-BrnJ-CVF.mjs.map} +1 -1
  5. package/dist/bin/bash-complete.mjs +1 -1
  6. package/dist/bin/cli.mjs +1 -1
  7. package/dist/bin/deprecated-command.mjs +1 -1
  8. package/dist/{buildExamples-BsMXuFwm.mjs → buildExamples-CnR3Kr1g.mjs} +2 -2
  9. package/dist/{buildExamples-BsMXuFwm.mjs.map → buildExamples-CnR3Kr1g.mjs.map} +1 -1
  10. package/dist/{buildXdiSyncEndpoint-CRJSuwdq.mjs → buildXdiSyncEndpoint-C5d116vB.mjs} +2 -2
  11. package/dist/{buildXdiSyncEndpoint-CRJSuwdq.mjs.map → buildXdiSyncEndpoint-C5d116vB.mjs.map} +1 -1
  12. package/dist/{bulkRestartRequests-C9cHasoq.mjs → bulkRestartRequests-C3cbvO42.mjs} +2 -2
  13. package/dist/bulkRestartRequests-C3cbvO42.mjs.map +1 -0
  14. package/dist/{bulkRetryEnrichers-BLDtr5Ws.mjs → bulkRetryEnrichers-C0Z-5oAG.mjs} +2 -2
  15. package/dist/{bulkRetryEnrichers-BLDtr5Ws.mjs.map → bulkRetryEnrichers-C0Z-5oAG.mjs.map} +1 -1
  16. package/dist/{cancelPrivacyRequests-DSjAUS5b.mjs → cancelPrivacyRequests-i7HTC4_n.mjs} +2 -2
  17. package/dist/{cancelPrivacyRequests-DSjAUS5b.mjs.map → cancelPrivacyRequests-i7HTC4_n.mjs.map} +1 -1
  18. package/dist/{command-DB0ydG0y.mjs → command-hkvdh57O.mjs} +2 -2
  19. package/dist/{command-DB0ydG0y.mjs.map → command-hkvdh57O.mjs.map} +1 -1
  20. package/dist/{constants-ZX8RSZos.mjs → constants-CusZYsjT.mjs} +2 -2
  21. package/dist/{constants-ZX8RSZos.mjs.map → constants-CusZYsjT.mjs.map} +1 -1
  22. package/dist/{createPoolingCommandUi-Bj6pO1t7.mjs → createPoolingCommandUi-C6uf3bzG.mjs} +2 -2
  23. package/dist/{createPoolingCommandUi-Bj6pO1t7.mjs.map → createPoolingCommandUi-C6uf3bzG.mjs.map} +1 -1
  24. package/dist/{downloadPrivacyRequestFiles-DKRfQoAo.mjs → downloadPrivacyRequestFiles-Do8cKTNC.mjs} +2 -2
  25. package/dist/downloadPrivacyRequestFiles-Do8cKTNC.mjs.map +1 -0
  26. package/dist/extractClientError-BQgmnp3e.mjs.map +1 -1
  27. package/dist/{generateCrossAccountApiKeys-BnUQ_wCI.mjs → generateCrossAccountApiKeys-D3EzSJV7.mjs} +2 -2
  28. package/dist/{generateCrossAccountApiKeys-BnUQ_wCI.mjs.map → generateCrossAccountApiKeys-D3EzSJV7.mjs.map} +1 -1
  29. package/dist/{impl-DpLBWRCJ.mjs → impl-8kZN8w0q.mjs} +2 -2
  30. package/dist/{impl-DpLBWRCJ.mjs.map → impl-8kZN8w0q.mjs.map} +1 -1
  31. package/dist/{impl-DgMukyER.mjs → impl-B5CqfbCN.mjs} +2 -2
  32. package/dist/{impl-DgMukyER.mjs.map → impl-B5CqfbCN.mjs.map} +1 -1
  33. package/dist/{impl-IQy1_4me.mjs → impl-B964vhTe.mjs} +2 -2
  34. package/dist/{impl-IQy1_4me.mjs.map → impl-B964vhTe.mjs.map} +1 -1
  35. package/dist/{impl-CWykfeDx.mjs → impl-BUbwefXr.mjs} +2 -2
  36. package/dist/{impl-CWykfeDx.mjs.map → impl-BUbwefXr.mjs.map} +1 -1
  37. package/dist/{impl-CihGggtZ.mjs → impl-BY1HzPeX.mjs} +2 -2
  38. package/dist/{impl-CihGggtZ.mjs.map → impl-BY1HzPeX.mjs.map} +1 -1
  39. package/dist/{impl-COz--q8S.mjs → impl-BiQ9lDze.mjs} +2 -2
  40. package/dist/{impl-COz--q8S.mjs.map → impl-BiQ9lDze.mjs.map} +1 -1
  41. package/dist/{impl-CDB2WA0G.mjs → impl-C7iC7S5D.mjs} +2 -2
  42. package/dist/{impl-CDB2WA0G.mjs.map → impl-C7iC7S5D.mjs.map} +1 -1
  43. package/dist/{impl-DEK7xark.mjs → impl-C9JlWA7i.mjs} +2 -2
  44. package/dist/{impl-DEK7xark.mjs.map → impl-C9JlWA7i.mjs.map} +1 -1
  45. package/dist/{impl-CaPpiEL2.mjs → impl-CALzjhEc.mjs} +2 -2
  46. package/dist/{impl-CaPpiEL2.mjs.map → impl-CALzjhEc.mjs.map} +1 -1
  47. package/dist/{impl-DHq2UBl4.mjs → impl-CBLQwa8E.mjs} +2 -2
  48. package/dist/{impl-DHq2UBl4.mjs.map → impl-CBLQwa8E.mjs.map} +1 -1
  49. package/dist/{impl-BUMhJG9i.mjs → impl-CFPN8z3c.mjs} +2 -2
  50. package/dist/{impl-BUMhJG9i.mjs.map → impl-CFPN8z3c.mjs.map} +1 -1
  51. package/dist/{impl-BLX_G5bC.mjs → impl-COD03bOu.mjs} +2 -2
  52. package/dist/{impl-BLX_G5bC.mjs.map → impl-COD03bOu.mjs.map} +1 -1
  53. package/dist/{impl-BOVkmW_l.mjs → impl-COpuW67i.mjs} +2 -2
  54. package/dist/{impl-BOVkmW_l.mjs.map → impl-COpuW67i.mjs.map} +1 -1
  55. package/dist/{impl-B8sKYzjF2.mjs → impl-CVjaQ_-Y2.mjs} +2 -2
  56. package/dist/{impl-B8sKYzjF2.mjs.map → impl-CVjaQ_-Y2.mjs.map} +1 -1
  57. package/dist/{impl-bWws4YDq.mjs → impl-Cm_hCZ5h.mjs} +2 -2
  58. package/dist/{impl-bWws4YDq.mjs.map → impl-Cm_hCZ5h.mjs.map} +1 -1
  59. package/dist/impl-CoqCyj7e.mjs.map +1 -1
  60. package/dist/{impl-CFza6vNy.mjs → impl-CuII9KiJ.mjs} +2 -2
  61. package/dist/{impl-CFza6vNy.mjs.map → impl-CuII9KiJ.mjs.map} +1 -1
  62. package/dist/{impl-t0qVrVte.mjs → impl-D-1QAfLV.mjs} +2 -2
  63. package/dist/{impl-t0qVrVte.mjs.map → impl-D-1QAfLV.mjs.map} +1 -1
  64. package/dist/{impl-CeR4wR2T.mjs → impl-D8KUHOU8.mjs} +2 -2
  65. package/dist/{impl-CeR4wR2T.mjs.map → impl-D8KUHOU8.mjs.map} +1 -1
  66. package/dist/{impl-fmS5PIlk.mjs → impl-DAlYoIsj.mjs} +2 -2
  67. package/dist/{impl-fmS5PIlk.mjs.map → impl-DAlYoIsj.mjs.map} +1 -1
  68. package/dist/{impl-BAd2ga54.mjs → impl-DHTAxvp8.mjs} +2 -2
  69. package/dist/{impl-BAd2ga54.mjs.map → impl-DHTAxvp8.mjs.map} +1 -1
  70. package/dist/{impl-C7oKqTBj.mjs → impl-DLaSUiZ7.mjs} +2 -2
  71. package/dist/{impl-C7oKqTBj.mjs.map → impl-DLaSUiZ7.mjs.map} +1 -1
  72. package/dist/{impl-77sXwCIh.mjs → impl-DQbP4dBX.mjs} +2 -2
  73. package/dist/{impl-77sXwCIh.mjs.map → impl-DQbP4dBX.mjs.map} +1 -1
  74. package/dist/{impl-BUziAyU5.mjs → impl-DWmQ_15Y.mjs} +2 -2
  75. package/dist/{impl-BUziAyU5.mjs.map → impl-DWmQ_15Y.mjs.map} +1 -1
  76. package/dist/{impl-Bizz-Eku.mjs → impl-D_03KeBQ.mjs} +2 -2
  77. package/dist/{impl-Bizz-Eku.mjs.map → impl-D_03KeBQ.mjs.map} +1 -1
  78. package/dist/{impl-CHq5HbuZ2.mjs → impl-DbTZmp_W2.mjs} +2 -2
  79. package/dist/{impl-CHq5HbuZ2.mjs.map → impl-DbTZmp_W2.mjs.map} +1 -1
  80. package/dist/{impl-XbrQy_Ti2.mjs → impl-Dd60oodD2.mjs} +2 -2
  81. package/dist/{impl-XbrQy_Ti2.mjs.map → impl-Dd60oodD2.mjs.map} +1 -1
  82. package/dist/{impl-B1TF4tzM2.mjs → impl-De1JItpM2.mjs} +2 -2
  83. package/dist/{impl-B1TF4tzM2.mjs.map → impl-De1JItpM2.mjs.map} +1 -1
  84. package/dist/{impl-DJ5kJ80R.mjs → impl-DnPaAp-d.mjs} +2 -2
  85. package/dist/{impl-DJ5kJ80R.mjs.map → impl-DnPaAp-d.mjs.map} +1 -1
  86. package/dist/{impl-CcVNbgw3.mjs → impl-DvLO4Qn3.mjs} +2 -2
  87. package/dist/{impl-CcVNbgw3.mjs.map → impl-DvLO4Qn3.mjs.map} +1 -1
  88. package/dist/{impl-B7PA6_OB.mjs → impl-Dw-MR5nR.mjs} +2 -2
  89. package/dist/{impl-B7PA6_OB.mjs.map → impl-Dw-MR5nR.mjs.map} +1 -1
  90. package/dist/{impl-DQftQKzh.mjs → impl-DwBrrrR3.mjs} +2 -2
  91. package/dist/{impl-DQftQKzh.mjs.map → impl-DwBrrrR3.mjs.map} +1 -1
  92. package/dist/{impl-B_5zE1r4.mjs → impl-DwGZvFYI.mjs} +2 -2
  93. package/dist/{impl-B_5zE1r4.mjs.map → impl-DwGZvFYI.mjs.map} +1 -1
  94. package/dist/{impl-tQda5dQ2.mjs → impl-F0lRLx7N.mjs} +2 -2
  95. package/dist/{impl-tQda5dQ2.mjs.map → impl-F0lRLx7N.mjs.map} +1 -1
  96. package/dist/impl-HsRwcfRu.mjs.map +1 -1
  97. package/dist/{impl-Do_3rQWz.mjs → impl-L-TZRGF5.mjs} +2 -2
  98. package/dist/{impl-Do_3rQWz.mjs.map → impl-L-TZRGF5.mjs.map} +1 -1
  99. package/dist/{impl-DRmy2-be.mjs → impl-Q8a16IkZ.mjs} +2 -2
  100. package/dist/{impl-DRmy2-be.mjs.map → impl-Q8a16IkZ.mjs.map} +1 -1
  101. package/dist/{impl-CIEPBjFF.mjs → impl-S3znb469.mjs} +2 -2
  102. package/dist/{impl-CIEPBjFF.mjs.map → impl-S3znb469.mjs.map} +1 -1
  103. package/dist/{impl-CPW2Q51W2.mjs → impl-UlLFb3UK2.mjs} +2 -2
  104. package/dist/{impl-CPW2Q51W2.mjs.map → impl-UlLFb3UK2.mjs.map} +1 -1
  105. package/dist/impl-WJBuS_0m.mjs.map +1 -1
  106. package/dist/{impl-CXOLmyC1.mjs → impl-wymd_Y3k.mjs} +2 -2
  107. package/dist/{impl-CXOLmyC1.mjs.map → impl-wymd_Y3k.mjs.map} +1 -1
  108. package/dist/{impl-5s_Ya3XD.mjs → impl-zH16n-0l.mjs} +2 -2
  109. package/dist/{impl-5s_Ya3XD.mjs.map → impl-zH16n-0l.mjs.map} +1 -1
  110. package/dist/index.d.mts +7 -7
  111. package/dist/index.d.mts.map +1 -1
  112. package/dist/index.mjs +1 -1
  113. package/dist/{markRequestDataSiloIdsCompleted-ZZvKQhtT.mjs → markRequestDataSiloIdsCompleted-CJdC2ta9.mjs} +2 -2
  114. package/dist/{markRequestDataSiloIdsCompleted-ZZvKQhtT.mjs.map → markRequestDataSiloIdsCompleted-CJdC2ta9.mjs.map} +1 -1
  115. package/dist/{markSilentPrivacyRequests-DZ13Jy20.mjs → markSilentPrivacyRequests-CIjtVoXr.mjs} +2 -2
  116. package/dist/{markSilentPrivacyRequests-DZ13Jy20.mjs.map → markSilentPrivacyRequests-CIjtVoXr.mjs.map} +1 -1
  117. package/dist/{notifyPrivacyRequestsAdditionalTime-BspmtrPe.mjs → notifyPrivacyRequestsAdditionalTime-DsjlAlh8.mjs} +2 -2
  118. package/dist/{notifyPrivacyRequestsAdditionalTime-BspmtrPe.mjs.map → notifyPrivacyRequestsAdditionalTime-DsjlAlh8.mjs.map} +1 -1
  119. package/dist/{policy-bundle-manifest-BKRoT17e.mjs → policy-bundle-manifest-xaiuhXv5.mjs} +2 -2
  120. package/dist/{policy-bundle-manifest-BKRoT17e.mjs.map → policy-bundle-manifest-xaiuhXv5.mjs.map} +1 -1
  121. package/dist/{policy-project-discovery-BXcvhfK7.mjs → policy-project-discovery-Dxikd8NT.mjs} +2 -2
  122. package/dist/{policy-project-discovery-BXcvhfK7.mjs.map → policy-project-discovery-Dxikd8NT.mjs.map} +1 -1
  123. package/dist/{policy-scaffold-config-DvyJctQz.mjs → policy-scaffold-config-DRWTaw0k.mjs} +2 -2
  124. package/dist/{policy-scaffold-config-DvyJctQz.mjs.map → policy-scaffold-config-DRWTaw0k.mjs.map} +1 -1
  125. package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-CDHCpiXU.mjs → pullChunkedCustomSiloOutstandingIdentifiers-DbNvJJrP.mjs} +2 -2
  126. package/dist/pullChunkedCustomSiloOutstandingIdentifiers-DbNvJJrP.mjs.map +1 -0
  127. package/dist/{pullManualEnrichmentIdentifiersToCsv-BSM4bSjf.mjs → pullManualEnrichmentIdentifiersToCsv-3hX7b2sn.mjs} +2 -2
  128. package/dist/{pullManualEnrichmentIdentifiersToCsv-BSM4bSjf.mjs.map → pullManualEnrichmentIdentifiersToCsv-3hX7b2sn.mjs.map} +1 -1
  129. package/dist/{pushCronIdentifiersFromCsv-CnMWjt94.mjs → pushCronIdentifiersFromCsv-DSsDe8er.mjs} +2 -2
  130. package/dist/pushCronIdentifiersFromCsv-DSsDe8er.mjs.map +1 -0
  131. package/dist/{pushManualEnrichmentIdentifiersFromCsv-iv9Ts42q.mjs → pushManualEnrichmentIdentifiersFromCsv-B4NWrYLP.mjs} +2 -2
  132. package/dist/pushManualEnrichmentIdentifiersFromCsv-B4NWrYLP.mjs.map +1 -0
  133. package/dist/{removeUnverifiedRequestIdentifiers-Bo5U01oM.mjs → removeUnverifiedRequestIdentifiers-eub1buew.mjs} +2 -2
  134. package/dist/{removeUnverifiedRequestIdentifiers-Bo5U01oM.mjs.map → removeUnverifiedRequestIdentifiers-eub1buew.mjs.map} +1 -1
  135. package/dist/resolveBundleIdByName-D7NUOpLc.mjs.map +1 -1
  136. package/dist/resolvePolicyBundleVersion-CKbF9vqc.mjs.map +1 -1
  137. package/dist/{retryRequestDataSilos-BWGlpn1o.mjs → retryRequestDataSilos-Bz2krPpO.mjs} +2 -2
  138. package/dist/{retryRequestDataSilos-BWGlpn1o.mjs.map → retryRequestDataSilos-Bz2krPpO.mjs.map} +1 -1
  139. package/dist/{skipPreflightJobs-B0LSm9IU.mjs → skipPreflightJobs-DnMtLNHR.mjs} +2 -2
  140. package/dist/{skipPreflightJobs-B0LSm9IU.mjs.map → skipPreflightJobs-DnMtLNHR.mjs.map} +1 -1
  141. package/dist/{skipRequestDataSilos-EEaly2ls.mjs → skipRequestDataSilos-hFvX-tmq.mjs} +2 -2
  142. package/dist/{skipRequestDataSilos-EEaly2ls.mjs.map → skipRequestDataSilos-hFvX-tmq.mjs.map} +1 -1
  143. package/dist/{streamPrivacyRequestsToCsv-DUFu0-AB.mjs → streamPrivacyRequestsToCsv-1D5tgLIt.mjs} +2 -2
  144. package/dist/{streamPrivacyRequestsToCsv-DUFu0-AB.mjs.map → streamPrivacyRequestsToCsv-1D5tgLIt.mjs.map} +1 -1
  145. package/dist/{updateConsentManagerVersionToLatest-C4D6cqMh.mjs → updateConsentManagerVersionToLatest-DYvkq9Lr.mjs} +2 -2
  146. package/dist/{updateConsentManagerVersionToLatest-C4D6cqMh.mjs.map → updateConsentManagerVersionToLatest-DYvkq9Lr.mjs.map} +1 -1
  147. package/dist/{uploadConsents-CMTmdRLm.mjs → uploadConsents-BIt4fC8B.mjs} +2 -2
  148. package/dist/{uploadConsents-CMTmdRLm.mjs.map → uploadConsents-BIt4fC8B.mjs.map} +1 -1
  149. package/dist/{uploadPrivacyRequestsFromCsv-BIe_wJ4A.mjs → uploadPrivacyRequestsFromCsv-Df79Xp4b.mjs} +2 -2
  150. package/dist/{uploadPrivacyRequestsFromCsv-BIe_wJ4A.mjs.map → uploadPrivacyRequestsFromCsv-Df79Xp4b.mjs.map} +1 -1
  151. package/package.json +4 -4
  152. package/dist/bulkRestartRequests-C9cHasoq.mjs.map +0 -1
  153. package/dist/downloadPrivacyRequestFiles-DKRfQoAo.mjs.map +0 -1
  154. package/dist/pullChunkedCustomSiloOutstandingIdentifiers-CDHCpiXU.mjs.map +0 -1
  155. package/dist/pushCronIdentifiersFromCsv-CnMWjt94.mjs.map +0 -1
  156. package/dist/pushManualEnrichmentIdentifiersFromCsv-iv9Ts42q.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"bulkRestartRequests-C9cHasoq.mjs","names":[],"sources":["../src/lib/requests/restartPrivacyRequest.ts","../src/lib/requests/bulkRestartRequests.ts"],"sourcesContent":["import { IdentifierType, RestartIdentifierStrategy } from '@transcend-io/privacy-types';\nimport type { RequestIdentifier } from '@transcend-io/sdk';\nimport { apply, decodeCodec } from '@transcend-io/type-utils';\nimport type { Got } from 'got';\nimport * as t from 'io-ts';\nimport { groupBy } from 'lodash-es';\n\nimport type { PrivacyRequest } from '../graphql/index.js';\nimport { IDENTIFIER_BLOCK_LIST } from './constants.js';\nimport { PrivacyRequestResponse } from './submitPrivacyRequest.js';\n\n/**\n * Restart a privacy request to the Transcend API\n *\n * @param sombra - Sombra instance configured to make requests\n * @param request - Request to restart\n * @param input - Request input\n * @returns Successfully submitted request\n */\nexport async function restartPrivacyRequest(\n sombra: Got,\n request: PrivacyRequest,\n {\n sendEmailReceipt = false,\n skipWaitingPeriod = false,\n emailIsVerified = true,\n requestIdentifiers = [],\n restartIdentifierStrategy,\n }: {\n /** List of request identifiers to include */\n requestIdentifiers?: RequestIdentifier[];\n /** When true, send an email receipt to data subject */\n sendEmailReceipt?: boolean;\n /** Whether the email is verified */\n emailIsVerified?: boolean;\n /** Whether to skip waiting period */\n skipWaitingPeriod?: boolean;\n /** How request identifiers should be handled when restarting */\n restartIdentifierStrategy?: RestartIdentifierStrategy;\n } = {},\n): Promise<PrivacyRequestResponse> {\n // Make the GraphQL request\n const response = await sombra\n .post('v1/data-subject-request', {\n json: {\n type: request.type,\n subject: {\n coreIdentifier: request.coreIdentifier,\n email: request.email,\n emailIsVerified,\n ...(requestIdentifiers.length > 0\n ? {\n attestedExtraIdentifiers: apply(\n groupBy(\n requestIdentifiers\n .filter(\n (ri) =>\n // these are already submitted above\n !(ri.name === 'email' && ri.value === request.email) &&\n !IDENTIFIER_BLOCK_LIST.includes(ri.name),\n )\n .map((ri) => ({\n ...ri,\n type: Object.values(IdentifierType).includes(\n ri.name as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n )\n ? ri.name\n : IdentifierType.Custom,\n })),\n 'type',\n ),\n (values, type) =>\n values.map(({ name, value }) => ({\n ...(type === IdentifierType.Custom ? { name } : {}),\n value,\n })),\n ),\n }\n : {}),\n },\n requestId: request.id,\n subjectType: request.subjectType,\n isSilent: request.isSilent,\n isTest: request.isTest,\n locale: request.locale,\n skipWaitingPeriod,\n createdAt: request.createdAt,\n details: `Restarted by Transcend cli: \"tr-request-restart\" - ${request.details}`,\n skipSendingReceipt: !sendEmailReceipt,\n ...(restartIdentifierStrategy ? { restartIdentifierStrategy } : {}),\n },\n })\n .json();\n\n const { request: requestResponse } = decodeCodec(\n t.type({\n request: PrivacyRequestResponse,\n }),\n response,\n );\n return requestResponse;\n}\n","import { join, resolve } from 'node:path';\n\nimport { PersistedState } from '@transcend-io/persisted-state';\nimport {\n RequestAction,\n RequestStatus,\n RestartIdentifierStrategy,\n} from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n createSombraGotInstance,\n fetchAllRequestIdentifiers,\n validateSombraVersion,\n} from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport * as t from 'io-ts';\nimport { difference } from 'lodash-es';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchAllRequests } from '../graphql/index.js';\nimport { SuccessfulRequest } from './constants.js';\nimport { extractClientError } from './extractClientError.js';\nimport { restartPrivacyRequest } from './restartPrivacyRequest.js';\n\n/** Minimal state we need to keep a list of requests */\nconst ErrorRequest = t.intersection([\n SuccessfulRequest,\n t.type({\n error: t.string,\n }),\n]);\n\n/** Type override */\ntype ErrorRequest = t.TypeOf<typeof ErrorRequest>;\n\n/** Persist this data between runs of the script */\nconst CachedRequestState = t.type({\n restartedRequests: t.array(SuccessfulRequest),\n failingRequests: t.array(ErrorRequest),\n});\n\n/**\n * Upload a set of privacy requests from CSV\n *\n * @param options - Options\n */\nexport async function bulkRestartRequests({\n requestReceiptFolder,\n auth,\n sombraAuth,\n requestActions,\n requestStatuses,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n transcendUrl = DEFAULT_TRANSCEND_API,\n requestIds = [],\n createdAt = new Date(),\n silentModeBefore,\n sendEmailReceipt = false,\n emailIsVerified = true,\n copyIdentifiers = false,\n restartIdentifierStrategy,\n skipWaitingPeriod = false,\n concurrency = 20,\n}: {\n /** Actions to filter for */\n requestActions: RequestAction[];\n /** Statues to filter for */\n requestStatuses: RequestStatus[];\n /** File where request receipts are stored */\n requestReceiptFolder: string;\n /** Transcend API key authentication */\n auth: string;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** Request IDs to filter for */\n requestIds?: string[];\n /** Whether to re-verify the email when restarting the request */\n emailIsVerified?: boolean;\n /** Filter for requests that were submitted before this date */\n createdAt?: Date;\n /** Requests that have been open for this length of time should be marked as silent mode */\n silentModeBefore?: Date;\n /** Send an email receipt to the restarted requests */\n sendEmailReceipt?: boolean;\n /** Copy over all identifiers rather than restarting the request only with the core identifier */\n copyIdentifiers?: boolean;\n /** How request identifiers should be handled when restarting */\n restartIdentifierStrategy?: RestartIdentifierStrategy;\n /** Skip the waiting period when restarting requests */\n skipWaitingPeriod?: boolean;\n /** Filter for requests created before this date */\n createdAtBefore?: Date;\n /** Filter for requests created after this date */\n createdAtAfter?: Date;\n /** Filter for requests updated before this date */\n updatedAtBefore?: Date;\n /** Filter for requests updated after this date */\n updatedAtAfter?: Date;\n /** Concurrency to upload requests at */\n concurrency?: number;\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 // Create a new state file to store the requests from this run.\n // `toISOString()` contains colons (e.g. 2026-07-06T04:33:12.345Z) which are\n // illegal characters in Windows filenames, so strip them out to keep the\n // auto-generated receipt filename cross-platform.\n const cacheFile = join(\n requestReceiptFolder,\n `tr-request-restart-${new Date().toISOString().replace(/:/g, '-')}.json`,\n );\n const state = new PersistedState(cacheFile, CachedRequestState, {\n restartedRequests: [],\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 // Find all requests made before createdAt that are in a removing data state\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n const allRequests = await fetchAllRequests(client, {\n requestIds,\n actions: requestActions,\n statuses: requestStatuses,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n });\n const requests = allRequests.filter((request) => new Date(request.createdAt) < createdAt);\n logger.info(`Found ${requests.length} requests to restart`);\n\n if (copyIdentifiers) {\n logger.info('copyIdentifiers detected - All Identifiers will be copied.');\n }\n if (restartIdentifierStrategy) {\n logger.info(\n `restartIdentifierStrategy detected - Using \"${restartIdentifierStrategy}\" strategy.`,\n );\n }\n if (sendEmailReceipt) {\n logger.info('sendEmailReceipt detected - Email receipts will be sent.');\n }\n if (skipWaitingPeriod) {\n logger.info('skipWaitingPeriod detected - Waiting period will be skipped.');\n }\n\n // Validate request IDs\n if (requestIds.length > 0 && requestIds.length !== requests.length) {\n const missingRequests = difference(\n requestIds,\n requests.map(({ id }) => id),\n );\n if (missingRequests.length > 0) {\n logger.error(\n colors.red(`Failed to find the following requests by ID: ${missingRequests.join(',')}.`),\n );\n process.exit(1);\n }\n }\n\n if (copyIdentifiers) {\n await validateSombraVersion(client, { logger });\n }\n\n // Map over the requests\n let total = 0;\n progressBar.start(requests.length, 0);\n await map(\n requests,\n async (request, ind) => {\n try {\n // Pull the request identifiers\n const requestIdentifiers = copyIdentifiers\n ? await fetchAllRequestIdentifiers(client, sombra, {\n filterBy: { requestId: request.id },\n skipSombraCheck: true,\n logger,\n })\n : [];\n\n // Make the GraphQL request to restart the request\n const requestResponse = await restartPrivacyRequest(\n sombra,\n {\n ...request,\n // override silent mode\n isSilent:\n !!silentModeBefore && new Date(request.createdAt) < silentModeBefore\n ? true\n : request.isSilent,\n },\n {\n requestIdentifiers,\n skipWaitingPeriod,\n sendEmailReceipt,\n emailIsVerified,\n restartIdentifierStrategy,\n },\n );\n\n // Cache successful upload\n const restartedRequests = state.getValue('restartedRequests');\n restartedRequests.push({\n id: requestResponse.id,\n link: requestResponse.link,\n rowIndex: ind,\n coreIdentifier: requestResponse.coreIdentifier,\n attemptedAt: new Date().toISOString(),\n });\n await state.setValue(restartedRequests, 'restartedRequests');\n } catch (err) {\n const msg = `${err.message} - ${JSON.stringify(err.response?.body, null, 2)}`;\n const clientError = extractClientError(msg);\n\n const failingRequests = state.getValue('failingRequests');\n failingRequests.push({\n id: request.id,\n link: request.link,\n rowIndex: ind,\n coreIdentifier: request.coreIdentifier,\n attemptedAt: new Date().toISOString(),\n error: clientError || msg,\n });\n await state.setValue(failingRequests, 'failingRequests');\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 // Log completion time\n logger.info(colors.green(`Completed restarting of requests in \"${totalTime / 1000}\" seconds.`));\n\n // Log errors\n if (state.getValue('failingRequests').length > 0) {\n logger.error(\n colors.red(\n `Encountered \"${state.getValue('failingRequests').length}\" errors. ` +\n `See \"${resolve(cacheFile)}\" to review the error messages and inputs.`,\n ),\n );\n process.exit(1);\n }\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"downloadPrivacyRequestFiles-DKRfQoAo.mjs","names":[],"sources":["../src/lib/requests/getFileMetadataForPrivacyRequests.ts","../src/lib/requests/streamPrivacyRequestFiles.ts","../src/lib/requests/downloadPrivacyRequestFiles.ts"],"sourcesContent":["import { TableEncryptionType } from '@transcend-io/privacy-types';\nimport { decodeCodec, valuesOf } from '@transcend-io/type-utils';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport type { Got } from 'got';\nimport * as t from 'io-ts';\n\nimport { logger } from '../../logger.js';\nimport { PrivacyRequest } from '../graphql/index.js';\n\nexport const IntlMessage = t.type({\n /** The message key */\n defaultMessage: t.string,\n /** ID */\n id: t.string,\n});\n\n/** Type */\nexport type IntlMessage = t.TypeOf<typeof IntlMessage>;\n\nexport const RequestFileMetadata = t.type({\n /** The key to pass to download the file contents */\n downloadKey: t.string,\n /** Error message related to file */\n error: t.union([t.null, t.string]),\n /** Mimetype of file */\n mimetype: t.string,\n /** Size of file, stored as string as this can be a BigInt */\n size: t.string,\n /** Name of file based on datapoint names in Transcend */\n fileName: t.string,\n /** The metadata on the datapoint */\n dataPoint: t.type({\n /** ID of datapoint */\n id: t.string,\n /** The title of datapoint */\n title: t.union([IntlMessage, t.null]),\n /** Description of datapoint */\n description: t.union([IntlMessage, t.null]),\n /** Name of datapoint */\n name: t.string,\n /** Slug of datapoint */\n slug: t.string,\n /** Table level encryption information */\n encryption: t.union([valuesOf(TableEncryptionType), t.null]),\n /** The name of the data silo */\n dataSilo: t.type({\n /** ID of the data silo */\n id: t.string,\n /** The title of the data silo */\n title: t.string,\n /** The description of the data silo */\n description: t.string,\n /** The type of the data silo */\n type: t.string,\n /** The outer type of the data silo */\n outerType: t.union([t.string, t.null]),\n }),\n /** The path to the datapoint if a database (e.g. name of schema) */\n path: t.array(t.string),\n }),\n});\n\n/** Type override */\nexport type RequestFileMetadata = t.TypeOf<typeof RequestFileMetadata>;\n\nexport const RequestFileMetadataResponse = t.type({\n /** The list of file metadata */\n nodes: t.array(RequestFileMetadata),\n /** The total number of file metadata */\n totalCount: t.number,\n /** Links to next pages */\n _links: t.partial({\n /** The link to the next page of file metadata */\n next: t.union([t.string, t.null]),\n /** The link to the previous page of file metadata */\n previous: t.union([t.string, t.null]),\n }),\n});\n\n/** Type override */\nexport type RequestFileMetadataResponse = t.TypeOf<typeof RequestFileMetadataResponse>;\n\n/**\n * Given a list of privacy requests, download the file metadata\n * for these requests - this is useful to prepare the files in a\n * data access request for download.\n *\n * @param requests - The list of privacy requests to download files for\n * @param options - Options\n * @returns The number of requests canceled\n */\nexport async function getFileMetadataForPrivacyRequests(\n requests: Pick<PrivacyRequest, 'id' | 'status'>[],\n {\n sombra,\n concurrency = 5,\n limit = 100,\n }: {\n /** Sombra instance */\n sombra: Got;\n /** Number of files to pull at once */\n limit?: number;\n /** Concurrency limit for approving */\n concurrency?: number;\n },\n): Promise<[Pick<PrivacyRequest, 'id' | 'status'>, RequestFileMetadata[]][]> {\n logger.info(colors.magenta(`Pulling file metadata for ${requests.length} requests`));\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 // Start timer\n let total = 0;\n progressBar.start(requests.length, 0);\n\n // Loop over the requests\n const results = await map(\n requests,\n async (\n requestToDownload,\n ): Promise<[Pick<PrivacyRequest, 'id' | 'status'>, RequestFileMetadata[]]> => {\n const localResults: RequestFileMetadata[] = [];\n\n // Paginate over the file metadata for this request\n let shouldContinue = true;\n let offset = 0;\n while (shouldContinue) {\n let response: RequestFileMetadataResponse;\n try {\n // Grab the file metadata for this request\n\n const rawResponse = await sombra\n .get(`v1/data-subject-request/${requestToDownload.id}/download-keys`, {\n searchParams: {\n limit,\n offset,\n },\n })\n .json();\n response = decodeCodec(RequestFileMetadataResponse, rawResponse);\n localResults.push(...response.nodes);\n\n // Increase offset and break if no more pages\n offset += limit;\n shouldContinue =\n // eslint-disable-next-line no-underscore-dangle\n !!response._links.next && response.nodes.length === limit;\n } catch (err) {\n throw new Error(`Received an error from server: ${err?.response?.body || err?.message}`);\n }\n }\n\n total += 1;\n progressBar.update(total);\n return [requestToDownload, localResults];\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 downloaded file metadata ${requests.length} requests in \"${\n totalTime / 1000\n }\" seconds!`,\n ),\n );\n\n return results;\n}\n","import { map } from '@transcend-io/utils';\nimport colors from 'colors';\nimport type { Got } from 'got';\n\nimport { logger } from '../../logger.js';\nimport { RequestFileMetadata } from './getFileMetadataForPrivacyRequests.js';\n\n/**\n * This function will take in a set of file metadata for privacy requests\n * call the Transcend API to stream the file metadata for these requests\n * and pass that through a callback function\n *\n * @param fileMetadata - Metadata to download\n * @param options - Options for the request\n */\nexport async function streamPrivacyRequestFiles(\n fileMetadata: RequestFileMetadata[],\n {\n requestId,\n sombra,\n onFileDownloaded,\n concurrency = 20,\n }: {\n /** Request ID for logging */\n requestId: string;\n /** Sombra got instance */\n sombra: Got;\n /** Handler on each file */\n onFileDownloaded: (metadata: RequestFileMetadata, stream: Uint8Array) => void;\n /** Concurrent downloads at once */\n concurrency?: number;\n },\n): Promise<void> {\n // Loop over each file\n await map(\n fileMetadata,\n async (metadata) => {\n try {\n // Construct the stream\n await sombra\n .get('v1/files', {\n searchParams: {\n downloadKey: metadata.downloadKey,\n },\n })\n .buffer()\n .then((fileResponse) => onFileDownloaded(metadata, fileResponse));\n } catch (err) {\n if (err?.response?.body?.includes('fileMetadata#verify')) {\n logger.error(\n colors.red(\n `Failed to pull file for: ${metadata.fileName} (request:${requestId}) - JWT expired. ` +\n 'This likely means that the file is no longer available. ' +\n 'Try restarting the request from scratch in Transcend Admin Dashboard. ' +\n 'Skipping the download of this file.',\n ),\n );\n return;\n }\n throw new Error(`Received an error from server: ${err?.response?.body || err?.message}`);\n }\n },\n {\n concurrency,\n },\n );\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport { RequestAction, RequestStatus } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n createSombraGotInstance,\n makeGraphQLRequest,\n} from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchAllRequests, APPROVE_PRIVACY_REQUEST } from '../graphql/index.js';\nimport { getFileMetadataForPrivacyRequests } from './getFileMetadataForPrivacyRequests.js';\nimport { streamPrivacyRequestFiles } from './streamPrivacyRequestFiles.js';\n\n/**\n * Download a set of privacy requests to disk\n *\n * @param options - Options\n * @returns The number of requests canceled\n */\nexport async function downloadPrivacyRequestFiles({\n auth,\n folderPath,\n requestIds,\n createdAtBefore,\n sombraAuth,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n statuses = [RequestStatus.Approving, RequestStatus.Downloadable],\n concurrency = 5,\n transcendUrl = DEFAULT_TRANSCEND_API,\n approveAfterDownload = false,\n}: {\n /** The folder path to download the files to */\n folderPath: string;\n /** Transcend API key authentication */\n auth: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** Concurrency limit for approving */\n concurrency?: number;\n /** The request statuses to cancel */\n statuses?: RequestStatus[];\n /** The set of privacy requests to cancel */\n requestIds?: string[];\n /** Filter for requests created before this date */\n createdAtBefore?: Date;\n /** Filter for requests created after this date */\n createdAtAfter?: Date;\n /** Filter for requests updated before this date */\n updatedAtBefore?: Date;\n /** Filter for requests updated after this date */\n updatedAtAfter?: Date;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** When true, approve any requests in Transcend that are in status=APPROVING */\n approveAfterDownload?: boolean;\n}): Promise<number> {\n // Find all requests made before createdAt that are in a removing data state\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\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 // Create the folder if it does not exist\n if (!existsSync(folderPath)) {\n mkdirSync(folderPath);\n }\n\n // Pull in the requests\n const allRequests = await fetchAllRequests(client, {\n actions: [RequestAction.Access],\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n statuses,\n requestIds,\n });\n\n // Download the file metadata for each request\n const requestFileMetadata = await getFileMetadataForPrivacyRequests(allRequests, {\n sombra,\n concurrency,\n });\n\n // Start timer for download process\n const t0 = new Date().getTime();\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n let total = 0;\n let totalApproved = 0;\n progressBar.start(allRequests.length, 0);\n\n // Download the files for each request\n await map(\n requestFileMetadata,\n async ([request, metadata]) => {\n // Create a new folder to store request files\n const requestFolder = join(folderPath, request.id);\n if (!existsSync(requestFolder)) {\n mkdirSync(requestFolder);\n }\n\n // Stream each file to disk\n await streamPrivacyRequestFiles(metadata, {\n sombra,\n requestId: request.id,\n onFileDownloaded: (fil, stream) => {\n // Ensure a folder exists for the file\n // filename looks like Health/heartbeat.csv\n const filePath = join(requestFolder, fil.fileName);\n const folder = dirname(filePath);\n if (!existsSync(folder)) {\n mkdirSync(folder, { recursive: true });\n }\n\n // Write to disk\n writeFileSync(filePath, stream);\n },\n });\n\n // Approve the request if requested\n if (approveAfterDownload && request.status === RequestStatus.Approving) {\n await makeGraphQLRequest(client, APPROVE_PRIVACY_REQUEST, {\n variables: { input: { requestId: request.id } },\n logger,\n });\n totalApproved += 1;\n }\n\n // Increment the progress bar\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(`Successfully downloaded ${total} requests in \"${totalTime / 1000}\" seconds!`),\n );\n if (totalApproved > 0) {\n logger.info(colors.green(`Approved ${totalApproved} requests in Transcend.`));\n }\n return allRequests.length;\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pullChunkedCustomSiloOutstandingIdentifiers-CDHCpiXU.mjs","names":[],"sources":["../src/lib/graphql/fetchRequestDataSiloActiveCount.ts","../src/lib/cron/pullCronPageOfIdentifiers.ts","../src/lib/cron/pullChunkedCustomSiloOutstandingIdentifiers.ts"],"sourcesContent":["import { makeGraphQLRequest, REDUCED_REQUESTS_FOR_DATA_SILO_COUNT } from '@transcend-io/sdk';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { logger } from '../../logger.js';\n\n/**\n * Get number of open requests for a data silo\n *\n * @param client - GraphQL client\n * @param options - Filter options\n * @returns List of request identifiers\n */\nexport async function fetchRequestDataSiloActiveCount(\n client: GraphQLClient,\n {\n dataSiloId,\n }: {\n /** Data silo ID */\n dataSiloId: string;\n },\n): Promise<number> {\n const {\n listReducedRequestsForDataSilo: { totalCount },\n } = await makeGraphQLRequest<{\n /** Requests */\n listReducedRequestsForDataSilo: {\n /** Total count */\n totalCount: number;\n };\n }>(client, REDUCED_REQUESTS_FOR_DATA_SILO_COUNT, {\n variables: {\n input: {\n dataSiloId,\n isResolved: false,\n },\n },\n logger,\n });\n\n return totalCount;\n}\n","import { RequestAction } from '@transcend-io/privacy-types';\nimport { withTransientRetry } from '@transcend-io/sdk';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport type { Got } from 'got';\nimport * as t from 'io-ts';\n\nimport { logger } from '../../logger.js';\n\n/** Consent partition metadata when a DSR is scoped to a partition */\nexport const CronPartition = t.type({\n /** ID of the partition */\n id: t.string,\n /** The human readable name of the partition */\n name: t.string,\n /** The unique identifying partition value */\n partition: t.string,\n});\n\n/** Type override */\nexport type CronPartition = t.TypeOf<typeof CronPartition>;\n\nexport const CronIdentifier = t.intersection([\n t.type({\n /** The identifier value */\n identifier: t.string,\n /** The type of identifier */\n type: t.string,\n /** The core identifier of the request */\n coreIdentifier: t.string,\n /** The ID of the underlying data silo */\n dataSiloId: t.string,\n /** The ID of the underlying request */\n requestId: t.string,\n /** The request nonce */\n nonce: t.string,\n /** The time the request was created */\n requestCreatedAt: t.string,\n /** The number of days until the request is overdue */\n daysUntilOverdue: t.number,\n /** Request attributes */\n attributes: t.array(\n t.type({\n key: t.string,\n values: t.array(t.string),\n }),\n ),\n }),\n t.partial({\n /** The consent partition that scopes this request, when applicable */\n partition: t.union([CronPartition, t.null]),\n }),\n]);\n\n/** Type override */\nexport type CronIdentifier = t.TypeOf<typeof CronIdentifier>;\n\n/**\n * Pull a offset of identifiers for a cron job\n *\n * @see https://docs.transcend.io/docs/api-reference/GET/v1/data-silo/(id)/pending-requests/(type)\n * @param sombra - Sombra instance configured to make requests\n * @param options - Additional options\n * @returns Successfully submitted request\n */\nexport async function pullCronPageOfIdentifiers(\n sombra: Got,\n {\n dataSiloId,\n limit = 100,\n offset = 0,\n requestType,\n }: {\n /** Data Silo ID */\n dataSiloId: string;\n /** Type of request */\n requestType: RequestAction;\n /** Number of identifiers to pull in */\n limit?: number;\n /** Page to pull in */\n offset?: number;\n },\n): Promise<CronIdentifier[]> {\n try {\n // `GET pending-requests` is a read and therefore safe to retry on transient\n // gateway / network errors. Customers running `transcend cron\n // pull-identifiers` against large silos would otherwise see the command\n // abort on a single 502 from the Sombra reverse tunnel.\n const response = await withTransientRetry(\n 'pullCronPageOfIdentifiers',\n () =>\n sombra\n .get(`v1/data-silo/${dataSiloId}/pending-requests/${requestType}`, {\n searchParams: {\n offset,\n limit,\n },\n })\n .json(),\n { logger, maxAttempts: 6, baseDelayMs: 500 },\n );\n\n const { items } = decodeCodec(\n t.type({\n items: t.array(CronIdentifier),\n }),\n response,\n );\n return items;\n } catch (err) {\n throw new Error(`Received an error from server: ${err?.response?.body || err?.message}`);\n }\n}\n","import { RequestAction } from '@transcend-io/privacy-types';\nimport { buildTranscendGraphQLClient, createSombraGotInstance } from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchRequestDataSiloActiveCount } from '../graphql/index.js';\nimport { pullCronPageOfIdentifiers, CronIdentifier } from './pullCronPageOfIdentifiers.js';\n\n/**\n * A CSV formatted identifier\n */\nexport type CsvFormattedIdentifier = {\n [k in string]: string | null | boolean | number;\n};\n\nexport interface CronIdentifierWithAction extends CronIdentifier {\n /** The request action that the identifier relates to */\n action: RequestAction;\n}\n\n/**\n * Pull the set of identifiers outstanding for a cron or AVC integration\n *\n * This function is designed to be used in a loop, and will call the onSave callback\n * with a chunk of identifiers when the savePageSize is reached.\n *\n * @param options - Options\n * @returns The identifiers and identifiers formatted for CSV\n */\nexport async function pullChunkedCustomSiloOutstandingIdentifiers({\n dataSiloId,\n auth,\n sombraAuth,\n actions,\n apiPageSize = 100,\n savePageSize = 1000,\n onSave,\n transcendUrl = DEFAULT_TRANSCEND_API,\n skipRequestCount = false,\n}: {\n /** Transcend API key authentication */\n auth: string;\n /** Data Silo ID to pull down jobs for */\n dataSiloId: string;\n /** The request actions to fetch */\n actions: RequestAction[];\n /** How many identifiers to pull in a single call to the backend */\n apiPageSize: number;\n /** How many identifiers to save at a time (usually to a CSV file, should be a multiple of apiPageSize) */\n savePageSize: number;\n /** Callback function called when a chunk of identifiers is ready to be saved */\n onSave: (chunk: CsvFormattedIdentifier[]) => Promise<void>;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** Skip request count */\n skipRequestCount?: boolean;\n}): Promise<{\n /** Raw Identifiers */\n identifiers: CronIdentifierWithAction[];\n}> {\n // Validate savePageSize\n if (savePageSize % apiPageSize !== 0) {\n throw new Error(\n `savePageSize must be a multiple of apiPageSize. savePageSize: ${savePageSize}, apiPageSize: ${apiPageSize}`,\n );\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 // Create GraphQL client to connect to Transcend backend\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n let totalRequestCount = 0;\n if (!skipRequestCount) {\n totalRequestCount = await fetchRequestDataSiloActiveCount(client, {\n dataSiloId,\n });\n }\n\n logger.info(\n colors.magenta(\n `Pulling ${skipRequestCount ? 'all' : totalRequestCount} outstanding request identifiers ` +\n `for data silo: \"${dataSiloId}\" for requests of types \"${actions.join('\", \"')}\"`,\n ),\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 const foundRequestIds = new Set<string>();\n\n // identifiers found in total\n const identifiers: CronIdentifierWithAction[] = [];\n // current chunk of identifiers to be saved\n let currentChunk: CsvFormattedIdentifier[] = [];\n\n // map over each action\n if (!skipRequestCount) {\n progressBar.start(totalRequestCount, 0);\n }\n await mapSeries(actions, async (action) => {\n let offset = 0;\n let shouldContinue = true;\n\n // Fetch a page of identifiers\n while (shouldContinue) {\n const pageIdentifiers = await pullCronPageOfIdentifiers(sombra, {\n dataSiloId,\n limit: apiPageSize,\n offset,\n requestType: action,\n });\n\n const identifiersWithAction: CronIdentifierWithAction[] = pageIdentifiers.map(\n (identifier) => {\n foundRequestIds.add(identifier.requestId);\n return {\n ...identifier,\n action,\n };\n },\n );\n\n const csvFormattedIdentifiers = identifiersWithAction.map(\n ({ attributes, partition, ...identifier }) => ({\n ...identifier,\n ...(partition\n ? {\n partitionId: partition.id,\n partitionName: partition.name,\n partitionKey: partition.partition,\n }\n : {}),\n ...attributes.reduce(\n (acc, val) =>\n Object.assign(acc, {\n [val.key]: val.values.join(','),\n }),\n {},\n ),\n }),\n );\n\n identifiers.push(...identifiersWithAction);\n currentChunk.push(...csvFormattedIdentifiers);\n\n // Check if we've reached the savePageSize and call the onSave callback\n if (currentChunk.length >= savePageSize) {\n await onSave(currentChunk);\n currentChunk = [];\n }\n\n shouldContinue = pageIdentifiers.length === apiPageSize;\n offset += apiPageSize;\n if (!skipRequestCount) {\n progressBar.update(foundRequestIds.size);\n } else {\n logger.info(\n colors.magenta(\n `Pulled ${pageIdentifiers.length} outstanding identifiers for ${foundRequestIds.size} requests`,\n ),\n );\n }\n }\n });\n\n // Save any remaining identifiers in the current chunk\n if (currentChunk.length > 0) {\n await onSave(currentChunk);\n }\n\n if (!skipRequestCount) {\n progressBar.stop();\n }\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Successfully pulled ${identifiers.length} outstanding identifiers from ${\n foundRequestIds.size\n } requests in \"${totalTime / 1000}\" seconds!`,\n ),\n );\n\n return { identifiers };\n}\n"],"mappings":"0bAYA,eAAsB,EACpB,EACA,CACE,cAKe,CACjB,GAAM,CACJ,+BAAgC,CAAE,eAChC,MAAM,EAMP,EAAQ,EAAsC,CAC/C,UAAW,CACT,MAAO,CACL,aACA,WAAY,GACb,CACF,CACD,SACD,CAAC,CAEF,OAAO,EC9BT,MAAa,EAAgB,EAAE,KAAK,CAElC,GAAI,EAAE,OAEN,KAAM,EAAE,OAER,UAAW,EAAE,OACd,CAAC,CAKW,EAAiB,EAAE,aAAa,CAC3C,EAAE,KAAK,CAEL,WAAY,EAAE,OAEd,KAAM,EAAE,OAER,eAAgB,EAAE,OAElB,WAAY,EAAE,OAEd,UAAW,EAAE,OAEb,MAAO,EAAE,OAET,iBAAkB,EAAE,OAEpB,iBAAkB,EAAE,OAEpB,WAAY,EAAE,MACZ,EAAE,KAAK,CACL,IAAK,EAAE,OACP,OAAQ,EAAE,MAAM,EAAE,OAAO,CAC1B,CAAC,CACH,CACF,CAAC,CACF,EAAE,QAAQ,CAER,UAAW,EAAE,MAAM,CAAC,EAAe,EAAE,KAAK,CAAC,CAC5C,CAAC,CACH,CAAC,CAaF,eAAsB,EACpB,EACA,CACE,aACA,QAAQ,IACR,SAAS,EACT,eAWyB,CAC3B,GAAI,CAKF,IAAM,EAAW,MAAM,EACrB,gCAEE,EACG,IAAI,gBAAgB,EAAW,oBAAoB,IAAe,CACjE,aAAc,CACZ,SACA,QACD,CACF,CAAC,CACD,MAAM,CACX,CAAE,SAAQ,YAAa,EAAG,YAAa,IAAK,CAC7C,CAEK,CAAE,SAAU,EAChB,EAAE,KAAK,CACL,MAAO,EAAE,MAAM,EAAe,CAC/B,CAAC,CACF,EACD,CACD,OAAO,QACA,EAAK,CACZ,MAAU,MAAM,kCAAkC,GAAK,UAAU,MAAQ,GAAK,UAAU,EC7E5F,eAAsB,EAA4C,CAChE,aACA,OACA,aACA,UACA,cAAc,IACd,eAAe,IACf,SACA,eAAe,EACf,mBAAmB,IAuBlB,CAED,GAAI,EAAe,IAAgB,EACjC,MAAU,MACR,iEAAiE,EAAa,iBAAiB,IAChG,CAIH,IAAM,EAAS,MAAM,EAAwB,EAAc,EAAM,CAC/D,SACA,aAAc,EACd,UAAW,QAAQ,IAAI,WACxB,CAAC,CAGI,EAAS,EAA4B,EAAc,EAAK,CAE1D,EAAoB,EACnB,IACH,EAAoB,MAAM,EAAgC,EAAQ,CAChE,aACD,CAAC,EAGJ,EAAO,KACL,EAAO,QACL,WAAW,EAAmB,MAAQ,EAAkB,mDACnC,EAAW,2BAA2B,EAAQ,KAAK,OAAO,CAAC,GACjF,CACF,CAGD,IAAM,EAAK,IAAI,MAAM,CAAC,SAAS,CAEzB,EAAc,IAAI,EAAY,UAAU,EAAE,CAAE,EAAY,QAAQ,eAAe,CAC/E,EAAkB,IAAI,IAGtB,EAA0C,EAAE,CAE9C,EAAyC,EAAE,CAG1C,GACH,EAAY,MAAM,EAAmB,EAAE,CAEzC,MAAM,EAAU,EAAS,KAAO,IAAW,CACzC,IAAI,EAAS,EACT,EAAiB,GAGrB,KAAO,GAAgB,CACrB,IAAM,EAAkB,MAAM,EAA0B,EAAQ,CAC9D,aACA,MAAO,EACP,SACA,YAAa,EACd,CAAC,CAEI,EAAoD,EAAgB,IACvE,IACC,EAAgB,IAAI,EAAW,UAAU,CAClC,CACL,GAAG,EACH,SACD,EAEJ,CAEK,EAA0B,EAAsB,KACnD,CAAE,aAAY,YAAW,GAAG,MAAkB,CAC7C,GAAG,EACH,GAAI,EACA,CACE,YAAa,EAAU,GACvB,cAAe,EAAU,KACzB,aAAc,EAAU,UACzB,CACD,EAAE,CACN,GAAG,EAAW,QACX,EAAK,IACJ,OAAO,OAAO,EAAK,EAChB,EAAI,KAAM,EAAI,OAAO,KAAK,IAAI,CAChC,CAAC,CACJ,EAAE,CACH,CACF,EACF,CAED,EAAY,KAAK,GAAG,EAAsB,CAC1C,EAAa,KAAK,GAAG,EAAwB,CAGzC,EAAa,QAAU,IACzB,MAAM,EAAO,EAAa,CAC1B,EAAe,EAAE,EAGnB,EAAiB,EAAgB,SAAW,EAC5C,GAAU,EACL,EAGH,EAAO,KACL,EAAO,QACL,UAAU,EAAgB,OAAO,+BAA+B,EAAgB,KAAK,WACtF,CACF,CAND,EAAY,OAAO,EAAgB,KAAK,GAS5C,CAGE,EAAa,OAAS,GACxB,MAAM,EAAO,EAAa,CAGvB,GACH,EAAY,MAAM,CAGpB,IAAM,EADK,IAAI,MAAM,CAAC,SACF,CAAG,EAUvB,OARA,EAAO,KACL,EAAO,MACL,uBAAuB,EAAY,OAAO,gCACxC,EAAgB,KACjB,gBAAgB,EAAY,IAAK,YACnC,CACF,CAEM,CAAE,cAAa"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pushCronIdentifiersFromCsv-CnMWjt94.mjs","names":[],"sources":["../src/lib/cron/markCronIdentifierCompleted.ts","../src/lib/cron/pushCronIdentifiersFromCsv.ts"],"sourcesContent":["import type { Got } from 'got';\nimport * as t from 'io-ts';\n\n/**\n * Minimal set required to mark as completed\n */\nexport const CronIdentifierPush = t.type({\n nonce: t.string,\n identifier: t.string,\n});\n\n/** Type override */\nexport type CronIdentifierPush = t.TypeOf<typeof CronIdentifierPush>;\n\n/**\n * Mark an identifier output by the cron job as completed.\n *\n * @see https://docs.transcend.io/docs/api-reference/PUT/v1/data-silo\n * @param sombra - Sombra instance configured to make requests\n * @param options - Additional options\n * @returns Successfully submitted request, false if not in a state to update\n */\nexport async function markCronIdentifierCompleted(\n sombra: Got,\n { nonce, identifier }: CronIdentifierPush,\n): Promise<boolean> {\n try {\n // Make the GraphQL request\n await sombra.put('v1/data-silo', {\n headers: {\n 'x-transcend-nonce': nonce,\n },\n json: {\n profiles: [\n {\n profileId: identifier,\n },\n ],\n },\n });\n return true;\n } catch (err) {\n // handle gracefully\n if (err.response?.statusCode === 409) {\n return false;\n }\n throw new Error(`Received an error from server: ${err?.response?.body || err?.message}`);\n }\n}\n","import { createSombraGotInstance } from '@transcend-io/sdk';\nimport { map, mapSeries } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport { chunk } from 'lodash-es';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { readCsv } from '../requests/index.js';\nimport { markCronIdentifierCompleted, CronIdentifierPush } from './markCronIdentifierCompleted.js';\n\n/**\n * Given a CSV of cron job outputs, mark all requests as completed in Transcend\n *\n * @param options - Options\n * @returns Number of items marked as completed\n */\nexport async function pushCronIdentifiersFromCsv({\n file,\n dataSiloId,\n auth,\n sombraAuth,\n concurrency = 100,\n transcendUrl = DEFAULT_TRANSCEND_API,\n sleepSeconds = 10,\n}: {\n /** CSV file path */\n file: string;\n /** Transcend API key authentication */\n auth: string;\n /** Data Silo ID to pull down jobs for */\n dataSiloId: string;\n /** Upload concurrency */\n concurrency?: number;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** Sleep time in seconds between chunks of concurrent calls */\n sleepSeconds?: number;\n}): Promise<number> {\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 from CSV\n logger.info(colors.magenta(`Reading \"${file}\" from disk`));\n const activeResults = readCsv(file, CronIdentifierPush);\n\n // Notify Transcend\n logger.info(\n colors.magenta(\n `Notifying Transcend for data silo \"${dataSiloId}\" marking \"${activeResults.length}\" identifiers as completed.`,\n ),\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 let successCount = 0;\n let failureCount = 0;\n let errorCount = 0;\n progressBar.start(activeResults.length, 0);\n\n // Process in chunks with sleep intervals\n const chunks = chunk(activeResults, concurrency);\n const totalChunks = chunks.length;\n const processChunk = async (items: CronIdentifierPush[], chunkIndex: number): Promise<void> => {\n logger.info(\n colors.blue(`Processing chunk ${chunkIndex + 1}/${totalChunks} (${chunk.length} items)`),\n );\n\n // Process the items of the chunk concurrently\n await map(items, async (identifier) => {\n try {\n const success = await markCronIdentifierCompleted(sombra, identifier);\n if (success) {\n successCount += 1;\n } else {\n failureCount += 1;\n }\n } catch (e) {\n logger.error(\n colors.red(\n `Error notifying Transcend for identifier \"${identifier.identifier}\" - ${e?.message}`,\n ),\n );\n errorCount += 1;\n }\n progressBar.update(successCount + failureCount);\n });\n\n // Sleep between chunks (except for the last chunk)\n if (sleepSeconds > 0 && chunkIndex < totalChunks - 1) {\n logger.info(colors.yellow(`Sleeping for ${sleepSeconds}s before next chunk...`));\n\n await new Promise((resolve) => {\n setTimeout(resolve, sleepSeconds * 1000);\n });\n }\n };\n\n // Process all chunks sequentially\n await mapSeries(chunks, processChunk);\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Successfully notified Transcend for ${successCount} identifiers in \"${\n totalTime / 1000\n }\" seconds!`,\n ),\n );\n if (failureCount) {\n logger.info(\n colors.magenta(\n `There were ${failureCount} identifiers that were not in a state to be updated.` +\n 'They likely have already been resolved.',\n ),\n );\n }\n if (errorCount) {\n logger.error(\n colors.red(\n `There were ${errorCount} identifiers that failed to be updated. Please review the logs for more information.`,\n ),\n );\n throw new Error('Failed to update all identifiers');\n }\n return activeResults.length;\n}\n"],"mappings":"yVAMA,MAAa,EAAqB,EAAE,KAAK,CACvC,MAAO,EAAE,OACT,WAAY,EAAE,OACf,CAAC,CAaF,eAAsB,EACpB,EACA,CAAE,QAAO,cACS,CAClB,GAAI,CAcF,OAZA,MAAM,EAAO,IAAI,eAAgB,CAC/B,QAAS,CACP,oBAAqB,EACtB,CACD,KAAM,CACJ,SAAU,CACR,CACE,UAAW,EACZ,CACF,CACF,CACF,CAAC,CACK,SACA,EAAK,CAEZ,GAAI,EAAI,UAAU,aAAe,IAC/B,MAAO,GAET,MAAU,MAAM,kCAAkC,GAAK,UAAU,MAAQ,GAAK,UAAU,EC7B5F,eAAsB,EAA2B,CAC/C,OACA,aACA,OACA,aACA,cAAc,IACd,eAAe,EACf,eAAe,IAgBG,CAElB,IAAM,EAAS,MAAM,EAAwB,EAAc,EAAM,CAC/D,SACA,aAAc,EACd,UAAW,QAAQ,IAAI,WACxB,CAAC,CAGF,EAAO,KAAK,EAAO,QAAQ,YAAY,EAAK,aAAa,CAAC,CAC1D,IAAM,EAAgB,EAAQ,EAAM,EAAmB,CAGvD,EAAO,KACL,EAAO,QACL,sCAAsC,EAAW,aAAa,EAAc,OAAO,6BACpF,CACF,CAGD,IAAM,EAAK,IAAI,MAAM,CAAC,SAAS,CAEzB,EAAc,IAAI,EAAY,UAAU,EAAE,CAAE,EAAY,QAAQ,eAAe,CAEjF,EAAe,EACf,EAAe,EACf,EAAa,EACjB,EAAY,MAAM,EAAc,OAAQ,EAAE,CAG1C,IAAM,EAAS,EAAM,EAAe,EAAY,CAC1C,EAAc,EAAO,OAqC3B,MAAM,EAAU,EAAQ,MApCI,EAA6B,IAAsC,CAC7F,EAAO,KACL,EAAO,KAAK,oBAAoB,EAAa,EAAE,GAAG,EAAY,IAAI,EAAM,OAAO,SAAS,CACzF,CAGD,MAAM,EAAI,EAAO,KAAO,IAAe,CACrC,GAAI,CAEE,MADkB,EAA4B,EAAQ,EAAW,CAEnE,GAAgB,EAEhB,GAAgB,QAEX,EAAG,CACV,EAAO,MACL,EAAO,IACL,6CAA6C,EAAW,WAAW,MAAM,GAAG,UAC7E,CACF,CACD,GAAc,EAEhB,EAAY,OAAO,EAAe,EAAa,EAC/C,CAGE,EAAe,GAAK,EAAa,EAAc,IACjD,EAAO,KAAK,EAAO,OAAO,gBAAgB,EAAa,wBAAwB,CAAC,CAEhF,MAAM,IAAI,QAAS,GAAY,CAC7B,WAAW,EAAS,EAAe,IAAK,EACxC,GAK+B,CAErC,EAAY,MAAM,CAElB,IAAM,EADK,IAAI,MAAM,CAAC,SACF,CAAG,EAiBvB,GAfA,EAAO,KACL,EAAO,MACL,uCAAuC,EAAa,mBAClD,EAAY,IACb,YACF,CACF,CACG,GACF,EAAO,KACL,EAAO,QACL,cAAc,EAAa,6FAE5B,CACF,CAEC,EAMF,MALA,EAAO,MACL,EAAO,IACL,cAAc,EAAW,sFAC1B,CACF,CACS,MAAM,mCAAmC,CAErD,OAAO,EAAc"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pushManualEnrichmentIdentifiersFromCsv-iv9Ts42q.mjs","names":[],"sources":["../src/lib/manual-enrichment/enrichPrivacyRequest.ts","../src/lib/manual-enrichment/pushManualEnrichmentIdentifiersFromCsv.ts"],"sourcesContent":["import { splitCsvToList } from '@transcend-io/utils';\nimport colors from 'colors';\nimport type { Got } from 'got';\nimport * as t from 'io-ts';\nimport { uniq } from 'lodash-es';\n\nimport { logger } from '../../logger.js';\n\nconst ADMIN_URL = 'https://app.transcend.io/privacy-requests/incoming-requests/';\n/**\n * Minimal set required to mark as completed\n */\nexport const EnrichPrivacyRequest = t.record(t.string, t.string);\n\n/** Type override */\nexport type EnrichPrivacyRequest = t.TypeOf<typeof EnrichPrivacyRequest>;\n\n/**\n * Upload identifiers to a privacy request or mark request as\n *\n * @param sombra - Sombra instance configured to make requests\n * @param request - Request to enricher\n * @param enricherId - The ID of the enricher being uploaded to\n * @param index - Index of request ID\n * @returns True if enriched successfully, false if skipped, throws error if failed\n */\nexport async function enrichPrivacyRequest(\n sombra: Got,\n { id: rawId, ...rest }: EnrichPrivacyRequest,\n enricherId: string,\n index?: number,\n): Promise<boolean> {\n if (!rawId) {\n // error\n const msg = `Request ID must be provided to enricher request.${\n index ? ` Found error in row: ${index}` : ''\n }`;\n logger.error(colors.red(msg));\n throw new Error(msg);\n }\n\n const id = rawId.toLowerCase();\n\n // Pull out the identifiers\n const enrichedIdentifiers = Object.entries(rest).reduce(\n (acc, [key, value]) => {\n const values = uniq(splitCsvToList(value));\n return values.length === 0\n ? acc\n : Object.assign(acc, {\n [key]: uniq(splitCsvToList(value)).map((val) => ({\n value: key === 'email' ? val.toLowerCase() : val,\n })),\n });\n },\n {} as Record<string, string[]>,\n );\n\n // Make the GraphQL request\n try {\n await sombra\n .post('v1/enrich-identifiers', {\n headers: {\n 'x-transcend-request-id': id,\n 'x-transcend-enricher-id': enricherId,\n },\n json: {\n enrichedIdentifiers,\n },\n })\n .json();\n\n logger.error(colors.green(`Successfully enriched request: ${ADMIN_URL}${id}`));\n return true;\n } catch (err) {\n // skip if already enriched\n if (\n typeof err.response.body === 'string' &&\n err.response.body.includes('Cannot update a resolved RequestEnricher')\n ) {\n logger.warn(\n colors.magenta(\n `Skipped enrichment for request: ${ADMIN_URL}${id}, request is no longer in the enriching phase.`,\n ),\n );\n return false;\n }\n\n // error\n logger.error(\n colors.red(\n `Failed to enricher identifiers for request with id: ${ADMIN_URL}${id} - ${err.message} - ${err.response.body}`,\n ),\n );\n throw err;\n }\n}\n","import {\n buildTranscendGraphQLClient,\n createSombraGotInstance,\n makeGraphQLRequest,\n} from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { UPDATE_PRIVACY_REQUEST } from '../graphql/index.js';\nimport { readCsv } from '../requests/index.js';\nimport { enrichPrivacyRequest, EnrichPrivacyRequest } from './enrichPrivacyRequest.js';\n\n/**\n * Push a CSV of enriched requests back into Transcend\n *\n * @param options - Options\n * @returns Number of items processed\n */\nexport async function pushManualEnrichmentIdentifiersFromCsv({\n file,\n auth,\n sombraAuth,\n enricherId,\n markSilent,\n concurrency = 100,\n transcendUrl = DEFAULT_TRANSCEND_API,\n}: {\n /** CSV file path */\n file: string;\n /** Transcend API key authentication */\n auth: string;\n /** ID of enricher being uploaded to */\n enricherId: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** Concurrency */\n concurrency?: number;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Mark requests in silent mode before enriching */\n markSilent?: boolean;\n}): Promise<number> {\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 const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Read from CSV\n logger.info(colors.magenta(`Reading \"${file}\" from disk`));\n const activeResults = readCsv(file, EnrichPrivacyRequest);\n\n // Notify Transcend\n logger.info(colors.magenta(`Enriching \"${activeResults.length}\" privacy requests.`));\n\n let successCount = 0;\n let skippedCount = 0;\n let errorCount = 0;\n\n await map(\n activeResults,\n async (request, index) => {\n try {\n // Mark requests in silent mode before a certain date\n if (markSilent) {\n await makeGraphQLRequest(client, UPDATE_PRIVACY_REQUEST, {\n variables: {\n input: {\n id: request.id,\n isSilent: true,\n },\n },\n logger,\n });\n\n logger.info(colors.magenta(`Mark request as silent mode - ${request.id}`));\n }\n\n const result = await enrichPrivacyRequest(sombra, request, enricherId, index);\n if (result) {\n successCount += 1;\n } else {\n skippedCount += 1;\n }\n } catch {\n errorCount += 1;\n }\n },\n { concurrency },\n );\n\n logger.info(colors.green(`Successfully notified Transcend! \\n Success count: ${successCount}.`));\n\n if (skippedCount > 0) {\n logger.info(colors.magenta(`Skipped count: ${skippedCount}.`));\n }\n\n if (errorCount > 0) {\n logger.info(colors.red(`Error Count: ${errorCount}.`));\n throw new Error(`Failed to enrich: ${errorCount} requests.`);\n }\n\n return activeResults.length;\n}\n"],"mappings":"qaAQA,MAAM,EAAY,+DAIL,EAAuB,EAAE,OAAO,EAAE,OAAQ,EAAE,OAAO,CAchE,eAAsB,EACpB,EACA,CAAE,GAAI,EAAO,GAAG,GAChB,EACA,EACkB,CAClB,GAAI,CAAC,EAAO,CAEV,IAAM,EAAM,mDACV,EAAQ,wBAAwB,IAAU,KAG5C,MADA,EAAO,MAAM,EAAO,IAAI,EAAI,CAAC,CACnB,MAAM,EAAI,CAGtB,IAAM,EAAK,EAAM,aAAa,CAGxB,EAAsB,OAAO,QAAQ,EAAK,CAAC,QAC9C,EAAK,CAAC,EAAK,KACK,EAAK,EAAe,EAAM,CAC5B,CAAC,SAAW,EACrB,EACA,OAAO,OAAO,EAAK,EAChB,GAAM,EAAK,EAAe,EAAM,CAAC,CAAC,IAAK,IAAS,CAC/C,MAAO,IAAQ,QAAU,EAAI,aAAa,CAAG,EAC9C,EAAE,CACJ,CAAC,CAER,EAAE,CACH,CAGD,GAAI,CAcF,OAbA,MAAM,EACH,KAAK,wBAAyB,CAC7B,QAAS,CACP,yBAA0B,EAC1B,0BAA2B,EAC5B,CACD,KAAM,CACJ,sBACD,CACF,CAAC,CACD,MAAM,CAET,EAAO,MAAM,EAAO,MAAM,kCAAkC,IAAY,IAAK,CAAC,CACvE,SACA,EAAK,CAEZ,GACE,OAAO,EAAI,SAAS,MAAS,UAC7B,EAAI,SAAS,KAAK,SAAS,2CAA2C,CAOtE,OALA,EAAO,KACL,EAAO,QACL,mCAAmC,IAAY,EAAG,gDACnD,CACF,CACM,GAST,MALA,EAAO,MACL,EAAO,IACL,uDAAuD,IAAY,EAAG,KAAK,EAAI,QAAQ,KAAK,EAAI,SAAS,OAC1G,CACF,CACK,GC1EV,eAAsB,EAAuC,CAC3D,OACA,OACA,aACA,aACA,aACA,cAAc,IACd,eAAe,GAgBG,CAElB,IAAM,EAAS,MAAM,EAAwB,EAAc,EAAM,CAC/D,SACA,aAAc,EACd,UAAW,QAAQ,IAAI,WACxB,CAAC,CACI,EAAS,EAA4B,EAAc,EAAK,CAG9D,EAAO,KAAK,EAAO,QAAQ,YAAY,EAAK,aAAa,CAAC,CAC1D,IAAM,EAAgB,EAAQ,EAAM,EAAqB,CAGzD,EAAO,KAAK,EAAO,QAAQ,cAAc,EAAc,OAAO,qBAAqB,CAAC,CAEpF,IAAI,EAAe,EACf,EAAe,EACf,EAAa,EAwCjB,GAtCA,MAAM,EACJ,EACA,MAAO,EAAS,IAAU,CACxB,GAAI,CAEE,IACF,MAAM,EAAmB,EAAQ,EAAwB,CACvD,UAAW,CACT,MAAO,CACL,GAAI,EAAQ,GACZ,SAAU,GACX,CACF,CACD,SACD,CAAC,CAEF,EAAO,KAAK,EAAO,QAAQ,iCAAiC,EAAQ,KAAK,CAAC,EAIxE,MADiB,EAAqB,EAAQ,EAAS,EAAY,EAAM,CAE3E,GAAgB,EAEhB,GAAgB,OAEZ,CACN,GAAc,IAGlB,CAAE,cAAa,CAChB,CAED,EAAO,KAAK,EAAO,MAAM,sDAAsD,EAAa,GAAG,CAAC,CAE5F,EAAe,GACjB,EAAO,KAAK,EAAO,QAAQ,kBAAkB,EAAa,GAAG,CAAC,CAG5D,EAAa,EAEf,MADA,EAAO,KAAK,EAAO,IAAI,gBAAgB,EAAW,GAAG,CAAC,CAC5C,MAAM,qBAAqB,EAAW,YAAY,CAG9D,OAAO,EAAc"}