@voxgig/sdkgen 4.34.1 → 4.34.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 (305) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/ReadmeErrors.js +3 -3
  3. package/dist/cmp/ReadmeErrors.js.map +1 -1
  4. package/dist/cmp/ReadmeExplanation.js +3 -3
  5. package/dist/cmp/ReadmeExplanation.js.map +1 -1
  6. package/dist/cmp/ReadmeQuick.js +3 -1
  7. package/dist/cmp/ReadmeQuick.js.map +1 -1
  8. package/dist/cmp/ReadmeTop.js +9 -8
  9. package/dist/cmp/ReadmeTop.js.map +1 -1
  10. package/dist/helpers/definitionPlan.js +8 -4
  11. package/dist/helpers/definitionPlan.js.map +1 -1
  12. package/dist/helpers/naming.d.ts +2 -1
  13. package/dist/helpers/naming.js +11 -4
  14. package/dist/helpers/naming.js.map +1 -1
  15. package/dist/helpers/opExample.d.ts +16 -2
  16. package/dist/helpers/opExample.js +80 -21
  17. package/dist/helpers/opExample.js.map +1 -1
  18. package/dist/helpers/opShape.d.ts +3 -1
  19. package/dist/helpers/opShape.js +21 -8
  20. package/dist/helpers/opShape.js.map +1 -1
  21. package/dist/sdkgen.d.ts +4 -4
  22. package/dist/sdkgen.js +12 -4
  23. package/dist/sdkgen.js.map +1 -1
  24. package/dist/tsconfig.tsbuildinfo +1 -1
  25. package/dist/utility.d.ts +2 -1
  26. package/dist/utility.js +29 -3
  27. package/dist/utility.js.map +1 -1
  28. package/package.json +2 -2
  29. package/project/.sdk/src/cmp/c/ReadmeEntity_c.ts +3 -2
  30. package/project/.sdk/src/cmp/c/ReadmeHowto_c.ts +7 -3
  31. package/project/.sdk/src/cmp/c/ReadmeQuick_c.ts +3 -2
  32. package/project/.sdk/src/cmp/c/ReadmeRef_c.ts +5 -4
  33. package/project/.sdk/src/cmp/c/ReadmeTopQuick_c.ts +3 -2
  34. package/project/.sdk/src/cmp/c/ReadmeTopTest_c.ts +8 -7
  35. package/project/.sdk/src/cmp/c/utility_c.ts +21 -2
  36. package/project/.sdk/src/cmp/clojure/ReadmeEntity_clojure.ts +4 -1
  37. package/project/.sdk/src/cmp/clojure/ReadmeHowto_clojure.ts +17 -6
  38. package/project/.sdk/src/cmp/clojure/ReadmeModel_clojure.ts +6 -6
  39. package/project/.sdk/src/cmp/clojure/ReadmeQuick_clojure.ts +13 -8
  40. package/project/.sdk/src/cmp/clojure/ReadmeRef_clojure.ts +21 -18
  41. package/project/.sdk/src/cmp/clojure/ReadmeTopQuick_clojure.ts +6 -3
  42. package/project/.sdk/src/cmp/clojure/ReadmeTopTest_clojure.ts +16 -9
  43. package/project/.sdk/src/cmp/clojure/TestClean_clojure.ts +4 -4
  44. package/project/.sdk/src/cmp/clojure/fragment/Entity.fragment.clj +2 -1
  45. package/project/.sdk/src/cmp/clojure/utility_clojure.ts +14 -0
  46. package/project/.sdk/src/cmp/cpp/ReadmeEntity_cpp.ts +3 -2
  47. package/project/.sdk/src/cmp/cpp/ReadmeHowto_cpp.ts +7 -3
  48. package/project/.sdk/src/cmp/cpp/ReadmeQuick_cpp.ts +3 -2
  49. package/project/.sdk/src/cmp/cpp/ReadmeRef_cpp.ts +6 -5
  50. package/project/.sdk/src/cmp/cpp/ReadmeTopQuick_cpp.ts +3 -2
  51. package/project/.sdk/src/cmp/cpp/ReadmeTopTest_cpp.ts +8 -7
  52. package/project/.sdk/src/cmp/cpp/utility_cpp.ts +13 -0
  53. package/project/.sdk/src/cmp/csharp/ReadmeEntity_csharp.ts +3 -2
  54. package/project/.sdk/src/cmp/csharp/ReadmeHowto_csharp.ts +20 -8
  55. package/project/.sdk/src/cmp/csharp/ReadmeQuick_csharp.ts +13 -8
  56. package/project/.sdk/src/cmp/csharp/ReadmeRef_csharp.ts +10 -5
  57. package/project/.sdk/src/cmp/csharp/ReadmeTopQuick_csharp.ts +14 -7
  58. package/project/.sdk/src/cmp/csharp/ReadmeTopTest_csharp.ts +18 -10
  59. package/project/.sdk/src/cmp/csharp/utility_csharp.ts +13 -0
  60. package/project/.sdk/src/cmp/elixir/ReadmeEntity_elixir.ts +4 -4
  61. package/project/.sdk/src/cmp/elixir/ReadmeExplanation_elixir.ts +2 -1
  62. package/project/.sdk/src/cmp/elixir/ReadmeHowto_elixir.ts +13 -6
  63. package/project/.sdk/src/cmp/elixir/ReadmeModel_elixir.ts +5 -5
  64. package/project/.sdk/src/cmp/elixir/ReadmeQuick_elixir.ts +13 -10
  65. package/project/.sdk/src/cmp/elixir/ReadmeRef_elixir.ts +11 -11
  66. package/project/.sdk/src/cmp/elixir/ReadmeTopQuick_elixir.ts +8 -6
  67. package/project/.sdk/src/cmp/elixir/ReadmeTopTest_elixir.ts +14 -10
  68. package/project/.sdk/src/cmp/elixir/fragment/EntityCreateOp.fragment.ex +4 -3
  69. package/project/.sdk/src/cmp/elixir/fragment/EntityListOp.fragment.ex +4 -3
  70. package/project/.sdk/src/cmp/elixir/fragment/EntityLoadOp.fragment.ex +4 -3
  71. package/project/.sdk/src/cmp/elixir/fragment/EntityPatchOp.fragment.ex +4 -3
  72. package/project/.sdk/src/cmp/elixir/fragment/EntityRemoveOp.fragment.ex +4 -3
  73. package/project/.sdk/src/cmp/elixir/fragment/EntityUpdateOp.fragment.ex +4 -3
  74. package/project/.sdk/src/cmp/elixir/utility_elixir.ts +12 -0
  75. package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +8 -5
  76. package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +3 -1
  77. package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +20 -8
  78. package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +16 -13
  79. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +14 -13
  80. package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +14 -13
  81. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +10 -8
  82. package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +6 -2
  83. package/project/.sdk/src/cmp/go/utility_go.ts +1 -1
  84. package/project/.sdk/src/cmp/java/ReadmeEntity_java.ts +8 -19
  85. package/project/.sdk/src/cmp/java/ReadmeHowto_java.ts +19 -22
  86. package/project/.sdk/src/cmp/java/ReadmeModel_java.ts +10 -9
  87. package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +24 -28
  88. package/project/.sdk/src/cmp/java/ReadmeRef_java.ts +27 -35
  89. package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +22 -25
  90. package/project/.sdk/src/cmp/java/ReadmeTopTest_java.ts +18 -24
  91. package/project/.sdk/src/cmp/java/utility_java.ts +25 -0
  92. package/project/.sdk/src/cmp/js/EntityOperation_js.ts +3 -1
  93. package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +2 -8
  94. package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +11 -9
  95. package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +9 -9
  96. package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +7 -13
  97. package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +14 -15
  98. package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +16 -11
  99. package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +11 -7
  100. package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +1 -1
  101. package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +1 -1
  102. package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +1 -1
  103. package/project/.sdk/src/cmp/js/fragment/EntityPatchOp.fragment.js +1 -1
  104. package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +1 -1
  105. package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +1 -1
  106. package/project/.sdk/src/cmp/js/utility_js.ts +2 -1
  107. package/project/.sdk/src/cmp/kotlin/ReadmeEntity_kotlin.ts +3 -2
  108. package/project/.sdk/src/cmp/kotlin/ReadmeHowto_kotlin.ts +19 -9
  109. package/project/.sdk/src/cmp/kotlin/ReadmeModel_kotlin.ts +10 -9
  110. package/project/.sdk/src/cmp/kotlin/ReadmeQuick_kotlin.ts +14 -9
  111. package/project/.sdk/src/cmp/kotlin/ReadmeRef_kotlin.ts +18 -15
  112. package/project/.sdk/src/cmp/kotlin/ReadmeTopQuick_kotlin.ts +14 -8
  113. package/project/.sdk/src/cmp/kotlin/ReadmeTopTest_kotlin.ts +17 -10
  114. package/project/.sdk/src/cmp/kotlin/utility_kotlin.ts +21 -1
  115. package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +3 -2
  116. package/project/.sdk/src/cmp/lua/ReadmeExamplesTest_lua.ts +7 -4
  117. package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +9 -7
  118. package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +13 -13
  119. package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +20 -9
  120. package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +9 -8
  121. package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +15 -7
  122. package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +6 -5
  123. package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +1 -0
  124. package/project/.sdk/src/cmp/lua/fragment/EntityCreateOp.fragment.lua +1 -1
  125. package/project/.sdk/src/cmp/lua/fragment/EntityListOp.fragment.lua +1 -1
  126. package/project/.sdk/src/cmp/lua/fragment/EntityLoadOp.fragment.lua +1 -1
  127. package/project/.sdk/src/cmp/lua/fragment/EntityPatchOp.fragment.lua +1 -1
  128. package/project/.sdk/src/cmp/lua/fragment/EntityRemoveOp.fragment.lua +1 -1
  129. package/project/.sdk/src/cmp/lua/fragment/EntityUpdateOp.fragment.lua +1 -1
  130. package/project/.sdk/src/cmp/ocaml/ReadmeEntity_ocaml.ts +3 -2
  131. package/project/.sdk/src/cmp/ocaml/ReadmeHowto_ocaml.ts +3 -2
  132. package/project/.sdk/src/cmp/ocaml/ReadmeModel_ocaml.ts +1 -0
  133. package/project/.sdk/src/cmp/ocaml/ReadmeQuick_ocaml.ts +5 -3
  134. package/project/.sdk/src/cmp/ocaml/ReadmeRef_ocaml.ts +10 -8
  135. package/project/.sdk/src/cmp/ocaml/ReadmeTopQuick_ocaml.ts +4 -2
  136. package/project/.sdk/src/cmp/ocaml/ReadmeTopTest_ocaml.ts +3 -2
  137. package/project/.sdk/src/cmp/ocaml/utility_ocaml.ts +13 -1
  138. package/project/.sdk/src/cmp/perl/ReadmeEntity_perl.ts +4 -1
  139. package/project/.sdk/src/cmp/perl/ReadmeHowto_perl.ts +10 -5
  140. package/project/.sdk/src/cmp/perl/ReadmeModel_perl.ts +6 -6
  141. package/project/.sdk/src/cmp/perl/ReadmeQuick_perl.ts +8 -5
  142. package/project/.sdk/src/cmp/perl/ReadmeRef_perl.ts +21 -18
  143. package/project/.sdk/src/cmp/perl/ReadmeTopQuick_perl.ts +6 -3
  144. package/project/.sdk/src/cmp/perl/ReadmeTopTest_perl.ts +11 -8
  145. package/project/.sdk/src/cmp/perl/fragment/Entity.fragment.pm +1 -1
  146. package/project/.sdk/src/cmp/perl/fragment/EntityCreateOp.fragment.pm +2 -2
  147. package/project/.sdk/src/cmp/perl/fragment/EntityLoadOp.fragment.pm +2 -2
  148. package/project/.sdk/src/cmp/perl/fragment/EntityPatchOp.fragment.pm +2 -2
  149. package/project/.sdk/src/cmp/perl/fragment/EntityRemoveOp.fragment.pm +1 -1
  150. package/project/.sdk/src/cmp/perl/fragment/EntityUpdateOp.fragment.pm +2 -2
  151. package/project/.sdk/src/cmp/perl/utility_perl.ts +11 -1
  152. package/project/.sdk/src/cmp/php/EntityOperation_php.ts +3 -1
  153. package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +4 -3
  154. package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +12 -6
  155. package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +9 -9
  156. package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +3 -2
  157. package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +15 -14
  158. package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +5 -4
  159. package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +13 -7
  160. package/project/.sdk/src/cmp/php/fragment/EntityCreateOp.fragment.php +2 -2
  161. package/project/.sdk/src/cmp/php/fragment/EntityListOp.fragment.php +2 -2
  162. package/project/.sdk/src/cmp/php/fragment/EntityLoadOp.fragment.php +2 -2
  163. package/project/.sdk/src/cmp/php/fragment/EntityPatchOp.fragment.php +2 -2
  164. package/project/.sdk/src/cmp/php/fragment/EntityRemoveOp.fragment.php +2 -2
  165. package/project/.sdk/src/cmp/php/fragment/EntityUpdateOp.fragment.php +2 -2
  166. package/project/.sdk/src/cmp/py/EntityOperation_py.ts +3 -1
  167. package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +3 -8
  168. package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +9 -9
  169. package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +9 -9
  170. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +9 -14
  171. package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +23 -30
  172. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +7 -12
  173. package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +8 -7
  174. package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +1 -1
  175. package/project/.sdk/src/cmp/py/fragment/EntityListOp.fragment.py +1 -1
  176. package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +1 -1
  177. package/project/.sdk/src/cmp/py/fragment/EntityPatchOp.fragment.py +1 -1
  178. package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +1 -1
  179. package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +1 -1
  180. package/project/.sdk/src/cmp/rb/EntityOperation_rb.ts +3 -1
  181. package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +2 -1
  182. package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +4 -1
  183. package/project/.sdk/src/cmp/rb/ReadmeExamplesTest_rb.ts +3 -2
  184. package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +20 -10
  185. package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +10 -9
  186. package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +6 -3
  187. package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +14 -11
  188. package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +6 -3
  189. package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +14 -10
  190. package/project/.sdk/src/cmp/rb/fragment/EntityCreateOp.fragment.rb +2 -1
  191. package/project/.sdk/src/cmp/rb/fragment/EntityListOp.fragment.rb +1 -1
  192. package/project/.sdk/src/cmp/rb/fragment/EntityLoadOp.fragment.rb +2 -1
  193. package/project/.sdk/src/cmp/rb/fragment/EntityPatchOp.fragment.rb +2 -1
  194. package/project/.sdk/src/cmp/rb/fragment/EntityRemoveOp.fragment.rb +2 -1
  195. package/project/.sdk/src/cmp/rb/fragment/EntityUpdateOp.fragment.rb +2 -1
  196. package/project/.sdk/src/cmp/rb/utility_rb.ts +10 -0
  197. package/project/.sdk/src/cmp/rust/ReadmeEntity_rust.ts +3 -2
  198. package/project/.sdk/src/cmp/rust/ReadmeHowto_rust.ts +6 -2
  199. package/project/.sdk/src/cmp/rust/ReadmeQuick_rust.ts +3 -2
  200. package/project/.sdk/src/cmp/rust/ReadmeRef_rust.ts +5 -4
  201. package/project/.sdk/src/cmp/rust/ReadmeTopQuick_rust.ts +3 -2
  202. package/project/.sdk/src/cmp/rust/ReadmeTopTest_rust.ts +7 -6
  203. package/project/.sdk/src/cmp/rust/utility_rust.ts +13 -1
  204. package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +5 -2
  205. package/project/.sdk/src/cmp/scala/ReadmeEntity_scala.ts +8 -19
  206. package/project/.sdk/src/cmp/scala/ReadmeHowto_scala.ts +17 -22
  207. package/project/.sdk/src/cmp/scala/ReadmeModel_scala.ts +10 -9
  208. package/project/.sdk/src/cmp/scala/ReadmeQuick_scala.ts +18 -28
  209. package/project/.sdk/src/cmp/scala/ReadmeRef_scala.ts +25 -35
  210. package/project/.sdk/src/cmp/scala/ReadmeTopQuick_scala.ts +14 -25
  211. package/project/.sdk/src/cmp/scala/ReadmeTopTest_scala.ts +16 -24
  212. package/project/.sdk/src/cmp/scala/utility_scala.ts +25 -0
  213. package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +1 -1
  214. package/project/.sdk/src/cmp/swift/ReadmeEntity_swift.ts +3 -2
  215. package/project/.sdk/src/cmp/swift/ReadmeHowto_swift.ts +22 -8
  216. package/project/.sdk/src/cmp/swift/ReadmeModel_swift.ts +10 -9
  217. package/project/.sdk/src/cmp/swift/ReadmeQuick_swift.ts +16 -8
  218. package/project/.sdk/src/cmp/swift/ReadmeRef_swift.ts +20 -15
  219. package/project/.sdk/src/cmp/swift/ReadmeTopQuick_swift.ts +12 -7
  220. package/project/.sdk/src/cmp/swift/ReadmeTopTest_swift.ts +21 -10
  221. package/project/.sdk/src/cmp/swift/utility_swift.ts +14 -1
  222. package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +2 -8
  223. package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +11 -9
  224. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +9 -9
  225. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +7 -13
  226. package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +14 -23
  227. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +8 -14
  228. package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +29 -14
  229. package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +1 -1
  230. package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +1 -1
  231. package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +1 -1
  232. package/project/.sdk/src/cmp/ts/fragment/EntityPatchOp.fragment.ts +1 -1
  233. package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +1 -1
  234. package/project/.sdk/src/cmp/ts/utility_ts.ts +2 -1
  235. package/project/.sdk/src/cmp/zig/ReadmeEntity_zig.ts +3 -2
  236. package/project/.sdk/src/cmp/zig/ReadmeHowto_zig.ts +6 -2
  237. package/project/.sdk/src/cmp/zig/ReadmeQuick_zig.ts +4 -3
  238. package/project/.sdk/src/cmp/zig/ReadmeRef_zig.ts +5 -4
  239. package/project/.sdk/src/cmp/zig/ReadmeTopQuick_zig.ts +3 -2
  240. package/project/.sdk/src/cmp/zig/ReadmeTopTest_zig.ts +7 -6
  241. package/project/.sdk/src/cmp/zig/utility_zig.ts +13 -0
  242. package/project/.sdk/tm/c/utility/transform_request.c +1 -1
  243. package/project/.sdk/tm/clojure/src/sdk/core.clj +32 -26
  244. package/project/.sdk/tm/clojure/test/sdk/test/concurrency.clj +136 -0
  245. package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +3 -1
  246. package/project/.sdk/tm/cpp/core/types.hpp +36 -6
  247. package/project/.sdk/tm/cpp/test/concurrency_test.cpp +348 -0
  248. package/project/.sdk/tm/cpp/utility/pipeline.hpp +42 -25
  249. package/project/.sdk/tm/csharp/core/Context.cs +8 -5
  250. package/project/.sdk/tm/csharp/test/ConcurrencyTest.cs +238 -0
  251. package/project/.sdk/tm/csharp/utility/Clean.cs +12 -10
  252. package/project/.sdk/tm/csharp/utility/TransformRequest.cs +1 -1
  253. package/project/.sdk/tm/elixir/lib/projectname/context.ex +3 -0
  254. package/project/.sdk/tm/elixir/lib/projectname/entity_base.ex +2 -1
  255. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +31 -10
  256. package/project/.sdk/tm/elixir/test/concurrency_test.exs +176 -0
  257. package/project/.sdk/tm/go/core/context.go +5 -1
  258. package/project/.sdk/tm/go/test/concurrency_test.go +214 -0
  259. package/project/.sdk/tm/go/utility/transform_request.go +1 -1
  260. package/project/.sdk/tm/java/core/Context.java +6 -3
  261. package/project/.sdk/tm/java/test/ConcurrencyTest.java +221 -0
  262. package/project/.sdk/tm/java/utility/Clean.java +22 -17
  263. package/project/.sdk/tm/java/utility/TransformRequest.java +1 -1
  264. package/project/.sdk/tm/js/src/utility/TransformRequestUtility.js +1 -1
  265. package/project/.sdk/tm/kotlin/core/Context.kt +5 -3
  266. package/project/.sdk/tm/kotlin/test/ConcurrencyTest.kt +193 -0
  267. package/project/.sdk/tm/kotlin/utility/ResultTransform.kt +1 -1
  268. package/project/.sdk/tm/lua/utility/transform_request.lua +1 -1
  269. package/project/.sdk/tm/ocaml/Makefile +21 -8
  270. package/project/.sdk/tm/ocaml/sdk_runtime.ml +27 -12
  271. package/project/.sdk/tm/ocaml/sdk_types.ml +6 -2
  272. package/project/.sdk/tm/ocaml/test/concurrency_test.ml +148 -0
  273. package/project/.sdk/tm/perl/utility/transform_request.pm +1 -1
  274. package/project/.sdk/tm/php/feature/TestFeature.php +9 -9
  275. package/project/.sdk/tm/php/utility/TransformRequest.php +1 -1
  276. package/project/.sdk/tm/py/pkg/core/context.py +2 -2
  277. package/project/.sdk/tm/py/pkg/utility/clean.py +12 -8
  278. package/project/.sdk/tm/py/pkg/utility/transform_request.py +1 -1
  279. package/project/.sdk/tm/py/test/test_concurrency.py +177 -0
  280. package/project/.sdk/tm/rb/core/context.rb +8 -3
  281. package/project/.sdk/tm/rb/test/concurrency_test.rb +160 -0
  282. package/project/.sdk/tm/rb/utility/clean.rb +8 -7
  283. package/project/.sdk/tm/rb/utility/transform_request.rb +1 -1
  284. package/project/.sdk/tm/rust/utility/transform_request.rs +1 -1
  285. package/project/.sdk/tm/scala/Makefile +2 -0
  286. package/project/.sdk/tm/scala/core/Context.scala +6 -4
  287. package/project/.sdk/tm/scala/sdktest/ConcurrencyTest.scala +186 -0
  288. package/project/.sdk/tm/scala/utility/Transform.scala +1 -1
  289. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +2 -0
  290. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +35 -12
  291. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/ConcurrencyTest.swift +214 -0
  292. package/project/.sdk/tm/ts/src/utility/TransformRequestUtility.ts +1 -1
  293. package/project/.sdk/tm/zig/core/utility.zig +1 -1
  294. package/project/.sdk/tm/zig/feature/test.zig +10 -8
  295. package/project/sdkgen-package.json +1 -1
  296. package/src/cmp/ReadmeErrors.ts +5 -5
  297. package/src/cmp/ReadmeExplanation.ts +4 -5
  298. package/src/cmp/ReadmeQuick.ts +3 -1
  299. package/src/cmp/ReadmeTop.ts +10 -12
  300. package/src/helpers/definitionPlan.ts +8 -4
  301. package/src/helpers/naming.ts +12 -4
  302. package/src/helpers/opExample.ts +97 -25
  303. package/src/helpers/opShape.ts +25 -9
  304. package/src/sdkgen.ts +14 -3
  305. package/src/utility.ts +31 -4
@@ -0,0 +1,177 @@
1
+ # ProjectName SDK concurrency test
2
+ #
3
+ # Requests in flight at once on one client. Each resolves its operation
4
+ # through the cache the client's root context shares with every request, and
5
+ # registers and cleans secrets through the one registry the client holds.
6
+
7
+ import sys
8
+ import threading
9
+
10
+ from projectname_sdk import ProjectNameSDK
11
+
12
+
13
+ ROUNDS = 50
14
+ WIDTH = 8
15
+ OPS = 32
16
+ MASKED = "a [redacted] b [redacted] c"
17
+
18
+
19
+ # Runs body on WIDTH threads released together, and returns what they raised.
20
+ def _at_once(body):
21
+ start = threading.Barrier(WIDTH)
22
+ raised = []
23
+
24
+ def run(n):
25
+ try:
26
+ start.wait()
27
+ body(n)
28
+ except BaseException as err:
29
+ raised.append(err)
30
+
31
+ threads = [threading.Thread(target=run, args=(n,)) for n in range(WIDTH)]
32
+ for thread in threads:
33
+ thread.start()
34
+ for thread in threads:
35
+ thread.join()
36
+ return raised
37
+
38
+
39
+ # A live client whose transport answers at once.
40
+ def _live_client():
41
+ def fetch(url, fetchdef):
42
+ return {
43
+ "status": 200,
44
+ "statusText": "OK",
45
+ "headers": {},
46
+ "json": lambda: {"ok": True},
47
+ }, None
48
+
49
+ return ProjectNameSDK({
50
+ "base": "http://concurrency.test/api",
51
+ "allow": {"op": "direct"},
52
+ "system": {"fetch": fetch},
53
+ })
54
+
55
+
56
+ def _added_secret(rnd, n, k):
57
+ return "ADDED-SECRET-%d-%d-%d" % (rnd, n, k)
58
+
59
+
60
+ # Half the threads register their secrets; the rest run body(n) until those
61
+ # are done or body returns False. Returns what the threads raised.
62
+ def _while_registering(utility, root, rnd, body):
63
+ lock = threading.Lock()
64
+ registering = [WIDTH // 2]
65
+ registered = threading.Event()
66
+
67
+ def work(n):
68
+ if n < WIDTH // 2:
69
+ try:
70
+ for k in range(OPS):
71
+ utility.clean_add(root, _added_secret(rnd, n, k))
72
+ finally:
73
+ with lock:
74
+ registering[0] -= 1
75
+ if 0 == registering[0]:
76
+ registered.set()
77
+ return
78
+ while not registered.is_set():
79
+ if not body(n):
80
+ return
81
+
82
+ return _at_once(work)
83
+
84
+
85
+ # The first secret registered in the round that a clean leaves raw.
86
+ def _unmasked_secret(utility, root, rnd):
87
+ for n in range(WIDTH // 2):
88
+ for k in range(OPS):
89
+ added = _added_secret(rnd, n, k)
90
+ if "[redacted]" != utility.clean(root, added):
91
+ return added
92
+ return None
93
+
94
+
95
+ class TestConcurrency:
96
+
97
+ # Threads switch as often as the interpreter allows, so a race a few
98
+ # bytecodes wide has room to show.
99
+ def setup_method(self):
100
+ self._interval = sys.getswitchinterval()
101
+ sys.setswitchinterval(1e-6)
102
+
103
+ def teardown_method(self):
104
+ sys.setswitchinterval(self._interval)
105
+
106
+ def test_concurrent_resolutions_share_one_cached_operation(self):
107
+ for rnd in range(ROUNDS):
108
+ client = ProjectNameSDK.test(None, None)
109
+ utility = client.get_utility()
110
+ root = client.get_root_ctx()
111
+ ops = [[None] * OPS for _ in range(WIDTH)]
112
+
113
+ def resolve(n):
114
+ for k in range(OPS):
115
+ ops[n][k] = utility.make_context({"opname": "op%d" % k}, root).op
116
+
117
+ raised = _at_once(resolve)
118
+
119
+ assert [] == raised, "round %d raised: %r" % (rnd, raised)
120
+ for k in range(OPS):
121
+ cached = utility.make_context({"opname": "op%d" % k}, root).op
122
+ for n in range(WIDTH):
123
+ assert ops[n][k] is cached, \
124
+ "round %d: op%d resolved to more than one Operation" % (rnd, k)
125
+
126
+ # Secrets registered on some threads while others clean: every clean
127
+ # masks what was registered before it, the longer secret whole, and no
128
+ # registration is lost.
129
+ def test_concurrent_registration_keeps_every_secret_masked(self):
130
+ for rnd in range(ROUNDS // 4):
131
+ client = ProjectNameSDK.test(None, None)
132
+ utility = client.get_utility()
133
+ root = client.get_root_ctx()
134
+ inner = "INNER-SECRET-%d" % rnd
135
+ utility.clean_add(root, inner)
136
+ utility.clean_add(root, "OUTER-" + inner + "-TAIL")
137
+ text = "a " + inner + " b OUTER-" + inner + "-TAIL c"
138
+ assert MASKED == utility.clean(root, text)
139
+
140
+ wrong = []
141
+
142
+ def clean(n):
143
+ got = utility.clean(root, text)
144
+ if MASKED != got:
145
+ wrong.append(got)
146
+ return False
147
+ return True
148
+
149
+ raised = _while_registering(utility, root, rnd, clean)
150
+
151
+ assert [] == raised, "round %d raised: %r" % (rnd, raised)
152
+ assert [] == wrong, "round %d cleaned to: %r" % (rnd, wrong)
153
+ added = _unmasked_secret(utility, root, rnd)
154
+ assert added is None, "round %d: %s was registered but not masked" % (rnd, added)
155
+
156
+ # Requests on one client while secrets register on it: each request
157
+ # copies the client's options, the registry among them.
158
+ def test_concurrent_requests_survive_registration(self):
159
+ for rnd in range(ROUNDS // 4):
160
+ client = _live_client()
161
+ utility = client.get_utility()
162
+ root = client.get_root_ctx()
163
+ failed = []
164
+
165
+ def request(n):
166
+ res = client.direct({"path": "p%d" % n})
167
+ if True is not res.get("ok"):
168
+ failed.append(res)
169
+ return False
170
+ return True
171
+
172
+ raised = _while_registering(utility, root, rnd, request)
173
+
174
+ assert [] == raised, "round %d raised: %r" % (rnd, raised)
175
+ assert [] == failed, "round %d, a request failed: %r" % (rnd, failed)
176
+ added = _unmasked_secret(utility, root, rnd)
177
+ assert added is None, "round %d: %s was registered but not masked" % (rnd, added)
@@ -10,6 +10,10 @@ require_relative 'error'
10
10
  require_relative 'helpers'
11
11
 
12
12
  class ProjectNameContext
13
+ # Every context of a client shares its root's operation cache. CRuby's GVL
14
+ # makes each Hash operation atomic; a Ruby without one needs this lock.
15
+ OPMAP_LOCK = Mutex.new
16
+
13
17
  attr_accessor :id, :out, :client, :utility, :ctrl, :meta, :config,
14
18
  :entopts, :options, :entity, :shared, :opmap,
15
19
  :data, :reqdata, :match, :reqmatch, :point,
@@ -83,7 +87,8 @@ class ProjectNameContext
83
87
  # served to every subsequent entity's call.
84
88
  entname = @entity&.respond_to?(:get_name) ? @entity.get_name : "_"
85
89
  cache_key = "#{entname}:#{opname}"
86
- return @opmap[cache_key] if @opmap[cache_key]
90
+ cached = OPMAP_LOCK.synchronize { @opmap[cache_key] }
91
+ return cached if cached
87
92
  return ProjectNameOperation.new({}) if opname.empty?
88
93
 
89
94
  opcfg = VoxgigStruct.getpath(@config, "entity.#{entname}.op.#{opname}")
@@ -102,8 +107,8 @@ class ProjectNameContext
102
107
  "input" => input,
103
108
  "points" => points,
104
109
  })
105
- @opmap[cache_key] = op
106
- op
110
+ # Racing requests get the Operation stored first.
111
+ OPMAP_LOCK.synchronize { @opmap[cache_key] ||= op }
107
112
  end
108
113
 
109
114
  def make_error(code, msg)
@@ -0,0 +1,160 @@
1
+ # ProjectName SDK concurrency test
2
+ #
3
+ # Requests in flight at once on one client. Each resolves its operation
4
+ # through the cache the client's root context shares with every request, and
5
+ # registers and cleans secrets through the one registry the client holds.
6
+ #
7
+ # The interpreter switches threads about every 100ms, so each test runs long
8
+ # enough to be switched in the middle of a resolution or a registration.
9
+
10
+ require "minitest/autorun"
11
+ require_relative "../ProjectName_sdk"
12
+
13
+ class ConcurrencyTest < Minitest::Test
14
+ ROUNDS = 3
15
+ WIDTH = 8
16
+ OPS = 6000
17
+ SEEDED = 1000
18
+ ADDED = 400
19
+ MASKED = "a [redacted] b [redacted] c"
20
+
21
+ # Runs the block on WIDTH threads, released once every one is waiting, and
22
+ # returns what they raised.
23
+ def at_once
24
+ lock = Mutex.new
25
+ waiting = 0
26
+ ready = Queue.new
27
+ gate = Queue.new
28
+ seen = Queue.new
29
+ raised = Queue.new
30
+ threads = (0...WIDTH).map do |n|
31
+ Thread.new do
32
+ lock.synchronize { waiting += 1 }
33
+ ready << n
34
+ gate.pop
35
+ seen << lock.synchronize { waiting }
36
+ yield n
37
+ rescue Exception => e
38
+ raised << e
39
+ end
40
+ end
41
+ WIDTH.times { ready.pop }
42
+ WIDTH.times { gate << true }
43
+ threads.each(&:join)
44
+ released = Array.new(seen.size) { seen.pop }
45
+ assert_equal [WIDTH] * WIDTH, released, "threads released before #{WIDTH} were waiting"
46
+ Array.new(raised.size) { raised.pop }
47
+ end
48
+
49
+ def test_concurrent_resolutions_share_one_cached_operation
50
+ ROUNDS.times do |round|
51
+ client = ProjectNameSDK.test(nil, nil)
52
+ utility = client.get_utility
53
+ root = client.get_root_ctx
54
+ ops = Array.new(WIDTH) { Array.new(OPS) }
55
+
56
+ raised = at_once do |n|
57
+ OPS.times { |k| ops[n][k] = utility.make_context.call({ "opname" => "op#{k}" }, root).op }
58
+ end
59
+
60
+ assert_empty raised, "round #{round} raised: #{raised.inspect}"
61
+ OPS.times do |k|
62
+ cached = utility.make_context.call({ "opname" => "op#{k}" }, root).op
63
+ WIDTH.times do |n|
64
+ assert_same cached, ops[n][k], "round #{round}: op#{k} resolved to more than one Operation"
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # A live client whose transport answers at once.
71
+ def live_client
72
+ ProjectNameSDK.new({
73
+ "base" => "http://concurrency.test/api",
74
+ "allow" => { "op" => "direct" },
75
+ "system" => {
76
+ "fetch" => lambda { |_url, _fetchdef|
77
+ [{ "status" => 200, "statusText" => "OK", "headers" => {},
78
+ "json" => lambda { { "ok" => true } } }, nil]
79
+ },
80
+ },
81
+ })
82
+ end
83
+
84
+ # Half the threads register their secrets; the rest run body until those
85
+ # are done or it returns false. Returns what the threads raised.
86
+ def while_registering(utility, root, &body)
87
+ lock = Mutex.new
88
+ registering = WIDTH / 2
89
+ at_once do |n|
90
+ if n < WIDTH / 2
91
+ begin
92
+ ADDED.times { |k| utility.clean_add.call(root, "ADDED-SECRET-#{n}-#{k}") }
93
+ ensure
94
+ lock.synchronize { registering -= 1 }
95
+ end
96
+ else
97
+ while 0 < lock.synchronize { registering }
98
+ break unless body.call(n)
99
+ Thread.pass
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ def assert_every_secret_masked(utility, root)
106
+ (WIDTH / 2).times do |n|
107
+ ADDED.times do |k|
108
+ added = "ADDED-SECRET-#{n}-#{k}"
109
+ assert_equal "[redacted]", utility.clean.call(root, added), "#{added} was registered but not masked"
110
+ end
111
+ end
112
+ end
113
+
114
+ # Secrets registered on some threads while others clean: every clean masks
115
+ # what was registered before it, the longer secret whole, and no
116
+ # registration is lost.
117
+ def test_concurrent_registration_keeps_every_secret_masked
118
+ client = ProjectNameSDK.test(nil, nil)
119
+ utility = client.get_utility
120
+ root = client.get_root_ctx
121
+ SEEDED.times { |k| utility.clean_add.call(root, "SEEDED-SECRET-#{k}") }
122
+ utility.clean_add.call(root, "INNER-SECRET")
123
+ utility.clean_add.call(root, "OUTER-INNER-SECRET-TAIL")
124
+ text = "a INNER-SECRET b OUTER-INNER-SECRET-TAIL c"
125
+ assert_equal MASKED, utility.clean.call(root, text)
126
+
127
+ wrong = Queue.new
128
+ raised = while_registering(utility, root) do
129
+ got = utility.clean.call(root, text)
130
+ wrong << got if MASKED != got
131
+ MASKED == got
132
+ end
133
+
134
+ cleaned = Array.new(wrong.size) { wrong.pop }
135
+ assert_empty raised, "raised: #{raised.inspect}"
136
+ assert_empty cleaned, "cleaned to: #{cleaned.inspect}"
137
+ assert_every_secret_masked(utility, root)
138
+ end
139
+
140
+ # Requests on one client while secrets register on it: each request copies
141
+ # the client's options, the registry among them.
142
+ def test_concurrent_requests_survive_registration
143
+ client = live_client
144
+ utility = client.get_utility
145
+ root = client.get_root_ctx
146
+ SEEDED.times { |k| utility.clean_add.call(root, "SEEDED-SECRET-#{k}") }
147
+
148
+ failed = Queue.new
149
+ raised = while_registering(utility, root) do |n|
150
+ res = client.direct({ "path" => "p#{n}" })
151
+ failed << res unless true == res["ok"]
152
+ true == res["ok"]
153
+ end
154
+
155
+ requests = Array.new(failed.size) { failed.pop }
156
+ assert_empty raised, "raised: #{raised.inspect}"
157
+ assert_empty requests, "a request failed: #{requests.inspect}"
158
+ assert_every_secret_masked(utility, root)
159
+ end
160
+ end
@@ -83,19 +83,20 @@ module ProjectNameUtilities
83
83
  out
84
84
  end
85
85
 
86
+ # Requests on other threads clean while one registers, so a registration
87
+ # publishes a new list and never changes a published one.
88
+ REGISTERING = Mutex.new
89
+
86
90
  # Register a secret value. Idempotent; shorter than `min` is not a secret
87
91
  # the SDK can mask without blanking ordinary text.
88
92
  def self.add(ctx, value)
89
93
  cfg = config(ctx)
90
94
  return unless value.is_a?(String) && value.length >= cfg["min"]
91
- values = cfg["values"]
92
- changed = false
93
- forms(value).each do |form|
94
- next if form.length < cfg["min"] || values.include?(form)
95
- values << form
96
- changed = true
95
+ REGISTERING.synchronize do
96
+ values = cfg["values"]
97
+ added = forms(value).reject { |form| form.length < cfg["min"] || values.include?(form) }
98
+ cfg["values"] = (values + added).sort_by { |v| -v.length } unless added.empty?
97
99
  end
98
- values.sort_by! { |v| -v.length } if changed
99
100
  nil
100
101
  end
101
102
 
@@ -11,7 +11,7 @@ module ProjectNameUtilities
11
11
 
12
12
  # A header, cookie or query argument travels where PrepareHeaders or
13
13
  # PrepareQuery sends it, so the body is built from the request data without
14
- # it, unless the entity declares it as a field too.
14
+ # it, unless the point marks it as a field the body keeps.
15
15
  def self.routed_arg_names(ctx)
16
16
  (call_args(ctx, "header") + call_args(ctx, "cookie") + call_args(ctx, "query")).map(&:first)
17
17
  .reject { |name| field_arg?(ctx, name) }
@@ -14,7 +14,7 @@ fn strip_action(reqdata: Value) -> Value {
14
14
 
15
15
  // A header, cookie or query argument travels where prepare_headers_util or
16
16
  // prepare_query_util sends it, so the body is built from the request data
17
- // without it, unless the entity declares it as a field too.
17
+ // without it, unless the point marks it as a field the body keeps.
18
18
  fn routed_arg_names(ctx: &Rc<Context>) -> Vec<String> {
19
19
  let mut names = Vec::new();
20
20
  for kind in ["header", "cookie", "query"] {
@@ -18,12 +18,14 @@ build:
18
18
  # and so runs in every scala SDK, model-activated or not. SdkCleanTestMain is
19
19
  # the canary sweep: no credential leaves the SDK in any form. SdkServerTestMain
20
20
  # checks how a templated base URL takes its server variables.
21
+ # SdkConcurrencyTestMain races first requests on one client.
21
22
  test:
22
23
  scala-cli run . --main-class OmniSmoke
23
24
  scala-cli run . --main-class SdkTestMain
24
25
  scala-cli run . --main-class SdkEntityTestMain
25
26
  scala-cli run . --main-class SdkCleanTestMain
26
27
  scala-cli run . --main-class SdkServerTestMain
28
+ scala-cli run . --main-class SdkConcurrencyTestMain
27
29
  scala-cli run . --main-class SecretsTestMain
28
30
  scala-cli run . --main-class Runner -- ../.sdk/test/test.json
29
31
  scala-cli run . --main-class PrimaryCorpusMain -- ../.sdk/test/test.json
@@ -1,7 +1,7 @@
1
1
  package SCALAPACKAGE.core
2
2
 
3
3
  import java.util.{ArrayList, LinkedHashMap, List => JList, Map => JMap}
4
- import java.util.concurrent.ThreadLocalRandom
4
+ import java.util.concurrent.{ConcurrentHashMap, ThreadLocalRandom}
5
5
  import SCALAPACKAGE.utility.struct.Struct
6
6
 
7
7
  // Per-operation context threaded through the pipeline and feature hooks.
@@ -85,7 +85,7 @@ class Context(ctxmap: JMap[String, Object], basectx: Context) {
85
85
  // Opmap
86
86
  Helpers.getCtxProp(ctxmap, "opmap") match { case om: JMap[_, _] => opmap = om.asInstanceOf[JMap[String, Operation]]; case _ => }
87
87
  if (opmap == null && basectx != null) opmap = basectx.opmap
88
- if (opmap == null) opmap = new LinkedHashMap[String, Operation]()
88
+ if (opmap == null) opmap = new ConcurrentHashMap[String, Operation]()
89
89
 
90
90
  // Data
91
91
  data = Helpers.toMapAny(Helpers.getCtxProp(ctxmap, "data"))
@@ -153,8 +153,10 @@ class Context(ctxmap: JMap[String, Object], basectx: Context) {
153
153
  opdef.put("points", points)
154
154
 
155
155
  val newop = new Operation(opdef)
156
- opmap.put(cacheKey, newop)
157
- newop
156
+ // Requests on other threads share this cache; every one of them gets
157
+ // the Operation stored first.
158
+ val stored = opmap.putIfAbsent(cacheKey, newop)
159
+ if (stored != null) stored else newop
158
160
  }
159
161
 
160
162
  def makeError(code: String, msg: String): SdkError = new SdkError(code, msg, this)
@@ -0,0 +1,186 @@
1
+ // Requests in flight at once on one client. Each resolves its operation
2
+ // through the cache the client's root context shares with every request, and
3
+ // registers and cleans secrets through the one registry the client holds.
4
+
5
+ import java.util.concurrent.{ConcurrentLinkedQueue, CountDownLatch, CyclicBarrier}
6
+ import java.util.function.{BiFunction, Supplier}
7
+ import java.util.{Map => JMap}
8
+
9
+ import SCALAPACKAGE.core.{Context, Operation, ProjectNameSDK, Utility}
10
+
11
+ object SdkConcurrencyTestMain {
12
+
13
+ private val Rounds = 200
14
+ private val Width = 8
15
+ private val Ops = 32
16
+
17
+ private def om(kv: (String, Object)*): JMap[String, Object] = SdkTestSupport.om(kv*)
18
+
19
+ // A live client whose transport answers at once.
20
+ private def liveClient(): ProjectNameSDK = {
21
+ val json: Supplier[Object] = () => om("ok" -> SdkTestSupport.B(true))
22
+ val fetch: BiFunction[String, JMap[String, Object], Object] = (_, _) =>
23
+ om("status" -> SdkTestSupport.I(200), "statusText" -> "OK", "headers" -> om(), "json" -> json)
24
+ new ProjectNameSDK(om(
25
+ "base" -> "http://concurrency.test/api",
26
+ "allow" -> om("op" -> "direct"),
27
+ "system" -> om("fetch" -> fetch)))
28
+ }
29
+
30
+ // Runs body on Width threads released together, and returns what they threw.
31
+ private def atOnce(body: Int => Unit): List[Throwable] = {
32
+ val start = new CyclicBarrier(Width)
33
+ val thrown = new ConcurrentLinkedQueue[Throwable]()
34
+ val threads = (0 until Width).map { n =>
35
+ val runner: Runnable = () =>
36
+ try {
37
+ start.await()
38
+ body(n)
39
+ }
40
+ catch { case e: Throwable => thrown.add(e); () }
41
+ val thread = new Thread(runner)
42
+ thread.start()
43
+ thread
44
+ }
45
+ threads.foreach(_.join())
46
+ thrown.toArray(new Array[Throwable](0)).toList
47
+ }
48
+
49
+ private def addedSecret(round: Int, n: Int, k: Int): String =
50
+ "ADDED-SECRET-" + round + "-" + n + "-" + k
51
+
52
+ // Registers thread n's secrets, then counts the thread out of registering.
53
+ private def registerSecrets(utility: Utility, root: Context, round: Int, n: Int,
54
+ registering: CountDownLatch): Unit =
55
+ try {
56
+ for (k <- 0 until Ops) utility.cleanAdd(root, addedSecret(round, n, k))
57
+ }
58
+ finally registering.countDown()
59
+
60
+ // The first secret registered in the round that a clean leaves raw.
61
+ private def unmasked(utility: Utility, root: Context, round: Int): Option[String] =
62
+ (for (n <- 0 until Width / 2; k <- 0 until Ops) yield addedSecret(round, n, k))
63
+ .find(added => "[redacted]" != utility.clean(root, added))
64
+
65
+ // The first failure across the rounds, or null.
66
+ private def firstFailure(round: Int => String): String = {
67
+ var failure: String = null
68
+ var n = 0
69
+ while (failure == null && n < Rounds) {
70
+ failure = round(n)
71
+ n += 1
72
+ }
73
+ failure
74
+ }
75
+
76
+ def main(args: Array[String]): Unit = {
77
+ val rep = new SdkTestReport()
78
+
79
+ rep.scope("concurrency.requests") {
80
+ val failure = firstFailure { round =>
81
+ // A fresh client each round, so every request in it is a first request.
82
+ val client = liveClient()
83
+ val results = new Array[JMap[String, Object]](Width)
84
+ val thrown = atOnce(n => results(n) = client.direct(om("path" -> ("p" + n))))
85
+ val failed = (0 until Width).find(n =>
86
+ results(n) == null || java.lang.Boolean.TRUE != results(n).get("ok"))
87
+ if (thrown.nonEmpty) "round " + round + " threw: " + thrown
88
+ else failed.map(n => "round " + round + ", request " + n + " failed: " + results(n)).orNull
89
+ }
90
+ rep.check("concurrency.requests", failure == null, failure)
91
+ }
92
+
93
+ rep.scope("concurrency.operations") {
94
+ val failure = firstFailure { round =>
95
+ val client = liveClient()
96
+ val utility = client.getUtility()
97
+ val root = client.getRootCtx()
98
+ val got = Array.ofDim[Operation](Width, Ops)
99
+ val thrown = atOnce { n =>
100
+ for (k <- 0 until Ops) {
101
+ got(n)(k) = utility.makeContext(om("opname" -> ("op" + k)), root).op
102
+ }
103
+ }
104
+ val split = (0 until Ops).find { k =>
105
+ val cached = utility.makeContext(om("opname" -> ("op" + k)), root).op
106
+ (0 until Width).exists(n => !(got(n)(k) eq cached))
107
+ }
108
+ if (thrown.nonEmpty) "round " + round + " threw: " + thrown
109
+ else split.map(k => "round " + round + ": op" + k + " resolved to more than one Operation").orNull
110
+ }
111
+ rep.check("concurrency.operations", failure == null, failure)
112
+ }
113
+
114
+ // Secrets registered on some threads while others clean: every clean
115
+ // masks what was registered before it, the longer secret whole, and no
116
+ // registration is lost.
117
+ rep.scope("concurrency.registry") {
118
+ val masked = "a [redacted] b [redacted] c"
119
+ val failure = firstFailure { round =>
120
+ if (round >= Rounds / 4) null
121
+ else {
122
+ val client = liveClient()
123
+ val utility = client.getUtility()
124
+ val root = client.getRootCtx()
125
+ val inner = "INNER-SECRET-" + round
126
+ utility.cleanAdd(root, inner)
127
+ utility.cleanAdd(root, "OUTER-" + inner + "-TAIL")
128
+ val text = "a " + inner + " b OUTER-" + inner + "-TAIL c"
129
+ val before = utility.clean(root, text)
130
+ val registering = new CountDownLatch(Width / 2)
131
+ val wrong = new ConcurrentLinkedQueue[Object]()
132
+ val thrown = atOnce { n =>
133
+ if (n < Width / 2) registerSecrets(utility, root, round, n, registering)
134
+ else {
135
+ while (0L < registering.getCount) {
136
+ val got = utility.clean(root, text)
137
+ if (masked != got) wrong.add(got)
138
+ }
139
+ }
140
+ }
141
+ if (masked != before) "round " + round + " cleaned to: " + before
142
+ else if (thrown.nonEmpty) "round " + round + " threw: " + thrown
143
+ else if (!wrong.isEmpty) "round " + round + " cleaned to: " + wrong
144
+ else unmasked(utility, root, round)
145
+ .map(added => "round " + round + ": " + added + " was registered but not masked").orNull
146
+ }
147
+ }
148
+ rep.check("concurrency.registry", failure == null, failure)
149
+ }
150
+
151
+ // Requests on one client while secrets register on it: each request
152
+ // copies the client's options, the registry among them.
153
+ rep.scope("concurrency.registry.requests") {
154
+ val failure = firstFailure { round =>
155
+ if (round >= Rounds / 4) null
156
+ else {
157
+ val client = liveClient()
158
+ val utility = client.getUtility()
159
+ val root = client.getRootCtx()
160
+ val registering = new CountDownLatch(Width / 2)
161
+ val failed = new ConcurrentLinkedQueue[Object]()
162
+ val thrown = atOnce { n =>
163
+ if (n < Width / 2) registerSecrets(utility, root, round, n, registering)
164
+ else {
165
+ var going = true
166
+ while (going && 0L < registering.getCount) {
167
+ val res = client.direct(om("path" -> ("p" + n)))
168
+ if (java.lang.Boolean.TRUE != res.get("ok")) {
169
+ failed.add(res)
170
+ going = false
171
+ }
172
+ }
173
+ }
174
+ }
175
+ if (thrown.nonEmpty) "round " + round + " threw: " + thrown
176
+ else if (!failed.isEmpty) "round " + round + ", a request failed: " + failed
177
+ else unmasked(utility, root, round)
178
+ .map(added => "round " + round + ": " + added + " was registered but not masked").orNull
179
+ }
180
+ }
181
+ rep.check("concurrency.registry.requests", failure == null, failure)
182
+ }
183
+
184
+ rep.finish("CONCURRENCY")
185
+ }
186
+ }
@@ -28,7 +28,7 @@ object TransformRequest {
28
28
 
29
29
  // A header, cookie or query argument travels where PrepareHeaders or
30
30
  // PrepareQuery sends it, so the body is built from the request data without
31
- // it, unless the entity declares it as a field too.
31
+ // it, unless the point marks it as a field the body keeps.
32
32
  private def routedArgNames(ctx: Context): Seq[String] =
33
33
  (Param.callArgs(ctx, "header") ++ Param.callArgs(ctx, "cookie") ++ Param.callArgs(ctx, "query")).map(_._1)
34
34
  .filterNot(name => fieldArg(ctx, name))
@@ -150,6 +150,8 @@ public final class Context {
150
150
  opm.entries["input"] = .string(input)
151
151
  opm.entries["points"] = .list(points)
152
152
 
153
+ // Each context holds its own copy of the cache, so a request racing
154
+ // another builds its own Operation, a read-only descriptor.
153
155
  let op = Operation(opm)
154
156
  opmap[cacheKey] = op
155
157
  return op