@voxgig/sdkgen 1.3.17 → 1.3.18

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 (220) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Entity.js +5 -1
  3. package/dist/cmp/Entity.js.map +1 -1
  4. package/dist/cmp/Feature.js +2 -1
  5. package/dist/cmp/Feature.js.map +1 -1
  6. package/dist/helpers/canonType.js +8 -3
  7. package/dist/helpers/canonType.js.map +1 -1
  8. package/dist/helpers/collectDeps.d.ts +1 -1
  9. package/dist/helpers/collectDeps.js +36 -13
  10. package/dist/helpers/collectDeps.js.map +1 -1
  11. package/dist/helpers/naming.d.ts +3 -1
  12. package/dist/helpers/naming.js +53 -0
  13. package/dist/helpers/naming.js.map +1 -1
  14. package/dist/helpers/opShape.d.ts +3 -1
  15. package/dist/helpers/opShape.js +87 -8
  16. package/dist/helpers/opShape.js.map +1 -1
  17. package/dist/sdkgen.d.ts +3 -3
  18. package/dist/sdkgen.js +6 -2
  19. package/dist/sdkgen.js.map +1 -1
  20. package/dist/tsconfig.tsbuildinfo +1 -1
  21. package/package.json +15 -10
  22. package/project/.sdk/model/target/cpp.aontu +4 -4
  23. package/project/.sdk/model/target/go-cli.aontu +17 -17
  24. package/project/.sdk/model/target/go-mcp.aontu +11 -11
  25. package/project/.sdk/model/target/go.aontu +1 -1
  26. package/project/.sdk/model/target/java.aontu +4 -4
  27. package/project/.sdk/model/target/kotlin.aontu +4 -4
  28. package/project/.sdk/model/target/lean.aontu +51 -0
  29. package/project/.sdk/model/target/scala.aontu +4 -4
  30. package/project/.sdk/src/cmp/c/EntityTypes_c.ts +3 -4
  31. package/project/.sdk/src/cmp/c/Entity_c.ts +3 -3
  32. package/project/.sdk/src/cmp/c/ReadmeExamplesTest_c.ts +24 -7
  33. package/project/.sdk/src/cmp/c/TestDirect_c.ts +3 -3
  34. package/project/.sdk/src/cmp/c/TestEntity_c.ts +1 -1
  35. package/project/.sdk/src/cmp/clojure/Entity_clojure.ts +1 -1
  36. package/project/.sdk/src/cmp/clojure/ReadmeExamplesTest_clojure.ts +19 -8
  37. package/project/.sdk/src/cmp/cpp/EntityTypes_cpp.ts +2 -2
  38. package/project/.sdk/src/cmp/cpp/Entity_cpp.ts +3 -3
  39. package/project/.sdk/src/cmp/cpp/MainEntity_cpp.ts +2 -2
  40. package/project/.sdk/src/cmp/cpp/ReadmeExamplesTest_cpp.ts +136 -0
  41. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +3 -3
  42. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -0
  43. package/project/.sdk/src/cmp/csharp/EntityTypes_csharp.ts +2 -3
  44. package/project/.sdk/src/cmp/csharp/Entity_csharp.ts +3 -3
  45. package/project/.sdk/src/cmp/csharp/MainEntity_csharp.ts +2 -2
  46. package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
  47. package/project/.sdk/src/cmp/csharp/ReadmeExamplesTest_csharp.ts +13 -1
  48. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +3 -3
  49. package/project/.sdk/src/cmp/dart/EntityTypes_dart.ts +2 -3
  50. package/project/.sdk/src/cmp/dart/Entity_dart.ts +3 -3
  51. package/project/.sdk/src/cmp/dart/MainEntity_dart.ts +2 -2
  52. package/project/.sdk/src/cmp/dart/Main_dart.ts +3 -3
  53. package/project/.sdk/src/cmp/dart/Package_dart.ts +1 -1
  54. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +4 -4
  55. package/project/.sdk/src/cmp/elixir/EntityTypes_elixir.ts +2 -3
  56. package/project/.sdk/src/cmp/elixir/Entity_elixir.ts +1 -1
  57. package/project/.sdk/src/cmp/elixir/ReadmeExamplesTest_elixir.ts +21 -3
  58. package/project/.sdk/src/cmp/go/Config_go.ts +4 -1
  59. package/project/.sdk/src/cmp/go/EntityTypes_go.ts +110 -18
  60. package/project/.sdk/src/cmp/go/Entity_go.ts +3 -3
  61. package/project/.sdk/src/cmp/go/Main_go.ts +7 -6
  62. package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
  63. package/project/.sdk/src/cmp/go/TestDirect_go.ts +3 -3
  64. package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
  65. package/project/.sdk/src/cmp/go/utility_go.ts +25 -0
  66. package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +9 -1
  67. package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +9 -1
  68. package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +28 -18
  69. package/project/.sdk/src/cmp/java/EntityTypes_java.ts +2 -3
  70. package/project/.sdk/src/cmp/java/Entity_java.ts +3 -3
  71. package/project/.sdk/src/cmp/java/MainEntity_java.ts +2 -2
  72. package/project/.sdk/src/cmp/java/Package_java.ts +1 -1
  73. package/project/.sdk/src/cmp/java/ReadmeExamplesTest_java.ts +23 -1
  74. package/project/.sdk/src/cmp/java/TestDirect_java.ts +3 -3
  75. package/project/.sdk/src/cmp/js/EntityTypes_js.ts +2 -3
  76. package/project/.sdk/src/cmp/js/Entity_js.ts +3 -3
  77. package/project/.sdk/src/cmp/js/MainEntity_js.ts +2 -2
  78. package/project/.sdk/src/cmp/js/Main_js.ts +2 -2
  79. package/project/.sdk/src/cmp/js/TestDirect_js.ts +4 -4
  80. package/project/.sdk/src/cmp/kotlin/EntityTypes_kotlin.ts +2 -3
  81. package/project/.sdk/src/cmp/kotlin/Entity_kotlin.ts +3 -3
  82. package/project/.sdk/src/cmp/kotlin/MainEntity_kotlin.ts +2 -2
  83. package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +1 -1
  84. package/project/.sdk/src/cmp/kotlin/ReadmeExamplesTest_kotlin.ts +27 -2
  85. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +3 -3
  86. package/project/.sdk/src/cmp/lean/Config_lean.ts +88 -0
  87. package/project/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
  88. package/project/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
  89. package/project/.sdk/src/cmp/lean/Main_lean.ts +120 -0
  90. package/project/.sdk/src/cmp/lean/Package_lean.ts +68 -0
  91. package/project/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
  92. package/project/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
  93. package/project/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
  94. package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +38 -0
  95. package/project/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
  96. package/project/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
  97. package/project/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
  98. package/project/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
  99. package/project/.sdk/src/cmp/lean/ReadmeRef_lean.ts +16 -0
  100. package/project/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
  101. package/project/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
  102. package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +18 -0
  103. package/project/.sdk/src/cmp/lean/Test_lean.ts +207 -0
  104. package/project/.sdk/src/cmp/lean/utility_lean.ts +78 -0
  105. package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +2 -3
  106. package/project/.sdk/src/cmp/lua/Entity_lua.ts +3 -3
  107. package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -1
  108. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +3 -3
  109. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +1 -1
  110. package/project/.sdk/src/cmp/ocaml/ReadmeExamplesTest_ocaml.ts +29 -14
  111. package/project/.sdk/src/cmp/perl/Entity_perl.ts +3 -3
  112. package/project/.sdk/src/cmp/perl/MainEntity_perl.ts +2 -2
  113. package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
  114. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +3 -3
  115. package/project/.sdk/src/cmp/php/EntityTypes_php.ts +2 -3
  116. package/project/.sdk/src/cmp/php/Entity_php.ts +3 -3
  117. package/project/.sdk/src/cmp/php/MainEntity_php.ts +2 -2
  118. package/project/.sdk/src/cmp/php/Package_php.ts +1 -1
  119. package/project/.sdk/src/cmp/php/TestDirect_php.ts +3 -3
  120. package/project/.sdk/src/cmp/php/TestEntity_php.ts +1 -1
  121. package/project/.sdk/src/cmp/py/EntityTypes_py.ts +2 -3
  122. package/project/.sdk/src/cmp/py/Entity_py.ts +3 -3
  123. package/project/.sdk/src/cmp/py/MainEntity_py.ts +2 -2
  124. package/project/.sdk/src/cmp/py/Main_py.ts +2 -2
  125. package/project/.sdk/src/cmp/py/Package_py.ts +1 -1
  126. package/project/.sdk/src/cmp/py/TestDirect_py.ts +3 -3
  127. package/project/.sdk/src/cmp/py/TestEntity_py.ts +1 -1
  128. package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +15 -5
  129. package/project/.sdk/src/cmp/rb/Entity_rb.ts +3 -3
  130. package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +2 -2
  131. package/project/.sdk/src/cmp/rb/Package_rb.ts +2 -2
  132. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +3 -3
  133. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +1 -1
  134. package/project/.sdk/src/cmp/rust/EntityBase_rust.ts +2 -2
  135. package/project/.sdk/src/cmp/rust/EntityTypes_rust.ts +3 -4
  136. package/project/.sdk/src/cmp/rust/Entity_rust.ts +3 -3
  137. package/project/.sdk/src/cmp/rust/MainEntity_rust.ts +2 -2
  138. package/project/.sdk/src/cmp/rust/Package_rust.ts +1 -1
  139. package/project/.sdk/src/cmp/rust/ReadmeExamplesTest_rust.ts +22 -10
  140. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +3 -3
  141. package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +2 -3
  142. package/project/.sdk/src/cmp/scala/Entity_scala.ts +3 -3
  143. package/project/.sdk/src/cmp/scala/MainEntity_scala.ts +2 -2
  144. package/project/.sdk/src/cmp/scala/ReadmeExamplesTest_scala.ts +22 -3
  145. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +3 -3
  146. package/project/.sdk/src/cmp/scala/Test_scala.ts +1 -1
  147. package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +3 -4
  148. package/project/.sdk/src/cmp/swift/Entity_swift.ts +3 -3
  149. package/project/.sdk/src/cmp/swift/MainEntity_swift.ts +2 -2
  150. package/project/.sdk/src/cmp/swift/Package_swift.ts +1 -1
  151. package/project/.sdk/src/cmp/swift/ReadmeExamplesTest_swift.ts +18 -1
  152. package/project/.sdk/src/cmp/swift/TestDirect_swift.ts +1 -1
  153. package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +2 -3
  154. package/project/.sdk/src/cmp/ts/Entity_ts.ts +3 -3
  155. package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +2 -2
  156. package/project/.sdk/src/cmp/ts/Main_ts.ts +2 -2
  157. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +4 -4
  158. package/project/.sdk/src/cmp/zig/Entity_zig.ts +3 -3
  159. package/project/.sdk/src/cmp/zig/MainEntity_zig.ts +2 -2
  160. package/project/.sdk/src/cmp/zig/Package_zig.ts +10 -2
  161. package/project/.sdk/tm/cpp/test/primary_utility_test.cpp +8 -16
  162. package/project/.sdk/tm/csharp/test/PrimaryUtilityTest.cs +10 -28
  163. package/project/.sdk/tm/dart/test/primary_test.dart +7 -0
  164. package/project/.sdk/tm/go/feature/test_feature.go +33 -1
  165. package/project/.sdk/tm/go/test/primary_utility_test.go +41 -42
  166. package/project/.sdk/tm/go/test/runner_test.go +30 -1
  167. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +7 -24
  168. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +18 -1
  169. package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +8 -0
  170. package/project/.sdk/tm/kotlin/test/PrimaryUtilityTest.kt +7 -20
  171. package/project/.sdk/tm/lean/LICENSE +21 -0
  172. package/project/.sdk/tm/lean/VERSION +1 -0
  173. package/project/.sdk/tm/lean/src/SdkFeature.lean +171 -0
  174. package/project/.sdk/tm/lean/src/SdkFeatures.lean +602 -0
  175. package/project/.sdk/tm/lean/src/SdkJson.lean +134 -0
  176. package/project/.sdk/tm/lean/src/SdkRuntime.lean +356 -0
  177. package/project/.sdk/tm/lean/src/SdkUtility.lean +617 -0
  178. package/project/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
  179. package/project/.sdk/tm/lean/src/Vregex.lean +412 -0
  180. package/project/.sdk/tm/lean/src/feature/README.md +5 -0
  181. package/project/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  182. package/project/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  183. package/project/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  184. package/project/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  185. package/project/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  186. package/project/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  187. package/project/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  188. package/project/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  189. package/project/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  190. package/project/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  191. package/project/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  192. package/project/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  193. package/project/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  194. package/project/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  195. package/project/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  196. package/project/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  197. package/project/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  198. package/project/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  199. package/project/.sdk/tm/lean/test/StructCorpus.lean +697 -0
  200. package/project/.sdk/tm/lean/test/TFeature.lean +268 -0
  201. package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +349 -0
  202. package/project/.sdk/tm/lua/test/primary_utility_test.lua +10 -8
  203. package/project/.sdk/tm/perl/t/primary_utility.t +8 -17
  204. package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +9 -7
  205. package/project/.sdk/tm/py/feature/test_feature.py +28 -1
  206. package/project/.sdk/tm/py/test/test_primary_utility.py +13 -18
  207. package/project/.sdk/tm/rb/feature/test_feature.rb +24 -1
  208. package/project/.sdk/tm/rb/test/primary_utility_test.rb +7 -8
  209. package/project/.sdk/tm/rust/tests/common/mod.rs +37 -1
  210. package/project/.sdk/tm/rust/tests/primary_utility_test.rs +12 -26
  211. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +7 -8
  212. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +18 -1
  213. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +65 -21
  214. package/src/cmp/Entity.ts +5 -1
  215. package/src/cmp/Feature.ts +2 -1
  216. package/src/helpers/canonType.ts +8 -3
  217. package/src/helpers/collectDeps.ts +39 -12
  218. package/src/helpers/naming.ts +59 -0
  219. package/src/helpers/opShape.ts +99 -12
  220. package/src/sdkgen.ts +7 -3
@@ -1,6 +1,7 @@
1
1
  package sdktest
2
2
 
3
3
  import (
4
+ "fmt"
4
5
  "strings"
5
6
  "testing"
6
7
 
@@ -119,7 +120,7 @@ func TestPrimaryUtility(t *testing.T) {
119
120
  })
120
121
 
121
122
  t.Run("done-basic", func(t *testing.T) {
122
- runset(t, getSpec(primary, "done", "basic"), func(entry map[string]any) (any, error) {
123
+ runsetNamed(t, "done", getSpec(primary, "done", "basic"), func(entry map[string]any) (any, error) {
123
124
  ctxmap, _ := entry["ctx"].(map[string]any)
124
125
  ctx := makeCtxFromMap(ctxmap, client, utility)
125
126
  fixctx(ctx, client)
@@ -128,7 +129,7 @@ func TestPrimaryUtility(t *testing.T) {
128
129
  })
129
130
 
130
131
  t.Run("makeError-basic", func(t *testing.T) {
131
- runset(t, getSpec(primary, "makeError", "basic"), func(entry map[string]any) (any, error) {
132
+ runsetNamed(t, "makeError", getSpec(primary, "makeError", "basic"), func(entry map[string]any) (any, error) {
132
133
  args, _ := entry["args"].([]any)
133
134
  if len(args) == 0 {
134
135
  args = []any{map[string]any{}}
@@ -306,7 +307,7 @@ func TestPrimaryUtility(t *testing.T) {
306
307
  })
307
308
 
308
309
  t.Run("makeContext-basic", func(t *testing.T) {
309
- runset(t, getSpec(primary, "makeContext", "basic"), func(entry map[string]any) (any, error) {
310
+ runsetNamed(t, "makeContext", getSpec(primary, "makeContext", "basic"), func(entry map[string]any) (any, error) {
310
311
  in := entry["in"]
311
312
  if inMap, ok := in.(map[string]any); ok {
312
313
  ctx := utility.MakeContext(inMap, nil)
@@ -395,7 +396,7 @@ func TestPrimaryUtility(t *testing.T) {
395
396
  })
396
397
 
397
398
  t.Run("makeOptions-basic", func(t *testing.T) {
398
- runset(t, getSpec(primary, "makeOptions", "basic"), func(entry map[string]any) (any, error) {
399
+ runsetNamed(t, "makeOptions", getSpec(primary, "makeOptions", "basic"), func(entry map[string]any) (any, error) {
399
400
  in, _ := entry["in"].(map[string]any)
400
401
  ctx := utility.MakeContext(map[string]any{
401
402
  "options": in["options"],
@@ -408,7 +409,7 @@ func TestPrimaryUtility(t *testing.T) {
408
409
  })
409
410
 
410
411
  t.Run("makeRequest-basic", func(t *testing.T) {
411
- runset(t, getSpec(primary, "makeRequest", "basic"), func(entry map[string]any) (any, error) {
412
+ runsetNamed(t, "makeRequest", getSpec(primary, "makeRequest", "basic"), func(entry map[string]any) (any, error) {
412
413
  ctxmap, _ := entry["ctx"].(map[string]any)
413
414
  ctx := makeCtxFromMap(ctxmap, client, utility)
414
415
  ctx.Options = client.OptionsMap()
@@ -432,7 +433,7 @@ func TestPrimaryUtility(t *testing.T) {
432
433
  })
433
434
 
434
435
  t.Run("makeResponse-basic", func(t *testing.T) {
435
- runset(t, getSpec(primary, "makeResponse", "basic"), func(entry map[string]any) (any, error) {
436
+ runsetNamed(t, "makeResponse", getSpec(primary, "makeResponse", "basic"), func(entry map[string]any) (any, error) {
436
437
  ctxmap, _ := entry["ctx"].(map[string]any)
437
438
  ctx := makeCtxFromMap(ctxmap, client, utility)
438
439
  fixctx(ctx, client)
@@ -521,7 +522,7 @@ func TestPrimaryUtility(t *testing.T) {
521
522
  specClient := sdk.TestSDK(nil, setupOpts)
522
523
  specUtility := specClient.GetUtility()
523
524
 
524
- runset(t, getSpec(primary, "makeSpec", "basic"), func(entry map[string]any) (any, error) {
525
+ runsetNamed(t, "makeSpec", getSpec(primary, "makeSpec", "basic"), func(entry map[string]any) (any, error) {
525
526
  ctxmap, _ := entry["ctx"].(map[string]any)
526
527
  ctx := makeCtxFromMap(ctxmap, specClient, specUtility)
527
528
  ctx.Options = specClient.OptionsMap()
@@ -574,7 +575,7 @@ func TestPrimaryUtility(t *testing.T) {
574
575
  })
575
576
 
576
577
  t.Run("makeUrl-basic", func(t *testing.T) {
577
- runset(t, getSpec(primary, "makeUrl", "basic"), func(entry map[string]any) (any, error) {
578
+ runsetNamed(t, "makeUrl", getSpec(primary, "makeUrl", "basic"), func(entry map[string]any) (any, error) {
578
579
  ctxmap, _ := entry["ctx"].(map[string]any)
579
580
  ctx := makeCtxFromMap(ctxmap, client, utility)
580
581
  if ctx.Result == nil {
@@ -585,7 +586,7 @@ func TestPrimaryUtility(t *testing.T) {
585
586
  })
586
587
 
587
588
  t.Run("operator-basic", func(t *testing.T) {
588
- runset(t, getSpec(primary, "operator", "basic"), func(entry map[string]any) (any, error) {
589
+ runsetNamed(t, "operator", getSpec(primary, "operator", "basic"), func(entry map[string]any) (any, error) {
589
590
  in, _ := entry["in"].(map[string]any)
590
591
  op := sdk.NewOperation(in)
591
592
  return map[string]any{
@@ -598,7 +599,7 @@ func TestPrimaryUtility(t *testing.T) {
598
599
  })
599
600
 
600
601
  t.Run("param-basic", func(t *testing.T) {
601
- runset(t, getSpec(primary, "param", "basic"), func(entry map[string]any) (any, error) {
602
+ runsetNamed(t, "param", getSpec(primary, "param", "basic"), func(entry map[string]any) (any, error) {
602
603
  args, _ := entry["args"].([]any)
603
604
  if len(args) < 2 {
604
605
  return nil, nil
@@ -647,7 +648,7 @@ func TestPrimaryUtility(t *testing.T) {
647
648
  authClient := sdk.TestSDK(nil, setupOpts)
648
649
  authUtility := authClient.GetUtility()
649
650
 
650
- runset(t, getSpec(primary, "prepareAuth", "basic"), func(entry map[string]any) (any, error) {
651
+ runsetNamed(t, "prepareAuth", getSpec(primary, "prepareAuth", "basic"), func(entry map[string]any) (any, error) {
651
652
  ctxmap, _ := entry["ctx"].(map[string]any)
652
653
  ctx := makeCtxFromMap(ctxmap, authClient, authUtility)
653
654
  fixctx(ctx, authClient)
@@ -670,7 +671,7 @@ func TestPrimaryUtility(t *testing.T) {
670
671
  })
671
672
 
672
673
  t.Run("prepareBody-basic", func(t *testing.T) {
673
- runset(t, getSpec(primary, "prepareBody", "basic"), func(entry map[string]any) (any, error) {
674
+ runsetNamed(t, "prepareBody", getSpec(primary, "prepareBody", "basic"), func(entry map[string]any) (any, error) {
674
675
  ctxmap, _ := entry["ctx"].(map[string]any)
675
676
  ctx := makeCtxFromMap(ctxmap, client, utility)
676
677
  fixctx(ctx, client)
@@ -679,7 +680,7 @@ func TestPrimaryUtility(t *testing.T) {
679
680
  })
680
681
 
681
682
  t.Run("prepareHeaders-basic", func(t *testing.T) {
682
- runset(t, getSpec(primary, "prepareHeaders", "basic"), func(entry map[string]any) (any, error) {
683
+ runsetNamed(t, "prepareHeaders", getSpec(primary, "prepareHeaders", "basic"), func(entry map[string]any) (any, error) {
683
684
  ctxmap, _ := entry["ctx"].(map[string]any)
684
685
  ctx := makeCtxFromMap(ctxmap, client, utility)
685
686
  return utility.PrepareHeaders(ctx), nil
@@ -687,7 +688,7 @@ func TestPrimaryUtility(t *testing.T) {
687
688
  })
688
689
 
689
690
  t.Run("prepareMethod-basic", func(t *testing.T) {
690
- runset(t, getSpec(primary, "prepareMethod", "basic"), func(entry map[string]any) (any, error) {
691
+ runsetNamed(t, "prepareMethod", getSpec(primary, "prepareMethod", "basic"), func(entry map[string]any) (any, error) {
691
692
  ctxmap, _ := entry["ctx"].(map[string]any)
692
693
  ctx := makeCtxFromMap(ctxmap, client, utility)
693
694
  return utility.PrepareMethod(ctx), nil
@@ -695,41 +696,39 @@ func TestPrimaryUtility(t *testing.T) {
695
696
  })
696
697
 
697
698
  t.Run("prepareParams-basic", func(t *testing.T) {
698
- runset(t, getSpec(primary, "prepareParams", "basic"), func(entry map[string]any) (any, error) {
699
+ runsetNamed(t, "prepareParams", getSpec(primary, "prepareParams", "basic"), func(entry map[string]any) (any, error) {
699
700
  ctxmap, _ := entry["ctx"].(map[string]any)
700
701
  ctx := makeCtxFromMap(ctxmap, client, utility)
701
702
  return utility.PrepareParams(ctx), nil
702
703
  })
703
704
  })
704
705
 
706
+ // Was two hand-written cases that had drifted out of the shared corpus
707
+ // (the preparePath fixture shipped as an empty `set: []`). Now driven by
708
+ // the corpus like every other section, so all ports assert the same
709
+ // separator/blank-segment behaviour.
705
710
  t.Run("preparePath-basic", func(t *testing.T) {
706
- ctx := makeTestFullCtx(client, utility)
707
- ctx.Point = map[string]any{
708
- "parts": []any{"api", "planet", "{id}"},
709
- "args": map[string]any{"params": []any{}},
710
- }
711
-
712
- path := utility.PreparePath(ctx)
713
- if path != "api/planet/{id}" {
714
- t.Errorf("expected api/planet/{id}, got %s", path)
715
- }
711
+ runsetNamed(t, "preparePath", getSpec(primary, "preparePath", "basic"), func(entry map[string]any) (any, error) {
712
+ ctxmap, _ := entry["ctx"].(map[string]any)
713
+ ctx := makeCtxFromMap(ctxmap, client, utility)
714
+ return utility.PreparePath(ctx), nil
715
+ })
716
716
  })
717
717
 
718
- t.Run("preparePath-single", func(t *testing.T) {
719
- ctx := makeTestFullCtx(client, utility)
720
- ctx.Point = map[string]any{
721
- "parts": []any{"items"},
722
- "args": map[string]any{"params": []any{}},
723
- }
724
-
725
- path := utility.PreparePath(ctx)
726
- if path != "items" {
727
- t.Errorf("expected items, got %s", path)
728
- }
718
+ t.Run("clean-corpus", func(t *testing.T) {
719
+ runsetNamed(t, "clean", getSpec(primary, "clean", "basic"), func(entry map[string]any) (any, error) {
720
+ args, _ := entry["args"].([]any)
721
+ if 2 != len(args) {
722
+ return nil, fmt.Errorf("clean: expected 2 args, got %d", len(args))
723
+ }
724
+ ctxmap, _ := args[0].(map[string]any)
725
+ ctx := makeCtxFromMap(ctxmap, client, utility)
726
+ return utility.Clean(ctx, args[1]), nil
727
+ })
729
728
  })
730
729
 
731
730
  t.Run("prepareQuery-basic", func(t *testing.T) {
732
- runset(t, getSpec(primary, "prepareQuery", "basic"), func(entry map[string]any) (any, error) {
731
+ runsetNamed(t, "prepareQuery", getSpec(primary, "prepareQuery", "basic"), func(entry map[string]any) (any, error) {
733
732
  ctxmap, _ := entry["ctx"].(map[string]any)
734
733
  ctx := makeCtxFromMap(ctxmap, client, utility)
735
734
  return utility.PrepareQuery(ctx), nil
@@ -737,7 +736,7 @@ func TestPrimaryUtility(t *testing.T) {
737
736
  })
738
737
 
739
738
  t.Run("resultBasic-basic", func(t *testing.T) {
740
- runset(t, getSpec(primary, "resultBasic", "basic"), func(entry map[string]any) (any, error) {
739
+ runsetNamed(t, "resultBasic", getSpec(primary, "resultBasic", "basic"), func(entry map[string]any) (any, error) {
741
740
  ctxmap, _ := entry["ctx"].(map[string]any)
742
741
  ctx := makeCtxFromMap(ctxmap, client, utility)
743
742
  fixctx(ctx, client)
@@ -759,7 +758,7 @@ func TestPrimaryUtility(t *testing.T) {
759
758
  })
760
759
 
761
760
  t.Run("resultBody-basic", func(t *testing.T) {
762
- runset(t, getSpec(primary, "resultBody", "basic"), func(entry map[string]any) (any, error) {
761
+ runsetNamed(t, "resultBody", getSpec(primary, "resultBody", "basic"), func(entry map[string]any) (any, error) {
763
762
  ctxmap, _ := entry["ctx"].(map[string]any)
764
763
  ctx := makeCtxFromMap(ctxmap, client, utility)
765
764
 
@@ -778,7 +777,7 @@ func TestPrimaryUtility(t *testing.T) {
778
777
  })
779
778
 
780
779
  t.Run("resultHeaders-basic", func(t *testing.T) {
781
- runset(t, getSpec(primary, "resultHeaders", "basic"), func(entry map[string]any) (any, error) {
780
+ runsetNamed(t, "resultHeaders", getSpec(primary, "resultHeaders", "basic"), func(entry map[string]any) (any, error) {
782
781
  ctxmap, _ := entry["ctx"].(map[string]any)
783
782
  ctx := makeCtxFromMap(ctxmap, client, utility)
784
783
 
@@ -797,7 +796,7 @@ func TestPrimaryUtility(t *testing.T) {
797
796
  })
798
797
 
799
798
  t.Run("transformRequest-basic", func(t *testing.T) {
800
- runset(t, getSpec(primary, "transformRequest", "basic"), func(entry map[string]any) (any, error) {
799
+ runsetNamed(t, "transformRequest", getSpec(primary, "transformRequest", "basic"), func(entry map[string]any) (any, error) {
801
800
  ctxmap, _ := entry["ctx"].(map[string]any)
802
801
  ctx := makeCtxFromMap(ctxmap, client, utility)
803
802
 
@@ -816,7 +815,7 @@ func TestPrimaryUtility(t *testing.T) {
816
815
  })
817
816
 
818
817
  t.Run("transformResponse-basic", func(t *testing.T) {
819
- runset(t, getSpec(primary, "transformResponse", "basic"), func(entry map[string]any) (any, error) {
818
+ runsetNamed(t, "transformResponse", getSpec(primary, "transformResponse", "basic"), func(entry map[string]any) (any, error) {
820
819
  ctxmap, _ := entry["ctx"].(map[string]any)
821
820
  ctx := makeCtxFromMap(ctxmap, client, utility)
822
821
 
@@ -215,11 +215,40 @@ func getSpec(spec map[string]any, keys ...string) map[string]any {
215
215
 
216
216
  type RunSubject func(entry map[string]any) (any, error)
217
217
 
218
+ // PENDING sections are the ones deliberately left empty in the shared corpus
219
+ // (.sdk/test/primary/<name>.aontu). Everything else MUST contribute cases.
220
+ var pendingSections = map[string]bool{
221
+ "fetcher": true, "makeFetchDef": true, "makePoint": true, "makeResult": true,
222
+ "featureAdd": true, "featureHook": true, "featureInit": true,
223
+ }
224
+
225
+ // runset drives one section of the shared test corpus.
226
+ //
227
+ // It used to `return` silently when "set" was missing or not a list, so a
228
+ // renamed section, a fixture that failed to compile, or an empty set reported
229
+ // PASS while running zero assertions — the whole point of a shared oracle
230
+ // lost without a single red test. Both conditions are now fatal, except for
231
+ // the sections explicitly marked PENDING in the corpus.
218
232
  func runset(t *testing.T, testspec map[string]any, subject RunSubject) {
219
233
  t.Helper()
234
+ runsetNamed(t, "", testspec, subject)
235
+ }
236
+
237
+ func runsetNamed(t *testing.T, name string, testspec map[string]any, subject RunSubject) {
238
+ t.Helper()
239
+
240
+ if testspec == nil {
241
+ t.Fatalf("test corpus section %q missing — check the name against .sdk/test/primary/", name)
242
+ }
243
+
220
244
  set, ok := testspec["set"].([]any)
221
245
  if !ok {
222
- return
246
+ t.Fatalf("test corpus section %q has no `set` list — zero cases would run", name)
247
+ }
248
+
249
+ if 0 == len(set) && !pendingSections[name] {
250
+ t.Fatalf("test corpus section %q is EMPTY — zero cases would run; "+
251
+ "add cases, or mark the fixture PENDING in .sdk/test/primary/", name)
223
252
  }
224
253
 
225
254
  for i, e := range set {
@@ -717,32 +717,15 @@ public class PrimaryUtilityTest {
717
717
 
718
718
  @Test
719
719
  public void preparePathBasic() {
720
+ // Was two hand-written cases that had drifted out of the shared corpus
721
+ // (the preparePath fixture shipped as an empty `set: []`).
720
722
  ProjectNameSDK client = client();
721
723
  Utility utility = client.getUtility();
722
- Context ctx = makeTestFullCtx(client, utility);
723
- List<Object> parts = new ArrayList<>();
724
- parts.add("api");
725
- parts.add("planet");
726
- parts.add("{id}");
727
- ctx.point = fhMap(
728
- "parts", parts,
729
- "args", fhMap("params", new ArrayList<>()));
730
-
731
- assertEquals("api/planet/{id}", utility.preparePath.apply(ctx));
732
- }
733
-
734
- @Test
735
- public void preparePathSingle() {
736
- ProjectNameSDK client = client();
737
- Utility utility = client.getUtility();
738
- Context ctx = makeTestFullCtx(client, utility);
739
- List<Object> parts = new ArrayList<>();
740
- parts.add("items");
741
- ctx.point = fhMap(
742
- "parts", parts,
743
- "args", fhMap("params", new ArrayList<>()));
744
-
745
- assertEquals("items", utility.preparePath.apply(ctx));
724
+ runset(getSpec(primary(), "preparePath", "basic"), (entry) -> {
725
+ Map<String, Object> ctxmap = Helpers.toMapAny(entry.get("ctx"));
726
+ Context ctx = makeCtxFromMap(ctxmap, client, utility);
727
+ return utility.preparePath.apply(ctx);
728
+ });
746
729
  }
747
730
 
748
731
  @Test
@@ -49,12 +49,29 @@ class TestFeature extends BaseFeature {
49
49
  const delprop = struct.delprop
50
50
  const getdef = struct.getdef
51
51
 
52
+ // Shape the mock payload the way the real API would, so the op's
53
+ // response transform recovers the entity from it. A point carrying
54
+ // `transform.res: \`body.item\`` describes an API that answers
55
+ // `{item: {...}}`; handing back the bare entity means the transform
56
+ // unwraps a property that is not there and the caller gets undefined.
57
+ // The mock has to agree with the model, or it only ever simulates APIs
58
+ // whose responses happen to be unwrapped.
59
+ function envelope(data) {
60
+ const restf = getprop(getprop(ctx.point, 'transform', {}), 'res')
61
+ if (null == data || 'string' !== typeof restf) {
62
+ return data
63
+ }
64
+ const m = restf.match(/^`body\.([^`.]+)`$/)
65
+ return null == m ? data : { [m[1]]: data }
66
+ }
67
+
52
68
  function respond(status, data, res) {
69
+ const payload = envelope(data)
53
70
  const out = merge([
54
71
  {
55
72
  status,
56
73
  statusText: 'OK',
57
- json: async () => data,
74
+ json: async () => payload,
58
75
  body: 'not-used',
59
76
  },
60
77
  getdef(res, {})
@@ -96,6 +96,14 @@ describe('PrimaryUtility', async () => {
96
96
  })
97
97
 
98
98
 
99
+ // preparePath shipped as an empty `set: []`, so no port drove it and several
100
+ // kept private hand-written cases instead. Corpus-driven like every other
101
+ // section now.
102
+ test('path-basic', async () => {
103
+ await runset(spec.preparePath.basic, utility.preparePath)
104
+ })
105
+
106
+
99
107
  test('body-basic', async () => {
100
108
  await runset(spec.prepareBody.basic, (ctx) => {
101
109
  fixctx(ctx)
@@ -694,28 +694,15 @@ class PrimaryUtilityTest {
694
694
 
695
695
  @Test
696
696
  fun preparePathBasic() {
697
+ // Was two hand-written cases that had drifted out of the shared corpus
698
+ // (the preparePath fixture shipped as an empty `set: []`).
697
699
  val client = client()
698
700
  val utility = client.getUtility()
699
- val ctx = makeTestFullCtx(client, utility)
700
- val parts = mutableListOf<Any?>()
701
- parts.add("api")
702
- parts.add("planet")
703
- parts.add("{id}")
704
- ctx.point = fhMap("parts", parts, "args", fhMap("params", mutableListOf<Any?>()))
705
-
706
- assertEquals("api/planet/{id}", utility.preparePath(ctx))
707
- }
708
-
709
- @Test
710
- fun preparePathSingle() {
711
- val client = client()
712
- val utility = client.getUtility()
713
- val ctx = makeTestFullCtx(client, utility)
714
- val parts = mutableListOf<Any?>()
715
- parts.add("items")
716
- ctx.point = fhMap("parts", parts, "args", fhMap("params", mutableListOf<Any?>()))
717
-
718
- assertEquals("items", utility.preparePath(ctx))
701
+ runset(getSpec(primary(), "preparePath", "basic")) { entry ->
702
+ val ctxmap = Helpers.toMapAny(entry["ctx"])
703
+ val ctx = makeCtxFromMap(ctxmap, client, utility)
704
+ utility.preparePath(ctx)
705
+ }
719
706
  }
720
707
 
721
708
  @Test
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 voxgig
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,171 @@
1
+ /- ProjectName SDK feature catalog.
2
+
3
+ A feature observes and modifies the operation pipeline in two ways:
4
+
5
+ - by WRAPPING THE TRANSPORT: `init` replaces the client's fetcher with one
6
+ that calls the previous fetcher (retry, timeout, ratelimit, cache, proxy,
7
+ netsim, test all do this), so features compose as a middleware chain in
8
+ add order;
9
+ - by HANDLING A HOOK: a pipeline stage (PrePoint, PreSpec, PreRequest,
10
+ PreResponse, PreResult, PreDone, PreUnexpected) dispatched to every active
11
+ feature.
12
+
13
+ Lean cannot put closures inside the struct `Value` model, so features and
14
+ fetchers live in module-level registries and the client `Value` carries their
15
+ indices. Timing uses the monotonic clock; where the donors use randomness we
16
+ use a deterministic counter, which keeps generated suites reproducible. -/
17
+
18
+ import VoxgigStruct
19
+ import SdkJson
20
+ import SdkUtility
21
+
22
+ open VoxgigStruct
23
+
24
+ namespace SdkFeature
25
+
26
+ /-- Transport: ctx -> url -> fetchdef -> (response, error). -/
27
+ abbrev Fetcher := Value → String → Value → SIO (Value × Option Value)
28
+
29
+ structure Feature where
30
+ name : String
31
+ version : String := "0.0.1"
32
+ /-- init ctx options -/
33
+ init : Value → Value → SIO Unit := fun _ _ => pure ()
34
+ /-- hook stage ctx -/
35
+ hook : String → Value → SIO Unit := fun _ _ => pure ()
36
+
37
+ initialize featureReg : IO.Ref (Array Feature) ← IO.mkRef #[]
38
+ initialize fetcherReg : IO.Ref (Array Fetcher) ← IO.mkRef #[]
39
+ initialize seqRef : IO.Ref Nat ← IO.mkRef 0
40
+
41
+ def nextSeq : SIO Nat := do seqRef.modifyGet fun n => (n + 1, n + 1)
42
+
43
+ def nowMs : SIO Float := do pure (← IO.monoMsNow).toFloat
44
+
45
+ def sleepMs (ms : Float) : SIO Unit := do
46
+ if ms > 0.0 then IO.sleep (ms.toUInt32) else pure ()
47
+
48
+ /-- Deterministic id (no RNG: reproducible suites). -/
49
+ def genId : SIO String := do pure ("k" ++ toString (← nextSeq))
50
+
51
+ -- ---------------------------------------------------------------------------
52
+ -- option readers
53
+ -- ---------------------------------------------------------------------------
54
+
55
+ def optActive (opts : Value) : SIO Bool := do
56
+ match (← SdkUtility.gp opts "active") with
57
+ | .bool b => pure b
58
+ | _ => pure false
59
+
60
+ def optNum (opts : Value) (k : String) (d : Float) : SIO Float := do
61
+ match (← SdkUtility.gp opts k) with
62
+ | .num n => pure n
63
+ | _ => pure d
64
+
65
+ def optInt (opts : Value) (k : String) (d : Int) : SIO Int := do
66
+ match (← SdkUtility.gp opts k) with
67
+ | .num n => pure (fToInt n)
68
+ | _ => pure d
69
+
70
+ def optStr (opts : Value) (k d : String) : SIO String := do
71
+ let s ← SdkUtility.gpS opts k
72
+ pure (if s == "" then d else s)
73
+
74
+ def optStrList (opts : Value) (k : String) (d : Array String) : SIO (Array String) := do
75
+ match (← SdkUtility.gp opts k) with
76
+ | .list i => do
77
+ let its ← listItems i
78
+ pure (its.map SdkUtility.vs)
79
+ | _ => pure d
80
+
81
+ /-- The feature's own options map (the client passes `{active, ...}`). -/
82
+ def toOptsMap (opts : Value) : SIO Value := do
83
+ match opts with
84
+ | .map i => pure (.map i)
85
+ | _ => emptyMap
86
+
87
+ def upper (s : String) : String := s.toUpper
88
+ def lower (s : String) : String := s.toLower
89
+
90
+ /-- Case-insensitive header lookup. -/
91
+ def headerCI (headers : Value) (name : String) : SIO Value := do
92
+ match headers with
93
+ | .map i => do
94
+ let want := lower name
95
+ for k in (← keysof (.map i)) do
96
+ if lower k == want then return (← SdkUtility.gp (.map i) k)
97
+ pure .noval
98
+ | _ => pure .noval
99
+
100
+ -- ---------------------------------------------------------------------------
101
+ -- client wiring: fetcher chain + tracking buckets
102
+ -- ---------------------------------------------------------------------------
103
+
104
+ def registerFetcher (f : Fetcher) : SIO Nat := do
105
+ fetcherReg.modifyGet fun a => (a.size, a.push f)
106
+
107
+ def getFetcher (client : Value) : SIO Fetcher := do
108
+ match (← SdkUtility.gp client "fetcher") with
109
+ | .num n => do
110
+ let reg ← fetcherReg.get
111
+ pure (reg[fToInt n |>.toNat]!)
112
+ | _ => pure (fun _ _ _ => do pure (.noval, none))
113
+
114
+ def setFetcher (client : Value) (f : Fetcher) : SIO Unit := do
115
+ let id ← registerFetcher f
116
+ SdkUtility.sp client "fetcher" (.num id.toFloat)
117
+
118
+ /-- The client of a ctx. -/
119
+ def clientOf (ctx : Value) : SIO Value := do SdkUtility.gp ctx "client"
120
+
121
+ /-- A named metrics bucket on the client's `track` map. -/
122
+ def trackBucket (client : Value) (name : String) (mk : SIO Value) : SIO Value := do
123
+ let track ← SdkUtility.gpMap client "track"
124
+ match (← SdkUtility.gp track name) with
125
+ | .map i => pure (.map i)
126
+ | _ => do
127
+ let b ← mk
128
+ SdkUtility.sp track name b
129
+ pure b
130
+
131
+ def bumpNum (bucket : Value) (k : String) (n : Float) : SIO Unit := do
132
+ let cur ← SdkUtility.gp bucket k
133
+ let v := (match cur with | .num x => x | _ => 0.0) + n
134
+ SdkUtility.sp bucket k (.num v)
135
+
136
+ def trackCtx (ctx : Value) (name : String) (mk : SIO Value) : SIO Value := do
137
+ trackBucket (← clientOf ctx) name mk
138
+
139
+ -- ---------------------------------------------------------------------------
140
+ -- feature registry / dispatch
141
+ -- ---------------------------------------------------------------------------
142
+
143
+ def registerFeature (f : Feature) : SIO Nat := do
144
+ featureReg.modifyGet fun a => (a.size, a.push f)
145
+
146
+ def clientFeatures (client : Value) : SIO (Array (Nat × Feature)) := do
147
+ let reg ← featureReg.get
148
+ let mut out : Array (Nat × Feature) := #[]
149
+ match (← SdkUtility.gp client "features") with
150
+ | .list i =>
151
+ for fv in (← listItems i) do
152
+ match fv with
153
+ | .num n => do
154
+ let idx := fToInt n |>.toNat
155
+ if h : idx < reg.size then out := out.push (idx, reg[idx])
156
+ | _ => pure ()
157
+ | _ => pure ()
158
+ pure out
159
+
160
+ /-- Is the named feature active on this client? -/
161
+ def isActive (client : Value) (name : String) : SIO Bool := do
162
+ let fo ← SdkUtility.gp (← SdkUtility.gp client "featureopts") name
163
+ optActive fo
164
+
165
+ /-- Dispatch a pipeline stage to every active feature. -/
166
+ def dispatch (client : Value) (stage : String) (ctx : Value) : SIO Unit := do
167
+ for (_, f) in (← clientFeatures client) do
168
+ if (← isActive client f.name) then
169
+ f.hook stage ctx
170
+
171
+ end SdkFeature