@transcend-io/cli 10.28.0 → 10.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -15
- package/dist/{app-DCY8E776.mjs → app-C5baDSA2.mjs} +21 -21
- package/dist/app-C5baDSA2.mjs.map +1 -0
- package/dist/{approvePrivacyRequests-80SV_94y.mjs → approvePrivacyRequests-B8x7oxTn.mjs} +2 -2
- package/dist/{approvePrivacyRequests-80SV_94y.mjs.map → approvePrivacyRequests-B8x7oxTn.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-2YDkSvtA.mjs → buildExamples-7KXBjl-b.mjs} +2 -2
- package/dist/{buildExamples-2YDkSvtA.mjs.map → buildExamples-7KXBjl-b.mjs.map} +1 -1
- package/dist/{buildXdiSyncEndpoint-B3zUrI16.mjs → buildXdiSyncEndpoint-DcrkZ2Gu.mjs} +2 -2
- package/dist/{buildXdiSyncEndpoint-B3zUrI16.mjs.map → buildXdiSyncEndpoint-DcrkZ2Gu.mjs.map} +1 -1
- package/dist/{bulkRestartRequests-BLb8OJmf.mjs → bulkRestartRequests-CysUfl98.mjs} +2 -2
- package/dist/{bulkRestartRequests-BLb8OJmf.mjs.map → bulkRestartRequests-CysUfl98.mjs.map} +1 -1
- package/dist/{bulkRetryEnrichers-BA9cboSe.mjs → bulkRetryEnrichers-DaLE67Ht.mjs} +2 -2
- package/dist/{bulkRetryEnrichers-BA9cboSe.mjs.map → bulkRetryEnrichers-DaLE67Ht.mjs.map} +1 -1
- package/dist/{cancelPrivacyRequests-BEyOBJ2r.mjs → cancelPrivacyRequests-D0zNUNdS.mjs} +2 -2
- package/dist/{cancelPrivacyRequests-BEyOBJ2r.mjs.map → cancelPrivacyRequests-D0zNUNdS.mjs.map} +1 -1
- package/dist/codecs-CTxFjCg4.mjs +2 -0
- package/dist/codecs-CTxFjCg4.mjs.map +1 -0
- package/dist/{collectCsvFilesOrExit-BqnmK63h.mjs → collectCsvFilesOrExit-B1VOVGWN.mjs} +1 -1
- package/dist/{collectCsvFilesOrExit-BqnmK63h.mjs.map → collectCsvFilesOrExit-B1VOVGWN.mjs.map} +1 -1
- package/dist/{collectParquetFilesOrExit-BiksFI-Q.mjs → collectParquetFilesOrExit-DVlv5ctt.mjs} +1 -1
- package/dist/{collectParquetFilesOrExit-BiksFI-Q.mjs.map → collectParquetFilesOrExit-DVlv5ctt.mjs.map} +1 -1
- package/dist/{command-eIenZMOx.mjs → command-CIv9yk86.mjs} +2 -2
- package/dist/{command-eIenZMOx.mjs.map → command-CIv9yk86.mjs.map} +1 -1
- package/dist/commands/admin/chunk-csv/worker.mjs +1 -1
- package/dist/commands/admin/parquet-to-csv/worker.mjs +1 -1
- package/dist/{consentManagersToBusinessEntities-O4KBox9u.mjs → consentManagersToBusinessEntities-CBJ81aGO.mjs} +1 -1
- package/dist/{consentManagersToBusinessEntities-O4KBox9u.mjs.map → consentManagersToBusinessEntities-CBJ81aGO.mjs.map} +1 -1
- package/dist/{constants-BpQ5awGp.mjs → constants-DXzsg2CQ.mjs} +2 -2
- package/dist/{constants-BpQ5awGp.mjs.map → constants-DXzsg2CQ.mjs.map} +1 -1
- package/dist/{constants-C1seVfCu.mjs → constants-D_HVDaOy.mjs} +1 -1
- package/dist/{constants-C1seVfCu.mjs.map → constants-D_HVDaOy.mjs.map} +1 -1
- package/dist/{constants-BG53Mbi9.mjs → constants-jjHIzHGm.mjs} +2 -2
- package/dist/{constants-BG53Mbi9.mjs.map → constants-jjHIzHGm.mjs.map} +1 -1
- package/dist/{context-CZYrQfAn.mjs → context-B33NT3ki.mjs} +1 -1
- package/dist/{context-CZYrQfAn.mjs.map → context-B33NT3ki.mjs.map} +1 -1
- package/dist/{createPoolingCommandUi-C39R_zo3.mjs → createPoolingCommandUi-uiDjLlqf.mjs} +2 -2
- package/dist/{createPoolingCommandUi-C39R_zo3.mjs.map → createPoolingCommandUi-uiDjLlqf.mjs.map} +1 -1
- package/dist/{dataFlowsToDataSilos-CLSHb1GA.mjs → dataFlowsToDataSilos-BF6fzWcm.mjs} +1 -1
- package/dist/{dataFlowsToDataSilos-CLSHb1GA.mjs.map → dataFlowsToDataSilos-BF6fzWcm.mjs.map} +1 -1
- package/dist/deno-runtime-DV52X8-P.mjs +2 -0
- package/dist/deno-runtime-DV52X8-P.mjs.map +1 -0
- package/dist/{done-input-validation-Cjk7a3jX.mjs → done-input-validation-BUz-aVC5.mjs} +1 -1
- package/dist/{done-input-validation-Cjk7a3jX.mjs.map → done-input-validation-BUz-aVC5.mjs.map} +1 -1
- package/dist/{downloadPrivacyRequestFiles-CLFgQItk.mjs → downloadPrivacyRequestFiles-B_BVxW5Z.mjs} +2 -2
- package/dist/{downloadPrivacyRequestFiles-CLFgQItk.mjs.map → downloadPrivacyRequestFiles-B_BVxW5Z.mjs.map} +1 -1
- package/dist/{extractClientError-C_6ly7Gw.mjs → extractClientError-HZ2U7yqV.mjs} +1 -1
- package/dist/{extractClientError-C_6ly7Gw.mjs.map → extractClientError-HZ2U7yqV.mjs.map} +1 -1
- package/dist/{fetchAllRequests-CEDKdq0c.mjs → fetchAllRequests-2TZr-LfT.mjs} +2 -2
- package/dist/{fetchAllRequests-CEDKdq0c.mjs.map → fetchAllRequests-2TZr-LfT.mjs.map} +1 -1
- package/dist/{formatPolicyBundleVersionSummary-CrFtcPwS.mjs → formatPolicyBundleVersionSummary-CJmMCLtJ.mjs} +1 -1
- package/dist/{formatPolicyBundleVersionSummary-CrFtcPwS.mjs.map → formatPolicyBundleVersionSummary-CJmMCLtJ.mjs.map} +1 -1
- package/dist/{fuzzyMatchColumns-OTsFJ21f.mjs → fuzzyMatchColumns-CQCzpjWC.mjs} +2 -2
- package/dist/{fuzzyMatchColumns-OTsFJ21f.mjs.map → fuzzyMatchColumns-CQCzpjWC.mjs.map} +1 -1
- package/dist/{generateCrossAccountApiKeys-D6O_Qm3Y.mjs → generateCrossAccountApiKeys-Bnqgmj-H.mjs} +2 -2
- package/dist/{generateCrossAccountApiKeys-D6O_Qm3Y.mjs.map → generateCrossAccountApiKeys-Bnqgmj-H.mjs.map} +1 -1
- package/dist/impl--JGnR3wp.mjs +3 -0
- package/dist/{impl-CzxvoiGW.mjs.map → impl--JGnR3wp.mjs.map} +1 -1
- package/dist/{impl-BqrSLoig.mjs → impl-2iVTxNn4.mjs} +2 -2
- package/dist/{impl-BqrSLoig.mjs.map → impl-2iVTxNn4.mjs.map} +1 -1
- package/dist/{impl-Drql0g72.mjs → impl-7UqkSO3x2.mjs} +2 -2
- package/dist/impl-7UqkSO3x2.mjs.map +1 -0
- package/dist/{impl-C50tdWlL2.mjs → impl-B-j5ACi02.mjs} +2 -2
- package/dist/{impl-C50tdWlL2.mjs.map → impl-B-j5ACi02.mjs.map} +1 -1
- package/dist/impl-B4BgGuF_.mjs +3 -0
- package/dist/impl-B4BgGuF_.mjs.map +1 -0
- package/dist/{impl-zZFaSmxd2.mjs → impl-BDrvzlpf2.mjs} +2 -2
- package/dist/{impl-zZFaSmxd2.mjs.map → impl-BDrvzlpf2.mjs.map} +1 -1
- package/dist/{impl-Dk6TZKrp2.mjs → impl-BH_A5rgL.mjs} +2 -2
- package/dist/impl-BH_A5rgL.mjs.map +1 -0
- package/dist/{impl-CHmjV-Pa.mjs → impl-BOawPhYE.mjs} +2 -2
- package/dist/{impl-CHmjV-Pa.mjs.map → impl-BOawPhYE.mjs.map} +1 -1
- package/dist/{impl-CXUJ1eRC.mjs → impl-BPoEH3Nf2.mjs} +2 -2
- package/dist/impl-BPoEH3Nf2.mjs.map +1 -0
- package/dist/{impl-CqF9xIIy.mjs → impl-BXUYpR27.mjs} +2 -2
- package/dist/{impl-CqF9xIIy.mjs.map → impl-BXUYpR27.mjs.map} +1 -1
- package/dist/{impl-DYf10-BD2.mjs → impl-Bc6p8A1l2.mjs} +2 -2
- package/dist/{impl-DYf10-BD2.mjs.map → impl-Bc6p8A1l2.mjs.map} +1 -1
- package/dist/{impl-M3Eu4QjS.mjs → impl-BqWQh9Ss.mjs} +2 -2
- package/dist/{impl-M3Eu4QjS.mjs.map → impl-BqWQh9Ss.mjs.map} +1 -1
- package/dist/impl-C6HtWd1t.mjs +2 -0
- package/dist/{impl-BcJ019T2.mjs.map → impl-C6HtWd1t.mjs.map} +1 -1
- package/dist/{impl-GHukPBnk.mjs → impl-CL29fOWM.mjs} +2 -2
- package/dist/{impl-GHukPBnk.mjs.map → impl-CL29fOWM.mjs.map} +1 -1
- package/dist/{impl-BvNZqLrT.mjs → impl-CO4ZqlLV.mjs} +2 -2
- package/dist/{impl-BvNZqLrT.mjs.map → impl-CO4ZqlLV.mjs.map} +1 -1
- package/dist/{impl-DJLlcqSg.mjs → impl-COujMWgp.mjs} +2 -2
- package/dist/{impl-DJLlcqSg.mjs.map → impl-COujMWgp.mjs.map} +1 -1
- package/dist/{impl-Bs-drNBf.mjs → impl-CPSEzCc9.mjs} +2 -2
- package/dist/{impl-Bs-drNBf.mjs.map → impl-CPSEzCc9.mjs.map} +1 -1
- package/dist/{impl-Dib6Fff12.mjs → impl-CQVy2k3o.mjs} +2 -2
- package/dist/impl-CQVy2k3o.mjs.map +1 -0
- package/dist/{impl-B3_hrFPX.mjs → impl-CUOVJsRg.mjs} +2 -2
- package/dist/{impl-B3_hrFPX.mjs.map → impl-CUOVJsRg.mjs.map} +1 -1
- package/dist/{impl-BySmbxEt.mjs → impl-CUiYbk6G.mjs} +2 -2
- package/dist/{impl-BySmbxEt.mjs.map → impl-CUiYbk6G.mjs.map} +1 -1
- package/dist/{impl-CWh9H7mL.mjs → impl-CXTBcYo6.mjs} +2 -2
- package/dist/{impl-CWh9H7mL.mjs.map → impl-CXTBcYo6.mjs.map} +1 -1
- package/dist/{impl-Cx3T0Iei.mjs → impl-CfvxD3RI.mjs} +2 -2
- package/dist/{impl-Cx3T0Iei.mjs.map → impl-CfvxD3RI.mjs.map} +1 -1
- package/dist/{impl-DHfYO9T52.mjs → impl-Cli2XRp02.mjs} +2 -2
- package/dist/{impl-DHfYO9T52.mjs.map → impl-Cli2XRp02.mjs.map} +1 -1
- package/dist/{impl-CDtUusmg.mjs → impl-CuDs6SsI.mjs} +2 -2
- package/dist/{impl-CDtUusmg.mjs.map → impl-CuDs6SsI.mjs.map} +1 -1
- package/dist/{impl-BYGNx-8W2.mjs → impl-CvLI_Jig.mjs} +2 -2
- package/dist/impl-CvLI_Jig.mjs.map +1 -0
- package/dist/{impl-BFJtKA2o2.mjs → impl-CwKYKBYb.mjs} +2 -2
- package/dist/impl-CwKYKBYb.mjs.map +1 -0
- package/dist/{impl-BewF8tB6.mjs → impl-CwXp3Tbr.mjs} +2 -2
- package/dist/{impl-BewF8tB6.mjs.map → impl-CwXp3Tbr.mjs.map} +1 -1
- package/dist/{impl-B--lr5RJ2.mjs → impl-CyQRmuM72.mjs} +2 -2
- package/dist/{impl-B--lr5RJ2.mjs.map → impl-CyQRmuM72.mjs.map} +1 -1
- package/dist/impl-DEJpYLPB.mjs +2 -0
- package/dist/impl-DEJpYLPB.mjs.map +1 -0
- package/dist/impl-DEhw1iNn.mjs +2 -0
- package/dist/impl-DEhw1iNn.mjs.map +1 -0
- package/dist/impl-DK7nSMle2.mjs +303 -0
- package/dist/impl-DK7nSMle2.mjs.map +1 -0
- package/dist/{impl-DOoR93T6.mjs → impl-DKepRc812.mjs} +2 -2
- package/dist/impl-DKepRc812.mjs.map +1 -0
- package/dist/{impl-CTIWliTx.mjs → impl-DLG0Egy6.mjs} +2 -2
- package/dist/{impl-CTIWliTx.mjs.map → impl-DLG0Egy6.mjs.map} +1 -1
- package/dist/impl-DMe_OqJx.mjs +2 -0
- package/dist/{impl-BsC1KVxS.mjs.map → impl-DMe_OqJx.mjs.map} +1 -1
- package/dist/{impl-BwRcpAno.mjs → impl-DNe-gRXe2.mjs} +2 -2
- package/dist/impl-DNe-gRXe2.mjs.map +1 -0
- package/dist/{impl-Dm-Fkr392.mjs → impl-DNzkRayg.mjs} +2 -2
- package/dist/impl-DNzkRayg.mjs.map +1 -0
- package/dist/{impl-DzNk41xd.mjs → impl-DPtx5iZO.mjs} +2 -2
- package/dist/{impl-DzNk41xd.mjs.map → impl-DPtx5iZO.mjs.map} +1 -1
- package/dist/{impl-syp5hclX.mjs → impl-DRpK-QLC.mjs} +2 -2
- package/dist/{impl-syp5hclX.mjs.map → impl-DRpK-QLC.mjs.map} +1 -1
- package/dist/{impl-Bq6g_xYv.mjs → impl-DVQmuZQY.mjs} +2 -2
- package/dist/{impl-Bq6g_xYv.mjs.map → impl-DVQmuZQY.mjs.map} +1 -1
- package/dist/{impl-DYwojWvN.mjs → impl-DVeGQCrr.mjs} +2 -2
- package/dist/{impl-DYwojWvN.mjs.map → impl-DVeGQCrr.mjs.map} +1 -1
- package/dist/{impl-DF8RGVlT.mjs → impl-DXRDWiB1.mjs} +2 -2
- package/dist/{impl-DF8RGVlT.mjs.map → impl-DXRDWiB1.mjs.map} +1 -1
- package/dist/{impl-CiYKuCHV.mjs → impl-DXowiNOz.mjs} +2 -2
- package/dist/{impl-CiYKuCHV.mjs.map → impl-DXowiNOz.mjs.map} +1 -1
- package/dist/{impl-DpuhHpuk.mjs → impl-DaH1_ZzK.mjs} +2 -2
- package/dist/{impl-DpuhHpuk.mjs.map → impl-DaH1_ZzK.mjs.map} +1 -1
- package/dist/{impl-DIPvFKiP.mjs → impl-DdIa0zla.mjs} +2 -2
- package/dist/{impl-DIPvFKiP.mjs.map → impl-DdIa0zla.mjs.map} +1 -1
- package/dist/{impl-DbZ1cYoR.mjs → impl-DqhZR11m.mjs} +2 -2
- package/dist/{impl-DbZ1cYoR.mjs.map → impl-DqhZR11m.mjs.map} +1 -1
- package/dist/{impl-VPBEqV0w2.mjs → impl-Dqqb0lVN.mjs} +2 -2
- package/dist/impl-Dqqb0lVN.mjs.map +1 -0
- package/dist/{impl-6m7GXUAe.mjs → impl-DryC3OJu.mjs} +2 -2
- package/dist/{impl-6m7GXUAe.mjs.map → impl-DryC3OJu.mjs.map} +1 -1
- package/dist/{impl-CQGeH47F.mjs → impl-DuZjI6p0.mjs} +2 -2
- package/dist/{impl-CQGeH47F.mjs.map → impl-DuZjI6p0.mjs.map} +1 -1
- package/dist/{impl-C5dzaDga2.mjs → impl-Dw8NYkzw.mjs} +2 -2
- package/dist/impl-Dw8NYkzw.mjs.map +1 -0
- package/dist/impl-Dx_TfvW1.mjs +2 -0
- package/dist/{impl-C8BmxFQ2.mjs.map → impl-Dx_TfvW1.mjs.map} +1 -1
- package/dist/{impl-DXNcD7CZ.mjs → impl-DypttZd6.mjs} +2 -2
- package/dist/{impl-DXNcD7CZ.mjs.map → impl-DypttZd6.mjs.map} +1 -1
- package/dist/{impl-CfPXYgT42.mjs → impl-DzUTmaID2.mjs} +2 -2
- package/dist/{impl-CfPXYgT42.mjs.map → impl-DzUTmaID2.mjs.map} +1 -1
- package/dist/{impl-BJlKQJdS2.mjs → impl-INhq_CiK.mjs} +2 -2
- package/dist/impl-INhq_CiK.mjs.map +1 -0
- package/dist/{impl-KKrDTraf.mjs → impl-KmCNH-yw.mjs} +2 -2
- package/dist/{impl-KKrDTraf.mjs.map → impl-KmCNH-yw.mjs.map} +1 -1
- package/dist/{impl-CE56-wQh.mjs → impl-RPG-BTmX.mjs} +2 -2
- package/dist/{impl-CE56-wQh.mjs.map → impl-RPG-BTmX.mjs.map} +1 -1
- package/dist/{impl-amV0wRWk.mjs → impl-XrTL-TDl.mjs} +2 -2
- package/dist/{impl-amV0wRWk.mjs.map → impl-XrTL-TDl.mjs.map} +1 -1
- package/dist/impl-Z8oWVENM.mjs +2 -0
- package/dist/impl-Z8oWVENM.mjs.map +1 -0
- package/dist/{impl-m3QXCc6n.mjs → impl-a_Y8bnSn.mjs} +4 -4
- package/dist/impl-a_Y8bnSn.mjs.map +1 -0
- package/dist/{impl-CEmXPEq22.mjs → impl-hwpT9u5o2.mjs} +2 -2
- package/dist/{impl-CEmXPEq22.mjs.map → impl-hwpT9u5o2.mjs.map} +1 -1
- package/dist/{impl-BWVe4VGF2.mjs → impl-xq89cSSz2.mjs} +2 -2
- package/dist/{impl-BWVe4VGF2.mjs.map → impl-xq89cSSz2.mjs.map} +1 -1
- package/dist/index.d.mts +12797 -1840
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{initializeCliRuntime-802-P8MC.mjs → initializeCliRuntime-6wI_VIHV.mjs} +1 -1
- package/dist/{initializeCliRuntime-802-P8MC.mjs.map → initializeCliRuntime-6wI_VIHV.mjs.map} +1 -1
- package/dist/{inquirer-DkFTQU00.mjs → inquirer-BeaBetMO.mjs} +2 -2
- package/dist/{inquirer-DkFTQU00.mjs.map → inquirer-BeaBetMO.mjs.map} +1 -1
- package/dist/jsonc-D9dDWb1J.mjs +6 -0
- package/dist/jsonc-D9dDWb1J.mjs.map +1 -0
- package/dist/{listFiles-CWHAdKY-.mjs → listFiles-BdFUbX5x.mjs} +1 -1
- package/dist/{listFiles-CWHAdKY-.mjs.map → listFiles-BdFUbX5x.mjs.map} +1 -1
- package/dist/logger-zplFSeXU.mjs +2 -0
- package/dist/logger-zplFSeXU.mjs.map +1 -0
- package/dist/manifest-BNIpO_zW.mjs +2 -0
- package/dist/manifest-BNIpO_zW.mjs.map +1 -0
- package/dist/{mapConsentCsvRowsToInputs-MMPZwzrV.mjs → mapConsentCsvRowsToInputs-Dqb6WL4r.mjs} +1 -1
- package/dist/{mapConsentCsvRowsToInputs-MMPZwzrV.mjs.map → mapConsentCsvRowsToInputs-Dqb6WL4r.mjs.map} +1 -1
- package/dist/{markRequestDataSiloIdsCompleted-PKCcn5wQ.mjs → markRequestDataSiloIdsCompleted-B5N-cQWr.mjs} +2 -2
- package/dist/{markRequestDataSiloIdsCompleted-PKCcn5wQ.mjs.map → markRequestDataSiloIdsCompleted-B5N-cQWr.mjs.map} +1 -1
- package/dist/{markSilentPrivacyRequests-7l9I99pe.mjs → markSilentPrivacyRequests-BiaButWF.mjs} +2 -2
- package/dist/{markSilentPrivacyRequests-7l9I99pe.mjs.map → markSilentPrivacyRequests-BiaButWF.mjs.map} +1 -1
- package/dist/mergeTranscendInputs-DFQcbh8b.mjs +3 -0
- package/dist/mergeTranscendInputs-DFQcbh8b.mjs.map +1 -0
- package/dist/missing-manifest-DgcukXfg.mjs +2 -0
- package/dist/missing-manifest-DgcukXfg.mjs.map +1 -0
- package/dist/{module-graph-DBC7ZvyA.mjs → module-graph-Cb0g74Ft.mjs} +1 -1
- package/dist/{module-graph-DBC7ZvyA.mjs.map → module-graph-Cb0g74Ft.mjs.map} +1 -1
- package/dist/{notifyPrivacyRequestsAdditionalTime-BPIYuCov.mjs → notifyPrivacyRequestsAdditionalTime-D3Vtvg5G.mjs} +2 -2
- package/dist/{notifyPrivacyRequestsAdditionalTime-BPIYuCov.mjs.map → notifyPrivacyRequestsAdditionalTime-D3Vtvg5G.mjs.map} +1 -1
- package/dist/{parquetToCsvOneFile-CSVdrLvq.mjs → parquetToCsvOneFile-ChmPC6dL.mjs} +2 -2
- package/dist/{parquetToCsvOneFile-CSVdrLvq.mjs.map → parquetToCsvOneFile-ChmPC6dL.mjs.map} +1 -1
- package/dist/{parseAttributesFromString-C5-KGkPH.mjs → parseAttributesFromString-CO16oAlx.mjs} +2 -2
- package/dist/{parseAttributesFromString-C5-KGkPH.mjs.map → parseAttributesFromString-CO16oAlx.mjs.map} +1 -1
- package/dist/{parseVariablesFromString-Cr3x2uq3.mjs → parseVariablesFromString-BOSfQ4QF.mjs} +1 -1
- package/dist/{parseVariablesFromString-Cr3x2uq3.mjs.map → parseVariablesFromString-BOSfQ4QF.mjs.map} +1 -1
- package/dist/path-safety-D5Qwl40b.mjs +2 -0
- package/dist/path-safety-D5Qwl40b.mjs.map +1 -0
- package/dist/policy-bundle-manifest-D6dkbjNi.mjs +7 -0
- package/dist/policy-bundle-manifest-D6dkbjNi.mjs.map +1 -0
- package/dist/policy-runtime-BMKa-Upy.mjs +2 -0
- package/dist/policy-runtime-BMKa-Upy.mjs.map +1 -0
- package/dist/{printResult-esxW3HhT.mjs → printResult-CHe3nRN4.mjs} +5 -5
- package/dist/printResult-CHe3nRN4.mjs.map +1 -0
- package/dist/project-plan-apply-Dvndx455.mjs +3 -0
- package/dist/project-plan-apply-Dvndx455.mjs.map +1 -0
- package/dist/prompts-ClBXdELk.mjs +4 -0
- package/dist/prompts-ClBXdELk.mjs.map +1 -0
- package/dist/{pullAllDatapoints-H92qjLSx.mjs → pullAllDatapoints-uk5ggk6O.mjs} +2 -2
- package/dist/{pullAllDatapoints-H92qjLSx.mjs.map → pullAllDatapoints-uk5ggk6O.mjs.map} +1 -1
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-BrDmPbvc.mjs → pullChunkedCustomSiloOutstandingIdentifiers-D85P1W_8.mjs} +2 -2
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-BrDmPbvc.mjs.map → pullChunkedCustomSiloOutstandingIdentifiers-D85P1W_8.mjs.map} +1 -1
- package/dist/{pullConsentManagerMetrics-Cvex-gpd.mjs → pullConsentManagerMetrics-Dv5n1ZGp.mjs} +2 -2
- package/dist/{pullConsentManagerMetrics-Cvex-gpd.mjs.map → pullConsentManagerMetrics-Dv5n1ZGp.mjs.map} +1 -1
- package/dist/{pullManualEnrichmentIdentifiersToCsv-qjdNA75O.mjs → pullManualEnrichmentIdentifiersToCsv-DbNSrKEk.mjs} +2 -2
- package/dist/{pullManualEnrichmentIdentifiersToCsv-qjdNA75O.mjs.map → pullManualEnrichmentIdentifiersToCsv-DbNSrKEk.mjs.map} +1 -1
- package/dist/{pullTranscendConfiguration-CD7Frn8z.mjs → pullTranscendConfiguration-B63M3ild.mjs} +3 -3
- package/dist/{pullTranscendConfiguration-CD7Frn8z.mjs.map → pullTranscendConfiguration-B63M3ild.mjs.map} +1 -1
- package/dist/{pullUnstructuredSubDataPointRecommendations-Dmh_-r95.mjs → pullUnstructuredSubDataPointRecommendations-YTBzfkRZ.mjs} +2 -2
- package/dist/{pullUnstructuredSubDataPointRecommendations-Dmh_-r95.mjs.map → pullUnstructuredSubDataPointRecommendations-YTBzfkRZ.mjs.map} +1 -1
- package/dist/{pushCronIdentifiersFromCsv-DjeY7TtK.mjs → pushCronIdentifiersFromCsv-5ruUKHtL.mjs} +2 -2
- package/dist/{pushCronIdentifiersFromCsv-DjeY7TtK.mjs.map → pushCronIdentifiersFromCsv-5ruUKHtL.mjs.map} +1 -1
- package/dist/{pushManualEnrichmentIdentifiersFromCsv-BDtMo13j.mjs → pushManualEnrichmentIdentifiersFromCsv-B0i2239u.mjs} +2 -2
- package/dist/{pushManualEnrichmentIdentifiersFromCsv-BDtMo13j.mjs.map → pushManualEnrichmentIdentifiersFromCsv-B0i2239u.mjs.map} +1 -1
- package/dist/{readCsv-Cr_1t8F5.mjs → readCsv-DLg8pwjd.mjs} +1 -1
- package/dist/{readCsv-Cr_1t8F5.mjs.map → readCsv-DLg8pwjd.mjs.map} +1 -1
- package/dist/readTranscendYaml-BNn6-Ofi.mjs +4 -0
- package/dist/readTranscendYaml-BNn6-Ofi.mjs.map +1 -0
- package/dist/{removeUnverifiedRequestIdentifiers-B9uPApVR.mjs → removeUnverifiedRequestIdentifiers-QmeqrmxM.mjs} +2 -2
- package/dist/{removeUnverifiedRequestIdentifiers-B9uPApVR.mjs.map → removeUnverifiedRequestIdentifiers-QmeqrmxM.mjs.map} +1 -1
- package/dist/{renderTable-DKTZbMjY.mjs → renderTable-D68QasPc.mjs} +1 -1
- package/dist/{renderTable-DKTZbMjY.mjs.map → renderTable-D68QasPc.mjs.map} +1 -1
- package/dist/{request-B1yRTdG-.mjs → request-BiPv6jmP.mjs} +1 -1
- package/dist/{request-B1yRTdG-.mjs.map → request-BiPv6jmP.mjs.map} +1 -1
- package/dist/{resolveBundleIdByName-DuxWYrDF.mjs → resolveBundleIdByName-qXZBrUz_.mjs} +2 -2
- package/dist/{resolveBundleIdByName-DuxWYrDF.mjs.map → resolveBundleIdByName-qXZBrUz_.mjs.map} +1 -1
- package/dist/{resolvePolicyBundleVersion-DhXZKwb4.mjs → resolvePolicyBundleVersion-BG7XqBBP.mjs} +2 -2
- package/dist/{resolvePolicyBundleVersion-DhXZKwb4.mjs.map → resolvePolicyBundleVersion-BG7XqBBP.mjs.map} +1 -1
- package/dist/{retryRequestDataSilos-D9dBtbdl.mjs → retryRequestDataSilos-BfckLjOJ.mjs} +2 -2
- package/dist/{retryRequestDataSilos-D9dBtbdl.mjs.map → retryRequestDataSilos-BfckLjOJ.mjs.map} +1 -1
- package/dist/{run-captured-process-CpZBh0lG.mjs → run-captured-process-BXwWXuTh.mjs} +1 -1
- package/dist/{run-captured-process-CpZBh0lG.mjs.map → run-captured-process-BXwWXuTh.mjs.map} +1 -1
- package/dist/runOpa-CswzJxIs.mjs +2 -0
- package/dist/runOpa-CswzJxIs.mjs.map +1 -0
- package/dist/scaffold-model-BAbYTR0q.mjs +2 -0
- package/dist/scaffold-model-BAbYTR0q.mjs.map +1 -0
- package/dist/scaffold-planning-CNv83t_v.mjs +123 -0
- package/dist/scaffold-planning-CNv83t_v.mjs.map +1 -0
- package/dist/scaffold-templates-DzDGTZie.mjs +84 -0
- package/dist/scaffold-templates-DzDGTZie.mjs.map +1 -0
- package/dist/{skipPreflightJobs-Beia_iB6.mjs → skipPreflightJobs-Do_kwu3m.mjs} +2 -2
- package/dist/{skipPreflightJobs-Beia_iB6.mjs.map → skipPreflightJobs-Do_kwu3m.mjs.map} +1 -1
- package/dist/{skipRequestDataSilos-BSNLacnR.mjs → skipRequestDataSilos-BNDhdllD.mjs} +2 -2
- package/dist/{skipRequestDataSilos-BSNLacnR.mjs.map → skipRequestDataSilos-BNDhdllD.mjs.map} +1 -1
- package/dist/{streamPrivacyRequestsToCsv-Cksb9D49.mjs → streamPrivacyRequestsToCsv-CbJfhV6A.mjs} +2 -2
- package/dist/{streamPrivacyRequestsToCsv-Cksb9D49.mjs.map → streamPrivacyRequestsToCsv-CbJfhV6A.mjs.map} +1 -1
- package/dist/{syncCodePackages-DsC1tE8B.mjs → syncCodePackages-BHE2Tn1Q.mjs} +2 -2
- package/dist/{syncCodePackages-DsC1tE8B.mjs.map → syncCodePackages-BHE2Tn1Q.mjs.map} +1 -1
- package/dist/{updateConsentManagerVersionToLatest-CaEkTnlL.mjs → updateConsentManagerVersionToLatest-DEFwH4Se.mjs} +2 -2
- package/dist/{updateConsentManagerVersionToLatest-CaEkTnlL.mjs.map → updateConsentManagerVersionToLatest-DEFwH4Se.mjs.map} +1 -1
- package/dist/{uploadConsents-CNyuYx0o.mjs → uploadConsents-ChmFIw_j.mjs} +2 -2
- package/dist/{uploadConsents-CNyuYx0o.mjs.map → uploadConsents-ChmFIw_j.mjs.map} +1 -1
- package/dist/{uploadPrivacyRequestsFromCsv-BdTQOZnF.mjs → uploadPrivacyRequestsFromCsv-D3KSb1qH.mjs} +2 -2
- package/dist/{uploadPrivacyRequestsFromCsv-BdTQOZnF.mjs.map → uploadPrivacyRequestsFromCsv-D3KSb1qH.mjs.map} +1 -1
- package/dist/{validateTranscendAuth-CnHMn77m.mjs → validateTranscendAuth-D6Wr1cxY.mjs} +2 -2
- package/dist/{validateTranscendAuth-CnHMn77m.mjs.map → validateTranscendAuth-D6Wr1cxY.mjs.map} +1 -1
- package/dist/{writeCsv-D46aDkGf.mjs → writeCsv-D-2siSwE.mjs} +1 -1
- package/dist/{writeCsv-D46aDkGf.mjs.map → writeCsv-D-2siSwE.mjs.map} +1 -1
- package/package.json +8 -7
- package/dist/app-DCY8E776.mjs.map +0 -1
- package/dist/codecs-BsQFAFGT.mjs +0 -2
- package/dist/codecs-BsQFAFGT.mjs.map +0 -1
- package/dist/constants-BMAOvdcA.mjs +0 -2
- package/dist/constants-BMAOvdcA.mjs.map +0 -1
- package/dist/deno-runtime-QqwBDKXX.mjs +0 -2
- package/dist/deno-runtime-QqwBDKXX.mjs.map +0 -1
- package/dist/impl-BFJtKA2o2.mjs.map +0 -1
- package/dist/impl-BJlKQJdS2.mjs.map +0 -1
- package/dist/impl-BNPgYSBT.mjs +0 -3
- package/dist/impl-BNPgYSBT.mjs.map +0 -1
- package/dist/impl-BYGNx-8W2.mjs.map +0 -1
- package/dist/impl-BcJ019T2.mjs +0 -2
- package/dist/impl-BsC1KVxS.mjs +0 -2
- package/dist/impl-BwRcpAno.mjs.map +0 -1
- package/dist/impl-C5dzaDga2.mjs.map +0 -1
- package/dist/impl-C8BmxFQ2.mjs +0 -2
- package/dist/impl-CXUJ1eRC.mjs.map +0 -1
- package/dist/impl-CzxvoiGW.mjs +0 -3
- package/dist/impl-D-SQJc3t.mjs +0 -2
- package/dist/impl-D-SQJc3t.mjs.map +0 -1
- package/dist/impl-D23kJPAQ2.mjs +0 -3
- package/dist/impl-D23kJPAQ2.mjs.map +0 -1
- package/dist/impl-DOoR93T6.mjs.map +0 -1
- package/dist/impl-Dib6Fff12.mjs.map +0 -1
- package/dist/impl-Dk6TZKrp2.mjs.map +0 -1
- package/dist/impl-Dm-Fkr392.mjs.map +0 -1
- package/dist/impl-Drql0g72.mjs.map +0 -1
- package/dist/impl-FLj_mWLO.mjs +0 -2
- package/dist/impl-FLj_mWLO.mjs.map +0 -1
- package/dist/impl-VPBEqV0w2.mjs.map +0 -1
- package/dist/impl-m3QXCc6n.mjs.map +0 -1
- package/dist/logger-9T6vva73.mjs +0 -2
- package/dist/logger-9T6vva73.mjs.map +0 -1
- package/dist/manifest-DVuW_vvO.mjs +0 -2
- package/dist/manifest-DVuW_vvO.mjs.map +0 -1
- package/dist/mergeTranscendInputs-CWHXH0kF.mjs +0 -3
- package/dist/mergeTranscendInputs-CWHXH0kF.mjs.map +0 -1
- package/dist/missing-manifest-BKj1j-jg.mjs +0 -2
- package/dist/missing-manifest-BKj1j-jg.mjs.map +0 -1
- package/dist/printResult-esxW3HhT.mjs.map +0 -1
- package/dist/project-plan-apply-C2DfikVN.mjs +0 -3
- package/dist/project-plan-apply-C2DfikVN.mjs.map +0 -1
- package/dist/readTranscendYaml-STYnRzq-.mjs +0 -4
- package/dist/readTranscendYaml-STYnRzq-.mjs.map +0 -1
- package/dist/runOpa-CkKAwsvB.mjs +0 -2
- package/dist/runOpa-CkKAwsvB.mjs.map +0 -1
- package/dist/scaffold-planning-DMACXSnX.mjs +0 -121
- package/dist/scaffold-planning-DMACXSnX.mjs.map +0 -1
- package/dist/scaffold-templates-CsHM1y80.mjs +0 -84
- package/dist/scaffold-templates-CsHM1y80.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-D23kJPAQ2.mjs","names":["fg"],"sources":["../src/commands/policy/helpers/buildOpaBundleTarball.ts","../src/commands/policy/helpers/buildPolicyBundleFormData.ts","../src/commands/policy/helpers/defaultPolicyVersionLabel.ts","../src/commands/policy/publish/impl.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { gunzipSync } from 'node:zlib';\n\nimport fg from 'fast-glob';\n\nimport { MAX_BUNDLE_COMPRESSED_BYTES, MAX_BUNDLE_DECOMPRESSED_BYTES } from '../constants.js';\nimport { assertOpaInstalled } from './assertOpaInstalled.js';\nimport { runOPACapture } from './runOpa.js';\n\n/**\n * Returns whether a relative path is a publishable Rego policy file.\n *\n * OPA test files (`*_test.rego`) are excluded because they are for local\n * validation only and are not part of the upload contract.\n *\n * @param relativePath - Path relative to the bundle directory\n * @returns Whether the file should be included in the upload archive\n */\nfunction isPublishableRegoFile(relativePath: string): boolean {\n return relativePath.endsWith('.rego') && !relativePath.endsWith('_test.rego');\n}\n\n/** Shape of the OPA bundle `manifest.json` as accepted by the Policy Engine. */\ninterface PolicyBundleManifest {\n /** Roots of the bundle, e.g. `[\"policy_engine\"]` or `[\"policy_engine/transcend\"]` */\n roots: string[];\n}\n\n/**\n * Reads and validates `manifest.json` from a policy bundle directory.\n *\n * The Policy Engine requires `manifest.json` to declare `roots` as an array of\n * strings. OPA's own tooling does not always enforce this against the Rego\n * packages on upload, so we validate the shape client-side to surface a clear,\n * actionable error instead of an opaque server `400` or a decide-time\n * fail-closed footgun.\n *\n * @param dir - Absolute path to the policy bundle directory\n * @returns The parsed manifest\n */\nfunction readPolicyBundleManifest(dir: string): PolicyBundleManifest {\n const manifestPath = path.join(dir, 'manifest.json');\n if (!fs.existsSync(manifestPath)) {\n throw new Error('Policy bundle directory must contain a manifest.json file.');\n }\n\n const raw = fs.readFileSync(manifestPath, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n throw new Error(\n `manifest.json is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,\n { cause: err },\n );\n }\n\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('manifest.json must contain a JSON object.');\n }\n\n const roots = (parsed as { roots?: unknown }).roots;\n if (!Array.isArray(roots) || roots.length === 0) {\n throw new Error(\n 'manifest.json must declare \"roots\" as a non-empty array of strings (e.g. {\"roots\":[\"policy_engine\"]}).',\n );\n }\n\n if (!roots.every((root) => typeof root === 'string' && root.length > 0)) {\n throw new Error('manifest.json \"roots\" must be an array of non-empty strings.');\n }\n\n return { roots };\n}\n\n/** Result of collecting publishable entries from a policy bundle directory. */\ninterface PolicyBundleArchiveContents {\n /** Relative paths to include in the upload tarball (manifest first, then rego) */\n entries: string[];\n /** Parsed manifest */\n manifest: PolicyBundleManifest;\n}\n\n/**\n * Collects `manifest.json` and publishable `.rego` files from a policy directory.\n *\n * @param dir - Absolute path to the policy bundle directory\n * @returns Archive entries and the parsed manifest\n */\nfunction collectPolicyBundleArchiveEntries(dir: string): PolicyBundleArchiveContents {\n const manifest = readPolicyBundleManifest(dir);\n\n const regoFiles = fg\n .sync('**/*.rego', {\n cwd: dir,\n onlyFiles: true,\n dot: false,\n })\n .filter(isPublishableRegoFile);\n\n if (regoFiles.length === 0) {\n throw new Error('Policy bundle directory must contain at least one .rego policy file.');\n }\n\n return { entries: ['manifest.json', ...regoFiles.sort()], manifest };\n}\n\n/** Matches a Rego `package <path>` declaration. */\nconst PACKAGE_DECLARATION_PATTERN = /^\\s*package\\s+([A-Za-z_][\\w.]*)/m;\n\n/**\n * Normalizes an OPA manifest root to a dotted package prefix.\n *\n * Roots use `/` as the path separator (e.g. `policy_engine/transcend`); Rego\n * package paths use `.` (e.g. `policy_engine.transcend`).\n *\n * @param root - A manifest root string\n * @returns The root in dotted form\n */\nfunction normalizeRootToPackagePrefix(root: string): string {\n return root.replace(/\\//g, '.');\n}\n\n/**\n * Reads the Rego package path declared in a `.rego` file.\n *\n * @param filePath - Absolute path to the `.rego` file\n * @returns The dotted package path, or `undefined` if no `package` declaration\n */\nfunction readRegoPackagePath(filePath: string): string | undefined {\n const contents = fs.readFileSync(filePath, 'utf8');\n const match = PACKAGE_DECLARATION_PATTERN.exec(contents);\n return match?.[1];\n}\n\n/**\n * Verifies that every publishable `.rego` package is covered by a manifest root.\n *\n * A bundle whose `roots` do not cover its Rego packages will upload cleanly but\n * fail-closed at decide time — the customer only discovers the mismatch via\n * denied decisions. This surfaces the mismatch at upload with a clear message.\n *\n * @param dir - Absolute path to the policy bundle directory\n * @param regoFiles - Relative paths to publishable `.rego` files\n * @param roots - Manifest roots\n */\nfunction assertRootsCoverPackages(dir: string, regoFiles: string[], roots: string[]): void {\n const rootPrefixes = roots.map(normalizeRootToPackagePrefix);\n\n const uncovered: string[] = [];\n for (const relativeRego of regoFiles) {\n const pkg = readRegoPackagePath(path.join(dir, relativeRego));\n if (!pkg) {\n continue;\n }\n const covered = rootPrefixes.some((prefix) => pkg === prefix || pkg.startsWith(`${prefix}.`));\n if (!covered) {\n uncovered.push(\n ` - ${relativeRego} (package ${pkg}) is not covered by roots [${roots.join(', ')}]`,\n );\n }\n }\n\n if (uncovered.length > 0) {\n throw new Error(\n [\n 'manifest.json \"roots\" do not cover all Rego packages in the bundle; ' +\n 'uncovered packages will fail-closed at decide time. Either broaden \"roots\" or move the policy under a covered package:',\n ...uncovered,\n ].join('\\n'),\n );\n }\n}\n\n/**\n * Verifies that a policy directory compiles end-to-end with `opa build`.\n *\n * The compiled output is discarded — the server receives the `manifest.json` +\n * `.rego` archive produced by {@link buildOpaBundleTarball}, not the OPA bundle\n * — but a successful build guarantees the policies compile and link, surfacing\n * errors (syntax, missing imports, undefined references, etc.) before upload.\n *\n * @param dir - Absolute path to the policy bundle directory\n */\nasync function assertBundleCompiles(dir: string): Promise<void> {\n const buildOutputPath = path.join(\n os.tmpdir(),\n `transcend-policy-bundle-build-${Date.now()}-${Math.random().toString(36).slice(2)}.tar.gz`,\n );\n try {\n // Run with `cwd` set to the bundle directory and pass `.` so `opa build`\n // resolves the bundle root correctly. `*_test.rego` files are local-only.\n const { code, stderr } = await runOPACapture(\n ['build', '--v0-compatible', '--ignore', '*_test.rego', '-o', buildOutputPath, '.'],\n { cwd: dir },\n );\n if (code !== 0) {\n throw new Error(stderr.trim() || `opa build failed with exit code ${code}`);\n }\n } finally {\n if (fs.existsSync(buildOutputPath)) {\n fs.unlinkSync(buildOutputPath);\n }\n }\n}\n\n/**\n * Formats a byte count as a human-readable size with binary units.\n *\n * @param bytes - Number of bytes\n * @returns Human-readable size, e.g. `14 MiB` or `4 KiB`\n */\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n const kib = bytes / 1024;\n if (kib < 1024) {\n return `${kib % 1 === 0 ? kib.toFixed(0) : kib.toFixed(1)} KiB`;\n }\n const mib = kib / 1024;\n return `${mib % 1 === 0 ? mib.toFixed(0) : mib.toFixed(1)} MiB`;\n}\n\n/**\n * Builds a gzip-compressed policy bundle tarball for upload to Transcend.\n *\n * The Policy Engine API expects a plain archive containing `manifest.json` and\n * one or more `.rego` files. This differs from `opa build` output, which embeds\n * `.manifest`, `data.json`, and other OPA bundle metadata that the server\n * rejects. Before packaging, the manifest is validated (shape + root coverage)\n * and the bundle is validated with `opa check` (strict Rego linting) and\n * `opa build` (full compilation) so failures surface client-side rather than\n * after upload.\n *\n * @param dir - Directory containing `manifest.json` and `.rego` policy files\n * @returns Absolute path to the generated `.tar.gz` bundle\n */\nexport async function buildOpaBundleTarball(dir: string): Promise<string> {\n assertOpaInstalled();\n\n const resolvedDir = path.resolve(dir);\n if (!fs.existsSync(resolvedDir) || !fs.statSync(resolvedDir).isDirectory()) {\n throw new Error(`Policy directory does not exist or is not a directory: ${resolvedDir}`);\n }\n\n // Validate the manifest shape and that roots cover every Rego package before\n // invoking OPA, so invalid manifests surface a clear error instead of an\n // opaque `opa build failed with exit code 1`.\n const { entries: archiveEntries, manifest } = collectPolicyBundleArchiveEntries(resolvedDir);\n const regoFiles = archiveEntries.filter((entry) => entry !== 'manifest.json');\n assertRootsCoverPackages(resolvedDir, regoFiles, manifest.roots);\n\n // Match the Rego v1 validation the Policy Engine API runs on upload.\n const { code: checkCode, stderr: checkStderr } = await runOPACapture([\n 'check',\n '--strict',\n '--v0-compatible',\n resolvedDir,\n ]);\n if (checkCode !== 0) {\n throw new Error(checkStderr.trim() || `opa check failed with exit code ${checkCode}`);\n }\n\n // Ensure the bundle compiles end-to-end before packaging for upload.\n await assertBundleCompiles(resolvedDir);\n\n const outputPath = path.join(\n os.tmpdir(),\n `transcend-policy-bundle-${Date.now()}-${Math.random().toString(36).slice(2)}.tar.gz`,\n );\n\n const tarResult = spawnSync('tar', ['-czf', outputPath, '-C', resolvedDir, ...archiveEntries], {\n env: { ...process.env, COPYFILE_DISABLE: '1' },\n encoding: 'utf8',\n });\n if (tarResult.status !== 0) {\n throw new Error(\n `Failed to create policy bundle archive: ${tarResult.stderr.trim() || 'tar failed'}`,\n );\n }\n\n const compressedBytes = fs.readFileSync(outputPath);\n if (compressedBytes.byteLength > MAX_BUNDLE_COMPRESSED_BYTES) {\n fs.unlinkSync(outputPath);\n throw new Error(\n `Policy bundle exceeds the ${formatBytes(MAX_BUNDLE_COMPRESSED_BYTES)} compressed upload limit ` +\n `(bundle is ${formatBytes(compressedBytes.byteLength)}). ` +\n `The server also rejects decompressed bundles larger than ${formatBytes(MAX_BUNDLE_DECOMPRESSED_BYTES)}.`,\n );\n }\n\n const decompressedBytes = gunzipSync(compressedBytes);\n if (decompressedBytes.byteLength > MAX_BUNDLE_DECOMPRESSED_BYTES) {\n fs.unlinkSync(outputPath);\n throw new Error(\n `Policy bundle exceeds the ${formatBytes(MAX_BUNDLE_DECOMPRESSED_BYTES)} decompressed upload limit ` +\n `(bundle is ${formatBytes(decompressedBytes.byteLength)} decompressed).`,\n );\n }\n\n return outputPath;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\n/** Fields for building a policy bundle upload form. */\nexport interface BuildPolicyBundleFormDataOptions {\n /** Absolute path to the bundle tarball */\n bundlePath: string;\n /** Version label */\n version: string;\n /** Optional description */\n description?: string;\n /** Bundle name (only for create) */\n bundleName?: string;\n}\n\n/**\n * Builds multipart form data for a policy bundle upload.\n *\n * @param options - Upload fields\n * @returns FormData ready for POST\n */\nexport function buildPolicyBundleFormData(options: BuildPolicyBundleFormDataOptions): FormData {\n const bundleBytes = fs.readFileSync(options.bundlePath);\n const form = new FormData();\n form.append(\n 'bundle',\n new Blob([bundleBytes], { type: 'application/gzip' }),\n path.basename(options.bundlePath),\n );\n form.append('version', options.version);\n if (options.description) {\n form.append('description', options.description);\n }\n if (options.bundleName) {\n form.append('bundleName', options.bundleName);\n }\n return form;\n}\n","/**\n * Formats a date as `yyyy-mm-dd-hh-mm-ss` in UTC.\n *\n * @param date - Date to format\n * @returns Timestamp label\n */\nfunction formatPolicyVersionTimestamp(date: Date): string {\n return date.toISOString().slice(0, 19).replace(/[T:]/g, '-');\n}\n\n/**\n * Returns a default version label from the bundle name and current UTC timestamp.\n *\n * @param bundleName - Tenant-unique policy bundle name\n * @param now - Current time (for testing)\n * @returns Version label in `{bundleName}-yyyy-mm-dd-hh-mm-ss` form\n */\nexport function defaultPolicyVersionLabel(bundleName: string, now: Date = new Date()): string {\n return `${bundleName}-${formatPolicyVersionTimestamp(now)}`;\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 { buildExampleCommand } from '../../../lib/docgen/buildExamples.js';\nimport { inquirerConfirmBoolean } from '../../../lib/helpers/inquirer.js';\nimport type { ActivateCommandFlags } from '../activate/impl.js';\nimport {\n buildPolicyBundleFormData,\n buildPolicyEngineClient,\n buildOpaBundleTarball,\n defaultPolicyVersionLabel,\n formatPolicyBundleVersionSummary,\n policyEngineRequest,\n printResult,\n resolveBundleIdByName,\n setPolicyEngineCliDebug,\n} from '../helpers/index.js';\nimport type { CreatePolicyBundleResponse, CreatePolicyBundleVersionResponse } from '../types.js';\n\n/** CLI flags for `transcend policy publish`. */\nexport interface PublishCommandFlags {\n /** Directory containing Rego policy files */\n dir: string;\n /** Tenant-unique bundle name */\n 'bundle-name': string;\n /** Transcend API key */\n auth: string;\n /** Transcend API URL */\n 'transcend-url': string;\n /** Version label (defaults to `{bundleName}-yyyy-mm-dd-hh-mm-ss`) */\n version?: string;\n /** Optional version description */\n description?: string;\n /** Print raw JSON response */\n json: boolean;\n /** Skip the \"create new bundle\" confirmation */\n yes: boolean;\n /** Include technical error details when a command fails */\n debug?: boolean;\n}\n\n/**\n * Build and upload a new immutable policy bundle version.\n *\n * @param this - CLI context\n * @param flags - Command flags\n */\nexport async function publish(\n this: LocalContext,\n {\n dir,\n 'bundle-name': bundleName,\n auth,\n 'transcend-url': transcendUrl,\n version,\n description,\n json,\n yes,\n debug = false,\n }: PublishCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n setPolicyEngineCliDebug(debug);\n\n const resolvedDir = path.resolve(dir);\n const versionLabel = version ?? defaultPolicyVersionLabel(bundleName);\n const client = buildPolicyEngineClient(transcendUrl, auth);\n\n let bundlePath: string | undefined;\n try {\n this.logger.info(colors.green(`Building policy bundle from ${resolvedDir}...`));\n bundlePath = await buildOpaBundleTarball(resolvedDir);\n\n const existingBundleId = await resolveBundleIdByName(client, bundleName);\n\n let responseBody: CreatePolicyBundleResponse | CreatePolicyBundleVersionResponse;\n\n if (existingBundleId) {\n this.logger.info(colors.green(`Uploading new version for bundle \"${bundleName}\"...`));\n const form = buildPolicyBundleFormData({\n bundlePath,\n version: versionLabel,\n description,\n });\n responseBody = await policyEngineRequest(\n client\n .post(`v1/policy-engine/policy-bundles/${existingBundleId}/versions`, { body: form })\n .json<CreatePolicyBundleVersionResponse>(),\n );\n } else {\n if (!this.process.stdin.isTTY && !yes) {\n this.logger.error(\n colors.red(\n 'Cannot create a new bundle in a non-interactive environment; pass --yes to confirm.',\n ),\n );\n this.process.exit(1);\n return;\n }\n\n if (!yes) {\n this.logger.warn(\n colors.yellow(`No policy bundle named \"${bundleName}\" exists for this organization.`),\n );\n const shouldCreate = await inquirerConfirmBoolean({\n message: `No policy bundle named \"${bundleName}\" exists. Create a new bundle and upload its first version?`,\n });\n if (!shouldCreate) {\n this.logger.info(colors.yellow('Publish cancelled.'));\n return;\n }\n }\n\n this.logger.info(\n colors.green(`Creating bundle \"${bundleName}\" and uploading first version...`),\n );\n const createForm = buildPolicyBundleFormData({\n bundlePath,\n version: versionLabel,\n description,\n bundleName,\n });\n responseBody = await policyEngineRequest(\n client\n .post('v1/policy-engine/policy-bundles', {\n body: createForm,\n })\n .json<CreatePolicyBundleResponse>(),\n );\n }\n\n printResult(this.process.stdout, {\n json,\n data: responseBody,\n renderTable: () => formatPolicyBundleVersionSummary(responseBody.version),\n });\n\n this.logger.info(colors.green('Policy bundle version uploaded successfully.'));\n\n const activateCommand = buildExampleCommand<ActivateCommandFlags>(['policy', 'activate'], {\n version: responseBody.version.version,\n 'bundle-name': bundleName,\n });\n this.logger.info(\n colors.yellow(\n `Publishing a policy does not activate it. To activate this version, run:\\n ${activateCommand}`,\n ),\n );\n } finally {\n if (bundlePath && this.fs.existsSync(bundlePath)) {\n this.fs.unlinkSync(bundlePath);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-DOoR93T6.mjs","names":[],"sources":["../src/commands/inventory/derive-data-silos-from-data-flows-cross-instance/impl.ts"],"sourcesContent":["import path from 'node:path';\n\nimport { buildTranscendGraphQLClient, fetchAndIndexCatalogs } from '@transcend-io/sdk';\nimport colors from 'colors';\nimport { difference } from 'lodash-es';\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 DeriveDataSilosFromDataFlowsCrossInstanceCommandFlags {\n auth: string;\n dataFlowsYmlFolder: string;\n output: string;\n ignoreYmls?: string[];\n transcendUrl: string;\n}\n\nexport async function deriveDataSilosFromDataFlowsCrossInstance(\n this: LocalContext,\n {\n auth,\n dataFlowsYmlFolder,\n output,\n ignoreYmls = [],\n transcendUrl,\n }: DeriveDataSilosFromDataFlowsCrossInstanceCommandFlags,\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 // Ignore the data flows in these yml files\n const instancesToIgnore = ignoreYmls.map((x) => x.split('.')[0]);\n\n // Map over each data flow yml file and convert to data silo configurations\n const dataSiloInputs = filterFileNames(this.fs.readdirSync(dataFlowsYmlFolder)).map(\n (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 return {\n adTechDataSilos,\n siteTechDataSilos,\n organizationName: directory.split('.')[0],\n };\n },\n );\n\n // Mapping from service name to instances that have that service\n const serviceToInstance: { [k in string]: string[] } = {};\n dataSiloInputs.forEach(({ adTechDataSilos, siteTechDataSilos, organizationName }) => {\n const allDataSilos = [...adTechDataSilos, ...siteTechDataSilos];\n allDataSilos.forEach((dataSilo) => {\n const service = dataSilo['outer-type'] || dataSilo.integrationName;\n // create mapping to instance\n if (!serviceToInstance[service]) {\n serviceToInstance[service] = [];\n }\n serviceToInstance[service]!.push(organizationName);\n serviceToInstance[service] = [...new Set(serviceToInstance[service])];\n });\n });\n\n // List of ad tech integrations\n const adTechIntegrations = [\n ...new Set(\n dataSiloInputs\n .map(({ adTechDataSilos }) =>\n adTechDataSilos.map((silo) => silo['outer-type'] || silo.integrationName),\n )\n .flat(),\n ),\n ];\n\n // List of site tech integrations\n const siteTechIntegrations = difference(\n [\n ...new Set(\n dataSiloInputs\n .map(({ siteTechDataSilos }) =>\n siteTechDataSilos.map((silo) => silo['outer-type'] || silo.integrationName),\n )\n .flat(),\n ),\n ],\n adTechIntegrations,\n );\n\n // Mapping from service name to list of\n const serviceToFoundOnDomain: { [k in string]: string[] } = {};\n dataSiloInputs.forEach(({ adTechDataSilos, siteTechDataSilos }) => {\n const allDataSilos = [...adTechDataSilos, ...siteTechDataSilos];\n allDataSilos.forEach((dataSilo) => {\n const service = dataSilo['outer-type'] || dataSilo.integrationName;\n const foundOnDomain = dataSilo.attributes?.find((attr) => attr.key === 'Found On Domain');\n // create mapping to instance\n if (!serviceToFoundOnDomain[service]) {\n serviceToFoundOnDomain[service] = [];\n }\n serviceToFoundOnDomain[service]!.push(...(foundOnDomain?.values || []));\n serviceToFoundOnDomain[service] = [...new Set(serviceToFoundOnDomain[service])];\n });\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 // construct the aggregated data silo inputs\n const dataSilos = [...adTechIntegrations, ...siteTechIntegrations].map((service) => ({\n title: serviceToTitle[service],\n ...(serviceToSupportedIntegration[service]\n ? { integrationName: service }\n : { integrationName: 'promptAPerson', 'outer-type': service }),\n attributes: [\n {\n key: 'Tech Type',\n values: ['Ad Tech'],\n },\n {\n key: 'Business Units',\n values: difference(serviceToInstance[service] || [], instancesToIgnore),\n },\n {\n key: 'Found On Domain',\n values: serviceToFoundOnDomain[service] || [],\n },\n ],\n }));\n\n // Log output\n this.logger.log(`Total Services: ${dataSilos.length}`);\n this.logger.log(`Ad Tech Services: ${adTechIntegrations.length}`);\n this.logger.log(`Site Tech Services: ${siteTechIntegrations.length}`);\n\n this.fs.writeFileSync(\n output,\n serializeTranscendYaml({\n 'data-silos': dataSilos,\n }),\n );\n}\n"],"mappings":"2YAqBA,eAAsB,EAEpB,CACE,OACA,qBACA,SACA,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,EAItB,IAAM,EAAoB,EAAW,IAAK,GAAM,EAAE,MAAM,IAAI,CAAC,GAAG,CAG1D,EAAiB,EAAgB,KAAK,GAAG,YAAY,EAAmB,CAAC,CAAC,IAC7E,GAAc,CACb,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,CAED,MAAO,CACL,kBACA,oBACA,iBAAkB,EAAU,MAAM,IAAI,CAAC,GACxC,EAEJ,CAGK,EAAiD,EAAE,CACzD,EAAe,SAAS,CAAE,kBAAiB,oBAAmB,sBAAuB,CAEnF,CADsB,GAAG,EAAiB,GAAG,EACjC,CAAC,QAAS,GAAa,CACjC,IAAM,EAAU,EAAS,eAAiB,EAAS,gBAE9C,EAAkB,KACrB,EAAkB,GAAW,EAAE,EAEjC,EAAkB,GAAU,KAAK,EAAiB,CAClD,EAAkB,GAAW,CAAC,GAAG,IAAI,IAAI,EAAkB,GAAS,CAAC,EACrE,EACF,CAGF,IAAM,EAAqB,CACzB,GAAG,IAAI,IACL,EACG,KAAK,CAAE,qBACN,EAAgB,IAAK,GAAS,EAAK,eAAiB,EAAK,gBAAgB,CAC1E,CACA,MAAM,CACV,CACF,CAGK,EAAuB,EAC3B,CACE,GAAG,IAAI,IACL,EACG,KAAK,CAAE,uBACN,EAAkB,IAAK,GAAS,EAAK,eAAiB,EAAK,gBAAgB,CAC5E,CACA,MAAM,CACV,CACF,CACD,EACD,CAGK,EAAsD,EAAE,CAC9D,EAAe,SAAS,CAAE,kBAAiB,uBAAwB,CAEjE,CADsB,GAAG,EAAiB,GAAG,EACjC,CAAC,QAAS,GAAa,CACjC,IAAM,EAAU,EAAS,eAAiB,EAAS,gBAC7C,EAAgB,EAAS,YAAY,KAAM,GAAS,EAAK,MAAQ,kBAAkB,CAEpF,EAAuB,KAC1B,EAAuB,GAAW,EAAE,EAEtC,EAAuB,GAAU,KAAK,GAAI,GAAe,QAAU,EAAE,CAAE,CACvE,EAAuB,GAAW,CAAC,GAAG,IAAI,IAAI,EAAuB,GAAS,CAAC,EAC/E,EACF,CAIF,GAAM,CAAE,iBAAgB,iCAAkC,MAAM,EADjD,EAA4B,EAAc,EACmC,CAAE,CAC5F,OAAQ,KAAK,OACd,CAAC,CAGI,EAAY,CAAC,GAAG,EAAoB,GAAG,EAAqB,CAAC,IAAK,IAAa,CACnF,MAAO,EAAe,GACtB,GAAI,EAA8B,GAC9B,CAAE,gBAAiB,EAAS,CAC5B,CAAE,gBAAiB,gBAAiB,aAAc,EAAS,CAC/D,WAAY,CACV,CACE,IAAK,YACL,OAAQ,CAAC,UAAU,CACpB,CACD,CACE,IAAK,iBACL,OAAQ,EAAW,EAAkB,IAAY,EAAE,CAAE,EAAkB,CACxE,CACD,CACE,IAAK,kBACL,OAAQ,EAAuB,IAAY,EAAE,CAC9C,CACF,CACF,EAAE,CAGH,KAAK,OAAO,IAAI,mBAAmB,EAAU,SAAS,CACtD,KAAK,OAAO,IAAI,qBAAqB,EAAmB,SAAS,CACjE,KAAK,OAAO,IAAI,uBAAuB,EAAqB,SAAS,CAErE,KAAK,GAAG,cACN,EACA,EAAuB,CACrB,aAAc,EACf,CAAC,CACH"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-Dib6Fff12.mjs","names":[],"sources":["../src/commands/request/mark-silent/impl.ts"],"sourcesContent":["import type { RequestAction, RequestStatus } from '@transcend-io/privacy-types';\n\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { markSilentPrivacyRequests } from '../../../lib/requests/index.js';\n\nexport interface MarkSilentCommandFlags {\n auth: string;\n actions: RequestAction[];\n statuses?: RequestStatus[];\n requestIds?: string[];\n createdAtBefore?: Date;\n createdAtAfter?: Date;\n updatedAtBefore?: Date;\n updatedAtAfter?: Date;\n transcendUrl: string;\n concurrency: number;\n}\n\nexport async function markSilent(\n this: LocalContext,\n {\n auth,\n transcendUrl,\n actions,\n statuses,\n requestIds,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n concurrency,\n }: MarkSilentCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n\n await markSilentPrivacyRequests({\n transcendUrl,\n requestActions: actions,\n auth,\n requestIds,\n statuses,\n concurrency,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n });\n}\n"],"mappings":"iHAmBA,eAAsB,EAEpB,CACE,OACA,eACA,UACA,WACA,aACA,kBACA,iBACA,kBACA,iBACA,eAEa,CACf,EAAoB,KAAK,QAAQ,CAEjC,MAAM,EAA0B,CAC9B,eACA,eAAgB,EAChB,OACA,aACA,WACA,cACA,kBACA,iBACA,kBACA,iBACD,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-Dk6TZKrp2.mjs","names":[],"sources":["../src/commands/request/export/impl.ts"],"sourcesContent":["import type { RequestAction, RequestStatus } from '@transcend-io/privacy-types';\nimport colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { streamPrivacyRequestsToCsv } from '../../../lib/requests/index.js';\n\nexport interface ExportCommandFlags {\n auth: string;\n sombraAuth?: string;\n actions?: RequestAction[];\n statuses?: RequestStatus[];\n transcendUrl: string;\n file: string;\n concurrency: number;\n createdAtBefore?: Date;\n createdAtAfter?: Date;\n updatedAtBefore?: Date;\n updatedAtAfter?: Date;\n showTests?: boolean;\n skipRequestIdentifiers?: boolean;\n pageLimit: number;\n}\n\n// `export` is a reserved keyword, so we need to prefix it with an underscore\n// eslint-disable-next-line no-underscore-dangle\nexport async function _export(\n this: LocalContext,\n {\n auth,\n transcendUrl,\n file,\n pageLimit,\n concurrency,\n actions,\n sombraAuth,\n skipRequestIdentifiers,\n statuses,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n showTests,\n }: ExportCommandFlags,\n): Promise<void> {\n doneInputValidation(this.process);\n\n const { filePaths, totalCount } = await streamPrivacyRequestsToCsv({\n transcendUrl,\n concurrency,\n pageLimit,\n actions,\n statuses,\n auth,\n sombraAuth,\n skipRequestIdentifiers,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n isTest: showTests,\n file,\n });\n\n this.logger.info(\n colors.green(\n `Successfully wrote ${totalCount} requests to ` +\n `${filePaths.length} file(s): ${filePaths.join(', ')}`,\n ),\n );\n}\n"],"mappings":"wIA0BA,eAAsB,EAEpB,CACE,OACA,eACA,OACA,YACA,cACA,UACA,aACA,yBACA,WACA,kBACA,iBACA,kBACA,iBACA,aAEa,CACf,EAAoB,KAAK,QAAQ,CAEjC,GAAM,CAAE,YAAW,cAAe,MAAM,EAA2B,CACjE,eACA,cACA,YACA,UACA,WACA,OACA,aACA,yBACA,kBACA,iBACA,kBACA,iBACA,OAAQ,EACR,OACD,CAAC,CAEF,KAAK,OAAO,KACV,EAAO,MACL,sBAAsB,EAAW,eAC5B,EAAU,OAAO,YAAY,EAAU,KAAK,KAAK,GACvD,CACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-Dm-Fkr392.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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-Drql0g72.mjs","names":[],"sources":["../src/commands/policy/test/impl.ts"],"sourcesContent":["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 { assertOpaInstalled, runOpa } from '../helpers/index.js';\n\n/** CLI flags for `transcend policy test`. */\nexport interface TestCommandFlags {\n /** Directory containing Rego policy files and tests */\n dir: string;\n}\n\n/**\n * Run OPA tests against a local policy bundle.\n *\n * @param this - CLI context\n * @param flags - Command flags\n */\nexport async function test(this: LocalContext, { dir }: TestCommandFlags): Promise<void> {\n doneInputValidation(this.process);\n\n assertOpaInstalled();\n const resolvedDir = path.resolve(dir);\n\n this.logger.info(colors.green(`Running policy tests in ${resolvedDir}...`));\n\n const exitCode = await runOpa(['test', resolvedDir]);\n if (exitCode !== 0) {\n this.process.exit(exitCode);\n }\n\n this.logger.info(colors.green('Policy tests passed.'));\n}\n"],"mappings":"yJAoBA,eAAsB,EAAyB,CAAE,OAAwC,CACvF,EAAoB,KAAK,QAAQ,CAEjC,GAAoB,CACpB,IAAM,EAAc,EAAK,QAAQ,EAAI,CAErC,KAAK,OAAO,KAAK,EAAO,MAAM,2BAA2B,EAAY,KAAK,CAAC,CAE3E,IAAM,EAAW,MAAM,EAAO,CAAC,OAAQ,EAAY,CAAC,CAChD,IAAa,GACf,KAAK,QAAQ,KAAK,EAAS,CAG7B,KAAK,OAAO,KAAK,EAAO,MAAM,uBAAuB,CAAC"}
|
package/dist/impl-FLj_mWLO.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{r as e,t}from"./scaffold-templates-CsHM1y80.mjs";import{t as n}from"./done-input-validation-Cjk7a3jX.mjs";import{a as r,c as i,f as a,i as o,o as s,s as c}from"./deno-runtime-QqwBDKXX.mjs";import{t as l}from"./missing-manifest-BKj1j-jg.mjs";import{t as u}from"./project-plan-apply-C2DfikVN.mjs";import{a as d,c as f,d as p,f as m,l as h,o as g,r as _,s as v,t as y}from"./scaffold-planning-DMACXSnX.mjs";import{join as b}from"node:path";import x from"colors";function S(e,t,n){return!e.json&&!e.noInteractive&&!!(t&&n)}async function C(C,w){n(this.process);try{let n=i(this,{...w?{directory:w}:{},...C.manifest?{manifest:C.manifest}:{}});if(!this.fs.existsSync(n.manifestPath)){let e=this.process.cwd();throw Error(l({cwd:e,manifestPath:n.manifestPath,discoveredManifestPaths:c(this,e),command:`new`}))}let r=new o(this),T=S(C,this.process.stdin.isTTY,this.process.stderr.isTTY),E=C.name??(T?await r.text(`Custom Function display name:`,void 0,t=>{try{return e(t),!0}catch(e){return e instanceof Error?e.message:String(e)}}):void 0);if(!E)throw Error(`Missing Custom Function name. Pass --name in a non-interactive invocation.`);let D=C.template??(T?await r.select(`Starter type:`,[{name:`General`,value:`general`},{name:`DSR datapoint`,value:`dsr-datapoint`},{name:`DSR request enricher`,value:`dsr-enricher`},{name:`Combined DSR datapoint + enricher`,value:`dsr-both`}],`general`):void 0);if(!D||!t.includes(D))throw Error(`Missing Custom Function template. Pass --template=general, --template=dsr-datapoint, --template=dsr-enricher, or --template=dsr-both.`);let O=g(E,D),k=y({state:n,snapshots:s(this,n.manifestDirectory,_(n,O))},{generated:O});this.fs.existsSync(n.denoConfigPath)||k.warnings.push(`No Deno configuration was found. Run transcend custom-functions init ${a(n.targetDirectory,n.manifestPath,this.process.cwd())} --deno --noInteractive.`),C.json||this.logger.info(h(k,this.process.cwd()));let A=k.changes.length===0||C.dryRun;if(k.changes.length>0&&!C.dryRun)if(C.yes)A=!0;else if(T)A=await r.confirm(`Apply this complete plan?`,!0);else throw Error(`The plan requires approval in a non-interactive invocation. Review with --dryRun, then pass --yes.`);A&&!C.dryRun&&k.changes.length>0&&await u(this,k);let j=A&&!C.dryRun&&k.changes.length>0,M=n.existingSkillDirectories.some(({path:e})=>this.fs.existsSync(b(n.projectRoot,e,m,`SKILL.md`))),N=p({displayName:O.displayName,sourcePath:f(this.process.cwd(),b(n.manifestDirectory,O.sourceFile.path)),targetDirectory:f(this.process.cwd(),n.targetDirectory),manifestPath:f(this.process.cwd(),n.manifestPath),hasSkill:M,variableNames:d(O.manifestEntry)}),P=v(k,{applied:j,dryRun:C.dryRun,cwd:this.process.cwd(),aiHandoff:N});if(C.json){this.process.stdout.write(`${JSON.stringify(P)}\n`);return}if(C.dryRun){this.logger.info(x.yellow(`Dry run complete. No changes were written.`));return}if(!A){this.logger.info(x.yellow(`No changes applied.`));return}this.logger.info(x.green(`Custom Function added.`)),k.nextSteps.length>0&&(this.logger.info(`\n${x.bold(`Next steps`)}`),k.nextSteps.forEach(e=>this.logger.info(e))),this.logger.info(`\n${x.bold(`AI handoff — paste into your coding agent`)}`),this.logger.info(N)}catch(e){throw e instanceof r&&this.process.exit(130),e}}export{C as _new,C as newCustomFunction};
|
|
2
|
-
//# sourceMappingURL=impl-FLj_mWLO.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-FLj_mWLO.mjs","names":[],"sources":["../src/commands/custom-functions/new/impl.ts"],"sourcesContent":["import { join } 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 { buildNewFunctionAiHandoff } from '../../../lib/custom-functions/ai-handoff.js';\nimport { CUSTOM_FUNCTION_SKILL_NAME } from '../../../lib/custom-functions/custom-function-skill.js';\nimport { formatMissingManifestMessage } from '../../../lib/custom-functions/missing-manifest.js';\nimport { buildCustomFunctionProjectArguments } from '../../../lib/custom-functions/paths.js';\nimport {\n collectPlanningSnapshots,\n discoverCustomFunctionManifests,\n discoverCustomFunctionProject,\n} from '../../../lib/custom-functions/project-discovery.js';\nimport {\n CustomFunctionPrompts,\n PromptCancelledError,\n} from '../../../lib/custom-functions/prompts.js';\nimport {\n buildPlanResult,\n displayPath,\n renderProjectPlan,\n} from '../../../lib/custom-functions/scaffold-output.js';\nimport {\n buildAddFunctionPlan,\n getAddFunctionPlanningCandidatePaths,\n parameterNamesInManifestValue,\n prepareGeneratedCustomFunction,\n} from '../../../lib/custom-functions/scaffold-planning.js';\nimport {\n CUSTOM_FUNCTION_TEMPLATE_NAMES,\n type CustomFunctionTemplateName,\n validateCustomFunctionDisplayName,\n} from '../../../lib/custom-functions/scaffold-templates.js';\nimport { applyProjectPlan } from '../../../lib/scaffolding/project-plan-apply.js';\n\n/** Flags for `custom-functions new`. */\nexport interface CustomFunctionNewFlags {\n /** Explicit existing manifest path. */\n manifest?: string;\n /** Custom Function display name. */\n name?: string;\n /** Starter handler shape. */\n template?: CustomFunctionTemplateName;\n /** Disable prompts. */\n noInteractive: boolean;\n /** Render but do not apply. */\n dryRun: boolean;\n /** Approve the final displayed plan. */\n yes: boolean;\n /** Emit one stable JSON result on stdout. */\n json: boolean;\n}\n\n/**\n * Whether this invocation can ask questions.\n *\n * @param context - CLI context\n * @param flags - Interaction flags\n * @returns Whether prompts are enabled\n */\nfunction isInteractiveInvocation(\n flags: Pick<CustomFunctionNewFlags, 'json' | 'noInteractive'>,\n stdinIsTTY: boolean | undefined,\n stderrIsTTY: boolean | undefined,\n): boolean {\n return !flags.json && !flags.noInteractive && Boolean(stdinIsTTY && stderrIsTTY);\n}\n\n/**\n * Scaffold one deterministic local Custom Function.\n *\n * @param this - CLI context\n * @param flags - Template and interaction flags\n * @param directory - Optional target directory\n */\nexport async function _new(\n this: LocalContext,\n flags: CustomFunctionNewFlags,\n directory?: string,\n): Promise<void> {\n doneInputValidation(this.process);\n try {\n const state = discoverCustomFunctionProject(this, {\n ...(directory ? { directory } : {}),\n ...(flags.manifest ? { manifest: flags.manifest } : {}),\n });\n if (!this.fs.existsSync(state.manifestPath)) {\n const cwd = this.process.cwd();\n throw new Error(\n formatMissingManifestMessage({\n cwd,\n manifestPath: state.manifestPath,\n discoveredManifestPaths: discoverCustomFunctionManifests(this, cwd),\n command: 'new',\n }),\n );\n }\n const prompts = new CustomFunctionPrompts(this);\n const interactive = isInteractiveInvocation(\n flags,\n this.process.stdin.isTTY,\n this.process.stderr.isTTY,\n );\n const name =\n flags.name ??\n (interactive\n ? await prompts.text('Custom Function display name:', undefined, (value) => {\n try {\n validateCustomFunctionDisplayName(value);\n return true;\n } catch (error) {\n return error instanceof Error ? error.message : String(error);\n }\n })\n : undefined);\n if (!name) {\n throw new Error('Missing Custom Function name. Pass --name in a non-interactive invocation.');\n }\n const template =\n flags.template ??\n (interactive\n ? await prompts.select<CustomFunctionTemplateName>(\n 'Starter type:',\n [\n { name: 'General', value: 'general' },\n { name: 'DSR datapoint', value: 'dsr-datapoint' },\n { name: 'DSR request enricher', value: 'dsr-enricher' },\n { name: 'Combined DSR datapoint + enricher', value: 'dsr-both' },\n ],\n 'general',\n )\n : undefined);\n if (!template || !CUSTOM_FUNCTION_TEMPLATE_NAMES.includes(template)) {\n throw new Error(\n 'Missing Custom Function template. Pass --template=general, --template=dsr-datapoint, --template=dsr-enricher, or --template=dsr-both.',\n );\n }\n const generated = prepareGeneratedCustomFunction(name, template);\n const snapshots = collectPlanningSnapshots(\n this,\n state.manifestDirectory,\n getAddFunctionPlanningCandidatePaths(state, generated),\n );\n const plan = buildAddFunctionPlan(\n {\n state,\n snapshots,\n },\n { generated },\n );\n if (!this.fs.existsSync(state.denoConfigPath)) {\n plan.warnings.push(\n `No Deno configuration was found. Run ` +\n `transcend custom-functions init ${buildCustomFunctionProjectArguments(\n state.targetDirectory,\n state.manifestPath,\n this.process.cwd(),\n )} --deno --noInteractive.`,\n );\n }\n if (!flags.json) {\n this.logger.info(renderProjectPlan(plan, this.process.cwd()));\n }\n\n let approved = plan.changes.length === 0 || flags.dryRun;\n if (plan.changes.length > 0 && !flags.dryRun) {\n if (flags.yes) {\n approved = true;\n } else if (interactive) {\n approved = await prompts.confirm('Apply this complete plan?', true);\n } else {\n throw new Error(\n 'The plan requires approval in a non-interactive invocation. Review with --dryRun, then pass --yes.',\n );\n }\n }\n\n if (approved && !flags.dryRun && plan.changes.length > 0) {\n await applyProjectPlan(this, plan);\n }\n const applied = approved && !flags.dryRun && plan.changes.length > 0;\n const hasSkill = state.existingSkillDirectories.some(({ path }) =>\n this.fs.existsSync(join(state.projectRoot, path, CUSTOM_FUNCTION_SKILL_NAME, 'SKILL.md')),\n );\n const aiHandoff = buildNewFunctionAiHandoff({\n displayName: generated.displayName,\n sourcePath: displayPath(\n this.process.cwd(),\n join(state.manifestDirectory, generated.sourceFile.path),\n ),\n targetDirectory: displayPath(this.process.cwd(), state.targetDirectory),\n manifestPath: displayPath(this.process.cwd(), state.manifestPath),\n hasSkill,\n variableNames: parameterNamesInManifestValue(generated.manifestEntry),\n });\n const result = buildPlanResult(plan, {\n applied,\n dryRun: flags.dryRun,\n cwd: this.process.cwd(),\n aiHandoff,\n });\n if (flags.json) {\n this.process.stdout.write(`${JSON.stringify(result)}\\n`);\n return;\n }\n if (flags.dryRun) {\n this.logger.info(colors.yellow('Dry run complete. No changes were written.'));\n return;\n }\n if (!approved) {\n this.logger.info(colors.yellow('No changes applied.'));\n return;\n }\n this.logger.info(colors.green('Custom Function added.'));\n if (plan.nextSteps.length > 0) {\n this.logger.info(`\\n${colors.bold('Next steps')}`);\n plan.nextSteps.forEach((step) => this.logger.info(step));\n }\n this.logger.info(`\\n${colors.bold('AI handoff — paste into your coding agent')}`);\n this.logger.info(aiHandoff);\n } catch (error) {\n if (error instanceof PromptCancelledError) {\n this.process.exit(130);\n }\n throw error;\n }\n}\n\nexport { _new as newCustomFunction };\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-VPBEqV0w2.mjs","names":[],"sources":["../src/lib/oneTrust/helpers/oneTrustAssessmentToJson.ts","../src/lib/oneTrust/helpers/syncOneTrustAssessmentToDisk.ts","../src/lib/oneTrust/endpoints/getListOfOneTrustAssessments.ts","../src/lib/oneTrust/endpoints/getOneTrustAssessment.ts","../src/lib/oneTrust/endpoints/getOneTrustRisk.ts","../src/lib/oneTrust/endpoints/getOneTrustUser.ts","../src/lib/oneTrust/helpers/enrichOneTrustAssessment.ts","../src/lib/oneTrust/helpers/syncOneTrustAssessmentToTranscend.ts","../src/lib/oneTrust/helpers/syncOneTrustAssessmentsFromOneTrust.ts","../src/lib/oneTrust/helpers/syncOneTrustAssessmentsFromFile.ts","../src/lib/oneTrust/createOneTrustGotInstance.ts","../src/commands/migration/sync-ot/impl.ts"],"sourcesContent":["import { OneTrustEnrichedAssessment } from '@transcend-io/privacy-types';\n\n/**\n * Converts the assessment into a json entry.\n *\n * @param param - information about the assessment and amount of entries\n * @returns a stringified json entry ready to be appended to a file\n */\nexport const oneTrustAssessmentToJson = ({\n assessment,\n index,\n total,\n wrap = true,\n}: {\n /** The assessment to convert */\n assessment: OneTrustEnrichedAssessment;\n /** The position of the assessment in the final Json object */\n index: number;\n /** The total amount of the assessments in the final Json object */\n total?: number;\n /** Whether to wrap every entry in brackets */\n wrap?: boolean;\n}): string => {\n let jsonEntry = '';\n // start with an opening bracket\n if (index === 0 || wrap) {\n jsonEntry = '[\\n';\n }\n\n const stringifiedAssessment = JSON.stringify(assessment);\n\n // Add comma for all items except the last one\n const comma = total && index < total - 1 && !wrap ? ',' : '';\n\n // write to file\n jsonEntry = `${jsonEntry + stringifiedAssessment + comma}\\n`;\n\n // end with closing bracket\n if ((total && index === total - 1) || wrap) {\n jsonEntry += '\\n]';\n }\n\n return jsonEntry;\n};\n","import fs from 'node:fs';\n\nimport { OneTrustEnrichedAssessment } from '@transcend-io/privacy-types';\nimport colors from 'colors';\n\nimport { logger } from '../../../logger.js';\nimport { oneTrustAssessmentToJson } from './oneTrustAssessmentToJson.js';\n\n/**\n * Write the assessment to disk at the specified file path.\n *\n *\n * @param param - information about the assessment to write\n */\nexport const syncOneTrustAssessmentToDisk = ({\n file,\n assessment,\n index,\n total,\n}: {\n /** The file path to write the assessment to */\n file: string;\n /** The basic assessment */\n assessment: OneTrustEnrichedAssessment;\n /** The index of the assessment being written to the file */\n index: number;\n /** The total amount of assessments that we will write */\n total: number;\n}): void => {\n logger.info(\n colors.magenta(`Writing enriched assessment ${index + 1} of ${total} to file \"${file}\"...`),\n );\n\n if (index === 0) {\n fs.writeFileSync(\n file,\n oneTrustAssessmentToJson({\n assessment,\n index,\n total,\n wrap: false,\n }),\n );\n } else {\n fs.appendFileSync(\n file,\n oneTrustAssessmentToJson({\n assessment,\n index,\n total,\n wrap: false,\n }),\n );\n }\n};\n","import {\n OneTrustAssessment,\n OneTrustGetListOfAssessmentsResponse,\n} from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { Got } from 'got';\n\nimport { logger } from '../../../logger.js';\n\n/**\n * Fetch a list of all assessments from the OneTrust client.\n * ref: https://developer.onetrust.com/onetrust/reference/getallassessmentbasicdetailsusingget\n *\n * @param param - the information about the OneTrust client\n * @returns a list of OneTrustAssessment\n */\nexport const getListOfOneTrustAssessments = async ({\n oneTrust,\n}: {\n /** The OneTrust client instance */\n oneTrust: Got;\n}): Promise<OneTrustAssessment[]> => {\n let currentPage = 0;\n let totalPages = 1;\n let totalElements = 0;\n\n const allAssessments: OneTrustAssessment[] = [];\n\n while (currentPage < totalPages) {\n const { body } = await oneTrust.get(\n `api/assessment/v2/assessments?page=${currentPage}&size=2000`,\n );\n\n const { page, content } = decodeCodec(OneTrustGetListOfAssessmentsResponse, body);\n allAssessments.push(...(content ?? []));\n if (currentPage === 0) {\n totalPages = page?.totalPages ?? 0;\n totalElements = page?.totalElements ?? 0;\n }\n currentPage += 1;\n\n // log progress\n logger.info(`Fetched ${allAssessments.length} of ${totalElements} assessments.`);\n }\n\n return allAssessments;\n};\n","import { OneTrustGetAssessmentResponse } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { Got } from 'got';\n\n/**\n * Retrieve details about a particular assessment.\n * ref: https://developer.onetrust.com/onetrust/reference/exportassessmentusingget\n *\n * @param param - the information about the OneTrust client and assessment to retrieve\n * @returns details about the assessment\n */\nexport const getOneTrustAssessment = async ({\n oneTrust,\n assessmentId,\n}: {\n /** The OneTrust client instance */\n oneTrust: Got;\n /** The ID of the assessment to retrieve */\n assessmentId: string;\n}): Promise<OneTrustGetAssessmentResponse> => {\n const { body } = await oneTrust.get(\n `api/assessment/v2/assessments/${assessmentId}/export?ExcludeSkippedQuestions=false`,\n );\n\n return decodeCodec(OneTrustGetAssessmentResponse, body);\n};\n","import { OneTrustGetRiskResponse } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { Got } from 'got';\n\n/**\n * Retrieve details about a particular risk.\n * ref: https://developer.onetrust.com/onetrust/reference/getriskusingget\n *\n * @param param - the information about the OneTrust client and risk to retrieve\n * @returns the OneTrust risk\n */\nexport const getOneTrustRisk = async ({\n oneTrust,\n riskId,\n}: {\n /** The OneTrust client instance */\n oneTrust: Got;\n /** The ID of the OneTrust risk to retrieve */\n riskId: string;\n}): Promise<OneTrustGetRiskResponse> => {\n const { body } = await oneTrust.get(`api/risk/v2/risks/${riskId}`);\n\n return decodeCodec(OneTrustGetRiskResponse, body);\n};\n","import { OneTrustGetUserResponse } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { Got } from 'got';\n\n/**\n * Retrieve details about a particular user.\n * ref: https://developer.onetrust.com/onetrust/reference/getriskusingget\n *\n * @param param - the information about the OneTrust client and risk to retrieve\n * @returns the OneTrust risk\n */\nexport const getOneTrustUser = async ({\n oneTrust,\n userId,\n}: {\n /** The OneTrust client instance */\n oneTrust: Got;\n /** The ID of the OneTrust user to retrieve */\n userId: string;\n}): Promise<OneTrustGetUserResponse> => {\n const { body } = await oneTrust.get(`api/scim/v2/Users/${userId}`);\n\n return decodeCodec(OneTrustGetUserResponse, body);\n};\n","import {\n OneTrustAssessment,\n OneTrustEnrichedAssessment,\n OneTrustGetAssessmentResponse,\n OneTrustGetRiskResponse,\n OneTrustGetUserResponse,\n} from '@transcend-io/privacy-types';\nimport { keyBy } from 'lodash-es';\n\n/**\n * Merge the assessment, assessmentDetails, and riskDetails into one object.\n *\n * @param param - the assessment and risk information\n * @returns the assessment enriched with details and risk information\n */\nexport const enrichOneTrustAssessment = ({\n assessment,\n assessmentDetails,\n riskDetails,\n creatorDetails,\n approversDetails,\n respondentsDetails,\n}: {\n /** The OneTrust risk details */\n riskDetails: OneTrustGetRiskResponse[];\n /** The OneTrust assessment as returned from Get List of Assessments endpoint */\n assessment: OneTrustAssessment;\n /** The OneTrust assessment details */\n assessmentDetails: OneTrustGetAssessmentResponse;\n /** The OneTrust assessment creator details */\n creatorDetails: OneTrustGetUserResponse;\n /** The OneTrust assessment approvers details */\n approversDetails: OneTrustGetUserResponse[];\n /** The OneTrust assessment internal respondents details */\n respondentsDetails: OneTrustGetUserResponse[];\n}): OneTrustEnrichedAssessment => {\n const riskDetailsById = keyBy(riskDetails, 'id');\n const { sections, createdBy, ...restAssessmentDetails } = assessmentDetails;\n const sectionsWithEnrichedRisk = sections.map((section) => {\n const { questions, ...restSection } = section;\n const enrichedQuestions = questions.map((question) => {\n const { risks, ...restQuestion } = question;\n const enrichedRisks = (risks ?? []).map((risk) => {\n const details = riskDetailsById[risk.riskId];\n return {\n ...risk,\n ...details,\n level: risk.level,\n impactLevel: risk.impactLevel ?? 0,\n };\n });\n return {\n ...restQuestion,\n risks: enrichedRisks,\n };\n });\n return {\n ...restSection,\n questions: enrichedQuestions,\n };\n });\n\n // grab creator details\n const enrichedCreatedBy = {\n ...createdBy,\n active: creatorDetails?.active ?? false,\n userType: creatorDetails?.userType ?? 'Internal',\n emails: creatorDetails?.emails ?? [],\n title: creatorDetails?.title ?? null,\n givenName: creatorDetails?.name.givenName ?? null,\n familyName: creatorDetails?.name.familyName ?? null,\n };\n\n // grab approvers details\n const approverDetailsById = keyBy(approversDetails, 'id');\n const enrichedApprovers = assessmentDetails.approvers.flatMap((originalApprover) =>\n approverDetailsById[originalApprover.id]\n ? [\n {\n ...originalApprover,\n approver: {\n ...originalApprover.approver,\n active: approverDetailsById[originalApprover.id].active,\n userType: approverDetailsById[originalApprover.id].userType,\n emails: approverDetailsById[originalApprover.id].emails,\n title: approverDetailsById[originalApprover.id].title,\n givenName: approverDetailsById[originalApprover.id].name.givenName ?? null,\n familyName: approverDetailsById[originalApprover.id].name.familyName ?? null,\n },\n },\n ]\n : [],\n );\n\n // grab respondents details\n const respondentsDetailsById = keyBy(respondentsDetails, 'id');\n const enrichedRespondents = assessmentDetails.respondents\n .filter((r) => !r.name.includes('@')) // search only internal respondents\n .flatMap((respondent) =>\n respondentsDetailsById[respondent.id]\n ? [\n {\n ...respondent,\n active: respondentsDetailsById[respondent.id].active,\n userType: respondentsDetailsById[respondent.id].userType,\n emails: respondentsDetailsById[respondent.id].emails,\n title: respondentsDetailsById[respondent.id].title,\n givenName: respondentsDetailsById[respondent.id].name.givenName ?? null,\n familyName: respondentsDetailsById[respondent.id].name.familyName ?? null,\n },\n ]\n : [],\n );\n\n // combine everything into a single enriched assessment\n return {\n ...assessment,\n ...restAssessmentDetails,\n approvers: enrichedApprovers,\n respondents: enrichedRespondents,\n createdBy: enrichedCreatedBy,\n sections: sectionsWithEnrichedRisk,\n };\n};\n","import { OneTrustEnrichedAssessment } from '@transcend-io/privacy-types';\nimport { makeGraphQLRequest, IMPORT_ONE_TRUST_ASSESSMENT_FORMS } from '@transcend-io/sdk';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\n\nimport { ImportOnetrustAssessmentsInput } from '../../../codecs.js';\nimport { logger } from '../../../logger.js';\nimport { oneTrustAssessmentToJson } from './oneTrustAssessmentToJson.js';\n\nexport interface AssessmentForm {\n /** ID of Assessment Form */\n id: string;\n /** Title of Assessment Form */\n name: string;\n}\n\n/**\n * Write the assessment to a Transcend instance.\n *\n *\n * @param param - information about the assessment and Transcend instance to write to\n */\nexport const syncOneTrustAssessmentToTranscend = async ({\n transcend,\n assessment,\n total,\n index,\n}: {\n /** the Transcend client instance */\n transcend: GraphQLClient;\n /** the assessment to sync to Transcend */\n assessment: OneTrustEnrichedAssessment;\n /** The index of the assessment being written to the file */\n index: number;\n /** The total amount of assessments that we will write */\n total?: number;\n}): Promise<void> => {\n logger.info(\n colors.magenta(\n `Writing enriched assessment ${index + 1} ${total ? `of ${total} ` : ' '}to Transcend...`,\n ),\n );\n\n // convert the OneTrust assessment object into a json record\n const json = oneTrustAssessmentToJson({\n assessment,\n index,\n total,\n });\n\n // transform the json record into a valid input to the mutation\n const input: ImportOnetrustAssessmentsInput = {\n json,\n };\n\n try {\n await makeGraphQLRequest<{\n /** the importOneTrustAssessmentForms mutation */\n importOneTrustAssessmentForms: {\n /** Created Assessment Forms */\n assessmentForms: AssessmentForm[];\n };\n }>(transcend, IMPORT_ONE_TRUST_ASSESSMENT_FORMS, {\n variables: { input },\n logger,\n });\n } catch (error) {\n logger.error(\n colors.red(\n `Failed to sync assessment ${index + 1} ${total ? `of ${total} ` : ' '}to Transcend.\\n` +\n `\\tAssessment Title: ${assessment.name}. Template Title: ${assessment.template.name}\\n`,\n ),\n error,\n );\n }\n};\n","import {\n OneTrustAssessmentQuestion,\n OneTrustAssessmentSection,\n OneTrustEnrichedAssessment,\n OneTrustGetRiskResponse,\n OneTrustGetUserResponse,\n} from '@transcend-io/privacy-types';\nimport { mapSeries, map } from '@transcend-io/utils';\nimport colors from 'colors';\nimport type { Got } from 'got';\nimport { GraphQLClient } from 'graphql-request';\nimport { uniq } from 'lodash-es';\n\nimport { logger } from '../../../logger.js';\nimport {\n getListOfOneTrustAssessments,\n getOneTrustAssessment,\n getOneTrustRisk,\n getOneTrustUser,\n} from '../endpoints/index.js';\nimport { enrichOneTrustAssessment } from './enrichOneTrustAssessment.js';\nimport { syncOneTrustAssessmentToDisk } from './syncOneTrustAssessmentToDisk.js';\nimport { syncOneTrustAssessmentToTranscend } from './syncOneTrustAssessmentToTranscend.js';\n\nexport interface AssessmentForm {\n /** ID of Assessment Form */\n id: string;\n /** Title of Assessment Form */\n name: string;\n}\n\n/**\n * Reads all the assessments from a OneTrust instance and syncs them to Transcend or to Disk.\n *\n * @param param - the information about the assessment, its OneTrust source, and destination (disk or Transcend)\n */\nexport const syncOneTrustAssessmentsFromOneTrust = async ({\n oneTrust,\n file,\n dryRun,\n transcend,\n}: {\n /** the OneTrust client instance */\n oneTrust: Got;\n /** the Transcend client instance */\n transcend?: GraphQLClient;\n /** Whether to write to file instead of syncing to Transcend */\n dryRun: boolean;\n /** the path to the file in case dryRun is true */\n file?: string;\n}): Promise<void> => {\n // fetch the list of all assessments in the OneTrust organization\n logger.info('Getting list of all assessments from OneTrust...');\n const assessments = await getListOfOneTrustAssessments({ oneTrust });\n\n // a cache of OneTrust users so we avoid requesting already fetched users\n const oneTrustCachedUsers: Record<string, OneTrustGetUserResponse> = {};\n\n // split all assessments in batches, so we can process some of steps in parallel\n const BATCH_SIZE = 5;\n const assessmentBatches = Array.from(\n {\n length: Math.ceil(assessments.length / BATCH_SIZE),\n },\n (_, i) => assessments.slice(i * BATCH_SIZE, (i + 1) * BATCH_SIZE),\n );\n\n // process each batch and sync the batch right away so it's garbage collected and we don't run out of memory\n await mapSeries(assessmentBatches, async (assessmentBatch, batch) => {\n const batchEnrichedAssessments: OneTrustEnrichedAssessment[] = [];\n\n // fetch assessment details from OneTrust in parallel\n await map(\n assessmentBatch,\n async (assessment, index) => {\n const assessmentNumber = BATCH_SIZE * batch + index + 1;\n logger.info(\n `[assessment ${assessmentNumber} of ${assessments.length}]: fetching details...`,\n );\n const { templateName, assessmentId } = assessment;\n const assessmentDetails = await getOneTrustAssessment({\n oneTrust,\n assessmentId,\n });\n // fetch assessment's creator information\n const creatorId = assessmentDetails.createdBy.id;\n let creator = oneTrustCachedUsers[creatorId];\n if (!creator) {\n logger.info(\n `[assessment ${assessmentNumber} of ${assessments.length}]: fetching creator...`,\n );\n try {\n creator = await getOneTrustUser({\n oneTrust,\n userId: creatorId,\n });\n oneTrustCachedUsers[creatorId] = creator;\n } catch (error) {\n logger.warn(\n colors.yellow(\n `[assessment ${assessmentNumber} of ${assessments.length}]: failed to fetch form creator.` +\n `\\tcreatorId: ${creatorId}. Assessment Title: ${assessment.name}. Template Title: ${templateName}`,\n ),\n error,\n );\n }\n }\n\n // fetch assessment approvers information\n const { approvers } = assessmentDetails;\n let approversDetails: OneTrustGetUserResponse[][] = [];\n if (approvers.length > 0) {\n logger.info(\n `[assessment ${assessmentNumber} of ${assessments.length}]: fetching approvers...`,\n );\n approversDetails = await map(\n approvers.map(({ id }) => id),\n async (userId) => {\n try {\n let approver = oneTrustCachedUsers[userId];\n if (!approver) {\n approver = await getOneTrustUser({ oneTrust, userId });\n oneTrustCachedUsers[userId] = approver;\n }\n return [approver];\n } catch (error) {\n logger.warn(\n colors.yellow(\n `[assessment ${assessmentNumber} of ${assessments.length}]: failed to fetch a form approver.` +\n `\\tapproverId: ${userId}. Assessment Title: ${assessment.name}. Template Title: ${templateName}`,\n ),\n error,\n );\n return [];\n }\n },\n { concurrency: 5 },\n );\n }\n\n // fetch assessment internal respondents information\n const { respondents } = assessmentDetails;\n // if a user is an internal respondents, their 'name' field can't be an email.\n const internalRespondents = respondents.filter((r) => !r.name.includes('@'));\n let respondentsDetails: OneTrustGetUserResponse[][] = [];\n if (internalRespondents.length > 0) {\n logger.info(\n `[assessment ${assessmentNumber} of ${assessments.length}]: fetching respondents...`,\n );\n respondentsDetails = await map(\n internalRespondents.map(({ id }) => id),\n async (userId) => {\n try {\n let respondent = oneTrustCachedUsers[userId];\n if (!respondent) {\n respondent = await getOneTrustUser({ oneTrust, userId });\n oneTrustCachedUsers[userId] = respondent;\n }\n return [respondent];\n } catch (error) {\n logger.warn(\n colors.yellow(\n `[assessment ${assessmentNumber} of ${assessments.length}]: failed to fetch a respondent.` +\n `\\trespondentId: ${userId}. Assessment Title: ${assessment.name}. Template Title: ${templateName}`,\n ),\n error,\n );\n return [];\n }\n },\n { concurrency: 5 },\n );\n }\n\n // fetch assessment risk information\n let riskDetails: OneTrustGetRiskResponse[] = [];\n const riskIds = uniq(\n assessmentDetails.sections.flatMap((s: OneTrustAssessmentSection) =>\n s.questions.flatMap((q: OneTrustAssessmentQuestion) =>\n (q.risks ?? []).flatMap((r) => r.riskId),\n ),\n ),\n );\n if (riskIds.length > 0) {\n logger.info(\n `[assessment ${assessmentNumber} of ${assessments.length}]: fetching risks...`,\n );\n riskDetails = await map(\n riskIds,\n (riskId) => getOneTrustRisk({ oneTrust, riskId: riskId as string }),\n {\n concurrency: 5,\n },\n );\n }\n\n // enrich the assessments with user and risk details\n const enrichedAssessment = enrichOneTrustAssessment({\n assessment,\n assessmentDetails,\n riskDetails,\n creatorDetails: creator,\n approversDetails: approversDetails.flat(),\n respondentsDetails: respondentsDetails.flat(),\n });\n\n batchEnrichedAssessments.push(enrichedAssessment);\n },\n { concurrency: BATCH_SIZE },\n );\n\n // sync assessments in series to avoid concurrency bugs\n await mapSeries(batchEnrichedAssessments, async (enrichedAssessment, index) => {\n // the assessment's global index takes its batch into consideration\n const globalIndex = batch * BATCH_SIZE + index;\n\n if (dryRun && file) {\n // sync to file\n syncOneTrustAssessmentToDisk({\n assessment: enrichedAssessment,\n index: globalIndex,\n total: assessments.length,\n file,\n });\n } else if (transcend) {\n // sync to transcend\n await syncOneTrustAssessmentToTranscend({\n assessment: enrichedAssessment,\n transcend,\n total: assessments.length,\n index: globalIndex,\n });\n }\n });\n });\n};\n","import { createReadStream } from 'node:fs';\n\nimport { OneTrustEnrichedAssessment } from '@transcend-io/privacy-types';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport JSONStream from 'JSONStream';\n\nimport { logger } from '../../../logger.js';\nimport { syncOneTrustAssessmentToTranscend } from './syncOneTrustAssessmentToTranscend.js';\n\n/**\n * Reads assessments from a file and syncs them to Transcend.\n *\n * @param param - the information about the source file and Transcend instance to write them to.\n */\nexport const syncOneTrustAssessmentsFromFile = ({\n transcend,\n file,\n}: {\n /** the Transcend client instance */\n transcend: GraphQLClient;\n /** The name of the file from which to read the OneTrust assessments */\n file: string;\n}): Promise<void> => {\n logger.info(`Getting list of all assessments from file ${file}...`);\n\n return new Promise((resolve, reject) => {\n // Create a readable stream from the file\n const fileStream = createReadStream(file, {\n encoding: 'utf-8',\n highWaterMark: 64 * 1024, // 64KB chunks\n });\n\n // Create a JSONStream parser to parse the array of OneTrust assessments from the file\n const parser = JSONStream.parse('*'); // '*' matches each element in the root array\n\n let index = 0;\n\n // Pipe the file stream into the JSON parser\n fileStream.pipe(parser);\n\n // Handle each parsed assessment object\n parser.on('data', async (assessment) => {\n try {\n // Pause the stream while processing to avoid overwhelming memory\n parser.pause();\n\n // Decode and validate the assessment\n const parsedAssessment = decodeCodec(OneTrustEnrichedAssessment, assessment);\n\n // Sync the assessment to transcend\n await syncOneTrustAssessmentToTranscend({\n assessment: parsedAssessment,\n transcend,\n index,\n });\n\n index += 1;\n\n // Resume the stream after processing\n parser.resume();\n } catch (e) {\n // if failed to parse a line, report error and continue\n logger.error(\n colors.red(`Failed to parse the assessment ${index} from file '${file}': ${e.message}.`),\n );\n }\n });\n\n // Handle completion\n parser.on('end', () => {\n logger.info(`Finished processing ${index} assessments from file ${file}`);\n resolve();\n });\n\n // Handle stream or parsing errors\n parser.on('error', (error) => {\n logger.error(colors.red(`Error parsing file '${file}': ${error.message}`));\n reject(error);\n });\n\n fileStream.on('error', (error) => {\n logger.error(colors.red(`Error reading file '${file}': ${error.message}`));\n reject(error);\n });\n });\n};\n","import got, { Got } from 'got';\n\n/**\n * Instantiate an instance of got that is capable of making requests to OneTrust\n *\n * @param param - information about the OneTrust URL\n * @returns The instance of got that is capable of making requests to the customer ingress\n */\nexport const createOneTrustGotInstance = ({\n hostname,\n auth,\n}: {\n /** Hostname of the OneTrust API */\n hostname: string;\n /** The OAuth access token */\n auth: string;\n}): Got =>\n got.extend({\n prefixUrl: `https://${hostname}`,\n headers: {\n accept: 'application/json',\n 'content-type': 'application/json',\n authorization: `Bearer ${auth}`,\n },\n });\n","import { buildTranscendGraphQLClient } from '@transcend-io/sdk';\nimport colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { OneTrustFileFormat, OneTrustPullResource, OneTrustPullSource } from '../../../enums.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport {\n syncOneTrustAssessmentsFromFile,\n syncOneTrustAssessmentsFromOneTrust,\n} from '../../../lib/oneTrust/helpers/index.js';\nimport { createOneTrustGotInstance } from '../../../lib/oneTrust/index.js';\n\n// Command flag interface\nexport interface SyncOtCommandFlags {\n hostname?: string;\n oneTrustAuth?: string;\n source: OneTrustPullSource;\n transcendAuth?: string;\n transcendUrl: string;\n file?: string;\n resource: OneTrustPullResource;\n dryRun: boolean;\n debug: boolean;\n}\n\n// Command implementation\nexport async function syncOt(\n this: LocalContext,\n {\n hostname,\n oneTrustAuth,\n source,\n transcendAuth,\n transcendUrl,\n resource,\n file,\n dryRun,\n debug,\n }: SyncOtCommandFlags,\n): Promise<void> {\n // Must be able to authenticate to transcend to sync resources to it\n if (!dryRun && !transcendAuth) {\n throw new Error(\n // eslint-disable-next-line no-template-curly-in-string\n 'Must specify a \"transcendAuth\" parameter to sync resources to Transcend. e.g. --transcendAuth=${TRANSCEND_API_KEY}',\n );\n }\n\n // If trying to sync to disk, must specify a file path\n if (dryRun && !file) {\n throw new Error(\n 'Must set a \"file\" parameter when \"dryRun\" is \"true\". e.g. --file=./oneTrustAssessments.json',\n );\n }\n\n if (file) {\n const splitFile = file.split('.');\n if (splitFile.length < 2) {\n throw new Error(\n 'The \"file\" parameter has an invalid format. Expected a path with extensions. e.g. --file=./pathToFile.json.',\n );\n }\n if (splitFile.at(-1) !== OneTrustFileFormat.Json) {\n throw new Error(\n `Expected the format of the \"file\" parameters '${file}' to be '${\n OneTrustFileFormat.Json\n }', but got '${splitFile.at(-1)}'.`,\n );\n }\n }\n\n // if reading assessments from a OneTrust\n if (source === OneTrustPullSource.OneTrust) {\n // must specify the OneTrust hostname\n if (!hostname) {\n throw new Error(\n 'Missing required parameter \"hostname\". e.g. --hostname=customer.my.onetrust.com',\n );\n }\n // must specify the OneTrust auth\n if (!oneTrustAuth) {\n throw new Error(\n 'Missing required parameter \"oneTrustAuth\". e.g. --oneTrustAuth=$ONE_TRUST_AUTH_TOKEN',\n );\n }\n } else {\n // if reading the assessments from a file, must specify a file to read from\n if (!file) {\n throw new Error(\n 'Must specify a \"file\" parameter to read the OneTrust assessments from. e.g. --source=./oneTrustAssessments.json',\n );\n }\n\n // Cannot try reading from file and save assessments to a file simultaneously\n if (dryRun) {\n throw new Error(\n 'Cannot read and write to a file simultaneously.' +\n ` Emit the \"source\" parameter or set it to ${OneTrustPullSource.OneTrust} if \"dryRun\" is enabled.`,\n );\n }\n }\n\n doneInputValidation(this.process);\n\n // instantiate a client to talk to OneTrust\n const oneTrust =\n hostname && oneTrustAuth\n ? createOneTrustGotInstance({\n hostname,\n auth: oneTrustAuth,\n })\n : undefined;\n\n // instantiate a client to talk to Transcend\n const transcend =\n transcendUrl && transcendAuth\n ? buildTranscendGraphQLClient(transcendUrl, transcendAuth)\n : undefined;\n\n try {\n if (resource === OneTrustPullResource.Assessments) {\n if (source === OneTrustPullSource.OneTrust && oneTrust) {\n await syncOneTrustAssessmentsFromOneTrust({\n oneTrust,\n file,\n dryRun,\n ...(transcend && { transcend }),\n });\n } else if (source === OneTrustPullSource.File && file && transcend) {\n await syncOneTrustAssessmentsFromFile({ file, transcend });\n }\n }\n } catch (err) {\n throw new Error(\n `An error occurred syncing the resource ${resource} from OneTrust: ${\n debug ? err.stack : err.message\n }`,\n );\n }\n\n // Indicate success\n this.logger.info(\n colors.green(\n `Successfully synced OneTrust ${resource} to ${dryRun ? `disk at \"${file}\"` : 'Transcend'}!`,\n ),\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"impl-m3QXCc6n.mjs","names":[],"sources":["../src/lib/custom-functions/local-simulator.ts","../src/commands/custom-functions/run/impl.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { stripVTControlCharacters } from 'node:util';\n\nimport {\n DSR_DATAPOINT_CUSTOM_FUNCTION_PAYLOAD_SCHEMA,\n DSR_REQUEST_ENRICHER_CUSTOM_FUNCTION_PAYLOAD_SCHEMA,\n GENERAL_CUSTOM_FUNCTION_PAYLOAD_SCHEMA,\n} from '@transcend-io/custom-function-types';\nimport { CustomFunctionPayloadType, CustomFunctionType } from '@transcend-io/privacy-types';\nimport Ajv from 'ajv';\n\nimport { validateCustomFunctionExecutionContext } from './execution-context.js';\nimport type { CustomFunctionManifestConfig } from './manifest.js';\n\n/** Value injected when production-only identifiers are absent from a fixture. */\nexport const LOCAL_SIMULATOR_IDENTIFIER = 'example-identifier';\n\n/** Synthetic data silo used by local DSR simulations. */\nexport const LOCAL_SIMULATOR_DATA_SILO_ID = '00000000-0000-4000-8000-000000000000';\n\n/** Default local execution timeout. */\nexport const LOCAL_SIMULATOR_TIMEOUT_MS = 30_000;\n\n/** Maximum stdout and stderr retained from one local invocation. */\nexport const LOCAL_SIMULATOR_MAX_OUTPUT_BYTES = 1024 * 1024;\n\n/** Isolated dependency cache readable by simulated functions. */\nexport const LOCAL_SIMULATOR_DENO_DIR = join(tmpdir(), 'transcend-custom-functions-deno');\n\nconst ajv = new Ajv({ allErrors: true, strict: false });\nconst payloadValidators = {\n general: ajv.compile(GENERAL_CUSTOM_FUNCTION_PAYLOAD_SCHEMA),\n datapoint: ajv.compile(DSR_DATAPOINT_CUSTOM_FUNCTION_PAYLOAD_SCHEMA),\n enricher: ajv.compile(DSR_REQUEST_ENRICHER_CUSTOM_FUNCTION_PAYLOAD_SCHEMA),\n};\n\n/** Complete local Deno process invocation. */\nexport interface LocalSimulatorInvocation {\n /** Deno CLI arguments. */\n args: string[];\n /** JSON sent privately over stdin. */\n input: string;\n /** Minimal environment exposed to the process. */\n env: NodeJS.ProcessEnv;\n /** Maximum execution time. */\n timeoutMs: number;\n}\n\n/**\n * Runtime wrapper modeled after Sombra's Deno executor.\n *\n * The local SDK intentionally simulates customer-ingress calls rather than\n * sending authenticated requests. Native fetch remains governed by Deno's\n * allow-net permissions.\n */\nconst LOCAL_SIMULATOR_SOURCE = `\nconst input = JSON.parse(await new Response(Deno.stdin.readable).text());\nfor (const [name, value] of Object.entries(input.functionArgs.environment)) {\n Deno.env.set(name, value);\n}\n\nclass KV {\n constructor() {\n this.store = {};\n }\n\n get(key) {\n return Promise.resolve(key in this.store ? this.store[key] : null);\n }\n\n async set(key, value) {\n if (typeof value !== 'string') throw new Error('Value must be of type string');\n if (value.length > 2048) throw new Error('Cannot store value greater than 2048 bytes');\n if (key.length > 128) throw new Error('Key length cannot be greater than 128 bytes');\n if (!(key in this.store) && Object.keys(this.store).length >= 128) {\n throw new Error('Maximum number of keys allowed is 128');\n }\n this.store[key] = value;\n }\n\n keys() {\n return Promise.resolve(Object.keys(this.store));\n }\n\n has(key) {\n return Promise.resolve(key in this.store);\n }\n\n async del(key) {\n if (!(key in this.store)) return false;\n delete this.store[key];\n return true;\n }\n}\n\nclass SDK {\n constructor(nonce) {\n this.transcendNonce = nonce;\n this.customerIngressBaseUrl = 'local://customer-ingress';\n }\n\n nonce() {\n return this.transcendNonce;\n }\n\n setCustomerIngressUrl(newUrl) {\n this.customerIngressBaseUrl = newUrl;\n }\n\n async ping() {\n await this.fetch('/test', { method: 'GET' });\n }\n\n fetch(path, options = {}) {\n const method = options.method ?? 'GET';\n console.warn(\n \\`[simulator] sdk.fetch \\${method} \\${path} -> HTTP 200 (request not sent)\\`,\n );\n return Promise.resolve(new Response(null, { status: 200, statusText: 'OK' }));\n }\n}\n\ntry {\n const custom = await import(\n \\`data:application/typescript;base64,\\${input.base64Function}\\`\n );\n if (!(input.functionToRun in custom)) {\n throw new Error(\n \\`Custom Function does not export \"\\${input.functionToRun}\" for this payload\\`,\n );\n }\n await custom[input.functionToRun]({\n ...input.functionArgs,\n sdk: new SDK(input.functionArgs.nonce),\n kv: new KV(),\n });\n Deno.exit(0);\n} catch (error) {\n console.error(error);\n Deno.exit(1);\n}\n`;\n\n/**\n * Add values that Sombra normally injects into test payloads.\n *\n * @param config - Selected manifest function\n * @param payload - Parsed test fixture\n * @param payloadType - Optional DSR export selector\n * @returns Prepared local handler payload\n */\nexport function prepareLocalSimulatorPayload(\n config: Pick<CustomFunctionManifestConfig, 'dataSiloId' | 'name' | 'type'>,\n payload: object,\n payloadType?: CustomFunctionPayloadType,\n): object {\n const record = payload as {\n /** Runtime core identifier. */\n coreIdentifier?: { value: string };\n /** DSR metadata. */\n extras?: Record<string, unknown>;\n };\n const extras = record.extras ?? {};\n const profile = extras['profile'] as { identifier?: unknown } | undefined;\n const type = config.type ?? CustomFunctionType.General;\n const isDataPoint =\n type === CustomFunctionType.Dsr && payloadType !== CustomFunctionPayloadType.RequestEnricher;\n const preparedProfile =\n isDataPoint && profile?.identifier === '{{identifier}}'\n ? { ...profile, identifier: LOCAL_SIMULATOR_IDENTIFIER }\n : profile;\n const dataSilo = extras['dataSilo'] as Record<string, unknown> | undefined;\n const preparedExtras =\n type === CustomFunctionType.Dsr\n ? {\n ...extras,\n ...(preparedProfile ? { profile: preparedProfile } : {}),\n dataSilo: {\n title: config.name,\n description: '',\n link: '',\n ...dataSilo,\n id: config.dataSiloId ?? dataSilo?.['id'] ?? LOCAL_SIMULATOR_DATA_SILO_ID,\n },\n }\n : preparedProfile\n ? { ...extras, profile: preparedProfile }\n : extras;\n return {\n ...record,\n ...(Object.keys(preparedExtras).length > 0 ? { extras: preparedExtras } : {}),\n coreIdentifier: record.coreIdentifier ?? { value: LOCAL_SIMULATOR_IDENTIFIER },\n };\n}\n\n/**\n * Validate a fixture with the same published schemas used by `check`.\n *\n * @param config - Selected manifest function\n * @param testPayload - Fixture and optional DSR export selector\n */\nfunction validateLocalSimulatorPayload(\n config: CustomFunctionManifestConfig,\n testPayload: NonNullable<CustomFunctionManifestConfig['testPayloads']>[number],\n): void {\n const type = config.type ?? CustomFunctionType.General;\n const validate =\n type !== CustomFunctionType.Dsr\n ? payloadValidators.general\n : testPayload.payloadType === CustomFunctionPayloadType.RequestEnricher\n ? payloadValidators.enricher\n : payloadValidators.datapoint;\n if (!validate(testPayload.payload)) {\n const details = (validate.errors ?? [])\n .map((error) => `${error.instancePath || '/'} ${error.message ?? 'is invalid'}`)\n .join('; ');\n throw new Error(`Invalid test payload for \"${config.name}\": ${details}`);\n }\n}\n\n/**\n * Build one constrained Deno invocation for a test fixture.\n *\n * @param config - Selected manifest function\n * @param testPayload - Fixture and optional DSR export selector\n * @param options - Discovered Deno configuration\n * @returns Process arguments, input, environment, and timeout\n */\nexport function buildLocalSimulatorInvocation(\n config: CustomFunctionManifestConfig,\n testPayload: NonNullable<CustomFunctionManifestConfig['testPayloads']>[number],\n options: {\n /** Existing deno.json or deno.jsonc path. */\n denoConfigPath?: string;\n /** Permit real native fetch calls to manifest allowed hosts. */\n allowNetwork?: boolean;\n } = {},\n): LocalSimulatorInvocation {\n validateLocalSimulatorPayload(config, testPayload);\n const environment = config.env ?? {};\n const envNames = Object.keys(environment);\n const allowedHosts = config.allowedHosts ?? [];\n validateCustomFunctionExecutionContext(config);\n const allowNetArgs = options.allowNetwork\n ? allowedHosts.length === 0\n ? ['--allow-net=localhost']\n : allowedHosts[0] === '*'\n ? ['--allow-net']\n : [`--allow-net=${allowedHosts.join(',')}`]\n : [];\n const type = config.type ?? CustomFunctionType.General;\n const functionToRun =\n type === CustomFunctionType.Dsr &&\n testPayload.payloadType === CustomFunctionPayloadType.RequestEnricher\n ? 'enricher'\n : 'default';\n const runnerUrl = `data:application/javascript;base64,${Buffer.from(\n LOCAL_SIMULATOR_SOURCE,\n ).toString('base64')}`;\n const args = [\n 'run',\n '--no-check',\n '--no-prompt',\n '--no-lock',\n '--node-modules-dir=none',\n '--vendor=false',\n `--allow-read=${LOCAL_SIMULATOR_DENO_DIR}`,\n ...(envNames.length > 0 ? [`--allow-env=${envNames.join(',')}`] : []),\n ...(options.denoConfigPath ? [`--config=${options.denoConfigPath}`] : ['--no-config']),\n ...allowNetArgs,\n ...(config.allowThirdPartyImports ? [] : ['--no-remote', '--no-npm']),\n runnerUrl,\n ];\n return {\n args,\n input: JSON.stringify({\n base64Function: Buffer.from(config.code).toString('base64'),\n functionToRun,\n functionArgs: {\n environment,\n payload: prepareLocalSimulatorPayload(config, testPayload.payload, testPayload.payloadType),\n nonce: 'local-simulator',\n },\n }),\n env: {\n ...environment,\n DENO_DIR: LOCAL_SIMULATOR_DENO_DIR,\n DENO_TLS_CA_STORE: 'system',\n NO_COLOR: '1',\n },\n timeoutMs:\n config.timeoutMs && config.timeoutMs > 0\n ? Math.min(config.timeoutMs, LOCAL_SIMULATOR_TIMEOUT_MS)\n : LOCAL_SIMULATOR_TIMEOUT_MS,\n };\n}\n\n/**\n * Remove terminal controls and opaque data URLs from local output.\n *\n * @param value - Untrusted terminal text\n * @returns Plain printable text\n */\nfunction sanitizeLocalSimulatorText(value: string): string {\n return Array.from(\n stripVTControlCharacters(\n value.replace(\n /data:application\\/(?:javascript|typescript);base64,[A-Za-z\\d+/=]+/gu,\n '<local-module>',\n ),\n ).replace(/\\r(?!\\n)/gu, '\\n'),\n )\n .filter((character) => {\n const code = character.codePointAt(0)!;\n return (\n code === 9 || code === 10 || (code >= 32 && code !== 127 && !(code >= 128 && code <= 159))\n );\n })\n .join('');\n}\n\n/**\n * Redact configured environment values from captured local output.\n *\n * @param output - Captured stdout or stderr\n * @param environment - User-defined environment\n * @param sourceTruncated - Whether capture stopped before the stream ended\n * @returns Redacted output\n */\nexport function redactLocalSimulatorOutput(\n output: string,\n environment: Record<string, string> = {},\n sourceTruncated = false,\n): string {\n const values = Object.values(environment)\n .filter((value) => value.length > 0)\n .map(sanitizeLocalSimulatorText)\n .sort((left, right) => right.length - left.length);\n let sanitized = sanitizeLocalSimulatorText(output);\n if (sourceTruncated) {\n const trailingFragmentLength = Math.max(\n 0,\n ...values.map((value) => {\n const maximum = Math.min(value.length - 1, sanitized.length);\n for (let length = maximum; length > 0; length -= 1) {\n if (sanitized.endsWith(value.slice(0, length))) {\n return length;\n }\n }\n return 0;\n }),\n );\n if (trailingFragmentLength > 0) {\n sanitized = sanitized.slice(0, -trailingFragmentLength);\n }\n }\n return values.reduce((redacted, value) => redacted.split(value).join('[REDACTED]'), sanitized);\n}\n\n/**\n * Truncate sanitized output without splitting a UTF-8 code point.\n *\n * @param output - Sanitized and redacted output\n * @param maxBytes - Maximum output size\n * @returns Bounded output and whether truncation occurred\n */\nexport function truncateLocalSimulatorOutput(\n output: string,\n maxBytes = LOCAL_SIMULATOR_MAX_OUTPUT_BYTES,\n): { output: string; truncated: boolean } {\n const bytes = Buffer.from(output);\n if (bytes.length <= maxBytes) {\n return { output, truncated: false };\n }\n let end = maxBytes;\n while (end > 0 && (bytes[end]! & 0b1100_0000) === 0b1000_0000) {\n end -= 1;\n }\n return {\n output: bytes.subarray(0, end).toString('utf8'),\n truncated: true,\n };\n}\n","import { Buffer } from 'node:buffer';\n\nimport colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport { doneInputValidation } from '../../../lib/cli/done-input-validation.js';\nimport { runCapturedProcess } from '../../../lib/cli/run-captured-process.js';\nimport {\n DENO_INSTALL_URL,\n getDenoRuntimeCompatibility,\n SUPPORTED_DENO_MAJOR_VERSION,\n} from '../../../lib/custom-functions/deno-runtime.js';\nimport {\n buildLocalSimulatorInvocation,\n LOCAL_SIMULATOR_MAX_OUTPUT_BYTES,\n redactLocalSimulatorOutput,\n truncateLocalSimulatorOutput,\n} from '../../../lib/custom-functions/local-simulator.js';\nimport {\n parseCustomFunctionsManifest,\n readCustomFunctionManifestEntry,\n type CustomFunctionManifestEntry,\n} from '../../../lib/custom-functions/manifest.js';\nimport { formatMissingManifestMessage } from '../../../lib/custom-functions/missing-manifest.js';\nimport { findNonSelfContainedRuntimeImports } from '../../../lib/custom-functions/module-graph.js';\nimport {\n discoverCustomFunctionManifests,\n discoverCustomFunctionProject,\n} from '../../../lib/custom-functions/project-discovery.js';\nimport {\n CustomFunctionPrompts,\n PromptCancelledError,\n} from '../../../lib/custom-functions/prompts.js';\nimport { parseVariablesFromString } from '../../../lib/helpers/parseVariablesFromString.js';\nimport { assertPathPhysicallyContained } from '../../../lib/scaffolding/path-safety.js';\n\n/** Flags accepted by `transcend custom-functions run`. */\nexport interface CustomFunctionRunFlags {\n /** Explicit manifest path. */\n manifest?: string;\n /** Exact function name. */\n function?: string;\n /** Manifest variable substitutions. */\n variables: string;\n /** Disable prompts. */\n noInteractive: boolean;\n /** Permit native network calls to allowed hosts. */\n allowNetwork: boolean;\n}\n\n/**\n * Select one function by exact name or ID.\n *\n * @param configs - Hydrated manifest functions\n * @param selector - Optional exact name or ID\n * @returns Selected function, or undefined when interactive selection is needed\n */\nfunction selectNamedFunction<Config extends Pick<CustomFunctionManifestEntry, 'id' | 'name'>>(\n configs: readonly Config[],\n selector: string | undefined,\n): Config | undefined {\n if (!selector) {\n return configs.length === 1 ? configs[0] : undefined;\n }\n const matches = configs.filter((config) => config.name === selector || config.id === selector);\n if (matches.length === 0) {\n throw new Error(`Custom Function \"${selector}\" was not found in the manifest.`);\n }\n if (matches.length > 1) {\n throw new Error(\n `Custom Function selector \"${selector}\" is ambiguous. Pass its manifest ID instead.`,\n );\n }\n return matches[0];\n}\n\n/**\n * Run one manifest function in the credential-free local Deno simulator.\n *\n * @param this - CLI context\n * @param flags - Selection and manifest flags\n * @param directory - Optional project directory\n */\nexport async function run(\n this: LocalContext,\n flags: CustomFunctionRunFlags,\n directory?: string,\n): Promise<void> {\n doneInputValidation(this.process);\n try {\n const state = discoverCustomFunctionProject(this, {\n ...(directory ? { directory } : {}),\n ...(flags.manifest ? { manifest: flags.manifest } : {}),\n });\n if (!this.fs.existsSync(state.manifestPath)) {\n const cwd = this.process.cwd();\n throw new Error(\n formatMissingManifestMessage({\n cwd,\n manifestPath: state.manifestPath,\n discoveredManifestPaths: discoverCustomFunctionManifests(this, cwd),\n command: 'run',\n }),\n );\n }\n\n const parsedManifest = parseCustomFunctionsManifest(\n this.fs.readFileSync(state.manifestPath, 'utf8'),\n );\n if (parsedManifest.functions.length === 0) {\n throw new Error('The Custom Function manifest does not define any functions.');\n }\n const interactive =\n !flags.noInteractive && Boolean(this.process.stdin.isTTY && this.process.stderr.isTTY);\n let selectedEntry = selectNamedFunction(parsedManifest.functions, flags.function);\n if (!selectedEntry && interactive) {\n const prompts = new CustomFunctionPrompts(this);\n const selectedIndex = await prompts.select(\n 'Custom Function to run:',\n parsedManifest.functions.map((entry, index) => ({\n name: entry.id ? `${entry.name} (${entry.id})` : entry.name,\n value: String(index),\n })),\n '0',\n );\n selectedEntry = parsedManifest.functions[Number(selectedIndex)];\n }\n if (!selectedEntry) {\n throw new Error('Select a Custom Function with --function in a non-interactive invocation.');\n }\n const { config: selected, sourcePath } = readCustomFunctionManifestEntry(\n state.manifestPath,\n selectedEntry,\n parseVariablesFromString(flags.variables),\n (path) => assertPathPhysicallyContained(this, state.manifestDirectory, path),\n );\n if (!selected.testPayloads || selected.testPayloads.length === 0) {\n throw new Error(\n `Custom Function \"${selected.name}\" has no test payloads. Add test-payload or test-payloads to its manifest entry.`,\n );\n }\n const denoConfig = this.fs.existsSync(state.denoConfigPath)\n ? { denoConfigPath: state.denoConfigPath }\n : {};\n const simulations = selected.testPayloads.map((testPayload) => ({\n testPayload,\n invocation: buildLocalSimulatorInvocation(selected, testPayload, {\n ...denoConfig,\n allowNetwork: flags.allowNetwork,\n }),\n }));\n\n const denoVersion = await runCapturedProcess(\n 'deno',\n ['--version'],\n { cwd: state.manifestDirectory },\n this,\n );\n if (denoVersion.error?.code === 'ENOENT') {\n throw new Error(\n `Deno ${SUPPORTED_DENO_MAJOR_VERSION}.x is required. Install it from ${DENO_INSTALL_URL}`,\n );\n }\n if (denoVersion.code !== 0) {\n throw new Error('Deno was found, but its version could not be determined.');\n }\n const compatibility = getDenoRuntimeCompatibility(denoVersion.stdout);\n if (compatibility.level === 'error') {\n throw new Error(compatibility.message);\n }\n if (compatibility.level === 'warning') {\n this.logger.warn(compatibility.message);\n }\n const moduleGraph = await runCapturedProcess(\n 'deno',\n [\n 'info',\n '--json',\n ...(denoConfig.denoConfigPath\n ? [`--config=${denoConfig.denoConfigPath}`]\n : ['--no-config']),\n sourcePath,\n ],\n { cwd: state.manifestDirectory },\n this,\n );\n if (moduleGraph.code !== 0) {\n throw new Error(\n `Could not inspect Custom Function runtime imports: ${\n moduleGraph.stderr.trim() || `Deno exited with code ${moduleGraph.code}`\n }`,\n );\n }\n const unsupportedImports = findNonSelfContainedRuntimeImports(moduleGraph.stdout);\n if (unsupportedImports.length > 0) {\n throw new Error(\n `Custom Functions must be self-contained. Local or import-map runtime dependencies are not deployed: ${unsupportedImports.join(\n ', ',\n )}`,\n );\n }\n\n this.logger.warn(\n colors.yellow(\n 'Local simulator: sdk.fetch calls are logged and return HTTP 200 without sending a request; KV state starts empty for each payload.',\n ),\n );\n if (flags.allowNetwork) {\n this.logger.warn(\n colors.yellow(\n `Network enabled: native fetch can make real requests to ${\n selected.allowedHosts?.length ? selected.allowedHosts.join(', ') : 'localhost'\n }.`,\n ),\n );\n } else if (selected.allowedHosts?.length) {\n this.logger.warn(\n colors.yellow(\n 'Native network access is disabled. Pass --allowNetwork to permit calls to allowed-hosts.',\n ),\n );\n }\n let failed = false;\n for (const [index, { invocation, testPayload }] of simulations.entries()) {\n const label = testPayload.payloadType ?? `payload ${index + 1}`;\n this.logger.info(`\\n${colors.bold(`Running \"${selected.name}\" (${label})`)}`);\n const result = await runCapturedProcess(\n 'deno',\n invocation.args,\n {\n cwd: state.manifestDirectory,\n input: invocation.input,\n env: invocation.env,\n timeoutMs: invocation.timeoutMs,\n maxOutputBytes:\n LOCAL_SIMULATOR_MAX_OUTPUT_BYTES +\n Math.max(\n 0,\n ...Object.values(selected.env ?? {}).map((value) => Buffer.byteLength(value)),\n ),\n },\n this,\n );\n const environment = selected.env ?? {};\n const stdout = truncateLocalSimulatorOutput(\n redactLocalSimulatorOutput(result.stdout, environment, result.stdoutTruncated),\n );\n const stderr = truncateLocalSimulatorOutput(\n redactLocalSimulatorOutput(result.stderr, environment, result.stderrTruncated),\n );\n if (stdout.output) {\n this.process.stdout.write(\n stdout.output.endsWith('\\n') ? stdout.output : `${stdout.output}\\n`,\n );\n }\n if (stderr.output) {\n this.process.stderr.write(\n stderr.output.endsWith('\\n') ? stderr.output : `${stderr.output}\\n`,\n );\n }\n if (result.outputTruncated || stdout.truncated || stderr.truncated) {\n this.logger.warn(\n colors.yellow(\n `Simulator output was truncated after ${LOCAL_SIMULATOR_MAX_OUTPUT_BYTES} bytes per stream.`,\n ),\n );\n }\n if (result.code === 0) {\n this.logger.info(colors.green(`Passed \"${selected.name}\" (${label})`));\n } else {\n failed = true;\n const detail = result.timedOut\n ? `timed out after ${invocation.timeoutMs}ms`\n : result.inputError\n ? `could not send the fixture to Deno: ${result.inputError.message}`\n : result.error\n ? `could not start Deno: ${result.error.message}`\n : result.signal\n ? `was terminated with ${result.signal}`\n : `exited with code ${result.code}`;\n this.logger.error(colors.red(`Failed \"${selected.name}\" (${label}): ${detail}`));\n }\n }\n if (failed) {\n this.process.exitCode = 1;\n }\n } catch (error) {\n if (error instanceof PromptCancelledError) {\n this.process.exit(130);\n }\n throw error;\n }\n}\n"],"mappings":"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"}
|
package/dist/logger-9T6vva73.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"logger-9T6vva73.mjs","names":[],"sources":["../src/logger.ts"],"sourcesContent":["export const logger = console;\n"],"mappings":"AAAA,MAAa,EAAS"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{a as e}from"./readTranscendYaml-STYnRzq-.mjs";import{CustomFunctionPayloadType as t,CustomFunctionType as n}from"@transcend-io/privacy-types";import{existsSync as r,readFileSync as i,writeFileSync as a}from"node:fs";import{basename as o,dirname as s,isAbsolute as c,relative as l,resolve as u,sep as d}from"node:path";import{decodeCodec as f}from"@transcend-io/type-utils";import*as p from"io-ts";import m from"js-yaml";import{isMap as h,isScalar as g,isSeq as _,parseDocument as v}from"yaml";import{Buffer as y}from"node:buffer";import{isIP as b}from"node:net";function x(e,t){let n=u(t),r=[];for(;;)try{return e.fs.lstatSync(n),u(e.fs.realpathSync(n),...r)}catch(e){let t=e.code;if(t!==`ENOENT`&&t!==`ENOTDIR`)throw e;let i=s(n);if(i===n)throw e;r.unshift(o(n)),n=i}}function S(e,t,n){let r=l(x(e,t),x(e,n));return r!==`..`&&!r.startsWith(`..${d}`)&&!c(r)}function C(e,t,n){if(!S(e,t,n))throw Error(`Refusing to access path outside project root through a symlink: ${n}`)}const w=64*1024,T=new Set([`DENO_CERT`,`DENO_DIR`,`DENO_TLS_CA_STORE`,`HTTP_PROXY`,`HTTPS_PROXY`,`NODE_EXTRA_CA_CERTS`,`NO_COLOR`,`NO_PROXY`,`NPM_CONFIG_REGISTRY`]);function E(e){let t=/^\[([^\]]+)\](?::(\d+))?$/u.exec(e);if(t){let e=t[2];return b(t[1])===6&&(!e||Number(e)>0&&Number(e)<=65535)}if(b(e)>0)return!0;let n=/^([^:]+):(\d+)$/u.exec(e),r=n?.[1]??e,i=n?.[2];return(!i||Number(i)>0&&Number(i)<=65535)&&r.length<=253&&r.split(`.`).every(e=>e.length>0&&e.length<=63&&/^[A-Za-z\d](?:[A-Za-z\d-]*[A-Za-z\d])?$/u.test(e))}function D(e){let t=e.env??{},n=Object.keys(t),r=n.filter(e=>!/^[A-Za-z][A-Za-z\d_]*$/u.test(e)),i=n.filter(e=>T.has(e)||e.startsWith(`DENO_`));if(r.length>0||i.length>0)throw Error([r.length>0?`Invalid environment variable names: ${r.join(`, `)}`:void 0,i.length>0?`Reserved environment variable names: ${i.join(`, `)}`:void 0].filter(Boolean).join(`. `));let a=n.filter(e=>y.byteLength(t[e])>w);if(a.length>0)throw Error(`Environment values exceed ${w} bytes: ${a.join(`, `)}`);let o=e.allowedHosts??[];if(o.length>1&&o.includes(`*`))throw Error(`allowed-hosts cannot combine "*" with specific hosts.`);let s=o.filter(e=>e!==`*`&&!E(e));if(s.length>0)throw Error(`Invalid allowed-hosts: ${s.join(`, `)}`)}const O=p.intersection([p.type({name:p.string,code:p.string}),p.partial({id:p.string,description:p.string,type:p.union([p.literal(n.Dsr),p.literal(n.General)]),"data-silo-id":p.string,"sombra-id":p.string,"sombra-auth-env":p.string,"allowed-hosts":p.array(p.string),"timeout-ms":p.number,"allow-third-party-imports":p.boolean,env:p.record(p.string,p.string),"test-payload":p.string,"test-payload-type":p.union([p.literal(t.DataPoint),p.literal(t.RequestEnricher)]),"test-payloads":p.array(p.intersection([p.type({payload:p.string}),p.partial({"payload-type":p.union([p.literal(t.DataPoint),p.literal(t.RequestEnricher)])})]))})]),k=p.type({functions:p.array(O)});function A(e){let t=u(`/`,`__custom-function-manifest__`),n=l(t,u(t,e));return n!==`..`&&!n.startsWith(`..${d}`)&&!c(n)}function j(e,t={}){let r=e.functions.map(({id:e})=>e).filter((e,t,n)=>e!==void 0&&n.indexOf(e)!==t);if(r.length>0)throw Error(`Duplicate custom function ids in manifest: ${[...new Set(r)].join(`, `)}`);let i=e.functions.map(({name:e})=>e),a=e.functions.filter(({id:e,name:t})=>e===void 0&&i.filter(e=>e===t).length>1).map(({name:e})=>e);if(a.length>0)throw Error(`Duplicate custom function names in manifest without ids: ${[...new Set(a)].join(`, `)}. Add an \`id\` field to each duplicated entry to disambiguate.`);e.functions.forEach(e=>{if(e[`test-payload`]!==void 0&&e[`test-payloads`]!==void 0)throw Error(`Custom function "${e.name}" sets both test-payload and test-payloads — use test-payloads alone to define multiple payloads.`);if(e[`test-payload-type`]!==void 0&&e[`test-payload`]===void 0)throw Error(`Custom function "${e.name}" sets test-payload-type without test-payload — set payload-type per item in test-payloads instead.`);if(e.type!==n.Dsr&&(e[`test-payload-type`]!==void 0||e[`test-payloads`]?.some(({"payload-type":e})=>e!==void 0)))throw Error(`Custom function "${e.name}" sets a DSR payload type but is not type DSR.`);[{field:`code`,path:e.code},...e[`test-payload`]===void 0?[]:[{field:`test-payload`,path:e[`test-payload`]}],...(e[`test-payloads`]??[]).map(({payload:e},t)=>({field:`test-payloads[${t}].payload`,path:e}))].forEach(({field:n,path:r})=>{if(!t.skipPathValidation&&!A(r))throw Error(`Custom function "${e.name}" has a ${n} path outside the manifest directory: ${r}`)})})}function M(e,t={}){let n=f(k,m.load(e));return j(n,t),n}function N(e,t,n){let a=s(u(e)),o=(e,t)=>{let o=u(a,t);if(n?.(o),!r(o))throw Error(`Test payload file for custom function "${e}" does not exist: ${o}`);let s=i(o,`utf-8`);try{return JSON.parse(s)}catch(t){throw Error(`Test payload file for custom function "${e}" is not valid JSON (${o}): ${t.message}`)}};return t.functions.map(e=>{D({...e.env?{env:e.env}:{},...e[`allowed-hosts`]?{allowedHosts:e[`allowed-hosts`]}:{}});let t=u(a,e.code);if(n?.(t),!r(t))throw Error(`Code file for custom function "${e.name}" does not exist: ${t}`);let s;return e[`test-payload`]===void 0?e[`test-payloads`]!==void 0&&e[`test-payloads`].length>0&&(s=e[`test-payloads`].map(t=>({payload:o(e.name,t.payload),...t[`payload-type`]===void 0?{}:{payloadType:t[`payload-type`]}}))):s=[{payload:o(e.name,e[`test-payload`]),...e[`test-payload-type`]===void 0?{}:{payloadType:e[`test-payload-type`]}}],{name:e.name,code:i(t,`utf-8`),...e.id===void 0?{}:{id:e.id},...e.description===void 0?{}:{description:e.description},...e.type===void 0?{}:{type:e.type},...e[`data-silo-id`]===void 0?{}:{dataSiloId:e[`data-silo-id`]},...e[`sombra-id`]===void 0?{}:{sombraId:e[`sombra-id`]},...e[`sombra-auth-env`]===void 0?{}:{sombraAuthEnv:e[`sombra-auth-env`]},...e[`allowed-hosts`]===void 0?{}:{allowedHosts:e[`allowed-hosts`]},...e[`timeout-ms`]===void 0?{}:{timeoutMs:e[`timeout-ms`]},...e[`allow-third-party-imports`]===void 0?{}:{allowThirdPartyImports:e[`allow-third-party-imports`]},...e.env===void 0?{}:{env:e.env},...s===void 0?{}:{testPayloads:s}}})}function P(t,n={},r){return N(t,M(e(i(t,`utf-8`),n,`Also check that there are no extra variables defined in your manifest: ${t}`)),r)}function F(t,n,r={},i){let a=M(e(m.dump({functions:[n]}),r,`Also check that there are no extra variables defined for "${n.name}" in: ${t}`)),o=a.functions[0],c=u(s(u(t)),o.code);return{config:N(t,a,i)[0],sourcePath:c}}function I(e,t){let n=v(e);if(n.errors.length>0)throw n.errors[0];let r=n.get(`functions`);if(!_(r))throw Error("Expected a `functions` list in custom function manifest");r.items.length===0&&(r.flow=!1),r.add(n.createNode(t));let i=n.toString();return M(i),i}function L(e,t){let n=v(i(e,`utf-8`)),r=n.get(`functions`);if(!_(r))throw Error(`Expected a \`functions\` list in manifest: ${e}`);let o=0;return r.items.forEach((e,r)=>{let i=t[r];if(!i||!h(e))return;let a=!1;if(i.id&&!e.has(`id`)&&(e.items.unshift(n.createPair(`id`,i.id)),a=!0),i.dataSiloId&&!e.has(`data-silo-id`)){let t=e.items.findIndex(e=>g(e.key)&&e.key.value===`id`);e.items.splice(t+1,0,n.createPair(`data-silo-id`,i.dataSiloId)),a=!0}a&&(o+=1)}),o>0&&a(e,n.toString()),o}export{P as a,C as c,F as i,S as l,A as n,L as o,M as r,D as s,I as t};
|
|
2
|
-
//# sourceMappingURL=manifest-DVuW_vvO.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-DVuW_vvO.mjs","names":[],"sources":["../src/lib/scaffolding/path-safety.ts","../src/lib/custom-functions/execution-context.ts","../src/lib/custom-functions/manifest.ts"],"sourcesContent":["import { basename, dirname, isAbsolute, relative, resolve, sep } from 'node:path';\n\nimport type { LocalContext } from '../../context.js';\n\n/**\n * Resolve a path through its nearest existing ancestor.\n *\n * This retains nonexistent suffixes while resolving every existing symlink in\n * the prefix, which is required before safely creating a new file.\n *\n * @param context - CLI context\n * @param path - Absolute or relative path\n * @returns Physical absolute path\n */\nfunction resolvePhysicalPath(context: LocalContext, path: string): string {\n let current = resolve(path);\n const suffix: string[] = [];\n while (true) {\n try {\n context.fs.lstatSync(current);\n return resolve(context.fs.realpathSync(current), ...suffix);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== 'ENOENT' && code !== 'ENOTDIR') {\n throw error;\n }\n const parent = dirname(current);\n if (parent === current) {\n throw error;\n }\n suffix.unshift(basename(current));\n current = parent;\n }\n }\n}\n\n/**\n * Determine whether a path physically remains under an approved root.\n *\n * @param context - CLI context\n * @param root - Approved directory\n * @param path - Candidate destination or referenced file\n * @returns Whether existing symlinks keep the path under the root\n */\nexport function isPathPhysicallyContained(\n context: LocalContext,\n root: string,\n path: string,\n): boolean {\n const physicalRoot = resolvePhysicalPath(context, root);\n const physicalPath = resolvePhysicalPath(context, path);\n const child = relative(physicalRoot, physicalPath);\n return child !== '..' && !child.startsWith(`..${sep}`) && !isAbsolute(child);\n}\n\n/**\n * Reject a path whose existing symlink ancestors escape an approved root.\n *\n * @param context - CLI context\n * @param root - Approved directory\n * @param path - Candidate path\n */\nexport function assertPathPhysicallyContained(\n context: LocalContext,\n root: string,\n path: string,\n): void {\n if (!isPathPhysicallyContained(context, root, path)) {\n throw new Error(`Refusing to access path outside project root through a symlink: ${path}`);\n }\n}\n","import { Buffer } from 'node:buffer';\nimport { isIP } from 'node:net';\n\n/** Maximum environment value size supported by the local process. */\nexport const CUSTOM_FUNCTION_MAX_ENV_VALUE_BYTES = 64 * 1024;\n\nconst RESERVED_ENV_NAMES = new Set([\n 'DENO_CERT',\n 'DENO_DIR',\n 'DENO_TLS_CA_STORE',\n 'HTTP_PROXY',\n 'HTTPS_PROXY',\n 'NODE_EXTRA_CA_CERTS',\n 'NO_COLOR',\n 'NO_PROXY',\n 'NPM_CONFIG_REGISTRY',\n]);\n\n/**\n * Check whether a value matches Sombra's hostname/IP contract.\n *\n * @param host - Manifest allowed-host value\n * @returns Whether the host is valid\n */\nfunction isValidAllowedHost(host: string): boolean {\n const bracketedIp = /^\\[([^\\]]+)\\](?::(\\d+))?$/u.exec(host);\n if (bracketedIp) {\n const port = bracketedIp[2];\n return isIP(bracketedIp[1]!) === 6 && (!port || (Number(port) > 0 && Number(port) <= 65_535));\n }\n if (isIP(host) > 0) {\n return true;\n }\n const hostWithPort = /^([^:]+):(\\d+)$/u.exec(host);\n const hostname = hostWithPort?.[1] ?? host;\n const port = hostWithPort?.[2];\n return (\n (!port || (Number(port) > 0 && Number(port) <= 65_535)) &&\n hostname.length <= 253 &&\n hostname\n .split('.')\n .every(\n (label) =>\n label.length > 0 &&\n label.length <= 63 &&\n /^[A-Za-z\\d](?:[A-Za-z\\d-]*[A-Za-z\\d])?$/u.test(label),\n )\n );\n}\n\n/**\n * Validate execution settings shared by check, push, and local simulation.\n *\n * @param config - Environment and network settings\n */\nexport function validateCustomFunctionExecutionContext(config: {\n /** User-defined environment variables. */\n env?: Record<string, string>;\n /** Native network destinations. */\n allowedHosts?: string[];\n}): void {\n const environment = config.env ?? {};\n const envNames = Object.keys(environment);\n const malformedEnvNames = envNames.filter((name) => !/^[A-Za-z][A-Za-z\\d_]*$/u.test(name));\n const reservedEnvNames = envNames.filter(\n (name) => RESERVED_ENV_NAMES.has(name) || name.startsWith('DENO_'),\n );\n if (malformedEnvNames.length > 0 || reservedEnvNames.length > 0) {\n throw new Error(\n [\n malformedEnvNames.length > 0\n ? `Invalid environment variable names: ${malformedEnvNames.join(', ')}`\n : undefined,\n reservedEnvNames.length > 0\n ? `Reserved environment variable names: ${reservedEnvNames.join(', ')}`\n : undefined,\n ]\n .filter(Boolean)\n .join('. '),\n );\n }\n const oversizedEnvNames = envNames.filter(\n (name) => Buffer.byteLength(environment[name]!) > CUSTOM_FUNCTION_MAX_ENV_VALUE_BYTES,\n );\n if (oversizedEnvNames.length > 0) {\n throw new Error(\n `Environment values exceed ${CUSTOM_FUNCTION_MAX_ENV_VALUE_BYTES} bytes: ${oversizedEnvNames.join(', ')}`,\n );\n }\n const allowedHosts = config.allowedHosts ?? [];\n if (allowedHosts.length > 1 && allowedHosts.includes('*')) {\n throw new Error('allowed-hosts cannot combine \"*\" with specific hosts.');\n }\n const invalidHosts = allowedHosts.filter((host) => host !== '*' && !isValidAllowedHost(host));\n if (invalidHosts.length > 0) {\n throw new Error(`Invalid allowed-hosts: ${invalidHosts.join(', ')}`);\n }\n}\n","import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve, sep } from 'node:path';\n\nimport { CustomFunctionPayloadType, CustomFunctionType } from '@transcend-io/privacy-types';\nimport type { CustomFunctionConfigInput } from '@transcend-io/sdk';\nimport { decodeCodec, type ObjByString } from '@transcend-io/type-utils';\nimport * as t from 'io-ts';\nimport yaml from 'js-yaml';\nimport { isMap, isScalar, isSeq, parseDocument } from 'yaml';\n\nimport { replaceVariablesInYaml } from '../readTranscendYaml.js';\nimport { validateCustomFunctionExecutionContext } from './execution-context.js';\n\nexport const CustomFunctionManifestEntry = t.intersection([\n t.type({\n /** Display name of the custom function — used as the sync key when no `id` is set */\n name: t.string,\n /** Path to the TypeScript source file, relative to the manifest */\n code: t.string,\n }),\n t.partial({\n /**\n * Custom function ID. Optional, but required to disambiguate when multiple\n * custom functions share a name. Written back by `push --updateManifest`.\n */\n id: t.string,\n /** Description shown in the Transcend dashboard */\n description: t.string,\n /** Custom function type. Defaults to GENERAL */\n type: t.union([t.literal(CustomFunctionType.Dsr), t.literal(CustomFunctionType.General)]),\n /** Data silo ID to attach to (required for DSR functions) */\n 'data-silo-id': t.string,\n /** The Sombra gateway the function belongs to */\n 'sombra-id': t.string,\n /**\n * Name of the environment variable holding the internal key of the\n * function's Sombra gateway. The key itself never lives in the manifest —\n * it is read from the CLI's process environment at push time. Overrides\n * `--sombraAuth` for this entry.\n */\n 'sombra-auth-env': t.string,\n /** Hosts the function may make network requests to */\n 'allowed-hosts': t.array(t.string),\n /** Execution timeout in milliseconds */\n 'timeout-ms': t.number,\n /** Whether the function may import third party modules */\n 'allow-third-party-imports': t.boolean,\n /** Environment variables to expose to the function */\n env: t.record(t.string, t.string),\n /**\n * Path to a JSON file (relative to the manifest) holding the test payload\n * to run the function with before pushing. When set, the function is\n * test-run after signing and only pushed/promoted when the test passes.\n * Shorthand for a single-item `test-payloads` list.\n */\n 'test-payload': t.string,\n /**\n * Which export to invoke when test-running a DSR function:\n * `DATA_POINT` invokes the default export, `REQUEST_ENRICHER` invokes the\n * `enricher` export. Defaults to DATA_POINT. Ignored for GENERAL functions.\n * Only valid alongside `test-payload`.\n */\n 'test-payload-type': t.union([\n t.literal(CustomFunctionPayloadType.DataPoint),\n t.literal(CustomFunctionPayloadType.RequestEnricher),\n ]),\n /**\n * Test payloads to run the function with before pushing. Every payload\n * runs and all must pass for the function to be pushed/promoted. DSR\n * functions should list one payload per export they implement\n * (`DATA_POINT` for the default export, `REQUEST_ENRICHER` for the\n * `enricher` export). Mutually exclusive with `test-payload`.\n */\n 'test-payloads': t.array(\n t.intersection([\n t.type({\n /** Path to the JSON payload file, relative to the manifest */\n payload: t.string,\n }),\n t.partial({\n /** Which export the payload invokes (DSR only). Defaults to DATA_POINT */\n 'payload-type': t.union([\n t.literal(CustomFunctionPayloadType.DataPoint),\n t.literal(CustomFunctionPayloadType.RequestEnricher),\n ]),\n }),\n ]),\n ),\n }),\n]);\n\n/** Override type */\nexport type CustomFunctionManifestEntry = t.TypeOf<typeof CustomFunctionManifestEntry>;\n\nexport const CustomFunctionsManifest = t.type({\n /** The custom functions to sync */\n functions: t.array(CustomFunctionManifestEntry),\n});\n\n/** Override type */\nexport type CustomFunctionsManifest = t.TypeOf<typeof CustomFunctionsManifest>;\n\n/**\n * Determine whether a relative manifest path stays inside the manifest's\n * directory after normalization.\n *\n * @param filePath - Path from a custom function manifest entry\n * @returns Whether the normalized path is contained by the manifest directory\n */\nexport function isCustomFunctionManifestPathContained(filePath: string): boolean {\n const manifestDirectory = resolve('/', '__custom-function-manifest__');\n const relativePath = relative(manifestDirectory, resolve(manifestDirectory, filePath));\n return relativePath !== '..' && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath);\n}\n\n/**\n * Validate semantic constraints shared by manifest readers and editors.\n *\n * @param manifest - Shape-validated custom function manifest\n * @param options - Validation behavior\n */\nexport function validateCustomFunctionsManifest(\n manifest: CustomFunctionsManifest,\n options: {\n /** Skip path validation while identifying candidate manifest files. */\n skipPathValidation?: boolean;\n } = {},\n): void {\n // IDs must be unique — two entries cannot target the same function\n const duplicateIds = manifest.functions\n .map(({ id }) => id)\n .filter((id, index, ids) => id !== undefined && ids.indexOf(id) !== index);\n if (duplicateIds.length > 0) {\n throw new Error(\n `Duplicate custom function ids in manifest: ${[...new Set(duplicateIds)].join(', ')}`,\n );\n }\n\n // Names may repeat only when every entry sharing the name carries an `id`\n // to disambiguate; an id-less entry syncs by name and would be ambiguous.\n const allNames = manifest.functions.map(({ name }) => name);\n const ambiguousNames = manifest.functions\n .filter(({ id, name }) => id === undefined && allNames.filter((n) => n === name).length > 1)\n .map(({ name }) => name);\n if (ambiguousNames.length > 0) {\n throw new Error(\n `Duplicate custom function names in manifest without ids: ${[...new Set(ambiguousNames)].join(', ')}. ` +\n 'Add an `id` field to each duplicated entry to disambiguate.',\n );\n }\n\n manifest.functions.forEach((entry) => {\n if (entry['test-payload'] !== undefined && entry['test-payloads'] !== undefined) {\n throw new Error(\n `Custom function \"${entry.name}\" sets both test-payload and test-payloads — ` +\n 'use test-payloads alone to define multiple payloads.',\n );\n }\n if (entry['test-payload-type'] !== undefined && entry['test-payload'] === undefined) {\n throw new Error(\n `Custom function \"${entry.name}\" sets test-payload-type without test-payload — ` +\n 'set payload-type per item in test-payloads instead.',\n );\n }\n if (\n entry.type !== CustomFunctionType.Dsr &&\n (entry['test-payload-type'] !== undefined ||\n entry['test-payloads']?.some(\n ({ ['payload-type']: payloadType }) => payloadType !== undefined,\n ))\n ) {\n throw new Error(\n `Custom function \"${entry.name}\" sets a DSR payload type but is not type DSR.`,\n );\n }\n\n const referencedPaths = [\n { field: 'code', path: entry.code },\n ...(entry['test-payload'] === undefined\n ? []\n : [{ field: 'test-payload', path: entry['test-payload'] }]),\n ...(entry['test-payloads'] ?? []).map(({ payload }, index) => ({\n field: `test-payloads[${index}].payload`,\n path: payload,\n })),\n ];\n referencedPaths.forEach(({ field, path }) => {\n if (!options.skipPathValidation && !isCustomFunctionManifestPathContained(path)) {\n throw new Error(\n `Custom function \"${entry.name}\" has a ${field} path outside the manifest directory: ${path}`,\n );\n }\n });\n });\n}\n\n/**\n * Parse and validate a custom functions manifest without resolving variables\n * or reading any referenced files.\n *\n * Unresolved `<<parameters.*>>` values remain plain strings in the returned\n * manifest.\n *\n * @param contents - Custom function manifest YAML\n * @param options - Validation behavior\n * @returns The parsed, shape-validated, and semantically valid manifest\n */\nexport function parseCustomFunctionsManifest(\n contents: string,\n options: {\n /** Skip path validation while identifying candidate manifest files. */\n skipPathValidation?: boolean;\n } = {},\n): CustomFunctionsManifest {\n const manifest = decodeCodec(CustomFunctionsManifest, yaml.load(contents));\n validateCustomFunctionsManifest(manifest, options);\n return manifest;\n}\n\n/**\n * A custom function config from the manifest, plus CLI-level settings that\n * are not part of the SDK sync input.\n */\nexport type CustomFunctionManifestConfig = CustomFunctionConfigInput & {\n /** Env variable name holding the internal key of the function's Sombra gateway */\n sombraAuthEnv?: string;\n /** Parsed JSON test payloads to run the function with before pushing */\n testPayloads?: {\n /** The parsed JSON payload */\n payload: object;\n /** Which export the payload invokes when test-running a DSR function */\n payloadType?: Exclude<CustomFunctionPayloadType, typeof CustomFunctionPayloadType.Maestro>;\n }[];\n};\n\n/**\n * Hydrate parsed manifest entries from their referenced files.\n *\n * @param filePath - Path to the manifest YAML file\n * @param manifest - Parsed manifest\n * @param assertPath - Optional safety check applied before referenced files are read\n * @returns Custom Function configs with code and payloads loaded\n */\nfunction hydrateCustomFunctionsManifest(\n filePath: string,\n manifest: CustomFunctionsManifest,\n assertPath?: (path: string) => void,\n): CustomFunctionManifestConfig[] {\n const manifestDir = dirname(resolve(filePath));\n\n /**\n * Load and parse a JSON test payload file.\n *\n * @param entryName - The manifest entry name, for error messages\n * @param payloadFile - The payload file path, relative to the manifest\n * @returns The parsed payload object\n */\n const loadTestPayload = (entryName: string, payloadFile: string): object => {\n const testPayloadPath = resolve(manifestDir, payloadFile);\n assertPath?.(testPayloadPath);\n if (!existsSync(testPayloadPath)) {\n throw new Error(\n `Test payload file for custom function \"${entryName}\" does not exist: ${testPayloadPath}`,\n );\n }\n const rawPayload = readFileSync(testPayloadPath, 'utf-8');\n try {\n return JSON.parse(rawPayload);\n } catch (err) {\n throw new Error(\n `Test payload file for custom function \"${entryName}\" is not valid JSON ` +\n `(${testPayloadPath}): ${(err as Error).message}`,\n );\n }\n };\n\n return manifest.functions.map((entry) => {\n validateCustomFunctionExecutionContext({\n ...(entry.env ? { env: entry.env } : {}),\n ...(entry['allowed-hosts'] ? { allowedHosts: entry['allowed-hosts'] } : {}),\n });\n const codePath = resolve(manifestDir, entry.code);\n assertPath?.(codePath);\n if (!existsSync(codePath)) {\n throw new Error(`Code file for custom function \"${entry.name}\" does not exist: ${codePath}`);\n }\n\n // `test-payload` is shorthand for a single-item `test-payloads` list\n let testPayloads: CustomFunctionManifestConfig['testPayloads'];\n if (entry['test-payload'] !== undefined) {\n testPayloads = [\n {\n payload: loadTestPayload(entry.name, entry['test-payload']),\n ...(entry['test-payload-type'] !== undefined\n ? { payloadType: entry['test-payload-type'] }\n : {}),\n },\n ];\n } else if (entry['test-payloads'] !== undefined && entry['test-payloads'].length > 0) {\n testPayloads = entry['test-payloads'].map((item) => ({\n payload: loadTestPayload(entry.name, item.payload),\n ...(item['payload-type'] !== undefined ? { payloadType: item['payload-type'] } : {}),\n }));\n }\n\n return {\n name: entry.name,\n code: readFileSync(codePath, 'utf-8'),\n ...(entry.id !== undefined ? { id: entry.id } : {}),\n ...(entry.description !== undefined ? { description: entry.description } : {}),\n ...(entry.type !== undefined ? { type: entry.type } : {}),\n ...(entry['data-silo-id'] !== undefined ? { dataSiloId: entry['data-silo-id'] } : {}),\n ...(entry['sombra-id'] !== undefined ? { sombraId: entry['sombra-id'] } : {}),\n ...(entry['sombra-auth-env'] !== undefined\n ? { sombraAuthEnv: entry['sombra-auth-env'] }\n : {}),\n ...(entry['allowed-hosts'] !== undefined ? { allowedHosts: entry['allowed-hosts'] } : {}),\n ...(entry['timeout-ms'] !== undefined ? { timeoutMs: entry['timeout-ms'] } : {}),\n ...(entry['allow-third-party-imports'] !== undefined\n ? { allowThirdPartyImports: entry['allow-third-party-imports'] }\n : {}),\n ...(entry.env !== undefined ? { env: entry.env } : {}),\n ...(testPayloads !== undefined ? { testPayloads } : {}),\n };\n });\n}\n\n/**\n * Read a custom functions manifest from disk, apply variable substitution,\n * validate it, and hydrate its referenced code and payload files.\n *\n * Variable substitution deliberately happens before parsing and hydration to\n * preserve push behavior.\n *\n * @param filePath - Path to the manifest YAML file\n * @param variables - Variables to fill into `<<parameters.x>>` placeholders\n * @param assertPath - Optional safety check applied before referenced files are read\n * @returns The custom function configs, with code loaded from disk\n */\nexport function readCustomFunctionsManifest(\n filePath: string,\n variables: ObjByString = {},\n assertPath?: (path: string) => void,\n): CustomFunctionManifestConfig[] {\n const fileContents = readFileSync(filePath, 'utf-8');\n const replacedVariables = replaceVariablesInYaml(\n fileContents,\n variables,\n `Also check that there are no extra variables defined in your manifest: ${filePath}`,\n );\n const manifest = parseCustomFunctionsManifest(replacedVariables);\n return hydrateCustomFunctionsManifest(filePath, manifest, assertPath);\n}\n\n/**\n * Resolve and hydrate one selected manifest entry.\n *\n * This lets local execution ignore unrelated entries whose files or\n * variables are currently incomplete.\n *\n * @param filePath - Path to the containing manifest\n * @param entry - Selected raw manifest entry\n * @param variables - Variables to fill into `<<parameters.x>>` placeholders\n * @param assertPath - Optional safety check applied before referenced files are read\n * @returns Hydrated selected configuration and its source path\n */\nexport function readCustomFunctionManifestEntry(\n filePath: string,\n entry: CustomFunctionManifestEntry,\n variables: ObjByString = {},\n assertPath?: (path: string) => void,\n): { config: CustomFunctionManifestConfig; sourcePath: string } {\n const entryContents = yaml.dump({ functions: [entry] });\n const replacedVariables = replaceVariablesInYaml(\n entryContents,\n variables,\n `Also check that there are no extra variables defined for \"${entry.name}\" in: ${filePath}`,\n );\n const manifest = parseCustomFunctionsManifest(replacedVariables);\n const resolvedEntry = manifest.functions[0]!;\n const manifestDirectory = dirname(resolve(filePath));\n const sourcePath = resolve(manifestDirectory, resolvedEntry.code);\n return {\n config: hydrateCustomFunctionsManifest(filePath, manifest, assertPath)[0]!,\n sourcePath,\n };\n}\n\n/**\n * Append an entry to a manifest YAML document while preserving the existing\n * document's comments, ordering, scalar styles, and unresolved placeholders.\n *\n * The complete result is parsed and validated with the same codec and\n * semantic rules used by {@link readCustomFunctionsManifest}.\n *\n * @param contents - Existing custom function manifest YAML\n * @param entry - Manifest entry to append\n * @returns Updated manifest YAML\n */\nexport function insertCustomFunctionManifestEntry(\n contents: string,\n entry: CustomFunctionManifestEntry,\n): string {\n const document = parseDocument(contents);\n if (document.errors.length > 0) {\n throw document.errors[0];\n }\n\n const functions = document.get('functions');\n if (!isSeq(functions)) {\n throw new Error('Expected a `functions` list in custom function manifest');\n }\n\n if (functions.items.length === 0) {\n functions.flow = false;\n }\n functions.add(document.createNode(entry));\n const updatedContents = document.toString();\n parseCustomFunctionsManifest(updatedContents);\n return updatedContents;\n}\n\n/**\n * The IDs assigned to a manifest entry during a push, to write back into the\n * manifest file.\n */\nexport interface CustomFunctionManifestIds {\n /** Custom function ID */\n id?: string;\n /** Data silo (DSR integration) ID, for DSR functions */\n dataSiloId?: string;\n}\n\n/**\n * Write custom function and data silo IDs back into a manifest file, so\n * future pushes match by ID instead of by (potentially non-unique) name and\n * DSR entries keep pointing at their integration.\n *\n * The raw file text is edited via a comment- and formatting-preserving YAML\n * document, so comments and un-substituted `<<parameters.x>>` placeholders\n * survive untouched. Only entries that are missing the respective key are\n * modified.\n *\n * @param filePath - Path to the manifest YAML file\n * @param idsByIndex - IDs for each manifest entry, by array index (undefined\n * entries are left unchanged)\n * @returns The number of entries that were updated\n */\nexport function writeCustomFunctionIdsToManifest(\n filePath: string,\n idsByIndex: (CustomFunctionManifestIds | undefined)[],\n): number {\n const document = parseDocument(readFileSync(filePath, 'utf-8'));\n const functions = document.get('functions');\n if (!isSeq(functions)) {\n throw new Error(`Expected a \\`functions\\` list in manifest: ${filePath}`);\n }\n\n let updated = 0;\n functions.items.forEach((item, index) => {\n const ids = idsByIndex[index];\n if (!ids || !isMap(item)) {\n return;\n }\n let changed = false;\n if (ids.id && !item.has('id')) {\n // Place `id` first so it reads as the entry's key\n item.items.unshift(document.createPair('id', ids.id));\n changed = true;\n }\n if (ids.dataSiloId && !item.has('data-silo-id')) {\n // Place `data-silo-id` right after `id` (or first when there is no id)\n const idIndex = item.items.findIndex((pair) => isScalar(pair.key) && pair.key.value === 'id');\n item.items.splice(idIndex + 1, 0, document.createPair('data-silo-id', ids.dataSiloId));\n changed = true;\n }\n if (changed) {\n updated += 1;\n }\n });\n\n if (updated > 0) {\n writeFileSync(filePath, document.toString());\n }\n return updated;\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./logger-9T6vva73.mjs";import{chunk as t,difference as n,flatten as r,keyBy as i,uniq as a}from"lodash-es";import o from"colors";import{apply as s,getEntries as c}from"@transcend-io/type-utils";import{CREATE_DATA_SILOS as l,UPDATE_DATA_SILOS as u,UPDATE_OR_CREATE_DATA_POINT as d,convertToDataSubjectBlockList as f,createDataSubject as p,fetchAllActions as m,fetchAllAttributes as h,fetchAllDataSilos as g,fetchAllDataSubjects as _,fetchApiKeys as v,fetchIdentifiersAndCreateMissing as y,makeGraphQLRequest as b,syncAction as x,syncActionItemCollections as S,syncActionItems as C,syncAttribute as w,syncBusinessEntities as T,syncConsentManager as ee,syncConsentWorkflowTriggers as te,syncCookies as ne,syncDataCategories as re,syncDataFlows as ie,syncDataSiloDependencies as ae,syncDataSubject as oe,syncEnricher as se,syncIdentifier as ce,syncIntlMessages as le,syncPartitions as ue,syncPolicies as de,syncPreferenceOptionValues as fe,syncPreferenceTopics as pe,syncPrivacyCenter as me,syncProcessingActivities as he,syncProcessingPurposes as ge,syncPurposes as _e,syncTeams as ve,syncTemplate as ye,syncVendors as be,syncWorkflowConfigs as xe}from"@transcend-io/sdk";import{map as E,mapSeries as D}from"@transcend-io/utils";import O from"cli-progress";const k=/^[A-Za-z]+$/;function Se(e){let t=[],n=(e,n)=>{k.test(e)||t.push(`${n}: slug "${e}" is invalid — slugs must contain only alphabetical letters (A-Z, a-z). Topic slugs are normalized to PascalCase server-side, so YAML slugs should already be PascalCase or matching will break on subsequent pulls.`)};for(let t of e[`preference-options`]??[])n(t.slug,`preference-options`);for(let t of e.purposes??[])for(let e of t[`preference-topics`]??[]){n(e.slug,`purposes (topic "${e.title}")`);for(let t of e.options??[])n(t.slug,`purposes (topic "${e.title}" option "${t.title}")`)}return t}async function A({"data-silos":t=[],"data-subjects":s=[],"processing-activities":c=[],enrichers:l=[]},u,d=!1){let f=a([...r(t.map(e=>e[`data-subjects`]||[])||[]),...r(c.map(({dataSubjectTypes:e})=>e??[])??[]),...r(l.map(e=>e[`data-subjects`]||[])||[]),...s.map(e=>e.type)]);if(f.length===0&&!d)return{};let m=await _(u,{logger:e}),h=i(m,`type`),g=n(f,m.map(({type:e})=>e));if(g.length>0){e.info(o.magenta(`Creating ${g.length} new data subjects...`));for(let t of g){e.info(o.magenta(`Creating data subject ${t}...`));let n=await p(u,{input:t,logger:e});e.info(o.green(`Created data subject ${t}!`)),h[t]=n}}return h}function Ce(e,t){if(e.length===0)return[];if(typeof e[0]==`string`)return[{titles:a(e)}];let n,r=[],i=new Set,o=(e,n)=>{if(i.has(e))throw Error(`Data silo "${t}" has multiple deletion-dependencies entries for workflow "${e}". Combine them into a single entry.`);i.add(e),r.push({workflowConfigInternalName:e,...n})};return e.forEach(e=>{if(`reset-to-global`in e&&`titles`in e)throw Error(`Data silo "${t}" has a deletion-dependencies entry for workflow "${e.workflow}" that sets both "reset-to-global" and "titles". Use "titles: []" to override the global configuration with no dependencies, or "reset-to-global: true" to fall back to the global configuration.`);if(`reset-to-global`in e){o(e.workflow,{resetToGlobal:!0});return}if(e.workflow){o(e.workflow,{titles:e.titles});return}if(n!==void 0)throw Error(`Data silo "${t}" has multiple global deletion-dependencies entries. Combine them into a single \`{ titles: [...] }\` entry.`);n=e.titles}),[...n===void 0?[]:[{titles:a(n)}],...r]}async function j(n,r,{pageSize:a,dataSubjectsByName:c,apiKeysByTitle:p}){let m=!1,h=new Date().getTime();e.info(o.magenta(`Syncing "${n.length}" data silos...`));let _=i(await g(r,{titles:n.map(({title:e})=>e),pageSize:a,logger:e}),`title`),v=n.filter(({title:e})=>!_[e]);v.length>0&&(e.info(o.magenta(`Creating "${v.length}" data silos that did not exist...`)),await D(t(v,20),async t=>{let{createDataSilos:{dataSilos:n}}=await b(r,l,{variables:{input:t.map(e=>({name:e[`outer-type`]||e.integrationName,title:e.title,country:e.country,countrySubDivision:e.countrySubDivision,sombraId:e[`sombra-id`]}))},logger:e});n.forEach(e=>{_[e.title]=e})}),e.info(o.green(`Successfully created "${v.length}" data silos!`)));let y=t(n,20);await D(y,async(t,n)=>{e.info(o.magenta(`[Batch ${n+1}/${y.length}] Syncing "${t.length}" data silos`)),await b(r,u,{variables:{input:{dataSilos:t.map(e=>({id:_[e.title].id,country:e.country,countrySubDivision:e.countrySubDivision,url:e.url,headers:e.headers,description:e.description,identifiers:e[`identity-keys`],isLive:!e.disabled,ownerEmails:e.owners,teamNames:e.teams,dependedOnDataSilos:e[`deletion-dependencies`]?void 0:[{titles:[]}],apiKeyId:e[`api-key-title`]?p[e[`api-key-title`]].id:void 0,dataSubjectBlockListIds:e[`data-subjects`]?f(e[`data-subjects`],c):void 0,attributes:e.attributes,businessEntityTitles:e.businessEntityTitles,sombraId:e[`sombra-id`],notifyEmailAddress:e[`email-settings`]?.[`notify-email-address`],promptAVendorEmailSendFrequency:e[`email-settings`]?.[`send-frequency`],promptAVendorEmailSendType:e[`email-settings`]?.[`send-type`],promptAVendorEmailIncludeIdentifiersAttachment:e[`email-settings`]?.[`include-identifiers-attachment`],promptAVendorEmailCompletionLinkType:e[`email-settings`]?.[`completion-link-type`],manualWorkRetryFrequency:e[`email-settings`]?.[`manual-work-retry-frequency`]}))}},logger:e}),e.info(o.green(`[Batch ${n+1}/${y.length}] Synced "${t.length}" data silos!`))});let x=new O.SingleBar({},O.Presets.shades_classic),S=n.filter(({datapoints:e=[]})=>e.length>0),C=n.map(({datapoints:e=[]})=>e.length).reduce((e,t)=>e+t,0);e.info(o.magenta(`Syncing "${C}" datapoints from "${S.length}" data silos...`)),x.start(C,0);let w=0;await E(S,async({datapoints:t,title:n})=>{t&&await D(t,async t=>{let i=t.fields?t.fields.map(({key:e,description:t,categories:n,purposes:r,attributes:i,...a})=>({name:e,description:t,categories:n?n.map(e=>({...e,name:e.name||`Other`})):void 0,purposes:r?r.map(e=>({...e,name:e.name||`Other`})):void 0,attributes:i,accessRequestVisibilityEnabled:a[`access-request-visibility-enabled`],erasureRequestRedactionEnabled:a[`erasure-request-redaction-enabled`]})):void 0,a={dataSiloId:_[n].id,path:t.path,name:t.key,title:t.title,description:t.description,...t.owners?{ownerEmails:t.owners}:{},...t.teams?{teamNames:t.teams}:{},...t[`data-collection-tag`]?{dataCollectionTag:t[`data-collection-tag`]}:{},querySuggestions:t[`privacy-action-queries`]?Object.entries(t[`privacy-action-queries`]).map(([e,t])=>({requestType:e,suggestedQuery:t})):void 0,enabledActions:t[`privacy-actions`]||[],subDataPoints:i},s=(a.subDataPoints||[]).map(({name:e})=>e),c=s.filter((e,t)=>s.indexOf(e)!==t);if(c.length>0)e.info(o.red(`\nCannot update datapoint "${t.key}" as it has duplicate sub-datapoints with the same name: \n${c.join(`
|
|
2
|
-
`)}`)),m=!0;else try{await b(r,d,{variables:a,logger:e})}catch(r){e.info(o.red(`\nFailed to update datapoint "${t.key}" for data silo "${n}"! - \n${r.message}`)),m=!0}w+=1,x.update(w)})},{concurrency:10}),x.stop();let T=new Date().getTime()-h;return e.info(o.green(`Synced "${n.length}" data silos and "${C}" datapoints in "${T/1e3}" seconds!`)),{success:!m,dataSiloTitleToId:s(_,({id:e})=>e)}}async function M(t,n,{pageSize:r=50,publishToPrivacyCenter:i=!0,classifyService:a=!1,deleteExtraAttributeValues:s=!1,logger:c,warnings:l=[]}){let u=c??e,d=[],f=!1,p=(e,t,n)=>{d.push({resource:e,item:n,message:t}),f=!0};u.info(o.magenta(`Fetching data with page size ${r}...`));let{templates:g,attributes:b,actions:D,identifiers:O,"data-subjects":k,"business-entities":M,enrichers:N,cookies:P,"consent-manager":F,"data-silos":I,"data-flows":L,vendors:R,"data-categories":z,"processing-activities":B,"processing-purposes":V,"action-items":H,"action-item-collections":U,teams:W,"privacy-center":G,messages:K,policies:q,partitions:J,"preference-workflow-configs":we,"workflow-configs":Y}=t,Te=t[`preference-options`],X=t.purposes,Ee=Se(t);if(Ee.length>0){for(let e of Ee)p(`preference-slugs`,e);return{success:!1,errors:d,warnings:l.length>0?l:void 0}}let[Z,Q,De]=await Promise.all([N||O?y(n,{input:t,skipPublish:!i,logger:e}):{},I||k||N||B?A(t,n):{},I&&I.map(e=>e[`api-key-title`]||[]).reduce((e,t)=>e+t.length,0)>0?v(n,{apiKeyInputs:t,logger:e}):{}]);if(we?.length&&(await te(n,we,{logger:u,pageSize:r})||p(`preference-workflow-configs`,`Failed to sync preference workflow configs`)),Y?.length&&(await xe(n,Y,{logger:u})||p(`workflow-configs`,`Failed to sync workflow configs`)),Te?.length&&(await fe(n,Te,{logger:u})||p(`preference-options`,`Failed to sync preference option values`)),X?.length){let{success:e,purposeIdByTrackingType:t}=await _e(n,X,{logger:u});e||p(`purposes`,`Failed to sync one or more purposes`);let r=X.flatMap(e=>(e[`preference-topics`]??[]).map(t=>({...t,"tracking-type":e.trackingType})));r.length>0&&(await pe(n,r,{logger:u,purposeIdByTrackingType:t})||p(`preference-topics`,`Failed to sync one or more preference topics`))}if(F){u.info(o.magenta(`Syncing consent manager...`));try{await ee(n,F,{logger:u}),u.info(o.green(`Successfully synced consent manager!`))}catch(e){p(`consent-manager`,e.message),u.error(o.red(`Failed to sync consent manager! - ${e.message}`))}}if(W){let t=await ve(n,W,{logger:e});f||=!t}if(g&&(e.info(o.magenta(`Syncing "${g.length}" email templates...`)),await E(g,async t=>{e.info(o.magenta(`Syncing template "${t.title}"...`));try{await ye(n,t,{logger:e}),e.info(o.green(`Successfully synced template "${t.title}"!`))}catch(n){f=!0,e.error(o.red(`Failed to sync template "${t.title}"! - ${n.message}`))}},{concurrency:10}),e.info(o.green(`Synced "${g.length}" email templates!`))),M){let t=await T(n,M,{logger:e});f||=!t}if(R){let t=await be(n,R,{logger:e});f||=!t}if(z){let t=await re(n,z,{logger:e});f||=!t}if(V){let t=await ge(n,V,{logger:e});f||=!t}if(J){let t=await ue(n,J,{logger:e});f||=!t}if(P){let t=await ne(n,P,{logger:e});f||=!t}if(U){let t=await S(n,U,{logger:e});f||=!t}if(b){e.info(o.magenta(`Syncing "${b.length}" attributes...`));let t=await h(n,{logger:e});await E(b,async r=>{let i=t.find(e=>e.name===r.name);e.info(o.magenta(`Syncing attribute "${r.name}"...`));try{await w(n,r,{existingAttribute:i,deleteExtraAttributeValues:s,logger:e}),e.info(o.green(`Successfully synced attribute "${r.name}"!`))}catch(t){f=!0,e.error(o.red(`Failed to sync attribute "${r.name}"! - ${t.message}`))}},{concurrency:10}),e.info(o.green(`Synced "${b.length}" attributes!`))}if(H){let t=await C(n,H,{logger:e});f||=!t}if(N&&(e.info(o.magenta(`Syncing "${N.length}" enrichers...`)),await E(N,async t=>{e.info(o.magenta(`Syncing enricher "${t.title}"...`));try{await se(n,{input:t,identifierByName:Z,dataSubjectsByName:Q,logger:e}),e.info(o.green(`Successfully synced enricher "${t.title}"!`))}catch(n){f=!0,e.error(o.red(`Failed to sync enricher "${t.title}"! - ${n.message}`))}},{concurrency:10}),e.info(o.green(`Synced "${N.length}" enrichers!`))),O&&(e.info(o.magenta(`Syncing "${O.length}" identifiers...`)),await E(O,async t=>{let r=Z[t.name];if(!r)throw Error(`Failed to find identifier with name: ${t.type}. Should have been auto-created by cli.`);e.info(o.magenta(`Syncing identifier "${t.type}"...`));try{await ce(n,{input:t,dataSubjectsByName:Q,identifierId:r.id,skipPublish:!i,logger:e}),e.info(o.green(`Successfully synced identifier "${t.type}"!`))}catch(n){f=!0,e.info(o.red(`Failed to sync identifier "${t.type}"! - ${n.message}`))}},{concurrency:10}),e.info(o.green(`Synced "${O.length}" identifiers!`))),D){e.info(o.magenta(`Syncing "${D.length}" actions...`));let t=await m(n,{logger:e});await E(D,async r=>{let a=t.find(e=>e.type===r.type);if(!a)throw Error(`Failed to find action with type: ${r.type}. Should have already existing in the organization.`);e.info(o.magenta(`Syncing action "${r.type}"...`));try{await x(n,{action:r,actionId:a.id,skipPublish:!i},{logger:e}),e.info(o.green(`Successfully synced action "${r.type}"!`))}catch(t){f=!0,e.error(o.red(`Failed to sync action "${r.type}"! - ${t.message}`))}},{concurrency:10}),e.info(o.green(`Synced "${D.length}" actions!`))}if(k){e.info(o.magenta(`Syncing "${k.length}" data subjects...`));let t=await _(n,{logger:e});await E(k,async r=>{let a=t.find(e=>e.type===r.type);if(!a)throw Error(`Failed to find data subject with type: ${r.type}. Should have already existing in the organization.`);e.info(o.magenta(`Syncing data subject "${r.type}"...`));try{await oe(n,{input:r,dataSubjectId:a.id,skipPublish:!i,logger:e}),e.info(o.green(`Successfully synced data subject "${r.type}"!`))}catch(t){f=!0;let n=r.supportsAuthorizedAgent!==void 0&&/authorized.agent/i.test(t.message)?` (enable the Authorized Agents feature for this organization before setting "supportsAuthorizedAgent")`:``;e.info(o.red(`Failed to sync data subject "${r.type}"! - ${t.message}${n}`))}},{concurrency:10}),e.info(o.green(`Synced "${k.length}" data subjects!`))}if(L){let t=await ie(n,L,{classifyService:a,logger:e});f||=!t}if(G)try{await me(n,G,{logger:e,skipPublish:!i})||p(`privacy-center`,`Failed to sync privacy center`)}catch(e){p(`privacy-center`,e.message)}if(K){let t=await le(n,K,{logger:e});f||=!t}if(q){let t=await de(n,q,{logger:e});f||=!t}let $=[];if(I){let{success:e,dataSiloTitleToId:t}=await j(I,n,{dataSubjectsByName:Q,apiKeysByTitle:De,pageSize:r});I?.forEach(e=>{let n=e[`deletion-dependencies`];if(n)try{$.push([t[e.title],Ce(n,e.title)])}catch(t){p(`data-silos`,t.message,e.title),u.error(o.red(t.message))}}),f||=!e}if($.length>0){let t=await ae(n,{input:$,logger:e});f||=!t}if(B){let t=await he(n,B,{logger:e});f||=!t}return{success:!f,errors:d,warnings:l.length>0?l:void 0}}function N(e,...t){let n=JSON.parse(JSON.stringify(e));return t.forEach(e=>{c(e).forEach(([e,t])=>{n[e]===void 0?n[e]=t:Array.isArray(t)?n[e]=[...n[e],...t]:n[e]=t})}),n}export{A as i,M as n,j as r,N as t};
|
|
3
|
-
//# sourceMappingURL=mergeTranscendInputs-CWHXH0kF.mjs.map
|