@voxgig/sdkgen 1.5.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/dist/cmp/AgentGuideContent.js +5 -0
  2. package/dist/cmp/AgentGuideContent.js.map +1 -1
  3. package/dist/cmp/Feature.js +13 -0
  4. package/dist/cmp/Feature.js.map +1 -1
  5. package/dist/helpers/canonType.d.ts +3 -1
  6. package/dist/helpers/canonType.js +44 -1
  7. package/dist/helpers/canonType.js.map +1 -1
  8. package/dist/helpers/packageMeta.js +7 -0
  9. package/dist/helpers/packageMeta.js.map +1 -1
  10. package/dist/sdkgen.d.ts +3 -3
  11. package/dist/sdkgen.js +4 -1
  12. package/dist/sdkgen.js.map +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/package.json +7 -6
  15. package/project/.sdk/model/feature/paging.aontu +7 -0
  16. package/project/.sdk/model/target/py-data.aontu +64 -0
  17. package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +89 -1
  18. package/project/.sdk/src/cmp/clojure/Main_clojure.ts +1 -0
  19. package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +97 -0
  20. package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +78 -0
  21. package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +84 -0
  22. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +87 -0
  23. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +75 -0
  24. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +85 -0
  25. package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +4 -0
  26. package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +80 -0
  27. package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +86 -0
  28. package/project/.sdk/src/cmp/py/Entity_py.ts +9 -2
  29. package/project/.sdk/src/cmp/py/MainEntity_py.ts +1 -1
  30. package/project/.sdk/src/cmp/py/Main_py.ts +34 -6
  31. package/project/.sdk/src/cmp/py/Package_py.ts +6 -9
  32. package/project/.sdk/src/cmp/py/TestDirect_py.ts +2 -2
  33. package/project/.sdk/src/cmp/py/TestEntity_py.ts +3 -3
  34. package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +2 -2
  35. package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +2 -2
  36. package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +2 -2
  37. package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +2 -2
  38. package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +2 -2
  39. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +75 -8
  40. package/project/.sdk/src/cmp/py-data/Main_py-data.ts +987 -0
  41. package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +71 -0
  42. package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +95 -0
  43. package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +86 -0
  44. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +75 -0
  45. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +95 -0
  46. package/project/.sdk/tm/c/core/sdk.h +7 -0
  47. package/project/.sdk/tm/c/feature/paging.c +141 -2
  48. package/project/.sdk/tm/c/utility/graphql.c +178 -0
  49. package/project/.sdk/tm/c/utility/make_options.c +1 -1
  50. package/project/.sdk/tm/c/utility/make_response.c +6 -0
  51. package/project/.sdk/tm/c/utility/make_spec.c +23 -3
  52. package/project/.sdk/tm/clojure/src/sdk/client.clj +64 -1
  53. package/project/.sdk/tm/clojure/src/sdk/core.clj +126 -3
  54. package/project/.sdk/tm/clojure/src/sdk/features.clj +80 -14
  55. package/project/.sdk/tm/cpp/core/types.hpp +83 -0
  56. package/project/.sdk/tm/cpp/feature/paging.hpp +103 -1
  57. package/project/.sdk/tm/cpp/utility/pipeline.hpp +152 -3
  58. package/project/.sdk/tm/csharp/core/Utility.cs +4 -0
  59. package/project/.sdk/tm/csharp/feature/PagingFeature.cs +118 -1
  60. package/project/.sdk/tm/csharp/utility/Graphql.cs +190 -0
  61. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +1 -1
  62. package/project/.sdk/tm/csharp/utility/MakeResponse.cs +7 -0
  63. package/project/.sdk/tm/csharp/utility/MakeSpec.cs +21 -2
  64. package/project/.sdk/tm/csharp/utility/Register.cs +2 -0
  65. package/project/.sdk/tm/dart/lib/Point.dart +7 -0
  66. package/project/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +99 -4
  67. package/project/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
  68. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +1 -1
  69. package/project/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +5 -0
  70. package/project/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +18 -2
  71. package/project/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +6 -1
  72. package/project/.sdk/tm/dart/lib/utility/Utility.dart +9 -0
  73. package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +136 -13
  74. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +187 -3
  75. package/project/.sdk/tm/go/core/utility_type.go +2 -0
  76. package/project/.sdk/tm/go/feature/paging_feature.go +87 -1
  77. package/project/.sdk/tm/go/utility/graphql.go +168 -0
  78. package/project/.sdk/tm/go/utility/make_options.go +1 -1
  79. package/project/.sdk/tm/go/utility/make_response.go +6 -0
  80. package/project/.sdk/tm/go/utility/make_spec.go +19 -2
  81. package/project/.sdk/tm/go/utility/register.go +2 -0
  82. package/project/.sdk/tm/haskell/src/SdkFeatures.hs +146 -7
  83. package/project/.sdk/tm/haskell/src/SdkRuntime.hs +153 -3
  84. package/project/.sdk/tm/java/core/SdkClient.java +93 -0
  85. package/project/.sdk/tm/java/core/Utility.java +4 -0
  86. package/project/.sdk/tm/java/feature/PagingFeature.java +100 -1
  87. package/project/.sdk/tm/java/utility/Graphql.java +191 -0
  88. package/project/.sdk/tm/java/utility/MakeOptions.java +1 -1
  89. package/project/.sdk/tm/java/utility/MakeResponse.java +6 -0
  90. package/project/.sdk/tm/java/utility/MakeSpec.java +17 -2
  91. package/project/.sdk/tm/java/utility/Register.java +2 -0
  92. package/project/.sdk/tm/js/src/Point.js +5 -0
  93. package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +82 -3
  94. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +14 -2
  95. package/project/.sdk/tm/js/src/utility/GraphqlUtility.js +148 -0
  96. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +1 -1
  97. package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +7 -0
  98. package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +19 -2
  99. package/project/.sdk/tm/js/src/utility/Utility.js +4 -0
  100. package/project/.sdk/tm/kotlin/core/SdkClient.kt +84 -0
  101. package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
  102. package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +92 -1
  103. package/project/.sdk/tm/kotlin/utility/Graphql.kt +145 -0
  104. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +1 -1
  105. package/project/.sdk/tm/kotlin/utility/MakeReqRes.kt +6 -0
  106. package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +17 -2
  107. package/project/.sdk/tm/kotlin/utility/Register.kt +2 -0
  108. package/project/.sdk/tm/lean/src/SdkUtility.lean +134 -1
  109. package/project/.sdk/tm/lua/feature/paging_feature.lua +97 -2
  110. package/project/.sdk/tm/lua/utility/graphql.lua +163 -0
  111. package/project/.sdk/tm/lua/utility/make_options.lua +1 -1
  112. package/project/.sdk/tm/lua/utility/make_response.lua +6 -0
  113. package/project/.sdk/tm/lua/utility/make_spec.lua +18 -2
  114. package/project/.sdk/tm/lua/utility/register.lua +3 -0
  115. package/project/.sdk/tm/ocaml/sdk_features.ml +164 -20
  116. package/project/.sdk/tm/ocaml/sdk_runtime.ml +143 -4
  117. package/project/.sdk/tm/ocaml/sdk_types.ml +2 -0
  118. package/project/.sdk/tm/perl/feature/paging_feature.pm +91 -3
  119. package/project/.sdk/tm/perl/utility/graphql.pm +150 -0
  120. package/project/.sdk/tm/perl/utility/make_options.pm +1 -1
  121. package/project/.sdk/tm/perl/utility/make_response.pm +6 -0
  122. package/project/.sdk/tm/perl/utility/make_spec.pm +20 -2
  123. package/project/.sdk/tm/perl/utility/register.pm +1 -0
  124. package/project/.sdk/tm/php/core/UtilityType.php +2 -0
  125. package/project/.sdk/tm/php/feature/PagingFeature.php +106 -4
  126. package/project/.sdk/tm/php/utility/Graphql.php +176 -0
  127. package/project/.sdk/tm/php/utility/MakeOptions.php +1 -1
  128. package/project/.sdk/tm/php/utility/MakeResponse.php +7 -0
  129. package/project/.sdk/tm/php/utility/MakeSpec.php +20 -2
  130. package/project/.sdk/tm/php/utility/Register.php +3 -0
  131. package/project/.sdk/tm/py/pkg/core/__init__.py +0 -0
  132. package/project/.sdk/tm/py/{core → pkg/core}/context.py +8 -8
  133. package/project/.sdk/tm/py/{core → pkg/core}/operation.py +1 -1
  134. package/project/.sdk/tm/py/{core → pkg/core}/response.py +1 -1
  135. package/project/.sdk/tm/py/{core → pkg/core}/result.py +1 -1
  136. package/project/.sdk/tm/py/{core → pkg/core}/utility_type.py +2 -0
  137. package/project/.sdk/tm/py/pkg/entity/__init__.py +0 -0
  138. package/project/.sdk/tm/py/pkg/feature/__init__.py +0 -0
  139. package/project/.sdk/tm/py/{feature → pkg/feature}/audit_feature.py +1 -1
  140. package/project/.sdk/tm/py/{feature → pkg/feature}/cache_feature.py +1 -1
  141. package/project/.sdk/tm/py/{feature → pkg/feature}/clienttrack_feature.py +1 -1
  142. package/project/.sdk/tm/py/{feature → pkg/feature}/debug_feature.py +1 -1
  143. package/project/.sdk/tm/py/{feature → pkg/feature}/idempotency_feature.py +1 -1
  144. package/project/.sdk/tm/py/{feature → pkg/feature}/log_feature.py +1 -1
  145. package/project/.sdk/tm/py/{feature → pkg/feature}/metrics_feature.py +1 -1
  146. package/project/.sdk/tm/py/{feature → pkg/feature}/netsim_feature.py +1 -1
  147. package/project/.sdk/tm/py/{feature → pkg/feature}/paging_feature.py +87 -6
  148. package/project/.sdk/tm/py/{feature → pkg/feature}/proxy_feature.py +1 -1
  149. package/project/.sdk/tm/py/{feature → pkg/feature}/ratelimit_feature.py +1 -1
  150. package/project/.sdk/tm/py/{feature → pkg/feature}/rbac_feature.py +1 -1
  151. package/project/.sdk/tm/py/{feature → pkg/feature}/retry_feature.py +1 -1
  152. package/project/.sdk/tm/py/{feature → pkg/feature}/streaming_feature.py +1 -1
  153. package/project/.sdk/tm/py/{feature → pkg/feature}/telemetry_feature.py +1 -1
  154. package/project/.sdk/tm/py/{feature → pkg/feature}/test_feature.py +2 -2
  155. package/project/.sdk/tm/py/{feature → pkg/feature}/timeout_feature.py +1 -1
  156. package/project/.sdk/tm/py/pkg/utility/__init__.py +0 -0
  157. package/project/.sdk/tm/py/{utility → pkg/utility}/feature_init.py +1 -1
  158. package/project/.sdk/tm/py/{utility → pkg/utility}/fetcher.py +1 -1
  159. package/project/.sdk/tm/py/pkg/utility/graphql.py +145 -0
  160. package/project/.sdk/tm/py/{utility → pkg/utility}/make_context.py +1 -1
  161. package/project/.sdk/tm/py/{utility → pkg/utility}/make_error.py +5 -5
  162. package/project/.sdk/tm/py/{utility → pkg/utility}/make_fetch_def.py +2 -2
  163. package/project/.sdk/tm/py/{utility → pkg/utility}/make_options.py +2 -2
  164. package/project/.sdk/tm/py/{utility → pkg/utility}/make_point.py +2 -2
  165. package/project/.sdk/tm/py/{utility → pkg/utility}/make_request.py +2 -2
  166. package/project/.sdk/tm/py/{utility → pkg/utility}/make_response.py +6 -0
  167. package/project/.sdk/tm/py/{utility → pkg/utility}/make_spec.py +19 -4
  168. package/project/.sdk/tm/py/{utility → pkg/utility}/make_url.py +1 -1
  169. package/project/.sdk/tm/py/{utility → pkg/utility}/param.py +2 -2
  170. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_auth.py +1 -1
  171. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_headers.py +1 -1
  172. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_method.py +1 -1
  173. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_params.py +1 -1
  174. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_path.py +1 -1
  175. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_query.py +1 -1
  176. package/project/.sdk/tm/py/pkg/utility/register.py +71 -0
  177. package/project/.sdk/tm/py/{utility → pkg/utility}/transform_request.py +2 -2
  178. package/project/.sdk/tm/py/{utility → pkg/utility}/transform_response.py +2 -2
  179. package/project/.sdk/tm/py/test/feature_harness.py +6 -6
  180. package/project/.sdk/tm/py/test/runner.py +1 -1
  181. package/project/.sdk/tm/py/test/struct_runner.py +1 -1
  182. package/project/.sdk/tm/py/test/test_pipeline.py +5 -5
  183. package/project/.sdk/tm/py/test/test_primary_utility.py +8 -8
  184. package/project/.sdk/tm/py/test/test_struct_utility.py +3 -3
  185. package/project/.sdk/tm/py-data/LICENSE +22 -0
  186. package/project/.sdk/tm/py-data/Makefile +57 -0
  187. package/project/.sdk/tm/py-data/pkg/auth.py +131 -0
  188. package/project/.sdk/tm/py-data/pkg/fetch.py +178 -0
  189. package/project/.sdk/tm/py-data/pkg/frames.py +236 -0
  190. package/project/.sdk/tm/py-data/src/feature/README.md +8 -0
  191. package/project/.sdk/tm/py-data/src/feature/debug/.gitkeep +0 -0
  192. package/project/.sdk/tm/py-data/src/feature/idempotency/.gitkeep +0 -0
  193. package/project/.sdk/tm/py-data/src/feature/log/.gitkeep +0 -0
  194. package/project/.sdk/tm/py-data/src/feature/metrics/.gitkeep +0 -0
  195. package/project/.sdk/tm/py-data/src/feature/netsim/.gitkeep +0 -0
  196. package/project/.sdk/tm/py-data/src/feature/paging/.gitkeep +0 -0
  197. package/project/.sdk/tm/py-data/src/feature/proxy/.gitkeep +0 -0
  198. package/project/.sdk/tm/py-data/src/feature/ratelimit/.gitkeep +0 -0
  199. package/project/.sdk/tm/py-data/src/feature/rbac/.gitkeep +0 -0
  200. package/project/.sdk/tm/py-data/src/feature/retry/.gitkeep +0 -0
  201. package/project/.sdk/tm/py-data/src/feature/streaming/.gitkeep +0 -0
  202. package/project/.sdk/tm/py-data/src/feature/telemetry/.gitkeep +0 -0
  203. package/project/.sdk/tm/py-data/src/feature/test/.gitkeep +0 -0
  204. package/project/.sdk/tm/py-data/src/feature/timeout/.gitkeep +0 -0
  205. package/project/.sdk/tm/py-data/test/__init__.py +0 -0
  206. package/project/.sdk/tm/py-data/test/test_auth.py +95 -0
  207. package/project/.sdk/tm/py-data/test/test_fetch.py +152 -0
  208. package/project/.sdk/tm/py-data/test/test_frames.py +250 -0
  209. package/project/.sdk/tm/rb/core/utility_type.rb +1 -0
  210. package/project/.sdk/tm/rb/feature/paging_feature.rb +89 -3
  211. package/project/.sdk/tm/rb/utility/graphql.rb +131 -0
  212. package/project/.sdk/tm/rb/utility/make_options.rb +1 -1
  213. package/project/.sdk/tm/rb/utility/make_response.rb +6 -0
  214. package/project/.sdk/tm/rb/utility/make_spec.rb +18 -2
  215. package/project/.sdk/tm/rb/utility/register.rb +3 -0
  216. package/project/.sdk/tm/rust/feature/paging.rs +124 -2
  217. package/project/.sdk/tm/rust/utility/graphql.rs +193 -0
  218. package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
  219. package/project/.sdk/tm/rust/utility/make_response.rs +6 -0
  220. package/project/.sdk/tm/rust/utility/make_spec.rs +32 -4
  221. package/project/.sdk/tm/rust/utility/mod.rs +1 -0
  222. package/project/.sdk/tm/scala/core/SdkClient.scala +90 -0
  223. package/project/.sdk/tm/scala/core/Utility.scala +4 -0
  224. package/project/.sdk/tm/scala/feature/PagingFeature.scala +104 -3
  225. package/project/.sdk/tm/scala/utility/Graphql.scala +160 -0
  226. package/project/.sdk/tm/scala/utility/Make.scala +7 -1
  227. package/project/.sdk/tm/scala/utility/MakeSpec.scala +22 -2
  228. package/project/.sdk/tm/scala/utility/Register.scala +2 -0
  229. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +4 -0
  230. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +86 -1
  231. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Graphql.swift +142 -0
  232. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +24 -2
  233. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +1 -1
  234. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +2 -0
  235. package/project/.sdk/tm/ts/src/Point.ts +7 -0
  236. package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +82 -3
  237. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +14 -2
  238. package/project/.sdk/tm/ts/src/utility/GraphqlUtility.ts +152 -0
  239. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +1 -1
  240. package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +7 -0
  241. package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +19 -2
  242. package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -0
  243. package/project/.sdk/tm/zig/core/utility.zig +198 -3
  244. package/project/.sdk/tm/zig/feature/paging.zig +128 -1
  245. package/src/cmp/AgentGuideContent.ts +5 -0
  246. package/src/cmp/Feature.ts +13 -0
  247. package/src/helpers/canonType.ts +46 -0
  248. package/src/helpers/packageMeta.ts +7 -0
  249. package/src/sdkgen.ts +6 -1
  250. package/project/.sdk/tm/py/utility/register.py +0 -68
  251. /package/project/.sdk/tm/py/{core → pkg/core}/control.py +0 -0
  252. /package/project/.sdk/tm/py/{core → pkg/core}/error.py +0 -0
  253. /package/project/.sdk/tm/py/{core → pkg/core}/helpers.py +0 -0
  254. /package/project/.sdk/tm/py/{core → pkg/core}/spec.py +0 -0
  255. /package/project/.sdk/tm/py/{feature → pkg/feature}/base_feature.py +0 -0
  256. /package/project/.sdk/tm/py/{utility → pkg/utility}/clean.py +0 -0
  257. /package/project/.sdk/tm/py/{utility → pkg/utility}/done.py +0 -0
  258. /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_add.py +0 -0
  259. /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_hook.py +0 -0
  260. /package/project/.sdk/tm/py/{utility → pkg/utility}/make_result.py +0 -0
  261. /package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_body.py +0 -0
  262. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_basic.py +0 -0
  263. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_body.py +0 -0
  264. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_headers.py +0 -0
  265. /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/__init__.py +0 -0
  266. /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/voxgig_struct.py +0 -0
  267. /package/project/.sdk/tm/{py/core/__init__.py → py-data/src/feature/audit/.gitkeep} +0 -0
  268. /package/project/.sdk/tm/{py/entity/__init__.py → py-data/src/feature/base/.gitkeep} +0 -0
  269. /package/project/.sdk/tm/{py/feature/__init__.py → py-data/src/feature/cache/.gitkeep} +0 -0
  270. /package/project/.sdk/tm/{py/utility/__init__.py → py-data/src/feature/clienttrack/.gitkeep} +0 -0
@@ -3,6 +3,8 @@ package feature
3
3
  import (
4
4
  "regexp"
5
5
 
6
+ vs "github.com/voxgig/struct"
7
+
6
8
  "GOMODULE/core"
7
9
  )
8
10
 
@@ -64,6 +66,14 @@ func (f *PagingFeature) PreRequest(ctx *core.Context) {
64
66
  paging = ctx.Ctrl.Paging
65
67
  }
66
68
 
69
+ // GraphQL paginates through operation VARIABLES, not the query string.
70
+ // This hook runs after makeSpec, so spec.Body already holds the
71
+ // { query, variables } envelope, and before makeFetchDef serialises it.
72
+ if kind, _ := vs.GetProp(ctx.Point, "kind").(string); kind == "graphql" {
73
+ f.graphqlPreRequest(ctx, paging)
74
+ return
75
+ }
76
+
67
77
  if cursor, has := paging["cursor"]; has && cursor != nil {
68
78
  spec.Query[cursorParam] = cursor
69
79
  } else if spec.Query[pageParam] == nil {
@@ -79,6 +89,43 @@ func (f *PagingFeature) PreRequest(ctx *core.Context) {
79
89
  }
80
90
  }
81
91
 
92
+ // Relay pagination: the cursor is the `after` variable (or whatever the
93
+ // model named it), and the page size is `first`.
94
+ func (f *PagingFeature) graphqlPreRequest(ctx *core.Context, paging map[string]any) {
95
+ body, ok := ctx.Spec.Body.(map[string]any)
96
+ if !ok {
97
+ return
98
+ }
99
+
100
+ vars, ok := body["variables"].(map[string]any)
101
+ if !ok {
102
+ vars = map[string]any{}
103
+ body["variables"] = vars
104
+ }
105
+
106
+ afterVar := foptStr(f.options, "afterVar", "after")
107
+ firstVar := foptStr(f.options, "firstVar", "first")
108
+
109
+ // Only bind variables the operation actually declares, or the server
110
+ // rejects the document.
111
+ declared := map[string]bool{}
112
+ if varlist, ok := vs.GetPath([]any{"graphql", "vars"}, ctx.Point).([]any); ok {
113
+ for _, v := range varlist {
114
+ if name, ok := vs.GetProp(v, "name").(string); ok {
115
+ declared[name] = true
116
+ }
117
+ }
118
+ }
119
+
120
+ if cursor, has := paging["cursor"]; has && cursor != nil && declared[afterVar] {
121
+ vars[afterVar] = cursor
122
+ }
123
+
124
+ if f.options["limit"] != nil && vars[firstVar] == nil && declared[firstVar] {
125
+ vars[firstVar] = foptInt(f.options, "limit", 0)
126
+ }
127
+ }
128
+
82
129
  func (f *PagingFeature) PreResult(ctx *core.Context) {
83
130
  if !f.Active || !f.isList(ctx) {
84
131
  return
@@ -107,6 +154,39 @@ func (f *PagingFeature) PreResult(ctx *core.Context) {
107
154
  }
108
155
  }
109
156
 
157
+ // Set when the response states hasMore outright, rather than leaving it
158
+ // to be inferred from the presence of a cursor.
159
+ explicitMore := false
160
+
161
+ // Relay connections carry the cursor in pageInfo, at the path the model
162
+ // recorded for this op.
163
+ page := vs.GetPath([]any{"graphql", "page"}, ctx.Point)
164
+ if pm, ok := page.(map[string]any); ok {
165
+ if _, isMap := body.(map[string]any); isMap {
166
+ // `connpath` locates the connection object inside the response
167
+ // envelope (data.<field>); the cursor/more paths are relative
168
+ // to it.
169
+ conn := body
170
+ if cp, ok := pm["connpath"].(string); ok && cp != "" {
171
+ if sub := vs.GetPath(cp, body); sub != nil {
172
+ conn = sub
173
+ }
174
+ }
175
+
176
+ if cp, ok := pm["cursor"].(string); ok && cp != "" {
177
+ if cursor := vs.GetPath(cp, conn); cursor != nil {
178
+ paging["cursor"] = cursor
179
+ }
180
+ }
181
+ if mp, ok := pm["more"].(string); ok && mp != "" {
182
+ if more, ok := vs.GetPath(mp, conn).(bool); ok {
183
+ paging["hasMore"] = more
184
+ explicitMore = true
185
+ }
186
+ }
187
+ }
188
+ }
189
+
110
190
  // Body-level cursors.
111
191
  if bm, ok := body.(map[string]any); ok {
112
192
  if bm["next"] != nil && paging["next"] == nil {
@@ -120,10 +200,16 @@ func (f *PagingFeature) PreResult(ctx *core.Context) {
120
200
  }
121
201
  if hasMore, ok := bm["hasMore"].(bool); ok {
122
202
  paging["hasMore"] = hasMore
203
+ explicitMore = true
123
204
  }
124
205
  }
125
206
 
126
- if paging["hasMore"] != true &&
207
+ // Cursor presence only INFERS another page. When the server stated the
208
+ // answer outright — relay's `hasNextPage: false`, or a body `hasMore` —
209
+ // that wins: a final page normally carries both an end cursor and
210
+ // hasNextPage false, and inferring from the cursor there would send the
211
+ // caller back for a page that does not exist, forever.
212
+ if !explicitMore && paging["hasMore"] != true &&
127
213
  (paging["next"] != nil || paging["cursor"] != nil || paging["nextPage"] != nil) {
128
214
  paging["hasMore"] = true
129
215
  }
@@ -0,0 +1,168 @@
1
+ package utility
2
+
3
+ import (
4
+ "strconv"
5
+ "strings"
6
+
7
+ vs "github.com/voxgig/struct"
8
+
9
+ "GOMODULE/core"
10
+ )
11
+
12
+ // GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
13
+ // produces uses this file unchanged. The API-specific part — which
14
+ // operations exist and what each one's document is — is model data,
15
+ // computed once by apidef and emitted into Config.
16
+ //
17
+ // Two jobs:
18
+ //
19
+ // GraphqlBody — build { query, variables } for a point, binding the
20
+ // op's arguments to the document's declared variables.
21
+ //
22
+ // GraphqlErrors — lift a GraphQL failure into an SDK error. GraphQL
23
+ // reports failures as a top-level `errors` array under
24
+ // HTTP 200, so the status-driven path in resultBasic
25
+ // never sees them.
26
+
27
+ // Content type every GraphQL-over-HTTP request uses.
28
+ const GraphqlContentType = "application/json"
29
+
30
+ // Map a GraphQL error to the same error codes the HTTP path produces, so a
31
+ // caller handles auth or rate limiting identically on both transports.
32
+ // Servers put the machine-readable code in `extensions.code`; Linear-style
33
+ // APIs use `extensions.type`.
34
+ func graphqlErrorCodeUtil(gqlerr any) string {
35
+ ext, _ := vs.GetProp(gqlerr, "extensions").(map[string]any)
36
+
37
+ raw, _ := vs.GetProp(ext, "code").(string)
38
+ if raw == "" {
39
+ raw, _ = vs.GetProp(ext, "type").(string)
40
+ }
41
+ raw = strings.ToUpper(raw)
42
+
43
+ if strings.Contains(raw, "AUTH") || strings.Contains(raw, "FORBIDDEN") ||
44
+ strings.Contains(raw, "UNAUTHENTICATED") {
45
+ return "request_auth"
46
+ }
47
+ if strings.Contains(raw, "RATELIMIT") || strings.Contains(raw, "RATE_LIMIT") ||
48
+ strings.Contains(raw, "TOO_MANY") {
49
+ return "request_ratelimit"
50
+ }
51
+ if strings.Contains(raw, "BAD_USER_INPUT") || strings.Contains(raw, "VALIDATION") ||
52
+ strings.Contains(raw, "INVALID") {
53
+ return "request_invalid"
54
+ }
55
+
56
+ return "request_graphql"
57
+ }
58
+
59
+ // Build the request body for a GraphQL point.
60
+ //
61
+ // Variables come from the op's own arguments: a named variable binds to the
62
+ // like-named argument (`from`), and the input-object variable (empty `from`)
63
+ // takes the request data as a whole — which is what makes a generated
64
+ // create/update call look exactly like its REST equivalent.
65
+ func graphqlBodyUtil(ctx *core.Context) any {
66
+ gql, _ := vs.GetProp(ctx.Point, "graphql").(map[string]any)
67
+ if gql == nil {
68
+ return nil
69
+ }
70
+
71
+ // reqmatch/reqdata hold the caller's arguments for THIS call; data/match
72
+ // hold the entity's current state. Which pair depends on whether the op
73
+ // takes match or data input. A named variable falls back to the current
74
+ // state, so updating a loaded entity with just {title} still binds the
75
+ // stored id the mutation requires.
76
+ reqsrc, datasrc := ctx.Reqmatch, ctx.Match
77
+ if ctx.Op != nil && ctx.Op.Input == "data" {
78
+ reqsrc, datasrc = ctx.Reqdata, ctx.Data
79
+ }
80
+ if reqsrc == nil {
81
+ reqsrc = map[string]any{}
82
+ }
83
+ if datasrc == nil {
84
+ datasrc = map[string]any{}
85
+ }
86
+
87
+ variables := map[string]any{}
88
+
89
+ varlist, _ := vs.GetProp(gql, "vars").([]any)
90
+ for _, v := range varlist {
91
+ spec, _ := v.(map[string]any)
92
+ if spec == nil {
93
+ continue
94
+ }
95
+
96
+ name, _ := vs.GetProp(spec, "name").(string)
97
+ from, _ := vs.GetProp(spec, "from").(string)
98
+
99
+ if from == "" {
100
+ // The input object IS the request body. Strip the action
101
+ // selector, which is an SDK-side point discriminator, not an
102
+ // API field.
103
+ body := map[string]any{}
104
+ for k, val := range reqsrc {
105
+ if k != "$action" {
106
+ body[k] = val
107
+ }
108
+ }
109
+ variables[name] = body
110
+ continue
111
+ }
112
+
113
+ // Only send variables the caller actually supplied: sending an
114
+ // explicit null would clear a field on many APIs.
115
+ val := vs.GetProp(reqsrc, from)
116
+ if val == nil {
117
+ val = vs.GetProp(datasrc, from)
118
+ }
119
+ if val != nil {
120
+ variables[name] = val
121
+ }
122
+ }
123
+
124
+ doc, _ := vs.GetProp(gql, "doc").(string)
125
+
126
+ return map[string]any{
127
+ "query": doc,
128
+ "variables": variables,
129
+ }
130
+ }
131
+
132
+ // Inspect a decoded GraphQL response body and record a failure when the
133
+ // server reported one. Returns true when an error was recorded.
134
+ //
135
+ // Partial data (`data` alongside `errors`) is treated as failure: the REST
136
+ // surface has no partial-success concept, and silently returning half an
137
+ // object would be worse than failing. The raw envelope stays available on
138
+ // the result for callers that need it.
139
+ func graphqlErrorsUtil(ctx *core.Context) bool {
140
+ if ctx.Result == nil || ctx.Point == nil {
141
+ return false
142
+ }
143
+
144
+ kind, _ := vs.GetProp(ctx.Point, "kind").(string)
145
+ if kind != "graphql" {
146
+ return false
147
+ }
148
+
149
+ errlist, _ := vs.GetProp(ctx.Result.Body, "errors").([]any)
150
+ if len(errlist) == 0 {
151
+ return false
152
+ }
153
+
154
+ first := errlist[0]
155
+ msg, _ := vs.GetProp(first, "message").(string)
156
+ if msg == "" {
157
+ msg = "graphql error"
158
+ }
159
+ if len(errlist) > 1 {
160
+ msg = msg + " (+" + strconv.Itoa(len(errlist)-1) + " more)"
161
+ }
162
+
163
+ ctx.Result.Err = ctx.MakeError(graphqlErrorCodeUtil(first), "graphql: "+msg)
164
+ ctx.Result.Ok = false
165
+
166
+ return true
167
+ }
168
+
@@ -86,7 +86,7 @@ func makeOptionsUtil(ctx *core.Context) map[string]any {
86
86
  },
87
87
  "allow": map[string]any{
88
88
  "method": "GET,PUT,POST,PATCH,DELETE,OPTIONS",
89
- "op": "create,update,load,list,remove,command,direct",
89
+ "op": "create,update,load,list,remove,command,direct,graphql",
90
90
  },
91
91
  "entity": map[string]any{
92
92
  "`$CHILD`": map[string]any{
@@ -32,6 +32,12 @@ func makeResponseUtil(ctx *core.Context) (*core.Response, error) {
32
32
  utility.ResultBasic(ctx)
33
33
  utility.ResultHeaders(ctx)
34
34
  utility.ResultBody(ctx)
35
+
36
+ // GraphQL reports failures as a top-level `errors` array under HTTP
37
+ // 200, so ResultBasic's status check never sees them. Lift them here,
38
+ // before the response transform tries to unwrap data that is not there.
39
+ utility.GraphqlErrors(ctx)
40
+
35
41
  utility.TransformResponse(ctx)
36
42
 
37
43
  if result.Err == nil {
@@ -51,8 +51,25 @@ func makeSpecUtil(ctx *core.Context) (*core.Spec, error) {
51
51
  ctx.Spec.Params = utility.PrepareParams(ctx)
52
52
  ctx.Spec.Query = utility.PrepareQuery(ctx)
53
53
  ctx.Spec.Headers = utility.PrepareHeaders(ctx)
54
- ctx.Spec.Body = utility.PrepareBody(ctx)
55
- ctx.Spec.Path = utility.PreparePath(ctx)
54
+
55
+ kind, _ := vs.GetProp(point, "kind").(string)
56
+
57
+ if kind == "graphql" {
58
+ // GraphQL addresses one endpoint: no path parts, no query string,
59
+ // and the body carries the operation. PrepareBody is skipped
60
+ // deliberately — it only emits a body for data-input ops, whereas
61
+ // every GraphQL op posts one, including load/list/remove.
62
+ ctx.Spec.Body = utility.GraphqlBody(ctx)
63
+ ctx.Spec.Path = ""
64
+ // PrepareQuery already copied the op's match arguments into the
65
+ // query string. Those same values are bound as operation
66
+ // variables, so leaving them would send /graphql?id=i1.
67
+ ctx.Spec.Query = map[string]any{}
68
+ ctx.Spec.Headers["content-type"] = GraphqlContentType
69
+ } else {
70
+ ctx.Spec.Body = utility.PrepareBody(ctx)
71
+ ctx.Spec.Path = utility.PreparePath(ctx)
72
+ }
56
73
 
57
74
  if ctx.Ctrl.Explain != nil {
58
75
  ctx.Ctrl.Explain["spec"] = ctx.Spec
@@ -31,6 +31,8 @@ func registerAll(u *core.Utility) {
31
31
  u.PrepareParams = prepareParamsUtil
32
32
  u.PreparePath = preparePathUtil
33
33
  u.PrepareQuery = prepareQueryUtil
34
+ u.GraphqlBody = graphqlBodyUtil
35
+ u.GraphqlErrors = graphqlErrorsUtil
34
36
  u.ResultBasic = resultBasicUtil
35
37
  u.ResultBody = resultBodyUtil
36
38
  u.ResultHeaders = resultHeadersUtil
@@ -672,8 +672,14 @@ pagingFeature = do
672
672
  opts <- readIORef options
673
673
  pageParam <- optStr opts "pageParam" "page"; limitParam <- optStr opts "limitParam" "limit"; cursorParam <- optStr opts "cursorParam" "cursor"
674
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" ""
675
681
  cur <- case paging of VMap _ -> getp paging "cursor"; _ -> pure VNoval
676
- case cur of
682
+ if kind == "graphql" then graphqlPreRequest opts specV point paging else case cur of
677
683
  VNoval -> do
678
684
  pv <- getp q pageParam
679
685
  case pv of
@@ -688,8 +694,9 @@ pagingFeature = do
688
694
  VNoval -> do sp <- getp opts "startPage"; setp q pageParam (case sp of VNum n -> VNum n; _ -> VNum 1)
689
695
  _ -> pure ()
690
696
  c -> setp q cursorParam c
691
- lim <- getp opts "limit"
692
- case lim of VNoval -> pure (); VNull -> pure (); _ -> do lv <- getp q limitParam; case lv of VNoval -> setp q limitParam lim; _ -> pure ()
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 ()
693
700
  _ -> pure ()
694
701
  "PreResult" -> do
695
702
  rv <- readIORef (cResult ctx)
@@ -704,16 +711,49 @@ pagingFeature = do
704
711
  paging <- jo [("page", xpage), ("totalCount", xtot), ("nextPage", xnext), ("next", VNoval), ("cursor", VNoval), ("hasMore", VBool False)]
705
712
  lnk <- headerCI headersM "link"
706
713
  case lnk of VNoval -> pure (); VNull -> pure (); _ -> case extractNext (vstring lnk) of Just nx -> setp paging "next" (VStr nx); Nothing -> pure ()
707
- case body of
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
708
741
  VMap _ -> do
709
742
  bn <- getp body "next"; case bn of VNoval -> pure (); VNull -> pure (); _ -> do cn <- getp paging "next"; when (isNullish cn) (setp paging "next" bn)
710
743
  bc <- getp body "cursor"; case bc of VNoval -> pure (); VNull -> pure (); _ -> setp paging "cursor" bc
711
744
  bnc <- getp body "nextCursor"; case bnc of VNoval -> pure (); VNull -> pure (); _ -> setp paging "cursor" bnc
712
- bhm <- getp body "hasMore"; case bhm of VBool bb -> setp paging "hasMore" (VBool bb); _ -> pure ()
713
- _ -> pure ()
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
714
754
  hmV <- getp paging "hasMore"; nx <- getp paging "next"; cu2 <- getp paging "cursor"; np <- getp paging "nextPage"
715
755
  let hm = isTrueV hmV || not (isNullish nx) || not (isNullish cu2) || not (isNullish np)
716
- setp paging "hasMore" (VBool hm)
756
+ when (not explicitMore) (setp paging "hasMore" (VBool hm))
717
757
  setp rv "paging" paging
718
758
  cl <- cc ctx; lastM <- jo [("last", paging)]; trackSet cl "paging" lastM
719
759
  _ -> pure ()
@@ -721,6 +761,37 @@ pagingFeature = do
721
761
  initFn _ opts = do om <- toOptsMap opts; writeIORef options om; a <- optActive opts; writeIORef active a
722
762
  pure Feature { fName = "paging", fVersion = "0.0.1", fActive = active, fOptions = fopts, fInit = initFn, fHook = hookFn }
723
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
+
724
795
  elemIndex :: Eq a => a -> [a] -> Maybe Int
725
796
  elemIndex x = go 0 where go _ [] = Nothing; go i (y : ys) = if x == y then Just i else go (i + 1) ys
726
797
 
@@ -1074,8 +1145,76 @@ prepare client fetchargs = do
1074
1145
  (fd, merr2) <- makeFetchDefUtil ctx
1075
1146
  case merr2 of Just e -> throwIO (SdkException e); Nothing -> pure fd
1076
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.
1077
1167
  direct :: Client -> Value -> IO Value
1078
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
1079
1218
  let u = clUtility client
1080
1219
  fa <- case fetchargs of VNoval -> emptyMap; _ -> pure fetchargs
1081
1220
  res <- try (prepare client fa) :: IO (Either SdkException Value)