@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
@@ -415,6 +415,136 @@ prepareBodyUtil ctx = do
415
415
  op <- readIORef (cOp ctx)
416
416
  if opInput op == "data" then transformRequestUtil ctx else pure VNoval
417
417
 
418
+ -- ------------------------------------------------------------------
419
+ -- graphql (transport)
420
+ --
421
+ -- GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
422
+ -- produces uses this code unchanged. The API-specific part — which
423
+ -- operations exist and what each one's document is — is model data, computed
424
+ -- once by apidef and emitted into Config.
425
+ --
426
+ -- Two jobs:
427
+ --
428
+ -- graphqlBodyUtil — build { query, variables } for a point, binding the
429
+ -- op's arguments to the document's declared variables.
430
+ --
431
+ -- graphqlErrorsUtil — lift a GraphQL failure into an SDK error. GraphQL
432
+ -- reports failures as a top-level `errors` array under
433
+ -- HTTP 200, so the status-driven path in resultBasic
434
+ -- never sees them.
435
+ -- ------------------------------------------------------------------
436
+
437
+ -- Content type every GraphQL-over-HTTP request uses.
438
+ graphqlContentType :: String
439
+ graphqlContentType = "application/json"
440
+
441
+ -- Map a GraphQL error to the same error codes the HTTP path produces, so a
442
+ -- caller handles auth or rate limiting identically on both transports.
443
+ -- Servers put the machine-readable code in `extensions.code`; Linear-style
444
+ -- APIs use `extensions.type`.
445
+ graphqlErrorCode :: Value -> IO String
446
+ graphqlErrorCode gqlerr = do
447
+ ext <- getp gqlerr "extensions"
448
+ c0 <- getStrD ext "code" ""
449
+ code <- if null c0 then getStrD ext "type" "" else pure c0
450
+ let raw = map toUpper code
451
+ pure $
452
+ if substrContains raw "AUTH" || substrContains raw "FORBIDDEN"
453
+ || substrContains raw "UNAUTHENTICATED"
454
+ then "request_auth"
455
+ else if substrContains raw "RATELIMIT" || substrContains raw "RATE_LIMIT"
456
+ || substrContains raw "TOO_MANY"
457
+ then "request_ratelimit"
458
+ else if substrContains raw "BAD_USER_INPUT"
459
+ || substrContains raw "VALIDATION"
460
+ || substrContains raw "INVALID"
461
+ then "request_invalid"
462
+ else "request_graphql"
463
+
464
+ -- Build the request body for a GraphQL point.
465
+ --
466
+ -- Variables come from the op's own arguments: a named variable binds to the
467
+ -- like-named argument (`from`), and the input-object variable (empty `from`)
468
+ -- takes the request data as a whole — which is what makes a generated
469
+ -- create/update call look exactly like its REST equivalent.
470
+ graphqlBodyUtil :: Context -> IO Value
471
+ graphqlBodyUtil ctx = do
472
+ point <- readIORef (cPoint ctx)
473
+ gql <- getp point "graphql"
474
+ case gql of
475
+ VMap _ -> do
476
+ -- reqmatch/reqdata hold the caller's arguments for THIS call;
477
+ -- data/match hold the entity's current state. Which pair depends on
478
+ -- whether the op takes match or data input. A named variable falls
479
+ -- back to the current state, so updating a loaded entity with just
480
+ -- {title} still binds the stored id the mutation requires.
481
+ op <- readIORef (cOp ctx)
482
+ let datainput = opInput op == "data"
483
+ rsV <- readIORef (if datainput then cReqdata ctx else cReqmatch ctx)
484
+ dsV <- readIORef (if datainput then cData ctx else cMatch ctx)
485
+ reqsrc <- case rsV of VMap _ -> pure rsV; _ -> emptyMap
486
+ datasrc <- case dsV of VMap _ -> pure dsV; _ -> emptyMap
487
+ variables <- emptyMap
488
+ vl <- getp gql "vars"
489
+ varlist <- case vl of VList _ -> listItems vl; _ -> pure []
490
+ forM_ varlist $ \spec -> case spec of
491
+ VMap _ -> do
492
+ name <- getStrD spec "name" ""
493
+ from <- getStrD spec "from" ""
494
+ when (not (null name)) $
495
+ if null from
496
+ then do
497
+ -- The input object IS the request body. Strip the action
498
+ -- selector, which is an SDK-side point discriminator, not an
499
+ -- API field.
500
+ body <- emptyMap
501
+ ks <- keysof reqsrc
502
+ forM_ ks $ \k -> when (k /= "$action") $ do
503
+ v <- getp reqsrc k
504
+ setp body k v
505
+ setp variables name body
506
+ else do
507
+ -- Only send variables the caller actually supplied: sending
508
+ -- an explicit null would clear a field on many APIs.
509
+ v0 <- getp reqsrc from
510
+ val <- if isNullish v0 then getp datasrc from else pure v0
511
+ when (not (isNullish val)) (setp variables name val)
512
+ _ -> pure ()
513
+ doc <- getp gql "doc"
514
+ jo [("query", doc), ("variables", variables)]
515
+ _ -> pure VNoval
516
+
517
+ -- Inspect a decoded GraphQL response body and record a failure when the
518
+ -- server reported one. Returns True when an error was recorded.
519
+ --
520
+ -- Partial data (`data` alongside `errors`) is treated as failure: the REST
521
+ -- surface has no partial-success concept, and silently returning half an
522
+ -- object would be worse than failing.
523
+ graphqlErrorsUtil :: Context -> IO Bool
524
+ graphqlErrorsUtil ctx = do
525
+ resultV <- readIORef (cResult ctx)
526
+ point <- readIORef (cPoint ctx)
527
+ kind <- getStrD point "kind" ""
528
+ case resultV of
529
+ VMap _ | kind == "graphql" -> do
530
+ body <- getp resultV "body"
531
+ ev <- getp body "errors"
532
+ errors <- case ev of VList _ -> listItems ev; _ -> pure []
533
+ if null errors
534
+ then pure False
535
+ else do
536
+ let firsterr = head errors
537
+ n = length errors
538
+ m0 <- getStrD firsterr "message" ""
539
+ let m1 = if null m0 then "graphql error" else m0
540
+ msg = if 1 < n then m1 ++ " (+" ++ show (n - 1) ++ " more)" else m1
541
+ code <- graphqlErrorCode firsterr
542
+ e <- mkErr code ("graphql: " ++ msg)
543
+ setp resultV "err" e
544
+ setp resultV "ok" (VBool False)
545
+ pure True
546
+ _ -> pure False
547
+
418
548
  prepareAuthUtil :: Context -> UResult
419
549
  prepareAuthUtil ctx = do
420
550
  specV <- readIORef (cSpec ctx)
@@ -642,8 +772,23 @@ makeSpecUtil ctx = do
642
772
  params <- prepareParamsUtil ctx; setp sp "params" params
643
773
  query <- prepareQueryUtil ctx; setp sp "query" query
644
774
  headers <- prepareHeadersUtil ctx; setp sp "headers" headers
645
- body <- prepareBodyUtil ctx; setp sp "body" body
646
- path <- preparePathUtil ctx; setp sp "path" (VStr path)
775
+ kind <- getStrD point "kind" ""
776
+ if kind == "graphql"
777
+ -- GraphQL addresses one endpoint: no path parts, no query string,
778
+ -- and the body carries the operation. prepareBody is skipped
779
+ -- deliberately — it only emits a body for data-input ops, whereas
780
+ -- every GraphQL op posts one, including load/list/remove.
781
+ then do
782
+ body <- graphqlBodyUtil ctx; setp sp "body" body
783
+ setp sp "path" (VStr "")
784
+ -- prepareQuery already copied the op's match arguments into the
785
+ -- query string. Those same values are bound as operation
786
+ -- variables, so leaving them would send /graphql?id=i1.
787
+ emptyq <- emptyMap; setp sp "query" emptyq
788
+ setp headers "content-type" (VStr graphqlContentType)
789
+ else do
790
+ body <- prepareBodyUtil ctx; setp sp "body" body
791
+ path <- preparePathUtil ctx; setp sp "path" (VStr path)
647
792
  ctrl <- readIORef (cCtrl ctx)
648
793
  explain <- getp ctrl "explain"
649
794
  case explain of { VMap _ -> do { snap <- specToValue sp; setp explain "spec" snap }; _ -> pure () }
@@ -780,6 +925,11 @@ makeResponseUtil ctx = do
780
925
  VMap _ -> do
781
926
  setp specV "step" (VStr "response")
782
927
  resultBasicUtil ctx; resultHeadersUtil ctx; resultBodyUtil ctx
928
+ -- GraphQL reports failures as a top-level `errors` array under
929
+ -- HTTP 200, so resultBasic's status check never sees them. Lift
930
+ -- them here, before the response transform tries to unwrap data
931
+ -- that is not there.
932
+ _ <- graphqlErrorsUtil ctx
783
933
  _ <- transformResponseUtil ctx
784
934
  errv <- getp resultV "err"
785
935
  isErrR <- isErr errv
@@ -866,7 +1016,7 @@ optSpecValue :: IO Value
866
1016
  optSpecValue = do
867
1017
  auth <- jo [("prefix", VStr "")]
868
1018
  hdrs <- jo [("`$CHILD`", VStr "`$STRING`")]
869
- allow <- jo [("method", VStr "GET,PUT,POST,PATCH,DELETE,OPTIONS"), ("op", VStr "create,update,load,list,remove,command,direct")]
1019
+ allow <- jo [("method", VStr "GET,PUT,POST,PATCH,DELETE,OPTIONS"), ("op", VStr "create,update,load,list,remove,command,direct,graphql")]
870
1020
  entChild <- do a <- emptyMap; jo [("`$OPEN`", VBool True), ("active", VBool False), ("alias", a)]
871
1021
  ent <- jo [("`$CHILD`", entChild)]
872
1022
  featChild <- jo [("`$OPEN`", VBool True), ("active", VBool False)]
@@ -176,7 +176,100 @@ public abstract class SdkClient {
176
176
  return utility.makeFetchDef.apply(ctx);
177
177
  }
178
178
 
179
+ /**
180
+ * Raw endpoint access is operator-controllable, like every entity op.
181
+ * Blocking it means denying BOTH the 'direct' and 'graphql' tokens, since
182
+ * either one reaches the same endpoint.
183
+ */
179
184
  public Map<String, Object> direct(Map<String, Object> fetchargs) {
185
+ if (!opAllowed("direct")) {
186
+ return opDenied("direct");
187
+ }
188
+
189
+ return rawRequest(fetchargs);
190
+ }
191
+
192
+ /** Is this raw-access op permitted by the SDK's allow.op option? */
193
+ private boolean opAllowed(String op) {
194
+ Object allow = Struct.getpath(this.options, List.of("allow", "op"));
195
+ return allow instanceof String && ((String) allow).contains(op);
196
+ }
197
+
198
+ private Map<String, Object> opDenied(String op) {
199
+ Object allow = Struct.getpath(this.options, List.of("allow", "op"));
200
+ Map<String, Object> out = new LinkedHashMap<>();
201
+ out.put("ok", false);
202
+ out.put("err", new SdkError(op + "_allow",
203
+ "ProjectNameSDK: " + op + ": operation not allowed by"
204
+ + " SDK option allow.op value: \""
205
+ + (allow instanceof String ? allow : "") + "\"", null));
206
+ return out;
207
+ }
208
+
209
+ /**
210
+ * Raw GraphQL access: the pressure valve that makes the generated
211
+ * surface's deliberate omissions (per-call selection sets, typed filter
212
+ * builders, batching, subscriptions) livable — the whole schema stays
213
+ * reachable.
214
+ *
215
+ * <p>Thin wrapper over the same prepare/fetch path direct uses, with the
216
+ * one thing raw direct cannot do for GraphQL: a GraphQL failure rides HTTP
217
+ * 200 as a top-level {@code errors} array, so status alone would report a
218
+ * failed query as ok.
219
+ *
220
+ * <p>NOTE: like direct, this bypasses the feature pipeline — no retry,
221
+ * ratelimit or paging features apply.
222
+ */
223
+ public Map<String, Object> graphql(String query,
224
+ Map<String, Object> variables, Map<String, Object> ctrl) {
225
+
226
+ if (!opAllowed("graphql")) {
227
+ return opDenied("graphql");
228
+ }
229
+
230
+ Map<String, Object> headers = new LinkedHashMap<>();
231
+ headers.put("content-type", "application/json");
232
+
233
+ Map<String, Object> body = new LinkedHashMap<>();
234
+ body.put("query", query);
235
+ body.put("variables", variables == null ? new LinkedHashMap<>() : variables);
236
+
237
+ Map<String, Object> fetchargs = new LinkedHashMap<>();
238
+ fetchargs.put("method", "POST");
239
+ fetchargs.put("headers", headers);
240
+ fetchargs.put("body", body);
241
+ fetchargs.put("ctrl", ctrl == null ? new LinkedHashMap<>() : ctrl);
242
+
243
+ Map<String, Object> res = rawRequest(fetchargs);
244
+
245
+ // Errors are read BEFORE any status check: a GraphQL parse or validation
246
+ // failure comes back as HTTP 400 carrying the standard { errors: [...] }
247
+ // body, and the raw path represents a non-2xx as ok:false with no err —
248
+ // so returning early on status would discard the server's own
249
+ // diagnostics, which are the only useful part of that response.
250
+ Object errors = Struct.getpath(res, List.of("data", "errors"));
251
+
252
+ if (errors instanceof List && !((List<Object>) errors).isEmpty()) {
253
+ Object first = ((List<Object>) errors).get(0);
254
+ Object m = Struct.getprop(first, "message");
255
+ String msg = m instanceof String && !((String) m).isEmpty()
256
+ ? (String) m : "graphql error";
257
+ res.put("ok", false);
258
+ res.put("err", new SdkError("graphql_error",
259
+ "ProjectNameSDK: graphql: " + msg, null));
260
+ res.put("graphql", errors);
261
+ }
262
+
263
+ return res;
264
+ }
265
+
266
+ /**
267
+ * Ungated request path shared by direct and graphql, each of which checks
268
+ * its own allow.op token first. Private, rather than a flag on fetchargs:
269
+ * a caller-supplied marker would let anyone opt straight back out of the
270
+ * gate by passing it.
271
+ */
272
+ private Map<String, Object> rawRequest(Map<String, Object> fetchargs) {
180
273
  Utility utility = this.utility;
181
274
 
182
275
  if (fetchargs == null) {
@@ -74,6 +74,8 @@ public class Utility {
74
74
  public CtxFn<Map<String, Object>> prepareParams;
75
75
  public CtxFn<String> preparePath;
76
76
  public CtxFn<Map<String, Object>> prepareQuery;
77
+ public CtxFn<Object> graphqlBody;
78
+ public CtxFn<Result> graphqlErrors;
77
79
  public CtxFn<Result> resultBasic;
78
80
  public CtxFn<Result> resultBody;
79
81
  public CtxFn<Result> resultHeaders;
@@ -115,6 +117,8 @@ public class Utility {
115
117
  u.prepareParams = this.prepareParams;
116
118
  u.preparePath = this.preparePath;
117
119
  u.prepareQuery = this.prepareQuery;
120
+ u.graphqlBody = this.graphqlBody;
121
+ u.graphqlErrors = this.graphqlErrors;
118
122
  u.resultBasic = this.resultBasic;
119
123
  u.resultBody = this.resultBody;
120
124
  u.resultHeaders = this.resultHeaders;
@@ -1,7 +1,9 @@
1
1
  package JAVAPACKAGE.feature;
2
2
 
3
3
  import java.util.LinkedHashMap;
4
+ import java.util.LinkedHashSet;
4
5
  import java.util.List;
6
+ import java.util.Set;
5
7
  import java.util.Map;
6
8
  import java.util.regex.Matcher;
7
9
  import java.util.regex.Pattern;
@@ -10,6 +12,7 @@ import JAVAPACKAGE.core.Context;
10
12
  import JAVAPACKAGE.core.Result;
11
13
  import JAVAPACKAGE.core.SdkClient;
12
14
  import JAVAPACKAGE.core.Spec;
15
+ import JAVAPACKAGE.utility.struct.Struct;
13
16
 
14
17
  // Pagination support for list operations. On the way out (PreRequest) it
15
18
  // stamps page/limit (or a cursor) into the request query; on the way back
@@ -66,6 +69,14 @@ public class PagingFeature extends BaseFeature {
66
69
  paging = new LinkedHashMap<>();
67
70
  }
68
71
 
72
+ // GraphQL paginates through operation VARIABLES, not the query string.
73
+ // This hook runs after makeSpec, so spec.body already holds the
74
+ // { query, variables } envelope, and before makeFetchDef serialises it.
75
+ if ("graphql".equals(Struct.getprop(ctx.point, "kind", null))) {
76
+ graphqlPreRequest(ctx, paging);
77
+ return;
78
+ }
79
+
69
80
  Object cursor = paging.get("cursor");
70
81
  if (cursor != null) {
71
82
  spec.query.put(cursorParam, cursor);
@@ -85,6 +96,51 @@ public class PagingFeature extends BaseFeature {
85
96
  }
86
97
  }
87
98
 
99
+ // Relay pagination: the cursor is the `after` variable (or whatever the
100
+ // model named it), and the page size is `first`.
101
+ @SuppressWarnings("unchecked")
102
+ private void graphqlPreRequest(Context ctx, Map<String, Object> paging) {
103
+ if (!(ctx.spec.body instanceof Map)) {
104
+ return;
105
+ }
106
+ Map<String, Object> body = (Map<String, Object>) ctx.spec.body;
107
+
108
+ Object varsval = body.get("variables");
109
+ Map<String, Object> variables;
110
+ if (varsval instanceof Map) {
111
+ variables = (Map<String, Object>) varsval;
112
+ }
113
+ else {
114
+ variables = new LinkedHashMap<>();
115
+ body.put("variables", variables);
116
+ }
117
+
118
+ String afterVar = FeatureOptions.foptStr(this.options, "afterVar", "after");
119
+ String firstVar = FeatureOptions.foptStr(this.options, "firstVar", "first");
120
+
121
+ // Only bind variables the operation actually declares, or the server
122
+ // rejects the document.
123
+ Set<String> declared = new LinkedHashSet<>();
124
+ Object varlist = Struct.getpath(ctx.point, List.of("graphql", "vars"));
125
+ if (varlist instanceof List) {
126
+ for (Object v : (List<Object>) varlist) {
127
+ Object name = Struct.getprop(v, "name", null);
128
+ if (name instanceof String) {
129
+ declared.add((String) name);
130
+ }
131
+ }
132
+ }
133
+
134
+ if (paging.get("cursor") != null && declared.contains(afterVar)) {
135
+ variables.put(afterVar, paging.get("cursor"));
136
+ }
137
+
138
+ if (this.options.get("limit") != null && variables.get(firstVar) == null
139
+ && declared.contains(firstVar)) {
140
+ variables.put(firstVar, FeatureOptions.foptInt(this.options, "limit", 0));
141
+ }
142
+ }
143
+
88
144
  @Override
89
145
  public void preResult(Context ctx) {
90
146
  if (!this.active || !isList(ctx)) {
@@ -113,6 +169,43 @@ public class PagingFeature extends BaseFeature {
113
169
  }
114
170
  }
115
171
 
172
+ // Set when the response states hasMore outright, rather than leaving it
173
+ // to be inferred from the presence of a cursor.
174
+ boolean explicitMore = false;
175
+
176
+ // Relay connections carry the cursor in pageInfo, at the path the model
177
+ // recorded for this op.
178
+ Object page = Struct.getpath(ctx.point, List.of("graphql", "page"));
179
+ if (page instanceof Map && body instanceof Map) {
180
+ // `connpath` locates the connection object inside the response
181
+ // envelope (data.<field>); the cursor/more paths are relative to it.
182
+ Object connpath = Struct.getprop(page, "connpath", null);
183
+ Object conn = body;
184
+ if (connpath instanceof String && !"".equals(connpath)) {
185
+ Object sub = Struct.getpath(body, connpath);
186
+ if (sub != null) {
187
+ conn = sub;
188
+ }
189
+ }
190
+
191
+ Object cursorpath = Struct.getprop(page, "cursor", null);
192
+ if (cursorpath instanceof String && !"".equals(cursorpath)) {
193
+ Object cursor = Struct.getpath(conn, cursorpath);
194
+ if (cursor != null) {
195
+ paging.put("cursor", cursor);
196
+ }
197
+ }
198
+
199
+ Object morepath = Struct.getprop(page, "more", null);
200
+ if (morepath instanceof String && !"".equals(morepath)) {
201
+ Object more = Struct.getpath(conn, morepath);
202
+ if (more instanceof Boolean) {
203
+ paging.put("hasMore", more);
204
+ explicitMore = true;
205
+ }
206
+ }
207
+ }
208
+
116
209
  // Body-level cursors.
117
210
  if (body instanceof Map) {
118
211
  Map<String, Object> bm = (Map<String, Object>) body;
@@ -127,10 +220,16 @@ public class PagingFeature extends BaseFeature {
127
220
  }
128
221
  if (bm.get("hasMore") instanceof Boolean) {
129
222
  paging.put("hasMore", bm.get("hasMore"));
223
+ explicitMore = true;
130
224
  }
131
225
  }
132
226
 
133
- if (!Boolean.TRUE.equals(paging.get("hasMore"))
227
+ // Cursor presence only INFERS another page. When the server stated the
228
+ // answer outright — relay's `hasNextPage: false`, or a body `hasMore` —
229
+ // that wins: a final page normally carries both an end cursor and
230
+ // hasNextPage false, and inferring from the cursor there would send the
231
+ // caller back for a page that does not exist, forever.
232
+ if (!explicitMore && !Boolean.TRUE.equals(paging.get("hasMore"))
134
233
  && (paging.get("next") != null || paging.get("cursor") != null
135
234
  || paging.get("nextPage") != null)) {
136
235
  paging.put("hasMore", true);
@@ -0,0 +1,191 @@
1
+ package JAVAPACKAGE.utility;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.LinkedHashMap;
5
+ import java.util.List;
6
+ import java.util.Map;
7
+
8
+ import JAVAPACKAGE.core.Context;
9
+ import JAVAPACKAGE.core.Result;
10
+ import JAVAPACKAGE.utility.struct.Struct;
11
+
12
+ /**
13
+ * GraphQL transport. API-INDEPENDENT: every GraphQL SDK this generator
14
+ * produces uses this file unchanged. The API-specific part — which
15
+ * operations exist and what each one's document is — is model data,
16
+ * computed once by apidef and emitted into Config.
17
+ *
18
+ * <p>Two jobs:
19
+ *
20
+ * <p>graphqlBody — build { query, variables } for a point, binding the op's
21
+ * arguments to the document's declared variables.
22
+ *
23
+ * <p>graphqlErrors — lift a GraphQL failure into an SDK error. GraphQL
24
+ * reports failures as a top-level {@code errors} array under HTTP 200, so
25
+ * the status-driven path in resultBasic never sees them.
26
+ */
27
+ @SuppressWarnings({"unchecked"})
28
+ final class Graphql {
29
+
30
+ /** Content type every GraphQL-over-HTTP request uses. */
31
+ static final String GRAPHQL_CONTENT_TYPE = "application/json";
32
+
33
+ private Graphql() {}
34
+
35
+ /**
36
+ * Map a GraphQL error to the same error codes the HTTP path produces, so
37
+ * a caller handles auth or rate limiting identically on both transports.
38
+ * Servers put the machine-readable code in {@code extensions.code};
39
+ * Linear-style APIs use {@code extensions.type}.
40
+ */
41
+ static String graphqlErrorCode(Object gqlerr) {
42
+ Object ext = Struct.getprop(gqlerr, "extensions", null);
43
+
44
+ Object code = Struct.getprop(ext, "code", null);
45
+ if (null == code || "".equals(code)) {
46
+ code = Struct.getprop(ext, "type", null);
47
+ }
48
+
49
+ String raw = (code instanceof String) ? ((String) code).toUpperCase() : "";
50
+
51
+ if (raw.contains("AUTH") || raw.contains("FORBIDDEN")
52
+ || raw.contains("UNAUTHENTICATED")) {
53
+ return "request_auth";
54
+ }
55
+ if (raw.contains("RATELIMIT") || raw.contains("RATE_LIMIT")
56
+ || raw.contains("TOO_MANY")) {
57
+ return "request_ratelimit";
58
+ }
59
+ if (raw.contains("BAD_USER_INPUT") || raw.contains("VALIDATION")
60
+ || raw.contains("INVALID")) {
61
+ return "request_invalid";
62
+ }
63
+
64
+ return "request_graphql";
65
+ }
66
+
67
+ /**
68
+ * Build the request body for a GraphQL point.
69
+ *
70
+ * <p>Variables come from the op's own arguments: a named variable binds to
71
+ * the like-named argument ({@code from}), and the input-object variable
72
+ * (empty {@code from}) takes the request data as a whole — which is what
73
+ * makes a generated create/update call look exactly like its REST
74
+ * equivalent.
75
+ */
76
+ static Object graphqlBody(Context ctx) {
77
+ Object gql = Struct.getprop(ctx.point, "graphql", null);
78
+
79
+ if (!(gql instanceof Map)) {
80
+ return null;
81
+ }
82
+
83
+ // reqmatch/reqdata hold the caller's arguments for THIS call; data/match
84
+ // hold the entity's current state. Which pair depends on whether the op
85
+ // takes match or data input. A named variable falls back to the current
86
+ // state, so updating a loaded entity with just {title} still binds the
87
+ // stored id the mutation requires.
88
+ Map<String, Object> reqsrc = ctx.reqmatch;
89
+ Map<String, Object> datasrc = ctx.match;
90
+ if (null != ctx.op && "data".equals(ctx.op.input)) {
91
+ reqsrc = ctx.reqdata;
92
+ datasrc = ctx.data;
93
+ }
94
+ if (null == reqsrc) {
95
+ reqsrc = new LinkedHashMap<>();
96
+ }
97
+ if (null == datasrc) {
98
+ datasrc = new LinkedHashMap<>();
99
+ }
100
+
101
+ Map<String, Object> variables = new LinkedHashMap<>();
102
+
103
+ Object rawvars = Struct.getprop(gql, "vars", null);
104
+ List<Object> varlist =
105
+ (rawvars instanceof List) ? (List<Object>) rawvars : new ArrayList<>();
106
+
107
+ for (Object spec : varlist) {
108
+ if (!(spec instanceof Map)) {
109
+ continue;
110
+ }
111
+
112
+ Object nameval = Struct.getprop(spec, "name", null);
113
+ String name = (nameval instanceof String) ? (String) nameval : "";
114
+
115
+ Object fromval = Struct.getprop(spec, "from", null);
116
+ String from = (fromval instanceof String) ? (String) fromval : "";
117
+
118
+ if ("".equals(from)) {
119
+ // The input object IS the request body. Strip the action selector,
120
+ // which is an SDK-side point discriminator, not an API field.
121
+ Map<String, Object> body = new LinkedHashMap<>();
122
+ for (Map.Entry<String, Object> e : reqsrc.entrySet()) {
123
+ if (!"$action".equals(e.getKey())) {
124
+ body.put(e.getKey(), e.getValue());
125
+ }
126
+ }
127
+ variables.put(name, body);
128
+ continue;
129
+ }
130
+
131
+ // Only send variables the caller actually supplied: sending an
132
+ // explicit null would clear a field on many APIs.
133
+ Object val = Struct.getprop(reqsrc, from, null);
134
+ if (null == val) {
135
+ val = Struct.getprop(datasrc, from, null);
136
+ }
137
+ if (null != val) {
138
+ variables.put(name, val);
139
+ }
140
+ }
141
+
142
+ Map<String, Object> out = new LinkedHashMap<>();
143
+ out.put("query", Struct.getprop(gql, "doc", null));
144
+ out.put("variables", variables);
145
+
146
+ return out;
147
+ }
148
+
149
+ /**
150
+ * Inspect a decoded GraphQL response body and record a failure when the
151
+ * server reported one. Returns true when an error was recorded.
152
+ *
153
+ * <p>Partial data ({@code data} alongside {@code errors}) is treated as
154
+ * failure: the REST surface has no partial-success concept, and silently
155
+ * returning half an object would be worse than failing. The raw envelope
156
+ * stays available on the result for callers that need it.
157
+ */
158
+ static Result graphqlErrors(Context ctx) {
159
+ Result result = ctx.result;
160
+
161
+ if (null == result || null == ctx.point) {
162
+ return result;
163
+ }
164
+
165
+ if (!"graphql".equals(Struct.getprop(ctx.point, "kind", null))) {
166
+ return result;
167
+ }
168
+
169
+ Object rawerrors = Struct.getprop(result.body, "errors", null);
170
+ if (!(rawerrors instanceof List)) {
171
+ return result;
172
+ }
173
+
174
+ List<Object> errors = (List<Object>) rawerrors;
175
+ if (0 == errors.size()) {
176
+ return result;
177
+ }
178
+
179
+ Object first = errors.get(0);
180
+ Object msgval = Struct.getprop(first, "message", null);
181
+ String msg = (msgval instanceof String) ? (String) msgval : "graphql error";
182
+ if (1 < errors.size()) {
183
+ msg = msg + " (+" + (errors.size() - 1) + " more)";
184
+ }
185
+
186
+ result.err = ctx.makeError(graphqlErrorCode(first), "graphql: " + msg);
187
+ result.ok = false;
188
+
189
+ return result;
190
+ }
191
+ }
@@ -73,7 +73,7 @@ final class MakeOptions {
73
73
  + "\"headers\": { \"`$CHILD`\": \"`$STRING`\" },"
74
74
  + "\"allow\": {"
75
75
  + " \"method\": \"GET,PUT,POST,PATCH,DELETE,OPTIONS\","
76
- + " \"op\": \"create,update,load,list,remove,command,direct\""
76
+ + " \"op\": \"create,update,load,list,remove,command,direct,graphql\""
77
77
  + "},"
78
78
  + "\"entity\": { \"`$CHILD`\": {"
79
79
  + " \"`$OPEN`\": true, \"active\": false, \"alias\": {} } },"
@@ -39,6 +39,12 @@ final class MakeResponse {
39
39
  utility.resultBasic.apply(ctx);
40
40
  utility.resultHeaders.apply(ctx);
41
41
  utility.resultBody.apply(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
+ utility.graphqlErrors.apply(ctx);
47
+
42
48
  utility.transformResponse.apply(ctx);
43
49
 
44
50
  if (result.err == null) {