@voxgig/sdkgen 1.3.17 → 1.3.19

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 (267) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Deploy.js +16 -16
  3. package/dist/cmp/Deploy.js.map +1 -1
  4. package/dist/cmp/Entity.js +5 -1
  5. package/dist/cmp/Entity.js.map +1 -1
  6. package/dist/cmp/Feature.js +2 -1
  7. package/dist/cmp/Feature.js.map +1 -1
  8. package/dist/cmp/ReadmeTop.js +13 -11
  9. package/dist/cmp/ReadmeTop.js.map +1 -1
  10. package/dist/helpers/canonType.js +8 -3
  11. package/dist/helpers/canonType.js.map +1 -1
  12. package/dist/helpers/collectDeps.d.ts +1 -1
  13. package/dist/helpers/collectDeps.js +36 -13
  14. package/dist/helpers/collectDeps.js.map +1 -1
  15. package/dist/helpers/naming.d.ts +5 -1
  16. package/dist/helpers/naming.js +118 -0
  17. package/dist/helpers/naming.js.map +1 -1
  18. package/dist/helpers/opShape.d.ts +3 -1
  19. package/dist/helpers/opShape.js +87 -8
  20. package/dist/helpers/opShape.js.map +1 -1
  21. package/dist/helpers/serverVars.d.ts +10 -0
  22. package/dist/helpers/serverVars.js +87 -0
  23. package/dist/helpers/serverVars.js.map +1 -0
  24. package/dist/sdkgen.d.ts +4 -3
  25. package/dist/sdkgen.js +11 -2
  26. package/dist/sdkgen.js.map +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/package.json +15 -10
  29. package/project/.sdk/model/target/cpp.aontu +4 -4
  30. package/project/.sdk/model/target/go-cli.aontu +17 -17
  31. package/project/.sdk/model/target/go-mcp.aontu +11 -11
  32. package/project/.sdk/model/target/go.aontu +1 -1
  33. package/project/.sdk/model/target/java.aontu +4 -4
  34. package/project/.sdk/model/target/kotlin.aontu +4 -4
  35. package/project/.sdk/model/target/lean.aontu +51 -0
  36. package/project/.sdk/model/target/scala.aontu +4 -4
  37. package/project/.sdk/src/cmp/c/EntityTypes_c.ts +3 -4
  38. package/project/.sdk/src/cmp/c/Entity_c.ts +3 -3
  39. package/project/.sdk/src/cmp/c/ReadmeExamplesTest_c.ts +24 -7
  40. package/project/.sdk/src/cmp/c/TestDirect_c.ts +3 -3
  41. package/project/.sdk/src/cmp/c/TestEntity_c.ts +1 -1
  42. package/project/.sdk/src/cmp/clojure/Entity_clojure.ts +1 -1
  43. package/project/.sdk/src/cmp/clojure/ReadmeExamplesTest_clojure.ts +19 -8
  44. package/project/.sdk/src/cmp/cpp/EntityTypes_cpp.ts +2 -2
  45. package/project/.sdk/src/cmp/cpp/Entity_cpp.ts +3 -3
  46. package/project/.sdk/src/cmp/cpp/MainEntity_cpp.ts +2 -2
  47. package/project/.sdk/src/cmp/cpp/ReadmeExamplesTest_cpp.ts +136 -0
  48. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +3 -3
  49. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -0
  50. package/project/.sdk/src/cmp/csharp/EntityTypes_csharp.ts +2 -3
  51. package/project/.sdk/src/cmp/csharp/Entity_csharp.ts +3 -3
  52. package/project/.sdk/src/cmp/csharp/MainEntity_csharp.ts +2 -2
  53. package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
  54. package/project/.sdk/src/cmp/csharp/ReadmeExamplesTest_csharp.ts +13 -1
  55. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +3 -3
  56. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +22 -15
  57. package/project/.sdk/src/cmp/dart/EntityTypes_dart.ts +2 -3
  58. package/project/.sdk/src/cmp/dart/Entity_dart.ts +3 -3
  59. package/project/.sdk/src/cmp/dart/MainEntity_dart.ts +2 -2
  60. package/project/.sdk/src/cmp/dart/Main_dart.ts +3 -3
  61. package/project/.sdk/src/cmp/dart/Package_dart.ts +1 -1
  62. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +4 -4
  63. package/project/.sdk/src/cmp/elixir/EntityTypes_elixir.ts +2 -3
  64. package/project/.sdk/src/cmp/elixir/Entity_elixir.ts +1 -1
  65. package/project/.sdk/src/cmp/elixir/ReadmeExamplesTest_elixir.ts +21 -3
  66. package/project/.sdk/src/cmp/go/Config_go.ts +14 -2
  67. package/project/.sdk/src/cmp/go/EntityTypes_go.ts +110 -18
  68. package/project/.sdk/src/cmp/go/Entity_go.ts +3 -3
  69. package/project/.sdk/src/cmp/go/Main_go.ts +7 -6
  70. package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
  71. package/project/.sdk/src/cmp/go/TestDirect_go.ts +3 -3
  72. package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
  73. package/project/.sdk/src/cmp/go/utility_go.ts +25 -0
  74. package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +62 -35
  75. package/project/.sdk/src/cmp/go-cli/fragment/main.fragment.go +7 -7
  76. package/project/.sdk/src/cmp/go-cli/fragment/words.fragment.go +21 -18
  77. package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +9 -1
  78. package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +28 -18
  79. package/project/.sdk/src/cmp/java/EntityTypes_java.ts +2 -3
  80. package/project/.sdk/src/cmp/java/Entity_java.ts +3 -3
  81. package/project/.sdk/src/cmp/java/MainEntity_java.ts +2 -2
  82. package/project/.sdk/src/cmp/java/Package_java.ts +1 -1
  83. package/project/.sdk/src/cmp/java/ReadmeExamplesTest_java.ts +23 -1
  84. package/project/.sdk/src/cmp/java/TestDirect_java.ts +3 -3
  85. package/project/.sdk/src/cmp/js/Config_js.ts +11 -0
  86. package/project/.sdk/src/cmp/js/EntityTypes_js.ts +2 -3
  87. package/project/.sdk/src/cmp/js/Entity_js.ts +3 -3
  88. package/project/.sdk/src/cmp/js/MainEntity_js.ts +2 -2
  89. package/project/.sdk/src/cmp/js/Main_js.ts +2 -2
  90. package/project/.sdk/src/cmp/js/TestDirect_js.ts +4 -4
  91. package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +2 -2
  92. package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +4 -0
  93. package/project/.sdk/src/cmp/kotlin/EntityTypes_kotlin.ts +2 -3
  94. package/project/.sdk/src/cmp/kotlin/Entity_kotlin.ts +3 -3
  95. package/project/.sdk/src/cmp/kotlin/MainEntity_kotlin.ts +2 -2
  96. package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +1 -1
  97. package/project/.sdk/src/cmp/kotlin/ReadmeExamplesTest_kotlin.ts +27 -2
  98. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +3 -3
  99. package/project/.sdk/src/cmp/lean/Config_lean.ts +88 -0
  100. package/project/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
  101. package/project/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
  102. package/project/.sdk/src/cmp/lean/Main_lean.ts +120 -0
  103. package/project/.sdk/src/cmp/lean/Package_lean.ts +68 -0
  104. package/project/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
  105. package/project/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
  106. package/project/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
  107. package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +38 -0
  108. package/project/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
  109. package/project/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
  110. package/project/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
  111. package/project/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
  112. package/project/.sdk/src/cmp/lean/ReadmeRef_lean.ts +16 -0
  113. package/project/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
  114. package/project/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
  115. package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +18 -0
  116. package/project/.sdk/src/cmp/lean/Test_lean.ts +207 -0
  117. package/project/.sdk/src/cmp/lean/utility_lean.ts +78 -0
  118. package/project/.sdk/src/cmp/lua/Config_lua.ts +10 -1
  119. package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +2 -3
  120. package/project/.sdk/src/cmp/lua/Entity_lua.ts +3 -3
  121. package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -1
  122. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +3 -3
  123. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +1 -1
  124. package/project/.sdk/src/cmp/ocaml/ReadmeExamplesTest_ocaml.ts +29 -14
  125. package/project/.sdk/src/cmp/perl/Entity_perl.ts +3 -3
  126. package/project/.sdk/src/cmp/perl/MainEntity_perl.ts +2 -2
  127. package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
  128. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +3 -3
  129. package/project/.sdk/src/cmp/php/Config_php.ts +13 -2
  130. package/project/.sdk/src/cmp/php/EntityTypes_php.ts +2 -3
  131. package/project/.sdk/src/cmp/php/Entity_php.ts +3 -3
  132. package/project/.sdk/src/cmp/php/MainEntity_php.ts +2 -2
  133. package/project/.sdk/src/cmp/php/Package_php.ts +1 -1
  134. package/project/.sdk/src/cmp/php/TestDirect_php.ts +3 -3
  135. package/project/.sdk/src/cmp/php/TestEntity_php.ts +1 -1
  136. package/project/.sdk/src/cmp/py/Config_py.ts +10 -1
  137. package/project/.sdk/src/cmp/py/EntityTypes_py.ts +2 -3
  138. package/project/.sdk/src/cmp/py/Entity_py.ts +3 -3
  139. package/project/.sdk/src/cmp/py/MainEntity_py.ts +2 -2
  140. package/project/.sdk/src/cmp/py/Main_py.ts +2 -2
  141. package/project/.sdk/src/cmp/py/Package_py.ts +1 -1
  142. package/project/.sdk/src/cmp/py/TestDirect_py.ts +3 -3
  143. package/project/.sdk/src/cmp/py/TestEntity_py.ts +1 -1
  144. package/project/.sdk/src/cmp/rb/Config_rb.ts +12 -1
  145. package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +15 -5
  146. package/project/.sdk/src/cmp/rb/Entity_rb.ts +3 -3
  147. package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +2 -2
  148. package/project/.sdk/src/cmp/rb/Package_rb.ts +2 -2
  149. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +3 -3
  150. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +1 -1
  151. package/project/.sdk/src/cmp/rust/EntityBase_rust.ts +2 -2
  152. package/project/.sdk/src/cmp/rust/EntityTypes_rust.ts +3 -4
  153. package/project/.sdk/src/cmp/rust/Entity_rust.ts +3 -3
  154. package/project/.sdk/src/cmp/rust/MainEntity_rust.ts +2 -2
  155. package/project/.sdk/src/cmp/rust/Package_rust.ts +1 -1
  156. package/project/.sdk/src/cmp/rust/ReadmeExamplesTest_rust.ts +22 -10
  157. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +3 -3
  158. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +25 -18
  159. package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +2 -3
  160. package/project/.sdk/src/cmp/scala/Entity_scala.ts +3 -3
  161. package/project/.sdk/src/cmp/scala/MainEntity_scala.ts +2 -2
  162. package/project/.sdk/src/cmp/scala/ReadmeExamplesTest_scala.ts +22 -3
  163. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +3 -3
  164. package/project/.sdk/src/cmp/scala/Test_scala.ts +1 -1
  165. package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +12 -6
  166. package/project/.sdk/src/cmp/swift/Entity_swift.ts +3 -3
  167. package/project/.sdk/src/cmp/swift/MainEntity_swift.ts +2 -2
  168. package/project/.sdk/src/cmp/swift/Package_swift.ts +1 -1
  169. package/project/.sdk/src/cmp/swift/ReadmeExamplesTest_swift.ts +18 -1
  170. package/project/.sdk/src/cmp/swift/TestDirect_swift.ts +1 -1
  171. package/project/.sdk/src/cmp/ts/Config_ts.ts +11 -0
  172. package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +2 -3
  173. package/project/.sdk/src/cmp/ts/Entity_ts.ts +3 -3
  174. package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +2 -2
  175. package/project/.sdk/src/cmp/ts/Main_ts.ts +2 -2
  176. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +4 -4
  177. package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +2 -2
  178. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +4 -0
  179. package/project/.sdk/src/cmp/zig/Entity_zig.ts +3 -3
  180. package/project/.sdk/src/cmp/zig/MainEntity_zig.ts +2 -2
  181. package/project/.sdk/src/cmp/zig/Package_zig.ts +10 -2
  182. package/project/.sdk/tm/cpp/test/primary_utility_test.cpp +8 -16
  183. package/project/.sdk/tm/csharp/Makefile +6 -6
  184. package/project/.sdk/tm/csharp/test/PrimaryUtilityTest.cs +10 -28
  185. package/project/.sdk/tm/dart/Makefile +3 -3
  186. package/project/.sdk/tm/dart/test/primary_test.dart +7 -0
  187. package/project/.sdk/tm/go/Makefile +6 -6
  188. package/project/.sdk/tm/go/feature/test_feature.go +33 -1
  189. package/project/.sdk/tm/go/test/primary_utility_test.go +46 -42
  190. package/project/.sdk/tm/go/test/runner_test.go +30 -1
  191. package/project/.sdk/tm/go/utility/make_options.go +48 -0
  192. package/project/.sdk/tm/java/Makefile +6 -6
  193. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +7 -24
  194. package/project/.sdk/tm/js/Makefile +6 -6
  195. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +18 -1
  196. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +39 -1
  197. package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +13 -0
  198. package/project/.sdk/tm/kotlin/Makefile +6 -6
  199. package/project/.sdk/tm/kotlin/test/PrimaryUtilityTest.kt +7 -20
  200. package/project/.sdk/tm/lean/LICENSE +21 -0
  201. package/project/.sdk/tm/lean/VERSION +1 -0
  202. package/project/.sdk/tm/lean/src/SdkFeature.lean +171 -0
  203. package/project/.sdk/tm/lean/src/SdkFeatures.lean +602 -0
  204. package/project/.sdk/tm/lean/src/SdkJson.lean +134 -0
  205. package/project/.sdk/tm/lean/src/SdkRuntime.lean +356 -0
  206. package/project/.sdk/tm/lean/src/SdkUtility.lean +617 -0
  207. package/project/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
  208. package/project/.sdk/tm/lean/src/Vregex.lean +412 -0
  209. package/project/.sdk/tm/lean/src/feature/README.md +5 -0
  210. package/project/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  211. package/project/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  212. package/project/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  213. package/project/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  214. package/project/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  215. package/project/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  216. package/project/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  217. package/project/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  218. package/project/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  219. package/project/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  220. package/project/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  221. package/project/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  222. package/project/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  223. package/project/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  224. package/project/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  225. package/project/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  226. package/project/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  227. package/project/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  228. package/project/.sdk/tm/lean/test/StructCorpus.lean +697 -0
  229. package/project/.sdk/tm/lean/test/TFeature.lean +268 -0
  230. package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +349 -0
  231. package/project/.sdk/tm/lua/Makefile +7 -7
  232. package/project/.sdk/tm/lua/feature/test_feature.lua +29 -1
  233. package/project/.sdk/tm/lua/test/primary_utility_test.lua +14 -8
  234. package/project/.sdk/tm/lua/utility/make_options.lua +39 -0
  235. package/project/.sdk/tm/perl/Makefile +6 -6
  236. package/project/.sdk/tm/perl/t/primary_utility.t +8 -17
  237. package/project/.sdk/tm/php/Makefile +6 -6
  238. package/project/.sdk/tm/php/feature/TestFeature.php +30 -2
  239. package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +14 -7
  240. package/project/.sdk/tm/php/utility/MakeOptions.php +44 -0
  241. package/project/.sdk/tm/py/Makefile +6 -6
  242. package/project/.sdk/tm/py/feature/test_feature.py +28 -1
  243. package/project/.sdk/tm/py/test/test_primary_utility.py +18 -18
  244. package/project/.sdk/tm/py/utility/make_options.py +42 -0
  245. package/project/.sdk/tm/rb/Makefile +6 -6
  246. package/project/.sdk/tm/rb/feature/test_feature.rb +24 -1
  247. package/project/.sdk/tm/rb/test/primary_utility_test.rb +11 -8
  248. package/project/.sdk/tm/rb/utility/make_options.rb +37 -0
  249. package/project/.sdk/tm/rust/Makefile +6 -6
  250. package/project/.sdk/tm/rust/tests/common/mod.rs +37 -1
  251. package/project/.sdk/tm/rust/tests/primary_utility_test.rs +12 -26
  252. package/project/.sdk/tm/swift/Makefile +6 -6
  253. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +7 -8
  254. package/project/.sdk/tm/ts/Makefile +6 -6
  255. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +18 -1
  256. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +39 -1
  257. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +69 -21
  258. package/src/cmp/Deploy.ts +16 -16
  259. package/src/cmp/Entity.ts +5 -1
  260. package/src/cmp/Feature.ts +2 -1
  261. package/src/cmp/ReadmeTop.ts +13 -11
  262. package/src/helpers/canonType.ts +8 -3
  263. package/src/helpers/collectDeps.ts +39 -12
  264. package/src/helpers/naming.ts +130 -0
  265. package/src/helpers/opShape.ts +99 -12
  266. package/src/helpers/serverVars.ts +105 -0
  267. package/src/sdkgen.ts +12 -3
@@ -41,11 +41,39 @@ function TestFeature:init(ctx, options)
41
41
  local test_self = self
42
42
 
43
43
  local function test_fetcher(fctx, _fullurl, _fetchdef)
44
+ -- Shape the mock payload the way the real API would, so the op's
45
+ -- response transform recovers the entity from it. A point carrying
46
+ -- transform.res of `body.item` describes an API that answers
47
+ -- { item = {...} }; handing back the bare entity means the transform
48
+ -- unwraps a property that is not there and the caller gets nil.
49
+ -- The mock has to agree with the model, or it only ever simulates
50
+ -- APIs whose responses happen to be unwrapped. Mirrors the ts/py mocks.
51
+ local function envelope(data)
52
+ local point = fctx.point
53
+ if data == nil or type(point) ~= "table" then
54
+ return data
55
+ end
56
+ local transform = point.transform
57
+ if type(transform) ~= "table" then
58
+ return data
59
+ end
60
+ local restf = transform.res
61
+ if type(restf) ~= "string" then
62
+ return data
63
+ end
64
+ local key = string.match(restf, "^`body%.([^`%.]+)`$")
65
+ if key == nil then
66
+ return data
67
+ end
68
+ return { [key] = data }
69
+ end
70
+
44
71
  local function respond(status, data, extra)
72
+ local payload = envelope(data)
45
73
  local out = {
46
74
  status = status,
47
75
  statusText = "OK",
48
- json = function() return data end,
76
+ json = function() return payload end,
49
77
  body = "not-used",
50
78
  }
51
79
  if type(extra) == "table" then
@@ -576,6 +576,9 @@ describe("PrimaryUtility", function()
576
576
  it("fetcher-live", function()
577
577
  local calls = {}
578
578
  local live_client = sdk.new({
579
+ -- Concrete base: a live construction must satisfy any server variables
580
+ -- a templated base URL declares; a literal base sidesteps the requirement.
581
+ base = "http://localhost:8080",
579
582
  system = {
580
583
  fetch = function(url, fetchdef)
581
584
  table.insert(calls, { url = url, init = fetchdef })
@@ -600,6 +603,7 @@ describe("PrimaryUtility", function()
600
603
 
601
604
  it("fetcher-blocked-test-mode", function()
602
605
  local blocked_client = sdk.new({
606
+ base = "http://localhost:8080",
603
607
  system = {
604
608
  fetch = function(url, fetchdef)
605
609
  return {}, nil
@@ -1052,14 +1056,16 @@ describe("PrimaryUtility", function()
1052
1056
 
1053
1057
 
1054
1058
  it("preparePath-basic", function()
1055
- local ctx = make_test_full_ctx(client, utility)
1056
- ctx.point = {
1057
- parts = { "api", "planet", "{id}" },
1058
- args = { params = {} },
1059
- }
1060
-
1061
- local path = utility.prepare_path(ctx)
1062
- assert.are.equal("api/planet/{id}", path)
1059
+ -- Was hand-written cases that had drifted out of the shared corpus
1060
+ -- (the preparePath fixture shipped as an empty `set: []`).
1061
+ runset(get_spec(primary, "preparePath", "basic"), function(entry)
1062
+ local ctxmap = entry["ctx"]
1063
+ if type(ctxmap) ~= "table" then
1064
+ ctxmap = {}
1065
+ end
1066
+ local ctx = make_ctx_from_map(ctxmap, client, utility)
1067
+ return utility.prepare_path(ctx), nil
1068
+ end)
1063
1069
  end)
1064
1070
 
1065
1071
 
@@ -92,6 +92,13 @@ local function make_options_util(ctx)
92
92
  clean = {
93
93
  keys = "key,token,id",
94
94
  },
95
+ -- Server-variable values for a templated base URL (OpenAPI server
96
+ -- variables): {name} placeholders in `base` are substituted from this
97
+ -- map at construction. Spec defaults arrive via the generated config;
98
+ -- user values override them.
99
+ server = {
100
+ ["`$CHILD`"] = "",
101
+ },
95
102
  }
96
103
 
97
104
  -- Preserve system.fetch before merge/validate.
@@ -104,6 +111,38 @@ local function make_options_util(ctx)
104
111
  end
105
112
  opts = validated
106
113
 
114
+ -- Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
115
+ -- Every placeholder must resolve to a non-empty value: from
116
+ -- options.server (user), else the config default. A placeholder that
117
+ -- resolves to "" is a construction ERROR in live mode — the URL cannot
118
+ -- work — but in test mode substitutes the deterministic value
119
+ -- "test-<name>" so offline tests need no configuration.
120
+ local base = opts.base
121
+ if type(base) == "string" and string.find(base, "{", 1, true) then
122
+ local testmode = vs.getpath(opts, "test.active") == true
123
+ or vs.getpath(opts, "feature.test.active") == true
124
+ local server = type(opts.server) == "table" and opts.server or {}
125
+ local sdkname = vs.getpath(config, "main.name")
126
+ if type(sdkname) ~= "string" or sdkname == "" then
127
+ sdkname = "SDK"
128
+ end
129
+ opts.base = string.gsub(base, "{([%w_]+)}", function(name)
130
+ local val = server[name]
131
+ if type(val) ~= "string" then
132
+ val = ""
133
+ end
134
+ if val == "" then
135
+ if testmode then
136
+ return "test-" .. name
137
+ end
138
+ error(sdkname .. ": the server variable '" .. name .. "' is required: " ..
139
+ "the API base URL is '" .. base .. "' — pass " ..
140
+ "{ server = { " .. name .. " = \"...\" } } in the SDK options", 0)
141
+ end
142
+ return val
143
+ end)
144
+ end
145
+
107
146
  -- Restore system.fetch.
108
147
  if sys_fetch ~= nil then
109
148
  if type(opts["system"]) == "table" then
@@ -20,10 +20,10 @@ clean:
20
20
  # Build a CPAN dist in a throwaway .release/ dir to validate the package,
21
21
  # then tag perl/vX.Y.Z (version from $VERSION in lib/*SDK.pm). The CPAN
22
22
  # registry entry for this port is declared but pending, so publish is
23
- # tag-only: no upload happens. Run headless under the aql key vault, which
23
+ # tag-only: no upload happens. Run headless under the boru key vault, which
24
24
  # injects the GitHub token (never on disk or argv):
25
- # aql vault exec --for=github=<alias> -- make publish
26
- # aql `--dry-run` injects a filler token we detect to rehearse (build only,
25
+ # boru vault exec --for=github=<alias> -- make publish
26
+ # boru `--dry-run` injects a filler token we detect to rehearse (build only,
27
27
  # no tag). set -e + tag-last: a failed build never leaves a tag behind.
28
28
  #
29
29
  # NOTE: `perl Makefile.PL` (ExtUtils::MakeMaker) GENERATES a file named
@@ -31,7 +31,7 @@ clean:
31
31
  # dist build always happens inside .release/, never in place.
32
32
  VERSION := $(shell sed -nE "s/.*VERSION *= *'([^']+)'.*/\1/p" lib/*SDK.pm | head -1)
33
33
  TAG := perl/v$(VERSION)
34
- AQL_DRY_RUN_FILLER := AQL-DRY-RUN-FILLER-NOT-A-REAL-SECRET
34
+ BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
35
35
  publish: test
36
36
  @if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
37
37
  echo "tag $(TAG) already exists — bump VERSION in lib/*SDK.pm first"; exit 1; fi
@@ -42,8 +42,8 @@ publish: test
42
42
  { [ -f README.md ] && cp README.md .release/ || true; }; \
43
43
  (cd .release && $(PERL) Makefile.PL >/dev/null && echo "dist metadata OK"); \
44
44
  rm -rf .release; \
45
- if [ "$$token" = "$(AQL_DRY_RUN_FILLER)" ]; then \
46
- echo "[dry-run] aql filler token detected: would tag $(TAG); nothing pushed."; \
45
+ if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
46
+ echo "[dry-run] boru filler token detected: would tag $(TAG); nothing pushed."; \
47
47
  exit 0; \
48
48
  fi; \
49
49
  git tag -a "$(TAG)" -m "perl v$(VERSION)"; \
@@ -888,23 +888,14 @@ runset('prepareParams.basic', get_spec($PRIMARY, 'prepareParams', 'basic'), sub
888
888
 
889
889
  # === preparePath ===
890
890
 
891
- {
892
- my $ctx = make_test_full_ctx($client, $utility);
893
- $ctx->{point} = {
894
- 'parts' => ['api', 'planet', '{id}'],
895
- 'args' => { 'params' => [] },
896
- };
897
- is($utility->{prepare_path}->($ctx), 'api/planet/{id}', 'prepare_path basic');
898
- }
899
-
900
- {
901
- my $ctx = make_test_full_ctx($client, $utility);
902
- $ctx->{point} = {
903
- 'parts' => ['items'],
904
- 'args' => { 'params' => [] },
905
- };
906
- is($utility->{prepare_path}->($ctx), 'items', 'prepare_path single');
907
- }
891
+ # Was two hand-written cases that had drifted out of the shared corpus
892
+ # (the preparePath fixture shipped as an empty `set: []`).
893
+ runset('preparePath.basic', get_spec($PRIMARY, 'preparePath', 'basic'), sub {
894
+ my ($entry) = @_;
895
+ my $ctxmap = $entry->{ctx} || {};
896
+ my $ctx = make_ctx_from_map($ctxmap, $client, $utility);
897
+ return $utility->{prepare_path}->($ctx);
898
+ });
908
899
 
909
900
 
910
901
  # === prepareQuery ===
@@ -13,20 +13,20 @@ clean:
13
13
  # Packagist has no upload — it tracks the repo and indexes each git tag
14
14
  # php/vX.Y.Z (a configured GitHub webhook, or the Packagist "Update" API,
15
15
  # refreshes the package). So publishing a version means pushing the tag. Run
16
- # headless under the aql key vault, which injects the GitHub token:
17
- # aql vault exec --for=github=<alias> -- make publish
18
- # aql `--dry-run` injects a filler token we detect to rehearse (no tag/push).
16
+ # headless under the boru key vault, which injects the GitHub token:
17
+ # boru vault exec --for=github=<alias> -- make publish
18
+ # boru `--dry-run` injects a filler token we detect to rehearse (no tag/push).
19
19
  # set -e + tag-last: a failed push never leaves a tag behind.
20
20
  VERSION := $(shell cat VERSION)
21
21
  TAG := php/v$(VERSION)
22
- AQL_DRY_RUN_FILLER := AQL-DRY-RUN-FILLER-NOT-A-REAL-SECRET
22
+ BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
23
23
  publish: test
24
24
  @if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
25
25
  echo "tag $(TAG) already exists — bump VERSION first"; exit 1; fi
26
26
  @set -e; \
27
27
  token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
28
- if [ "$$token" = "$(AQL_DRY_RUN_FILLER)" ]; then \
29
- echo "[dry-run] aql filler token detected: would tag $(TAG) for Packagist; nothing pushed."; \
28
+ if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ]; then \
29
+ echo "[dry-run] boru filler token detected: would tag $(TAG) for Packagist; nothing pushed."; \
30
30
  exit 0; \
31
31
  fi; \
32
32
  git tag -a "$(TAG)" -m "php v$(VERSION)"; \
@@ -48,11 +48,39 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
48
48
  $entity->data = $entity_data;
49
49
 
50
50
  $test_fetcher = function (ProjectNameContext $fctx, string $_fullurl, array $_fetchdef) use ($entity): array {
51
- $respond = function (int $status, mixed $data, ?array $extra = null): array {
51
+ // Shape the mock payload the way the real API would, so the op's
52
+ // response transform recovers the entity from it. A point carrying
53
+ // transform.res of `body.item` describes an API that answers
54
+ // {"item": {...}}; handing back the bare entity means the
55
+ // transform unwraps a property that is not there and the caller
56
+ // gets null. The mock has to agree with the model, or it only
57
+ // ever simulates APIs whose responses happen to be unwrapped.
58
+ // Mirrors the ts/py mocks.
59
+ $envelope = function (mixed $data) use ($fctx): mixed {
60
+ $point = $fctx->point ?? null;
61
+ if ($data === null || !is_array($point)) {
62
+ return $data;
63
+ }
64
+ $transform = $point['transform'] ?? null;
65
+ if (!is_array($transform)) {
66
+ return $data;
67
+ }
68
+ $restf = $transform['res'] ?? null;
69
+ if (!is_string($restf)) {
70
+ return $data;
71
+ }
72
+ if (!preg_match('/^`body\.([^`.]+)`$/', $restf, $m)) {
73
+ return $data;
74
+ }
75
+ return [$m[1] => $data];
76
+ };
77
+
78
+ $respond = function (int $status, mixed $data, ?array $extra = null) use ($envelope): array {
79
+ $payload = $envelope($data);
52
80
  $out = [
53
81
  'status' => $status,
54
82
  'statusText' => 'OK',
55
- 'json' => function () use ($data) { return $data; },
83
+ 'json' => function () use ($payload) { return $payload; },
56
84
  'body' => 'not-used',
57
85
  ];
58
86
  if ($extra) {
@@ -587,6 +587,10 @@ class PrimaryUtilityTest extends TestCase
587
587
  {
588
588
  $calls = [];
589
589
  $live_client = new ProjectNameSDK([
590
+ // Concrete base: a live construction must satisfy any server
591
+ // variables a templated base URL declares; a literal base
592
+ // sidesteps the requirement.
593
+ 'base' => 'http://localhost:8080',
590
594
  'system' => [
591
595
  'fetch' => function (string $url, array $fetchdef) use (&$calls) {
592
596
  $calls[] = ['url' => $url, 'init' => $fetchdef];
@@ -614,6 +618,7 @@ class PrimaryUtilityTest extends TestCase
614
618
  public function test_fetcher_blocked_test_mode(): void
615
619
  {
616
620
  $blocked_client = new ProjectNameSDK([
621
+ 'base' => 'http://localhost:8080',
617
622
  'system' => [
618
623
  'fetch' => function (string $url, array $fetchdef) {
619
624
  return [[], null];
@@ -1136,16 +1141,18 @@ class PrimaryUtilityTest extends TestCase
1136
1141
 
1137
1142
  public function test_prepare_path_basic(): void
1138
1143
  {
1144
+ // Was hand-written cases that had drifted out of the shared corpus
1145
+ // (the preparePath fixture shipped as an empty `set: []`).
1146
+ $spec = self::load_test_spec();
1147
+ $primary = self::get_spec($spec, 'primary');
1139
1148
  $client = ProjectNameSDK::test(null, null);
1140
1149
  $utility = $client->get_utility();
1141
- $ctx = self::make_test_full_ctx($client, $utility);
1142
- $ctx->point = [
1143
- 'parts' => ['api', 'planet', '{id}'],
1144
- 'args' => ['params' => []],
1145
- ];
1146
1150
 
1147
- $path = ($utility->prepare_path)($ctx);
1148
- $this->assertEquals('api/planet/{id}', $path);
1151
+ $this->runset(self::get_spec($primary, 'preparePath', 'basic'), function (array $entry) use ($client, $utility) {
1152
+ $ctxmap = $entry['ctx'] ?? [];
1153
+ $ctx = self::make_ctx_from_map($ctxmap, $client, $utility);
1154
+ return [($utility->prepare_path)($ctx), null];
1155
+ });
1149
1156
  }
1150
1157
 
1151
1158
 
@@ -88,6 +88,11 @@ class ProjectNameMakeOptions
88
88
  'system' => (object)[],
89
89
  'test' => ['active' => false, 'entity' => ['`$OPEN`' => true]],
90
90
  'clean' => ['keys' => 'key,token,id'],
91
+ // Server-variable values for a templated base URL (OpenAPI server
92
+ // variables): {name} placeholders in 'base' are substituted from
93
+ // this map at construction. Spec defaults arrive via the generated
94
+ // config; user values override them.
95
+ 'server' => ['`$CHILD`' => ''],
91
96
  ];
92
97
 
93
98
  // Empty [] would be treated as a list and clobber the map under merge;
@@ -101,6 +106,45 @@ class ProjectNameMakeOptions
101
106
  $opts = [];
102
107
  }
103
108
 
109
+ // Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
110
+ // Every placeholder must resolve to a non-empty value: from
111
+ // $options['server'] (user), else the config default. A placeholder
112
+ // that resolves to '' is a construction ERROR in live mode — the URL
113
+ // cannot work — but in test mode substitutes the deterministic value
114
+ // "test-<name>" so offline tests need no configuration.
115
+ $base = $opts['base'] ?? null;
116
+ if (is_string($base) && strpos($base, '{') !== false) {
117
+ $testmode =
118
+ \Voxgig\Struct\Struct::getpath($opts, 'test.active') === true
119
+ || \Voxgig\Struct\Struct::getpath($opts, 'feature.test.active') === true;
120
+ $server = is_array($opts['server'] ?? null) ? $opts['server'] : [];
121
+ $sdkname = \Voxgig\Struct\Struct::getpath($config, 'main.name');
122
+ if (!is_string($sdkname) || $sdkname === '') {
123
+ $sdkname = 'SDK';
124
+ }
125
+ $opts['base'] = preg_replace_callback(
126
+ '/\{([A-Za-z0-9_]+)\}/',
127
+ function ($m) use ($server, $testmode, $sdkname, $base) {
128
+ $name = $m[1];
129
+ $val = $server[$name] ?? '';
130
+ if (!is_string($val)) {
131
+ $val = '';
132
+ }
133
+ if ($val === '') {
134
+ if ($testmode) {
135
+ return 'test-' . $name;
136
+ }
137
+ throw new \InvalidArgumentException(
138
+ $sdkname . ": the server variable '" . $name . "' is required: " .
139
+ "the API base URL is '" . $base . "' — pass " .
140
+ "['server' => ['" . $name . "' => '...']] in the SDK options");
141
+ }
142
+ return $val;
143
+ },
144
+ $base
145
+ );
146
+ }
147
+
104
148
  if ($sys_fetch) {
105
149
  if (!is_array($opts['system'] ?? null)) {
106
150
  $opts['system'] = [];
@@ -10,16 +10,16 @@ clean:
10
10
  find . -name __pycache__ -exec rm -rf {} + 2>/dev/null; true
11
11
 
12
12
  # --- Release: publish to PyPI ---------------------------------------------
13
- # Publish this package to PyPI, then tag py/vX.Y.Z. Run headless under the aql
13
+ # Publish this package to PyPI, then tag py/vX.Y.Z. Run headless under the boru
14
14
  # key vault, which injects the PyPI + GitHub tokens as environment variables
15
15
  # (never written to disk or argv):
16
- # aql vault exec --for=pypi=<alias> --for=github=<alias> -- make publish
17
- # aql `--dry-run` injects a filler token we detect to rehearse: builds the dist
16
+ # boru vault exec --for=pypi=<alias> --for=github=<alias> -- make publish
17
+ # boru `--dry-run` injects a filler token we detect to rehearse: builds the dist
18
18
  # but performs no upload, tag, or push. set -e + tag-last: a failed upload never
19
19
  # leaves a tag behind. Needs python `build` + `twine` (pip install build twine).
20
20
  VERSION := $(shell sed -nE 's/^version *= *"([^"]+)".*/\1/p' pyproject.toml | head -1)
21
21
  TAG := py/v$(VERSION)
22
- AQL_DRY_RUN_FILLER := AQL-DRY-RUN-FILLER-NOT-A-REAL-SECRET
22
+ BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
23
23
  publish: test
24
24
  @if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
25
25
  echo "tag $(TAG) already exists — bump pyproject.toml version first"; exit 1; fi
@@ -27,8 +27,8 @@ publish: test
27
27
  token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
28
28
  rm -rf dist; \
29
29
  python3 -m build; \
30
- if [ "$$token" = "$(AQL_DRY_RUN_FILLER)" ] || [ "$$TWINE_PASSWORD" = "$(AQL_DRY_RUN_FILLER)" ]; then \
31
- echo "[dry-run] aql filler token detected: built the dist; would upload to PyPI and tag $(TAG); nothing pushed."; \
30
+ if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ] || [ "$$TWINE_PASSWORD" = "$(BORU_DRY_RUN_FILLER)" ]; then \
31
+ echo "[dry-run] boru filler token detected: built the dist; would upload to PyPI and tag $(TAG); nothing pushed."; \
32
32
  exit 0; \
33
33
  fi; \
34
34
  python3 -m twine upload dist/*; \
@@ -1,6 +1,7 @@
1
1
  # ProjectName SDK test feature
2
2
 
3
3
  from __future__ import annotations
4
+ import re
4
5
  import random
5
6
  import time
6
7
 
@@ -8,6 +9,11 @@ from utility.voxgig_struct import voxgig_struct as vs
8
9
  from feature.base_feature import ProjectNameBaseFeature
9
10
 
10
11
 
12
+ # The `body.<key>` form of an op's response transform: the mock wraps its
13
+ # payload in <key> so the transform can unwrap it again.
14
+ ENVELOPE_RES_RE = re.compile(r"^`body\.([^`.]+)`$")
15
+
16
+
11
17
  class ProjectNameTestFeature(ProjectNameBaseFeature):
12
18
  def __init__(self):
13
19
  super().__init__()
@@ -36,11 +42,32 @@ class ProjectNameTestFeature(ProjectNameBaseFeature):
36
42
  test_self = self
37
43
 
38
44
  def test_fetcher(fctx, _fullurl, _fetchdef):
45
+ # Shape the mock payload the way the real API would, so the op's
46
+ # response transform recovers the entity from it. A point carrying
47
+ # transform.res of `body.item` describes an API that answers
48
+ # {"item": {...}}; handing back the bare entity means the transform
49
+ # unwraps a property that is not there and the caller gets None.
50
+ # The mock has to agree with the model, or it only ever simulates
51
+ # APIs whose responses happen to be unwrapped. Mirrors the ts mock.
52
+ def envelope(data):
53
+ point = getattr(fctx, "point", None)
54
+ if data is None or not isinstance(point, dict):
55
+ return data
56
+ transform = point.get("transform")
57
+ if not isinstance(transform, dict):
58
+ return data
59
+ restf = transform.get("res")
60
+ if not isinstance(restf, str):
61
+ return data
62
+ m = ENVELOPE_RES_RE.match(restf)
63
+ return data if m is None else {m.group(1): data}
64
+
39
65
  def respond(status, data, extra=None):
66
+ payload = envelope(data)
40
67
  out = {
41
68
  "status": status,
42
69
  "statusText": "OK",
43
- "json": lambda: data,
70
+ "json": lambda: payload,
44
71
  "body": "not-used",
45
72
  }
46
73
  if isinstance(extra, dict):
@@ -468,6 +468,10 @@ class TestPrimaryUtility:
468
468
  return {"status": 200, "statusText": "OK"}, None
469
469
 
470
470
  live_client = ProjectNameSDK({
471
+ # Concrete base: a live construction must satisfy any server
472
+ # variables a templated base URL declares; a literal base
473
+ # sidesteps the requirement.
474
+ "base": "http://localhost:8080",
471
475
  "system": {
472
476
  "fetch": mock_fetch,
473
477
  },
@@ -490,6 +494,7 @@ class TestPrimaryUtility:
490
494
  return {}, None
491
495
 
492
496
  blocked_client = ProjectNameSDK({
497
+ "base": "http://localhost:8080",
493
498
  "system": {
494
499
  "fetch": mock_fetch,
495
500
  },
@@ -945,28 +950,23 @@ class TestPrimaryUtility:
945
950
  _runset(_get_spec(primary, "prepareParams", "basic"), subject)
946
951
 
947
952
  def test_prepare_path_basic(self):
953
+ # Was two hand-written cases that had drifted out of the shared corpus
954
+ # (the preparePath fixture shipped as an empty `set: []`). Now driven
955
+ # by the corpus like every other section, so all ports assert the same
956
+ # separator / blank-segment behaviour.
957
+ spec = _load_test_spec()
958
+ primary = _get_spec(spec, "primary")
948
959
  client = ProjectNameSDK.test(None, None)
949
960
  utility = client._utility
950
- ctx = _make_test_full_ctx(client, utility)
951
- ctx.point = {
952
- "parts": ["api", "planet", "{id}"],
953
- "args": {"params": []},
954
- }
955
961
 
956
- path = utility.prepare_path(ctx)
957
- assert path == "api/planet/{id}"
958
-
959
- def test_prepare_path_single(self):
960
- client = ProjectNameSDK.test(None, None)
961
- utility = client._utility
962
- ctx = _make_test_full_ctx(client, utility)
963
- ctx.point = {
964
- "parts": ["items"],
965
- "args": {"params": []},
966
- }
962
+ def subject(entry):
963
+ ctxmap = entry.get("ctx")
964
+ if not isinstance(ctxmap, dict):
965
+ ctxmap = {}
966
+ ctx = _make_ctx_from_map(ctxmap, client, utility)
967
+ return utility.prepare_path(ctx), None
967
968
 
968
- path = utility.prepare_path(ctx)
969
- assert path == "items"
969
+ _runset(_get_spec(primary, "preparePath", "basic"), subject)
970
970
 
971
971
  def test_prepare_query_basic(self):
972
972
  spec = _load_test_spec()
@@ -84,6 +84,13 @@ def make_options_util(ctx):
84
84
  "clean": {
85
85
  "keys": "key,token,id",
86
86
  },
87
+ # Server-variable values for a templated base URL (OpenAPI server
88
+ # variables): {name} placeholders in "base" are substituted from this
89
+ # map at construction. Spec defaults arrive via the generated config;
90
+ # user values override them.
91
+ "server": {
92
+ "`$CHILD`": "",
93
+ },
87
94
  }
88
95
 
89
96
  # Preserve system.fetch before merge/validate.
@@ -95,6 +102,41 @@ def make_options_util(ctx):
95
102
  validated = {}
96
103
  opts = validated
97
104
 
105
+ # Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
106
+ # Every placeholder must resolve to a non-empty value: from
107
+ # options["server"] (user), else the config default. A placeholder that
108
+ # resolves to "" is a construction ERROR in live mode — the URL cannot
109
+ # work — but in test mode substitutes the deterministic value
110
+ # "test-<name>" so offline tests need no configuration.
111
+ base = opts.get("base")
112
+ if isinstance(base, str) and "{" in base:
113
+ import re as _re
114
+ testmode = (
115
+ vs.getpath(opts, "test.active") is True
116
+ or vs.getpath(opts, "feature.test.active") is True
117
+ )
118
+ server = opts.get("server") if isinstance(opts.get("server"), dict) else {}
119
+ sdkname = "SDK"
120
+ if isinstance(config, dict):
121
+ mn = vs.getpath(config, "main.name")
122
+ if isinstance(mn, str) and mn != "":
123
+ sdkname = mn
124
+
125
+ def _sub(m):
126
+ name = m.group(1)
127
+ val = server.get(name)
128
+ val = val if isinstance(val, str) else ""
129
+ if val == "":
130
+ if testmode:
131
+ return "test-" + name
132
+ raise ValueError(
133
+ sdkname + ": the server variable '" + name + "' is required: "
134
+ "the API base URL is '" + base + "' — pass "
135
+ '{"server": {"' + name + '": "..."}} in the SDK options')
136
+ return val
137
+
138
+ opts["base"] = _re.sub(r"\{([A-Za-z0-9_]+)\}", _sub, base)
139
+
98
140
  # Restore system.fetch.
99
141
  if sys_fetch is not None:
100
142
  if isinstance(opts.get("system"), dict):
@@ -13,14 +13,14 @@ clean:
13
13
 
14
14
  # --- Release: publish to RubyGems -----------------------------------------
15
15
  # Build the gem and push it to RubyGems, then tag rb/vX.Y.Z. Run headless under
16
- # the aql key vault, which injects GEM_HOST_API_KEY + the GitHub token (never on
16
+ # the boru key vault, which injects GEM_HOST_API_KEY + the GitHub token (never on
17
17
  # disk or argv):
18
- # aql vault exec --for=gem=<alias> --for=github=<alias> -- make publish
19
- # aql `--dry-run` injects a filler token we detect to rehearse (build only, no
18
+ # boru vault exec --for=gem=<alias> --for=github=<alias> -- make publish
19
+ # boru `--dry-run` injects a filler token we detect to rehearse (build only, no
20
20
  # push/tag). set -e + tag-last: a failed push never leaves a tag behind.
21
21
  VERSION := $(shell sed -nE 's/.*\.version *= *"([^"]+)".*/\1/p' *.gemspec | head -1)
22
22
  TAG := rb/v$(VERSION)
23
- AQL_DRY_RUN_FILLER := AQL-DRY-RUN-FILLER-NOT-A-REAL-SECRET
23
+ BORU_DRY_RUN_FILLER := BORU-DRY-RUN-FILLER-NOT-A-REAL-SECRET
24
24
  publish: test
25
25
  @if git rev-parse -q --verify "refs/tags/$(TAG)" >/dev/null; then \
26
26
  echo "tag $(TAG) already exists — bump the gemspec version first"; exit 1; fi
@@ -28,8 +28,8 @@ publish: test
28
28
  token="$${GITHUB_TOKEN:-$$GH_TOKEN}"; \
29
29
  rm -f *.gem; \
30
30
  gem build *.gemspec; \
31
- if [ "$$token" = "$(AQL_DRY_RUN_FILLER)" ] || [ "$$GEM_HOST_API_KEY" = "$(AQL_DRY_RUN_FILLER)" ]; then \
32
- echo "[dry-run] aql filler token detected: built the gem; would push to RubyGems and tag $(TAG); nothing pushed."; \
31
+ if [ "$$token" = "$(BORU_DRY_RUN_FILLER)" ] || [ "$$GEM_HOST_API_KEY" = "$(BORU_DRY_RUN_FILLER)" ]; then \
32
+ echo "[dry-run] boru filler token detected: built the gem; would push to RubyGems and tag $(TAG); nothing pushed."; \
33
33
  exit 0; \
34
34
  fi; \
35
35
  gem push *.gem; \
@@ -4,6 +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/
10
+
7
11
  def initialize
8
12
  super
9
13
  @version = "0.0.1"
@@ -33,11 +37,30 @@ class ProjectNameTestFeature < ProjectNameBaseFeature
33
37
  test_self = self
34
38
 
35
39
  test_fetcher = ->(fctx, _fullurl, _fetchdef) {
40
+ # Shape the mock payload the way the real API would, so the op's
41
+ # response transform recovers the entity from it. A point carrying
42
+ # transform.res of `body.item` describes an API that answers
43
+ # {"item" => {...}}; handing back the bare entity means the transform
44
+ # unwraps a property that is not there and the caller gets nil. The mock
45
+ # has to agree with the model, or it only ever simulates APIs whose
46
+ # responses happen to be unwrapped. Mirrors the ts mock.
47
+ envelope = ->(data) {
48
+ point = fctx.point
49
+ next data if data.nil? || !point.is_a?(Hash)
50
+ transform = point["transform"]
51
+ next data unless transform.is_a?(Hash)
52
+ restf = transform["res"]
53
+ next data unless restf.is_a?(String)
54
+ m = ENVELOPE_RES_RE.match(restf)
55
+ m.nil? ? data : { m[1] => data }
56
+ }
57
+
36
58
  respond = ->(status, data, extra) {
59
+ payload = envelope.call(data)
37
60
  out = {
38
61
  "status" => status,
39
62
  "statusText" => "OK",
40
- "json" => -> { data },
63
+ "json" => -> { payload },
41
64
  "body" => "not-used",
42
65
  }
43
66
  extra&.each { |k, v| out[k] = v }