@voxgig/sdkgen-langpack 0.0.1

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 (300) hide show
  1. package/.sdk/model/target/dart.aon +63 -0
  2. package/.sdk/model/target/haskell.aon +72 -0
  3. package/.sdk/model/target/lean.aon +91 -0
  4. package/.sdk/src/cmp/dart/Config_dart.ts +248 -0
  5. package/.sdk/src/cmp/dart/EntityBase_dart.ts +34 -0
  6. package/.sdk/src/cmp/dart/EntityOperation_dart.ts +43 -0
  7. package/.sdk/src/cmp/dart/EntityTypes_dart.ts +207 -0
  8. package/.sdk/src/cmp/dart/Entity_dart.ts +87 -0
  9. package/.sdk/src/cmp/dart/Gitignore_dart.ts +34 -0
  10. package/.sdk/src/cmp/dart/MainEntity_dart.ts +33 -0
  11. package/.sdk/src/cmp/dart/Main_dart.ts +178 -0
  12. package/.sdk/src/cmp/dart/Package_dart.ts +82 -0
  13. package/.sdk/src/cmp/dart/ReadmeEntity_dart.ts +177 -0
  14. package/.sdk/src/cmp/dart/ReadmeExamplesTest_dart.ts +238 -0
  15. package/.sdk/src/cmp/dart/ReadmeExplanation_dart.ts +53 -0
  16. package/.sdk/src/cmp/dart/ReadmeHowto_dart.ts +152 -0
  17. package/.sdk/src/cmp/dart/ReadmeInstall_dart.ts +59 -0
  18. package/.sdk/src/cmp/dart/ReadmeIntro_dart.ts +65 -0
  19. package/.sdk/src/cmp/dart/ReadmeModel_dart.ts +151 -0
  20. package/.sdk/src/cmp/dart/ReadmeOptions_dart.ts +63 -0
  21. package/.sdk/src/cmp/dart/ReadmeQuick_dart.ts +225 -0
  22. package/.sdk/src/cmp/dart/ReadmeRef_dart.ts +397 -0
  23. package/.sdk/src/cmp/dart/ReadmeTopHowto_dart.ts +24 -0
  24. package/.sdk/src/cmp/dart/ReadmeTopQuick_dart.ts +106 -0
  25. package/.sdk/src/cmp/dart/ReadmeTopTest_dart.ts +78 -0
  26. package/.sdk/src/cmp/dart/SdkError_dart.ts +42 -0
  27. package/.sdk/src/cmp/dart/TestDirect_dart.ts +516 -0
  28. package/.sdk/src/cmp/dart/TestEntity_dart.ts +628 -0
  29. package/.sdk/src/cmp/dart/Test_dart.ts +138 -0
  30. package/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +98 -0
  31. package/.sdk/src/cmp/dart/fragment/Config.fragment.dart +70 -0
  32. package/.sdk/src/cmp/dart/fragment/Direct.test.fragment.dart +25 -0
  33. package/.sdk/src/cmp/dart/fragment/Entity.fragment.dart +29 -0
  34. package/.sdk/src/cmp/dart/fragment/Entity.test.fragment.dart +28 -0
  35. package/.sdk/src/cmp/dart/fragment/EntityBase.fragment.dart +302 -0
  36. package/.sdk/src/cmp/dart/fragment/EntityCreateOp.fragment.dart +98 -0
  37. package/.sdk/src/cmp/dart/fragment/EntityListOp.fragment.dart +90 -0
  38. package/.sdk/src/cmp/dart/fragment/EntityLoadOp.fragment.dart +102 -0
  39. package/.sdk/src/cmp/dart/fragment/EntityRemoveOp.fragment.dart +108 -0
  40. package/.sdk/src/cmp/dart/fragment/EntityUpdateOp.fragment.dart +102 -0
  41. package/.sdk/src/cmp/dart/fragment/Main.fragment.dart +339 -0
  42. package/.sdk/src/cmp/dart/fragment/SdkError.fragment.dart +26 -0
  43. package/.sdk/src/cmp/dart/tsconfig.json +15 -0
  44. package/.sdk/src/cmp/dart/utility_dart.ts +155 -0
  45. package/.sdk/src/cmp/haskell/Config_haskell.ts +98 -0
  46. package/.sdk/src/cmp/haskell/Entity_haskell.ts +13 -0
  47. package/.sdk/src/cmp/haskell/Gitignore_haskell.ts +27 -0
  48. package/.sdk/src/cmp/haskell/Main_haskell.ts +123 -0
  49. package/.sdk/src/cmp/haskell/Package_haskell.ts +60 -0
  50. package/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +186 -0
  51. package/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +135 -0
  52. package/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +48 -0
  53. package/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +168 -0
  54. package/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +51 -0
  55. package/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +67 -0
  56. package/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +162 -0
  57. package/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +74 -0
  58. package/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +241 -0
  59. package/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +408 -0
  60. package/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +31 -0
  61. package/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +104 -0
  62. package/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +80 -0
  63. package/.sdk/src/cmp/haskell/Test_haskell.ts +297 -0
  64. package/.sdk/src/cmp/haskell/tsconfig.json +15 -0
  65. package/.sdk/src/cmp/haskell/utility_haskell.ts +166 -0
  66. package/.sdk/src/cmp/lean/Config_lean.ts +93 -0
  67. package/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
  68. package/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
  69. package/.sdk/src/cmp/lean/Main_lean.ts +120 -0
  70. package/.sdk/src/cmp/lean/Package_lean.ts +93 -0
  71. package/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
  72. package/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
  73. package/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
  74. package/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +39 -0
  75. package/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
  76. package/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
  77. package/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
  78. package/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
  79. package/.sdk/src/cmp/lean/ReadmeRef_lean.ts +40 -0
  80. package/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
  81. package/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
  82. package/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +19 -0
  83. package/.sdk/src/cmp/lean/Test_lean.ts +261 -0
  84. package/.sdk/src/cmp/lean/utility_lean.ts +129 -0
  85. package/.sdk/tm/dart/LICENSE +22 -0
  86. package/.sdk/tm/dart/Makefile +50 -0
  87. package/.sdk/tm/dart/lib/Context.dart +150 -0
  88. package/.sdk/tm/dart/lib/Control.dart +15 -0
  89. package/.sdk/tm/dart/lib/Operation.dart +31 -0
  90. package/.sdk/tm/dart/lib/Point.dart +51 -0
  91. package/.sdk/tm/dart/lib/Response.dart +32 -0
  92. package/.sdk/tm/dart/lib/Result.dart +39 -0
  93. package/.sdk/tm/dart/lib/Spec.dart +49 -0
  94. package/.sdk/tm/dart/lib/feature/audit/AuditFeature.dart +103 -0
  95. package/.sdk/tm/dart/lib/feature/base/BaseFeature.dart +76 -0
  96. package/.sdk/tm/dart/lib/feature/cache/CacheFeature.dart +152 -0
  97. package/.sdk/tm/dart/lib/feature/clienttrack/ClienttrackFeature.dart +104 -0
  98. package/.sdk/tm/dart/lib/feature/cost/CostFeature.dart +446 -0
  99. package/.sdk/tm/dart/lib/feature/debug/DebugFeature.dart +153 -0
  100. package/.sdk/tm/dart/lib/feature/idempotency/IdempotencyFeature.dart +92 -0
  101. package/.sdk/tm/dart/lib/feature/log/LogFeature.dart +85 -0
  102. package/.sdk/tm/dart/lib/feature/metrics/MetricsFeature.dart +103 -0
  103. package/.sdk/tm/dart/lib/feature/netsim/NetsimFeature.dart +186 -0
  104. package/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +240 -0
  105. package/.sdk/tm/dart/lib/feature/proxy/ProxyFeature.dart +118 -0
  106. package/.sdk/tm/dart/lib/feature/ratelimit/RatelimitFeature.dart +103 -0
  107. package/.sdk/tm/dart/lib/feature/rbac/RbacFeature.dart +121 -0
  108. package/.sdk/tm/dart/lib/feature/retry/RetryFeature.dart +155 -0
  109. package/.sdk/tm/dart/lib/feature/secrets/SecretsFeature.dart +649 -0
  110. package/.sdk/tm/dart/lib/feature/secrets/plugin/capability.dart +105 -0
  111. package/.sdk/tm/dart/lib/feature/secrets/plugin/catalog.dart +46 -0
  112. package/.sdk/tm/dart/lib/feature/secrets/plugin/config.dart +297 -0
  113. package/.sdk/tm/dart/lib/feature/secrets/plugin/depend.dart +198 -0
  114. package/.sdk/tm/dart/lib/feature/secrets/plugin/env.dart +144 -0
  115. package/.sdk/tm/dart/lib/feature/secrets/plugin/export.dart +58 -0
  116. package/.sdk/tm/dart/lib/feature/secrets/plugin/graph.dart +160 -0
  117. package/.sdk/tm/dart/lib/feature/secrets/plugin/host.dart +1067 -0
  118. package/.sdk/tm/dart/lib/feature/secrets/plugin/order.dart +172 -0
  119. package/.sdk/tm/dart/lib/feature/secrets/plugin/plugin.dart +23 -0
  120. package/.sdk/tm/dart/lib/feature/secrets/plugin/point.dart +117 -0
  121. package/.sdk/tm/dart/lib/feature/secrets/plugin/ref.dart +105 -0
  122. package/.sdk/tm/dart/lib/feature/secrets/plugin/resolve.dart +58 -0
  123. package/.sdk/tm/dart/lib/feature/secrets/plugin/types.dart +201 -0
  124. package/.sdk/tm/dart/lib/feature/secrets/plugin/version.dart +116 -0
  125. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/aws.dart +285 -0
  126. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/azuresecrets.dart +178 -0
  127. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/boru.dart +145 -0
  128. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/crypto.dart +194 -0
  129. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/doppler.dart +101 -0
  130. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/gcpsecrets.dart +143 -0
  131. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/hashicorp.dart +190 -0
  132. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/httpjson.dart +363 -0
  133. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/infisical.dart +149 -0
  134. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/onepassword.dart +146 -0
  135. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/secretspec.dart +116 -0
  136. package/.sdk/tm/dart/lib/feature/secrets/sekreto/plugins/sigv4.dart +237 -0
  137. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/addr.dart +138 -0
  138. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/json.dart +276 -0
  139. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/provider.dart +31 -0
  140. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/providers.dart +243 -0
  141. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/sekreto.dart +730 -0
  142. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/spec.dart +200 -0
  143. package/.sdk/tm/dart/lib/feature/secrets/sekreto/src/support.dart +217 -0
  144. package/.sdk/tm/dart/lib/feature/streaming/StreamingFeature.dart +97 -0
  145. package/.sdk/tm/dart/lib/feature/telemetry/TelemetryFeature.dart +136 -0
  146. package/.sdk/tm/dart/lib/feature/test/TestFeature.dart +328 -0
  147. package/.sdk/tm/dart/lib/feature/timeout/TimeoutFeature.dart +104 -0
  148. package/.sdk/tm/dart/lib/utility/CleanUtility.dart +12 -0
  149. package/.sdk/tm/dart/lib/utility/DoneUtility.dart +18 -0
  150. package/.sdk/tm/dart/lib/utility/ErrUtility.dart +46 -0
  151. package/.sdk/tm/dart/lib/utility/FeatureAddUtility.dart +35 -0
  152. package/.sdk/tm/dart/lib/utility/FeatureHookUtility.dart +20 -0
  153. package/.sdk/tm/dart/lib/utility/FeatureInitUtility.dart +9 -0
  154. package/.sdk/tm/dart/lib/utility/FetcherUtility.dart +104 -0
  155. package/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
  156. package/.sdk/tm/dart/lib/utility/MakeContextUtility.dart +6 -0
  157. package/.sdk/tm/dart/lib/utility/MakeErrorUtility.dart +71 -0
  158. package/.sdk/tm/dart/lib/utility/MakeFetchDefUtility.dart +42 -0
  159. package/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +233 -0
  160. package/.sdk/tm/dart/lib/utility/MakePointUtility.dart +130 -0
  161. package/.sdk/tm/dart/lib/utility/MakeRequestUtility.dart +66 -0
  162. package/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +59 -0
  163. package/.sdk/tm/dart/lib/utility/MakeResultUtility.dart +49 -0
  164. package/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +77 -0
  165. package/.sdk/tm/dart/lib/utility/MakeUrlUtility.dart +51 -0
  166. package/.sdk/tm/dart/lib/utility/ParamUtility.dart +56 -0
  167. package/.sdk/tm/dart/lib/utility/PrepareAuthUtility.dart +46 -0
  168. package/.sdk/tm/dart/lib/utility/PrepareBodyUtility.dart +19 -0
  169. package/.sdk/tm/dart/lib/utility/PrepareHeadersUtility.dart +11 -0
  170. package/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +30 -0
  171. package/.sdk/tm/dart/lib/utility/PrepareParamsUtility.dart +23 -0
  172. package/.sdk/tm/dart/lib/utility/PreparePathUtility.dart +9 -0
  173. package/.sdk/tm/dart/lib/utility/PrepareQueryUtility.dart +18 -0
  174. package/.sdk/tm/dart/lib/utility/ResultBasicUtility.dart +29 -0
  175. package/.sdk/tm/dart/lib/utility/ResultBodyUtility.dart +15 -0
  176. package/.sdk/tm/dart/lib/utility/ResultHeadersUtility.dart +18 -0
  177. package/.sdk/tm/dart/lib/utility/StructUtility.dart +127 -0
  178. package/.sdk/tm/dart/lib/utility/TransformRequestUtility.dart +28 -0
  179. package/.sdk/tm/dart/lib/utility/TransformResponseUtility.dart +44 -0
  180. package/.sdk/tm/dart/lib/utility/Utility.dart +239 -0
  181. package/.sdk/tm/dart/lib/utility/voxgig_struct.dart +2462 -0
  182. package/.sdk/tm/dart/src/feature/README.md +3 -0
  183. package/.sdk/tm/dart/src/feature/audit/.gitkeep +0 -0
  184. package/.sdk/tm/dart/src/feature/base/.gitkeep +0 -0
  185. package/.sdk/tm/dart/src/feature/cache/.gitkeep +0 -0
  186. package/.sdk/tm/dart/src/feature/clienttrack/.gitkeep +0 -0
  187. package/.sdk/tm/dart/src/feature/cost/.gitkeep +0 -0
  188. package/.sdk/tm/dart/src/feature/debug/.gitkeep +0 -0
  189. package/.sdk/tm/dart/src/feature/idempotency/.gitkeep +0 -0
  190. package/.sdk/tm/dart/src/feature/log/.gitkeep +0 -0
  191. package/.sdk/tm/dart/src/feature/metrics/.gitkeep +0 -0
  192. package/.sdk/tm/dart/src/feature/netsim/.gitkeep +0 -0
  193. package/.sdk/tm/dart/src/feature/paging/.gitkeep +0 -0
  194. package/.sdk/tm/dart/src/feature/proxy/.gitkeep +0 -0
  195. package/.sdk/tm/dart/src/feature/ratelimit/.gitkeep +0 -0
  196. package/.sdk/tm/dart/src/feature/rbac/.gitkeep +0 -0
  197. package/.sdk/tm/dart/src/feature/retry/.gitkeep +0 -0
  198. package/.sdk/tm/dart/src/feature/secrets/.gitkeep +0 -0
  199. package/.sdk/tm/dart/src/feature/streaming/.gitkeep +0 -0
  200. package/.sdk/tm/dart/src/feature/telemetry/.gitkeep +0 -0
  201. package/.sdk/tm/dart/src/feature/test/.gitkeep +0 -0
  202. package/.sdk/tm/dart/src/feature/timeout/.gitkeep +0 -0
  203. package/.sdk/tm/dart/test/custom_test.dart +65 -0
  204. package/.sdk/tm/dart/test/exists_test.dart +12 -0
  205. package/.sdk/tm/dart/test/feature/harness.dart +347 -0
  206. package/.sdk/tm/dart/test/feature/secrets/secrets_test.dart +1107 -0
  207. package/.sdk/tm/dart/test/feature_test.dart +1193 -0
  208. package/.sdk/tm/dart/test/harness.dart +153 -0
  209. package/.sdk/tm/dart/test/netsim_test.dart +48 -0
  210. package/.sdk/tm/dart/test/omni.dart +520 -0
  211. package/.sdk/tm/dart/test/omni_smoke_test.dart +143 -0
  212. package/.sdk/tm/dart/test/pipeline_test.dart +608 -0
  213. package/.sdk/tm/dart/test/primary_test.dart +476 -0
  214. package/.sdk/tm/dart/test/sdk-test-control.json +19 -0
  215. package/.sdk/tm/dart/test/struct_test.dart +468 -0
  216. package/.sdk/tm/dart/test/utility.dart +198 -0
  217. package/.sdk/tm/dart/test/vendor/omni/omni.dart +7 -0
  218. package/.sdk/tm/dart/test/vendor/omni/runner.dart +653 -0
  219. package/.sdk/tm/dart/test/vendor/omni/util.dart +232 -0
  220. package/.sdk/tm/haskell/LICENSE +21 -0
  221. package/.sdk/tm/haskell/Makefile +22 -0
  222. package/.sdk/tm/haskell/VERSION +1 -0
  223. package/.sdk/tm/haskell/src/SdkFeatures.hs +1440 -0
  224. package/.sdk/tm/haskell/src/SdkHelpers.hs +337 -0
  225. package/.sdk/tm/haskell/src/SdkJson.hs +111 -0
  226. package/.sdk/tm/haskell/src/SdkRuntime.hs +1279 -0
  227. package/.sdk/tm/haskell/src/SdkTypes.hs +195 -0
  228. package/.sdk/tm/haskell/src/VoxgigStruct.hs +2299 -0
  229. package/.sdk/tm/haskell/src/Vregex.hs +237 -0
  230. package/.sdk/tm/haskell/src/feature/README.md +5 -0
  231. package/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
  232. package/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
  233. package/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
  234. package/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
  235. package/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
  236. package/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
  237. package/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
  238. package/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
  239. package/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
  240. package/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
  241. package/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
  242. package/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
  243. package/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
  244. package/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
  245. package/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
  246. package/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
  247. package/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
  248. package/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
  249. package/.sdk/tm/haskell/test/Harness.hs +281 -0
  250. package/.sdk/tm/haskell/test/OmniResolver.hs +252 -0
  251. package/.sdk/tm/haskell/test/Runner.hs +49 -0
  252. package/.sdk/tm/haskell/test/StructCorpus.hs +449 -0
  253. package/.sdk/tm/haskell/test/TCustomUtility.hs +38 -0
  254. package/.sdk/tm/haskell/test/TFeature.hs +794 -0
  255. package/.sdk/tm/haskell/test/TNetsim.hs +50 -0
  256. package/.sdk/tm/haskell/test/TPipeline.hs +334 -0
  257. package/.sdk/tm/haskell/test/TPrimaryCorpus.hs +263 -0
  258. package/.sdk/tm/haskell/test/TPrimaryUtility.hs +386 -0
  259. package/.sdk/tm/haskell/test/Testutil.hs +59 -0
  260. package/.sdk/tm/haskell/test/vendor/omni/Omni.hs +1147 -0
  261. package/.sdk/tm/lean/LICENSE +21 -0
  262. package/.sdk/tm/lean/Makefile +32 -0
  263. package/.sdk/tm/lean/VERSION +1 -0
  264. package/.sdk/tm/lean/src/SdkFeature.lean +171 -0
  265. package/.sdk/tm/lean/src/SdkFeatures.lean +787 -0
  266. package/.sdk/tm/lean/src/SdkJson.lean +134 -0
  267. package/.sdk/tm/lean/src/SdkRuntime.lean +374 -0
  268. package/.sdk/tm/lean/src/SdkUtility.lean +809 -0
  269. package/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
  270. package/.sdk/tm/lean/src/Vregex.lean +412 -0
  271. package/.sdk/tm/lean/src/feature/README.md +5 -0
  272. package/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  273. package/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  274. package/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  275. package/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  276. package/.sdk/tm/lean/src/feature/cost/.gitkeep +0 -0
  277. package/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  278. package/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  279. package/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  280. package/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  281. package/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  282. package/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  283. package/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  284. package/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  285. package/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  286. package/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  287. package/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  288. package/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  289. package/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  290. package/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  291. package/.sdk/tm/lean/test/OmniResolver.lean +358 -0
  292. package/.sdk/tm/lean/test/OmniSmoke.lean +174 -0
  293. package/.sdk/tm/lean/test/StructCorpus.lean +368 -0
  294. package/.sdk/tm/lean/test/TFeature.lean +281 -0
  295. package/.sdk/tm/lean/test/TPrimaryUtility.lean +227 -0
  296. package/.sdk/tm/lean/test/vendor/omni/Omni.lean +997 -0
  297. package/LICENSE +21 -0
  298. package/README.md +104 -0
  299. package/package.json +78 -0
  300. package/sdkgen-package.json +22 -0
@@ -0,0 +1,1107 @@
1
+ // Behavioural tests for the secrets feature (vendored @voxgig/sekreto).
2
+ //
3
+ // The contract under test: the `apikey` OPTION keeps its exact old meaning
4
+ // and always wins, because SecretsFeature places it FIRST in the provider
5
+ // chain (a `memory` store named `options`) - explicit-beats-lookup falls
6
+ // out of sekreto's first-hit rule rather than from special-case logic. With
7
+ // the option unset, the chain (memory, env, dotenv, a vault) supplies the
8
+ // credential instead. And a broken store FAILS the operation rather than
9
+ // quietly sending it unauthenticated.
10
+ //
11
+ // WHERE THESE TESTS OBSERVE, AND WHY IT MATTERS.
12
+ //
13
+ // The dart port resolves at the TRANSPORT SEAM and holds the credential in
14
+ // feature state, so the only honest place to assert is the transport - what
15
+ // actually reached the wire, and how many times. Two seams are used, and
16
+ // each one is REAL in its leg:
17
+ //
18
+ // * A LIVE ProjectNameSDK with `system.fetch` injected. Nothing replaces
19
+ // that function, so a counter hung off it counts wire attempts. This is
20
+ // the RAW path (direct/graphql), which runs no feature hooks at all.
21
+ // * The offline feature harness, whose `server` is installed as
22
+ // `utility.fetcher` BEFORE features initialise - so SecretsFeature
23
+ // wraps it and the server is genuinely the inner transport. This is the
24
+ // ENTITY path, through the hook pipeline.
25
+ //
26
+ // What is deliberately NOT used for a fail-closed assertion: `SDK.test()`
27
+ // with `prepare()`. Test mode REPLACES the transport with its own mock and
28
+ // prepare() never reaches one at all, so "no request was sent" holds for a
29
+ // healthy SDK carrying no secrets feature whatsoever - an assertion that
30
+ // cannot fail pins no rule. Every refusal below is therefore paired with a
31
+ // CONTROL leg: the same construction with a WORKING provider, which must
32
+ // reach the SAME transport exactly once, carrying the credential. Only then
33
+ // does a zero mean REFUSED rather than UNWIRED. And each failure is matched
34
+ // on the PROVIDER'S OWN message, so an unrelated error (a blocked op, a bad
35
+ // URL) cannot stand in for fail-closed.
36
+ //
37
+ // This file lives in the `feature/` container on purpose: `target add`
38
+ // trims it, along with the feature source and the vendored library, for a
39
+ // project whose model does not select `secrets`.
40
+
41
+ import 'dart:async';
42
+
43
+ import '../../harness.dart';
44
+ import '../harness.dart' as fh;
45
+
46
+ import '../../../lib/ProjectNameSDK.dart' show ProjectNameSDK;
47
+ import '../../../lib/Config.dart' show FEATURE_PLUGINS;
48
+ import '../../../lib/feature/secrets/sekreto/src/provider.dart';
49
+ import '../../../lib/feature/secrets/sekreto/src/sekreto.dart' show SekretoError;
50
+
51
+
52
+ const String RAWBASE = 'http://raw.test/api';
53
+ const String EXBASE = 'http://exchange.test/api';
54
+ const String REFRESH = 'REFRESH01';
55
+
56
+
57
+ // The Authorization header carries the SPEC's credential prefix, which a
58
+ // TEMPLATE cannot know: an OpenAPI `http`/`bearer` scheme gives
59
+ // `Bearer <token>`, an apiKey scheme the raw token. So assert on the
60
+ // CREDENTIAL and let the prefix be whatever this SDK's API declares -
61
+ // pinning the whole header value passes only for a prefix-less API, and
62
+ // this file ships to every project that selects the feature.
63
+ void credentialIs(dynamic header, String token) {
64
+ final got = null == header ? '' : '$header';
65
+ ok(got == token || got.endsWith(' $token'),
66
+ 'expected the Authorization header to carry $token, got: "$got"');
67
+ }
68
+
69
+
70
+ // A store that HAS the secret. The chain's ordinary case.
71
+ class GoodProvider extends Provider {
72
+ final String value;
73
+ int asked = 0;
74
+ final List<String> names = [];
75
+
76
+ GoodProvider([this.value = 'RAWKEY01']);
77
+
78
+ @override
79
+ FutureOr<String?> lookup(String name) {
80
+ asked++;
81
+ names.add(name);
82
+ return 'apikey' == name ? value : null;
83
+ }
84
+
85
+ @override
86
+ String describe() => 'working:test';
87
+ }
88
+
89
+
90
+ // A store that could not ANSWER - the case that must never degrade into an
91
+ // unauthenticated request. Distinct from a store that simply lacks the
92
+ // secret, which is a MISS and lets the chain carry on.
93
+ class BrokenProvider extends Provider {
94
+ int asked = 0;
95
+
96
+ @override
97
+ FutureOr<String?> lookup(String _name) {
98
+ asked++;
99
+ throw SekretoError('vault unreachable');
100
+ }
101
+
102
+ @override
103
+ String describe() => 'broken:test';
104
+ }
105
+
106
+
107
+ // A store that has nothing. Not an error.
108
+ class EmptyProvider extends Provider {
109
+ @override
110
+ FutureOr<String?> lookup(String _name) => null;
111
+
112
+ @override
113
+ String describe() => 'empty:test';
114
+ }
115
+
116
+
117
+ // Fails once, then recovers - a transient vault outage.
118
+ class FlakyProvider extends Provider {
119
+ int asked = 0;
120
+
121
+ @override
122
+ FutureOr<String?> lookup(String _name) {
123
+ asked++;
124
+ if (1 == asked) {
125
+ throw SekretoError('vault unreachable');
126
+ }
127
+ return 'RECOVERED01';
128
+ }
129
+
130
+ @override
131
+ String describe() => 'flaky:test';
132
+ }
133
+
134
+
135
+ // Counts every lookup, answering with a different value each time, so
136
+ // `cache: false` can be told from `cache: true`.
137
+ class CountingProvider extends Provider {
138
+ int asked = 0;
139
+
140
+ @override
141
+ FutureOr<String?> lookup(String _name) {
142
+ asked++;
143
+ return 'KEY$asked';
144
+ }
145
+
146
+ @override
147
+ String describe() => 'counting:test';
148
+ }
149
+
150
+
151
+ // A transport that records every call it is handed, so "sent" is a fact
152
+ // about the wire rather than about a mock. Doubles as the token endpoint
153
+ // for the exchange tests: `apiStatus` is a SCRIPT - one status per API
154
+ // call, the last repeating - so a case can say "401 then 200" without
155
+ // counting calls itself.
156
+ class Wire {
157
+ final List<int> apiStatus;
158
+ final List<String> tokens;
159
+ final String tokenPath;
160
+ final String responseField;
161
+ final bool tokenFails;
162
+
163
+ final List<Map<String, dynamic>> calls = [];
164
+ int _issued = 0;
165
+ int _apicall = -1;
166
+
167
+ Wire({
168
+ List<int>? apiStatus,
169
+ List<String>? tokens,
170
+ String? tokenPath,
171
+ String? responseField,
172
+ bool? tokenFails,
173
+ }) : apiStatus = apiStatus ?? const [200],
174
+ tokens = tokens ?? const ['ACCESS01', 'ACCESS02', 'ACCESS03'],
175
+ tokenPath = tokenPath ?? 'auth/token',
176
+ responseField = responseField ?? 'access_token',
177
+ tokenFails = tokenFails ?? false;
178
+
179
+ Future<dynamic> fetch(dynamic url, dynamic fetchdef) async {
180
+ final headers = fetchdef is Map ? fetchdef['headers'] : null;
181
+
182
+ calls.add(<String, dynamic>{
183
+ 'url': '$url',
184
+ 'fetchdef': fetchdef,
185
+ 'auth': headers is Map ? headers['authorization'] : null,
186
+ 'body': fetchdef is Map ? fetchdef['body'] : null,
187
+ });
188
+
189
+ if (_istoken('$url')) {
190
+ if (tokenFails) {
191
+ return <String, dynamic>{
192
+ 'status': 500,
193
+ 'json': () => <String, dynamic>{},
194
+ 'headers': <String, dynamic>{},
195
+ };
196
+ }
197
+ final at = _issued < tokens.length ? _issued : tokens.length - 1;
198
+ _issued++;
199
+ return <String, dynamic>{
200
+ 'status': 200,
201
+ 'json': () => <String, dynamic>{responseField: tokens[at]},
202
+ 'headers': <String, dynamic>{},
203
+ };
204
+ }
205
+
206
+ _apicall++;
207
+ final at = _apicall < apiStatus.length ? _apicall : apiStatus.length - 1;
208
+ final status = apiStatus[at];
209
+ return <String, dynamic>{
210
+ 'status': status,
211
+ 'json': () => <String, dynamic>{'ok': 400 > status},
212
+ 'headers': <String, dynamic>{},
213
+ };
214
+ }
215
+
216
+ bool _istoken(String url) => url.endsWith('/$tokenPath');
217
+
218
+ int get sent => calls.length;
219
+
220
+ List<Map<String, dynamic>> api() =>
221
+ calls.where((c) => !_istoken('${c['url']}')).toList();
222
+
223
+ List<Map<String, dynamic>> token() =>
224
+ calls.where((c) => _istoken('${c['url']}')).toList();
225
+
226
+ // What went out, for a failure message that names the leak rather than
227
+ // just its count.
228
+ String leak() =>
229
+ calls.map((c) => '${c['url']} auth=${c['auth']}').join(', ');
230
+ }
231
+
232
+
233
+ // A LIVE client on the raw path. `allow.op` is named explicitly: a project
234
+ // that narrows the default set would otherwise turn the control leg into a
235
+ // false RED (refused before it reached the transport), and the rule under
236
+ // test lives downstream of the allow gate either way.
237
+ ProjectNameSDK rawSdk(Wire wire, List<dynamic> providers,
238
+ [Map<String, dynamic>? extra]) {
239
+ final secrets = <String, dynamic>{'active': true, 'providers': providers};
240
+ if (null != extra) {
241
+ extra.forEach((k, v) => secrets[k] = v);
242
+ }
243
+
244
+ final opts = <String, dynamic>{
245
+ 'base': RAWBASE,
246
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
247
+ 'system': <String, dynamic>{'fetch': wire.fetch},
248
+ 'feature': <String, dynamic>{'secrets': secrets},
249
+ };
250
+
251
+ return ProjectNameSDK(opts);
252
+ }
253
+
254
+
255
+ // The feature instance itself, for the credential the transport injects -
256
+ // this feature never writes the options map, so there is nothing to read
257
+ // there.
258
+ dynamic secretsOf(dynamic sdk) {
259
+ for (final f in sdk.features) {
260
+ if ('secrets' == f.name) {
261
+ return f;
262
+ }
263
+ }
264
+ return null;
265
+ }
266
+
267
+
268
+ // The ENTITY path: the offline harness pipeline (PrePoint, PreSpec,
269
+ // makeSpec, makeRequest, the transport), with `server` as the real inner
270
+ // transport SecretsFeature wraps.
271
+ class Entity {
272
+ final List<Map<String, dynamic>> calls = [];
273
+ late fh.Harness harness;
274
+
275
+ int get sent => calls.length;
276
+
277
+ dynamic auth(int i) {
278
+ final headers = calls[i]['fetchdef']['headers'];
279
+ return headers is Map ? headers['authorization'] : null;
280
+ }
281
+
282
+ String leak() =>
283
+ calls.map((c) => '${c['url']} auth=${c['fetchdef']['headers']}').join(', ');
284
+ }
285
+
286
+
287
+ Entity entitySdk(List<dynamic> providers, [Map<String, dynamic>? options]) {
288
+ final out = Entity();
289
+
290
+ out.harness = fh.makeClient(
291
+ features: [
292
+ <String, dynamic>{
293
+ 'name': 'secrets',
294
+ 'options': <String, dynamic>{'active': true, 'providers': providers},
295
+ }
296
+ ],
297
+ options: options,
298
+ server: (dynamic _ctx, dynamic url, dynamic fetchdef) {
299
+ out.calls.add(<String, dynamic>{'url': url, 'fetchdef': fetchdef});
300
+ return fh.makeResponse(200, <String, dynamic>{'ok': true});
301
+ },
302
+ );
303
+
304
+ return out;
305
+ }
306
+
307
+
308
+ void tests() {
309
+ describe('secrets', () {
310
+
311
+ // THE SUITE IS NOT VACUOUS. Test_dart registers this file only when the
312
+ // model selects the feature, so a run that gets here with no secrets
313
+ // feature is a wiring failure, not a skip - and every assertion below
314
+ // would otherwise pass for the wrong reason.
315
+ test('the secrets feature is present in this SDK', (t) {
316
+ equal(true, fh.hasFeature('secrets'),
317
+ 'the secrets suite ran against an SDK with no secrets feature');
318
+ });
319
+
320
+
321
+ // --- the chain --------------------------------------------------------
322
+
323
+ test('an explicit apikey wins over the chain', (t) async {
324
+ // The explicit option seats FIRST as a `memory` store named
325
+ // 'options', so it wins by sekreto's own first-hit rule rather than
326
+ // by any special case in this feature.
327
+ final wire = Wire();
328
+ final chain = GoodProvider('CHAINKEY01');
329
+ final sdk = ProjectNameSDK(<String, dynamic>{
330
+ 'base': RAWBASE,
331
+ 'apikey': 'OPTKEY01',
332
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
333
+ 'system': <String, dynamic>{'fetch': wire.fetch},
334
+ 'feature': <String, dynamic>{
335
+ 'secrets': <String, dynamic>{
336
+ 'active': true,
337
+ 'providers': [chain],
338
+ }
339
+ },
340
+ });
341
+
342
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
343
+
344
+ equal(1, wire.sent, 'the request never reached the transport');
345
+ credentialIs(wire.calls[0]['auth'], 'OPTKEY01');
346
+ equal(0, chain.asked,
347
+ 'first-hit means the chain behind the explicit option is not '
348
+ 'even asked');
349
+
350
+ // The explicit option is a real store, not a special case: a directed
351
+ // read names it like any other.
352
+ equal('OPTKEY01', await sdk.secrets().getfrom('options', 'apikey'));
353
+ });
354
+
355
+
356
+ test('an omitted apikey defers to the chain', (t) async {
357
+ final wire = Wire();
358
+ final sdk = rawSdk(wire, [GoodProvider('CHAINKEY01')]);
359
+
360
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
361
+
362
+ equal(1, wire.sent);
363
+ credentialIs(wire.calls[0]['auth'], 'CHAINKEY01');
364
+
365
+ // The credential lives in FEATURE STATE, and the shared options map
366
+ // is left exactly as makeOptions built it.
367
+ equal('CHAINKEY01', secretsOf(sdk).credential);
368
+ equal('', sdk.options()['apikey'],
369
+ 'the feature wrote the shared options map');
370
+ });
371
+
372
+
373
+ test('a declarative provider MAP is accepted, not only an object',
374
+ (t) async {
375
+ // The shape a config file and the docs use. sekreto's dart
376
+ // constructor takes only a ProviderSpec or a Provider, so the feature
377
+ // has to convert - and a silent failure here means an SDK with an
378
+ // EMPTY chain that quietly sends nothing.
379
+ final wire = Wire();
380
+ final sdk = rawSdk(wire, [
381
+ <String, dynamic>{
382
+ 'kind': 'memory',
383
+ 'values': <String, String>{'APIKEY': 'MAPKEY01'},
384
+ }
385
+ ]);
386
+
387
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
388
+
389
+ equal(1, wire.sent);
390
+ credentialIs(wire.calls[0]['auth'], 'MAPKEY01');
391
+ });
392
+
393
+
394
+ test('a custom provider object is used verbatim', (t) async {
395
+ final wire = Wire();
396
+ final provider = GoodProvider('CUSTOM01');
397
+ final sdk = rawSdk(wire, [provider]);
398
+
399
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
400
+
401
+ credentialIs(wire.calls[0]['auth'], 'CUSTOM01');
402
+ deepEqual(['apikey'], provider.names,
403
+ 'the chain asked for something other than the configured name');
404
+ });
405
+
406
+
407
+ test('the secret name is configurable', (t) async {
408
+ final wire = Wire();
409
+ final sdk = rawSdk(wire, [
410
+ <String, dynamic>{
411
+ 'kind': 'memory',
412
+ 'values': <String, String>{'API_TOKEN': 'TOKKEY01'},
413
+ }
414
+ ], <String, dynamic>{'name': 'api.token'});
415
+
416
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
417
+
418
+ credentialIs(wire.calls[0]['auth'], 'TOKKEY01');
419
+ });
420
+
421
+
422
+ test('a miss everywhere sends the request with NO credential', (t) async {
423
+ // A store that does not hold the secret is a MISS, and the operation
424
+ // proceeds - unauthenticated. That is the whole difference from the
425
+ // error case below.
426
+ final wire = Wire();
427
+ final sdk = rawSdk(wire, [EmptyProvider()]);
428
+
429
+ final res = await sdk.direct(<String, dynamic>{'path': '/thing'});
430
+
431
+ equal(1, wire.sent, 'a miss must not block the request');
432
+ equal(null, wire.calls[0]['auth']);
433
+ equal(true, res['ok']);
434
+ });
435
+
436
+
437
+ test('sekreto is live for arbitrary secrets and redaction', (t) async {
438
+ final wire = Wire();
439
+ final sdk = rawSdk(wire, [
440
+ <String, dynamic>{
441
+ 'kind': 'memory',
442
+ 'values': <String, String>{'DB_PASSWORD': 'dbpass01'},
443
+ }
444
+ ]);
445
+
446
+ final secrets = sdk.secrets();
447
+ equal('dbpass01', await secrets.get('db.password'));
448
+ equal('the password is [redacted], keep it safe',
449
+ secrets.redact('the password is dbpass01, keep it safe'));
450
+ });
451
+
452
+
453
+ // THE PROVIDER VOCABULARY. Upstream sekreto has no registry any more: a
454
+ // kind not handed in through `plugins` is unknown to that Sekreto. So
455
+ // the model's active plugin groups have to arrive as real Definitions,
456
+ // and a Config that emitted the map but not the imports (or the other
457
+ // way round) leaves an SDK that refuses every plugin kind at runtime
458
+ // while every builtin-only test stays green.
459
+ test('the model\'s plugin definitions reach the feature', (t) {
460
+ final defs = FEATURE_PLUGINS['secrets'];
461
+
462
+ if (null == defs || defs.isEmpty) {
463
+ // This project selects no plugin group. Nothing to check.
464
+ t.skip('no plugin group active in this model');
465
+ return;
466
+ }
467
+
468
+ for (final one in defs) {
469
+ ok(one is Map && one['name'] is String,
470
+ 'FEATURE_PLUGINS carries something that is not a voxgig/plugin '
471
+ 'Definition: $one');
472
+ }
473
+ });
474
+
475
+
476
+ // --- auth suppression -------------------------------------------------
477
+
478
+ test('auth null suppresses the credential, chain or no chain', (t) async {
479
+ final wire = Wire();
480
+ final sdk = ProjectNameSDK(<String, dynamic>{
481
+ 'base': RAWBASE,
482
+ 'auth': null,
483
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
484
+ 'system': <String, dynamic>{'fetch': wire.fetch},
485
+ 'feature': <String, dynamic>{
486
+ 'secrets': <String, dynamic>{
487
+ 'active': true,
488
+ 'providers': [GoodProvider('CHAINKEY01')],
489
+ }
490
+ },
491
+ });
492
+
493
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
494
+
495
+ equal(1, wire.sent);
496
+ equal(null, wire.calls[0]['auth'],
497
+ 'a credential went out despite auth: null');
498
+
499
+ // The suppression survives option validation rather than being
500
+ // replaced by the optspec's default auth map.
501
+ equal(null, sdk.options()['auth']);
502
+
503
+ // And the chain DID resolve - the suppression is at the header, not a
504
+ // side effect of an empty chain.
505
+ equal('CHAINKEY01', secretsOf(sdk).credential);
506
+ });
507
+
508
+
509
+ test('auth null suppresses an EXPLICIT apikey too', (t) async {
510
+ final wire = Wire();
511
+ final sdk = ProjectNameSDK(<String, dynamic>{
512
+ 'base': RAWBASE,
513
+ 'apikey': 'OPTKEY01',
514
+ 'auth': null,
515
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
516
+ 'system': <String, dynamic>{'fetch': wire.fetch},
517
+ 'feature': <String, dynamic>{
518
+ 'secrets': <String, dynamic>{
519
+ 'active': true,
520
+ 'providers': <dynamic>[],
521
+ }
522
+ },
523
+ });
524
+
525
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
526
+
527
+ equal(1, wire.sent);
528
+ equal(null, wire.calls[0]['auth']);
529
+ });
530
+
531
+
532
+ // --- FAIL CLOSED: the raw paths ---------------------------------------
533
+ //
534
+ // direct() and graphql() run NO feature hooks at all. If resolution
535
+ // lived only in a PreSpec hook these would send an unauthenticated
536
+ // request and never notice. They are covered because the feature
537
+ // resolves at the transport, which is the one seam both cross.
538
+
539
+ test('a provider ERROR fails direct() rather than sending', (t) async {
540
+ // THE RULE, asserted first so a regression reports the leak itself.
541
+ final wire = Wire();
542
+ final broken = BrokenProvider();
543
+ final res =
544
+ await rawSdk(wire, [broken]).direct(<String, dynamic>{'path': '/thing'});
545
+
546
+ equal(0, wire.sent,
547
+ 'a request must not go out unauthenticated because a provider '
548
+ 'broke, but one reached the transport: ${wire.leak()}');
549
+ equal(false, res['ok'], 'expected a failure, got: $res');
550
+ ok('${res['err']}'.contains('vault unreachable'),
551
+ 'the failure must carry the provider\'s own message, got: '
552
+ '${res['err']}');
553
+ equal(1, broken.asked, 'the chain was never asked at all');
554
+
555
+ // CONTROL, which makes that zero mean REFUSED rather than UNWIRED:
556
+ // the same construction with a WORKING provider must reach the same
557
+ // transport, once, carrying the credential.
558
+ final control = Wire();
559
+ final ok2 = await rawSdk(control, [GoodProvider()])
560
+ .direct(<String, dynamic>{'path': '/thing'});
561
+
562
+ equal(true, ok2['ok'], 'the control request failed: $ok2');
563
+ equal(1, control.sent,
564
+ 'the control request never reached system.fetch, so this test '
565
+ 'cannot observe a request going out at all');
566
+ credentialIs(control.calls[0]['auth'], 'RAWKEY01');
567
+ });
568
+
569
+
570
+ test('a provider ERROR fails graphql() rather than sending', (t) async {
571
+ final wire = Wire();
572
+ final res =
573
+ await rawSdk(wire, [BrokenProvider()]).graphql('{ thing }', {});
574
+
575
+ equal(0, wire.sent,
576
+ 'a graphql request must not go out unauthenticated, but one '
577
+ 'reached the transport: ${wire.leak()}');
578
+ equal(false, res['ok'], 'expected a failure, got: $res');
579
+ ok('${res['err']}'.contains('vault unreachable'),
580
+ 'the failure must carry the provider\'s own message, got: '
581
+ '${res['err']}');
582
+
583
+ final control = Wire();
584
+ final ok2 =
585
+ await rawSdk(control, [GoodProvider()]).graphql('{ thing }', {});
586
+
587
+ equal(true, ok2['ok'], 'the control request failed: $ok2');
588
+ equal(1, control.sent,
589
+ 'the control request never reached system.fetch, so this test '
590
+ 'cannot observe a request going out at all');
591
+ credentialIs(control.calls[0]['auth'], 'RAWKEY01');
592
+ });
593
+
594
+
595
+ test('a BROKEN store does not fall through to a weaker one', (t) async {
596
+ // sekreto's miss-vs-error invariant at its sharpest: a store sitting
597
+ // BEHIND the broken one holds a usable value, and reaching it would
598
+ // look like success while silently downgrading the credential.
599
+ final wire = Wire();
600
+ final res = await rawSdk(wire, [
601
+ BrokenProvider(),
602
+ <String, dynamic>{
603
+ 'kind': 'memory',
604
+ 'values': <String, String>{'APIKEY': 'WEAKER01'},
605
+ }
606
+ ]).direct(<String, dynamic>{'path': '/thing'});
607
+
608
+ equal(0, wire.sent,
609
+ 'the chain fell through a broken store to a weaker one: '
610
+ '${wire.leak()}');
611
+ equal(false, res['ok']);
612
+ ok('${res['err']}'.contains('vault unreachable'),
613
+ 'expected the provider error, got: ${res['err']}');
614
+ });
615
+
616
+
617
+ // --- FAIL CLOSED: the entity path -------------------------------------
618
+ //
619
+ // The hook pipeline, through the offline harness. Its `server` is
620
+ // installed as utility.fetcher BEFORE features initialise, so
621
+ // SecretsFeature wraps it and the server IS the inner transport - the
622
+ // same counter argument as the raw legs above.
623
+
624
+ test('a provider ERROR fails an entity op rather than sending', (t) async {
625
+ final broken = BrokenProvider();
626
+ final ent = entitySdk([broken]);
627
+
628
+ final res = await ent.harness.op(<String, dynamic>{'op': 'load'});
629
+
630
+ equal(0, ent.sent,
631
+ 'an entity op must not go out unauthenticated because a provider '
632
+ 'broke, but one reached the transport: ${ent.leak()}');
633
+ equal(false, res['ok'], 'expected the op to fail, got: $res');
634
+ ok('${res['error']}'.contains('vault unreachable'),
635
+ 'the failure must carry the provider\'s own message, got: '
636
+ '${res['error']}');
637
+ equal(1, broken.asked, 'the chain was never asked at all');
638
+
639
+ // CONTROL: the same pipeline with a WORKING provider reaches the same
640
+ // transport exactly once, carrying the credential.
641
+ final control = entitySdk([GoodProvider()]);
642
+ final ok2 = await control.harness.op(<String, dynamic>{'op': 'load'});
643
+
644
+ equal(true, ok2['ok'], 'the control op failed: ${ok2['error']}');
645
+ equal(1, control.sent,
646
+ 'the control op never reached the transport, so this test cannot '
647
+ 'observe a request going out at all');
648
+ credentialIs(control.auth(0), 'RAWKEY01');
649
+ });
650
+
651
+
652
+ test('auth null suppresses the credential on the entity path too',
653
+ (t) async {
654
+ final ent = entitySdk([GoodProvider()], <String, dynamic>{'auth': null});
655
+
656
+ final res = await ent.harness.op(<String, dynamic>{'op': 'load'});
657
+
658
+ equal(true, res['ok']);
659
+ equal(1, ent.sent);
660
+ equal(null, ent.auth(0),
661
+ 'a credential went out on the entity path despite auth: null');
662
+ });
663
+
664
+
665
+ // --- resolution lifecycle ---------------------------------------------
666
+
667
+ test('a provider recovers after a transient failure', (t) async {
668
+ // A settled FAILURE must not be held forever: keeping it meant a
669
+ // transient vault outage poisoned the client permanently, every later
670
+ // operation failing with the original error long after the vault
671
+ // recovered.
672
+ final wire = Wire();
673
+ final sdk = rawSdk(wire, [FlakyProvider()]);
674
+
675
+ final first = await sdk.direct(<String, dynamic>{'path': '/one'});
676
+ equal(false, first['ok'], 'the first attempt should surface the outage');
677
+ equal(0, wire.sent);
678
+
679
+ final second = await sdk.direct(<String, dynamic>{'path': '/two'});
680
+ equal(true, second['ok'], 'the second attempt should recover: $second');
681
+ equal(1, wire.sent);
682
+ credentialIs(wire.calls[0]['auth'], 'RECOVERED01');
683
+ });
684
+
685
+
686
+ test('cache true asks the chain once', (t) async {
687
+ final wire = Wire();
688
+ final counting = CountingProvider();
689
+ final sdk = rawSdk(wire, [counting]);
690
+
691
+ await sdk.direct(<String, dynamic>{'path': '/one'});
692
+ await sdk.direct(<String, dynamic>{'path': '/two'});
693
+
694
+ equal(1, counting.asked, 'a cached resolution asked the chain twice');
695
+ credentialIs(wire.calls[1]['auth'], 'KEY1');
696
+ });
697
+
698
+
699
+ // A MISS IS NOT A CACHEABLE ANSWER - sekreto's own rule, which this
700
+ // feature used to override from the layer above.
701
+ //
702
+ // DEFAULT caching here, which is the whole point: `cache: true` is
703
+ // about holding a HIT (the test above pins that half), and keeping the
704
+ // settled future after a miss meant the chain was never asked again for
705
+ // the life of the client. A secret provisioned after startup (a mounted
706
+ // file, a vault policy granted a minute late) was invisible forever,
707
+ // and the only workaround was giving up hit caching entirely.
708
+ test('cache true re-asks after a MISS, so a late secret is picked up',
709
+ (t) async {
710
+ final wire = Wire();
711
+ final late = LateProvider();
712
+ final sdk = rawSdk(wire, [late]);
713
+
714
+ await sdk.direct(<String, dynamic>{'path': '/one'});
715
+ equal(null, wire.calls[0]['auth'],
716
+ 'the chain has nothing yet, so no credential should go out');
717
+
718
+ final asked = late.asked;
719
+ ok(0 < asked, 'the chain was never asked');
720
+
721
+ // The secret is provisioned while the client is live.
722
+ late.present = true;
723
+
724
+ await sdk.direct(<String, dynamic>{'path': '/two'});
725
+
726
+ ok(asked < late.asked,
727
+ 'the MISS was cached: a secret that appears later can never be '
728
+ 'picked up');
729
+ credentialIs(wire.calls[1]['auth'], 'LATEKEY01');
730
+ });
731
+
732
+
733
+ test('cache false asks the chain on every request', (t) async {
734
+ // `cache: false` is documented as "every resolve asks the chain
735
+ // again". Caching the settled future made that a lie.
736
+ final wire = Wire();
737
+ final counting = CountingProvider();
738
+ final sdk = rawSdk(wire, [counting], <String, dynamic>{'cache': false});
739
+
740
+ await sdk.direct(<String, dynamic>{'path': '/one'});
741
+ await sdk.direct(<String, dynamic>{'path': '/two'});
742
+
743
+ ok(1 < counting.asked,
744
+ 'the chain was asked once and cached, despite cache: false');
745
+ credentialIs(wire.calls[1]['auth'], 'KEY2');
746
+ });
747
+
748
+
749
+ test('an UNCACHED miss after a hit retracts the credential', (t) async {
750
+ // A revocation must stop going out on the wire.
751
+ final store = RetractingProvider();
752
+ final wire = Wire();
753
+ final sdk = rawSdk(wire, [store], <String, dynamic>{'cache': false});
754
+
755
+ await sdk.direct(<String, dynamic>{'path': '/one'});
756
+ credentialIs(wire.calls[0]['auth'], 'LIVEKEY01');
757
+
758
+ store.revoked = true;
759
+ await sdk.direct(<String, dynamic>{'path': '/two'});
760
+
761
+ equal(2, wire.sent);
762
+ equal(null, wire.calls[1]['auth'],
763
+ 'a revoked credential kept going out on the wire');
764
+ });
765
+
766
+ });
767
+
768
+
769
+ // ACCESS-TOKEN EXCHANGE.
770
+ //
771
+ // What the chain resolves is a REFRESH token, which is POSTed to a token
772
+ // endpoint for a short-lived ACCESS token; the access token is what the
773
+ // Authorization header carries; and when the API answers 401 the client
774
+ // buys another and tries the same request again, once.
775
+ describe('secrets exchange', () {
776
+
777
+ ProjectNameSDK exchangeSdk(Wire wire, [Map<String, dynamic>? extra]) {
778
+ final secrets = <String, dynamic>{
779
+ 'active': true,
780
+ 'name': 'refresh_token',
781
+ 'providers': [
782
+ <String, dynamic>{
783
+ 'kind': 'memory',
784
+ 'values': <String, String>{'REFRESH_TOKEN': REFRESH},
785
+ }
786
+ ],
787
+ 'exchange': <String, dynamic>{'active': true},
788
+ };
789
+ if (null != extra) {
790
+ extra.forEach((k, v) => secrets[k] = v);
791
+ }
792
+
793
+ return ProjectNameSDK(<String, dynamic>{
794
+ 'base': EXBASE,
795
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
796
+ 'system': <String, dynamic>{'fetch': wire.fetch},
797
+ 'feature': <String, dynamic>{'secrets': secrets},
798
+ });
799
+ }
800
+
801
+
802
+ test('the refresh token buys an access token the request carries',
803
+ (t) async {
804
+ final wire = Wire();
805
+ final sdk = exchangeSdk(wire);
806
+
807
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
808
+
809
+ equal(1, wire.token().length, 'expected exactly one token purchase');
810
+ // MARSHALLED, not concatenated: a refresh token carrying a quote or a
811
+ // newline must arrive as that literal value.
812
+ equal('{"refresh_token":"$REFRESH"}', wire.token()[0]['body']);
813
+
814
+ equal(1, wire.api().length);
815
+ credentialIs(wire.api()[0]['auth'], 'ACCESS01');
816
+ });
817
+
818
+
819
+ test('one purchase serves many requests', (t) async {
820
+ final wire = Wire();
821
+ final sdk = exchangeSdk(wire);
822
+
823
+ await sdk.direct(<String, dynamic>{'path': '/one'});
824
+ await sdk.direct(<String, dynamic>{'path': '/two'});
825
+ await sdk.direct(<String, dynamic>{'path': '/three'});
826
+
827
+ equal(1, wire.token().length,
828
+ 'a token still working must not be re-bought');
829
+ equal(3, wire.api().length);
830
+ });
831
+
832
+
833
+ test('concurrent first requests share ONE purchase', (t) async {
834
+ final wire = Wire();
835
+ final sdk = exchangeSdk(wire);
836
+
837
+ await Future.wait(<Future<dynamic>>[
838
+ sdk.direct(<String, dynamic>{'path': '/a'}),
839
+ sdk.direct(<String, dynamic>{'path': '/b'}),
840
+ sdk.direct(<String, dynamic>{'path': '/c'}),
841
+ ]);
842
+
843
+ equal(1, wire.token().length,
844
+ 'three operations at once must not open three token requests');
845
+ });
846
+
847
+
848
+ test('a 401 buys another token and retries the SAME request', (t) async {
849
+ final wire = Wire(apiStatus: [401, 200]);
850
+ final sdk = exchangeSdk(wire);
851
+
852
+ final res = await sdk.direct(<String, dynamic>{'path': '/thing'});
853
+
854
+ equal(2, wire.token().length, 'expected a second token purchase');
855
+ equal(2, wire.api().length, 'expected the request to be retried');
856
+ credentialIs(wire.api()[0]['auth'], 'ACCESS01');
857
+ // The retry must carry the NEW token, not the spent one.
858
+ credentialIs(wire.api()[1]['auth'], 'ACCESS02');
859
+ equal(true, res['ok'], 'the caller sees the successful retry');
860
+ });
861
+
862
+
863
+ test('the retry happens once, not in a loop', (t) async {
864
+ // Every API call is refused: a second 401 on a token bought moments
865
+ // ago is a real failure, and spinning on it would hang.
866
+ final wire = Wire(apiStatus: [401]);
867
+ final sdk = exchangeSdk(wire);
868
+
869
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
870
+
871
+ equal(2, wire.api().length, 'exactly one retry');
872
+ equal(2, wire.token().length);
873
+ });
874
+
875
+
876
+ test('a status outside exchange.statuses is not an expiry', (t) async {
877
+ final wire = Wire(apiStatus: [403]);
878
+ final sdk = exchangeSdk(wire);
879
+
880
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
881
+
882
+ equal(1, wire.api().length, '403 is not in the default statuses');
883
+ equal(1, wire.token().length);
884
+ });
885
+
886
+
887
+ test('exchange.statuses is configurable', (t) async {
888
+ final wire = Wire(apiStatus: [403, 200]);
889
+ final sdk = exchangeSdk(wire, <String, dynamic>{
890
+ 'exchange': <String, dynamic>{
891
+ 'active': true,
892
+ 'statuses': [403],
893
+ }
894
+ });
895
+
896
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
897
+
898
+ equal(2, wire.api().length, '403 was declared an expiry');
899
+ });
900
+
901
+
902
+ test('the endpoint and field names are configurable', (t) async {
903
+ final wire =
904
+ Wire(tokenPath: 'oauth/grant', responseField: 'token');
905
+ final sdk = exchangeSdk(wire, <String, dynamic>{
906
+ 'exchange': <String, dynamic>{
907
+ 'active': true,
908
+ 'path': 'oauth/grant',
909
+ 'request': 'grant',
910
+ 'response': 'token',
911
+ }
912
+ });
913
+
914
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
915
+
916
+ equal(1, wire.token().length);
917
+ ok('${wire.token()[0]['url']}'.endsWith('/oauth/grant'),
918
+ 'the token endpoint is relative to base: ${wire.token()[0]['url']}');
919
+ equal('{"grant":"$REFRESH"}', wire.token()[0]['body']);
920
+ credentialIs(wire.api()[0]['auth'], 'ACCESS01');
921
+ });
922
+
923
+
924
+ test('an explicit exchange.refresh wins over the chain', (t) async {
925
+ final wire = Wire();
926
+ final sdk = exchangeSdk(wire, <String, dynamic>{
927
+ 'exchange': <String, dynamic>{'active': true, 'refresh': 'EXPLICIT01'}
928
+ });
929
+
930
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
931
+
932
+ equal('{"refresh_token":"EXPLICIT01"}', wire.token()[0]['body']);
933
+ });
934
+
935
+
936
+ test('an explicit apikey is spent before anything is bought', (t) async {
937
+ // A caller who already holds an access token should use it; expiry is
938
+ // what moves them onto the exchange, and the API says when.
939
+ final wire = Wire();
940
+ final sdk = ProjectNameSDK(<String, dynamic>{
941
+ 'base': EXBASE,
942
+ 'apikey': 'HELDTOKEN01',
943
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
944
+ 'system': <String, dynamic>{'fetch': wire.fetch},
945
+ 'feature': <String, dynamic>{
946
+ 'secrets': <String, dynamic>{
947
+ 'active': true,
948
+ 'name': 'refresh_token',
949
+ 'providers': [
950
+ <String, dynamic>{
951
+ 'kind': 'memory',
952
+ 'values': <String, String>{'REFRESH_TOKEN': REFRESH},
953
+ }
954
+ ],
955
+ 'exchange': <String, dynamic>{'active': true},
956
+ }
957
+ },
958
+ });
959
+
960
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
961
+
962
+ equal(0, wire.token().length, 'nothing needed buying');
963
+ credentialIs(wire.api()[0]['auth'], 'HELDTOKEN01');
964
+ });
965
+
966
+
967
+ test('no refresh token anywhere is an error, not an unauthenticated call',
968
+ (t) async {
969
+ final wire = Wire();
970
+ final sdk = ProjectNameSDK(<String, dynamic>{
971
+ 'base': EXBASE,
972
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
973
+ 'system': <String, dynamic>{'fetch': wire.fetch},
974
+ 'feature': <String, dynamic>{
975
+ 'secrets': <String, dynamic>{
976
+ 'active': true,
977
+ 'name': 'refresh_token',
978
+ 'providers': [EmptyProvider()],
979
+ 'exchange': <String, dynamic>{'active': true},
980
+ }
981
+ },
982
+ });
983
+
984
+ final res = await sdk.direct(<String, dynamic>{'path': '/thing'});
985
+
986
+ equal(false, res['ok'], 'expected a failure, got: $res');
987
+ equal(0, wire.api().length,
988
+ 'a request must not go out unauthenticated because the chain was '
989
+ 'empty: ${wire.leak()}');
990
+ ok('${res['err']}'.contains('no refresh token'),
991
+ 'expected the missing-refresh-token error, got: ${res['err']}');
992
+ });
993
+
994
+
995
+ test('a failing token endpoint fails the request rather than sending',
996
+ (t) async {
997
+ final wire = Wire(apiStatus: [401], tokenFails: true);
998
+ final sdk = exchangeSdk(wire);
999
+
1000
+ final res = await sdk.direct(<String, dynamic>{'path': '/thing'});
1001
+
1002
+ ok(null != res, 'the caller got an answer rather than a hang');
1003
+ equal(0, wire.api().length,
1004
+ 'nothing may go out when the first purchase failed');
1005
+ });
1006
+
1007
+
1008
+ test('auth null suppresses the credential, refusal or not', (t) async {
1009
+ // A refusal of a deliberately unauthenticated request is not an
1010
+ // expired token: buying one and retrying would transmit exactly the
1011
+ // credential the caller suppressed.
1012
+ final wire = Wire(apiStatus: [401]);
1013
+ final sdk = ProjectNameSDK(<String, dynamic>{
1014
+ 'base': EXBASE,
1015
+ 'auth': null,
1016
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
1017
+ 'system': <String, dynamic>{'fetch': wire.fetch},
1018
+ 'feature': <String, dynamic>{
1019
+ 'secrets': <String, dynamic>{
1020
+ 'active': true,
1021
+ 'name': 'refresh_token',
1022
+ 'providers': [
1023
+ <String, dynamic>{
1024
+ 'kind': 'memory',
1025
+ 'values': <String, String>{'REFRESH_TOKEN': REFRESH},
1026
+ }
1027
+ ],
1028
+ 'exchange': <String, dynamic>{'active': true},
1029
+ }
1030
+ },
1031
+ });
1032
+
1033
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
1034
+
1035
+ equal(1, wire.api().length, 'a suppressed request must not be retried');
1036
+ equal(null, wire.api()[0]['auth'],
1037
+ 'no credential may be sent when auth is suppressed');
1038
+
1039
+ // AND NO PURCHASE. resolve() runs before _withRefresh's suppression
1040
+ // check, so the refresh token used to go to the token endpoint in a
1041
+ // request body even here. Stopping the retry does not unsend it, and
1042
+ // only the token endpoint can see this.
1043
+ equal(0, wire.token().length,
1044
+ 'auth null suppressed the credential but the refresh token was '
1045
+ 'still POSTed to the exchange endpoint');
1046
+ });
1047
+
1048
+
1049
+ test('test mode buys nothing and needs no token endpoint', (t) async {
1050
+ final wire = Wire();
1051
+ final sdk = ProjectNameSDK.test(<String, dynamic>{}, <String, dynamic>{
1052
+ 'base': EXBASE,
1053
+ 'allow': <String, dynamic>{'op': 'direct,graphql'},
1054
+ 'system': <String, dynamic>{'fetch': wire.fetch},
1055
+ 'feature': <String, dynamic>{
1056
+ 'secrets': <String, dynamic>{
1057
+ 'active': true,
1058
+ 'name': 'refresh_token',
1059
+ 'providers': [
1060
+ <String, dynamic>{
1061
+ 'kind': 'memory',
1062
+ 'values': <String, String>{'REFRESH_TOKEN': REFRESH},
1063
+ }
1064
+ ],
1065
+ 'exchange': <String, dynamic>{'active': true},
1066
+ }
1067
+ },
1068
+ });
1069
+
1070
+ await sdk.direct(<String, dynamic>{'path': '/thing'});
1071
+
1072
+ equal(0, wire.sent, 'test mode must not do IO');
1073
+ // A deterministic placeholder, so offline suites need no
1074
+ // configuration.
1075
+ equal('test-access_token', secretsOf(sdk).credential);
1076
+ });
1077
+
1078
+ });
1079
+ }
1080
+
1081
+
1082
+ // A store whose secret is REVOKED partway through the run.
1083
+ // Absent until `present` is set, then answers: a secret provisioned while
1084
+ // the client is already live.
1085
+ class LateProvider extends Provider {
1086
+ bool present = false;
1087
+ int asked = 0;
1088
+
1089
+ @override
1090
+ FutureOr<String?> lookup(String _name) {
1091
+ asked++;
1092
+ return present ? 'LATEKEY01' : null;
1093
+ }
1094
+
1095
+ @override
1096
+ String describe() => 'late:test';
1097
+ }
1098
+
1099
+ class RetractingProvider extends Provider {
1100
+ bool revoked = false;
1101
+
1102
+ @override
1103
+ FutureOr<String?> lookup(String _name) => revoked ? null : 'LIVEKEY01';
1104
+
1105
+ @override
1106
+ String describe() => 'retracting:test';
1107
+ }