@voxgig/sdkgen 4.22.0 → 4.23.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 (195) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/ReadmeTop.js +1 -1
  3. package/dist/cmp/ReadmeTop.js.map +1 -1
  4. package/dist/cmp/ReadmeUnions.js +1 -1
  5. package/dist/cmp/ReadmeUnions.js.map +1 -1
  6. package/dist/cmp/Test.js +1 -1
  7. package/dist/cmp/Test.js.map +1 -1
  8. package/dist/helpers/buildIdNames.d.ts +3 -1
  9. package/dist/helpers/buildIdNames.js +15 -7
  10. package/dist/helpers/buildIdNames.js.map +1 -1
  11. package/dist/helpers/canonSpec.js +2 -2
  12. package/dist/helpers/canonSpec.js.map +1 -1
  13. package/dist/helpers/getMatchEntries.js +2 -2
  14. package/dist/helpers/getMatchEntries.js.map +1 -1
  15. package/dist/helpers/modelNames.js +5 -1
  16. package/dist/helpers/modelNames.js.map +1 -1
  17. package/dist/helpers/opShape.js +33 -33
  18. package/dist/helpers/opShape.js.map +1 -1
  19. package/dist/helpers/pointPath.js +5 -10
  20. package/dist/helpers/pointPath.js.map +1 -1
  21. package/dist/helpers/resolved.js +2 -29
  22. package/dist/helpers/resolved.js.map +1 -1
  23. package/dist/sdkgen.d.ts +2 -2
  24. package/dist/sdkgen.js +5 -3
  25. package/dist/sdkgen.js.map +1 -1
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/dist/utility.js +15 -7
  28. package/dist/utility.js.map +1 -1
  29. package/package.json +3 -3
  30. package/project/.sdk/src/cmp/c/EntityTypes_c.ts +4 -4
  31. package/project/.sdk/src/cmp/c/ReadmeEntity_c.ts +3 -3
  32. package/project/.sdk/src/cmp/c/ReadmeModel_c.ts +3 -3
  33. package/project/.sdk/src/cmp/c/ReadmeRef_c.ts +5 -5
  34. package/project/.sdk/src/cmp/c/Schema_c.ts +1 -1
  35. package/project/.sdk/src/cmp/c/TestDirect_c.ts +12 -12
  36. package/project/.sdk/src/cmp/clojure/ReadmeEntity_clojure.ts +3 -3
  37. package/project/.sdk/src/cmp/clojure/ReadmeModel_clojure.ts +3 -3
  38. package/project/.sdk/src/cmp/clojure/ReadmeRef_clojure.ts +5 -5
  39. package/project/.sdk/src/cmp/clojure/Schema_clojure.ts +1 -1
  40. package/project/.sdk/src/cmp/cpp/EntityTypes_cpp.ts +3 -3
  41. package/project/.sdk/src/cmp/cpp/ReadmeEntity_cpp.ts +3 -3
  42. package/project/.sdk/src/cmp/cpp/ReadmeModel_cpp.ts +3 -3
  43. package/project/.sdk/src/cmp/cpp/ReadmeRef_cpp.ts +5 -5
  44. package/project/.sdk/src/cmp/cpp/Schema_cpp.ts +1 -1
  45. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +14 -14
  46. package/project/.sdk/src/cmp/cpp/TestEntity_cpp.ts +51 -50
  47. package/project/.sdk/src/cmp/csharp/EntityTypes_csharp.ts +4 -4
  48. package/project/.sdk/src/cmp/csharp/ReadmeEntity_csharp.ts +3 -3
  49. package/project/.sdk/src/cmp/csharp/ReadmeModel_csharp.ts +3 -3
  50. package/project/.sdk/src/cmp/csharp/ReadmeRef_csharp.ts +5 -5
  51. package/project/.sdk/src/cmp/csharp/Schema_csharp.ts +1 -1
  52. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -30
  53. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +66 -65
  54. package/project/.sdk/src/cmp/elixir/EntityTypes_elixir.ts +4 -4
  55. package/project/.sdk/src/cmp/elixir/ReadmeEntity_elixir.ts +3 -3
  56. package/project/.sdk/src/cmp/elixir/ReadmeModel_elixir.ts +3 -3
  57. package/project/.sdk/src/cmp/elixir/ReadmeRef_elixir.ts +4 -4
  58. package/project/.sdk/src/cmp/elixir/Schema_elixir.ts +1 -1
  59. package/project/.sdk/src/cmp/go/EntityTypes_go.ts +4 -4
  60. package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +3 -3
  61. package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +3 -3
  62. package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +5 -5
  63. package/project/.sdk/src/cmp/go/Schema_go.ts +1 -1
  64. package/project/.sdk/src/cmp/go/TestDirect_go.ts +38 -38
  65. package/project/.sdk/src/cmp/go/TestEntity_go.ts +75 -74
  66. package/project/.sdk/src/cmp/go/utility_go.ts +5 -5
  67. package/project/.sdk/src/cmp/java/EntityTypes_java.ts +4 -4
  68. package/project/.sdk/src/cmp/java/ReadmeEntity_java.ts +3 -3
  69. package/project/.sdk/src/cmp/java/ReadmeModel_java.ts +3 -3
  70. package/project/.sdk/src/cmp/java/ReadmeRef_java.ts +5 -5
  71. package/project/.sdk/src/cmp/java/Schema_java.ts +1 -1
  72. package/project/.sdk/src/cmp/java/TestDirect_java.ts +30 -30
  73. package/project/.sdk/src/cmp/java/TestEntity_java.ts +69 -68
  74. package/project/.sdk/src/cmp/js/EntityTypes_js.ts +5 -5
  75. package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +3 -3
  76. package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +3 -3
  77. package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +5 -5
  78. package/project/.sdk/src/cmp/js/Schema_js.ts +1 -1
  79. package/project/.sdk/src/cmp/js/TestDirect_js.ts +27 -27
  80. package/project/.sdk/src/cmp/js/TestEntity_js.ts +66 -69
  81. package/project/.sdk/src/cmp/js/TestLive_js.ts +3 -3
  82. package/project/.sdk/src/cmp/js/utility_js.ts +5 -5
  83. package/project/.sdk/src/cmp/kotlin/EntityTypes_kotlin.ts +4 -4
  84. package/project/.sdk/src/cmp/kotlin/ReadmeEntity_kotlin.ts +3 -3
  85. package/project/.sdk/src/cmp/kotlin/ReadmeModel_kotlin.ts +3 -3
  86. package/project/.sdk/src/cmp/kotlin/ReadmeRef_kotlin.ts +5 -5
  87. package/project/.sdk/src/cmp/kotlin/Schema_kotlin.ts +1 -1
  88. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +30 -30
  89. package/project/.sdk/src/cmp/kotlin/TestEntity_kotlin.ts +67 -66
  90. package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +5 -5
  91. package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +3 -3
  92. package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +3 -3
  93. package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +4 -4
  94. package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +5 -5
  95. package/project/.sdk/src/cmp/lua/Schema_lua.ts +1 -1
  96. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +25 -25
  97. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +66 -65
  98. package/project/.sdk/src/cmp/ocaml/ReadmeEntity_ocaml.ts +3 -3
  99. package/project/.sdk/src/cmp/ocaml/ReadmeModel_ocaml.ts +3 -3
  100. package/project/.sdk/src/cmp/ocaml/ReadmeRef_ocaml.ts +5 -5
  101. package/project/.sdk/src/cmp/ocaml/Schema_ocaml.ts +1 -1
  102. package/project/.sdk/src/cmp/perl/ReadmeEntity_perl.ts +3 -3
  103. package/project/.sdk/src/cmp/perl/ReadmeModel_perl.ts +3 -3
  104. package/project/.sdk/src/cmp/perl/ReadmeRef_perl.ts +5 -5
  105. package/project/.sdk/src/cmp/perl/Schema_perl.ts +1 -1
  106. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -25
  107. package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +60 -59
  108. package/project/.sdk/src/cmp/php/EntityTypes_php.ts +4 -4
  109. package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +3 -3
  110. package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +3 -3
  111. package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +4 -4
  112. package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +5 -5
  113. package/project/.sdk/src/cmp/php/Schema_php.ts +1 -1
  114. package/project/.sdk/src/cmp/php/TestDirect_php.ts +25 -25
  115. package/project/.sdk/src/cmp/php/TestEntity_php.ts +66 -65
  116. package/project/.sdk/src/cmp/py/EntityTypes_py.ts +4 -4
  117. package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +3 -3
  118. package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +3 -3
  119. package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +5 -5
  120. package/project/.sdk/src/cmp/py/Schema_py.ts +1 -1
  121. package/project/.sdk/src/cmp/py/TestDirect_py.ts +25 -25
  122. package/project/.sdk/src/cmp/py/TestEntity_py.ts +66 -65
  123. package/project/.sdk/src/cmp/py-data/Main_py-data.ts +4 -4
  124. package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +4 -4
  125. package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +3 -3
  126. package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +3 -3
  127. package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +4 -4
  128. package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +5 -5
  129. package/project/.sdk/src/cmp/rb/Schema_rb.ts +1 -1
  130. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +25 -25
  131. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +66 -65
  132. package/project/.sdk/src/cmp/rust/EntityTypes_rust.ts +4 -4
  133. package/project/.sdk/src/cmp/rust/ReadmeEntity_rust.ts +3 -3
  134. package/project/.sdk/src/cmp/rust/ReadmeModel_rust.ts +3 -3
  135. package/project/.sdk/src/cmp/rust/ReadmeRef_rust.ts +5 -5
  136. package/project/.sdk/src/cmp/rust/Schema_rust.ts +1 -1
  137. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +18 -18
  138. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +63 -62
  139. package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +4 -4
  140. package/project/.sdk/src/cmp/scala/ReadmeEntity_scala.ts +3 -3
  141. package/project/.sdk/src/cmp/scala/ReadmeModel_scala.ts +3 -3
  142. package/project/.sdk/src/cmp/scala/ReadmeRef_scala.ts +5 -5
  143. package/project/.sdk/src/cmp/scala/Schema_scala.ts +1 -1
  144. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +14 -14
  145. package/project/.sdk/src/cmp/scala/TestEntity_scala.ts +60 -59
  146. package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +4 -4
  147. package/project/.sdk/src/cmp/swift/ReadmeEntity_swift.ts +3 -3
  148. package/project/.sdk/src/cmp/swift/ReadmeModel_swift.ts +3 -3
  149. package/project/.sdk/src/cmp/swift/ReadmeRef_swift.ts +5 -5
  150. package/project/.sdk/src/cmp/swift/Schema_swift.ts +1 -1
  151. package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +5 -5
  152. package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +3 -3
  153. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +3 -3
  154. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +1 -1
  155. package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +5 -5
  156. package/project/.sdk/src/cmp/ts/Schema_ts.ts +1 -1
  157. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +40 -40
  158. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +72 -75
  159. package/project/.sdk/src/cmp/ts/TestLive_ts.ts +3 -3
  160. package/project/.sdk/src/cmp/ts/utility_ts.ts +5 -5
  161. package/project/.sdk/src/cmp/zig/ReadmeEntity_zig.ts +3 -3
  162. package/project/.sdk/src/cmp/zig/ReadmeModel_zig.ts +3 -3
  163. package/project/.sdk/src/cmp/zig/ReadmeRef_zig.ts +5 -5
  164. package/project/.sdk/src/cmp/zig/Schema_zig.ts +1 -1
  165. package/project/.sdk/tm/c/feature/test.c +20 -4
  166. package/project/.sdk/tm/clojure/src/sdk/features.clj +6 -4
  167. package/project/.sdk/tm/cpp/feature/test.hpp +17 -6
  168. package/project/.sdk/tm/csharp/feature/TestFeature.cs +10 -4
  169. package/project/.sdk/tm/elixir/lib/projectname/feature/test.ex +11 -5
  170. package/project/.sdk/tm/java/feature/TestFeature.java +11 -6
  171. package/project/.sdk/tm/js/test/live-entity.js +29 -29
  172. package/project/.sdk/tm/js/test/live-scenarios.js +4 -4
  173. package/project/.sdk/tm/kotlin/feature/TestFeature.kt +8 -4
  174. package/project/.sdk/tm/ocaml/sdk_features.ml +6 -4
  175. package/project/.sdk/tm/perl/feature/test_feature.pm +6 -4
  176. package/project/.sdk/tm/rb/feature/test_feature.rb +6 -4
  177. package/project/.sdk/tm/rust/feature/test.rs +8 -4
  178. package/project/.sdk/tm/scala/feature/TestFeature.scala +10 -6
  179. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +10 -6
  180. package/project/.sdk/tm/ts/test/live-entity.ts +26 -26
  181. package/project/.sdk/tm/ts/test/live-scenarios.ts +3 -3
  182. package/project/.sdk/tm/zig/feature/test.zig +11 -4
  183. package/project/sdkgen-package.json +1 -1
  184. package/src/cmp/ReadmeTop.ts +1 -1
  185. package/src/cmp/ReadmeUnions.ts +1 -1
  186. package/src/cmp/Test.ts +1 -1
  187. package/src/helpers/buildIdNames.ts +17 -7
  188. package/src/helpers/canonSpec.ts +2 -2
  189. package/src/helpers/getMatchEntries.ts +2 -2
  190. package/src/helpers/modelNames.ts +6 -1
  191. package/src/helpers/opShape.ts +33 -33
  192. package/src/helpers/pointPath.ts +5 -10
  193. package/src/helpers/resolved.ts +3 -14
  194. package/src/sdkgen.ts +3 -1
  195. package/src/utility.ts +16 -7
@@ -30,20 +30,36 @@ static voxgig_value* envelope(Context* ctx, voxgig_value* data) {
30
30
  if (NULL == spec) {
31
31
  return data;
32
32
  }
33
- // Exactly `body.<key>`; a deeper path is not an envelope this mock can
34
- // synthesise, so it is left alone rather than guessed at.
33
+ // Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
34
+ // GraphQL ops unwrap `body.data.<field>` (and `body.data.<field>.<entity>`
35
+ // for mutation payloads), not just a single envelope property.
35
36
  size_t n = strlen(spec);
36
37
  if (n < 8 || 0 != strncmp(spec, "`body.", 6) || '`' != spec[n - 1]) {
37
38
  return data;
38
39
  }
39
40
  size_t inner_len = n - 7;
40
- if (0 == inner_len || NULL != memchr(spec + 6, '.', inner_len)) {
41
+ if (0 == inner_len) {
41
42
  return data;
42
43
  }
43
44
  char* inner = (char*)malloc(inner_len + 1);
44
45
  memcpy(inner, spec + 6, inner_len);
45
46
  inner[inner_len] = '\0';
46
- voxgig_value* out = cmap(1, inner, data);
47
+ // Innermost segment first, so walk back from the end, terminating each
48
+ // segment in place as it is consumed. cmap copies the key.
49
+ voxgig_value* out = data;
50
+ size_t pos = inner_len;
51
+ while (1) {
52
+ size_t start = pos;
53
+ while (start > 0 && '.' != inner[start - 1]) {
54
+ start--;
55
+ }
56
+ inner[pos] = '\0';
57
+ out = cmap(1, inner + start, out);
58
+ if (0 == start) {
59
+ break;
60
+ }
61
+ pos = start - 1;
62
+ }
47
63
  free(inner);
48
64
  return out;
49
65
  }
@@ -124,11 +124,13 @@
124
124
  (let [envelope (fn [data]
125
125
  (let [tm (vs/getprop (core/oget fctx :point) "transform")
126
126
  spec (vs/getprop tm "res")]
127
- ;; Exactly `body.<key>`; a deeper path is not an
128
- ;; envelope this mock can synthesise.
127
+ ;; Rebuild whatever nesting the transform unwraps;
128
+ ;; GraphQL ops unwrap `body.data.<field>`.
129
129
  (if (and (some? data) (string? spec))
130
- (if-let [m (re-matches #"`body\.([^`.]+)`" spec)]
131
- (vs/jm (second m) data)
130
+ (if-let [m (re-matches #"`body\.(.+)`" spec)]
131
+ (reduce (fn [out seg] (vs/jm seg out))
132
+ data
133
+ (reverse (str/split (second m) #"\." -1)))
132
134
  data)
133
135
  data)))
134
136
  respond (fn [status data extra]
@@ -68,16 +68,27 @@ private:
68
68
  Value restf = getp(tm, "res");
69
69
  if (!restf.is_string()) return data;
70
70
  std::string spec = restf.as_string();
71
- // Exactly `body.<key>`; a deeper path is not an envelope this mock can
72
- // synthesise, so it is left alone rather than guessed at.
71
+ // Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
72
+ // GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
73
73
  if (spec.size() < 8) return data;
74
74
  if (0 != spec.compare(0, 6, "`body.")) return data;
75
75
  if ('`' != spec[spec.size() - 1]) return data;
76
76
  std::string inner = spec.substr(6, spec.size() - 7);
77
- if (inner.empty() || std::string::npos != inner.find('.')) return data;
78
- Value wrapped = vmap();
79
- map_put(wrapped, inner, data);
80
- return wrapped;
77
+ if (inner.empty()) return data;
78
+ std::vector<std::string> segs;
79
+ for (size_t at = 0;;) {
80
+ size_t dot = inner.find('.', at);
81
+ if (std::string::npos == dot) { segs.push_back(inner.substr(at)); break; }
82
+ segs.push_back(inner.substr(at, dot - at));
83
+ at = dot + 1;
84
+ }
85
+ Value out = data;
86
+ for (size_t i = segs.size(); i > 0; i--) {
87
+ Value wrapped = vmap();
88
+ map_put(wrapped, segs[i - 1], out);
89
+ out = wrapped;
90
+ }
91
+ return out;
81
92
  }
82
93
 
83
94
  Value respond(CtxPtr ctx, int status, const Value& data, const Value& extra) {
@@ -59,15 +59,21 @@ public class TestFeature : BaseFeature
59
59
  if (data == null || ctx2.Point == null) { return data; }
60
60
  var tm = StructUtils.GetProp(ctx2.Point, "transform");
61
61
  if (StructUtils.GetProp(tm, "res") is not string spec) { return data; }
62
- // Exactly `body.<key>`; a deeper path is not an envelope this
63
- // mock can synthesise, so it is left alone.
62
+ // Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
63
+ // GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
64
64
  if (!spec.StartsWith("`body.") || !spec.EndsWith("`") || spec.Length < 8)
65
65
  {
66
66
  return data;
67
67
  }
68
68
  var inner = spec.Substring(6, spec.Length - 7);
69
- if (inner.Length == 0 || inner.Contains('.')) { return data; }
70
- return new Dictionary<string, object?> { [inner] = data };
69
+ if (inner.Length == 0) { return data; }
70
+ var segs = inner.Split('.');
71
+ object? built = data;
72
+ for (var i = segs.Length - 1; 0 <= i; i--)
73
+ {
74
+ built = new Dictionary<string, object?> { [segs[i]] = built };
75
+ }
76
+ return built;
71
77
  }
72
78
 
73
79
  Dictionary<string, object?> Respond(int status, object? data,
@@ -55,16 +55,22 @@ defmodule ProjectName.Feature.Test do
55
55
  defp envelope(fctx, data) do
56
56
  spec = S.getprop(S.getprop(S.getprop(fctx, "point"), "transform"), "res")
57
57
 
58
- # Exactly `body.<key>`; a deeper path is not an envelope this mock can
59
- # synthesise, so it is left alone rather than guessed at.
58
+ # Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
59
+ # GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
60
60
  case {data, spec} do
61
61
  {nil, _} ->
62
62
  data
63
63
 
64
64
  {_, s} when is_binary(s) ->
65
- case Regex.run(~r/^`body\.([^`.]+)`$/, s) do
66
- [_, inner] -> S.jm([inner, data])
67
- _ -> data
65
+ case Regex.run(~r/^`body\.(.+)`$/, s) do
66
+ [_, inner] ->
67
+ inner
68
+ |> String.split(".")
69
+ |> Enum.reverse()
70
+ |> Enum.reduce(data, fn seg, out -> S.jm([seg, out]) end)
71
+
72
+ _ ->
73
+ data
68
74
  end
69
75
 
70
76
  _ ->
@@ -109,18 +109,23 @@ public class TestFeature extends BaseFeature {
109
109
  return data;
110
110
  }
111
111
  String spec = (String) restf;
112
- // Exactly `body.<key>`; a deeper path is not an envelope this mock can
113
- // synthesise, so it is left alone rather than guessed at.
112
+ // Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
113
+ // GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
114
114
  if (!spec.startsWith("`body.") || !spec.endsWith("`") || spec.length() < 8) {
115
115
  return data;
116
116
  }
117
117
  String inner = spec.substring(6, spec.length() - 1);
118
- if (inner.isEmpty() || inner.contains(".")) {
118
+ if (inner.isEmpty()) {
119
119
  return data;
120
120
  }
121
- Map<String, Object> wrapped = new LinkedHashMap<>();
122
- wrapped.put(inner, data);
123
- return wrapped;
121
+ String[] segs = inner.split("\\.", -1);
122
+ Object out = data;
123
+ for (int i = segs.length - 1; 0 <= i; i--) {
124
+ Map<String, Object> wrapped = new LinkedHashMap<>();
125
+ wrapped.put(segs[i], out);
126
+ out = wrapped;
127
+ }
128
+ return out;
124
129
  }
125
130
 
126
131
  private Map<String, Object> respond(Context ctx, int status, Object data, Map<String, Object> extra) {
@@ -10,26 +10,26 @@ const live_runner_1 = require("./live-runner");
10
10
  const utility_1 = require("./utility");
11
11
  // The offline flow keeps its deterministic fixture assertions. Live flows
12
12
  // resolve real prerequisites per operation and collect failures until done.
13
- async function runLiveEntity(setup, entity, flow, accessor) {
13
+ async function runLiveEntity(setup, entity, flow, accessor, facts = {}) {
14
14
  const { client, transport } = setup;
15
- const steps = flow.step || [];
15
+ const steps = (flow.step || []).filter((step) => false !== step.a);
16
16
  const created = new Map();
17
17
  const listed = [];
18
18
  const marks = new Map();
19
19
  const idField = entity.id?.field || 'id';
20
20
  const copy = (value) => JSON.parse(JSON.stringify(value ?? {}));
21
- const hasCreate = steps.some(step => step.op === 'create');
21
+ const hasCreate = steps.some(step => step.o === 'create');
22
22
  const report = await (0, live_runner_1.runLiveSteps)(steps.map((step, index) => {
23
- const ref = step.input?.ref || entity.name + '_ref01';
24
- const op = step.op;
23
+ const ref = step.i?.ref || entity.name + '_ref01';
24
+ const op = step.o;
25
25
  const excluded = (0, utility_1.isControlSkipped)('entityOp', entity.name + '.' + op, 'live');
26
26
  return {
27
27
  id: entity.name + '.' + op + '.' + index,
28
- cleanup: op === 'remove' || (step.valid || []).some((v) => v.apply === 'ItemNotExists'),
28
+ cleanup: op === 'remove' || (step.v || []).some((v) => v.apply === 'ItemNotExists'),
29
29
  excluded: excluded.skip ? excluded.reason || 'Excluded by test control' : undefined,
30
30
  run: async (context) => {
31
31
  transport.enter(context);
32
- const points = entity.op?.[op]?.points || [];
32
+ const points = (entity.op?.[op]?.points || []).filter((point) => false !== point.a);
33
33
  if (!points.length)
34
34
  throw new live_runner_1.LiveBlocked('No modelled operation point');
35
35
  const record = created.get(ref);
@@ -48,7 +48,7 @@ async function runLiveEntity(setup, entity, flow, accessor) {
48
48
  }
49
49
  let input = op === 'create'
50
50
  ? copy(setup.data.new?.[entity.name]?.[ref]) : {};
51
- for (const [name, binding] of Object.entries({ ...step.match, ...step.data })) {
51
+ for (const [name, binding] of Object.entries({ ...step.m, ...step.d })) {
52
52
  const value = setup.idmap[binding] ?? setup.idmap[name];
53
53
  if (undefined !== value)
54
54
  input[name] = value;
@@ -66,22 +66,22 @@ async function runLiveEntity(setup, entity, flow, accessor) {
66
66
  let selected;
67
67
  const missing = new Set();
68
68
  for (const point of points) {
69
- if (point.select?.$action !== input.$action)
69
+ if (point.q?.$action !== input.$action)
70
70
  continue;
71
71
  const candidate = { ...input };
72
72
  let viable = true;
73
- const params = point.args?.params || [];
74
- const query = (point.args?.query || []).filter((arg) => arg.reqd);
73
+ const params = point.g?.params || [];
74
+ const query = (point.g?.query || []).filter((arg) => arg.r);
75
75
  for (const arg of [...params, ...query]) {
76
- if (undefined !== candidate[arg.name] && null !== candidate[arg.name])
76
+ if (undefined !== candidate[arg.n] && null !== candidate[arg.n])
77
77
  continue;
78
- const key = arg.name === 'id' ? entity.name + '01' : arg.name.replace(/_id$/, '') + '01';
79
- const value = setup.idmap[key] ?? setup.idmap[arg.name] ?? loaded?.[arg.name] ?? arg.example;
78
+ const key = arg.n === 'id' ? entity.name + '01' : arg.n.replace(/_id$/, '') + '01';
79
+ const value = setup.idmap[key] ?? setup.idmap[arg.n] ?? loaded?.[arg.n] ?? arg.ex;
80
80
  if (undefined !== value && null !== value)
81
- candidate[arg.name] = value;
82
- else if (arg.reqd !== false) {
81
+ candidate[arg.n] = value;
82
+ else if (arg.r !== false) {
83
83
  viable = false;
84
- missing.add(arg.name);
84
+ missing.add(arg.n);
85
85
  }
86
86
  }
87
87
  if (viable) {
@@ -93,22 +93,22 @@ async function runLiveEntity(setup, entity, flow, accessor) {
93
93
  if (!resolved)
94
94
  throw new live_runner_1.LiveBlocked('No usable route; missing arguments: ' + [...missing].join(', '));
95
95
  input = resolved;
96
- if (op === 'create' && selected.contract) {
97
- const facts = JSON.parse(selected.contract.json);
98
- const request = (0, live_contract_1.requestContract)(facts);
96
+ if (op === 'create' && facts[selected.m + ' ' + selected.o]) {
97
+ const selectedFacts = facts[selected.m + ' ' + selected.o];
98
+ const request = (0, live_contract_1.requestContract)(selectedFacts);
99
99
  if (request.schema)
100
- input = { ...(0, live_contract_1.synthesizeInput)(request.schema, facts.live?.input ?? request.example) };
101
- else if (facts.protocol === 'http')
100
+ input = { ...(0, live_contract_1.synthesizeInput)(request.schema, selected.li?.input ?? request.example) };
101
+ else if (selectedFacts.protocol === 'http')
102
102
  input = {};
103
- for (const arg of selected.args?.params || [])
104
- if (resolved[arg.name] !== undefined)
105
- input[arg.name] = resolved[arg.name];
103
+ for (const arg of selected.g?.params || [])
104
+ if (resolved[arg.n] !== undefined)
105
+ input[arg.n] = resolved[arg.n];
106
106
  }
107
107
  let intendedMark;
108
108
  if (op === 'update') {
109
- for (const spec of step.spec || []) {
110
- if (spec.apply === 'TextFieldMark' && step.input?.textfield) {
111
- const mark = { name: step.input.textfield, value: spec.def.mark + '_' + setup.now };
109
+ for (const spec of step.s || []) {
110
+ if (spec.apply === 'TextFieldMark' && step.i?.textfield) {
111
+ const mark = { name: step.i.textfield, value: spec.def.mark + '_' + setup.now };
112
112
  input[mark.name] = mark.value;
113
113
  intendedMark = mark;
114
114
  }
@@ -127,7 +127,7 @@ async function runLiveEntity(setup, entity, flow, accessor) {
127
127
  });
128
128
  listed.splice(0, listed.length, ...data);
129
129
  context.publish(data);
130
- for (const validation of step.valid || []) {
130
+ for (const validation of step.v || []) {
131
131
  const previous = created.get(validation.def?.ref);
132
132
  const id = previous?.[idField] ?? previous?.id;
133
133
  if (undefined === id)
@@ -64,10 +64,10 @@ async function runLiveScenarios(SDK, plan, envPrefix, liveDefaults = {}) {
64
64
  let input = request.schema ? (0, live_contract_1.synthesizeInput)(request.schema, explicit) : explicit ?? {};
65
65
  for (const kind of ['params', 'query', 'header', 'cookie'])
66
66
  for (const arg of point.args?.[kind] || []) {
67
- if (arg.reqd && input[arg.name] === undefined) {
68
- if (arg.example === undefined)
69
- throw new live_runner_1.LiveBlocked('Missing required argument: ' + arg.name);
70
- input[arg.name] = arg.example;
67
+ if (arg.r && input[arg.n] === undefined) {
68
+ if (arg.ex === undefined)
69
+ throw new live_runner_1.LiveBlocked('Missing required argument: ' + arg.n);
70
+ input[arg.n] = arg.ex;
71
71
  }
72
72
  }
73
73
  const role = hint.auth || (point.facts.security?.length === 0 || point.facts.securitySource === 'unspecified' ? 'public' : 'account');
@@ -63,12 +63,16 @@ class TestFeature : BaseFeature("test", "0.0.1", true) {
63
63
  if (null == data || null == ctx) return data
64
64
  val tm = Struct.getprop(ctx.point, "transform")
65
65
  val restf = Struct.getprop(tm, "res") as? String ?: return data
66
- // Exactly `body.<key>`; a deeper path is not an envelope this mock can
67
- // synthesise, so it is left alone rather than guessed at.
66
+ // Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
67
+ // GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
68
68
  if (!restf.startsWith("`body.") || !restf.endsWith("`") || restf.length < 8) return data
69
69
  val inner = restf.substring(6, restf.length - 1)
70
- if (inner.isEmpty() || inner.contains(".")) return data
71
- return linkedMapOf<String, Any?>(inner to data)
70
+ if (inner.isEmpty()) return data
71
+ var out: Any? = data
72
+ for (seg in inner.split(".").reversed()) {
73
+ out = linkedMapOf<String, Any?>(seg to out)
74
+ }
75
+ return out
72
76
  }
73
77
 
74
78
  private fun respond(ctx: Context?, status: Int, data: Any?, extra: MutableMap<String, Any?>?): MutableMap<String, Any?> {
@@ -1394,12 +1394,14 @@ let test_feature () : feature =
1394
1394
  match getp (getp ctx.c_point "transform") "res" with
1395
1395
  | Str spec ->
1396
1396
  let n = String.length spec in
1397
- (* Exactly `body.<key>`; a deeper path is not an envelope this mock
1398
- can synthesise, so it is left alone rather than guessed at. *)
1397
+ (* Rebuild whatever nesting the transform unwraps. Multi-segment on
1398
+ purpose: GraphQL ops unwrap `body.data.<field>`. *)
1399
1399
  if n > 7 && String.sub spec 0 6 = "`body." && spec.[n - 1] = '`' then
1400
1400
  let inner = String.sub spec 6 (n - 7) in
1401
- if String.length inner = 0 || String.contains inner '.' then data
1402
- else jo [(inner, data)]
1401
+ if String.length inner = 0 then data
1402
+ else
1403
+ List.fold_left (fun out seg -> jo [(seg, out)]) data
1404
+ (List.rev (String.split_on_char '.' inner))
1403
1405
  else data
1404
1406
  | _ -> data in
1405
1407
  let respond ctx status data extra =
@@ -66,10 +66,12 @@ sub init {
66
66
  my $tm = ProjectNameHelpers::gp($fctx->{point}, 'transform');
67
67
  my $spec = ProjectNameHelpers::gp($tm, 'res');
68
68
  return $data unless defined $spec && !ref($spec);
69
- # Exactly `body.<key>`; a deeper path is not an envelope this mock can
70
- # synthesise, so it is left alone rather than guessed at.
71
- return $data unless $spec =~ /^`body\.([^`.]+)`$/;
72
- return { $1 => $data };
69
+ # Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
70
+ # GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
71
+ return $data unless $spec =~ /^`body\.(.+)`$/;
72
+ my $out = $data;
73
+ $out = { $_ => $out } for reverse split /\./, $1, -1;
74
+ return $out;
73
75
  };
74
76
 
75
77
  my $respond = sub {
@@ -4,9 +4,10 @@ require_relative '../utility/struct/voxgig_struct'
4
4
  require_relative 'base_feature'
5
5
 
6
6
  class ProjectNameTestFeature < ProjectNameBaseFeature
7
- # The `body.<key>` form of an op's response transform: the mock wraps its
8
- # payload in <key> so the transform can unwrap it again.
9
- ENVELOPE_RES_RE = /\A`body\.([^`.]+)`\z/
7
+ # The `body.<path>` form of an op's response transform: the mock rebuilds
8
+ # whatever nesting the transform unwraps. Multi-segment on purpose: GraphQL
9
+ # ops unwrap body.data.<field>, not just one level.
10
+ ENVELOPE_RES_RE = /\A`body\.(.+)`\z/
10
11
 
11
12
  def initialize
12
13
  super
@@ -52,7 +53,8 @@ class ProjectNameTestFeature < ProjectNameBaseFeature
52
53
  restf = transform["res"]
53
54
  next data unless restf.is_a?(String)
54
55
  m = ENVELOPE_RES_RE.match(restf)
55
- m.nil? ? data : { m[1] => data }
56
+ next data if m.nil?
57
+ m[1].split('.', -1).reverse.reduce(data) { |out, seg| { seg => out } }
56
58
  }
57
59
 
58
60
  respond = ->(status, data, extra) {
@@ -34,11 +34,15 @@ fn envelope(ctx: &Rc<Context>, data: Value) -> Value {
34
34
  }
35
35
  let restf = crate::core::helpers::getpath(&["transform", "res"], &ctx.point.borrow());
36
36
  if let Value::Str(spec) = restf {
37
- // Exactly `body.<key>` — a deeper path is not an envelope this mock
38
- // can synthesise, so it is left alone rather than guessed at.
37
+ // Rebuild whatever nesting the transform unwraps: a GraphQL op unwraps
38
+ // `body.data.<field>`, not just one envelope property.
39
39
  if let Some(inner) = spec.strip_prefix("`body.").and_then(|r| r.strip_suffix('`')) {
40
- if !inner.is_empty() && !inner.contains('.') {
41
- return jo(vec![(inner, data)]);
40
+ if !inner.is_empty() {
41
+ let mut out = data;
42
+ for seg in inner.split('.').rev() {
43
+ out = jo(vec![(seg, out)]);
44
+ }
45
+ return out;
42
46
  }
43
47
  }
44
48
  }
@@ -50,14 +50,18 @@ class TestFeature extends BaseFeature("test", "0.0.1", true) {
50
50
  val tm = Struct.getprop(ctx.point, "transform")
51
51
  Struct.getprop(tm, "res") match {
52
52
  case spec: String =>
53
- // Exactly `body.<key>`; a deeper path is not an envelope this mock
54
- // can synthesise, so it is left alone rather than guessed at.
53
+ // Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
54
+ // GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
55
55
  if (!spec.startsWith("`body.") || !spec.endsWith("`") || spec.length < 8) return data
56
56
  val inner = spec.substring(6, spec.length - 1)
57
- if (inner.isEmpty || inner.contains(".")) return data
58
- val wrapped = new java.util.LinkedHashMap[String, Object]()
59
- wrapped.put(inner, data)
60
- wrapped
57
+ if (inner.isEmpty) return data
58
+ var out: Object = data
59
+ for (seg <- inner.split("\\.", -1).reverse) {
60
+ val wrapped = new java.util.LinkedHashMap[String, Object]()
61
+ wrapped.put(seg, out)
62
+ out = wrapped
63
+ }
64
+ out
61
65
  case _ => data
62
66
  }
63
67
  }
@@ -16,14 +16,18 @@ private func testEnvelope(_ ctx: Context, _ data: Value) -> Value {
16
16
  guard let point = ctx.point else { return data }
17
17
  guard case .map(let tm)? = point.entries["transform"] else { return data }
18
18
  guard case .string(let spec)? = tm.entries["res"] else { return data }
19
- // Exactly `body.<key>`; a deeper path is not an envelope this mock can
20
- // synthesise, so it is left alone rather than guessed at.
19
+ // Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
20
+ // GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
21
21
  guard spec.hasPrefix("`body."), spec.hasSuffix("`"), spec.count >= 8 else { return data }
22
22
  let inner = String(spec.dropFirst(6).dropLast(1))
23
- guard !inner.isEmpty, !inner.contains(".") else { return data }
24
- let wrapped = VMap()
25
- wrapped.entries[inner] = data
26
- return .map(wrapped)
23
+ guard !inner.isEmpty else { return data }
24
+ var out = data
25
+ for seg in inner.split(separator: ".", omittingEmptySubsequences: false).reversed() {
26
+ let wrapped = VMap()
27
+ wrapped.entries[String(seg)] = out
28
+ out = .map(wrapped)
29
+ }
30
+ return out
27
31
  }
28
32
 
29
33
  private func testRespond(_ ctx: Context, _ status: Int, _ data: Value, _ extra: VMap?) -> Value {
@@ -5,26 +5,26 @@ import { isControlSkipped, liveDelayMs } from './utility'
5
5
 
6
6
  // The offline flow keeps its deterministic fixture assertions. Live flows
7
7
  // resolve real prerequisites per operation and collect failures until done.
8
- async function runLiveEntity(setup: any, entity: any, flow: any, accessor: string) {
8
+ async function runLiveEntity(setup: any, entity: any, flow: any, accessor: string, facts: any = {}) {
9
9
  const { client, transport } = setup
10
- const steps: any[] = flow.step || []
10
+ const steps: any[] = (flow.step || []).filter((step: any) => false !== step.a)
11
11
  const created = new Map<string, any>()
12
12
  const listed: any[] = []
13
13
  const marks = new Map<string, { name: string, value: any }>()
14
14
  const idField = entity.id?.field || 'id'
15
15
  const copy = (value: any) => JSON.parse(JSON.stringify(value ?? {}))
16
- const hasCreate = steps.some(step => step.op === 'create')
16
+ const hasCreate = steps.some(step => step.o === 'create')
17
17
  const report = await runLiveSteps(steps.map((step, index) => {
18
- const ref = step.input?.ref || entity.name + '_ref01'
19
- const op = step.op
18
+ const ref = step.i?.ref || entity.name + '_ref01'
19
+ const op = step.o
20
20
  const excluded = isControlSkipped('entityOp', entity.name + '.' + op, 'live')
21
21
  return {
22
22
  id: entity.name + '.' + op + '.' + index,
23
- cleanup: op === 'remove' || (step.valid || []).some((v: any) => v.apply === 'ItemNotExists'),
23
+ cleanup: op === 'remove' || (step.v || []).some((v: any) => v.apply === 'ItemNotExists'),
24
24
  excluded: excluded.skip ? excluded.reason || 'Excluded by test control' : undefined,
25
25
  run: async (context: any) => {
26
26
  transport.enter(context)
27
- const points = entity.op?.[op]?.points || []
27
+ const points = (entity.op?.[op]?.points || []).filter((point: any) => false !== point.a)
28
28
  if (!points.length) throw new LiveBlocked('No modelled operation point')
29
29
  const record = created.get(ref)
30
30
  // A failed create must not turn a later update/remove into a write
@@ -41,7 +41,7 @@ async function runLiveEntity(setup: any, entity: any, flow: any, accessor: strin
41
41
 
42
42
  let input: any = op === 'create'
43
43
  ? copy(setup.data.new?.[entity.name]?.[ref]) : {}
44
- for (const [name, binding] of Object.entries({ ...step.match, ...step.data })) {
44
+ for (const [name, binding] of Object.entries({ ...step.m, ...step.d })) {
45
45
  const value = setup.idmap[binding as string] ?? setup.idmap[name]
46
46
  if (undefined !== value) input[name] = value
47
47
  }
@@ -57,34 +57,34 @@ async function runLiveEntity(setup: any, entity: any, flow: any, accessor: strin
57
57
  let selected: any
58
58
  const missing = new Set<string>()
59
59
  for (const point of points) {
60
- if (point.select?.$action !== input.$action) continue
60
+ if (point.q?.$action !== input.$action) continue
61
61
  const candidate = { ...input }
62
62
  let viable = true
63
- const params = point.args?.params || []
64
- const query = (point.args?.query || []).filter((arg: any) => arg.reqd)
63
+ const params = point.g?.params || []
64
+ const query = (point.g?.query || []).filter((arg: any) => arg.r)
65
65
  for (const arg of [...params, ...query]) {
66
- if (undefined !== candidate[arg.name] && null !== candidate[arg.name]) continue
67
- const key = arg.name === 'id' ? entity.name + '01' : arg.name.replace(/_id$/, '') + '01'
68
- const value = setup.idmap[key] ?? setup.idmap[arg.name] ?? loaded?.[arg.name] ?? arg.example
69
- if (undefined !== value && null !== value) candidate[arg.name] = value
70
- else if (arg.reqd !== false) { viable = false; missing.add(arg.name) }
66
+ if (undefined !== candidate[arg.n] && null !== candidate[arg.n]) continue
67
+ const key = arg.n === 'id' ? entity.name + '01' : arg.n.replace(/_id$/, '') + '01'
68
+ const value = setup.idmap[key] ?? setup.idmap[arg.n] ?? loaded?.[arg.n] ?? arg.ex
69
+ if (undefined !== value && null !== value) candidate[arg.n] = value
70
+ else if (arg.r !== false) { viable = false; missing.add(arg.n) }
71
71
  }
72
72
  if (viable) { resolved = candidate; selected = point; break }
73
73
  }
74
74
  if (!resolved) throw new LiveBlocked('No usable route; missing arguments: ' + [...missing].join(', '))
75
75
  input = resolved
76
- if (op === 'create' && selected.contract) {
77
- const facts = JSON.parse(selected.contract.json)
78
- const request = requestContract(facts)
79
- if (request.schema) input = { ...synthesizeInput(request.schema, facts.live?.input ?? request.example) }
80
- else if (facts.protocol === 'http') input = {}
81
- for (const arg of selected.args?.params || []) if (resolved[arg.name] !== undefined) input[arg.name] = resolved[arg.name]
76
+ if (op === 'create' && facts[selected.m + ' ' + selected.o]) {
77
+ const selectedFacts = facts[selected.m + ' ' + selected.o]
78
+ const request = requestContract(selectedFacts)
79
+ if (request.schema) input = { ...synthesizeInput(request.schema, selected.li?.input ?? request.example) }
80
+ else if (selectedFacts.protocol === 'http') input = {}
81
+ for (const arg of selected.g?.params || []) if (resolved[arg.n] !== undefined) input[arg.n] = resolved[arg.n]
82
82
  }
83
83
  let intendedMark: { name: string, value: any } | undefined
84
84
  if (op === 'update') {
85
- for (const spec of step.spec || []) {
86
- if (spec.apply === 'TextFieldMark' && step.input?.textfield) {
87
- const mark = { name: step.input.textfield, value: spec.def.mark + '_' + setup.now }
85
+ for (const spec of step.s || []) {
86
+ if (spec.apply === 'TextFieldMark' && step.i?.textfield) {
87
+ const mark = { name: step.i.textfield, value: spec.def.mark + '_' + setup.now }
88
88
  input[mark.name] = mark.value
89
89
  intendedMark = mark
90
90
  }
@@ -103,7 +103,7 @@ async function runLiveEntity(setup: any, entity: any, flow: any, accessor: strin
103
103
  })
104
104
  listed.splice(0, listed.length, ...data)
105
105
  context.publish(data)
106
- for (const validation of step.valid || []) {
106
+ for (const validation of step.v || []) {
107
107
  const previous = created.get(validation.def?.ref)
108
108
  const id = previous?.[idField] ?? previous?.id
109
109
  if (undefined === id) continue
@@ -52,9 +52,9 @@ export async function runLiveScenarios(SDK: any, plan: any[], envPrefix: string,
52
52
  const explicit = hint.input === undefined ? request.example : resolveRecipe(hint.input, ctx.values)
53
53
  let input = request.schema ? synthesizeInput(request.schema, explicit) : explicit ?? {}
54
54
  for (const kind of ['params', 'query', 'header', 'cookie']) for (const arg of point.args?.[kind] || []) {
55
- if (arg.reqd && input[arg.name] === undefined) {
56
- if (arg.example === undefined) throw new LiveBlocked('Missing required argument: ' + arg.name)
57
- input[arg.name] = arg.example
55
+ if (arg.r && input[arg.n] === undefined) {
56
+ if (arg.ex === undefined) throw new LiveBlocked('Missing required argument: ' + arg.n)
57
+ input[arg.n] = arg.ex
58
58
  }
59
59
  }
60
60
  const role = hint.auth || (point.facts.security?.length === 0 || point.facts.securitySource === 'unspecified' ? 'public' : 'account')