@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,238 @@
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
+ using System.Collections.Concurrent;
6
+
7
+ using Xunit;
8
+
9
+ using ProjectNameSdk;
10
+
11
+ namespace ProjectNameSdk.Test;
12
+
13
+ public class ConcurrencyTest
14
+ {
15
+ private const int Rounds = 200;
16
+ private const int Width = 8;
17
+ private const int Ops = 32;
18
+
19
+ private static Dictionary<string, object?> Answer(string url, Dictionary<string, object?> fetchdef)
20
+ {
21
+ return new Dictionary<string, object?>
22
+ {
23
+ ["status"] = 200,
24
+ ["statusText"] = "OK",
25
+ ["headers"] = new Dictionary<string, object?>(),
26
+ ["json"] = (Func<object?>)(() => new Dictionary<string, object?> { ["ok"] = true }),
27
+ };
28
+ }
29
+
30
+ // A live client whose transport answers at once.
31
+ private static ProjectNameSDK LiveClient()
32
+ {
33
+ return new ProjectNameSDK(new Dictionary<string, object?>
34
+ {
35
+ ["base"] = "http://concurrency.test/api",
36
+ ["allow"] = new Dictionary<string, object?> { ["op"] = "direct" },
37
+ ["system"] = new Dictionary<string, object?>
38
+ {
39
+ ["fetch"] = (Func<string, Dictionary<string, object?>,
40
+ Dictionary<string, object?>>)Answer,
41
+ },
42
+ });
43
+ }
44
+
45
+ // Runs body on Width threads released together, and returns what they threw.
46
+ private static List<Exception> AtOnce(Action<int> body)
47
+ {
48
+ var start = new Barrier(Width);
49
+ var thrown = new ConcurrentQueue<Exception>();
50
+ var threads = new List<Thread>();
51
+ for (var i = 0; i < Width; i++)
52
+ {
53
+ var n = i;
54
+ var thread = new Thread(() =>
55
+ {
56
+ try
57
+ {
58
+ start.SignalAndWait();
59
+ body(n);
60
+ }
61
+ catch (Exception e)
62
+ {
63
+ thrown.Enqueue(e);
64
+ }
65
+ });
66
+ thread.Start();
67
+ threads.Add(thread);
68
+ }
69
+ threads.ForEach(t => t.Join());
70
+ return thrown.ToList();
71
+ }
72
+
73
+ [Fact]
74
+ public void ConcurrentFirstRequestsSucceed()
75
+ {
76
+ for (var round = 0; round < Rounds; round++)
77
+ {
78
+ // A fresh client each round, so every request in it is a first request.
79
+ var client = LiveClient();
80
+ var results = new Dictionary<string, object?>?[Width];
81
+ var thrown = AtOnce(n =>
82
+ results[n] = client.Direct(new Dictionary<string, object?> { ["path"] = "p" + n }));
83
+
84
+ Assert.True(0 == thrown.Count, "round " + round + " threw:\n" + string.Join("\n", thrown));
85
+ for (var n = 0; n < Width; n++)
86
+ {
87
+ var res = results[n];
88
+ Assert.True(Equals(true, res?["ok"]), "round " + round + ", request " + n +
89
+ " failed: " + (res?.GetValueOrDefault("err") ?? "no result"));
90
+ }
91
+ }
92
+ }
93
+
94
+ [Fact]
95
+ public void ConcurrentResolutionsShareOneCachedOperation()
96
+ {
97
+ for (var round = 0; round < Rounds; round++)
98
+ {
99
+ var client = LiveClient();
100
+ var utility = client.GetUtility();
101
+ var root = client.GetRootCtx();
102
+ var ops = new Operation?[Width, Ops];
103
+ var thrown = AtOnce(n =>
104
+ {
105
+ for (var k = 0; k < Ops; k++)
106
+ {
107
+ ops[n, k] = utility.MakeContext(
108
+ new Dictionary<string, object?> { ["opname"] = "op" + k }, root).Op;
109
+ }
110
+ });
111
+
112
+ Assert.True(0 == thrown.Count, "round " + round + " threw:\n" + string.Join("\n", thrown));
113
+ for (var k = 0; k < Ops; k++)
114
+ {
115
+ var cached = utility.MakeContext(
116
+ new Dictionary<string, object?> { ["opname"] = "op" + k }, root).Op;
117
+ for (var n = 0; n < Width; n++)
118
+ {
119
+ Assert.True(ReferenceEquals(cached, ops[n, k]),
120
+ "round " + round + ": op" + k + " resolved to more than one Operation");
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+ private static string AddedSecret(int round, int n, int k)
127
+ {
128
+ return "ADDED-SECRET-" + round + "-" + n + "-" + k;
129
+ }
130
+
131
+ // Registers thread n's secrets, then counts the thread out of registering.
132
+ private static void RegisterSecrets(Utility utility, Context root, int round, int n,
133
+ CountdownEvent registering)
134
+ {
135
+ try
136
+ {
137
+ for (var k = 0; k < Ops; k++)
138
+ {
139
+ utility.CleanAdd(root, AddedSecret(round, n, k));
140
+ }
141
+ }
142
+ finally
143
+ {
144
+ registering.Signal();
145
+ }
146
+ }
147
+
148
+ private static void AssertEverySecretMasked(Utility utility, Context root, int round)
149
+ {
150
+ for (var n = 0; n < Width / 2; n++)
151
+ {
152
+ for (var k = 0; k < Ops; k++)
153
+ {
154
+ var added = AddedSecret(round, n, k);
155
+ Assert.True("[redacted]" == utility.Clean(root, added) as string,
156
+ "round " + round + ": " + added + " was registered but not masked");
157
+ }
158
+ }
159
+ }
160
+
161
+ // Secrets registered on some threads while others clean: every clean
162
+ // masks what was registered before it, the longer secret whole, and no
163
+ // registration is lost.
164
+ [Fact]
165
+ public void ConcurrentRegistrationKeepsEverySecretMasked()
166
+ {
167
+ for (var round = 0; round < Rounds / 4; round++)
168
+ {
169
+ var client = LiveClient();
170
+ var utility = client.GetUtility();
171
+ var root = client.GetRootCtx();
172
+ var inner = "INNER-SECRET-" + round;
173
+ utility.CleanAdd(root, inner);
174
+ utility.CleanAdd(root, "OUTER-" + inner + "-TAIL");
175
+ var text = "a " + inner + " b OUTER-" + inner + "-TAIL c";
176
+ Assert.Equal("a [redacted] b [redacted] c", utility.Clean(root, text));
177
+
178
+ var registering = new CountdownEvent(Width / 2);
179
+ var wrong = new ConcurrentQueue<string>();
180
+ var thrown = AtOnce(n =>
181
+ {
182
+ if (n < Width / 2)
183
+ {
184
+ RegisterSecrets(utility, root, round, n, registering);
185
+ return;
186
+ }
187
+ while (!registering.IsSet)
188
+ {
189
+ var got = utility.Clean(root, text) as string;
190
+ if ("a [redacted] b [redacted] c" != got)
191
+ {
192
+ wrong.Enqueue(got ?? "null");
193
+ }
194
+ }
195
+ });
196
+
197
+ Assert.True(0 == thrown.Count, "round " + round + " threw:\n" + string.Join("\n", thrown));
198
+ Assert.True(wrong.IsEmpty, "round " + round + " cleaned to: " + string.Join(" | ", wrong));
199
+ AssertEverySecretMasked(utility, root, round);
200
+ }
201
+ }
202
+
203
+ // Requests on one client while secrets register on it: each request
204
+ // copies the client's options, the registry among them.
205
+ [Fact]
206
+ public void ConcurrentRequestsSurviveRegistration()
207
+ {
208
+ for (var round = 0; round < Rounds / 4; round++)
209
+ {
210
+ var client = LiveClient();
211
+ var utility = client.GetUtility();
212
+ var root = client.GetRootCtx();
213
+ var registering = new CountdownEvent(Width / 2);
214
+ var failed = new ConcurrentQueue<string>();
215
+ var thrown = AtOnce(n =>
216
+ {
217
+ if (n < Width / 2)
218
+ {
219
+ RegisterSecrets(utility, root, round, n, registering);
220
+ return;
221
+ }
222
+ while (!registering.IsSet)
223
+ {
224
+ var res = client.Direct(new Dictionary<string, object?> { ["path"] = "p" + n });
225
+ if (!Equals(true, res.GetValueOrDefault("ok")))
226
+ {
227
+ failed.Enqueue(Convert.ToString(res.GetValueOrDefault("err")) ?? "no error");
228
+ return;
229
+ }
230
+ }
231
+ });
232
+
233
+ Assert.True(0 == thrown.Count, "round " + round + " threw:\n" + string.Join("\n", thrown));
234
+ Assert.True(failed.IsEmpty, "round " + round + ", a request failed: " + string.Join(" | ", failed));
235
+ AssertEverySecretMasked(utility, root, round);
236
+ }
237
+ }
238
+ }
@@ -21,10 +21,13 @@ public sealed class CleanConfig
21
21
  {
22
22
  public bool Active = true;
23
23
  public List<string> Keys = new();
24
+ // Requests on other threads clean while one registers, so a registration
25
+ // publishes a new list and never changes a published one.
24
26
  public List<string> Values = new();
25
27
  public string Mask = "[redacted]";
26
28
  public int Hint;
27
29
  public int Min = 4;
30
+ internal readonly object Registering = new();
28
31
  }
29
32
 
30
33
  public static partial class SdkUtility
@@ -152,18 +155,17 @@ public static partial class SdkUtility
152
155
  {
153
156
  return;
154
157
  }
155
- var changed = false;
156
- foreach (var form in CleanForms(s))
158
+ lock (cfg.Registering)
157
159
  {
158
- if (form.Length >= cfg.Min && !cfg.Values.Contains(form))
160
+ var added = CleanForms(s).Where(f => f.Length >= cfg.Min && !cfg.Values.Contains(f)).ToList();
161
+ if (0 == added.Count)
159
162
  {
160
- cfg.Values.Add(form);
161
- changed = true;
163
+ return;
162
164
  }
163
- }
164
- if (changed)
165
- {
166
- cfg.Values.Sort((a, b) => b.Length.CompareTo(a.Length));
165
+ var values = new List<string>(cfg.Values);
166
+ values.AddRange(added);
167
+ values.Sort((a, b) => b.Length.CompareTo(a.Length));
168
+ Volatile.Write(ref cfg.Values, values);
167
169
  }
168
170
  }
169
171
 
@@ -179,7 +181,7 @@ public static partial class SdkUtility
179
181
  private static string CleanString(CleanConfig cfg, string text)
180
182
  {
181
183
  var out_ = text;
182
- foreach (var value in cfg.Values)
184
+ foreach (var value in Volatile.Read(ref cfg.Values))
183
185
  {
184
186
  if (out_.Contains(value))
185
187
  {
@@ -49,7 +49,7 @@ public static partial class SdkUtility
49
49
 
50
50
  // A header, cookie or query argument travels where PrepareHeadersUtil or
51
51
  // PrepareQueryUtil sends it, so the body is built from the request data
52
- // without it, unless the entity declares it as a field too.
52
+ // without it, unless the point marks it as a field the body keeps.
53
53
  private static List<string> RoutedArgNames(Context ctx)
54
54
  {
55
55
  return CallArgs(ctx, "header").Concat(CallArgs(ctx, "cookie")).Concat(CallArgs(ctx, "query"))
@@ -150,6 +150,9 @@ defmodule ProjectName.Context do
150
150
  S.jm(["entity", entname, "name", opname, "input", inpt, "points", points])
151
151
  )
152
152
 
153
+ # Racing processes may each build and store an Operation, and an entry
154
+ # lost to a concurrent write is rebuilt; an Operation is a read-only
155
+ # descriptor, so a duplicate is harmless.
153
156
  S.setprop(opmap, cache_key, op)
154
157
  op
155
158
  end
@@ -108,7 +108,8 @@ defmodule ProjectName.EntityBase do
108
108
  # materialises the whole result). When the streaming feature is active the
109
109
  # result carries a `stream` closure and this yields from it (honouring
110
110
  # chunkSize); otherwise it falls back to the materialised items, so stream
111
- # always yields. Records are unwrapped to bare struct maps (matching list).
111
+ # always yields. An entity is yielded as its record, the struct map its
112
+ # data_get/1 returns.
112
113
  # `callopts` parameterises the call:
113
114
  # - ctrl: per-call pipeline control (threaded onto the op ctx);
114
115
  # - body: an enumerable/list payload for outbound (upload) streaming,
@@ -204,24 +204,45 @@ defmodule ProjectName.Utility do
204
204
  def clean_add_impl(ctx, value) do
205
205
  cfg = clean_config(ctx)
206
206
  minlen = numof(S.getprop(cfg, "min"), 4)
207
- values = S.getprop(cfg, "values")
208
207
 
209
- if is_binary(value) and String.length(value) >= minlen and S.islist(values) do
210
- have = list_values(values)
208
+ if is_binary(value) and String.length(value) >= minlen and S.islist(S.getprop(cfg, "values")) do
209
+ forms = Enum.filter(clean_forms(value), &(String.length(&1) >= minlen))
211
210
 
212
- add =
213
- clean_forms(value)
214
- |> Enum.filter(fn f -> String.length(f) >= minlen and f not in have end)
211
+ # A registration reads the registry and writes a new one, so a request
212
+ # in another process registering at once would drop it; they take turns.
213
+ have = list_values(S.getprop(cfg, "values"))
215
214
 
216
- # Longest first, so a value is never masked by a substring of itself.
217
- if add != [] do
218
- S.setprop(cfg, "values", S.jt(Enum.sort_by(have ++ add, &(-String.length(&1)))))
215
+ if Enum.any?(forms, &(&1 not in have)) do
216
+ registering(fn -> publish(cfg, forms) end)
219
217
  end
220
218
  end
221
219
 
222
220
  nil
223
221
  end
224
222
 
223
+ # Retried after a millisecond rather than after global's back-off sleep,
224
+ # which grows to seconds; the lock is released if its holder dies.
225
+ defp registering(fun) do
226
+ case :global.trans({{__MODULE__, :clean_registry}, self()}, fun, [node()], 0) do
227
+ :aborted ->
228
+ Process.sleep(1)
229
+ registering(fun)
230
+
231
+ done ->
232
+ done
233
+ end
234
+ end
235
+
236
+ defp publish(cfg, forms) do
237
+ have = list_values(S.getprop(cfg, "values"))
238
+ add = Enum.reject(forms, &(&1 in have))
239
+
240
+ # Longest first, so a value is never masked by a substring of itself.
241
+ if add != [] do
242
+ S.setprop(cfg, "values", S.jt(Enum.sort_by(have ++ add, &(-String.length(&1)))))
243
+ end
244
+ end
245
+
225
246
  defp mask_value(cfg, value) do
226
247
  hint = numof(S.getprop(cfg, "hint"), 0)
227
248
  mask = strof(S.getprop(cfg, "mask"))
@@ -2280,7 +2301,7 @@ defmodule ProjectName.Utility do
2280
2301
 
2281
2302
  # A header, cookie or query argument travels where prepare_headers_impl or
2282
2303
  # prepare_query_impl sends it, so the body is built from the request data
2283
- # without it, unless the entity declares it as a field too.
2304
+ # without it, unless the point marks it as a field the body keeps.
2284
2305
  defp routed_arg_names(ctx) do
2285
2306
  (call_args(ctx, "header") ++ call_args(ctx, "cookie") ++ call_args(ctx, "query"))
2286
2307
  |> Enum.map(&elem(&1, 0))
@@ -0,0 +1,176 @@
1
+ # ProjectName SDK concurrency test
2
+ #
3
+ # Requests in flight at once on one client, each in its own process, register
4
+ # and clean secrets through the one registry the client holds.
5
+
6
+ defmodule ProjectName.ConcurrencyTest do
7
+ use ExUnit.Case
8
+
9
+ alias ProjectName.Utility
10
+ alias Voxgig.Struct, as: S
11
+
12
+ @rounds 3
13
+ @width 8
14
+ @added 16
15
+ @masked "a [redacted] b [redacted] c"
16
+
17
+ # Runs body in @width processes, released once every one is waiting, and
18
+ # returns what they raised.
19
+ defp at_once(body) do
20
+ parent = self()
21
+ waiting = :counters.new(1, [:atomics])
22
+
23
+ tasks =
24
+ for n <- 0..(@width - 1) do
25
+ Task.async(fn ->
26
+ :counters.add(waiting, 1, 1)
27
+ send(parent, {:ready, self()})
28
+
29
+ receive do
30
+ :go -> :ok
31
+ end
32
+
33
+ seen = :counters.get(waiting, 1)
34
+
35
+ try do
36
+ body.(n)
37
+ {seen, nil}
38
+ rescue
39
+ err -> {seen, err}
40
+ end
41
+ end)
42
+ end
43
+
44
+ ready =
45
+ for _ <- tasks do
46
+ receive do
47
+ {:ready, pid} -> pid
48
+ end
49
+ end
50
+
51
+ Enum.each(ready, &send(&1, :go))
52
+ outcomes = Task.await_many(tasks, :infinity)
53
+
54
+ for {seen, _} <- outcomes do
55
+ assert @width == seen, "a process was released with #{seen} of #{@width} waiting"
56
+ end
57
+
58
+ for {_, err} <- outcomes, nil != err, do: err
59
+ end
60
+
61
+ # A live client whose transport answers at once.
62
+ defp live_client do
63
+ fetch = fn _url, _fetchdef ->
64
+ {S.jm([
65
+ "status", 200,
66
+ "statusText", "OK",
67
+ "headers", S.jm([]),
68
+ "json", fn -> S.jm(["ok", true]) end
69
+ ]), nil}
70
+ end
71
+
72
+ ProjectName.new(
73
+ S.jm([
74
+ "base", "http://concurrency.test/api",
75
+ "allow", S.jm(["op", "direct"]),
76
+ "system", S.jm(["fetch", fetch])
77
+ ])
78
+ )
79
+ end
80
+
81
+ defp register_secrets(root, round, n, registering) do
82
+ try do
83
+ for k <- 1..@added, do: Utility.clean_add(root, "ADDED-SECRET-#{round}-#{n}-#{k}")
84
+ after
85
+ :counters.sub(registering, 1, 1)
86
+ end
87
+ end
88
+
89
+ defp assert_every_secret_masked(root, round) do
90
+ for n <- 0..(div(@width, 2) - 1), k <- 1..@added do
91
+ added = "ADDED-SECRET-#{round}-#{n}-#{k}"
92
+
93
+ assert "[redacted]" == Utility.clean(root, added),
94
+ "round #{round}: #{added} was registered but not masked"
95
+ end
96
+ end
97
+
98
+ # A cleaner pauses between passes, so the registrars contend with each other.
99
+ defp clean_while(root, text, registering) do
100
+ if 0 < :counters.get(registering, 1) do
101
+ got = Utility.clean(root, text)
102
+
103
+ if @masked != got do
104
+ raise "cleaned to: " <> inspect(got)
105
+ end
106
+
107
+ Process.sleep(1)
108
+ clean_while(root, text, registering)
109
+ end
110
+ end
111
+
112
+ # Secrets registered in some processes while others clean: every clean masks
113
+ # what was registered before it, the longer secret whole, and no
114
+ # registration is lost.
115
+ test "concurrent registration keeps every secret masked" do
116
+ for round <- 1..@rounds do
117
+ client = ProjectName.test()
118
+ root = ProjectName.get_root_ctx(client)
119
+ inner = "INNER-SECRET-#{round}"
120
+ Utility.clean_add(root, inner)
121
+ Utility.clean_add(root, "OUTER-#{inner}-TAIL")
122
+ text = "a #{inner} b OUTER-#{inner}-TAIL c"
123
+ assert @masked == Utility.clean(root, text)
124
+
125
+ registering = :counters.new(1, [:atomics])
126
+ :counters.put(registering, 1, div(@width, 2))
127
+
128
+ raised =
129
+ at_once(fn n ->
130
+ if n < div(@width, 2) do
131
+ register_secrets(root, round, n, registering)
132
+ else
133
+ clean_while(root, text, registering)
134
+ end
135
+ end)
136
+
137
+ assert [] == raised, "round #{round} raised: #{inspect(raised)}"
138
+ assert_every_secret_masked(root, round)
139
+ end
140
+ end
141
+
142
+ defp request_while(client, n, registering) do
143
+ if 0 < :counters.get(registering, 1) do
144
+ res = ProjectName.direct(client, S.jm(["path", "p#{n}"]))
145
+
146
+ if true != S.getprop(res, "ok") do
147
+ raise "a request failed: " <> inspect(S.getprop(res, "err"))
148
+ end
149
+
150
+ request_while(client, n, registering)
151
+ end
152
+ end
153
+
154
+ # Requests on one client while secrets register on it: each request copies
155
+ # the client's options, the registry among them.
156
+ test "concurrent requests survive registration" do
157
+ for round <- 1..@rounds do
158
+ client = live_client()
159
+ root = ProjectName.get_root_ctx(client)
160
+ registering = :counters.new(1, [:atomics])
161
+ :counters.put(registering, 1, div(@width, 2))
162
+
163
+ raised =
164
+ at_once(fn n ->
165
+ if n < div(@width, 2) do
166
+ register_secrets(root, round, n, registering)
167
+ else
168
+ request_while(client, n, registering)
169
+ end
170
+ end)
171
+
172
+ assert [] == raised, "round #{round} raised: #{inspect(raised)}"
173
+ assert_every_secret_masked(root, round)
174
+ end
175
+ end
176
+ end
@@ -280,9 +280,13 @@ func (ctx *Context) resolveOp(opname string) *Operation {
280
280
  "points": targets,
281
281
  })
282
282
 
283
+ // Every request racing to build this Operation gets the one stored first.
283
284
  ctx.Opmu.Lock()
285
+ defer ctx.Opmu.Unlock()
286
+ if stored, ok := ctx.Opmap[cacheKey]; ok && stored != nil {
287
+ return stored
288
+ }
284
289
  ctx.Opmap[cacheKey] = op
285
- ctx.Opmu.Unlock()
286
290
  return op
287
291
  }
288
292