@voxgig/sdkgen-langpack 0.0.1

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 (300) hide show
  1. package/.sdk/model/target/dart.aon +63 -0
  2. package/.sdk/model/target/haskell.aon +72 -0
  3. package/.sdk/model/target/lean.aon +91 -0
  4. package/.sdk/src/cmp/dart/Config_dart.ts +248 -0
  5. package/.sdk/src/cmp/dart/EntityBase_dart.ts +34 -0
  6. package/.sdk/src/cmp/dart/EntityOperation_dart.ts +43 -0
  7. package/.sdk/src/cmp/dart/EntityTypes_dart.ts +207 -0
  8. package/.sdk/src/cmp/dart/Entity_dart.ts +87 -0
  9. package/.sdk/src/cmp/dart/Gitignore_dart.ts +34 -0
  10. package/.sdk/src/cmp/dart/MainEntity_dart.ts +33 -0
  11. package/.sdk/src/cmp/dart/Main_dart.ts +178 -0
  12. package/.sdk/src/cmp/dart/Package_dart.ts +82 -0
  13. package/.sdk/src/cmp/dart/ReadmeEntity_dart.ts +177 -0
  14. package/.sdk/src/cmp/dart/ReadmeExamplesTest_dart.ts +238 -0
  15. package/.sdk/src/cmp/dart/ReadmeExplanation_dart.ts +53 -0
  16. package/.sdk/src/cmp/dart/ReadmeHowto_dart.ts +152 -0
  17. package/.sdk/src/cmp/dart/ReadmeInstall_dart.ts +59 -0
  18. package/.sdk/src/cmp/dart/ReadmeIntro_dart.ts +65 -0
  19. package/.sdk/src/cmp/dart/ReadmeModel_dart.ts +151 -0
  20. package/.sdk/src/cmp/dart/ReadmeOptions_dart.ts +63 -0
  21. package/.sdk/src/cmp/dart/ReadmeQuick_dart.ts +225 -0
  22. package/.sdk/src/cmp/dart/ReadmeRef_dart.ts +397 -0
  23. package/.sdk/src/cmp/dart/ReadmeTopHowto_dart.ts +24 -0
  24. package/.sdk/src/cmp/dart/ReadmeTopQuick_dart.ts +106 -0
  25. package/.sdk/src/cmp/dart/ReadmeTopTest_dart.ts +78 -0
  26. package/.sdk/src/cmp/dart/SdkError_dart.ts +42 -0
  27. package/.sdk/src/cmp/dart/TestDirect_dart.ts +516 -0
  28. package/.sdk/src/cmp/dart/TestEntity_dart.ts +628 -0
  29. package/.sdk/src/cmp/dart/Test_dart.ts +138 -0
  30. package/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +98 -0
  31. package/.sdk/src/cmp/dart/fragment/Config.fragment.dart +70 -0
  32. package/.sdk/src/cmp/dart/fragment/Direct.test.fragment.dart +25 -0
  33. package/.sdk/src/cmp/dart/fragment/Entity.fragment.dart +29 -0
  34. package/.sdk/src/cmp/dart/fragment/Entity.test.fragment.dart +28 -0
  35. package/.sdk/src/cmp/dart/fragment/EntityBase.fragment.dart +302 -0
  36. package/.sdk/src/cmp/dart/fragment/EntityCreateOp.fragment.dart +98 -0
  37. package/.sdk/src/cmp/dart/fragment/EntityListOp.fragment.dart +90 -0
  38. package/.sdk/src/cmp/dart/fragment/EntityLoadOp.fragment.dart +102 -0
  39. package/.sdk/src/cmp/dart/fragment/EntityRemoveOp.fragment.dart +108 -0
  40. package/.sdk/src/cmp/dart/fragment/EntityUpdateOp.fragment.dart +102 -0
  41. package/.sdk/src/cmp/dart/fragment/Main.fragment.dart +339 -0
  42. package/.sdk/src/cmp/dart/fragment/SdkError.fragment.dart +26 -0
  43. package/.sdk/src/cmp/dart/tsconfig.json +15 -0
  44. package/.sdk/src/cmp/dart/utility_dart.ts +155 -0
  45. package/.sdk/src/cmp/haskell/Config_haskell.ts +98 -0
  46. package/.sdk/src/cmp/haskell/Entity_haskell.ts +13 -0
  47. package/.sdk/src/cmp/haskell/Gitignore_haskell.ts +27 -0
  48. package/.sdk/src/cmp/haskell/Main_haskell.ts +123 -0
  49. package/.sdk/src/cmp/haskell/Package_haskell.ts +60 -0
  50. package/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +186 -0
  51. package/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +135 -0
  52. package/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +48 -0
  53. package/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +168 -0
  54. package/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +51 -0
  55. package/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +67 -0
  56. package/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +162 -0
  57. package/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +74 -0
  58. package/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +241 -0
  59. package/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +408 -0
  60. package/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +31 -0
  61. package/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +104 -0
  62. package/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +80 -0
  63. package/.sdk/src/cmp/haskell/Test_haskell.ts +297 -0
  64. package/.sdk/src/cmp/haskell/tsconfig.json +15 -0
  65. package/.sdk/src/cmp/haskell/utility_haskell.ts +166 -0
  66. package/.sdk/src/cmp/lean/Config_lean.ts +93 -0
  67. package/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
  68. package/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
  69. package/.sdk/src/cmp/lean/Main_lean.ts +120 -0
  70. package/.sdk/src/cmp/lean/Package_lean.ts +93 -0
  71. package/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
  72. package/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
  73. package/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
  74. package/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +39 -0
  75. package/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
  76. package/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
  77. package/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
  78. package/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
  79. package/.sdk/src/cmp/lean/ReadmeRef_lean.ts +40 -0
  80. package/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
  81. package/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
  82. package/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +19 -0
  83. package/.sdk/src/cmp/lean/Test_lean.ts +261 -0
  84. package/.sdk/src/cmp/lean/utility_lean.ts +129 -0
  85. package/.sdk/tm/dart/LICENSE +22 -0
  86. package/.sdk/tm/dart/Makefile +50 -0
  87. package/.sdk/tm/dart/lib/Context.dart +150 -0
  88. package/.sdk/tm/dart/lib/Control.dart +15 -0
  89. package/.sdk/tm/dart/lib/Operation.dart +31 -0
  90. package/.sdk/tm/dart/lib/Point.dart +51 -0
  91. package/.sdk/tm/dart/lib/Response.dart +32 -0
  92. package/.sdk/tm/dart/lib/Result.dart +39 -0
  93. package/.sdk/tm/dart/lib/Spec.dart +49 -0
  94. package/.sdk/tm/dart/lib/feature/audit/AuditFeature.dart +103 -0
  95. package/.sdk/tm/dart/lib/feature/base/BaseFeature.dart +76 -0
  96. package/.sdk/tm/dart/lib/feature/cache/CacheFeature.dart +152 -0
  97. package/.sdk/tm/dart/lib/feature/clienttrack/ClienttrackFeature.dart +104 -0
  98. package/.sdk/tm/dart/lib/feature/cost/CostFeature.dart +446 -0
  99. package/.sdk/tm/dart/lib/feature/debug/DebugFeature.dart +153 -0
  100. package/.sdk/tm/dart/lib/feature/idempotency/IdempotencyFeature.dart +92 -0
  101. package/.sdk/tm/dart/lib/feature/log/LogFeature.dart +85 -0
  102. package/.sdk/tm/dart/lib/feature/metrics/MetricsFeature.dart +103 -0
  103. package/.sdk/tm/dart/lib/feature/netsim/NetsimFeature.dart +186 -0
  104. package/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +240 -0
  105. package/.sdk/tm/dart/lib/feature/proxy/ProxyFeature.dart +118 -0
  106. package/.sdk/tm/dart/lib/feature/ratelimit/RatelimitFeature.dart +103 -0
  107. package/.sdk/tm/dart/lib/feature/rbac/RbacFeature.dart +121 -0
  108. package/.sdk/tm/dart/lib/feature/retry/RetryFeature.dart +155 -0
  109. package/.sdk/tm/dart/lib/feature/secrets/SecretsFeature.dart +649 -0
  110. package/.sdk/tm/dart/lib/feature/secrets/plugin/capability.dart +105 -0
  111. package/.sdk/tm/dart/lib/feature/secrets/plugin/catalog.dart +46 -0
  112. package/.sdk/tm/dart/lib/feature/secrets/plugin/config.dart +297 -0
  113. package/.sdk/tm/dart/lib/feature/secrets/plugin/depend.dart +198 -0
  114. package/.sdk/tm/dart/lib/feature/secrets/plugin/env.dart +144 -0
  115. package/.sdk/tm/dart/lib/feature/secrets/plugin/export.dart +58 -0
  116. package/.sdk/tm/dart/lib/feature/secrets/plugin/graph.dart +160 -0
  117. package/.sdk/tm/dart/lib/feature/secrets/plugin/host.dart +1067 -0
  118. package/.sdk/tm/dart/lib/feature/secrets/plugin/order.dart +172 -0
  119. package/.sdk/tm/dart/lib/feature/secrets/plugin/plugin.dart +23 -0
  120. package/.sdk/tm/dart/lib/feature/secrets/plugin/point.dart +117 -0
  121. package/.sdk/tm/dart/lib/feature/secrets/plugin/ref.dart +105 -0
  122. package/.sdk/tm/dart/lib/feature/secrets/plugin/resolve.dart +58 -0
  123. package/.sdk/tm/dart/lib/feature/secrets/plugin/types.dart +201 -0
  124. package/.sdk/tm/dart/lib/feature/secrets/plugin/version.dart +116 -0
  125. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/aws.dart +285 -0
  126. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/azuresecrets.dart +178 -0
  127. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/boru.dart +145 -0
  128. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/crypto.dart +194 -0
  129. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/doppler.dart +101 -0
  130. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/gcpsecrets.dart +143 -0
  131. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/hashicorp.dart +190 -0
  132. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/httpjson.dart +363 -0
  133. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/infisical.dart +149 -0
  134. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/onepassword.dart +146 -0
  135. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/secretspec.dart +116 -0
  136. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/sigv4.dart +237 -0
  137. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/addr.dart +138 -0
  138. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/json.dart +276 -0
  139. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/provider.dart +31 -0
  140. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/providers.dart +243 -0
  141. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/sekreto.dart +730 -0
  142. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/spec.dart +200 -0
  143. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/support.dart +217 -0
  144. package/.sdk/tm/dart/lib/feature/streaming/StreamingFeature.dart +97 -0
  145. package/.sdk/tm/dart/lib/feature/telemetry/TelemetryFeature.dart +136 -0
  146. package/.sdk/tm/dart/lib/feature/test/TestFeature.dart +328 -0
  147. package/.sdk/tm/dart/lib/feature/timeout/TimeoutFeature.dart +104 -0
  148. package/.sdk/tm/dart/lib/utility/CleanUtility.dart +12 -0
  149. package/.sdk/tm/dart/lib/utility/DoneUtility.dart +18 -0
  150. package/.sdk/tm/dart/lib/utility/ErrUtility.dart +46 -0
  151. package/.sdk/tm/dart/lib/utility/FeatureAddUtility.dart +35 -0
  152. package/.sdk/tm/dart/lib/utility/FeatureHookUtility.dart +20 -0
  153. package/.sdk/tm/dart/lib/utility/FeatureInitUtility.dart +9 -0
  154. package/.sdk/tm/dart/lib/utility/FetcherUtility.dart +104 -0
  155. package/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
  156. package/.sdk/tm/dart/lib/utility/MakeContextUtility.dart +6 -0
  157. package/.sdk/tm/dart/lib/utility/MakeErrorUtility.dart +71 -0
  158. package/.sdk/tm/dart/lib/utility/MakeFetchDefUtility.dart +42 -0
  159. package/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +233 -0
  160. package/.sdk/tm/dart/lib/utility/MakePointUtility.dart +130 -0
  161. package/.sdk/tm/dart/lib/utility/MakeRequestUtility.dart +66 -0
  162. package/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +59 -0
  163. package/.sdk/tm/dart/lib/utility/MakeResultUtility.dart +49 -0
  164. package/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +77 -0
  165. package/.sdk/tm/dart/lib/utility/MakeUrlUtility.dart +51 -0
  166. package/.sdk/tm/dart/lib/utility/ParamUtility.dart +56 -0
  167. package/.sdk/tm/dart/lib/utility/PrepareAuthUtility.dart +46 -0
  168. package/.sdk/tm/dart/lib/utility/PrepareBodyUtility.dart +19 -0
  169. package/.sdk/tm/dart/lib/utility/PrepareHeadersUtility.dart +11 -0
  170. package/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +30 -0
  171. package/.sdk/tm/dart/lib/utility/PrepareParamsUtility.dart +23 -0
  172. package/.sdk/tm/dart/lib/utility/PreparePathUtility.dart +9 -0
  173. package/.sdk/tm/dart/lib/utility/PrepareQueryUtility.dart +18 -0
  174. package/.sdk/tm/dart/lib/utility/ResultBasicUtility.dart +29 -0
  175. package/.sdk/tm/dart/lib/utility/ResultBodyUtility.dart +15 -0
  176. package/.sdk/tm/dart/lib/utility/ResultHeadersUtility.dart +18 -0
  177. package/.sdk/tm/dart/lib/utility/StructUtility.dart +127 -0
  178. package/.sdk/tm/dart/lib/utility/TransformRequestUtility.dart +28 -0
  179. package/.sdk/tm/dart/lib/utility/TransformResponseUtility.dart +44 -0
  180. package/.sdk/tm/dart/lib/utility/Utility.dart +239 -0
  181. package/.sdk/tm/dart/lib/utility/voxgig_struct.dart +2462 -0
  182. package/.sdk/tm/dart/src/feature/README.md +3 -0
  183. package/.sdk/tm/dart/src/feature/audit/.gitkeep +0 -0
  184. package/.sdk/tm/dart/src/feature/base/.gitkeep +0 -0
  185. package/.sdk/tm/dart/src/feature/cache/.gitkeep +0 -0
  186. package/.sdk/tm/dart/src/feature/clienttrack/.gitkeep +0 -0
  187. package/.sdk/tm/dart/src/feature/cost/.gitkeep +0 -0
  188. package/.sdk/tm/dart/src/feature/debug/.gitkeep +0 -0
  189. package/.sdk/tm/dart/src/feature/idempotency/.gitkeep +0 -0
  190. package/.sdk/tm/dart/src/feature/log/.gitkeep +0 -0
  191. package/.sdk/tm/dart/src/feature/metrics/.gitkeep +0 -0
  192. package/.sdk/tm/dart/src/feature/netsim/.gitkeep +0 -0
  193. package/.sdk/tm/dart/src/feature/paging/.gitkeep +0 -0
  194. package/.sdk/tm/dart/src/feature/proxy/.gitkeep +0 -0
  195. package/.sdk/tm/dart/src/feature/ratelimit/.gitkeep +0 -0
  196. package/.sdk/tm/dart/src/feature/rbac/.gitkeep +0 -0
  197. package/.sdk/tm/dart/src/feature/retry/.gitkeep +0 -0
  198. package/.sdk/tm/dart/src/feature/secrets/.gitkeep +0 -0
  199. package/.sdk/tm/dart/src/feature/streaming/.gitkeep +0 -0
  200. package/.sdk/tm/dart/src/feature/telemetry/.gitkeep +0 -0
  201. package/.sdk/tm/dart/src/feature/test/.gitkeep +0 -0
  202. package/.sdk/tm/dart/src/feature/timeout/.gitkeep +0 -0
  203. package/.sdk/tm/dart/test/custom_test.dart +65 -0
  204. package/.sdk/tm/dart/test/exists_test.dart +12 -0
  205. package/.sdk/tm/dart/test/feature/harness.dart +347 -0
  206. package/.sdk/tm/dart/test/feature/secrets/secrets_test.dart +1107 -0
  207. package/.sdk/tm/dart/test/feature_test.dart +1193 -0
  208. package/.sdk/tm/dart/test/harness.dart +153 -0
  209. package/.sdk/tm/dart/test/netsim_test.dart +48 -0
  210. package/.sdk/tm/dart/test/omni.dart +520 -0
  211. package/.sdk/tm/dart/test/omni_smoke_test.dart +143 -0
  212. package/.sdk/tm/dart/test/pipeline_test.dart +608 -0
  213. package/.sdk/tm/dart/test/primary_test.dart +476 -0
  214. package/.sdk/tm/dart/test/sdk-test-control.json +19 -0
  215. package/.sdk/tm/dart/test/struct_test.dart +468 -0
  216. package/.sdk/tm/dart/test/utility.dart +198 -0
  217. package/.sdk/tm/dart/test/vendor/omni/omni.dart +7 -0
  218. package/.sdk/tm/dart/test/vendor/omni/runner.dart +653 -0
  219. package/.sdk/tm/dart/test/vendor/omni/util.dart +232 -0
  220. package/.sdk/tm/haskell/LICENSE +21 -0
  221. package/.sdk/tm/haskell/Makefile +22 -0
  222. package/.sdk/tm/haskell/VERSION +1 -0
  223. package/.sdk/tm/haskell/src/SdkFeatures.hs +1440 -0
  224. package/.sdk/tm/haskell/src/SdkHelpers.hs +337 -0
  225. package/.sdk/tm/haskell/src/SdkJson.hs +111 -0
  226. package/.sdk/tm/haskell/src/SdkRuntime.hs +1279 -0
  227. package/.sdk/tm/haskell/src/SdkTypes.hs +195 -0
  228. package/.sdk/tm/haskell/src/VoxgigStruct.hs +2299 -0
  229. package/.sdk/tm/haskell/src/Vregex.hs +237 -0
  230. package/.sdk/tm/haskell/src/feature/README.md +5 -0
  231. package/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
  232. package/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
  233. package/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
  234. package/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
  235. package/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
  236. package/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
  237. package/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
  238. package/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
  239. package/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
  240. package/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
  241. package/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
  242. package/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
  243. package/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
  244. package/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
  245. package/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
  246. package/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
  247. package/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
  248. package/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
  249. package/.sdk/tm/haskell/test/Harness.hs +281 -0
  250. package/.sdk/tm/haskell/test/OmniResolver.hs +252 -0
  251. package/.sdk/tm/haskell/test/Runner.hs +49 -0
  252. package/.sdk/tm/haskell/test/StructCorpus.hs +449 -0
  253. package/.sdk/tm/haskell/test/TCustomUtility.hs +38 -0
  254. package/.sdk/tm/haskell/test/TFeature.hs +794 -0
  255. package/.sdk/tm/haskell/test/TNetsim.hs +50 -0
  256. package/.sdk/tm/haskell/test/TPipeline.hs +334 -0
  257. package/.sdk/tm/haskell/test/TPrimaryCorpus.hs +263 -0
  258. package/.sdk/tm/haskell/test/TPrimaryUtility.hs +386 -0
  259. package/.sdk/tm/haskell/test/Testutil.hs +59 -0
  260. package/.sdk/tm/haskell/test/vendor/omni/Omni.hs +1147 -0
  261. package/.sdk/tm/lean/LICENSE +21 -0
  262. package/.sdk/tm/lean/Makefile +32 -0
  263. package/.sdk/tm/lean/VERSION +1 -0
  264. package/.sdk/tm/lean/src/SdkFeature.lean +171 -0
  265. package/.sdk/tm/lean/src/SdkFeatures.lean +787 -0
  266. package/.sdk/tm/lean/src/SdkJson.lean +134 -0
  267. package/.sdk/tm/lean/src/SdkRuntime.lean +374 -0
  268. package/.sdk/tm/lean/src/SdkUtility.lean +809 -0
  269. package/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
  270. package/.sdk/tm/lean/src/Vregex.lean +412 -0
  271. package/.sdk/tm/lean/src/feature/README.md +5 -0
  272. package/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  273. package/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  274. package/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  275. package/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  276. package/.sdk/tm/lean/src/feature/cost/.gitkeep +0 -0
  277. package/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  278. package/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  279. package/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  280. package/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  281. package/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  282. package/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  283. package/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  284. package/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  285. package/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  286. package/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  287. package/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  288. package/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  289. package/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  290. package/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  291. package/.sdk/tm/lean/test/OmniResolver.lean +358 -0
  292. package/.sdk/tm/lean/test/OmniSmoke.lean +174 -0
  293. package/.sdk/tm/lean/test/StructCorpus.lean +368 -0
  294. package/.sdk/tm/lean/test/TFeature.lean +281 -0
  295. package/.sdk/tm/lean/test/TPrimaryUtility.lean +227 -0
  296. package/.sdk/tm/lean/test/vendor/omni/Omni.lean +997 -0
  297. package/LICENSE +21 -0
  298. package/README.md +104 -0
  299. package/package.json +78 -0
  300. package/sdkgen-package.json +22 -0
@@ -0,0 +1,1440 @@
1
+ -- ProjectName SDK features + API-agnostic client helpers.
2
+ --
3
+ -- The 18 pipeline features (base/test/log + the 15 enterprise features) and
4
+ -- the transport they wrap, plus make_client_base / direct / prepare / test and
5
+ -- the generic (config-driven) entity constructor. Each feature is built by an
6
+ -- IO constructor that allocates IORefs for its mutable state and returns a
7
+ -- `Feature` whose init/hook closures observe and mutate the shared pipeline
8
+ -- state. Transport-wrapping features re-bind the mutable `uFetcher` cell so
9
+ -- later inits sit outermost.
10
+
11
+ module SdkFeatures where
12
+
13
+ import Control.Concurrent (threadDelay)
14
+ import Control.Exception (throwIO, try)
15
+ import Control.Monad (forM_, when)
16
+ import Data.Bits ((.&.))
17
+ import Data.IORef
18
+ import Data.Maybe (isJust, isNothing)
19
+ import Data.Time.Clock.POSIX (getPOSIXTime)
20
+ import System.Environment (lookupEnv)
21
+
22
+ import VoxgigStruct
23
+ ( Value (..), InjArg (..), emptyList, emptyMap, mkList
24
+ , getprop, setprop, delprop, getelem, keysof, listItems
25
+ , clone, merge, transform, select, size, isempty, ismap, isNoval, isNullish
26
+ , vint, setpath, walk )
27
+ import SdkTypes
28
+ import SdkHelpers
29
+ import SdkRuntime
30
+
31
+ -- ------------------------------------------------------------------
32
+ -- clock / option readers
33
+ -- ------------------------------------------------------------------
34
+
35
+ defaultNowMs :: IO Double
36
+ defaultNowMs = (* 1000) . realToFrac <$> getPOSIXTime
37
+
38
+ realSleep :: Double -> IO ()
39
+ realSleep ms = when (ms > 0) (threadDelay (round (ms * 1000)))
40
+
41
+ optNum :: Value -> String -> Double -> IO Double
42
+ optNum opts k d = do v <- getp opts k; pure (case v of VNum n -> n; _ -> d)
43
+
44
+ optInt :: Value -> String -> Int -> IO Int
45
+ optInt opts k d = do v <- getp opts k; pure (case v of VNum n -> truncate n; _ -> d)
46
+
47
+ optStr :: Value -> String -> String -> IO String
48
+ optStr = getStrD
49
+
50
+ optActive :: Value -> IO Bool
51
+ optActive opts = isTrueV <$> getp opts "active"
52
+
53
+ optStrList :: Value -> String -> [String] -> IO [String]
54
+ optStrList opts k d = do
55
+ v <- getp opts k
56
+ case v of { VList _ -> do { its <- listItems v; pure [s | VStr s <- its] }; _ -> pure d }
57
+
58
+ nowOf :: Value -> IO Double
59
+ nowOf opts = do
60
+ v <- getp opts "now"
61
+ case v of
62
+ VFunc _ -> do r <- callVfn v VNoval; pure (case r of VNum n -> n; _ -> 0)
63
+ _ -> defaultNowMs
64
+
65
+ sleepOf :: Value -> Double -> IO ()
66
+ sleepOf opts ms = when (ms > 0) $ do
67
+ v <- getp opts "sleep"
68
+ case v of VFunc _ -> () <$ callVfn v (VNum ms); _ -> realSleep ms
69
+
70
+ toOptsMap :: Value -> IO Value
71
+ toOptsMap opts = case toMap opts of VMap _ -> pure opts; _ -> emptyMap
72
+
73
+ -- ------------------------------------------------------------------
74
+ -- small helpers
75
+ -- ------------------------------------------------------------------
76
+
77
+ endsWith :: String -> String -> Bool
78
+ endsWith s suf = length s >= length suf && drop (length s - length suf) s == suf
79
+
80
+ stripLeadDot :: String -> String
81
+ stripLeadDot ('.' : r) = r
82
+ stripLeadDot s = s
83
+
84
+ urlHost :: String -> String
85
+ urlHost url = case findScheme 0 of
86
+ Just start -> takeWhile (\c -> c /= '/' && c /= ':') (drop start url)
87
+ Nothing -> url
88
+ where
89
+ n = length url
90
+ findScheme i
91
+ | i + 3 > n = Nothing
92
+ | take 3 (drop i url) == "://" = Just (i + 3)
93
+ | otherwise = findScheme (i + 1)
94
+
95
+ featureBase :: IO (IORef Bool, IORef Value)
96
+ featureBase = do active <- newIORef True; fopts <- newIORef VNoval; pure (active, fopts)
97
+
98
+ -- ------------------------------------------------------------------
99
+ -- base / log
100
+ -- ------------------------------------------------------------------
101
+
102
+ baseFeature :: IO Feature
103
+ baseFeature = do
104
+ (active, fopts) <- featureBase
105
+ pure Feature { fName = "base", fVersion = "0.0.1", fActive = active, fOptions = fopts
106
+ , fInit = \_ _ -> pure (), fHook = \_ _ -> pure () }
107
+
108
+ logFeature :: IO Feature
109
+ logFeature = do
110
+ (active, fopts) <- featureBase
111
+ let initFn _ opts = do a <- optActive opts; writeIORef active a
112
+ pure Feature { fName = "log", fVersion = "0.0.1", fActive = active, fOptions = fopts
113
+ , fInit = initFn, fHook = \_ _ -> pure () }
114
+
115
+ -- ------------------------------------------------------------------
116
+ -- retry
117
+ -- ------------------------------------------------------------------
118
+
119
+ retryFeature :: IO Feature
120
+ retryFeature = do
121
+ (active, fopts) <- featureBase
122
+ options <- newIORef =<< emptyMap
123
+ let statuses = do
124
+ opts <- readIORef options; v <- getp opts "statuses"
125
+ case v of { VList _ -> do { its <- listItems v; pure [truncate n | VNum n <- its] }; _ -> pure [408, 425, 429, 500, 502, 503, 504] }
126
+ retryAfterOf resV = case resV of
127
+ VMap _ -> do
128
+ h <- getp resV "headers"
129
+ case h of
130
+ VMap _ -> do
131
+ ra <- headerCI h "retry-after"
132
+ case ra of
133
+ VNoval -> pure Nothing; VNull -> pure Nothing
134
+ _ -> case reads (vstring ra) :: [(Double, String)] of { [(x, _)] -> pure (Just (x * 1000)); _ -> pure Nothing }
135
+ _ -> pure Nothing
136
+ _ -> pure Nothing
137
+ retryable resV merr =
138
+ if isJust merr then pure True
139
+ else if isNoval resV then pure True
140
+ else case resV of { VMap _ -> do { st <- getp resV "status"; sts <- statuses; pure (case st of { VNum n -> truncate n `elem` sts; _ -> False }) }; _ -> pure False }
141
+ backoff resV attempt = do
142
+ opts <- readIORef options
143
+ minDelay <- optNum opts "minDelay" 50; maxDelay <- optNum opts "maxDelay" 2000; factor <- optNum opts "factor" 2
144
+ ra <- retryAfterOf resV
145
+ case ra of
146
+ Just r -> pure (min maxDelay r)
147
+ Nothing -> do
148
+ let base = minDelay * (factor ^^ attempt)
149
+ jv <- getp opts "jitter"
150
+ jitter <- case jv of VBool False -> pure 0; _ -> do j <- randInt (max 1 (round minDelay)); pure (fromIntegral j)
151
+ pure (min maxDelay (base + jitter))
152
+ track ctx = do cl <- cc ctx; bucket <- trackBucket cl "retry" (do rs <- emptyList; jo [("attempts", VNum 0), ("retries", rs)]); bumpNum bucket "attempts" 1
153
+ withRetry ctx url fd inner = do
154
+ opts <- readIORef options
155
+ retries <- optInt opts "retries" 2
156
+ let loop attempt = do
157
+ (r, e) <- inner ctx url fd
158
+ ret <- retryable r e
159
+ if not ret || attempt >= retries then pure (r, e)
160
+ else do w <- backoff r attempt; track ctx; sleepOf opts w; loop (attempt + 1)
161
+ loop 0
162
+ initFn ctx opts = do
163
+ om <- toOptsMap opts; writeIORef options om
164
+ a <- optActive opts; writeIORef active a
165
+ when a $ do u <- cu ctx; inner <- readIORef (uFetcher u); writeIORef (uFetcher u) (\c ur f -> withRetry c ur f inner)
166
+ pure Feature { fName = "retry", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = \_ _ -> pure () }
167
+
168
+ -- ------------------------------------------------------------------
169
+ -- timeout
170
+ -- ------------------------------------------------------------------
171
+
172
+ timeoutFeature :: IO Feature
173
+ timeoutFeature = do
174
+ (active, fopts) <- featureBase
175
+ options <- newIORef =<< emptyMap
176
+ let track ctx ms = do cl <- cc ctx; bucket <- trackBucket cl "timeout" (jo [("count", VNum 0), ("ms", VNum ms)]); bumpNum bucket "count" 1
177
+ withTimeout ctx url fd inner = do
178
+ opts <- readIORef options
179
+ ms <- optNum opts "ms" 30000
180
+ if ms <= 0 then inner ctx url fd
181
+ else do
182
+ fdC <- clone fd
183
+ fd2 <- case fdC of VMap _ -> pure fdC; _ -> emptyMap
184
+ setp fd2 "timeout" (VNum (ms / 1000))
185
+ start <- nowOf opts
186
+ (res, err) <- inner ctx url fd2
187
+ now <- nowOf opts
188
+ let elapsed = now - start
189
+ if elapsed > ms
190
+ then do track ctx ms; e <- mkErr "timeout" ("Request exceeded timeout of " ++ show (truncate ms :: Int) ++ "ms"); pure (VNoval, Just e)
191
+ else pure (res, err)
192
+ initFn ctx opts = do
193
+ om <- toOptsMap opts; writeIORef options om
194
+ a <- optActive opts; writeIORef active a
195
+ when a $ do u <- cu ctx; inner <- readIORef (uFetcher u); writeIORef (uFetcher u) (\c ur f -> withTimeout c ur f inner)
196
+ pure Feature { fName = "timeout", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = \_ _ -> pure () }
197
+
198
+ -- ------------------------------------------------------------------
199
+ -- ratelimit
200
+ -- ------------------------------------------------------------------
201
+
202
+ ratelimitFeature :: IO Feature
203
+ ratelimitFeature = do
204
+ (active, fopts) <- featureBase
205
+ options <- newIORef =<< emptyMap
206
+ tokens <- newIORef (0 :: Double)
207
+ lastR <- newIORef (0 :: Double)
208
+ let rateOf = do opts <- readIORef options; v <- getp opts "rate"; pure (case v of VNum n | n /= 0 -> n; _ -> 5)
209
+ burstOf = do opts <- readIORef options; v <- getp opts "burst"; case v of VNum n -> pure n; _ -> rateOf
210
+ track ctx wait = do cl <- cc ctx; bucket <- trackBucket cl "ratelimit" (jo [("throttled", VNum 0), ("waitMs", VNum 0)]); bumpNum bucket "throttled" 1; bumpNum bucket "waitMs" wait
211
+ acquire ctx = do
212
+ opts <- readIORef options
213
+ r <- rateOf; b <- burstOf
214
+ now <- nowOf opts
215
+ lst <- readIORef lastR
216
+ let elapsed = now - lst
217
+ writeIORef lastR now
218
+ tk <- readIORef tokens
219
+ let tk' = min b (tk + (elapsed / 1000) * r)
220
+ if tk' >= 1 then writeIORef tokens (tk' - 1)
221
+ else do
222
+ let needed = 1 - tk'
223
+ waitMs = fromIntegral (ceiling ((needed / r) * 1000) :: Int)
224
+ track ctx waitMs; sleepOf opts waitMs
225
+ now2 <- nowOf opts; writeIORef lastR now2; writeIORef tokens 0
226
+ initFn ctx opts = do
227
+ om <- toOptsMap opts; writeIORef options om
228
+ a <- optActive opts; writeIORef active a
229
+ when a $ do
230
+ b <- burstOf; writeIORef tokens b
231
+ n0 <- nowOf =<< readIORef options; writeIORef lastR n0
232
+ u <- cu ctx; inner <- readIORef (uFetcher u); writeIORef (uFetcher u) (\c ur f -> do acquire c; inner c ur f)
233
+ pure Feature { fName = "ratelimit", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = \_ _ -> pure () }
234
+
235
+ -- ------------------------------------------------------------------
236
+ -- cache
237
+ -- ------------------------------------------------------------------
238
+
239
+ cacheFeature :: IO Feature
240
+ cacheFeature = do
241
+ (active, fopts) <- featureBase
242
+ options <- newIORef =<< emptyMap
243
+ store <- newIORef ([] :: [(String, Value)])
244
+ let track ctx kind = do cl <- cc ctx; bucket <- trackBucket cl "cache" (jo [("hit", VNum 0), ("miss", VNum 0), ("bypass", VNum 0)]); bumpNum bucket kind 1
245
+ cacheable res = case res of { VMap _ -> do { st <- getp res "status"; pure (case st of { VNum n -> n >= 200 && n < 300; _ -> False }) }; _ -> pure False }
246
+ snapshot res = do
247
+ jv <- getp res "json"
248
+ d <- case jv of VFunc _ -> callJson jv; _ -> pure VNoval
249
+ headers <- emptyMap
250
+ h <- getp res "headers"
251
+ case h of { VMap _ -> do { ks <- keysof h; forM_ ks $ \k -> do { vv <- getp h k; setp headers (lower k) vv } }; _ -> pure () }
252
+ st <- getp res "status"; stt <- getp res "statusText"
253
+ jo [("status", st), ("statusText", stt), ("data", d), ("headers", headers)]
254
+ replay snap = do
255
+ d <- getp snap "data"; st <- getp snap "status"; stt <- getp snap "statusText"
256
+ h <- getp snap "headers"; hC <- clone h; h2 <- case hC of VMap _ -> pure hC; _ -> emptyMap
257
+ jo [("status", st), ("statusText", stt), ("body", VStr "not-used"), ("json", jsonThunk d), ("headers", h2)]
258
+ evict = do opts <- readIORef options; mx <- optInt opts "max" 256; let go = do s <- readIORef store; when (length s >= mx) (case s of (_ : tl) -> writeIORef store tl >> go; [] -> pure ()) in go
259
+ through ctx url fd inner = do
260
+ opts <- readIORef options
261
+ meth <- (\v -> case v of VStr s -> upper s; _ -> "GET") <$> getp fd "method"
262
+ methods <- map upper <$> optStrList opts "methods" ["GET"]
263
+ if meth `notElem` methods then inner ctx url fd
264
+ else do
265
+ let key = meth ++ " " ++ url
266
+ now <- nowOf opts
267
+ s <- readIORef store
268
+ case lookup key s of
269
+ Just hit -> do
270
+ exp0 <- getp hit "expiry"
271
+ let fresh = case exp0 of { VNum e -> e > now; _ -> False }
272
+ if fresh then do track ctx "hit"; snp <- getp hit "snapshot"; rp <- replay snp; pure (rp, Nothing)
273
+ else doMiss ctx url fd inner opts key now
274
+ Nothing -> doMiss ctx url fd inner opts key now
275
+ doMiss ctx url fd inner opts key now = do
276
+ (res, err) <- inner ctx url fd
277
+ ok <- cacheable res
278
+ if isNothing err && ok
279
+ then do
280
+ snp <- snapshot res
281
+ ttl <- optNum opts "ttl" 5000
282
+ evict
283
+ entry <- jo [("expiry", VNum (now + ttl)), ("snapshot", snp)]
284
+ modifyIORef store (\st -> filter ((/= key) . fst) st ++ [(key, entry)])
285
+ track ctx "miss"
286
+ rp <- replay snp; pure (rp, Nothing)
287
+ else do track ctx "bypass"; pure (res, err)
288
+ initFn ctx opts = do
289
+ om <- toOptsMap opts; writeIORef options om
290
+ a <- optActive opts; writeIORef active a
291
+ when a $ do writeIORef store []; u <- cu ctx; inner <- readIORef (uFetcher u); writeIORef (uFetcher u) (\c ur f -> through c ur f inner)
292
+ pure Feature { fName = "cache", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = \_ _ -> pure () }
293
+
294
+ -- ------------------------------------------------------------------
295
+ -- idempotency
296
+ -- ------------------------------------------------------------------
297
+
298
+ idempotencyFeature :: IO Feature
299
+ idempotencyFeature = do
300
+ (active, fopts) <- featureBase
301
+ options <- newIORef =<< emptyMap
302
+ let genkey = do opts <- readIORef options; v <- getp opts "keygen"; case v of VFunc _ -> vstring <$> callVfn v VNoval; _ -> randId16
303
+ mutating ctx = do
304
+ opts <- readIORef options
305
+ methods <- map upper <$> optStrList opts "methods" ["POST", "PUT", "PATCH", "DELETE"]
306
+ specV <- readIORef (cSpec ctx)
307
+ meth <- case specV of VMap _ -> upper <$> getStrD specV "method" ""; _ -> pure ""
308
+ if meth /= "" && meth `elem` methods then pure True
309
+ else do op <- readIORef (cOp ctx); ops <- optStrList opts "ops" ["create", "update", "remove"]; pure (opName op `elem` ops)
310
+ hookFn name ctx = do
311
+ a <- readIORef active
312
+ when (name == "PreRequest" && a) $ do
313
+ specV <- readIORef (cSpec ctx)
314
+ case specV of
315
+ VMap _ -> do
316
+ mut <- mutating ctx
317
+ when mut $ do
318
+ opts <- readIORef options
319
+ header <- optStr opts "header" "Idempotency-Key"
320
+ hdrs <- getp specV "headers"
321
+ existing <- headerCI hdrs header
322
+ when (isNoval existing) $ do
323
+ key <- genkey
324
+ setp hdrs header (VStr key)
325
+ cl <- cc ctx
326
+ bucket <- trackBucket cl "idempotency" (jo [("issued", VNum 0), ("last", VNoval)])
327
+ bumpNum bucket "issued" 1; setp bucket "last" (VStr key)
328
+ _ -> pure ()
329
+ initFn _ opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a
330
+ pure Feature { fName = "idempotency", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
331
+
332
+ -- ------------------------------------------------------------------
333
+ -- rbac (PrePoint short-circuit)
334
+ -- ------------------------------------------------------------------
335
+
336
+ rbacFeature :: IO Feature
337
+ rbacFeature = do
338
+ (active, fopts) <- featureBase
339
+ options <- newIORef =<< emptyMap
340
+ perms <- newIORef ([] :: [String])
341
+ let track ctx required allowed = do
342
+ cl <- cc ctx
343
+ bucket <- trackBucket cl "rbac" (jo [("allowed", VNum 0), ("denied", VNum 0), ("last", VNoval)])
344
+ bumpNum bucket (if allowed then "allowed" else "denied") 1
345
+ op <- readIORef (cOp ctx)
346
+ lastM <- jo [("required", VStr required), ("allowed", VBool allowed), ("op", VStr (opName op))]
347
+ setp bucket "last" lastM
348
+ requiredOf ctx = do
349
+ opts <- readIORef options
350
+ rulesV <- getp opts "rules"
351
+ let rules = case rulesV of { VMap _ -> rulesV; _ -> VNoval }
352
+ ment <- readIORef (cEntity ctx); op <- readIORef (cOp ctx)
353
+ let entity = case ment of Just e -> eName e; Nothing -> if opEntity op /= "" then opEntity op else ""
354
+ opname = opName op
355
+ ruleFor k = do v <- getp rules k; pure (case v of VNoval -> Nothing; VNull -> Nothing; _ -> Just (vstring v))
356
+ r1 <- ruleFor (entity ++ "." ++ opname)
357
+ case r1 of
358
+ Just v -> pure (Just v)
359
+ Nothing -> do r2 <- ruleFor opname; case r2 of Just v -> pure (Just v); Nothing -> ruleFor "*"
360
+ reject ctx req = do
361
+ track ctx req False
362
+ op <- readIORef (cOp ctx)
363
+ let opname = if opName op /= "" && opName op /= "_" then opName op else "?"
364
+ e <- mkErr "rbac_denied" ("Permission \"" ++ req ++ "\" required for operation \"" ++ opname ++ "\"")
365
+ out <- readIORef (cOut ctx); setp out "point" e
366
+ hookFn name ctx = do
367
+ a <- readIORef active
368
+ when (name == "PrePoint" && a) $ do
369
+ mreq <- requiredOf ctx
370
+ case mreq of
371
+ Nothing -> do opts <- readIORef options; d <- getp opts "deny"; when (isTrueV d) (reject ctx "<default-deny>")
372
+ Just req -> do ps <- readIORef perms; if "*" `elem` ps || req `elem` ps then track ctx req True else reject ctx req
373
+ initFn _ opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a; pl <- optStrList om "permissions" []; writeIORef perms pl
374
+ pure Feature { fName = "rbac", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
375
+
376
+ -- ------------------------------------------------------------------
377
+ -- metrics
378
+ -- ------------------------------------------------------------------
379
+
380
+ metricsFeature :: IO Feature
381
+ metricsFeature = do
382
+ (active, fopts) <- featureBase
383
+ options <- newIORef =<< emptyMap
384
+ let metrics ctx = do cl <- cc ctx; trackBucket cl "metrics" (do tot <- jo [("count", VNum 0), ("ok", VNum 0), ("err", VNum 0), ("totalMs", VNum 0), ("maxMs", VNum 0)]; ops <- emptyMap; jo [("total", tot), ("ops", ops)])
385
+ bump b ok dur = do bumpNum b "count" 1; bumpNum b (if ok then "ok" else "err") 1; bumpNum b "totalMs" dur; mx <- getp b "maxMs"; let { m = case mx of { VNum n -> n; _ -> 0 } }; when (dur > m) (setp b "maxMs" (VNum dur))
386
+ record ctx ok = do
387
+ startV <- scratchGet ctx "metrics_start"
388
+ case startV of
389
+ VNum start -> do
390
+ scratchDel ctx "metrics_start"
391
+ opts <- readIORef options; now <- nowOf opts
392
+ let dur = max 0 (now - start)
393
+ m <- metrics ctx; op <- readIORef (cOp ctx)
394
+ let key = opEntity op ++ "." ++ opName op
395
+ ops <- getp m "ops"; opbV <- getp ops key
396
+ opb <- case opbV of VMap _ -> pure opbV; _ -> do b <- jo [("count", VNum 0), ("ok", VNum 0), ("err", VNum 0), ("totalMs", VNum 0), ("maxMs", VNum 0)]; setp ops key b; pure b
397
+ tot <- getp m "total"; bump tot ok dur; bump opb ok dur
398
+ _ -> pure ()
399
+ hookFn name ctx = do
400
+ a <- readIORef active
401
+ when a $ case name of
402
+ "PrePoint" -> do opts <- readIORef options; now <- nowOf opts; scratchSet ctx "metrics_start" (VNum now)
403
+ "PreDone" -> do ok <- resultOk ctx; record ctx ok
404
+ "PreUnexpected" -> record ctx False
405
+ _ -> pure ()
406
+ initFn ctx opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a; _ <- metrics ctx; pure ()
407
+ pure Feature { fName = "metrics", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
408
+
409
+ resultOk :: Context -> IO Bool
410
+ resultOk ctx = do
411
+ rv <- readIORef (cResult ctx)
412
+ case rv of
413
+ VMap _ -> do ok <- getp rv "ok"; err <- getp rv "err"; isE <- isErr err; pure (isTrueV ok && not isE)
414
+ _ -> pure False
415
+
416
+ -- ------------------------------------------------------------------
417
+ -- telemetry
418
+ -- ------------------------------------------------------------------
419
+
420
+ telemetryFeature :: IO Feature
421
+ telemetryFeature = do
422
+ (active, fopts) <- featureBase
423
+ options <- newIORef =<< emptyMap
424
+ seqR <- newIORef (0 :: Int)
425
+ let telemetry ctx = do cl <- cc ctx; trackBucket cl "telemetry" (do sp <- emptyList; jo [("spans", sp), ("active", VNum 0)])
426
+ genId kind = do
427
+ opts <- readIORef options; v <- getp opts "idgen"
428
+ case v of
429
+ VFunc _ -> vstring <$> callVfn v (VStr kind)
430
+ _ -> do modifyIORef seqR (+ 1); s <- readIORef seqR; let { n = pad4 s; padded = n ++ replicate (max 0 (16 - length n)) '0' }; pure ((if kind == "trace" then "t" else "s") ++ padded)
431
+ close ctx ok = do
432
+ spanV <- scratchGet ctx "telemetry_span"
433
+ case spanV of
434
+ VMap _ -> do
435
+ scratchDel ctx "telemetry_span"
436
+ opts <- readIORef options; end <- nowOf opts
437
+ setp spanV "end" (VNum end)
438
+ start <- getp spanV "start"; let s = case start of { VNum n -> n; _ -> 0 }
439
+ setp spanV "durationMs" (VNum (max 0 (end - s)))
440
+ setp spanV "ok" (VBool ok)
441
+ t <- telemetry ctx; bumpNum t "active" (-1)
442
+ spans <- getp t "spans"; appendList spans spanV
443
+ expv <- getp opts "exporter"; case expv of VFunc _ -> () <$ callVfn expv spanV; _ -> pure ()
444
+ _ -> pure ()
445
+ hookFn name ctx = do
446
+ a <- readIORef active
447
+ when a $ case name of
448
+ "PrePoint" -> do
449
+ op <- readIORef (cOp ctx)
450
+ let entity = if opEntity op /= "" then opEntity op else "_"
451
+ opname = if opName op /= "" then opName op else "_"
452
+ tid <- genId "trace"; sid <- genId "span"; opts <- readIORef options; now <- nowOf opts
453
+ span0 <- jo [("traceId", VStr tid), ("spanId", VStr sid), ("name", VStr (entity ++ "." ++ opname)), ("start", VNum now), ("end", VNoval), ("durationMs", VNoval), ("ok", VNoval)]
454
+ scratchSet ctx "telemetry_span" span0
455
+ t <- telemetry ctx; bumpNum t "active" 1
456
+ "PreRequest" -> do
457
+ spanV <- scratchGet ctx "telemetry_span"; specV <- readIORef (cSpec ctx)
458
+ case (spanV, specV) of
459
+ (VMap _, VMap _) -> do
460
+ opts <- readIORef options; hopt <- getp opts "headers"; let hget k d = case hopt of { VMap _ -> getStrD hopt k d; _ -> pure d }
461
+ hdrs <- getp specV "headers"
462
+ th <- hget "trace" "X-Trace-Id"; tid <- getp spanV "traceId"; setp hdrs th tid
463
+ sh <- hget "span" "X-Span-Id"; sid <- getp spanV "spanId"; setp hdrs sh sid
464
+ ph <- hget "parent" "traceparent"; setp hdrs ph (VStr ("00-" ++ vstring tid ++ "-" ++ vstring sid ++ "-01"))
465
+ _ -> pure ()
466
+ "PreDone" -> do ok <- resultOk ctx; close ctx ok
467
+ "PreUnexpected" -> close ctx False
468
+ _ -> pure ()
469
+ initFn ctx opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a; writeIORef seqR 0; _ <- telemetry ctx; pure ()
470
+ pure Feature { fName = "telemetry", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
471
+
472
+ pad4 :: Int -> String
473
+ pad4 n = let s = showHexLower n in replicate (max 0 (4 - length s)) '0' ++ s
474
+
475
+ showHexLower :: Int -> String
476
+ showHexLower 0 = "0"
477
+ showHexLower n = go n ""
478
+ where go 0 acc = acc
479
+ go x acc = go (x `div` 16) (hexDigit (x `mod` 16) : acc)
480
+ hexDigit d = "0123456789abcdef" !! d
481
+
482
+ appendList :: Value -> Value -> IO ()
483
+ appendList lst v = do its <- listItems lst; new <- mkList (its ++ [v]); case lst of VList r -> writeIORef r =<< listItems new; _ -> pure ()
484
+
485
+ -- ------------------------------------------------------------------
486
+ -- debug
487
+ -- ------------------------------------------------------------------
488
+
489
+ debugFeature :: IO Feature
490
+ debugFeature = do
491
+ (active, fopts) <- featureBase
492
+ options <- newIORef =<< emptyMap
493
+ let debug ctx = do cl <- cc ctx; trackBucket cl "debug" (do es <- emptyList; jo [("entries", es)])
494
+ redact headers = case headers of
495
+ VMap _ -> do
496
+ opts <- readIORef options
497
+ patterns <- optStrList opts "redact" ["authorization", "cookie", "set-cookie", "api-key", "apikey", "x-api-key", "idempotency-key"]
498
+ out <- emptyMap; ks <- keysof headers
499
+ forM_ ks $ \k -> if lower k `elem` patterns then setp out k (VStr "<redacted>") else do v <- getp headers k; setp out k v
500
+ pure out
501
+ _ -> emptyMap
502
+ finish ctx ok = do
503
+ entryV <- scratchGet ctx "debug_entry"
504
+ case entryV of
505
+ VMap _ -> do
506
+ scratchDel ctx "debug_entry"
507
+ opts <- readIORef options
508
+ rv <- readIORef (cResult ctx); rok <- case rv of VMap _ -> isTrueV <$> getp rv "ok"; _ -> pure True
509
+ setp entryV "ok" (VBool (ok && rok))
510
+ now <- nowOf opts; start <- getp entryV "start"; let s = case start of { VNum n -> n; _ -> 0 }
511
+ setp entryV "durationMs" (VNum (max 0 (now - s)))
512
+ st <- getp entryV "status"
513
+ case st of VNoval -> case rv of { VMap _ -> do { rs <- getp rv "status"; setp entryV "status" rs }; _ -> pure () }; _ -> pure ()
514
+ d <- debug ctx; buf <- getp d "entries"; appendList buf entryV
515
+ mx <- optInt opts "max" 100
516
+ trimList buf mx
517
+ oe <- getp opts "onEntry"; case oe of VFunc _ -> () <$ callVfn oe entryV; _ -> pure ()
518
+ _ -> pure ()
519
+ hookFn name ctx = do
520
+ a <- readIORef active
521
+ when a $ case name of
522
+ "PreRequest" -> do
523
+ op <- readIORef (cOp ctx)
524
+ let opname = (if opEntity op /= "" then opEntity op else "_") ++ "." ++ (if opName op /= "" then opName op else "_")
525
+ specV <- readIORef (cSpec ctx)
526
+ opts <- readIORef options; now <- nowOf opts
527
+ (methodV, urlV, hdrs) <- case specV of
528
+ VMap _ -> do m <- getp specV "method"; u0 <- getStrD specV "url" ""; p0 <- getStrD specV "path" ""; h <- getp specV "headers"; pure (m, VStr (if u0 /= "" then u0 else p0), h)
529
+ _ -> pure (VNoval, VNoval, VNoval)
530
+ rh <- redact hdrs
531
+ entry <- jo [("op", VStr opname), ("method", methodV), ("url", urlV), ("headers", rh), ("start", VNum now), ("status", VNoval), ("ok", VNoval), ("durationMs", VNoval), ("error", VNoval)]
532
+ scratchSet ctx "debug_entry" entry
533
+ "PreResponse" -> do
534
+ entryV <- scratchGet ctx "debug_entry"
535
+ case entryV of
536
+ VMap _ -> do
537
+ respV <- readIORef (cResponse ctx)
538
+ case respV of VMap _ -> do { rs <- getp respV "status"; setp entryV "status" rs }; _ -> pure ()
539
+ u0 <- getp entryV "url"
540
+ case u0 of
541
+ VNoval -> setUrlFromSpec ctx entryV
542
+ VStr "" -> setUrlFromSpec ctx entryV
543
+ _ -> pure ()
544
+ _ -> pure ()
545
+ "PreDone" -> finish ctx True
546
+ "PreUnexpected" -> do
547
+ entryV <- scratchGet ctx "debug_entry"
548
+ case entryV of
549
+ VMap _ -> do ctrl <- readIORef (cCtrl ctx); err <- getp ctrl "err"; isE <- isErr err; when isE (do m <- errMsg err; setp entryV "error" (VStr m))
550
+ _ -> pure ()
551
+ finish ctx False
552
+ _ -> pure ()
553
+ setUrlFromSpec ctx entryV = do specV <- readIORef (cSpec ctx); case specV of { VMap _ -> do { u <- getStrD specV "url" ""; when (u /= "") (setp entryV "url" (VStr u)) }; _ -> pure () }
554
+ initFn ctx opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a; _ <- debug ctx; pure ()
555
+ pure Feature { fName = "debug", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
556
+
557
+ trimList :: Value -> Int -> IO ()
558
+ trimList lst mx = do
559
+ its <- listItems lst
560
+ when (length its > mx) $ case lst of VList r -> writeIORef r (drop (length its - mx) its); _ -> pure ()
561
+
562
+ -- ------------------------------------------------------------------
563
+ -- audit
564
+ -- ------------------------------------------------------------------
565
+
566
+ auditFeature :: IO Feature
567
+ auditFeature = do
568
+ (active, fopts) <- featureBase
569
+ options <- newIORef =<< emptyMap
570
+ seqR <- newIORef (0 :: Int)
571
+ let audit ctx = do cl <- cc ctx; trackBucket cl "audit" (do rs <- emptyList; jo [("records", rs)])
572
+ emit ctx outcome = do
573
+ seenV <- scratchGet ctx "audit_seen"
574
+ case seenV of
575
+ VBool True -> pure ()
576
+ _ -> do
577
+ scratchSet ctx "audit_seen" (VBool True)
578
+ modifyIORef seqR (+ 1); sq <- readIORef seqR
579
+ opts <- readIORef options
580
+ ctrl <- readIORef (cCtrl ctx); ac <- getp ctrl "actor"
581
+ actor <- case ac of
582
+ VNoval -> do a2 <- getp opts "actor"; case a2 of VNoval -> pure (VStr "anonymous"); VNull -> pure (VStr "anonymous"); _ -> pure a2
583
+ VNull -> do a2 <- getp opts "actor"; case a2 of VNoval -> pure (VStr "anonymous"); VNull -> pure (VStr "anonymous"); _ -> pure a2
584
+ _ -> pure ac
585
+ now <- nowOf opts; op <- readIORef (cOp ctx)
586
+ rv <- readIORef (cResult ctx); statusV <- case rv of VMap _ -> getp rv "status"; _ -> pure VNoval
587
+ record <- jo [ ("seq", vint sq), ("ts", VNum now), ("actor", actor)
588
+ , ("entity", VStr (if opEntity op /= "" then opEntity op else "_"))
589
+ , ("op", VStr (if opName op /= "" then opName op else "_"))
590
+ , ("outcome", VStr outcome), ("status", statusV), ("correlationId", VStr (cId ctx)) ]
591
+ a <- audit ctx; recs <- getp a "records"; appendList recs record
592
+ mx <- optInt opts "max" 1000; trimList recs mx
593
+ sink <- getp opts "sink"; case sink of VFunc _ -> () <$ callVfn sink record; _ -> pure ()
594
+ hookFn name ctx = do
595
+ a <- readIORef active
596
+ when a $ case name of
597
+ "PreDone" -> do ok <- resultOk ctx; emit ctx (if ok then "ok" else "error")
598
+ "PreUnexpected" -> emit ctx "error"
599
+ _ -> pure ()
600
+ initFn ctx opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a; writeIORef seqR 0; _ <- audit ctx; pure ()
601
+ pure Feature { fName = "audit", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
602
+
603
+ -- ------------------------------------------------------------------
604
+ -- clienttrack
605
+ -- ------------------------------------------------------------------
606
+
607
+ clienttrackFeature :: IO Feature
608
+ clienttrackFeature = do
609
+ (active, fopts) <- featureBase
610
+ options <- newIORef =<< emptyMap
611
+ session <- newIORef ""
612
+ requests <- newIORef (0 :: Int)
613
+ let nameOf = do opts <- readIORef options; nm <- optStr opts "clientName" "ProjectName-SDK"; ver <- optStr opts "clientVersion" "0.0.1"; pure (nm ++ "/" ++ ver)
614
+ genId kind = do
615
+ opts <- readIORef options; v <- getp opts "idgen"
616
+ case v of VFunc _ -> vstring <$> callVfn v (VStr kind); _ -> do { r <- randId16; let { s = take 1 kind ++ "-" ++ r }; pure (take 20 s) }
617
+ setNC headers hname val = do e <- headerCI headers hname; when (isNoval e) (setp headers hname (VStr val))
618
+ hookFn name ctx = do
619
+ a <- readIORef active
620
+ when a $ case name of
621
+ "PostConstruct" -> do
622
+ opts <- readIORef options; sid <- getStrD opts "sessionId" ""
623
+ s <- if sid /= "" then pure sid else genId "session"
624
+ writeIORef session s
625
+ cl <- cc ctx; nm <- nameOf; tk <- jo [("session", VStr s), ("requests", VNum 0), ("clientName", VStr nm)]; trackSet cl "clienttrack" tk
626
+ "PreRequest" -> do
627
+ specV <- readIORef (cSpec ctx)
628
+ case specV of
629
+ VMap _ -> do
630
+ s0 <- readIORef session
631
+ s <- if s0 == "" then do opts <- readIORef options; sid <- getStrD opts "sessionId" ""; s' <- if sid /= "" then pure sid else genId "session"; writeIORef session s'; pure s' else pure s0
632
+ opts <- readIORef options; hopt <- getp opts "headers"; let hget k d = case hopt of { VMap _ -> getStrD hopt k d; _ -> pure d }
633
+ modifyIORef requests (+ 1); reqCount <- readIORef requests
634
+ rid <- genId "request"; nm <- nameOf
635
+ hdrs <- getp specV "headers"
636
+ agentH <- hget "agent" "User-Agent"; setNC hdrs agentH nm
637
+ clientH <- hget "client" "X-Client-Id"; setNC hdrs clientH s
638
+ reqH <- hget "request" "X-Request-Id"; setp hdrs reqH (VStr rid)
639
+ cl <- cc ctx
640
+ bucket <- trackBucket cl "clienttrack" (jo [("session", VStr s), ("requests", VNum 0), ("clientName", VStr nm)])
641
+ setp bucket "requests" (vint reqCount); setp bucket "lastRequestId" (VStr rid)
642
+ _ -> pure ()
643
+ _ -> pure ()
644
+ initFn _ opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a; writeIORef requests 0
645
+ pure Feature { fName = "clienttrack", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
646
+
647
+ -- ------------------------------------------------------------------
648
+ -- paging
649
+ -- ------------------------------------------------------------------
650
+
651
+ pagingFeature :: IO Feature
652
+ pagingFeature = do
653
+ (active, fopts) <- featureBase
654
+ options <- newIORef =<< emptyMap
655
+ let isList ctx = do opts <- readIORef options; ops <- optStrList opts "ops" ["list"]; op <- readIORef (cOp ctx); pure (opName op `elem` ops)
656
+ numOf v = case v of VNoval -> VNoval; VNull -> VNoval; _ -> case reads (strip (vstring v)) :: [(Double, String)] of { [(x, "")] -> VNum x; _ -> VNoval }
657
+ extractNext link = case (elemIndex '<' link, elemIndex '>' link) of
658
+ (Just i, Just j) | j > i ->
659
+ let inner = take (j - i - 1) (drop (i + 1) link)
660
+ rest = lower (drop (j + 1) link)
661
+ in if substrContains rest "rel" && substrContains rest "next" then Just inner else Nothing
662
+ _ -> Nothing
663
+ hookFn name ctx = do
664
+ a <- readIORef active; il <- isList ctx
665
+ when (a && il) $ case name of
666
+ "PreRequest" -> do
667
+ specV <- readIORef (cSpec ctx)
668
+ case specV of
669
+ VMap _ -> do
670
+ qv <- getp specV "query"
671
+ q <- case qv of VMap _ -> pure qv; _ -> do m <- emptyMap; setp specV "query" m; pure m
672
+ opts <- readIORef options
673
+ pageParam <- optStr opts "pageParam" "page"; limitParam <- optStr opts "limitParam" "limit"; cursorParam <- optStr opts "cursorParam" "cursor"
674
+ ctrl <- readIORef (cCtrl ctx); pgv <- getp ctrl "paging"; let paging = case pgv of { VMap _ -> pgv; _ -> VNoval }
675
+ -- GraphQL paginates through operation VARIABLES, not the
676
+ -- query string. This hook runs after makeSpec, so spec.body
677
+ -- already holds the { query, variables } envelope, and before
678
+ -- makeFetchDef serialises it.
679
+ point <- readIORef (cPoint ctx)
680
+ kind <- getStrD point "kind" ""
681
+ cur <- case paging of VMap _ -> getp paging "cursor"; _ -> pure VNoval
682
+ if kind == "graphql" then graphqlPreRequest opts specV point paging else case cur of
683
+ VNoval -> do
684
+ pv <- getp q pageParam
685
+ case pv of
686
+ VNoval -> do
687
+ pgPage <- case paging of VMap _ -> getp paging "page"; _ -> pure VNoval
688
+ page <- case pgPage of { VNoval -> do { sp <- getp opts "startPage"; pure (case sp of { VNum n -> VNum n; _ -> VNum 1 }) }; VNull -> do { sp <- getp opts "startPage"; pure (case sp of { VNum n -> VNum n; _ -> VNum 1 }) }; p -> pure p }
689
+ setp q pageParam page
690
+ _ -> pure ()
691
+ VNull -> do
692
+ pv <- getp q pageParam
693
+ case pv of
694
+ VNoval -> do sp <- getp opts "startPage"; setp q pageParam (case sp of VNum n -> VNum n; _ -> VNum 1)
695
+ _ -> pure ()
696
+ c -> setp q cursorParam c
697
+ when (kind /= "graphql") $ do
698
+ lim <- getp opts "limit"
699
+ case lim of VNoval -> pure (); VNull -> pure (); _ -> do lv <- getp q limitParam; case lv of VNoval -> setp q limitParam lim; _ -> pure ()
700
+ _ -> pure ()
701
+ "PreResult" -> do
702
+ rv <- readIORef (cResult ctx)
703
+ case rv of
704
+ VMap _ -> do
705
+ hv <- getp rv "headers"; let headers = case hv of { VMap _ -> hv; _ -> VNoval }
706
+ headersM <- case headers of VMap _ -> pure headers; _ -> emptyMap
707
+ body <- getp rv "body"
708
+ xpage <- numOf <$> headerCI headersM "x-page"
709
+ xtot <- numOf <$> headerCI headersM "x-total-count"
710
+ xnext <- numOf <$> headerCI headersM "x-next-page"
711
+ paging <- jo [("page", xpage), ("totalCount", xtot), ("nextPage", xnext), ("next", VNoval), ("cursor", VNoval), ("hasMore", VBool False)]
712
+ lnk <- headerCI headersM "link"
713
+ case lnk of VNoval -> pure (); VNull -> pure (); _ -> case extractNext (vstring lnk) of Just nx -> setp paging "next" (VStr nx); Nothing -> pure ()
714
+ -- Relay connections carry the cursor in pageInfo, at the
715
+ -- path the model recorded for this op. `explicitMore` is set
716
+ -- when the server states hasMore outright, rather than
717
+ -- leaving it to be inferred from the presence of a cursor.
718
+ point <- readIORef (cPoint ctx)
719
+ gqlpage <- getpathS point "graphql.page"
720
+ relayMore <- case (gqlpage, body) of
721
+ (VMap _, VMap _) -> do
722
+ -- `connpath` locates the connection object inside the
723
+ -- response envelope (data.<field>); the cursor/more paths
724
+ -- are relative to it.
725
+ connpath <- getStrD gqlpage "connpath" ""
726
+ conn <- if null connpath then pure body else do
727
+ sub <- getpathS body connpath
728
+ pure (if isNullish sub then body else sub)
729
+ cursorpath <- getStrD gqlpage "cursor" ""
730
+ when (not (null cursorpath)) $ do
731
+ c <- getpathS conn cursorpath
732
+ when (not (isNullish c)) (setp paging "cursor" c)
733
+ morepath <- getStrD gqlpage "more" ""
734
+ if null morepath then pure False else do
735
+ mv <- getpathS conn morepath
736
+ case mv of
737
+ VBool mb -> do setp paging "hasMore" (VBool mb); pure True
738
+ _ -> pure False
739
+ _ -> pure False
740
+ bodyMore <- case body of
741
+ VMap _ -> do
742
+ bn <- getp body "next"; case bn of VNoval -> pure (); VNull -> pure (); _ -> do cn <- getp paging "next"; when (isNullish cn) (setp paging "next" bn)
743
+ bc <- getp body "cursor"; case bc of VNoval -> pure (); VNull -> pure (); _ -> setp paging "cursor" bc
744
+ bnc <- getp body "nextCursor"; case bnc of VNoval -> pure (); VNull -> pure (); _ -> setp paging "cursor" bnc
745
+ bhm <- getp body "hasMore"; case bhm of { VBool bb -> do { setp paging "hasMore" (VBool bb); pure True }; _ -> pure False }
746
+ _ -> pure False
747
+ -- Cursor presence only INFERS another page. When the server
748
+ -- stated the answer outright — relay's `hasNextPage: false`,
749
+ -- or a body `hasMore` — that wins: a final page normally
750
+ -- carries both an end cursor and hasNextPage false, and
751
+ -- inferring from the cursor there would send the caller back
752
+ -- for a page that does not exist, forever.
753
+ let explicitMore = relayMore || bodyMore
754
+ hmV <- getp paging "hasMore"; nx <- getp paging "next"; cu2 <- getp paging "cursor"; np <- getp paging "nextPage"
755
+ let hm = isTrueV hmV || not (isNullish nx) || not (isNullish cu2) || not (isNullish np)
756
+ when (not explicitMore) (setp paging "hasMore" (VBool hm))
757
+ setp rv "paging" paging
758
+ cl <- cc ctx; lastM <- jo [("last", paging)]; trackSet cl "paging" lastM
759
+ _ -> pure ()
760
+ _ -> pure ()
761
+ initFn _ opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a
762
+ pure Feature { fName = "paging", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
763
+
764
+ -- Relay pagination: the cursor is the `after` variable (or whatever the model
765
+ -- named it), and the page size is `first`.
766
+ graphqlPreRequest :: Value -> Value -> Value -> Value -> IO ()
767
+ graphqlPreRequest opts specV point paging = do
768
+ body <- getp specV "body"
769
+ case body of
770
+ VMap _ -> do
771
+ vv <- getp body "variables"
772
+ variables <- case vv of
773
+ VMap _ -> pure vv
774
+ _ -> do m <- emptyMap; setp body "variables" m; pure m
775
+
776
+ afterVar <- optStr opts "afterVar" "after"
777
+ firstVar <- optStr opts "firstVar" "first"
778
+
779
+ -- Only bind variables the operation actually declares, or the server
780
+ -- rejects the document.
781
+ vl <- getpathS point "graphql.vars"
782
+ varlist <- case vl of VList _ -> listItems vl; _ -> pure []
783
+ declared <- mapM (\v -> getStrD v "name" "") varlist
784
+
785
+ cur <- case paging of VMap _ -> getp paging "cursor"; _ -> pure VNoval
786
+ when (not (isNullish cur) && afterVar `elem` declared) $
787
+ setp variables afterVar cur
788
+
789
+ lim <- getp opts "limit"
790
+ existing <- getp variables firstVar
791
+ when (not (isNullish lim) && isNullish existing && firstVar `elem` declared) $
792
+ setp variables firstVar lim
793
+ _ -> pure ()
794
+
795
+ elemIndex :: Eq a => a -> [a] -> Maybe Int
796
+ elemIndex x = go 0 where go _ [] = Nothing; go i (y : ys) = if x == y then Just i else go (i + 1) ys
797
+
798
+ -- ------------------------------------------------------------------
799
+ -- streaming
800
+ -- ------------------------------------------------------------------
801
+
802
+ streamingFeature :: IO Feature
803
+ streamingFeature = do
804
+ (active, fopts) <- featureBase
805
+ options <- newIORef =<< emptyMap
806
+ let streamable ctx = do opts <- readIORef options; ops <- optStrList opts "ops" ["list"]; op <- readIORef (cOp ctx); pure (opName op `elem` ops)
807
+ iterate_ result = do
808
+ opts <- readIORef options
809
+ chunkDelay <- optNum opts "chunkDelay" 0
810
+ chunkSize <- optInt opts "chunkSize" 0
811
+ resdata <- getp result "resdata"
812
+ items0 <- case resdata of VList _ -> listItems resdata; _ -> pure []
813
+ if chunkSize > 0
814
+ then do
815
+ let go [] = pure []
816
+ go xs = do { let { (h, t) = splitAt chunkSize xs }; when (chunkDelay > 0) (sleepOf opts chunkDelay); hl <- mkList h; rest <- go t; pure (hl : rest) }
817
+ go items0
818
+ else mapM (\item -> do when (chunkDelay > 0) (sleepOf opts chunkDelay); pure item) items0
819
+ hookFn name ctx = do
820
+ a <- readIORef active; s <- streamable ctx
821
+ when (name == "PreResult" && a && s) $ do
822
+ rv <- readIORef (cResult ctx)
823
+ case rv of
824
+ VMap _ -> do
825
+ setp rv "streaming" (VBool True)
826
+ let streamFn = VFunc (\_ _ _ _ -> do items0 <- iterate_ rv; mkList items0)
827
+ setp rv "stream" streamFn
828
+ cl <- cc ctx; bucket <- trackBucket cl "streaming" (jo [("opened", VNum 0)]); bumpNum bucket "opened" 1
829
+ _ -> pure ()
830
+ initFn _ opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a
831
+ pure Feature { fName = "streaming", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
832
+
833
+ -- ------------------------------------------------------------------
834
+ -- proxy
835
+ -- ------------------------------------------------------------------
836
+
837
+ proxyFeature :: IO Feature
838
+ proxyFeature = do
839
+ (active, fopts) <- featureBase
840
+ options <- newIORef =<< emptyMap
841
+ purl <- newIORef VNoval
842
+ noproxy <- newIORef ([] :: [String])
843
+ let track ctx = do cl <- cc ctx; pv <- readIORef purl; bucket <- trackBucket cl "proxy" (jo [("routed", VNum 0), ("url", pv)]); bumpNum bucket "routed" 1
844
+ bypass url = do np <- readIORef noproxy; if null np then pure False else do { let { host = urlHost url }; pure (any (\p -> p == "*" || host == p || endsWith host ("." ++ stripLeadDot p)) np) }
845
+ route ctx url fd = do
846
+ pv <- readIORef purl
847
+ byp <- bypass url
848
+ if isNullish pv || byp then pure fd
849
+ else do
850
+ fdC <- clone fd; out <- case fdC of VMap _ -> pure fdC; _ -> emptyMap
851
+ setp out "proxy" pv
852
+ proxies <- jo [("http", pv), ("https", pv)]; setp out "proxies" proxies
853
+ opts <- readIORef options; agent <- getp opts "agent"
854
+ case agent of { VFunc _ -> do { argsL <- ja [pv, VStr url]; made <- callVfn agent argsL; setp out "dispatcher" made; setp out "agent" made }; _ -> pure () }
855
+ track ctx; pure out
856
+ initFn ctx opts = do
857
+ om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a
858
+ when a $ do
859
+ pv <- getp om "url"; writeIORef purl pv
860
+ npV <- getp om "noProxy"
861
+ npList0 <- case npV of
862
+ VList _ -> do its <- listItems npV; pure [s | VStr s <- its]
863
+ VStr s -> pure (filter (/= "") (map strip (splitOnChar ',' s)))
864
+ _ -> pure []
865
+ npListRef <- newIORef npList0
866
+ fromEnv <- getp om "fromEnv"
867
+ when (isTrueV fromEnv) $ do
868
+ pcur <- readIORef purl
869
+ when (isNullish pcur) $ do
870
+ mv <- firstEnv ["HTTPS_PROXY", "https_proxy", "HTTP_PROXY", "http_proxy"]
871
+ case mv of Just v -> writeIORef purl (VStr v); Nothing -> pure ()
872
+ npc <- readIORef npListRef
873
+ when (null npc) $ do
874
+ mv <- firstEnv ["NO_PROXY", "no_proxy"]
875
+ case mv of Just v -> writeIORef npListRef (filter (/= "") (map strip (splitOnChar ',' v))); Nothing -> pure ()
876
+ npFinal <- readIORef npListRef; writeIORef noproxy npFinal
877
+ u <- cu ctx; inner <- readIORef (uFetcher u); writeIORef (uFetcher u) (\c ur f -> do f2 <- route c ur f; inner c ur f2)
878
+ pure Feature { fName = "proxy", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = \_ _ -> pure () }
879
+
880
+ firstEnv :: [String] -> IO (Maybe String)
881
+ firstEnv [] = pure Nothing
882
+ firstEnv (k : ks) = do mv <- lookupEnv k; case mv of Just v | v /= "" -> pure (Just v); _ -> firstEnv ks
883
+
884
+ -- ------------------------------------------------------------------
885
+ -- netsim (feature)
886
+ -- ------------------------------------------------------------------
887
+
888
+ netsimFeature :: IO Feature
889
+ netsimFeature = do
890
+ (active, fopts) <- featureBase
891
+ options <- newIORef =<< emptyMap
892
+ calls <- newIORef (0 :: Int)
893
+ seedR <- newIORef (1 :: Int)
894
+ let randD = do { s <- readIORef seedR; let { s' = (s * 1103515245 + 12345) .&. 0x7fffffff }; writeIORef seedR s'; pure (fromIntegral s' / fromIntegral (0x7fffffff :: Int)) }
895
+ pickLatency = do
896
+ opts <- readIORef options; lat <- getp opts "latency"
897
+ case lat of
898
+ VNoval -> pure 0; VNull -> pure 0
899
+ VNum n -> pure (if n < 0 then 0 else n)
900
+ VMap _ -> do { mn <- optInt lat "min" 0; mxv <- getp lat "max"; let { mx = case mxv of { VNum k -> truncate k; _ -> mn } }; if mx <= mn then pure (fromIntegral mn) else do { r <- randD; pure (fromIntegral (mn + truncate (r * fromIntegral (mx - mn)))) } }
901
+ _ -> pure 0
902
+ track ctx applied = do
903
+ cl <- cc ctx
904
+ bucket <- trackBucket cl "netsim" (do ap <- emptyList; jo [("calls", VNum 0), ("applied", ap)])
905
+ bumpNum bucket "calls" 1; ap <- getp bucket "applied"; appendList ap applied
906
+ ctrl <- readIORef (cCtrl ctx); explain <- getp ctrl "explain"; case explain of VMap _ -> setp explain "netsim" bucket; _ -> pure ()
907
+ respond status dat extra = do
908
+ out <- jo [("status", vint status), ("statusText", VStr "OK"), ("json", jsonThunk dat), ("body", VStr "not-used")]
909
+ case extra of { VMap _ -> do { ks <- keysof extra; forM_ ks $ \k -> do { v <- getp extra k; setp out k v } }; _ -> pure () }
910
+ hv <- getp out "headers"; let headers = case hv of { VMap _ -> hv; _ -> VNoval }
911
+ headersM <- case headers of VMap _ -> pure headers; _ -> emptyMap
912
+ lowerM <- emptyMap; hks <- keysof headersM; forM_ hks $ \k -> do v <- getp headersM k; setp lowerM (lower k) v
913
+ setp out "headers" lowerM
914
+ pure (out, Nothing)
915
+ simulate ctx url fd inner = do
916
+ opts <- readIORef options
917
+ modifyIORef calls (+ 1); call <- readIORef calls
918
+ applied <- emptyMap
919
+ off <- getp opts "offline"
920
+ if isTrueV off
921
+ then do lat <- pickLatency; sleepOf opts lat; setp applied "offline" (VBool True); track ctx applied; e <- mkErr "netsim_offline" ("Simulated network offline (URL was: \"" ++ url ++ "\")"); pure (VNoval, Just e)
922
+ else do
923
+ errTimes <- optInt opts "errorTimes" 0
924
+ if call <= errTimes
925
+ then do lat <- pickLatency; sleepOf opts lat; setp applied "error" (VBool True); track ctx applied; e <- mkErr "netsim_conn" ("Simulated connection error (call " ++ show call ++ ")"); pure (VNoval, Just e)
926
+ else do
927
+ rlTimes <- optInt opts "rateLimitTimes" 0
928
+ if call <= rlTimes
929
+ then do
930
+ lat <- pickLatency; sleepOf opts lat; setp applied "rateLimited" (VBool True); track ctx applied
931
+ ra <- getp opts "retryAfter"; let raN = case ra of { VNum n -> truncate n :: Int; _ -> 0 }
932
+ hdrs <- jo [("retry-after", VStr (show raN))]; extra <- jo [("statusText", VStr "Too Many Requests"), ("headers", hdrs)]
933
+ respond 429 VNoval extra
934
+ else do
935
+ failStatusV <- getp opts "failStatus"; let failStatus = case failStatusV of { VNum n -> truncate n :: Int; _ -> 503 }
936
+ failEvery <- optInt opts "failEvery" 0
937
+ failRate <- optNum opts "failRate" 0
938
+ failTimes <- optInt opts "failTimes" 0
939
+ r <- randD
940
+ let failByCount = call <= failTimes
941
+ failByEvery = failEvery > 0 && call `mod` failEvery == 0
942
+ failByRate = failRate > 0 && r < failRate
943
+ if failByCount || failByEvery || failByRate
944
+ then do lat <- pickLatency; sleepOf opts lat; setp applied "failStatus" (vint failStatus); track ctx applied; extra <- jo [("statusText", VStr "Simulated Failure")]; respond failStatus VNoval extra
945
+ else do lat <- pickLatency; setp applied "latency" (VNum lat); track ctx applied; sleepOf opts lat; inner ctx url fd
946
+ initFn ctx opts = do
947
+ om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a
948
+ sd <- getp om "seed"; writeIORef seedR (case sd of VNum n | truncate n /= (0 :: Int) -> truncate n; _ -> 1)
949
+ when a $ do u <- cu ctx; inner <- readIORef (uFetcher u); writeIORef (uFetcher u) (\c ur f -> simulate c ur f inner)
950
+ pure Feature { fName = "netsim", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = \_ _ -> pure () }
951
+
952
+ -- ------------------------------------------------------------------
953
+ -- test feature (in-memory mock transport + optional net simulation)
954
+ -- ------------------------------------------------------------------
955
+
956
+ testFeature :: IO Feature
957
+ testFeature = do
958
+ (active, fopts) <- featureBase
959
+ let respondM status dat extra = do
960
+ out <- jo [("status", vint status), ("statusText", VStr "OK"), ("json", jsonThunk dat), ("body", VStr "not-used")]
961
+ case extra of { Just e@(VMap _) -> do { ks <- keysof e; forM_ ks $ \k -> do { v <- getp e k; setp out k v } }; _ -> pure () }
962
+ pure (out, Nothing)
963
+ buildArgs fctx op args = do
964
+ let opname = opName op
965
+ ment <- readIORef (cEntity fctx); let entname = maybe "_" eName ment
966
+ cfg <- readIORef (cConfig fctx)
967
+ points <- getpathS cfg ("entity." ++ entname ++ ".op." ++ opname ++ ".points")
968
+ point <- getelem points (VNum (-1))
969
+ paramsPath <- getpathS point "args.params"
970
+ reqdTrue <- jo [("reqd", VBool True)]
971
+ reqdParams <- select paramsPath reqdTrue
972
+ eachSpec <- ja [VStr "`$EACH`", VStr "", VStr "`$KEY.name`"]
973
+ reqd <- transform INone reqdParams eachSpec
974
+ qandRef <- newIORef []
975
+ case args of
976
+ VMap _ -> do
977
+ ks <- keysof args
978
+ forM_ ks $ \key -> do
979
+ let isId = key == "id"
980
+ sel <- select reqd (VStr key); emptySel <- isempty sel; let isReqd = not emptySel
981
+ when (isId || isReqd) $ do
982
+ u <- cu fctx; pfn <- readIORef (uParam u); v <- pfn fctx (VStr key)
983
+ kaV <- case opAlias op of VMap _ -> getp (opAlias op) key; _ -> pure VNoval
984
+ orItem1 <- jo [(key, v)]
985
+ orList <- case kaV of { VStr s -> do { o2 <- jo [(s, v)]; ja [orItem1, o2] }; _ -> ja [orItem1] }
986
+ orMap <- jo [("`$OR`", orList)]
987
+ modifyIORef qandRef (++ [orMap])
988
+ _ -> pure ()
989
+ qand <- readIORef qandRef; qandL <- ja qand
990
+ q <- jo [("`$AND`", qandL)]
991
+ ctrl <- readIORef (cCtrl fctx); explain <- getp ctrl "explain"
992
+ case explain of { VMap _ -> do { tq <- jo [("query", q)]; setp explain "test" tq }; _ -> pure () }
993
+ pure q
994
+ resolveMatch fctx explicit = do
995
+ sz <- case explicit of VMap _ -> size explicit; _ -> pure 0
996
+ let isMapNonEmpty = case explicit of { VMap _ -> sz > 0; _ -> False }
997
+ if isMapNonEmpty then pure explicit
998
+ else do
999
+ mv <- readIORef (cMatch fctx); r1 <- trySrc mv
1000
+ case r1 of
1001
+ Just v -> jo [("id", v)]
1002
+ Nothing -> do dv <- readIORef (cData fctx); r2 <- trySrc dv; case r2 of Just v -> jo [("id", v)]; Nothing -> emptyMap
1003
+ trySrc src = case src of
1004
+ VMap _ -> do i <- getp src "id"; pure (case i of VNoval -> Nothing; VStr "__UNDEFINED__" -> Nothing; v -> Just v)
1005
+ _ -> pure Nothing
1006
+ makeMock entity = \fctx _url _fd -> do
1007
+ op <- readIORef (cOp fctx)
1008
+ entmapV <- getp entity (opEntity op); entmap <- case entmapV of VMap _ -> pure entmapV; _ -> emptyMap
1009
+ case opName op of
1010
+ "load" -> do
1011
+ rm <- readIORef (cReqmatch fctx); m <- resolveMatch fctx rm
1012
+ args <- buildArgs fctx op m; found <- select entmap args; ent <- getelem found (VNum 0)
1013
+ if isNullish ent then respondM 404 VNoval . Just =<< jo [("statusText", VStr "Not found")]
1014
+ else do delp ent "$KEY"; c <- clone ent; respondM 200 c Nothing
1015
+ "list" -> do
1016
+ rm <- readIORef (cReqmatch fctx)
1017
+ args <- buildArgs fctx op rm; found <- select entmap args
1018
+ if isNullish found then respondM 404 VNoval . Just =<< jo [("statusText", VStr "Not found")]
1019
+ else do { case found of { VList _ -> do { its <- listItems found; forM_ its (\i -> delp i "$KEY") }; _ -> pure () }; c <- clone found; respondM 200 c Nothing }
1020
+ "update" -> do
1021
+ rd <- readIORef (cReqdata fctx)
1022
+ um0 <- emptyMap
1023
+ case rd of VMap _ -> do { i <- getp rd "id"; case i of VNoval -> pure (); v -> setp um0 "id" v }; _ -> pure ()
1024
+ umSz <- size um0
1025
+ um <- if umSz > 0 then pure um0 else do em <- emptyMap; resolveMatch fctx em
1026
+ args <- buildArgs fctx op um; found <- select entmap args; ent0 <- getelem found (VNum 0)
1027
+ ent <- if isNullish ent0 then entFallback entmap else pure ent0
1028
+ if isNullish ent then respondM 404 VNoval . Just =<< jo [("statusText", VStr "Not found")]
1029
+ else do
1030
+ case ent of { VMap _ -> case rd of { VMap _ -> do { ks <- keysof rd; forM_ ks (\k -> do { v <- getp rd k; setp ent k v }) }; _ -> pure () }; _ -> pure () }
1031
+ delp ent "$KEY"; c <- clone ent; respondM 200 c Nothing
1032
+ "remove" -> do
1033
+ rm <- readIORef (cReqmatch fctx); m <- resolveMatch fctx rm
1034
+ args <- buildArgs fctx op m; found <- select entmap args; ent <- getelem found (VNum 0)
1035
+ case ent of VMap _ -> do { eid <- getp ent "id"; () <$ delprop entmap eid }; _ -> pure ()
1036
+ respondM 200 VNoval Nothing
1037
+ "create" -> do
1038
+ rd <- readIORef (cReqdata fctx)
1039
+ _ <- buildArgs fctx op rd
1040
+ u <- cu fctx; pfn <- readIORef (uParam u); eidV <- pfn fctx (VStr "id")
1041
+ eid <- if isNullish eidV then VStr <$> randId16 else pure eidV
1042
+ ent <- clone rd
1043
+ case ent of
1044
+ VMap _ -> do { setp ent "id" eid; case eid of { VStr s -> setp entmap s ent; _ -> pure () }; delp ent "$KEY"; c <- clone ent; respondM 200 c Nothing }
1045
+ _ -> respondM 200 ent Nothing
1046
+ _ -> respondM 404 VNoval . Just =<< jo [("statusText", VStr "Unknown operation")]
1047
+ makeNetsim net inner = do
1048
+ netcalls <- newIORef (0 :: Int)
1049
+ let pickLat = do
1050
+ lat <- getp net "latency"
1051
+ case lat of
1052
+ VNoval -> pure 0; VNull -> pure 0
1053
+ VNum n -> pure (if n < 0 then 0 else n)
1054
+ VMap _ -> do { mn <- optInt lat "min" 0; mxv <- getp lat "max"; let { mx = case mxv of { VNum k -> truncate k; _ -> mn } }; pure (if mx <= mn then fromIntegral mn else fromIntegral (mn + ((mx - mn) `div` 2))) }
1055
+ _ -> pure 0
1056
+ sleepN ms = when (ms > 0) $ do s <- getp net "sleep"; case s of VFunc _ -> () <$ callVfn s (VNum ms); _ -> realSleep ms
1057
+ pure $ \fctx url fd -> do
1058
+ modifyIORef netcalls (+ 1); call <- readIORef netcalls
1059
+ off <- getp net "offline"
1060
+ if isTrueV off then do lat <- pickLat; sleepN lat; e <- mkErr "netsim_offline" ("Simulated network offline (URL was: \"" ++ url ++ "\")"); pure (VNoval, Just e)
1061
+ else do
1062
+ errTimes <- optInt net "errorTimes" 0
1063
+ if call <= errTimes then do lat <- pickLat; sleepN lat; e <- mkErr "netsim_conn" ("Simulated connection error (call " ++ show call ++ ")"); pure (VNoval, Just e)
1064
+ else do
1065
+ failTimes <- optInt net "failTimes" 0
1066
+ if call <= failTimes then do lat <- pickLat; sleepN lat; failStatusV <- getp net "failStatus"; let { fs = case failStatusV of { VNum n -> truncate n :: Int; _ -> 503 } }; hdrs <- emptyMap; out <- jo [("status", vint fs), ("statusText", VStr "Simulated Failure"), ("body", VStr "not-used"), ("json", jsonThunk VNoval), ("headers", hdrs)]; pure (out, Nothing)
1067
+ else do lat <- pickLat; sleepN lat; inner fctx url fd
1068
+ initFn ctx opts = do
1069
+ entityV <- getp opts "entity"; entity <- case entityV of VMap _ -> pure entityV; _ -> emptyMap
1070
+ cl <- cc ctx; writeIORef (clMode cl) "test"
1071
+ let walkFn key v _parent path = do d <- size path; when (d == 2 && ismap v && not (isNullish key)) (setp v "id" key); pure v
1072
+ _ <- walk (Just walkFn) Nothing VNoval entity
1073
+ let mock = makeMock entity
1074
+ u <- cu ctx
1075
+ net <- getp opts "net"
1076
+ case net of VMap _ -> do { ns <- makeNetsim net mock; writeIORef (uFetcher u) ns }; _ -> writeIORef (uFetcher u) mock
1077
+ pure Feature { fName = "test", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = \_ _ -> pure () }
1078
+
1079
+ entFallback :: Value -> IO Value
1080
+ entFallback entmap = do ks <- keysof entmap; go ks
1081
+ where go [] = pure VNoval
1082
+ go (k : rest) = do v <- getp entmap k; case v of VMap _ -> pure v; _ -> go rest
1083
+
1084
+ -- ------------------------------------------------------------------
1085
+ -- client construction + direct + prepare + test
1086
+ -- ------------------------------------------------------------------
1087
+
1088
+ makeClientBase :: Value -> (String -> IO Feature) -> Value -> IO Client
1089
+ makeClientBase config makeFeature options = do
1090
+ utility <- newUtility
1091
+ modeR <- newIORef "live"; featsR <- newIORef []; optsR <- newIORef VNoval; rootR <- newIORef Nothing; trackR <- newIORef =<< emptyMap
1092
+ let client = Client { clMode = modeR, clFeatures = featsR, clOptions = optsR, clUtility = utility, clRootctx = rootR, clTrack = trackR, clConfig = config, clMakeFeature = makeFeature }
1093
+ rootopts <- case options of VNoval -> emptyMap; _ -> pure options
1094
+ sh <- emptyMap
1095
+ rootctx <- makeContextImpl (defaultCtxSpec { csClient = Just client, csUtility = Just utility, csConfig = Just config, csOptions = Just rootopts, csShared = Just sh }) Nothing
1096
+ writeIORef rootR (Just rootctx)
1097
+ opts <- makeOptionsUtil rootctx
1098
+ writeIORef optsR opts
1099
+ ta <- getpathS opts "feature.test.active"
1100
+ when (isTrueV ta) (writeIORef modeR "test")
1101
+ writeIORef (cOptions rootctx) opts
1102
+ -- Add features in the resolved order (makeOptions records an explicit array
1103
+ -- order, else defaults to test-first). Ordering matters: the `test` feature
1104
+ -- installs the base mock transport and the transport features
1105
+ -- (retry/cache/netsim/proxy/ratelimit) wrap whatever is current, so `test`
1106
+ -- must be added before them to sit at the base of the wrapper chain.
1107
+ featureOpts <- do fmV <- toMap <$> getp opts "feature"; case fmV of VMap _ -> pure fmV; _ -> emptyMap
1108
+ orderV <- getpathS opts "__derived__.featureorder"
1109
+ order <- case orderV of VList ref -> readIORef ref; _ -> pure []
1110
+ forM_ order $ \fnameV -> case fnameV of
1111
+ VStr fname -> do
1112
+ foptsV <- toMap <$> getp featureOpts fname
1113
+ case foptsV of
1114
+ VMap _ -> do a <- getp foptsV "active"; when (isTrueV a) $ do ftr <- makeFeature fname; featureAddUtil rootctx ftr
1115
+ _ -> pure ()
1116
+ _ -> pure ()
1117
+ feats <- readIORef featsR
1118
+ forM_ feats (featureInitUtil rootctx)
1119
+ featureHookUtil rootctx "PostConstruct"
1120
+ pure client
1121
+
1122
+ prepare :: Client -> Value -> IO Value
1123
+ prepare client fetchargs = do
1124
+ let u = clUtility client
1125
+ fa <- case fetchargs of VNoval -> emptyMap; _ -> pure fetchargs
1126
+ ctrlV <- toMap <$> getp fa "ctrl"
1127
+ ctrl <- case ctrlV of VMap _ -> pure ctrlV; _ -> emptyMap
1128
+ root <- readIORef (clRootctx client)
1129
+ ctx <- makeContextImpl (defaultCtxSpec { csOpname = Just "prepare", csCtrl = Just ctrl }) root
1130
+ options <- readIORef (clOptions client)
1131
+ path <- getStrD fa "path" ""
1132
+ method <- getStrD fa "method" "GET"
1133
+ paramsV <- toMap <$> getp fa "params"; params <- case paramsV of VMap _ -> pure paramsV; _ -> emptyMap
1134
+ queryV <- toMap <$> getp fa "query"; query <- case queryV of VMap _ -> pure queryV; _ -> emptyMap
1135
+ headers <- prepareHeadersUtil ctx
1136
+ base <- getStrD options "base" ""; prefix <- getStrD options "prefix" ""; suffix <- getStrD options "suffix" ""
1137
+ body <- getp fa "body"
1138
+ specm <- jo [("base", VStr base), ("prefix", VStr prefix), ("suffix", VStr suffix), ("path", VStr path), ("method", VStr method), ("params", params), ("query", query), ("headers", headers), ("body", body), ("step", VStr "start")]
1139
+ sp <- newSpec specm
1140
+ writeIORef (cSpec ctx) sp
1141
+ uh <- getp fa "headers"
1142
+ case uh of VMap _ -> do { spH <- getp sp "headers"; ks <- keysof uh; forM_ ks (\k -> do { v <- getp uh k; setp spH k v }) }; _ -> pure ()
1143
+ (_, merr) <- prepareAuthUtil ctx
1144
+ case merr of Just e -> throwIO (SdkException e); Nothing -> pure ()
1145
+ (fd, merr2) <- makeFetchDefUtil ctx
1146
+ case merr2 of Just e -> throwIO (SdkException e); Nothing -> pure fd
1147
+
1148
+ -- Is this raw-access op permitted by the SDK's allow.op option?
1149
+ opAllowed :: Client -> String -> IO Bool
1150
+ opAllowed client op = do
1151
+ opts <- readIORef (clOptions client)
1152
+ allow <- getpathS opts "allow.op"
1153
+ pure (case allow of VStr s -> substrContains s op; _ -> False)
1154
+
1155
+ opDenied :: Client -> String -> IO Value
1156
+ opDenied client op = do
1157
+ opts <- readIORef (clOptions client)
1158
+ allow <- getpathS opts "allow.op"
1159
+ let a = case allow of VStr s -> s; _ -> ""
1160
+ jo [ ("ok", VBool False)
1161
+ , ("err", VStr ("ProjectNameSDK: " ++ op ++ ": operation not allowed by"
1162
+ ++ " SDK option allow.op value: \"" ++ a ++ "\"")) ]
1163
+
1164
+ -- Raw endpoint access is operator-controllable, like every entity op.
1165
+ -- Blocking it means denying BOTH the 'direct' and 'graphql' tokens, since
1166
+ -- either one reaches the same endpoint.
1167
+ direct :: Client -> Value -> IO Value
1168
+ direct client fetchargs = do
1169
+ allowed <- opAllowed client "direct"
1170
+ if not allowed then opDenied client "direct" else rawRequest client fetchargs
1171
+
1172
+ -- Raw GraphQL access: the pressure valve that makes the generated surface's
1173
+ -- deliberate omissions (per-call selection sets, typed filter builders,
1174
+ -- batching, subscriptions) livable — the whole schema stays reachable.
1175
+ --
1176
+ -- Thin wrapper over the same prepare/fetch path direct uses, with the one
1177
+ -- thing raw direct cannot do for GraphQL: a GraphQL failure rides HTTP 200 as
1178
+ -- a top-level `errors` array, so status alone would report a failed query as
1179
+ -- ok.
1180
+ --
1181
+ -- NOTE: like direct, this bypasses the feature pipeline — no retry, ratelimit
1182
+ -- or paging features apply.
1183
+ graphql :: Client -> String -> Value -> Value -> IO Value
1184
+ graphql client query variables ctrl = do
1185
+ allowed <- opAllowed client "graphql"
1186
+ if not allowed then opDenied client "graphql" else do
1187
+ vars <- case variables of VMap _ -> pure variables; _ -> emptyMap
1188
+ ctl <- case ctrl of VMap _ -> pure ctrl; _ -> emptyMap
1189
+ headers <- jo [("content-type", VStr "application/json")]
1190
+ body <- jo [("query", VStr query), ("variables", vars)]
1191
+ fa <- jo [ ("method", VStr "POST"), ("headers", headers)
1192
+ , ("body", body), ("ctrl", ctl) ]
1193
+ res <- rawRequest client fa
1194
+
1195
+ -- Errors are read BEFORE any status check: a GraphQL parse or validation
1196
+ -- failure comes back as HTTP 400 carrying the standard { errors: [...] }
1197
+ -- body, and the raw path represents a non-2xx as ok:False with no err —
1198
+ -- so returning early on status would discard the server's own
1199
+ -- diagnostics, which are the only useful part of that response.
1200
+ ev <- getpathS res "data.errors"
1201
+ errors <- case ev of VList _ -> listItems ev; _ -> pure []
1202
+ case errors of
1203
+ [] -> pure res
1204
+ (firsterr : _) -> do
1205
+ m0 <- getStrD firsterr "message" ""
1206
+ let msg = if null m0 then "graphql error" else m0
1207
+ setp res "ok" (VBool False)
1208
+ setp res "err" (VStr ("ProjectNameSDK: graphql: " ++ msg))
1209
+ setp res "graphql" ev
1210
+ pure res
1211
+
1212
+ -- Ungated request path shared by direct and graphql, each of which checks its
1213
+ -- own allow.op token first. Separate, rather than a flag on fetchargs: a
1214
+ -- caller-supplied marker would let anyone opt straight back out of the gate
1215
+ -- by passing it.
1216
+ rawRequest :: Client -> Value -> IO Value
1217
+ rawRequest client fetchargs = do
1218
+ let u = clUtility client
1219
+ fa <- case fetchargs of VNoval -> emptyMap; _ -> pure fetchargs
1220
+ res <- try (prepare client fa) :: IO (Either SdkException Value)
1221
+ case res of
1222
+ Left (SdkException e) -> do ev <- errToValue e; jo [("ok", VBool False), ("err", ev)]
1223
+ Right fetchdef -> do
1224
+ ctrlV <- toMap <$> getp fa "ctrl"; ctrl <- case ctrlV of VMap _ -> pure ctrlV; _ -> emptyMap
1225
+ root <- readIORef (clRootctx client)
1226
+ ctx <- makeContextImpl (defaultCtxSpec { csOpname = Just "direct", csCtrl = Just ctrl }) root
1227
+ url <- getStrD fetchdef "url" ""
1228
+ fetcher <- readIORef (uFetcher u)
1229
+ (fetched, ferr) <- fetcher ctx url fetchdef
1230
+ case ferr of
1231
+ Just fe -> do ev <- errToValue fe; jo [("ok", VBool False), ("err", ev)]
1232
+ Nothing ->
1233
+ if isNoval fetched || isNullV fetched
1234
+ then do e <- mkErr "direct_no_response" "response: undefined"; ev <- errToValue e; jo [("ok", VBool False), ("err", ev)]
1235
+ else case fetched of
1236
+ VMap _ -> do
1237
+ st <- getp fetched "status"; let status = toInt st
1238
+ headersV <- getp fetched "headers"; headers <- case headersV of VMap _ -> pure headersV; _ -> emptyMap
1239
+ clv <- getp headers "content-length"; let cl = case clv of { VStr s -> s; VNum n -> show (truncate n :: Int); _ -> "" }
1240
+ let noBody = status == 204 || status == 304 || cl == "0"
1241
+ jsonData <- if noBody then pure VNoval else do jf <- getp fetched "json"; case jf of VFunc _ -> callJson jf; _ -> pure VNoval
1242
+ jo [("ok", VBool (status >= 200 && status < 300)), ("status", vint status), ("headers", headers), ("data", jsonData)]
1243
+ _ -> do e <- mkErr "direct_invalid" "invalid response type"; ev <- errToValue e; jo [("ok", VBool False), ("err", ev)]
1244
+
1245
+ sdkTest :: Value -> (String -> IO Feature) -> Value -> Value -> IO Client
1246
+ sdkTest config makeFeature testopts sdkopts = do
1247
+ so0 <- case sdkopts of VNoval -> emptyMap; _ -> pure sdkopts
1248
+ soC <- clone so0; sdkopts' <- case soC of VMap _ -> pure soC; _ -> emptyMap
1249
+ to0 <- case testopts of VNoval -> emptyMap; _ -> pure testopts
1250
+ toC <- clone to0; testopts' <- case toC of VMap _ -> pure toC; _ -> emptyMap
1251
+ setp testopts' "active" (VBool True)
1252
+ p <- ja [VStr "feature", VStr "test"]
1253
+ _ <- setpath sdkopts' p testopts'
1254
+ sdk <- makeClientBase config makeFeature sdkopts'
1255
+ writeIORef (clMode sdk) "test"
1256
+ pure sdk
1257
+
1258
+ -- ------------------------------------------------------------------
1259
+ -- generic entity (config-driven)
1260
+ -- ------------------------------------------------------------------
1261
+
1262
+ runOpPipeline :: Context -> IO () -> IO Value
1263
+ runOpPipeline ctx postDone = do
1264
+ let fh n = featureHookUtil ctx n
1265
+ setOut k v = do out <- readIORef (cOut ctx); setp out k v
1266
+ fh "PrePoint"
1267
+ (point, e1) <- makePointUtil ctx
1268
+ case e1 of
1269
+ Just e -> makeErrorUtil ctx (Just e)
1270
+ Nothing -> do
1271
+ setOut "point" point
1272
+ fh "PreSpec"
1273
+ (spec, e2) <- makeSpecUtil ctx
1274
+ case e2 of
1275
+ Just e -> makeErrorUtil ctx (Just e)
1276
+ Nothing -> do
1277
+ setOut "spec" spec
1278
+ fh "PreRequest"
1279
+ (resp, e3) <- makeRequestUtil ctx
1280
+ case e3 of
1281
+ Just e -> makeErrorUtil ctx (Just e)
1282
+ Nothing -> do
1283
+ setOut "request" resp
1284
+ fh "PreResponse"
1285
+ (resp2, e4) <- makeResponseUtil ctx
1286
+ case e4 of
1287
+ Just e -> makeErrorUtil ctx (Just e)
1288
+ Nothing -> do
1289
+ setOut "response" resp2
1290
+ fh "PreResult"
1291
+ (result, e5) <- makeResultUtil ctx
1292
+ case e5 of
1293
+ Just e -> makeErrorUtil ctx (Just e)
1294
+ Nothing -> do
1295
+ setOut "result" result
1296
+ fh "PreDone"
1297
+ postDone
1298
+ doneUtil ctx
1299
+
1300
+ -- Truncate a materialised stream when the signal fn returns true (checked
1301
+ -- before each element, mirroring an async iterator's per-yield cancellation).
1302
+ streamTakeUntil :: Value -> [Value] -> IO [Value]
1303
+ streamTakeUntil _ [] = pure []
1304
+ streamTakeUntil sig (x : xs) = do
1305
+ r <- callVfn sig VNoval
1306
+ if isTrueV r then pure [] else do rest <- streamTakeUntil sig xs; pure (x : rest)
1307
+
1308
+ makeEntity :: Client -> String -> Value -> IO Entity
1309
+ makeEntity client name entopts = do
1310
+ entopts' <- case entopts of VMap _ -> pure entopts; _ -> emptyMap
1311
+ a <- getBool entopts' "active"
1312
+ case a of Just False -> pure (); _ -> setp entopts' "active" (VBool True)
1313
+ utility <- copyUtility (clUtility client)
1314
+ dataR <- newIORef =<< emptyMap
1315
+ matchR <- newIORef =<< emptyMap
1316
+ deletedR <- newIORef False
1317
+ entctxR <- newIORef Nothing
1318
+ let entCtx = do m <- readIORef entctxR; case m of Just c -> pure c; Nothing -> error "entity context not initialised"
1319
+ setDataFrom rv = do
1320
+ resdata <- getp rv "resdata"
1321
+ when (not (isNoval resdata) && not (isNullV resdata)) $ do { c <- clone resdata; m <- case toMap c of { VMap _ -> pure c; _ -> emptyMap }; writeIORef dataR m }
1322
+ setMatchFrom rv = do resmatch <- getp rv "resmatch"; case resmatch of VMap _ -> writeIORef matchR resmatch; _ -> pure ()
1323
+ mkOp opname inputKind reqval ctrl postDone = do
1324
+ ec <- entCtx
1325
+ mv <- readIORef matchR; dv <- readIORef dataR
1326
+ mC <- clone mv; dC <- clone dv
1327
+ ctrlM <- case toMap ctrl of VMap _ -> pure ctrl; _ -> emptyMap
1328
+ let base = defaultCtxSpec { csOpname = Just opname, csCtrl = Just ctrlM, csMatch = Just mC, csData = Just dC }
1329
+ cspec = if inputKind == "data" then base { csReqdata = Just reqval } else base { csReqmatch = Just reqval }
1330
+ ctx <- makeContextImpl cspec (Just ec)
1331
+ runOpPipeline ctx (postDone ctx)
1332
+ postLoad ctx = do rv <- readIORef (cResult ctx); case rv of VMap _ -> do { setMatchFrom rv; setDataFrom rv }; _ -> pure ()
1333
+ postList ctx = do rv <- readIORef (cResult ctx); case rv of VMap _ -> setMatchFrom rv; _ -> pure ()
1334
+ postCreate ctx = do rv <- readIORef (cResult ctx); case rv of VMap _ -> setDataFrom rv; _ -> pure ()
1335
+ postUpdate = postLoad
1336
+ postRemove = postLoad
1337
+ let ent = Entity
1338
+ { eName = name, eClient = client, eUtility = utility, eEntopts = entopts'
1339
+ , eData = dataR, eMatch = matchR, eEntctx = entctxR
1340
+ , eMake = do o <- clone entopts'; makeEntity client name o
1341
+ , eDataSet = \d -> writeIORef dataR d
1342
+ , eDataGet = readIORef dataR
1343
+ -- Ops resolve to the ENTITY (see SdkTypes). mkOp still runs the whole
1344
+ -- pipeline and throws SdkException on failure; with throwing disabled
1345
+ -- it returns the error payload, which an IO Entity cannot carry, so
1346
+ -- that path resolves to the entity too and the caller reads ctrl.err
1347
+ -- — the mechanism the no-throw mode documents anyway.
1348
+ , eLoad = \rm ctrl -> mkOp "load" "match" rm ctrl postLoad >> pure ent
1349
+ , eList = \rm ctrl -> do
1350
+ out <- mkOp "list" "match" rm ctrl postList
1351
+ -- `list` resolves to one ENTITY per record. makeResult cannot
1352
+ -- build them — it works in Value — so this does.
1353
+ case out of
1354
+ VList _ -> do
1355
+ items <- listItems out
1356
+ mapM (\entry -> do
1357
+ e <- eMake ent
1358
+ case entry of VMap _ -> eDataSet e entry; _ -> pure ()
1359
+ pure e) items
1360
+ _ -> pure []
1361
+ , eCreate = \rd ctrl -> mkOp "create" "data" rd ctrl postCreate >> pure ent
1362
+ , eUpdate = \rd ctrl -> mkOp "update" "data" rd ctrl postUpdate >> pure ent
1363
+ , eRemove = \rm ctrl -> do
1364
+ _ <- mkOp "remove" "match" rm ctrl postRemove
1365
+ -- A removed entity keeps its data but is no longer a live record.
1366
+ writeIORef deletedR True
1367
+ pure ent
1368
+ , eDeleted = deletedR
1369
+ , eMarkDeleted = writeIORef deletedR True
1370
+ -- Streaming operation. Runs `action` through the full pipeline and
1371
+ -- returns a lazy list of result items, so the streaming feature's
1372
+ -- incremental output is reachable (a normal op call materialises the
1373
+ -- whole result). When the streaming feature is active the result
1374
+ -- carries a stream closure and this yields from it (honouring
1375
+ -- chunkSize); otherwise it falls back to the materialised items, so
1376
+ -- stream always yields. callopts: ctrl (per-call pipeline control),
1377
+ -- body (an enumerable/list payload attached to the request for
1378
+ -- outbound streaming), signal (a 0-arity fn -> Bool; stop when true).
1379
+ , eStream = \action args callopts -> do
1380
+ ec <- entCtx
1381
+ coV <- case toMap callopts of VMap _ -> pure callopts; _ -> emptyMap
1382
+ ctrlV <- getp coV "ctrl"
1383
+ ctrl <- case toMap ctrlV of VMap _ -> pure ctrlV; _ -> emptyMap
1384
+ setp ctrl "stream" coV
1385
+ mv <- readIORef matchR; dv <- readIORef dataR
1386
+ mC <- clone mv; dC <- clone dv
1387
+ reqmatch <- case toMap args of VMap _ -> pure args; _ -> emptyMap
1388
+ let cspec = defaultCtxSpec { csOpname = Just action, csCtrl = Just ctrl, csMatch = Just mC, csData = Just dC, csReqmatch = Just reqmatch }
1389
+ ctx <- makeContextImpl cspec (Just ec)
1390
+ body <- getp coV "body"
1391
+ when (not (isNoval body) && not (isNullV body)) $ do
1392
+ rdV <- readIORef (cReqdata ctx)
1393
+ rd <- case rdV of VMap _ -> pure rdV; _ -> emptyMap
1394
+ setp rd "body$" body
1395
+ writeIORef (cReqdata ctx) rd
1396
+ _ <- runOpPipeline ctx (pure ())
1397
+ rv <- readIORef (cResult ctx)
1398
+ raw <- case rv of
1399
+ VMap _ -> do
1400
+ sf <- getp rv "stream"
1401
+ case sf of
1402
+ VFunc _ -> do r <- callVfn sf VNoval; case r of VList _ -> listItems r; _ -> pure []
1403
+ _ -> do resdata <- getp rv "resdata"; case resdata of VList _ -> listItems resdata; VNoval -> pure []; v -> pure [v]
1404
+ _ -> pure []
1405
+ sig <- getp coV "signal"
1406
+ case sig of
1407
+ VFunc _ -> streamTakeUntil sig raw
1408
+ _ -> pure raw
1409
+ }
1410
+ root <- readIORef (clRootctx client)
1411
+ entctx <- makeContextImpl (defaultCtxSpec { csEntity = Just ent, csEntopts = Just entopts' }) root
1412
+ featureHookUtil entctx "PostConstructEntity"
1413
+ writeIORef entctxR (Just entctx)
1414
+ pure ent
1415
+
1416
+ -- public entity data/match accessors (fire hooks)
1417
+ entityData :: Entity -> Maybe Value -> IO Value
1418
+ entityData ent margs = do
1419
+ case margs of
1420
+ Just arg | not (isNoval arg) && not (isNullV arg) -> do
1421
+ c <- clone arg; m <- case toMap c of { VMap _ -> pure c; _ -> emptyMap }
1422
+ writeIORef (eData ent) m
1423
+ ec <- entCtxOf ent; featureHookUtil ec "SetData"
1424
+ _ -> pure ()
1425
+ ec <- entCtxOf ent; featureHookUtil ec "GetData"
1426
+ d <- readIORef (eData ent); clone d
1427
+
1428
+ entityMatch :: Entity -> Maybe Value -> IO Value
1429
+ entityMatch ent margs = do
1430
+ case margs of
1431
+ Just arg | not (isNoval arg) && not (isNullV arg) -> do
1432
+ c <- clone arg; m <- case toMap c of { VMap _ -> pure c; _ -> emptyMap }
1433
+ writeIORef (eMatch ent) m
1434
+ ec <- entCtxOf ent; featureHookUtil ec "SetMatch"
1435
+ _ -> pure ()
1436
+ ec <- entCtxOf ent; featureHookUtil ec "GetMatch"
1437
+ m <- readIORef (eMatch ent); clone m
1438
+
1439
+ entCtxOf :: Entity -> IO Context
1440
+ entCtxOf ent = do m <- readIORef (eEntctx ent); case m of Just c -> pure c; Nothing -> error "entity context not initialised"