@transcend-io/cli 12.1.5 → 13.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +64 -14
  2. package/dist/{app-BMLQABJX.mjs → app-CCGKgPiC.mjs} +19 -19
  3. package/dist/app-CCGKgPiC.mjs.map +1 -0
  4. package/dist/{approvePrivacyRequests-CY6rQT4J.mjs → approvePrivacyRequests-BBcqydVb.mjs} +2 -2
  5. package/dist/{approvePrivacyRequests-CY6rQT4J.mjs.map → approvePrivacyRequests-BBcqydVb.mjs.map} +1 -1
  6. package/dist/bin/bash-complete.mjs +1 -1
  7. package/dist/bin/cli.mjs +1 -1
  8. package/dist/bin/deprecated-command.mjs +1 -1
  9. package/dist/{buildExamples-BNGtj-DW.mjs → buildExamples-DRU0o6EK.mjs} +2 -2
  10. package/dist/{buildExamples-BNGtj-DW.mjs.map → buildExamples-DRU0o6EK.mjs.map} +1 -1
  11. package/dist/{buildXdiSyncEndpoint-BNCfOc-g.mjs → buildXdiSyncEndpoint-CSjQKSn3.mjs} +2 -2
  12. package/dist/{buildXdiSyncEndpoint-BNCfOc-g.mjs.map → buildXdiSyncEndpoint-CSjQKSn3.mjs.map} +1 -1
  13. package/dist/{bulkRestartRequests-CGvODu3r.mjs → bulkRestartRequests-Cx5LfpdX.mjs} +2 -2
  14. package/dist/{bulkRestartRequests-CGvODu3r.mjs.map → bulkRestartRequests-Cx5LfpdX.mjs.map} +1 -1
  15. package/dist/{bulkRetryEnrichers-BgDd4GWH.mjs → bulkRetryEnrichers-BSJtvqJD.mjs} +2 -2
  16. package/dist/{bulkRetryEnrichers-BgDd4GWH.mjs.map → bulkRetryEnrichers-BSJtvqJD.mjs.map} +1 -1
  17. package/dist/{cancelPrivacyRequests-Ccsiwrnu.mjs → cancelPrivacyRequests-Cy48R4tJ.mjs} +2 -2
  18. package/dist/{cancelPrivacyRequests-Ccsiwrnu.mjs.map → cancelPrivacyRequests-Cy48R4tJ.mjs.map} +1 -1
  19. package/dist/codecs-8T8Jhxsr.mjs +2 -0
  20. package/dist/codecs-8T8Jhxsr.mjs.map +1 -0
  21. package/dist/{command-BjzdoKgJ.mjs → command-Gip5wB8W.mjs} +2 -2
  22. package/dist/{command-BjzdoKgJ.mjs.map → command-Gip5wB8W.mjs.map} +1 -1
  23. package/dist/constants-D6y_AX3P.mjs +2 -0
  24. package/dist/constants-D6y_AX3P.mjs.map +1 -0
  25. package/dist/{constants-FDjw7bPS.mjs → constants-DAx9VK1C.mjs} +2 -2
  26. package/dist/{constants-FDjw7bPS.mjs.map → constants-DAx9VK1C.mjs.map} +1 -1
  27. package/dist/{createPoolingCommandUi-BdtnLBBM.mjs → createPoolingCommandUi-DZx16Cn8.mjs} +2 -2
  28. package/dist/{createPoolingCommandUi-BdtnLBBM.mjs.map → createPoolingCommandUi-DZx16Cn8.mjs.map} +1 -1
  29. package/dist/{downloadPrivacyRequestFiles-C9-xSCpt.mjs → downloadPrivacyRequestFiles-DBeuW9b3.mjs} +2 -2
  30. package/dist/{downloadPrivacyRequestFiles-C9-xSCpt.mjs.map → downloadPrivacyRequestFiles-DBeuW9b3.mjs.map} +1 -1
  31. package/dist/fetchAllRequests-BV9K0wXv.mjs +2 -0
  32. package/dist/fetchAllRequests-BV9K0wXv.mjs.map +1 -0
  33. package/dist/{fuzzyMatchColumns-D18n9Beu.mjs → fuzzyMatchColumns-Djl3a7lV.mjs} +2 -2
  34. package/dist/{fuzzyMatchColumns-D18n9Beu.mjs.map → fuzzyMatchColumns-Djl3a7lV.mjs.map} +1 -1
  35. package/dist/{generateCrossAccountApiKeys-FtWwlf_L.mjs → generateCrossAccountApiKeys-CjmSyfwm.mjs} +2 -2
  36. package/dist/{generateCrossAccountApiKeys-FtWwlf_L.mjs.map → generateCrossAccountApiKeys-CjmSyfwm.mjs.map} +1 -1
  37. package/dist/{impl-Dg7XSQdO2.mjs → impl-7qTKg-YP2.mjs} +2 -2
  38. package/dist/{impl-Dg7XSQdO2.mjs.map → impl-7qTKg-YP2.mjs.map} +1 -1
  39. package/dist/{impl-D58JUOdA.mjs → impl-9W0QG6fB.mjs} +2 -2
  40. package/dist/{impl-D58JUOdA.mjs.map → impl-9W0QG6fB.mjs.map} +1 -1
  41. package/dist/{impl-B94dMG4x2.mjs → impl-B-BkGfqq2.mjs} +2 -2
  42. package/dist/{impl-B94dMG4x2.mjs.map → impl-B-BkGfqq2.mjs.map} +1 -1
  43. package/dist/{impl-yIVxAhCG2.mjs → impl-B8DsKzNn2.mjs} +2 -2
  44. package/dist/{impl-yIVxAhCG2.mjs.map → impl-B8DsKzNn2.mjs.map} +1 -1
  45. package/dist/impl-B9jwlKCX2.mjs +2 -0
  46. package/dist/impl-B9jwlKCX2.mjs.map +1 -0
  47. package/dist/{impl-C3vTm9zQ.mjs → impl-BIhXJ0Nw.mjs} +2 -2
  48. package/dist/{impl-C3vTm9zQ.mjs.map → impl-BIhXJ0Nw.mjs.map} +1 -1
  49. package/dist/{impl-B_N_fOq0.mjs → impl-BLM6rMBq.mjs} +2 -2
  50. package/dist/{impl-B_N_fOq0.mjs.map → impl-BLM6rMBq.mjs.map} +1 -1
  51. package/dist/{impl-Hj5CgbWd.mjs → impl-BPmzcgpy.mjs} +2 -2
  52. package/dist/{impl-Hj5CgbWd.mjs.map → impl-BPmzcgpy.mjs.map} +1 -1
  53. package/dist/{impl-COqlYiiw.mjs → impl-BUKV7sYp.mjs} +2 -2
  54. package/dist/{impl-COqlYiiw.mjs.map → impl-BUKV7sYp.mjs.map} +1 -1
  55. package/dist/{impl-DFnWzZyY.mjs → impl-BahDGEJJ.mjs} +2 -2
  56. package/dist/{impl-DFnWzZyY.mjs.map → impl-BahDGEJJ.mjs.map} +1 -1
  57. package/dist/{impl-qNF5eqX-.mjs → impl-BfU0GFmW.mjs} +1 -1
  58. package/dist/{impl-qNF5eqX-.mjs.map → impl-BfU0GFmW.mjs.map} +1 -1
  59. package/dist/{impl-DDVhG8Gf.mjs → impl-Bh1CkZhV.mjs} +2 -2
  60. package/dist/{impl-DDVhG8Gf.mjs.map → impl-Bh1CkZhV.mjs.map} +1 -1
  61. package/dist/{impl-BvwRiPSG.mjs → impl-BkQryDNq.mjs} +2 -2
  62. package/dist/{impl-BvwRiPSG.mjs.map → impl-BkQryDNq.mjs.map} +1 -1
  63. package/dist/{impl-bl6NF3Lr.mjs → impl-BsdneZZP.mjs} +2 -2
  64. package/dist/{impl-bl6NF3Lr.mjs.map → impl-BsdneZZP.mjs.map} +1 -1
  65. package/dist/{impl-D6rMLiNB2.mjs → impl-BuOc87b6.mjs} +2 -2
  66. package/dist/impl-BuOc87b6.mjs.map +1 -0
  67. package/dist/{impl-DUYRVzMo.mjs → impl-BzzkiK_b.mjs} +1 -1
  68. package/dist/{impl-DUYRVzMo.mjs.map → impl-BzzkiK_b.mjs.map} +1 -1
  69. package/dist/{impl-ClT3G845.mjs → impl-C2BYhujB.mjs} +2 -2
  70. package/dist/{impl-ClT3G845.mjs.map → impl-C2BYhujB.mjs.map} +1 -1
  71. package/dist/{impl-DnDM9zcu.mjs → impl-C5qif_gE.mjs} +2 -2
  72. package/dist/{impl-DnDM9zcu.mjs.map → impl-C5qif_gE.mjs.map} +1 -1
  73. package/dist/{impl-BLtOfbtk.mjs → impl-C8auyA7u.mjs} +2 -2
  74. package/dist/{impl-BLtOfbtk.mjs.map → impl-C8auyA7u.mjs.map} +1 -1
  75. package/dist/{impl-BEiCEzl_2.mjs → impl-CHoglMch.mjs} +1 -1
  76. package/dist/impl-CHoglMch.mjs.map +1 -0
  77. package/dist/{impl-QD3_9HTk.mjs → impl-CJ4oVuXN.mjs} +2 -2
  78. package/dist/{impl-QD3_9HTk.mjs.map → impl-CJ4oVuXN.mjs.map} +1 -1
  79. package/dist/{impl-DhXzxQDN2.mjs → impl-CRif3TpB2.mjs} +2 -2
  80. package/dist/{impl-DhXzxQDN2.mjs.map → impl-CRif3TpB2.mjs.map} +1 -1
  81. package/dist/{impl-Cvyc-iTI2.mjs → impl-CWINXCFq2.mjs} +2 -2
  82. package/dist/{impl-Cvyc-iTI2.mjs.map → impl-CWINXCFq2.mjs.map} +1 -1
  83. package/dist/{impl-Dd39KsYO.mjs → impl-C_lIkMLn2.mjs} +2 -2
  84. package/dist/impl-C_lIkMLn2.mjs.map +1 -0
  85. package/dist/{impl-EDwngLc7.mjs → impl-CcPwm8a4.mjs} +2 -2
  86. package/dist/{impl-EDwngLc7.mjs.map → impl-CcPwm8a4.mjs.map} +1 -1
  87. package/dist/{impl-DGtCNANL.mjs → impl-CjjiXsMh.mjs} +2 -2
  88. package/dist/{impl-DGtCNANL.mjs.map → impl-CjjiXsMh.mjs.map} +1 -1
  89. package/dist/{impl-BLOhoP-b2.mjs → impl-CzFfjx8n2.mjs} +2 -2
  90. package/dist/{impl-BLOhoP-b2.mjs.map → impl-CzFfjx8n2.mjs.map} +1 -1
  91. package/dist/{impl-BXGpJz21.mjs → impl-D6e2LNBm.mjs} +2 -2
  92. package/dist/{impl-BXGpJz21.mjs.map → impl-D6e2LNBm.mjs.map} +1 -1
  93. package/dist/{impl-SOebshza.mjs → impl-D8yF0jw9.mjs} +2 -2
  94. package/dist/{impl-SOebshza.mjs.map → impl-D8yF0jw9.mjs.map} +1 -1
  95. package/dist/{impl-DeCRXfAh2.mjs → impl-DEF-meSg2.mjs} +2 -2
  96. package/dist/{impl-DeCRXfAh2.mjs.map → impl-DEF-meSg2.mjs.map} +1 -1
  97. package/dist/{impl-DZoTWLfw2.mjs → impl-DEPuP4Oy2.mjs} +2 -2
  98. package/dist/{impl-DZoTWLfw2.mjs.map → impl-DEPuP4Oy2.mjs.map} +1 -1
  99. package/dist/impl-DGLrKNNs2.mjs +2 -0
  100. package/dist/impl-DGLrKNNs2.mjs.map +1 -0
  101. package/dist/{impl-XxYQHKO5.mjs → impl-DPq1O87s.mjs} +2 -2
  102. package/dist/{impl-XxYQHKO5.mjs.map → impl-DPq1O87s.mjs.map} +1 -1
  103. package/dist/{impl-ChQAmyBN.mjs → impl-DiaD11OP.mjs} +2 -2
  104. package/dist/{impl-ChQAmyBN.mjs.map → impl-DiaD11OP.mjs.map} +1 -1
  105. package/dist/{impl-0-_oxvck.mjs → impl-DioQW8jR.mjs} +2 -2
  106. package/dist/{impl-0-_oxvck.mjs.map → impl-DioQW8jR.mjs.map} +1 -1
  107. package/dist/{impl-D6frX-PK.mjs → impl-DjBbx05w.mjs} +2 -2
  108. package/dist/{impl-D6frX-PK.mjs.map → impl-DjBbx05w.mjs.map} +1 -1
  109. package/dist/{impl-BCKUj-ko.mjs → impl-DnBmd-wi.mjs} +2 -2
  110. package/dist/{impl-BCKUj-ko.mjs.map → impl-DnBmd-wi.mjs.map} +1 -1
  111. package/dist/{impl-Cp5o0Q2g.mjs → impl-Do3hnGHt.mjs} +2 -2
  112. package/dist/{impl-Cp5o0Q2g.mjs.map → impl-Do3hnGHt.mjs.map} +1 -1
  113. package/dist/{impl-VSo8bO-J.mjs → impl-DqbJ33ve.mjs} +2 -2
  114. package/dist/{impl-VSo8bO-J.mjs.map → impl-DqbJ33ve.mjs.map} +1 -1
  115. package/dist/{impl-Cb-7C1Ml2.mjs → impl-DvHiVaGW.mjs} +12 -105
  116. package/dist/impl-DvHiVaGW.mjs.map +1 -0
  117. package/dist/{impl-CTOgxZFi.mjs → impl-Dx8BYv7U.mjs} +2 -2
  118. package/dist/{impl-CTOgxZFi.mjs.map → impl-Dx8BYv7U.mjs.map} +1 -1
  119. package/dist/{impl-Bz1wZxa-.mjs → impl-GphhprmF.mjs} +2 -2
  120. package/dist/{impl-Bz1wZxa-.mjs.map → impl-GphhprmF.mjs.map} +1 -1
  121. package/dist/{impl-agm4lpyq.mjs → impl-HsRwcfRu.mjs} +2 -2
  122. package/dist/{impl-agm4lpyq.mjs.map → impl-HsRwcfRu.mjs.map} +1 -1
  123. package/dist/{impl-BHEAKFX02.mjs → impl-LT1FBKID2.mjs} +2 -2
  124. package/dist/{impl-BHEAKFX02.mjs.map → impl-LT1FBKID2.mjs.map} +1 -1
  125. package/dist/{impl-xY_5o78i.mjs → impl-Nwbb2xaH.mjs} +2 -2
  126. package/dist/{impl-xY_5o78i.mjs.map → impl-Nwbb2xaH.mjs.map} +1 -1
  127. package/dist/{impl-CSnBFv5P.mjs → impl-SKxxByoN.mjs} +2 -2
  128. package/dist/{impl-CSnBFv5P.mjs.map → impl-SKxxByoN.mjs.map} +1 -1
  129. package/dist/{impl-ojAnhhbi.mjs → impl-bUltX0u3.mjs} +2 -2
  130. package/dist/{impl-ojAnhhbi.mjs.map → impl-bUltX0u3.mjs.map} +1 -1
  131. package/dist/{impl-Bs2pNgMK.mjs → impl-cU3bjY3P.mjs} +2 -2
  132. package/dist/{impl-Bs2pNgMK.mjs.map → impl-cU3bjY3P.mjs.map} +1 -1
  133. package/dist/{impl-xVmMN-Vt.mjs → impl-h0z8OSQY.mjs} +2 -2
  134. package/dist/{impl-xVmMN-Vt.mjs.map → impl-h0z8OSQY.mjs.map} +1 -1
  135. package/dist/{impl-Ay6JeH-7.mjs → impl-kuuX5KaC.mjs} +2 -2
  136. package/dist/{impl-Ay6JeH-7.mjs.map → impl-kuuX5KaC.mjs.map} +1 -1
  137. package/dist/{impl-Ar7VAh0Z.mjs → impl-nYlbrsD7.mjs} +2 -2
  138. package/dist/{impl-Ar7VAh0Z.mjs.map → impl-nYlbrsD7.mjs.map} +1 -1
  139. package/dist/{impl-zZd6fuXS.mjs → impl-nte7qanu.mjs} +2 -2
  140. package/dist/{impl-zZd6fuXS.mjs.map → impl-nte7qanu.mjs.map} +1 -1
  141. package/dist/{impl-ApZGIx6l.mjs → impl-o66EB2o7.mjs} +2 -2
  142. package/dist/{impl-ApZGIx6l.mjs.map → impl-o66EB2o7.mjs.map} +1 -1
  143. package/dist/{impl-BpVe6fMe.mjs → impl-yxR3rwGm.mjs} +2 -2
  144. package/dist/{impl-BpVe6fMe.mjs.map → impl-yxR3rwGm.mjs.map} +1 -1
  145. package/dist/index.d.mts +36 -26
  146. package/dist/index.d.mts.map +1 -1
  147. package/dist/index.mjs +1 -1
  148. package/dist/{inquirer-jzIQWAvq.mjs → inquirer-DK1JbPA0.mjs} +2 -2
  149. package/dist/{inquirer-jzIQWAvq.mjs.map → inquirer-DK1JbPA0.mjs.map} +1 -1
  150. package/dist/{markRequestDataSiloIdsCompleted-JGJf33yp.mjs → markRequestDataSiloIdsCompleted-loC-xyyW.mjs} +2 -2
  151. package/dist/{markRequestDataSiloIdsCompleted-JGJf33yp.mjs.map → markRequestDataSiloIdsCompleted-loC-xyyW.mjs.map} +1 -1
  152. package/dist/{markSilentPrivacyRequests-BTI3rH-V.mjs → markSilentPrivacyRequests-D7ncAg8k.mjs} +2 -2
  153. package/dist/{markSilentPrivacyRequests-BTI3rH-V.mjs.map → markSilentPrivacyRequests-D7ncAg8k.mjs.map} +1 -1
  154. package/dist/{mergeTranscendInputs-B7WjxYMN.mjs → mergeTranscendInputs-9kQ3t5VY.mjs} +2 -2
  155. package/dist/{mergeTranscendInputs-B7WjxYMN.mjs.map → mergeTranscendInputs-9kQ3t5VY.mjs.map} +1 -1
  156. package/dist/{notifyPrivacyRequestsAdditionalTime-n5fvOJaA.mjs → notifyPrivacyRequestsAdditionalTime-DpCfIR_O.mjs} +2 -2
  157. package/dist/{notifyPrivacyRequestsAdditionalTime-n5fvOJaA.mjs.map → notifyPrivacyRequestsAdditionalTime-DpCfIR_O.mjs.map} +1 -1
  158. package/dist/{policy-bundle-manifest-DbbTXe0u.mjs → policy-bundle-manifest-DW3Wilp8.mjs} +2 -2
  159. package/dist/{policy-bundle-manifest-DbbTXe0u.mjs.map → policy-bundle-manifest-DW3Wilp8.mjs.map} +1 -1
  160. package/dist/{policy-project-discovery-DTSl0Gfl.mjs → policy-project-discovery-CND7N2HI.mjs} +4 -4
  161. package/dist/policy-project-discovery-CND7N2HI.mjs.map +1 -0
  162. package/dist/{policy-scaffold-config-RpxwwZ2H.mjs → policy-scaffold-config-CwY0QZC0.mjs} +8 -8
  163. package/dist/policy-scaffold-config-CwY0QZC0.mjs.map +1 -0
  164. package/dist/{project-discovery-BkHpTXDx.mjs → project-discovery-g29vd44a.mjs} +2 -2
  165. package/dist/{project-discovery-BkHpTXDx.mjs.map → project-discovery-g29vd44a.mjs.map} +1 -1
  166. package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DbybCU2T.mjs → pullChunkedCustomSiloOutstandingIdentifiers-7qvaMN58.mjs} +2 -2
  167. package/dist/{pullChunkedCustomSiloOutstandingIdentifiers-DbybCU2T.mjs.map → pullChunkedCustomSiloOutstandingIdentifiers-7qvaMN58.mjs.map} +1 -1
  168. package/dist/{pullManualEnrichmentIdentifiersToCsv-CxTPUfpV.mjs → pullManualEnrichmentIdentifiersToCsv-CWPs7VeX.mjs} +2 -2
  169. package/dist/{pullManualEnrichmentIdentifiersToCsv-CxTPUfpV.mjs.map → pullManualEnrichmentIdentifiersToCsv-CWPs7VeX.mjs.map} +1 -1
  170. package/dist/{pushCronIdentifiersFromCsv-CeRVlbFE.mjs → pushCronIdentifiersFromCsv-DQoliMdQ.mjs} +2 -2
  171. package/dist/{pushCronIdentifiersFromCsv-CeRVlbFE.mjs.map → pushCronIdentifiersFromCsv-DQoliMdQ.mjs.map} +1 -1
  172. package/dist/{pushManualEnrichmentIdentifiersFromCsv--4dYCvyv.mjs → pushManualEnrichmentIdentifiersFromCsv-R9BXYuFk.mjs} +2 -2
  173. package/dist/{pushManualEnrichmentIdentifiersFromCsv--4dYCvyv.mjs.map → pushManualEnrichmentIdentifiersFromCsv-R9BXYuFk.mjs.map} +1 -1
  174. package/dist/{readTranscendYaml-BfqQbPiT.mjs → readTranscendYaml-D576bvzR.mjs} +2 -2
  175. package/dist/{readTranscendYaml-BfqQbPiT.mjs.map → readTranscendYaml-D576bvzR.mjs.map} +1 -1
  176. package/dist/{removeUnverifiedRequestIdentifiers-CV6JGgt_.mjs → removeUnverifiedRequestIdentifiers-CgnfHdbc.mjs} +2 -2
  177. package/dist/{removeUnverifiedRequestIdentifiers-CV6JGgt_.mjs.map → removeUnverifiedRequestIdentifiers-CgnfHdbc.mjs.map} +1 -1
  178. package/dist/{retryRequestDataSilos-_OThvVwU.mjs → retryRequestDataSilos-BrLLNQqT.mjs} +2 -2
  179. package/dist/{retryRequestDataSilos-_OThvVwU.mjs.map → retryRequestDataSilos-BrLLNQqT.mjs.map} +1 -1
  180. package/dist/runRegalLintStep-DPaMm7Gg.mjs +3 -0
  181. package/dist/runRegalLintStep-DPaMm7Gg.mjs.map +1 -0
  182. package/dist/{scaffold-planning-BrX-pcz6.mjs → scaffold-planning-Bp_yqZWx.mjs} +2 -2
  183. package/dist/{scaffold-planning-BrX-pcz6.mjs.map → scaffold-planning-Bp_yqZWx.mjs.map} +1 -1
  184. package/dist/{skipPreflightJobs-D8og5pKe.mjs → skipPreflightJobs-CqjpB7ug.mjs} +2 -2
  185. package/dist/{skipPreflightJobs-D8og5pKe.mjs.map → skipPreflightJobs-CqjpB7ug.mjs.map} +1 -1
  186. package/dist/{skipRequestDataSilos-DUQ6AD_O.mjs → skipRequestDataSilos-DPi6-XVr.mjs} +2 -2
  187. package/dist/{skipRequestDataSilos-DUQ6AD_O.mjs.map → skipRequestDataSilos-DPi6-XVr.mjs.map} +1 -1
  188. package/dist/{streamPrivacyRequestsToCsv-BApu4dvI.mjs → streamPrivacyRequestsToCsv-DVPQ860_.mjs} +2 -2
  189. package/dist/{streamPrivacyRequestsToCsv-BApu4dvI.mjs.map → streamPrivacyRequestsToCsv-DVPQ860_.mjs.map} +1 -1
  190. package/dist/{updateConsentManagerVersionToLatest-BrgPZ5hX.mjs → updateConsentManagerVersionToLatest-gJMWny-i.mjs} +2 -2
  191. package/dist/{updateConsentManagerVersionToLatest-BrgPZ5hX.mjs.map → updateConsentManagerVersionToLatest-gJMWny-i.mjs.map} +1 -1
  192. package/dist/{uploadConsents-DRlDHQak.mjs → uploadConsents-5HlMj7EK.mjs} +2 -2
  193. package/dist/{uploadConsents-DRlDHQak.mjs.map → uploadConsents-5HlMj7EK.mjs.map} +1 -1
  194. package/dist/uploadPrivacyRequestsFromCsv-Dh8KEW3n.mjs +2 -0
  195. package/dist/uploadPrivacyRequestsFromCsv-Dh8KEW3n.mjs.map +1 -0
  196. package/dist/{validateTranscendAuth-D-dDINof.mjs → validateTranscendAuth-BrVGfq6X.mjs} +2 -2
  197. package/dist/{validateTranscendAuth-D-dDINof.mjs.map → validateTranscendAuth-BrVGfq6X.mjs.map} +1 -1
  198. package/package.json +22 -22
  199. package/dist/app-BMLQABJX.mjs.map +0 -1
  200. package/dist/codecs-CfcaFoZR.mjs +0 -2
  201. package/dist/codecs-CfcaFoZR.mjs.map +0 -1
  202. package/dist/constants-DlH2vWm_.mjs +0 -2
  203. package/dist/constants-DlH2vWm_.mjs.map +0 -1
  204. package/dist/fetchAllRequests-BI0tFl_a.mjs +0 -2
  205. package/dist/fetchAllRequests-BI0tFl_a.mjs.map +0 -1
  206. package/dist/impl-BEiCEzl_2.mjs.map +0 -1
  207. package/dist/impl-Cb-7C1Ml2.mjs.map +0 -1
  208. package/dist/impl-D6rMLiNB2.mjs.map +0 -1
  209. package/dist/impl-DNOgBcsu.mjs +0 -3
  210. package/dist/impl-DNOgBcsu.mjs.map +0 -1
  211. package/dist/impl-Dd39KsYO.mjs.map +0 -1
  212. package/dist/policy-project-discovery-DTSl0Gfl.mjs.map +0 -1
  213. package/dist/policy-scaffold-config-RpxwwZ2H.mjs.map +0 -1
  214. package/dist/uploadPrivacyRequestsFromCsv-FAGllBdd.mjs +0 -2
  215. package/dist/uploadPrivacyRequestsFromCsv-FAGllBdd.mjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runRegalLintStep-DPaMm7Gg.mjs","names":[],"sources":["../src/lib/policy/policy-process-output.ts","../src/commands/policy/helpers/probePolicyToolVersions.ts","../src/commands/policy/helpers/runOpaFormatStep.ts","../src/commands/policy/helpers/resolveRegalConfigFile.ts","../src/commands/policy/helpers/runRegalLintStep.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { CapturedProcessResult } from '../cli/run-captured-process.js';\n\n/**\n * Use captured tool output as a concise diagnostic.\n *\n * @param result - Captured process result\n * @param fallback - Message used when the tool emitted no output\n * @returns Diagnostic message\n */\nexport function getPolicyProcessFailureMessage(\n result: CapturedProcessResult,\n fallback: string,\n): string {\n const output = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join('\\n');\n return output || fallback;\n}\n\n/**\n * Convert absolute OPA formatter paths to stable project-relative paths.\n *\n * @param stdout - `opa fmt --list` output\n * @param projectDirectory - Absolute policy directory\n * @returns Sorted unique paths\n */\nexport function parseUnformattedPolicyFiles(stdout: string, projectDirectory: string): string[] {\n return [\n ...new Set(\n stdout\n .split(/\\r?\\n/gu)\n .map((file) => file.trim())\n .filter(Boolean)\n .map((file) =>\n path.isAbsolute(file)\n ? path.relative(projectDirectory, file).split(path.sep).join('/')\n : file.split(path.sep).join('/'),\n ),\n ),\n ].sort();\n}\n","import type { LocalContext } from '../../../context.js';\nimport type { CapturedProcessRunner } from '../../../lib/cli/run-captured-process.js';\nimport { getPolicyProcessFailureMessage } from '../../../lib/policy/policy-process-output.js';\nimport {\n OPA_MISSING_MESSAGE,\n parsePolicyToolVersion,\n REGAL_MISSING_MESSAGE,\n unsupportedOpaVersionMessage,\n unsupportedRegalVersionMessage,\n} from '../../../lib/policy/policy-runtime.js';\n\n/** Outcome of probing local OPA and Regal versions. */\nexport interface ProbePolicyToolVersionsResult {\n /** Detected OPA version, or null when missing or unsupported. */\n opa: string | null;\n /** Detected Regal version, or null when missing or unsupported. */\n regal: string | null;\n /** Diagnostics produced while probing. */\n diagnostics: Array<{\n /** Stable diagnostic identifier. */\n code: string;\n /** Human-readable detail. */\n message: string;\n }>;\n /** Whether the OPA version probe passed. */\n opaStatus: 'passed' | 'failed';\n /** Whether the Regal version probe passed. */\n regalStatus: 'passed' | 'failed';\n}\n\n/**\n * Probe installed OPA and Regal versions for policy commands.\n *\n * @param this - CLI context\n * @param resolvedDir - Absolute policy bundle directory\n * @param runner - Captured subprocess runner\n * @returns Detected versions and diagnostics\n */\nexport async function probePolicyToolVersions(\n this: LocalContext,\n resolvedDir: string,\n runner: CapturedProcessRunner,\n): Promise<ProbePolicyToolVersionsResult> {\n const diagnostics: ProbePolicyToolVersionsResult['diagnostics'] = [];\n let opa: string | null = null;\n let regal: string | null = null;\n let opaStatus: 'passed' | 'failed' = 'failed';\n let regalStatus: 'passed' | 'failed' = 'failed';\n\n const opaVersionResult = await runner('opa', ['version'], { cwd: resolvedDir }, this);\n if (opaVersionResult.error?.code === 'ENOENT') {\n diagnostics.push({ code: 'opa.missing', message: OPA_MISSING_MESSAGE });\n } else {\n const opaVersionOutput = `${opaVersionResult.stdout}\\n${opaVersionResult.stderr}`;\n const unsupportedOpa = unsupportedOpaVersionMessage(opaVersionOutput);\n if (opaVersionResult.code !== 0 || unsupportedOpa) {\n diagnostics.push({\n code: 'opa.version',\n message:\n unsupportedOpa ??\n getPolicyProcessFailureMessage(\n opaVersionResult,\n 'Unable to determine the installed OPA version.',\n ),\n });\n } else {\n opa = parsePolicyToolVersion(opaVersionOutput)!.version;\n opaStatus = 'passed';\n }\n }\n\n const regalVersionResult = await runner('regal', ['version'], { cwd: resolvedDir }, this);\n if (regalVersionResult.error?.code === 'ENOENT') {\n diagnostics.push({ code: 'regal.missing', message: REGAL_MISSING_MESSAGE });\n } else {\n const regalVersionOutput = `${regalVersionResult.stdout}\\n${regalVersionResult.stderr}`;\n const unsupportedRegal = unsupportedRegalVersionMessage(regalVersionOutput);\n if (regalVersionResult.code !== 0 || unsupportedRegal) {\n diagnostics.push({\n code: 'regal.version',\n message:\n unsupportedRegal ??\n getPolicyProcessFailureMessage(\n regalVersionResult,\n 'Unable to determine the installed Regal version.',\n ),\n });\n } else {\n regal = parsePolicyToolVersion(regalVersionOutput)!.version;\n regalStatus = 'passed';\n }\n }\n\n return { opa, regal, diagnostics, opaStatus, regalStatus };\n}\n","import colors from 'colors';\n\nimport type { LocalContext } from '../../../context.js';\nimport type { CapturedProcessRunner } from '../../../lib/cli/run-captured-process.js';\nimport { inquirerConfirmBoolean } from '../../../lib/helpers/inquirer.js';\nimport {\n getPolicyProcessFailureMessage,\n parseUnformattedPolicyFiles,\n} from '../../../lib/policy/policy-process-output.js';\nimport { isInteractivePromptInvocation } from '../../../lib/scaffolding/prompts.js';\n\n/** Inputs for the shared OPA formatting step. */\nexport interface RunOpaFormatStepInput {\n /** Absolute policy bundle directory. */\n resolvedDir: string;\n /** Apply OPA formatting without prompting. */\n fix: boolean;\n /** Disable the optional formatting confirmation. */\n noInteractive: boolean;\n /** Whether the command is emitting JSON. */\n json: boolean;\n /**\n * CLI invocation users should re-run with `--fix`.\n *\n * Example: `transcend policy lint --fix`\n */\n fixCommandHint: string;\n /** Captured subprocess runner. */\n runner: CapturedProcessRunner;\n}\n\n/** Outcome of the shared OPA formatting step. */\nexport interface RunOpaFormatStepResult {\n /** Step status. */\n status: 'passed' | 'failed';\n /** Files found to need OPA formatting, relative to the policy directory. */\n unformattedFiles: string[];\n /** Files repaired with OPA formatting, relative to the policy directory. */\n fixedFiles: string[];\n /** Diagnostics produced by this step. */\n diagnostics: Array<{\n /** Stable diagnostic identifier. */\n code: string;\n /** Human-readable detail. */\n message: string;\n }>;\n}\n\n/**\n * Check or repair OPA formatting for one policy bundle directory.\n *\n * @param this - CLI context\n * @param input - Format step inputs\n * @returns Format step outcome\n */\nexport async function runOpaFormatStep(\n this: LocalContext,\n { resolvedDir, fix, noInteractive, json, fixCommandHint, runner }: RunOpaFormatStepInput,\n): Promise<RunOpaFormatStepResult> {\n const formatResult = await runner(\n 'opa',\n ['fmt', '--list', resolvedDir],\n { cwd: resolvedDir },\n this,\n );\n if (formatResult.code !== 0) {\n return {\n status: 'failed',\n unformattedFiles: [],\n fixedFiles: [],\n diagnostics: [\n {\n code: 'opa.format',\n message: getPolicyProcessFailureMessage(\n formatResult,\n `opa fmt --list failed with exit code ${formatResult.code}`,\n ),\n },\n ],\n };\n }\n\n const unformattedFiles = parseUnformattedPolicyFiles(formatResult.stdout, resolvedDir);\n if (unformattedFiles.length === 0) {\n return {\n status: 'passed',\n unformattedFiles: [],\n fixedFiles: [],\n diagnostics: [],\n };\n }\n\n let shouldFormat = fix;\n const interactive = isInteractivePromptInvocation(\n { json, noInteractive },\n this.process.stdin.isTTY,\n this.process.stderr.isTTY,\n );\n if (!json && !fix) {\n this.logger.error(colors.red('Policy files are not formatted:'));\n unformattedFiles.forEach((file) => {\n this.logger.error(colors.red(` - ${file}`));\n });\n const diffResult = await runner(\n 'opa',\n ['fmt', '--diff', resolvedDir],\n { cwd: resolvedDir },\n this,\n );\n const diff = diffResult.stdout.trim() || diffResult.stderr.trim();\n if (diff) {\n this.logger.error('');\n this.logger.error(diff);\n }\n }\n if (!fix && interactive) {\n try {\n shouldFormat = await inquirerConfirmBoolean({\n message: 'Format the unformatted policy files listed above?',\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (/force closed|prompt.*cancel|user.*close|SIGINT/iu.test(message)) {\n this.process.exit(130);\n }\n throw error;\n }\n }\n\n if (shouldFormat) {\n const writeResult = await runner(\n 'opa',\n ['fmt', '--write', resolvedDir],\n { cwd: resolvedDir },\n this,\n );\n if (writeResult.code === 0) {\n return {\n status: 'passed',\n unformattedFiles,\n fixedFiles: [...unformattedFiles],\n diagnostics: [],\n };\n }\n return {\n status: 'failed',\n unformattedFiles,\n fixedFiles: [],\n diagnostics: [\n {\n code: 'opa.format-fix',\n message: getPolicyProcessFailureMessage(\n writeResult,\n `opa fmt --write failed with exit code ${writeResult.code}`,\n ),\n },\n ],\n };\n }\n\n return {\n status: 'failed',\n unformattedFiles,\n fixedFiles: [],\n diagnostics: [\n {\n code: 'opa.format-required',\n message: `Policy files are not formatted. Re-run \\`${fixCommandHint}\\` to repair them.`,\n },\n ],\n };\n}\n","import path from 'node:path';\n\nimport type { LocalContext } from '../../../context.js';\n\n/**\n * Resolve a Regal config file for a bundle, walking up to the workspace.\n *\n * @param context - CLI context\n * @param bundleDirectory - Absolute bundle directory\n * @returns Absolute config path, or undefined when none is found\n */\nexport function resolveRegalConfigFile(\n context: LocalContext,\n bundleDirectory: string,\n): string | undefined {\n let current = bundleDirectory;\n for (;;) {\n const nestedRegalConfig = path.join(current, '.regal', 'config.yaml');\n if (context.fs.existsSync(nestedRegalConfig)) {\n return nestedRegalConfig;\n }\n const rootRegalConfig = path.join(current, '.regal.yaml');\n if (context.fs.existsSync(rootRegalConfig)) {\n return rootRegalConfig;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n return undefined;\n }\n current = parent;\n }\n}\n","import type { LocalContext } from '../../../context.js';\nimport type { CapturedProcessRunner } from '../../../lib/cli/run-captured-process.js';\nimport { getPolicyProcessFailureMessage } from '../../../lib/policy/policy-process-output.js';\nimport { resolveRegalConfigFile } from './resolveRegalConfigFile.js';\n\n/** Inputs for the shared Regal lint step. */\nexport interface RunRegalLintStepInput {\n /** Absolute policy bundle directory. */\n resolvedDir: string;\n /** Captured subprocess runner. */\n runner: CapturedProcessRunner;\n}\n\n/** Outcome of the shared Regal lint step. */\nexport interface RunRegalLintStepResult {\n /** Step status. */\n status: 'passed' | 'failed';\n /** Diagnostics produced by this step. */\n diagnostics: Array<{\n /** Stable diagnostic identifier. */\n code: string;\n /** Human-readable detail. */\n message: string;\n }>;\n}\n\n/**\n * Run Regal lint with warnings treated as failures.\n *\n * @param this - CLI context\n * @param input - Regal lint step inputs\n * @returns Regal lint step outcome\n */\nexport async function runRegalLintStep(\n this: LocalContext,\n { resolvedDir, runner }: RunRegalLintStepInput,\n): Promise<RunRegalLintStepResult> {\n const regalConfig = resolveRegalConfigFile(this, resolvedDir);\n const regalResult = await runner(\n 'regal',\n [\n 'lint',\n '--fail-level',\n 'warning',\n ...(regalConfig ? ['--config-file', regalConfig] : []),\n resolvedDir,\n ],\n { cwd: resolvedDir },\n this,\n );\n if (regalResult.code === 0) {\n return { status: 'passed', diagnostics: [] };\n }\n return {\n status: 'failed',\n diagnostics: [\n {\n code: 'regal.lint',\n message: getPolicyProcessFailureMessage(\n regalResult,\n `regal lint failed with exit code ${regalResult.code}`,\n ),\n },\n ],\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import{i as e,n as t,t as n}from"./project-plan-output-BHHlkLxe.mjs";import{n as r,r as i}from"./paths-J5KScOaQ.mjs";import{a}from"./scaffold-templates-CUL82Zf8.mjs";import{c as o,f as s,l as c,u as l}from"./project-discovery-CWRjlFnC.mjs";import{a as u,r as d}from"./project-discovery-BkHpTXDx.mjs";import{n as f}from"./deno-runtime-CB2HBach.mjs";import{t as p}from"./scaffold-model-CUZER0BC.mjs";import{n as m,r as h,t as g}from"./jsonc-CdTubr53.mjs";import{basename as _,isAbsolute as v,join as y,relative as b,resolve as x,sep as S}from"node:path";import{createHash as C}from"node:crypto";var w=`# Project setup and CI
1
+ import{i as e,n as t,t as n}from"./project-plan-output-BHHlkLxe.mjs";import{n as r,r as i}from"./paths-J5KScOaQ.mjs";import{a}from"./scaffold-templates-CUL82Zf8.mjs";import{c as o,f as s,l as c,u as l}from"./project-discovery-CWRjlFnC.mjs";import{a as u,r as d}from"./project-discovery-g29vd44a.mjs";import{n as f}from"./deno-runtime-CB2HBach.mjs";import{t as p}from"./scaffold-model-CUZER0BC.mjs";import{n as m,r as h,t as g}from"./jsonc-CdTubr53.mjs";import{basename as _,isAbsolute as v,join as y,relative as b,resolve as x,sep as S}from"node:path";import{createHash as C}from"node:crypto";var w=`# Project setup and CI
2
2
 
3
3
  ## Install the CLI
4
4
 
@@ -122,4 +122,4 @@ jobs:
122
122
  `,`editor extension recommendations`).recommendations,[`denoland.vscode-deno`])}],`editor extension recommendations`)}const R={name:D,displayName:`Custom Function`,owner:`@transcend-io/cli`,files:O};function z(e){let t=JSON.stringify(e)??``;return[...new Set(Array.from(t.matchAll(/<<parameters\.([^>]+)>>/gu),e=>e[1]))]}function B(e){return e.projectRoot}function V(e,t){let n=B(e),r=new Set([e.manifestPath]),i=new Set(t.features);return i.has(p.Deno)&&r.add(e.denoConfigPath),i.has(p.Editor)&&(r.add(y(n,`.vscode`,`settings.json`)),r.add(y(n,`.vscode`,`extensions.json`))),i.has(p.Skill)&&o(n,e.existingSkillDirectories,R).forEach(e=>r.add(e)),i.has(p.Ci)&&r.add(y(n,`.github`,`workflows`,`transcend-custom-functions.yml`)),[...r].sort((e,t)=>e.localeCompare(t))}function H(e,t){return[e.manifestPath,y(e.manifestDirectory,t.sourceFile.path),...t.payloadFiles.map(t=>y(e.manifestDirectory,t.path))].sort((e,t)=>e.localeCompare(t))}function U(e,t){let n=b(e,t);if(n===`..`||n.startsWith(`..${S}`)||v(n))throw Error(`Refusing to modify path outside project root: ${t}`)}function W(e,t){let n=B(e);U(e.targetDirectory,e.manifestPath),t.forEach(e=>U(n,e));let r=new Map(e.relativePaths.map(e=>[e.toLocaleLowerCase(`en-US`),e]));t.forEach(t=>{let n=b(e.manifestDirectory,t).split(S).join(`/`);if(n.startsWith(`../`))return;let i=n.split(`/`);for(let e=1;e<=i.length;e+=1){let t=i.slice(0,e).join(`/`),n=r.get(t.toLocaleLowerCase(`en-US`));if(n&&n!==t)throw Error(`Case-insensitive path collision: "${t}" conflicts with "${n}".`)}})}function G(e,t,n){let r=s({snapshot:l(t.snapshots,n.path),after:n.contents,description:n.description,...n.createOnly===void 0?{}:{createOnly:n.createOnly}});r?e.changes.push(r):e.unchanged.push(n.path)}function K(e,t){let{state:n}=e,i=r(n.targetDirectory,n.manifestPath,n.invocationDirectory),a={version:1,command:`init`,rootDirectory:B(n),targetDirectory:n.targetDirectory,manifestPath:n.manifestPath,changes:[],unchanged:[],warnings:[],nextSteps:[`transcend custom-functions new ${i}`,`transcend custom-functions check ${i}`]},o=l(e.snapshots,n.manifestPath),s=o.contents===null?void 0:u(o.contents);o.contents===null?G(a,e,{path:n.manifestPath,contents:`# Custom Functions managed as code.
123
123
  functions: []
124
124
  `,description:`Create the Custom Function manifest`}):a.unchanged.push(n.manifestPath);let d=new Set(t.features);if(d.has(p.Deno)){let t=l(e.snapshots,n.denoConfigPath),r=(s?.functions??[]).map(({code:e})=>e).filter(e=>!e.includes(`<<parameters.`)),i=(s?.functions??[]).flatMap(e=>[...e[`test-payload`]?[e[`test-payload`]]:[],...(e[`test-payloads`]??[]).map(({payload:e})=>e)]).filter(e=>!e.includes(`<<parameters.`));G(a,e,{path:n.denoConfigPath,contents:F(t.contents,e.contractVersion,_(n.manifestPath),{sources:r,payloads:i}),description:`Merge strict target-scoped Deno authoring configuration`})}let f=B(n);if(d.has(p.Editor)){let t=y(f,`.vscode`,`settings.json`),r=y(f,`.vscode`,`extensions.json`),i=l(e.snapshots,t),o=l(e.snapshots,r);G(a,e,{path:t,contents:I(i.contents,f,n.targetDirectory),description:`Scope Deno language support to Custom Functions`}),G(a,e,{path:r,contents:L(o.contents),description:`Recommend the Deno editor extension`})}if(d.has(p.Skill)){let t=c({rootDirectory:f,existingDirectories:n.existingSkillDirectories,snapshots:e.snapshots,skill:R});a.changes.push(...t.changes),a.unchanged.push(...t.unchanged),a.warnings.push(...t.warnings)}if(d.has(p.Ci))if(!n.repositoryRoot||!n.usesGithub)a.warnings.push(`GitHub Actions setup was selected, but this target is not inside a detected GitHub repository.`);else{let t=y(n.repositoryRoot,`.github`,`workflows`,`transcend-custom-functions.yml`),r=b(n.repositoryRoot,n.targetDirectory).split(S).join(`/`)||`.`,i=b(n.repositoryRoot,n.manifestDirectory).split(S).join(`/`)||`.`,o=b(n.repositoryRoot,n.manifestPath).split(S).join(`/`),c=(s?.functions??[]).flatMap(e=>[e.code,...e[`test-payload`]?[e[`test-payload`]]:[],...(e[`test-payloads`]??[]).map(({payload:e})=>e)]).filter(e=>!e.includes(`<<parameters.`)).map(e=>b(n.repositoryRoot,x(n.manifestDirectory,e)).split(S).join(`/`)),u=P({cliVersion:e.cliVersion,targetDirectory:r,contentDirectory:i,manifestPath:o,watchedPaths:c}),d=l(e.snapshots,t);d.contents===null?G(a,e,{path:t,contents:u,description:`Add credential-free Custom Function checks`,createOnly:!0}):d.contents===u?a.unchanged.push(t):N(d.contents)?G(a,e,{path:t,contents:u,description:`Update managed Custom Function checks`}):(a.unchanged.push(t),a.warnings.push(`Existing GitHub Actions workflow was left unchanged: ${t}`))}return W(n,a.changes.map(({path:e})=>e)),a}function q(e,a){let{state:o}=e,s=l(e.snapshots,o.manifestPath).contents;if(s===null)throw Error(`Custom Function manifest does not exist: ${o.manifestPath}`);let c=a.generated.sourceFile.path.split(S).join(`/`),f=o.relativePaths.find(e=>e.toLocaleLowerCase(`en-US`)===c.toLocaleLowerCase(`en-US`));if(f)throw Error(`Custom Function name "${a.generated.displayName}" maps to ${c}, which conflicts with existing path ${f}. Choose another name.`);let p=u(s).functions.find(e=>x(o.manifestDirectory,e.code)===x(o.manifestDirectory,c));if(p)throw Error(`Custom Function name "${a.generated.displayName}" maps to source used by "${p.name}". Choose another name.`);let m={version:1,command:`new`,rootDirectory:o.manifestDirectory,targetDirectory:o.targetDirectory,manifestPath:o.manifestPath,changes:[],unchanged:[],warnings:[],nextSteps:[]},h=d(s,a.generated.manifestEntry);G(m,e,{path:o.manifestPath,contents:h,description:`Register ${a.generated.displayName} in the manifest`}),[a.generated.sourceFile,...a.generated.payloadFiles].forEach(t=>{G(m,e,{path:y(o.manifestDirectory,t.path),contents:t.contents,description:t===a.generated.sourceFile?`Create ${a.generated.displayName} source`:`Create ${a.generated.displayName} test payload`,createOnly:!0})});let g=y(o.manifestDirectory,a.generated.sourceFile.path),_=r(o.targetDirectory,o.manifestPath,o.invocationDirectory),v=z(a.generated.manifestEntry),b=i(v);return m.nextSteps=[`Edit ${t(n(o.invocationDirectory,g))}`,`transcend custom-functions run ${_} --function=${t(a.generated.displayName)}${b}`,`transcend custom-functions check ${_}${b}`,`transcend custom-functions push ${_} --dryRun${b}`],v.length>0&&m.warnings.push(`Supply ${v.join(`, `)} through --variables when running or pushing; never commit secret values.`),W(o,m.changes.map(({path:e})=>e)),m}function J(e,t){return a(e,t)}export{z as a,k as c,V as i,A as l,K as n,J as o,H as r,j as s,q as t,D as u};
125
- //# sourceMappingURL=scaffold-planning-BrX-pcz6.mjs.map
125
+ //# sourceMappingURL=scaffold-planning-Bp_yqZWx.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold-planning-BrX-pcz6.mjs","names":["CUSTOM_FUNCTION_SKILL_MD","CUSTOM_FUNCTION_SETUP_REFERENCE_MD","CUSTOM_FUNCTION_WRITING_REFERENCE_MD"],"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/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';\nimport type { AgentSkillFile } from '../scaffolding/agent-skill.js';\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/** Complete portable Agent Skill file set. */\nexport const CUSTOM_FUNCTION_SKILL_FILES: readonly AgentSkillFile[] = [\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 { quoteShellArgument } from '../scaffolding/project-plan-output.js';\nimport { CUSTOM_FUNCTION_SKILL_NAME } from './custom-function-skill.js';\nimport { SUPPORTED_DENO_MAJOR_VERSION } from './deno-runtime.js';\nimport { buildCustomFunctionProjectArguments, buildPlaceholderVariablesArgument } 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=${quoteShellArgument(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 {\n toPublicPlannedChange,\n type PublicPlannedChange,\n} from '../scaffolding/project-plan-output.js';\nimport type { CustomFunctionProjectPlan } from './scaffold-model.js';\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 * 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) => toPublicPlannedChange(options.cwd, change)),\n warnings: plan.warnings,\n nextSteps: plan.nextSteps,\n aiHandoff: options.aiHandoff,\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 branches:\n - main\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 {\n mergeJsonc,\n mergeStringArray,\n parseJsoncObject,\n type JsoncUpdate,\n} from '../scaffolding/jsonc.js';\nimport { buildCustomFunctionProjectArguments } from './paths.js';\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 = parseJsoncObject(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 = parseJsoncObject(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 = parseJsoncObject(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, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nimport {\n getManagedAgentSkillCandidatePaths,\n planManagedAgentSkill,\n type ManagedAgentSkillDefinition,\n} from '../scaffolding/agent-skill.js';\nimport { displayProjectPath, quoteShellArgument } from '../scaffolding/project-plan-output.js';\nimport {\n getPlanningFileSnapshot,\n planFileChange,\n type PlanningPathSnapshot,\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 { buildCustomFunctionProjectArguments, buildPlaceholderVariablesArgument } 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/** Managed Custom Function Agent Skill definition. */\nconst CUSTOM_FUNCTION_MANAGED_SKILL: ManagedAgentSkillDefinition = {\n name: CUSTOM_FUNCTION_SKILL_NAME,\n displayName: 'Custom Function',\n owner: '@transcend-io/cli',\n files: CUSTOM_FUNCTION_SKILL_FILES,\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 getManagedAgentSkillCandidatePaths(\n root,\n state.existingSkillDirectories,\n CUSTOM_FUNCTION_MANAGED_SKILL,\n ).forEach((path) => paths.add(path));\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 * 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: getPlanningFileSnapshot(input.snapshots, 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 * 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 = getPlanningFileSnapshot(input.snapshots, 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 = getPlanningFileSnapshot(input.snapshots, 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 = getPlanningFileSnapshot(input.snapshots, settingsPath);\n const extensionsSnapshot = getPlanningFileSnapshot(input.snapshots, 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 const skillPlan = planManagedAgentSkill({\n rootDirectory: root,\n existingDirectories: state.existingSkillDirectories,\n snapshots: input.snapshots,\n skill: CUSTOM_FUNCTION_MANAGED_SKILL,\n });\n plan.changes.push(...skillPlan.changes);\n plan.unchanged.push(...skillPlan.unchanged);\n plan.warnings.push(...skillPlan.warnings);\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 = getPlanningFileSnapshot(input.snapshots, 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 = getPlanningFileSnapshot(input.snapshots, 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 ${quoteShellArgument(displayProjectPath(state.invocationDirectory, sourcePath))}`,\n `transcend custom-functions run ${projectArguments} --function=${quoteShellArgument(\n options.generated.displayName,\n )}${variablesArgument}`,\n `transcend custom-functions check ${projectArguments}${variablesArgument}`,\n `transcend custom-functions push ${projectArguments} --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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"}
1
+ {"version":3,"file":"scaffold-planning-Bp_yqZWx.mjs","names":["CUSTOM_FUNCTION_SKILL_MD","CUSTOM_FUNCTION_SETUP_REFERENCE_MD","CUSTOM_FUNCTION_WRITING_REFERENCE_MD"],"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/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';\nimport type { AgentSkillFile } from '../scaffolding/agent-skill.js';\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/** Complete portable Agent Skill file set. */\nexport const CUSTOM_FUNCTION_SKILL_FILES: readonly AgentSkillFile[] = [\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 { quoteShellArgument } from '../scaffolding/project-plan-output.js';\nimport { CUSTOM_FUNCTION_SKILL_NAME } from './custom-function-skill.js';\nimport { SUPPORTED_DENO_MAJOR_VERSION } from './deno-runtime.js';\nimport { buildCustomFunctionProjectArguments, buildPlaceholderVariablesArgument } 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=${quoteShellArgument(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 {\n toPublicPlannedChange,\n type PublicPlannedChange,\n} from '../scaffolding/project-plan-output.js';\nimport type { CustomFunctionProjectPlan } from './scaffold-model.js';\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 * 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) => toPublicPlannedChange(options.cwd, change)),\n warnings: plan.warnings,\n nextSteps: plan.nextSteps,\n aiHandoff: options.aiHandoff,\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 branches:\n - main\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 {\n mergeJsonc,\n mergeStringArray,\n parseJsoncObject,\n type JsoncUpdate,\n} from '../scaffolding/jsonc.js';\nimport { buildCustomFunctionProjectArguments } from './paths.js';\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 = parseJsoncObject(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 = parseJsoncObject(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 = parseJsoncObject(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, isAbsolute, join, relative, resolve, sep } from 'node:path';\n\nimport {\n getManagedAgentSkillCandidatePaths,\n planManagedAgentSkill,\n type ManagedAgentSkillDefinition,\n} from '../scaffolding/agent-skill.js';\nimport { displayProjectPath, quoteShellArgument } from '../scaffolding/project-plan-output.js';\nimport {\n getPlanningFileSnapshot,\n planFileChange,\n type PlanningPathSnapshot,\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 { buildCustomFunctionProjectArguments, buildPlaceholderVariablesArgument } 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/** Managed Custom Function Agent Skill definition. */\nconst CUSTOM_FUNCTION_MANAGED_SKILL: ManagedAgentSkillDefinition = {\n name: CUSTOM_FUNCTION_SKILL_NAME,\n displayName: 'Custom Function',\n owner: '@transcend-io/cli',\n files: CUSTOM_FUNCTION_SKILL_FILES,\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 getManagedAgentSkillCandidatePaths(\n root,\n state.existingSkillDirectories,\n CUSTOM_FUNCTION_MANAGED_SKILL,\n ).forEach((path) => paths.add(path));\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 * 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: getPlanningFileSnapshot(input.snapshots, 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 * 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 = getPlanningFileSnapshot(input.snapshots, 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 = getPlanningFileSnapshot(input.snapshots, 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 = getPlanningFileSnapshot(input.snapshots, settingsPath);\n const extensionsSnapshot = getPlanningFileSnapshot(input.snapshots, 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 const skillPlan = planManagedAgentSkill({\n rootDirectory: root,\n existingDirectories: state.existingSkillDirectories,\n snapshots: input.snapshots,\n skill: CUSTOM_FUNCTION_MANAGED_SKILL,\n });\n plan.changes.push(...skillPlan.changes);\n plan.unchanged.push(...skillPlan.unchanged);\n plan.warnings.push(...skillPlan.warnings);\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 = getPlanningFileSnapshot(input.snapshots, 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 = getPlanningFileSnapshot(input.snapshots, 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 ${quoteShellArgument(displayProjectPath(state.invocationDirectory, sourcePath))}`,\n `transcend custom-functions run ${projectArguments} --function=${quoteShellArgument(\n options.generated.displayName,\n )}${variablesArgument}`,\n `transcend custom-functions check ${projectArguments}${variablesArgument}`,\n `transcend custom-functions push ${projectArguments} --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":"ulBAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ICAA,y/GCAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECMA,MAAa,EAA6B,6BAS7B,EAAyD,CACpE,CAAE,KAAM,WAAY,SAAUA,EAA0B,CACxD,CAAE,KAAM,sBAAuB,SAAUC,EAAoC,CAC7E,CACE,KAAM,yCACN,SAAUC,EACX,CACF,CCXD,SAAgB,EAAmB,EAWxB,CACT,IAAM,EAAS,EAAQ,SACnB,aAAa,EAA2B,aACxC,2BACE,EAAe,oCAAoC,EACvD,EAAQ,gBACR,EAAQ,aACT,CAAC,kBAIF,OAHI,EAAQ,kBACH,GAAG,EAAO,yCAAyC,EAAQ,gBAAgB,8FAA8F,EAAa,KAExL,GAAG,EAAO,yCAAyC,EAAQ,gBAAgB,uFAAqH,EAAQ,WAAW,iBAAiB,EAAa,KAS1P,SAAgB,EAA0B,EAa/B,CACT,IAAM,EAAS,EAAQ,SACnB,aAAa,EAA2B,aACxC,2BACE,EAAoB,EAAkC,EAAQ,cAAc,CAC5E,EAAe,oCAAoC,EACvD,EAAQ,gBACR,EAAQ,aACT,GAAG,IACE,EACJ,kCACG,EAAoC,EAAQ,gBAAiB,EAAQ,aAAa,CAAC,cACxE,EAAmB,EAAQ,YAAY,GAAG,IAC1D,MAAO,GAAG,EAAO,eAAe,EAAQ,YAAY,UAAU,EAAQ,WAAW,+DAA+D,EAAW,+CAA+C,EAAa,KC/BzN,SAAgB,EACd,EACA,EAU0B,CAC1B,MAAO,CACL,QAAS,EAAK,QACd,QAAS,EAAK,QACd,QAAS,EAAQ,QACjB,OAAQ,EAAQ,OAChB,gBAAiB,EAAK,gBACtB,aAAc,EAAK,aACnB,QAAS,EAAK,QAAQ,IAAK,GAAW,EAAsB,EAAQ,IAAK,EAAO,CAAC,CACjF,SAAU,EAAK,SACf,UAAW,EAAK,UAChB,UAAW,EAAQ,UACpB,CC7CH,SAAS,EAAiB,EAAsB,CAC9C,OAAO,EAAK,QAAQ,iBAAkB,OAAO,CAS/C,SAAgB,EAAmC,EAA2B,CAC5E,IAAM,EAAS,EAAS,MACtB,wEACD,CACD,GAAI,CAAC,GAAU,EAAO,QAAU,IAAA,GAC9B,MAAO,GAET,IAAM,EAAO,EAAS,MAAM,EAAG,EAAO,MAAM,CAAC,SAAS,CACtD,OAAO,EAAW,SAAS,CAAC,OAAO,EAAK,CAAC,OAAO,MAAM,GAAK,EAAO,GASpE,SAAgB,EAA8B,EAWnC,CACT,GAAM,CAAE,aAAY,kBAAiB,gBAAiB,EAChD,EAAmB,EAAQ,kBAAoB,EAC/C,EACJ,IAAqB,IAAM,GAAK,GAAG,EAAiB,EAAiB,QAAQ,QAAS,GAAG,CAAC,CAAC,GAQvF,EAPe,CACnB,GAAG,EAAW,cACd,GAAG,EAAW,kBACd,EAAiB,EAAa,CAC9B,GAAG,EAAW,YACd,IAAI,EAAQ,cAAgB,EAAE,EAAE,IAAI,EAAiB,CACtD,CAAC,QAAQ,EAAM,EAAO,IAAU,EAAM,QAAQ,EAAK,GAAK,EACzB,CAAC,IAAK,GAAS,WAAW,KAAK,UAAU,EAAK,GAAG,CAAC,KAAK;EAAK,CACtF,EAAO;;;;;;EAMb,EAAY;;;;;EAKZ,EAAY;;;;;;;;;;;;0BAYY,EAAyB;;sDAEG,EAAW;;;uCAG1B,KAAK,UAAU,EAAgB,CAAC;sCACjC,KAAK,UAAU,EAAa,CAAC;;;;;;;EAQ3D,EAAS,EAAW,SAAS,CAAC,OAAO,EAAK,SAAS,CAAC,CAAC,OAAO,MAAM,CACxE,MAAO,GAAG,EAAK,SAAS,CAAC,uDAAuD,EAAO,ICtFzF,SAAgB,EACd,EACA,EACA,EAAmB,0BACnB,EAKI,EAAE,CACE,CACR,IAAM,EAAU,EAAiB,GAAY;EAAQ,qBAAqB,CACpE,EACJ,EAAQ,iBACR,OAAO,EAAQ,iBAAoB,UACnC,CAAC,MAAM,QAAQ,EAAQ,gBAAgB,CAClC,EAAQ,gBACT,EAAE,CACF,EACJ,EAAQ,MAAQ,OAAO,EAAQ,MAAS,UAAY,CAAC,MAAM,QAAQ,EAAQ,KAAK,CAC3E,EAAQ,KACT,EAAE,CACF,EACJ,EAAQ,KAAO,OAAO,EAAQ,KAAQ,UAAY,CAAC,MAAM,QAAQ,EAAQ,IAAI,CACxE,EAAQ,IACT,EAAE,CACF,EACJ,EAAQ,OAAS,OAAO,EAAQ,OAAU,UAAY,CAAC,MAAM,QAAQ,EAAQ,MAAM,CAC9E,EAAQ,MACT,EAAE,CACF,EAAW,yBAGX,EAAsB,oCAAoC,EAC9D,IACA,EACD,CAAC,kBACI,EAAc,oCAAoC,EACtD,IACA,EACD,CAAC,4DACF,GACE,EAAM,KAAc,IAAA,IACpB,EAAM,KAAc,sGACpB,EAAM,KAAc,GACpB,EAAM,KAAc,EAEpB,MAAU,MACR,cAAc,EAAS,8DACxB,CA8BH,OAAO,EAAW,EAAU,CA3B1B,CACE,KAAM,CAAC,UAAW,sCAAsC,CACxD,MAAO,2CAA2C,IACnD,CACD,CAAE,KAAM,CAAC,kBAAmB,SAAS,CAAE,MAAO,GAAM,CACpD,CACE,KAAM,CAAC,kBAAmB,MAAM,CAChC,MAAO,EAAiB,EAAgB,IAAK,CAAC,UAAW,MAAO,eAAgB,SAAS,CAAC,CAC3F,CACD,CACE,KAAM,CAAC,OAAQ,UAAU,CACzB,MAAO,EAAiB,EAAK,QAAS,CAAC,aAAc,GAAI,EAAgB,SAAW,EAAE,CAAE,CAAC,CAC1F,CACD,CACE,KAAM,CAAC,MAAO,UAAU,CACxB,MAAO,EAAiB,EAAI,QAAS,CACnC,aACA,iBACA,EACA,GAAI,EAAgB,SAAW,EAAE,CACjC,GAAI,EAAgB,UAAY,EAAE,CACnC,CAAC,CACH,CACD,CAAE,KAAM,CAAC,MAAO,cAAc,CAAE,MAAO,GAAM,CAC7C,CAAE,KAAM,CAAC,MAAO,YAAY,CAAE,MAAO,IAAK,CAC1C,CAAE,KAAM,CAAC,QAAS,EAAS,CAAE,MAAO,EAAa,CAEhB,CAAE,qBAAqB,CAW5D,SAAgB,EACd,EACA,EACA,EACQ,CACR,IAAM,EAAU,EAAiB,GAAY;EAAQ,kBAAkB,CACjE,EAAS,EAAS,EAAgB,EAAgB,CAAC,MAAM,EAAI,CAAC,KAAK,IAAI,EAAI,IACjF,OAAO,EACL,EACA,CACE,CAAE,KAAM,CAAC,cAAc,CAAE,MAAO,GAAM,CACtC,CACE,KAAM,CAAC,mBAAmB,CAC1B,MAAO,EAAiB,EAAQ,oBAAqB,CAAC,EAAO,CAAC,CAC/D,CACF,CACD,kBACD,CASH,SAAgB,EAAsB,EAAiC,CAErE,OAAO,EACL,EACA,CACE,CACE,KAAM,CAAC,kBAAkB,CACzB,MAAO,EANG,EAAiB,GAAY;EAAQ,mCAMhB,CAAC,gBAAiB,CAAC,uBAAuB,CAAC,CAC3E,CACF,CACD,mCACD,CCzGH,MAKM,EAA6D,CACjE,KAAM,EACN,YAAa,kBACb,MAAO,oBACP,MAAO,EACR,CAgBD,SAAgB,EAA8B,EAA0B,CACtE,IAAM,EAAa,KAAK,UAAU,EAAM,EAAI,GAC5C,MAAO,CACL,GAAG,IAAI,IAAI,MAAM,KAAK,EAAW,SAAS,4BAA4B,CAAG,GAAU,EAAM,GAAI,CAAC,CAC/F,CAuBH,SAAS,EAAU,EAA2C,CAC5D,OAAO,EAAM,YAUf,SAAgB,EACd,EACA,EAIU,CACV,IAAM,EAAO,EAAU,EAAM,CACvB,EAAQ,IAAI,IAAY,CAAC,EAAM,aAAa,CAAC,CAC7C,EAAW,IAAI,IAAI,EAAQ,SAAS,CAkB1C,OAjBI,EAAS,IAAI,EAA2B,KAAK,EAC/C,EAAM,IAAI,EAAM,eAAe,CAE7B,EAAS,IAAI,EAA2B,OAAO,GACjD,EAAM,IAAI,EAAK,EAAM,UAAW,gBAAgB,CAAC,CACjD,EAAM,IAAI,EAAK,EAAM,UAAW,kBAAkB,CAAC,EAEjD,EAAS,IAAI,EAA2B,MAAM,EAChD,EACE,EACA,EAAM,yBACN,EACD,CAAC,QAAS,GAAS,EAAM,IAAI,EAAK,CAAC,CAElC,EAAS,IAAI,EAA2B,GAAG,EAC7C,EAAM,IAAI,EAAK,EAAM,UAAW,YAAa,iCAAiC,CAAC,CAE1E,CAAC,GAAG,EAAM,CAAC,MAAM,EAAM,IAAU,EAAK,cAAc,EAAM,CAAC,CAUpE,SAAgB,EACd,EACA,EACU,CACV,MAAO,CACL,EAAM,aACN,EAAK,EAAM,kBAAmB,EAAU,WAAW,KAAK,CACxD,GAAG,EAAU,aAAa,IAAK,GAAS,EAAK,EAAM,kBAAmB,EAAK,KAAK,CAAC,CAClF,CAAC,MAAM,EAAM,IAAU,EAAK,cAAc,EAAM,CAAC,CASpD,SAAS,EAAgB,EAAc,EAAoB,CACzD,IAAM,EAAQ,EAAS,EAAM,EAAK,CAClC,GAAI,IAAU,MAAQ,EAAM,WAAW,KAAK,IAAM,EAAI,EAAW,EAAM,CACrE,MAAU,MAAM,iDAAiD,IAAO,CAU5E,SAAS,EACP,EACA,EACM,CACN,IAAM,EAAO,EAAU,EAAM,CAC7B,EAAgB,EAAM,gBAAiB,EAAM,aAAa,CAC1D,EAAM,QAAS,GAAS,EAAgB,EAAM,EAAK,CAAC,CACpD,IAAM,EAAW,IAAI,IACnB,EAAM,cAAc,IAAK,GAAS,CAAC,EAAK,kBAAkB,QAAQ,CAAE,EAAK,CAAC,CAC3E,CACD,EAAM,QAAS,GAAS,CACtB,IAAM,EAAe,EAAS,EAAM,kBAAmB,EAAK,CAAC,MAAM,EAAI,CAAC,KAAK,IAAI,CACjF,GAAI,EAAa,WAAW,MAAM,CAChC,OAEF,IAAM,EAAa,EAAa,MAAM,IAAI,CAC1C,IAAK,IAAI,EAAS,EAAG,GAAU,EAAW,OAAQ,GAAU,EAAG,CAC7D,IAAM,EAAY,EAAW,MAAM,EAAG,EAAO,CAAC,KAAK,IAAI,CACjD,EAAY,EAAS,IAAI,EAAU,kBAAkB,QAAQ,CAAC,CACpE,GAAI,GAAa,IAAc,EAC7B,MAAU,MACR,qCAAqC,EAAU,oBAAoB,EAAU,IAC9E,GAGL,CAUJ,SAAS,EACP,EACA,EACA,EAUM,CACN,IAAM,EAAS,EAAe,CAC5B,SAAU,EAAwB,EAAM,UAAW,EAAQ,KAAK,CAChE,MAAO,EAAQ,SACf,YAAa,EAAQ,YACrB,GAAI,EAAQ,aAAe,IAAA,GAAY,EAAE,CAAG,CAAE,WAAY,EAAQ,WAAY,CAC/E,CAAC,CACE,EACF,EAAK,QAAQ,KAAK,EAAO,CAEzB,EAAK,UAAU,KAAK,EAAQ,KAAK,CAWrC,SAAgB,EACd,EACA,EAC2B,CAC3B,GAAM,CAAE,SAAU,EACZ,EAAmB,EACvB,EAAM,gBACN,EAAM,aACN,EAAM,oBACP,CACK,EAAkC,CACtC,QAAA,EACA,QAAS,OACT,cAAe,EAAU,EAAM,CAC/B,gBAAiB,EAAM,gBACvB,aAAc,EAAM,aACpB,QAAS,EAAE,CACX,UAAW,EAAE,CACb,SAAU,EAAE,CACZ,UAAW,CACT,kCAAkC,IAClC,oCAAoC,IACrC,CACF,CACK,EAAmB,EAAwB,EAAM,UAAW,EAAM,aAAa,CAC/E,EACJ,EAAiB,WAAa,KAC1B,IAAA,GACA,EAA6B,EAAiB,SAAS,CACzD,EAAiB,WAAa,KAChC,EAAc,EAAM,EAAO,CACzB,KAAM,EAAM,aACZ,SAAU;;EACV,YAAa,sCACd,CAAC,CAEF,EAAK,UAAU,KAAK,EAAM,aAAa,CAGzC,IAAM,EAAW,IAAI,IAAI,EAAQ,SAAS,CAC1C,GAAI,EAAS,IAAI,EAA2B,KAAK,CAAE,CACjD,IAAM,EAAW,EAAwB,EAAM,UAAW,EAAM,eAAe,CACzE,GAAe,GAAkB,WAAa,EAAE,EACnD,KAAK,CAAE,UAAW,EAAK,CACvB,OAAQ,GAAS,CAAC,EAAK,SAAS,gBAAgB,CAAC,CAC9C,GAAgB,GAAkB,WAAa,EAAE,EACpD,QAAS,GAAU,CAClB,GAAI,EAAM,gBAAkB,CAAC,EAAM,gBAAgB,CAAG,EAAE,CACxD,IAAI,EAAM,kBAAoB,EAAE,EAAE,KAAK,CAAE,aAAc,EAAQ,CAChE,CAAC,CACD,OAAQ,GAAS,CAAC,EAAK,SAAS,gBAAgB,CAAC,CACpD,EAAc,EAAM,EAAO,CACzB,KAAM,EAAM,eACZ,SAAU,EACR,EAAS,SACT,EAAM,gBACN,EAAS,EAAM,aAAa,CAC5B,CAAE,QAAS,EAAa,SAAU,EAAc,CACjD,CACD,YAAa,0DACd,CAAC,CAGJ,IAAM,EAAO,EAAU,EAAM,CAC7B,GAAI,EAAS,IAAI,EAA2B,OAAO,CAAE,CACnD,IAAM,EAAe,EAAK,EAAM,UAAW,gBAAgB,CACrD,EAAiB,EAAK,EAAM,UAAW,kBAAkB,CACzD,EAAmB,EAAwB,EAAM,UAAW,EAAa,CACzE,EAAqB,EAAwB,EAAM,UAAW,EAAe,CACnF,EAAc,EAAM,EAAO,CACzB,KAAM,EACN,SAAU,EAAoB,EAAiB,SAAU,EAAM,EAAM,gBAAgB,CACrF,YAAa,kDACd,CAAC,CACF,EAAc,EAAM,EAAO,CACzB,KAAM,EACN,SAAU,EAAsB,EAAmB,SAAS,CAC5D,YAAa,sCACd,CAAC,CAGJ,GAAI,EAAS,IAAI,EAA2B,MAAM,CAAE,CAClD,IAAM,EAAY,EAAsB,CACtC,cAAe,EACf,oBAAqB,EAAM,yBAC3B,UAAW,EAAM,UACjB,MAAO,EACR,CAAC,CACF,EAAK,QAAQ,KAAK,GAAG,EAAU,QAAQ,CACvC,EAAK,UAAU,KAAK,GAAG,EAAU,UAAU,CAC3C,EAAK,SAAS,KAAK,GAAG,EAAU,SAAS,CAE3C,GAAI,EAAS,IAAI,EAA2B,GAAG,CAC7C,GAAI,CAAC,EAAM,gBAAkB,CAAC,EAAM,WAClC,EAAK,SAAS,KACZ,iGACD,KACI,CACL,IAAM,EAAe,EACnB,EAAM,eACN,UACA,YACA,iCACD,CACK,EACJ,EAAS,EAAM,eAAgB,EAAM,gBAAgB,CAAC,MAAM,EAAI,CAAC,KAAK,IAAI,EAAI,IAC1E,EACJ,EAAS,EAAM,eAAgB,EAAM,kBAAkB,CAAC,MAAM,EAAI,CAAC,KAAK,IAAI,EAAI,IAC5E,EAAW,EAAS,EAAM,eAAgB,EAAM,aAAa,CAAC,MAAM,EAAI,CAAC,KAAK,IAAI,CAClF,GAAgB,GAAkB,WAAa,EAAE,EACpD,QAAS,GAAU,CAClB,EAAM,KACN,GAAI,EAAM,gBAAkB,CAAC,EAAM,gBAAgB,CAAG,EAAE,CACxD,IAAI,EAAM,kBAAoB,EAAE,EAAE,KAAK,CAAE,aAAc,EAAQ,CAChE,CAAC,CACD,OAAQ,GAAS,CAAC,EAAK,SAAS,gBAAgB,CAAC,CACjD,IAAK,GACJ,EAAS,EAAM,eAAiB,EAAQ,EAAM,kBAAmB,EAAK,CAAC,CACpE,MAAM,EAAI,CACV,KAAK,IAAI,CACb,CACG,EAAmB,EAA8B,CACrD,WAAY,EAAM,WAClB,gBAAiB,EACjB,mBACA,aAAc,EACd,eACD,CAAC,CACI,EAAmB,EAAwB,EAAM,UAAW,EAAa,CAC3E,EAAiB,WAAa,KAChC,EAAc,EAAM,EAAO,CACzB,KAAM,EACN,SAAU,EACV,YAAa,6CACb,WAAY,GACb,CAAC,CACO,EAAiB,WAAa,EACvC,EAAK,UAAU,KAAK,EAAa,CACxB,EAAmC,EAAiB,SAAS,CACtE,EAAc,EAAM,EAAO,CACzB,KAAM,EACN,SAAU,EACV,YAAa,wCACd,CAAC,EAEF,EAAK,UAAU,KAAK,EAAa,CACjC,EAAK,SAAS,KAAK,wDAAwD,IAAe,EAQhG,OAJA,EACE,EACA,EAAK,QAAQ,KAAK,CAAE,UAAW,EAAK,CACrC,CACM,EAUT,SAAgB,EACd,EACA,EAI2B,CAC3B,GAAM,CAAE,SAAU,EACZ,EAAkB,EAAwB,EAAM,UAAW,EAAM,aAAa,CAAC,SACrF,GAAI,IAAoB,KACtB,MAAU,MAAM,4CAA4C,EAAM,eAAe,CAEnF,IAAM,EAAqB,EAAQ,UAAU,WAAW,KAAK,MAAM,EAAI,CAAC,KAAK,IAAI,CAC3E,EAAkB,EAAM,cAAc,KACzC,GAAS,EAAK,kBAAkB,QAAQ,GAAK,EAAmB,kBAAkB,QAAQ,CAC5F,CACD,GAAI,EACF,MAAU,MACR,yBAAyB,EAAQ,UAAU,YAAY,YAAY,EAAmB,uCAC9C,EAAgB,wBACzD,CAEH,IAAM,EAAsB,EAA6B,EAAgB,CAAC,UAAU,KACjF,GACC,EAAQ,EAAM,kBAAmB,EAAM,KAAK,GAC5C,EAAQ,EAAM,kBAAmB,EAAmB,CACvD,CACD,GAAI,EACF,MAAU,MACR,yBAAyB,EAAQ,UAAU,YAAY,4BACjD,EAAoB,KAAK,yBAChC,CAEH,IAAM,EAAkC,CACtC,QAAA,EACA,QAAS,MACT,cAAe,EAAM,kBACrB,gBAAiB,EAAM,gBACvB,aAAc,EAAM,aACpB,QAAS,EAAE,CACX,UAAW,EAAE,CACb,SAAU,EAAE,CACZ,UAAW,EAAE,CACd,CACK,EAAkB,EACtB,EACA,EAAQ,UAAU,cACnB,CACD,EAAc,EAAM,EAAO,CACzB,KAAM,EAAM,aACZ,SAAU,EACV,YAAa,YAAY,EAAQ,UAAU,YAAY,kBACxD,CAAC,CAGF,CADwB,EAAQ,UAAU,WAAY,GAAG,EAAQ,UAAU,aAC7D,CAAC,QAAS,GAAS,CAC/B,EAAc,EAAM,EAAO,CACzB,KAAM,EAAK,EAAM,kBAAmB,EAAK,KAAK,CAC9C,SAAU,EAAK,SACf,YACE,IAAS,EAAQ,UAAU,WACvB,UAAU,EAAQ,UAAU,YAAY,SACxC,UAAU,EAAQ,UAAU,YAAY,eAC9C,WAAY,GACb,CAAC,EACF,CACF,IAAM,EAAa,EAAK,EAAM,kBAAmB,EAAQ,UAAU,WAAW,KAAK,CAC7E,EAAmB,EACvB,EAAM,gBACN,EAAM,aACN,EAAM,oBACP,CACK,EAAgB,EAA8B,EAAQ,UAAU,cAAc,CAC9E,EAAoB,EAAkC,EAAc,CAkB1E,MAjBA,GAAK,UAAY,CACf,QAAQ,EAAmB,EAAmB,EAAM,oBAAqB,EAAW,CAAC,GACrF,kCAAkC,EAAiB,cAAc,EAC/D,EAAQ,UAAU,YACnB,GAAG,IACJ,oCAAoC,IAAmB,IACvD,mCAAmC,EAAiB,WAAW,IAChE,CACG,EAAc,OAAS,GACzB,EAAK,SAAS,KACZ,UAAU,EAAc,KAAK,KAAK,CAAC,2EACpC,CAEH,EACE,EACA,EAAK,QAAQ,KAAK,CAAE,UAAW,EAAK,CACrC,CACM,EAUT,SAAgB,EACd,EACA,EACiC,CACjC,OAAO,EAA+B,EAAa,EAAS"}
@@ -1,2 +1,2 @@
1
- import{a as e}from"./constants-FDjw7bPS.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{r as n}from"./fetchAllRequests-BI0tFl_a.mjs";import{RequestEnricherStatus as r,RequestStatus as i}from"@transcend-io/privacy-types";import a from"colors";import{map as o,mapSeries as s}from"@transcend-io/utils";import{SKIP_REQUEST_ENRICHER as c,buildTranscendGraphQLClient as l,fetchAllRequestEnrichers as u,makeGraphQLRequest as d}from"@transcend-io/sdk";import f from"cli-progress";async function p({enricherIds:p,auth:m,concurrency:h=100,transcendUrl:g=e}){let _=l(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 o(y,async e=>{let n=(await u(_,{filterBy:{requestId:e.id},logger:t})).filter(e=>p.includes(e.enricher.id)&&![r.Resolved,r.Skipped].includes(e.status));n.length>0&&await s(n,async e=>{try{await d(_,c,{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-D8og5pKe.mjs.map
1
+ import{a as e}from"./constants-DAx9VK1C.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{r as n}from"./fetchAllRequests-BV9K0wXv.mjs";import{RequestEnricherStatus as r,RequestStatus as i}from"@transcend-io/privacy-types";import a from"colors";import{map as o,mapSeries as s}from"@transcend-io/utils";import{SKIP_REQUEST_ENRICHER as c,buildTranscendGraphQLClient as l,fetchAllRequestEnrichers as u,makeGraphQLRequest as d}from"@transcend-io/sdk";import f from"cli-progress";async function p({enricherIds:p,auth:m,concurrency:h=100,transcendUrl:g=e}){let _=l(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 o(y,async e=>{let n=(await u(_,{filterBy:{requestId:e.id},logger:t})).filter(e=>p.includes(e.enricher.id)&&![r.Resolved,r.Skipped].includes(e.status));n.length>0&&await s(n,async e=>{try{await d(_,c,{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-CqjpB7ug.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"skipPreflightJobs-D8og5pKe.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
+ {"version":3,"file":"skipPreflightJobs-CqjpB7ug.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-FDjw7bPS.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{RequestStatus as n}from"@transcend-io/privacy-types";import r from"colors";import{map as i}from"@transcend-io/utils";import{CHANGE_REQUEST_DATA_SILO_STATUS as a,buildTranscendGraphQLClient as o,fetchRequestDataSilos as s,fetchRequestDataSilosCount as c,makeGraphQLRequest as l}from"@transcend-io/sdk";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=o(h,f),y=new Date().getTime(),b=await c(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 i(await s(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 l(v,a,{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-DUQ6AD_O.mjs.map
1
+ import{a as e}from"./constants-DAx9VK1C.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{RequestStatus as n}from"@transcend-io/privacy-types";import r from"colors";import{map as i}from"@transcend-io/utils";import{CHANGE_REQUEST_DATA_SILO_STATUS as a,buildTranscendGraphQLClient as o,fetchRequestDataSilos as s,fetchRequestDataSilosCount as c,makeGraphQLRequest as l}from"@transcend-io/sdk";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=o(h,f),y=new Date().getTime(),b=await c(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 i(await s(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 l(v,a,{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-DPi6-XVr.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"skipRequestDataSilos-DUQ6AD_O.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"}
1
+ {"version":3,"file":"skipRequestDataSilos-DPi6-XVr.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"}
@@ -1,2 +1,2 @@
1
- import{a as e}from"./constants-FDjw7bPS.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{i as n,r}from"./fetchAllRequests-BI0tFl_a.mjs";import{i,r as a,t as o}from"./writeCsv-CNZrq3zU.mjs";import{groupBy as s,uniq as c}from"lodash-es";import l from"colors";import{map as u}from"@transcend-io/utils";import{buildTranscendGraphQLClient as d,createSombraGotInstance as f,fetchAllRequestIdentifiers as p,validateSombraVersion as m}from"@transcend-io/sdk";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=d(w,s),N=A?void 0:await f(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 m(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 u(I,async(e,n)=>{let i=W[n],s,d=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 u(e,async e=>({...e,requestIdentifiers:await p(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),d+=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,[]),d},{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-BApu4dvI.mjs.map
1
+ import{a as e}from"./constants-DAx9VK1C.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{i as n,r}from"./fetchAllRequests-BV9K0wXv.mjs";import{i,r as a,t as o}from"./writeCsv-CNZrq3zU.mjs";import{groupBy as s,uniq as c}from"lodash-es";import l from"colors";import{map as u}from"@transcend-io/utils";import{buildTranscendGraphQLClient as d,createSombraGotInstance as f,fetchAllRequestIdentifiers as p,validateSombraVersion as m}from"@transcend-io/sdk";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=d(w,s),N=A?void 0:await f(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 m(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 u(I,async(e,n)=>{let i=W[n],s,d=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 u(e,async e=>({...e,requestIdentifiers:await p(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),d+=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,[]),d},{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-DVPQ860_.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"streamPrivacyRequestsToCsv-BApu4dvI.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-DVPQ860_.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":"+dAmBA,SAAgB,EAAoB,CAClC,kBACA,qBACA,KACA,QACA,OACA,SACA,cACA,UACA,YACA,0BACA,UACA,SACA,SACA,qBACA,WACA,SACA,iBACA,UACA,GAAG,GAC8B,CACjC,MAAO,CACL,aAAc,EACd,aAAc,EACd,4BAA6B,GAA2B,GACxD,MAAO,EACP,kBAAmB,EACnB,eAAgB,EAChB,oBAAqB,EACrB,OAAQ,EACR,QAAS,EACT,uBAAwB,EACxB,QAAS,EACT,OAAQ,EACR,cAAe,EACf,kBAAmB,EACnB,SAAU,EACV,uBAAwB,GAAS,OAAS,GAAS,MAAQ,GAC3D,wBAAyB,GAAS,SAAS,UAAU,EAAI,GACzD,IAAI,GAAS,qBAAuB,EAAE,EAAE,QAAQ,EAAuC,IAAM,CAC3F,IAAM,EAAQ,EAAE,iBAAiB,MAAM,gBAAkB,EAAE,KAC3D,OAAO,EACH,CACE,GAAG,GACF,GAAQ,EAAE,aACP,EAAE,aAAa,IAAK,GAAM,EAAE,KAAK,CAAC,KAAK,IAAI,CAC3C,EAAE,aAAa,MAAQ,EAAE,aAC9B,CACD,GACH,EAAE,CAAC,CACN,GAAG,EACH,GAAG,OAAO,QAAQ,EAAQ,EAAiB,oBAAoB,CAAC,CAAC,QAC9D,EAAK,CAAC,EAAM,KACX,OAAO,OAAO,EAAK,EAChB,GAAO,EAAO,KAAK,CAAE,KAAM,KAAQ,EAAE,CAAC,KAAK,IAAI,CACjD,CAAC,CACJ,EAAE,CACH,CACD,GAAG,OAAO,QAAQ,EAAQ,EAAoB,OAAO,CAAC,CAAC,QACpD,EAAK,CAAC,EAAM,KACX,OAAO,OAAO,EAAK,EAChB,GAAO,EAAO,KAAK,CAAE,WAAY,EAAM,CAAC,KAAK,IAAI,CACnD,CAAC,CACJ,EAAE,CACH,CACF,CCrEH,SAAgB,EAAe,EAAa,EAAc,EAAoC,CAC5F,IAAM,EAAU,EAAM,SAAS,CACzB,EAAQ,EAAO,SAAS,CACxB,GAAe,EAAQ,GAAW,EACxC,OAAO,MAAM,KAAK,CAAE,OAAQ,EAAQ,EAAG,EAAG,KAAO,CAC/C,eAAgB,IAAI,KAAK,EAAU,EAAc,EAAE,CACnD,gBAAiB,IAAI,KAAK,IAAM,EAAS,EAAI,EAAQ,EAAU,GAAe,EAAI,GAAG,CACtF,EAAE,CCKL,eAAsB,EAA2B,CAC/C,OACA,aACA,UAAU,EAAE,CACZ,WAAW,EAAE,CACb,mBACA,cAAc,EACd,YAAY,IACZ,eAAe,EACf,kBACA,iBACA,kBACA,iBACA,SACA,yBAAyB,GACzB,QAqCC,CACD,IAAM,EAAS,EAA4B,EAAc,EAAK,CACxD,EAAS,EACX,IAAA,GACA,MAAM,EAAwB,EAAc,EAAM,CAChD,SACA,aAAc,EACd,UAAW,QAAQ,IAAI,WACxB,CAAC,CAGF,EAAY,GACZ,IACF,GAAa,WAAW,EAAgB,aAAa,IAEnD,IACF,GAAa,GAAG,EAAY,QAAU,GAAG,SAAS,EAAe,aAAa,IAEhF,EAAO,KACL,EAAO,QACL,GACE,EAAQ,OAAS,EACb,6BAA6B,EAAQ,KAAK,QAAQ,CAAC,GACnD,yBACH,IACJ,CACF,CAGD,IAAM,EAAY,EAAc,GAAK,GAAkB,EACjD,EAAS,EACX,EAAe,EAAgB,EAAiB,EAAY,CAC5D,CAAC,CAAE,iBAAgB,kBAAiB,CAAC,CAErC,GACF,EAAO,KAAK,EAAO,QAAQ,6BAA6B,EAAY,kBAAkB,CAAC,CAIzF,IAAM,EAAW,CACf,KAAM,EAAQ,OAAS,EAAI,EAAU,IAAA,GACrC,OAAQ,EAAS,OAAS,EAAI,EAAW,IAAA,GACzC,SACA,gBAAiB,EAAkB,EAAgB,aAAa,CAAG,IAAA,GACnE,eAAgB,EAAiB,EAAe,aAAa,CAAG,IAAA,GAChE,gBAAiB,EAAkB,EAAgB,aAAa,CAAG,IAAA,GACnE,eAAgB,EAAiB,EAAe,aAAa,CAAG,IAAA,GACjE,CAEK,EAAK,KAAK,KAAK,CAGhB,GACH,MAAM,EAAsB,EAAQ,CAAE,SAAQ,CAAC,CAGjD,IAAM,EAAgB,MAAM,EAAwB,EAAQ,EAAS,CACrE,EAAO,KAAK,EAAO,QAAQ,YAAY,EAAc,WAAW,CAAC,CAEjE,IAAM,EAAc,IAAI,EAAY,UAAU,EAAE,CAAE,EAAY,QAAQ,eAAe,CACrF,EAAY,MAAM,EAAe,EAAE,CAEnC,IAAI,EAAgB,EAEd,CAAE,WAAU,aAAc,EAAc,EAAK,CAE7C,EAAY,EAAO,KAAK,EAAG,IAC/B,EAAO,SAAW,EAAI,EAAO,GAAG,EAAS,GAAG,IAAI,IACjD,CAaK,EAA8B,EAAE,CAEhC,EAAc,MAAM,EACxB,EACA,MAAO,EAAO,IAAM,CAClB,IAAM,EAAY,EAAU,GACxB,EACA,EAAW,EAEf,GAAI,CACF,MAAM,EAAiB,EAAQ,CAC7B,UACA,KAAM,EACN,WACA,gBAAiB,EAAM,gBACvB,eAAgB,EAAM,eACtB,kBACA,iBACA,SACA,OAAQ,KAAO,IAAU,CACvB,GAAI,EAAM,SAAW,EAAG,OAkBxB,IAAM,GAfqC,EACvC,EAAM,IAAK,IAAO,CAAE,GAAG,EAAG,mBAAoB,EAAE,CAAE,EAAE,CACpD,MAAM,EACJ,EACA,KAAO,KAAO,CACZ,GAAG,EACH,mBAAoB,MAAM,EAA2B,EAAQ,EAAS,CACpE,SAAU,CAAE,UAAW,EAAE,GAAI,CAC7B,gBAAiB,GACjB,SACD,CAAC,CACH,EACD,CAAE,YAAa,EAAW,CAC3B,EAGM,IAAI,EAAoB,CAE9B,IACH,EAAU,EAAK,EAAK,IAAK,GAA+B,OAAO,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAC/E,EAAY,EAAW,EAAQ,EAGjC,EAAqB,EAAW,EAAM,EAAQ,CAC9C,GAAY,EAAK,OACjB,GAAiB,EAAK,OACtB,EAAY,OAAO,EAAc,EAEpC,CAAC,OACK,EAAK,CACZ,IAAM,EAAU,aAAe,MAAQ,EAAI,QAAU,OAAO,EAAI,CAChE,EAAO,MACL,EAAO,IACL,SAAS,EAAE,WACT,EAAM,gBAAgB,aAAa,EAAI,QACxC,KAAK,EAAM,iBAAiB,aAAa,EAAI,MAAM,KAAK,IAC1D,CACF,CACD,EAAa,KAAK,CAChB,MAAO,EACP,eAAgB,EAAM,eACtB,gBAAiB,EAAM,gBACvB,MAAO,EACR,CAAC,CAOJ,OAJK,GACH,EAAY,EAAW,EAAE,CAAC,CAGrB,GAET,CAAE,YAAa,EAAY,EAAc,EAAG,CAC7C,CAED,EAAY,MAAM,CAClB,IAAM,EAAa,EAAY,QAAQ,EAAG,IAAM,EAAI,EAAG,EAAE,CACnD,GAAW,KAAK,KAAK,CAAG,GAAM,IAEpC,GAAI,EAAa,OAAS,EAAG,CAC3B,EAAO,MACL,EAAO,IACL,KAAK,EAAa,OAAO,mEAE1B,CACF,CACD,IAAK,IAAM,KAAM,EACf,EAAO,MACL,EAAO,IACL,WAAW,EAAG,MAAM,qBAClB,EAAG,gBAAgB,aAAa,EAAI,GACrC,qBAAqB,EAAG,iBAAiB,aAAa,EAAI,KAC5D,CACF,CAQL,OAJA,EAAO,KACL,EAAO,MAAM,YAAY,EAAW,eAAe,EAAU,OAAO,cAAc,EAAQ,GAAG,CAC9F,CAEM,CAAE,YAAW,aAAY"}
@@ -1,2 +1,2 @@
1
- import{a as e}from"./constants-FDjw7bPS.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{ConsentBundleType as n}from"@transcend-io/privacy-types";import r from"colors";import{mapSeries as i}from"@transcend-io/utils";import{buildTranscendGraphQLClient as a,deployConsentManager as o,fetchConsentManagerId as s,updateConsentManagerToLatest as c}from"@transcend-io/sdk";async function l({auth:l,deploy:u=!1,transcendUrl:d=e,bundleTypes:f=Object.values(n)}){let p=a(d,l),m=await s(p,{logger:t});await i(f,async e=>{t.info(r.magenta(`Update Consent Manager bundle with ID "${m}" and type "${e}" to latest version...`)),await c(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 i(f,async e=>{t.info(r.magenta(`Deploying Consent Manager bundle with ID "${m}" and type "${e}"...`)),await o(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-BrgPZ5hX.mjs.map
1
+ import{a as e}from"./constants-DAx9VK1C.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import{ConsentBundleType as n}from"@transcend-io/privacy-types";import r from"colors";import{mapSeries as i}from"@transcend-io/utils";import{buildTranscendGraphQLClient as a,deployConsentManager as o,fetchConsentManagerId as s,updateConsentManagerToLatest as c}from"@transcend-io/sdk";async function l({auth:l,deploy:u=!1,transcendUrl:d=e,bundleTypes:f=Object.values(n)}){let p=a(d,l),m=await s(p,{logger:t});await i(f,async e=>{t.info(r.magenta(`Update Consent Manager bundle with ID "${m}" and type "${e}" to latest version...`)),await c(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 i(f,async e=>{t.info(r.magenta(`Deploying Consent Manager bundle with ID "${m}" and type "${e}"...`)),await o(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-gJMWny-i.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"updateConsentManagerVersionToLatest-BrgPZ5hX.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"}
1
+ {"version":3,"file":"updateConsentManagerVersionToLatest-gJMWny-i.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"}
@@ -1,2 +1,2 @@
1
- import{s as e}from"./constants-FDjw7bPS.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import n from"colors";import{map as r}from"@transcend-io/utils";import{decodeCodec as i}from"@transcend-io/type-utils";import*as a from"io-ts";import{createTranscendConsentGotInstance as o}from"@transcend-io/sdk";import s from"cli-progress";import*as c from"crypto";import l from"jsonwebtoken";function u(e,t,n){let r=Buffer.from(n,`base64`),i=Buffer.from(t,`base64`),a=Buffer.from(`A65959A6`,`hex`),o=c.createCipheriv(`id-aes256-wrap-pad`,i,a),s={encryptedIdentifier:Buffer.concat([o.update(e),o.final()]).toString(`base64`)};return l.sign(s,r,{algorithm:`HS384`})}const d=/^[0-9][Y|N]([Y|N])[Y|N]$/,f=a.record(a.string,a.union([a.boolean,a.literal(`Auto`)]));async function p({base64EncryptionKey:a,base64SigningKey:c,preferences:l,partition:p,concurrency:m=100,transcendUrl:h=e}){let g=o(h),_=l.filter(e=>e.usp&&!d.test(e.usp));if(_.length>0)throw Error(`Received invalid usp strings: ${JSON.stringify(_,null,2)}`);let v=l.map((e,t)=>[e,t]).filter(([e])=>{if(!e.purposes)return!1;try{return i(f,e.purposes),!1}catch{return!0}});if(v.length>0)throw Error(`Received invalid purpose maps: ${JSON.stringify(v,null,2)}`);let y=l.filter(e=>!e.usp&&!e.purposes);if(y.length>0)throw Error(`Received invalid inputs, expected either purposes or usp to be defined: ${JSON.stringify(y,null,2)}`);t.info(n.magenta(`Uploading ${l.length} user preferences to partition ${p}`));let b=new Date().getTime(),x=new s.SingleBar({},s.Presets.shades_classic),S=0;x.start(l.length,0),await r(l,async({userId:e,confirmed:r=`true`,updated:o,prompted:s,purposes:l,...m})=>{let h=u(e,a,c),[,_]=m.usp&&d.exec(m.usp)||[],v={token:h,partition:p,consent:{confirmed:r===`true`,purposes:l?i(f,l):m.usp?{SaleOfInfo:_===`Y`}:{},...o?{updated:o===`true`}:{},...s?{prompted:s===`true`}:{},...m}};try{await g.post(`sync`,{json:v}).json()}catch(e){try{let r=JSON.parse(e?.response?.body||`{}`);r.error&&t.error(n.red(`Error: ${r.error}`))}catch{}throw Error(`Received an error from server: ${e?.response?.body||e?.message}`)}S+=1,x.update(S)},{concurrency:m}),x.stop();let C=new Date().getTime()-b;t.info(n.green(`Successfully uploaded ${l.length} user preferences to partition ${p} in "${C/1e3}" seconds!`))}export{u as i,d as n,p as r,f as t};
2
- //# sourceMappingURL=uploadConsents-DRlDHQak.mjs.map
1
+ import{s as e}from"./constants-DAx9VK1C.mjs";import{t}from"./logger-CPu6zbyQ.mjs";import n from"colors";import{map as r}from"@transcend-io/utils";import{decodeCodec as i}from"@transcend-io/type-utils";import*as a from"io-ts";import{createTranscendConsentGotInstance as o}from"@transcend-io/sdk";import s from"cli-progress";import*as c from"crypto";import l from"jsonwebtoken";function u(e,t,n){let r=Buffer.from(n,`base64`),i=Buffer.from(t,`base64`),a=Buffer.from(`A65959A6`,`hex`),o=c.createCipheriv(`id-aes256-wrap-pad`,i,a),s={encryptedIdentifier:Buffer.concat([o.update(e),o.final()]).toString(`base64`)};return l.sign(s,r,{algorithm:`HS384`})}const d=/^[0-9][Y|N]([Y|N])[Y|N]$/,f=a.record(a.string,a.union([a.boolean,a.literal(`Auto`)]));async function p({base64EncryptionKey:a,base64SigningKey:c,preferences:l,partition:p,concurrency:m=100,transcendUrl:h=e}){let g=o(h),_=l.filter(e=>e.usp&&!d.test(e.usp));if(_.length>0)throw Error(`Received invalid usp strings: ${JSON.stringify(_,null,2)}`);let v=l.map((e,t)=>[e,t]).filter(([e])=>{if(!e.purposes)return!1;try{return i(f,e.purposes),!1}catch{return!0}});if(v.length>0)throw Error(`Received invalid purpose maps: ${JSON.stringify(v,null,2)}`);let y=l.filter(e=>!e.usp&&!e.purposes);if(y.length>0)throw Error(`Received invalid inputs, expected either purposes or usp to be defined: ${JSON.stringify(y,null,2)}`);t.info(n.magenta(`Uploading ${l.length} user preferences to partition ${p}`));let b=new Date().getTime(),x=new s.SingleBar({},s.Presets.shades_classic),S=0;x.start(l.length,0),await r(l,async({userId:e,confirmed:r=`true`,updated:o,prompted:s,purposes:l,...m})=>{let h=u(e,a,c),[,_]=m.usp&&d.exec(m.usp)||[],v={token:h,partition:p,consent:{confirmed:r===`true`,purposes:l?i(f,l):m.usp?{SaleOfInfo:_===`Y`}:{},...o?{updated:o===`true`}:{},...s?{prompted:s===`true`}:{},...m}};try{await g.post(`sync`,{json:v}).json()}catch(e){try{let r=JSON.parse(e?.response?.body||`{}`);r.error&&t.error(n.red(`Error: ${r.error}`))}catch{}throw Error(`Received an error from server: ${e?.response?.body||e?.message}`)}S+=1,x.update(S)},{concurrency:m}),x.stop();let C=new Date().getTime()-b;t.info(n.green(`Successfully uploaded ${l.length} user preferences to partition ${p} in "${C/1e3}" seconds!`))}export{u as i,d as n,p as r,f as t};
2
+ //# sourceMappingURL=uploadConsents-5HlMj7EK.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"uploadConsents-DRlDHQak.mjs","names":[],"sources":["../src/lib/consent-manager/createConsentToken.ts","../src/lib/consent-manager/uploadConsents.ts"],"sourcesContent":["import * as crypto from 'crypto';\n\nimport jwt from 'jsonwebtoken';\n\n/**\n * Function to create a consent manager token\n *\n * @see https://docs.transcend.io/docs/consent/reference/managed-consent-database\n * @param userId - User ID\n * @param base64EncryptionKey - Encryption key\n * @param base64SigningKey - Signing key\n * @returns Token\n */\nexport function createConsentToken(\n userId: string,\n base64EncryptionKey: string,\n base64SigningKey: string,\n): string {\n // Read on for where to find these keys\n const signingKey = Buffer.from(base64SigningKey, 'base64');\n const encryptionKey = Buffer.from(base64EncryptionKey, 'base64');\n\n // NIST's AES-KWP implementation { aes 48 } - see https://tools.ietf.org/html/rfc5649\n const encryptionAlgorithm = 'id-aes256-wrap-pad';\n // Initial Value for AES-KWP integrity check - see https://tools.ietf.org/html/rfc5649#section-3\n const iv = Buffer.from('A65959A6', 'hex');\n // Set up encryption algorithm\n const cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, iv);\n\n // Encrypt the userId and base64-encode the result\n const encryptedIdentifier = Buffer.concat([cipher.update(userId), cipher.final()]).toString(\n 'base64',\n );\n\n // Create the JWT content - jwt.sign will add a 'iat' (issued at) field to the payload\n // If you wanted to add something manually, consider\n // const issued: Date = new Date();\n // const isoDate = issued.toISOString();\n const jwtPayload = {\n encryptedIdentifier,\n };\n\n // Create a JSON web token and HMAC it with SHA-384\n const consentToken = jwt.sign(jwtPayload, signingKey, {\n algorithm: 'HS384',\n });\n\n return consentToken;\n}\n","import { ConsentPreferencesBody } from '@transcend-io/airgap.js-types';\nimport { createTranscendConsentGotInstance } from '@transcend-io/sdk';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport * as t from 'io-ts';\n\nimport { DEFAULT_TRANSCEND_CONSENT_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { createConsentToken } from './createConsentToken.js';\nimport type { ConsentPreferenceUpload } from './types.js';\n\nexport const USP_STRING_REGEX = /^[0-9][Y|N]([Y|N])[Y|N]$/;\n\nexport const PurposeMap = t.record(t.string, t.union([t.boolean, t.literal('Auto')]));\n\n/**\n * Upload a set of consent preferences\n *\n * @param options - Options\n */\nexport async function uploadConsents({\n base64EncryptionKey,\n base64SigningKey,\n preferences,\n partition,\n concurrency = 100,\n transcendUrl = DEFAULT_TRANSCEND_CONSENT_API,\n}: {\n /** base64 encryption key */\n base64EncryptionKey: string;\n /** base64 signing key */\n base64SigningKey: string;\n /** Partition key */\n partition: string;\n /** Sombra API key authentication */\n preferences: ConsentPreferenceUpload[];\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Concurrency limit for approving */\n concurrency?: number;\n}): Promise<void> {\n // Create connection to API\n const transcendConsentApi = createTranscendConsentGotInstance(transcendUrl);\n\n // Ensure usp strings are valid\n const invalidUspStrings = preferences.filter(\n (pref) => pref.usp && !USP_STRING_REGEX.test(pref.usp),\n );\n if (invalidUspStrings.length > 0) {\n throw new Error(`Received invalid usp strings: ${JSON.stringify(invalidUspStrings, null, 2)}`);\n }\n\n // Ensure purpose maps are valid\n const invalidPurposeMaps = preferences\n .map((pref, ind) => [pref, ind] as [ConsentPreferenceUpload, number])\n .filter(([pref]) => {\n if (!pref.purposes) {\n return false;\n }\n try {\n decodeCodec(PurposeMap, pref.purposes);\n return false;\n } catch {\n return true;\n }\n });\n if (invalidPurposeMaps.length > 0) {\n throw new Error(\n `Received invalid purpose maps: ${JSON.stringify(invalidPurposeMaps, null, 2)}`,\n );\n }\n\n // Ensure usp or preferences are provided\n const invalidInputs = preferences.filter((pref) => !pref.usp && !pref.purposes);\n if (invalidInputs.length > 0) {\n throw new Error(\n `Received invalid inputs, expected either purposes or usp to be defined: ${JSON.stringify(\n invalidInputs,\n null,\n 2,\n )}`,\n );\n }\n\n logger.info(\n colors.magenta(`Uploading ${preferences.length} user preferences to partition ${partition}`),\n );\n\n // Time duration\n const t0 = new Date().getTime();\n // create a new progress bar instance and use shades_classic theme\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n // Build a GraphQL client\n let total = 0;\n progressBar.start(preferences.length, 0);\n await map(\n preferences,\n async ({ userId, confirmed = 'true', updated, prompted, purposes, ...consent }) => {\n const token = createConsentToken(userId, base64EncryptionKey, base64SigningKey);\n\n // parse usp string\n const [, saleStatus] = consent.usp ? USP_STRING_REGEX.exec(consent.usp) || [] : [];\n\n const input = {\n token,\n partition,\n consent: {\n confirmed: confirmed === 'true',\n purposes: purposes\n ? decodeCodec(PurposeMap, purposes)\n : consent.usp\n ? { SaleOfInfo: saleStatus === 'Y' }\n : {},\n ...(updated ? { updated: updated === 'true' } : {}),\n ...(prompted ? { prompted: prompted === 'true' } : {}),\n ...consent,\n },\n } as ConsentPreferencesBody;\n\n // Make the request\n try {\n await transcendConsentApi\n .post('sync', {\n json: input,\n })\n .json();\n } catch (err) {\n try {\n const parsed = JSON.parse(err?.response?.body || '{}');\n if (parsed.error) {\n logger.error(colors.red(`Error: ${parsed.error}`));\n }\n } catch {\n // continue\n }\n throw new Error(`Received an error from server: ${err?.response?.body || err?.message}`);\n }\n\n total += 1;\n progressBar.update(total);\n },\n { concurrency },\n );\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Successfully uploaded ${preferences.length} user preferences to partition ${partition} in \"${\n totalTime / 1000\n }\" seconds!`,\n ),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"uploadConsents-5HlMj7EK.mjs","names":[],"sources":["../src/lib/consent-manager/createConsentToken.ts","../src/lib/consent-manager/uploadConsents.ts"],"sourcesContent":["import * as crypto from 'crypto';\n\nimport jwt from 'jsonwebtoken';\n\n/**\n * Function to create a consent manager token\n *\n * @see https://docs.transcend.io/docs/consent/reference/managed-consent-database\n * @param userId - User ID\n * @param base64EncryptionKey - Encryption key\n * @param base64SigningKey - Signing key\n * @returns Token\n */\nexport function createConsentToken(\n userId: string,\n base64EncryptionKey: string,\n base64SigningKey: string,\n): string {\n // Read on for where to find these keys\n const signingKey = Buffer.from(base64SigningKey, 'base64');\n const encryptionKey = Buffer.from(base64EncryptionKey, 'base64');\n\n // NIST's AES-KWP implementation { aes 48 } - see https://tools.ietf.org/html/rfc5649\n const encryptionAlgorithm = 'id-aes256-wrap-pad';\n // Initial Value for AES-KWP integrity check - see https://tools.ietf.org/html/rfc5649#section-3\n const iv = Buffer.from('A65959A6', 'hex');\n // Set up encryption algorithm\n const cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, iv);\n\n // Encrypt the userId and base64-encode the result\n const encryptedIdentifier = Buffer.concat([cipher.update(userId), cipher.final()]).toString(\n 'base64',\n );\n\n // Create the JWT content - jwt.sign will add a 'iat' (issued at) field to the payload\n // If you wanted to add something manually, consider\n // const issued: Date = new Date();\n // const isoDate = issued.toISOString();\n const jwtPayload = {\n encryptedIdentifier,\n };\n\n // Create a JSON web token and HMAC it with SHA-384\n const consentToken = jwt.sign(jwtPayload, signingKey, {\n algorithm: 'HS384',\n });\n\n return consentToken;\n}\n","import { ConsentPreferencesBody } from '@transcend-io/airgap.js-types';\nimport { createTranscendConsentGotInstance } from '@transcend-io/sdk';\nimport { decodeCodec } from '@transcend-io/type-utils';\nimport { map } from '@transcend-io/utils';\nimport cliProgress from 'cli-progress';\nimport colors from 'colors';\nimport * as t from 'io-ts';\n\nimport { DEFAULT_TRANSCEND_CONSENT_API } from '../../constants.js';\nimport { logger } from '../../logger.js';\nimport { createConsentToken } from './createConsentToken.js';\nimport type { ConsentPreferenceUpload } from './types.js';\n\nexport const USP_STRING_REGEX = /^[0-9][Y|N]([Y|N])[Y|N]$/;\n\nexport const PurposeMap = t.record(t.string, t.union([t.boolean, t.literal('Auto')]));\n\n/**\n * Upload a set of consent preferences\n *\n * @param options - Options\n */\nexport async function uploadConsents({\n base64EncryptionKey,\n base64SigningKey,\n preferences,\n partition,\n concurrency = 100,\n transcendUrl = DEFAULT_TRANSCEND_CONSENT_API,\n}: {\n /** base64 encryption key */\n base64EncryptionKey: string;\n /** base64 signing key */\n base64SigningKey: string;\n /** Partition key */\n partition: string;\n /** Sombra API key authentication */\n preferences: ConsentPreferenceUpload[];\n /** API URL for Transcend backend */\n transcendUrl?: string;\n /** Concurrency limit for approving */\n concurrency?: number;\n}): Promise<void> {\n // Create connection to API\n const transcendConsentApi = createTranscendConsentGotInstance(transcendUrl);\n\n // Ensure usp strings are valid\n const invalidUspStrings = preferences.filter(\n (pref) => pref.usp && !USP_STRING_REGEX.test(pref.usp),\n );\n if (invalidUspStrings.length > 0) {\n throw new Error(`Received invalid usp strings: ${JSON.stringify(invalidUspStrings, null, 2)}`);\n }\n\n // Ensure purpose maps are valid\n const invalidPurposeMaps = preferences\n .map((pref, ind) => [pref, ind] as [ConsentPreferenceUpload, number])\n .filter(([pref]) => {\n if (!pref.purposes) {\n return false;\n }\n try {\n decodeCodec(PurposeMap, pref.purposes);\n return false;\n } catch {\n return true;\n }\n });\n if (invalidPurposeMaps.length > 0) {\n throw new Error(\n `Received invalid purpose maps: ${JSON.stringify(invalidPurposeMaps, null, 2)}`,\n );\n }\n\n // Ensure usp or preferences are provided\n const invalidInputs = preferences.filter((pref) => !pref.usp && !pref.purposes);\n if (invalidInputs.length > 0) {\n throw new Error(\n `Received invalid inputs, expected either purposes or usp to be defined: ${JSON.stringify(\n invalidInputs,\n null,\n 2,\n )}`,\n );\n }\n\n logger.info(\n colors.magenta(`Uploading ${preferences.length} user preferences to partition ${partition}`),\n );\n\n // Time duration\n const t0 = new Date().getTime();\n // create a new progress bar instance and use shades_classic theme\n const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);\n\n // Build a GraphQL client\n let total = 0;\n progressBar.start(preferences.length, 0);\n await map(\n preferences,\n async ({ userId, confirmed = 'true', updated, prompted, purposes, ...consent }) => {\n const token = createConsentToken(userId, base64EncryptionKey, base64SigningKey);\n\n // parse usp string\n const [, saleStatus] = consent.usp ? USP_STRING_REGEX.exec(consent.usp) || [] : [];\n\n const input = {\n token,\n partition,\n consent: {\n confirmed: confirmed === 'true',\n purposes: purposes\n ? decodeCodec(PurposeMap, purposes)\n : consent.usp\n ? { SaleOfInfo: saleStatus === 'Y' }\n : {},\n ...(updated ? { updated: updated === 'true' } : {}),\n ...(prompted ? { prompted: prompted === 'true' } : {}),\n ...consent,\n },\n } as ConsentPreferencesBody;\n\n // Make the request\n try {\n await transcendConsentApi\n .post('sync', {\n json: input,\n })\n .json();\n } catch (err) {\n try {\n const parsed = JSON.parse(err?.response?.body || '{}');\n if (parsed.error) {\n logger.error(colors.red(`Error: ${parsed.error}`));\n }\n } catch {\n // continue\n }\n throw new Error(`Received an error from server: ${err?.response?.body || err?.message}`);\n }\n\n total += 1;\n progressBar.update(total);\n },\n { concurrency },\n );\n\n progressBar.stop();\n const t1 = new Date().getTime();\n const totalTime = t1 - t0;\n\n logger.info(\n colors.green(\n `Successfully uploaded ${preferences.length} user preferences to partition ${partition} in \"${\n totalTime / 1000\n }\" seconds!`,\n ),\n );\n}\n"],"mappings":"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"}