@voxgig/sdkgen 1.5.1 → 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 +3 -3
  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
@@ -114,8 +114,24 @@ func makeSpecUtil(_ ctx: Context) throws -> Spec {
114
114
  ctx.spec!.params = utility.prepareParams(ctx)
115
115
  ctx.spec!.query = utility.prepareQuery(ctx)
116
116
  ctx.spec!.headers = utility.prepareHeaders(ctx)
117
- ctx.spec!.body = utility.prepareBody(ctx)
118
- ctx.spec!.path = utility.preparePath(ctx)
117
+
118
+ if "graphql" == gp(ctx.point, "kind").asString {
119
+ // GraphQL addresses one endpoint: no path parts, no query string, and
120
+ // the body carries the operation. prepareBody is skipped deliberately —
121
+ // it only emits a body for data-input ops, whereas every GraphQL op
122
+ // posts one, including load/list/remove.
123
+ ctx.spec!.body = utility.graphqlBody(ctx)
124
+ ctx.spec!.path = ""
125
+ // prepareQuery already copied the op's match arguments into the query
126
+ // string. Those same values are bound as operation variables, so
127
+ // leaving them would send /graphql?id=i1.
128
+ ctx.spec!.query = VMap()
129
+ ctx.spec!.headers.entries["content-type"] = .string(graphqlContentType)
130
+ }
131
+ else {
132
+ ctx.spec!.body = utility.prepareBody(ctx)
133
+ ctx.spec!.path = utility.preparePath(ctx)
134
+ }
119
135
 
120
136
  if let explain = ctx.ctrl.explain {
121
137
  explain.entries["spec"] = .nat(ctx.spec!)
@@ -266,6 +282,12 @@ func makeResponseUtil(_ ctx: Context) throws -> Response {
266
282
  _ = utility.resultBasic(ctx)
267
283
  _ = utility.resultHeaders(ctx)
268
284
  _ = utility.resultBody(ctx)
285
+
286
+ // GraphQL reports failures as a top-level `errors` array under HTTP 200,
287
+ // so resultBasic's status check never sees them. Lift them here, before
288
+ // the response transform tries to unwrap data that is not there.
289
+ _ = utility.graphqlErrors(ctx)
290
+
269
291
  _ = utility.transformResponse(ctx)
270
292
 
271
293
  if result.err == nil {
@@ -12,7 +12,7 @@ private func buildOptSpec() -> Value {
12
12
 
13
13
  let allow = VMap()
14
14
  allow.entries["method"] = .string("GET,PUT,POST,PATCH,DELETE,OPTIONS")
15
- allow.entries["op"] = .string("create,update,load,list,remove,command,direct")
15
+ allow.entries["op"] = .string("create,update,load,list,remove,command,direct,graphql")
16
16
 
17
17
  let entityChild = VMap()
18
18
  entityChild.entries["`$OPEN`"] = .bool(true)
@@ -28,6 +28,8 @@ func registerAll(_ u: Utility) {
28
28
  u.prepareParams = prepareParamsUtil
29
29
  u.preparePath = preparePathUtil
30
30
  u.prepareQuery = prepareQueryUtil
31
+ u.graphqlBody = graphqlBodyUtil
32
+ u.graphqlErrors = graphqlErrorsUtil
31
33
  u.resultBasic = resultBasicUtil
32
34
  u.resultBody = resultBodyUtil
33
35
  u.resultHeaders = resultHeadersUtil
@@ -4,6 +4,11 @@ import { getprop } from './utility/StructUtility'
4
4
 
5
5
  class Point {
6
6
  args: { params: any[] }
7
+ // Transport this point speaks: 'http' (default) or 'graphql'. GraphQL
8
+ // points carry their operation document in `graphql` and address the
9
+ // single endpoint, so method is always POST and parts is empty.
10
+ kind: string
11
+ graphql?: any
7
12
  rename: { params: Record<string, string> }
8
13
  method: string
9
14
  orig: string
@@ -17,6 +22,8 @@ class Point {
17
22
 
18
23
  constructor(altmap: Record<string, any>) {
19
24
  this.args = getprop(altmap, 'args', { params: [] })
25
+ this.kind = getprop(altmap, 'kind', 'http')
26
+ this.graphql = getprop(altmap, 'graphql')
20
27
  this.rename = getprop(altmap, 'rename', { params: {} })
21
28
  this.method = getprop(altmap, 'method', '')
22
29
  this.orig = getprop(altmap, 'orig', '')
@@ -48,6 +48,14 @@ class PagingFeature extends BaseFeature {
48
48
  // A per-call cursor/page from ctrl takes priority (used by auto-iteration).
49
49
  const paging = (ctx.ctrl && ctx.ctrl.paging) || {}
50
50
 
51
+ // GraphQL paginates through operation VARIABLES, not the query string.
52
+ // This hook runs after makeSpec, so spec.body already holds the
53
+ // { query, variables } envelope, and before makeFetchDef serialises it.
54
+ const point: any = ctx.point
55
+ if ('graphql' === (null == point ? undefined : point.kind)) {
56
+ return this._graphqlPreRequest(ctx, paging)
57
+ }
58
+
51
59
  if (null != paging.cursor) {
52
60
  spec.query[cursorParam] = paging.cursor
53
61
  }
@@ -73,6 +81,10 @@ class PagingFeature extends BaseFeature {
73
81
  const headers = result.headers || {}
74
82
  const body = result.body
75
83
 
84
+ // Set when the response states hasMore outright, rather than leaving it
85
+ // to be inferred from the presence of a cursor.
86
+ let explicitMore = false
87
+
76
88
  const paging: any = {
77
89
  page: this._num(this._header(headers, 'x-page')),
78
90
  totalCount: this._num(this._header(headers, 'x-total-count')),
@@ -91,16 +103,49 @@ class PagingFeature extends BaseFeature {
91
103
  }
92
104
  }
93
105
 
106
+ // Relay connections carry the cursor in pageInfo, at the path the model
107
+ // recorded for this op.
108
+ const point: any = ctx.point
109
+ const page = null == point ? undefined : (point.graphql || {}).page
110
+
111
+ if (null != page && body && 'object' === typeof body) {
112
+ const struct: any = ctx.utility.struct
113
+
114
+ // `connpath` locates the connection object inside the response
115
+ // envelope (data.<field>); the cursor/more paths are relative to it.
116
+ const conn = null == page.connpath || '' === page.connpath ? body :
117
+ (struct.getpath(body, page.connpath) || body)
118
+
119
+ const cursor = struct.getpath(conn, page.cursor)
120
+ const more = struct.getpath(conn, page.more)
121
+
122
+ if (null != cursor) { paging.cursor = cursor }
123
+ if ('boolean' === typeof more) {
124
+ paging.hasMore = more
125
+ explicitMore = true
126
+ }
127
+ }
128
+
94
129
  // Body-level cursors.
95
130
  if (body && 'object' === typeof body) {
96
131
  if (null != body.next) { paging.next = paging.next || body.next }
97
132
  if (null != body.cursor) { paging.cursor = body.cursor }
98
133
  if (null != body.nextCursor) { paging.cursor = body.nextCursor }
99
- if ('boolean' === typeof body.hasMore) { paging.hasMore = body.hasMore }
134
+ if ('boolean' === typeof body.hasMore) {
135
+ paging.hasMore = body.hasMore
136
+ explicitMore = true
137
+ }
100
138
  }
101
139
 
102
- paging.hasMore = paging.hasMore ||
103
- null != paging.next || null != paging.cursor || null != paging.nextPage
140
+ // Cursor presence only INFERS another page. When the server stated the
141
+ // answer outright relay's `hasNextPage: false`, or a body `hasMore` —
142
+ // that wins: a final page normally carries both an end cursor and
143
+ // hasNextPage false, and inferring from the cursor there would send the
144
+ // caller back for a page that does not exist, forever.
145
+ if (!explicitMore) {
146
+ paging.hasMore = paging.hasMore ||
147
+ null != paging.next || null != paging.cursor || null != paging.nextPage
148
+ }
104
149
 
105
150
  result.paging = paging
106
151
 
@@ -109,6 +154,40 @@ class PagingFeature extends BaseFeature {
109
154
  }
110
155
 
111
156
 
157
+
158
+ // Relay pagination: the cursor is the `after` variable (or whatever the
159
+ // model recorded), and the page size the `first` variable.
160
+ _graphqlPreRequest(this: any, ctx: any, paging: any) {
161
+ const spec = ctx.spec
162
+ const point = ctx.point
163
+
164
+ const body = spec.body
165
+ if (null == body || 'object' !== typeof body) {
166
+ return
167
+ }
168
+
169
+ const vars = (body.variables = body.variables || {})
170
+
171
+ const afterVar = this._options.afterVar || 'after'
172
+ const firstVar = this._options.firstVar || 'first'
173
+
174
+ // Only bind variables the operation actually declares, or the server
175
+ // rejects the document.
176
+ const declared: Record<string, boolean> = {}
177
+ for (const v of ((point.graphql && point.graphql.vars) || [])) {
178
+ declared[v.name] = true
179
+ }
180
+
181
+ if (null != paging.cursor && declared[afterVar]) {
182
+ vars[afterVar] = paging.cursor
183
+ }
184
+
185
+ if (null != this._options.limit && null == vars[firstVar] &&
186
+ declared[firstVar]) {
187
+ vars[firstVar] = this._options.limit
188
+ }
189
+ }
190
+
112
191
  _isList(this: any, ctx: any): boolean {
113
192
  const ops = this._options.ops || ['list']
114
193
  return ops.indexOf(ctx.op && ctx.op.name) >= 0
@@ -64,8 +64,20 @@ class TestFeature extends BaseFeature {
64
64
  if (null == data || 'string' !== typeof restf) {
65
65
  return data
66
66
  }
67
- const m = restf.match(/^`body\.([^`.]+)`$/)
68
- return null == m ? data : { [m[1]]: data }
67
+ // Rebuild whatever nesting the op's response transform unwraps, so
68
+ // the mock agrees with the model. Multi-segment on purpose: GraphQL
69
+ // ops unwrap `body.data.<field>` (and `body.data.<field>.<entity>`
70
+ // for mutation payloads), not just a single envelope property.
71
+ const m = restf.match(/^`body\.(.+)`$/)
72
+ if (null == m) {
73
+ return data
74
+ }
75
+ let out: any = data
76
+ const segs = m[1].split('.')
77
+ for (let i = segs.length - 1; 0 <= i; i--) {
78
+ out = { [segs[i]]: out }
79
+ }
80
+ return out
69
81
  }
70
82
 
71
83
  function respond(status: number, data?: any, res?: any) {
@@ -0,0 +1,152 @@
1
+
2
+ import { Context } from '../types'
3
+
4
+
5
+ // GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
6
+ // produces uses this file unchanged. The API-specific part — which
7
+ // operations exist and what each one's document is — is model data,
8
+ // computed once by apidef and emitted into Config.
9
+ //
10
+ // Two jobs:
11
+ //
12
+ // graphqlBody — build { query, variables } for a point, binding the
13
+ // op's arguments to the document's declared variables.
14
+ //
15
+ // graphqlErrors — lift a GraphQL failure into an SDK error. GraphQL
16
+ // reports failures as a top-level `errors` array under
17
+ // HTTP 200, so the status-driven path in resultBasic
18
+ // never sees them.
19
+
20
+
21
+ // Content type every GraphQL-over-HTTP request uses.
22
+ const GRAPHQL_CONTENT_TYPE = 'application/json'
23
+
24
+
25
+ // Map a GraphQL error to the same error codes the HTTP path produces, so a
26
+ // caller handles auth or rate limiting identically on both transports.
27
+ // Servers put the machine-readable code in `extensions.code`; Linear-style
28
+ // APIs use `extensions.type`.
29
+ function graphqlErrorCode(gqlerr: any): string {
30
+ const ext = (null == gqlerr ? undefined : gqlerr.extensions) || {}
31
+ const raw = String(ext.code || ext.type || '').toUpperCase()
32
+
33
+ if (raw.includes('AUTH') || raw.includes('FORBIDDEN') ||
34
+ raw.includes('UNAUTHENTICATED')) {
35
+ return 'request_auth'
36
+ }
37
+ if (raw.includes('RATELIMIT') || raw.includes('RATE_LIMIT') ||
38
+ raw.includes('TOO_MANY')) {
39
+ return 'request_ratelimit'
40
+ }
41
+ if (raw.includes('BAD_USER_INPUT') || raw.includes('VALIDATION') ||
42
+ raw.includes('INVALID')) {
43
+ return 'request_invalid'
44
+ }
45
+
46
+ return 'request_graphql'
47
+ }
48
+
49
+
50
+ // Build the request body for a GraphQL point.
51
+ //
52
+ // Variables come from the op's own arguments: a named variable binds to the
53
+ // like-named argument (`from`), and the input-object variable (empty `from`)
54
+ // takes the request data as a whole — which is what makes a generated
55
+ // create/update call look exactly like its REST equivalent.
56
+ function graphqlBody(ctx: Context): any {
57
+ const utility = ctx.utility
58
+ const struct = utility.struct
59
+ const getprop = struct.getprop
60
+
61
+ const point: any = ctx.point
62
+ const gql = null == point ? undefined : point.graphql
63
+
64
+ if (null == gql) {
65
+ return undefined
66
+ }
67
+
68
+ const op: any = ctx.op
69
+ const input = null == op ? 'match' : op.input
70
+
71
+ // reqmatch/reqdata hold the caller's arguments for this operation.
72
+ const reqsrc = getprop(ctx, 'req' + input) || {}
73
+ const datasrc = getprop(ctx, input) || {}
74
+
75
+ const variables: Record<string, any> = {}
76
+
77
+ for (const spec of (gql.vars || [])) {
78
+ if ('' === spec.from || null == spec.from) {
79
+ // The input object IS the request body. Strip the action selector,
80
+ // which is an SDK-side point discriminator, not an API field.
81
+ const body: Record<string, any> = {}
82
+ for (const key of Object.keys(reqsrc)) {
83
+ if ('$action' !== key) {
84
+ body[key] = reqsrc[key]
85
+ }
86
+ }
87
+ variables[spec.name] = body
88
+ }
89
+ else {
90
+ const val = undefined !== getprop(reqsrc, spec.from) ?
91
+ getprop(reqsrc, spec.from) : getprop(datasrc, spec.from)
92
+
93
+ // Only send variables the caller actually supplied: sending an
94
+ // explicit null would clear a field on many APIs.
95
+ if (undefined !== val && null !== val) {
96
+ variables[spec.name] = val
97
+ }
98
+ }
99
+ }
100
+
101
+ return {
102
+ query: gql.doc,
103
+ variables,
104
+ }
105
+ }
106
+
107
+
108
+ // Inspect a decoded GraphQL response body and record a failure when the
109
+ // server reported one. Returns true when an error was recorded.
110
+ //
111
+ // Partial data (`data` alongside `errors`) is treated as failure: the REST
112
+ // surface has no partial-success concept, and silently returning half an
113
+ // object would be worse than failing. The raw envelope stays available on
114
+ // the result for callers that need it.
115
+ function graphqlErrors(ctx: Context): boolean {
116
+ const result: any = ctx.result
117
+ const point: any = ctx.point
118
+
119
+ if (null == result || null == point || 'graphql' !== point.kind) {
120
+ return false
121
+ }
122
+
123
+ const body: any = result.body
124
+ const errors = null == body ? undefined : body.errors
125
+
126
+ if (null == errors || !Array.isArray(errors) || 0 === errors.length) {
127
+ return false
128
+ }
129
+
130
+ const first = errors[0]
131
+ const msg = (null == first ? undefined : first.message) || 'graphql error'
132
+ const code = graphqlErrorCode(first)
133
+
134
+ const err: any = ctx.error(code,
135
+ 'graphql: ' + msg + (1 < errors.length ?
136
+ ' (+' + (errors.length - 1) + ' more)' : ''))
137
+
138
+ // Keep the full error list reachable for diagnostics.
139
+ err.graphql = errors
140
+ result.err = err
141
+ result.ok = false
142
+
143
+ return true
144
+ }
145
+
146
+
147
+ export {
148
+ graphqlBody,
149
+ graphqlErrors,
150
+ graphqlErrorCode,
151
+ GRAPHQL_CONTENT_TYPE,
152
+ }
@@ -55,7 +55,7 @@ function makeOptions(ctx: Context) {
55
55
  },
56
56
  allow: {
57
57
  method: 'GET,PUT,POST,PATCH,DELETE,OPTIONS',
58
- op: 'create,update,load,list,remove,command,direct'
58
+ op: 'create,update,load,list,remove,command,direct,graphql'
59
59
  },
60
60
  entity: {
61
61
  '`$CHILD`': {
@@ -13,6 +13,7 @@ async function makeResponse(ctx: Context): Promise<Response | Error> {
13
13
  const resultHeaders = utility.resultHeaders
14
14
  const resultBody = utility.resultBody
15
15
  const transformResponse = utility.transformResponse
16
+ const graphqlErrors = utility.graphqlErrors
16
17
 
17
18
  const spec = ctx.spec
18
19
  const result = ctx.result
@@ -38,6 +39,12 @@ async function makeResponse(ctx: Context): Promise<Response | Error> {
38
39
  resultBasic(ctx)
39
40
  resultHeaders(ctx)
40
41
  await resultBody(ctx)
42
+
43
+ // GraphQL reports failures as a top-level `errors` array under HTTP 200,
44
+ // so resultBasic's status check never sees them. Lift them here, before
45
+ // the response transform tries to unwrap data that is not there.
46
+ graphqlErrors(ctx)
47
+
41
48
  transformResponse(ctx)
42
49
 
43
50
  if (null == result.err) {
@@ -39,8 +39,25 @@ function makeSpec(ctx: Context): Spec | Error {
39
39
  ctx.spec.params = prepareParams(ctx)
40
40
  ctx.spec.query = prepareQuery(ctx)
41
41
  ctx.spec.headers = prepareHeaders(ctx)
42
- ctx.spec.body = prepareBody(ctx)
43
- ctx.spec.path = preparePath(ctx)
42
+
43
+ if ('graphql' === (point as any).kind) {
44
+ // GraphQL addresses one endpoint: no path parts, no query string, and
45
+ // the body carries the operation. prepareBody is skipped deliberately —
46
+ // it only emits a body for data-input ops (create/update), whereas every
47
+ // GraphQL op posts one, including load/list/remove.
48
+ ctx.spec.body = utility.graphqlBody(ctx)
49
+ ctx.spec.path = ''
50
+ // prepareQuery already copied the op's match arguments into the query
51
+ // string. Those same values are bound as operation variables, so leaving
52
+ // them would send /graphql?id=i1 — duplicating the argument, leaking it
53
+ // into the URL, and failing servers that reject unknown query params.
54
+ ctx.spec.query = {}
55
+ ctx.spec.headers['content-type'] = utility.GRAPHQL_CONTENT_TYPE
56
+ }
57
+ else {
58
+ ctx.spec.body = prepareBody(ctx)
59
+ ctx.spec.path = preparePath(ctx)
60
+ }
44
61
 
45
62
  if (ctx.ctrl.explain) {
46
63
  ctx.ctrl.explain.spec = ctx.spec
@@ -15,6 +15,7 @@ import { makeResponse } from './MakeResponseUtility'
15
15
  import { makeResult } from './MakeResultUtility'
16
16
  import { makePoint } from './MakePointUtility'
17
17
  import { makeSpec } from './MakeSpecUtility'
18
+ import { graphqlBody, graphqlErrors, GRAPHQL_CONTENT_TYPE } from './GraphqlUtility'
18
19
  import { makeUrl } from './MakeUrlUtility'
19
20
  import { param } from './ParamUtility'
20
21
  import { prepareAuth } from './PrepareAuthUtility'
@@ -50,6 +51,9 @@ class Utility {
50
51
  makeResult = makeResult
51
52
  makePoint = makePoint
52
53
  makeSpec = makeSpec
54
+ graphqlBody = graphqlBody
55
+ graphqlErrors = graphqlErrors
56
+ GRAPHQL_CONTENT_TYPE = GRAPHQL_CONTENT_TYPE
53
57
  makeUrl = makeUrl
54
58
  param = param
55
59
  prepareAuth = prepareAuth
@@ -350,7 +350,7 @@ pub fn make_options_util(ctx: *Context) Value {
350
350
  .{ "headers", h.jo(&.{.{ "`$CHILD`", h.vstr("`$STRING`") }}) },
351
351
  .{ "allow", h.jo(&.{
352
352
  .{ "method", h.vstr("GET,PUT,POST,PATCH,DELETE,OPTIONS") },
353
- .{ "op", h.vstr("create,update,load,list,remove,command,direct") },
353
+ .{ "op", h.vstr("create,update,load,list,remove,command,direct,graphql") },
354
354
  }) },
355
355
  .{ "entity", h.jo(&.{.{ "`$CHILD`", h.jo(&.{
356
356
  .{ "`$OPEN`", h.vbool(true) },
@@ -565,8 +565,28 @@ pub fn make_spec_util(ctx: *Context) E!*Spec {
565
565
  spec.params = prepare_params_util(ctx);
566
566
  spec.query = prepare_query_util(ctx);
567
567
  spec.headers = prepare_headers_util(ctx);
568
- spec.body = prepare_body_util(ctx);
569
- spec.path = prepare_path_util(ctx);
568
+
569
+ const kind: []const u8 = switch (h.getp(point, "kind")) {
570
+ .string => |s| s,
571
+ else => "",
572
+ };
573
+
574
+ if (std.mem.eql(u8, kind, "graphql")) {
575
+ // GraphQL addresses one endpoint: no path parts, no query string,
576
+ // and the body carries the operation. prepare_body is skipped
577
+ // deliberately — it only emits a body for data-input ops, whereas
578
+ // every GraphQL op posts one, including load/list/remove.
579
+ spec.body = graphql_body_util(ctx);
580
+ spec.path = "";
581
+ // prepare_query already copied the op's match arguments into the
582
+ // query string. Those same values are bound as operation variables,
583
+ // so leaving them would send /graphql?id=i1.
584
+ spec.query = h.omap();
585
+ h.setp(spec.headers, "content-type", h.vstr(GRAPHQL_CONTENT_TYPE));
586
+ } else {
587
+ spec.body = prepare_body_util(ctx);
588
+ spec.path = prepare_path_util(ctx);
589
+ }
570
590
 
571
591
  const c = ctx.ctrl;
572
592
  if (c.has_explain()) h.setp(c.explain, "spec", spec.to_value());
@@ -721,6 +741,12 @@ pub fn make_response_util(ctx: *Context) E!*Response {
721
741
  _ = result_basic_util(ctx);
722
742
  _ = result_headers_util(ctx);
723
743
  _ = result_body_util(ctx);
744
+
745
+ // GraphQL reports failures as a top-level `errors` array under HTTP 200,
746
+ // so result_basic's status check never sees them. Lift them here, before
747
+ // the response transform tries to unwrap data that is not there.
748
+ _ = graphql_errors_util(ctx);
749
+
724
750
  _ = transform_response_util(ctx);
725
751
 
726
752
  if (result.err == null) result.ok = true;
@@ -936,6 +962,175 @@ pub fn prepare_query_util(ctx: *Context) Value {
936
962
  return out;
937
963
  }
938
964
 
965
+ // ============================================================================
966
+ // graphql (transport)
967
+ //
968
+ // GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
969
+ // produces uses this code unchanged. The API-specific part — which
970
+ // operations exist and what each one's document is — is model data,
971
+ // computed once by apidef and emitted into Config.
972
+ //
973
+ // Two jobs:
974
+ //
975
+ // graphql_body — build { query, variables } for a point, binding the
976
+ // op's arguments to the document's declared variables.
977
+ //
978
+ // graphql_errors — lift a GraphQL failure into an SDK error. GraphQL
979
+ // reports failures as a top-level `errors` array under
980
+ // HTTP 200, so the status-driven path in result_basic
981
+ // never sees them.
982
+ // ============================================================================
983
+
984
+ // Content type every GraphQL-over-HTTP request uses.
985
+ pub const GRAPHQL_CONTENT_TYPE = "application/json";
986
+
987
+ // Map a GraphQL error to the same error codes the HTTP path produces, so a
988
+ // caller handles auth or rate limiting identically on both transports.
989
+ // Servers put the machine-readable code in `extensions.code`; Linear-style
990
+ // APIs use `extensions.type`.
991
+ pub fn graphql_error_code(gqlerr: Value) []const u8 {
992
+ const ext = h.getp(gqlerr, "extensions");
993
+
994
+ var code: []const u8 = switch (h.getp(ext, "code")) {
995
+ .string => |s| s,
996
+ else => "",
997
+ };
998
+ if (code.len == 0) {
999
+ code = switch (h.getp(ext, "type")) {
1000
+ .string => |s| s,
1001
+ else => "",
1002
+ };
1003
+ }
1004
+
1005
+ const raw: []const u8 = std.ascii.allocUpperString(h.A(), code) catch "";
1006
+
1007
+ if (std.mem.indexOf(u8, raw, "AUTH") != null or
1008
+ std.mem.indexOf(u8, raw, "FORBIDDEN") != null or
1009
+ std.mem.indexOf(u8, raw, "UNAUTHENTICATED") != null)
1010
+ {
1011
+ return "request_auth";
1012
+ }
1013
+ if (std.mem.indexOf(u8, raw, "RATELIMIT") != null or
1014
+ std.mem.indexOf(u8, raw, "RATE_LIMIT") != null or
1015
+ std.mem.indexOf(u8, raw, "TOO_MANY") != null)
1016
+ {
1017
+ return "request_ratelimit";
1018
+ }
1019
+ if (std.mem.indexOf(u8, raw, "BAD_USER_INPUT") != null or
1020
+ std.mem.indexOf(u8, raw, "VALIDATION") != null or
1021
+ std.mem.indexOf(u8, raw, "INVALID") != null)
1022
+ {
1023
+ return "request_invalid";
1024
+ }
1025
+
1026
+ return "request_graphql";
1027
+ }
1028
+
1029
+ // Build the request body for a GraphQL point.
1030
+ //
1031
+ // Variables come from the op's own arguments: a named variable binds to the
1032
+ // like-named argument (`from`), and the input-object variable (empty `from`)
1033
+ // takes the request data as a whole — which is what makes a generated
1034
+ // create/update call look exactly like its REST equivalent.
1035
+ pub fn graphql_body_util(ctx: *Context) Value {
1036
+ const gql = h.getp(ctx.point, "graphql");
1037
+ if (gql != .object) return h.vnull();
1038
+
1039
+ // reqmatch/reqdata hold the caller's arguments for this operation; which
1040
+ // one depends on whether the op takes match or data input.
1041
+ var reqsrc = ctx.reqmatch;
1042
+ var datasrc = ctx.mtch;
1043
+ if (std.mem.eql(u8, ctx.op.input, "data")) {
1044
+ reqsrc = ctx.reqdata;
1045
+ datasrc = ctx.data;
1046
+ }
1047
+ if (reqsrc != .object) reqsrc = h.omap();
1048
+ if (datasrc != .object) datasrc = h.omap();
1049
+
1050
+ const variables = h.omap();
1051
+
1052
+ const varlist = h.getp(gql, "vars");
1053
+ if (varlist == .array) {
1054
+ for (varlist.array.data.items) |spec| {
1055
+ if (spec != .object) continue;
1056
+
1057
+ const name: []const u8 = switch (h.getp(spec, "name")) {
1058
+ .string => |s| s,
1059
+ else => "",
1060
+ };
1061
+ if (name.len == 0) continue;
1062
+
1063
+ const from: []const u8 = switch (h.getp(spec, "from")) {
1064
+ .string => |s| s,
1065
+ else => "",
1066
+ };
1067
+
1068
+ if (from.len == 0) {
1069
+ // The input object IS the request body. Strip the action
1070
+ // selector, which is an SDK-side point discriminator, not
1071
+ // an API field.
1072
+ const body = h.omap();
1073
+ var it = reqsrc.object.iterator();
1074
+ while (it.next()) |kv| {
1075
+ const key = kv.key_ptr.*;
1076
+ if (!std.mem.eql(u8, key, "$action")) {
1077
+ h.setp(body, key, kv.value_ptr.*);
1078
+ }
1079
+ }
1080
+ h.setp(variables, name, body);
1081
+ continue;
1082
+ }
1083
+
1084
+ // Only send variables the caller actually supplied: sending an
1085
+ // explicit null would clear a field on many APIs.
1086
+ var val = h.getp(reqsrc, from);
1087
+ if (h.is_noval(val) or h.is_null(val)) val = h.getp(datasrc, from);
1088
+ if (!h.is_noval(val) and !h.is_null(val)) {
1089
+ h.setp(variables, name, val);
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ const out = h.omap();
1095
+ h.setp(out, "query", h.getp(gql, "doc"));
1096
+ h.setp(out, "variables", variables);
1097
+ return out;
1098
+ }
1099
+
1100
+ // Inspect a decoded GraphQL response body and record a failure when the
1101
+ // server reported one. Returns true when an error was recorded.
1102
+ //
1103
+ // Partial data (`data` alongside `errors`) is treated as failure: the REST
1104
+ // surface has no partial-success concept, and silently returning half an
1105
+ // object would be worse than failing.
1106
+ pub fn graphql_errors_util(ctx: *Context) bool {
1107
+ const result = ctx.result orelse return false;
1108
+
1109
+ const kind: []const u8 = switch (h.getp(ctx.point, "kind")) {
1110
+ .string => |s| s,
1111
+ else => "",
1112
+ };
1113
+ if (!std.mem.eql(u8, kind, "graphql")) return false;
1114
+
1115
+ const errors = h.getp(result.body, "errors");
1116
+ if (errors != .array or errors.array.data.items.len == 0) return false;
1117
+
1118
+ const count = errors.array.data.items.len;
1119
+ const first = errors.array.data.items[0];
1120
+
1121
+ var msg: []const u8 = switch (h.getp(first, "message")) {
1122
+ .string => |s| s,
1123
+ else => "",
1124
+ };
1125
+ if (msg.len == 0) msg = "graphql error";
1126
+ if (1 < count) msg = fmt("{s} (+{d} more)", .{ msg, count - 1 });
1127
+
1128
+ result.err = ctx.make_error(graphql_error_code(first), fmt("graphql: {s}", .{msg}));
1129
+ result.ok = false;
1130
+
1131
+ return true;
1132
+ }
1133
+
939
1134
  const HEADER_AUTH = "authorization";
940
1135
  const OPTION_APIKEY = "apikey";
941
1136
  const NOT_FOUND = "__NOTFOUND__";