@voxgig/sdkgen 0.35.2 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Main.js +0 -12
  3. package/dist/cmp/Main.js.map +1 -1
  4. package/dist/cmp/Readme.js +5 -5
  5. package/dist/cmp/Readme.js.map +1 -1
  6. package/dist/cmp/ReadmeEntity.js +74 -24
  7. package/dist/cmp/ReadmeEntity.js.map +1 -1
  8. package/dist/cmp/ReadmeExplanation.d.ts +2 -0
  9. package/dist/cmp/ReadmeExplanation.js +308 -0
  10. package/dist/cmp/ReadmeExplanation.js.map +1 -0
  11. package/dist/cmp/ReadmeHowto.d.ts +2 -0
  12. package/dist/cmp/ReadmeHowto.js +18 -0
  13. package/dist/cmp/ReadmeHowto.js.map +1 -0
  14. package/dist/cmp/ReadmeIntro.js +8 -23
  15. package/dist/cmp/ReadmeIntro.js.map +1 -1
  16. package/dist/cmp/ReadmeModel.js +55 -91
  17. package/dist/cmp/ReadmeModel.js.map +1 -1
  18. package/dist/cmp/ReadmeOptions.js +35 -11
  19. package/dist/cmp/ReadmeOptions.js.map +1 -1
  20. package/dist/cmp/ReadmeQuick.js +4 -1
  21. package/dist/cmp/ReadmeQuick.js.map +1 -1
  22. package/dist/cmp/ReadmeRef.js +1042 -40
  23. package/dist/cmp/ReadmeRef.js.map +1 -1
  24. package/dist/cmp/ReadmeTop.d.ts +2 -0
  25. package/dist/cmp/ReadmeTop.js +171 -0
  26. package/dist/cmp/ReadmeTop.js.map +1 -0
  27. package/dist/sdkgen.d.ts +7 -1
  28. package/dist/sdkgen.js +13 -1
  29. package/dist/sdkgen.js.map +1 -1
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +2 -2
  32. package/project/.sdk/model/feature/feature-index.jsonic +5 -0
  33. package/project/.sdk/model/feature/log.jsonic +5 -1
  34. package/project/.sdk/model/feature/test.jsonic +1 -1
  35. package/project/.sdk/model/target/lua.jsonic +23 -0
  36. package/project/.sdk/model/target/php.jsonic +22 -0
  37. package/project/.sdk/model/target/py.jsonic +23 -0
  38. package/project/.sdk/model/target/rb.jsonic +23 -0
  39. package/project/.sdk/src/cmp/go/Config_go.ts +6 -2
  40. package/project/.sdk/src/cmp/go/Main_go.ts +2 -6
  41. package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +42 -0
  42. package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +112 -0
  43. package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +29 -0
  44. package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +129 -0
  45. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +163 -0
  46. package/project/.sdk/src/cmp/go/ReadmeTopHowto_go.ts +24 -0
  47. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +67 -0
  48. package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +40 -0
  49. package/project/.sdk/src/cmp/go/TestDirect_go.ts +46 -2
  50. package/project/.sdk/src/cmp/go/TestEntity_go.ts +360 -160
  51. package/project/.sdk/src/cmp/go/Test_go.ts +24 -1
  52. package/project/.sdk/src/cmp/js/EntityBase_js.ts +34 -0
  53. package/project/.sdk/src/cmp/js/Main_js.ts +10 -0
  54. package/project/.sdk/src/cmp/js/ReadmeExplanation_js.ts +33 -0
  55. package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +123 -0
  56. package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +152 -0
  57. package/project/.sdk/src/cmp/js/ReadmeQuick_js.ts +1 -1
  58. package/project/.sdk/src/cmp/js/ReadmeTopHowto_js.ts +25 -0
  59. package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +65 -0
  60. package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +36 -0
  61. package/project/.sdk/src/cmp/js/TestDirect_js.ts +53 -5
  62. package/project/.sdk/src/cmp/js/TestEntity_js.ts +114 -20
  63. package/project/.sdk/src/cmp/js/fragment/Entity.fragment.js +7 -139
  64. package/project/.sdk/src/cmp/js/fragment/EntityBase.fragment.js +149 -0
  65. package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +6 -10
  66. package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +6 -10
  67. package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +7 -11
  68. package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +7 -11
  69. package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +7 -11
  70. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +2 -0
  71. package/project/.sdk/src/cmp/js/fragment/SdkError.fragment.js +0 -2
  72. package/project/.sdk/src/cmp/lua/Config_lua.ts +116 -0
  73. package/project/.sdk/src/cmp/lua/EntityOperation_lua.ts +44 -0
  74. package/project/.sdk/src/cmp/lua/Entity_lua.ts +62 -0
  75. package/project/.sdk/src/cmp/lua/MainEntity_lua.ts +23 -0
  76. package/project/.sdk/src/cmp/lua/Main_lua.ts +133 -0
  77. package/project/.sdk/src/cmp/lua/Package_lua.ts +80 -0
  78. package/project/.sdk/src/cmp/lua/ReadmeExplanation_lua.ts +42 -0
  79. package/project/.sdk/src/cmp/lua/ReadmeHowto_lua.ts +100 -0
  80. package/project/.sdk/src/cmp/lua/ReadmeInstall_lua.ts +26 -0
  81. package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +129 -0
  82. package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +99 -0
  83. package/project/.sdk/src/cmp/lua/ReadmeTopHowto_lua.ts +24 -0
  84. package/project/.sdk/src/cmp/lua/ReadmeTopQuick_lua.ts +55 -0
  85. package/project/.sdk/src/cmp/lua/ReadmeTopTest_lua.ts +38 -0
  86. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +261 -0
  87. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +485 -0
  88. package/project/.sdk/src/cmp/lua/Test_lua.ts +49 -0
  89. package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +147 -0
  90. package/project/.sdk/src/cmp/lua/fragment/EntityCreateOp.fragment.lua +27 -0
  91. package/project/.sdk/src/cmp/lua/fragment/EntityListOp.fragment.lua +24 -0
  92. package/project/.sdk/src/cmp/lua/fragment/EntityLoadOp.fragment.lua +30 -0
  93. package/project/.sdk/src/cmp/lua/fragment/EntityRemoveOp.fragment.lua +30 -0
  94. package/project/.sdk/src/cmp/lua/fragment/EntityUpdateOp.fragment.lua +30 -0
  95. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +256 -0
  96. package/project/.sdk/src/cmp/lua/fragment/SdkError.fragment.lua +26 -0
  97. package/project/.sdk/src/cmp/lua/tsconfig.json +15 -0
  98. package/project/.sdk/src/cmp/lua/utility_lua.ts +84 -0
  99. package/project/.sdk/src/cmp/php/Config_php.ts +111 -0
  100. package/project/.sdk/src/cmp/php/EntityOperation_php.ts +44 -0
  101. package/project/.sdk/src/cmp/php/Entity_php.ts +62 -0
  102. package/project/.sdk/src/cmp/php/MainEntity_php.ts +24 -0
  103. package/project/.sdk/src/cmp/php/Main_php.ts +135 -0
  104. package/project/.sdk/src/cmp/php/Package_php.ts +87 -0
  105. package/project/.sdk/src/cmp/php/ReadmeExplanation_php.ts +42 -0
  106. package/project/.sdk/src/cmp/php/ReadmeHowto_php.ts +101 -0
  107. package/project/.sdk/src/cmp/php/ReadmeInstall_php.ts +19 -0
  108. package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +129 -0
  109. package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +100 -0
  110. package/project/.sdk/src/cmp/php/ReadmeTopHowto_php.ts +24 -0
  111. package/project/.sdk/src/cmp/php/ReadmeTopQuick_php.ts +56 -0
  112. package/project/.sdk/src/cmp/php/ReadmeTopTest_php.ts +38 -0
  113. package/project/.sdk/src/cmp/php/TestDirect_php.ts +270 -0
  114. package/project/.sdk/src/cmp/php/TestEntity_php.ts +479 -0
  115. package/project/.sdk/src/cmp/php/Test_php.ts +56 -0
  116. package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +146 -0
  117. package/project/.sdk/src/cmp/php/fragment/EntityCreateOp.fragment.php +26 -0
  118. package/project/.sdk/src/cmp/php/fragment/EntityListOp.fragment.php +23 -0
  119. package/project/.sdk/src/cmp/php/fragment/EntityLoadOp.fragment.php +29 -0
  120. package/project/.sdk/src/cmp/php/fragment/EntityRemoveOp.fragment.php +29 -0
  121. package/project/.sdk/src/cmp/php/fragment/EntityUpdateOp.fragment.php +29 -0
  122. package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +233 -0
  123. package/project/.sdk/src/cmp/php/fragment/SdkError.fragment.php +24 -0
  124. package/project/.sdk/src/cmp/php/tsconfig.json +15 -0
  125. package/project/.sdk/src/cmp/php/utility_php.ts +85 -0
  126. package/project/.sdk/src/cmp/py/Config_py.ts +97 -0
  127. package/project/.sdk/src/cmp/py/EntityOperation_py.ts +44 -0
  128. package/project/.sdk/src/cmp/py/Entity_py.ts +62 -0
  129. package/project/.sdk/src/cmp/py/MainEntity_py.ts +22 -0
  130. package/project/.sdk/src/cmp/py/Main_py.ts +156 -0
  131. package/project/.sdk/src/cmp/py/Package_py.ts +75 -0
  132. package/project/.sdk/src/cmp/py/ReadmeExplanation_py.ts +41 -0
  133. package/project/.sdk/src/cmp/py/ReadmeHowto_py.ts +98 -0
  134. package/project/.sdk/src/cmp/py/ReadmeInstall_py.ts +25 -0
  135. package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +130 -0
  136. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +100 -0
  137. package/project/.sdk/src/cmp/py/ReadmeTopHowto_py.ts +24 -0
  138. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +56 -0
  139. package/project/.sdk/src/cmp/py/ReadmeTopTest_py.ts +38 -0
  140. package/project/.sdk/src/cmp/py/TestDirect_py.ts +248 -0
  141. package/project/.sdk/src/cmp/py/TestEntity_py.ts +477 -0
  142. package/project/.sdk/src/cmp/py/Test_py.ts +55 -0
  143. package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +112 -0
  144. package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +25 -0
  145. package/project/.sdk/src/cmp/py/fragment/EntityListOp.fragment.py +22 -0
  146. package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +27 -0
  147. package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +27 -0
  148. package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +27 -0
  149. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +222 -0
  150. package/project/.sdk/src/cmp/py/fragment/SdkError.fragment.py +16 -0
  151. package/project/.sdk/src/cmp/py/tsconfig.json +15 -0
  152. package/project/.sdk/src/cmp/py/utility_py.ts +84 -0
  153. package/project/.sdk/src/cmp/rb/Config_rb.ts +105 -0
  154. package/project/.sdk/src/cmp/rb/EntityOperation_rb.ts +44 -0
  155. package/project/.sdk/src/cmp/rb/Entity_rb.ts +62 -0
  156. package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +23 -0
  157. package/project/.sdk/src/cmp/rb/Main_rb.ts +130 -0
  158. package/project/.sdk/src/cmp/rb/Package_rb.ts +111 -0
  159. package/project/.sdk/src/cmp/rb/ReadmeExplanation_rb.ts +42 -0
  160. package/project/.sdk/src/cmp/rb/ReadmeHowto_rb.ts +98 -0
  161. package/project/.sdk/src/cmp/rb/ReadmeInstall_rb.ts +31 -0
  162. package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +129 -0
  163. package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +99 -0
  164. package/project/.sdk/src/cmp/rb/ReadmeTopHowto_rb.ts +24 -0
  165. package/project/.sdk/src/cmp/rb/ReadmeTopQuick_rb.ts +55 -0
  166. package/project/.sdk/src/cmp/rb/ReadmeTopTest_rb.ts +38 -0
  167. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +260 -0
  168. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +476 -0
  169. package/project/.sdk/src/cmp/rb/Test_rb.ts +50 -0
  170. package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +116 -0
  171. package/project/.sdk/src/cmp/rb/fragment/EntityCreateOp.fragment.rb +25 -0
  172. package/project/.sdk/src/cmp/rb/fragment/EntityListOp.fragment.rb +20 -0
  173. package/project/.sdk/src/cmp/rb/fragment/EntityLoadOp.fragment.rb +26 -0
  174. package/project/.sdk/src/cmp/rb/fragment/EntityRemoveOp.fragment.rb +26 -0
  175. package/project/.sdk/src/cmp/rb/fragment/EntityUpdateOp.fragment.rb +26 -0
  176. package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +203 -0
  177. package/project/.sdk/src/cmp/rb/fragment/SdkError.fragment.rb +16 -0
  178. package/project/.sdk/src/cmp/rb/tsconfig.json +15 -0
  179. package/project/.sdk/src/cmp/rb/utility_rb.ts +84 -0
  180. package/project/.sdk/src/cmp/ts/Main_ts.ts +7 -0
  181. package/project/.sdk/src/cmp/ts/ReadmeExplanation_ts.ts +34 -0
  182. package/project/.sdk/src/cmp/ts/ReadmeHowto_ts.ts +123 -0
  183. package/project/.sdk/src/cmp/ts/ReadmeInstall_ts.ts +1 -1
  184. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +159 -0
  185. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +69 -54
  186. package/project/.sdk/src/cmp/ts/ReadmeTopHowto_ts.ts +25 -0
  187. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +65 -0
  188. package/project/.sdk/src/cmp/ts/ReadmeTopTest_ts.ts +36 -0
  189. package/project/.sdk/tm/go/feature/log_feature.go +133 -0
  190. package/project/.sdk/tm/go/src/feature/log/.gitkeep +0 -0
  191. package/project/.sdk/tm/go/test/runner_test.go +16 -2
  192. package/project/.sdk/tm/js/src/Context.js +142 -0
  193. package/project/.sdk/tm/js/src/Control.js +16 -0
  194. package/project/.sdk/tm/js/src/Operation.js +19 -0
  195. package/project/.sdk/tm/js/src/Point.js +24 -0
  196. package/project/.sdk/tm/js/src/README.md +1 -0
  197. package/project/.sdk/tm/js/src/Response.js +19 -0
  198. package/project/.sdk/tm/js/src/Result.js +21 -0
  199. package/project/.sdk/tm/js/src/Spec.js +26 -0
  200. package/project/.sdk/tm/js/src/feature/README.md +1 -0
  201. package/project/.sdk/tm/js/src/feature/base/BaseFeature.js +45 -0
  202. package/project/.sdk/tm/js/src/feature/log/LogFeature.js +46 -47
  203. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +207 -0
  204. package/project/.sdk/tm/js/src/types.js +22 -0
  205. package/project/.sdk/tm/js/src/utility/CleanUtility.js +31 -0
  206. package/project/.sdk/tm/js/src/utility/DoneUtility.js +11 -4
  207. package/project/.sdk/tm/js/src/utility/FeatureAddUtility.js +42 -0
  208. package/project/.sdk/tm/js/src/utility/FeatureHookUtility.js +25 -0
  209. package/project/.sdk/tm/js/src/utility/FeatureInitUtility.js +11 -0
  210. package/project/.sdk/tm/js/src/utility/FetcherUtility.js +28 -0
  211. package/project/.sdk/tm/js/src/utility/MakeContextUtility.js +11 -0
  212. package/project/.sdk/tm/js/src/utility/MakeErrorUtility.js +55 -0
  213. package/project/.sdk/tm/js/src/utility/MakeFetchDefUtility.js +44 -0
  214. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +93 -0
  215. package/project/.sdk/tm/js/src/utility/MakePointUtility.js +77 -0
  216. package/project/.sdk/tm/js/src/utility/MakeRequestUtility.js +63 -0
  217. package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +55 -0
  218. package/project/.sdk/tm/js/src/utility/MakeResultUtility.js +54 -0
  219. package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +58 -0
  220. package/project/.sdk/tm/js/src/utility/MakeUrlUtility.js +40 -0
  221. package/project/.sdk/tm/js/src/utility/ParamUtility.js +61 -0
  222. package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +41 -0
  223. package/project/.sdk/tm/js/src/utility/PrepareBodyUtility.js +25 -0
  224. package/project/.sdk/tm/js/src/utility/PrepareHeadersUtility.js +18 -0
  225. package/project/.sdk/tm/js/src/utility/{MethodUtility.js → PrepareMethodUtility.js} +7 -7
  226. package/project/.sdk/tm/js/src/utility/PrepareParamsUtility.js +25 -0
  227. package/project/.sdk/tm/js/src/utility/PreparePathUtility.js +13 -0
  228. package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +26 -0
  229. package/project/.sdk/tm/js/src/utility/README.md +1 -0
  230. package/project/.sdk/tm/js/src/utility/ResultBasicUtility.js +34 -0
  231. package/project/.sdk/tm/js/src/utility/ResultBodyUtility.js +18 -0
  232. package/project/.sdk/tm/js/src/utility/ResultHeadersUtility.js +22 -0
  233. package/project/.sdk/tm/js/src/utility/StructUtility.js +2219 -1078
  234. package/project/.sdk/tm/js/src/utility/TransformRequestUtility.js +28 -0
  235. package/project/.sdk/tm/js/src/utility/TransformResponseUtility.js +31 -0
  236. package/project/.sdk/tm/js/src/utility/Utility.js +61 -61
  237. package/project/.sdk/tm/js/test/README.md +1 -0
  238. package/project/.sdk/tm/js/test/exists.test.js +16 -0
  239. package/project/.sdk/tm/js/test/runner.js +323 -107
  240. package/project/.sdk/tm/js/test/utility/Custom.test.js +41 -63
  241. package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +390 -116
  242. package/project/.sdk/tm/js/test/utility/StructUtility.test.js +728 -175
  243. package/project/.sdk/tm/js/test/utility/index.js +9 -0
  244. package/project/.sdk/tm/js/test/utility.js +72 -0
  245. package/project/.sdk/tm/lua/LICENSE +22 -0
  246. package/project/.sdk/tm/lua/Makefile +10 -0
  247. package/project/.sdk/tm/lua/core/context.lua +208 -0
  248. package/project/.sdk/tm/lua/core/control.lua +17 -0
  249. package/project/.sdk/tm/lua/core/error.lua +30 -0
  250. package/project/.sdk/tm/lua/core/helpers.lua +30 -0
  251. package/project/.sdk/tm/lua/core/operation.lua +51 -0
  252. package/project/.sdk/tm/lua/core/response.lua +45 -0
  253. package/project/.sdk/tm/lua/core/result.lua +58 -0
  254. package/project/.sdk/tm/lua/core/spec.lua +29 -0
  255. package/project/.sdk/tm/lua/core/utility_type.lua +90 -0
  256. package/project/.sdk/tm/lua/feature/base_feature.lua +35 -0
  257. package/project/.sdk/tm/lua/feature/log_feature.lua +80 -0
  258. package/project/.sdk/tm/lua/feature/test_feature.lua +202 -0
  259. package/project/.sdk/tm/lua/src/feature/README.md +3 -0
  260. package/project/.sdk/tm/lua/src/feature/base/.gitkeep +0 -0
  261. package/project/.sdk/tm/lua/src/feature/log/.gitkeep +0 -0
  262. package/project/.sdk/tm/lua/src/feature/test/.gitkeep +0 -0
  263. package/project/.sdk/tm/lua/test/primary_utility_test.lua +1213 -0
  264. package/project/.sdk/tm/lua/test/runner.lua +86 -0
  265. package/project/.sdk/tm/lua/test/struct_runner.lua +602 -0
  266. package/project/.sdk/tm/lua/test/struct_utility_test.lua +959 -0
  267. package/project/.sdk/tm/lua/utility/clean.lua +7 -0
  268. package/project/.sdk/tm/lua/utility/done.lua +19 -0
  269. package/project/.sdk/tm/lua/utility/feature_add.lua +8 -0
  270. package/project/.sdk/tm/lua/utility/feature_hook.lua +21 -0
  271. package/project/.sdk/tm/lua/utility/feature_init.lua +24 -0
  272. package/project/.sdk/tm/lua/utility/fetcher.lua +96 -0
  273. package/project/.sdk/tm/lua/utility/make_context.lua +9 -0
  274. package/project/.sdk/tm/lua/utility/make_error.lua +73 -0
  275. package/project/.sdk/tm/lua/utility/make_fetch_def.lua +43 -0
  276. package/project/.sdk/tm/lua/utility/make_options.lua +116 -0
  277. package/project/.sdk/tm/lua/utility/make_point.lua +92 -0
  278. package/project/.sdk/tm/lua/utility/make_request.lua +58 -0
  279. package/project/.sdk/tm/lua/utility/make_response.lua +44 -0
  280. package/project/.sdk/tm/lua/utility/make_result.lua +51 -0
  281. package/project/.sdk/tm/lua/utility/make_spec.lua +72 -0
  282. package/project/.sdk/tm/lua/utility/make_url.lua +46 -0
  283. package/project/.sdk/tm/lua/utility/param.lua +68 -0
  284. package/project/.sdk/tm/lua/utility/prepare_auth.lua +39 -0
  285. package/project/.sdk/tm/lua/utility/prepare_body.lua +14 -0
  286. package/project/.sdk/tm/lua/utility/prepare_headers.lua +20 -0
  287. package/project/.sdk/tm/lua/utility/prepare_method.lua +17 -0
  288. package/project/.sdk/tm/lua/utility/prepare_params.lua +36 -0
  289. package/project/.sdk/tm/lua/utility/prepare_path.lua +19 -0
  290. package/project/.sdk/tm/lua/utility/prepare_query.lua +41 -0
  291. package/project/.sdk/tm/lua/utility/register.lua +71 -0
  292. package/project/.sdk/tm/lua/utility/result_basic.lua +32 -0
  293. package/project/.sdk/tm/lua/utility/result_body.lua +17 -0
  294. package/project/.sdk/tm/lua/utility/result_headers.lua +22 -0
  295. package/project/.sdk/tm/lua/utility/struct/struct.lua +3427 -0
  296. package/project/.sdk/tm/lua/utility/transform_request.lua +31 -0
  297. package/project/.sdk/tm/lua/utility/transform_response.lua +44 -0
  298. package/project/.sdk/tm/php/LICENSE +22 -0
  299. package/project/.sdk/tm/php/Makefile +10 -0
  300. package/project/.sdk/tm/php/core/Context.php +139 -0
  301. package/project/.sdk/tm/php/core/Control.php +18 -0
  302. package/project/.sdk/tm/php/core/Error.php +37 -0
  303. package/project/.sdk/tm/php/core/Helpers.php +25 -0
  304. package/project/.sdk/tm/php/core/Operation.php +36 -0
  305. package/project/.sdk/tm/php/core/Response.php +30 -0
  306. package/project/.sdk/tm/php/core/Result.php +35 -0
  307. package/project/.sdk/tm/php/core/Spec.php +38 -0
  308. package/project/.sdk/tm/php/core/UtilityType.php +89 -0
  309. package/project/.sdk/tm/php/feature/BaseFeature.php +37 -0
  310. package/project/.sdk/tm/php/feature/LogFeature.php +65 -0
  311. package/project/.sdk/tm/php/feature/TestFeature.php +156 -0
  312. package/project/.sdk/tm/php/src/feature/README.md +3 -0
  313. package/project/.sdk/tm/php/src/feature/base/.gitkeep +0 -0
  314. package/project/.sdk/tm/php/src/feature/log/.gitkeep +0 -0
  315. package/project/.sdk/tm/php/src/feature/test/.gitkeep +0 -0
  316. package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +1309 -0
  317. package/project/.sdk/tm/php/test/Runner.php +112 -0
  318. package/project/.sdk/tm/php/test/StructRunner.php +275 -0
  319. package/project/.sdk/tm/php/test/StructUtilityTest.php +1336 -0
  320. package/project/.sdk/tm/php/utility/Clean.php +12 -0
  321. package/project/.sdk/tm/php/utility/Done.php +26 -0
  322. package/project/.sdk/tm/php/utility/FeatureAdd.php +12 -0
  323. package/project/.sdk/tm/php/utility/FeatureHook.php +23 -0
  324. package/project/.sdk/tm/php/utility/FeatureInit.php +25 -0
  325. package/project/.sdk/tm/php/utility/Fetcher.php +105 -0
  326. package/project/.sdk/tm/php/utility/MakeContext.php +14 -0
  327. package/project/.sdk/tm/php/utility/MakeError.php +59 -0
  328. package/project/.sdk/tm/php/utility/MakeFetchDef.php +36 -0
  329. package/project/.sdk/tm/php/utility/MakeOptions.php +102 -0
  330. package/project/.sdk/tm/php/utility/MakePoint.php +87 -0
  331. package/project/.sdk/tm/php/utility/MakeRequest.php +57 -0
  332. package/project/.sdk/tm/php/utility/MakeResponse.php +43 -0
  333. package/project/.sdk/tm/php/utility/MakeResult.php +53 -0
  334. package/project/.sdk/tm/php/utility/MakeSpec.php +64 -0
  335. package/project/.sdk/tm/php/utility/MakeUrl.php +41 -0
  336. package/project/.sdk/tm/php/utility/Param.php +68 -0
  337. package/project/.sdk/tm/php/utility/PrepareAuth.php +33 -0
  338. package/project/.sdk/tm/php/utility/PrepareBody.php +15 -0
  339. package/project/.sdk/tm/php/utility/PrepareHeaders.php +18 -0
  340. package/project/.sdk/tm/php/utility/PrepareMethod.php +21 -0
  341. package/project/.sdk/tm/php/utility/PrepareParams.php +34 -0
  342. package/project/.sdk/tm/php/utility/PreparePath.php +20 -0
  343. package/project/.sdk/tm/php/utility/PrepareQuery.php +32 -0
  344. package/project/.sdk/tm/php/utility/Register.php +67 -0
  345. package/project/.sdk/tm/php/utility/ResultBasic.php +29 -0
  346. package/project/.sdk/tm/php/utility/ResultBody.php +17 -0
  347. package/project/.sdk/tm/php/utility/ResultHeaders.php +21 -0
  348. package/project/.sdk/tm/php/utility/TransformRequest.php +27 -0
  349. package/project/.sdk/tm/php/utility/TransformResponse.php +42 -0
  350. package/project/.sdk/tm/php/utility/struct/Struct.php +3535 -0
  351. package/project/.sdk/tm/py/Makefile +10 -0
  352. package/project/.sdk/tm/py/core/__init__.py +0 -0
  353. package/project/.sdk/tm/py/core/context.py +199 -0
  354. package/project/.sdk/tm/py/core/control.py +12 -0
  355. package/project/.sdk/tm/py/core/error.py +18 -0
  356. package/project/.sdk/tm/py/core/helpers.py +15 -0
  357. package/project/.sdk/tm/py/core/operation.py +37 -0
  358. package/project/.sdk/tm/py/core/response.py +34 -0
  359. package/project/.sdk/tm/py/core/result.py +44 -0
  360. package/project/.sdk/tm/py/core/spec.py +23 -0
  361. package/project/.sdk/tm/py/core/utility_type.py +82 -0
  362. package/project/.sdk/tm/py/entity/__init__.py +0 -0
  363. package/project/.sdk/tm/py/feature/__init__.py +0 -0
  364. package/project/.sdk/tm/py/feature/base_feature.py +61 -0
  365. package/project/.sdk/tm/py/feature/log_feature.py +84 -0
  366. package/project/.sdk/tm/py/feature/test_feature.py +164 -0
  367. package/project/.sdk/tm/py/src/feature/README.md +3 -0
  368. package/project/.sdk/tm/py/src/feature/base/.gitkeep +0 -0
  369. package/project/.sdk/tm/py/src/feature/log/.gitkeep +0 -0
  370. package/project/.sdk/tm/py/src/feature/test/.gitkeep +0 -0
  371. package/project/.sdk/tm/py/test/__init__.py +0 -0
  372. package/project/.sdk/tm/py/test/runner.py +90 -0
  373. package/project/.sdk/tm/py/test/struct_runner.py +411 -0
  374. package/project/.sdk/tm/py/test/test_primary_utility.py +1101 -0
  375. package/project/.sdk/tm/py/test/test_struct_utility.py +751 -0
  376. package/project/.sdk/tm/py/utility/__init__.py +0 -0
  377. package/project/.sdk/tm/py/utility/clean.py +5 -0
  378. package/project/.sdk/tm/py/utility/done.py +14 -0
  379. package/project/.sdk/tm/py/utility/feature_add.py +6 -0
  380. package/project/.sdk/tm/py/utility/feature_hook.py +15 -0
  381. package/project/.sdk/tm/py/utility/feature_init.py +18 -0
  382. package/project/.sdk/tm/py/utility/fetcher.py +95 -0
  383. package/project/.sdk/tm/py/utility/make_context.py +7 -0
  384. package/project/.sdk/tm/py/utility/make_error.py +64 -0
  385. package/project/.sdk/tm/py/utility/make_fetch_def.py +37 -0
  386. package/project/.sdk/tm/py/utility/make_options.py +103 -0
  387. package/project/.sdk/tm/py/utility/make_point.py +74 -0
  388. package/project/.sdk/tm/py/utility/make_request.py +52 -0
  389. package/project/.sdk/tm/py/utility/make_response.py +36 -0
  390. package/project/.sdk/tm/py/utility/make_result.py +41 -0
  391. package/project/.sdk/tm/py/utility/make_spec.py +68 -0
  392. package/project/.sdk/tm/py/utility/make_url.py +34 -0
  393. package/project/.sdk/tm/py/utility/param.py +55 -0
  394. package/project/.sdk/tm/py/utility/prepare_auth.py +34 -0
  395. package/project/.sdk/tm/py/utility/prepare_body.py +11 -0
  396. package/project/.sdk/tm/py/utility/prepare_headers.py +17 -0
  397. package/project/.sdk/tm/py/utility/prepare_method.py +15 -0
  398. package/project/.sdk/tm/py/utility/prepare_params.py +28 -0
  399. package/project/.sdk/tm/py/utility/prepare_path.py +16 -0
  400. package/project/.sdk/tm/py/utility/prepare_query.py +33 -0
  401. package/project/.sdk/tm/py/utility/register.py +68 -0
  402. package/project/.sdk/tm/py/utility/result_basic.py +26 -0
  403. package/project/.sdk/tm/py/utility/result_body.py +13 -0
  404. package/project/.sdk/tm/py/utility/result_headers.py +17 -0
  405. package/project/.sdk/tm/py/utility/transform_request.py +27 -0
  406. package/project/.sdk/tm/py/utility/transform_response.py +39 -0
  407. package/project/.sdk/tm/py/utility/voxgig_struct/__init__.py +72 -0
  408. package/project/.sdk/tm/py/utility/voxgig_struct/voxgig_struct.py +2770 -0
  409. package/project/.sdk/tm/rb/Gemfile +4 -0
  410. package/project/.sdk/tm/rb/LICENSE +22 -0
  411. package/project/.sdk/tm/rb/Makefile +10 -0
  412. package/project/.sdk/tm/rb/core/context.rb +105 -0
  413. package/project/.sdk/tm/rb/core/control.rb +11 -0
  414. package/project/.sdk/tm/rb/core/error.rb +24 -0
  415. package/project/.sdk/tm/rb/core/helpers.rb +16 -0
  416. package/project/.sdk/tm/rb/core/operation.rb +26 -0
  417. package/project/.sdk/tm/rb/core/response.rb +20 -0
  418. package/project/.sdk/tm/rb/core/result.rb +23 -0
  419. package/project/.sdk/tm/rb/core/spec.rb +23 -0
  420. package/project/.sdk/tm/rb/core/utility_type.rb +32 -0
  421. package/project/.sdk/tm/rb/feature/base_feature.rb +30 -0
  422. package/project/.sdk/tm/rb/feature/log_feature.rb +50 -0
  423. package/project/.sdk/tm/rb/feature/test_feature.rb +154 -0
  424. package/project/.sdk/tm/rb/src/feature/README.md +3 -0
  425. package/project/.sdk/tm/rb/src/feature/base/.gitkeep +0 -0
  426. package/project/.sdk/tm/rb/src/feature/log/.gitkeep +0 -0
  427. package/project/.sdk/tm/rb/src/feature/test/.gitkeep +0 -0
  428. package/project/.sdk/tm/rb/test/primary_utility_test.rb +1083 -0
  429. package/project/.sdk/tm/rb/test/runner.rb +70 -0
  430. package/project/.sdk/tm/rb/test/struct_runner.rb +309 -0
  431. package/project/.sdk/tm/rb/test/struct_utility_test.rb +670 -0
  432. package/project/.sdk/tm/rb/utility/clean.rb +4 -0
  433. package/project/.sdk/tm/rb/utility/done.rb +14 -0
  434. package/project/.sdk/tm/rb/utility/feature_add.rb +6 -0
  435. package/project/.sdk/tm/rb/utility/feature_hook.rb +11 -0
  436. package/project/.sdk/tm/rb/utility/feature_init.rb +16 -0
  437. package/project/.sdk/tm/rb/utility/fetcher.rb +67 -0
  438. package/project/.sdk/tm/rb/utility/make_context.rb +7 -0
  439. package/project/.sdk/tm/rb/utility/make_error.rb +44 -0
  440. package/project/.sdk/tm/rb/utility/make_fetch_def.rb +24 -0
  441. package/project/.sdk/tm/rb/utility/make_options.rb +57 -0
  442. package/project/.sdk/tm/rb/utility/make_point.rb +77 -0
  443. package/project/.sdk/tm/rb/utility/make_request.rb +44 -0
  444. package/project/.sdk/tm/rb/utility/make_response.rb +25 -0
  445. package/project/.sdk/tm/rb/utility/make_result.rb +33 -0
  446. package/project/.sdk/tm/rb/utility/make_spec.rb +50 -0
  447. package/project/.sdk/tm/rb/utility/make_url.rb +32 -0
  448. package/project/.sdk/tm/rb/utility/param.rb +48 -0
  449. package/project/.sdk/tm/rb/utility/prepare_auth.rb +26 -0
  450. package/project/.sdk/tm/rb/utility/prepare_body.rb +6 -0
  451. package/project/.sdk/tm/rb/utility/prepare_headers.rb +11 -0
  452. package/project/.sdk/tm/rb/utility/prepare_method.rb +5 -0
  453. package/project/.sdk/tm/rb/utility/prepare_params.rb +25 -0
  454. package/project/.sdk/tm/rb/utility/prepare_path.rb +13 -0
  455. package/project/.sdk/tm/rb/utility/prepare_query.rb +22 -0
  456. package/project/.sdk/tm/rb/utility/register.rb +63 -0
  457. package/project/.sdk/tm/rb/utility/result_basic.rb +23 -0
  458. package/project/.sdk/tm/rb/utility/result_body.rb +11 -0
  459. package/project/.sdk/tm/rb/utility/result_headers.rb +15 -0
  460. package/project/.sdk/tm/rb/utility/struct/voxgig_struct.rb +2256 -0
  461. package/project/.sdk/tm/rb/utility/transform_request.rb +15 -0
  462. package/project/.sdk/tm/rb/utility/transform_response.rb +23 -0
  463. package/src/cmp/Main.ts +1 -16
  464. package/src/cmp/Readme.ts +5 -5
  465. package/src/cmp/ReadmeEntity.ts +77 -25
  466. package/src/cmp/ReadmeExplanation.ts +333 -0
  467. package/src/cmp/ReadmeHowto.ts +28 -0
  468. package/src/cmp/ReadmeIntro.ts +10 -24
  469. package/src/cmp/ReadmeModel.ts +57 -88
  470. package/src/cmp/ReadmeOptions.ts +40 -11
  471. package/src/cmp/ReadmeQuick.ts +4 -1
  472. package/src/cmp/ReadmeRef.ts +1057 -40
  473. package/src/cmp/ReadmeTop.ts +213 -0
  474. package/src/sdkgen.ts +12 -0
  475. package/project/.sdk/src/cmp/js/Quick_js.ts +0 -78
  476. package/project/.sdk/src/cmp/js/TestAcceptEntity_js.ts +0 -13
  477. package/project/.sdk/src/cmp/js/TestAccept_js.ts +0 -18
  478. package/project/.sdk/tm/go/test/exists_test.go +0 -16
  479. package/project/.sdk/tm/js/src/utility/AuthUtility.js +0 -21
  480. package/project/.sdk/tm/js/src/utility/BodyUtility.js +0 -29
  481. package/project/.sdk/tm/js/src/utility/ErrorUtility.js +0 -33
  482. package/project/.sdk/tm/js/src/utility/FindparamUtility.js +0 -31
  483. package/project/.sdk/tm/js/src/utility/FullurlUtility.js +0 -39
  484. package/project/.sdk/tm/js/src/utility/HeadersUtility.js +0 -13
  485. package/project/.sdk/tm/js/src/utility/JoinurlUtility.js +0 -14
  486. package/project/.sdk/tm/js/src/utility/OperatorUtility.js +0 -44
  487. package/project/.sdk/tm/js/src/utility/OptionsUtility.js +0 -54
  488. package/project/.sdk/tm/js/src/utility/ParamsUtility.js +0 -21
  489. package/project/.sdk/tm/js/src/utility/QueryUtility.js +0 -21
  490. package/project/.sdk/tm/js/src/utility/ReqformUtility.js +0 -32
  491. package/project/.sdk/tm/js/src/utility/RequestUtility.js +0 -48
  492. package/project/.sdk/tm/js/src/utility/ResbasicUtility.js +0 -27
  493. package/project/.sdk/tm/js/src/utility/ResbodyUtility.js +0 -15
  494. package/project/.sdk/tm/js/src/utility/ResformUtility.js +0 -34
  495. package/project/.sdk/tm/js/src/utility/ResheadersUtility.js +0 -19
  496. package/project/.sdk/tm/js/src/utility/ResponseUtility.js +0 -37
  497. package/project/.sdk/tm/js/src/utility/ResultUtility.js +0 -28
  498. package/project/.sdk/tm/js/src/utility/SpecUtility.js +0 -35
@@ -0,0 +1,2256 @@
1
+ require 'json'
2
+ require 'uri'
3
+
4
+ module VoxgigStruct
5
+ # --- Debug Logging Configuration ---
6
+ DEBUG = false
7
+
8
+ def self.log(msg)
9
+ puts "[DEBUG] #{msg}" if DEBUG
10
+ end
11
+
12
+ # --- Helper to convert internal undefined marker to Ruby nil ---
13
+ def self.conv(val)
14
+ val.equal?(UNDEF) ? nil : val
15
+ end
16
+
17
+ # --- Constants ---
18
+ S_MKEYPRE = 'key:pre'
19
+ S_MKEYPOST = 'key:post'
20
+ S_MVAL = 'val'
21
+ S_MKEY = 'key'
22
+
23
+ S_DKEY = '`$KEY`'
24
+ S_DMETA = '`$META`'
25
+ S_DTOP = '$TOP'
26
+ S_DERRS = '$ERRS'
27
+
28
+ S_any = 'any'
29
+ S_array = 'array'
30
+ S_boolean = 'boolean'
31
+ S_decimal = 'decimal'
32
+ S_function = 'function'
33
+ S_instance = 'instance'
34
+ S_integer = 'integer'
35
+ S_list = 'list'
36
+ S_map = 'map'
37
+ S_nil = 'nil'
38
+ S_node = 'node'
39
+ S_number = 'number'
40
+ S_null = 'null'
41
+ S_object = 'object'
42
+ S_scalar = 'scalar'
43
+ S_string = 'string'
44
+ S_symbol = 'symbol'
45
+ S_MT = '' # empty string constant (used as a prefix)
46
+ S_BT = '`'
47
+ S_DS = '$'
48
+ S_DT = '.' # delimiter for key paths
49
+ S_CN = ':' # colon for unknown paths
50
+ S_SP = ' '
51
+ S_VIZ = ': '
52
+ S_KEY = 'KEY'
53
+
54
+ # Types - bitfield integers matching TypeScript canonical
55
+ _t = 31
56
+ T_any = (1 << _t) - 1; _t -= 1
57
+ T_noval = 1 << _t; _t -= 1
58
+ T_boolean = 1 << _t; _t -= 1
59
+ T_decimal = 1 << _t; _t -= 1
60
+ T_integer = 1 << _t; _t -= 1
61
+ T_number = 1 << _t; _t -= 1
62
+ T_string = 1 << _t; _t -= 1
63
+ T_function = 1 << _t; _t -= 1
64
+ T_symbol = 1 << _t; _t -= 1
65
+ T_null = 1 << _t; _t -= 8
66
+ T_list = 1 << _t; _t -= 1
67
+ T_map = 1 << _t; _t -= 1
68
+ T_instance = 1 << _t; _t -= 5
69
+ T_scalar = 1 << _t; _t -= 1
70
+ T_node = 1 << _t
71
+
72
+ TYPENAME = [
73
+ S_any, S_nil, S_boolean, S_decimal, S_integer, S_number, S_string,
74
+ S_function, S_symbol, S_null,
75
+ '', '', '', '', '', '', '',
76
+ S_list, S_map, S_instance,
77
+ '', '', '', '',
78
+ S_scalar, S_node,
79
+ ]
80
+
81
+ SKIP = { '`$SKIP`' => true }
82
+ DELETE = { '`$DELETE`' => true }
83
+
84
+ # Unique undefined marker.
85
+ UNDEF = Object.new.freeze
86
+
87
+ # Mode constants (bitfield) matching TypeScript canonical
88
+ M_KEYPRE = 1
89
+ M_KEYPOST = 2
90
+ M_VAL = 4
91
+
92
+ MODENAME = { M_VAL => 'val', M_KEYPRE => 'key:pre', M_KEYPOST => 'key:post' }.freeze
93
+ PLACEMENT = { M_VAL => 'value', M_KEYPRE => S_MKEY, M_KEYPOST => S_MKEY }.freeze
94
+
95
+ MAXDEPTH = 32
96
+
97
+ # --- Utility functions ---
98
+
99
+ def self.sorted(val)
100
+ case val
101
+ when Hash
102
+ sorted_hash = {}
103
+ val.keys.sort.each { |k| sorted_hash[k] = sorted(val[k]) }
104
+ sorted_hash
105
+ when Array
106
+ val.map { |elem| sorted(elem) }
107
+ else
108
+ val
109
+ end
110
+ end
111
+
112
+ def self.clone(val)
113
+ return nil if val.nil? || val.equal?(UNDEF)
114
+ if isfunc(val)
115
+ val
116
+ elsif islist(val)
117
+ val.map { |v| clone(v) }
118
+ elsif ismap(val)
119
+ result = {}
120
+ val.each { |k, v| result[k] = isfunc(v) ? v : clone(v) }
121
+ result
122
+ else
123
+ val
124
+ end
125
+ end
126
+
127
+ def self.escre(s)
128
+ s = s.nil? ? "" : s
129
+ Regexp.escape(s)
130
+ end
131
+
132
+ def self.escurl(s)
133
+ s = s.nil? ? "" : s
134
+ URI::DEFAULT_PARSER.escape(s, /[^A-Za-z0-9\-\.\_\~]/)
135
+ end
136
+
137
+ # --- Internal getprop ---
138
+ # Returns the value if found; otherwise returns alt (default is UNDEF)
139
+ def self._getprop(val, key, alt = UNDEF)
140
+ log("(_getprop) called with val=#{val.inspect} and key=#{key.inspect}")
141
+ return alt if val.nil? || key.nil?
142
+ if islist(val)
143
+ key = (key.to_s =~ /\A\d+\z/) ? key.to_i : key
144
+ unless key.is_a?(Numeric) && key >= 0 && key < val.size
145
+ log("(_getprop) index #{key.inspect} out of bounds; returning alt")
146
+ return alt
147
+ end
148
+ result = val[key]
149
+ log("(_getprop) returning #{result.inspect} from array for key #{key}")
150
+ return result
151
+ elsif ismap(val)
152
+ key_str = key.to_s
153
+ if val.key?(key_str)
154
+ result = val[key_str]
155
+ log("(_getprop) found key #{key_str.inspect} in hash, returning #{result.inspect}")
156
+ return result
157
+ elsif key.is_a?(String) && val.key?(key.to_sym)
158
+ result = val[key.to_sym]
159
+ log("(_getprop) found symbol key #{key.to_sym.inspect} in hash, returning #{result.inspect}")
160
+ return result
161
+ else
162
+ log("(_getprop) key #{key.inspect} not found; returning alt")
163
+ return alt
164
+ end
165
+ else
166
+ log("(_getprop) value is not a node; returning alt")
167
+ alt
168
+ end
169
+ end
170
+
171
+ # --- Public getprop ---
172
+ # Wraps _getprop. If the result equals UNDEF, returns the provided alt.
173
+ def self.getprop(val, key, alt = nil)
174
+ result = _getprop(val, key, alt.nil? ? UNDEF : alt)
175
+ result.equal?(UNDEF) ? alt : result
176
+ end
177
+
178
+ def self.isempty(val)
179
+ return true if val.nil? || val.equal?(UNDEF) || val == ""
180
+ return true if islist(val) && val.empty?
181
+ return true if ismap(val) && val.empty?
182
+ false
183
+ end
184
+
185
+ def self.iskey(key)
186
+ (key.is_a?(String) && !key.empty?) || key.is_a?(Numeric)
187
+ end
188
+
189
+ def self.islist(val)
190
+ val.is_a?(Array)
191
+ end
192
+
193
+ def self.ismap(val)
194
+ val.is_a?(Hash)
195
+ end
196
+
197
+ def self.isnode(val)
198
+ ismap(val) || islist(val)
199
+ end
200
+
201
+ def self.items(val, apply = nil)
202
+ if ismap(val)
203
+ pairs = val.keys.sort.map { |k| [k, val[k]] }
204
+ elsif islist(val)
205
+ pairs = val.each_with_index.map { |v, i| [i.to_s, v] }
206
+ else
207
+ return []
208
+ end
209
+ apply ? pairs.map { |item| apply.call(item) } : pairs
210
+ end
211
+
212
+ def self.setprop(parent, key, val = :no_val_provided)
213
+ log(">>> setprop called with parent=#{parent.inspect}, key=#{key.inspect}, val=#{val.inspect}")
214
+ return parent unless iskey(key)
215
+ if ismap(parent)
216
+ key_str = key.to_s
217
+ if val == :no_val_provided
218
+ parent.delete(key_str)
219
+ else
220
+ parent[key_str] = val
221
+ end
222
+ elsif islist(parent)
223
+ begin
224
+ key_i = Integer(key)
225
+ rescue ArgumentError
226
+ return parent
227
+ end
228
+ if val == :no_val_provided
229
+ parent.delete_at(key_i) if key_i >= 0 && key_i < parent.length
230
+ else
231
+ if key_i >= 0
232
+ index = key_i >= parent.length ? parent.length : key_i
233
+ parent[index] = val
234
+ else
235
+ parent.unshift(val)
236
+ end
237
+ end
238
+ end
239
+ log("<<< setprop result: #{parent.inspect}")
240
+ parent
241
+ end
242
+
243
+ def self.stringify(val, maxlen = nil, pretty = nil)
244
+ return '' if val.equal?(UNDEF)
245
+ return 'null' if val.nil?
246
+
247
+ if val.is_a?(String)
248
+ valstr = val
249
+ else
250
+ begin
251
+ v = val.is_a?(Hash) ? sorted(val) : val
252
+ valstr = JSON.generate(v)
253
+ valstr = valstr.gsub('"', '')
254
+ rescue StandardError
255
+ valstr = val.to_s
256
+ end
257
+ end
258
+
259
+ if !maxlen.nil? && maxlen >= 0
260
+ if valstr.length > maxlen
261
+ valstr = valstr[0, maxlen - 3] + '...'
262
+ end
263
+ end
264
+
265
+ valstr
266
+ end
267
+
268
+ def self.pathify(val, startin = nil, endin = nil)
269
+ pathstr = nil
270
+
271
+ path = if islist(val)
272
+ val
273
+ elsif val.is_a?(String)
274
+ [val]
275
+ elsif val.is_a?(Numeric)
276
+ [val]
277
+ else
278
+ nil
279
+ end
280
+
281
+ start = startin.nil? ? 0 : startin < 0 ? 0 : startin
282
+ end_idx = endin.nil? ? 0 : endin < 0 ? 0 : endin
283
+
284
+ if path && start >= 0
285
+ path = path[start..-end_idx-1] || []
286
+ if path.empty?
287
+ pathstr = '<root>'
288
+ else
289
+ pathstr = path
290
+ .select { |p| iskey(p) }
291
+ .map { |p|
292
+ if p.is_a?(Numeric)
293
+ S_MT + p.floor.to_s
294
+ else
295
+ p.gsub('.', S_MT)
296
+ end
297
+ }
298
+ .join(S_DT)
299
+ end
300
+ end
301
+
302
+ if pathstr.nil?
303
+ pathstr = '<unknown-path' + (val.equal?(UNDEF) ? '' : S_CN + stringify(val, 47)) + '>'
304
+ end
305
+
306
+ pathstr
307
+ end
308
+
309
+ def self.strkey(key = nil)
310
+ return "" if key.nil?
311
+ return key if key.is_a?(String)
312
+ return key.floor.to_s if key.is_a?(Numeric)
313
+ ""
314
+ end
315
+
316
+ def self.isfunc(val)
317
+ val.respond_to?(:call)
318
+ end
319
+
320
+ def self.getdef(val, alt)
321
+ val.nil? ? alt : val
322
+ end
323
+
324
+ def self.size(val)
325
+ return 0 if val.nil? || val.equal?(UNDEF)
326
+ return val.length if val.is_a?(String) || islist(val)
327
+ return val.keys.length if ismap(val)
328
+ return (val == true ? 1 : 0) if val == true || val == false
329
+ return val.to_i if val.is_a?(Numeric)
330
+ 0
331
+ end
332
+
333
+ def self.slice(val, start_idx = nil, end_idx = nil, mutate = false)
334
+ return val if val.nil? || val.equal?(UNDEF)
335
+
336
+ if val.is_a?(Numeric) && !val.is_a?(TrueClass) && !val.is_a?(FalseClass)
337
+ s = start_idx.nil? ? (-Float::INFINITY) : start_idx
338
+ e = end_idx.nil? ? Float::INFINITY : (end_idx - 1)
339
+ return [[val, s].max, e].min
340
+ end
341
+
342
+ vlen = size(val)
343
+
344
+ start_idx = 0 if !end_idx.nil? && start_idx.nil?
345
+
346
+ if !start_idx.nil?
347
+ s = start_idx
348
+ e = end_idx
349
+
350
+ if s < 0
351
+ e = vlen + s
352
+ e = 0 if e < 0
353
+ s = 0
354
+ elsif !e.nil?
355
+ if e < 0
356
+ e = vlen + e
357
+ e = 0 if e < 0
358
+ elsif vlen < e
359
+ e = vlen
360
+ end
361
+ else
362
+ e = vlen
363
+ end
364
+
365
+ s = vlen if vlen < s
366
+
367
+ if islist(val)
368
+ result = val[s...e] || []
369
+ if mutate
370
+ val.replace(result)
371
+ return val
372
+ end
373
+ return result
374
+ elsif val.is_a?(String)
375
+ return val[s...e] || ''
376
+ end
377
+ end
378
+
379
+ val
380
+ end
381
+
382
+ def self.pad(str, padding = nil, padchar = nil)
383
+ str = str.is_a?(String) ? str : stringify(str)
384
+ padding = padding.nil? ? 44 : padding
385
+ padchar = padchar.nil? ? ' ' : (padchar.to_s + ' ')[0]
386
+ if padding >= 0
387
+ str.ljust(padding, padchar)
388
+ else
389
+ str.rjust(-padding, padchar)
390
+ end
391
+ end
392
+
393
+ def self.getelem(val, key, alt = UNDEF)
394
+ out = UNDEF
395
+ if islist(val) && !key.nil? && !key.equal?(UNDEF)
396
+ begin
397
+ nkey = key.to_i
398
+ if key.to_s.strip.match?(/\A-?\d+\z/)
399
+ nkey = val.length + nkey if nkey < 0
400
+ out = (0 <= nkey && nkey < val.length) ? val[nkey] : UNDEF
401
+ end
402
+ rescue
403
+ end
404
+ end
405
+ if out.equal?(UNDEF)
406
+ return isfunc(alt) ? alt.call : (alt.equal?(UNDEF) ? nil : alt)
407
+ end
408
+ out
409
+ end
410
+
411
+ def self.flatten(lst, depth = nil)
412
+ depth = 1 if depth.nil?
413
+ return lst unless islist(lst)
414
+ out = []
415
+ lst.each do |item|
416
+ if islist(item) && depth > 0
417
+ out.concat(flatten(item, depth - 1))
418
+ else
419
+ out << item unless item.nil? || item.equal?(UNDEF)
420
+ end
421
+ end
422
+ out
423
+ end
424
+
425
+ def self.filter(val, check)
426
+ return [] unless isnode(val)
427
+ items(val).select { |item| check.call(item) }.map { |item| item[1] }
428
+ end
429
+
430
+ def self.delprop(parent, key)
431
+ return parent unless iskey(key)
432
+ if ismap(parent)
433
+ ks = strkey(key)
434
+ parent.delete(ks)
435
+ elsif islist(parent)
436
+ return parent unless key.to_s.match?(/\A-?\d+\z/)
437
+ begin
438
+ ki = key.to_i
439
+ if 0 <= ki && ki < parent.length
440
+ parent.delete_at(ki)
441
+ end
442
+ rescue
443
+ end
444
+ end
445
+ parent
446
+ end
447
+
448
+ def self.join(arr, sep = nil, url = nil)
449
+ return '' unless islist(arr)
450
+ sepdef = sep.nil? ? ',' : sep.to_s
451
+ sepre = (sepdef.length == 1) ? Regexp.escape(sepdef) : nil
452
+
453
+ # Filter to non-empty strings only
454
+ parts = arr.select { |n| n.is_a?(String) && n != '' }
455
+
456
+ parts = parts.map.with_index { |s, i|
457
+ if sepre
458
+ if url && i == 0
459
+ s = s.sub(/#{sepre}+$/, '')
460
+ next s
461
+ end
462
+ s = s.sub(/^#{sepre}+/, '') if i > 0
463
+ s = s.sub(/#{sepre}+$/, '') if i < parts.length - 1 || !url
464
+ # Collapse internal duplicate separators
465
+ s = s.gsub(/([^#{sepre}])#{sepre}+([^#{sepre}])/, "\\1#{sepdef}\\2")
466
+ end
467
+ s
468
+ }.reject(&:empty?)
469
+
470
+ parts.join(sepdef)
471
+ end
472
+
473
+ def self.joinurl(sarr)
474
+ join(sarr, '/', true)
475
+ end
476
+
477
+ def self.jsonify(val, flags = nil)
478
+ str = 'null'
479
+ if !val.nil?
480
+ begin
481
+ indent = (flags.is_a?(Hash) ? (flags['indent'] || flags[:indent]) : nil) || 2
482
+ str = _json_stringify(val, indent, 0)
483
+ if str.nil?
484
+ str = 'null'
485
+ end
486
+ offset = (flags.is_a?(Hash) ? (flags['offset'] || flags[:offset]) : nil) || 0
487
+ if offset > 0
488
+ lines = str.split("\n")
489
+ first = lines[0] || ''
490
+ rest = lines[1..-1] || []
491
+ rest_indented = rest.map { |l| (' ' * offset) + l }
492
+ str = "{\n" + rest_indented.join("\n")
493
+ end
494
+ rescue => e
495
+ str = '__JSONIFY_FAILED__'
496
+ end
497
+ end
498
+ str
499
+ end
500
+
501
+ # Mimic JSON.stringify(val, null, indent) from JavaScript
502
+ def self._json_stringify(val, indent, depth)
503
+ return 'null' if val.nil?
504
+ return val.to_s if val == true || val == false
505
+ return val.is_a?(Float) ? val.to_s : val.to_s if val.is_a?(Numeric)
506
+ return JSON.generate(val) if val.is_a?(String)
507
+
508
+ ind = ' ' * indent
509
+ current_indent = ind * (depth + 1)
510
+ closing_indent = ind * depth
511
+
512
+ if islist(val)
513
+ return '[]' if val.empty?
514
+ items_str = val.map { |v| current_indent + _json_stringify(v, indent, depth + 1) }
515
+ "[\n" + items_str.join(",\n") + "\n" + closing_indent + "]"
516
+ elsif ismap(val)
517
+ return '{}' if val.empty?
518
+ pairs = val.keys.sort.map { |k|
519
+ current_indent + JSON.generate(k) + ': ' + _json_stringify(val[k], indent, depth + 1)
520
+ }
521
+ "{\n" + pairs.join(",\n") + "\n" + closing_indent + "}"
522
+ elsif isfunc(val)
523
+ 'null'
524
+ else
525
+ 'null'
526
+ end
527
+ end
528
+
529
+ def self.jm(*kv)
530
+ result = {}
531
+ i = 0
532
+ while i < kv.length - 1
533
+ result[kv[i].to_s] = kv[i + 1]
534
+ i += 2
535
+ end
536
+ result
537
+ end
538
+
539
+ def self.jt(*v)
540
+ v.to_a
541
+ end
542
+
543
+ def self.replace(s, from, to)
544
+ return s.to_s unless s.is_a?(String)
545
+ if from.is_a?(Regexp)
546
+ s.gsub(from, to.to_s)
547
+ else
548
+ s.gsub(from.to_s, to.to_s)
549
+ end
550
+ end
551
+
552
+ def self.keysof(val)
553
+ return [] unless isnode(val)
554
+ if ismap(val)
555
+ val.keys.sort
556
+ elsif islist(val)
557
+ (0...val.length).map(&:to_s)
558
+ else
559
+ []
560
+ end
561
+ end
562
+
563
+ # Public haskey uses getprop (so that missing keys yield nil)
564
+ def self.haskey(val = UNDEF, key = UNDEF)
565
+ _getprop(val, key, UNDEF) != UNDEF
566
+ end
567
+
568
+ def self.joinurl(parts)
569
+ parts.compact.map.with_index do |s, i|
570
+ s = s.to_s
571
+ if i.zero?
572
+ s.sub(/\/+$/, '')
573
+ else
574
+ s.sub(/([^\/])\/+/, '\1/').sub(/^\/+/, '').sub(/\/+$/, '')
575
+ end
576
+ end.reject { |s| s.empty? }.join('/')
577
+ end
578
+
579
+ # Get type name string from type bitfield value.
580
+ def self._clz32(n)
581
+ return 32 if n <= 0
582
+ 31 - (n.bit_length - 1)
583
+ end
584
+
585
+ def self.typename(t)
586
+ t = t.to_i
587
+ idx = _clz32(t)
588
+ return TYPENAME[0] if idx < 0 || idx >= TYPENAME.length
589
+ r = TYPENAME[idx]
590
+ (r.nil? || r == S_MT) ? TYPENAME[0] : r
591
+ end
592
+
593
+ # Determine the type of a value as a bitfield integer.
594
+ def self.typify(value = UNDEF)
595
+ return T_noval if value.equal?(UNDEF)
596
+ return T_scalar | T_null if value.nil?
597
+
598
+ if value == true || value == false
599
+ return T_scalar | T_boolean
600
+ end
601
+
602
+ if isfunc(value)
603
+ return T_scalar | T_function
604
+ end
605
+
606
+ if value.is_a?(Integer)
607
+ return T_scalar | T_number | T_integer
608
+ end
609
+
610
+ if value.is_a?(Float)
611
+ return value.nan? ? T_noval : (T_scalar | T_number | T_decimal)
612
+ end
613
+
614
+ if value.is_a?(String)
615
+ return T_scalar | T_string
616
+ end
617
+
618
+ if value.is_a?(Symbol)
619
+ return T_scalar | T_symbol
620
+ end
621
+
622
+ if islist(value)
623
+ return T_node | T_list
624
+ end
625
+
626
+ if ismap(value)
627
+ return T_node | T_map
628
+ end
629
+
630
+ T_any
631
+ end
632
+
633
+ def self.walk(val, before = nil, after = nil, maxdepth = nil, key: nil, parent: nil, path: nil)
634
+ path = [] if path.nil?
635
+
636
+ _before = before
637
+ _after = after
638
+
639
+ out = _before.nil? ? val : _before.call(key, val, parent, path)
640
+
641
+ md = (maxdepth.is_a?(Numeric) && maxdepth >= 0) ? maxdepth : MAXDEPTH
642
+ if md == 0 || (!path.empty? && md > 0 && md <= path.length)
643
+ return out
644
+ end
645
+
646
+ if isnode(out)
647
+ items(out).each do |ckey, child|
648
+ new_path = flatten([path, ckey.to_s])
649
+ result = walk(child, _before, _after, md, key: ckey, parent: out, path: new_path)
650
+ if ismap(out)
651
+ out[ckey.to_s] = result
652
+ elsif islist(out)
653
+ out[ckey.to_i] = result
654
+ end
655
+ end
656
+ end
657
+
658
+ out = _after.call(key, out, parent, path) unless _after.nil?
659
+
660
+ out
661
+ end
662
+
663
+ # --- Deep Merge Helpers for merge ---
664
+ #
665
+ # deep_merge recursively combines two nodes.
666
+ # For hashes, keys in b override those in a.
667
+ # For arrays, merge index-by-index; b's element overrides a's at that position,
668
+ # while preserving items that b does not provide.
669
+ def self.deep_merge(a, b)
670
+ if ismap(a) && ismap(b)
671
+ merged = a.dup
672
+ b.each do |k, v|
673
+ if merged.key?(k)
674
+ merged[k] = deep_merge(merged[k], v)
675
+ else
676
+ merged[k] = v
677
+ end
678
+ end
679
+ merged
680
+ elsif islist(a) && islist(b)
681
+ max_len = [a.size, b.size].max
682
+ merged = []
683
+ (0...max_len).each do |i|
684
+ if i < a.size && i < b.size
685
+ merged[i] = deep_merge(a[i], b[i])
686
+ elsif i < b.size
687
+ merged[i] = b[i]
688
+ else
689
+ merged[i] = a[i]
690
+ end
691
+ end
692
+ merged
693
+ else
694
+ # For non-node values, b wins.
695
+ b
696
+ end
697
+ end
698
+
699
+ # --- Merge function ---
700
+ # Merge a list of values. Later values have precedence.
701
+ # Nodes override scalars. Matching node kinds merge recursively.
702
+ def self.merge(val, maxdepth = nil)
703
+ md = maxdepth.nil? ? MAXDEPTH : [maxdepth, 0].max
704
+
705
+ return val unless islist(val)
706
+
707
+ lenlist = val.length
708
+ return nil if lenlist == 0
709
+ return val[0] if lenlist == 1
710
+
711
+ out = getprop(val, 0, {})
712
+
713
+ (1...lenlist).each do |oI|
714
+ obj = val[oI]
715
+
716
+ if !isnode(obj)
717
+ # Non-nodes (including nil) override directly
718
+ out = obj
719
+ else
720
+ cur = [out]
721
+ dst = [out]
722
+
723
+ before_fn = lambda { |key, v, _parent, path|
724
+ pI = path.length
725
+
726
+ if md <= pI
727
+ while cur.length <= pI; cur << nil; end
728
+ cur[pI] = v
729
+ setprop(cur[pI - 1], key, v) if pI > 0 && pI - 1 < cur.length
730
+ next nil # stop descending
731
+ elsif !isnode(v)
732
+ cur[pI] = v
733
+ else
734
+ # Extend arrays as needed
735
+ while dst.length <= pI; dst << nil; end
736
+ while cur.length <= pI; cur << nil; end
737
+
738
+ dst[pI] = pI > 0 ? getprop(dst[pI - 1], key) : dst[pI]
739
+ tval = dst[pI]
740
+
741
+ if tval.nil?
742
+ cur[pI] = islist(v) ? [] : {}
743
+ elsif (islist(v) && islist(tval)) || (ismap(v) && ismap(tval))
744
+ cur[pI] = tval
745
+ else
746
+ cur[pI] = v
747
+ v = nil # stop descending
748
+ end
749
+ end
750
+
751
+ v
752
+ }
753
+
754
+ after_fn = lambda { |key, _v, _parent, path|
755
+ cI = path.length
756
+ if cI < 1
757
+ next (cur.length > 0 ? cur[0] : _v)
758
+ end
759
+
760
+ target = (cI - 1 < cur.length) ? cur[cI - 1] : nil
761
+ value = (cI < cur.length) ? cur[cI] : nil
762
+
763
+ setprop(target, key, value) if target
764
+ value
765
+ }
766
+
767
+ out = walk(obj, before_fn, after_fn)
768
+ end
769
+ end
770
+
771
+ if md == 0
772
+ out = getelem(val, -1)
773
+ out = islist(out) ? [] : ismap(out) ? {} : out
774
+ end
775
+
776
+ out
777
+ end
778
+
779
+ # Get value at a key path deep inside a store.
780
+ # Matches TS canonical: getpath(store, path, injdef?)
781
+ def self.getpath(store, path, injdef = nil)
782
+ # Operate on a string array.
783
+ if islist(path)
784
+ parts = path.dup
785
+ elsif path.is_a?(String)
786
+ parts = path.split(S_DT, -1)
787
+ elsif path.is_a?(Numeric)
788
+ parts = [strkey(path)]
789
+ else
790
+ return nil
791
+ end
792
+
793
+ val = store
794
+
795
+ # Extract injdef properties (support both Hash and object with accessors)
796
+ if injdef.is_a?(Hash)
797
+ base = injdef['base'] || injdef[:base]
798
+ dparent = injdef['dparent'] || injdef[:dparent]
799
+ inj_meta = injdef['meta'] || injdef[:meta]
800
+ inj_key = injdef['key'] || injdef[:key]
801
+ dpath = injdef['dpath'] || injdef[:dpath]
802
+ handler = injdef['handler'] || injdef[:handler]
803
+ elsif injdef.respond_to?(:base)
804
+ base = injdef.base
805
+ dparent = injdef.dparent
806
+ inj_meta = injdef.meta
807
+ inj_key = injdef.key
808
+ dpath = injdef.dpath
809
+ handler = injdef.handler
810
+ else
811
+ base = nil; dparent = nil; inj_meta = nil; inj_key = nil; dpath = nil; handler = nil
812
+ end
813
+
814
+ src = base ? _getprop(store, base, store) : store
815
+ numparts = parts.length
816
+
817
+ # An empty path (incl empty string) just finds the src.
818
+ if path.nil? || store.nil? || (numparts == 1 && parts[0] == S_MT) || numparts == 0
819
+ val = src
820
+ elsif numparts > 0
821
+ # Check for $ACTIONs
822
+ if numparts == 1
823
+ val = _getprop(store, parts[0], UNDEF)
824
+ end
825
+
826
+ if !isfunc(val)
827
+ val = src
828
+
829
+ # Check for meta path syntax
830
+ if parts[0].is_a?(String) && (m = parts[0].match(/^([^$]+)\$([=~])(.+)$/)) && inj_meta
831
+ val = _getprop(inj_meta, m[1], UNDEF)
832
+ parts[0] = m[3]
833
+ end
834
+
835
+ pI = 0
836
+ while !val.equal?(UNDEF) && !val.nil? && pI < numparts
837
+ part = parts[pI]
838
+
839
+ if injdef && part == '$KEY'
840
+ part = inj_key || part
841
+ elsif part.is_a?(String) && part.start_with?('$GET:')
842
+ part = stringify(getpath(src, part[5..-2]))
843
+ elsif part.is_a?(String) && part.start_with?('$REF:')
844
+ part = stringify(getpath(_getprop(store, '$SPEC', UNDEF), part[5..-2]))
845
+ elsif injdef && part.is_a?(String) && part.start_with?('$META:')
846
+ part = stringify(getpath(inj_meta, part[6..-2]))
847
+ end
848
+
849
+ # $$ escapes $
850
+ part = part.gsub('$$', '$') if part.is_a?(String)
851
+
852
+ if part == S_MT
853
+ ascends = 0
854
+ while pI + 1 < parts.length && parts[pI + 1] == S_MT
855
+ ascends += 1
856
+ pI += 1
857
+ end
858
+
859
+ if injdef && ascends > 0
860
+ ascends -= 1 if pI == parts.length - 1
861
+ if ascends == 0
862
+ val = dparent
863
+ else
864
+ fullpath = flatten([slice(dpath, 0 - ascends), parts[(pI + 1)..-1]])
865
+ if dpath.is_a?(Array) && ascends <= dpath.length
866
+ val = getpath(store, fullpath)
867
+ else
868
+ val = UNDEF
869
+ end
870
+ break
871
+ end
872
+ else
873
+ val = dparent || src
874
+ end
875
+ else
876
+ val = _getprop(val, part, UNDEF)
877
+ end
878
+ pI += 1
879
+ end
880
+ end
881
+ end
882
+
883
+ # Injdef may provide a custom handler to modify found value.
884
+ if handler && isfunc(handler)
885
+ ref = pathify(path)
886
+ val = handler.call(injdef, val.equal?(UNDEF) ? nil : val, ref, store)
887
+ end
888
+
889
+ val.equal?(UNDEF) ? nil : val
890
+ end
891
+
892
+
893
+ S_BKEY = '`$KEY`'
894
+ S_BANNO = '`$ANNO`'
895
+ S_BEXACT = '`$EXACT`'
896
+ S_BVAL = '`$VAL`'
897
+ S_DSPEC = '$SPEC'
898
+
899
+ R_FULL_INJECT = /\A`(\$[A-Z]+|[^`]*)[0-9]*`\z/
900
+ R_PART_INJECT = /`([^`]*)`/
901
+ R_META_PATH = /\A([^$]+)\$([=~])(.+)\z/
902
+ R_DOUBLE_DOLLAR = /\$\$/
903
+
904
+ # --- _injectstr: Resolve backtick expressions in strings ---
905
+ def self._injectstr(val, store, inj = nil)
906
+ return S_MT unless val.is_a?(String) && val != S_MT
907
+
908
+ out = val
909
+ m = R_FULL_INJECT.match(val)
910
+
911
+ # Full string injection: "`path.ref`" or "`$CMD`"
912
+ if m
913
+ inj.full = true if inj
914
+
915
+ pathref = m[1]
916
+ if pathref.length > 3
917
+ pathref = pathref.gsub('$BT', S_BT).gsub('$DS', S_DS)
918
+ end
919
+
920
+ out = getpath(store, pathref, inj)
921
+
922
+ else
923
+ # Partial string injection: "prefix`ref`suffix"
924
+ out = val.gsub(R_PART_INJECT) do |_match|
925
+ ref = $1
926
+ if ref.length > 3
927
+ ref = ref.gsub('$BT', S_BT).gsub('$DS', S_DS)
928
+ end
929
+
930
+ inj.full = false if inj
931
+
932
+ found = getpath(store, ref, inj)
933
+
934
+ if found.nil?
935
+ # Check if key exists in base data (nil = JSON null, vs not-found)
936
+ base_data = _getprop(store, S_DTOP, store)
937
+ ref_parts = ref.split(S_DT)
938
+ exists = !_getprop(base_data, ref_parts[0], UNDEF).equal?(UNDEF)
939
+ exists ? 'null' : S_MT
940
+ elsif found.is_a?(String)
941
+ found
942
+ elsif isfunc(found)
943
+ found
944
+ else
945
+ begin
946
+ JSON.generate(found)
947
+ rescue
948
+ stringify(found)
949
+ end
950
+ end
951
+ end
952
+
953
+ # Call the inj handler on the entire string for custom injection.
954
+ if inj && isfunc(inj.handler)
955
+ inj.full = true
956
+ out = inj.handler.call(inj, out, val, store)
957
+ end
958
+ end
959
+
960
+ out
961
+ end
962
+
963
+ # --- inject: Recursively inject store values into a node ---
964
+ # Matches TS canonical: inject(val, store, injdef?)
965
+ def self.inject(val, store, injdef = nil)
966
+ # Reuse existing Injection state during recursion; otherwise create new one.
967
+ if injdef.is_a?(Injection)
968
+ inj = injdef
969
+ else
970
+ parent = { S_DTOP => val }
971
+ inj = Injection.new(val, parent)
972
+ inj.handler = method(:_injecthandler)
973
+ inj.base = S_DTOP
974
+ inj.modify = _injdef_prop(injdef, 'modify')
975
+ inj.meta = _injdef_prop(injdef, 'meta') || {}
976
+ inj.errs = getprop(store, S_DERRS, [])
977
+ inj.dparent = store
978
+ inj.dpath = [S_DTOP]
979
+ inj.root = parent
980
+
981
+ h = _injdef_prop(injdef, 'handler')
982
+ inj.handler = h if h
983
+ dp = _injdef_prop(injdef, 'dparent')
984
+ inj.dparent = dp if dp
985
+ dpth = _injdef_prop(injdef, 'dpath')
986
+ inj.dpath = dpth if dpth
987
+ ex = _injdef_prop(injdef, 'extra')
988
+ inj.extra = ex if ex
989
+ end
990
+
991
+ inj.descend
992
+
993
+ # Descend into node.
994
+ if isnode(val)
995
+ if ismap(val)
996
+ normal = val.keys.select { |k| !k.include?(S_DS) }.sort
997
+ transforms = val.keys.select { |k| k.include?(S_DS) }.sort
998
+ nodekeys = normal + transforms
999
+ else
1000
+ nodekeys = (0...val.length).to_a
1001
+ end
1002
+
1003
+ nkI = 0
1004
+ while nkI < nodekeys.length
1005
+ childinj = inj.child(nkI, nodekeys)
1006
+ nodekey = childinj.key
1007
+ childinj.mode = S_MKEYPRE
1008
+
1009
+ prekey = _injectstr(nodekey, store, childinj)
1010
+
1011
+ nkI = childinj.keyI
1012
+ nodekeys = childinj.keys
1013
+
1014
+ if !prekey.nil?
1015
+ childinj.val = getprop(val, prekey)
1016
+ childinj.mode = S_MVAL
1017
+
1018
+ inject(childinj.val, store, childinj)
1019
+
1020
+ nkI = childinj.keyI
1021
+ nodekeys = childinj.keys
1022
+
1023
+ childinj.mode = S_MKEYPOST
1024
+ _injectstr(nodekey, store, childinj)
1025
+
1026
+ nkI = childinj.keyI
1027
+ nodekeys = childinj.keys
1028
+ end
1029
+
1030
+ nkI += 1
1031
+ end
1032
+
1033
+ elsif val.is_a?(String)
1034
+ inj.mode = S_MVAL
1035
+ val = _injectstr(val, store, inj)
1036
+ inj.setval(val) if val != SKIP
1037
+ end
1038
+
1039
+ # Custom modification.
1040
+ if inj.modify && val != SKIP
1041
+ mkey = inj.key
1042
+ mparent = inj.parent
1043
+ mval = getprop(mparent, mkey)
1044
+ inj.modify.call(mval, mkey, mparent, inj)
1045
+ end
1046
+
1047
+ inj.val = val
1048
+
1049
+ if inj.prior.nil? && inj.root && haskey(inj.root, S_DTOP)
1050
+ return getprop(inj.root, S_DTOP)
1051
+ end
1052
+ if inj.key == S_DTOP && inj.parent && haskey(inj.parent, S_DTOP)
1053
+ return getprop(inj.parent, S_DTOP)
1054
+ end
1055
+ val
1056
+ end
1057
+
1058
+ # Helper to read a property from injdef (Hash or object)
1059
+ def self._injdef_prop(injdef, key)
1060
+ return nil if injdef.nil?
1061
+ if injdef.is_a?(Hash)
1062
+ injdef[key] || injdef[key.to_sym]
1063
+ elsif injdef.respond_to?(key.to_sym)
1064
+ injdef.send(key.to_sym)
1065
+ else
1066
+ nil
1067
+ end
1068
+ end
1069
+
1070
+ # Default inject handler
1071
+ def self._injecthandler(inj, val, ref, store)
1072
+ out = val
1073
+ iscmd = isfunc(val) && (ref.nil? || (ref.is_a?(String) && ref.start_with?(S_DS)))
1074
+
1075
+ if iscmd
1076
+ out = val.call(inj, val, ref, store)
1077
+ elsif inj.mode == S_MVAL && inj.full
1078
+ inj.setval(val)
1079
+ end
1080
+
1081
+ out
1082
+ end
1083
+
1084
+ # --- Transform commands ---
1085
+
1086
+ def self.transform_DELETE(inj, _val, _ref, _store)
1087
+ inj.setval(nil)
1088
+ nil
1089
+ end
1090
+
1091
+ def self.transform_COPY(inj, _val, _ref, _store)
1092
+ mode = inj.mode
1093
+ key = inj.key
1094
+
1095
+ out = nil
1096
+ if mode.start_with?('key')
1097
+ out = key
1098
+ else
1099
+ if !isnode(inj.dparent)
1100
+ out = (inj.path.length != 2) ? inj.dparent : nil
1101
+ else
1102
+ out = getprop(inj.dparent, key)
1103
+ end
1104
+ inj.setval(out)
1105
+ end
1106
+ out
1107
+ end
1108
+
1109
+ def self.transform_KEY(inj, _val, _ref, _store)
1110
+ mode = inj.mode
1111
+ path = inj.path
1112
+ parent = inj.parent
1113
+
1114
+ return inj.key if mode == S_MKEYPRE
1115
+ return nil if mode != S_MVAL
1116
+
1117
+ keyspec = getprop(parent, S_BKEY)
1118
+ if keyspec
1119
+ delprop(parent, S_BKEY)
1120
+ return getprop(inj.dparent, keyspec)
1121
+ end
1122
+
1123
+ if ismap(inj.dparent) && inj.key && haskey(inj.dparent, inj.key)
1124
+ return getprop(inj.dparent, inj.key)
1125
+ end
1126
+
1127
+ meta = getprop(parent, S_BANNO)
1128
+ getprop(meta, S_KEY, getprop(path, path.length - 2))
1129
+ end
1130
+
1131
+ def self.transform_ANNO(inj, _val, _ref, _store)
1132
+ delprop(inj.parent, S_BANNO)
1133
+ nil
1134
+ end
1135
+
1136
+ def self.transform_META(inj, _val, _ref, _store)
1137
+ delprop(inj.parent, S_DMETA)
1138
+ nil
1139
+ end
1140
+
1141
+ def self.transform_MERGE(inj, _val, _ref, _store)
1142
+ mode = inj.mode
1143
+ key = inj.key
1144
+ parent = inj.parent
1145
+
1146
+ if mode == S_MKEYPRE
1147
+ return key
1148
+ elsif mode == S_MKEYPOST
1149
+ args = getprop(parent, key)
1150
+ args = islist(args) ? args : [args]
1151
+ inj.setval(nil)
1152
+ mergelist = [parent] + args + [clone(parent)]
1153
+ merge(mergelist)
1154
+ return key
1155
+ elsif mode == S_MVAL && islist(parent)
1156
+ if strkey(inj.key) == '0' && size(parent) > 0
1157
+ parent.delete_at(0)
1158
+ return getprop(parent, 0)
1159
+ else
1160
+ return getprop(parent, inj.key)
1161
+ end
1162
+ end
1163
+ nil
1164
+ end
1165
+
1166
+ def self.transform_EACH(inj, _val, _ref, store)
1167
+ mode = inj.mode
1168
+ keys_ = inj.keys
1169
+ path = inj.path
1170
+ parent = inj.parent
1171
+ nodes_ = inj.nodes
1172
+
1173
+ keys_.replace(keys_[0, 1]) if keys_
1174
+
1175
+ return nil if mode != S_MVAL || !path || !nodes_
1176
+
1177
+ srcpath = parent[1] if parent.length > 1
1178
+ child_template = clone(parent[2]) if parent.length > 2
1179
+
1180
+ srcstore = getprop(store, inj.base, store)
1181
+ src = getpath(srcstore, srcpath, inj)
1182
+
1183
+ tkey = getelem(path, -2)
1184
+ target = nodes_.length >= 2 ? nodes_[-2] : nodes_[-1]
1185
+
1186
+ rval = []
1187
+
1188
+ if isnode(src)
1189
+ if islist(src)
1190
+ tval = src.map { clone(child_template) }
1191
+ else
1192
+ tval = []
1193
+ src.each do |k, v|
1194
+ cc = clone(child_template)
1195
+ setprop(cc, S_BANNO, { S_KEY => k }) if ismap(cc)
1196
+ tval << cc
1197
+ end
1198
+ end
1199
+ tcurrent = ismap(src) ? src.values : src
1200
+
1201
+ if size(tval) > 0
1202
+ ckey = getelem(path, -2)
1203
+ tpath = path[0...-1]
1204
+
1205
+ dpath = [S_DTOP]
1206
+ if srcpath.is_a?(String) && !srcpath.empty?
1207
+ srcpath.split(S_DT).each { |p| dpath << p if p != S_MT }
1208
+ end
1209
+ dpath << ('$:' + ckey.to_s) if ckey
1210
+
1211
+ tcur = { ckey => tcurrent }
1212
+
1213
+ if size(tpath) > 1
1214
+ pkey = getelem(path, -3, S_DTOP)
1215
+ tcur = { pkey => tcur }
1216
+ dpath << ('$:' + pkey.to_s)
1217
+ end
1218
+
1219
+ tinj = inj.child(0, ckey ? [ckey] : [])
1220
+ tinj.path = tpath
1221
+ tinj.nodes = nodes_.length > 0 ? nodes_[0...-1] : []
1222
+ tinj.parent = getelem(tinj.nodes, -1)
1223
+ setprop(tinj.parent, ckey, tval) if ckey && tinj.parent
1224
+ tinj.val = tval
1225
+ tinj.dpath = dpath
1226
+ tinj.dparent = tcur
1227
+
1228
+ inject(tval, store, tinj)
1229
+ rval = tinj.val
1230
+ end
1231
+ end
1232
+
1233
+ setprop(target, tkey, rval)
1234
+ islist(rval) && size(rval) > 0 ? rval[0] : nil
1235
+ end
1236
+
1237
+ def self.transform_PACK(inj, _val, _ref, store)
1238
+ mode = inj.mode
1239
+ key = inj.key
1240
+ path = inj.path
1241
+ parent = inj.parent
1242
+ nodes_ = inj.nodes
1243
+
1244
+ return nil if mode != S_MKEYPRE || !key.is_a?(String) || !path || !nodes_
1245
+
1246
+ args_val = getprop(parent, key)
1247
+ return nil if !islist(args_val) || size(args_val) < 2
1248
+
1249
+ srcpath = args_val[0]
1250
+ origchildspec = args_val[1]
1251
+
1252
+ tkey = getelem(path, -2)
1253
+ pathsize = size(path)
1254
+ target = getelem(nodes_, pathsize - 2, lambda { getelem(nodes_, pathsize - 1) })
1255
+
1256
+ srcstore = getprop(store, inj.base, store)
1257
+ src = getpath(srcstore, srcpath, inj)
1258
+
1259
+ if !islist(src)
1260
+ if ismap(src)
1261
+ new_src = []
1262
+ items(src).each do |item|
1263
+ setprop(item[1], S_BANNO, { S_KEY => item[0] })
1264
+ new_src << item[1]
1265
+ end
1266
+ src = new_src
1267
+ else
1268
+ src = nil
1269
+ end
1270
+ end
1271
+
1272
+ return nil if src.nil?
1273
+
1274
+ keypath = getprop(origchildspec, S_BKEY)
1275
+ childspec = delprop(clone(origchildspec), S_BKEY)
1276
+ child = getprop(childspec, S_BVAL, childspec)
1277
+
1278
+ tval = {}
1279
+ items(src).each do |item|
1280
+ srckey = item[0]
1281
+ srcnode = item[1]
1282
+
1283
+ k = srckey
1284
+ if keypath
1285
+ if keypath.is_a?(String) && keypath.start_with?(S_BT)
1286
+ k = inject(keypath, merge([{}, store, { S_DTOP => srcnode }], 1))
1287
+ else
1288
+ k = getpath(srcnode, keypath, inj)
1289
+ end
1290
+ end
1291
+
1292
+ tchild = clone(child)
1293
+ setprop(tval, k, tchild)
1294
+
1295
+ anno = getprop(srcnode, S_BANNO)
1296
+ if anno.nil?
1297
+ delprop(tchild, S_BANNO)
1298
+ else
1299
+ setprop(tchild, S_BANNO, anno)
1300
+ end
1301
+ end
1302
+
1303
+ rval = {}
1304
+
1305
+ if !isempty(tval)
1306
+ tsrc = {}
1307
+ src.each_with_index do |n, i|
1308
+ if keypath.nil?
1309
+ kn = i
1310
+ elsif keypath.is_a?(String) && keypath.start_with?(S_BT)
1311
+ kn = inject(keypath, merge([{}, store, { S_DTOP => n }], 1))
1312
+ else
1313
+ kn = getpath(n, keypath, inj)
1314
+ end
1315
+ setprop(tsrc, kn, n)
1316
+ end
1317
+
1318
+ tpath = slice(inj.path, -1)
1319
+ ckey = getelem(inj.path, -2)
1320
+ dpath = flatten([S_DTOP, srcpath.to_s.split(S_DT), '$:' + ckey.to_s])
1321
+
1322
+ tcur = { ckey => tsrc }
1323
+ if size(tpath) > 1
1324
+ pkey = getelem(inj.path, -3, S_DTOP)
1325
+ tcur = { pkey => tcur }
1326
+ dpath << ('$:' + pkey.to_s)
1327
+ end
1328
+
1329
+ tinj = inj.child(0, [ckey])
1330
+ tinj.path = tpath
1331
+ tinj.nodes = slice(inj.nodes, -1)
1332
+ tinj.parent = getelem(tinj.nodes, -1)
1333
+ tinj.val = tval
1334
+ tinj.dpath = dpath
1335
+ tinj.dparent = tcur
1336
+
1337
+ inject(tval, store, tinj)
1338
+ rval = tinj.val
1339
+ end
1340
+
1341
+ setprop(target, tkey, rval)
1342
+ nil
1343
+ end
1344
+
1345
+ def self.transform_REF(inj, _val, _ref, store)
1346
+ nodes_ = inj.nodes
1347
+ return nil if S_MVAL != inj.mode
1348
+
1349
+ refpath = getprop(inj.parent, 1)
1350
+ inj.keyI = size(inj.keys)
1351
+
1352
+ specFn = getprop(store, S_DSPEC)
1353
+ spec = isfunc(specFn) ? specFn.call : nil
1354
+
1355
+ dpath = slice(inj.path, 1)
1356
+ ref = getpath(spec, refpath, {
1357
+ 'dpath' => dpath,
1358
+ 'dparent' => getpath(spec, dpath),
1359
+ })
1360
+
1361
+ tref = clone(ref)
1362
+
1363
+ cpath = slice(inj.path, -3)
1364
+ tpath = slice(inj.path, -1)
1365
+ tcur = getpath(store, cpath)
1366
+ tval = getpath(store, tpath)
1367
+ rval = nil
1368
+
1369
+ if tval || !isnode(ref)
1370
+ tinj = inj.child(0, [getelem(tpath, -1)])
1371
+ tinj.path = tpath
1372
+ tinj.nodes = slice(inj.nodes, -1)
1373
+ tinj.parent = getelem(nodes_, -2)
1374
+ tinj.val = tref
1375
+
1376
+ tinj.dpath = flatten([cpath])
1377
+ tinj.dparent = tcur
1378
+
1379
+ inject(tref, store, tinj)
1380
+ rval = tinj.val
1381
+ end
1382
+
1383
+ tkey = getelem(inj.path, -2)
1384
+ target = getelem(nodes_, -2, lambda { getelem(nodes_, -1) })
1385
+ if rval.nil?
1386
+ delprop(target, tkey)
1387
+ else
1388
+ setprop(target, tkey, rval)
1389
+ end
1390
+
1391
+ if islist(target) && inj.prior
1392
+ inj.prior.keyI -= 1
1393
+ end
1394
+
1395
+ _val
1396
+ end
1397
+
1398
+ FORMATTER = {
1399
+ 'identity' => lambda { |_k, v, *_a| v },
1400
+ 'upper' => lambda { |_k, v, *_a| isnode(v) ? v : (v.nil? ? 'null' : '' + v.to_s).upcase },
1401
+ 'lower' => lambda { |_k, v, *_a| isnode(v) ? v : (v.nil? ? 'null' : '' + v.to_s).downcase },
1402
+ 'string' => lambda { |_k, v, *_a| isnode(v) ? v : (v.nil? ? 'null' : '' + v.to_s) },
1403
+ 'number' => lambda { |_k, v, *_a|
1404
+ if isnode(v)
1405
+ v
1406
+ else
1407
+ n = Float(v) rescue 0
1408
+ n
1409
+ end
1410
+ },
1411
+ 'integer' => lambda { |_k, v, *_a|
1412
+ if isnode(v)
1413
+ v
1414
+ else
1415
+ n = Integer(Float(v)) rescue 0
1416
+ n
1417
+ end
1418
+ },
1419
+ 'concat' => lambda { |k, v, *_a|
1420
+ if k.nil? && islist(v)
1421
+ items(v, lambda { |n| isnode(n[1]) ? '' : (n[1].nil? ? 'null' : '' + n[1].to_s) }).join('')
1422
+ else
1423
+ v
1424
+ end
1425
+ },
1426
+ }
1427
+
1428
+ def self.transform_FORMAT(inj, _val, _ref, store)
1429
+ slice(inj.keys, 0, 1, true)
1430
+ return nil if S_MVAL != inj.mode
1431
+
1432
+ name = getprop(inj.parent, 1)
1433
+ child = getprop(inj.parent, 2)
1434
+
1435
+ tkey = getelem(inj.path, -2)
1436
+ target = getelem(inj.nodes, -2, lambda { getelem(inj.nodes, -1) })
1437
+
1438
+ cinj = injectChild(child, store, inj)
1439
+ resolved = cinj.val
1440
+
1441
+ formatter = (0 < (T_function & typify(name))) ? name : FORMATTER[name]
1442
+
1443
+ if formatter.nil?
1444
+ inj.errs << ('$FORMAT: unknown format: ' + name.to_s + '.')
1445
+ return nil
1446
+ end
1447
+
1448
+ out = walk(resolved, formatter)
1449
+ setprop(target, tkey, out)
1450
+ out
1451
+ end
1452
+
1453
+ def self.transform_APPLY(inj, _val, _ref, store)
1454
+ ijname = 'APPLY'
1455
+ return nil unless checkPlacement(M_VAL, ijname, T_list, inj)
1456
+
1457
+ args = slice(inj.parent, 1)
1458
+ args_list = islist(args) ? args : []
1459
+ err, apply, child = injectorArgs([T_function, T_any], args_list)
1460
+ if err
1461
+ inj.errs << ('$' + ijname + ': ' + err)
1462
+ return nil
1463
+ end
1464
+
1465
+ tkey = getelem(inj.path, -2)
1466
+ target = getelem(inj.nodes, -2, lambda { getelem(inj.nodes, -1) })
1467
+
1468
+ cinj = injectChild(child, store, inj)
1469
+ resolved = cinj.val
1470
+
1471
+ out = apply.call(resolved, store, cinj)
1472
+ setprop(target, tkey, out)
1473
+ out
1474
+ end
1475
+
1476
+ def self.checkPlacement(modes, ijname, parentTypes, inj)
1477
+ mode_num = { S_MKEYPRE => M_KEYPRE, S_MKEYPOST => M_KEYPOST, S_MVAL => M_VAL }
1478
+ mode_int = mode_num[inj.mode] || 0
1479
+ if 0 == (modes & mode_int)
1480
+ inj.errs << '$' + ijname + ': invalid placement as ' + (PLACEMENT[mode_int] || '') +
1481
+ ', expected: ' + [M_KEYPRE, M_KEYPOST, M_VAL].select { |m| modes & m != 0 }.map { |m| PLACEMENT[m] }.join(',') + '.'
1482
+ return false
1483
+ end
1484
+ if !isempty(parentTypes)
1485
+ ptype = typify(inj.parent)
1486
+ if 0 == (parentTypes & ptype)
1487
+ inj.errs << '$' + ijname + ': invalid placement in parent ' + typename(ptype) +
1488
+ ', expected: ' + typename(parentTypes) + '.'
1489
+ return false
1490
+ end
1491
+ end
1492
+ true
1493
+ end
1494
+
1495
+ def self.injectorArgs(argTypes, args)
1496
+ numargs = size(argTypes)
1497
+ found = Array.new(1 + numargs)
1498
+ found[0] = nil
1499
+ (0...numargs).each do |argI|
1500
+ arg = args[argI]
1501
+ argType = typify(arg)
1502
+ if 0 == (argTypes[argI] & argType)
1503
+ found[0] = 'invalid argument: ' + stringify(arg, 22) +
1504
+ ' (' + typename(argType) + ' at position ' + (1 + argI).to_s +
1505
+ ') is not of type: ' + typename(argTypes[argI]) + '.'
1506
+ break
1507
+ end
1508
+ found[1 + argI] = arg
1509
+ end
1510
+ found
1511
+ end
1512
+
1513
+ def self.injectChild(child, store, inj)
1514
+ cinj = inj
1515
+ if inj.prior
1516
+ if inj.prior.prior
1517
+ cinj = inj.prior.prior.child(inj.prior.keyI, inj.prior.keys)
1518
+ cinj.val = child
1519
+ setprop(cinj.parent, inj.prior.key, child)
1520
+ else
1521
+ cinj = inj.prior.child(inj.keyI, inj.keys)
1522
+ cinj.val = child
1523
+ setprop(cinj.parent, inj.key, child)
1524
+ end
1525
+ end
1526
+ inject(child, store, cinj)
1527
+ cinj
1528
+ end
1529
+
1530
+ # --- transform: Transform data using spec ---
1531
+ def self.transform(data, spec, injdef = nil)
1532
+ origspec = spec
1533
+ spec = clone(spec)
1534
+
1535
+ extra = _injdef_prop(injdef, 'extra')
1536
+ collect = !_injdef_prop(injdef, 'errs').nil?
1537
+ errs = collect ? _injdef_prop(injdef, 'errs') : []
1538
+
1539
+ extraTransforms = {}
1540
+ extraData = {}
1541
+
1542
+ if extra && isnode(extra)
1543
+ items(extra).each do |item|
1544
+ k, v = item
1545
+ if k.is_a?(String) && k.start_with?(S_DS)
1546
+ extraTransforms[k] = v
1547
+ else
1548
+ extraData[k] = v
1549
+ end
1550
+ end
1551
+ end
1552
+
1553
+ data_clone = merge([
1554
+ isempty(extraData) ? nil : clone(extraData),
1555
+ clone(data)
1556
+ ])
1557
+
1558
+ store = {
1559
+ S_DTOP => data_clone,
1560
+ S_DSPEC => lambda { origspec },
1561
+ '$BT' => lambda { |*_a| S_BT },
1562
+ '$DS' => lambda { |*_a| S_DS },
1563
+ '$WHEN' => lambda { |*_a| Time.now.iso8601 },
1564
+ '$DELETE' => method(:transform_DELETE),
1565
+ '$COPY' => method(:transform_COPY),
1566
+ '$KEY' => method(:transform_KEY),
1567
+ '$ANNO' => method(:transform_ANNO),
1568
+ '$META' => method(:transform_META),
1569
+ '$MERGE' => method(:transform_MERGE),
1570
+ '$EACH' => method(:transform_EACH),
1571
+ '$PACK' => method(:transform_PACK),
1572
+ '$REF' => method(:transform_REF),
1573
+ '$FORMAT' => method(:transform_FORMAT),
1574
+ '$APPLY' => method(:transform_APPLY),
1575
+ }
1576
+ extraTransforms.each { |k, v| store[k] = v }
1577
+ store[S_DERRS] = errs
1578
+
1579
+ injdef = {} if injdef.nil?
1580
+ injdef = {} unless injdef.is_a?(Hash)
1581
+ injdef = injdef.merge('errs' => errs)
1582
+
1583
+ out = inject(spec, store, injdef)
1584
+
1585
+ if !errs.empty? && !collect
1586
+ raise errs.join(' | ')
1587
+ end
1588
+
1589
+ out
1590
+ end
1591
+
1592
+ # --- Validators ---
1593
+
1594
+ def self._invalidTypeMsg(path, needtype, vt, v, _whence = nil)
1595
+ vs = (v.nil? || v.equal?(UNDEF)) ? 'no value' : stringify(v)
1596
+ 'Expected ' +
1597
+ (size(path) > 1 ? ('field ' + pathify(path, 1) + ' to be ') : '') +
1598
+ needtype.to_s + ', but found ' +
1599
+ ((v.nil? || v.equal?(UNDEF)) ? '' : typename(vt) + S_VIZ) + vs + '.'
1600
+ end
1601
+
1602
+ def self.validate_STRING(inj, _val = nil, _ref = nil, _store = nil)
1603
+ out = getprop(inj.dparent, inj.key)
1604
+ t = typify(out)
1605
+ if 0 == (T_string & t)
1606
+ inj.errs << _invalidTypeMsg(inj.path, S_string, t, out, 'V1010')
1607
+ return nil
1608
+ end
1609
+ if out == S_MT
1610
+ inj.errs << ('Empty string at ' + pathify(inj.path, 1))
1611
+ return nil
1612
+ end
1613
+ out
1614
+ end
1615
+
1616
+ TYPE_CHECKS = {
1617
+ S_number => lambda { |v| v.is_a?(Numeric) && !(v == true || v == false) },
1618
+ S_integer => lambda { |v| v.is_a?(Integer) && !(v == true || v == false) },
1619
+ S_decimal => lambda { |v| v.is_a?(Float) },
1620
+ S_boolean => lambda { |v| v == true || v == false },
1621
+ S_null => lambda { |v| v.nil? },
1622
+ S_nil => lambda { |v| v.equal?(UNDEF) },
1623
+ S_map => lambda { |v| v.is_a?(Hash) },
1624
+ S_list => lambda { |v| v.is_a?(Array) },
1625
+ S_function => lambda { |v| v.respond_to?(:call) },
1626
+ S_instance => lambda { |v|
1627
+ !v.is_a?(Hash) && !v.is_a?(Array) && !v.is_a?(String) &&
1628
+ !v.is_a?(Numeric) && !(v == true || v == false) && !v.nil? && !v.equal?(UNDEF)
1629
+ },
1630
+ }
1631
+
1632
+ def self.validate_TYPE(inj, _val = nil, ref = nil, _store = nil)
1633
+ tname = (ref.is_a?(String) && ref.length > 1) ? ref[1..-1].downcase : S_any
1634
+ idx = TYPENAME.index(tname)
1635
+ typev = idx ? (1 << (31 - idx)) : 0
1636
+ typev = typev | T_null if tname == S_nil
1637
+
1638
+ out = getprop(inj.dparent, inj.key)
1639
+ t = typify(out)
1640
+
1641
+ if 0 == (t & typev)
1642
+ inj.errs << _invalidTypeMsg(inj.path, tname, t, out, 'V1001')
1643
+ return nil
1644
+ end
1645
+ out
1646
+ end
1647
+
1648
+ def self.validate_ANY(inj, _val = nil, _ref = nil, _store = nil)
1649
+ getprop(inj.dparent, inj.key)
1650
+ end
1651
+
1652
+ def self.validate_CHILD(inj, _val = nil, _ref = nil, _store = nil)
1653
+ mode = inj.mode
1654
+ key = inj.key
1655
+ parent = inj.parent
1656
+ path = inj.path
1657
+ keys = inj.keys
1658
+
1659
+ if S_MKEYPRE == mode
1660
+ childtm = getprop(parent, key)
1661
+ pkey = getelem(path, -2)
1662
+ tval = getprop(inj.dparent, pkey)
1663
+
1664
+ if tval.nil?
1665
+ tval = {}
1666
+ elsif !ismap(tval)
1667
+ inj.errs << _invalidTypeMsg(path[0...-1], S_object, typify(tval), tval, 'V0220')
1668
+ return nil
1669
+ end
1670
+
1671
+ keysof(tval).each do |ckey|
1672
+ setprop(parent, ckey, clone(childtm))
1673
+ keys << ckey
1674
+ end
1675
+
1676
+ inj.setval(nil)
1677
+ return nil
1678
+ end
1679
+
1680
+ if S_MVAL == mode
1681
+ if !islist(parent)
1682
+ inj.errs << 'Invalid $CHILD as value'
1683
+ return nil
1684
+ end
1685
+
1686
+ childtm = getprop(parent, 1)
1687
+
1688
+ if inj.dparent.nil?
1689
+ parent.clear
1690
+ return nil
1691
+ end
1692
+
1693
+ if !islist(inj.dparent)
1694
+ inj.errs << _invalidTypeMsg(path[0...-1], S_list, typify(inj.dparent), inj.dparent, 'V0230')
1695
+ inj.keyI = size(parent)
1696
+ return inj.dparent
1697
+ end
1698
+
1699
+ items(inj.dparent).each do |n|
1700
+ setprop(parent, n[0], clone(childtm))
1701
+ end
1702
+ parent.slice!(inj.dparent.length..-1) if parent.length > inj.dparent.length
1703
+ inj.keyI = 0
1704
+ return getprop(inj.dparent, 0)
1705
+ end
1706
+
1707
+ nil
1708
+ end
1709
+
1710
+ def self.validate_ONE(inj, _val = nil, _ref = nil, store = nil)
1711
+ mode = inj.mode
1712
+ parent = inj.parent
1713
+ keyI = inj.keyI
1714
+
1715
+ if S_MVAL == mode
1716
+ if !islist(parent) || 0 != keyI
1717
+ inj.errs << ('The $ONE validator at field ' + pathify(inj.path, 1, 1) +
1718
+ ' must be the first element of an array.')
1719
+ return nil
1720
+ end
1721
+
1722
+ inj.keyI = size(inj.keys)
1723
+ inj.setval(inj.dparent, 2)
1724
+ inj.path = inj.path[0...-1]
1725
+ inj.key = getelem(inj.path, -1)
1726
+
1727
+ tvals = parent[1..-1]
1728
+ if size(tvals) == 0
1729
+ inj.errs << ('The $ONE validator at field ' + pathify(inj.path, 1, 1) +
1730
+ ' must have at least one argument.')
1731
+ return nil
1732
+ end
1733
+
1734
+ tvals.each do |tval|
1735
+ terrs = []
1736
+ vstore = merge([{}, store], 1)
1737
+ vstore[S_DTOP] = inj.dparent
1738
+
1739
+ vcurrent = validate(inj.dparent, tval, {
1740
+ 'extra' => vstore,
1741
+ 'errs' => terrs,
1742
+ 'meta' => inj.meta,
1743
+ })
1744
+
1745
+ inj.setval(vcurrent, -2)
1746
+ return nil if size(terrs) == 0
1747
+ end
1748
+
1749
+ valdesc = items(tvals).map { |n| stringify(n[1]) }.join(', ')
1750
+ valdesc = valdesc.gsub(/`\$([A-Z]+)`/) { $1.downcase }
1751
+
1752
+ inj.errs << _invalidTypeMsg(
1753
+ inj.path,
1754
+ (size(tvals) > 1 ? 'one of ' : '') + valdesc,
1755
+ typify(inj.dparent), inj.dparent, 'V0210')
1756
+ end
1757
+ end
1758
+
1759
+ def self.validate_EXACT(inj, _val = nil, _ref = nil, _store = nil)
1760
+ mode = inj.mode
1761
+ parent = inj.parent
1762
+ key = inj.key
1763
+ keyI = inj.keyI
1764
+
1765
+ if S_MVAL == mode
1766
+ if !islist(parent) || 0 != keyI
1767
+ inj.errs << ('The $EXACT validator at field ' + pathify(inj.path, 1, 1) +
1768
+ ' must be the first element of an array.')
1769
+ return nil
1770
+ end
1771
+
1772
+ inj.keyI = size(inj.keys)
1773
+ inj.setval(inj.dparent, 2)
1774
+ inj.path = inj.path[0...-1]
1775
+ inj.key = getelem(inj.path, -1)
1776
+
1777
+ tvals = parent[1..-1]
1778
+ if size(tvals) == 0
1779
+ inj.errs << ('The $EXACT validator at field ' + pathify(inj.path, 1, 1) +
1780
+ ' must have at least one argument.')
1781
+ return nil
1782
+ end
1783
+
1784
+ currentstr = nil
1785
+ tvals.each do |tval|
1786
+ exactmatch = (tval == inj.dparent)
1787
+ if !exactmatch && isnode(tval)
1788
+ currentstr ||= stringify(inj.dparent)
1789
+ exactmatch = stringify(tval) == currentstr
1790
+ end
1791
+ return nil if exactmatch
1792
+ end
1793
+
1794
+ valdesc = items(tvals).map { |n| stringify(n[1]) }.join(', ')
1795
+ valdesc = valdesc.gsub(/`\$([A-Z]+)`/) { $1.downcase }
1796
+
1797
+ inj.errs << _invalidTypeMsg(
1798
+ inj.path,
1799
+ (size(inj.path) > 1 ? '' : 'value ') +
1800
+ 'exactly equal to ' + (size(tvals) == 1 ? '' : 'one of ') + valdesc,
1801
+ typify(inj.dparent), inj.dparent, 'V0110')
1802
+ else
1803
+ delprop(parent, key)
1804
+ end
1805
+ end
1806
+
1807
+ # --- _validation: Modify callback for validate ---
1808
+ def self._validation(pval, key, parent, inj)
1809
+ return if inj.nil?
1810
+ return if pval == SKIP
1811
+
1812
+ exact = getprop(inj.meta, S_BEXACT, false)
1813
+ cval = getprop(inj.dparent, key)
1814
+
1815
+ return if !exact && cval.nil?
1816
+
1817
+ ptype = typify(pval)
1818
+ return if 0 < (T_string & ptype) && pval.is_a?(String) && pval.include?(S_DS)
1819
+
1820
+ ctype = typify(cval)
1821
+
1822
+ if ptype != ctype && !pval.nil?
1823
+ inj.errs << _invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0010')
1824
+ return
1825
+ end
1826
+
1827
+ if ismap(cval)
1828
+ if !ismap(pval)
1829
+ inj.errs << _invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0020')
1830
+ return
1831
+ end
1832
+
1833
+ ckeys = keysof(cval)
1834
+ pkeys = keysof(pval)
1835
+
1836
+ if pkeys.length > 0 && getprop(pval, '`$OPEN`') != true
1837
+ badkeys = ckeys.select { |ck| !haskey(pval, ck) }
1838
+ if badkeys.length > 0
1839
+ inj.errs << ('Unexpected keys at field ' + pathify(inj.path, 1) + S_VIZ + join(badkeys, ', '))
1840
+ end
1841
+ else
1842
+ merge([pval, cval])
1843
+ delprop(pval, '`$OPEN`') if isnode(pval)
1844
+ end
1845
+
1846
+ elsif islist(cval)
1847
+ if !islist(pval)
1848
+ inj.errs << _invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0030')
1849
+ end
1850
+
1851
+ elsif exact
1852
+ # In exact mode, check key existence for nil values
1853
+ if cval.nil? && pval.nil?
1854
+ # Both nil: only match if key actually exists in data
1855
+ if ismap(inj.dparent) && !inj.dparent.key?(key.to_s)
1856
+ inj.errs << ('Value at field ' + pathify(inj.path, 1) + ': key not present.')
1857
+ end
1858
+ elsif cval != pval
1859
+ pathmsg = size(inj.path) > 1 ? ('at field ' + pathify(inj.path, 1) + ': ') : ''
1860
+ inj.errs << ('Value ' + pathmsg + cval.to_s + ' should equal ' + pval.to_s + '.')
1861
+ end
1862
+
1863
+ else
1864
+ setprop(parent, key, cval)
1865
+ end
1866
+ end
1867
+
1868
+ def self._validatehandler(inj, val, ref, store)
1869
+ out = val
1870
+ m = ref.is_a?(String) ? R_META_PATH.match(ref) : nil
1871
+
1872
+ if m
1873
+ if m[2] == '='
1874
+ inj.setval([S_BEXACT, val])
1875
+ else
1876
+ inj.setval(val)
1877
+ end
1878
+ inj.keyI = -1
1879
+ out = SKIP
1880
+ else
1881
+ out = _injecthandler(inj, val, ref, store)
1882
+ end
1883
+
1884
+ out
1885
+ end
1886
+
1887
+ # --- validate: Validate data against shape spec ---
1888
+ def self.validate(data, spec, injdef = nil)
1889
+ extra = _injdef_prop(injdef, 'extra')
1890
+ collect = !_injdef_prop(injdef, 'errs').nil?
1891
+ errs = collect ? _injdef_prop(injdef, 'errs') : []
1892
+
1893
+ store = merge([
1894
+ {
1895
+ '$DELETE' => nil, '$COPY' => nil, '$KEY' => nil, '$META' => nil,
1896
+ '$MERGE' => nil, '$EACH' => nil, '$PACK' => nil,
1897
+
1898
+ '$STRING' => method(:validate_STRING),
1899
+ '$NUMBER' => method(:validate_TYPE),
1900
+ '$INTEGER' => method(:validate_TYPE),
1901
+ '$DECIMAL' => method(:validate_TYPE),
1902
+ '$BOOLEAN' => method(:validate_TYPE),
1903
+ '$NULL' => method(:validate_TYPE),
1904
+ '$NIL' => method(:validate_TYPE),
1905
+ '$MAP' => method(:validate_TYPE),
1906
+ '$LIST' => method(:validate_TYPE),
1907
+ '$FUNCTION' => method(:validate_TYPE),
1908
+ '$INSTANCE' => method(:validate_TYPE),
1909
+ '$ANY' => method(:validate_ANY),
1910
+ '$CHILD' => method(:validate_CHILD),
1911
+ '$ONE' => method(:validate_ONE),
1912
+ '$EXACT' => method(:validate_EXACT),
1913
+ },
1914
+ (extra.nil? ? {} : extra),
1915
+ { S_DERRS => errs },
1916
+ ], 1)
1917
+
1918
+ meta = _injdef_prop(injdef, 'meta') || {}
1919
+ setprop(meta, S_BEXACT, getprop(meta, S_BEXACT, false)) if ismap(meta)
1920
+
1921
+ out = transform(data, spec, {
1922
+ 'meta' => meta,
1923
+ 'extra' => store,
1924
+ 'modify' => method(:_validation),
1925
+ 'handler' => method(:_validatehandler),
1926
+ 'errs' => errs,
1927
+ })
1928
+
1929
+ if !errs.empty? && !collect
1930
+ raise errs.join(' | ')
1931
+ end
1932
+
1933
+ out
1934
+ end
1935
+
1936
+ # --- Select operators ---
1937
+
1938
+ def self.select_AND(inj, _val, _ref, store)
1939
+ if S_MKEYPRE == inj.mode
1940
+ terms = getprop(inj.parent, inj.key)
1941
+ ppath = slice(inj.path, -1)
1942
+ point = getpath(store, ppath)
1943
+
1944
+ vstore = merge([{}, store], 1)
1945
+ vstore[S_DTOP] = point
1946
+
1947
+ terms.each do |term|
1948
+ terrs = []
1949
+ validate(point, term, {
1950
+ 'extra' => vstore,
1951
+ 'errs' => terrs,
1952
+ 'meta' => inj.meta,
1953
+ })
1954
+ if !terrs.empty?
1955
+ inj.errs << ('AND:' + pathify(ppath) + "\u2A2F" + stringify(point) +
1956
+ ' fail:' + stringify(terms))
1957
+ end
1958
+ end
1959
+
1960
+ gkey = getelem(inj.path, -2)
1961
+ gp = getelem(inj.nodes, -2)
1962
+ setprop(gp, gkey, point)
1963
+ end
1964
+ nil
1965
+ end
1966
+
1967
+ def self.select_OR(inj, _val, _ref, store)
1968
+ if S_MKEYPRE == inj.mode
1969
+ terms = getprop(inj.parent, inj.key)
1970
+ ppath = slice(inj.path, -1)
1971
+ point = getpath(store, ppath)
1972
+
1973
+ vstore = merge([{}, store], 1)
1974
+ vstore[S_DTOP] = point
1975
+
1976
+ terms.each do |term|
1977
+ terrs = []
1978
+ validate(point, term, {
1979
+ 'extra' => vstore,
1980
+ 'errs' => terrs,
1981
+ 'meta' => inj.meta,
1982
+ })
1983
+ if terrs.empty?
1984
+ gkey = getelem(inj.path, -2)
1985
+ gp = getelem(inj.nodes, -2)
1986
+ setprop(gp, gkey, point)
1987
+ return nil
1988
+ end
1989
+ end
1990
+
1991
+ inj.errs << ('OR:' + pathify(ppath) + "\u2A2F" + stringify(point) +
1992
+ ' fail:' + stringify(terms))
1993
+ end
1994
+ nil
1995
+ end
1996
+
1997
+ def self.select_NOT(inj, _val, _ref, store)
1998
+ if S_MKEYPRE == inj.mode
1999
+ term = getprop(inj.parent, inj.key)
2000
+ ppath = slice(inj.path, -1)
2001
+ point = getpath(store, ppath)
2002
+
2003
+ vstore = merge([{}, store], 1)
2004
+ vstore[S_DTOP] = point
2005
+
2006
+ terrs = []
2007
+ validate(point, term, {
2008
+ 'extra' => vstore,
2009
+ 'errs' => terrs,
2010
+ 'meta' => inj.meta,
2011
+ })
2012
+
2013
+ if terrs.empty?
2014
+ inj.errs << ('NOT:' + pathify(ppath) + "\u2A2F" + stringify(point) +
2015
+ ' fail:' + stringify(term))
2016
+ end
2017
+
2018
+ gkey = getelem(inj.path, -2)
2019
+ gp = getelem(inj.nodes, -2)
2020
+ setprop(gp, gkey, point)
2021
+ end
2022
+ nil
2023
+ end
2024
+
2025
+ def self.select_CMP(inj, _val, ref, store)
2026
+ if S_MKEYPRE == inj.mode
2027
+ term = getprop(inj.parent, inj.key)
2028
+ gkey = getelem(inj.path, -2)
2029
+ ppath = slice(inj.path, -1)
2030
+ point = getpath(store, ppath)
2031
+
2032
+ pass_test = false
2033
+
2034
+ begin
2035
+ if '$GT' == ref && point > term
2036
+ pass_test = true
2037
+ elsif '$LT' == ref && point < term
2038
+ pass_test = true
2039
+ elsif '$GTE' == ref && point >= term
2040
+ pass_test = true
2041
+ elsif '$LTE' == ref && point <= term
2042
+ pass_test = true
2043
+ elsif '$LIKE' == ref
2044
+ pass_test = true if stringify(point).match?(Regexp.new(term.to_s))
2045
+ end
2046
+ rescue
2047
+ end
2048
+
2049
+ if pass_test
2050
+ gp = getelem(inj.nodes, -2)
2051
+ setprop(gp, gkey, point)
2052
+ else
2053
+ inj.errs << ('CMP: ' + pathify(ppath) + "\u2A2F" + stringify(point) +
2054
+ ' fail:' + ref.to_s + ' ' + stringify(term))
2055
+ end
2056
+ end
2057
+ nil
2058
+ end
2059
+
2060
+ # --- select: Select children matching query ---
2061
+ def self.select(children, query)
2062
+ return [] unless isnode(children)
2063
+
2064
+ if ismap(children)
2065
+ children = items(children).map { |item|
2066
+ v = item[1]
2067
+ setprop(v, '$KEY', item[0]) if ismap(v)
2068
+ v
2069
+ }
2070
+ else
2071
+ children = children.each_with_index.map { |n, i|
2072
+ setprop(n, '$KEY', i) if ismap(n)
2073
+ n
2074
+ }
2075
+ end
2076
+
2077
+ results = []
2078
+ q = clone(query)
2079
+
2080
+ # Add $OPEN to all maps in query
2081
+ walk(q, lambda { |_k, v, _p, _t|
2082
+ setprop(v, '`$OPEN`', getprop(v, '`$OPEN`', true)) if ismap(v)
2083
+ v
2084
+ })
2085
+
2086
+ select_extra = {
2087
+ '$AND' => method(:select_AND),
2088
+ '$OR' => method(:select_OR),
2089
+ '$NOT' => method(:select_NOT),
2090
+ '$GT' => method(:select_CMP),
2091
+ '$LT' => method(:select_CMP),
2092
+ '$GTE' => method(:select_CMP),
2093
+ '$LTE' => method(:select_CMP),
2094
+ '$LIKE' => method(:select_CMP),
2095
+ }
2096
+
2097
+ children.each do |child|
2098
+ terrs = []
2099
+ validate(child, clone(q), {
2100
+ 'errs' => terrs,
2101
+ 'meta' => { S_BEXACT => true },
2102
+ 'extra' => select_extra,
2103
+ })
2104
+ results << child if terrs.empty?
2105
+ end
2106
+
2107
+ results
2108
+ end
2109
+
2110
+ # --- setpath ---
2111
+ def self.setpath(store, path, val, injdef = nil)
2112
+ pt = typify(path)
2113
+ if 0 < (T_list & pt)
2114
+ parts = path
2115
+ elsif 0 < (T_string & pt)
2116
+ parts = path.split(S_DT)
2117
+ elsif 0 < (T_number & pt)
2118
+ parts = [path]
2119
+ else
2120
+ return nil
2121
+ end
2122
+
2123
+ base = _injdef_prop(injdef, 'base')
2124
+ numparts = size(parts)
2125
+ parent = base ? getprop(store, base, store) : store
2126
+
2127
+ (0...numparts - 1).each do |pI|
2128
+ part_key = getelem(parts, pI)
2129
+ next_parent = getprop(parent, part_key)
2130
+ unless isnode(next_parent)
2131
+ next_part = getelem(parts, pI + 1)
2132
+ next_parent = (0 < (T_number & typify(next_part))) ? [] : {}
2133
+ setprop(parent, part_key, next_parent)
2134
+ end
2135
+ parent = next_parent
2136
+ end
2137
+
2138
+ if val == DELETE
2139
+ delprop(parent, getelem(parts, -1))
2140
+ else
2141
+ setprop(parent, getelem(parts, -1), val)
2142
+ end
2143
+
2144
+ parent
2145
+ end
2146
+
2147
+
2148
+ # --- Injection class ---
2149
+ class Injection
2150
+ attr_accessor :mode, :full, :keyI, :keys, :key, :val, :parent,
2151
+ :path, :nodes, :handler, :errs, :meta, :base,
2152
+ :modify, :extra, :prior, :dparent, :dpath, :root
2153
+
2154
+ def initialize(val, parent)
2155
+ @mode = VoxgigStruct::S_MVAL
2156
+ @full = false
2157
+ @keyI = 0
2158
+ @keys = [VoxgigStruct::S_DTOP]
2159
+ @key = VoxgigStruct::S_DTOP
2160
+ @val = val
2161
+ @parent = parent
2162
+ @path = [VoxgigStruct::S_DTOP]
2163
+ @nodes = [parent]
2164
+ @handler = nil
2165
+ @errs = []
2166
+ @meta = {}
2167
+ @base = nil
2168
+ @modify = nil
2169
+ @extra = nil
2170
+ @prior = nil
2171
+ @dparent = nil
2172
+ @dpath = [VoxgigStruct::S_DTOP]
2173
+ @root = nil
2174
+ end
2175
+
2176
+ def descend
2177
+ @meta['__d'] = (@meta['__d'] || 0) + 1
2178
+
2179
+ parentkey = VoxgigStruct.getelem(@path, -2)
2180
+
2181
+ if @dparent.nil?
2182
+ if VoxgigStruct.size(@dpath) > 1
2183
+ @dpath = @dpath + [parentkey]
2184
+ end
2185
+ else
2186
+ if parentkey
2187
+ @dparent = VoxgigStruct.getprop(@dparent, parentkey)
2188
+ lastpart = VoxgigStruct.getelem(@dpath, -1)
2189
+ if lastpart == '$:' + parentkey.to_s
2190
+ @dpath = VoxgigStruct.slice(@dpath, -1)
2191
+ else
2192
+ @dpath = @dpath + [parentkey]
2193
+ end
2194
+ end
2195
+ end
2196
+
2197
+ @dparent
2198
+ end
2199
+
2200
+ def child(keyI, keys)
2201
+ key = VoxgigStruct.strkey(keys[keyI])
2202
+ val = @val
2203
+
2204
+ cinj = Injection.new(VoxgigStruct.getprop(val, key), val)
2205
+ cinj.mode = @mode
2206
+ cinj.full = @full
2207
+ cinj.keyI = keyI
2208
+ cinj.keys = keys
2209
+ cinj.key = key
2210
+ cinj.path = @path + [key]
2211
+ cinj.nodes = @nodes + [val]
2212
+ cinj.handler = @handler
2213
+ cinj.errs = @errs
2214
+ cinj.meta = @meta
2215
+ cinj.base = @base
2216
+ cinj.modify = @modify
2217
+ cinj.prior = self
2218
+ cinj.dpath = @dpath.dup
2219
+ cinj.dparent = @dparent
2220
+ cinj.extra = @extra
2221
+ cinj.root = @root
2222
+
2223
+ cinj
2224
+ end
2225
+
2226
+ def setval(val, ancestor = nil)
2227
+ if val.nil? && (ancestor.nil? || (ancestor.is_a?(Numeric) && ancestor < 2))
2228
+ # nil without ancestor: delete from parent (matches TS undefined)
2229
+ VoxgigStruct.delprop(@parent, @key)
2230
+ elsif val.nil? && ancestor.is_a?(Numeric) && ancestor >= 2
2231
+ # nil with ancestor: set to nil in grandparent (preserves key for $ONE/$EXACT)
2232
+ VoxgigStruct.setprop(
2233
+ VoxgigStruct.getelem(@nodes, 0 - ancestor),
2234
+ VoxgigStruct.getelem(@path, 0 - ancestor),
2235
+ val
2236
+ )
2237
+ elsif ancestor.nil? || (ancestor.is_a?(Numeric) && ancestor < 2)
2238
+ VoxgigStruct.setprop(@parent, @key, val)
2239
+ else
2240
+ VoxgigStruct.setprop(
2241
+ VoxgigStruct.getelem(@nodes, 0 - ancestor),
2242
+ VoxgigStruct.getelem(@path, 0 - ancestor),
2243
+ val
2244
+ )
2245
+ end
2246
+ end
2247
+
2248
+ def to_s(prefix = nil)
2249
+ 'INJ' + (prefix ? '/' + prefix : '') + ':' +
2250
+ VoxgigStruct.pad(VoxgigStruct.pathify(@path, 1)) +
2251
+ (VoxgigStruct::MODENAME[VoxgigStruct::M_VAL] || '') + (@full ? '/full' : '') + ':' +
2252
+ 'key=' + @keyI.to_s + '/' + @key.to_s
2253
+ end
2254
+ end
2255
+
2256
+ end