@voxgig/sdkgen 4.32.1 → 4.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (442) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/action/copies.d.ts +21 -0
  3. package/dist/action/copies.js +146 -0
  4. package/dist/action/copies.js.map +1 -0
  5. package/dist/action/doctor.d.ts +15 -2
  6. package/dist/action/doctor.js +169 -23
  7. package/dist/action/doctor.js.map +1 -1
  8. package/dist/action/edition.js +6 -2
  9. package/dist/action/edition.js.map +1 -1
  10. package/dist/action/feature.js +6 -2
  11. package/dist/action/feature.js.map +1 -1
  12. package/dist/action/package.js +28 -10
  13. package/dist/action/package.js.map +1 -1
  14. package/dist/action/remove.js +2 -0
  15. package/dist/action/remove.js.map +1 -1
  16. package/dist/action/resolve.d.ts +1 -0
  17. package/dist/action/resolve.js +10 -2
  18. package/dist/action/resolve.js.map +1 -1
  19. package/dist/action/target.js +6 -2
  20. package/dist/action/target.js.map +1 -1
  21. package/dist/helpers/naming.js +1 -1
  22. package/dist/helpers/naming.js.map +1 -1
  23. package/dist/sdkgen.d.ts +2 -1
  24. package/dist/sdkgen.js +36 -3
  25. package/dist/sdkgen.js.map +1 -1
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/model/sdkgen.aontu +7 -1
  28. package/package.json +1 -1
  29. package/project/.sdk/model/feature/debug.aontu +3 -1
  30. package/project/.sdk/src/cmp/c/PrepareAuth_c.ts +8 -28
  31. package/project/.sdk/src/cmp/c/TestClean_c.ts +774 -0
  32. package/project/.sdk/src/cmp/c/Test_c.ts +3 -0
  33. package/project/.sdk/src/cmp/c/fragment/Entity.fragment.c +16 -5
  34. package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +1 -1
  35. package/project/.sdk/src/cmp/clojure/PrepareAuth_clojure.ts +3 -0
  36. package/project/.sdk/src/cmp/clojure/TestClean_clojure.ts +473 -0
  37. package/project/.sdk/src/cmp/clojure/Test_clojure.ts +6 -0
  38. package/project/.sdk/src/cmp/clojure/fragment/Entity.fragment.clj +18 -8
  39. package/project/.sdk/src/cmp/cpp/PrepareAuth_cpp.ts +3 -0
  40. package/project/.sdk/src/cmp/cpp/TestClean_cpp.ts +782 -0
  41. package/project/.sdk/src/cmp/cpp/TestEntity_cpp.ts +1 -1
  42. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +2 -0
  43. package/project/.sdk/src/cmp/csharp/PrepareAuth_csharp.ts +3 -0
  44. package/project/.sdk/src/cmp/csharp/TestClean_csharp.ts +887 -0
  45. package/project/.sdk/src/cmp/csharp/Test_csharp.ts +2 -0
  46. package/project/.sdk/src/cmp/csharp/fragment/EntityBase.fragment.cs +68 -4
  47. package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +8 -2
  48. package/project/.sdk/src/cmp/csharp/fragment/SdkError.fragment.cs +49 -0
  49. package/project/.sdk/src/cmp/elixir/PrepareAuth_elixir.ts +3 -0
  50. package/project/.sdk/src/cmp/elixir/Schema_elixir.ts +20 -10
  51. package/project/.sdk/src/cmp/elixir/TestClean_elixir.ts +666 -0
  52. package/project/.sdk/src/cmp/elixir/Test_elixir.ts +4 -0
  53. package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +1 -1
  54. package/project/.sdk/src/cmp/elixir/fragment/Pipeline.fragment.ex +14 -2
  55. package/project/.sdk/src/cmp/go/PrepareAuth_go.ts +3 -0
  56. package/project/.sdk/src/cmp/go/TestClean_go.ts +804 -0
  57. package/project/.sdk/src/cmp/go/Test_go.ts +3 -0
  58. package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +54 -4
  59. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +27 -2
  60. package/project/.sdk/src/cmp/java/PrepareAuth_java.ts +3 -0
  61. package/project/.sdk/src/cmp/java/TestClean_java.ts +884 -0
  62. package/project/.sdk/src/cmp/java/Test_java.ts +2 -0
  63. package/project/.sdk/src/cmp/java/fragment/EntityBase.fragment.java +56 -11
  64. package/project/.sdk/src/cmp/java/fragment/SdkError.fragment.java +36 -3
  65. package/project/.sdk/src/cmp/js/PrepareAuth_js.ts +3 -0
  66. package/project/.sdk/src/cmp/js/TestClean_js.ts +458 -0
  67. package/project/.sdk/src/cmp/js/Test_js.ts +2 -0
  68. package/project/.sdk/src/cmp/js/fragment/EntityBase.fragment.js +5 -4
  69. package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +7 -1
  70. package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +7 -1
  71. package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +7 -1
  72. package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +7 -1
  73. package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +7 -1
  74. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +11 -2
  75. package/project/.sdk/src/cmp/js/fragment/SdkError.fragment.js +17 -1
  76. package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +5 -0
  77. package/project/.sdk/src/cmp/kotlin/PrepareAuth_kotlin.ts +3 -0
  78. package/project/.sdk/src/cmp/kotlin/TestClean_kotlin.ts +654 -0
  79. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +3 -0
  80. package/project/.sdk/src/cmp/kotlin/fragment/EntityBase.fragment.kt +44 -11
  81. package/project/.sdk/src/cmp/kotlin/fragment/SdkError.fragment.kt +31 -0
  82. package/project/.sdk/src/cmp/lua/PrepareAuth_lua.ts +3 -0
  83. package/project/.sdk/src/cmp/lua/TestClean_lua.ts +769 -0
  84. package/project/.sdk/src/cmp/lua/Test_lua.ts +3 -0
  85. package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +77 -1
  86. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +49 -2
  87. package/project/.sdk/src/cmp/ocaml/Entity_ocaml.ts +3 -2
  88. package/project/.sdk/src/cmp/ocaml/PrepareAuth_ocaml.ts +4 -25
  89. package/project/.sdk/src/cmp/ocaml/TestClean_ocaml.ts +538 -0
  90. package/project/.sdk/src/cmp/ocaml/Test_ocaml.ts +5 -0
  91. package/project/.sdk/src/cmp/ocaml/fragment/RunOp.fragment.ml +16 -4
  92. package/project/.sdk/src/cmp/perl/PrepareAuth_perl.ts +3 -0
  93. package/project/.sdk/src/cmp/perl/TestClean_perl.ts +619 -0
  94. package/project/.sdk/src/cmp/perl/Test_perl.ts +3 -0
  95. package/project/.sdk/src/cmp/perl/fragment/Entity.fragment.pm +67 -5
  96. package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +13 -1
  97. package/project/.sdk/src/cmp/php/PrepareAuth_php.ts +3 -0
  98. package/project/.sdk/src/cmp/php/TestClean_php.ts +809 -0
  99. package/project/.sdk/src/cmp/php/Test_php.ts +2 -0
  100. package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +96 -55
  101. package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +15 -2
  102. package/project/.sdk/src/cmp/py/PrepareAuth_py.ts +3 -0
  103. package/project/.sdk/src/cmp/py/TestClean_py.ts +549 -0
  104. package/project/.sdk/src/cmp/py/Test_py.ts +3 -0
  105. package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +83 -55
  106. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +19 -3
  107. package/project/.sdk/src/cmp/rb/PrepareAuth_rb.ts +3 -0
  108. package/project/.sdk/src/cmp/rb/TestClean_rb.ts +535 -0
  109. package/project/.sdk/src/cmp/rb/Test_rb.ts +3 -0
  110. package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +108 -46
  111. package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +12 -1
  112. package/project/.sdk/src/cmp/rust/PrepareAuth_rust.ts +9 -45
  113. package/project/.sdk/src/cmp/rust/TestClean_rust.ts +881 -0
  114. package/project/.sdk/src/cmp/rust/Test_rust.ts +3 -0
  115. package/project/.sdk/src/cmp/rust/fragment/Entity.fragment.rs +14 -6
  116. package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +17 -1
  117. package/project/.sdk/src/cmp/rust/fragment/SdkError.fragment.rs +34 -2
  118. package/project/.sdk/src/cmp/scala/PrepareAuth_scala.ts +3 -0
  119. package/project/.sdk/src/cmp/scala/TestClean_scala.ts +608 -0
  120. package/project/.sdk/src/cmp/scala/Test_scala.ts +3 -0
  121. package/project/.sdk/src/cmp/scala/fragment/EntityBase.fragment.scala +58 -6
  122. package/project/.sdk/src/cmp/swift/PrepareAuth_swift.ts +3 -0
  123. package/project/.sdk/src/cmp/swift/TestClean_swift.ts +671 -0
  124. package/project/.sdk/src/cmp/swift/Test_swift.ts +3 -0
  125. package/project/.sdk/src/cmp/swift/fragment/EntityBase.fragment.swift +10 -0
  126. package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +16 -2
  127. package/project/.sdk/src/cmp/swift/fragment/SdkError.fragment.swift +47 -3
  128. package/project/.sdk/src/cmp/ts/PrepareAuth_ts.ts +3 -0
  129. package/project/.sdk/src/cmp/ts/TestClean_ts.ts +465 -0
  130. package/project/.sdk/src/cmp/ts/Test_ts.ts +2 -0
  131. package/project/.sdk/src/cmp/ts/fragment/EntityBase.fragment.ts +5 -4
  132. package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +7 -1
  133. package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +7 -1
  134. package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +7 -1
  135. package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +7 -1
  136. package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +7 -1
  137. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +8 -2
  138. package/project/.sdk/src/cmp/ts/fragment/SdkError.fragment.ts +20 -2
  139. package/project/.sdk/src/cmp/zig/PrepareAuth_zig.ts +3 -0
  140. package/project/.sdk/src/cmp/zig/TestClean_zig.ts +791 -0
  141. package/project/.sdk/src/cmp/zig/Test_zig.ts +5 -0
  142. package/project/.sdk/src/cmp/zig/fragment/Build.fragment.zig +19 -0
  143. package/project/.sdk/src/cmp/zig/fragment/Entity.fragment.zig +14 -5
  144. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +1 -1
  145. package/project/.sdk/tm/c/core/context.c +25 -0
  146. package/project/.sdk/tm/c/core/error.c +16 -0
  147. package/project/.sdk/tm/c/core/sdk.h +22 -0
  148. package/project/.sdk/tm/c/feature/audit.c +2 -0
  149. package/project/.sdk/tm/c/feature/cost.c +1 -0
  150. package/project/.sdk/tm/c/feature/debug.c +19 -29
  151. package/project/.sdk/tm/c/feature/log.c +21 -2
  152. package/project/.sdk/tm/c/feature/proxy.c +55 -3
  153. package/project/.sdk/tm/c/feature/secrets.c +7 -0
  154. package/project/.sdk/tm/c/feature/telemetry.c +2 -0
  155. package/project/.sdk/tm/c/tests/feature_test.c +2 -2
  156. package/project/.sdk/tm/c/utility/clean.c +530 -8
  157. package/project/.sdk/tm/c/utility/done.c +28 -12
  158. package/project/.sdk/tm/c/utility/make_error.c +4 -3
  159. package/project/.sdk/tm/c/utility/make_options.c +71 -45
  160. package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +15 -0
  161. package/project/.sdk/tm/clojure/src/sdk/client.clj +20 -4
  162. package/project/.sdk/tm/clojure/src/sdk/core.clj +319 -35
  163. package/project/.sdk/tm/clojure/src/sdk/features.clj +68 -30
  164. package/project/.sdk/tm/clojure/test/sdk/test/feature.clj +4 -2
  165. package/project/.sdk/tm/clojure/test/sdk/test/pipeline.clj +47 -20
  166. package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +4 -0
  167. package/project/.sdk/tm/cpp/core/types.hpp +93 -3
  168. package/project/.sdk/tm/cpp/feature/audit.hpp +1 -0
  169. package/project/.sdk/tm/cpp/feature/cost.hpp +1 -0
  170. package/project/.sdk/tm/cpp/feature/debug.hpp +20 -12
  171. package/project/.sdk/tm/cpp/feature/log.hpp +31 -9
  172. package/project/.sdk/tm/cpp/feature/proxy.hpp +52 -6
  173. package/project/.sdk/tm/cpp/feature/secrets.hpp +13 -0
  174. package/project/.sdk/tm/cpp/feature/telemetry.hpp +4 -2
  175. package/project/.sdk/tm/cpp/test/feature_test.cpp +2 -2
  176. package/project/.sdk/tm/cpp/utility/pipeline.hpp +426 -51
  177. package/project/.sdk/tm/csharp/core/Context.cs +32 -0
  178. package/project/.sdk/tm/csharp/core/Utility.cs +2 -0
  179. package/project/.sdk/tm/csharp/feature/AuditFeature.cs +1 -0
  180. package/project/.sdk/tm/csharp/feature/CostFeature.cs +7 -5
  181. package/project/.sdk/tm/csharp/feature/DebugFeature.cs +19 -16
  182. package/project/.sdk/tm/csharp/feature/FeatureOptions.cs +16 -0
  183. package/project/.sdk/tm/csharp/feature/LogFeature.cs +4 -1
  184. package/project/.sdk/tm/csharp/feature/ProxyFeature.cs +26 -5
  185. package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +16 -0
  186. package/project/.sdk/tm/csharp/feature/TelemetryFeature.cs +4 -2
  187. package/project/.sdk/tm/csharp/test/FeatureTest.cs +2 -2
  188. package/project/.sdk/tm/csharp/utility/Clean.cs +440 -2
  189. package/project/.sdk/tm/csharp/utility/Done.cs +27 -11
  190. package/project/.sdk/tm/csharp/utility/MakeError.cs +19 -8
  191. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +119 -29
  192. package/project/.sdk/tm/csharp/utility/MakeResponse.cs +21 -11
  193. package/project/.sdk/tm/csharp/utility/Register.cs +1 -0
  194. package/project/.sdk/tm/elixir/lib/projectname/context.ex +19 -0
  195. package/project/.sdk/tm/elixir/lib/projectname/entity_base.ex +22 -1
  196. package/project/.sdk/tm/elixir/lib/projectname/error.ex +24 -0
  197. package/project/.sdk/tm/elixir/lib/projectname/feature/audit.ex +2 -2
  198. package/project/.sdk/tm/elixir/lib/projectname/feature/cost.ex +2 -2
  199. package/project/.sdk/tm/elixir/lib/projectname/feature/debug.ex +13 -13
  200. package/project/.sdk/tm/elixir/lib/projectname/feature/log.ex +18 -5
  201. package/project/.sdk/tm/elixir/lib/projectname/feature/proxy.ex +19 -1
  202. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +15 -0
  203. package/project/.sdk/tm/elixir/lib/projectname/feature/telemetry.ex +4 -2
  204. package/project/.sdk/tm/elixir/lib/projectname/feature.ex +22 -0
  205. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +464 -41
  206. package/project/.sdk/tm/elixir/test/feature_test.exs +3 -2
  207. package/project/.sdk/tm/go/core/context.go +42 -0
  208. package/project/.sdk/tm/go/core/error.go +30 -10
  209. package/project/.sdk/tm/go/core/utility_type.go +2 -0
  210. package/project/.sdk/tm/go/feature/audit_feature.go +1 -0
  211. package/project/.sdk/tm/go/feature/base_feature.go +12 -0
  212. package/project/.sdk/tm/go/feature/cost_feature.go +2 -2
  213. package/project/.sdk/tm/go/feature/debug_feature.go +14 -12
  214. package/project/.sdk/tm/go/feature/log_feature.go +13 -8
  215. package/project/.sdk/tm/go/feature/proxy_feature.go +26 -6
  216. package/project/.sdk/tm/go/feature/secrets_feature.go +19 -0
  217. package/project/.sdk/tm/go/feature/telemetry_feature.go +4 -2
  218. package/project/.sdk/tm/go/test/feature_test.go +2 -2
  219. package/project/.sdk/tm/go/utility/clean.go +585 -2
  220. package/project/.sdk/tm/go/utility/done.go +29 -9
  221. package/project/.sdk/tm/go/utility/make_error.go +51 -16
  222. package/project/.sdk/tm/go/utility/make_options.go +77 -36
  223. package/project/.sdk/tm/go/utility/register.go +12 -0
  224. package/project/.sdk/tm/java/core/Context.java +29 -0
  225. package/project/.sdk/tm/java/core/SdkClient.java +4 -2
  226. package/project/.sdk/tm/java/core/Utility.java +7 -0
  227. package/project/.sdk/tm/java/feature/AuditFeature.java +6 -0
  228. package/project/.sdk/tm/java/feature/CostFeature.java +5 -0
  229. package/project/.sdk/tm/java/feature/DebugFeature.java +25 -14
  230. package/project/.sdk/tm/java/feature/LogFeature.java +11 -4
  231. package/project/.sdk/tm/java/feature/ProxyFeature.java +41 -6
  232. package/project/.sdk/tm/java/feature/SecretsFeature.java +18 -0
  233. package/project/.sdk/tm/java/feature/TelemetryFeature.java +8 -2
  234. package/project/.sdk/tm/java/test/FeatureTest.java +2 -2
  235. package/project/.sdk/tm/java/utility/Clean.java +519 -1
  236. package/project/.sdk/tm/java/utility/Done.java +1 -1
  237. package/project/.sdk/tm/java/utility/MakeError.java +5 -1
  238. package/project/.sdk/tm/java/utility/MakeOptions.java +55 -31
  239. package/project/.sdk/tm/java/utility/Register.java +4 -0
  240. package/project/.sdk/tm/js/src/Context.js +5 -1
  241. package/project/.sdk/tm/js/src/feature/audit/AuditFeature.js +2 -2
  242. package/project/.sdk/tm/js/src/feature/cost/CostFeature.js +2 -2
  243. package/project/.sdk/tm/js/src/feature/debug/DebugFeature.js +13 -7
  244. package/project/.sdk/tm/js/src/feature/log/LogFeature.js +9 -5
  245. package/project/.sdk/tm/js/src/feature/proxy/ProxyFeature.js +19 -1
  246. package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +14 -0
  247. package/project/.sdk/tm/js/src/feature/telemetry/TelemetryFeature.js +4 -2
  248. package/project/.sdk/tm/js/src/utility/CleanUtility.js +294 -21
  249. package/project/.sdk/tm/js/src/utility/DoneUtility.js +17 -5
  250. package/project/.sdk/tm/js/src/utility/MakeErrorUtility.js +18 -2
  251. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +39 -15
  252. package/project/.sdk/tm/js/src/utility/Utility.js +4 -2
  253. package/project/.sdk/tm/js/test/feature/harness.js +11 -1
  254. package/project/.sdk/tm/js/test/feature.test.js +9 -2
  255. package/project/.sdk/tm/kotlin/core/Context.kt +19 -0
  256. package/project/.sdk/tm/kotlin/core/SdkClient.kt +9 -2
  257. package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
  258. package/project/.sdk/tm/kotlin/feature/AuditFeature.kt +11 -9
  259. package/project/.sdk/tm/kotlin/feature/CostFeature.kt +8 -5
  260. package/project/.sdk/tm/kotlin/feature/DebugFeature.kt +15 -22
  261. package/project/.sdk/tm/kotlin/feature/LogFeature.kt +8 -4
  262. package/project/.sdk/tm/kotlin/feature/ProxyFeature.kt +26 -2
  263. package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +11 -0
  264. package/project/.sdk/tm/kotlin/feature/TelemetryFeature.kt +4 -2
  265. package/project/.sdk/tm/kotlin/test/FeatureTest.kt +2 -2
  266. package/project/.sdk/tm/kotlin/utility/CoreUtil.kt +447 -11
  267. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +38 -31
  268. package/project/.sdk/tm/kotlin/utility/Register.kt +1 -0
  269. package/project/.sdk/tm/lua/core/context.lua +43 -0
  270. package/project/.sdk/tm/lua/core/error.lua +36 -1
  271. package/project/.sdk/tm/lua/core/utility_type.lua +4 -0
  272. package/project/.sdk/tm/lua/feature/audit_feature.lua +2 -2
  273. package/project/.sdk/tm/lua/feature/cost_feature.lua +2 -2
  274. package/project/.sdk/tm/lua/feature/debug_feature.lua +21 -17
  275. package/project/.sdk/tm/lua/feature/log_feature.lua +2 -1
  276. package/project/.sdk/tm/lua/feature/proxy_feature.lua +23 -1
  277. package/project/.sdk/tm/lua/feature/secrets_feature.lua +18 -0
  278. package/project/.sdk/tm/lua/feature/telemetry_feature.lua +4 -2
  279. package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +3 -2
  280. package/project/.sdk/tm/lua/test/feature_test.lua +10 -3
  281. package/project/.sdk/tm/lua/utility/clean.lua +426 -2
  282. package/project/.sdk/tm/lua/utility/clean_explain.lua +34 -0
  283. package/project/.sdk/tm/lua/utility/done.lua +1 -7
  284. package/project/.sdk/tm/lua/utility/make_error.lua +3 -1
  285. package/project/.sdk/tm/lua/utility/make_options.lua +76 -30
  286. package/project/.sdk/tm/lua/utility/register.lua +4 -1
  287. package/project/.sdk/tm/ocaml/Makefile +5 -1
  288. package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +12 -1
  289. package/project/.sdk/tm/ocaml/sdk_features.ml +78 -18
  290. package/project/.sdk/tm/ocaml/sdk_helpers.ml +27 -0
  291. package/project/.sdk/tm/ocaml/sdk_runtime.ml +304 -26
  292. package/project/.sdk/tm/ocaml/sdk_types.ml +1 -0
  293. package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +3 -1
  294. package/project/.sdk/tm/ocaml/test/t_feature.ml +2 -2
  295. package/project/.sdk/tm/perl/core/context.pm +27 -0
  296. package/project/.sdk/tm/perl/core/error.pm +52 -5
  297. package/project/.sdk/tm/perl/feature/audit_feature.pm +2 -2
  298. package/project/.sdk/tm/perl/feature/cost_feature.pm +2 -2
  299. package/project/.sdk/tm/perl/feature/debug_feature.pm +17 -13
  300. package/project/.sdk/tm/perl/feature/log_feature.pm +3 -1
  301. package/project/.sdk/tm/perl/feature/proxy_feature.pm +21 -1
  302. package/project/.sdk/tm/perl/feature/secrets_feature.pm +20 -1
  303. package/project/.sdk/tm/perl/feature/telemetry_feature.pm +4 -2
  304. package/project/.sdk/tm/perl/t/feature.t +4 -2
  305. package/project/.sdk/tm/perl/utility/clean.pm +345 -1
  306. package/project/.sdk/tm/perl/utility/done.pm +24 -6
  307. package/project/.sdk/tm/perl/utility/make_error.pm +17 -4
  308. package/project/.sdk/tm/perl/utility/make_options.pm +33 -14
  309. package/project/.sdk/tm/php/core/Context.php +24 -1
  310. package/project/.sdk/tm/php/core/Error.php +34 -3
  311. package/project/.sdk/tm/php/core/UtilityType.php +4 -0
  312. package/project/.sdk/tm/php/feature/AuditFeature.php +2 -2
  313. package/project/.sdk/tm/php/feature/CostFeature.php +2 -2
  314. package/project/.sdk/tm/php/feature/DebugFeature.php +18 -9
  315. package/project/.sdk/tm/php/feature/LogFeature.php +5 -2
  316. package/project/.sdk/tm/php/feature/ProxyFeature.php +20 -1
  317. package/project/.sdk/tm/php/feature/SecretsFeature.php +18 -0
  318. package/project/.sdk/tm/php/feature/TelemetryFeature.php +4 -2
  319. package/project/.sdk/tm/php/test/FeatureTest.php +2 -2
  320. package/project/.sdk/tm/php/utility/Clean.php +438 -2
  321. package/project/.sdk/tm/php/utility/Done.php +14 -7
  322. package/project/.sdk/tm/php/utility/MakeError.php +9 -7
  323. package/project/.sdk/tm/php/utility/MakeOptions.php +62 -38
  324. package/project/.sdk/tm/php/utility/Register.php +2 -0
  325. package/project/.sdk/tm/py/pkg/core/context.py +17 -0
  326. package/project/.sdk/tm/py/pkg/core/error.py +42 -1
  327. package/project/.sdk/tm/py/pkg/core/utility_type.py +4 -0
  328. package/project/.sdk/tm/py/pkg/feature/audit_feature.py +2 -2
  329. package/project/.sdk/tm/py/pkg/feature/cost_feature.py +2 -2
  330. package/project/.sdk/tm/py/pkg/feature/debug_feature.py +17 -12
  331. package/project/.sdk/tm/py/pkg/feature/log_feature.py +2 -1
  332. package/project/.sdk/tm/py/pkg/feature/proxy_feature.py +18 -1
  333. package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +12 -0
  334. package/project/.sdk/tm/py/pkg/feature/telemetry_feature.py +4 -2
  335. package/project/.sdk/tm/py/pkg/utility/clean.py +311 -1
  336. package/project/.sdk/tm/py/pkg/utility/done.py +18 -5
  337. package/project/.sdk/tm/py/pkg/utility/make_error.py +9 -6
  338. package/project/.sdk/tm/py/pkg/utility/make_options.py +64 -25
  339. package/project/.sdk/tm/py/pkg/utility/make_response.py +19 -13
  340. package/project/.sdk/tm/py/pkg/utility/register.py +4 -2
  341. package/project/.sdk/tm/py/test/feature_harness.py +4 -0
  342. package/project/.sdk/tm/py/test/test_feature.py +2 -2
  343. package/project/.sdk/tm/py/test/test_primary_utility.py +1 -0
  344. package/project/.sdk/tm/rb/core/context.rb +28 -0
  345. package/project/.sdk/tm/rb/core/error.rb +30 -0
  346. package/project/.sdk/tm/rb/core/utility_type.rb +1 -1
  347. package/project/.sdk/tm/rb/feature/audit_feature.rb +2 -2
  348. package/project/.sdk/tm/rb/feature/cost_feature.rb +2 -2
  349. package/project/.sdk/tm/rb/feature/debug_feature.rb +15 -12
  350. package/project/.sdk/tm/rb/feature/log_feature.rb +3 -1
  351. package/project/.sdk/tm/rb/feature/proxy_feature.rb +25 -1
  352. package/project/.sdk/tm/rb/feature/secrets_feature.rb +17 -0
  353. package/project/.sdk/tm/rb/feature/telemetry_feature.rb +4 -2
  354. package/project/.sdk/tm/rb/test/feature_test.rb +4 -2
  355. package/project/.sdk/tm/rb/utility/clean.rb +312 -1
  356. package/project/.sdk/tm/rb/utility/done.rb +15 -5
  357. package/project/.sdk/tm/rb/utility/make_error.rb +13 -4
  358. package/project/.sdk/tm/rb/utility/make_options.rb +31 -12
  359. package/project/.sdk/tm/rb/utility/register.rb +2 -0
  360. package/project/.sdk/tm/rust/core/context.rs +55 -0
  361. package/project/.sdk/tm/rust/core/utility_type.rs +17 -0
  362. package/project/.sdk/tm/rust/feature/audit.rs +2 -0
  363. package/project/.sdk/tm/rust/feature/cost.rs +3 -2
  364. package/project/.sdk/tm/rust/feature/debug.rs +16 -16
  365. package/project/.sdk/tm/rust/feature/log.rs +42 -7
  366. package/project/.sdk/tm/rust/feature/proxy.rs +48 -2
  367. package/project/.sdk/tm/rust/feature/secrets.rs +12 -0
  368. package/project/.sdk/tm/rust/feature/telemetry.rs +2 -0
  369. package/project/.sdk/tm/rust/tests/feature_test.rs +2 -2
  370. package/project/.sdk/tm/rust/utility/clean.rs +446 -5
  371. package/project/.sdk/tm/rust/utility/done.rs +22 -12
  372. package/project/.sdk/tm/rust/utility/make_error.rs +4 -1
  373. package/project/.sdk/tm/rust/utility/make_options.rs +73 -19
  374. package/project/.sdk/tm/scala/Makefile +3 -1
  375. package/project/.sdk/tm/scala/core/Context.scala +16 -0
  376. package/project/.sdk/tm/scala/core/SdkClient.scala +14 -4
  377. package/project/.sdk/tm/scala/core/SdkError.scala +28 -0
  378. package/project/.sdk/tm/scala/core/Types.scala +3 -0
  379. package/project/.sdk/tm/scala/core/Utility.scala +2 -0
  380. package/project/.sdk/tm/scala/feature/AuditFeature.scala +16 -10
  381. package/project/.sdk/tm/scala/feature/CostFeature.scala +10 -5
  382. package/project/.sdk/tm/scala/feature/DebugFeature.scala +24 -21
  383. package/project/.sdk/tm/scala/feature/LogFeature.scala +9 -4
  384. package/project/.sdk/tm/scala/feature/ProxyFeature.scala +23 -2
  385. package/project/.sdk/tm/scala/feature/SecretsFeature.scala +14 -0
  386. package/project/.sdk/tm/scala/feature/TelemetryFeature.scala +9 -2
  387. package/project/.sdk/tm/scala/sdktest/SdkTestMain.scala +2 -2
  388. package/project/.sdk/tm/scala/utility/Make.scala +63 -20
  389. package/project/.sdk/tm/scala/utility/Misc.scala +358 -5
  390. package/project/.sdk/tm/scala/utility/Register.scala +1 -0
  391. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +29 -0
  392. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +2 -0
  393. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/AuditFeature.swift +10 -9
  394. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/CostFeature.swift +7 -5
  395. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/DebugFeature.swift +19 -15
  396. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/FeatureOptions.swift +16 -0
  397. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/LogFeature.swift +4 -1
  398. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/ProxyFeature.swift +20 -5
  399. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/SecretsFeature.swift +13 -0
  400. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TelemetryFeature.swift +4 -2
  401. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeError.swift +15 -5
  402. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +58 -19
  403. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +1 -0
  404. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +391 -4
  405. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/FeatureTest.swift +2 -2
  406. package/project/.sdk/tm/ts/src/Context.ts +5 -1
  407. package/project/.sdk/tm/ts/src/feature/audit/AuditFeature.ts +2 -2
  408. package/project/.sdk/tm/ts/src/feature/cost/CostFeature.ts +2 -2
  409. package/project/.sdk/tm/ts/src/feature/debug/DebugFeature.ts +13 -7
  410. package/project/.sdk/tm/ts/src/feature/log/LogFeature.ts +9 -5
  411. package/project/.sdk/tm/ts/src/feature/proxy/ProxyFeature.ts +19 -1
  412. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +12 -0
  413. package/project/.sdk/tm/ts/src/feature/telemetry/TelemetryFeature.ts +4 -2
  414. package/project/.sdk/tm/ts/src/utility/CleanUtility.ts +287 -9
  415. package/project/.sdk/tm/ts/src/utility/DoneUtility.ts +18 -5
  416. package/project/.sdk/tm/ts/src/utility/MakeErrorUtility.ts +18 -8
  417. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +38 -16
  418. package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -2
  419. package/project/.sdk/tm/ts/test/feature/harness.ts +11 -1
  420. package/project/.sdk/tm/ts/test/feature.test.ts +9 -2
  421. package/project/.sdk/tm/zig/core/context.zig +34 -0
  422. package/project/.sdk/tm/zig/core/error.zig +22 -1
  423. package/project/.sdk/tm/zig/core/utility.zig +403 -39
  424. package/project/.sdk/tm/zig/feature/audit.zig +4 -2
  425. package/project/.sdk/tm/zig/feature/cost.zig +3 -2
  426. package/project/.sdk/tm/zig/feature/debug.zig +20 -25
  427. package/project/.sdk/tm/zig/feature/log.zig +3 -1
  428. package/project/.sdk/tm/zig/feature/proxy.zig +47 -6
  429. package/project/.sdk/tm/zig/feature/secrets.zig +7 -0
  430. package/project/.sdk/tm/zig/feature/telemetry.zig +4 -2
  431. package/project/.sdk/tm/zig/test/feature_test.zig +2 -2
  432. package/project/sdkgen-package.json +1 -1
  433. package/src/action/copies.ts +219 -0
  434. package/src/action/doctor.ts +247 -21
  435. package/src/action/edition.ts +10 -2
  436. package/src/action/feature.ts +11 -2
  437. package/src/action/package.ts +31 -10
  438. package/src/action/remove.ts +4 -0
  439. package/src/action/resolve.ts +16 -3
  440. package/src/action/target.ts +10 -2
  441. package/src/helpers/naming.ts +1 -1
  442. package/src/sdkgen.ts +45 -5
@@ -168,6 +168,14 @@ let make ?(plugins : Defs.definition list = [])
168
168
  let refuse (msg : string) =
169
169
  (match !initerr with None -> initerr := Some msg | Some _ -> ()) in
170
170
 
171
+ (* Every value the chain resolves and every token bought can leave through
172
+ * the SDK's diagnostics, so each is registered with the clean utility the
173
+ * moment this feature sees it. *)
174
+ let register (v : string) : unit =
175
+ match !client with
176
+ | Some { cl_rootctx = Some ctx; _ } -> (try (cu ctx).u_clean_add ctx v with _ -> ())
177
+ | _ -> () in
178
+
171
179
  let live_options () : value =
172
180
  match !client with Some c -> c.cl_options | None -> empty_map () in
173
181
 
@@ -263,11 +271,12 @@ let make ?(plugins : Defs.definition list = [])
263
271
  if "live" <> mode then begin
264
272
  let t = "test-" ^ x.xresponse in
265
273
  cred := t;
274
+ register t;
266
275
  Ok t
267
276
  end
268
277
  else
269
278
  match buyonce x with
270
- | Ok t -> cred := t; Ok t
279
+ | Ok t -> cred := t; register t; Ok t
271
280
  | Error m -> Error m in
272
281
 
273
282
  (* One resolution. sekreto caches a hit itself when `cache` is on, so
@@ -283,6 +292,7 @@ let make ?(plugins : Defs.definition list = [])
283
292
  (match Sekreto.tryget s !secretname with
284
293
  | exception e -> Error (exn_message e)
285
294
  | found ->
295
+ (match found with Some v -> register v | None -> ());
286
296
  (match !exchange with
287
297
  | None ->
288
298
  (* An UNCACHED miss after an earlier hit is a revocation: the
@@ -417,6 +427,7 @@ let make ?(plugins : Defs.definition list = [])
417
427
  let synthesized : Defs.definition list ref = ref [] in
418
428
 
419
429
  if "" <> explicit then begin
430
+ register explicit;
420
431
  match Secret.envkey !secretname with
421
432
  | key ->
422
433
  specs := [ { Provider.nospec with Provider.kind = "memory"; name = "options";
@@ -84,10 +84,29 @@ let base_feature () : feature =
84
84
  { f_name = "base"; f_version = "0.0.1"; f_active = true; f_options = Noval;
85
85
  f_init = (fun _ _ -> ()); f_hook = (fun _ _ -> ()) }
86
86
 
87
+ let log_hooks = ["PostConstruct"; "PostConstructEntity"; "SetData"; "GetData"; "SetMatch";
88
+ "GetMatch"; "PrePoint"; "PreSpec"; "PreRequest"; "PreResponse"; "PreResult"]
89
+
87
90
  let log_feature () : feature =
91
+ let options = ref (empty_map ()) in
88
92
  let f = { f_name = "log"; f_version = "0.0.1"; f_active = true; f_options = Noval;
89
93
  f_init = (fun _ _ -> ()); f_hook = (fun _ _ -> ()) } in
90
- f.f_init <- (fun _ctx opts -> f.f_active <- opt_active opts);
94
+ (* A log line leaves the pipeline, so it carries the cleaned record: the
95
+ spec after auth holds the credential, and a logger serialises whatever
96
+ it is handed. `logger` is a Func of the record; otherwise stderr. *)
97
+ let loghook hook ctx =
98
+ let record = (cu ctx).u_clean ctx (jo [
99
+ ("hook", Str hook); ("op", Str ctx.c_op.op_name);
100
+ ("spec", (match ctx.c_spec with Some s -> spec_to_value s | None -> Noval));
101
+ ("ctx", ctx_to_value ctx)]) in
102
+ match getp !options "logger" with
103
+ | Func _ as fn -> ignore (call_vfn fn record)
104
+ | _ -> prerr_endline ("[INFO] hook=" ^ hook ^ " op=" ^ ctx.c_op.op_name ^ " "
105
+ ^ jsonify ~flags:(jo [("indent", Num 0.)]) record) in
106
+ f.f_init <- (fun _ctx opts ->
107
+ options := (match to_map opts with Map _ -> opts | _ -> empty_map ());
108
+ f.f_active <- opt_active opts);
109
+ f.f_hook <- (fun name ctx -> if f.f_active && List.mem name log_hooks then loghook name ctx);
91
110
  f
92
111
 
93
112
  (* ------------------------------------------------------------------ *)
@@ -620,10 +639,11 @@ let telemetry_feature () : feature =
620
639
  setp span "end" (Num end_);
621
640
  setp span "durationMs" (Num (Float.max 0. (end_ -. (match getp span "start" with Num n -> n | _ -> 0.))));
622
641
  setp span "ok" (Bool ok);
642
+ let out = (cu ctx).u_clean ctx span in
623
643
  let t = telemetry ctx in
624
644
  bump_num t "active" (-1.);
625
- (match getp t "spans" with List r -> r := !r @ [span] | _ -> ());
626
- (match getp !options "exporter" with Func _ as fn -> (try ignore (call_vfn fn span) with _ -> ()) | _ -> ())
645
+ (match getp t "spans" with List r -> r := !r @ [out] | _ -> ());
646
+ (match getp !options "exporter" with Func _ as fn -> (try ignore (call_vfn fn out) with _ -> ()) | _ -> ())
627
647
  | _ -> () in
628
648
  f.f_init <- (fun ctx opts ->
629
649
  options := (match to_map opts with Map _ -> opts | _ -> empty_map ());
@@ -666,14 +686,15 @@ let debug_feature () : feature =
666
686
  let f = { f_name = "debug"; f_version = "0.0.1"; f_active = true; f_options = Noval;
667
687
  f_init = (fun _ _ -> ()); f_hook = (fun _ _ -> ()) } in
668
688
  let debug ctx = track_bucket (cc ctx) "debug" (fun () -> jo [("entries", empty_list ())]) in
669
- let redact headers =
689
+ (* The core clean rules apply (clean.keys, every registered value); the
690
+ feature's own `redact` list ADDS header names on top of them. *)
691
+ let redact ctx headers =
670
692
  match headers with
671
693
  | Map _ ->
672
- let patterns = opt_str_list !options "redact"
673
- ~default:["authorization"; "cookie"; "set-cookie"; "api-key"; "apikey"; "x-api-key"; "idempotency-key"] in
694
+ let patterns = List.map string_lower (opt_str_list !options "redact" ~default:[]) in
674
695
  let out = empty_map () in
675
- List.iter (fun k -> if List.mem (string_lower k) patterns then setp out k (Str "<redacted>") else setp out k (getp headers k)) (keysof headers);
676
- out
696
+ List.iter (fun k -> if List.mem (string_lower k) patterns then setp out k (Str "[redacted]") else setp out k (getp headers k)) (keysof headers);
697
+ (cu ctx).u_clean ctx out
677
698
  | _ -> empty_map () in
678
699
  let finish ctx ok =
679
700
  match scratch_get ctx "debug_entry" with
@@ -683,6 +704,10 @@ let debug_feature () : feature =
683
704
  setp entry "ok" (Bool (ok && result_ok));
684
705
  setp entry "durationMs" (Num (Float.max 0. (now_of !options -. (match getp entry "start" with Num n -> n | _ -> 0.))));
685
706
  (if getp entry "status" = Noval then match ctx.c_result with Some r -> setp entry "status" (vint_of r.rt_status) | None -> ());
707
+ (* The whole entry leaves through the buffer and the callback: the url
708
+ and the error message can carry a query credential the header mask
709
+ never saw. *)
710
+ let entry = (cu ctx).u_clean ctx entry in
686
711
  let buf = getp (debug ctx) "entries" in
687
712
  (match buf with List r -> r := !r @ [entry] | _ -> ());
688
713
  let mx = opt_int !options "max" ~default:100 in
@@ -701,7 +726,7 @@ let debug_feature () : feature =
701
726
  ("op", Str opname);
702
727
  ("method", (match ctx.c_spec with Some s -> Str s.sp_method | None -> Noval));
703
728
  ("url", (match ctx.c_spec with Some s -> Str (if s.sp_url <> "" then s.sp_url else s.sp_path) | None -> Noval));
704
- ("headers", redact (match ctx.c_spec with Some s -> s.sp_headers | None -> Noval));
729
+ ("headers", redact ctx (match ctx.c_spec with Some s -> s.sp_headers | None -> Noval));
705
730
  ("start", Num (now_of !options)); ("status", Noval); ("ok", Noval);
706
731
  ("durationMs", Noval); ("error", Noval) ] in
707
732
  scratch_set ctx "debug_entry" entry
@@ -740,13 +765,13 @@ let audit_feature () : feature =
740
765
  let actor = match ctx.c_ctrl.ctrl_actor with
741
766
  | Noval | Null -> (match getp !options "actor" with Noval | Null -> Str "anonymous" | a -> a)
742
767
  | a -> a in
743
- let record = jo [
768
+ let record = (cu ctx).u_clean ctx (jo [
744
769
  ("seq", vint_of !seq); ("ts", Num (now_of !options)); ("actor", actor);
745
770
  ("entity", Str (if ctx.c_op.op_entity <> "" then ctx.c_op.op_entity else "_"));
746
771
  ("op", Str (if ctx.c_op.op_name <> "" then ctx.c_op.op_name else "_"));
747
772
  ("outcome", Str outcome);
748
773
  ("status", (match ctx.c_result with Some r -> vint_of r.rt_status | None -> Noval));
749
- ("correlationId", Str ctx.c_id) ] in
774
+ ("correlationId", Str ctx.c_id) ]) in
750
775
  let records = getp (audit ctx) "records" in
751
776
  (match records with List r -> r := !r @ [record] | _ -> ());
752
777
  let mx = opt_int !options "max" ~default:1000 in
@@ -905,11 +930,13 @@ let cost_feature () : feature =
905
930
  bump (getp rec_ "actors") actor !amount;
906
931
 
907
932
  incr seq;
908
- setp rec_ "last"
909
- (jo [("seq", Num (float_of_int !seq)); ("entity", Str entity); ("op", Str opname);
910
- ("actor", Str actor); ("amount", Num !amount);
911
- ("currency", getp rec_ "currency"); ("source", Str !source);
912
- ("attempts", getp p "attempts")]) in
933
+ let last = (cu ctx).u_clean ctx
934
+ (jo [("seq", Num (float_of_int !seq)); ("entity", Str entity); ("op", Str opname);
935
+ ("actor", Str actor); ("amount", Num !amount);
936
+ ("currency", getp rec_ "currency"); ("source", Str !source);
937
+ ("attempts", getp p "attempts")]) in
938
+ setp rec_ "last" last;
939
+ (match getp !options "sink" with Func _ as fn -> (try ignore (call_vfn fn last) with _ -> ()) | _ -> ()) in
913
940
 
914
941
  let finish ctx done_ =
915
942
  if f.f_active then
@@ -1265,9 +1292,35 @@ let streaming_feature () : feature =
1265
1292
  (* proxy *)
1266
1293
  (* ------------------------------------------------------------------ *)
1267
1294
 
1295
+ (* The userinfo of a URL, as its colon-separated parts (empty when none). *)
1296
+ let url_userinfo (url : string) : string list =
1297
+ match String.index_opt url '@' with
1298
+ | None -> []
1299
+ | Some at ->
1300
+ let n = String.length url in
1301
+ let rec scheme_end i = if i + 3 > n then 0 else if String.sub url i 3 = "://" then i + 3 else scheme_end (i + 1) in
1302
+ let start = scheme_end 0 in
1303
+ if at <= start then []
1304
+ else
1305
+ let ui = String.sub url start (at - start) in
1306
+ if String.contains ui '/' then []
1307
+ else List.filter (fun s -> s <> "") (String.split_on_char ':' ui)
1308
+
1309
+ let pct_decode (s : string) : string =
1310
+ let n = String.length s in
1311
+ let b = Buffer.create n in
1312
+ let i = ref 0 in
1313
+ while !i < n do
1314
+ (match s.[!i], (if !i + 2 < n then int_of_string_opt ("0x" ^ String.sub s (!i + 1) 2) else None) with
1315
+ | '%', Some c -> Buffer.add_char b (Char.chr (c land 0xff)); i := !i + 3
1316
+ | c, _ -> Buffer.add_char b c; incr i)
1317
+ done;
1318
+ Buffer.contents b
1319
+
1268
1320
  let proxy_feature () : feature =
1269
1321
  let options = ref (empty_map ()) in
1270
1322
  let purl = ref Noval and noproxy = ref [] in
1323
+ let clean_url = ref Noval in
1271
1324
  let f = { f_name = "proxy"; f_version = "0.0.1"; f_active = true; f_options = Noval;
1272
1325
  f_init = (fun _ _ -> ()); f_hook = (fun _ _ -> ()) } in
1273
1326
  let env k = match Sys.getenv_opt k with Some v when v <> "" -> Some v | _ -> None in
@@ -1279,7 +1332,7 @@ let proxy_feature () : feature =
1279
1332
  end in
1280
1333
  let track ctx =
1281
1334
  let cl = cc ctx in
1282
- let bucket = track_bucket cl "proxy" (fun () -> jo [("routed", Num 0.); ("url", !purl)]) in
1335
+ let bucket = track_bucket cl "proxy" (fun () -> jo [("routed", Num 0.); ("url", !clean_url)]) in
1283
1336
  bump_num bucket "routed" 1. in
1284
1337
  let route ctx url fetchdef =
1285
1338
  if is_nullish !purl || bypass url then fetchdef
@@ -1314,6 +1367,13 @@ let proxy_feature () : feature =
1314
1367
  end);
1315
1368
  noproxy := !np_list;
1316
1369
  let u = cu ctx in
1370
+ (* A proxy URL may carry credentials as userinfo, from the option or
1371
+ the environment, and neither is under a sensitive key name. *)
1372
+ (match !purl with
1373
+ | Str s ->
1374
+ List.iter (fun part -> u.u_clean_add ctx part; u.u_clean_add ctx (pct_decode part)) (url_userinfo s);
1375
+ clean_url := u.u_clean ctx (Str s)
1376
+ | v -> clean_url := v);
1317
1377
  let inner = u.u_fetcher in
1318
1378
  u.u_fetcher <- (fun fctx url fd -> inner fctx url (route fctx url fd))
1319
1379
  end);
@@ -1670,7 +1730,7 @@ let raw_request (client : sdk_client) (fetchargs : value) : value =
1670
1730
  let url = match getp fetchdef "url" with Str s -> s | _ -> "" in
1671
1731
  let (fetched, fetch_err) = u.u_fetcher ctx url fetchdef in
1672
1732
  (match fetch_err with
1673
- | Some fe -> jo [("ok", Bool false); ("err", err_to_value fe)]
1733
+ | Some fe -> jo [("ok", Bool false); ("err", u.u_clean ctx (err_to_value fe))]
1674
1734
  | None ->
1675
1735
  if is_noval fetched || fetched = Null then
1676
1736
  jo [("ok", Bool false); ("err", err_to_value (ctx_make_error ctx "direct_no_response" "response: undefined"))]
@@ -60,6 +60,33 @@ let json_thunk (data : value) : value = Func (fun _ _ _ _ -> data)
60
60
  let call_json (j : value) : value = call_vfn j Noval
61
61
  let is_callable (v : value) : bool = match v with Func _ -> true | _ -> false
62
62
 
63
+ (* ----- base64 (no opam dependency) -----
64
+ * Shared by the generated prepare_auth (the Basic wire form) and the clean
65
+ * registry (the encoded forms a registered value travels in). *)
66
+
67
+ let b64_alphabet =
68
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
69
+
70
+ let base64_encode (text : string) : string =
71
+ let n = String.length text in
72
+ let buf = Buffer.create (((n + 2) / 3) * 4) in
73
+ let i = ref 0 in
74
+ while !i < n do
75
+ let rest = n - !i in
76
+ let a = Char.code text.[!i] in
77
+ let b = if 1 < rest then Char.code text.[!i + 1] else 0 in
78
+ let c = if 2 < rest then Char.code text.[!i + 2] else 0 in
79
+ let word = (a lsl 16) lor (b lsl 8) lor c in
80
+ Buffer.add_char buf b64_alphabet.[(word lsr 18) land 0x3f];
81
+ Buffer.add_char buf b64_alphabet.[(word lsr 12) land 0x3f];
82
+ (* One trailing source byte yields two characters and "==", two yield
83
+ * three and "=". *)
84
+ Buffer.add_char buf (if 1 < rest then b64_alphabet.[(word lsr 6) land 0x3f] else '=');
85
+ Buffer.add_char buf (if 2 < rest then b64_alphabet.[word land 0x3f] else '=');
86
+ i := !i + 3
87
+ done;
88
+ Buffer.contents buf
89
+
63
90
  (* ----- errors ----- *)
64
91
 
65
92
  let mk_error (code : string) (msg : string) : sdk_error =
@@ -241,7 +241,244 @@ let make_context_impl (cs : ctxspec) (basectx : ctx option) : ctx =
241
241
  (* utilities *)
242
242
  (* ------------------------------------------------------------------ *)
243
243
 
244
- let clean_util (_ctx : ctx) (v : value) : value = v
244
+ (* ----- clean -----
245
+ * Everything that leaves the pipeline passes through clean; inside it data
246
+ * stays raw, so a hook can still read the header it must add to. See
247
+ * docs/explanation/secret-redaction.md. *)
248
+
249
+ let clean_maxdepth = 32
250
+ let clean_circular = "[circular]"
251
+ let clean_drop = Sentinel "clean_drop"
252
+
253
+ let normkey (k : string) : string =
254
+ let b = Buffer.create (String.length k) in
255
+ String.iter (fun c -> match c with
256
+ | '-' | '_' -> ()
257
+ | c -> Buffer.add_char b (Char.lowercase_ascii c)) k;
258
+ Buffer.contents b
259
+
260
+ let split_commas (s : string) : string list =
261
+ List.filter (fun x -> x <> "") (List.map String.trim (String.split_on_char ',' s))
262
+
263
+ let splitkeys (v : value) : string list =
264
+ match v with
265
+ | Str s -> List.filter (fun x -> x <> "") (List.map normkey (split_commas s))
266
+ | _ -> []
267
+
268
+ let splitvalues (v : value) : string list =
269
+ match v with
270
+ | List r -> List.filter_map (function Str s -> Some s | _ -> None) !r
271
+ | Str s -> split_commas s
272
+ | _ -> []
273
+
274
+ let count_opt (v : value) (dflt : int) : int =
275
+ let n = match v with
276
+ | Num n -> Some n
277
+ | Str s -> float_of_string_opt (String.trim s)
278
+ | _ -> None in
279
+ match n with Some n when n >= 0. -> int_of_float (Float.floor n) | _ -> dflt
280
+
281
+ (* The derived clean block: a struct map so it lives in options.__derived__
282
+ * and its `values` list stays MUTABLE after make_options - features
283
+ * register what they resolve later. *)
284
+ let make_clean_config (cleanopts : value) : value =
285
+ jo [("active", Bool (getp cleanopts "active" <> Bool false));
286
+ ("keys", ja (List.map (fun s -> Str s) (splitkeys (getp cleanopts "keys"))));
287
+ ("values", empty_list ());
288
+ ("mask", (match getp cleanopts "mask" with Str s -> Str s | _ -> Str "[redacted]"));
289
+ ("hint", vint_of (count_opt (getp cleanopts "hint") 0));
290
+ ("min", vint_of (max 1 (count_opt (getp cleanopts "min") 4)))]
291
+
292
+ (* A context without options (make_error accepts a bare one) still masks by
293
+ * the schema defaults. *)
294
+ let clean_config (ctx : ctx) : value =
295
+ match getpath_s ctx.c_options "__derived__.clean" with
296
+ | Map _ as m -> m
297
+ | _ -> make_clean_config (getp (Sdk_schema.opt_spec_value ()) "clean")
298
+
299
+ let str_values (v : value) : string list =
300
+ match v with List r -> List.filter_map (function Str s -> Some s | _ -> None) !r | _ -> []
301
+
302
+ (* The encoded forms a value travels in. *)
303
+ let clean_forms (value : string) : string list =
304
+ let out = ref [] in
305
+ let add s = if s <> "" && not (List.mem s !out) then out := !out @ [s] in
306
+ add value;
307
+ add (base64_encode value);
308
+ add (escurl_s value);
309
+ (let j = json_encode (Str value) in
310
+ if String.length j >= 2 then add (String.sub j 1 (String.length j - 2)));
311
+ !out
312
+
313
+ let clean_add_util (ctx : ctx) (value : string) : unit =
314
+ let cfg = clean_config ctx in
315
+ let minlen = match getp cfg "min" with Num n -> int_of_float n | _ -> 4 in
316
+ match getp cfg "values" with
317
+ | List r when String.length value >= minlen ->
318
+ let have = str_values (List r) in
319
+ let add = List.filter (fun f -> String.length f >= minlen && not (List.mem f have))
320
+ (clean_forms value) in
321
+ (* Longest first, so a value is never masked by a substring of itself. *)
322
+ if add <> [] then
323
+ r := List.map (fun s -> Str s)
324
+ (List.stable_sort (fun a b -> compare (String.length b) (String.length a)) (have @ add))
325
+ | _ -> ()
326
+
327
+ let mask_value (cfg : value) (value : string) : string =
328
+ let hint = match getp cfg "hint" with Num n -> int_of_float n | _ -> 0 in
329
+ let mask = match getp cfg "mask" with Str s -> s | _ -> "[redacted]" in
330
+ if hint > 0 && String.length value > 2 * hint
331
+ then mask ^ String.sub value (String.length value - hint) hint
332
+ else mask
333
+
334
+ let clean_string (cfg : value) (text : string) : string =
335
+ List.fold_left (fun out v ->
336
+ if substr_contains out v then str_replace_all out v (mask_value cfg v) else out)
337
+ text (str_values (getp cfg "values"))
338
+
339
+ let sensitive_key (cfg : value) (key : string option) : bool =
340
+ match key with
341
+ | None -> false
342
+ | Some k ->
343
+ (match int_of_string_opt k with
344
+ | Some _ -> false
345
+ | None ->
346
+ let nk = normkey k in
347
+ List.exists (fun sk -> substr_contains nk sk) (str_values (getp cfg "keys")))
348
+
349
+ (* Every scalar under a sensitive name, at any depth and of any shape: a
350
+ * credential mistyped as a map or a number is still a credential, and the
351
+ * validation error that rejects it quotes it. *)
352
+ let clean_add_sensitive (ctx : ctx) (v : value) : unit =
353
+ let cfg = clean_config ctx in
354
+ let rec walk v under depth seen =
355
+ if depth < clean_maxdepth then begin
356
+ match v with
357
+ | Str s -> if under then clean_add_util ctx s
358
+ | Num n -> if under then clean_add_util ctx (num_to_string n)
359
+ | Map m when not (List.memq v seen) ->
360
+ List.iter (fun (k, x) ->
361
+ walk x (under || sensitive_key cfg (Some k)) (depth + 1) (v :: seen)) m.entries
362
+ | List r when not (List.memq v seen) ->
363
+ List.iter (fun x -> walk x under (depth + 1) (v :: seen)) !r
364
+ | _ -> ()
365
+ end in
366
+ walk v false 0 []
367
+
368
+ (* A registered value used as a property name is masked like any other
369
+ * string; names that mask alike take a counter, so none is lost. *)
370
+ let clean_name (cfg : value) (out : value) (key : string) : string =
371
+ let name = clean_string cfg key in
372
+ let taken = keysof out in
373
+ if name = key || not (List.mem name taken) then name
374
+ else
375
+ let rec next i =
376
+ let n = name ^ "#" ^ string_of_int i in
377
+ if List.mem n taken then next (i + 1) else n in
378
+ next 1
379
+
380
+ (* A masked plain-data COPY: functions dropped, cycles cut, and nothing
381
+ * shared with the live value, whose spec must stay raw. *)
382
+ let rec clean_snapshot (cfg : value) (v : value) (key : string option) (depth : int) (seen : value list) : value =
383
+ match v with
384
+ | Noval | Null -> v
385
+ | Str s -> Str (if sensitive_key cfg key then mask_value cfg s else clean_string cfg s)
386
+ | Func _ | Sentinel _ -> clean_drop
387
+ | Bool _ | Num _ -> if sensitive_key cfg key then getp cfg "mask" else v
388
+ | Map _ | List _ ->
389
+ if depth >= clean_maxdepth || List.memq v seen then Str clean_circular
390
+ else if sensitive_key cfg key then getp cfg "mask"
391
+ else begin
392
+ let seen = v :: seen in
393
+ match v with
394
+ | Map m ->
395
+ let out = empty_map () in
396
+ List.iter (fun (k, x) ->
397
+ match clean_snapshot cfg x (Some k) (depth + 1) seen with
398
+ | Sentinel "clean_drop" -> ()
399
+ | c -> setp out (clean_name cfg out k) c) m.entries;
400
+ out
401
+ | List r ->
402
+ lst (List.map (fun x ->
403
+ match clean_snapshot cfg x None (depth + 1) seen with
404
+ | Sentinel "clean_drop" -> Null
405
+ | c -> c) !r)
406
+ | _ -> v
407
+ end
408
+
409
+ let clean_util (ctx : ctx) (v : value) : value =
410
+ let cfg = clean_config ctx in
411
+ if getp cfg "active" = Bool false then v
412
+ else match v with
413
+ | Str s -> Str (clean_string cfg s)
414
+ | _ -> (match clean_snapshot cfg v None 0 [] with Sentinel "clean_drop" -> Noval | c -> c)
415
+
416
+ (* An exception is immutable and of any shape, so one that never passed
417
+ * through make_error (a hook's, a fetcher's) leaves as a cleaned copy: a
418
+ * standard string-carrying one keeps its constructor, and any other whose
419
+ * printed form quotes a registered value leaves as a Failure of that form,
420
+ * cleaned. *)
421
+ let clean_exn (ctx : ctx) (e : exn) : exn =
422
+ let cfg = clean_config ctx in
423
+ if getp cfg "active" = Bool false then e
424
+ else match e with
425
+ | Sdk_error_exc er ->
426
+ Sdk_error_exc { err_code = clean_string cfg er.err_code; err_msg = clean_string cfg er.err_msg;
427
+ err_result = clean_util ctx er.err_result; err_spec = clean_util ctx er.err_spec }
428
+ | Failure msg -> Failure (clean_string cfg msg)
429
+ | Invalid_argument msg -> Invalid_argument (clean_string cfg msg)
430
+ | Struct_error msg -> Struct_error (clean_string cfg msg)
431
+ | _ ->
432
+ let text = Printexc.to_string e in
433
+ let cleaned = clean_string cfg text in
434
+ if cleaned = text then e else Failure cleaned
435
+
436
+ (* The explain map is the CALLER's, so it is cleaned in place: what they hold
437
+ * after the call is the cleaned record. An omap is a mutable record, so its
438
+ * entries are replaced under the value the caller still references. Its
439
+ * result is a snapshot (result_to_value), never the live result, so err is
440
+ * pruned from it in place. *)
441
+ let clean_explain (ctx : ctx) : unit =
442
+ match ctx.c_ctrl.ctrl_explain with
443
+ | Map m as ex ->
444
+ (match (cu ctx).u_clean ctx ex with
445
+ | Map cm when cm != m -> m.entries <- cm.entries
446
+ | _ -> ());
447
+ (match getp ex "result" with Map _ as r -> ignore (delprop r (Str "err")) | _ -> ())
448
+ | _ -> ()
449
+
450
+ (* The serialised context leaves the pipeline (a logger, an error dump), so
451
+ * it is the cleaned record; the live fields stay raw for the pipeline's use.
452
+ * An OCaml record has no default print, so this is the SDK's own
453
+ * serialisation of a context. *)
454
+ let ctx_to_value (ctx : ctx) : value =
455
+ let record = jo [
456
+ ("id", Str ctx.c_id);
457
+ ("op", jo [("entity", Str ctx.c_op.op_entity); ("name", Str ctx.c_op.op_name);
458
+ ("input", Str ctx.c_op.op_input); ("points", ctx.c_op.op_points);
459
+ ("alias", ctx.c_op.op_alias)]);
460
+ ("spec", (match ctx.c_spec with Some s -> spec_to_value s | None -> Noval));
461
+ ("entity", (match ctx.c_entity with Some e -> Str e.e_name | None -> Noval));
462
+ ("result", (match ctx.c_result with Some r -> result_to_value r | None -> Noval));
463
+ ("response", (match ctx.c_response with
464
+ | Some r -> jo [("status", vint_of r.rs_status); ("statusText", Str r.rs_status_text);
465
+ ("headers", r.rs_headers); ("body", r.rs_body)]
466
+ | None -> Noval));
467
+ ("meta", ctx.c_meta) ] in
468
+ match ctx.c_utility with Some u -> u.u_clean ctx record | None -> clean_util ctx record
469
+
470
+ (* The client's own serialisation: its name and mode, never its options. *)
471
+ let client_to_value (client : sdk_client) : value =
472
+ jo [("name", Str "ProjectName"); ("mode", Str client.cl_mode)]
473
+
474
+ (* The error prints as the cleaned pieces make_error attached. *)
475
+ let () =
476
+ Printexc.register_printer (function
477
+ | Sdk_error_exc e ->
478
+ Some ("Sdk_error_exc " ^ jsonify ~flags:(jo [("indent", Num 0.)])
479
+ (jo [("code", Str e.err_code); ("message", Str e.err_msg);
480
+ ("result", e.err_result); ("spec", e.err_spec)]))
481
+ | _ -> None)
245
482
 
246
483
  let make_error_util (ctx : ctx) (err_opt : sdk_error option) : value =
247
484
  let op = ctx.c_op in
@@ -256,11 +493,15 @@ let make_error_util (ctx : ctx) (err_opt : sdk_error option) : value =
256
493
  let msg = "ProjectNameSDK: " ^ opname ^ ": " ^ err.err_msg in
257
494
  let msg = match (cu ctx).u_clean ctx (Str msg) with Str s -> s | _ -> msg in
258
495
  result.rt_err <- None;
496
+ clean_explain ctx;
259
497
  (match ctx.c_ctrl.ctrl_explain with
260
498
  | Map _ -> setp ctx.c_ctrl.ctrl_explain "err" (jo [("message", Str msg)])
261
499
  | _ -> ());
500
+ (* Cleaned COPIES of the result and spec, never the live objects. *)
501
+ (* A hook's own error supplies the code as well as the message. *)
502
+ let code = match (cu ctx).u_clean ctx (Str err.err_code) with Str s -> s | _ -> err.err_code in
262
503
  let sdk_err = {
263
- err_code = err.err_code; err_msg = msg;
504
+ err_code = code; err_msg = msg;
264
505
  err_result = (cu ctx).u_clean ctx (result_to_value result);
265
506
  err_spec = (match ctx.c_spec with Some s -> (cu ctx).u_clean ctx (spec_to_value s) | None -> Noval);
266
507
  } in
@@ -274,13 +515,7 @@ let make_error_util (ctx : ctx) (err_opt : sdk_error option) : value =
274
515
  else raise (Sdk_error_exc sdk_err)
275
516
 
276
517
  let done_util (ctx : ctx) : value =
277
- (match ctx.c_ctrl.ctrl_explain with
278
- | Map _ as ex ->
279
- ctx.c_ctrl.ctrl_explain <- (cu ctx).u_clean ctx ex;
280
- (match ctx.c_ctrl.ctrl_explain with
281
- | Map _ as ex2 -> (match getp ex2 "result" with Map _ as r -> ignore (delprop r (Str "err")) | _ -> ())
282
- | _ -> ())
283
- | _ -> ());
518
+ clean_explain ctx;
284
519
  match ctx.c_result with
285
520
  | Some result when result.rt_ok -> result.rt_resdata
286
521
  | _ -> (cu ctx).u_make_error ctx None
@@ -942,14 +1177,20 @@ let make_response_util (ctx : ctx) : (response option * sdk_error option) =
942
1177
  | None -> (None, Some (ctx_make_error ctx "response_no_result" "Expected context result property to be defined."))
943
1178
  | Some result ->
944
1179
  spec.sp_step <- "response";
945
- u.u_result_basic ctx; u.u_result_headers ctx; u.u_result_body ctx;
946
- (* GraphQL reports failures as a top-level `errors` array under
947
- * HTTP 200, so result_basic's status check never sees them. Lift
948
- * them here, before the response transform tries to unwrap data
949
- * that is not there. *)
950
- ignore (u.u_graphql_errors ctx);
951
- ignore (u.u_transform_response ctx);
952
- if result.rt_err = None then result.rt_ok <- true;
1180
+ (* A body reader that raises (a non-JSON body) fails the result,
1181
+ * as in ts; it must not escape the pipeline with the raw spec
1182
+ * still on the explain record. *)
1183
+ (try
1184
+ u.u_result_basic ctx; u.u_result_headers ctx; u.u_result_body ctx;
1185
+ (* GraphQL reports failures as a top-level `errors` array under
1186
+ * HTTP 200, so result_basic's status check never sees them.
1187
+ * Lift them here, before the response transform tries to
1188
+ * unwrap data that is not there. *)
1189
+ ignore (u.u_graphql_errors ctx);
1190
+ ignore (u.u_transform_response ctx);
1191
+ if result.rt_err = None then result.rt_ok <- true
1192
+ with e ->
1193
+ result.rt_err <- Some (ctx_make_error ctx "response_body" (Printexc.to_string e)));
953
1194
  (match ctx.c_ctrl.ctrl_explain with Map _ -> setp ctx.c_ctrl.ctrl_explain "result" (result_to_value result) | _ -> ());
954
1195
  (Some response, None))))
955
1196
 
@@ -1016,6 +1257,28 @@ let fetcher_util (ctx : ctx) (fullurl : string) (fetchdef : value) : (value * sd
1016
1257
  (* ------------------------------------------------------------------ *)
1017
1258
 
1018
1259
 
1260
+ let noentity (settings : value) : value =
1261
+ match settings with
1262
+ | Map _ ->
1263
+ let out = empty_map () in
1264
+ List.iter (fun k -> if k <> "entity" then setp out k (getp settings k)) (keysof settings);
1265
+ out
1266
+ | v -> v
1267
+
1268
+ (* The options to scan for secrets. The feature map is keyed by feature
1269
+ * names, not field names, so it is scanned as a list: `secrets` must not
1270
+ * make every setting of that feature a secret. Entity blocks hold entity
1271
+ * settings and seeded records, never a credential, so none is scanned. *)
1272
+ let secret_scan (opts : value) (names : string list) : value =
1273
+ let out = empty_map () in
1274
+ List.iter (fun k ->
1275
+ if k <> "entity" && not (List.mem k names) then
1276
+ match k, getp opts k with
1277
+ | "feature", (Map _ as fm) -> setp out k (ja (List.map (fun f -> noentity (getp fm f)) (keysof fm)))
1278
+ | "test", v -> setp out k (noentity v)
1279
+ | _, v -> setp out k v) (keysof opts);
1280
+ out
1281
+
1019
1282
  let make_options_util (ctx : ctx) : value =
1020
1283
  let options = match ctx.c_options with Noval -> empty_map () | v -> v in
1021
1284
  (match getp options "utility" with
@@ -1071,9 +1334,25 @@ let make_options_util (ctx : ctx) : value =
1071
1334
  Parsed once and memoised by Sdk_schema: validate reads the spec and
1072
1335
  writes into the options, never into the spec. *)
1073
1336
  let optspec = Sdk_schema.opt_spec_value () in
1337
+ (* The secret registry exists BEFORE validation, fed from the raw input, so
1338
+ the constructor's own rejection of a mistyped credential is clean too. A
1339
+ shallow merge over the schema defaults: the clean block is flat. *)
1340
+ let cleanraw = empty_map () in
1341
+ List.iter (fun src -> match src with
1342
+ | Map _ -> List.iter (fun k -> setp cleanraw k (getp src k)) (keysof src)
1343
+ | _ -> ()) [getp optspec "clean"; getp cfgopts "clean"; getp opts "clean"];
1344
+ let cleancfg = make_clean_config cleanraw in
1345
+ let cleanctx = { ctx with c_options = jo [("__derived__", jo [("clean", cleancfg)])] } in
1346
+ clean_add_sensitive cleanctx (secret_scan opts ["clean"]);
1347
+ List.iter (fun src -> List.iter (clean_add_util cleanctx) (splitvalues (getpath_s src "clean.values")))
1348
+ [cfgopts; opts];
1074
1349
  let sys_fetch = getpath_s opts "system.fetch" in
1075
- let merged = merge (ja [empty_map (); cfgopts; opts]) in
1076
- let validated = validate merged optspec in
1350
+ (* Clone the config side: merge writes into the nested maps it takes. *)
1351
+ let merged = merge (ja [empty_map (); clone cfgopts; opts]) in
1352
+ let validated =
1353
+ try validate merged optspec
1354
+ with Struct_error msg ->
1355
+ raise (Struct_error (match clean_util cleanctx (Str msg) with Str s -> s | _ -> msg)) in
1077
1356
  let opts = match validated with Map _ as m -> m | _ -> empty_map () in
1078
1357
  (* Restore the suppression the optspec default would otherwise erase. *)
1079
1358
  let () = if auth_suppressed then setp opts "auth" Null in
@@ -1132,10 +1411,6 @@ let make_options_util (ctx : ctx) : value =
1132
1411
  match getp opts "system" with
1133
1412
  | Map _ as sys -> setp sys "fetch" sys_fetch
1134
1413
  | _ -> setp opts "system" (jo [("fetch", sys_fetch)]));
1135
- let clean_keys = match getpath_s opts "clean.keys" with Str s -> s | _ -> "key,token,id" in
1136
- let parts = List.filter_map (fun p -> let t = String.trim p in if t = "" then None else Some (escre_s t))
1137
- (String.split_on_char ',' clean_keys) in
1138
- let keyre = String.concat "|" parts in
1139
1414
  (* Resolve the feature add-order: an explicit list order (above) wins;
1140
1415
  otherwise order the map test-first, then the remaining names sorted, so
1141
1416
  the outcome is deterministic and `test` is always the base transport. *)
@@ -1148,10 +1423,11 @@ let make_options_util (ctx : ctx) : value =
1148
1423
  if List.mem "test" names
1149
1424
  then "test" :: List.filter (fun n -> n <> "test") names
1150
1425
  else names in
1151
- let derived = jo [("clean", empty_map ())] in
1152
- if keyre <> "" then setp derived "clean" (jo [("keyre", Str keyre)]);
1153
- setp derived "featureorder" (ja (List.map (fun s -> Str s) feature_order));
1426
+ let derived = jo [("clean", cleancfg);
1427
+ ("featureorder", ja (List.map (fun s -> Str s) feature_order))] in
1154
1428
  setp opts "__derived__" derived;
1429
+ (* Again over the merged result: the config's own defaults can carry one. *)
1430
+ clean_add_sensitive { ctx with c_options = opts } (secret_scan opts ["clean"; "__derived__"]);
1155
1431
  opts
1156
1432
 
1157
1433
  (* ------------------------------------------------------------------ *)
@@ -1193,6 +1469,7 @@ let new_utility () : utility =
1193
1469
  u_struct = struct_api_instance;
1194
1470
  u_fetcher = fetcher_util;
1195
1471
  u_clean = clean_util;
1472
+ u_clean_add = clean_add_util;
1196
1473
  u_done = done_util;
1197
1474
  u_make_error = make_error_util;
1198
1475
  u_feature_add = feature_add_util;
@@ -1229,6 +1506,7 @@ let new_utility () : utility =
1229
1506
  let register (u : utility) : unit =
1230
1507
  u.u_fetcher <- fetcher_util;
1231
1508
  u.u_clean <- clean_util;
1509
+ u.u_clean_add <- clean_add_util;
1232
1510
  u.u_done <- done_util;
1233
1511
  u.u_make_error <- make_error_util;
1234
1512
  u.u_feature_add <- feature_add_util;