@voxgig/sdkgen 4.8.2 → 4.8.3

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 (376) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/action/doctor.d.ts +1 -0
  3. package/dist/action/doctor.js +50 -2
  4. package/dist/action/doctor.js.map +1 -1
  5. package/dist/helpers/naming.js +4 -2
  6. package/dist/helpers/naming.js.map +1 -1
  7. package/dist/tsconfig.tsbuildinfo +1 -1
  8. package/model/sdkgen.aon +21 -0
  9. package/package.json +1 -1
  10. package/project/.sdk/model/feature/secrets.aon +120 -18
  11. package/project/.sdk/model/target/c.aon +10 -0
  12. package/project/.sdk/model/target/clojure.aon +13 -0
  13. package/project/.sdk/model/target/cpp.aon +10 -0
  14. package/project/.sdk/model/target/csharp.aon +9 -0
  15. package/project/.sdk/model/target/dart.aon +10 -0
  16. package/project/.sdk/model/target/elixir.aon +9 -0
  17. package/project/.sdk/model/target/go.aon +13 -0
  18. package/project/.sdk/model/target/java.aon +9 -0
  19. package/project/.sdk/model/target/js.aon +7 -0
  20. package/project/.sdk/model/target/kotlin.aon +9 -0
  21. package/project/.sdk/model/target/lean.aon +17 -0
  22. package/project/.sdk/model/target/lua.aon +8 -0
  23. package/project/.sdk/model/target/ocaml.aon +9 -0
  24. package/project/.sdk/model/target/perl.aon +8 -0
  25. package/project/.sdk/model/target/php.aon +8 -0
  26. package/project/.sdk/model/target/py.aon +13 -0
  27. package/project/.sdk/model/target/rb.aon +8 -0
  28. package/project/.sdk/model/target/rust.aon +10 -0
  29. package/project/.sdk/model/target/scala.aon +21 -2
  30. package/project/.sdk/model/target/swift.aon +18 -0
  31. package/project/.sdk/model/target/ts.aon +13 -0
  32. package/project/.sdk/src/cmp/clojure/Package_clojure.ts +9 -2
  33. package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +3 -0
  34. package/project/.sdk/src/cmp/dart/Test_dart.ts +2 -0
  35. package/project/.sdk/src/cmp/elixir/Package_elixir.ts +4 -1
  36. package/project/.sdk/src/cmp/go/Config_go.ts +59 -2
  37. package/project/.sdk/src/cmp/go/Main_go.ts +8 -2
  38. package/project/.sdk/src/cmp/java/Test_java.ts +6 -5
  39. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +6 -5
  40. package/project/.sdk/src/cmp/lean/Package_lean.ts +23 -0
  41. package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +1 -0
  42. package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +1 -0
  43. package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
  44. package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +7 -3
  45. package/project/.sdk/src/cmp/py/Config_py.ts +82 -1
  46. package/project/.sdk/src/cmp/py/Main_py.ts +44 -0
  47. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +2 -0
  48. package/project/.sdk/src/cmp/swift/Main_swift.ts +10 -0
  49. package/project/.sdk/src/cmp/swift/Package_swift.ts +13 -1
  50. package/project/.sdk/src/cmp/ts/Config_ts.ts +42 -17
  51. package/project/.sdk/src/cmp/ts/fragment/Config.data.fragment.ts +13 -1
  52. package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +13 -1
  53. package/project/.sdk/tm/c/Makefile +38 -3
  54. package/project/.sdk/tm/c/tests/omni_resolver.h +361 -0
  55. package/project/.sdk/tm/c/tests/omni_smoke_test.c +237 -0
  56. package/project/.sdk/tm/c/tests/primary_corpus_test.c +205 -284
  57. package/project/.sdk/tm/c/tests/struct_corpus_test.c +292 -212
  58. package/project/.sdk/tm/c/tests/vendor/omni/json.c +811 -0
  59. package/project/.sdk/tm/c/tests/vendor/omni/omni.h +240 -0
  60. package/project/.sdk/tm/c/tests/vendor/omni/runner.c +940 -0
  61. package/project/.sdk/tm/c/tests/vendor/omni/util.c +351 -0
  62. package/project/.sdk/tm/c/utility/struct/utility.c +7 -2
  63. package/project/.sdk/tm/clojure/test/sdk/test/omni.clj +401 -0
  64. package/project/.sdk/tm/clojure/test/sdk/test/omni_smoke.clj +81 -0
  65. package/project/.sdk/tm/clojure/test/sdk/test/primary.clj +92 -58
  66. package/project/.sdk/tm/clojure/test/sdk/test/struct_corpus.clj +122 -149
  67. package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +8 -3
  68. package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/json.clj +145 -0
  69. package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/runner.clj +445 -0
  70. package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/util.clj +145 -0
  71. package/project/.sdk/tm/cpp/Makefile +5 -1
  72. package/project/.sdk/tm/cpp/test/omni_resolver.hpp +473 -0
  73. package/project/.sdk/tm/cpp/test/omni_smoke_test.cpp +209 -0
  74. package/project/.sdk/tm/cpp/test/primary_utility_test.cpp +243 -184
  75. package/project/.sdk/tm/cpp/test/runner_support.hpp +68 -181
  76. package/project/.sdk/tm/cpp/test/struct_corpus_test.cpp +183 -56
  77. package/project/.sdk/tm/cpp/test/vendor/omni/json.hpp +406 -0
  78. package/project/.sdk/tm/cpp/test/vendor/omni/omni.hpp +751 -0
  79. package/project/.sdk/tm/cpp/test/vendor/omni/util.hpp +256 -0
  80. package/project/.sdk/tm/cpp/utility/pipeline.hpp +7 -1
  81. package/project/.sdk/tm/csharp/test/OmniResolver.cs +491 -0
  82. package/project/.sdk/tm/csharp/test/OmniSmokeTest.cs +106 -0
  83. package/project/.sdk/tm/csharp/test/PrimaryUtilityTest.cs +299 -316
  84. package/project/.sdk/tm/csharp/test/Runner.cs +104 -269
  85. package/project/.sdk/tm/csharp/test/StructUtilityTest.cs +535 -724
  86. package/project/.sdk/tm/csharp/test/vendor/omni/Runner.cs +864 -0
  87. package/project/.sdk/tm/csharp/test/vendor/omni/Util.cs +347 -0
  88. package/project/.sdk/tm/csharp/utility/MakeSpec.cs +3 -1
  89. package/project/.sdk/tm/csharp/utility/PrepareMethod.cs +6 -1
  90. package/project/.sdk/tm/csharp/utility/struct/Struct.cs +21 -5
  91. package/project/.sdk/tm/dart/test/omni.dart +520 -0
  92. package/project/.sdk/tm/dart/test/omni_smoke_test.dart +143 -0
  93. package/project/.sdk/tm/dart/test/primary_test.dart +86 -28
  94. package/project/.sdk/tm/dart/test/struct_test.dart +438 -11
  95. package/project/.sdk/tm/dart/test/vendor/omni/omni.dart +7 -0
  96. package/project/.sdk/tm/dart/test/vendor/omni/runner.dart +653 -0
  97. package/project/.sdk/tm/dart/test/vendor/omni/util.dart +232 -0
  98. package/project/.sdk/tm/elixir/Makefile +11 -3
  99. package/project/.sdk/tm/elixir/test/omni_smoke_test.exs +117 -0
  100. package/project/.sdk/tm/elixir/test/primary_utility_test.exs +341 -16
  101. package/project/.sdk/tm/elixir/test/struct_corpus_test.exs +554 -9
  102. package/project/.sdk/tm/elixir/test/support/omni.ex +377 -0
  103. package/project/.sdk/tm/elixir/test/vendor/omni/json.ex +122 -0
  104. package/project/.sdk/tm/elixir/test/vendor/omni/runner.ex +623 -0
  105. package/project/.sdk/tm/elixir/test/vendor/omni/util.ex +187 -0
  106. package/project/.sdk/tm/go/core/context.go +32 -16
  107. package/project/.sdk/tm/go/feature/secrets/plugin/capability.go +261 -0
  108. package/project/.sdk/tm/go/feature/secrets/plugin/catalog.go +78 -0
  109. package/project/.sdk/tm/go/feature/secrets/plugin/config.go +652 -0
  110. package/project/.sdk/tm/go/feature/secrets/plugin/depend.go +250 -0
  111. package/project/.sdk/tm/go/feature/secrets/plugin/env.go +194 -0
  112. package/project/.sdk/tm/go/feature/secrets/plugin/export.go +80 -0
  113. package/project/.sdk/tm/go/feature/secrets/plugin/graph.go +238 -0
  114. package/project/.sdk/tm/go/feature/secrets/plugin/host.go +1575 -0
  115. package/project/.sdk/tm/go/feature/secrets/plugin/order.go +248 -0
  116. package/project/.sdk/tm/go/feature/secrets/plugin/point.go +184 -0
  117. package/project/.sdk/tm/go/feature/secrets/plugin/ref.go +123 -0
  118. package/project/.sdk/tm/go/feature/secrets/plugin/resolve.go +79 -0
  119. package/project/.sdk/tm/go/feature/secrets/plugin/types.go +296 -0
  120. package/project/.sdk/tm/go/feature/secrets/plugin/util.go +87 -0
  121. package/project/.sdk/tm/go/feature/secrets/plugin/version.go +161 -0
  122. package/project/.sdk/tm/go/feature/secrets/plugins/aws/aws.go +292 -0
  123. package/project/.sdk/tm/go/feature/secrets/plugins/aws/sigv4.go +201 -0
  124. package/project/.sdk/tm/go/feature/secrets/plugins/azuresecrets/azuresecrets.go +187 -0
  125. package/project/.sdk/tm/go/feature/secrets/plugins/boru/boru.go +184 -0
  126. package/project/.sdk/tm/go/feature/secrets/plugins/doppler/doppler.go +114 -0
  127. package/project/.sdk/tm/go/feature/secrets/plugins/gcpsecrets/gcpsecrets.go +156 -0
  128. package/project/.sdk/tm/go/feature/secrets/plugins/hashicorp/hashicorp.go +209 -0
  129. package/project/.sdk/tm/go/feature/secrets/plugins/httpjson/httpjson.go +241 -0
  130. package/project/.sdk/tm/go/feature/secrets/plugins/infisical/infisical.go +151 -0
  131. package/project/.sdk/tm/go/feature/secrets/plugins/onepassword/onepassword.go +152 -0
  132. package/project/.sdk/tm/go/feature/secrets/plugins/secretspec/secretspec.go +143 -0
  133. package/project/.sdk/tm/go/feature/secrets/sekreto/addr.go +120 -0
  134. package/project/.sdk/tm/go/feature/secrets/sekreto/providers.go +439 -0
  135. package/project/.sdk/tm/go/feature/secrets/sekreto/sekreto.go +685 -0
  136. package/project/.sdk/tm/go/feature/secrets_feature.go +675 -0
  137. package/project/.sdk/tm/go/src/feature/secrets/.gitkeep +0 -0
  138. package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +799 -0
  139. package/project/.sdk/tm/go/test/omni/omni.go +897 -0
  140. package/project/.sdk/tm/go/test/omni/util.go +360 -0
  141. package/project/.sdk/tm/go/test/omniresolver_test.go +748 -0
  142. package/project/.sdk/tm/go/test/omnismoke_test.go +112 -0
  143. package/project/.sdk/tm/go/test/primary_utility_test.go +123 -191
  144. package/project/.sdk/tm/go/test/struct_utility_test.go +323 -16
  145. package/project/.sdk/tm/go/test/testsupport_test.go +294 -0
  146. package/project/.sdk/tm/go/utility/make_options.go +8 -1
  147. package/project/.sdk/tm/go/utility/prepare_method.go +5 -1
  148. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +2 -2
  149. package/project/.sdk/tm/java/test/OmniResolver.java +355 -0
  150. package/project/.sdk/tm/java/test/OmniSmokeTest.java +99 -0
  151. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +254 -280
  152. package/project/.sdk/tm/java/test/RunnerSupport.java +9 -194
  153. package/project/.sdk/tm/java/test/StructCorpusTest.java +119 -42
  154. package/project/.sdk/tm/java/test/vendor/omni/Json.java +287 -0
  155. package/project/.sdk/tm/java/test/vendor/omni/Runner.java +851 -0
  156. package/project/.sdk/tm/java/test/vendor/omni/Util.java +322 -0
  157. package/project/.sdk/tm/java/utility/MakeSpec.java +3 -1
  158. package/project/.sdk/tm/java/utility/PrepareMethod.java +5 -5
  159. package/project/.sdk/tm/java/utility/struct/Struct.java +17 -4
  160. package/project/.sdk/tm/js/src/utility/StructUtility.js +3 -3
  161. package/project/.sdk/tm/js/test/omni.js +251 -0
  162. package/project/.sdk/tm/js/test/omni.test.js +105 -0
  163. package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +6 -2
  164. package/project/.sdk/tm/js/test/utility/StructUtility.test.js +27 -2
  165. package/project/.sdk/tm/js/test/vendor/omni/index.js +77 -0
  166. package/project/.sdk/tm/js/test/vendor/omni/runner.js +613 -0
  167. package/project/.sdk/tm/js/test/vendor/omni/util.js +212 -0
  168. package/project/.sdk/tm/kotlin/core/Utility.kt +1 -1
  169. package/project/.sdk/tm/kotlin/test/OmniResolver.kt +343 -0
  170. package/project/.sdk/tm/kotlin/test/OmniSmokeTest.kt +100 -0
  171. package/project/.sdk/tm/kotlin/test/PrimaryUtilityTest.kt +244 -262
  172. package/project/.sdk/tm/kotlin/test/RunnerSupport.kt +9 -178
  173. package/project/.sdk/tm/kotlin/test/StructCorpusTest.kt +121 -32
  174. package/project/.sdk/tm/kotlin/test/vendor/omni/Json.kt +264 -0
  175. package/project/.sdk/tm/kotlin/test/vendor/omni/Runner.kt +615 -0
  176. package/project/.sdk/tm/kotlin/test/vendor/omni/Util.kt +139 -0
  177. package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +6 -3
  178. package/project/.sdk/tm/kotlin/utility/Prepare.kt +6 -6
  179. package/project/.sdk/tm/kotlin/utility/struct/Struct.kt +18 -2
  180. package/project/.sdk/tm/lean/Makefile +12 -5
  181. package/project/.sdk/tm/lean/src/SdkUtility.lean +14 -1
  182. package/project/.sdk/tm/lean/test/OmniResolver.lean +358 -0
  183. package/project/.sdk/tm/lean/test/OmniSmoke.lean +174 -0
  184. package/project/.sdk/tm/lean/test/StructCorpus.lean +245 -574
  185. package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +155 -291
  186. package/project/.sdk/tm/lean/test/vendor/omni/Omni.lean +997 -0
  187. package/project/.sdk/tm/lua/test/omni.lua +456 -0
  188. package/project/.sdk/tm/lua/test/omni_smoke_test.lua +103 -0
  189. package/project/.sdk/tm/lua/test/primary_utility_test.lua +250 -509
  190. package/project/.sdk/tm/lua/test/struct_utility_test.lua +161 -70
  191. package/project/.sdk/tm/lua/test/vendor/omni/json.lua +268 -0
  192. package/project/.sdk/tm/lua/test/vendor/omni/regex.lua +385 -0
  193. package/project/.sdk/tm/lua/test/vendor/omni/runner.lua +641 -0
  194. package/project/.sdk/tm/lua/test/vendor/omni/util.lua +282 -0
  195. package/project/.sdk/tm/lua/utility/prepare_method.lua +5 -1
  196. package/project/.sdk/tm/lua/utility/struct/regex.lua +663 -0
  197. package/project/.sdk/tm/lua/utility/struct/struct.lua +660 -420
  198. package/project/.sdk/tm/ocaml/Makefile +37 -12
  199. package/project/.sdk/tm/ocaml/test/omni_resolver.ml +284 -0
  200. package/project/.sdk/tm/ocaml/test/omni_smoke_test.ml +169 -0
  201. package/project/.sdk/tm/ocaml/test/primary_utility_test.ml +84 -100
  202. package/project/.sdk/tm/ocaml/test/struct_corpus.ml +225 -201
  203. package/project/.sdk/tm/ocaml/test/vendor/omni/omni.ml +1157 -0
  204. package/project/.sdk/tm/perl/lib/Voxgig/Struct.pm +88 -12
  205. package/project/.sdk/tm/perl/t/omni.pm +647 -0
  206. package/project/.sdk/tm/perl/t/omni_smoke.t +87 -0
  207. package/project/.sdk/tm/perl/t/primary_utility.t +189 -487
  208. package/project/.sdk/tm/perl/t/struct_utility.t +455 -389
  209. package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Runner.pm +649 -0
  210. package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Util.pm +253 -0
  211. package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni.pm +24 -0
  212. package/project/.sdk/tm/perl/utility/prepare_method.pm +7 -2
  213. package/project/.sdk/tm/php/test/Omni.php +691 -0
  214. package/project/.sdk/tm/php/test/OmniSmokeTest.php +93 -0
  215. package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +210 -571
  216. package/project/.sdk/tm/php/test/StructUtilityTest.php +320 -342
  217. package/project/.sdk/tm/php/test/vendor/omni/Runner.php +644 -0
  218. package/project/.sdk/tm/php/test/vendor/omni/Util.php +283 -0
  219. package/project/.sdk/tm/php/utility/MakeSpec.php +10 -3
  220. package/project/.sdk/tm/php/utility/PrepareBody.php +7 -1
  221. package/project/.sdk/tm/php/utility/PrepareMethod.php +6 -2
  222. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/__init__.py +36 -0
  223. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/capability.py +141 -0
  224. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/catalog.py +50 -0
  225. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/config.py +333 -0
  226. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/depend.py +198 -0
  227. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/env.py +147 -0
  228. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/export.py +52 -0
  229. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/graph.py +139 -0
  230. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/host.py +1053 -0
  231. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/order.py +171 -0
  232. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/point.py +95 -0
  233. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/ref.py +116 -0
  234. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/resolve.py +59 -0
  235. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/types.py +87 -0
  236. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/version.py +112 -0
  237. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/__init__.py +74 -0
  238. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/addr.py +116 -0
  239. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/aws.py +192 -0
  240. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/azuresecrets.py +120 -0
  241. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/boru.py +140 -0
  242. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/doppler.py +62 -0
  243. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/gcpsecrets.py +101 -0
  244. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/hashicorp.py +152 -0
  245. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/httpjson.py +115 -0
  246. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/infisical.py +92 -0
  247. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/onepassword.py +101 -0
  248. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/secretspec.py +119 -0
  249. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/sigv4.py +125 -0
  250. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/providers.py +211 -0
  251. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/sekreto.py +455 -0
  252. package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +467 -0
  253. package/project/.sdk/tm/py/pkg/utility/prepare_method.py +5 -1
  254. package/project/.sdk/tm/py/pkg/utility/voxgig_struct/__init__.py +0 -2
  255. package/project/.sdk/tm/py/pkg/utility/voxgig_struct/voxgig_struct.py +780 -529
  256. package/project/.sdk/tm/py/src/feature/secrets/.gitkeep +0 -0
  257. package/project/.sdk/tm/py/test/feature/__init__.py +0 -0
  258. package/project/.sdk/tm/py/test/feature/secrets/__init__.py +0 -0
  259. package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +337 -0
  260. package/project/.sdk/tm/py/test/omni.py +415 -0
  261. package/project/.sdk/tm/py/test/test_omni_smoke.py +74 -0
  262. package/project/.sdk/tm/py/test/test_primary_utility.py +144 -668
  263. package/project/.sdk/tm/py/test/test_struct_utility.py +74 -27
  264. package/project/.sdk/tm/py/test/voxgig_omni/__init__.py +62 -0
  265. package/project/.sdk/tm/py/test/voxgig_omni/runner.py +566 -0
  266. package/project/.sdk/tm/py/test/voxgig_omni/util.py +237 -0
  267. package/project/.sdk/tm/rb/test/omni.rb +505 -0
  268. package/project/.sdk/tm/rb/test/omni_smoke_test.rb +72 -0
  269. package/project/.sdk/tm/rb/test/primary_utility_test.rb +174 -560
  270. package/project/.sdk/tm/rb/test/struct_utility_test.rb +13 -7
  271. package/project/.sdk/tm/rb/test/vendor/omni/runner.rb +493 -0
  272. package/project/.sdk/tm/rb/test/vendor/omni/util.rb +199 -0
  273. package/project/.sdk/tm/rb/test/vendor/omni/voxgig_omni.rb +15 -0
  274. package/project/.sdk/tm/rb/utility/prepare_method.rb +5 -1
  275. package/project/.sdk/tm/rust/tests/common/mod.rs +19 -242
  276. package/project/.sdk/tm/rust/tests/omni_resolver/mod.rs +888 -0
  277. package/project/.sdk/tm/rust/tests/omni_smoke_test.rs +286 -0
  278. package/project/.sdk/tm/rust/tests/primary_utility_test.rs +318 -312
  279. package/project/.sdk/tm/rust/tests/struct_utility_test.rs +66 -28
  280. package/project/.sdk/tm/rust/tests/vendor/omni/json.rs +412 -0
  281. package/project/.sdk/tm/rust/tests/vendor/omni/mod.rs +38 -0
  282. package/project/.sdk/tm/rust/tests/vendor/omni/regex.rs +392 -0
  283. package/project/.sdk/tm/rust/tests/vendor/omni/runner.rs +1001 -0
  284. package/project/.sdk/tm/rust/tests/vendor/omni/util.rs +161 -0
  285. package/project/.sdk/tm/rust/utility/prepare_method.rs +5 -1
  286. package/project/.sdk/tm/scala/Makefile +16 -6
  287. package/project/.sdk/tm/scala/sdktest/OmniResolver.scala +497 -0
  288. package/project/.sdk/tm/scala/sdktest/OmniSmoke.scala +192 -0
  289. package/project/.sdk/tm/scala/sdktest/PrimaryCorpus.scala +47 -19
  290. package/project/.sdk/tm/scala/sdktest/StructCorpus.scala +0 -0
  291. package/project/.sdk/tm/scala/sdktest/vendor/omni/Json.scala +229 -0
  292. package/project/.sdk/tm/scala/sdktest/vendor/omni/Runner.scala +552 -0
  293. package/project/.sdk/tm/scala/sdktest/vendor/omni/Util.scala +104 -0
  294. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Constants.swift +10 -0
  295. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Inject.swift +28 -5
  296. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Injection.swift +5 -1
  297. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Minor.swift +4 -1
  298. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Validate.swift +15 -5
  299. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +6 -1
  300. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/OmniResolver.swift +474 -0
  301. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/OmniSmokeTest.swift +107 -0
  302. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +141 -178
  303. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/Runner.swift +17 -113
  304. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/StructUtilityTest.swift +295 -446
  305. package/project/.sdk/tm/swift/Tests/vendor/omni/Json.swift +364 -0
  306. package/project/.sdk/tm/swift/Tests/vendor/omni/Runner.swift +730 -0
  307. package/project/.sdk/tm/swift/Tests/vendor/omni/Util.swift +162 -0
  308. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +7 -0
  309. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Capability.ts +148 -0
  310. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Catalog.ts +55 -0
  311. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Config.ts +344 -0
  312. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Depend.ts +205 -0
  313. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Env.ts +138 -0
  314. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Export.ts +46 -0
  315. package/project/.sdk/tm/ts/src/feature/secrets/plugin/FeatureHost.ts +349 -0
  316. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Graph.ts +181 -0
  317. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Host.ts +1132 -0
  318. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Order.ts +177 -0
  319. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Point.ts +119 -0
  320. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Ref.ts +102 -0
  321. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Resolve.ts +61 -0
  322. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Types.ts +112 -0
  323. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Version.ts +100 -0
  324. package/project/.sdk/tm/ts/src/feature/secrets/plugin/index.ts +37 -0
  325. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +163 -30
  326. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +27 -22
  327. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/aws.ts +26 -36
  328. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/azuresecrets.ts +19 -21
  329. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/boru.ts +36 -17
  330. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/doppler.ts +14 -21
  331. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/gcpsecrets.ts +27 -27
  332. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/hashicorp.ts +25 -39
  333. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/httpjson.ts +99 -0
  334. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/infisical.ts +14 -16
  335. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/onepassword.ts +15 -22
  336. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/secretspec.ts +98 -0
  337. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{Sigv4.ts → plugins/sigv4.ts} +7 -6
  338. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +97 -23
  339. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/builtin.ts +37 -0
  340. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +4 -17
  341. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +4 -16
  342. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +11 -25
  343. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +6 -23
  344. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +106 -39
  345. package/project/.sdk/tm/ts/src/utility/StructUtility.ts +3 -3
  346. package/project/.sdk/tm/ts/test/omni.ts +104 -32
  347. package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +27 -0
  348. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +32 -5
  349. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +2 -2
  350. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +2 -2
  351. package/project/.sdk/tm/zig/test/primary_utility_test.zig +1 -1
  352. package/project/.sdk/tm/zig/test/struct_runner.zig +5 -4
  353. package/project/sdkgen-package.json +1 -1
  354. package/src/action/doctor.ts +66 -2
  355. package/src/helpers/naming.ts +4 -2
  356. package/project/.sdk/tm/c/tests/runner.h +0 -274
  357. package/project/.sdk/tm/cpp/test/struct_runner.hpp +0 -314
  358. package/project/.sdk/tm/csharp/test/StructRunner.cs +0 -276
  359. package/project/.sdk/tm/dart/test/runner.dart +0 -406
  360. package/project/.sdk/tm/dart/test/struct_corpus.dart +0 -531
  361. package/project/.sdk/tm/elixir/test/support/struct_corpus.ex +0 -1088
  362. package/project/.sdk/tm/go/test/runner_test.go +0 -629
  363. package/project/.sdk/tm/go/test/struct_runner_test.go +0 -1094
  364. package/project/.sdk/tm/java/test/StructRunner.java +0 -281
  365. package/project/.sdk/tm/js/test/runner.js +0 -387
  366. package/project/.sdk/tm/kotlin/test/StructRunner.kt +0 -177
  367. package/project/.sdk/tm/lua/test/struct_runner.lua +0 -602
  368. package/project/.sdk/tm/ocaml/test/corpus_runner.ml +0 -185
  369. package/project/.sdk/tm/perl/t/struct_runner.pm +0 -275
  370. package/project/.sdk/tm/php/test/StructRunner.php +0 -275
  371. package/project/.sdk/tm/py/test/struct_runner.py +0 -411
  372. package/project/.sdk/tm/rb/test/struct_runner.rb +0 -309
  373. package/project/.sdk/tm/rust/tests/struct_runner/mod.rs +0 -424
  374. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +0 -90
  375. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +0 -66
  376. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +0 -188
@@ -1,7 +1,13 @@
1
- /* Copyright (c) 2025-2026 Voxgig Ltd. MIT LICENSE. */
2
-
3
- // RUN: cd cs/tests && dotnet test
4
- // RUN-SOME: cd cs/tests && dotnet test --filter "DisplayName~minor-isnode"
1
+ // Drives the shared struct corpus (../../.sdk/test/test.json, root key
2
+ // "struct") against the vendored struct utility THROUGH the vendored omni
3
+ // runner (OmniResolver over test/vendor/omni) - the engine half of the
4
+ // retired StructRunner. Mirrors tm/go/test/struct_utility_test.go.
5
+ // A missing category or section FAILS (the old runner skipped it silently
6
+ // - a renamed fixture reported PASS while running zero assertions); a
7
+ // failing entry throws OmniError with the entry named.
8
+ //
9
+ // RUN: cd test && dotnet test
10
+ // RUN-SOME: dotnet test --filter "DisplayName~MinorIsNode"
5
11
 
6
12
  using Voxgig.Struct;
7
13
  using Xunit;
@@ -11,15 +17,105 @@ namespace ProjectNameSdk.Test;
11
17
 
12
18
  public class StructUtilityTest
13
19
  {
14
- private readonly Dictionary<string, object?> _spec;
15
- private readonly Dictionary<string, object?> _minor;
20
+ private static OmniResolver.Run? RUN;
21
+ private static readonly object RunLock = new();
22
+
23
+ private static OmniResolver.Run StructRun()
24
+ {
25
+ lock (RunLock)
26
+ {
27
+ if (RUN == null)
28
+ {
29
+ RUN = OmniResolver.MakeRunner(StructRunner.TestJsonPath(),
30
+ ProjectNameSDK.TestSDK(null, null))("struct");
31
+ Assert.True(0 < RUN.Spec.Count,
32
+ "struct section not found in test.json");
33
+ }
34
+ return RUN;
35
+ }
36
+ }
37
+
38
+ /// <summary>A subject in the struct-corpus shape: one argument in, one value out.</summary>
39
+ private delegate object? StructSubject(object? input);
40
+
41
+ private static object? Getp(object? input, string key)
42
+ {
43
+ return input is Dictionary<string, object?> m && m.TryGetValue(key, out var v)
44
+ ? v : null;
45
+ }
46
+
47
+ private static object? GetpDef(object? input, string key, object? def)
48
+ {
49
+ return input is Dictionary<string, object?> m && m.ContainsKey(key)
50
+ ? m[key] : def;
51
+ }
16
52
 
17
- public StructUtilityTest()
53
+ private static int? IntArg(object? val)
54
+ => val == null ? null : (int?)Convert.ToInt32(val);
55
+
56
+ // Run one corpus section, failing loudly when a category or section is
57
+ // missing or EMPTY - a renamed fixture must not report PASS while
58
+ // running zero assertions.
59
+ private static void RunStruct(string category, string name, bool nullFlag,
60
+ StructSubject subject)
18
61
  {
19
- _spec = StructRunner.LoadSpec();
20
- _minor = _spec["minor"] as Dictionary<string, object?> ?? [];
62
+ var run = StructRun();
63
+ var cat = run.Spec.TryGetValue(category, out var c)
64
+ ? c as Dictionary<string, object?> : null;
65
+ Assert.True(null != cat, "struct corpus category missing: " + category +
66
+ " - check .sdk/test/struct/");
67
+ var spec = cat!.TryGetValue(name, out var s)
68
+ ? s as Dictionary<string, object?> : null;
69
+ Assert.True(null != spec, "struct corpus section missing: " + category +
70
+ "." + name + " - check .sdk/test/struct/");
71
+ var set = spec!.TryGetValue("set", out var sv) ? sv as List<object?> : null;
72
+ Assert.True(set != null && 0 < set.Count,
73
+ "struct corpus section is EMPTY: " + category + "." + name +
74
+ " - zero cases would run");
75
+ run.RunSetFlags(spec, new Dictionary<string, bool> { ["null"] = nullFlag },
76
+ args => subject(0 < args.Length ? args[0] : StructUtils.NONE));
21
77
  }
22
78
 
79
+ private static Dictionary<string, object?> Section(string category, string name)
80
+ {
81
+ var run = StructRun();
82
+ var cat = run.Spec.TryGetValue(category, out var c)
83
+ ? c as Dictionary<string, object?> : null;
84
+ Assert.True(null != cat, "struct corpus category missing: " + category);
85
+ var spec = cat!.TryGetValue(name, out var s)
86
+ ? s as Dictionary<string, object?> : null;
87
+ Assert.True(null != spec,
88
+ "struct corpus section missing: " + category + "." + name);
89
+ return spec!;
90
+ }
91
+
92
+ // The struct-corpus nullModifier (was the old runner's ad-hoc handling):
93
+ // a bare "__NULL__" becomes a real null, and an embedded "__NULL__"
94
+ // inside a larger string is rewritten to the literal text "null".
95
+ // Mirrors omni's NullModifier for struct's own Modify callback shape.
96
+ private static readonly Modify NullModifier = (val, key, parent, inj, store) =>
97
+ {
98
+ if (val is not string text)
99
+ {
100
+ return val;
101
+ }
102
+ object? repl;
103
+ if (OmniResolver.NULLMARK == text)
104
+ {
105
+ repl = null;
106
+ }
107
+ else if (text.Contains(OmniResolver.NULLMARK))
108
+ {
109
+ repl = text.Replace(OmniResolver.NULLMARK, "null");
110
+ }
111
+ else
112
+ {
113
+ return val;
114
+ }
115
+ StructUtils.SetProp(parent, key, repl);
116
+ return repl;
117
+ };
118
+
23
119
 
24
120
  // ========================================================================
25
121
  // minor-exists: verify all expected functions exist
@@ -59,59 +155,49 @@ public class StructUtilityTest
59
155
 
60
156
 
61
157
  // ========================================================================
62
- // Minor utility tests (driven from test.json)
158
+ // Minor utility tests (driven from test.json through the resolver)
63
159
  // ========================================================================
64
160
 
65
161
  [Fact]
66
162
  public void MinorIsNode()
67
163
  {
68
- StructRunner.RunSet(_minor["isnode"], input =>
69
- StructUtils.IsNode(input));
164
+ RunStruct("minor", "isnode", true, input => StructUtils.IsNode(input));
70
165
  }
71
166
 
72
167
  [Fact]
73
168
  public void MinorIsMap()
74
169
  {
75
- StructRunner.RunSet(_minor["ismap"], input =>
76
- StructUtils.IsMap(input));
170
+ RunStruct("minor", "ismap", true, input => StructUtils.IsMap(input));
77
171
  }
78
172
 
79
173
  [Fact]
80
174
  public void MinorIsList()
81
175
  {
82
- StructRunner.RunSet(_minor["islist"], input =>
83
- StructUtils.IsList(input));
176
+ RunStruct("minor", "islist", true, input => StructUtils.IsList(input));
84
177
  }
85
178
 
86
179
  [Fact]
87
180
  public void MinorIsKey()
88
181
  {
89
- StructRunner.RunSet(_minor["iskey"],
90
- input => StructUtils.IsKey(input),
91
- flags: new() { ["null"] = false });
182
+ RunStruct("minor", "iskey", false, input => StructUtils.IsKey(input));
92
183
  }
93
184
 
94
185
  [Fact]
95
186
  public void MinorStrKey()
96
187
  {
97
- StructRunner.RunSet(_minor["strkey"],
98
- input => StructUtils.StrKey(input),
99
- flags: new() { ["null"] = false });
188
+ RunStruct("minor", "strkey", false, input => StructUtils.StrKey(input));
100
189
  }
101
190
 
102
191
  [Fact]
103
192
  public void MinorIsEmpty()
104
193
  {
105
- StructRunner.RunSet(_minor["isempty"],
106
- input => StructUtils.IsEmpty(input),
107
- flags: new() { ["null"] = false });
194
+ RunStruct("minor", "isempty", false, input => StructUtils.IsEmpty(input));
108
195
  }
109
196
 
110
197
  [Fact]
111
198
  public void MinorIsFunc()
112
199
  {
113
- StructRunner.RunSet(_minor["isfunc"],
114
- input => StructUtils.IsFunc(input));
200
+ RunStruct("minor", "isfunc", true, input => StructUtils.IsFunc(input));
115
201
 
116
202
  // Edge: actual delegates should be recognised.
117
203
  Func<object?> f0 = () => null;
@@ -122,9 +208,7 @@ public class StructUtilityTest
122
208
  [Fact]
123
209
  public void MinorClone()
124
210
  {
125
- StructRunner.RunSet(_minor["clone"],
126
- input => StructUtils.Clone(input),
127
- flags: new() { ["null"] = false });
211
+ RunStruct("minor", "clone", false, input => StructUtils.Clone(input));
128
212
 
129
213
  // Edge: functions should be shallow-copied, not cloned.
130
214
  Func<object?> f0 = () => null;
@@ -137,83 +221,92 @@ public class StructUtilityTest
137
221
  [Fact]
138
222
  public void MinorEscRe()
139
223
  {
140
- StructRunner.RunSet(_minor["escre"],
141
- input => StructUtils.EscRe(input as string));
224
+ RunStruct("minor", "escre", true, input => StructUtils.EscRe(input as string));
142
225
  }
143
226
 
144
227
  [Fact]
145
228
  public void MinorEscUrl()
146
229
  {
147
- StructRunner.RunSet(_minor["escurl"],
148
- input => StructUtils.EscUrl(input as string));
230
+ RunStruct("minor", "escurl", true, input => StructUtils.EscUrl(input as string));
149
231
  }
150
232
 
151
233
  [Fact]
152
234
  public void MinorStringify()
153
235
  {
154
- StructRunner.RunSet(_minor["stringify"], input =>
236
+ RunStruct("minor", "stringify", false, input =>
155
237
  {
156
- var m = input as Dictionary<string, object?>;
157
- if (m == null) return StructUtils.Stringify(input);
158
-
159
- // Absent "val" key -> NONE (TS undefined) so Stringify returns "".
160
- // Present-but-null -> JSON null (Stringify returns "null").
161
- object? val = m.ContainsKey("val") ? m["val"] : StructUtils.NONE;
162
- if (val is string s && s == StructRunner.NULLMARK) val = null;
163
-
164
- if (m.TryGetValue("max", out object? maxObj) && maxObj != null)
165
- return StructUtils.Stringify(val, (int)Convert.ToInt64(maxObj));
166
-
167
- return StructUtils.Stringify(val);
238
+ // null:false keeps a JSON-null val as a real null (rendered
239
+ // "null"); an absent val is NONE (rendered ""). Mirrors the
240
+ // canonical harness.
241
+ var val = GetpDef(input, "val", StructUtils.NONE);
242
+ var max = Getp(input, "max");
243
+ return StructUtils.Stringify(val, IntArg(max));
168
244
  });
169
245
  }
170
246
 
171
247
  [Fact]
172
- public void MinorPathify()
248
+ public void MinorJsonify()
173
249
  {
174
- StructRunner.RunSet(_minor["pathify"],
175
- input =>
250
+ RunStruct("minor", "jsonify", false, input =>
251
+ {
252
+ var val = Getp(input, "val");
253
+ if (Getp(input, "flags") is Dictionary<string, object?> flags)
176
254
  {
177
- var m = input as Dictionary<string, object?>;
178
- if (m == null) return StructUtils.Pathify(input);
179
-
180
- // If the "path" key is absent entirely, pass NONE (not null) so
181
- // Pathify can distinguish "not provided" from "explicitly null".
182
- bool hasPath = m.ContainsKey("path");
183
- object? path = hasPath ? m["path"] : StructUtils.NONE;
184
- if (path is string ps && ps == StructRunner.NULLMARK) path = null;
185
-
186
- int from = 0;
187
- if (m.TryGetValue("from", out object? fv) && fv != null)
188
- from = (int)Convert.ToInt64(fv);
255
+ var indent = flags.TryGetValue("indent", out var iv) && iv != null
256
+ ? Convert.ToInt32(iv) : 2;
257
+ var offset = flags.TryGetValue("offset", out var ov) && ov != null
258
+ ? Convert.ToInt32(ov) : 0;
259
+ return StructUtils.Jsonify(val, indent, offset);
260
+ }
261
+ return StructUtils.Jsonify(val);
262
+ });
263
+ }
189
264
 
190
- return StructUtils.Pathify(path, from);
191
- },
192
- flags: new() { ["null"] = true });
265
+ [Fact]
266
+ public void MinorPathify()
267
+ {
268
+ RunStruct("minor", "pathify", false, input =>
269
+ {
270
+ // null:false keeps a JSON-null path as a real null
271
+ // ("<unknown-path:null>") and an absent path as NONE
272
+ // ("<unknown-path>"); null parts are dropped.
273
+ var path = GetpDef(input, "path", StructUtils.NONE);
274
+ var from = IntArg(Getp(input, "from"));
275
+ var to = IntArg(Getp(input, "to"));
276
+ return null == to
277
+ ? (null == from
278
+ ? StructUtils.Pathify(path)
279
+ : StructUtils.Pathify(path, from.Value))
280
+ : StructUtils.Pathify(path, from ?? 0, to.Value);
281
+ });
193
282
  }
194
283
 
195
284
  [Fact]
196
285
  public void MinorItems()
197
286
  {
198
- StructRunner.RunSet(_minor["items"],
199
- input => StructUtils.Items(input));
287
+ RunStruct("minor", "items", true, input => StructUtils.Items(input));
200
288
  }
201
289
 
202
290
  [Fact]
203
291
  public void MinorGetProp()
204
292
  {
205
- StructRunner.RunSet(_minor["getprop"],
206
- input =>
207
- {
208
- var m = input as Dictionary<string, object?>;
209
- if (m == null) return null;
210
- object? val = m.TryGetValue("val", out object? v) ? v : null;
211
- object? key = m.TryGetValue("key", out object? k) ? k : null;
212
- if (m.TryGetValue("alt", out object? alt) && alt != null)
213
- return StructUtils.GetProp(val, key, alt);
214
- return StructUtils.GetProp(val, key);
215
- },
216
- flags: new() { ["null"] = false });
293
+ RunStruct("minor", "getprop", false, input =>
294
+ {
295
+ var val = Getp(input, "val");
296
+ var key = Getp(input, "key");
297
+ // Canonical's default alt is `undefined`, so the no-alt call has
298
+ // to ask for NONE explicitly: `GetProp(val, key)` defaults `alt`
299
+ // to null, which answers "null" where canonical answers
300
+ // "undefined", and under `null: false` the corpus tells them
301
+ // apart. It also omits `alt` only when the KEY is missing
302
+ // (`undefined === vin.alt`), so an explicit `alt: null` is
303
+ // passed through - unlike getelem below, which omits a null alt
304
+ // too (`null == vin.alt`). (The struct repo's own C# suite binds
305
+ // it the same way.)
306
+ if (input is Dictionary<string, object?> m && m.ContainsKey("alt"))
307
+ return StructUtils.GetProp(val, key, m["alt"]);
308
+ return StructUtils.GetProp(val, key, StructUtils.NONE);
309
+ });
217
310
  }
218
311
 
219
312
  [Fact]
@@ -228,34 +321,28 @@ public class StructUtilityTest
228
321
  [Fact]
229
322
  public void MinorGetElem()
230
323
  {
231
- StructRunner.RunSet(_minor["getelem"],
232
- input =>
233
- {
234
- var m = input as Dictionary<string, object?>;
235
- if (m == null) return null;
236
- object? val = m.TryGetValue("val", out object? v) ? v : null;
237
- object? key = m.TryGetValue("key", out object? k) ? k : null;
238
- if (m.TryGetValue("alt", out object? alt) && alt != null)
239
- return StructUtils.GetElem(val, key, alt);
240
- return StructUtils.GetElem(val, key);
241
- },
242
- flags: new() { ["null"] = false });
324
+ RunStruct("minor", "getelem", false, input =>
325
+ {
326
+ var val = Getp(input, "val");
327
+ var key = Getp(input, "key");
328
+ // Canonical's default alt is `undefined`; see MinorGetProp.
329
+ var alt = Getp(input, "alt");
330
+ return null != alt
331
+ ? StructUtils.GetElem(val, key, alt)
332
+ : StructUtils.GetElem(val, key, StructUtils.NONE);
333
+ });
243
334
  }
244
335
 
245
336
  [Fact]
246
337
  public void MinorSetProp()
247
338
  {
248
- StructRunner.RunSet(_minor["setprop"],
249
- input =>
250
- {
251
- var m = input as Dictionary<string, object?>;
252
- if (m == null) return null;
253
- object? parent = m.TryGetValue("parent", out object? pv) ? pv : null;
254
- object? key = m.TryGetValue("key", out object? kv) ? kv : null;
255
- object? val = m.TryGetValue("val", out object? vv) ? vv : null;
256
- return StructUtils.SetProp(parent, key, val);
257
- },
258
- flags: new() { ["null"] = true });
339
+ RunStruct("minor", "setprop", true, input =>
340
+ {
341
+ var parent = Getp(input, "parent");
342
+ var key = Getp(input, "key");
343
+ var val = Getp(input, "val");
344
+ return StructUtils.SetProp(parent, key, val);
345
+ });
259
346
  }
260
347
 
261
348
  [Fact]
@@ -274,121 +361,25 @@ public class StructUtilityTest
274
361
  [Fact]
275
362
  public void MinorDelProp()
276
363
  {
277
- StructRunner.RunSet(_minor["delprop"],
278
- input =>
279
- {
280
- var m = input as Dictionary<string, object?>;
281
- if (m == null) return null;
282
- object? parent = m.TryGetValue("parent", out object? pv) ? pv : null;
283
- object? key = m.TryGetValue("key", out object? kv) ? kv : null;
284
- return StructUtils.DelProp(parent, key);
285
- },
286
- flags: new() { ["null"] = true });
364
+ RunStruct("minor", "delprop", true, input =>
365
+ {
366
+ var parent = Getp(input, "parent");
367
+ var key = Getp(input, "key");
368
+ return StructUtils.DelProp(parent, key);
369
+ });
287
370
  }
288
371
 
289
372
  [Fact]
290
373
  public void MinorKeysOf()
291
374
  {
292
- StructRunner.RunSet(_minor["keysof"],
293
- input => StructUtils.KeysOf(input));
375
+ RunStruct("minor", "keysof", true, input => StructUtils.KeysOf(input));
294
376
  }
295
377
 
296
378
  [Fact]
297
379
  public void MinorHasKey()
298
380
  {
299
- StructRunner.RunSet(_minor["haskey"],
300
- input =>
301
- {
302
- var m = input as Dictionary<string, object?>;
303
- if (m == null) return false;
304
- object? val = m.TryGetValue("src", out object? v) ? v : null;
305
- object? key = m.TryGetValue("key", out object? k) ? k : null;
306
- return StructUtils.HasKey(val, key);
307
- });
308
- }
309
-
310
-
311
- // ========================================================================
312
- // Sentinels — null / undefined unification across the readers.
313
- // (Group A: a stored JSON null counts as "no value".)
314
- // ========================================================================
315
-
316
- // The sdk-vendored corpus (.sdk/test/test.json) may predate the
317
- // sentinels section; the tests below no-op when it is absent
318
- // (StructRunner.RunSet returns on a null spec).
319
- private Dictionary<string, object?> Sentinels =>
320
- (_spec.TryGetValue("sentinels", out var s) ? s : null)
321
- as Dictionary<string, object?> ?? [];
322
-
323
- [Fact]
324
- public void SentinelsGetPropUnify()
325
- {
326
- StructRunner.RunSet(Sentinels.GetValueOrDefault("getprop_unify"),
327
- input =>
328
- {
329
- var m = input as Dictionary<string, object?>;
330
- if (m == null) return null;
331
- object? val = m.TryGetValue("val", out object? v) ? v : null;
332
- object? key = m.TryGetValue("key", out object? k) ? k : null;
333
- object? alt = m.TryGetValue("alt", out object? a) ? a : null;
334
- return StructUtils.GetProp(val, key, alt);
335
- },
336
- flags: new() { ["null"] = true });
337
- }
338
-
339
- [Fact]
340
- public void SentinelsGetElemAbsent()
341
- {
342
- StructRunner.RunSet(Sentinels.GetValueOrDefault("getelem_absent"),
343
- input =>
344
- {
345
- var m = input as Dictionary<string, object?>;
346
- if (m == null) return null;
347
- object? val = m.TryGetValue("val", out object? v) ? v : null;
348
- object? key = m.TryGetValue("key", out object? k) ? k : null;
349
- object? alt = m.TryGetValue("alt", out object? a) ? a : null;
350
- return StructUtils.GetElem(val, key, alt);
351
- },
352
- flags: new() { ["null"] = true });
353
- }
354
-
355
- [Fact]
356
- public void SentinelsHasKeyUnify()
357
- {
358
- StructRunner.RunSet(Sentinels.GetValueOrDefault("haskey_unify"),
359
- input =>
360
- {
361
- var m = input as Dictionary<string, object?>;
362
- if (m == null) return false;
363
- object? val = m.TryGetValue("val", out object? v) ? v : null;
364
- object? key = m.TryGetValue("key", out object? k) ? k : null;
365
- return StructUtils.HasKey(val, key);
366
- },
367
- flags: new() { ["null"] = true });
368
- }
369
-
370
- [Fact]
371
- public void SentinelsIsEmptyUnify()
372
- {
373
- StructRunner.RunSet(Sentinels.GetValueOrDefault("isempty_unify"),
374
- input => StructUtils.IsEmpty(input),
375
- flags: new() { ["null"] = true });
376
- }
377
-
378
- [Fact]
379
- public void SentinelsIsNodeUnify()
380
- {
381
- StructRunner.RunSet(Sentinels.GetValueOrDefault("isnode_unify"),
382
- input => StructUtils.IsNode(input),
383
- flags: new() { ["null"] = true });
384
- }
385
-
386
- [Fact]
387
- public void SentinelsStringifyNull()
388
- {
389
- StructRunner.RunSet(Sentinels.GetValueOrDefault("stringify_null"),
390
- input => StructUtils.Stringify(input),
391
- flags: new() { ["null"] = true });
381
+ RunStruct("minor", "haskey", false, input =>
382
+ StructUtils.HasKey(Getp(input, "src"), Getp(input, "key")));
392
383
  }
393
384
 
394
385
  [Fact]
@@ -399,64 +390,50 @@ public class StructUtilityTest
399
390
  Assert.Equal("true", StructUtils.Stringify(true));
400
391
  Assert.Equal("hello", StructUtils.Stringify("hello"));
401
392
  // Match TS: NONE (undefined) -> ""; JSON null -> "null".
402
- Assert.Equal(S_MT, StructUtils.Stringify(StructUtils.NONE));
393
+ Assert.Equal("", StructUtils.Stringify(StructUtils.NONE));
403
394
  Assert.Equal("null", StructUtils.Stringify(null));
404
395
  }
405
396
 
406
- private const string S_MT = "";
407
-
408
397
  [Fact]
409
398
  public void MinorTypify()
410
399
  {
411
- StructRunner.RunSet(_minor["typify"],
412
- input => StructUtils.Typify(input));
400
+ RunStruct("minor", "typify", false, input => StructUtils.Typify(input));
413
401
  }
414
402
 
415
403
  [Fact]
416
404
  public void MinorTypeName()
417
405
  {
418
- StructRunner.RunSet(_minor["typename"],
419
- input => StructUtils.TypeName((int)Convert.ToInt64(input)));
406
+ RunStruct("minor", "typename", true, input =>
407
+ StructUtils.TypeName(Convert.ToInt32(input)));
420
408
  }
421
409
 
422
410
  [Fact]
423
411
  public void MinorSize()
424
412
  {
425
- StructRunner.RunSet(_minor["size"],
426
- input => StructUtils.Size(input));
413
+ RunStruct("minor", "size", false, input => StructUtils.Size(input));
427
414
  }
428
415
 
429
416
  [Fact]
430
417
  public void MinorSlice()
431
418
  {
432
- StructRunner.RunSet(_minor["slice"],
433
- input =>
434
- {
435
- var m = input as Dictionary<string, object?>;
436
- if (m == null) return StructUtils.Slice(input);
437
-
438
- object? val = m.TryGetValue("val", out object? v) ? v : null;
439
- int? start = m.TryGetValue("start", out object? sv) && sv != null
440
- ? (int?)Convert.ToInt32(sv) : null;
441
- int? end = m.TryGetValue("end", out object? ev) && ev != null
442
- ? (int?)Convert.ToInt32(ev) : null;
443
- return StructUtils.Slice(val, start, end);
444
- });
419
+ RunStruct("minor", "slice", false, input =>
420
+ {
421
+ var val = Getp(input, "val");
422
+ var start = IntArg(Getp(input, "start"));
423
+ var end = IntArg(Getp(input, "end"));
424
+ return StructUtils.Slice(val, start, end);
425
+ });
445
426
  }
446
427
 
447
428
  [Fact]
448
429
  public void MinorFlatten()
449
430
  {
450
- StructRunner.RunSet(_minor["flatten"],
451
- input =>
452
- {
453
- var m = input as Dictionary<string, object?>;
454
- if (m == null) return StructUtils.Flatten(input as List<object?> ?? []);
455
- var val = m.TryGetValue("val", out object? v) ? v as List<object?> : null;
456
- int depth = m.TryGetValue("depth", out object? d) && d != null
457
- ? (int)Convert.ToInt64(d) : 1;
458
- return StructUtils.Flatten(val ?? [], depth);
459
- });
431
+ RunStruct("minor", "flatten", true, input =>
432
+ {
433
+ var val = Getp(input, "val") as List<object?>;
434
+ var depth = IntArg(Getp(input, "depth"));
435
+ return StructUtils.Flatten(val ?? [], depth ?? 1);
436
+ });
460
437
  }
461
438
 
462
439
  // Named filter predicates used in test.json.
@@ -469,54 +446,123 @@ public class StructUtilityTest
469
446
  [Fact]
470
447
  public void MinorFilter()
471
448
  {
472
- StructRunner.RunSet(_minor["filter"],
473
- input =>
474
- {
475
- var m = input as Dictionary<string, object?>;
476
- if (m == null) return null;
477
- object? val = m.TryGetValue("val", out object? v) ? v : null;
478
- string? chk = m.TryGetValue("check", out object? c) ? c as string : null;
479
- if (chk != null && FilterChecks.TryGetValue(chk, out var check))
480
- return StructUtils.Filter(val, check);
481
- return StructUtils.Filter(val, n => n[1] is string s && s.Length > 0);
482
- });
449
+ RunStruct("minor", "filter", true, input =>
450
+ {
451
+ var val = Getp(input, "val");
452
+ var chk = Getp(input, "check") as string;
453
+ if (chk != null && FilterChecks.TryGetValue(chk, out var check))
454
+ return StructUtils.Filter(val, check);
455
+ return StructUtils.Filter(val, n => n[1] is string s && s.Length > 0);
456
+ });
483
457
  }
484
458
 
485
459
  [Fact]
486
460
  public void MinorPad()
487
461
  {
488
- StructRunner.RunSet(_minor["pad"],
489
- input =>
490
- {
491
- var m = input as Dictionary<string, object?>;
492
- if (m == null) return StructUtils.Pad(input);
493
- object? str = m.TryGetValue("val", out object? sv) ? sv : null;
494
- object? padding = m.TryGetValue("pad", out object? pv) ? pv : null;
495
- // spec uses "char" as key for the pad character
496
- object? padchar = m.TryGetValue("char", out object? cv) ? cv
497
- : m.TryGetValue("padchar", out object? cv2) ? cv2 : null;
462
+ RunStruct("minor", "pad", false, input =>
463
+ {
464
+ var str = Getp(input, "val");
465
+ var padding = Getp(input, "pad");
466
+ // spec uses "char" as key for the pad character
467
+ var padchar = Getp(input, "char") ?? Getp(input, "padchar");
498
468
 
499
- int pad = padding != null ? (int)Convert.ToInt64(padding) : 44;
500
- string? pc = padchar as string;
501
- return StructUtils.Pad(str, pad, pc);
502
- });
469
+ var pad = padding != null ? Convert.ToInt32(padding) : 44;
470
+ return StructUtils.Pad(str, pad, padchar as string);
471
+ });
503
472
  }
504
473
 
505
474
  [Fact]
506
475
  public void MinorJoin()
507
476
  {
508
- StructRunner.RunSet(_minor["join"],
509
- input =>
510
- {
511
- var m = input as Dictionary<string, object?>;
512
- if (m == null) return StructUtils.Join([input]);
513
- object? arr = m.TryGetValue("val", out object? av) ? av : null;
514
- object? sep = m.TryGetValue("sep", out object? sv) ? sv : null;
515
- bool url = m.TryGetValue("url", out object? uv) && uv is bool b && b;
516
- return StructUtils.Join(arr as List<object?> ?? [], sep as string, url);
517
- });
477
+ RunStruct("minor", "join", false, input =>
478
+ {
479
+ if (input is not Dictionary<string, object?>)
480
+ return StructUtils.Join([input]);
481
+ var arr = Getp(input, "val");
482
+ var sep = Getp(input, "sep");
483
+ var url = Getp(input, "url") is bool b && b;
484
+ return StructUtils.Join(arr as List<object?> ?? [], sep as string, url);
485
+ });
486
+ }
487
+
488
+ [Fact]
489
+ public void MinorSetPath()
490
+ {
491
+ RunStruct("minor", "setpath", false, input =>
492
+ {
493
+ var store = Getp(input, "store");
494
+ var path = Getp(input, "path");
495
+ var val = Getp(input, "val");
496
+ return StructUtils.SetPath(store, path, val);
497
+ });
498
+ }
499
+
500
+
501
+ // ========================================================================
502
+ // The struct.nullsem section: does a PRESENT key holding a JSON null
503
+ // read as "no value"? Opt-in per target (create-sdkgen ships it; an
504
+ // older project corpus may predate it - the skip below says so OUT
505
+ // LOUD rather than passing vacuously). All lanes run {null: false}:
506
+ // without the flag the runner rewrites every null to '__NULL__' and
507
+ // the section asserts nothing about null at all.
508
+ // ========================================================================
509
+
510
+ [Fact]
511
+ public void Nullsem()
512
+ {
513
+ var run = StructRun();
514
+ var nullsem = run.Spec.TryGetValue("nullsem", out var ns)
515
+ ? ns as Dictionary<string, object?> : null;
516
+ if (nullsem == null)
517
+ {
518
+ // xUnit has no runtime skip without extra packages; say it loudly.
519
+ Console.Error.WriteLine("SKIP: corpus predates struct.nullsem - " +
520
+ "refresh .sdk/test/struct from create-sdkgen");
521
+ return;
522
+ }
523
+ var flags = new Dictionary<string, bool> { ["null"] = false };
524
+
525
+ run.RunSetFlags(nullsem["getprop"], flags, args =>
526
+ {
527
+ var input = args[0];
528
+ // Canonical's default alt is `undefined` -> NONE; see MinorGetProp.
529
+ if (input is Dictionary<string, object?> m && m.ContainsKey("alt"))
530
+ return StructUtils.GetProp(Getp(input, "val"), Getp(input, "key"), m["alt"]);
531
+ return StructUtils.GetProp(Getp(input, "val"), Getp(input, "key"),
532
+ StructUtils.NONE);
533
+ });
534
+
535
+ run.RunSetFlags(nullsem["getelem"], flags, args =>
536
+ {
537
+ var input = args[0];
538
+ var alt = Getp(input, "alt");
539
+ return null != alt
540
+ ? StructUtils.GetElem(Getp(input, "val"), Getp(input, "key"), alt)
541
+ : StructUtils.GetElem(Getp(input, "val"), Getp(input, "key"),
542
+ StructUtils.NONE);
543
+ });
544
+
545
+ run.RunSetFlags(nullsem["getpath"], flags, args =>
546
+ {
547
+ // The port's GetPath spells "no value" as null - for a plain
548
+ // MISS and for a stored null alike (its alt parameter is not
549
+ // consulted on the path walk), where canonical answers
550
+ // undefined for both. Map that spelling to omni absence at the
551
+ // boundary; the distinction the lane guards (null-as-VALUE vs
552
+ // no-value) stays observable, because a port that read a
553
+ // stored null as a value would fail the getprop/haskey lanes.
554
+ var res = StructUtils.GetPath(Getp(args[0], "store"), Getp(args[0], "path"));
555
+ return res ?? StructUtils.NONE;
556
+ });
557
+
558
+ run.RunSetFlags(nullsem["haskey"], flags, args =>
559
+ StructUtils.HasKey(Getp(args[0], "src"), Getp(args[0], "key")));
560
+
561
+ run.RunSetFlags(nullsem["keysof"], flags, args =>
562
+ StructUtils.KeysOf(args[0]));
518
563
  }
519
564
 
565
+
520
566
  // ========================================================================
521
567
  // Walk tests
522
568
  // ========================================================================
@@ -528,28 +574,24 @@ public class StructUtilityTest
528
574
  WalkApply walkpath = (key, val, parent, path) =>
529
575
  {
530
576
  if (val is string s)
531
- return s + "~" + string.Join(".", path.Cast<string>());
577
+ return s + "~" + string.Join(".", path.Select(p => p?.ToString()));
532
578
  return val;
533
579
  };
534
580
 
535
- StructRunner.RunSet(_spec["walk"] is Dictionary<string, object?> ws ? ws["basic"] : null,
536
- input => StructUtils.Walk(input, walkpath));
581
+ RunStruct("walk", "basic", true, input => StructUtils.Walk(input, walkpath));
537
582
  }
538
583
 
539
584
  [Fact]
540
585
  public void WalkLog()
541
586
  {
542
- var walkSpec = _spec["walk"] as Dictionary<string, object?>;
543
- var logSpec = walkSpec?["log"] as Dictionary<string, object?>;
544
- if (logSpec == null) return;
545
-
546
- var testIn = StructUtils.Clone(logSpec["in"]);
587
+ var logSpec = Section("walk", "log");
588
+ var testIn = StructUtils.Clone(logSpec["in"]);
547
589
  var outSpec = logSpec["out"] as Dictionary<string, object?>;
548
590
 
549
591
  WalkApply makeWalkLog(List<object?> log)
550
592
  {
551
593
  // TS Stringify(undefined)="" but Stringify(null)="null". The C#
552
- // walk passes null at root for both key and parent render those
594
+ // walk passes null at root for both key and parent - render those
553
595
  // as empty string to match the corpus log format ("p=", "k=").
554
596
  string Render(object? v) =>
555
597
  v == null ? "" : StructUtils.Stringify(v);
@@ -590,84 +632,74 @@ public class StructUtilityTest
590
632
  [Fact]
591
633
  public void WalkDepth()
592
634
  {
593
- var walkSpec = _spec["walk"] as Dictionary<string, object?>;
594
- StructRunner.RunSet(walkSpec?["depth"],
595
- input =>
596
- {
597
- var m = input as Dictionary<string, object?>;
598
- if (m == null) return null;
599
-
600
- object? src = m.TryGetValue("src", out object? sv) ? sv : null;
601
- int? md = m.TryGetValue("maxdepth", out object? dv) && dv != null
602
- ? (int?)Convert.ToInt32(dv) : null;
635
+ RunStruct("walk", "depth", false, input =>
636
+ {
637
+ var src = Getp(input, "src");
638
+ var md = IntArg(Getp(input, "maxdepth"));
603
639
 
604
- // Build a copy using a single current-node pointer (matches Go walk-depth test).
605
- object? top = null;
606
- object? cur = null;
640
+ // Build a copy using a single current-node pointer (matches the
641
+ // go walk-depth test).
642
+ object? top = null;
643
+ object? cur = null;
607
644
 
608
- WalkApply copy = (key, val, parent, path) =>
645
+ WalkApply copy = (key, val, parent, path) =>
646
+ {
647
+ if (StructUtils.IsNode(val))
609
648
  {
610
- if (StructUtils.IsNode(val))
611
- {
612
- object? child = StructUtils.IsList(val)
613
- ? (object?)new List<object?>()
614
- : new Dictionary<string, object?>();
615
- if (key == null) { top = child; cur = child; }
616
- else { StructUtils.SetProp(cur, key, child); cur = child; }
617
- }
618
- else if (key != null)
619
- StructUtils.SetProp(cur, key, val);
620
- return val;
621
- };
649
+ object? child = StructUtils.IsList(val)
650
+ ? (object?)new List<object?>()
651
+ : new Dictionary<string, object?>();
652
+ if (key == null) { top = child; cur = child; }
653
+ else { StructUtils.SetProp(cur, key, child); cur = child; }
654
+ }
655
+ else if (key != null)
656
+ StructUtils.SetProp(cur, key, val);
657
+ return val;
658
+ };
622
659
 
623
- StructUtils.Walk(src, copy, null, md);
624
- return top;
625
- },
626
- flags: new() { ["null"] = false });
660
+ StructUtils.Walk(src, copy, null, md);
661
+ return top;
662
+ });
627
663
  }
628
664
 
629
665
  [Fact]
630
666
  public void WalkCopy()
631
667
  {
632
- var walkSpec = _spec["walk"] as Dictionary<string, object?>;
633
- StructRunner.RunSet(walkSpec?["copy"],
634
- input =>
635
- {
636
- var cur = new object?[MAXDEPTH + 1];
668
+ RunStruct("walk", "copy", true, input =>
669
+ {
670
+ var cur = new object?[StructUtils.MAXDEPTH + 1];
637
671
 
638
- WalkApply walkcopy = (key, val, parent, path) =>
672
+ WalkApply walkcopy = (key, val, parent, path) =>
673
+ {
674
+ if (key == null) // root
639
675
  {
640
- if (key == null) // root
641
- {
642
- cur[0] = StructUtils.IsMap(val)
643
- ? (object?)new Dictionary<string, object?>()
644
- : StructUtils.IsList(val)
645
- ? new List<object?>()
646
- : val;
647
- return val;
648
- }
649
-
650
- int i = path.Count;
651
- object? v = val;
652
-
653
- if (StructUtils.IsNode(val))
654
- {
655
- cur[i] = StructUtils.IsMap(val)
656
- ? (object?)new Dictionary<string, object?>()
657
- : new List<object?>();
658
- v = cur[i];
659
- }
660
-
661
- cur[i - 1] = StructUtils.SetProp(cur[i - 1], key, v) ?? cur[i - 1];
676
+ cur[0] = StructUtils.IsMap(val)
677
+ ? (object?)new Dictionary<string, object?>()
678
+ : StructUtils.IsList(val)
679
+ ? new List<object?>()
680
+ : val;
662
681
  return val;
663
- };
682
+ }
664
683
 
665
- StructUtils.Walk(input, walkcopy);
666
- return cur[0];
667
- });
668
- }
684
+ int i = path.Count;
685
+ object? v = val;
686
+
687
+ if (StructUtils.IsNode(val))
688
+ {
689
+ cur[i] = StructUtils.IsMap(val)
690
+ ? (object?)new Dictionary<string, object?>()
691
+ : new List<object?>();
692
+ v = cur[i];
693
+ }
694
+
695
+ cur[i - 1] = StructUtils.SetProp(cur[i - 1], key, v) ?? cur[i - 1];
696
+ return val;
697
+ };
669
698
 
670
- private const int MAXDEPTH = StructUtils.MAXDEPTH;
699
+ StructUtils.Walk(input, walkcopy);
700
+ return cur[0];
701
+ });
702
+ }
671
703
 
672
704
 
673
705
  // ========================================================================
@@ -677,10 +709,7 @@ public class StructUtilityTest
677
709
  [Fact]
678
710
  public void MergeBasic()
679
711
  {
680
- var mergeSpec = _spec["merge"] as Dictionary<string, object?>;
681
- var basic = mergeSpec?["basic"] as Dictionary<string, object?>;
682
- if (basic == null) return;
683
-
712
+ var basic = Section("merge", "basic");
684
713
  object? result = StructUtils.Merge(basic["in"]);
685
714
  Assert.True(StructRunner.DeepEqual(basic["out"], result),
686
715
  $"merge-basic: expected {StructUtils.Stringify(basic["out"])} but got {StructUtils.Stringify(result)}");
@@ -689,42 +718,30 @@ public class StructUtilityTest
689
718
  [Fact]
690
719
  public void MergeCases()
691
720
  {
692
- var mergeSpec = _spec["merge"] as Dictionary<string, object?>;
693
- StructRunner.RunSet(mergeSpec?["cases"],
694
- input => StructUtils.Merge(input));
721
+ RunStruct("merge", "cases", true, input => StructUtils.Merge(input));
695
722
  }
696
723
 
697
724
  [Fact]
698
725
  public void MergeArray()
699
726
  {
700
- var mergeSpec = _spec["merge"] as Dictionary<string, object?>;
701
- StructRunner.RunSet(mergeSpec?["array"],
702
- input => StructUtils.Merge(input));
727
+ RunStruct("merge", "array", true, input => StructUtils.Merge(input));
703
728
  }
704
729
 
705
730
  [Fact]
706
731
  public void MergeIntegrity()
707
732
  {
708
- var mergeSpec = _spec["merge"] as Dictionary<string, object?>;
709
- StructRunner.RunSet(mergeSpec?["integrity"],
710
- input => StructUtils.Merge(input));
733
+ RunStruct("merge", "integrity", true, input => StructUtils.Merge(input));
711
734
  }
712
735
 
713
736
  [Fact]
714
737
  public void MergeDepth()
715
738
  {
716
- var mergeSpec = _spec["merge"] as Dictionary<string, object?>;
717
- StructRunner.RunSet(mergeSpec?["depth"],
718
- input =>
719
- {
720
- var m = input as Dictionary<string, object?>;
721
- if (m == null) return StructUtils.Merge(input);
722
-
723
- object? val = m.TryGetValue("val", out object? v) ? v : null;
724
- int? depth = m.TryGetValue("depth", out object? d) && d != null
725
- ? (int?)Convert.ToInt32(d) : null;
726
- return StructUtils.Merge(val, depth);
727
- });
739
+ RunStruct("merge", "depth", true, input =>
740
+ {
741
+ var val = Getp(input, "val");
742
+ var depth = IntArg(Getp(input, "depth"));
743
+ return StructUtils.Merge(val, depth);
744
+ });
728
745
  }
729
746
 
730
747
  [Fact]
@@ -750,26 +767,80 @@ public class StructUtilityTest
750
767
  Assert.Equal(f0(), fr2!());
751
768
  }
752
769
 
770
+
771
+ // ========================================================================
772
+ // GetPath tests
773
+ // ========================================================================
774
+
775
+ [Fact]
776
+ public void GetpathExists()
777
+ {
778
+ Delegate fn = (Func<object?, object?, object?, InjectState?, object?>)StructUtils.GetPath;
779
+ Assert.NotNull(fn);
780
+ }
781
+
753
782
  [Fact]
754
- public void MinorSetPath()
783
+ public void GetpathBasic()
784
+ {
785
+ RunStruct("getpath", "basic", true, input =>
786
+ StructUtils.GetPath(Getp(input, "store"), Getp(input, "path")));
787
+ }
788
+
789
+ [Fact]
790
+ public void GetpathRelative()
791
+ {
792
+ RunStruct("getpath", "relative", true, input =>
793
+ {
794
+ var state = new InjectState { DParent = Getp(input, "dparent") };
795
+
796
+ if (Getp(input, "dpath") is string dpathStr && dpathStr.Length > 0)
797
+ state.DPath = dpathStr.Split('.').Cast<object?>().ToList();
798
+
799
+ return StructUtils.GetPath(Getp(input, "store"), Getp(input, "path"),
800
+ null, state);
801
+ });
802
+ }
803
+
804
+ [Fact]
805
+ public void GetpathSpecial()
755
806
  {
756
- StructRunner.RunSet(_minor["setpath"],
757
- input =>
807
+ RunStruct("getpath", "special", true, input =>
808
+ {
809
+ InjectState? state = null;
810
+ if (Getp(input, "inj") is Dictionary<string, object?> injMap)
758
811
  {
759
- var m = input as Dictionary<string, object?>;
760
- if (m == null) return null;
761
- object? store = m.TryGetValue("store", out object? sv) ? sv : null;
762
- object? path = m.TryGetValue("path", out object? pv) ? pv : null;
763
- object? val = m.TryGetValue("val", out object? vv) ? vv : null;
764
- return StructUtils.SetPath(store, path, val);
765
- },
766
- flags: new() { ["null"] = true });
812
+ state = new InjectState();
813
+ if (injMap.TryGetValue("key", out var kv) && kv != null)
814
+ state.Key = StructUtils.Stringify(kv);
815
+ if (injMap.TryGetValue("meta", out var mv) &&
816
+ mv is Dictionary<string, object?> metaMap)
817
+ state.Meta = metaMap;
818
+ }
819
+
820
+ return StructUtils.GetPath(Getp(input, "store"), Getp(input, "path"),
821
+ null, state);
822
+ });
767
823
  }
768
824
 
825
+ [Fact]
826
+ public void GetpathHandler()
827
+ {
828
+ // Handler that turns any ref lookup into "<ref>" (e.g. "$FOO" → "foo").
829
+ var refMap = new Dictionary<string, object?> { ["$FOO"] = "foo" };
830
+
831
+ RunStruct("getpath", "handler", true, input =>
832
+ {
833
+ var state = new InjectState
834
+ {
835
+ Handler = (inj, val, refStr, st) =>
836
+ refStr != null && refMap.TryGetValue(refStr, out var mapped)
837
+ ? mapped : val,
838
+ };
839
+ return StructUtils.GetPath(Getp(input, "store"), Getp(input, "path"),
840
+ null, state);
841
+ });
842
+ }
769
843
 
770
- // ========================================================================
771
- // GetPath tests
772
- // ========================================================================
773
844
 
774
845
  // ========================================================================
775
846
  // Inject tests
@@ -785,15 +856,12 @@ public class StructUtilityTest
785
856
  [Fact]
786
857
  public void InjectBasic()
787
858
  {
788
- var injectSpec = _spec["inject"] as Dictionary<string, object?>;
789
- var basic = injectSpec?["basic"] as Dictionary<string, object?>;
790
- if (basic == null) return;
791
-
859
+ var basic = Section("inject", "basic");
792
860
  var inVal = basic["in"] as Dictionary<string, object?>;
793
- if (inVal == null) return;
794
- object? val = inVal.TryGetValue("val", out object? v) ? v : null;
795
- object? store = inVal.TryGetValue("store", out object? s) ? s : null;
796
- object? expected = basic["out"];
861
+ Assert.NotNull(inVal);
862
+ object? val = inVal!.TryGetValue("val", out var v) ? v : null;
863
+ object? store = inVal.TryGetValue("store", out var s) ? s : null;
864
+ object? expected = basic.TryGetValue("out", out var o) ? o : null;
797
865
 
798
866
  object? result = StructUtils.Inject(val, store);
799
867
  Assert.True(StructRunner.DeepEqual(expected, result),
@@ -803,32 +871,20 @@ public class StructUtilityTest
803
871
  [Fact]
804
872
  public void InjectString()
805
873
  {
806
- var injectSpec = _spec["inject"] as Dictionary<string, object?>;
807
- StructRunner.RunSet(injectSpec?["string"],
808
- input =>
809
- {
810
- var m = input as Dictionary<string, object?>;
811
- if (m == null) return null;
812
- object? val = m.TryGetValue("val", out object? v) ? v : null;
813
- object? store = m.TryGetValue("store", out object? s) ? s : null;
814
- return StructUtils.Inject(val, store);
815
- },
816
- flags: new() { ["null"] = true });
874
+ // The nullModifier renders a resolved JSON null (encoded by the
875
+ // runner as "__NULL__") as the literal text "null".
876
+ RunStruct("inject", "string", true, input =>
877
+ {
878
+ var state = new InjectState { ModifyFn = NullModifier };
879
+ return StructUtils.Inject(Getp(input, "val"), Getp(input, "store"), state);
880
+ });
817
881
  }
818
882
 
819
883
  [Fact]
820
884
  public void InjectDeep()
821
885
  {
822
- var injectSpec = _spec["inject"] as Dictionary<string, object?>;
823
- StructRunner.RunSet(injectSpec?["deep"],
824
- input =>
825
- {
826
- var m = input as Dictionary<string, object?>;
827
- if (m == null) return null;
828
- object? val = m.TryGetValue("val", out object? v) ? v : null;
829
- object? store = m.TryGetValue("store", out object? s) ? s : null;
830
- return StructUtils.Inject(val, store);
831
- });
886
+ RunStruct("inject", "deep", true, input =>
887
+ StructUtils.Inject(Getp(input, "val"), Getp(input, "store")));
832
888
  }
833
889
 
834
890
 
@@ -846,15 +902,12 @@ public class StructUtilityTest
846
902
  [Fact]
847
903
  public void TransformBasic()
848
904
  {
849
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
850
- var basic = tSpec?["basic"] as Dictionary<string, object?>;
851
- if (basic == null) return;
852
-
853
- var inVal = basic["in"] as Dictionary<string, object?>;
854
- if (inVal == null) return;
855
- object? data = inVal.TryGetValue("data", out object? d) ? d : null;
856
- object? spec = inVal.TryGetValue("spec", out object? s) ? s : null;
857
- object? expected = basic["out"];
905
+ var basic = Section("transform", "basic");
906
+ var inVal = basic["in"] as Dictionary<string, object?>;
907
+ Assert.NotNull(inVal);
908
+ object? data = inVal!.TryGetValue("data", out var d) ? d : null;
909
+ object? spec = inVal.TryGetValue("spec", out var s) ? s : null;
910
+ object? expected = basic.TryGetValue("out", out var o) ? o : null;
858
911
 
859
912
  object? result = StructUtils.Transform(data, spec);
860
913
  Assert.True(StructRunner.DeepEqual(expected, result),
@@ -865,262 +918,73 @@ public class StructUtilityTest
865
918
  [Fact]
866
919
  public void TransformPaths()
867
920
  {
868
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
869
- StructRunner.RunSet(tSpec?["paths"], input =>
870
- {
871
- var m = input as Dictionary<string, object?>;
872
- if (m == null) return null;
873
- object? data = m.TryGetValue("data", out object? d) ? d : null;
874
- object? spec = m.TryGetValue("spec", out object? s) ? s : null;
875
- return StructUtils.Transform(data, spec);
876
- });
921
+ RunStruct("transform", "paths", true, input =>
922
+ StructUtils.Transform(Getp(input, "data"), Getp(input, "spec")));
877
923
  }
878
924
 
879
925
  [Fact]
880
926
  public void TransformCmds()
881
927
  {
882
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
883
- StructRunner.RunSet(tSpec?["cmds"], input =>
884
- {
885
- var m = input as Dictionary<string, object?>;
886
- if (m == null) return null;
887
- object? data = m.TryGetValue("data", out object? d) ? d : null;
888
- object? spec = m.TryGetValue("spec", out object? s) ? s : null;
889
- return StructUtils.Transform(data, spec);
890
- });
928
+ RunStruct("transform", "cmds", true, input =>
929
+ StructUtils.Transform(Getp(input, "data"), Getp(input, "spec")));
891
930
  }
892
931
 
893
932
  [Fact]
894
933
  public void TransformEach()
895
934
  {
896
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
897
- StructRunner.RunSet(tSpec?["each"], input =>
898
- {
899
- var m = input as Dictionary<string, object?>;
900
- if (m == null) return null;
901
- object? data = m.TryGetValue("data", out object? d) ? d : null;
902
- object? spec = m.TryGetValue("spec", out object? s) ? s : null;
903
- return StructUtils.Transform(data, spec);
904
- });
935
+ RunStruct("transform", "each", true, input =>
936
+ StructUtils.Transform(Getp(input, "data"), Getp(input, "spec")));
905
937
  }
906
938
 
907
939
  [Fact]
908
940
  public void TransformPack()
909
941
  {
910
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
911
- StructRunner.RunSet(tSpec?["pack"], input =>
912
- {
913
- var m = input as Dictionary<string, object?>;
914
- if (m == null) return null;
915
- object? data = m.TryGetValue("data", out object? d) ? d : null;
916
- object? spec = m.TryGetValue("spec", out object? s) ? s : null;
917
- return StructUtils.Transform(data, spec);
918
- });
942
+ RunStruct("transform", "pack", true, input =>
943
+ StructUtils.Transform(Getp(input, "data"), Getp(input, "spec")));
919
944
  }
920
945
 
921
946
  [Fact]
922
947
  public void TransformRef()
923
948
  {
924
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
925
- StructRunner.RunSet(tSpec?["ref"], input =>
926
- {
927
- var m = input as Dictionary<string, object?>;
928
- if (m == null) return null;
929
- object? data = m.TryGetValue("data", out object? d) ? d : null;
930
- object? spec = m.TryGetValue("spec", out object? s) ? s : null;
931
- return StructUtils.Transform(data, spec);
932
- });
949
+ RunStruct("transform", "ref", true, input =>
950
+ StructUtils.Transform(Getp(input, "data"), Getp(input, "spec")));
933
951
  }
934
952
 
935
953
  [Fact]
936
954
  public void TransformFormat()
937
955
  {
938
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
939
- StructRunner.RunSet(tSpec?["format"], input =>
940
- {
941
- var m = input as Dictionary<string, object?>;
942
- if (m == null) return null;
943
- object? data = m.TryGetValue("data", out object? d) ? d : null;
944
- object? spec = m.TryGetValue("spec", out object? s) ? s : null;
945
- return StructUtils.Transform(data, spec);
946
- }, flags: new() { ["null"] = true });
956
+ RunStruct("transform", "format", false, input =>
957
+ StructUtils.Transform(Getp(input, "data"), Getp(input, "spec")));
947
958
  }
948
959
 
949
960
  [Fact]
950
961
  public void TransformModify()
951
962
  {
952
- var tSpec = _spec["transform"] as Dictionary<string, object?>;
953
- var modSet = tSpec?["modify"] as Dictionary<string, object?>;
954
- if (modSet == null) return;
955
-
956
- var set = modSet.TryGetValue("set", out object? sv) ? sv as List<object?> : null;
957
- if (set == null || set.Count == 0) return;
958
-
959
- var entry = set[0] as Dictionary<string, object?>;
960
- if (entry == null || !entry.ContainsKey("out")) return;
961
-
962
- var inVal = entry["in"] as Dictionary<string, object?>;
963
- if (inVal == null) return;
964
-
965
- object? data = inVal.TryGetValue("data", out object? d) ? d : null;
966
- object? spec = inVal.TryGetValue("spec", out object? s) ? s : null;
967
- object? expected = entry["out"];
968
-
969
- // Modify callback that prefixes every injected string value with "@".
970
- Modify myModify = (val, key, parent, inj, store) =>
971
- {
972
- if (val is string sv2 && sv2.Length > 0)
973
- StructUtils.SetProp(parent, key, "@" + sv2);
974
- return val;
975
- };
976
-
977
- var injState = new InjectState { ModifyFn = myModify };
978
- object? result = StructUtils.Transform(data, spec, injState);
979
-
980
- Assert.True(StructRunner.DeepEqual(expected, result),
981
- $"transform-modify: expected {StructUtils.Stringify(expected)} " +
982
- $"but got {StructUtils.Stringify(result)}");
983
- }
984
-
985
- // ========================================================================
986
- // GetPath tests
987
- // ========================================================================
988
-
989
- [Fact]
990
- public void GetpathExists()
991
- {
992
- Delegate fn = (Func<object?, object?, object?, InjectState?, object?>)StructUtils.GetPath;
993
- Assert.NotNull(fn);
994
- }
995
-
996
- [Fact]
997
- public void GetpathBasic()
998
- {
999
- var getpathSpec = _spec["getpath"] as Dictionary<string, object?>;
1000
- StructRunner.RunSet(getpathSpec?["basic"],
1001
- input =>
1002
- {
1003
- var m = input as Dictionary<string, object?>;
1004
- if (m == null) return null;
1005
- object? path = m.TryGetValue("path", out object? pv) ? pv : null;
1006
- object? store = m.TryGetValue("store", out object? sv) ? sv : null;
1007
- return StructUtils.GetPath(store, path);
1008
- });
1009
- }
1010
-
1011
- [Fact]
1012
- public void GetpathRelative()
1013
- {
1014
- var getpathSpec = _spec["getpath"] as Dictionary<string, object?>;
1015
- StructRunner.RunSet(getpathSpec?["relative"],
1016
- input =>
1017
- {
1018
- var m = input as Dictionary<string, object?>;
1019
- if (m == null) return null;
1020
- object? path = m.TryGetValue("path", out object? pv) ? pv : null;
1021
- object? store = m.TryGetValue("store", out object? sv) ? sv : null;
1022
- object? dparent = m.TryGetValue("dparent", out object? dv) ? dv : null;
1023
-
1024
- var state = new InjectState { DParent = dparent };
1025
-
1026
- if (m.TryGetValue("dpath", out object? dpv) && dpv is string dpathStr && dpathStr.Length > 0)
1027
- state.DPath = dpathStr.Split('.').Cast<object?>().ToList();
1028
-
1029
- return StructUtils.GetPath(store, path, null, state);
1030
- });
1031
- }
1032
-
1033
- [Fact]
1034
- public void GetpathSpecial()
1035
- {
1036
- var getpathSpec = _spec["getpath"] as Dictionary<string, object?>;
1037
- StructRunner.RunSet(getpathSpec?["special"],
1038
- input =>
1039
- {
1040
- var m = input as Dictionary<string, object?>;
1041
- if (m == null) return null;
1042
- object? path = m.TryGetValue("path", out object? pv) ? pv : null;
1043
- object? store = m.TryGetValue("store", out object? sv) ? sv : null;
1044
-
1045
- InjectState? state = null;
1046
- if (m.TryGetValue("inj", out object? injv) && injv is Dictionary<string, object?> injMap)
1047
- {
1048
- state = new InjectState();
1049
- if (injMap.TryGetValue("key", out object? kv) && kv != null)
1050
- state.Key = StructUtils.Stringify(kv);
1051
- if (injMap.TryGetValue("meta", out object? mv) && mv is Dictionary<string, object?> metaMap)
1052
- state.Meta = metaMap;
1053
- }
1054
-
1055
- return StructUtils.GetPath(store, path, null, state);
1056
- });
1057
- }
1058
-
1059
- [Fact]
1060
- public void GetpathHandler()
1061
- {
1062
- var getpathSpec = _spec["getpath"] as Dictionary<string, object?>;
1063
- var handlerSpec = getpathSpec?["handler"] as Dictionary<string, object?>;
1064
- if (handlerSpec == null) return;
1065
-
1066
- // Handler that turns any ref lookup into "<ref>" (e.g. "$FOO" → "foo").
1067
- var refMap = new Dictionary<string, object?> { ["$FOO"] = "foo" };
1068
-
1069
- var state = new InjectState
963
+ RunStruct("transform", "modify", true, input =>
1070
964
  {
1071
- Handler = (inj, val, refStr, st) =>
1072
- refStr != null && refMap.TryGetValue(refStr, out object? mapped) ? mapped : val,
1073
- };
1074
-
1075
- StructRunner.RunSet(handlerSpec,
1076
- input =>
965
+ // Match the JS test guard: only mutate string leaves.
966
+ Modify myModify = (val, key, parent, inj, store) =>
1077
967
  {
1078
- var m = input as Dictionary<string, object?>;
1079
- if (m == null) return null;
1080
- object? path = m.TryGetValue("path", out object? pv) ? pv : null;
1081
- object? store = m.TryGetValue("store", out object? sv) ? sv : null;
1082
- return StructUtils.GetPath(store, path, null, state);
1083
- });
1084
- }
1085
-
1086
- // ── minor.jsonify ────────────────────────────────────────────────────────
1087
-
1088
- [Fact]
1089
- public void MinorJsonify()
1090
- {
1091
- var jsonifySpec = _minor["jsonify"] as Dictionary<string, object?>;
1092
- StructRunner.RunSet(jsonifySpec,
1093
- input =>
1094
- {
1095
- var m = input as Dictionary<string, object?>;
1096
- if (m == null) return null;
1097
- object? val = m.TryGetValue("val", out object? vv) ? vv : null;
1098
- if (m.TryGetValue("flags", out object? fv) && fv is Dictionary<string, object?> flags)
1099
- {
1100
- int indent = flags.TryGetValue("indent", out object? iv) && iv is long li ? (int)li : 2;
1101
- int offset = flags.TryGetValue("offset", out object? ov) && ov is long lo ? (int)lo : 0;
1102
- return StructUtils.Jsonify(val, indent, offset);
1103
- }
1104
- return StructUtils.Jsonify(val);
1105
- }, new() { ["null"] = true });
968
+ if (key != null && val is string s && s.Length > 0)
969
+ StructUtils.SetProp(parent, key, "@" + s);
970
+ return val;
971
+ };
972
+ var state = new InjectState { ModifyFn = myModify };
973
+ return StructUtils.Transform(Getp(input, "data"), Getp(input, "spec"), state);
974
+ });
1106
975
  }
1107
976
 
1108
- // ── transform.apply ──────────────────────────────────────────────────────
1109
-
1110
977
  [Fact]
1111
978
  public void TransformApply()
1112
979
  {
1113
- var applySpec = (_spec["transform"] as Dictionary<string,object?>)?["apply"] as Dictionary<string, object?>;
1114
- StructRunner.RunSetFull(applySpec,
1115
- input =>
1116
- {
1117
- object? data = input?.GetValueOrDefault("data");
1118
- object? spec = input?.GetValueOrDefault("spec");
1119
- return StructUtils.Transform(data, spec);
1120
- }, new() { ["null"] = true });
980
+ RunStruct("transform", "apply", true, input =>
981
+ StructUtils.Transform(Getp(input, "data"), Getp(input, "spec")));
1121
982
  }
1122
983
 
1123
- // ── validate ─────────────────────────────────────────────────────────────
984
+
985
+ // ========================================================================
986
+ // Validate tests
987
+ // ========================================================================
1124
988
 
1125
989
  [Fact]
1126
990
  public void ValidateExists()
@@ -1131,83 +995,58 @@ public class StructUtilityTest
1131
995
  [Fact]
1132
996
  public void ValidateBasic()
1133
997
  {
1134
- var validateSpec = (_spec["validate"] as Dictionary<string,object?>)?["basic"] as Dictionary<string, object?>;
1135
- StructRunner.RunSetFull(validateSpec, input =>
1136
- {
1137
- object? data = input?.GetValueOrDefault("data");
1138
- object? spec = input?.GetValueOrDefault("spec");
1139
- return StructUtils.Validate(data, spec);
1140
- }, new() { ["null"] = true });
998
+ RunStruct("validate", "basic", false, input =>
999
+ StructUtils.Validate(Getp(input, "data"), Getp(input, "spec")));
1141
1000
  }
1142
1001
 
1143
1002
  [Fact]
1144
1003
  public void ValidateChild()
1145
1004
  {
1146
- var validateSpec = (_spec["validate"] as Dictionary<string,object?>)?["child"] as Dictionary<string, object?>;
1147
- StructRunner.RunSetFull(validateSpec, input =>
1148
- {
1149
- object? data = input?.GetValueOrDefault("data");
1150
- object? spec = input?.GetValueOrDefault("spec");
1151
- return StructUtils.Validate(data, spec);
1152
- }, new() { ["null"] = true });
1005
+ RunStruct("validate", "child", true, input =>
1006
+ StructUtils.Validate(Getp(input, "data"), Getp(input, "spec")));
1153
1007
  }
1154
1008
 
1155
1009
  [Fact]
1156
1010
  public void ValidateOne()
1157
1011
  {
1158
- var validateSpec = (_spec["validate"] as Dictionary<string,object?>)?["one"] as Dictionary<string, object?>;
1159
- StructRunner.RunSetFull(validateSpec, input =>
1160
- {
1161
- object? data = input?.GetValueOrDefault("data");
1162
- object? spec = input?.GetValueOrDefault("spec");
1163
- return StructUtils.Validate(data, spec);
1164
- }, new() { ["null"] = true });
1012
+ RunStruct("validate", "one", true, input =>
1013
+ StructUtils.Validate(Getp(input, "data"), Getp(input, "spec")));
1165
1014
  }
1166
1015
 
1167
1016
  [Fact]
1168
1017
  public void ValidateExact()
1169
1018
  {
1170
- var validateSpec = (_spec["validate"] as Dictionary<string,object?>)?["exact"] as Dictionary<string, object?>;
1171
- StructRunner.RunSetFull(validateSpec, input =>
1172
- {
1173
- object? data = input?.GetValueOrDefault("data");
1174
- object? spec = input?.GetValueOrDefault("spec");
1175
- return StructUtils.Validate(data, spec);
1176
- }, new() { ["null"] = true });
1019
+ RunStruct("validate", "exact", true, input =>
1020
+ StructUtils.Validate(Getp(input, "data"), Getp(input, "spec")));
1177
1021
  }
1178
1022
 
1179
1023
  [Fact]
1180
1024
  public void ValidateInvalid()
1181
1025
  {
1182
- var validateSpec = (_spec["validate"] as Dictionary<string,object?>)?["invalid"] as Dictionary<string, object?>;
1183
- StructRunner.RunSetFull(validateSpec, input =>
1184
- {
1185
- object? data = input?.GetValueOrDefault("data");
1186
- object? spec = input?.GetValueOrDefault("spec");
1187
- return StructUtils.Validate(data, spec);
1188
- }, new() { ["null"] = true });
1026
+ RunStruct("validate", "invalid", false, input =>
1027
+ StructUtils.Validate(Getp(input, "data"), Getp(input, "spec")));
1189
1028
  }
1190
1029
 
1191
1030
  [Fact]
1192
1031
  public void ValidateSpecial()
1193
1032
  {
1194
- var validateSpec = (_spec["validate"] as Dictionary<string,object?>)?["special"] as Dictionary<string, object?>;
1195
- StructRunner.RunSetFull(validateSpec, input =>
1033
+ RunStruct("validate", "special", true, input =>
1196
1034
  {
1197
- object? data = input?.GetValueOrDefault("data");
1198
- object? spec = input?.GetValueOrDefault("spec");
1199
- var injEntry = input?.GetValueOrDefault("inj") as Dictionary<string, object?>;
1200
- var injdef = new InjectState();
1201
- if (injEntry != null)
1035
+ var injdef = new InjectState();
1036
+ if (Getp(input, "inj") is Dictionary<string, object?> injEntry &&
1037
+ injEntry.TryGetValue("meta", out var mv) &&
1038
+ mv is Dictionary<string, object?> meta)
1202
1039
  {
1203
- var meta = injEntry.GetValueOrDefault("meta") as Dictionary<string, object?>;
1204
- if (meta != null) injdef.Meta = meta;
1040
+ injdef.Meta = meta;
1205
1041
  }
1206
- return StructUtils.Validate(data, spec, injdef);
1207
- }, new() { ["null"] = true });
1042
+ return StructUtils.Validate(Getp(input, "data"), Getp(input, "spec"), injdef);
1043
+ });
1208
1044
  }
1209
1045
 
1210
- // ── select ───────────────────────────────────────────────────────────────
1046
+
1047
+ // ========================================================================
1048
+ // Select tests
1049
+ // ========================================================================
1211
1050
 
1212
1051
  [Fact]
1213
1052
  public void SelectExists()
@@ -1218,56 +1057,28 @@ public class StructUtilityTest
1218
1057
  [Fact]
1219
1058
  public void SelectBasic()
1220
1059
  {
1221
- var selectSpec = (_spec["select"] as Dictionary<string,object?>)?["basic"] as Dictionary<string, object?>;
1222
- StructRunner.RunSet(selectSpec,
1223
- input =>
1224
- {
1225
- var m = input as Dictionary<string, object?>;
1226
- object? query = m?.GetValueOrDefault("query");
1227
- object? obj = m?.GetValueOrDefault("obj");
1228
- return (object?)StructUtils.Select(obj, query);
1229
- }, new() { ["null"] = true });
1060
+ RunStruct("select", "basic", true, input =>
1061
+ StructUtils.Select(Getp(input, "obj"), Getp(input, "query")));
1230
1062
  }
1231
1063
 
1232
1064
  [Fact]
1233
1065
  public void SelectOperators()
1234
1066
  {
1235
- var selectSpec = (_spec["select"] as Dictionary<string,object?>)?["operators"] as Dictionary<string, object?>;
1236
- StructRunner.RunSet(selectSpec,
1237
- input =>
1238
- {
1239
- var m = input as Dictionary<string, object?>;
1240
- object? query = m?.GetValueOrDefault("query");
1241
- object? obj = m?.GetValueOrDefault("obj");
1242
- return (object?)StructUtils.Select(obj, query);
1243
- }, new() { ["null"] = true });
1067
+ RunStruct("select", "operators", true, input =>
1068
+ StructUtils.Select(Getp(input, "obj"), Getp(input, "query")));
1244
1069
  }
1245
1070
 
1246
1071
  [Fact]
1247
1072
  public void SelectEdge()
1248
1073
  {
1249
- var selectSpec = (_spec["select"] as Dictionary<string,object?>)?["edge"] as Dictionary<string, object?>;
1250
- StructRunner.RunSet(selectSpec,
1251
- input =>
1252
- {
1253
- var m = input as Dictionary<string, object?>;
1254
- object? query = m?.GetValueOrDefault("query");
1255
- object? obj = m?.GetValueOrDefault("obj");
1256
- return (object?)StructUtils.Select(obj, query);
1257
- }, new() { ["null"] = true });
1074
+ RunStruct("select", "edge", true, input =>
1075
+ StructUtils.Select(Getp(input, "obj"), Getp(input, "query")));
1258
1076
  }
1259
1077
 
1260
1078
  [Fact]
1261
1079
  public void SelectAlts()
1262
1080
  {
1263
- var selectSpec = (_spec["select"] as Dictionary<string,object?>)?["alts"] as Dictionary<string, object?>;
1264
- StructRunner.RunSet(selectSpec,
1265
- input =>
1266
- {
1267
- var m = input as Dictionary<string, object?>;
1268
- object? query = m?.GetValueOrDefault("query");
1269
- object? obj = m?.GetValueOrDefault("obj");
1270
- return (object?)StructUtils.Select(obj, query);
1271
- }, new() { ["null"] = true });
1081
+ RunStruct("select", "alts", true, input =>
1082
+ StructUtils.Select(Getp(input, "obj"), Getp(input, "query")));
1272
1083
  }
1273
1084
  }