@transcend-io/cli 12.1.5 → 13.0.2
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.
- package/README.md +64 -14
- package/dist/{app-BMLQABJX.mjs → app-CCGKgPiC.mjs} +19 -19
- package/dist/app-CCGKgPiC.mjs.map +1 -0
- package/dist/{approvePrivacyRequests-CY6rQT4J.mjs → approvePrivacyRequests-BBcqydVb.mjs} +2 -2
- package/dist/{approvePrivacyRequests-CY6rQT4J.mjs.map → approvePrivacyRequests-BBcqydVb.mjs.map} +1 -1
- package/dist/bin/bash-complete.mjs +1 -1
- package/dist/bin/cli.mjs +1 -1
- package/dist/bin/deprecated-command.mjs +1 -1
- package/dist/{buildExamples-BNGtj-DW.mjs → buildExamples-DRU0o6EK.mjs} +2 -2
- package/dist/{buildExamples-BNGtj-DW.mjs.map → buildExamples-DRU0o6EK.mjs.map} +1 -1
- package/dist/{buildXdiSyncEndpoint-BNCfOc-g.mjs → buildXdiSyncEndpoint-CSjQKSn3.mjs} +2 -2
- package/dist/{buildXdiSyncEndpoint-BNCfOc-g.mjs.map → buildXdiSyncEndpoint-CSjQKSn3.mjs.map} +1 -1
- package/dist/{bulkRestartRequests-CGvODu3r.mjs → bulkRestartRequests-Cx5LfpdX.mjs} +2 -2
- package/dist/{bulkRestartRequests-CGvODu3r.mjs.map → bulkRestartRequests-Cx5LfpdX.mjs.map} +1 -1
- package/dist/{bulkRetryEnrichers-BgDd4GWH.mjs → bulkRetryEnrichers-BSJtvqJD.mjs} +2 -2
- package/dist/{bulkRetryEnrichers-BgDd4GWH.mjs.map → bulkRetryEnrichers-BSJtvqJD.mjs.map} +1 -1
- package/dist/{cancelPrivacyRequests-Ccsiwrnu.mjs → cancelPrivacyRequests-Cy48R4tJ.mjs} +2 -2
- package/dist/{cancelPrivacyRequests-Ccsiwrnu.mjs.map → cancelPrivacyRequests-Cy48R4tJ.mjs.map} +1 -1
- package/dist/codecs-8T8Jhxsr.mjs +2 -0
- package/dist/codecs-8T8Jhxsr.mjs.map +1 -0
- package/dist/{command-BjzdoKgJ.mjs → command-Gip5wB8W.mjs} +2 -2
- package/dist/{command-BjzdoKgJ.mjs.map → command-Gip5wB8W.mjs.map} +1 -1
- package/dist/constants-D6y_AX3P.mjs +2 -0
- package/dist/constants-D6y_AX3P.mjs.map +1 -0
- package/dist/{constants-FDjw7bPS.mjs → constants-DAx9VK1C.mjs} +2 -2
- package/dist/{constants-FDjw7bPS.mjs.map → constants-DAx9VK1C.mjs.map} +1 -1
- package/dist/{createPoolingCommandUi-BdtnLBBM.mjs → createPoolingCommandUi-DZx16Cn8.mjs} +2 -2
- package/dist/{createPoolingCommandUi-BdtnLBBM.mjs.map → createPoolingCommandUi-DZx16Cn8.mjs.map} +1 -1
- package/dist/{downloadPrivacyRequestFiles-C9-xSCpt.mjs → downloadPrivacyRequestFiles-DBeuW9b3.mjs} +2 -2
- package/dist/{downloadPrivacyRequestFiles-C9-xSCpt.mjs.map → downloadPrivacyRequestFiles-DBeuW9b3.mjs.map} +1 -1
- package/dist/fetchAllRequests-BV9K0wXv.mjs +2 -0
- package/dist/fetchAllRequests-BV9K0wXv.mjs.map +1 -0
- package/dist/{fuzzyMatchColumns-D18n9Beu.mjs → fuzzyMatchColumns-Djl3a7lV.mjs} +2 -2
- package/dist/{fuzzyMatchColumns-D18n9Beu.mjs.map → fuzzyMatchColumns-Djl3a7lV.mjs.map} +1 -1
- package/dist/{generateCrossAccountApiKeys-FtWwlf_L.mjs → generateCrossAccountApiKeys-CjmSyfwm.mjs} +2 -2
- package/dist/{generateCrossAccountApiKeys-FtWwlf_L.mjs.map → generateCrossAccountApiKeys-CjmSyfwm.mjs.map} +1 -1
- package/dist/{impl-Dg7XSQdO2.mjs → impl-7qTKg-YP2.mjs} +2 -2
- package/dist/{impl-Dg7XSQdO2.mjs.map → impl-7qTKg-YP2.mjs.map} +1 -1
- package/dist/{impl-D58JUOdA.mjs → impl-9W0QG6fB.mjs} +2 -2
- package/dist/{impl-D58JUOdA.mjs.map → impl-9W0QG6fB.mjs.map} +1 -1
- package/dist/{impl-B94dMG4x2.mjs → impl-B-BkGfqq2.mjs} +2 -2
- package/dist/{impl-B94dMG4x2.mjs.map → impl-B-BkGfqq2.mjs.map} +1 -1
- package/dist/{impl-yIVxAhCG2.mjs → impl-B8DsKzNn2.mjs} +2 -2
- package/dist/{impl-yIVxAhCG2.mjs.map → impl-B8DsKzNn2.mjs.map} +1 -1
- package/dist/impl-B9jwlKCX2.mjs +2 -0
- package/dist/impl-B9jwlKCX2.mjs.map +1 -0
- package/dist/{impl-C3vTm9zQ.mjs → impl-BIhXJ0Nw.mjs} +2 -2
- package/dist/{impl-C3vTm9zQ.mjs.map → impl-BIhXJ0Nw.mjs.map} +1 -1
- package/dist/{impl-B_N_fOq0.mjs → impl-BLM6rMBq.mjs} +2 -2
- package/dist/{impl-B_N_fOq0.mjs.map → impl-BLM6rMBq.mjs.map} +1 -1
- package/dist/{impl-Hj5CgbWd.mjs → impl-BPmzcgpy.mjs} +2 -2
- package/dist/{impl-Hj5CgbWd.mjs.map → impl-BPmzcgpy.mjs.map} +1 -1
- package/dist/{impl-COqlYiiw.mjs → impl-BUKV7sYp.mjs} +2 -2
- package/dist/{impl-COqlYiiw.mjs.map → impl-BUKV7sYp.mjs.map} +1 -1
- package/dist/{impl-DFnWzZyY.mjs → impl-BahDGEJJ.mjs} +2 -2
- package/dist/{impl-DFnWzZyY.mjs.map → impl-BahDGEJJ.mjs.map} +1 -1
- package/dist/{impl-qNF5eqX-.mjs → impl-BfU0GFmW.mjs} +1 -1
- package/dist/{impl-qNF5eqX-.mjs.map → impl-BfU0GFmW.mjs.map} +1 -1
- package/dist/{impl-DDVhG8Gf.mjs → impl-Bh1CkZhV.mjs} +2 -2
- package/dist/{impl-DDVhG8Gf.mjs.map → impl-Bh1CkZhV.mjs.map} +1 -1
- package/dist/{impl-BvwRiPSG.mjs → impl-BkQryDNq.mjs} +2 -2
- package/dist/{impl-BvwRiPSG.mjs.map → impl-BkQryDNq.mjs.map} +1 -1
- package/dist/{impl-bl6NF3Lr.mjs → impl-BsdneZZP.mjs} +2 -2
- package/dist/{impl-bl6NF3Lr.mjs.map → impl-BsdneZZP.mjs.map} +1 -1
- package/dist/{impl-D6rMLiNB2.mjs → impl-BuOc87b6.mjs} +2 -2
- package/dist/impl-BuOc87b6.mjs.map +1 -0
- package/dist/{impl-DUYRVzMo.mjs → impl-BzzkiK_b.mjs} +1 -1
- package/dist/{impl-DUYRVzMo.mjs.map → impl-BzzkiK_b.mjs.map} +1 -1
- package/dist/{impl-ClT3G845.mjs → impl-C2BYhujB.mjs} +2 -2
- package/dist/{impl-ClT3G845.mjs.map → impl-C2BYhujB.mjs.map} +1 -1
- package/dist/{impl-DnDM9zcu.mjs → impl-C5qif_gE.mjs} +2 -2
- package/dist/{impl-DnDM9zcu.mjs.map → impl-C5qif_gE.mjs.map} +1 -1
- package/dist/{impl-BLtOfbtk.mjs → impl-C8auyA7u.mjs} +2 -2
- package/dist/{impl-BLtOfbtk.mjs.map → impl-C8auyA7u.mjs.map} +1 -1
- package/dist/{impl-BEiCEzl_2.mjs → impl-CHoglMch.mjs} +1 -1
- package/dist/impl-CHoglMch.mjs.map +1 -0
- package/dist/{impl-QD3_9HTk.mjs → impl-CJ4oVuXN.mjs} +2 -2
- package/dist/{impl-QD3_9HTk.mjs.map → impl-CJ4oVuXN.mjs.map} +1 -1
- package/dist/{impl-DhXzxQDN2.mjs → impl-CRif3TpB2.mjs} +2 -2
- package/dist/{impl-DhXzxQDN2.mjs.map → impl-CRif3TpB2.mjs.map} +1 -1
- package/dist/{impl-Cvyc-iTI2.mjs → impl-CWINXCFq2.mjs} +2 -2
- package/dist/{impl-Cvyc-iTI2.mjs.map → impl-CWINXCFq2.mjs.map} +1 -1
- package/dist/{impl-Dd39KsYO.mjs → impl-C_lIkMLn2.mjs} +2 -2
- package/dist/impl-C_lIkMLn2.mjs.map +1 -0
- package/dist/{impl-EDwngLc7.mjs → impl-CcPwm8a4.mjs} +2 -2
- package/dist/{impl-EDwngLc7.mjs.map → impl-CcPwm8a4.mjs.map} +1 -1
- package/dist/{impl-DGtCNANL.mjs → impl-CjjiXsMh.mjs} +2 -2
- package/dist/{impl-DGtCNANL.mjs.map → impl-CjjiXsMh.mjs.map} +1 -1
- package/dist/{impl-BLOhoP-b2.mjs → impl-CzFfjx8n2.mjs} +2 -2
- package/dist/{impl-BLOhoP-b2.mjs.map → impl-CzFfjx8n2.mjs.map} +1 -1
- package/dist/{impl-BXGpJz21.mjs → impl-D6e2LNBm.mjs} +2 -2
- package/dist/{impl-BXGpJz21.mjs.map → impl-D6e2LNBm.mjs.map} +1 -1
- package/dist/{impl-SOebshza.mjs → impl-D8yF0jw9.mjs} +2 -2
- package/dist/{impl-SOebshza.mjs.map → impl-D8yF0jw9.mjs.map} +1 -1
- package/dist/{impl-DeCRXfAh2.mjs → impl-DEF-meSg2.mjs} +2 -2
- package/dist/{impl-DeCRXfAh2.mjs.map → impl-DEF-meSg2.mjs.map} +1 -1
- package/dist/{impl-DZoTWLfw2.mjs → impl-DEPuP4Oy2.mjs} +2 -2
- package/dist/{impl-DZoTWLfw2.mjs.map → impl-DEPuP4Oy2.mjs.map} +1 -1
- package/dist/impl-DGLrKNNs2.mjs +2 -0
- package/dist/impl-DGLrKNNs2.mjs.map +1 -0
- package/dist/{impl-XxYQHKO5.mjs → impl-DPq1O87s.mjs} +2 -2
- package/dist/{impl-XxYQHKO5.mjs.map → impl-DPq1O87s.mjs.map} +1 -1
- package/dist/{impl-ChQAmyBN.mjs → impl-DiaD11OP.mjs} +2 -2
- package/dist/{impl-ChQAmyBN.mjs.map → impl-DiaD11OP.mjs.map} +1 -1
- package/dist/{impl-0-_oxvck.mjs → impl-DioQW8jR.mjs} +2 -2
- package/dist/{impl-0-_oxvck.mjs.map → impl-DioQW8jR.mjs.map} +1 -1
- package/dist/{impl-D6frX-PK.mjs → impl-DjBbx05w.mjs} +2 -2
- package/dist/{impl-D6frX-PK.mjs.map → impl-DjBbx05w.mjs.map} +1 -1
- package/dist/{impl-BCKUj-ko.mjs → impl-DnBmd-wi.mjs} +2 -2
- package/dist/{impl-BCKUj-ko.mjs.map → impl-DnBmd-wi.mjs.map} +1 -1
- package/dist/{impl-Cp5o0Q2g.mjs → impl-Do3hnGHt.mjs} +2 -2
- package/dist/{impl-Cp5o0Q2g.mjs.map → impl-Do3hnGHt.mjs.map} +1 -1
- package/dist/{impl-VSo8bO-J.mjs → impl-DqbJ33ve.mjs} +2 -2
- package/dist/{impl-VSo8bO-J.mjs.map → impl-DqbJ33ve.mjs.map} +1 -1
- package/dist/{impl-Cb-7C1Ml2.mjs → impl-DvHiVaGW.mjs} +12 -105
- package/dist/impl-DvHiVaGW.mjs.map +1 -0
- package/dist/{impl-CTOgxZFi.mjs → impl-Dx8BYv7U.mjs} +2 -2
- package/dist/{impl-CTOgxZFi.mjs.map → impl-Dx8BYv7U.mjs.map} +1 -1
- package/dist/{impl-Bz1wZxa-.mjs → impl-GphhprmF.mjs} +2 -2
- package/dist/{impl-Bz1wZxa-.mjs.map → impl-GphhprmF.mjs.map} +1 -1
- package/dist/{impl-agm4lpyq.mjs → impl-HsRwcfRu.mjs} +2 -2
- package/dist/{impl-agm4lpyq.mjs.map → impl-HsRwcfRu.mjs.map} +1 -1
- package/dist/{impl-BHEAKFX02.mjs → impl-LT1FBKID2.mjs} +2 -2
- package/dist/{impl-BHEAKFX02.mjs.map → impl-LT1FBKID2.mjs.map} +1 -1
- package/dist/{impl-xY_5o78i.mjs → impl-Nwbb2xaH.mjs} +2 -2
- package/dist/{impl-xY_5o78i.mjs.map → impl-Nwbb2xaH.mjs.map} +1 -1
- package/dist/{impl-CSnBFv5P.mjs → impl-SKxxByoN.mjs} +2 -2
- package/dist/{impl-CSnBFv5P.mjs.map → impl-SKxxByoN.mjs.map} +1 -1
- package/dist/{impl-ojAnhhbi.mjs → impl-bUltX0u3.mjs} +2 -2
- package/dist/{impl-ojAnhhbi.mjs.map → impl-bUltX0u3.mjs.map} +1 -1
- package/dist/{impl-Bs2pNgMK.mjs → impl-cU3bjY3P.mjs} +2 -2
- package/dist/{impl-Bs2pNgMK.mjs.map → impl-cU3bjY3P.mjs.map} +1 -1
- package/dist/{impl-xVmMN-Vt.mjs → impl-h0z8OSQY.mjs} +2 -2
- package/dist/{impl-xVmMN-Vt.mjs.map → impl-h0z8OSQY.mjs.map} +1 -1
- package/dist/{impl-Ay6JeH-7.mjs → impl-kuuX5KaC.mjs} +2 -2
- package/dist/{impl-Ay6JeH-7.mjs.map → impl-kuuX5KaC.mjs.map} +1 -1
- package/dist/{impl-Ar7VAh0Z.mjs → impl-nYlbrsD7.mjs} +2 -2
- package/dist/{impl-Ar7VAh0Z.mjs.map → impl-nYlbrsD7.mjs.map} +1 -1
- package/dist/{impl-zZd6fuXS.mjs → impl-nte7qanu.mjs} +2 -2
- package/dist/{impl-zZd6fuXS.mjs.map → impl-nte7qanu.mjs.map} +1 -1
- package/dist/{impl-ApZGIx6l.mjs → impl-o66EB2o7.mjs} +2 -2
- package/dist/{impl-ApZGIx6l.mjs.map → impl-o66EB2o7.mjs.map} +1 -1
- package/dist/{impl-BpVe6fMe.mjs → impl-yxR3rwGm.mjs} +2 -2
- package/dist/{impl-BpVe6fMe.mjs.map → impl-yxR3rwGm.mjs.map} +1 -1
- package/dist/index.d.mts +36 -26
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{inquirer-jzIQWAvq.mjs → inquirer-DK1JbPA0.mjs} +2 -2
- package/dist/{inquirer-jzIQWAvq.mjs.map → inquirer-DK1JbPA0.mjs.map} +1 -1
- package/dist/{markRequestDataSiloIdsCompleted-JGJf33yp.mjs → markRequestDataSiloIdsCompleted-loC-xyyW.mjs} +2 -2
- package/dist/{markRequestDataSiloIdsCompleted-JGJf33yp.mjs.map → markRequestDataSiloIdsCompleted-loC-xyyW.mjs.map} +1 -1
- package/dist/{markSilentPrivacyRequests-BTI3rH-V.mjs → markSilentPrivacyRequests-D7ncAg8k.mjs} +2 -2
- package/dist/{markSilentPrivacyRequests-BTI3rH-V.mjs.map → markSilentPrivacyRequests-D7ncAg8k.mjs.map} +1 -1
- package/dist/{mergeTranscendInputs-B7WjxYMN.mjs → mergeTranscendInputs-9kQ3t5VY.mjs} +2 -2
- package/dist/{mergeTranscendInputs-B7WjxYMN.mjs.map → mergeTranscendInputs-9kQ3t5VY.mjs.map} +1 -1
- package/dist/{notifyPrivacyRequestsAdditionalTime-n5fvOJaA.mjs → notifyPrivacyRequestsAdditionalTime-DpCfIR_O.mjs} +2 -2
- package/dist/{notifyPrivacyRequestsAdditionalTime-n5fvOJaA.mjs.map → notifyPrivacyRequestsAdditionalTime-DpCfIR_O.mjs.map} +1 -1
- package/dist/{policy-bundle-manifest-DbbTXe0u.mjs → policy-bundle-manifest-DW3Wilp8.mjs} +2 -2
- package/dist/{policy-bundle-manifest-DbbTXe0u.mjs.map → policy-bundle-manifest-DW3Wilp8.mjs.map} +1 -1
- package/dist/{policy-project-discovery-DTSl0Gfl.mjs → policy-project-discovery-CND7N2HI.mjs} +4 -4
- package/dist/policy-project-discovery-CND7N2HI.mjs.map +1 -0
- package/dist/{policy-scaffold-config-RpxwwZ2H.mjs → policy-scaffold-config-CwY0QZC0.mjs} +8 -8
- package/dist/policy-scaffold-config-CwY0QZC0.mjs.map +1 -0
- package/dist/{project-discovery-BkHpTXDx.mjs → project-discovery-g29vd44a.mjs} +2 -2
- package/dist/{project-discovery-BkHpTXDx.mjs.map → project-discovery-g29vd44a.mjs.map} +1 -1
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DbybCU2T.mjs → pullChunkedCustomSiloOutstandingIdentifiers-7qvaMN58.mjs} +2 -2
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DbybCU2T.mjs.map → pullChunkedCustomSiloOutstandingIdentifiers-7qvaMN58.mjs.map} +1 -1
- package/dist/{pullManualEnrichmentIdentifiersToCsv-CxTPUfpV.mjs → pullManualEnrichmentIdentifiersToCsv-CWPs7VeX.mjs} +2 -2
- package/dist/{pullManualEnrichmentIdentifiersToCsv-CxTPUfpV.mjs.map → pullManualEnrichmentIdentifiersToCsv-CWPs7VeX.mjs.map} +1 -1
- package/dist/{pushCronIdentifiersFromCsv-CeRVlbFE.mjs → pushCronIdentifiersFromCsv-DQoliMdQ.mjs} +2 -2
- package/dist/{pushCronIdentifiersFromCsv-CeRVlbFE.mjs.map → pushCronIdentifiersFromCsv-DQoliMdQ.mjs.map} +1 -1
- package/dist/{pushManualEnrichmentIdentifiersFromCsv--4dYCvyv.mjs → pushManualEnrichmentIdentifiersFromCsv-R9BXYuFk.mjs} +2 -2
- package/dist/{pushManualEnrichmentIdentifiersFromCsv--4dYCvyv.mjs.map → pushManualEnrichmentIdentifiersFromCsv-R9BXYuFk.mjs.map} +1 -1
- package/dist/{readTranscendYaml-BfqQbPiT.mjs → readTranscendYaml-D576bvzR.mjs} +2 -2
- package/dist/{readTranscendYaml-BfqQbPiT.mjs.map → readTranscendYaml-D576bvzR.mjs.map} +1 -1
- package/dist/{removeUnverifiedRequestIdentifiers-CV6JGgt_.mjs → removeUnverifiedRequestIdentifiers-CgnfHdbc.mjs} +2 -2
- package/dist/{removeUnverifiedRequestIdentifiers-CV6JGgt_.mjs.map → removeUnverifiedRequestIdentifiers-CgnfHdbc.mjs.map} +1 -1
- package/dist/{retryRequestDataSilos-_OThvVwU.mjs → retryRequestDataSilos-BrLLNQqT.mjs} +2 -2
- package/dist/{retryRequestDataSilos-_OThvVwU.mjs.map → retryRequestDataSilos-BrLLNQqT.mjs.map} +1 -1
- package/dist/runRegalLintStep-DPaMm7Gg.mjs +3 -0
- package/dist/runRegalLintStep-DPaMm7Gg.mjs.map +1 -0
- package/dist/{scaffold-planning-BrX-pcz6.mjs → scaffold-planning-Bp_yqZWx.mjs} +2 -2
- package/dist/{scaffold-planning-BrX-pcz6.mjs.map → scaffold-planning-Bp_yqZWx.mjs.map} +1 -1
- package/dist/{skipPreflightJobs-D8og5pKe.mjs → skipPreflightJobs-CqjpB7ug.mjs} +2 -2
- package/dist/{skipPreflightJobs-D8og5pKe.mjs.map → skipPreflightJobs-CqjpB7ug.mjs.map} +1 -1
- package/dist/{skipRequestDataSilos-DUQ6AD_O.mjs → skipRequestDataSilos-DPi6-XVr.mjs} +2 -2
- package/dist/{skipRequestDataSilos-DUQ6AD_O.mjs.map → skipRequestDataSilos-DPi6-XVr.mjs.map} +1 -1
- package/dist/{streamPrivacyRequestsToCsv-BApu4dvI.mjs → streamPrivacyRequestsToCsv-DVPQ860_.mjs} +2 -2
- package/dist/{streamPrivacyRequestsToCsv-BApu4dvI.mjs.map → streamPrivacyRequestsToCsv-DVPQ860_.mjs.map} +1 -1
- package/dist/{updateConsentManagerVersionToLatest-BrgPZ5hX.mjs → updateConsentManagerVersionToLatest-gJMWny-i.mjs} +2 -2
- package/dist/{updateConsentManagerVersionToLatest-BrgPZ5hX.mjs.map → updateConsentManagerVersionToLatest-gJMWny-i.mjs.map} +1 -1
- package/dist/{uploadConsents-DRlDHQak.mjs → uploadConsents-5HlMj7EK.mjs} +2 -2
- package/dist/{uploadConsents-DRlDHQak.mjs.map → uploadConsents-5HlMj7EK.mjs.map} +1 -1
- package/dist/uploadPrivacyRequestsFromCsv-Dh8KEW3n.mjs +2 -0
- package/dist/uploadPrivacyRequestsFromCsv-Dh8KEW3n.mjs.map +1 -0
- package/dist/{validateTranscendAuth-D-dDINof.mjs → validateTranscendAuth-BrVGfq6X.mjs} +2 -2
- package/dist/{validateTranscendAuth-D-dDINof.mjs.map → validateTranscendAuth-BrVGfq6X.mjs.map} +1 -1
- package/package.json +22 -22
- package/dist/app-BMLQABJX.mjs.map +0 -1
- package/dist/codecs-CfcaFoZR.mjs +0 -2
- package/dist/codecs-CfcaFoZR.mjs.map +0 -1
- package/dist/constants-DlH2vWm_.mjs +0 -2
- package/dist/constants-DlH2vWm_.mjs.map +0 -1
- package/dist/fetchAllRequests-BI0tFl_a.mjs +0 -2
- package/dist/fetchAllRequests-BI0tFl_a.mjs.map +0 -1
- package/dist/impl-BEiCEzl_2.mjs.map +0 -1
- package/dist/impl-Cb-7C1Ml2.mjs.map +0 -1
- package/dist/impl-D6rMLiNB2.mjs.map +0 -1
- package/dist/impl-DNOgBcsu.mjs +0 -3
- package/dist/impl-DNOgBcsu.mjs.map +0 -1
- package/dist/impl-Dd39KsYO.mjs.map +0 -1
- package/dist/policy-project-discovery-DTSl0Gfl.mjs.map +0 -1
- package/dist/policy-scaffold-config-RpxwwZ2H.mjs.map +0 -1
- package/dist/uploadPrivacyRequestsFromCsv-FAGllBdd.mjs +0 -2
- package/dist/uploadPrivacyRequestsFromCsv-FAGllBdd.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"downloadPrivacyRequestFiles-C9-xSCpt.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
|
+
{"version":3,"file":"downloadPrivacyRequestFiles-DBeuW9b3.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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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{t as e}from"./logger-CPu6zbyQ.mjs";import{a as t,i as n}from"./request-BpKyN9Xm.mjs";import{IsoCountryCode as r,IsoCountrySubdivisionCode as i,LocaleCodec as a,RequestAction as o,RequestOrigin as s,RequestStatus as c}from"@transcend-io/privacy-types";import l from"colors";import{valuesOf as u}from"@transcend-io/type-utils";import*as d from"io-ts";import{makeGraphQLRequest as f}from"@transcend-io/sdk";import p from"cli-progress";const m=d.type({title:d.string,name:d.string,consent:d.boolean,enrichedPreferences:d.array(d.type({topic:d.string,selectValues:d.array(d.type({id:d.string,name:d.string,preferenceOption:d.type({id:d.string,slug:d.string,title:d.type({defaultMessage:d.string})})})),selectValue:d.type({id:d.string,name:d.string}),preferenceTopic:d.type({title:d.type({defaultMessage:d.string}),id:d.string,slug:d.string}),name:d.string,id:d.string,booleanValue:d.boolean}))}),h=d.intersection([d.type({id:d.string,createdAt:d.string,email:d.string,type:u(o),link:d.string,isSilent:d.boolean,origin:u(s),isTest:d.boolean,coreIdentifier:d.string,details:d.string,locale:a,status:u(c),subjectType:d.string,country:d.union([d.null,u(r)]),countrySubDivision:d.union([d.null,u(i)]),attributeValues:d.array(d.type({id:d.string,attributeKey:d.type({name:d.string,id:d.string}),name:d.string}))}),d.partial({daysRemaining:d.union([d.null,d.number]),successfullyCompletedAt:d.union([d.null,d.string]),purpose:m})]);async function g(n,r){let{requests:{totalCount:i}}=await f(n,t,{variables:{filterBy:r},logger:e});return i}async function _(r,{actions:i=[],statuses:a=[],origins:o=[],text:s,createdAtBefore:c,createdAtAfter:u,updatedAtBefore:d,updatedAtAfter:m,isTest:h,isSilent:g,isClosed:_,requestIds:v=[],onPage:y}={}){let b=!!y,x={text:s,type:i.length>0?i:void 0,status:a.length>0?a:void 0,origin:o.length>0?o:void 0,isTest:h,isSilent:g,isClosed:_,createdAtBefore:c?c.toISOString():void 0,createdAtAfter:u?u.toISOString():void 0,updatedAtBefore:d?d.toISOString():void 0,updatedAtAfter:m?m.toISOString():void 0},S=new Date().getTime(),C=new p.SingleBar({},p.Presets.shades_classic);if(!b){e.info(l.magenta(`Fetching requests...`));let{requests:{totalCount:n}}=await f(r,t,{variables:{filterBy:x},logger:e});n>100&&(e.info(l.magenta(`Fetching ${n} requests`)),C.start(n,0))}let w=[],T=0,E,D=!1;do{let{requests:{nodes:t,pageInfo:i}}=await f(r,n,{variables:{first:100,after:E,filterBy:x},logger:e});b?await y(t):w.push(...t),T+=t.length,E=i.endCursor??void 0,b||C.update(T),D=i.hasNextPage}while(D);if(!b){C.stop();let t=new Date().getTime()-S;e.info(l.green(`Completed fetching of ${T} request in "${t/1e3}" seconds.`))}if(b)return[];let O=w;return v&&v.length>0&&(O=O.filter(e=>v.includes(e.id)),e.info(l.green(`Filtered down to ${O.length} requests based on ${v.length} provided IDs.`))),O}export{g as i,m as n,_ as r,h as t};
|
|
2
|
+
//# sourceMappingURL=fetchAllRequests-BV9K0wXv.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetchAllRequests-BV9K0wXv.mjs","names":[],"sources":["../src/lib/graphql/fetchAllRequests.ts"],"sourcesContent":["import {\n RequestAction,\n RequestOrigin,\n RequestStatus,\n IsoCountryCode,\n IsoCountrySubdivisionCode,\n LocaleCodec,\n} from '@transcend-io/privacy-types';\nimport { makeGraphQLRequest } from '@transcend-io/sdk';\nimport { valuesOf } from '@transcend-io/type-utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport * as t from 'io-ts';\n\nimport { logger } from '../../logger.js';\nimport { REQUESTS, REQUESTS_COUNT } from './gqls/index.js';\n\nexport const RequestPurposeTrigger = t.type({\n title: t.string,\n name: t.string,\n consent: t.boolean,\n enrichedPreferences: t.array(\n t.type({\n topic: t.string,\n selectValues: t.array(\n t.type({\n id: t.string,\n name: t.string,\n preferenceOption: t.type({\n id: t.string,\n slug: t.string,\n title: t.type({\n defaultMessage: t.string,\n }),\n }),\n }),\n ),\n selectValue: t.type({\n id: t.string,\n name: t.string,\n }),\n preferenceTopic: t.type({\n title: t.type({\n defaultMessage: t.string,\n }),\n id: t.string,\n slug: t.string,\n }),\n name: t.string,\n id: t.string,\n booleanValue: t.boolean,\n }),\n ),\n});\n\n/** Override type */\nexport type RequestPurposeTrigger = t.TypeOf<typeof RequestPurposeTrigger>;\n\nexport const PrivacyRequest = t.intersection([\n t.type({\n /** Request ID */\n id: t.string,\n /** Time request was made */\n createdAt: t.string,\n /** Email of request */\n email: t.string,\n /** The type of request */\n type: valuesOf(RequestAction),\n /** Link to request in Transcend dashboard */\n link: t.string,\n /** Whether request is in silent mode */\n isSilent: t.boolean,\n /** Where request was made */\n origin: valuesOf(RequestOrigin),\n /** Whether request is a test request */\n isTest: t.boolean,\n /** The core identifier of the request */\n coreIdentifier: t.string,\n /** Request details */\n details: t.string,\n /** Locale of request */\n locale: LocaleCodec,\n /** Status of request */\n status: valuesOf(RequestStatus),\n /** Type of data subject */\n subjectType: t.string,\n /** Country of request */\n country: t.union([t.null, valuesOf(IsoCountryCode)]),\n /** Subdivision of request */\n countrySubDivision: t.union([t.null, valuesOf(IsoCountrySubdivisionCode)]),\n /** Request attributes */\n attributeValues: t.array(\n t.type({\n id: t.string,\n attributeKey: t.type({ name: t.string, id: t.string }),\n name: t.string,\n }),\n ),\n }),\n t.partial({\n /** Days remaining until expired */\n daysRemaining: t.union([t.null, t.number]),\n /** Time when request was successfully completed */\n successfullyCompletedAt: t.union([t.null, t.string]),\n /** Purpose */\n purpose: RequestPurposeTrigger,\n }),\n]);\n\n/** Type override */\nexport type PrivacyRequest = t.TypeOf<typeof PrivacyRequest>;\n\nconst PAGE_SIZE = 100;\n\n/**\n * Fetch just the total count of requests matching a set of filters.\n * Useful for setting up a progress bar before streaming pages.\n *\n * @param client - GraphQL client\n * @param filterBy - Filter object (already serialized to ISO strings)\n * @returns The total count\n */\nexport async function fetchRequestsTotalCount(\n client: GraphQLClient,\n filterBy: Record<string, unknown>,\n): Promise<number> {\n const {\n requests: { totalCount },\n } = await makeGraphQLRequest<{\n /** Requests */\n requests: {\n /** Total count */\n totalCount: number;\n };\n }>(client, REQUESTS_COUNT, { variables: { filterBy }, logger });\n return totalCount;\n}\n\n/**\n * Fetch all requests matching a set of filters.\n *\n * When `onPage` is provided the function streams pages to the callback\n * and never accumulates nodes in memory — ideal for very large exports.\n * Without `onPage` the function returns all nodes in a single array\n * (existing behaviour, kept for backward compatibility).\n *\n * @param client - GraphQL client\n * @param options - Filter options\n * @returns List of requests (empty when using onPage)\n */\nexport async function fetchAllRequests(\n client: GraphQLClient,\n {\n actions = [],\n statuses = [],\n origins = [],\n text,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n isTest,\n isSilent,\n isClosed,\n requestIds = [],\n onPage,\n }: {\n /** Actions to filter on */\n actions?: RequestAction[];\n /** Origins to filter on */\n origins?: RequestOrigin[];\n /** Statuses to filter on */\n statuses?: RequestStatus[];\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 /** Filter for requests with a specific identifier */\n text?: string;\n /** Return test requests */\n isTest?: boolean;\n /** Return silent mode requests */\n isSilent?: boolean;\n /** Filter by whether request is active */\n isClosed?: boolean;\n /**\n * Filter the list of requests for a set of IDs - these are applied\n * at runtime while other filters are applied at the GraphQL level.\n */\n requestIds?: string[];\n /** When provided, called with each page of nodes instead of accumulating in memory */\n onPage?: (nodes: PrivacyRequest[]) => void | Promise<void>;\n } = {},\n): Promise<PrivacyRequest[]> {\n const streaming = !!onPage;\n\n const filterBy = {\n text,\n type: actions.length > 0 ? actions : undefined,\n status: statuses.length > 0 ? statuses : undefined,\n origin: origins.length > 0 ? origins : undefined,\n isTest,\n isSilent,\n isClosed,\n createdAtBefore: createdAtBefore ? createdAtBefore.toISOString() : undefined,\n createdAtAfter: createdAtAfter ? createdAtAfter.toISOString() : undefined,\n updatedAtBefore: updatedAtBefore ? updatedAtBefore.toISOString() : undefined,\n updatedAtAfter: updatedAtAfter ? updatedAtAfter.toISOString() : undefined,\n };\n\n // When streaming, the caller manages progress/logging — skip count query & bar\n const t0 = new Date().getTime();\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n if (!streaming) {\n logger.info(colors.magenta('Fetching requests...'));\n const {\n requests: { totalCount },\n } = await makeGraphQLRequest<{\n /** Requests */\n requests: {\n /** Total count */\n totalCount: number;\n };\n }>(client, REQUESTS_COUNT, { variables: { filterBy }, logger });\n\n if (totalCount > PAGE_SIZE) {\n logger.info(colors.magenta(`Fetching ${totalCount} requests`));\n progressBar.start(totalCount, 0);\n }\n }\n\n const requests: PrivacyRequest[] = [];\n let fetchedCount = 0;\n\n // Paginate through all results\n let cursor: string | undefined;\n let shouldContinue = false;\n do {\n const {\n requests: { nodes, pageInfo },\n } = await makeGraphQLRequest<{\n /** Requests */\n requests: {\n /** List */\n nodes: PrivacyRequest[];\n /** Pagination info */\n pageInfo: {\n /** Cursor for the last item */\n endCursor: string | null;\n /** Whether more pages exist */\n hasNextPage: boolean;\n };\n };\n }>(client, REQUESTS, {\n variables: { first: PAGE_SIZE, after: cursor, filterBy },\n logger,\n });\n\n if (streaming) {\n await onPage!(nodes);\n } else {\n requests.push(...nodes);\n }\n\n fetchedCount += nodes.length;\n cursor = pageInfo.endCursor ?? undefined;\n if (!streaming) {\n progressBar.update(fetchedCount);\n }\n shouldContinue = pageInfo.hasNextPage;\n } while (shouldContinue);\n\n if (!streaming) {\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Completed fetching of ${fetchedCount} request in \"${totalTime / 1000}\" seconds.`,\n ),\n );\n }\n\n if (streaming) {\n return [];\n }\n\n // Filter down requests by request ID\n let allRequests = requests;\n if (requestIds && requestIds.length > 0) {\n allRequests = allRequests.filter((request) => requestIds.includes(request.id));\n logger.info(\n colors.green(\n `Filtered down to ${allRequests.length} requests based on ${requestIds.length} provided IDs.`,\n ),\n );\n }\n\n return allRequests;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{l as e,n as t}from"./constants-
|
|
2
|
-
//# sourceMappingURL=fuzzyMatchColumns-
|
|
1
|
+
import{l as e,n as t}from"./constants-D6y_AX3P.mjs";import n from"fuzzysearch";import r from"inquirer";function i(e,t){return n(e.toLowerCase(),t.toLowerCase())||n(t.toLowerCase(),e.toLowerCase())}function a(n,a,o,s){let c=n.filter(e=>i(a.toLowerCase(),e.toLowerCase()));return[...c,new r.Separator,...o?[]:[e],...s?[t]:[],...n.filter(e=>!c.includes(e))]}export{i as n,a as t};
|
|
2
|
+
//# sourceMappingURL=fuzzyMatchColumns-Djl3a7lV.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fuzzyMatchColumns-
|
|
1
|
+
{"version":3,"file":"fuzzyMatchColumns-Djl3a7lV.mjs","names":[],"sources":["../src/lib/requests/fuzzyMatchColumns.ts"],"sourcesContent":["import fuzzysearch from 'fuzzysearch';\nimport inquirer from 'inquirer';\n\nimport { NONE, BULK_APPLY } from './constants.js';\n\n/**\n * Check if word1 and word2 are a fuzzy match of each other.\n * Returns true if word1 is fuzzy match of word2 or vice versa.\n *\n * @param word1 - First word\n * @param word2 - Second word\n * @returns True if words are fuzzy match\n */\nexport function fuzzySearch(word1: string, word2: string): boolean {\n return (\n fuzzysearch(word1.toLowerCase(), word2.toLowerCase()) ||\n fuzzysearch(word2.toLowerCase(), word1.toLowerCase())\n );\n}\n\n/**\n * Fuzzy match column names for a particular field\n *\n * @param allColumnNames - List of all column names\n * @param fuzzyMapName - The name of field being mapped to\n * @param isRequired - When true, don't include \"NONE\" as an option\n * @param canApplyAll - When true, include an option to specify the value in bulk\n * @returns The list of suggestions for inquirer\n */\nexport function fuzzyMatchColumns(\n allColumnNames: string[],\n fuzzyMapName: string,\n isRequired: boolean,\n canApplyAll?: boolean,\n): (string | InstanceType<typeof inquirer.Separator>)[] {\n const matchingColumnNames = allColumnNames.filter((x) =>\n fuzzySearch(fuzzyMapName.toLowerCase(), x.toLowerCase()),\n );\n return [\n ...matchingColumnNames,\n new inquirer.Separator(),\n ...(isRequired ? [] : [NONE]),\n ...(canApplyAll ? [BULK_APPLY] : []),\n ...allColumnNames.filter((x) => !matchingColumnNames.includes(x)),\n ];\n}\n"],"mappings":"uGAaA,SAAgB,EAAY,EAAe,EAAwB,CACjE,OACE,EAAY,EAAM,aAAa,CAAE,EAAM,aAAa,CAAC,EACrD,EAAY,EAAM,aAAa,CAAE,EAAM,aAAa,CAAC,CAazD,SAAgB,EACd,EACA,EACA,EACA,EACsD,CACtD,IAAM,EAAsB,EAAe,OAAQ,GACjD,EAAY,EAAa,aAAa,CAAE,EAAE,aAAa,CAAC,CACzD,CACD,MAAO,CACL,GAAG,EACH,IAAI,EAAS,UACb,GAAI,EAAa,EAAE,CAAG,CAAC,EAAK,CAC5B,GAAI,EAAc,CAAC,EAAW,CAAG,EAAE,CACnC,GAAG,EAAe,OAAQ,GAAM,CAAC,EAAoB,SAAS,EAAE,CAAC,CAClE"}
|
package/dist/{generateCrossAccountApiKeys-FtWwlf_L.mjs → generateCrossAccountApiKeys-CjmSyfwm.mjs}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e}from"./constants-
|
|
2
|
-
//# sourceMappingURL=generateCrossAccountApiKeys-
|
|
1
|
+
import{a as e}from"./constants-DAx9VK1C.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import n from"colors";import{mapSeries as r}from"@transcend-io/utils";import{assumeRole as i,buildTranscendGraphQLClientGeneric as a,createApiKey as o,deleteApiKey as s,fetchAllApiKeys as c,loginUser as l}from"@transcend-io/sdk";async function u({email:u,password:d,scopes:f,apiKeyTitle:p,parentOrganizationId:m,deleteExistingApiKey:h=!0,createNewApiKey:g=!0,transcendUrl:_=e}){let v=await a(_,{});t.info(n.magenta(`Logging in using email and password.`));let{roles:y,loginCookie:b}=await l(v,{email:u,password:d,logger:t});t.info(n.green(`Successfully logged in and found ${y.length} role${y.length===1?``:`s`}!`));let x=m?y.filter(e=>e.organization.id===m||e.organization.parentOrganizationId===m):y;v.setHeaders({Cookie:b});let S=[],C=[];return t.info(n.magenta(`Generating API keys with title: ${p}, scopes: ${f.join(`,`)}.`)),await r(x,async e=>{try{await i(v,{roleId:e.id,email:u,logger:t}),t.info(n.magenta(`Checking if API key already exists in organization "${e.organization.name}" with title: "${p}".`));let[r]=await c(v,{logger:t,filterBy:{titles:[p]}});if(r&&h)t.info(n.yellow(`Deleting existing API key in "${e.organization.name}" with title: "${p}".`)),await s(v,{id:r.id,logger:t}),t.info(n.green(`Successfully deleted API key in "${e.organization.name}" with title: "${p}".`));else if(r)throw Error(`API key already exists with title: "${p}"`);if(g){t.info(n.magenta(`Creating API key in "${e.organization.name}" with title: "${p}".`));let{apiKey:r}=await o(v,{input:{title:p,scopes:f},logger:t});S.push({organizationName:e.organization.name,organizationId:e.organization.id,apiKey:r}),t.info(n.green(`Successfully created API key in "${e.organization.name}" with title: "${p}".`))}else S.push({organizationName:e.organization.name,organizationId:e.organization.id,apiKey:``})}catch(r){t.error(n.red(`Failed to create API key in organization "${e.organization.name}"! - ${r.message}`)),C.push({organizationName:e.organization.name,organizationId:e.organization.id,error:r.message})}}),t.info(n.green(`Successfully created ${S.length} API key${S.length===1?``:`s`}`)),C.length>0&&t.error(n.red(`Failed to create ${C.length} API key${C.length===1?``:`s`}!`)),{errors:C,apiKeys:S}}export{u as t};
|
|
2
|
+
//# sourceMappingURL=generateCrossAccountApiKeys-CjmSyfwm.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generateCrossAccountApiKeys-
|
|
1
|
+
{"version":3,"file":"generateCrossAccountApiKeys-CjmSyfwm.mjs","names":[],"sources":["../src/lib/api-keys/generateCrossAccountApiKeys.ts"],"sourcesContent":["import { ScopeName } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClientGeneric,\n loginUser,\n createApiKey,\n fetchAllApiKeys,\n deleteApiKey,\n assumeRole,\n} from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { StoredApiKey } from '../../codecs.js';\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\n\nexport interface ApiKeyGenerateError {\n /** Name of instance */\n organizationName: string;\n /** Error */\n error: string;\n /** Organization ID API key is for */\n organizationId: string;\n}\n\n/**\n * Generate API keys across multiple transcend accounts\n *\n * @param options - Options\n * @returns Number of API keys created\n */\nexport async function generateCrossAccountApiKeys({\n email,\n password,\n scopes,\n apiKeyTitle,\n parentOrganizationId,\n deleteExistingApiKey = true,\n createNewApiKey = true,\n transcendUrl = DEFAULT_TRANSCEND_API,\n}: {\n /** Email address of user generating API keys */\n email: string;\n /** Password of user generating API keys */\n password: string;\n /** Filter for organizations that match this parent organization ID */\n parentOrganizationId?: string;\n /** Title of the API create to create */\n apiKeyTitle: string;\n /** Title of the API create to create */\n scopes: ScopeName[];\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** When true delete existing API keys with that title, if set to false an API key exists with that title, an error is thrown */\n deleteExistingApiKey?: boolean;\n /** When true, generate new API keys, otherwise only will delete past API keys */\n createNewApiKey?: boolean;\n}): Promise<{\n /** Successfully generated */\n apiKeys: StoredApiKey[];\n /** Error results */\n errors: ApiKeyGenerateError[];\n}> {\n // Create GraphQL client\n const client = await buildTranscendGraphQLClientGeneric(transcendUrl, {});\n\n // Login the user\n logger.info(colors.magenta('Logging in using email and password.'));\n const { roles, loginCookie } = await loginUser(client, { email, password, logger });\n logger.info(\n colors.green(\n `Successfully logged in and found ${roles.length} role${roles.length === 1 ? '' : 's'}!`,\n ),\n );\n\n // Filter down by parentOrganizationId\n const filteredRoles = parentOrganizationId\n ? roles.filter(\n (role) =>\n role.organization.id === parentOrganizationId ||\n role.organization.parentOrganizationId === parentOrganizationId,\n )\n : roles;\n\n // Save cookie to call route subsequent times\n client.setHeaders({\n Cookie: loginCookie,\n });\n\n // Save the resulting API keys\n const results: StoredApiKey[] = [];\n const errors: ApiKeyGenerateError[] = [];\n\n // Generate API keys\n logger.info(\n colors.magenta(`Generating API keys with title: ${apiKeyTitle}, scopes: ${scopes.join(',')}.`),\n );\n\n // Map over each role\n await mapSeries(filteredRoles, async (role) => {\n try {\n // Log into the other instance\n await assumeRole(client, { roleId: role.id, email, logger });\n\n // Grab API keys with that title\n logger.info(\n colors.magenta(\n `Checking if API key already exists in organization \"${role.organization.name}\" with title: \"${apiKeyTitle}\".`,\n ),\n );\n\n // Delete existing API key\n const [apiKeyWithTitle] = await fetchAllApiKeys(client, {\n logger,\n filterBy: { titles: [apiKeyTitle] },\n });\n if (apiKeyWithTitle && deleteExistingApiKey) {\n logger.info(\n colors.yellow(\n `Deleting existing API key in \"${role.organization.name}\" with title: \"${apiKeyTitle}\".`,\n ),\n );\n await deleteApiKey(client, { id: apiKeyWithTitle.id, logger });\n logger.info(\n colors.green(\n `Successfully deleted API key in \"${role.organization.name}\" with title: \"${apiKeyTitle}\".`,\n ),\n );\n } else if (apiKeyWithTitle) {\n // throw error if one exists but not configured to delete\n throw new Error(`API key already exists with title: \"${apiKeyTitle}\"`);\n }\n\n // Create the API key\n if (createNewApiKey) {\n logger.info(\n colors.magenta(\n `Creating API key in \"${role.organization.name}\" with title: \"${apiKeyTitle}\".`,\n ),\n );\n const { apiKey } = await createApiKey(client, {\n input: { title: apiKeyTitle, scopes },\n logger,\n });\n results.push({\n organizationName: role.organization.name,\n organizationId: role.organization.id,\n apiKey,\n });\n logger.info(\n colors.green(\n `Successfully created API key in \"${role.organization.name}\" with title: \"${apiKeyTitle}\".`,\n ),\n );\n } else {\n // Delete only\n results.push({\n organizationName: role.organization.name,\n organizationId: role.organization.id,\n apiKey: '',\n });\n }\n } catch (err) {\n logger.error(\n colors.red(\n `Failed to create API key in organization \"${role.organization.name}\"! - ${err.message}`,\n ),\n );\n errors.push({\n organizationName: role.organization.name,\n organizationId: role.organization.id,\n error: err.message,\n });\n }\n });\n logger.info(\n colors.green(\n `Successfully created ${results.length} API key${results.length === 1 ? '' : 's'}`,\n ),\n );\n\n if (errors.length > 0) {\n logger.error(\n colors.red(`Failed to create ${errors.length} API key${errors.length === 1 ? '' : 's'}!`),\n );\n }\n\n return { errors, apiKeys: results };\n}\n"],"mappings":"uTA+BA,eAAsB,EAA4B,CAChD,QACA,WACA,SACA,cACA,uBACA,uBAAuB,GACvB,kBAAkB,GAClB,eAAe,GAuBd,CAED,IAAM,EAAS,MAAM,EAAmC,EAAc,EAAE,CAAC,CAGzE,EAAO,KAAK,EAAO,QAAQ,uCAAuC,CAAC,CACnE,GAAM,CAAE,QAAO,eAAgB,MAAM,EAAU,EAAQ,CAAE,QAAO,WAAU,SAAQ,CAAC,CACnF,EAAO,KACL,EAAO,MACL,oCAAoC,EAAM,OAAO,OAAO,EAAM,SAAW,EAAI,GAAK,IAAI,GACvF,CACF,CAGD,IAAM,EAAgB,EAClB,EAAM,OACH,GACC,EAAK,aAAa,KAAO,GACzB,EAAK,aAAa,uBAAyB,EAC9C,CACD,EAGJ,EAAO,WAAW,CAChB,OAAQ,EACT,CAAC,CAGF,IAAM,EAA0B,EAAE,CAC5B,EAAgC,EAAE,CAgGxC,OA7FA,EAAO,KACL,EAAO,QAAQ,mCAAmC,EAAY,YAAY,EAAO,KAAK,IAAI,CAAC,GAAG,CAC/F,CAGD,MAAM,EAAU,EAAe,KAAO,IAAS,CAC7C,GAAI,CAEF,MAAM,EAAW,EAAQ,CAAE,OAAQ,EAAK,GAAI,QAAO,SAAQ,CAAC,CAG5D,EAAO,KACL,EAAO,QACL,uDAAuD,EAAK,aAAa,KAAK,iBAAiB,EAAY,IAC5G,CACF,CAGD,GAAM,CAAC,GAAmB,MAAM,EAAgB,EAAQ,CACtD,SACA,SAAU,CAAE,OAAQ,CAAC,EAAY,CAAE,CACpC,CAAC,CACF,GAAI,GAAmB,EACrB,EAAO,KACL,EAAO,OACL,iCAAiC,EAAK,aAAa,KAAK,iBAAiB,EAAY,IACtF,CACF,CACD,MAAM,EAAa,EAAQ,CAAE,GAAI,EAAgB,GAAI,SAAQ,CAAC,CAC9D,EAAO,KACL,EAAO,MACL,oCAAoC,EAAK,aAAa,KAAK,iBAAiB,EAAY,IACzF,CACF,SACQ,EAET,MAAU,MAAM,uCAAuC,EAAY,GAAG,CAIxE,GAAI,EAAiB,CACnB,EAAO,KACL,EAAO,QACL,wBAAwB,EAAK,aAAa,KAAK,iBAAiB,EAAY,IAC7E,CACF,CACD,GAAM,CAAE,UAAW,MAAM,EAAa,EAAQ,CAC5C,MAAO,CAAE,MAAO,EAAa,SAAQ,CACrC,SACD,CAAC,CACF,EAAQ,KAAK,CACX,iBAAkB,EAAK,aAAa,KACpC,eAAgB,EAAK,aAAa,GAClC,SACD,CAAC,CACF,EAAO,KACL,EAAO,MACL,oCAAoC,EAAK,aAAa,KAAK,iBAAiB,EAAY,IACzF,CACF,MAGD,EAAQ,KAAK,CACX,iBAAkB,EAAK,aAAa,KACpC,eAAgB,EAAK,aAAa,GAClC,OAAQ,GACT,CAAC,OAEG,EAAK,CACZ,EAAO,MACL,EAAO,IACL,6CAA6C,EAAK,aAAa,KAAK,OAAO,EAAI,UAChF,CACF,CACD,EAAO,KAAK,CACV,iBAAkB,EAAK,aAAa,KACpC,eAAgB,EAAK,aAAa,GAClC,MAAO,EAAI,QACZ,CAAC,GAEJ,CACF,EAAO,KACL,EAAO,MACL,wBAAwB,EAAQ,OAAO,UAAU,EAAQ,SAAW,EAAI,GAAK,MAC9E,CACF,CAEG,EAAO,OAAS,GAClB,EAAO,MACL,EAAO,IAAI,oBAAoB,EAAO,OAAO,UAAU,EAAO,SAAW,EAAI,GAAK,IAAI,GAAG,CAC1F,CAGI,CAAE,SAAQ,QAAS,EAAS"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{o as e,r as t}from"./readTranscendYaml-
|
|
1
|
+
import{o as e,r as t}from"./readTranscendYaml-D576bvzR.mjs";import{t as n}from"./consentManagersToBusinessEntities-DG0_eby5.mjs";import{t as r}from"./listFiles-DDGCBdtx.mjs";import{t as i}from"./done-input-validation-LiTaCEzF.mjs";import a from"node:path";import o from"colors";function s({consentManagerYmlFolder:s,output:c}){i(this.process),(!this.fs.existsSync(s)||!this.fs.lstatSync(s).isDirectory())&&(this.logger.error(o.red(`Folder does not exist: "${s}"`)),this.process.exit(1));let l=n(r(this.fs.readdirSync(s)).map(e=>{let n=a.join(s,e),{"consent-manager":r}=t(this.fs.readFileSync(n,`utf8`),{},n);return{name:e,input:r}}));this.logger.info(`
|
|
2
2
|
|
|
3
3
|
~~~~~~~~~~~
|
|
4
4
|
Airgap scripts to host:`),l.forEach(({attributes:e,title:t},n)=>{e?.find(e=>e.key===`Airgap Production URL`)?.values?.forEach(e=>{this.logger.info(`${n}) ${t} - ${e}`)})}),this.fs.writeFileSync(c,e({"business-entities":l})),this.logger.info(o.green(`Successfully wrote ${l.length} business entities to file "${c}"`))}export{s as consentManagersToBusinessEntities};
|
|
5
|
-
//# sourceMappingURL=impl-
|
|
5
|
+
//# sourceMappingURL=impl-7qTKg-YP2.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"impl-
|
|
1
|
+
{"version":3,"file":"impl-7qTKg-YP2.mjs","names":["consentManagersToBusinessEntitiesHelper"],"sources":["../src/commands/inventory/consent-managers-to-business-entities/impl.ts"],"sourcesContent":["import path from 'node:path';\n\nimport colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { filterFileNames } from '../../../lib/api-keys/index.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { consentManagersToBusinessEntities as consentManagersToBusinessEntitiesHelper } from '../../../lib/consent-manager/index.js';\nimport { parseTranscendYaml, serializeTranscendYaml } from '../../../lib/readTranscendYaml.js';\n\nexport interface ConsentManagersToBusinessEntitiesCommandFlags {\n consentManagerYmlFolder: string;\n output: string;\n}\n\nexport function consentManagersToBusinessEntities(\n this: LocalContext,\n { consentManagerYmlFolder, output }: ConsentManagersToBusinessEntitiesCommandFlags,\n): void {\n doneInputValidation(this.process);\n\n // Ensure folder is passed\n if (\n !this.fs.existsSync(consentManagerYmlFolder) ||\n !this.fs.lstatSync(consentManagerYmlFolder).isDirectory()\n ) {\n this.logger.error(colors.red(`Folder does not exist: \"${consentManagerYmlFolder}\"`));\n this.process.exit(1);\n }\n\n // Read in each consent manager configuration\n const inputs = filterFileNames(this.fs.readdirSync(consentManagerYmlFolder)).map((directory) => {\n const inputPath = path.join(consentManagerYmlFolder, directory);\n const { 'consent-manager': consentManager } = parseTranscendYaml(\n this.fs.readFileSync(inputPath, 'utf8'),\n {},\n inputPath,\n );\n return { name: directory, input: consentManager };\n });\n\n // Convert to business entities\n const businessEntities = consentManagersToBusinessEntitiesHelper(inputs);\n\n this.logger.info('\\n\\n~~~~~~~~~~~\\nAirgap scripts to host:');\n businessEntities.forEach(({ attributes, title }, index) => {\n attributes\n ?.find((attribute) => attribute.key === 'Airgap Production URL')\n ?.values?.forEach((url) => {\n this.logger.info(`${index}) ${title} - ${url}`);\n });\n });\n\n this.fs.writeFileSync(\n output,\n serializeTranscendYaml({\n 'business-entities': businessEntities,\n }),\n );\n\n this.logger.info(\n colors.green(\n `Successfully wrote ${businessEntities.length} business entities to file \"${output}\"`,\n ),\n );\n}\n"],"mappings":"sRAeA,SAAgB,EAEd,CAAE,0BAAyB,UACrB,CACN,EAAoB,KAAK,QAAQ,EAI/B,CAAC,KAAK,GAAG,WAAW,EAAwB,EAC5C,CAAC,KAAK,GAAG,UAAU,EAAwB,CAAC,aAAa,IAEzD,KAAK,OAAO,MAAM,EAAO,IAAI,2BAA2B,EAAwB,GAAG,CAAC,CACpF,KAAK,QAAQ,KAAK,EAAE,EAetB,IAAM,EAAmBA,EAXV,EAAgB,KAAK,GAAG,YAAY,EAAwB,CAAC,CAAC,IAAK,GAAc,CAC9F,IAAM,EAAY,EAAK,KAAK,EAAyB,EAAU,CACzD,CAAE,kBAAmB,GAAmB,EAC5C,KAAK,GAAG,aAAa,EAAW,OAAO,CACvC,EAAE,CACF,EACD,CACD,MAAO,CAAE,KAAM,EAAW,MAAO,EAAgB,EAIoB,CAAC,CAExE,KAAK,OAAO,KAAK;;;yBAA2C,CAC5D,EAAiB,SAAS,CAAE,aAAY,SAAS,IAAU,CACzD,GACI,KAAM,GAAc,EAAU,MAAQ,wBAAwB,EAC9D,QAAQ,QAAS,GAAQ,CACzB,KAAK,OAAO,KAAK,GAAG,EAAM,IAAI,EAAM,KAAK,IAAM,EAC/C,EACJ,CAEF,KAAK,GAAG,cACN,EACA,EAAuB,CACrB,oBAAqB,EACtB,CAAC,CACH,CAED,KAAK,OAAO,KACV,EAAO,MACL,sBAAsB,EAAiB,OAAO,8BAA8B,EAAO,GACpF,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./notifyPrivacyRequestsAdditionalTime-
|
|
2
|
-
//# sourceMappingURL=impl-
|
|
1
|
+
import{t as e}from"./notifyPrivacyRequestsAdditionalTime-DpCfIR_O.mjs";import{t}from"./done-input-validation-LiTaCEzF.mjs";async function n({auth:n,transcendUrl:r,createdAtBefore:i,createdAtAfter:a,updatedAtBefore:o,updatedAtAfter:s,actions:c,daysLeft:l,days:u,requestIds:d,emailTemplate:f,concurrency:p}){t(this.process),await e({transcendUrl:r,requestActions:c,auth:n,emailTemplate:f,days:u,daysLeft:l,requestIds:d,concurrency:p,createdAtBefore:i,createdAtAfter:a,updatedAtBefore:o,updatedAtAfter:s})}export{n as notifyAdditionalTime};
|
|
2
|
+
//# sourceMappingURL=impl-9W0QG6fB.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"impl-
|
|
1
|
+
{"version":3,"file":"impl-9W0QG6fB.mjs","names":[],"sources":["../src/commands/request/notify-additional-time/impl.ts"],"sourcesContent":["import type { RequestAction } from '@transcend-io/privacy-types';\n\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { notifyPrivacyRequestsAdditionalTime } from '../../../lib/requests/index.js';\n\nexport interface NotifyAdditionalTimeCommandFlags {\n auth: string;\n createdAtBefore: Date;\n createdAtAfter?: Date;\n updatedAtBefore?: Date;\n updatedAtAfter?: Date;\n actions?: RequestAction[];\n daysLeft: number;\n days: number;\n requestIds?: string[];\n emailTemplate: string;\n transcendUrl: string;\n concurrency: number;\n}\n\nexport async function notifyAdditionalTime(\n this: LocalContext,\n {\n auth,\n transcendUrl,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n actions,\n daysLeft,\n days,\n requestIds,\n emailTemplate,\n concurrency,\n }: NotifyAdditionalTimeCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n\n await notifyPrivacyRequestsAdditionalTime({\n transcendUrl,\n requestActions: actions,\n auth,\n emailTemplate,\n days,\n daysLeft,\n requestIds,\n concurrency,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n });\n}\n"],"mappings":"2HAqBA,eAAsB,EAEpB,CACE,OACA,eACA,kBACA,iBACA,kBACA,iBACA,UACA,WACA,OACA,aACA,gBACA,eAEa,CACf,EAAoB,KAAK,QAAQ,CAEjC,MAAM,EAAoC,CACxC,eACA,eAAgB,EAChB,OACA,gBACA,OACA,WACA,aACA,cACA,kBACA,iBACA,kBACA,iBACD,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{o as e,r as t}from"./readTranscendYaml-
|
|
2
|
-
//# sourceMappingURL=impl-
|
|
1
|
+
import{o as e,r as t}from"./readTranscendYaml-D576bvzR.mjs";import{t as n}from"./listFiles-DDGCBdtx.mjs";import{t as r}from"./done-input-validation-LiTaCEzF.mjs";import{t as i}from"./dataFlowsToDataSilos-CRqbNjZ0.mjs";import a from"node:path";import o from"colors";import{buildTranscendGraphQLClient as s,fetchAndIndexCatalogs as c}from"@transcend-io/sdk";async function l({auth:l,dataFlowsYmlFolder:u,dataSilosYmlFolder:d,ignoreYmls:f=[],transcendUrl:p}){r(this.process),(!this.fs.existsSync(u)||!this.fs.lstatSync(u).isDirectory())&&(this.logger.error(o.red(`Folder does not exist: "${u}"`)),this.process.exit(1)),(!this.fs.existsSync(d)||!this.fs.lstatSync(d).isDirectory())&&(this.logger.error(o.red(`Folder does not exist: "${d}"`)),this.process.exit(1));let{serviceToTitle:m,serviceToSupportedIntegration:h}=await c(s(p,l),{logger:this.logger});n(this.fs.readdirSync(u)).forEach(n=>{let r=a.join(u,n),{"data-flows":o=[]}=t(this.fs.readFileSync(r,`utf8`),{},r),{adTechDataSilos:s,siteTechDataSilos:c}=i(o,{serviceToSupportedIntegration:h,serviceToTitle:m}),l=[...s,...c];this.logger.log(`Total Services: ${l.length}`),this.logger.log(`Ad Tech Services: ${s.length}`),this.logger.log(`Site Tech Services: ${c.length}`),this.fs.writeFileSync(a.join(d,n),e({"data-silos":f.includes(n)?[]:l}))})}export{l as deriveDataSilosFromDataFlows};
|
|
2
|
+
//# sourceMappingURL=impl-B-BkGfqq2.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"impl-
|
|
1
|
+
{"version":3,"file":"impl-B-BkGfqq2.mjs","names":[],"sources":["../src/commands/inventory/derive-data-silos-from-data-flows/impl.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { buildTranscendGraphQLClient, fetchAndIndexCatalogs } from '@transcend-io/sdk';\nimport colors from 'colors';\n\nimport { DataFlowInput } from '../../../codecs.js';\nimport type { LocalContext } from '../../../context.js';\nimport { filterFileNames } from '../../../lib/api-keys/index.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { dataFlowsToDataSilos } from '../../../lib/consent-manager/dataFlowsToDataSilos.js';\nimport { parseTranscendYaml, serializeTranscendYaml } from '../../../lib/readTranscendYaml.js';\n\nexport interface DeriveDataSilosFromDataFlowsCommandFlags {\n auth: string;\n dataFlowsYmlFolder: string;\n dataSilosYmlFolder: string;\n ignoreYmls?: string[];\n transcendUrl: string;\n}\n\nexport async function deriveDataSilosFromDataFlows(\n this: LocalContext,\n {\n auth,\n dataFlowsYmlFolder,\n dataSilosYmlFolder,\n ignoreYmls = [],\n transcendUrl,\n }: DeriveDataSilosFromDataFlowsCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n\n // Ensure folder is passed\n if (\n !this.fs.existsSync(dataFlowsYmlFolder) ||\n !this.fs.lstatSync(dataFlowsYmlFolder).isDirectory()\n ) {\n this.logger.error(colors.red(`Folder does not exist: \"${dataFlowsYmlFolder}\"`));\n this.process.exit(1);\n }\n\n // Ensure folder is passed\n if (\n !this.fs.existsSync(dataSilosYmlFolder) ||\n !this.fs.lstatSync(dataSilosYmlFolder).isDirectory()\n ) {\n this.logger.error(colors.red(`Folder does not exist: \"${dataSilosYmlFolder}\"`));\n this.process.exit(1);\n }\n\n // Fetch all integrations in the catalog\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n const { serviceToTitle, serviceToSupportedIntegration } = await fetchAndIndexCatalogs(client, {\n logger: this.logger,\n });\n\n // List of each data flow yml file\n filterFileNames(this.fs.readdirSync(dataFlowsYmlFolder)).forEach((directory) => {\n const inputPath = path.join(dataFlowsYmlFolder, directory);\n\n // read in the data flows for a specific instance\n const { 'data-flows': dataFlows = [] } = parseTranscendYaml(\n this.fs.readFileSync(inputPath, 'utf8'),\n {},\n inputPath,\n );\n\n // map the data flows to data silos\n const { adTechDataSilos, siteTechDataSilos } = dataFlowsToDataSilos(\n dataFlows as DataFlowInput[],\n {\n serviceToSupportedIntegration,\n serviceToTitle,\n },\n );\n\n // combine and write to yml file\n const dataSilos = [...adTechDataSilos, ...siteTechDataSilos];\n this.logger.log(`Total Services: ${dataSilos.length}`);\n this.logger.log(`Ad Tech Services: ${adTechDataSilos.length}`);\n this.logger.log(`Site Tech Services: ${siteTechDataSilos.length}`);\n this.fs.writeFileSync(\n path.join(dataSilosYmlFolder, directory),\n serializeTranscendYaml({\n 'data-silos': ignoreYmls.includes(directory) ? [] : dataSilos,\n }),\n );\n });\n}\n"],"mappings":"oWAoBA,eAAsB,EAEpB,CACE,OACA,qBACA,qBACA,aAAa,EAAE,CACf,gBAEa,CACf,EAAoB,KAAK,QAAQ,EAI/B,CAAC,KAAK,GAAG,WAAW,EAAmB,EACvC,CAAC,KAAK,GAAG,UAAU,EAAmB,CAAC,aAAa,IAEpD,KAAK,OAAO,MAAM,EAAO,IAAI,2BAA2B,EAAmB,GAAG,CAAC,CAC/E,KAAK,QAAQ,KAAK,EAAE,GAKpB,CAAC,KAAK,GAAG,WAAW,EAAmB,EACvC,CAAC,KAAK,GAAG,UAAU,EAAmB,CAAC,aAAa,IAEpD,KAAK,OAAO,MAAM,EAAO,IAAI,2BAA2B,EAAmB,GAAG,CAAC,CAC/E,KAAK,QAAQ,KAAK,EAAE,EAKtB,GAAM,CAAE,iBAAgB,iCAAkC,MAAM,EADjD,EAA4B,EAAc,EACmC,CAAE,CAC5F,OAAQ,KAAK,OACd,CAAC,CAGF,EAAgB,KAAK,GAAG,YAAY,EAAmB,CAAC,CAAC,QAAS,GAAc,CAC9E,IAAM,EAAY,EAAK,KAAK,EAAoB,EAAU,CAGpD,CAAE,aAAc,EAAY,EAAE,EAAK,EACvC,KAAK,GAAG,aAAa,EAAW,OAAO,CACvC,EAAE,CACF,EACD,CAGK,CAAE,kBAAiB,qBAAsB,EAC7C,EACA,CACE,gCACA,iBACD,CACF,CAGK,EAAY,CAAC,GAAG,EAAiB,GAAG,EAAkB,CAC5D,KAAK,OAAO,IAAI,mBAAmB,EAAU,SAAS,CACtD,KAAK,OAAO,IAAI,qBAAqB,EAAgB,SAAS,CAC9D,KAAK,OAAO,IAAI,uBAAuB,EAAkB,SAAS,CAClE,KAAK,GAAG,cACN,EAAK,KAAK,EAAoB,EAAU,CACxC,EAAuB,CACrB,aAAc,EAAW,SAAS,EAAU,CAAG,EAAE,CAAG,EACrD,CAAC,CACH,EACD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{t as e}from"./constants-
|
|
1
|
+
import{t as e}from"./constants-DAx9VK1C.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{t as n}from"./done-input-validation-LiTaCEzF.mjs";import{n as r}from"./constants-DC_bBO09.mjs";import i from"colors";import a from"fast-glob";import{buildTranscendGraphQLClient as o,fetchActiveSiloDiscoPlugin as s,uploadSiloDiscoveryResults as c}from"@transcend-io/sdk";import{stringify as l}from"query-string";async function u({scanPath:e,fileGlobs:n,ignoreDirs:r,config:i}){let{ignoreDirs:o,supportedFiles:s,scanFunction:c}=i,l=n===``?s:s.concat(n.split(`,`)),d=[...r.split(`,`),...o].filter(e=>e.length>0);try{let n=await a(`${e}/**/${l.join(`|`)}`,{ignore:d.map(t=>`${e}/**/${t}`),unique:!0,onlyFiles:!0});t.info(`Scanning: ${n.length} files`);let r=n.map(e=>c(e)).flat().map(e=>e.softwareDevelopmentKits||[]).flat(),i=[...new Set(r.map(e=>e.name))];return t.info(`Found: ${i.length} unique dependencies`),i.map(t=>({name:t,resourceId:`${e}/**/${t}`,useStrictClassifier:!0}))}catch(e){throw Error(`Error scanning globs ${u} with error: ${e}`)}}async function d({scanPath:t,dataSiloId:a,auth:d,fileGlobs:f,ignoreDirs:p,transcendUrl:m}){n(this.process);let h=o(m,d),g=await s(h,{logger:this.logger,filterBy:{dataSiloId:a}}),_=r[g.dataSilo.type];_||(this.logger.error(i.red(`This plugin "${g.dataSilo.type}" is not supported for offline silo discovery.`)),this.process.exit(1));let v=await u({scanPath:t,fileGlobs:f,ignoreDirs:p,config:_});await c(h,{pluginId:g.id,results:v,logger:this.logger});let y=new URL(e);y.pathname=`/data-map/data-inventory/silo-discovery/triage`,y.search=l({filters:JSON.stringify({pluginIds:[g.id]})}),this.logger.info(i.green(`Scan found ${v.length} potential data silos at ${t}! View at '${y.href}'
|
|
2
2
|
|
|
3
3
|
NOTE: it may take 2-3 minutes for scan results to appear in the UI.`))}export{d as discoverSilos};
|
|
4
|
-
//# sourceMappingURL=impl-
|
|
4
|
+
//# sourceMappingURL=impl-B8DsKzNn2.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"impl-
|
|
1
|
+
{"version":3,"file":"impl-B8DsKzNn2.mjs","names":[],"sources":["../src/lib/code-scanning/findFilesToScan.ts","../src/commands/inventory/discover-silos/impl.ts"],"sourcesContent":["import fastGlob from 'fast-glob';\n\nimport { logger } from '../../logger.js';\nimport { CodeScanningConfig } from './types.js';\n\nexport interface SiloDiscoveryRawResults {\n /** The name of the potential data silo entry */\n name: string;\n /** A unique UUID (represents the same resource across different silo discovery runs) */\n resourceId: string;\n /** Any hosts associated with the entry */\n host?: string;\n /** Type of data silo */\n type?: string | undefined;\n}\n\n/**\n * Helper to scan for data silos in all package.json files that it can find in a directory\n *\n * @deprecated TODO: https://transcend.height.app/T-32325 - use code scanning instead\n * @param options - Options\n * @returns the list of integrations\n */\nexport async function findFilesToScan({\n scanPath,\n fileGlobs,\n ignoreDirs,\n config,\n}: {\n /** Where to look for package.json files */\n scanPath: string;\n /** Globs to look for */\n fileGlobs: string;\n /** The directories to ignore (excludes node_modules and serverless-build) */\n ignoreDirs: string;\n /** Silo Discovery configuration */\n config: CodeScanningConfig;\n}): Promise<SiloDiscoveryRawResults[]> {\n const { ignoreDirs: IGNORE_DIRS, supportedFiles, scanFunction } = config;\n const globsToSupport =\n fileGlobs === '' ? supportedFiles : supportedFiles.concat(fileGlobs.split(','));\n const dirsToIgnore = [...ignoreDirs.split(','), ...IGNORE_DIRS].filter((dir) => dir.length > 0);\n try {\n const filesToScan: string[] = await fastGlob(`${scanPath}/**/${globsToSupport.join('|')}`, {\n ignore: dirsToIgnore.map((dir: string) => `${scanPath}/**/${dir}`),\n unique: true,\n onlyFiles: true,\n });\n logger.info(`Scanning: ${filesToScan.length} files`);\n const allPackages = filesToScan.map((filePath: string) => scanFunction(filePath)).flat();\n const allSdks = allPackages\n .map((appPackage) => appPackage.softwareDevelopmentKits || [])\n .flat();\n const uniqueDeps = new Set(allSdks.map((sdk) => sdk.name));\n const deps = [...uniqueDeps];\n logger.info(`Found: ${deps.length} unique dependencies`);\n return deps.map((dep) => ({\n name: dep,\n resourceId: `${scanPath}/**/${dep}`,\n useStrictClassifier: true,\n }));\n } catch (error) {\n throw new Error(`Error scanning globs ${findFilesToScan} with error: ${error}`);\n }\n}\n","import {\n buildTranscendGraphQLClient,\n fetchActiveSiloDiscoPlugin,\n uploadSiloDiscoveryResults,\n} from '@transcend-io/sdk';\nimport colors from 'colors';\nimport { stringify } from 'query-string';\n\nimport { ADMIN_DASH } from '../../../constants.js';\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { findFilesToScan } from '../../../lib/code-scanning/findFilesToScan.js';\nimport { SILO_DISCOVERY_CONFIGS } from '../../../lib/code-scanning/index.js';\n\nexport interface DiscoverSilosCommandFlags {\n scanPath: string;\n dataSiloId: string;\n auth: string;\n fileGlobs: string;\n ignoreDirs: string;\n transcendUrl: string;\n}\n\nexport async function discoverSilos(\n this: LocalContext,\n { scanPath, dataSiloId, auth, fileGlobs, ignoreDirs, transcendUrl }: DiscoverSilosCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n\n // Create a GraphQL client\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n const plugin = await fetchActiveSiloDiscoPlugin(client, {\n logger: this.logger,\n filterBy: { dataSiloId },\n });\n\n const config = SILO_DISCOVERY_CONFIGS[plugin.dataSilo.type];\n if (!config) {\n this.logger.error(\n colors.red(\n `This plugin \"${plugin.dataSilo.type}\" is not supported for offline silo discovery.`,\n ),\n );\n this.process.exit(1);\n }\n\n const results = await findFilesToScan({\n scanPath,\n fileGlobs,\n ignoreDirs,\n config,\n });\n\n await uploadSiloDiscoveryResults(client, {\n pluginId: plugin.id,\n results,\n logger: this.logger,\n });\n\n const newUrl = new URL(ADMIN_DASH);\n newUrl.pathname = '/data-map/data-inventory/silo-discovery/triage';\n newUrl.search = stringify({\n filters: JSON.stringify({ pluginIds: [plugin.id] }),\n });\n\n // Indicate success\n this.logger.info(\n colors.green(\n `Scan found ${results.length} potential data silos at ${scanPath}! ` +\n `View at '${newUrl.href}' ` +\n '\\n\\n NOTE: it may take 2-3 minutes for scan results to appear in the UI.',\n ),\n );\n}\n"],"mappings":"gZAuBA,eAAsB,EAAgB,CACpC,WACA,YACA,aACA,UAUqC,CACrC,GAAM,CAAE,WAAY,EAAa,iBAAgB,gBAAiB,EAC5D,EACJ,IAAc,GAAK,EAAiB,EAAe,OAAO,EAAU,MAAM,IAAI,CAAC,CAC3E,EAAe,CAAC,GAAG,EAAW,MAAM,IAAI,CAAE,GAAG,EAAY,CAAC,OAAQ,GAAQ,EAAI,OAAS,EAAE,CAC/F,GAAI,CACF,IAAM,EAAwB,MAAM,EAAS,GAAG,EAAS,MAAM,EAAe,KAAK,IAAI,GAAI,CACzF,OAAQ,EAAa,IAAK,GAAgB,GAAG,EAAS,MAAM,IAAM,CAClE,OAAQ,GACR,UAAW,GACZ,CAAC,CACF,EAAO,KAAK,aAAa,EAAY,OAAO,QAAQ,CAEpD,IAAM,EADc,EAAY,IAAK,GAAqB,EAAa,EAAS,CAAC,CAAC,MACvD,CACxB,IAAK,GAAe,EAAW,yBAA2B,EAAE,CAAC,CAC7D,MAAM,CAEH,EAAO,CAAC,GAAG,IADM,IAAI,EAAQ,IAAK,GAAQ,EAAI,KAAK,CAC9B,CAAC,CAE5B,OADA,EAAO,KAAK,UAAU,EAAK,OAAO,sBAAsB,CACjD,EAAK,IAAK,IAAS,CACxB,KAAM,EACN,WAAY,GAAG,EAAS,MAAM,IAC9B,oBAAqB,GACtB,EAAE,OACI,EAAO,CACd,MAAU,MAAM,wBAAwB,EAAgB,eAAe,IAAQ,ECvCnF,eAAsB,EAEpB,CAAE,WAAU,aAAY,OAAM,YAAW,aAAY,gBACtC,CACf,EAAoB,KAAK,QAAQ,CAGjC,IAAM,EAAS,EAA4B,EAAc,EAAK,CAExD,EAAS,MAAM,EAA2B,EAAQ,CACtD,OAAQ,KAAK,OACb,SAAU,CAAE,aAAY,CACzB,CAAC,CAEI,EAAS,EAAuB,EAAO,SAAS,MACjD,IACH,KAAK,OAAO,MACV,EAAO,IACL,gBAAgB,EAAO,SAAS,KAAK,gDACtC,CACF,CACD,KAAK,QAAQ,KAAK,EAAE,EAGtB,IAAM,EAAU,MAAM,EAAgB,CACpC,WACA,YACA,aACA,SACD,CAAC,CAEF,MAAM,EAA2B,EAAQ,CACvC,SAAU,EAAO,GACjB,UACA,OAAQ,KAAK,OACd,CAAC,CAEF,IAAM,EAAS,IAAI,IAAI,EAAW,CAClC,EAAO,SAAW,iDAClB,EAAO,OAAS,EAAU,CACxB,QAAS,KAAK,UAAU,CAAE,UAAW,CAAC,EAAO,GAAG,CAAE,CAAC,CACpD,CAAC,CAGF,KAAK,OAAO,KACV,EAAO,MACL,cAAc,EAAQ,OAAO,2BAA2B,EAAS,aACnD,EAAO,KAAK;;sEAE3B,CACF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{c as e,i as t,n,t as r}from"./policy-project-discovery-CND7N2HI.mjs";import{n as i}from"./policy-bundle-manifest-DW3Wilp8.mjs";import{t as a}from"./done-input-validation-LiTaCEzF.mjs";import{t as o}from"./run-captured-process-BjaSDWXG.mjs";import{i as s,n as c,r as l,t as u}from"./runRegalLintStep-DPaMm7Gg.mjs";import d from"node:path";import f from"colors";const p=[`manifest`,`opa-version`,`regal-version`,`format`,`opa-check`,`regal-lint`,`opa-test`],m={manifest:`Manifest and package roots`,"opa-version":`OPA 1.x`,"regal-version":`Regal`,format:`OPA formatting`,"opa-check":`OPA strict check`,"regal-lint":`Regal lint`,"opa-test":`OPA tests`};function h(e,t,n=t){return e.fs.readdirSync(n,{withFileTypes:!0}).sort((e,t)=>e.name.localeCompare(t.name)).flatMap(r=>{let i=d.join(n,r.name);return r.isDirectory()?h(e,t,i):!r.isFile()||!r.name.endsWith(`.rego`)?[]:[{path:d.relative(t,i).split(d.sep).join(`/`),contents:e.fs.readFileSync(i,`utf8`)}]})}function g(e,t,n){n?e.logger.info(`${f.bold(n)}\n`):e.logger.info(`${f.bold(`Policy verification`)}\n`),t.checks.forEach(({name:n,status:r})=>{let i=r===`passed`?`PASS`:r===`failed`?`FAIL`:`SKIP`,a=r===`passed`?f.green(i):r===`failed`?f.red(i):f.yellow(i),o=n===`opa-version`&&t.tools.opa?` (${t.tools.opa})`:n===`regal-version`&&t.tools.regal?` (${t.tools.regal})`:``;e.logger.info(`${a} ${m[n]}${o}`)}),t.diagnostics.length>0&&e.logger.error(``),t.diagnostics.forEach(t=>{let n=t.path?`${t.path}: `:``;e.logger.error(f.red(`error [${t.code}]: ${n}${t.message}`))}),t.status===`passed`?(e.logger.info(``),e.logger.info(f.green(`Policy verification passed.`))):(e.logger.error(``),e.logger.error(f.red(`Policy verification failed.`)))}async function _({fix:t,noInteractive:n,json:r},a,o){let f=p.map(e=>({name:e,status:`skipped`})),m=[],g={version:1,status:`passed`,directory:a,fix:t,tools:{opa:null,regal:null},checks:f,unformattedFiles:[],fixedFiles:[],diagnostics:m},_=(e,t)=>{f.find(t=>t.name===e).status=t},v=(e,t,n)=>{m.push({code:e,severity:`error`,message:t,...n?{path:n}:{}})};if(!this.fs.existsSync(a)||!this.fs.statSync(a).isDirectory())return _(`manifest`,`failed`),v(`project.directory`,`Policy directory does not exist or is not a directory: ${a}`,d.relative(this.process.cwd(),a)),g.status=`failed`,g;let y=d.join(a,e);try{i(this.fs.existsSync(y)?this.fs.readFileSync(y,`utf8`):void 0,h(this,a)),_(`manifest`,`passed`)}catch(e){_(`manifest`,`failed`),v(`manifest.invalid`,e instanceof Error?e.message:String(e),d.relative(this.process.cwd(),y))}let b=await l.call(this,a,o);if(g.tools={opa:b.opa,regal:b.regal},_(`opa-version`,b.opaStatus),_(`regal-version`,b.regalStatus),b.diagnostics.forEach(e=>{v(e.code,e.message)}),g.tools.opa){let e=await c.call(this,{resolvedDir:a,fix:t,noInteractive:n,json:r,fixCommandHint:`transcend policy check --fix`,runner:o});g.unformattedFiles=e.unformattedFiles,g.fixedFiles=e.fixedFiles,_(`format`,e.status),e.diagnostics.forEach(e=>{v(e.code,e.message)});let i=await o(`opa`,[`check`,`--strict`,`--ignore`,`*_test.rego`,a],{cwd:a},this);i.code===0?_(`opa-check`,`passed`):(_(`opa-check`,`failed`),v(`opa.check`,s(i,`opa check failed with exit code ${i.code}`)))}if(g.tools.regal){let e=await u.call(this,{resolvedDir:a,runner:o});_(`regal-lint`,e.status),e.diagnostics.forEach(e=>{v(e.code,e.message)})}if(g.tools.opa){let e=await o(`opa`,[`test`,`--fail-on-empty`,`-b`,a],{cwd:a},this);e.code===0?_(`opa-test`,`passed`):(_(`opa-test`,`failed`),v(`opa.test`,s(e,`opa test --fail-on-empty -b failed with exit code ${e.code}`)))}return g.status=f.some(({status:e})=>e===`failed`)?`failed`:`passed`,g}async function v(e,i=r,s=o){a(this.process);let{fix:c=!1,noInteractive:l=!1,json:u=!1}=e,f=t(this.process.cwd(),i);if(!this.fs.existsSync(f)||!this.fs.statSync(f).isDirectory()){let e={version:1,status:`failed`,directory:f,fix:c,tools:{opa:null,regal:null},checks:p.map(e=>({name:e,status:e===`manifest`?`failed`:`skipped`})),unformattedFiles:[],fixedFiles:[],diagnostics:[{code:`project.directory`,severity:`error`,message:`Policy directory does not exist or is not a directory: ${f}`,path:d.relative(this.process.cwd(),f)}]};u?this.process.stdout.write(`${JSON.stringify(e)}\n`):g(this,e),this.process.exitCode=1;return}let m=n(this,f);if(m.length===0){let e={version:1,status:`failed`,directory:f,fix:c,tools:{opa:null,regal:null},checks:p.map(e=>({name:e,status:e===`manifest`?`failed`:`skipped`})),unformattedFiles:[],fixedFiles:[],diagnostics:[{code:`project.bundles`,severity:`error`,message:"No publishable policy bundles found. Add one with `transcend policy new`, or pass a directory that contains a `.manifest`.",path:d.relative(this.process.cwd(),f)||`.`}]};u?this.process.stdout.write(`${JSON.stringify(e)}\n`):g(this,e),this.process.exitCode=1;return}let h=[];for(let e of m)h.push(await _.call(this,{fix:c,noInteractive:l,json:u},e,s));let v=h.some(e=>e.status===`failed`);if(u)if(h.length===1)this.process.stdout.write(`${JSON.stringify(h[0])}\n`);else{let e={version:2,status:v?`failed`:`passed`,directory:f,fix:c,results:h};this.process.stdout.write(`${JSON.stringify(e)}\n`)}else h.length===1?g(this,h[0]):h.forEach((e,t)=>{t>0&&this.logger.info(``);let n=d.relative(this.process.cwd(),e.directory)||e.directory;g(this,e,`Policy verification — ${n}`)});v&&(this.process.exitCode=1)}export{v as check};
|
|
2
|
+
//# sourceMappingURL=impl-B9jwlKCX2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impl-B9jwlKCX2.mjs","names":[],"sources":["../src/lib/policy/policy-check-model.ts","../src/commands/policy/helpers/collectPolicyRegoFiles.ts","../src/commands/policy/check/impl.ts"],"sourcesContent":["/** Stable policy check result schema version. */\nexport const POLICY_CHECK_RESULT_VERSION = 1;\n\n/** Stable multi-bundle check result schema version. */\nexport const POLICY_CHECK_MULTI_RESULT_VERSION = 2;\n\n/** Status of the full gate or an individual verification. */\nexport type PolicyCheckStatus = 'passed' | 'failed' | 'skipped';\n\n/** One named policy verification. */\nexport interface PolicyCheckStep {\n /** Stable verification name. */\n name:\n | 'manifest'\n | 'opa-version'\n | 'regal-version'\n | 'format'\n | 'opa-check'\n | 'regal-lint'\n | 'opa-test';\n /** Verification outcome. */\n status: PolicyCheckStatus;\n}\n\n/** Stable verification execution order. */\nexport const POLICY_CHECK_STEP_NAMES: PolicyCheckStep['name'][] = [\n 'manifest',\n 'opa-version',\n 'regal-version',\n 'format',\n 'opa-check',\n 'regal-lint',\n 'opa-test',\n];\n\n/** User-facing verification labels. */\nexport const POLICY_CHECK_STEP_LABELS: Readonly<Record<PolicyCheckStep['name'], string>> = {\n manifest: 'Manifest and package roots',\n 'opa-version': 'OPA 1.x',\n 'regal-version': 'Regal',\n format: 'OPA formatting',\n 'opa-check': 'OPA strict check',\n 'regal-lint': 'Regal lint',\n 'opa-test': 'OPA tests',\n};\n\n/** One machine-readable policy check diagnostic. */\nexport interface PolicyCheckDiagnostic {\n /** Stable diagnostic identifier. */\n code: string;\n /** Diagnostic severity. */\n severity: 'error';\n /** Human-readable detail, including captured tool output when available. */\n message: string;\n /** Optional path relative to the invocation directory. */\n path?: string;\n}\n\n/** Detected local policy tool versions. */\nexport interface PolicyCheckTools {\n /** Detected OPA version, or null when missing or unsupported. */\n opa: string | null;\n /** Detected Regal version, or null when missing or unsupported. */\n regal: string | null;\n}\n\n/** Stable result emitted by `transcend policy check --json` for one bundle. */\nexport interface PolicyCheckResult {\n /** Result schema version. */\n version: typeof POLICY_CHECK_RESULT_VERSION;\n /** Overall verification outcome. */\n status: Exclude<PolicyCheckStatus, 'skipped'>;\n /** Absolute policy bundle directory. */\n directory: string;\n /** Whether formatting repair was explicitly requested. */\n fix: boolean;\n /** Detected supported tool versions. */\n tools: PolicyCheckTools;\n /** Verification outcomes in stable execution order. */\n checks: PolicyCheckStep[];\n /** Files found to need OPA formatting, relative to the policy directory. */\n unformattedFiles: string[];\n /** Files repaired with OPA formatting, relative to the policy directory. */\n fixedFiles: string[];\n /** Ordered verification diagnostics. */\n diagnostics: PolicyCheckDiagnostic[];\n}\n\n/**\n * Stable result when `transcend policy check --json` verifies multiple bundles.\n *\n * Emitted only when the selected path is a workspace with more than one\n * publishable `*-bundle/` directory.\n */\nexport interface PolicyCheckMultiResult {\n /** Result schema version. */\n version: typeof POLICY_CHECK_MULTI_RESULT_VERSION;\n /** Overall verification outcome across all bundles. */\n status: Exclude<PolicyCheckStatus, 'skipped'>;\n /** Absolute policy workspace directory that was searched. */\n directory: string;\n /** Whether formatting repair was explicitly requested. */\n fix: boolean;\n /** Per-bundle results in discovery order. */\n results: PolicyCheckResult[];\n}\n","import path from 'node:path';\n\nimport type { LocalContext } from '../../../context.js';\nimport type { PolicyBundleRegoFile } from '../../../lib/policy/policy-bundle-manifest.js';\n\n/**\n * Collect Rego source snapshots without following symlinks.\n *\n * @param context - CLI context\n * @param projectDirectory - Absolute policy directory\n * @param currentDirectory - Current recursive directory\n * @returns Deterministically ordered Rego snapshots\n */\nexport function collectPolicyRegoFiles(\n context: LocalContext,\n projectDirectory: string,\n currentDirectory: string = projectDirectory,\n): PolicyBundleRegoFile[] {\n return context.fs\n .readdirSync(currentDirectory, { withFileTypes: true })\n .sort((left, right) => left.name.localeCompare(right.name))\n .flatMap((entry) => {\n const absolutePath = path.join(currentDirectory, entry.name);\n if (entry.isDirectory()) {\n return collectPolicyRegoFiles(context, projectDirectory, absolutePath);\n }\n if (!entry.isFile() || !entry.name.endsWith('.rego')) {\n return [];\n }\n return [\n {\n path: path.relative(projectDirectory, absolutePath).split(path.sep).join('/'),\n contents: context.fs.readFileSync(absolutePath, 'utf8'),\n },\n ];\n });\n}\n","import path from 'node:path';\n\nimport colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport {\n runCapturedProcess,\n type CapturedProcessRunner,\n} from '../../../lib/cli/run-captured-process.js';\nimport { validatePolicyBundleContents } from '../../../lib/policy/policy-bundle-manifest.js';\nimport {\n type PolicyCheckDiagnostic,\n type PolicyCheckMultiResult,\n type PolicyCheckResult,\n type PolicyCheckStep,\n POLICY_CHECK_MULTI_RESULT_VERSION,\n POLICY_CHECK_RESULT_VERSION,\n POLICY_CHECK_STEP_LABELS,\n POLICY_CHECK_STEP_NAMES,\n} from '../../../lib/policy/policy-check-model.js';\nimport { getPolicyProcessFailureMessage } from '../../../lib/policy/policy-process-output.js';\nimport {\n DEFAULT_POLICY_PROJECT_DIRECTORY,\n discoverPolicyBundleDirectories,\n resolvePolicyProjectDirectory,\n} from '../../../lib/policy/policy-project-discovery.js';\nimport { POLICY_MANIFEST_FILENAME } from '../../../lib/policy/policy-scaffold-templates.js';\nimport { collectPolicyRegoFiles } from '../helpers/collectPolicyRegoFiles.js';\nimport { probePolicyToolVersions } from '../helpers/probePolicyToolVersions.js';\nimport { runOpaFormatStep } from '../helpers/runOpaFormatStep.js';\nimport { runRegalLintStep } from '../helpers/runRegalLintStep.js';\n\n/** CLI flags for `transcend policy check`. */\nexport interface CheckCommandFlags {\n /** Apply OPA formatting. */\n fix: boolean;\n /** Disable the optional formatting confirmation. */\n noInteractive: boolean;\n /** Emit one stable JSON result on stdout. */\n json: boolean;\n}\n\n/**\n * Render the terminal summary for a policy verification result.\n *\n * @param context - CLI context\n * @param result - Completed verification result\n * @param heading - Optional section heading when checking multiple bundles\n */\nfunction renderResult(context: LocalContext, result: PolicyCheckResult, heading?: string): void {\n if (heading) {\n context.logger.info(`${colors.bold(heading)}\\n`);\n } else {\n context.logger.info(`${colors.bold('Policy verification')}\\n`);\n }\n result.checks.forEach(({ name, status }) => {\n const label = status === 'passed' ? 'PASS' : status === 'failed' ? 'FAIL' : 'SKIP';\n const styledLabel =\n status === 'passed'\n ? colors.green(label)\n : status === 'failed'\n ? colors.red(label)\n : colors.yellow(label);\n const version =\n name === 'opa-version' && result.tools.opa\n ? ` (${result.tools.opa})`\n : name === 'regal-version' && result.tools.regal\n ? ` (${result.tools.regal})`\n : '';\n context.logger.info(`${styledLabel} ${POLICY_CHECK_STEP_LABELS[name]}${version}`);\n });\n if (result.diagnostics.length > 0) {\n context.logger.error('');\n }\n result.diagnostics.forEach((diagnostic) => {\n const location = diagnostic.path ? `${diagnostic.path}: ` : '';\n context.logger.error(\n colors.red(`error [${diagnostic.code}]: ${location}${diagnostic.message}`),\n );\n });\n if (result.status === 'passed') {\n context.logger.info('');\n context.logger.info(colors.green('Policy verification passed.'));\n } else {\n context.logger.error('');\n context.logger.error(colors.red('Policy verification failed.'));\n }\n}\n\n/**\n * Verify one publishable policy bundle directory.\n *\n * @param this - CLI context\n * @param flags - Command flags\n * @param resolvedDir - Absolute bundle directory\n * @param runner - Captured subprocess runner\n * @returns Completed verification result\n */\nasync function checkBundle(\n this: LocalContext,\n { fix, noInteractive, json }: CheckCommandFlags,\n resolvedDir: string,\n runner: CapturedProcessRunner,\n): Promise<PolicyCheckResult> {\n const checks: PolicyCheckStep[] = POLICY_CHECK_STEP_NAMES.map((name) => ({\n name,\n status: 'skipped',\n }));\n const diagnostics: PolicyCheckDiagnostic[] = [];\n const result: PolicyCheckResult = {\n version: POLICY_CHECK_RESULT_VERSION,\n status: 'passed',\n directory: resolvedDir,\n fix,\n tools: { opa: null, regal: null },\n checks,\n unformattedFiles: [],\n fixedFiles: [],\n diagnostics,\n };\n const setStatus = (name: PolicyCheckStep['name'], status: PolicyCheckStep['status']): void => {\n checks.find((check) => check.name === name)!.status = status;\n };\n const addError = (code: string, message: string, diagnosticPath?: string): void => {\n diagnostics.push({\n code,\n severity: 'error',\n message,\n ...(diagnosticPath ? { path: diagnosticPath } : {}),\n });\n };\n\n if (!this.fs.existsSync(resolvedDir) || !this.fs.statSync(resolvedDir).isDirectory()) {\n setStatus('manifest', 'failed');\n addError(\n 'project.directory',\n `Policy directory does not exist or is not a directory: ${resolvedDir}`,\n path.relative(this.process.cwd(), resolvedDir),\n );\n result.status = 'failed';\n return result;\n }\n\n const manifestPath = path.join(resolvedDir, POLICY_MANIFEST_FILENAME);\n try {\n validatePolicyBundleContents(\n this.fs.existsSync(manifestPath) ? this.fs.readFileSync(manifestPath, 'utf8') : undefined,\n collectPolicyRegoFiles(this, resolvedDir),\n );\n setStatus('manifest', 'passed');\n } catch (error) {\n setStatus('manifest', 'failed');\n addError(\n 'manifest.invalid',\n error instanceof Error ? error.message : String(error),\n path.relative(this.process.cwd(), manifestPath),\n );\n }\n\n const toolVersions = await probePolicyToolVersions.call(this, resolvedDir, runner);\n result.tools = { opa: toolVersions.opa, regal: toolVersions.regal };\n setStatus('opa-version', toolVersions.opaStatus);\n setStatus('regal-version', toolVersions.regalStatus);\n toolVersions.diagnostics.forEach((diagnostic) => {\n addError(diagnostic.code, diagnostic.message);\n });\n\n if (result.tools.opa) {\n const formatStep = await runOpaFormatStep.call(this, {\n resolvedDir,\n fix,\n noInteractive,\n json,\n fixCommandHint: 'transcend policy check --fix',\n runner,\n });\n result.unformattedFiles = formatStep.unformattedFiles;\n result.fixedFiles = formatStep.fixedFiles;\n setStatus('format', formatStep.status);\n formatStep.diagnostics.forEach((diagnostic) => {\n addError(diagnostic.code, diagnostic.message);\n });\n\n const checkResult = await runner(\n 'opa',\n ['check', '--strict', '--ignore', '*_test.rego', resolvedDir],\n { cwd: resolvedDir },\n this,\n );\n if (checkResult.code === 0) {\n setStatus('opa-check', 'passed');\n } else {\n setStatus('opa-check', 'failed');\n addError(\n 'opa.check',\n getPolicyProcessFailureMessage(\n checkResult,\n `opa check failed with exit code ${checkResult.code}`,\n ),\n );\n }\n }\n\n if (result.tools.regal) {\n const regalStep = await runRegalLintStep.call(this, { resolvedDir, runner });\n setStatus('regal-lint', regalStep.status);\n regalStep.diagnostics.forEach((diagnostic) => {\n addError(diagnostic.code, diagnostic.message);\n });\n }\n\n if (result.tools.opa) {\n const testResult = await runner(\n 'opa',\n ['test', '--fail-on-empty', '-b', resolvedDir],\n { cwd: resolvedDir },\n this,\n );\n if (testResult.code === 0) {\n setStatus('opa-test', 'passed');\n } else {\n setStatus('opa-test', 'failed');\n addError(\n 'opa.test',\n getPolicyProcessFailureMessage(\n testResult,\n `opa test --fail-on-empty -b failed with exit code ${testResult.code}`,\n ),\n );\n }\n }\n\n result.status = checks.some(({ status }) => status === 'failed') ? 'failed' : 'passed';\n return result;\n}\n\n/**\n * Verify local policy bundles with the shared upload contract, OPA, and Regal.\n *\n * With no directory (or the default workspace path), discovers every immediate\n * child that contains a `.manifest` and verifies each. Pass one bundle path to\n * verify a single publishable unit.\n *\n * @param this - CLI context\n * @param flags - Command flags\n * @param directory - Policy workspace or bundle directory\n * @param runner - Captured subprocess runner\n */\nexport async function check(\n this: LocalContext,\n flags: CheckCommandFlags,\n directory: string = DEFAULT_POLICY_PROJECT_DIRECTORY,\n runner: CapturedProcessRunner = runCapturedProcess,\n): Promise<void> {\n doneInputValidation(this.process);\n\n const { fix = false, noInteractive = false, json = false } = flags;\n const resolvedDir = resolvePolicyProjectDirectory(this.process.cwd(), directory);\n\n if (!this.fs.existsSync(resolvedDir) || !this.fs.statSync(resolvedDir).isDirectory()) {\n const missing: PolicyCheckResult = {\n version: POLICY_CHECK_RESULT_VERSION,\n status: 'failed',\n directory: resolvedDir,\n fix,\n tools: { opa: null, regal: null },\n checks: POLICY_CHECK_STEP_NAMES.map((name) => ({\n name,\n status: name === 'manifest' ? 'failed' : 'skipped',\n })),\n unformattedFiles: [],\n fixedFiles: [],\n diagnostics: [\n {\n code: 'project.directory',\n severity: 'error',\n message: `Policy directory does not exist or is not a directory: ${resolvedDir}`,\n path: path.relative(this.process.cwd(), resolvedDir),\n },\n ],\n };\n if (json) {\n this.process.stdout.write(`${JSON.stringify(missing)}\\n`);\n } else {\n renderResult(this, missing);\n }\n this.process.exitCode = 1;\n return;\n }\n\n const bundleDirectories = discoverPolicyBundleDirectories(this, resolvedDir);\n if (bundleDirectories.length === 0) {\n const empty: PolicyCheckResult = {\n version: POLICY_CHECK_RESULT_VERSION,\n status: 'failed',\n directory: resolvedDir,\n fix,\n tools: { opa: null, regal: null },\n checks: POLICY_CHECK_STEP_NAMES.map((name) => ({\n name,\n status: name === 'manifest' ? 'failed' : 'skipped',\n })),\n unformattedFiles: [],\n fixedFiles: [],\n diagnostics: [\n {\n code: 'project.bundles',\n severity: 'error',\n message:\n 'No publishable policy bundles found. Add one with `transcend policy new`, ' +\n 'or pass a directory that contains a `.manifest`.',\n path: path.relative(this.process.cwd(), resolvedDir) || '.',\n },\n ],\n };\n if (json) {\n this.process.stdout.write(`${JSON.stringify(empty)}\\n`);\n } else {\n renderResult(this, empty);\n }\n this.process.exitCode = 1;\n return;\n }\n\n const results: PolicyCheckResult[] = [];\n for (const bundleDirectory of bundleDirectories) {\n results.push(\n await checkBundle.call(this, { fix, noInteractive, json }, bundleDirectory, runner),\n );\n }\n\n const failed = results.some((result) => result.status === 'failed');\n if (json) {\n if (results.length === 1) {\n this.process.stdout.write(`${JSON.stringify(results[0])}\\n`);\n } else {\n const multi: PolicyCheckMultiResult = {\n version: POLICY_CHECK_MULTI_RESULT_VERSION,\n status: failed ? 'failed' : 'passed',\n directory: resolvedDir,\n fix,\n results,\n };\n this.process.stdout.write(`${JSON.stringify(multi)}\\n`);\n }\n } else if (results.length === 1) {\n renderResult(this, results[0]!);\n } else {\n results.forEach((result, index) => {\n if (index > 0) {\n this.logger.info('');\n }\n const relative = path.relative(this.process.cwd(), result.directory) || result.directory;\n renderResult(this, result, `Policy verification — ${relative}`);\n });\n }\n\n if (failed) {\n this.process.exitCode = 1;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./pullChunkedCustomSiloOutstandingIdentifiers-
|
|
2
|
-
//# sourceMappingURL=impl-
|
|
1
|
+
import{t as e}from"./pullChunkedCustomSiloOutstandingIdentifiers-7qvaMN58.mjs";import{i as t,s as n}from"./writeCsv-CNZrq3zU.mjs";import{t as r}from"./done-input-validation-LiTaCEzF.mjs";import{chunk as i,uniq as a}from"lodash-es";import o from"colors";import{map as s}from"@transcend-io/utils";import{buildTranscendGraphQLClient as c,fetchRequestFilesForRequest as l}from"@transcend-io/sdk";async function u({file:u,fileTarget:d,transcendUrl:f,auth:p,sombraAuth:m,cronDataSiloId:h,targetDataSiloId:g,actions:_,skipRequestCount:v,pageLimit:y,chunkSize:b}){v&&this.logger.info(o.yellow(`Skipping request count as requested. This may help speed up the call.`)),(Number.isNaN(b)||b<=0||b%y!==0)&&(this.logger.error(o.red(`Invalid chunk size: "${b}". Must be a positive integer that is a multiple of ${y}.`)),this.process.exit(1)),r(this.process);let x=c(f,p),{baseName:S,extension:C}=t(u),{baseName:w,extension:T}=t(d),E=0,D=0,O=0;await e({dataSiloId:h,auth:p,sombraAuth:m,actions:_,apiPageSize:y,savePageSize:b,onSave:async e=>{E+=e.length;let t=await s(i(a(e.map(e=>e.requestId)),y),async e=>(this.logger.info(o.magenta(`Fetching target identifiers for ${e.length} requests`)),(await l(x,{logger:this.logger,pageSize:y*2,filterBy:{requestIds:e,dataSiloIds:[g]}})).map(({fileName:e,remoteId:t})=>{if(!t)throw Error(`Failed to find remoteId for ${e}`);return{RecordId:t,Object:e.replace(`.json`,``).split(`/`).pop()?.replace(` Information`,``),Comment:`Customer data deletion request submitted via transcend.io`}})),{concurrency:1});D+=t.flat().length;let r=a(e.map(e=>Object.keys(e)).flat()),c=`${S}-${O}${C}`,f=`${w}-${O}${T}`;await n(c,e,r),this.logger.info(o.green(`Successfully wrote ${e.length} identifiers to file "${u}"`));let p=t.flat();await n(f,p,a(p.map(e=>Object.keys(e)).flat())),this.logger.info(o.green(`Successfully wrote ${p.length} identifiers to file "${d}"`)),this.logger.info(o.blue(`Processed chunk of ${i.length} identifiers, found ${p.length} target identifiers`)),O+=1},transcendUrl:f,skipRequestCount:v}),this.logger.info(o.green(`Successfully wrote ${E} identifiers to file "${u}"`)),this.logger.info(o.green(`Successfully wrote ${D} identifiers to file "${d}"`))}export{u as pullProfiles};
|
|
2
|
+
//# sourceMappingURL=impl-BIhXJ0Nw.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"impl-
|
|
1
|
+
{"version":3,"file":"impl-BIhXJ0Nw.mjs","names":[],"sources":["../src/commands/request/cron/pull-profiles/impl.ts"],"sourcesContent":["import type { RequestAction } from '@transcend-io/privacy-types';\nimport { buildTranscendGraphQLClient, fetchRequestFilesForRequest } from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport colors from 'colors';\nimport { uniq, chunk } from 'lodash-es';\n\nimport type { LocalContext } from '../../../../context.js';\nimport { doneInputValidation } from '../../../../lib/cli/done-input-validation.js';\nimport {\n pullChunkedCustomSiloOutstandingIdentifiers,\n type CsvFormattedIdentifier,\n} from '../../../../lib/cron/index.js';\nimport { parseFilePath, writeLargeCsv } from '../../../../lib/helpers/index.js';\n\nexport interface PullProfilesCommandFlags {\n file: string;\n fileTarget: string;\n transcendUrl: string;\n auth: string;\n sombraAuth?: string;\n cronDataSiloId: string;\n targetDataSiloId: string;\n actions: RequestAction[];\n skipRequestCount: boolean;\n pageLimit: number;\n chunkSize: number;\n}\n\nexport async function pullProfiles(\n this: LocalContext,\n {\n file,\n fileTarget,\n transcendUrl,\n auth,\n sombraAuth,\n cronDataSiloId,\n targetDataSiloId,\n actions,\n skipRequestCount,\n pageLimit,\n chunkSize,\n }: PullProfilesCommandFlags,\n): Promise<void> {\n if (skipRequestCount) {\n this.logger.info(\n colors.yellow('Skipping request count as requested. This may help speed up the call.'),\n );\n }\n\n if (Number.isNaN(chunkSize) || chunkSize <= 0 || chunkSize % pageLimit !== 0) {\n this.logger.error(\n colors.red(\n `Invalid chunk size: \"${chunkSize}\". Must be a positive integer that is a multiple of ${pageLimit}.`,\n ),\n );\n this.process.exit(1);\n }\n\n doneInputValidation(this.process);\n\n // Create GraphQL client to connect to Transcend backend\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n const { baseName, extension } = parseFilePath(file);\n const { baseName: baseNameTarget, extension: extensionTarget } = parseFilePath(fileTarget);\n\n let allIdentifiersCount = 0;\n let allTargetIdentifiersCount = 0;\n let fileCount = 0;\n // Create onSave callback to handle chunked processing\n const onSave = async (chunkToSave: CsvFormattedIdentifier[]): Promise<void> => {\n // Add to all identifiers\n allIdentifiersCount += chunkToSave.length;\n\n // Get unique request IDs from this chunk\n const requestIds = chunkToSave.map((d) => d.requestId as string);\n const uniqueRequestIds = uniq(requestIds);\n\n // Pull down target identifiers for this chunk\n const chunkedRequestIds = chunk(uniqueRequestIds, pageLimit);\n const results = await map(\n chunkedRequestIds,\n async (requestIds) => {\n this.logger.info(\n colors.magenta(`Fetching target identifiers for ${requestIds.length} requests`),\n );\n const results = await fetchRequestFilesForRequest(client, {\n logger: this.logger,\n pageSize: pageLimit * 2,\n filterBy: {\n requestIds,\n dataSiloIds: [targetDataSiloId],\n },\n });\n return results.map(({ fileName, remoteId }) => {\n if (!remoteId) {\n throw new Error(`Failed to find remoteId for ${fileName}`);\n }\n return {\n RecordId: remoteId,\n Object: fileName.replace('.json', '').split('/').pop()?.replace(' Information', ''),\n Comment: 'Customer data deletion request submitted via transcend.io',\n };\n });\n },\n // We are grabbing all the request files for the 'pageLimit' # of requests at a time\n {\n concurrency: 1,\n },\n );\n\n allTargetIdentifiersCount += results.flat().length;\n\n // Write the identifiers and target identifiers to CSV\n const headers = uniq(chunkToSave.map((d) => Object.keys(d)).flat());\n const numberedFileName = `${baseName}-${fileCount}${extension}`;\n const numberedFileNameTarget = `${baseNameTarget}-${fileCount}${extensionTarget}`;\n await writeLargeCsv(numberedFileName, chunkToSave, headers);\n this.logger.info(\n colors.green(`Successfully wrote ${chunkToSave.length} identifiers to file \"${file}\"`),\n );\n\n const targetIdentifiers = results.flat();\n const headers2 = uniq(targetIdentifiers.map((d) => Object.keys(d)).flat());\n await writeLargeCsv(numberedFileNameTarget, targetIdentifiers, headers2);\n this.logger.info(\n colors.green(\n `Successfully wrote ${targetIdentifiers.length} identifiers to file \"${fileTarget}\"`,\n ),\n );\n\n this.logger.info(\n colors.blue(\n `Processed chunk of ${chunk.length} identifiers, found ${targetIdentifiers.length} target identifiers`,\n ),\n );\n fileCount += 1;\n };\n\n // Pull down outstanding identifiers using the new chunked function\n await pullChunkedCustomSiloOutstandingIdentifiers({\n dataSiloId: cronDataSiloId,\n auth,\n sombraAuth,\n actions,\n apiPageSize: pageLimit,\n savePageSize: chunkSize,\n onSave,\n transcendUrl,\n skipRequestCount,\n });\n\n this.logger.info(\n colors.green(`Successfully wrote ${allIdentifiersCount} identifiers to file \"${file}\"`),\n );\n this.logger.info(\n colors.green(\n `Successfully wrote ${allTargetIdentifiersCount} identifiers to file \"${fileTarget}\"`,\n ),\n );\n}\n"],"mappings":"wYA4BA,eAAsB,EAEpB,CACE,OACA,aACA,eACA,OACA,aACA,iBACA,mBACA,UACA,mBACA,YACA,aAEa,CACX,GACF,KAAK,OAAO,KACV,EAAO,OAAO,wEAAwE,CACvF,EAGC,OAAO,MAAM,EAAU,EAAI,GAAa,GAAK,EAAY,IAAc,KACzE,KAAK,OAAO,MACV,EAAO,IACL,wBAAwB,EAAU,sDAAsD,EAAU,GACnG,CACF,CACD,KAAK,QAAQ,KAAK,EAAE,EAGtB,EAAoB,KAAK,QAAQ,CAGjC,IAAM,EAAS,EAA4B,EAAc,EAAK,CACxD,CAAE,WAAU,aAAc,EAAc,EAAK,CAC7C,CAAE,SAAU,EAAgB,UAAW,GAAoB,EAAc,EAAW,CAEtF,EAAsB,EACtB,EAA4B,EAC5B,EAAY,EAwEhB,MAAM,EAA4C,CAChD,WAAY,EACZ,OACA,aACA,UACA,YAAa,EACb,aAAc,EACd,YA7EoB,IAAyD,CAE7E,GAAuB,EAAY,OAQnC,IAAM,EAAU,MAAM,EADI,EAHD,EADN,EAAY,IAAK,GAAM,EAAE,UACJ,CAGQ,CAAE,EAE/B,CACjB,KAAO,KACL,KAAK,OAAO,KACV,EAAO,QAAQ,mCAAmC,EAAW,OAAO,WAAW,CAChF,EASM,MARe,EAA4B,EAAQ,CACxD,OAAQ,KAAK,OACb,SAAU,EAAY,EACtB,SAAU,CACR,aACA,YAAa,CAAC,EAAiB,CAChC,CACF,CAAC,EACa,KAAK,CAAE,WAAU,cAAe,CAC7C,GAAI,CAAC,EACH,MAAU,MAAM,+BAA+B,IAAW,CAE5D,MAAO,CACL,SAAU,EACV,OAAQ,EAAS,QAAQ,QAAS,GAAG,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,QAAQ,eAAgB,GAAG,CACnF,QAAS,4DACV,EACD,EAGJ,CACE,YAAa,EACd,CACF,CAED,GAA6B,EAAQ,MAAM,CAAC,OAG5C,IAAM,EAAU,EAAK,EAAY,IAAK,GAAM,OAAO,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAC7D,EAAmB,GAAG,EAAS,GAAG,IAAY,IAC9C,EAAyB,GAAG,EAAe,GAAG,IAAY,IAChE,MAAM,EAAc,EAAkB,EAAa,EAAQ,CAC3D,KAAK,OAAO,KACV,EAAO,MAAM,sBAAsB,EAAY,OAAO,wBAAwB,EAAK,GAAG,CACvF,CAED,IAAM,EAAoB,EAAQ,MAAM,CAExC,MAAM,EAAc,EAAwB,EAD3B,EAAK,EAAkB,IAAK,GAAM,OAAO,KAAK,EAAE,CAAC,CAAC,MAAM,CACF,CAAC,CACxE,KAAK,OAAO,KACV,EAAO,MACL,sBAAsB,EAAkB,OAAO,wBAAwB,EAAW,GACnF,CACF,CAED,KAAK,OAAO,KACV,EAAO,KACL,sBAAsB,EAAM,OAAO,sBAAsB,EAAkB,OAAO,qBACnF,CACF,CACD,GAAa,GAYb,eACA,mBACD,CAAC,CAEF,KAAK,OAAO,KACV,EAAO,MAAM,sBAAsB,EAAoB,wBAAwB,EAAK,GAAG,CACxF,CACD,KAAK,OAAO,KACV,EAAO,MACL,sBAAsB,EAA0B,wBAAwB,EAAW,GACpF,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as e}from"./constants-
|
|
2
|
-
//# sourceMappingURL=impl-
|
|
1
|
+
import{r as e}from"./constants-DAx9VK1C.mjs";import{r as t}from"./readTranscendYaml-D576bvzR.mjs";import{n,t as r}from"./mergeTranscendInputs-9kQ3t5VY.mjs";import{t as i}from"./parseVariablesFromString-6TYW65LF.mjs";import{t as a}from"./validateTranscendAuth-BrVGfq6X.mjs";import{t as o}from"./listFiles-DDGCBdtx.mjs";import{t as s}from"./done-input-validation-LiTaCEzF.mjs";import c from"node:path";import l from"colors";import{mapSeries as u}from"@transcend-io/utils";import{buildTranscendGraphQLClient as d}from"@transcend-io/sdk";async function f({transcendUrl:e,auth:t,pageSize:r,publishToPrivacyCenter:i,contents:a,deleteExtraAttributeValues:o=!1,classifyService:s=!1,logger:c}){let u=d(e,t);try{let e=await n(a,u,{pageSize:r,publishToPrivacyCenter:i,classifyService:s,deleteExtraAttributeValues:o,logger:c});return e.success||e.errors.forEach(({resource:e,item:t,message:n})=>{c.error(l.red(`Failed to sync ${e}${t?` "${t}"`:``}: ${n}`))}),e.success}catch(e){return c.error(l.red(`An unexpected error occurred syncing the schema: ${e.message}`)),!1}}async function p({file:n=`./transcend.yml`,transcendUrl:d,auth:p,variables:m,pageSize:h,publishToPrivacyCenter:g,classifyService:_,deleteExtraAttributeValues:v}){s(this.process);let y=await a(p,this),b=i(m),x;if(x=Array.isArray(y)&&this.fs.lstatSync(n).isDirectory()?o(this.fs.readdirSync(n)).map(e=>c.join(n,e)):n.split(`,`),x.length<1)throw Error(`No file specified!`);let S=x.map(e=>{this.fs.existsSync(e)?this.logger.info(l.magenta(`Reading file "${e}"...`)):(this.logger.error(l.red(`The file path does not exist on disk: ${e}. You can specify the filepath using --file=./examples/transcend.yml`)),this.process.exit(1));try{let n=t(this.fs.readFileSync(e,`utf8`),b,e);return this.logger.info(l.green(`Successfully read in "${e}"`)),{content:n,name:e.split(`/`).pop().replace(`.yml`,``)}}catch(e){this.logger.error(l.red(`The shape of your yaml file is invalid with the following errors: ${e.message}`)),this.process.exit(1)}});if(typeof y==`string`){let[t,...n]=S.map(({content:e})=>e);await f({transcendUrl:d,auth:y,contents:r(t,...n),publishToPrivacyCenter:g,deleteExtraAttributeValues:v,pageSize:h,classifyService:!!_,logger:this.logger})||(this.logger.info(l.red(`Sync encountered errors. View output above for more information, or check out ${e}`)),this.process.exit(1))}else{if(S.length!==1&&S.length!==y.length)throw Error(`Expected list of yml files to be equal to the list of API keys.Got ${S.length} YML file${S.length===1?``:`s`} and ${y.length} API key${y.length===1?``:`s`}`);let t=[];await u(y,async(e,n)=>{let r=`[${n+1}/${y.length}][${e.organizationName}] `;this.logger.info(l.magenta(`~~~\n\n${r}Attempting to push configuration...\n\n~~~`));let i=S.length===1?S[0].content:S.find(t=>t.name===e.organizationName)?.content;if(!i){this.logger.error(l.red(`${r}Failed to find transcend.yml file for organization: "${e.organizationName}".`)),t.push(e.organizationName);return}await f({transcendUrl:d,auth:e.apiKey,contents:i,pageSize:h,publishToPrivacyCenter:g,deleteExtraAttributeValues:v,classifyService:_,logger:this.logger})?this.logger.info(l.green(`${r}Successfully pushed configuration!`)):(this.logger.error(l.red(`${r}Failed to sync configuration.`)),t.push(e.organizationName))}),t.length>0&&(this.logger.info(l.red(`Sync encountered errors for "${t.join(`,`)}". View output above for more information, or check out ${e}`)),this.process.exit(1))}this.logger.info(l.green(`Successfully synced yaml file to Transcend! View at ${e}`))}export{p as push};
|
|
2
|
+
//# sourceMappingURL=impl-BLM6rMBq.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"impl-B_N_fOq0.mjs","names":[],"sources":["../src/commands/inventory/push/impl.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { buildTranscendGraphQLClient } from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { TranscendInput } from '../../../codecs.js';\nimport { ADMIN_DASH_INTEGRATIONS } from '../../../constants.js';\nimport type { LocalContext } from '../../../context.js';\nimport { validateTranscendAuth, filterFileNames } from '../../../lib/api-keys/index.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { syncConfigurationToTranscend } from '../../../lib/graphql/index.js';\nimport { parseVariablesFromString } from '../../../lib/helpers/parseVariablesFromString.js';\nimport { mergeTranscendInputs } from '../../../lib/mergeTranscendInputs.js';\nimport { parseTranscendYaml } from '../../../lib/readTranscendYaml.js';\n\n/**\n * Sync configuration to Transcend\n *\n * @param options - Options\n * @returns True if synced successfully, false if error occurs\n */\nasync function syncConfiguration({\n transcendUrl,\n auth,\n pageSize,\n publishToPrivacyCenter,\n contents,\n deleteExtraAttributeValues = false,\n classifyService = false,\n logger,\n}: {\n /** Transcend YAML */\n contents: TranscendInput;\n /** Transcend URL */\n transcendUrl: string;\n /** API key */\n auth: string;\n /** Page size */\n pageSize: number;\n /** Skip privacy center publish step */\n publishToPrivacyCenter: boolean;\n /** classify data flow service if missing */\n classifyService?: boolean;\n /** Delete attributes when syncing */\n deleteExtraAttributeValues?: boolean;\n /** Logger for reporting sync progress and errors */\n logger: LocalContext['logger'];\n}): Promise<boolean> {\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Sync to Transcend\n try {\n const result = await syncConfigurationToTranscend(contents, client, {\n pageSize,\n publishToPrivacyCenter,\n classifyService,\n deleteExtraAttributeValues,\n logger,\n });\n if (!result.success) {\n result.errors.forEach(({ resource, item, message }) => {\n logger.error(\n colors.red(`Failed to sync ${resource}${item ? ` \"${item}\"` : ''}: ${message}`),\n );\n });\n }\n return result.success;\n } catch (err) {\n logger.error(colors.red(`An unexpected error occurred syncing the schema: ${err.message}`));\n return false;\n }\n}\n\nexport interface PushCommandFlags {\n auth: string;\n file: string;\n transcendUrl: string;\n pageSize: number;\n variables: string;\n publishToPrivacyCenter: boolean;\n classifyService: boolean;\n deleteExtraAttributeValues: boolean;\n}\n\nexport async function push(\n this: LocalContext,\n {\n file = './transcend.yml',\n transcendUrl,\n auth,\n variables,\n pageSize,\n publishToPrivacyCenter,\n classifyService,\n deleteExtraAttributeValues,\n }: PushCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n\n // Parse authentication as API key or path to list of API keys\n const apiKeyOrList = await validateTranscendAuth(auth, this);\n\n const vars = parseVariablesFromString(variables);\n\n // check if we are being passed a list of API keys and a list of files\n let fileList: string[];\n if (Array.isArray(apiKeyOrList) && this.fs.lstatSync(file).isDirectory()) {\n fileList = filterFileNames(this.fs.readdirSync(file)).map((filePath) =>\n path.join(file, filePath),\n );\n } else {\n fileList = file.split(',');\n }\n\n // Ensure at least one file is parsed\n if (fileList.length < 1) {\n throw new Error('No file specified!');\n }\n\n // eslint-disable-next-line array-callback-return,consistent-return\n const transcendInputs = fileList.map((filePath) => {\n // Ensure yaml file exists on disk\n if (!this.fs.existsSync(filePath)) {\n this.logger.error(\n colors.red(\n `The file path does not exist on disk: ${filePath}. You can specify the filepath using --file=./examples/transcend.yml`,\n ),\n );\n this.process.exit(1);\n } else {\n this.logger.info(colors.magenta(`Reading file \"${filePath}\"...`));\n }\n\n try {\n // Read in the yaml file and validate it's shape\n const newContents = parseTranscendYaml(\n this.fs.readFileSync(filePath, 'utf8'),\n vars,\n filePath,\n );\n this.logger.info(colors.green(`Successfully read in \"${filePath}\"`));\n return {\n content: newContents,\n name: filePath.split('/').pop()!.replace('.yml', ''),\n };\n } catch (err) {\n this.logger.error(\n colors.red(\n `The shape of your yaml file is invalid with the following errors: ${err.message}`,\n ),\n );\n this.process.exit(1);\n }\n });\n\n // process a single API key\n if (typeof apiKeyOrList === 'string') {\n // if passed multiple inputs, merge them together\n const [base, ...rest] = transcendInputs.map(({ content }) => content);\n const contents = mergeTranscendInputs(base, ...rest);\n\n // sync the configuration\n const success = await syncConfiguration({\n transcendUrl,\n auth: apiKeyOrList,\n contents,\n publishToPrivacyCenter,\n deleteExtraAttributeValues,\n pageSize,\n classifyService: !!classifyService,\n logger: this.logger,\n });\n\n // exist with error code\n if (!success) {\n this.logger.info(\n colors.red(\n `Sync encountered errors. View output above for more information, or check out ${ADMIN_DASH_INTEGRATIONS}`,\n ),\n );\n\n this.process.exit(1);\n }\n } else {\n // if passed multiple inputs, expect them to be one per instance\n if (transcendInputs.length !== 1 && transcendInputs.length !== apiKeyOrList.length) {\n throw new Error(\n 'Expected list of yml files to be equal to the list of API keys.' +\n `Got ${transcendInputs.length} YML file${\n transcendInputs.length === 1 ? '' : 's'\n } and ${apiKeyOrList.length} API key${apiKeyOrList.length === 1 ? '' : 's'}`,\n );\n }\n\n const encounteredErrors: string[] = [];\n await mapSeries(apiKeyOrList, async (apiKey, ind) => {\n const prefix = `[${ind + 1}/${apiKeyOrList.length}][${apiKey.organizationName}] `;\n this.logger.info(\n colors.magenta(`~~~\\n\\n${prefix}Attempting to push configuration...\\n\\n~~~`),\n );\n\n // use the merged contents if 1 yml passed, else use the contents that map to that organization\n const useContents =\n transcendInputs.length === 1\n ? transcendInputs[0].content\n : transcendInputs.find((input) => input.name === apiKey.organizationName)?.content;\n\n // Throw error if cannot find a yml file matching that organization name\n if (!useContents) {\n this.logger.error(\n colors.red(\n `${prefix}Failed to find transcend.yml file for organization: \"${apiKey.organizationName}\".`,\n ),\n );\n encounteredErrors.push(apiKey.organizationName);\n return;\n }\n\n const success = await syncConfiguration({\n transcendUrl,\n auth: apiKey.apiKey,\n contents: useContents,\n pageSize,\n publishToPrivacyCenter,\n deleteExtraAttributeValues,\n classifyService,\n logger: this.logger,\n });\n\n if (success) {\n this.logger.info(colors.green(`${prefix}Successfully pushed configuration!`));\n } else {\n this.logger.error(colors.red(`${prefix}Failed to sync configuration.`));\n encounteredErrors.push(apiKey.organizationName);\n }\n });\n\n if (encounteredErrors.length > 0) {\n this.logger.info(\n colors.red(\n `Sync encountered errors for \"${encounteredErrors.join(\n ',',\n )}\". View output above for more information, or check out ${ADMIN_DASH_INTEGRATIONS}`,\n ),\n );\n\n this.process.exit(1);\n }\n }\n\n // Indicate success\n this.logger.info(\n colors.green(`Successfully synced yaml file to Transcend! View at ${ADMIN_DASH_INTEGRATIONS}`),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"impl-BLM6rMBq.mjs","names":[],"sources":["../src/commands/inventory/push/impl.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { buildTranscendGraphQLClient } from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { TranscendInput } from '../../../codecs.js';\nimport { ADMIN_DASH_INTEGRATIONS } from '../../../constants.js';\nimport type { LocalContext } from '../../../context.js';\nimport { validateTranscendAuth, filterFileNames } from '../../../lib/api-keys/index.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { syncConfigurationToTranscend } from '../../../lib/graphql/index.js';\nimport { parseVariablesFromString } from '../../../lib/helpers/parseVariablesFromString.js';\nimport { mergeTranscendInputs } from '../../../lib/mergeTranscendInputs.js';\nimport { parseTranscendYaml } from '../../../lib/readTranscendYaml.js';\n\n/**\n * Sync configuration to Transcend\n *\n * @param options - Options\n * @returns True if synced successfully, false if error occurs\n */\nasync function syncConfiguration({\n transcendUrl,\n auth,\n pageSize,\n publishToPrivacyCenter,\n contents,\n deleteExtraAttributeValues = false,\n classifyService = false,\n logger,\n}: {\n /** Transcend YAML */\n contents: TranscendInput;\n /** Transcend URL */\n transcendUrl: string;\n /** API key */\n auth: string;\n /** Page size */\n pageSize: number;\n /** Skip privacy center publish step */\n publishToPrivacyCenter: boolean;\n /** classify data flow service if missing */\n classifyService?: boolean;\n /** Delete attributes when syncing */\n deleteExtraAttributeValues?: boolean;\n /** Logger for reporting sync progress and errors */\n logger: LocalContext['logger'];\n}): Promise<boolean> {\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Sync to Transcend\n try {\n const result = await syncConfigurationToTranscend(contents, client, {\n pageSize,\n publishToPrivacyCenter,\n classifyService,\n deleteExtraAttributeValues,\n logger,\n });\n if (!result.success) {\n result.errors.forEach(({ resource, item, message }) => {\n logger.error(\n colors.red(`Failed to sync ${resource}${item ? ` \"${item}\"` : ''}: ${message}`),\n );\n });\n }\n return result.success;\n } catch (err) {\n logger.error(colors.red(`An unexpected error occurred syncing the schema: ${err.message}`));\n return false;\n }\n}\n\nexport interface PushCommandFlags {\n auth: string;\n file: string;\n transcendUrl: string;\n pageSize: number;\n variables: string;\n publishToPrivacyCenter: boolean;\n classifyService: boolean;\n deleteExtraAttributeValues: boolean;\n}\n\nexport async function push(\n this: LocalContext,\n {\n file = './transcend.yml',\n transcendUrl,\n auth,\n variables,\n pageSize,\n publishToPrivacyCenter,\n classifyService,\n deleteExtraAttributeValues,\n }: PushCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n\n // Parse authentication as API key or path to list of API keys\n const apiKeyOrList = await validateTranscendAuth(auth, this);\n\n const vars = parseVariablesFromString(variables);\n\n // check if we are being passed a list of API keys and a list of files\n let fileList: string[];\n if (Array.isArray(apiKeyOrList) && this.fs.lstatSync(file).isDirectory()) {\n fileList = filterFileNames(this.fs.readdirSync(file)).map((filePath) =>\n path.join(file, filePath),\n );\n } else {\n fileList = file.split(',');\n }\n\n // Ensure at least one file is parsed\n if (fileList.length < 1) {\n throw new Error('No file specified!');\n }\n\n // eslint-disable-next-line array-callback-return,consistent-return\n const transcendInputs = fileList.map((filePath) => {\n // Ensure yaml file exists on disk\n if (!this.fs.existsSync(filePath)) {\n this.logger.error(\n colors.red(\n `The file path does not exist on disk: ${filePath}. You can specify the filepath using --file=./examples/transcend.yml`,\n ),\n );\n this.process.exit(1);\n } else {\n this.logger.info(colors.magenta(`Reading file \"${filePath}\"...`));\n }\n\n try {\n // Read in the yaml file and validate it's shape\n const newContents = parseTranscendYaml(\n this.fs.readFileSync(filePath, 'utf8'),\n vars,\n filePath,\n );\n this.logger.info(colors.green(`Successfully read in \"${filePath}\"`));\n return {\n content: newContents,\n name: filePath.split('/').pop()!.replace('.yml', ''),\n };\n } catch (err) {\n this.logger.error(\n colors.red(\n `The shape of your yaml file is invalid with the following errors: ${err.message}`,\n ),\n );\n this.process.exit(1);\n }\n });\n\n // process a single API key\n if (typeof apiKeyOrList === 'string') {\n // if passed multiple inputs, merge them together\n const [base, ...rest] = transcendInputs.map(({ content }) => content);\n const contents = mergeTranscendInputs(base, ...rest);\n\n // sync the configuration\n const success = await syncConfiguration({\n transcendUrl,\n auth: apiKeyOrList,\n contents,\n publishToPrivacyCenter,\n deleteExtraAttributeValues,\n pageSize,\n classifyService: !!classifyService,\n logger: this.logger,\n });\n\n // exist with error code\n if (!success) {\n this.logger.info(\n colors.red(\n `Sync encountered errors. View output above for more information, or check out ${ADMIN_DASH_INTEGRATIONS}`,\n ),\n );\n\n this.process.exit(1);\n }\n } else {\n // if passed multiple inputs, expect them to be one per instance\n if (transcendInputs.length !== 1 && transcendInputs.length !== apiKeyOrList.length) {\n throw new Error(\n 'Expected list of yml files to be equal to the list of API keys.' +\n `Got ${transcendInputs.length} YML file${\n transcendInputs.length === 1 ? '' : 's'\n } and ${apiKeyOrList.length} API key${apiKeyOrList.length === 1 ? '' : 's'}`,\n );\n }\n\n const encounteredErrors: string[] = [];\n await mapSeries(apiKeyOrList, async (apiKey, ind) => {\n const prefix = `[${ind + 1}/${apiKeyOrList.length}][${apiKey.organizationName}] `;\n this.logger.info(\n colors.magenta(`~~~\\n\\n${prefix}Attempting to push configuration...\\n\\n~~~`),\n );\n\n // use the merged contents if 1 yml passed, else use the contents that map to that organization\n const useContents =\n transcendInputs.length === 1\n ? transcendInputs[0].content\n : transcendInputs.find((input) => input.name === apiKey.organizationName)?.content;\n\n // Throw error if cannot find a yml file matching that organization name\n if (!useContents) {\n this.logger.error(\n colors.red(\n `${prefix}Failed to find transcend.yml file for organization: \"${apiKey.organizationName}\".`,\n ),\n );\n encounteredErrors.push(apiKey.organizationName);\n return;\n }\n\n const success = await syncConfiguration({\n transcendUrl,\n auth: apiKey.apiKey,\n contents: useContents,\n pageSize,\n publishToPrivacyCenter,\n deleteExtraAttributeValues,\n classifyService,\n logger: this.logger,\n });\n\n if (success) {\n this.logger.info(colors.green(`${prefix}Successfully pushed configuration!`));\n } else {\n this.logger.error(colors.red(`${prefix}Failed to sync configuration.`));\n encounteredErrors.push(apiKey.organizationName);\n }\n });\n\n if (encounteredErrors.length > 0) {\n this.logger.info(\n colors.red(\n `Sync encountered errors for \"${encounteredErrors.join(\n ',',\n )}\". View output above for more information, or check out ${ADMIN_DASH_INTEGRATIONS}`,\n ),\n );\n\n this.process.exit(1);\n }\n }\n\n // Indicate success\n this.logger.info(\n colors.green(`Successfully synced yaml file to Transcend! View at ${ADMIN_DASH_INTEGRATIONS}`),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./bulkRestartRequests-
|
|
2
|
-
//# sourceMappingURL=impl-
|
|
1
|
+
import{t as e}from"./bulkRestartRequests-Cx5LfpdX.mjs";import{t}from"./done-input-validation-LiTaCEzF.mjs";async function n({auth:n,requestReceiptFolder:r,sombraAuth:i,actions:a,statuses:o,requestIds:s,createdAt:c,emailIsVerified:l,silentModeBefore:u,sendEmailReceipt:d,copyIdentifiers:f,restartIdentifierStrategy:p,skipWaitingPeriod:m,createdAtBefore:h,createdAtAfter:g,updatedAtBefore:_,updatedAtAfter:v,concurrency:y,transcendUrl:b}){t(this.process),await e({requestReceiptFolder:r,auth:n,sombraAuth:i,requestActions:a,requestStatuses:o,requestIds:s,createdAt:c,emailIsVerified:l,silentModeBefore:u,sendEmailReceipt:d,copyIdentifiers:f,restartIdentifierStrategy:p,skipWaitingPeriod:m,createdAtBefore:h,createdAtAfter:g,updatedAtBefore:_,updatedAtAfter:v,concurrency:y,transcendUrl:b})}export{n as restart};
|
|
2
|
+
//# sourceMappingURL=impl-BPmzcgpy.mjs.map
|