@transcend-io/cli 10.27.5 → 10.28.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 +193 -24
- package/dist/app-DCY8E776.mjs +148 -0
- package/dist/app-DCY8E776.mjs.map +1 -0
- package/dist/{approvePrivacyRequests-B842tXn4.mjs → approvePrivacyRequests-80SV_94y.mjs} +2 -2
- package/dist/{approvePrivacyRequests-B842tXn4.mjs.map → approvePrivacyRequests-80SV_94y.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-CRrUU89C.mjs → buildExamples-2YDkSvtA.mjs} +2 -2
- package/dist/{buildExamples-CRrUU89C.mjs.map → buildExamples-2YDkSvtA.mjs.map} +1 -1
- package/dist/{buildXdiSyncEndpoint-B9seJTxP.mjs → buildXdiSyncEndpoint-B3zUrI16.mjs} +2 -2
- package/dist/{buildXdiSyncEndpoint-B9seJTxP.mjs.map → buildXdiSyncEndpoint-B3zUrI16.mjs.map} +1 -1
- package/dist/{bulkRestartRequests-CE7cDUMd.mjs → bulkRestartRequests-BLb8OJmf.mjs} +2 -2
- package/dist/{bulkRestartRequests-CE7cDUMd.mjs.map → bulkRestartRequests-BLb8OJmf.mjs.map} +1 -1
- package/dist/{bulkRetryEnrichers-BcSANZPC.mjs → bulkRetryEnrichers-BA9cboSe.mjs} +2 -2
- package/dist/{bulkRetryEnrichers-BcSANZPC.mjs.map → bulkRetryEnrichers-BA9cboSe.mjs.map} +1 -1
- package/dist/{cancelPrivacyRequests-BKibbagv.mjs → cancelPrivacyRequests-BEyOBJ2r.mjs} +2 -2
- package/dist/{cancelPrivacyRequests-BKibbagv.mjs.map → cancelPrivacyRequests-BEyOBJ2r.mjs.map} +1 -1
- package/dist/{codecs-zRkskjaJ.mjs → codecs-BsQFAFGT.mjs} +1 -1
- package/dist/{codecs-zRkskjaJ.mjs.map → codecs-BsQFAFGT.mjs.map} +1 -1
- package/dist/{collectCsvFilesOrExit-Dg5U3Qx_.mjs → collectCsvFilesOrExit-BqnmK63h.mjs} +1 -1
- package/dist/{collectCsvFilesOrExit-Dg5U3Qx_.mjs.map → collectCsvFilesOrExit-BqnmK63h.mjs.map} +1 -1
- package/dist/{collectParquetFilesOrExit-BlG2RRSC.mjs → collectParquetFilesOrExit-BiksFI-Q.mjs} +1 -1
- package/dist/{collectParquetFilesOrExit-BlG2RRSC.mjs.map → collectParquetFilesOrExit-BiksFI-Q.mjs.map} +1 -1
- package/dist/{command-DKNKpgUB.mjs → command-eIenZMOx.mjs} +2 -2
- package/dist/{command-DKNKpgUB.mjs.map → command-eIenZMOx.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-Rv-ajWya.mjs → consentManagersToBusinessEntities-O4KBox9u.mjs} +1 -1
- package/dist/{consentManagersToBusinessEntities-Rv-ajWya.mjs.map → consentManagersToBusinessEntities-O4KBox9u.mjs.map} +1 -1
- package/dist/{constants-CdgK1myP.mjs → constants-BG53Mbi9.mjs} +2 -2
- package/dist/{constants-CdgK1myP.mjs.map → constants-BG53Mbi9.mjs.map} +1 -1
- package/dist/{constants-FbK2iR--.mjs → constants-BMAOvdcA.mjs} +1 -1
- package/dist/{constants-FbK2iR--.mjs.map → constants-BMAOvdcA.mjs.map} +1 -1
- package/dist/{constants-B6wESCW2.mjs → constants-BpQ5awGp.mjs} +2 -2
- package/dist/{constants-B6wESCW2.mjs.map → constants-BpQ5awGp.mjs.map} +1 -1
- package/dist/{constants-BuoISmXP.mjs → constants-C1seVfCu.mjs} +1 -1
- package/dist/{constants-BuoISmXP.mjs.map → constants-C1seVfCu.mjs.map} +1 -1
- package/dist/{context-4GAUnhTE.mjs → context-CZYrQfAn.mjs} +1 -1
- package/dist/{context-4GAUnhTE.mjs.map → context-CZYrQfAn.mjs.map} +1 -1
- package/dist/{createPoolingCommandUi-vTsGgYZT.mjs → createPoolingCommandUi-C39R_zo3.mjs} +2 -2
- package/dist/{createPoolingCommandUi-vTsGgYZT.mjs.map → createPoolingCommandUi-C39R_zo3.mjs.map} +1 -1
- package/dist/{dataFlowsToDataSilos-DRuAfwIn.mjs → dataFlowsToDataSilos-CLSHb1GA.mjs} +1 -1
- package/dist/{dataFlowsToDataSilos-DRuAfwIn.mjs.map → dataFlowsToDataSilos-CLSHb1GA.mjs.map} +1 -1
- package/dist/deno-runtime-QqwBDKXX.mjs +2 -0
- package/dist/deno-runtime-QqwBDKXX.mjs.map +1 -0
- package/dist/{done-input-validation-BFgMCQWH.mjs → done-input-validation-Cjk7a3jX.mjs} +1 -1
- package/dist/{done-input-validation-BFgMCQWH.mjs.map → done-input-validation-Cjk7a3jX.mjs.map} +1 -1
- package/dist/{downloadPrivacyRequestFiles-Dn3VexN2.mjs → downloadPrivacyRequestFiles-CLFgQItk.mjs} +2 -2
- package/dist/{downloadPrivacyRequestFiles-Dn3VexN2.mjs.map → downloadPrivacyRequestFiles-CLFgQItk.mjs.map} +1 -1
- package/dist/{extractClientError-DeS63kje.mjs → extractClientError-C_6ly7Gw.mjs} +1 -1
- package/dist/{extractClientError-DeS63kje.mjs.map → extractClientError-C_6ly7Gw.mjs.map} +1 -1
- package/dist/{fetchAllRequests-DUQGTJjx.mjs → fetchAllRequests-CEDKdq0c.mjs} +2 -2
- package/dist/{fetchAllRequests-DUQGTJjx.mjs.map → fetchAllRequests-CEDKdq0c.mjs.map} +1 -1
- package/dist/{formatPolicyBundleVersionSummary-Bo7DYxoy.mjs → formatPolicyBundleVersionSummary-CrFtcPwS.mjs} +1 -1
- package/dist/{formatPolicyBundleVersionSummary-Bo7DYxoy.mjs.map → formatPolicyBundleVersionSummary-CrFtcPwS.mjs.map} +1 -1
- package/dist/{fuzzyMatchColumns-BDD4a2W0.mjs → fuzzyMatchColumns-OTsFJ21f.mjs} +2 -2
- package/dist/{fuzzyMatchColumns-BDD4a2W0.mjs.map → fuzzyMatchColumns-OTsFJ21f.mjs.map} +1 -1
- package/dist/{generateCrossAccountApiKeys-QhnLqtUN.mjs → generateCrossAccountApiKeys-D6O_Qm3Y.mjs} +2 -2
- package/dist/{generateCrossAccountApiKeys-QhnLqtUN.mjs.map → generateCrossAccountApiKeys-D6O_Qm3Y.mjs.map} +1 -1
- package/dist/{impl-CrY1za0t.mjs → impl-6m7GXUAe.mjs} +2 -2
- package/dist/{impl-CrY1za0t.mjs.map → impl-6m7GXUAe.mjs.map} +1 -1
- package/dist/{impl-D33Zr0vh2.mjs → impl-B--lr5RJ2.mjs} +2 -2
- package/dist/{impl-D33Zr0vh2.mjs.map → impl-B--lr5RJ2.mjs.map} +1 -1
- package/dist/{impl-QeWNZCg4.mjs → impl-B3_hrFPX.mjs} +2 -2
- package/dist/{impl-QeWNZCg4.mjs.map → impl-B3_hrFPX.mjs.map} +1 -1
- package/dist/{impl-CbijbA-M2.mjs → impl-BFJtKA2o2.mjs} +2 -2
- package/dist/{impl-CbijbA-M2.mjs.map → impl-BFJtKA2o2.mjs.map} +1 -1
- package/dist/{impl-GnxjtYL7.mjs → impl-BJlKQJdS2.mjs} +2 -2
- package/dist/impl-BJlKQJdS2.mjs.map +1 -0
- package/dist/{impl-_rrZ0KXo.mjs → impl-BNPgYSBT.mjs} +2 -2
- package/dist/{impl-_rrZ0KXo.mjs.map → impl-BNPgYSBT.mjs.map} +1 -1
- package/dist/{impl-s5h7dIlv2.mjs → impl-BWVe4VGF2.mjs} +2 -2
- package/dist/{impl-s5h7dIlv2.mjs.map → impl-BWVe4VGF2.mjs.map} +1 -1
- package/dist/{impl-DiyWNLJq.mjs → impl-BYGNx-8W2.mjs} +2 -2
- package/dist/impl-BYGNx-8W2.mjs.map +1 -0
- package/dist/impl-BcJ019T2.mjs +2 -0
- package/dist/impl-BcJ019T2.mjs.map +1 -0
- package/dist/{impl-CMYYX3Kz.mjs → impl-BewF8tB6.mjs} +2 -2
- package/dist/{impl-CMYYX3Kz.mjs.map → impl-BewF8tB6.mjs.map} +1 -1
- package/dist/{impl-DQG3PiAW2.mjs → impl-Bq6g_xYv.mjs} +2 -2
- package/dist/impl-Bq6g_xYv.mjs.map +1 -0
- package/dist/{impl-DRimS_G8.mjs → impl-BqrSLoig.mjs} +2 -2
- package/dist/{impl-DRimS_G8.mjs.map → impl-BqrSLoig.mjs.map} +1 -1
- package/dist/{impl-CccuY3Za.mjs → impl-Bs-drNBf.mjs} +2 -2
- package/dist/{impl-CccuY3Za.mjs.map → impl-Bs-drNBf.mjs.map} +1 -1
- package/dist/impl-BsC1KVxS.mjs +2 -0
- package/dist/impl-BsC1KVxS.mjs.map +1 -0
- package/dist/{impl-BgxjEv88.mjs → impl-BvNZqLrT.mjs} +2 -2
- package/dist/{impl-BgxjEv88.mjs.map → impl-BvNZqLrT.mjs.map} +1 -1
- package/dist/{impl-BkyqCY72.mjs → impl-BwRcpAno.mjs} +2 -2
- package/dist/{impl-BkyqCY72.mjs.map → impl-BwRcpAno.mjs.map} +1 -1
- package/dist/{impl-BddyFp_q.mjs → impl-BySmbxEt.mjs} +2 -2
- package/dist/{impl-BddyFp_q.mjs.map → impl-BySmbxEt.mjs.map} +1 -1
- package/dist/{impl-BPn_YPIq2.mjs → impl-C50tdWlL2.mjs} +2 -2
- package/dist/{impl-BPn_YPIq2.mjs.map → impl-C50tdWlL2.mjs.map} +1 -1
- package/dist/{impl-CusKk_JR2.mjs → impl-C5dzaDga2.mjs} +2 -2
- package/dist/{impl-CusKk_JR2.mjs.map → impl-C5dzaDga2.mjs.map} +1 -1
- package/dist/impl-C8BmxFQ2.mjs +2 -0
- package/dist/impl-C8BmxFQ2.mjs.map +1 -0
- package/dist/{impl-CBC5K-IH.mjs → impl-CDtUusmg.mjs} +2 -2
- package/dist/{impl-CBC5K-IH.mjs.map → impl-CDtUusmg.mjs.map} +1 -1
- package/dist/{impl-Dpes0u_6.mjs → impl-CE56-wQh.mjs} +2 -2
- package/dist/{impl-Dpes0u_6.mjs.map → impl-CE56-wQh.mjs.map} +1 -1
- package/dist/{impl-DzJG5HaX.mjs → impl-CEmXPEq22.mjs} +2 -2
- package/dist/impl-CEmXPEq22.mjs.map +1 -0
- package/dist/{impl-CZ-KvZE02.mjs → impl-CHmjV-Pa.mjs} +2 -2
- package/dist/impl-CHmjV-Pa.mjs.map +1 -0
- package/dist/{impl-CXdy0YMX.mjs → impl-CQGeH47F.mjs} +2 -2
- package/dist/{impl-CXdy0YMX.mjs.map → impl-CQGeH47F.mjs.map} +1 -1
- package/dist/{impl-DG9RwTkA2.mjs → impl-CTIWliTx.mjs} +2 -2
- package/dist/impl-CTIWliTx.mjs.map +1 -0
- package/dist/{impl-DbPLuDc22.mjs → impl-CWh9H7mL.mjs} +2 -2
- package/dist/impl-CWh9H7mL.mjs.map +1 -0
- package/dist/{impl-BQTw6jpw.mjs → impl-CXUJ1eRC.mjs} +2 -2
- package/dist/{impl-BQTw6jpw.mjs.map → impl-CXUJ1eRC.mjs.map} +1 -1
- package/dist/{impl-xRwX5ZLK.mjs → impl-CfPXYgT42.mjs} +2 -2
- package/dist/impl-CfPXYgT42.mjs.map +1 -0
- package/dist/{impl-CLVMZXJo.mjs → impl-CiYKuCHV.mjs} +2 -2
- package/dist/{impl-CLVMZXJo.mjs.map → impl-CiYKuCHV.mjs.map} +1 -1
- package/dist/{impl-BoCD0t4j.mjs → impl-CqF9xIIy.mjs} +2 -2
- package/dist/{impl-BoCD0t4j.mjs.map → impl-CqF9xIIy.mjs.map} +1 -1
- package/dist/{impl-CVChVd63.mjs → impl-Cx3T0Iei.mjs} +2 -2
- package/dist/{impl-CVChVd63.mjs.map → impl-Cx3T0Iei.mjs.map} +1 -1
- package/dist/impl-CzxvoiGW.mjs +3 -0
- package/dist/impl-CzxvoiGW.mjs.map +1 -0
- package/dist/impl-D-SQJc3t.mjs +2 -0
- package/dist/impl-D-SQJc3t.mjs.map +1 -0
- package/dist/{impl-BPjGRqOL2.mjs → impl-D23kJPAQ2.mjs} +2 -2
- package/dist/{impl-BPjGRqOL2.mjs.map → impl-D23kJPAQ2.mjs.map} +1 -1
- package/dist/{impl-uKjAibIR.mjs → impl-DF8RGVlT.mjs} +2 -2
- package/dist/{impl-uKjAibIR.mjs.map → impl-DF8RGVlT.mjs.map} +1 -1
- package/dist/{impl-PYtpY5hs2.mjs → impl-DHfYO9T52.mjs} +2 -2
- package/dist/{impl-PYtpY5hs2.mjs.map → impl-DHfYO9T52.mjs.map} +1 -1
- package/dist/{impl-p0AyZWFL.mjs → impl-DIPvFKiP.mjs} +2 -2
- package/dist/{impl-p0AyZWFL.mjs.map → impl-DIPvFKiP.mjs.map} +1 -1
- package/dist/{impl-CzbVqbm0.mjs → impl-DJLlcqSg.mjs} +2 -2
- package/dist/{impl-CzbVqbm0.mjs.map → impl-DJLlcqSg.mjs.map} +1 -1
- package/dist/{impl-3IyENr9T.mjs → impl-DOoR93T6.mjs} +2 -2
- package/dist/{impl-3IyENr9T.mjs.map → impl-DOoR93T6.mjs.map} +1 -1
- package/dist/{impl-BiBNqcob.mjs → impl-DXNcD7CZ.mjs} +2 -2
- package/dist/{impl-BiBNqcob.mjs.map → impl-DXNcD7CZ.mjs.map} +1 -1
- package/dist/{impl-nJ7LzilX2.mjs → impl-DYf10-BD2.mjs} +2 -2
- package/dist/{impl-nJ7LzilX2.mjs.map → impl-DYf10-BD2.mjs.map} +1 -1
- package/dist/{impl-wf0PvJms.mjs → impl-DYwojWvN.mjs} +2 -2
- package/dist/{impl-wf0PvJms.mjs.map → impl-DYwojWvN.mjs.map} +1 -1
- package/dist/{impl-CH40t5qf.mjs → impl-DbZ1cYoR.mjs} +2 -2
- package/dist/{impl-CH40t5qf.mjs.map → impl-DbZ1cYoR.mjs.map} +1 -1
- package/dist/{impl-DbF7uw7f2.mjs → impl-Dib6Fff12.mjs} +2 -2
- package/dist/{impl-DbF7uw7f2.mjs.map → impl-Dib6Fff12.mjs.map} +1 -1
- package/dist/{impl-4rLX5Wah2.mjs → impl-Dk6TZKrp2.mjs} +2 -2
- package/dist/{impl-4rLX5Wah2.mjs.map → impl-Dk6TZKrp2.mjs.map} +1 -1
- package/dist/{impl-Cq5wI-kP.mjs → impl-Dm-Fkr392.mjs} +2 -2
- package/dist/impl-Dm-Fkr392.mjs.map +1 -0
- package/dist/{impl-BsEYMBx2.mjs → impl-DpuhHpuk.mjs} +2 -2
- package/dist/{impl-BsEYMBx2.mjs.map → impl-DpuhHpuk.mjs.map} +1 -1
- package/dist/{impl-DlaWf-oS2.mjs → impl-Drql0g72.mjs} +2 -2
- package/dist/impl-Drql0g72.mjs.map +1 -0
- package/dist/{impl-_tY0CJdZ.mjs → impl-DzNk41xd.mjs} +2 -2
- package/dist/{impl-_tY0CJdZ.mjs.map → impl-DzNk41xd.mjs.map} +1 -1
- package/dist/impl-FLj_mWLO.mjs +2 -0
- package/dist/impl-FLj_mWLO.mjs.map +1 -0
- package/dist/{impl-DxXWaXFU.mjs → impl-GHukPBnk.mjs} +2 -2
- package/dist/{impl-DxXWaXFU.mjs.map → impl-GHukPBnk.mjs.map} +1 -1
- package/dist/{impl-V_ffkzbd2.mjs → impl-KKrDTraf.mjs} +2 -2
- package/dist/impl-KKrDTraf.mjs.map +1 -0
- package/dist/{impl-BbWzTCSd2.mjs → impl-M3Eu4QjS.mjs} +2 -2
- package/dist/impl-M3Eu4QjS.mjs.map +1 -0
- package/dist/{impl-BlFBTc6f.mjs → impl-VPBEqV0w2.mjs} +2 -2
- package/dist/impl-VPBEqV0w2.mjs.map +1 -0
- package/dist/{impl-DWBQKUuq.mjs → impl-amV0wRWk.mjs} +2 -2
- package/dist/{impl-DWBQKUuq.mjs.map → impl-amV0wRWk.mjs.map} +1 -1
- package/dist/impl-m3QXCc6n.mjs +91 -0
- package/dist/impl-m3QXCc6n.mjs.map +1 -0
- package/dist/{impl-Ba8naMzu.mjs → impl-syp5hclX.mjs} +2 -2
- package/dist/{impl-Ba8naMzu.mjs.map → impl-syp5hclX.mjs.map} +1 -1
- package/dist/{impl-9TDsbGfF2.mjs → impl-zZFaSmxd2.mjs} +2 -2
- package/dist/{impl-9TDsbGfF2.mjs.map → impl-zZFaSmxd2.mjs.map} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{initializeCliRuntime-B7zDhMok.mjs → initializeCliRuntime-802-P8MC.mjs} +1 -1
- package/dist/{initializeCliRuntime-B7zDhMok.mjs.map → initializeCliRuntime-802-P8MC.mjs.map} +1 -1
- package/dist/{inquirer-DNa31Mkx.mjs → inquirer-DkFTQU00.mjs} +2 -2
- package/dist/{inquirer-DNa31Mkx.mjs.map → inquirer-DkFTQU00.mjs.map} +1 -1
- package/dist/{listFiles-DrxRhlZ2.mjs → listFiles-CWHAdKY-.mjs} +1 -1
- package/dist/{listFiles-DrxRhlZ2.mjs.map → listFiles-CWHAdKY-.mjs.map} +1 -1
- package/dist/logger-9T6vva73.mjs +2 -0
- package/dist/logger-9T6vva73.mjs.map +1 -0
- package/dist/manifest-DVuW_vvO.mjs +2 -0
- package/dist/manifest-DVuW_vvO.mjs.map +1 -0
- package/dist/{mapConsentCsvRowsToInputs-DA7BRD7M.mjs → mapConsentCsvRowsToInputs-MMPZwzrV.mjs} +1 -1
- package/dist/{mapConsentCsvRowsToInputs-DA7BRD7M.mjs.map → mapConsentCsvRowsToInputs-MMPZwzrV.mjs.map} +1 -1
- package/dist/{markRequestDataSiloIdsCompleted-CK7PNwTz.mjs → markRequestDataSiloIdsCompleted-PKCcn5wQ.mjs} +2 -2
- package/dist/{markRequestDataSiloIdsCompleted-CK7PNwTz.mjs.map → markRequestDataSiloIdsCompleted-PKCcn5wQ.mjs.map} +1 -1
- package/dist/{markSilentPrivacyRequests-Cb-J9ckA.mjs → markSilentPrivacyRequests-7l9I99pe.mjs} +2 -2
- package/dist/{markSilentPrivacyRequests-Cb-J9ckA.mjs.map → markSilentPrivacyRequests-7l9I99pe.mjs.map} +1 -1
- package/dist/{mergeTranscendInputs-CkpitZbH.mjs → mergeTranscendInputs-CWHXH0kF.mjs} +2 -2
- package/dist/{mergeTranscendInputs-CkpitZbH.mjs.map → mergeTranscendInputs-CWHXH0kF.mjs.map} +1 -1
- package/dist/missing-manifest-BKj1j-jg.mjs +2 -0
- package/dist/missing-manifest-BKj1j-jg.mjs.map +1 -0
- package/dist/module-graph-DBC7ZvyA.mjs +2 -0
- package/dist/module-graph-DBC7ZvyA.mjs.map +1 -0
- package/dist/{notifyPrivacyRequestsAdditionalTime-it1FA62W.mjs → notifyPrivacyRequestsAdditionalTime-BPIYuCov.mjs} +2 -2
- package/dist/{notifyPrivacyRequestsAdditionalTime-it1FA62W.mjs.map → notifyPrivacyRequestsAdditionalTime-BPIYuCov.mjs.map} +1 -1
- package/dist/{parquetToCsvOneFile-Dtz90qfJ.mjs → parquetToCsvOneFile-CSVdrLvq.mjs} +2 -2
- package/dist/{parquetToCsvOneFile-Dtz90qfJ.mjs.map → parquetToCsvOneFile-CSVdrLvq.mjs.map} +1 -1
- package/dist/{parseAttributesFromString-BxJcnKmn.mjs → parseAttributesFromString-C5-KGkPH.mjs} +2 -2
- package/dist/{parseAttributesFromString-BxJcnKmn.mjs.map → parseAttributesFromString-C5-KGkPH.mjs.map} +1 -1
- package/dist/parseVariablesFromString-Cr3x2uq3.mjs +2 -0
- package/dist/parseVariablesFromString-Cr3x2uq3.mjs.map +1 -0
- package/dist/{policyEngineCliError-BnBghLd1.mjs → policyEngineCliError-DY8V-qkK.mjs} +1 -1
- package/dist/{policyEngineCliError-BnBghLd1.mjs.map → policyEngineCliError-DY8V-qkK.mjs.map} +1 -1
- package/dist/{printResult-oXnpZu_9.mjs → printResult-esxW3HhT.mjs} +2 -2
- package/dist/{printResult-oXnpZu_9.mjs.map → printResult-esxW3HhT.mjs.map} +1 -1
- package/dist/project-plan-apply-C2DfikVN.mjs +3 -0
- package/dist/project-plan-apply-C2DfikVN.mjs.map +1 -0
- package/dist/{pullAllDatapoints-XqreMBT5.mjs → pullAllDatapoints-H92qjLSx.mjs} +2 -2
- package/dist/{pullAllDatapoints-XqreMBT5.mjs.map → pullAllDatapoints-H92qjLSx.mjs.map} +1 -1
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DuPDOm6f.mjs → pullChunkedCustomSiloOutstandingIdentifiers-BrDmPbvc.mjs} +2 -2
- package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DuPDOm6f.mjs.map → pullChunkedCustomSiloOutstandingIdentifiers-BrDmPbvc.mjs.map} +1 -1
- package/dist/{pullConsentManagerMetrics-Dk5IejaY.mjs → pullConsentManagerMetrics-Cvex-gpd.mjs} +2 -2
- package/dist/{pullConsentManagerMetrics-Dk5IejaY.mjs.map → pullConsentManagerMetrics-Cvex-gpd.mjs.map} +1 -1
- package/dist/{pullManualEnrichmentIdentifiersToCsv-CAD1navz.mjs → pullManualEnrichmentIdentifiersToCsv-qjdNA75O.mjs} +2 -2
- package/dist/{pullManualEnrichmentIdentifiersToCsv-CAD1navz.mjs.map → pullManualEnrichmentIdentifiersToCsv-qjdNA75O.mjs.map} +1 -1
- package/dist/{pullTranscendConfiguration-NcFjHE8k.mjs → pullTranscendConfiguration-CD7Frn8z.mjs} +2 -2
- package/dist/{pullTranscendConfiguration-NcFjHE8k.mjs.map → pullTranscendConfiguration-CD7Frn8z.mjs.map} +1 -1
- package/dist/{pullUnstructuredSubDataPointRecommendations-Cf4irXl4.mjs → pullUnstructuredSubDataPointRecommendations-Dmh_-r95.mjs} +2 -2
- package/dist/{pullUnstructuredSubDataPointRecommendations-Cf4irXl4.mjs.map → pullUnstructuredSubDataPointRecommendations-Dmh_-r95.mjs.map} +1 -1
- package/dist/{pushCronIdentifiersFromCsv-BGaJycWo.mjs → pushCronIdentifiersFromCsv-DjeY7TtK.mjs} +2 -2
- package/dist/{pushCronIdentifiersFromCsv-BGaJycWo.mjs.map → pushCronIdentifiersFromCsv-DjeY7TtK.mjs.map} +1 -1
- package/dist/{pushManualEnrichmentIdentifiersFromCsv-B9okRkhL.mjs → pushManualEnrichmentIdentifiersFromCsv-BDtMo13j.mjs} +2 -2
- package/dist/{pushManualEnrichmentIdentifiersFromCsv-B9okRkhL.mjs.map → pushManualEnrichmentIdentifiersFromCsv-BDtMo13j.mjs.map} +1 -1
- package/dist/{readCsv-CmRYYFJc.mjs → readCsv-Cr_1t8F5.mjs} +1 -1
- package/dist/{readCsv-CmRYYFJc.mjs.map → readCsv-Cr_1t8F5.mjs.map} +1 -1
- package/dist/{readTranscendYaml-DtJz9mor.mjs → readTranscendYaml-STYnRzq-.mjs} +3 -3
- package/dist/readTranscendYaml-STYnRzq-.mjs.map +1 -0
- package/dist/{removeUnverifiedRequestIdentifiers-DJhxiy_D.mjs → removeUnverifiedRequestIdentifiers-B9uPApVR.mjs} +2 -2
- package/dist/{removeUnverifiedRequestIdentifiers-DJhxiy_D.mjs.map → removeUnverifiedRequestIdentifiers-B9uPApVR.mjs.map} +1 -1
- package/dist/{renderTable-DKXvortp.mjs → renderTable-DKTZbMjY.mjs} +1 -1
- package/dist/{renderTable-DKXvortp.mjs.map → renderTable-DKTZbMjY.mjs.map} +1 -1
- package/dist/{request-CCCU2SdV.mjs → request-B1yRTdG-.mjs} +1 -1
- package/dist/{request-CCCU2SdV.mjs.map → request-B1yRTdG-.mjs.map} +1 -1
- package/dist/{resolveBundleIdByName-By5Vl8Iz.mjs → resolveBundleIdByName-DuxWYrDF.mjs} +2 -2
- package/dist/{resolveBundleIdByName-By5Vl8Iz.mjs.map → resolveBundleIdByName-DuxWYrDF.mjs.map} +1 -1
- package/dist/{resolvePolicyBundleVersion-DO7IvEwb.mjs → resolvePolicyBundleVersion-DhXZKwb4.mjs} +2 -2
- package/dist/{resolvePolicyBundleVersion-DO7IvEwb.mjs.map → resolvePolicyBundleVersion-DhXZKwb4.mjs.map} +1 -1
- package/dist/{retryRequestDataSilos-6JfyZclD.mjs → retryRequestDataSilos-D9dBtbdl.mjs} +2 -2
- package/dist/{retryRequestDataSilos-6JfyZclD.mjs.map → retryRequestDataSilos-D9dBtbdl.mjs.map} +1 -1
- package/dist/run-captured-process-CpZBh0lG.mjs +2 -0
- package/dist/run-captured-process-CpZBh0lG.mjs.map +1 -0
- package/dist/{runOpa-Q2r3qPsi.mjs → runOpa-CkKAwsvB.mjs} +2 -2
- package/dist/{runOpa-Q2r3qPsi.mjs.map → runOpa-CkKAwsvB.mjs.map} +1 -1
- package/dist/scaffold-planning-DMACXSnX.mjs +121 -0
- package/dist/scaffold-planning-DMACXSnX.mjs.map +1 -0
- package/dist/scaffold-templates-CsHM1y80.mjs +84 -0
- package/dist/scaffold-templates-CsHM1y80.mjs.map +1 -0
- package/dist/{skipPreflightJobs-xpbtJ3dK.mjs → skipPreflightJobs-Beia_iB6.mjs} +2 -2
- package/dist/{skipPreflightJobs-xpbtJ3dK.mjs.map → skipPreflightJobs-Beia_iB6.mjs.map} +1 -1
- package/dist/{skipRequestDataSilos-B7RA6I3Z.mjs → skipRequestDataSilos-BSNLacnR.mjs} +2 -2
- package/dist/{skipRequestDataSilos-B7RA6I3Z.mjs.map → skipRequestDataSilos-BSNLacnR.mjs.map} +1 -1
- package/dist/{streamPrivacyRequestsToCsv-DT31SP6w.mjs → streamPrivacyRequestsToCsv-Cksb9D49.mjs} +2 -2
- package/dist/{streamPrivacyRequestsToCsv-DT31SP6w.mjs.map → streamPrivacyRequestsToCsv-Cksb9D49.mjs.map} +1 -1
- package/dist/{syncCodePackages-ozt9aBXf.mjs → syncCodePackages-DsC1tE8B.mjs} +2 -2
- package/dist/{syncCodePackages-ozt9aBXf.mjs.map → syncCodePackages-DsC1tE8B.mjs.map} +1 -1
- package/dist/{updateConsentManagerVersionToLatest-CCzWEBEj.mjs → updateConsentManagerVersionToLatest-CaEkTnlL.mjs} +2 -2
- package/dist/{updateConsentManagerVersionToLatest-CCzWEBEj.mjs.map → updateConsentManagerVersionToLatest-CaEkTnlL.mjs.map} +1 -1
- package/dist/{uploadConsents-CDqFNWX_.mjs → uploadConsents-CNyuYx0o.mjs} +2 -2
- package/dist/{uploadConsents-CDqFNWX_.mjs.map → uploadConsents-CNyuYx0o.mjs.map} +1 -1
- package/dist/{uploadPrivacyRequestsFromCsv-CVpBFaYB.mjs → uploadPrivacyRequestsFromCsv-BdTQOZnF.mjs} +2 -2
- package/dist/{uploadPrivacyRequestsFromCsv-CVpBFaYB.mjs.map → uploadPrivacyRequestsFromCsv-BdTQOZnF.mjs.map} +1 -1
- package/dist/{validateTranscendAuth-3guZJPKH.mjs → validateTranscendAuth-CnHMn77m.mjs} +2 -2
- package/dist/{validateTranscendAuth-3guZJPKH.mjs.map → validateTranscendAuth-CnHMn77m.mjs.map} +1 -1
- package/dist/{writeCsv-rhlEBjYp.mjs → writeCsv-D46aDkGf.mjs} +1 -1
- package/dist/{writeCsv-rhlEBjYp.mjs.map → writeCsv-D46aDkGf.mjs.map} +1 -1
- package/package.json +10 -5
- package/dist/app-dIu3pdTM.mjs +0 -148
- package/dist/app-dIu3pdTM.mjs.map +0 -1
- package/dist/impl-BbWzTCSd2.mjs.map +0 -1
- package/dist/impl-BlFBTc6f.mjs.map +0 -1
- package/dist/impl-BqiclSRZ.mjs +0 -2
- package/dist/impl-BqiclSRZ.mjs.map +0 -1
- package/dist/impl-CZ-KvZE02.mjs.map +0 -1
- package/dist/impl-CclEOdC32.mjs +0 -2
- package/dist/impl-CclEOdC32.mjs.map +0 -1
- package/dist/impl-Cq5wI-kP.mjs.map +0 -1
- package/dist/impl-DG9RwTkA2.mjs.map +0 -1
- package/dist/impl-DQG3PiAW2.mjs.map +0 -1
- package/dist/impl-DbPLuDc22.mjs.map +0 -1
- package/dist/impl-DiyWNLJq.mjs.map +0 -1
- package/dist/impl-DlaWf-oS2.mjs.map +0 -1
- package/dist/impl-DzJG5HaX.mjs.map +0 -1
- package/dist/impl-GYM4LDKp2.mjs +0 -2
- package/dist/impl-GYM4LDKp2.mjs.map +0 -1
- package/dist/impl-GnxjtYL7.mjs.map +0 -1
- package/dist/impl-V_ffkzbd2.mjs.map +0 -1
- package/dist/impl-xRwX5ZLK.mjs.map +0 -1
- package/dist/logger-CB_h_zbj.mjs +0 -2
- package/dist/logger-CB_h_zbj.mjs.map +0 -1
- package/dist/parseVariablesFromString-cu5gPC3M.mjs +0 -2
- package/dist/parseVariablesFromString-cu5gPC3M.mjs.map +0 -1
- package/dist/readTranscendYaml-DtJz9mor.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-planning-DMACXSnX.mjs","names":["CUSTOM_FUNCTION_SKILL_MD","CUSTOM_FUNCTION_SETUP_REFERENCE_MD","CUSTOM_FUNCTION_WRITING_REFERENCE_MD","parse"],"sources":["../../../skills/transcend-custom-functions/references/setup.md","../../../skills/transcend-custom-functions/references/writing-custom-functions.md","../../../skills/transcend-custom-functions/SKILL.md","../src/lib/custom-functions/custom-function-skill.ts","../src/lib/custom-functions/ai-handoff.ts","../src/lib/custom-functions/scaffold-output.ts","../src/lib/scaffolding/project-plan.ts","../src/lib/custom-functions/scaffold-artifacts.ts","../src/lib/custom-functions/scaffold-config.ts","../src/lib/custom-functions/scaffold-planning.ts"],"sourcesContent":[null,null,null,"import CUSTOM_FUNCTION_SETUP_REFERENCE_MD from '../../../../../skills/transcend-custom-functions/references/setup.md';\nimport CUSTOM_FUNCTION_WRITING_REFERENCE_MD from '../../../../../skills/transcend-custom-functions/references/writing-custom-functions.md';\nimport CUSTOM_FUNCTION_SKILL_MD from '../../../../../skills/transcend-custom-functions/SKILL.md';\n\n/** Namespaced directory and frontmatter name for the installed Agent Skill. */\nexport const CUSTOM_FUNCTION_SKILL_NAME = 'transcend-custom-functions';\n\nexport {\n CUSTOM_FUNCTION_SETUP_REFERENCE_MD,\n CUSTOM_FUNCTION_SKILL_MD,\n CUSTOM_FUNCTION_WRITING_REFERENCE_MD,\n};\n\n/** One generated file in the installed Agent Skill. */\nexport interface CustomFunctionSkillFile {\n /** Path relative to the skill directory. */\n path: string;\n /** Complete Markdown contents. */\n contents: string;\n}\n\n/** Complete portable Agent Skill file set. */\nexport const CUSTOM_FUNCTION_SKILL_FILES: readonly CustomFunctionSkillFile[] = [\n { path: 'SKILL.md', contents: CUSTOM_FUNCTION_SKILL_MD },\n { path: 'references/setup.md', contents: CUSTOM_FUNCTION_SETUP_REFERENCE_MD },\n {\n path: 'references/writing-custom-functions.md',\n contents: CUSTOM_FUNCTION_WRITING_REFERENCE_MD,\n },\n];\n","import { CUSTOM_FUNCTION_SKILL_NAME } from './custom-function-skill.js';\nimport { SUPPORTED_DENO_MAJOR_VERSION } from './deno-runtime.js';\nimport {\n buildCustomFunctionProjectArguments,\n buildPlaceholderVariablesArgument,\n quoteCliArgument,\n} from './paths.js';\n\n/**\n * Build a compact AI handoff after project initialization.\n *\n * @param options - Generated setup context\n * @returns Copyable agent instruction\n */\nexport function buildInitAiHandoff(options: {\n /** Display path to the Custom Function project. */\n targetDirectory: string;\n /** Display path to the manifest. */\n manifestPath: string;\n /** Exact CLI version used by generated CI. */\n cliVersion: string;\n /** Whether the authoring skill was installed. */\n hasSkill: boolean;\n /** Whether a GitHub Actions workflow was generated. */\n hasGithubWorkflow: boolean;\n}): string {\n const prefix = options.hasSkill\n ? `Use the \\`${CUSTOM_FUNCTION_SKILL_NAME}\\` skill to`\n : 'Ask your coding agent to';\n const checkCommand = `transcend custom-functions check ${buildCustomFunctionProjectArguments(\n options.targetDirectory,\n options.manifestPath,\n )} --noInteractive`;\n if (options.hasGithubWorkflow) {\n return `${prefix} review the Custom Function setup in \\`${options.targetDirectory}\\`, adapt the generated GitHub Actions workflow to this repository's conventions, and run \\`${checkCommand}\\`.`;\n }\n return `${prefix} review the Custom Function setup in \\`${options.targetDirectory}\\` and add equivalent CI for this repository: install Deno ${SUPPORTED_DENO_MAJOR_VERSION} and \\`@transcend-io/cli@${options.cliVersion}\\`, then run \\`${checkCommand}\\`.`;\n}\n\n/**\n * Build a compact AI handoff after adding one function.\n *\n * @param options - Generated function context\n * @returns Copyable agent instruction\n */\nexport function buildNewFunctionAiHandoff(options: {\n /** Customer-visible function name. */\n displayName: string;\n /** Display path to the generated source. */\n sourcePath: string;\n /** Display path to the Custom Function project. */\n targetDirectory: string;\n /** Display path to the selected manifest. */\n manifestPath: string;\n /** Whether the authoring skill is expected in this project. */\n hasSkill: boolean;\n /** Manifest variables required by this function. */\n variableNames: readonly string[];\n}): string {\n const prefix = options.hasSkill\n ? `Use the \\`${CUSTOM_FUNCTION_SKILL_NAME}\\` skill to`\n : 'Ask your coding agent to';\n const variablesArgument = buildPlaceholderVariablesArgument(options.variableNames);\n const checkCommand = `transcend custom-functions check ${buildCustomFunctionProjectArguments(\n options.targetDirectory,\n options.manifestPath,\n )}${variablesArgument}`;\n const runCommand =\n `transcend custom-functions run ` +\n `${buildCustomFunctionProjectArguments(options.targetDirectory, options.manifestPath)} ` +\n `--function=${quoteCliArgument(options.displayName)}${variablesArgument}`;\n return `${prefix} implement \\`${options.displayName}\\` in \\`${options.sourcePath}\\`, replace the example fixtures with realistic cases, run \\`${runCommand}\\` to exercise it locally, and finish with \\`${checkCommand}\\`.`;\n}\n","import colors from 'colors';\nimport { createTwoFilesPatch } from 'diff';\n\nimport type { PlannedChange } from '../scaffolding/project-plan.js';\nimport { displayCliPath } from './paths.js';\nimport type { CustomFunctionProjectPlan } from './scaffold-model.js';\n\n/** Stable public description of one planned mutation. */\nexport interface PublicPlannedChange {\n /** Create, merge, or link. */\n kind: 'create' | 'merge' | 'link';\n /** Display path. */\n target: string;\n /** Human-readable reason. */\n description: string;\n}\n\n/** Stable JSON scaffold result. */\nexport interface CustomFunctionPlanResult {\n /** JSON contract version. */\n version: number;\n /** Command that produced the plan. */\n command: 'init' | 'new';\n /** Whether the plan was applied. */\n applied: boolean;\n /** Whether this was a dry run. */\n dryRun: boolean;\n /** Absolute target directory. */\n targetDirectory: string;\n /** Absolute manifest path. */\n manifestPath: string;\n /** Planned mutations without file contents. */\n changes: PublicPlannedChange[];\n /** Non-fatal warnings. */\n warnings: string[];\n /** Suggested next commands. */\n nextSteps: string[];\n /** Compact prompt for continuing with a coding agent. */\n aiHandoff: string;\n}\n\n/**\n * Make a path relative for terminal output.\n *\n * @param cwd - Working directory\n * @param path - Absolute path\n * @returns Portable display path\n */\nexport function displayPath(cwd: string, path: string): string {\n return displayCliPath(cwd, path);\n}\n\n/**\n * Convert an internal mutation to its stable public shape.\n *\n * @param cwd - Working directory\n * @param change - Internal mutation\n * @returns Public mutation\n */\nfunction publicChange(cwd: string, change: PlannedChange): PublicPlannedChange {\n if (change.kind === 'link') {\n return {\n kind: 'link',\n target: `${displayPath(cwd, change.path)} -> ${change.target}`,\n description: change.description,\n };\n }\n return {\n kind: change.before === null ? 'create' : 'merge',\n target: displayPath(cwd, change.path),\n description: change.description,\n };\n}\n\n/**\n * Build the stable machine-readable scaffold result.\n *\n * @param plan - Project plan\n * @param options - Apply state\n * @returns JSON result\n */\nexport function buildPlanResult(\n plan: CustomFunctionProjectPlan,\n options: {\n /** Whether the plan was applied. */\n applied: boolean;\n /** Whether the invocation was a dry run. */\n dryRun: boolean;\n /** Working directory for portable paths. */\n cwd: string;\n /** Compact prompt for continuing with a coding agent. */\n aiHandoff: string;\n },\n): CustomFunctionPlanResult {\n return {\n version: plan.version,\n command: plan.command,\n applied: options.applied,\n dryRun: options.dryRun,\n targetDirectory: plan.targetDirectory,\n manifestPath: plan.manifestPath,\n changes: plan.changes.map((change) => publicChange(options.cwd, change)),\n warnings: plan.warnings,\n nextSteps: plan.nextSteps,\n aiHandoff: options.aiHandoff,\n };\n}\n\n/**\n * Render a human-readable complete project plan.\n *\n * @param plan - Project plan\n * @param cwd - Working directory\n * @returns Preview text\n */\nexport function renderProjectPlan(plan: CustomFunctionProjectPlan, cwd: string): string {\n const lines = [\n colors.bold('Custom Function plan'),\n ` ${colors.dim('Target:')} ${colors.cyan(displayPath(cwd, plan.targetDirectory))}`,\n ` ${colors.dim('Manifest:')} ${colors.cyan(displayPath(cwd, plan.manifestPath))}`,\n '',\n ];\n if (plan.changes.length === 0) {\n lines.push(colors.dim('No changes needed.'));\n } else {\n lines.push(colors.bold('Changes'));\n plan.changes.forEach((change) => {\n const item = publicChange(cwd, change);\n const kind = {\n create: colors.green(item.kind.padEnd(7)),\n merge: colors.cyan(item.kind.padEnd(7)),\n link: colors.magenta(item.kind.padEnd(7)),\n }[item.kind];\n lines.push(` ${kind} ${item.target}`);\n if (change.kind === 'file' && change.before !== null) {\n const path = displayPath(cwd, change.path);\n const patch = createTwoFilesPatch(path, path, change.before, change.after, '', '', {\n context: 3,\n });\n lines.push(\n ...patch\n .trimEnd()\n .split('\\n')\n .map((line) => ` ${line}`),\n );\n }\n });\n }\n if (plan.unchanged.length > 0) {\n lines.push('', colors.bold('Left unchanged'));\n plan.unchanged.forEach((path) => lines.push(` ${colors.dim(displayPath(cwd, path))}`));\n }\n if (plan.warnings.length > 0) {\n lines.push('', colors.bold(colors.yellow('Warnings')));\n plan.warnings.forEach((warning) => lines.push(` ${colors.yellow(warning)}`));\n }\n return `${lines.join('\\n')}\\n`;\n}\n","/** A file observed while collecting project state. */\nexport interface ProjectFileSnapshot {\n /** Absolute path. */\n path: string;\n /** Existing UTF-8 contents, or null when absent. */\n contents: string | null;\n /** Existing file mode, when present. */\n mode?: number;\n}\n\n/** A planned regular-file create or update. */\nexport interface PlannedFileChange {\n /** Discriminator. */\n kind: 'file';\n /** Absolute destination path. */\n path: string;\n /** Existing contents expected immediately before apply. */\n before: string | null;\n /** Complete desired contents. */\n after: string;\n /** Human-readable reason. */\n description: string;\n /** Existing file mode to preserve. */\n mode?: number;\n /** Whether an existing destination must always be rejected. */\n createOnly?: boolean;\n}\n\n/** A planned project-local symbolic link with a copy fallback. */\nexport interface PlannedLinkChange {\n /** Discriminator. */\n kind: 'link';\n /** Absolute link path. */\n path: string;\n /** Relative link target. */\n target: string;\n /** Managed files to copy when links are unavailable. */\n fallbackFiles: {\n /** Destination relative to the link path. */\n path: string;\n /** Complete desired contents. */\n contents: string;\n }[];\n /** Human-readable reason. */\n description: string;\n}\n\n/** One staged project mutation. */\nexport type PlannedChange = PlannedFileChange | PlannedLinkChange;\n\n/** Minimal plan consumed by the transactional filesystem applicator. */\nexport interface ProjectPlan {\n /** Approved physical root for every planned mutation. */\n rootDirectory: string;\n /** Ordered staged mutations. */\n changes: PlannedChange[];\n}\n\n/**\n * Stage a file only when its desired content differs.\n *\n * @param input - File change input\n * @returns A file change, or undefined for a no-op\n */\nexport function planFileChange(input: {\n /** File snapshot. */\n snapshot: ProjectFileSnapshot;\n /** Desired contents. */\n after: string;\n /** Human-readable reason. */\n description: string;\n /** Refuse every existing file. */\n createOnly?: boolean;\n}): PlannedFileChange | undefined {\n const { snapshot, after, description, createOnly } = input;\n if (createOnly && snapshot.contents !== null) {\n throw new Error(`Refusing to overwrite existing file: ${snapshot.path}`);\n }\n if (snapshot.contents === after) {\n return undefined;\n }\n return {\n kind: 'file',\n path: snapshot.path,\n before: snapshot.contents,\n after,\n description,\n ...(snapshot.mode === undefined ? {} : { mode: snapshot.mode }),\n ...(createOnly === undefined ? {} : { createOnly }),\n };\n}\n","import { createHash } from 'node:crypto';\n\nimport { RECOMMENDED_DENO_VERSION } from './deno-runtime.js';\n\n/** Immutable actions/checkout v6 release commit. */\nconst ACTIONS_CHECKOUT_SHA = 'd23441a48e516b6c34aea4fa41551a30e30af803';\n\n/** Immutable denoland/setup-deno v2.0.5 release commit. */\nconst SETUP_DENO_SHA = '22d081ff2d3a40755e97629de92e3bcbfa7cf2ed';\n\n/**\n * Escape a repository path before embedding intentional GitHub glob syntax.\n *\n * @param path - Literal repository-relative path\n * @returns Path with GitHub glob metacharacters escaped\n */\nfunction escapeGithubPath(path: string): string {\n return path.replace(/([!*?[\\]\\\\])/gu, '\\\\$1');\n}\n\n/**\n * Check whether a generated workflow still matches its ownership digest.\n *\n * @param contents - Existing workflow\n * @returns Whether automatic replacement is safe\n */\nexport function isUnmodifiedCustomFunctionWorkflow(contents: string): boolean {\n const marker = contents.match(\n /# managed-by: @transcend-io\\/cli; content-sha256: ([a-f0-9]{64})\\s*$/u,\n );\n if (!marker || marker.index === undefined) {\n return false;\n }\n const body = contents.slice(0, marker.index).trimEnd();\n return createHash('sha256').update(body).digest('hex') === marker[1];\n}\n\n/**\n * Generate the conservative Custom Function GitHub Actions workflow.\n *\n * @param options - Workflow paths and pinned CLI version\n * @returns Workflow YAML\n */\nexport function generateGithubActionsWorkflow(options: {\n /** Exact released CLI version. */\n cliVersion: string;\n /** Target directory relative to the repository root. */\n targetDirectory: string;\n /** Manifest directory where generated source, payload, and Deno files live. */\n contentDirectory?: string;\n /** Manifest path relative to the repository root. */\n manifestPath: string;\n /** Additional manifest-referenced files that should trigger validation. */\n watchedPaths?: readonly string[];\n}): string {\n const { cliVersion, targetDirectory, manifestPath } = options;\n const contentDirectory = options.contentDirectory ?? targetDirectory;\n const pathPrefix =\n contentDirectory === '.' ? '' : `${escapeGithubPath(contentDirectory.replace(/\\/+$/u, ''))}/`;\n const watchedPaths = [\n `${pathPrefix}functions/**`,\n `${pathPrefix}test-payloads/**`,\n escapeGithubPath(manifestPath),\n `${pathPrefix}deno.json*`,\n ...(options.watchedPaths ?? []).map(escapeGithubPath),\n ].filter((path, index, paths) => paths.indexOf(path) === index);\n const pathFilters = watchedPaths.map((path) => ` - ${JSON.stringify(path)}`).join('\\n');\n const body = `# Generated by transcend custom-functions init.\nname: Transcend Custom Functions\n\non:\n pull_request:\n paths:\n${pathFilters}\n push:\n paths:\n${pathFilters}\n\npermissions:\n contents: read\n\njobs:\n check:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@${ACTIONS_CHECKOUT_SHA} # v6\n - uses: denoland/setup-deno@${SETUP_DENO_SHA} # v2.0.5\n with:\n deno-version: ${RECOMMENDED_DENO_VERSION}\n - name: Install the Transcend CLI\n run: npm install --global @transcend-io/cli@${cliVersion}\n - name: Validate Custom Functions\n env:\n CUSTOM_FUNCTION_DIRECTORY: ${JSON.stringify(targetDirectory)}\n CUSTOM_FUNCTION_MANIFEST: ${JSON.stringify(manifestPath)}\n run: >-\n transcend custom-functions check \"$CUSTOM_FUNCTION_DIRECTORY\"\n --manifest=\"$CUSTOM_FUNCTION_MANIFEST\"\n --variables=TRANSCEND_API_KEY:placeholder\n --noInteractive\n --json\n`;\n const digest = createHash('sha256').update(body.trimEnd()).digest('hex');\n return `${body.trimEnd()}\\n\\n# managed-by: @transcend-io/cli; content-sha256: ${digest}\\n`;\n}\n","import { relative, sep } from 'node:path';\n\nimport { applyEdits, createScanner, modify, parse, type ParseError } from 'jsonc-parser';\n\nimport { buildCustomFunctionProjectArguments } from './paths.js';\n\n/**\n * Runtime values of jsonc-parser's ambient const enum, repeated locally\n * because isolated modules cannot access that enum directly.\n */\nconst JSONC_SYNTAX_KIND = {\n /** Closing object delimiter. */\n CloseBraceToken: 2,\n /** Closing array delimiter. */\n CloseBracketToken: 4,\n /** Single-line comment. */\n LineCommentTrivia: 12,\n /** Block comment. */\n BlockCommentTrivia: 13,\n /** Line break. */\n LineBreakTrivia: 14,\n /** Whitespace. */\n Trivia: 15,\n /** End of input. */\n EOF: 17,\n} as const;\n\n/** One JSONC path update. */\ninterface JsoncUpdate {\n /** Object path. */\n path: (string | number)[];\n /** Desired value. */\n value: unknown;\n}\n\n/**\n * Parse JSONC and fail with a manual-patch-oriented error.\n *\n * @param contents - JSON or JSONC text\n * @param label - Configuration label\n * @returns Parsed value\n */\nfunction parseJsonc(contents: string, label: string): Record<string, unknown> {\n const errors: ParseError[] = [];\n const value = parse(contents, errors, {\n allowTrailingComma: true,\n disallowComments: false,\n }) as unknown;\n if (errors.length > 0 || value === null || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(\n `Cannot safely merge ${label}; fix its JSONC syntax or apply the patch manually.`,\n );\n }\n return value as Record<string, unknown>;\n}\n\n/**\n * Infer formatting without reformatting existing content.\n *\n * @param contents - Existing JSONC\n * @returns Formatting settings\n */\nfunction inferFormatting(contents: string): {\n /** Number of spaces per indent. */\n tabSize: number;\n /** Whether indentation uses spaces. */\n insertSpaces: boolean;\n /** Existing line ending. */\n eol: string;\n} {\n const indent = contents.match(/\\n([ \\t]+)\"/)?.[1] ?? ' ';\n return {\n tabSize: indent.includes('\\t') ? 1 : indent.length,\n insertSpaces: !indent.includes('\\t'),\n eol: contents.includes('\\r\\n') ? '\\r\\n' : '\\n',\n };\n}\n\n/**\n * Capture comment text in order.\n *\n * @param contents - JSONC text\n * @returns Comments\n */\nfunction extractComments(contents: string): string[] {\n const scanner = createScanner(contents, false);\n const comments: string[] = [];\n let token = scanner.scan();\n while (token !== JSONC_SYNTAX_KIND.EOF) {\n if (\n token === JSONC_SYNTAX_KIND.LineCommentTrivia ||\n token === JSONC_SYNTAX_KIND.BlockCommentTrivia\n ) {\n comments.push(\n contents.slice(\n scanner.getTokenOffset(),\n scanner.getTokenOffset() + scanner.getTokenLength(),\n ),\n );\n }\n token = scanner.scan();\n }\n return comments;\n}\n\n/**\n * Detect the jsonc-parser insertion edge case that can reattach an inline\n * comment from the previous final property.\n *\n * @param contents - Existing JSONC text\n * @returns Whether a guarded manual patch is required\n */\nfunction hasTrailingPropertyComment(contents: string): boolean {\n const scanner = createScanner(contents, false);\n let previousSignificantLine = -1;\n let token = scanner.scan();\n while (token !== JSONC_SYNTAX_KIND.EOF) {\n if (token === JSONC_SYNTAX_KIND.LineCommentTrivia) {\n const hasInlineValue = previousSignificantLine === scanner.getTokenStartLine();\n let next = scanner.scan();\n while (\n next === JSONC_SYNTAX_KIND.Trivia ||\n next === JSONC_SYNTAX_KIND.LineBreakTrivia ||\n next === JSONC_SYNTAX_KIND.LineCommentTrivia ||\n next === JSONC_SYNTAX_KIND.BlockCommentTrivia\n ) {\n next = scanner.scan();\n }\n if (\n hasInlineValue &&\n (next === JSONC_SYNTAX_KIND.CloseBraceToken || next === JSONC_SYNTAX_KIND.CloseBracketToken)\n ) {\n return true;\n }\n token = next;\n continue;\n }\n if (\n token !== JSONC_SYNTAX_KIND.Trivia &&\n token !== JSONC_SYNTAX_KIND.LineBreakTrivia &&\n token !== JSONC_SYNTAX_KIND.BlockCommentTrivia\n ) {\n previousSignificantLine = scanner.getTokenStartLine();\n }\n token = scanner.scan();\n }\n return false;\n}\n\n/**\n * Apply surgical JSONC updates while retaining every existing comment.\n *\n * @param contents - Existing contents, or null for a new file\n * @param updates - Ordered updates\n * @param label - Configuration label\n * @returns Updated contents\n */\nexport function mergeJsonc(\n contents: string | null,\n updates: readonly JsoncUpdate[],\n label: string,\n): string {\n let next = contents ?? '{}\\n';\n parseJsonc(next, label);\n if (contents !== null && hasTrailingPropertyComment(contents)) {\n throw new Error(\n `Cannot prove comment-preserving edits for ${label}; apply the displayed patch manually.`,\n );\n }\n const originalComments = extractComments(next);\n const formattingOptions = inferFormatting(next);\n updates.forEach(({ path, value }) => {\n next = applyEdits(\n next,\n modify(next, path, value, {\n formattingOptions: {\n ...formattingOptions,\n insertFinalNewline: true,\n keepLines: true,\n },\n }),\n );\n });\n const finalComments = extractComments(next);\n if (\n originalComments.length !== finalComments.length ||\n originalComments.some((comment, index) => finalComments[index] !== comment)\n ) {\n throw new Error(`A comment would move or disappear in ${label}; apply the patch manually.`);\n }\n parseJsonc(next, label);\n return next.endsWith(formattingOptions.eol) ? next : `${next}${formattingOptions.eol}`;\n}\n\n/**\n * Merge unique string values without reordering existing entries.\n *\n * @param current - Existing value\n * @param additions - Values to append\n * @returns Merged strings\n */\nfunction mergeStringArray(current: unknown, additions: readonly string[]): string[] {\n const values = Array.isArray(current)\n ? current.filter((value): value is string => typeof value === 'string')\n : [];\n return [...values, ...additions.filter((value) => !values.includes(value))];\n}\n\n/**\n * Build target-scoped Deno authoring configuration.\n *\n * @param contents - Existing deno.json/jsonc contents\n * @param contractVersion - Exact authoring-contract version\n * @param manifestFileName - Manifest path relative to this Deno configuration\n * @param referencedPaths - Existing manifest-relative source and payload paths\n * @returns Merged configuration\n */\nexport function mergeDenoConfiguration(\n contents: string | null,\n contractVersion: string,\n manifestFileName = 'transcend-functions.yml',\n referencedPaths: {\n /** Existing source paths. */\n sources?: readonly string[];\n /** Existing payload paths. */\n payloads?: readonly string[];\n } = {},\n): string {\n const current = parseJsonc(contents ?? '{}\\n', 'Deno configuration');\n const compilerOptions =\n current.compilerOptions &&\n typeof current.compilerOptions === 'object' &&\n !Array.isArray(current.compilerOptions)\n ? (current.compilerOptions as Record<string, unknown>)\n : {};\n const lint =\n current.lint && typeof current.lint === 'object' && !Array.isArray(current.lint)\n ? (current.lint as Record<string, unknown>)\n : {};\n const fmt =\n current.fmt && typeof current.fmt === 'object' && !Array.isArray(current.fmt)\n ? (current.fmt as Record<string, unknown>)\n : {};\n const tasks =\n current.tasks && typeof current.tasks === 'object' && !Array.isArray(current.tasks)\n ? (current.tasks as Record<string, unknown>)\n : {};\n const taskName = 'custom-functions:check';\n const legacyTaskCommand =\n 'deno check functions/**/*.ts && deno lint functions/ && deno fmt --check functions/ test-payloads/';\n const previousTaskCommand = `transcend custom-functions check ${buildCustomFunctionProjectArguments(\n '.',\n manifestFileName,\n )} --noInteractive`;\n const taskCommand = `transcend custom-functions check ${buildCustomFunctionProjectArguments(\n '.',\n manifestFileName,\n )} --variables=TRANSCEND_API_KEY:placeholder --noInteractive`;\n if (\n tasks[taskName] !== undefined &&\n tasks[taskName] !== legacyTaskCommand &&\n tasks[taskName] !== previousTaskCommand &&\n tasks[taskName] !== taskCommand\n ) {\n throw new Error(\n `Deno task \"${taskName}\" already has a different command; apply the patch manually.`,\n );\n }\n const updates: JsoncUpdate[] = [\n {\n path: ['imports', '@transcend-io/custom-function-types'],\n value: `npm:@transcend-io/custom-function-types@${contractVersion}`,\n },\n { path: ['compilerOptions', 'strict'], value: true },\n {\n path: ['compilerOptions', 'lib'],\n value: mergeStringArray(compilerOptions.lib, ['deno.ns', 'dom', 'dom.iterable', 'esnext']),\n },\n {\n path: ['lint', 'include'],\n value: mergeStringArray(lint.include, ['functions/', ...(referencedPaths.sources ?? [])]),\n },\n {\n path: ['fmt', 'include'],\n value: mergeStringArray(fmt.include, [\n 'functions/',\n 'test-payloads/',\n manifestFileName,\n ...(referencedPaths.sources ?? []),\n ...(referencedPaths.payloads ?? []),\n ]),\n },\n { path: ['fmt', 'singleQuote'], value: true },\n { path: ['fmt', 'lineWidth'], value: 100 },\n { path: ['tasks', taskName], value: taskCommand },\n ];\n return mergeJsonc(contents, updates, 'Deno configuration');\n}\n\n/**\n * Merge VS Code-compatible Deno settings scoped to the target.\n *\n * @param contents - Existing settings JSONC\n * @param repositoryRoot - Repository root\n * @param targetDirectory - Custom Function project directory\n * @returns Merged settings\n */\nexport function mergeEditorSettings(\n contents: string | null,\n repositoryRoot: string,\n targetDirectory: string,\n): string {\n const current = parseJsonc(contents ?? '{}\\n', 'editor settings');\n const target = relative(repositoryRoot, targetDirectory).split(sep).join('/') || '.';\n return mergeJsonc(\n contents,\n [\n { path: ['deno.enable'], value: true },\n {\n path: ['deno.enablePaths'],\n value: mergeStringArray(current['deno.enablePaths'], [target]),\n },\n ],\n 'editor settings',\n );\n}\n\n/**\n * Merge the recommended Deno editor extension.\n *\n * @param contents - Existing extensions JSONC\n * @returns Merged extensions\n */\nexport function mergeEditorExtensions(contents: string | null): string {\n const current = parseJsonc(contents ?? '{}\\n', 'editor extension recommendations');\n return mergeJsonc(\n contents,\n [\n {\n path: ['recommendations'],\n value: mergeStringArray(current.recommendations, ['denoland.vscode-deno']),\n },\n ],\n 'editor extension recommendations',\n );\n}\n","import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nimport {\n buildManagedAgentSkill,\n isUnmodifiedManagedAgentSkill,\n resolveAgentSkillDirectories,\n} from '../scaffolding/agent-skill.js';\nimport {\n planFileChange,\n type PlannedLinkChange,\n type ProjectFileSnapshot,\n} from '../scaffolding/project-plan.js';\nimport {\n CUSTOM_FUNCTION_SKILL_FILES,\n CUSTOM_FUNCTION_SKILL_NAME,\n} from './custom-function-skill.js';\nimport { insertCustomFunctionManifestEntry, parseCustomFunctionsManifest } from './manifest.js';\nimport {\n buildCustomFunctionProjectArguments,\n buildPlaceholderVariablesArgument,\n displayCliPath,\n quoteCliArgument,\n} from './paths.js';\nimport {\n generateGithubActionsWorkflow,\n isUnmodifiedCustomFunctionWorkflow,\n} from './scaffold-artifacts.js';\nimport {\n mergeDenoConfiguration,\n mergeEditorExtensions,\n mergeEditorSettings,\n} from './scaffold-config.js';\nimport {\n CUSTOM_FUNCTION_RESULT_VERSION,\n CustomFunctionSetupFeature,\n type CustomFunctionProjectPlan,\n type CustomFunctionProjectState,\n type CustomFunctionSetupFeature as CustomFunctionSetupFeatureType,\n} from './scaffold-model.js';\nimport {\n generateCustomFunctionTemplate,\n type GeneratedCustomFunctionTemplate,\n} from './scaffold-templates.js';\n\n/** Empty manifest used for first initialization. */\nexport const EMPTY_CUSTOM_FUNCTION_MANIFEST = `# Custom Functions managed as code.\nfunctions: []\n`;\n\n/** State of one potentially mutated path. */\nexport type PlanningPathSnapshot =\n | {\n /** Snapshot kind. */\n kind: 'absent';\n /** Absolute path. */\n path: string;\n }\n | {\n /** Snapshot kind. */\n kind: 'file';\n /** Absolute path. */\n path: string;\n /** UTF-8 contents. */\n contents: string;\n /** File mode. */\n mode: number;\n }\n | {\n /** Snapshot kind. */\n kind: 'link';\n /** Absolute path. */\n path: string;\n /** Link target. */\n target: string;\n }\n | {\n /** Snapshot kind. */\n kind: 'directory';\n /** Absolute path. */\n path: string;\n };\n\n/** In-memory input consumed by pure project planners. */\nexport interface CustomFunctionPlanningInput {\n /** Project discovery state. */\n state: CustomFunctionProjectState;\n /** Potential mutation paths keyed by absolute path. */\n snapshots: Readonly<Record<string, PlanningPathSnapshot>>;\n}\n\n/**\n * Find parameter placeholders in a generated manifest value.\n *\n * @param value - Generated manifest entry\n * @returns Unique parameter names in encounter order\n */\nexport function parameterNamesInManifestValue(value: unknown): string[] {\n const serialized = JSON.stringify(value) ?? '';\n return [\n ...new Set(Array.from(serialized.matchAll(/<<parameters\\.([^>]+)>>/gu), (match) => match[1]!)),\n ];\n}\n\n/** In-memory input required only by project initialization. */\nexport interface CustomFunctionInitPlanningInput extends CustomFunctionPlanningInput {\n /** Exact authoring contract version. */\n contractVersion: string;\n /** Exact CLI version. */\n cliVersion: string;\n}\n\n/** Options shared by init and implicit init. */\nexport interface InitPlanOptions {\n /** Selected setup features. */\n features: readonly CustomFunctionSetupFeatureType[];\n}\n\n/**\n * Determine the repository-owned base for optional setup.\n *\n * @param state - Discovery state\n * @returns Repository root or target\n */\nfunction setupRoot(state: CustomFunctionProjectState): string {\n return state.projectRoot;\n}\n\n/**\n * Enumerate all paths that could be included in a plan.\n *\n * @param state - Discovery state\n * @param options - Selected setup and optional scaffold\n * @returns Absolute candidate paths\n */\nexport function getInitPlanningCandidatePaths(\n state: CustomFunctionProjectState,\n options: {\n /** Selected setup features. */\n features: readonly CustomFunctionSetupFeatureType[];\n },\n): string[] {\n const root = setupRoot(state);\n const paths = new Set<string>([state.manifestPath]);\n const selected = new Set(options.features);\n if (selected.has(CustomFunctionSetupFeature.Deno)) {\n paths.add(state.denoConfigPath);\n }\n if (selected.has(CustomFunctionSetupFeature.Editor)) {\n paths.add(join(root, '.vscode', 'settings.json'));\n paths.add(join(root, '.vscode', 'extensions.json'));\n }\n if (selected.has(CustomFunctionSetupFeature.Skill)) {\n const directories = resolveAgentSkillDirectories(state.existingSkillDirectories);\n CUSTOM_FUNCTION_SKILL_FILES.forEach(({ path }) => {\n paths.add(join(root, directories.canonical, CUSTOM_FUNCTION_SKILL_NAME, path));\n });\n directories.aliases.forEach((directory) => {\n const aliasDirectory = join(root, directory, CUSTOM_FUNCTION_SKILL_NAME);\n paths.add(aliasDirectory);\n CUSTOM_FUNCTION_SKILL_FILES.forEach(({ path }) => {\n paths.add(join(aliasDirectory, path));\n });\n });\n state.existingSkillDirectories.forEach(({ path: directory }) => {\n CUSTOM_FUNCTION_SKILL_FILES.forEach(({ path }) => {\n paths.add(join(root, directory, CUSTOM_FUNCTION_SKILL_NAME, path));\n });\n });\n }\n if (selected.has(CustomFunctionSetupFeature.Ci)) {\n paths.add(join(root, '.github', 'workflows', 'transcend-custom-functions.yml'));\n }\n return [...paths].sort((left, right) => left.localeCompare(right));\n}\n\n/**\n * Enumerate paths touched when adding one function to an initialized project.\n *\n * @param state - Project discovery state\n * @param generated - Generated function files\n * @returns Absolute candidate paths\n */\nexport function getAddFunctionPlanningCandidatePaths(\n state: CustomFunctionProjectState,\n generated: GeneratedCustomFunctionTemplate,\n): string[] {\n return [\n state.manifestPath,\n join(state.manifestDirectory, generated.sourceFile.path),\n ...generated.payloadFiles.map((file) => join(state.manifestDirectory, file.path)),\n ].sort((left, right) => left.localeCompare(right));\n}\n\n/**\n * Read a required candidate snapshot.\n *\n * @param input - Planning input\n * @param path - Absolute path\n * @returns Snapshot\n */\nfunction snapshotAt(input: CustomFunctionPlanningInput, path: string): PlanningPathSnapshot {\n const snapshot = input.snapshots[path];\n if (!snapshot) {\n throw new Error(`Missing preflight snapshot for ${path}`);\n }\n return snapshot;\n}\n\n/**\n * Adapt a generic snapshot to a regular-file plan input.\n *\n * @param input - Planning input\n * @param path - Absolute file path\n * @returns File snapshot\n */\nfunction fileSnapshotAt(input: CustomFunctionPlanningInput, path: string): ProjectFileSnapshot {\n const snapshot = snapshotAt(input, path);\n if (snapshot.kind === 'directory' || snapshot.kind === 'link') {\n throw new Error(`Expected a regular file or absent path: ${path}`);\n }\n return snapshot.kind === 'file'\n ? { path, contents: snapshot.contents, mode: snapshot.mode }\n : { path, contents: null };\n}\n\n/**\n * Ensure a mutation stays below an approved project root.\n *\n * @param root - Approved root\n * @param path - Candidate path\n */\nfunction assertContained(root: string, path: string): void {\n const child = relative(root, path);\n if (child === '..' || child.startsWith(`..${sep}`) || isAbsolute(child)) {\n throw new Error(`Refusing to modify path outside project root: ${path}`);\n }\n}\n\n/**\n * Validate roots and case-insensitive destinations.\n *\n * @param state - Discovery state\n * @param paths - Planned destination paths\n */\nfunction validatePlanDestinations(\n state: CustomFunctionProjectState,\n paths: readonly string[],\n): void {\n const root = setupRoot(state);\n assertContained(state.targetDirectory, state.manifestPath);\n paths.forEach((path) => assertContained(root, path));\n const existing = new Map(\n state.relativePaths.map((path) => [path.toLocaleLowerCase('en-US'), path]),\n );\n paths.forEach((path) => {\n const relativePath = relative(state.manifestDirectory, path).split(sep).join('/');\n if (relativePath.startsWith('../')) {\n return;\n }\n const components = relativePath.split('/');\n for (let length = 1; length <= components.length; length += 1) {\n const candidate = components.slice(0, length).join('/');\n const collision = existing.get(candidate.toLocaleLowerCase('en-US'));\n if (collision && collision !== candidate) {\n throw new Error(\n `Case-insensitive path collision: \"${candidate}\" conflicts with \"${collision}\".`,\n );\n }\n }\n });\n}\n\n/**\n * Add or classify a regular-file mutation.\n *\n * @param plan - Plan being assembled\n * @param input - Planning input\n * @param options - Desired file\n */\nfunction addFileChange(\n plan: CustomFunctionProjectPlan,\n input: CustomFunctionPlanningInput,\n options: {\n /** Absolute path. */\n path: string;\n /** Complete desired contents. */\n contents: string;\n /** Human-readable reason. */\n description: string;\n /** Refuse existing contents. */\n createOnly?: boolean;\n },\n): void {\n const change = planFileChange({\n snapshot: fileSnapshotAt(input, options.path),\n after: options.contents,\n description: options.description,\n ...(options.createOnly === undefined ? {} : { createOnly: options.createOnly }),\n });\n if (change) {\n plan.changes.push(change);\n } else {\n plan.unchanged.push(options.path);\n }\n}\n\n/**\n * Plan one direct skill installation and links into other existing directories.\n *\n * @param plan - Plan being assembled\n * @param input - Planning input\n */\nfunction planSkill(plan: CustomFunctionProjectPlan, input: CustomFunctionPlanningInput): void {\n const root = setupRoot(input.state);\n const directories = resolveAgentSkillDirectories(input.state.existingSkillDirectories);\n const canonicalDirectory = join(root, directories.canonical, CUSTOM_FUNCTION_SKILL_NAME);\n const managedFiles = CUSTOM_FUNCTION_SKILL_FILES.map((file) => ({\n path: file.path,\n contents: buildManagedAgentSkill(file.contents, '@transcend-io/cli'),\n }));\n const planManagedFiles = (directory: string, description: string): void => {\n managedFiles.forEach((file) => {\n const path = join(directory, file.path);\n const snapshot = fileSnapshotAt(input, path);\n if (\n snapshot.contents !== null &&\n snapshot.contents !== file.contents &&\n !isUnmodifiedManagedAgentSkill(snapshot.contents, '@transcend-io/cli')\n ) {\n throw new Error(\n `Refusing to replace user-managed skill: ${path}. Apply the skill update manually.`,\n );\n }\n addFileChange(plan, input, {\n path,\n contents: file.contents,\n description: `${description}: ${file.path}`,\n });\n });\n };\n planManagedFiles(canonicalDirectory, 'Install Custom Function skill file');\n\n directories.aliases.forEach((directory) => {\n const targetDirectory = join(root, directory, CUSTOM_FUNCTION_SKILL_NAME);\n const snapshot = snapshotAt(input, targetDirectory);\n const relativeTarget = relative(dirname(targetDirectory), canonicalDirectory);\n if (snapshot.kind === 'link' && snapshot.target === relativeTarget) {\n plan.unchanged.push(targetDirectory);\n return;\n }\n if (snapshot.kind === 'directory') {\n planManagedFiles(targetDirectory, `Update Custom Function skill copy in ${directory}`);\n return;\n }\n if (snapshot.kind !== 'absent') {\n throw new Error(`Refusing to replace unexpected skill target: ${targetDirectory}`);\n }\n const change: PlannedLinkChange = {\n kind: 'link',\n path: targetDirectory,\n target: relativeTarget,\n fallbackFiles: managedFiles,\n description: `Expose the canonical skill in ${directory}`,\n };\n plan.changes.push(change);\n });\n\n input.state.existingSkillDirectories\n .map(({ path }) => path)\n .filter(\n (directory) =>\n directory !== directories.canonical && !directories.aliases.includes(directory),\n )\n .forEach((directory) => {\n const existingDirectory = join(root, directory, CUSTOM_FUNCTION_SKILL_NAME);\n const skillSnapshot = fileSnapshotAt(input, join(existingDirectory, 'SKILL.md'));\n if (\n skillSnapshot.contents !== null &&\n isUnmodifiedManagedAgentSkill(skillSnapshot.contents, '@transcend-io/cli')\n ) {\n planManagedFiles(existingDirectory, `Update existing managed skill in ${directory}`);\n } else if (skillSnapshot.contents?.includes('managed-by: @transcend-io/cli')) {\n plan.unchanged.push(join(existingDirectory, 'SKILL.md'));\n plan.warnings.push(\n `Existing customized managed skill was left unchanged: ${existingDirectory}`,\n );\n }\n });\n}\n\n/**\n * Create the base plan and optional repository setup.\n *\n * @param input - Collected project state\n * @param options - Selected setup\n * @returns Validated project plan\n */\nexport function buildInitPlan(\n input: CustomFunctionInitPlanningInput,\n options: InitPlanOptions,\n): CustomFunctionProjectPlan {\n const { state } = input;\n const projectArguments = buildCustomFunctionProjectArguments(\n state.targetDirectory,\n state.manifestPath,\n state.invocationDirectory,\n );\n const plan: CustomFunctionProjectPlan = {\n version: CUSTOM_FUNCTION_RESULT_VERSION,\n command: 'init',\n rootDirectory: setupRoot(state),\n targetDirectory: state.targetDirectory,\n manifestPath: state.manifestPath,\n changes: [],\n unchanged: [],\n warnings: [],\n nextSteps: [\n `transcend custom-functions new ${projectArguments}`,\n `transcend custom-functions check ${projectArguments}`,\n ],\n };\n const manifestSnapshot = fileSnapshotAt(input, state.manifestPath);\n const existingManifest =\n manifestSnapshot.contents === null\n ? undefined\n : parseCustomFunctionsManifest(manifestSnapshot.contents);\n if (manifestSnapshot.contents === null) {\n addFileChange(plan, input, {\n path: state.manifestPath,\n contents: EMPTY_CUSTOM_FUNCTION_MANIFEST,\n description: 'Create the Custom Function manifest',\n });\n } else {\n plan.unchanged.push(state.manifestPath);\n }\n\n const selected = new Set(options.features);\n if (selected.has(CustomFunctionSetupFeature.Deno)) {\n const snapshot = fileSnapshotAt(input, state.denoConfigPath);\n const sourcePaths = (existingManifest?.functions ?? [])\n .map(({ code }) => code)\n .filter((path) => !path.includes('<<parameters.'));\n const payloadPaths = (existingManifest?.functions ?? [])\n .flatMap((entry) => [\n ...(entry['test-payload'] ? [entry['test-payload']] : []),\n ...(entry['test-payloads'] ?? []).map(({ payload }) => payload),\n ])\n .filter((path) => !path.includes('<<parameters.'));\n addFileChange(plan, input, {\n path: state.denoConfigPath,\n contents: mergeDenoConfiguration(\n snapshot.contents,\n input.contractVersion,\n basename(state.manifestPath),\n { sources: sourcePaths, payloads: payloadPaths },\n ),\n description: 'Merge strict target-scoped Deno authoring configuration',\n });\n }\n\n const root = setupRoot(state);\n if (selected.has(CustomFunctionSetupFeature.Editor)) {\n const settingsPath = join(root, '.vscode', 'settings.json');\n const extensionsPath = join(root, '.vscode', 'extensions.json');\n const settingsSnapshot = fileSnapshotAt(input, settingsPath);\n const extensionsSnapshot = fileSnapshotAt(input, extensionsPath);\n addFileChange(plan, input, {\n path: settingsPath,\n contents: mergeEditorSettings(settingsSnapshot.contents, root, state.targetDirectory),\n description: 'Scope Deno language support to Custom Functions',\n });\n addFileChange(plan, input, {\n path: extensionsPath,\n contents: mergeEditorExtensions(extensionsSnapshot.contents),\n description: 'Recommend the Deno editor extension',\n });\n }\n\n if (selected.has(CustomFunctionSetupFeature.Skill)) {\n planSkill(plan, input);\n }\n if (selected.has(CustomFunctionSetupFeature.Ci)) {\n if (!state.repositoryRoot || !state.usesGithub) {\n plan.warnings.push(\n 'GitHub Actions setup was selected, but this target is not inside a detected GitHub repository.',\n );\n } else {\n const workflowPath = join(\n state.repositoryRoot,\n '.github',\n 'workflows',\n 'transcend-custom-functions.yml',\n );\n const target =\n relative(state.repositoryRoot, state.targetDirectory).split(sep).join('/') || '.';\n const contentDirectory =\n relative(state.repositoryRoot, state.manifestDirectory).split(sep).join('/') || '.';\n const manifest = relative(state.repositoryRoot, state.manifestPath).split(sep).join('/');\n const watchedPaths = (existingManifest?.functions ?? [])\n .flatMap((entry) => [\n entry.code,\n ...(entry['test-payload'] ? [entry['test-payload']] : []),\n ...(entry['test-payloads'] ?? []).map(({ payload }) => payload),\n ])\n .filter((path) => !path.includes('<<parameters.'))\n .map((path) =>\n relative(state.repositoryRoot!, resolve(state.manifestDirectory, path))\n .split(sep)\n .join('/'),\n );\n const workflowContents = generateGithubActionsWorkflow({\n cliVersion: input.cliVersion,\n targetDirectory: target,\n contentDirectory,\n manifestPath: manifest,\n watchedPaths,\n });\n const workflowSnapshot = fileSnapshotAt(input, workflowPath);\n if (workflowSnapshot.contents === null) {\n addFileChange(plan, input, {\n path: workflowPath,\n contents: workflowContents,\n description: 'Add credential-free Custom Function checks',\n createOnly: true,\n });\n } else if (workflowSnapshot.contents === workflowContents) {\n plan.unchanged.push(workflowPath);\n } else if (isUnmodifiedCustomFunctionWorkflow(workflowSnapshot.contents)) {\n addFileChange(plan, input, {\n path: workflowPath,\n contents: workflowContents,\n description: 'Update managed Custom Function checks',\n });\n } else {\n plan.unchanged.push(workflowPath);\n plan.warnings.push(`Existing GitHub Actions workflow was left unchanged: ${workflowPath}`);\n }\n }\n }\n validatePlanDestinations(\n state,\n plan.changes.map(({ path }) => path),\n );\n return plan;\n}\n\n/**\n * Add a new function to an initialized project in one transaction.\n *\n * @param input - Collected project state\n * @param options - Generated function\n * @returns Validated project plan\n */\nexport function buildAddFunctionPlan(\n input: CustomFunctionPlanningInput,\n options: {\n /** Generated function scaffold. */\n generated: GeneratedCustomFunctionTemplate;\n },\n): CustomFunctionProjectPlan {\n const { state } = input;\n const currentManifest = fileSnapshotAt(input, state.manifestPath).contents;\n if (currentManifest === null) {\n throw new Error(`Custom Function manifest does not exist: ${state.manifestPath}`);\n }\n const sourceRelativePath = options.generated.sourceFile.path.split(sep).join('/');\n const sourceCollision = state.relativePaths.find(\n (path) => path.toLocaleLowerCase('en-US') === sourceRelativePath.toLocaleLowerCase('en-US'),\n );\n if (sourceCollision) {\n throw new Error(\n `Custom Function name \"${options.generated.displayName}\" maps to ${sourceRelativePath}, ` +\n `which conflicts with existing path ${sourceCollision}. Choose another name.`,\n );\n }\n const existingSourceEntry = parseCustomFunctionsManifest(currentManifest).functions.find(\n (entry) =>\n resolve(state.manifestDirectory, entry.code) ===\n resolve(state.manifestDirectory, sourceRelativePath),\n );\n if (existingSourceEntry) {\n throw new Error(\n `Custom Function name \"${options.generated.displayName}\" maps to source used by ` +\n `\"${existingSourceEntry.name}\". Choose another name.`,\n );\n }\n const plan: CustomFunctionProjectPlan = {\n version: CUSTOM_FUNCTION_RESULT_VERSION,\n command: 'new',\n rootDirectory: state.manifestDirectory,\n targetDirectory: state.targetDirectory,\n manifestPath: state.manifestPath,\n changes: [],\n unchanged: [],\n warnings: [],\n nextSteps: [],\n };\n const updatedManifest = insertCustomFunctionManifestEntry(\n currentManifest,\n options.generated.manifestEntry,\n );\n addFileChange(plan, input, {\n path: state.manifestPath,\n contents: updatedManifest,\n description: `Register ${options.generated.displayName} in the manifest`,\n });\n\n const generatedFiles = [options.generated.sourceFile, ...options.generated.payloadFiles];\n generatedFiles.forEach((file) => {\n addFileChange(plan, input, {\n path: join(state.manifestDirectory, file.path),\n contents: file.contents,\n description:\n file === options.generated.sourceFile\n ? `Create ${options.generated.displayName} source`\n : `Create ${options.generated.displayName} test payload`,\n createOnly: true,\n });\n });\n const sourcePath = join(state.manifestDirectory, options.generated.sourceFile.path);\n const projectArguments = buildCustomFunctionProjectArguments(\n state.targetDirectory,\n state.manifestPath,\n state.invocationDirectory,\n );\n const variableNames = parameterNamesInManifestValue(options.generated.manifestEntry);\n const variablesArgument = buildPlaceholderVariablesArgument(variableNames);\n plan.nextSteps = [\n `Edit ${quoteCliArgument(displayCliPath(state.invocationDirectory, sourcePath))}`,\n `transcend custom-functions run ${projectArguments} --function=${quoteCliArgument(\n options.generated.displayName,\n )}${variablesArgument}`,\n `transcend custom-functions check ${projectArguments}${variablesArgument}`,\n `transcend custom-functions push --file=${quoteCliArgument(\n displayCliPath(state.invocationDirectory, state.manifestPath),\n )} --dryRun${variablesArgument}`,\n ];\n if (variableNames.length > 0) {\n plan.warnings.push(\n `Supply ${variableNames.join(', ')} through --variables when running or pushing; never commit secret values.`,\n );\n }\n validatePlanDestinations(\n state,\n plan.changes.map(({ path }) => path),\n );\n return plan;\n}\n\n/**\n * Generate the template before collecting its destination snapshots.\n *\n * @param displayName - Custom Function display name\n * @param template - Template name\n * @returns Pure template output\n */\nexport function prepareGeneratedCustomFunction(\n displayName: string,\n template: Parameters<typeof generateCustomFunctionTemplate>[1],\n): GeneratedCustomFunctionTemplate {\n return generateCustomFunctionTemplate(displayName, template);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
const e=[`general`,`dsr-datapoint`,`dsr-enricher`,`dsr-both`],t=/^(?:con|prn|aux|nul|clock\$|com[1-9]|lpt[1-9])$/i;function n(e){let t=o(a(e));return s(t),t}function r(e){let t=a(e);return s(o(t)),t}function i(e,t){let i=r(e),a=n(i),o=`functions/${a}.ts`,s={path:o,contents:u(t)};if(t===`general`){let e=l(`test-payloads/${a}.json`,p());return{displayName:i,slug:a,sourceFile:s,payloadFiles:[e],manifestEntry:{name:i,code:c(o),"test-payload":c(e.path)}}}let d=[];(t===`dsr-datapoint`||t===`dsr-both`)&&d.push(l(`test-payloads/${a}-datapoint.json`,m())),(t===`dsr-enricher`||t===`dsr-both`)&&d.push(l(`test-payloads/${a}-enricher.json`,h()));let f={name:i,code:c(o),type:`DSR`,env:{TRANSCEND_API_KEY:`<<parameters.TRANSCEND_API_KEY>>`}};if(t===`dsr-datapoint`)f[`test-payload`]=c(d[0].path),f[`test-payload-type`]=`DATA_POINT`;else if(t===`dsr-enricher`)f[`test-payload`]=c(d[0].path),f[`test-payload-type`]=`REQUEST_ENRICHER`;else if(t===`dsr-both`)f[`test-payloads`]=[{payload:c(d[0].path),"payload-type":`DATA_POINT`},{payload:c(d[1].path),"payload-type":`REQUEST_ENRICHER`}];else throw Error(`Unsupported Custom Function template: ${String(t)}`);return{displayName:i,slug:a,sourceFile:s,payloadFiles:d,manifestEntry:f}}function a(e){if(/[\u0000-\u001f\u007f]/u.test(e))throw Error(`Custom Function name cannot contain control characters.`);if(/[\\/]/u.test(e))throw Error(`Custom Function name cannot contain path separators.`);let t=e.trim().replace(/\s+/gu,` `);if(!t)throw Error(`Custom Function name cannot be empty.`);return t}function o(e){return e.normalize(`NFKD`).replace(/\p{Mark}/gu,``).replace(/([A-Z]{2,})([A-Z][a-z])/g,`$1-$2`).replace(/([a-z\d])([A-Z])/g,`$1-$2`).replace(/['’]/gu,``).toLowerCase().replace(/[^a-z0-9]+/gu,`-`).replace(/^-+|-+$/gu,``)}function s(e){if(!e)throw Error(`Custom Function name must contain a letter or number.`);if(e.length>80)throw Error(`Custom Function slug cannot exceed 80 characters.`);if(t.test(e))throw Error(`Custom Function name resolves to reserved filename "${e}".`)}function c(e){return`./${e}`}function l(e,t){return{path:e,contents:`${JSON.stringify(t,null,2)}\n`}}function u(e){let t=`import type { CustomFunction } from '@transcend-io/custom-function-types';`;switch(e){case`general`:return`${t}
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handle a General Custom Function invocation.
|
|
5
|
+
*
|
|
6
|
+
* @param argument - Services and payload supplied by Transcend
|
|
7
|
+
*/
|
|
8
|
+
export default function customFunction({
|
|
9
|
+
payload,
|
|
10
|
+
}: CustomFunction.GeneralArgument): void {
|
|
11
|
+
// TODO: Handle the webhook or schedule payload.
|
|
12
|
+
void payload;
|
|
13
|
+
}
|
|
14
|
+
`;case`dsr-datapoint`:return`${t}
|
|
15
|
+
|
|
16
|
+
${d()}
|
|
17
|
+
`;case`dsr-enricher`:return`${t}
|
|
18
|
+
|
|
19
|
+
${f()}
|
|
20
|
+
`;case`dsr-both`:return`${t}
|
|
21
|
+
|
|
22
|
+
${d()}
|
|
23
|
+
|
|
24
|
+
${f()}
|
|
25
|
+
`;default:throw Error(`Unsupported Custom Function template: ${String(e)}`)}}function d(){return`/**
|
|
26
|
+
* Resolve a data subject request datapoint.
|
|
27
|
+
*
|
|
28
|
+
* @param argument - Services and payload supplied by Transcend
|
|
29
|
+
*/
|
|
30
|
+
export default async function customFunction({
|
|
31
|
+
environment,
|
|
32
|
+
payload,
|
|
33
|
+
sdk,
|
|
34
|
+
}: CustomFunction.Argument): Promise<void> {
|
|
35
|
+
if (payload.type !== 'ACCESS') {
|
|
36
|
+
throw new Error(\`Unsupported request type: \${payload.type}\`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// TODO: Fetch the matching record from your customer API.
|
|
40
|
+
// TODO: Map that record into the profiles array.
|
|
41
|
+
const profiles: unknown[] = [];
|
|
42
|
+
const response = await sdk.fetch('/v1/data-silo', {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: \`Bearer \${environment.TRANSCEND_API_KEY}\`,
|
|
46
|
+
'Content-Type': 'application/json',
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify({ profiles, status: 'READY' }),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (!response.ok) {
|
|
52
|
+
const body = await response.text();
|
|
53
|
+
throw new Error(
|
|
54
|
+
\`Failed to report ACCESS results: status=\${response.status} statusText=\${response.statusText} body=\${body}\`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}`}function f(){return`/**
|
|
58
|
+
* Enrich identifiers before a data subject request runs.
|
|
59
|
+
*
|
|
60
|
+
* @param argument - Services and payload supplied by Transcend
|
|
61
|
+
*/
|
|
62
|
+
export async function enricher({
|
|
63
|
+
environment,
|
|
64
|
+
sdk,
|
|
65
|
+
}: CustomFunction.EnricherArgument): Promise<void> {
|
|
66
|
+
// TODO: Query your customer API and map discovered identifiers here.
|
|
67
|
+
const enrichedIdentifiers = {};
|
|
68
|
+
const response = await sdk.fetch('/v1/enrich-identifiers', {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: \`Bearer \${environment.TRANSCEND_API_KEY}\`,
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
},
|
|
74
|
+
body: JSON.stringify({ enrichedIdentifiers }),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
if (!response.ok) {
|
|
78
|
+
const body = await response.text();
|
|
79
|
+
throw new Error(
|
|
80
|
+
\`Failed to report enriched identifiers: status=\${response.status} statusText=\${response.statusText} body=\${body}\`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}`}function p(){return{event:`example`}}function m(){return{type:`ACCESS`,dataSubject:{type:`customer`},isTest:!0,extras:{profile:{id:`00000000-0000-4000-8000-000000000001`,RequestDataSiloId:`00000000-0000-4000-8000-000000000002`,identifier:`person@example.com`,type:`email`},request:g(),organization:_()}}}function h(){return{type:`ACCESS`,dataSubject:{type:`customer`},isTest:!0,requestIdentifier:{name:`email`,value:`person@example.com`},extras:{enricher:{id:`00000000-0000-4000-8000-000000000003`,title:`Email enrichment`},identifier:{id:`00000000-0000-4000-8000-000000000004`,name:`email`,type:`email`},requestEnricherId:`00000000-0000-4000-8000-000000000005`,request:g(),organization:_()}}}function g(){return{details:``,id:`00000000-0000-4000-8000-000000000006`,link:`/requests/00000000-0000-4000-8000-000000000006`,createdAt:`2026-01-01T00:00:00.000Z`,locale:`en-US`,origin:`API`}}function _(){return{id:`00000000-0000-4000-8000-000000000007`,uri:`example`,name:`Example Organization`}}export{i as n,r,e as t};
|
|
84
|
+
//# sourceMappingURL=scaffold-templates-CsHM1y80.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold-templates-CsHM1y80.mjs","names":[],"sources":["../src/lib/custom-functions/scaffold-templates.ts"],"sourcesContent":["import type { CustomFunctionManifestEntry } from './manifest.js';\n\n/** Supported Custom Function scaffold templates. */\nexport const CUSTOM_FUNCTION_TEMPLATE_NAMES = [\n 'general',\n 'dsr-datapoint',\n 'dsr-enricher',\n 'dsr-both',\n] as const;\n\n/** A supported Custom Function scaffold template. */\nexport type CustomFunctionTemplateName = (typeof CUSTOM_FUNCTION_TEMPLATE_NAMES)[number];\n\n/** One generated, manifest-relative scaffold file. */\nexport interface GeneratedCustomFunctionFile {\n /** Path relative to the Custom Function manifest. */\n path: string;\n /** Complete deterministic file contents. */\n contents: string;\n}\n\n/** Pure output used to plan a Custom Function scaffold. */\nexport interface GeneratedCustomFunctionTemplate {\n /** Normalized display name retained in the manifest. */\n displayName: string;\n /** Filesystem-safe name used by source and payload files. */\n slug: string;\n /** Generated TypeScript source file. */\n sourceFile: GeneratedCustomFunctionFile;\n /** Generated JSON test payload files. */\n payloadFiles: GeneratedCustomFunctionFile[];\n /** Manifest entry matching the generated source and payloads. */\n manifestEntry: CustomFunctionManifestEntry;\n}\n\nconst MAX_SLUG_LENGTH = 80;\nconst WINDOWS_RESERVED_FILENAME = /^(?:con|prn|aux|nul|clock\\$|com[1-9]|lpt[1-9])$/i;\n\n/**\n * Convert a display name to an ASCII kebab-case filename stem.\n *\n * @param displayName - Human-readable Custom Function name\n * @returns Filesystem-safe kebab-case slug\n */\nexport function deriveCustomFunctionSlug(displayName: string): string {\n const normalizedName = normalizeAndValidateDisplayName(displayName);\n const slug = slugify(normalizedName);\n validateSlug(slug);\n return slug;\n}\n\n/**\n * Validate and normalize a customer-visible Custom Function name.\n *\n * @param displayName - Proposed human-readable name\n * @returns Trimmed name with internal whitespace normalized\n */\nexport function validateCustomFunctionDisplayName(displayName: string): string {\n const normalizedName = normalizeAndValidateDisplayName(displayName);\n validateSlug(slugify(normalizedName));\n return normalizedName;\n}\n\n/**\n * Generate source, test payloads, and matching manifest data without I/O.\n *\n * @param displayName - Human-readable Custom Function name\n * @param templateName - Handler shape to generate\n * @returns Deterministic scaffold contents\n */\nexport function generateCustomFunctionTemplate(\n displayName: string,\n templateName: CustomFunctionTemplateName,\n): GeneratedCustomFunctionTemplate {\n const normalizedName = validateCustomFunctionDisplayName(displayName);\n const slug = deriveCustomFunctionSlug(normalizedName);\n const sourcePath = `functions/${slug}.ts`;\n const sourceFile = {\n path: sourcePath,\n contents: buildSource(templateName),\n };\n\n if (templateName === 'general') {\n const payloadFile = buildPayloadFile(`test-payloads/${slug}.json`, buildGeneralPayload());\n return {\n displayName: normalizedName,\n slug,\n sourceFile,\n payloadFiles: [payloadFile],\n manifestEntry: {\n name: normalizedName,\n code: toManifestPath(sourcePath),\n 'test-payload': toManifestPath(payloadFile.path),\n },\n };\n }\n\n const payloadFiles: GeneratedCustomFunctionFile[] = [];\n if (templateName === 'dsr-datapoint' || templateName === 'dsr-both') {\n payloadFiles.push(\n buildPayloadFile(`test-payloads/${slug}-datapoint.json`, buildDataPointPayload()),\n );\n }\n if (templateName === 'dsr-enricher' || templateName === 'dsr-both') {\n payloadFiles.push(\n buildPayloadFile(`test-payloads/${slug}-enricher.json`, buildEnricherPayload()),\n );\n }\n\n const manifestEntry: CustomFunctionManifestEntry = {\n name: normalizedName,\n code: toManifestPath(sourcePath),\n type: 'DSR',\n env: {\n TRANSCEND_API_KEY: '<<parameters.TRANSCEND_API_KEY>>',\n },\n };\n\n if (templateName === 'dsr-datapoint') {\n manifestEntry['test-payload'] = toManifestPath(payloadFiles[0]!.path);\n manifestEntry['test-payload-type'] = 'DATA_POINT';\n } else if (templateName === 'dsr-enricher') {\n manifestEntry['test-payload'] = toManifestPath(payloadFiles[0]!.path);\n manifestEntry['test-payload-type'] = 'REQUEST_ENRICHER';\n } else if (templateName === 'dsr-both') {\n manifestEntry['test-payloads'] = [\n {\n payload: toManifestPath(payloadFiles[0]!.path),\n 'payload-type': 'DATA_POINT',\n },\n {\n payload: toManifestPath(payloadFiles[1]!.path),\n 'payload-type': 'REQUEST_ENRICHER',\n },\n ];\n } else {\n throw new Error(`Unsupported Custom Function template: ${String(templateName)}`);\n }\n\n return {\n displayName: normalizedName,\n slug,\n sourceFile,\n payloadFiles,\n manifestEntry,\n };\n}\n\n/**\n * Normalize the display name before validating its path-related properties.\n *\n * @param displayName - Proposed display name\n * @returns Normalized display name\n */\nfunction normalizeAndValidateDisplayName(displayName: string): string {\n if (/[\\u0000-\\u001f\\u007f]/u.test(displayName)) {\n throw new Error('Custom Function name cannot contain control characters.');\n }\n if (/[\\\\/]/u.test(displayName)) {\n throw new Error('Custom Function name cannot contain path separators.');\n }\n\n const normalizedName = displayName.trim().replace(/\\s+/gu, ' ');\n if (!normalizedName) {\n throw new Error('Custom Function name cannot be empty.');\n }\n return normalizedName;\n}\n\n/**\n * Derive an unvalidated slug from a normalized display name.\n *\n * @param displayName - Normalized display name\n * @returns Candidate slug\n */\nfunction slugify(displayName: string): string {\n return displayName\n .normalize('NFKD')\n .replace(/\\p{Mark}/gu, '')\n .replace(/([A-Z]{2,})([A-Z][a-z])/g, '$1-$2')\n .replace(/([a-z\\d])([A-Z])/g, '$1-$2')\n .replace(/['’]/gu, '')\n .toLowerCase()\n .replace(/[^a-z0-9]+/gu, '-')\n .replace(/^-+|-+$/gu, '');\n}\n\n/**\n * Reject candidate slugs that are not portable filenames.\n *\n * @param slug - Candidate filename stem\n */\nfunction validateSlug(slug: string): void {\n if (!slug) {\n throw new Error('Custom Function name must contain a letter or number.');\n }\n if (slug.length > MAX_SLUG_LENGTH) {\n throw new Error(`Custom Function slug cannot exceed ${MAX_SLUG_LENGTH} characters.`);\n }\n if (WINDOWS_RESERVED_FILENAME.test(slug)) {\n throw new Error(`Custom Function name resolves to reserved filename \"${slug}\".`);\n }\n}\n\n/**\n * Prefix a generated relative path for manifest storage.\n *\n * @param path - Relative generated file path\n * @returns Explicit manifest-relative path\n */\nfunction toManifestPath(path: string): string {\n return `./${path}`;\n}\n\n/**\n * Serialize a payload with stable indentation and a final newline.\n *\n * @param path - Manifest-relative payload path\n * @param payload - JSON-compatible payload\n * @returns Generated payload file\n */\nfunction buildPayloadFile(path: string, payload: object): GeneratedCustomFunctionFile {\n return {\n path,\n contents: `${JSON.stringify(payload, null, 2)}\\n`,\n };\n}\n\n/**\n * Select the source template for one handler shape.\n *\n * @param templateName - Handler shape\n * @returns TypeScript source\n */\nfunction buildSource(templateName: CustomFunctionTemplateName): string {\n const importStatement =\n \"import type { CustomFunction } from '@transcend-io/custom-function-types';\";\n\n switch (templateName) {\n case 'general':\n return `${importStatement}\n\n/**\n * Handle a General Custom Function invocation.\n *\n * @param argument - Services and payload supplied by Transcend\n */\nexport default function customFunction({\n payload,\n}: CustomFunction.GeneralArgument): void {\n // TODO: Handle the webhook or schedule payload.\n void payload;\n}\n`;\n case 'dsr-datapoint':\n return `${importStatement}\n\n${buildDataPointHandler()}\n`;\n case 'dsr-enricher':\n return `${importStatement}\n\n${buildEnricherHandler()}\n`;\n case 'dsr-both':\n return `${importStatement}\n\n${buildDataPointHandler()}\n\n${buildEnricherHandler()}\n`;\n default:\n throw new Error(`Unsupported Custom Function template: ${String(templateName)}`);\n }\n}\n\n/**\n * Build the DSR datapoint default export.\n *\n * @returns TypeScript source\n */\nfunction buildDataPointHandler(): string {\n return `/**\n * Resolve a data subject request datapoint.\n *\n * @param argument - Services and payload supplied by Transcend\n */\nexport default async function customFunction({\n environment,\n payload,\n sdk,\n}: CustomFunction.Argument): Promise<void> {\n if (payload.type !== 'ACCESS') {\n throw new Error(\\`Unsupported request type: \\${payload.type}\\`);\n }\n\n // TODO: Fetch the matching record from your customer API.\n // TODO: Map that record into the profiles array.\n const profiles: unknown[] = [];\n const response = await sdk.fetch('/v1/data-silo', {\n method: 'POST',\n headers: {\n Authorization: \\`Bearer \\${environment.TRANSCEND_API_KEY}\\`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ profiles, status: 'READY' }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n \\`Failed to report ACCESS results: status=\\${response.status} statusText=\\${response.statusText} body=\\${body}\\`,\n );\n }\n}`;\n}\n\n/**\n * Build the DSR request-enricher named export.\n *\n * @returns TypeScript source\n */\nfunction buildEnricherHandler(): string {\n return `/**\n * Enrich identifiers before a data subject request runs.\n *\n * @param argument - Services and payload supplied by Transcend\n */\nexport async function enricher({\n environment,\n sdk,\n}: CustomFunction.EnricherArgument): Promise<void> {\n // TODO: Query your customer API and map discovered identifiers here.\n const enrichedIdentifiers = {};\n const response = await sdk.fetch('/v1/enrich-identifiers', {\n method: 'POST',\n headers: {\n Authorization: \\`Bearer \\${environment.TRANSCEND_API_KEY}\\`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ enrichedIdentifiers }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(\n \\`Failed to report enriched identifiers: status=\\${response.status} statusText=\\${response.statusText} body=\\${body}\\`,\n );\n }\n}`;\n}\n\n/**\n * Build a representative General test payload.\n *\n * @returns JSON-compatible payload\n */\nfunction buildGeneralPayload(): object {\n return {\n event: 'example',\n };\n}\n\n/**\n * Build a representative DSR datapoint test payload.\n *\n * @returns JSON-compatible payload\n */\nfunction buildDataPointPayload(): object {\n return {\n type: 'ACCESS',\n dataSubject: {\n type: 'customer',\n },\n isTest: true,\n extras: {\n profile: {\n id: '00000000-0000-4000-8000-000000000001',\n RequestDataSiloId: '00000000-0000-4000-8000-000000000002',\n identifier: 'person@example.com',\n type: 'email',\n },\n request: buildRequest(),\n organization: buildOrganization(),\n },\n };\n}\n\n/**\n * Build a representative DSR request-enricher test payload.\n *\n * @returns JSON-compatible payload\n */\nfunction buildEnricherPayload(): object {\n return {\n type: 'ACCESS',\n dataSubject: {\n type: 'customer',\n },\n isTest: true,\n requestIdentifier: {\n name: 'email',\n value: 'person@example.com',\n },\n extras: {\n enricher: {\n id: '00000000-0000-4000-8000-000000000003',\n title: 'Email enrichment',\n },\n identifier: {\n id: '00000000-0000-4000-8000-000000000004',\n name: 'email',\n type: 'email',\n },\n requestEnricherId: '00000000-0000-4000-8000-000000000005',\n request: buildRequest(),\n organization: buildOrganization(),\n },\n };\n}\n\n/**\n * Build deterministic request metadata shared by DSR payloads.\n *\n * @returns JSON-compatible request metadata\n */\nfunction buildRequest(): object {\n return {\n details: '',\n id: '00000000-0000-4000-8000-000000000006',\n link: '/requests/00000000-0000-4000-8000-000000000006',\n createdAt: '2026-01-01T00:00:00.000Z',\n locale: 'en-US',\n origin: 'API',\n };\n}\n\n/**\n * Build deterministic organization metadata shared by DSR payloads.\n *\n * @returns JSON-compatible organization metadata\n */\nfunction buildOrganization(): object {\n return {\n id: '00000000-0000-4000-8000-000000000007',\n uri: 'example',\n name: 'Example Organization',\n };\n}\n"],"mappings":"AAGA,MAAa,EAAiC,CAC5C,UACA,gBACA,eACA,WACD,CA4BK,EAA4B,mDAQlC,SAAgB,EAAyB,EAA6B,CAEpE,IAAM,EAAO,EADU,EAAgC,EACpB,CAAC,CAEpC,OADA,EAAa,EAAK,CACX,EAST,SAAgB,EAAkC,EAA6B,CAC7E,IAAM,EAAiB,EAAgC,EAAY,CAEnE,OADA,EAAa,EAAQ,EAAe,CAAC,CAC9B,EAUT,SAAgB,EACd,EACA,EACiC,CACjC,IAAM,EAAiB,EAAkC,EAAY,CAC/D,EAAO,EAAyB,EAAe,CAC/C,EAAa,aAAa,EAAK,KAC/B,EAAa,CACjB,KAAM,EACN,SAAU,EAAY,EAAa,CACpC,CAED,GAAI,IAAiB,UAAW,CAC9B,IAAM,EAAc,EAAiB,iBAAiB,EAAK,OAAQ,GAAqB,CAAC,CACzF,MAAO,CACL,YAAa,EACb,OACA,aACA,aAAc,CAAC,EAAY,CAC3B,cAAe,CACb,KAAM,EACN,KAAM,EAAe,EAAW,CAChC,eAAgB,EAAe,EAAY,KAAK,CACjD,CACF,CAGH,IAAM,EAA8C,EAAE,EAClD,IAAiB,iBAAmB,IAAiB,aACvD,EAAa,KACX,EAAiB,iBAAiB,EAAK,iBAAkB,GAAuB,CAAC,CAClF,EAEC,IAAiB,gBAAkB,IAAiB,aACtD,EAAa,KACX,EAAiB,iBAAiB,EAAK,gBAAiB,GAAsB,CAAC,CAChF,CAGH,IAAM,EAA6C,CACjD,KAAM,EACN,KAAM,EAAe,EAAW,CAChC,KAAM,MACN,IAAK,CACH,kBAAmB,mCACpB,CACF,CAED,GAAI,IAAiB,gBACnB,EAAc,gBAAkB,EAAe,EAAa,GAAI,KAAK,CACrE,EAAc,qBAAuB,qBAC5B,IAAiB,eAC1B,EAAc,gBAAkB,EAAe,EAAa,GAAI,KAAK,CACrE,EAAc,qBAAuB,2BAC5B,IAAiB,WAC1B,EAAc,iBAAmB,CAC/B,CACE,QAAS,EAAe,EAAa,GAAI,KAAK,CAC9C,eAAgB,aACjB,CACD,CACE,QAAS,EAAe,EAAa,GAAI,KAAK,CAC9C,eAAgB,mBACjB,CACF,MAED,MAAU,MAAM,yCAAyC,OAAO,EAAa,GAAG,CAGlF,MAAO,CACL,YAAa,EACb,OACA,aACA,eACA,gBACD,CASH,SAAS,EAAgC,EAA6B,CACpE,GAAI,yBAAyB,KAAK,EAAY,CAC5C,MAAU,MAAM,0DAA0D,CAE5E,GAAI,SAAS,KAAK,EAAY,CAC5B,MAAU,MAAM,uDAAuD,CAGzE,IAAM,EAAiB,EAAY,MAAM,CAAC,QAAQ,QAAS,IAAI,CAC/D,GAAI,CAAC,EACH,MAAU,MAAM,wCAAwC,CAE1D,OAAO,EAST,SAAS,EAAQ,EAA6B,CAC5C,OAAO,EACJ,UAAU,OAAO,CACjB,QAAQ,aAAc,GAAG,CACzB,QAAQ,2BAA4B,QAAQ,CAC5C,QAAQ,oBAAqB,QAAQ,CACrC,QAAQ,SAAU,GAAG,CACrB,aAAa,CACb,QAAQ,eAAgB,IAAI,CAC5B,QAAQ,YAAa,GAAG,CAQ7B,SAAS,EAAa,EAAoB,CACxC,GAAI,CAAC,EACH,MAAU,MAAM,wDAAwD,CAE1E,GAAI,EAAK,OAAS,GAChB,MAAU,MAAM,oDAAoE,CAEtF,GAAI,EAA0B,KAAK,EAAK,CACtC,MAAU,MAAM,uDAAuD,EAAK,IAAI,CAUpF,SAAS,EAAe,EAAsB,CAC5C,MAAO,KAAK,IAUd,SAAS,EAAiB,EAAc,EAA8C,CACpF,MAAO,CACL,OACA,SAAU,GAAG,KAAK,UAAU,EAAS,KAAM,EAAE,CAAC,IAC/C,CASH,SAAS,EAAY,EAAkD,CACrE,IAAM,EACJ,6EAEF,OAAQ,EAAR,CACE,IAAK,UACH,MAAO,GAAG,EAAgB;;;;;;;;;;;;;EAc5B,IAAK,gBACH,MAAO,GAAG,EAAgB;;EAE9B,GAAuB,CAAC;EAEtB,IAAK,eACH,MAAO,GAAG,EAAgB;;EAE9B,GAAsB,CAAC;EAErB,IAAK,WACH,MAAO,GAAG,EAAgB;;EAE9B,GAAuB,CAAC;;EAExB,GAAsB,CAAC;EAErB,QACE,MAAU,MAAM,yCAAyC,OAAO,EAAa,GAAG,EAStF,SAAS,GAAgC,CACvC,MAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCT,SAAS,GAA+B,CACtC,MAAO;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCT,SAAS,GAA8B,CACrC,MAAO,CACL,MAAO,UACR,CAQH,SAAS,GAAgC,CACvC,MAAO,CACL,KAAM,SACN,YAAa,CACX,KAAM,WACP,CACD,OAAQ,GACR,OAAQ,CACN,QAAS,CACP,GAAI,uCACJ,kBAAmB,uCACnB,WAAY,qBACZ,KAAM,QACP,CACD,QAAS,GAAc,CACvB,aAAc,GAAmB,CAClC,CACF,CAQH,SAAS,GAA+B,CACtC,MAAO,CACL,KAAM,SACN,YAAa,CACX,KAAM,WACP,CACD,OAAQ,GACR,kBAAmB,CACjB,KAAM,QACN,MAAO,qBACR,CACD,OAAQ,CACN,SAAU,CACR,GAAI,uCACJ,MAAO,mBACR,CACD,WAAY,CACV,GAAI,uCACJ,KAAM,QACN,KAAM,QACP,CACD,kBAAmB,uCACnB,QAAS,GAAc,CACvB,aAAc,GAAmB,CAClC,CACF,CAQH,SAAS,GAAuB,CAC9B,MAAO,CACL,QAAS,GACT,GAAI,uCACJ,KAAM,iDACN,UAAW,2BACX,OAAQ,QACR,OAAQ,MACT,CAQH,SAAS,GAA4B,CACnC,MAAO,CACL,GAAI,uCACJ,IAAK,UACL,KAAM,uBACP"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e}from"./constants-
|
|
2
|
-
//# sourceMappingURL=skipPreflightJobs-
|
|
1
|
+
import{a as e}from"./constants-BG53Mbi9.mjs";import{t}from"./logger-9T6vva73.mjs";import{r as n}from"./fetchAllRequests-CEDKdq0c.mjs";import{RequestEnricherStatus as r,RequestStatus as i}from"@transcend-io/privacy-types";import a from"colors";import{SKIP_REQUEST_ENRICHER as o,buildTranscendGraphQLClient as s,fetchAllRequestEnrichers as c,makeGraphQLRequest as l}from"@transcend-io/sdk";import{map as u,mapSeries as d}from"@transcend-io/utils";import f from"cli-progress";async function p({enricherIds:p,auth:m,concurrency:h=100,transcendUrl:g=e}){let _=s(g,m),v=new Date().getTime(),y=await n(_,{statuses:[i.Enriching]});t.info(a.magenta(`Processing enricher: "${p.join(`,`)}" fetched "${y.length}" in enriching status.`));let b=new f.SingleBar({},f.Presets.shades_classic),x=0;b.start(y.length,0);let S=0;await u(y,async e=>{let n=(await c(_,{filterBy:{requestId:e.id},logger:t})).filter(e=>p.includes(e.enricher.id)&&![r.Resolved,r.Skipped].includes(e.status));n.length>0&&await d(n,async e=>{try{await l(_,o,{variables:{requestEnricherId:e.id},logger:t}),S+=1}catch(e){if(!e.message.includes(`Client error: Cannot skip Request enricher because it has already completed`))throw e}}),x+=1,b.update(x)},{concurrency:h}),b.stop();let C=new Date().getTime()-v;return t.info(a.green(`Successfully skipped "${S}" for "${y.length}" requests in "${C/1e3}" seconds!`)),y.length}export{p as t};
|
|
2
|
+
//# sourceMappingURL=skipPreflightJobs-Beia_iB6.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skipPreflightJobs-
|
|
1
|
+
{"version":3,"file":"skipPreflightJobs-Beia_iB6.mjs","names":[],"sources":["../src/lib/requests/skipPreflightJobs.ts"],"sourcesContent":["import { RequestEnricherStatus, RequestStatus } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n fetchAllRequestEnrichers,\n makeGraphQLRequest,\n SKIP_REQUEST_ENRICHER,\n} from '@transcend-io/sdk';\nimport { mapSeries, map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchAllRequests } from '../graphql/index.js';\n\n/**\n * Given an enricher ID, mark all open request enrichers as skipped\n *\n * @param options - Options\n * @returns Number of items skipped\n */\nexport async function skipPreflightJobs({\n enricherIds,\n auth,\n concurrency = 100,\n transcendUrl = DEFAULT_TRANSCEND_API,\n}: {\n /** Transcend API key authentication */\n auth: string;\n /** Enricher IDs to pull down jobs for */\n enricherIds: string[];\n /** Upload concurrency */\n concurrency?: number;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Request statuses to mark as completed */\n requestStatuses?: RequestStatus[];\n}): Promise<number> {\n // Find all requests made before createdAt that are in a removing data state\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Time duration\n const t0 = new Date().getTime();\n\n // fetch all RequestDataSilos that are open\n const requests = await fetchAllRequests(client, {\n statuses: [RequestStatus.Enriching],\n });\n\n // Notify Transcend\n logger.info(\n colors.magenta(\n `Processing enricher: \"${enricherIds.join(',')}\" fetched \"${\n requests.length\n }\" in enriching status.`,\n ),\n );\n\n // create a new progress bar instance and use shades_classic theme\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n let total = 0;\n progressBar.start(requests.length, 0);\n let totalSkipped = 0;\n await map(\n requests,\n async (request) => {\n // TODO dont pull all in\n const requestEnrichers = await fetchAllRequestEnrichers(client, {\n filterBy: { requestId: request.id },\n logger,\n });\n const requestEnrichersFiltered = requestEnrichers.filter(\n (enricher) =>\n enricherIds.includes(enricher.enricher.id) &&\n ![\n RequestEnricherStatus.Resolved,\n RequestEnricherStatus.Skipped,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ].includes(enricher.status as any),\n );\n\n // TODO\n if (requestEnrichersFiltered.length > 0) {\n await mapSeries(requestEnrichersFiltered, async (requestEnricher) => {\n try {\n await makeGraphQLRequest<{\n /** Whether we successfully uploaded the results */\n success: boolean;\n }>(client, SKIP_REQUEST_ENRICHER, {\n variables: { requestEnricherId: requestEnricher.id },\n logger,\n });\n totalSkipped += 1;\n } catch (err) {\n if (\n !err.message.includes(\n 'Client error: Cannot skip Request enricher because it has already completed',\n )\n ) {\n throw err;\n }\n }\n });\n }\n total += 1;\n progressBar.update(total);\n },\n { concurrency },\n );\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Successfully skipped \"${totalSkipped}\" for \"${\n requests.length\n }\" requests in \"${totalTime / 1000}\" seconds!`,\n ),\n );\n return requests.length;\n}\n"],"mappings":"ydAqBA,eAAsB,EAAkB,CACtC,cACA,OACA,cAAc,IACd,eAAe,GAYG,CAElB,IAAM,EAAS,EAA4B,EAAc,EAAK,CAGxD,EAAK,IAAI,MAAM,CAAC,SAAS,CAGzB,EAAW,MAAM,EAAiB,EAAQ,CAC9C,SAAU,CAAC,EAAc,UAAU,CACpC,CAAC,CAGF,EAAO,KACL,EAAO,QACL,yBAAyB,EAAY,KAAK,IAAI,CAAC,aAC7C,EAAS,OACV,wBACF,CACF,CAGD,IAAM,EAAc,IAAI,EAAY,UAAU,EAAE,CAAE,EAAY,QAAQ,eAAe,CAEjF,EAAQ,EACZ,EAAY,MAAM,EAAS,OAAQ,EAAE,CACrC,IAAI,EAAe,EACnB,MAAM,EACJ,EACA,KAAO,IAAY,CAMjB,IAAM,GAA2B,MAJF,EAAyB,EAAQ,CAC9D,SAAU,CAAE,UAAW,EAAQ,GAAI,CACnC,SACD,CAAC,EACgD,OAC/C,GACC,EAAY,SAAS,EAAS,SAAS,GAAG,EAC1C,CAAC,CACC,EAAsB,SACtB,EAAsB,QAEvB,CAAC,SAAS,EAAS,OAAc,CACrC,CAGG,EAAyB,OAAS,GACpC,MAAM,EAAU,EAA0B,KAAO,IAAoB,CACnE,GAAI,CACF,MAAM,EAGH,EAAQ,EAAuB,CAChC,UAAW,CAAE,kBAAmB,EAAgB,GAAI,CACpD,SACD,CAAC,CACF,GAAgB,QACT,EAAK,CACZ,GACE,CAAC,EAAI,QAAQ,SACX,8EACD,CAED,MAAM,IAGV,CAEJ,GAAS,EACT,EAAY,OAAO,EAAM,EAE3B,CAAE,cAAa,CAChB,CAED,EAAY,MAAM,CAElB,IAAM,EADK,IAAI,MAAM,CAAC,SACF,CAAG,EASvB,OAPA,EAAO,KACL,EAAO,MACL,yBAAyB,EAAa,UACpC,EAAS,OACV,iBAAiB,EAAY,IAAK,YACpC,CACF,CACM,EAAS"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e}from"./constants-
|
|
2
|
-
//# sourceMappingURL=skipRequestDataSilos-
|
|
1
|
+
import{a as e}from"./constants-BG53Mbi9.mjs";import{t}from"./logger-9T6vva73.mjs";import{RequestStatus as n}from"@transcend-io/privacy-types";import r from"colors";import{CHANGE_REQUEST_DATA_SILO_STATUS as i,buildTranscendGraphQLClient as a,fetchRequestDataSilos as o,fetchRequestDataSilosCount as s,makeGraphQLRequest as c}from"@transcend-io/sdk";import{map as l}from"@transcend-io/utils";import u from"cli-progress";async function d({dataSiloId:d,auth:f,concurrency:p=50,status:m=`SKIPPED`,transcendUrl:h=e,requestStatuses:g=[n.Compiling,n.Secondary],actionTypes:_=[]}){let v=a(h,f),y=new Date().getTime(),b=await s(v,{logger:t,filterBy:{dataSiloId:d,requestStatuses:g}});t.info(r.magenta(`Marking ${b} request data silos as completed${_.length>0?` for action types: ${_.join(`,`)}`:``}`));let x=new u.SingleBar({},u.Presets.shades_classic),S=0;x.start(b,0),await l(await o(v,{logger:t,filterBy:{dataSiloId:d,requestStatuses:g},onProgress:e=>{S+=e/2,x.update(S)}}),async e=>{if(_.length===0||_.includes(e.request.type))try{await c(v,i,{variables:{requestDataSiloId:e.id,status:m},logger:t})}catch(e){if(!e.message.includes(`Client error: Request must be active:`))throw e}S+=.5,x.update(S)},{concurrency:p}),x.stop();let C=new Date().getTime()-y;return t.info(r.green(`Successfully skipped "${b}" requests in "${C/1e3}" seconds!`)),b}export{d as t};
|
|
2
|
+
//# sourceMappingURL=skipRequestDataSilos-BSNLacnR.mjs.map
|
package/dist/{skipRequestDataSilos-B7RA6I3Z.mjs.map → skipRequestDataSilos-BSNLacnR.mjs.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skipRequestDataSilos-
|
|
1
|
+
{"version":3,"file":"skipRequestDataSilos-BSNLacnR.mjs","names":[],"sources":["../src/lib/requests/skipRequestDataSilos.ts"],"sourcesContent":["import { RequestAction, RequestStatus } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n makeGraphQLRequest,\n CHANGE_REQUEST_DATA_SILO_STATUS,\n fetchRequestDataSilos,\n fetchRequestDataSilosCount,\n} from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\n\n/**\n * Given a data silo ID, mark all open request data silos as skipped\n *\n * @param options - Options\n * @returns Number of items skipped\n */\nexport async function skipRequestDataSilos({\n dataSiloId,\n auth,\n concurrency = 50,\n status = 'SKIPPED',\n transcendUrl = DEFAULT_TRANSCEND_API,\n requestStatuses = [RequestStatus.Compiling, RequestStatus.Secondary],\n actionTypes = [],\n}: {\n /** Transcend API key authentication */\n auth: string;\n /** Data Silo ID to pull down jobs for */\n dataSiloId: string;\n /** Status to set */\n status?: 'SKIPPED' | 'RESOLVED';\n /** Upload concurrency */\n concurrency?: number;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Request statuses to mark as completed */\n requestStatuses?: RequestStatus[];\n /** Request action types to filter on */\n actionTypes?: RequestAction[];\n}): Promise<number> {\n // Find all requests made before createdAt that are in a removing data state\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Time duration\n const t0 = new Date().getTime();\n\n // Determine total number of request data silos\n const requestDataSiloCount = await fetchRequestDataSilosCount(client, {\n logger,\n filterBy: { dataSiloId, requestStatuses },\n });\n logger.info(\n colors.magenta(\n `Marking ${requestDataSiloCount} request data silos as completed${actionTypes.length > 0 ? ` for action types: ${actionTypes.join(',')}` : ''}`,\n ),\n );\n\n // create a new progress bar instance and use shades_classic theme\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n let total = 0;\n progressBar.start(requestDataSiloCount, 0);\n\n // Fetch all matching request data silos, updating progress as pages are fetched\n const requestDataSilos = await fetchRequestDataSilos(client, {\n logger,\n filterBy: { dataSiloId, requestStatuses },\n onProgress: (numFetched) => {\n total += numFetched / 2;\n progressBar.update(total);\n },\n });\n\n await map(\n requestDataSilos,\n async (requestDataSilo) => {\n if (actionTypes.length === 0 || actionTypes.includes(requestDataSilo.request.type)) {\n try {\n await makeGraphQLRequest<{\n /** Whether we successfully uploaded the results */\n success: boolean;\n }>(client, CHANGE_REQUEST_DATA_SILO_STATUS, {\n variables: { requestDataSiloId: requestDataSilo.id, status },\n logger,\n });\n } catch (err) {\n if (!err.message.includes('Client error: Request must be active:')) {\n throw err;\n }\n }\n }\n\n total += 0.5;\n progressBar.update(total);\n },\n { concurrency },\n );\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Successfully skipped \"${requestDataSiloCount}\" requests in \"${totalTime / 1000}\" seconds!`,\n ),\n );\n return requestDataSiloCount;\n}\n"],"mappings":"kaAqBA,eAAsB,EAAqB,CACzC,aACA,OACA,cAAc,GACd,SAAS,UACT,eAAe,EACf,kBAAkB,CAAC,EAAc,UAAW,EAAc,UAAU,CACpE,cAAc,EAAE,EAgBE,CAElB,IAAM,EAAS,EAA4B,EAAc,EAAK,CAGxD,EAAK,IAAI,MAAM,CAAC,SAAS,CAGzB,EAAuB,MAAM,EAA2B,EAAQ,CACpE,SACA,SAAU,CAAE,aAAY,kBAAiB,CAC1C,CAAC,CACF,EAAO,KACL,EAAO,QACL,WAAW,EAAqB,kCAAkC,EAAY,OAAS,EAAI,sBAAsB,EAAY,KAAK,IAAI,GAAK,KAC5I,CACF,CAGD,IAAM,EAAc,IAAI,EAAY,UAAU,EAAE,CAAE,EAAY,QAAQ,eAAe,CAEjF,EAAQ,EACZ,EAAY,MAAM,EAAsB,EAAE,CAY1C,MAAM,EACJ,MAV6B,EAAsB,EAAQ,CAC3D,SACA,SAAU,CAAE,aAAY,kBAAiB,CACzC,WAAa,GAAe,CAC1B,GAAS,EAAa,EACtB,EAAY,OAAO,EAAM,EAE5B,CAAC,CAIA,KAAO,IAAoB,CACzB,GAAI,EAAY,SAAW,GAAK,EAAY,SAAS,EAAgB,QAAQ,KAAK,CAChF,GAAI,CACF,MAAM,EAGH,EAAQ,EAAiC,CAC1C,UAAW,CAAE,kBAAmB,EAAgB,GAAI,SAAQ,CAC5D,SACD,CAAC,OACK,EAAK,CACZ,GAAI,CAAC,EAAI,QAAQ,SAAS,wCAAwC,CAChE,MAAM,EAKZ,GAAS,GACT,EAAY,OAAO,EAAM,EAE3B,CAAE,cAAa,CAChB,CAED,EAAY,MAAM,CAElB,IAAM,EADK,IAAI,MAAM,CAAC,SACF,CAAG,EAOvB,OALA,EAAO,KACL,EAAO,MACL,yBAAyB,EAAqB,iBAAiB,EAAY,IAAK,YACjF,CACF,CACM"}
|
package/dist/{streamPrivacyRequestsToCsv-DT31SP6w.mjs → streamPrivacyRequestsToCsv-Cksb9D49.mjs}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e}from"./constants-
|
|
2
|
-
//# sourceMappingURL=streamPrivacyRequestsToCsv-
|
|
1
|
+
import{a as e}from"./constants-BG53Mbi9.mjs";import{t}from"./logger-9T6vva73.mjs";import{i as n,r}from"./fetchAllRequests-CEDKdq0c.mjs";import{i,r as a,t as o}from"./writeCsv-D46aDkGf.mjs";import{groupBy as s,uniq as c}from"lodash-es";import l from"colors";import{buildTranscendGraphQLClient as u,createSombraGotInstance as d,fetchAllRequestIdentifiers as f,validateSombraVersion as p}from"@transcend-io/sdk";import{map as m}from"@transcend-io/utils";import h from"cli-progress";function g({attributeValues:e,requestIdentifiers:t,id:n,email:r,type:i,status:a,subjectType:o,details:c,createdAt:l,successfullyCompletedAt:u,country:d,locale:f,origin:p,countrySubDivision:m,isSilent:h,isTest:g,coreIdentifier:_,purpose:v,...y}){return{"Request ID":n,"Created At":l,"Successfully Completed At":u||``,Email:r,"Core Identifier":_,"Request Type":i,"Data Subject Type":o,Status:a,Country:d,"Country Sub Division":m,Details:c,Origin:p,"Silent Mode":h,"Is Test Request":g,Language:f,"Purpose Trigger Name":v?.title||v?.name||``,"Purpose Trigger Value":v?.consent?.toString()||``,...(v?.enrichedPreferences||[]).reduce((e,t)=>{let n=t.preferenceTopic?.title.defaultMessage||t.name;return n?{...e,[n]:t.selectValues?t.selectValues.map(e=>e.name).join(`;`):t.selectValue?.name||t.booleanValue}:e},{}),...y,...Object.entries(s(e,`attributeKey.name`)).reduce((e,[t,n])=>Object.assign(e,{[t]:n.map(({name:e})=>e).join(`,`)}),{}),...Object.entries(s(t,`name`)).reduce((e,[t,n])=>Object.assign(e,{[t]:n.map(({value:e})=>e).join(`,`)}),{})}}function _(e,t,n){let r=e.getTime(),i=t.getTime(),a=(i-r)/n;return Array.from({length:n},(e,t)=>({createdAtAfter:new Date(r+a*t),createdAtBefore:new Date(t===n-1?i:r+a*(t+1))}))}async function v({auth:s,sombraAuth:v,actions:y=[],statuses:b=[],identifierSearch:x,concurrency:S=1,pageLimit:C=100,transcendUrl:w=e,createdAtBefore:T,createdAtAfter:E,updatedAtBefore:D,updatedAtAfter:O,isTest:k,skipRequestIdentifiers:A=!1,file:j}){let M=u(w,s),N=A?void 0:await d(w,s,{logger:t,sombraApiKey:v,sombraUrl:process.env.SOMBRA_URL}),P=``;T&&(P+=` before ${T.toISOString()}`),E&&(P+=`${P?`, and`:``} after ${E.toISOString()}`),t.info(l.magenta(`${y.length>0?`Pulling requests of type "${y.join(`" , "`)}"`:`Pulling all requests`}${P}`));let F=S>1&&E&&T,I=F?_(E,T,S):[{createdAtAfter:E,createdAtBefore:T}];F&&t.info(l.magenta(`Splitting date range into ${S} parallel chunks`));let L={type:y.length>0?y:void 0,status:b.length>0?b:void 0,isTest:k,createdAtBefore:T?T.toISOString():void 0,createdAtAfter:E?E.toISOString():void 0,updatedAtBefore:D?D.toISOString():void 0,updatedAtAfter:O?O.toISOString():void 0},R=Date.now();A||await p(M,{logger:t});let z=await n(M,L);t.info(l.magenta(`Fetching ${z} requests`));let B=new h.SingleBar({},h.Presets.shades_classic);B.start(z,0);let V=0,{baseName:H,extension:U}=i(j),W=I.map((e,t)=>I.length===1?j:`${H}-${t}${U}`),G=[],K=await m(I,async(e,n)=>{let i=W[n],s,u=0;try{await r(M,{actions:y,text:x,statuses:b,createdAtBefore:e.createdAtBefore,createdAtAfter:e.createdAtAfter,updatedAtBefore:D,updatedAtAfter:O,isTest:k,onPage:async e=>{if(e.length===0)return;let n=(A?e.map(e=>({...e,requestIdentifiers:[]})):await m(e,async e=>({...e,requestIdentifiers:await f(M,N,{filterBy:{requestId:e.id},skipSombraCheck:!0,logger:t})}),{concurrency:C})).map(g);s||(s=c(n.map(e=>Object.keys(e)).flat()),a(i,s)),o(i,n,s),u+=n.length,V+=n.length,B.update(V)}})}catch(r){let i=r instanceof Error?r.message:String(r);t.error(l.red(`Chunk ${n} failed (${e.createdAtAfter?.toISOString()??`start`} → ${e.createdAtBefore?.toISOString()??`end`}): ${i}`)),G.push({index:n,createdAtAfter:e.createdAtAfter,createdAtBefore:e.createdAtBefore,error:i})}return s||a(i,[]),u},{concurrency:F?S:1});B.stop();let q=K.reduce((e,t)=>e+t,0),J=(Date.now()-R)/1e3;if(G.length>0){t.error(l.red(`\n${G.length} chunk(s) failed. Re-run with these date ranges to fill the gaps:`));for(let e of G)t.error(l.red(` Chunk ${e.index}: --createdAtAfter=${e.createdAtAfter?.toISOString()??``} --createdAtBefore=${e.createdAtBefore?.toISOString()??``}`))}return t.info(l.green(`Streamed ${q} requests to ${W.length} file(s) in ${J}s`)),{filePaths:W,totalCount:q}}export{_ as n,g as r,v as t};
|
|
2
|
+
//# sourceMappingURL=streamPrivacyRequestsToCsv-Cksb9D49.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamPrivacyRequestsToCsv-DT31SP6w.mjs","names":[],"sources":["../src/lib/requests/formatRequestForCsv.ts","../src/lib/requests/splitDateRange.ts","../src/lib/requests/streamPrivacyRequestsToCsv.ts"],"sourcesContent":["import type { RequestIdentifier } from '@transcend-io/sdk';\nimport { groupBy } from 'lodash-es';\n\nimport type { PrivacyRequest } from '../graphql/index.js';\n\nexport interface ExportedPrivacyRequest extends PrivacyRequest {\n /** Request identifiers */\n requestIdentifiers: RequestIdentifier[];\n}\n\n/** A single CSV row */\nexport type CsvRow = { [k in string]: string | null | number | boolean };\n\n/**\n * Format a single privacy request (with optional identifiers) into a flat CSV row.\n *\n * @param request - The request with identifiers attached\n * @returns Flat object suitable for CSV output\n */\nexport function formatRequestForCsv({\n attributeValues,\n requestIdentifiers,\n id,\n email,\n type,\n status,\n subjectType,\n details,\n createdAt,\n successfullyCompletedAt,\n country,\n locale,\n origin,\n countrySubDivision,\n isSilent,\n isTest,\n coreIdentifier,\n purpose,\n ...request\n}: ExportedPrivacyRequest): CsvRow {\n return {\n 'Request ID': id,\n 'Created At': createdAt,\n 'Successfully Completed At': successfullyCompletedAt || '',\n Email: email,\n 'Core Identifier': coreIdentifier,\n 'Request Type': type,\n 'Data Subject Type': subjectType,\n Status: status,\n Country: country,\n 'Country Sub Division': countrySubDivision,\n Details: details,\n Origin: origin,\n 'Silent Mode': isSilent,\n 'Is Test Request': isTest,\n Language: locale,\n 'Purpose Trigger Name': purpose?.title || purpose?.name || '',\n 'Purpose Trigger Value': purpose?.consent?.toString() || '',\n ...(purpose?.enrichedPreferences || []).reduce((acc: Record<string, string | boolean>, p) => {\n const title = p.preferenceTopic?.title.defaultMessage || p.name;\n return title\n ? {\n ...acc,\n [title]: p.selectValues\n ? p.selectValues.map((x) => x.name).join(';')\n : p.selectValue?.name || p.booleanValue,\n }\n : acc;\n }, {}),\n ...request,\n ...Object.entries(groupBy(attributeValues, 'attributeKey.name')).reduce(\n (acc, [name, values]) =>\n Object.assign(acc, {\n [name]: values.map(({ name: n }) => n).join(','),\n }),\n {},\n ),\n ...Object.entries(groupBy(requestIdentifiers, 'name')).reduce(\n (acc, [name, values]) =>\n Object.assign(acc, {\n [name]: values.map(({ value }) => value).join(','),\n }),\n {},\n ),\n };\n}\n","export interface ChunkedDateRange {\n /** Chunk start */\n createdAtAfter: Date;\n /** Chunk end */\n createdAtBefore: Date;\n}\n\n/**\n * Split a date range into N evenly-spaced chunks.\n *\n * @param after - Start of the date range\n * @param before - End of the date range\n * @param chunks - Number of chunks to split into\n * @returns Array of date range bounds\n */\nexport function splitDateRange(after: Date, before: Date, chunks: number): ChunkedDateRange[] {\n const startMs = after.getTime();\n const endMs = before.getTime();\n const chunkSizeMs = (endMs - startMs) / chunks;\n return Array.from({ length: chunks }, (_, i) => ({\n createdAtAfter: new Date(startMs + chunkSizeMs * i),\n createdAtBefore: new Date(i === chunks - 1 ? endMs : startMs + chunkSizeMs * (i + 1)),\n }));\n}\n","import { RequestAction, RequestStatus } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n createSombraGotInstance,\n fetchAllRequestIdentifiers,\n validateSombraVersion,\n} from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport { uniq } from 'lodash-es';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchAllRequests, fetchRequestsTotalCount } from '../graphql/index.js';\nimport { initCsvFile, appendCsvRowsOrdered, parseFilePath } from '../helpers/index.js';\nimport { formatRequestForCsv, ExportedPrivacyRequest } from './formatRequestForCsv.js';\nimport { splitDateRange } from './splitDateRange.js';\n\n/**\n * Stream privacy requests directly to CSV files, one file per date-range chunk.\n * Memory stays bounded to a single page of results at a time.\n * Supports both with and without request identifier enrichment.\n *\n * @param options - Options\n * @returns The list of written file paths and total row count\n */\nexport async function streamPrivacyRequestsToCsv({\n auth,\n sombraAuth,\n actions = [],\n statuses = [],\n identifierSearch,\n concurrency = 1,\n pageLimit = 100,\n transcendUrl = DEFAULT_TRANSCEND_API,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n isTest,\n skipRequestIdentifiers = false,\n file,\n}: {\n /** Transcend API key authentication */\n auth: string;\n /** Search for a specific identifier */\n identifierSearch?: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Statuses to filter on */\n statuses?: RequestStatus[];\n /** The request action to fetch */\n actions?: RequestAction[];\n /** Number of parallel date-range chunks */\n concurrency?: number;\n /** Concurrency for fetching identifiers per page */\n pageLimit?: number;\n /** Filter for requests created before this date */\n createdAtBefore?: Date;\n /** Filter for requests created after this date */\n createdAtAfter?: Date;\n /** Filter for requests updated before this date */\n updatedAtBefore?: Date;\n /** Filter for requests updated after this date */\n updatedAtAfter?: Date;\n /** Return test requests */\n isTest?: boolean;\n /** Skip fetching request identifiers */\n skipRequestIdentifiers?: boolean;\n /** Output CSV file path */\n file: string;\n}): Promise<{\n /** Paths to written CSV files */\n filePaths: string[];\n /** Total rows written */\n totalCount: number;\n}> {\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n const sombra = skipRequestIdentifiers\n ? undefined\n : await createSombraGotInstance(transcendUrl, auth, {\n logger,\n sombraApiKey: sombraAuth,\n sombraUrl: process.env.SOMBRA_URL,\n });\n\n // Log date range\n let dateRange = '';\n if (createdAtBefore) {\n dateRange += ` before ${createdAtBefore.toISOString()}`;\n }\n if (createdAtAfter) {\n dateRange += `${dateRange ? ', and' : ''} after ${createdAtAfter.toISOString()}`;\n }\n logger.info(\n colors.magenta(\n `${\n actions.length > 0\n ? `Pulling requests of type \"${actions.join('\" , \"')}\"`\n : 'Pulling all requests'\n }${dateRange}`,\n ),\n );\n\n // Split into parallel date-range chunks when possible\n const useChunks = concurrency > 1 && createdAtAfter && createdAtBefore;\n const chunks = useChunks\n ? splitDateRange(createdAtAfter, createdAtBefore, concurrency)\n : [{ createdAtAfter, createdAtBefore }];\n\n if (useChunks) {\n logger.info(colors.magenta(`Splitting date range into ${concurrency} parallel chunks`));\n }\n\n // Fetch total count once for the shared progress bar\n const filterBy = {\n type: actions.length > 0 ? actions : undefined,\n status: statuses.length > 0 ? statuses : undefined,\n isTest,\n createdAtBefore: createdAtBefore ? createdAtBefore.toISOString() : undefined,\n createdAtAfter: createdAtAfter ? createdAtAfter.toISOString() : undefined,\n updatedAtBefore: updatedAtBefore ? updatedAtBefore.toISOString() : undefined,\n updatedAtAfter: updatedAtAfter ? updatedAtAfter.toISOString() : undefined,\n };\n\n const t0 = Date.now();\n\n // Validate Sombra version once before bulk-fetching identifiers\n if (!skipRequestIdentifiers) {\n await validateSombraVersion(client, { logger });\n }\n\n const totalExpected = await fetchRequestsTotalCount(client, filterBy);\n logger.info(colors.magenta(`Fetching ${totalExpected} requests`));\n\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n progressBar.start(totalExpected, 0);\n\n let globalFetched = 0;\n\n const { baseName, extension } = parseFilePath(file);\n\n const filePaths = chunks.map((_, i) =>\n chunks.length === 1 ? file : `${baseName}-${i}${extension}`,\n );\n\n interface FailedChunk {\n /** Chunk index */\n index: number;\n /** Start of failed date range */\n createdAtAfter?: Date;\n /** End of failed date range */\n createdAtBefore?: Date;\n /** Error message */\n error: string;\n }\n\n const failedChunks: FailedChunk[] = [];\n\n const chunkCounts = await map(\n chunks,\n async (chunk, i) => {\n const chunkFile = filePaths[i];\n let headers: string[] | undefined;\n let rowCount = 0;\n\n try {\n await fetchAllRequests(client, {\n actions,\n text: identifierSearch,\n statuses,\n createdAtBefore: chunk.createdAtBefore,\n createdAtAfter: chunk.createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n isTest,\n onPage: async (nodes) => {\n if (nodes.length === 0) return;\n\n // Optionally enrich each request with its identifiers\n const enriched: ExportedPrivacyRequest[] = skipRequestIdentifiers\n ? nodes.map((n) => ({ ...n, requestIdentifiers: [] }))\n : await map(\n nodes,\n async (n) => ({\n ...n,\n requestIdentifiers: await fetchAllRequestIdentifiers(client, sombra!, {\n filterBy: { requestId: n.id },\n skipSombraCheck: true,\n logger,\n }),\n }),\n { concurrency: pageLimit },\n );\n\n const rows: Record<string, string | null | number | boolean>[] =\n enriched.map(formatRequestForCsv);\n\n if (!headers) {\n headers = uniq(rows.map((r: Record<string, unknown>) => Object.keys(r)).flat());\n initCsvFile(chunkFile, headers);\n }\n\n appendCsvRowsOrdered(chunkFile, rows, headers);\n rowCount += rows.length;\n globalFetched += rows.length;\n progressBar.update(globalFetched);\n },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.error(\n colors.red(\n `Chunk ${i} failed (${\n chunk.createdAtAfter?.toISOString() ?? 'start'\n } → ${chunk.createdAtBefore?.toISOString() ?? 'end'}): ${message}`,\n ),\n );\n failedChunks.push({\n index: i,\n createdAtAfter: chunk.createdAtAfter,\n createdAtBefore: chunk.createdAtBefore,\n error: message,\n });\n }\n\n if (!headers) {\n initCsvFile(chunkFile, []);\n }\n\n return rowCount;\n },\n { concurrency: useChunks ? concurrency : 1 },\n );\n\n progressBar.stop();\n const totalCount = chunkCounts.reduce((a, b) => a + b, 0);\n const elapsed = (Date.now() - t0) / 1000;\n\n if (failedChunks.length > 0) {\n logger.error(\n colors.red(\n `\\n${failedChunks.length} chunk(s) failed. ` +\n 'Re-run with these date ranges to fill the gaps:',\n ),\n );\n for (const fc of failedChunks) {\n logger.error(\n colors.red(\n ` Chunk ${fc.index}: --createdAtAfter=${\n fc.createdAtAfter?.toISOString() ?? ''\n } --createdAtBefore=${fc.createdAtBefore?.toISOString() ?? ''}`,\n ),\n );\n }\n }\n\n logger.info(\n colors.green(`Streamed ${totalCount} requests to ${filePaths.length} file(s) in ${elapsed}s`),\n );\n\n return { filePaths, totalCount };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"streamPrivacyRequestsToCsv-Cksb9D49.mjs","names":[],"sources":["../src/lib/requests/formatRequestForCsv.ts","../src/lib/requests/splitDateRange.ts","../src/lib/requests/streamPrivacyRequestsToCsv.ts"],"sourcesContent":["import type { RequestIdentifier } from '@transcend-io/sdk';\nimport { groupBy } from 'lodash-es';\n\nimport type { PrivacyRequest } from '../graphql/index.js';\n\nexport interface ExportedPrivacyRequest extends PrivacyRequest {\n /** Request identifiers */\n requestIdentifiers: RequestIdentifier[];\n}\n\n/** A single CSV row */\nexport type CsvRow = { [k in string]: string | null | number | boolean };\n\n/**\n * Format a single privacy request (with optional identifiers) into a flat CSV row.\n *\n * @param request - The request with identifiers attached\n * @returns Flat object suitable for CSV output\n */\nexport function formatRequestForCsv({\n attributeValues,\n requestIdentifiers,\n id,\n email,\n type,\n status,\n subjectType,\n details,\n createdAt,\n successfullyCompletedAt,\n country,\n locale,\n origin,\n countrySubDivision,\n isSilent,\n isTest,\n coreIdentifier,\n purpose,\n ...request\n}: ExportedPrivacyRequest): CsvRow {\n return {\n 'Request ID': id,\n 'Created At': createdAt,\n 'Successfully Completed At': successfullyCompletedAt || '',\n Email: email,\n 'Core Identifier': coreIdentifier,\n 'Request Type': type,\n 'Data Subject Type': subjectType,\n Status: status,\n Country: country,\n 'Country Sub Division': countrySubDivision,\n Details: details,\n Origin: origin,\n 'Silent Mode': isSilent,\n 'Is Test Request': isTest,\n Language: locale,\n 'Purpose Trigger Name': purpose?.title || purpose?.name || '',\n 'Purpose Trigger Value': purpose?.consent?.toString() || '',\n ...(purpose?.enrichedPreferences || []).reduce((acc: Record<string, string | boolean>, p) => {\n const title = p.preferenceTopic?.title.defaultMessage || p.name;\n return title\n ? {\n ...acc,\n [title]: p.selectValues\n ? p.selectValues.map((x) => x.name).join(';')\n : p.selectValue?.name || p.booleanValue,\n }\n : acc;\n }, {}),\n ...request,\n ...Object.entries(groupBy(attributeValues, 'attributeKey.name')).reduce(\n (acc, [name, values]) =>\n Object.assign(acc, {\n [name]: values.map(({ name: n }) => n).join(','),\n }),\n {},\n ),\n ...Object.entries(groupBy(requestIdentifiers, 'name')).reduce(\n (acc, [name, values]) =>\n Object.assign(acc, {\n [name]: values.map(({ value }) => value).join(','),\n }),\n {},\n ),\n };\n}\n","export interface ChunkedDateRange {\n /** Chunk start */\n createdAtAfter: Date;\n /** Chunk end */\n createdAtBefore: Date;\n}\n\n/**\n * Split a date range into N evenly-spaced chunks.\n *\n * @param after - Start of the date range\n * @param before - End of the date range\n * @param chunks - Number of chunks to split into\n * @returns Array of date range bounds\n */\nexport function splitDateRange(after: Date, before: Date, chunks: number): ChunkedDateRange[] {\n const startMs = after.getTime();\n const endMs = before.getTime();\n const chunkSizeMs = (endMs - startMs) / chunks;\n return Array.from({ length: chunks }, (_, i) => ({\n createdAtAfter: new Date(startMs + chunkSizeMs * i),\n createdAtBefore: new Date(i === chunks - 1 ? endMs : startMs + chunkSizeMs * (i + 1)),\n }));\n}\n","import { RequestAction, RequestStatus } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n createSombraGotInstance,\n fetchAllRequestIdentifiers,\n validateSombraVersion,\n} from '@transcend-io/sdk';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport { uniq } from 'lodash-es';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { fetchAllRequests, fetchRequestsTotalCount } from '../graphql/index.js';\nimport { initCsvFile, appendCsvRowsOrdered, parseFilePath } from '../helpers/index.js';\nimport { formatRequestForCsv, ExportedPrivacyRequest } from './formatRequestForCsv.js';\nimport { splitDateRange } from './splitDateRange.js';\n\n/**\n * Stream privacy requests directly to CSV files, one file per date-range chunk.\n * Memory stays bounded to a single page of results at a time.\n * Supports both with and without request identifier enrichment.\n *\n * @param options - Options\n * @returns The list of written file paths and total row count\n */\nexport async function streamPrivacyRequestsToCsv({\n auth,\n sombraAuth,\n actions = [],\n statuses = [],\n identifierSearch,\n concurrency = 1,\n pageLimit = 100,\n transcendUrl = DEFAULT_TRANSCEND_API,\n createdAtBefore,\n createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n isTest,\n skipRequestIdentifiers = false,\n file,\n}: {\n /** Transcend API key authentication */\n auth: string;\n /** Search for a specific identifier */\n identifierSearch?: string;\n /** Sombra API key authentication */\n sombraAuth?: string;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Statuses to filter on */\n statuses?: RequestStatus[];\n /** The request action to fetch */\n actions?: RequestAction[];\n /** Number of parallel date-range chunks */\n concurrency?: number;\n /** Concurrency for fetching identifiers per page */\n pageLimit?: number;\n /** Filter for requests created before this date */\n createdAtBefore?: Date;\n /** Filter for requests created after this date */\n createdAtAfter?: Date;\n /** Filter for requests updated before this date */\n updatedAtBefore?: Date;\n /** Filter for requests updated after this date */\n updatedAtAfter?: Date;\n /** Return test requests */\n isTest?: boolean;\n /** Skip fetching request identifiers */\n skipRequestIdentifiers?: boolean;\n /** Output CSV file path */\n file: string;\n}): Promise<{\n /** Paths to written CSV files */\n filePaths: string[];\n /** Total rows written */\n totalCount: number;\n}> {\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n const sombra = skipRequestIdentifiers\n ? undefined\n : await createSombraGotInstance(transcendUrl, auth, {\n logger,\n sombraApiKey: sombraAuth,\n sombraUrl: process.env.SOMBRA_URL,\n });\n\n // Log date range\n let dateRange = '';\n if (createdAtBefore) {\n dateRange += ` before ${createdAtBefore.toISOString()}`;\n }\n if (createdAtAfter) {\n dateRange += `${dateRange ? ', and' : ''} after ${createdAtAfter.toISOString()}`;\n }\n logger.info(\n colors.magenta(\n `${\n actions.length > 0\n ? `Pulling requests of type \"${actions.join('\" , \"')}\"`\n : 'Pulling all requests'\n }${dateRange}`,\n ),\n );\n\n // Split into parallel date-range chunks when possible\n const useChunks = concurrency > 1 && createdAtAfter && createdAtBefore;\n const chunks = useChunks\n ? splitDateRange(createdAtAfter, createdAtBefore, concurrency)\n : [{ createdAtAfter, createdAtBefore }];\n\n if (useChunks) {\n logger.info(colors.magenta(`Splitting date range into ${concurrency} parallel chunks`));\n }\n\n // Fetch total count once for the shared progress bar\n const filterBy = {\n type: actions.length > 0 ? actions : undefined,\n status: statuses.length > 0 ? statuses : undefined,\n isTest,\n createdAtBefore: createdAtBefore ? createdAtBefore.toISOString() : undefined,\n createdAtAfter: createdAtAfter ? createdAtAfter.toISOString() : undefined,\n updatedAtBefore: updatedAtBefore ? updatedAtBefore.toISOString() : undefined,\n updatedAtAfter: updatedAtAfter ? updatedAtAfter.toISOString() : undefined,\n };\n\n const t0 = Date.now();\n\n // Validate Sombra version once before bulk-fetching identifiers\n if (!skipRequestIdentifiers) {\n await validateSombraVersion(client, { logger });\n }\n\n const totalExpected = await fetchRequestsTotalCount(client, filterBy);\n logger.info(colors.magenta(`Fetching ${totalExpected} requests`));\n\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n progressBar.start(totalExpected, 0);\n\n let globalFetched = 0;\n\n const { baseName, extension } = parseFilePath(file);\n\n const filePaths = chunks.map((_, i) =>\n chunks.length === 1 ? file : `${baseName}-${i}${extension}`,\n );\n\n interface FailedChunk {\n /** Chunk index */\n index: number;\n /** Start of failed date range */\n createdAtAfter?: Date;\n /** End of failed date range */\n createdAtBefore?: Date;\n /** Error message */\n error: string;\n }\n\n const failedChunks: FailedChunk[] = [];\n\n const chunkCounts = await map(\n chunks,\n async (chunk, i) => {\n const chunkFile = filePaths[i];\n let headers: string[] | undefined;\n let rowCount = 0;\n\n try {\n await fetchAllRequests(client, {\n actions,\n text: identifierSearch,\n statuses,\n createdAtBefore: chunk.createdAtBefore,\n createdAtAfter: chunk.createdAtAfter,\n updatedAtBefore,\n updatedAtAfter,\n isTest,\n onPage: async (nodes) => {\n if (nodes.length === 0) return;\n\n // Optionally enrich each request with its identifiers\n const enriched: ExportedPrivacyRequest[] = skipRequestIdentifiers\n ? nodes.map((n) => ({ ...n, requestIdentifiers: [] }))\n : await map(\n nodes,\n async (n) => ({\n ...n,\n requestIdentifiers: await fetchAllRequestIdentifiers(client, sombra!, {\n filterBy: { requestId: n.id },\n skipSombraCheck: true,\n logger,\n }),\n }),\n { concurrency: pageLimit },\n );\n\n const rows: Record<string, string | null | number | boolean>[] =\n enriched.map(formatRequestForCsv);\n\n if (!headers) {\n headers = uniq(rows.map((r: Record<string, unknown>) => Object.keys(r)).flat());\n initCsvFile(chunkFile, headers);\n }\n\n appendCsvRowsOrdered(chunkFile, rows, headers);\n rowCount += rows.length;\n globalFetched += rows.length;\n progressBar.update(globalFetched);\n },\n });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n logger.error(\n colors.red(\n `Chunk ${i} failed (${\n chunk.createdAtAfter?.toISOString() ?? 'start'\n } → ${chunk.createdAtBefore?.toISOString() ?? 'end'}): ${message}`,\n ),\n );\n failedChunks.push({\n index: i,\n createdAtAfter: chunk.createdAtAfter,\n createdAtBefore: chunk.createdAtBefore,\n error: message,\n });\n }\n\n if (!headers) {\n initCsvFile(chunkFile, []);\n }\n\n return rowCount;\n },\n { concurrency: useChunks ? concurrency : 1 },\n );\n\n progressBar.stop();\n const totalCount = chunkCounts.reduce((a, b) => a + b, 0);\n const elapsed = (Date.now() - t0) / 1000;\n\n if (failedChunks.length > 0) {\n logger.error(\n colors.red(\n `\\n${failedChunks.length} chunk(s) failed. ` +\n 'Re-run with these date ranges to fill the gaps:',\n ),\n );\n for (const fc of failedChunks) {\n logger.error(\n colors.red(\n ` Chunk ${fc.index}: --createdAtAfter=${\n fc.createdAtAfter?.toISOString() ?? ''\n } --createdAtBefore=${fc.createdAtBefore?.toISOString() ?? ''}`,\n ),\n );\n }\n }\n\n logger.info(\n colors.green(`Streamed ${totalCount} requests to ${filePaths.length} file(s) in ${elapsed}s`),\n );\n\n return { filePaths, totalCount };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{t as e}from"./logger-
|
|
2
|
-
//# sourceMappingURL=syncCodePackages-
|
|
1
|
+
import{t as e}from"./logger-9T6vva73.mjs";import{chunk as t,keyBy as n,uniq as r,uniqBy as i}from"lodash-es";import a from"colors";import{CREATE_CODE_PACKAGE as o,UPDATE_CODE_PACKAGES as s,fetchAllCodePackages as c,makeGraphQLRequest as l,syncRepositories as u,syncSoftwareDevelopmentKits as d}from"@transcend-io/sdk";import{map as f,mapSeries as p}from"@transcend-io/utils";const m=`%%%%`;async function h(t,n){let{createCodePackage:{codePackage:r}}=await l(t,o,{variables:{input:n},logger:e});return e.info(a.green(`Successfully created code package "${n.name}"!`)),r}async function g(t,n){let{updateCodePackages:{codePackages:r}}=await l(t,s,{variables:{input:{codePackages:n}},logger:e});return e.info(a.green(`Successfully updated ${n.length} code packages!`)),r}async function _(o,s,l=20){let _=!1,[v,{softwareDevelopmentKits:y}]=await Promise.all([c(o,{logger:e}),d(o,i(s.map(({type:e,softwareDevelopmentKits:t=[]})=>t.map(({name:t})=>({name:t,codePackageType:e}))).flat(),({name:e,codePackageType:t})=>`${e}${m}${t}`),{logger:e,concurrency:l}),u(o,i(s,`repositoryName`).map(({repositoryName:e})=>({name:e,url:`https://github.com/${e}`})),{logger:e})]),b=n(y,({name:e,codePackageType:t})=>`${e}${m}${t}`),x=n(v,({name:e,type:t})=>`${e}${m}${t}`),S=s.map(e=>[e,x[`${e.name}${m}${e.type}`]?.id]),C=S.filter(([,e])=>!e).map(([e])=>e);try{e.info(a.magenta(`Creating "${C.length}" new code packages...`)),await f(C,async({softwareDevelopmentKits:e,...t})=>{await h(o,{...t,...e?{softwareDevelopmentKitIds:r(e.map(({name:e})=>{let n=b[`${e}${m}${t.type}`];if(!n)throw Error(`Failed to find SDK with name: "${e}"`);return n.id}))}:{}})},{concurrency:l}),e.info(a.green(`Successfully synced ${C.length} code packages!`))}catch(t){_=!0,e.error(a.red(`Failed to create code packages! - ${t.message}`))}let w=S.filter(e=>!!e[1]);return e.info(a.magenta(`Updating "${w.length}" code packages...`)),await p(t(w,100),async t=>{try{await g(o,t.map(([{softwareDevelopmentKits:e,repositoryName:t,...n},i])=>({...n,...e?{softwareDevelopmentKitIds:r(e.map(({name:e})=>{let t=b[`${e}${m}${n.type}`];if(!t)throw Error(`Failed to find SDK with name: "${e}"`);return t.id}))}:{},id:i}))),e.info(a.green(`Successfully updated "${t.length}" code packages!`))}catch(t){_=!0,e.error(a.red(`Failed to update code packages! - ${t.message}`))}}),e.info(a.green(`Synced "${s.length}" code packages!`)),!_}export{_ as n,g as r,h as t};
|
|
2
|
+
//# sourceMappingURL=syncCodePackages-DsC1tE8B.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncCodePackages-ozt9aBXf.mjs","names":[],"sources":["../src/lib/graphql/syncCodePackages.ts"],"sourcesContent":["import { CodePackageType } from '@transcend-io/privacy-types';\nimport {\n CREATE_CODE_PACKAGE,\n UPDATE_CODE_PACKAGES,\n fetchAllCodePackages,\n type CodePackage,\n makeGraphQLRequest,\n syncRepositories,\n syncSoftwareDevelopmentKits,\n} from '@transcend-io/sdk';\nimport { map, mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, uniq, keyBy, uniqBy } from 'lodash-es';\n\nimport { CodePackageInput, RepositoryInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\n\nconst CHUNK_SIZE = 100;\n\nconst LOOKUP_SPLIT_KEY = '%%%%';\n\n/**\n * Create a new code package\n *\n * @param client - GraphQL client\n * @param input - Code package input\n * @returns Code package ID\n */\nexport async function createCodePackage(\n client: GraphQLClient,\n input: {\n /** Name of package */\n name: string;\n /** Description of package */\n description?: string;\n /** Type of package */\n type: CodePackageType;\n /** Relative path to package */\n relativePath: string;\n /** Repository ID */\n repositoryId?: string;\n /** Name of repository */\n repositoryName?: string;\n /** IDs of SDKs */\n softwareDevelopmentKitIds?: string[];\n /** IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** IDs of teams */\n teamIds?: string[];\n /** Names of teams */\n teamNames?: string[];\n },\n): Promise<CodePackage> {\n const {\n createCodePackage: { codePackage },\n } = await makeGraphQLRequest<{\n /** createCodePackage mutation */\n createCodePackage: {\n /** Code package */\n codePackage: CodePackage;\n };\n }>(client, CREATE_CODE_PACKAGE, {\n variables: { input },\n logger,\n });\n logger.info(colors.green(`Successfully created code package \"${input.name}\"!`));\n return codePackage;\n}\n\n/**\n * Update an existing code package\n *\n * @param client - GraphQL client\n * @param inputs - Code package input\n * @returns Code packages that were updated\n */\nexport async function updateCodePackages(\n client: GraphQLClient,\n inputs: {\n /** ID of code package */\n id: string;\n /** Name of package */\n name: string;\n /** Description of package */\n description?: string;\n /** Type of package */\n type: CodePackageType;\n /** Relative path to package */\n relativePath: string;\n /** Repository ID */\n repositoryId?: string;\n /** Name of repository */\n repositoryName?: string;\n /** IDs of SDKs */\n softwareDevelopmentKitIds?: string[];\n /** IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** IDs of teams */\n teamIds?: string[];\n /** Names of teams */\n teamNames?: string[];\n }[],\n): Promise<CodePackage[]> {\n const {\n updateCodePackages: { codePackages },\n } = await makeGraphQLRequest<{\n /** updateCodePackages mutation */\n updateCodePackages: {\n /** Code packages */\n codePackages: CodePackage[];\n };\n }>(client, UPDATE_CODE_PACKAGES, {\n variables: {\n input: {\n codePackages: inputs,\n },\n },\n logger,\n });\n logger.info(colors.green(`Successfully updated ${inputs.length} code packages!`));\n return codePackages;\n}\n\n/**\n * Uploads silo discovery results for Transcend to classify\n *\n * @param client - GraphQL Client\n * @param codePackages - Packages to upload\n * @param concurrency - How many concurrent requests to make\n * @returns True if successful, false if any updates failed, or an error occurs\n */\nexport async function syncCodePackages(\n client: GraphQLClient,\n codePackages: CodePackageInput[],\n concurrency = 20,\n): Promise<boolean> {\n let encounteredError = false;\n const [existingCodePackages, { softwareDevelopmentKits: existingSoftwareDevelopmentKits }] =\n await Promise.all([\n // fetch all code packages\n fetchAllCodePackages(client, { logger }),\n // make sure all SDKs exist\n syncSoftwareDevelopmentKits(\n client,\n uniqBy(\n codePackages\n .map(({ type, softwareDevelopmentKits = [] }) =>\n softwareDevelopmentKits.map(({ name }) => ({\n name,\n codePackageType: type,\n })),\n )\n .flat(),\n ({ name, codePackageType }) => `${name}${LOOKUP_SPLIT_KEY}${codePackageType}`,\n ),\n { logger, concurrency },\n ),\n // make sure all Repositories exist\n syncRepositories(\n client,\n uniqBy(codePackages, 'repositoryName').map(\n ({ repositoryName }) =>\n ({\n name: repositoryName,\n url: `https://github.com/${repositoryName}`,\n }) as RepositoryInput,\n ),\n { logger },\n ),\n ]);\n\n const softwareDevelopmentKitLookup = keyBy(\n existingSoftwareDevelopmentKits,\n ({ name, codePackageType }) => `${name}${LOOKUP_SPLIT_KEY}${codePackageType}`,\n );\n const codePackagesLookup = keyBy(\n existingCodePackages,\n ({ name, type }) => `${name}${LOOKUP_SPLIT_KEY}${type}`,\n );\n\n // Determine which codePackages are new vs existing\n const mapCodePackagesToExisting = codePackages.map((codePackageInput) => [\n codePackageInput,\n codePackagesLookup[`${codePackageInput.name}${LOOKUP_SPLIT_KEY}${codePackageInput.type}`]?.id,\n ]);\n\n // Create the new codePackages\n const newCodePackages = mapCodePackagesToExisting\n .filter(([, existing]) => !existing)\n .map(([codePackageInput]) => codePackageInput as CodePackageInput);\n try {\n logger.info(colors.magenta(`Creating \"${newCodePackages.length}\" new code packages...`));\n await map(\n newCodePackages,\n async ({ softwareDevelopmentKits, ...codePackage }) => {\n await createCodePackage(client, {\n ...codePackage,\n ...(softwareDevelopmentKits\n ? {\n softwareDevelopmentKitIds: uniq(\n softwareDevelopmentKits.map(({ name }) => {\n const sdk =\n softwareDevelopmentKitLookup[`${name}${LOOKUP_SPLIT_KEY}${codePackage.type}`];\n if (!sdk) {\n throw new Error(`Failed to find SDK with name: \"${name}\"`);\n }\n return sdk.id;\n }),\n ),\n }\n : {}),\n });\n },\n {\n concurrency,\n },\n );\n logger.info(colors.green(`Successfully synced ${newCodePackages.length} code packages!`));\n } catch (err) {\n encounteredError = true;\n logger.error(colors.red(`Failed to create code packages! - ${err.message}`));\n }\n\n // Update existing codePackages\n const existingCodePackageInputs = mapCodePackagesToExisting.filter(\n (x): x is [CodePackageInput, string] => !!x[1],\n );\n logger.info(colors.magenta(`Updating \"${existingCodePackageInputs.length}\" code packages...`));\n const chunks = chunk(existingCodePackageInputs, CHUNK_SIZE);\n\n await mapSeries(chunks, async (chunk) => {\n try {\n await updateCodePackages(\n client,\n chunk.map(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ([{ softwareDevelopmentKits, repositoryName, ...input }, id]) => ({\n ...input,\n ...(softwareDevelopmentKits\n ? {\n softwareDevelopmentKitIds: uniq(\n softwareDevelopmentKits.map(({ name }) => {\n const sdk =\n softwareDevelopmentKitLookup[`${name}${LOOKUP_SPLIT_KEY}${input.type}`];\n if (!sdk) {\n throw new Error(`Failed to find SDK with name: \"${name}\"`);\n }\n return sdk.id;\n }),\n ),\n }\n : {}),\n id,\n }),\n ),\n );\n logger.info(colors.green(`Successfully updated \"${chunk.length}\" code packages!`));\n } catch (err) {\n encounteredError = true;\n logger.error(colors.red(`Failed to update code packages! - ${err.message}`));\n }\n });\n\n logger.info(colors.green(`Synced \"${codePackages.length}\" code packages!`));\n return !encounteredError;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"syncCodePackages-DsC1tE8B.mjs","names":[],"sources":["../src/lib/graphql/syncCodePackages.ts"],"sourcesContent":["import { CodePackageType } from '@transcend-io/privacy-types';\nimport {\n CREATE_CODE_PACKAGE,\n UPDATE_CODE_PACKAGES,\n fetchAllCodePackages,\n type CodePackage,\n makeGraphQLRequest,\n syncRepositories,\n syncSoftwareDevelopmentKits,\n} from '@transcend-io/sdk';\nimport { map, mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\nimport { GraphQLClient } from 'graphql-request';\nimport { chunk, uniq, keyBy, uniqBy } from 'lodash-es';\n\nimport { CodePackageInput, RepositoryInput } from '../../codecs.js';\nimport { logger } from '../../logger.js';\n\nconst CHUNK_SIZE = 100;\n\nconst LOOKUP_SPLIT_KEY = '%%%%';\n\n/**\n * Create a new code package\n *\n * @param client - GraphQL client\n * @param input - Code package input\n * @returns Code package ID\n */\nexport async function createCodePackage(\n client: GraphQLClient,\n input: {\n /** Name of package */\n name: string;\n /** Description of package */\n description?: string;\n /** Type of package */\n type: CodePackageType;\n /** Relative path to package */\n relativePath: string;\n /** Repository ID */\n repositoryId?: string;\n /** Name of repository */\n repositoryName?: string;\n /** IDs of SDKs */\n softwareDevelopmentKitIds?: string[];\n /** IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** IDs of teams */\n teamIds?: string[];\n /** Names of teams */\n teamNames?: string[];\n },\n): Promise<CodePackage> {\n const {\n createCodePackage: { codePackage },\n } = await makeGraphQLRequest<{\n /** createCodePackage mutation */\n createCodePackage: {\n /** Code package */\n codePackage: CodePackage;\n };\n }>(client, CREATE_CODE_PACKAGE, {\n variables: { input },\n logger,\n });\n logger.info(colors.green(`Successfully created code package \"${input.name}\"!`));\n return codePackage;\n}\n\n/**\n * Update an existing code package\n *\n * @param client - GraphQL client\n * @param inputs - Code package input\n * @returns Code packages that were updated\n */\nexport async function updateCodePackages(\n client: GraphQLClient,\n inputs: {\n /** ID of code package */\n id: string;\n /** Name of package */\n name: string;\n /** Description of package */\n description?: string;\n /** Type of package */\n type: CodePackageType;\n /** Relative path to package */\n relativePath: string;\n /** Repository ID */\n repositoryId?: string;\n /** Name of repository */\n repositoryName?: string;\n /** IDs of SDKs */\n softwareDevelopmentKitIds?: string[];\n /** IDs of owners */\n ownerIds?: string[];\n /** Emails of owners */\n ownerEmails?: string[];\n /** IDs of teams */\n teamIds?: string[];\n /** Names of teams */\n teamNames?: string[];\n }[],\n): Promise<CodePackage[]> {\n const {\n updateCodePackages: { codePackages },\n } = await makeGraphQLRequest<{\n /** updateCodePackages mutation */\n updateCodePackages: {\n /** Code packages */\n codePackages: CodePackage[];\n };\n }>(client, UPDATE_CODE_PACKAGES, {\n variables: {\n input: {\n codePackages: inputs,\n },\n },\n logger,\n });\n logger.info(colors.green(`Successfully updated ${inputs.length} code packages!`));\n return codePackages;\n}\n\n/**\n * Uploads silo discovery results for Transcend to classify\n *\n * @param client - GraphQL Client\n * @param codePackages - Packages to upload\n * @param concurrency - How many concurrent requests to make\n * @returns True if successful, false if any updates failed, or an error occurs\n */\nexport async function syncCodePackages(\n client: GraphQLClient,\n codePackages: CodePackageInput[],\n concurrency = 20,\n): Promise<boolean> {\n let encounteredError = false;\n const [existingCodePackages, { softwareDevelopmentKits: existingSoftwareDevelopmentKits }] =\n await Promise.all([\n // fetch all code packages\n fetchAllCodePackages(client, { logger }),\n // make sure all SDKs exist\n syncSoftwareDevelopmentKits(\n client,\n uniqBy(\n codePackages\n .map(({ type, softwareDevelopmentKits = [] }) =>\n softwareDevelopmentKits.map(({ name }) => ({\n name,\n codePackageType: type,\n })),\n )\n .flat(),\n ({ name, codePackageType }) => `${name}${LOOKUP_SPLIT_KEY}${codePackageType}`,\n ),\n { logger, concurrency },\n ),\n // make sure all Repositories exist\n syncRepositories(\n client,\n uniqBy(codePackages, 'repositoryName').map(\n ({ repositoryName }) =>\n ({\n name: repositoryName,\n url: `https://github.com/${repositoryName}`,\n }) as RepositoryInput,\n ),\n { logger },\n ),\n ]);\n\n const softwareDevelopmentKitLookup = keyBy(\n existingSoftwareDevelopmentKits,\n ({ name, codePackageType }) => `${name}${LOOKUP_SPLIT_KEY}${codePackageType}`,\n );\n const codePackagesLookup = keyBy(\n existingCodePackages,\n ({ name, type }) => `${name}${LOOKUP_SPLIT_KEY}${type}`,\n );\n\n // Determine which codePackages are new vs existing\n const mapCodePackagesToExisting = codePackages.map((codePackageInput) => [\n codePackageInput,\n codePackagesLookup[`${codePackageInput.name}${LOOKUP_SPLIT_KEY}${codePackageInput.type}`]?.id,\n ]);\n\n // Create the new codePackages\n const newCodePackages = mapCodePackagesToExisting\n .filter(([, existing]) => !existing)\n .map(([codePackageInput]) => codePackageInput as CodePackageInput);\n try {\n logger.info(colors.magenta(`Creating \"${newCodePackages.length}\" new code packages...`));\n await map(\n newCodePackages,\n async ({ softwareDevelopmentKits, ...codePackage }) => {\n await createCodePackage(client, {\n ...codePackage,\n ...(softwareDevelopmentKits\n ? {\n softwareDevelopmentKitIds: uniq(\n softwareDevelopmentKits.map(({ name }) => {\n const sdk =\n softwareDevelopmentKitLookup[`${name}${LOOKUP_SPLIT_KEY}${codePackage.type}`];\n if (!sdk) {\n throw new Error(`Failed to find SDK with name: \"${name}\"`);\n }\n return sdk.id;\n }),\n ),\n }\n : {}),\n });\n },\n {\n concurrency,\n },\n );\n logger.info(colors.green(`Successfully synced ${newCodePackages.length} code packages!`));\n } catch (err) {\n encounteredError = true;\n logger.error(colors.red(`Failed to create code packages! - ${err.message}`));\n }\n\n // Update existing codePackages\n const existingCodePackageInputs = mapCodePackagesToExisting.filter(\n (x): x is [CodePackageInput, string] => !!x[1],\n );\n logger.info(colors.magenta(`Updating \"${existingCodePackageInputs.length}\" code packages...`));\n const chunks = chunk(existingCodePackageInputs, CHUNK_SIZE);\n\n await mapSeries(chunks, async (chunk) => {\n try {\n await updateCodePackages(\n client,\n chunk.map(\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n ([{ softwareDevelopmentKits, repositoryName, ...input }, id]) => ({\n ...input,\n ...(softwareDevelopmentKits\n ? {\n softwareDevelopmentKitIds: uniq(\n softwareDevelopmentKits.map(({ name }) => {\n const sdk =\n softwareDevelopmentKitLookup[`${name}${LOOKUP_SPLIT_KEY}${input.type}`];\n if (!sdk) {\n throw new Error(`Failed to find SDK with name: \"${name}\"`);\n }\n return sdk.id;\n }),\n ),\n }\n : {}),\n id,\n }),\n ),\n );\n logger.info(colors.green(`Successfully updated \"${chunk.length}\" code packages!`));\n } catch (err) {\n encounteredError = true;\n logger.error(colors.red(`Failed to update code packages! - ${err.message}`));\n }\n });\n\n logger.info(colors.green(`Synced \"${codePackages.length}\" code packages!`));\n return !encounteredError;\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e}from"./constants-
|
|
2
|
-
//# sourceMappingURL=updateConsentManagerVersionToLatest-
|
|
1
|
+
import{a as e}from"./constants-BG53Mbi9.mjs";import{t}from"./logger-9T6vva73.mjs";import{ConsentBundleType as n}from"@transcend-io/privacy-types";import r from"colors";import{buildTranscendGraphQLClient as i,deployConsentManager as a,fetchConsentManagerId as o,updateConsentManagerToLatest as s}from"@transcend-io/sdk";import{mapSeries as c}from"@transcend-io/utils";async function l({auth:l,deploy:u=!1,transcendUrl:d=e,bundleTypes:f=Object.values(n)}){let p=i(d,l),m=await o(p,{logger:t});await c(f,async e=>{t.info(r.magenta(`Update Consent Manager bundle with ID "${m}" and type "${e}" to latest version...`)),await s(p,{input:{id:m,bundleType:e},logger:t}),t.info(r.green(`Updated Consent Manager bundle with ID "${m}" and type "${e}" to latest version!`))}),u&&await c(f,async e=>{t.info(r.magenta(`Deploying Consent Manager bundle with ID "${m}" and type "${e}"...`)),await a(p,{id:m,bundleType:e},{logger:t}),t.info(r.green(`Deployed Consent Manager bundle with ID "${m}" and type "${e}"!`))})}export{l as t};
|
|
2
|
+
//# sourceMappingURL=updateConsentManagerVersionToLatest-CaEkTnlL.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateConsentManagerVersionToLatest-
|
|
1
|
+
{"version":3,"file":"updateConsentManagerVersionToLatest-CaEkTnlL.mjs","names":[],"sources":["../src/lib/consent-manager/updateConsentManagerVersionToLatest.ts"],"sourcesContent":["import { ConsentBundleType } from '@transcend-io/privacy-types';\nimport {\n buildTranscendGraphQLClient,\n deployConsentManager,\n fetchConsentManagerId,\n updateConsentManagerToLatest,\n} from '@transcend-io/sdk';\nimport { mapSeries } from '@transcend-io/utils';\nimport colors from 'colors';\n\nimport { DEFAULT_TRANSCEND_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\n\n/**\n * Update the consent manager to latest version\n *\n * @param options - Options\n */\nexport async function updateConsentManagerVersionToLatest({\n auth,\n deploy = false,\n transcendUrl = DEFAULT_TRANSCEND_API,\n bundleTypes = Object.values(ConsentBundleType),\n}: {\n /** Transcend API key authentication */\n auth: string;\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Deploy consent manager with this update */\n deploy?: boolean;\n /** The bundle types to update and deploy */\n bundleTypes?: ConsentBundleType[];\n}): Promise<void> {\n // Find all requests made before createdAt that are in a removing data state\n const client = buildTranscendGraphQLClient(transcendUrl, auth);\n\n // Grab Consent Manager ID\n const consentManagerId = await fetchConsentManagerId(client, { logger });\n\n // Update each bundle type to latest version\n await mapSeries(bundleTypes, async (bundleType) => {\n logger.info(\n colors.magenta(\n `Update Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\" to latest version...`,\n ),\n );\n await updateConsentManagerToLatest(client, {\n input: {\n id: consentManagerId,\n bundleType,\n },\n logger,\n });\n logger.info(\n colors.green(\n `Updated Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\" to latest version!`,\n ),\n );\n });\n\n // deploy Consent Managers\n if (deploy) {\n // Update each bundle type to latest version\n await mapSeries(bundleTypes, async (bundleType) => {\n logger.info(\n colors.magenta(\n `Deploying Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\"...`,\n ),\n );\n await deployConsentManager(\n client,\n {\n id: consentManagerId,\n bundleType,\n },\n { logger },\n );\n logger.info(\n colors.green(\n `Deployed Consent Manager bundle with ID \"${consentManagerId}\" and type \"${bundleType}\"!`,\n ),\n );\n });\n }\n}\n"],"mappings":"+WAkBA,eAAsB,EAAoC,CACxD,OACA,SAAS,GACT,eAAe,EACf,cAAc,OAAO,OAAO,EAAkB,EAU9B,CAEhB,IAAM,EAAS,EAA4B,EAAc,EAAK,CAGxD,EAAmB,MAAM,EAAsB,EAAQ,CAAE,SAAQ,CAAC,CAGxE,MAAM,EAAU,EAAa,KAAO,IAAe,CACjD,EAAO,KACL,EAAO,QACL,0CAA0C,EAAiB,cAAc,EAAW,wBACrF,CACF,CACD,MAAM,EAA6B,EAAQ,CACzC,MAAO,CACL,GAAI,EACJ,aACD,CACD,SACD,CAAC,CACF,EAAO,KACL,EAAO,MACL,2CAA2C,EAAiB,cAAc,EAAW,sBACtF,CACF,EACD,CAGE,GAEF,MAAM,EAAU,EAAa,KAAO,IAAe,CACjD,EAAO,KACL,EAAO,QACL,6CAA6C,EAAiB,cAAc,EAAW,MACxF,CACF,CACD,MAAM,EACJ,EACA,CACE,GAAI,EACJ,aACD,CACD,CAAE,SAAQ,CACX,CACD,EAAO,KACL,EAAO,MACL,4CAA4C,EAAiB,cAAc,EAAW,IACvF,CACF,EACD"}
|