@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,348 @@
1
+ // ProjectName SDK - requests in flight at once on one client. Each resolves
2
+ // its operation through the cache the client's root context shares with
3
+ // every request, and registers and cleans secrets through the one registry
4
+ // the client holds.
5
+
6
+ #include "testlib.hpp"
7
+
8
+ #include <algorithm>
9
+ #include <atomic>
10
+ #include <cstdlib>
11
+ #include <functional>
12
+ #include <mutex>
13
+ #include <thread>
14
+ #include <vector>
15
+
16
+ using namespace sdk;
17
+
18
+ // PROJECTENV_TEST_CONCURRENCY_ROUNDS sets the rounds: fewer where each costs
19
+ // more, as under ThreadSanitizer.
20
+ static int envRounds() {
21
+ const char* set = std::getenv("PROJECTENV_TEST_CONCURRENCY_ROUNDS");
22
+ int rounds = nullptr == set ? 0 : std::atoi(set);
23
+ return 0 < rounds ? rounds : 200;
24
+ }
25
+
26
+ static const int ROUNDS = envRounds();
27
+ static const int QUARTER = std::max(1, ROUNDS / 4);
28
+ static const int WIDTH = 8;
29
+ static const int OPS = 32;
30
+
31
+ static const std::string BASIC_USER = "CONCURRENCY-BASIC-USER";
32
+ static const std::string BASIC_PASS = "CONCURRENCY-BASIC-PASS";
33
+
34
+ static Value liveOptions() {
35
+ return vmap({{"base", Value(std::string("http://concurrency.test/api"))},
36
+ {"allow", vmap({{"op", Value(std::string("direct"))}})}});
37
+ }
38
+
39
+ // A live client whose transport answers at once, showing `seen` the headers
40
+ // of each request.
41
+ static std::shared_ptr<ProjectNameSDK> liveClient(
42
+ const Value& options = liveOptions(), std::function<void(const Value&)> seen = nullptr) {
43
+ auto client = std::make_shared<ProjectNameSDK>(options);
44
+ // Through the base: an entity accessor may share the member's name.
45
+ SdkClient& base = *client;
46
+ base.utility->fetcher = [seen](CtxPtr, const std::string&, const Value& fetchdef) -> Value {
47
+ if (seen) seen(getp(fetchdef, "headers"));
48
+ vs::Injector json = [](vs::Injection&, const Value&, const std::string&, const Value&) -> Value {
49
+ return vmap({{"ok", Value(true)}});
50
+ };
51
+ return vmap({{"status", Value(200)}, {"statusText", Value(std::string("OK"))},
52
+ {"headers", vmap()}, {"json", Value(json)}});
53
+ };
54
+ return client;
55
+ }
56
+
57
+ // Runs body on WIDTH threads released together, and returns what they threw.
58
+ template <typename F>
59
+ static std::vector<std::string> atOnce(F body) {
60
+ std::atomic<int> ready{0};
61
+ std::mutex mu;
62
+ std::vector<std::string> thrown;
63
+ std::vector<std::thread> threads;
64
+ for (int n = 0; n < WIDTH; n++) {
65
+ threads.emplace_back([&, n]() {
66
+ ready.fetch_add(1);
67
+ while (ready.load() < WIDTH) std::this_thread::yield();
68
+ try {
69
+ body(n);
70
+ } catch (const SdkErrorPtr& e) {
71
+ std::lock_guard<std::mutex> lk(mu);
72
+ thrown.push_back(e->msg);
73
+ } catch (const std::exception& e) {
74
+ std::lock_guard<std::mutex> lk(mu);
75
+ thrown.push_back(e.what());
76
+ }
77
+ });
78
+ }
79
+ for (auto& t : threads) t.join();
80
+ return thrown;
81
+ }
82
+
83
+ static void concurrent_first_requests_succeed() {
84
+ for (int round = 0; round < ROUNDS; round++) {
85
+ // A fresh client each round, so every request in it is a first request.
86
+ auto client = liveClient();
87
+ std::vector<Value> results(WIDTH);
88
+ auto thrown = atOnce([&](int n) {
89
+ results[n] = client->direct(vmap({{"path", Value("p" + std::to_string(n))}}));
90
+ });
91
+ if (!thrown.empty()) {
92
+ ASSERT_TRUE(false, "round " + std::to_string(round) + " threw: " + thrown[0]);
93
+ return;
94
+ }
95
+ for (int n = 0; n < WIDTH; n++) {
96
+ if (!is_true(getp(results[n], "ok"))) {
97
+ ASSERT_TRUE(false, "round " + std::to_string(round) + ", request " + std::to_string(n) +
98
+ " failed: " + sdktest::vstr(results[n]));
99
+ return;
100
+ }
101
+ }
102
+ }
103
+ ASSERT_TRUE(true, "every request succeeded");
104
+ }
105
+
106
+ static void concurrent_resolutions_share_one_cached_operation() {
107
+ for (int round = 0; round < ROUNDS; round++) {
108
+ auto client = liveClient();
109
+ auto utility = client->getUtility();
110
+ auto root = client->getRootCtx();
111
+ std::vector<std::vector<OperationPtr>> ops(WIDTH, std::vector<OperationPtr>(OPS));
112
+ auto resolve = [&](int k) {
113
+ CtxSpec cs;
114
+ cs.setOpname("op" + std::to_string(k));
115
+ return utility->makeContext(cs, root)->op;
116
+ };
117
+ auto thrown = atOnce([&](int n) {
118
+ for (int k = 0; k < OPS; k++) ops[n][k] = resolve(k);
119
+ });
120
+ if (!thrown.empty()) {
121
+ ASSERT_TRUE(false, "round " + std::to_string(round) + " threw: " + thrown[0]);
122
+ return;
123
+ }
124
+ for (int k = 0; k < OPS; k++) {
125
+ OperationPtr cached = resolve(k);
126
+ for (int n = 0; n < WIDTH; n++) {
127
+ if (ops[n][k] != cached) {
128
+ ASSERT_TRUE(false, "round " + std::to_string(round) + ": op" + std::to_string(k) +
129
+ " resolved to more than one Operation");
130
+ return;
131
+ }
132
+ }
133
+ }
134
+ }
135
+ ASSERT_TRUE(true, "every operation resolved to one cached Operation");
136
+ }
137
+
138
+ // The k-th secret registering thread n adds in round r.
139
+ static std::string addedSecret(const std::string& r, int n, int k) {
140
+ return "ADDED-SECRET-" + r + "-" + std::to_string(n) + "-" + std::to_string(k);
141
+ }
142
+
143
+ // Registers thread n's secrets, then counts the thread out of `left`.
144
+ static void registerSecrets(UtilityPtr utility, CtxPtr root, const std::string& r, int n,
145
+ std::atomic<int>& left) {
146
+ struct Done {
147
+ std::atomic<int>& left;
148
+ ~Done() { left.fetch_sub(1); }
149
+ } done{left};
150
+ for (int k = 0; k < OPS; k++) utility->cleanAdd(root, Value(addedSecret(r, n, k)));
151
+ }
152
+
153
+ // The first of `known` and the secrets `registrars` threads registered in
154
+ // round r that a clean leaves raw, or "".
155
+ static std::string unmaskedSecret(UtilityPtr utility, CtxPtr root, const std::string& r,
156
+ int registrars, std::vector<std::string> known = {}) {
157
+ for (int n = 0; n < registrars; n++) {
158
+ for (int k = 0; k < OPS; k++) known.push_back(addedSecret(r, n, k));
159
+ }
160
+ for (const auto& secret : known) {
161
+ Value got = utility->clean(root, Value(secret));
162
+ if (!got.is_string() || "[redacted]" != got.as_string()) return secret;
163
+ }
164
+ return "";
165
+ }
166
+
167
+ // Secrets registered on some threads while others clean: every clean masks
168
+ // what was registered before it, the longer secret whole, and no
169
+ // registration is lost.
170
+ static void concurrent_registration_keeps_every_secret_masked() {
171
+ const std::string masked = "a [redacted] b [redacted] c";
172
+ for (int round = 0; round < QUARTER; round++) {
173
+ auto client = liveClient();
174
+ auto utility = client->getUtility();
175
+ auto root = client->getRootCtx();
176
+ std::string r = std::to_string(round);
177
+ std::string inner = "INNER-SECRET-" + r;
178
+ utility->cleanAdd(root, Value(inner));
179
+ utility->cleanAdd(root, Value("OUTER-" + inner + "-TAIL"));
180
+ Value text("a " + inner + " b OUTER-" + inner + "-TAIL c");
181
+ Value before = utility->clean(root, text);
182
+ if (!before.is_string() || masked != before.as_string()) {
183
+ ASSERT_TRUE(false, "round " + r + " cleaned to: " + sdktest::vstr(before));
184
+ return;
185
+ }
186
+
187
+ std::atomic<int> registering{WIDTH / 2};
188
+ std::mutex mu;
189
+ std::vector<std::string> wrong;
190
+ auto thrown = atOnce([&](int n) {
191
+ if (n < WIDTH / 2) {
192
+ registerSecrets(utility, root, r, n, registering);
193
+ return;
194
+ }
195
+ while (0 < registering.load()) {
196
+ Value got = utility->clean(root, text);
197
+ if (!got.is_string() || masked != got.as_string()) {
198
+ std::lock_guard<std::mutex> lk(mu);
199
+ wrong.push_back(sdktest::vstr(got));
200
+ return;
201
+ }
202
+ }
203
+ });
204
+ if (!thrown.empty()) {
205
+ ASSERT_TRUE(false, "round " + r + " threw: " + thrown[0]);
206
+ return;
207
+ }
208
+ if (!wrong.empty()) {
209
+ ASSERT_TRUE(false, "round " + r + " cleaned to: " + wrong[0]);
210
+ return;
211
+ }
212
+ std::string raw = unmaskedSecret(utility, root, r, WIDTH / 2);
213
+ if (!raw.empty()) {
214
+ ASSERT_TRUE(false, "round " + r + ": " + raw + " was registered but not masked");
215
+ return;
216
+ }
217
+ }
218
+ ASSERT_TRUE(true, "every secret stayed masked");
219
+ }
220
+
221
+ // Requests on one client while secrets register on it: each request copies
222
+ // the client's options, the registry among them.
223
+ static void concurrent_requests_survive_registration() {
224
+ for (int round = 0; round < QUARTER; round++) {
225
+ auto client = liveClient();
226
+ auto utility = client->getUtility();
227
+ auto root = client->getRootCtx();
228
+ std::string r = std::to_string(round);
229
+ std::atomic<int> registering{WIDTH / 2};
230
+ std::mutex mu;
231
+ std::vector<std::string> failed;
232
+ auto thrown = atOnce([&](int n) {
233
+ if (n < WIDTH / 2) {
234
+ registerSecrets(utility, root, r, n, registering);
235
+ return;
236
+ }
237
+ while (0 < registering.load()) {
238
+ Value got = client->direct(vmap({{"path", Value("p" + std::to_string(n))}}));
239
+ if (!is_true(getp(got, "ok"))) {
240
+ std::lock_guard<std::mutex> lk(mu);
241
+ failed.push_back(sdktest::vstr(got));
242
+ return;
243
+ }
244
+ }
245
+ });
246
+ if (!thrown.empty()) {
247
+ ASSERT_TRUE(false, "round " + r + " threw: " + thrown[0]);
248
+ return;
249
+ }
250
+ if (!failed.empty()) {
251
+ ASSERT_TRUE(false, "round " + r + ", a request failed: " + failed[0]);
252
+ return;
253
+ }
254
+ std::string raw = unmaskedSecret(utility, root, r, WIDTH / 2);
255
+ if (!raw.empty()) {
256
+ ASSERT_TRUE(false, "round " + r + ": " + raw + " was registered but not masked");
257
+ return;
258
+ }
259
+ }
260
+ ASSERT_TRUE(true, "every request succeeded while secrets registered");
261
+ }
262
+
263
+ // Whether a header value holds `text`.
264
+ static bool carries(const Value& headers, const std::string& text) {
265
+ if (!headers.is_map()) return false;
266
+ for (const auto& kv : *headers.as_map()) {
267
+ if (kv.second.is_string() && std::string::npos != kv.second.as_string().find(text)) return true;
268
+ }
269
+ return false;
270
+ }
271
+
272
+ // First requests on a client carrying Basic credentials while secrets
273
+ // register on it: where the API takes HTTP Basic, prepareAuth registers the
274
+ // encoded pair as the other requests copy the options.
275
+ static void concurrent_basic_requests_survive_registration() {
276
+ const std::string pair = util::cleanBase64(BASIC_USER + ":" + BASIC_PASS);
277
+ int sent = 0;
278
+ int paired = 0;
279
+ for (int round = 0; round < QUARTER; round++) {
280
+ std::atomic<int> roundSent{0};
281
+ std::atomic<int> roundPaired{0};
282
+ Value options = liveOptions();
283
+ map_put(options, "apikey", Value(BASIC_USER));
284
+ map_put(options, "secret", Value(BASIC_PASS));
285
+ auto client = liveClient(options, [&](const Value& headers) {
286
+ roundSent.fetch_add(1);
287
+ if (carries(headers, pair)) roundPaired.fetch_add(1);
288
+ });
289
+ auto utility = client->getUtility();
290
+ auto root = client->getRootCtx();
291
+ std::string r = "basic-" + std::to_string(round);
292
+ std::atomic<int> registering{1};
293
+ std::mutex mu;
294
+ std::vector<std::string> failed;
295
+ auto thrown = atOnce([&](int n) {
296
+ if (0 == n) {
297
+ registerSecrets(utility, root, r, n, registering);
298
+ return;
299
+ }
300
+ // Bounded, as requests that never pause can hold off a registration.
301
+ int made = 0;
302
+ do {
303
+ Value got = client->direct(vmap({{"path", Value("p" + std::to_string(n))}}));
304
+ if (!is_true(getp(got, "ok"))) {
305
+ std::lock_guard<std::mutex> lk(mu);
306
+ failed.push_back(sdktest::vstr(got));
307
+ return;
308
+ }
309
+ } while (0 < registering.load() && ++made < OPS);
310
+ });
311
+ if (!thrown.empty()) {
312
+ ASSERT_TRUE(false, "round " + r + " threw: " + thrown[0]);
313
+ return;
314
+ }
315
+ if (!failed.empty()) {
316
+ ASSERT_TRUE(false, "round " + r + ", a request failed: " + failed[0]);
317
+ return;
318
+ }
319
+ int rsent = roundSent.load();
320
+ int rpaired = roundPaired.load();
321
+ if (0 < rpaired && rpaired != rsent) {
322
+ ASSERT_TRUE(false, "round " + r + ": " + std::to_string(rpaired) + " of " +
323
+ std::to_string(rsent) + " requests carried the Basic pair");
324
+ return;
325
+ }
326
+ std::vector<std::string> known{BASIC_USER, BASIC_PASS};
327
+ if (0 < rpaired) known.push_back(pair);
328
+ std::string raw = unmaskedSecret(utility, root, r, 1, known);
329
+ if (!raw.empty()) {
330
+ ASSERT_TRUE(false, "round " + r + ": " + raw + " was registered but not masked");
331
+ return;
332
+ }
333
+ sent += rsent;
334
+ paired += rpaired;
335
+ }
336
+ std::cout << "concurrency_test: " << paired << " of " << sent
337
+ << " requests carried the Basic pair\n";
338
+ ASSERT_TRUE(true, "every request succeeded while the Basic pair and secrets registered");
339
+ }
340
+
341
+ int main() {
342
+ T_RUN(concurrent_first_requests_succeed);
343
+ T_RUN(concurrent_resolutions_share_one_cached_operation);
344
+ T_RUN(concurrent_registration_keeps_every_secret_masked);
345
+ T_RUN(concurrent_requests_survive_registration);
346
+ T_RUN(concurrent_basic_requests_survive_registration);
347
+ return sdktest::summary("concurrency_test");
348
+ }
@@ -9,6 +9,8 @@
9
9
 
10
10
  #include <algorithm>
11
11
  #include <memory>
12
+ #include <mutex>
13
+ #include <shared_mutex>
12
14
  #include <cctype>
13
15
  #include <string>
14
16
  #include <vector>
@@ -66,7 +68,7 @@ inline CtxPtr makeContext(const CtxSpec& cs, CtxPtr basectx) {
66
68
  //
67
69
  // The configuration is the derived clean block makeOptions builds
68
70
  // (`options.__derived__.clean`): a plain map, so the registry list inside
69
- // it stays mutable after construction - features register later.
71
+ // it can be replaced after construction - features register later.
70
72
 
71
73
  constexpr int CLEAN_MAXDEPTH = 32;
72
74
 
@@ -176,36 +178,51 @@ inline std::vector<std::string> cleanForms(const std::string& value) {
176
178
  return out;
177
179
  }
178
180
 
181
+ inline Value cleanValues(const Value& cfg) {
182
+ std::shared_lock<std::shared_mutex> guard(cleanRegistryLock());
183
+ return getp(cfg, "values");
184
+ }
185
+
186
+ inline bool cleanKnown(const Value& values, const std::string& form) {
187
+ if (!values.is_list()) return false;
188
+ for (const auto& v : *values.as_list()) {
189
+ if (v.is_string() && v.as_string() == form) return true;
190
+ }
191
+ return false;
192
+ }
193
+
179
194
  // Register a secret value. Idempotent; shorter than `min` is not a secret
180
- // the SDK can mask without blanking ordinary text.
195
+ // the SDK can mask without blanking ordinary text. A registration publishes
196
+ // a new list and never changes a published one.
181
197
  inline void cleanAddCfg(const Value& cfg, const Value& value) {
182
198
  if (!cfg.is_map() || !value.is_string()) return;
183
199
  size_t min = static_cast<size_t>(cleanCount(getp(cfg, "min"), 4));
184
200
  const std::string& raw = value.as_string();
185
201
  if (raw.size() < min) return;
186
- Value values = getp(cfg, "values");
187
- if (!values.is_list()) {
188
- values = vlist();
189
- map_put(cfg, "values", values);
190
- }
191
- auto& list = *values.as_list();
192
- bool changed = false;
202
+ std::vector<std::string> forms;
193
203
  for (const auto& form : cleanForms(raw)) {
194
- if (form.size() < min) continue;
195
- bool known = false;
196
- for (const auto& v : list) {
197
- if (v.is_string() && v.as_string() == form) { known = true; break; }
198
- }
199
- if (!known) {
200
- list.push_back(Value(form));
201
- changed = true;
202
- }
203
- }
204
- if (changed) {
205
- std::stable_sort(list.begin(), list.end(), [](const Value& a, const Value& b) {
206
- return a.as_string().size() > b.as_string().size();
207
- });
204
+ if (min <= form.size()) forms.push_back(form);
208
205
  }
206
+ auto unknown = [&forms](const Value& values) {
207
+ std::vector<std::string> out;
208
+ for (const auto& form : forms) {
209
+ if (!cleanKnown(values, form)) out.push_back(form);
210
+ }
211
+ return out;
212
+ };
213
+ if (unknown(cleanValues(cfg)).empty()) return;
214
+ std::unique_lock<std::shared_mutex> guard(cleanRegistryLock());
215
+ Value values = getp(cfg, "values");
216
+ std::vector<std::string> added = unknown(values);
217
+ if (added.empty()) return;
218
+ Value next = vlist();
219
+ if (values.is_list()) *next.as_list() = *values.as_list();
220
+ auto& list = *next.as_list();
221
+ for (const auto& form : added) list.push_back(Value(form));
222
+ std::stable_sort(list.begin(), list.end(), [](const Value& a, const Value& b) {
223
+ return a.as_string().size() > b.as_string().size();
224
+ });
225
+ map_put(cfg, "values", next);
209
226
  }
210
227
 
211
228
  inline void cleanAdd(CtxPtr ctx, const Value& value) {
@@ -221,7 +238,7 @@ inline std::string cleanMaskValue(const Value& cfg, const std::string& value) {
221
238
 
222
239
  inline std::string cleanString(const Value& cfg, const std::string& text) {
223
240
  std::string out = text;
224
- Value values = getp(cfg, "values");
241
+ Value values = cleanValues(cfg);
225
242
  if (!values.is_list()) return out;
226
243
  for (const auto& v : *values.as_list()) {
227
244
  if (!v.is_string() || v.as_string().empty()) continue;
@@ -1776,7 +1793,7 @@ inline bool fieldArg(CtxPtr ctx, const std::string& name) {
1776
1793
 
1777
1794
  // A header, cookie or query argument travels where prepareHeaders or
1778
1795
  // prepareQuery sends it, so the body is built from the request data without
1779
- // it, unless the entity declares it as a field too.
1796
+ // it, unless the point marks it as a field the body keeps.
1780
1797
  inline std::vector<std::string> routedArgNames(CtxPtr ctx) {
1781
1798
  std::vector<std::string> names;
1782
1799
  for (const char* kind : {"header", "cookie", "query"}) {
@@ -1,5 +1,7 @@
1
1
  // ProjectName SDK - operation context.
2
2
 
3
+ using System.Collections.Concurrent;
4
+
3
5
  using Voxgig.Struct;
4
6
 
5
7
  namespace ProjectNameSdk;
@@ -17,7 +19,7 @@ public class Context
17
19
  public Dictionary<string, object?>? Config;
18
20
  public Dictionary<string, object?>? Entopts;
19
21
  public Dictionary<string, object?>? Options;
20
- public Dictionary<string, Operation> Opmap = new();
22
+ public ConcurrentDictionary<string, Operation> Opmap = new();
21
23
  public Response? Response;
22
24
  public Result? Result;
23
25
  public Spec? Spec;
@@ -145,7 +147,7 @@ public class Context
145
147
  }
146
148
 
147
149
  // Opmap
148
- if (Helpers.GetCtxProp(ctxmap, "opmap") is Dictionary<string, Operation> opm)
150
+ if (Helpers.GetCtxProp(ctxmap, "opmap") is ConcurrentDictionary<string, Operation> opm)
149
151
  {
150
152
  Opmap = opm;
151
153
  }
@@ -153,7 +155,7 @@ public class Context
153
155
  {
154
156
  Opmap = basectx.Opmap;
155
157
  }
156
- Opmap ??= new Dictionary<string, Operation>();
158
+ Opmap ??= new ConcurrentDictionary<string, Operation>();
157
159
 
158
160
  // Data maps
159
161
  Data = Helpers.ToMapAny(Helpers.GetCtxProp(ctxmap, "data")) ?? new Dictionary<string, object?>();
@@ -246,8 +248,9 @@ public class Context
246
248
  ["points"] = points,
247
249
  });
248
250
 
249
- Opmap[cacheKey] = op;
250
- return op;
251
+ // Requests on other threads share this cache; every one of them gets
252
+ // the Operation stored first.
253
+ return Opmap.GetOrAdd(cacheKey, op);
251
254
  }
252
255
 
253
256
  public ProjectNameError MakeError(string code, string msg)