@voxgig/sdkgen 4.9.0 → 4.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1159) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/helpers/manifest.d.ts +2 -1
  3. package/dist/helpers/manifest.js +75 -1
  4. package/dist/helpers/manifest.js.map +1 -1
  5. package/dist/helpers/modelNames.d.ts +7 -0
  6. package/dist/helpers/modelNames.js +232 -0
  7. package/dist/helpers/modelNames.js.map +1 -0
  8. package/dist/helpers/naming.d.ts +2 -1
  9. package/dist/helpers/naming.js +45 -1
  10. package/dist/helpers/naming.js.map +1 -1
  11. package/dist/sdkgen.d.ts +3 -2
  12. package/dist/sdkgen.js +12 -3
  13. package/dist/sdkgen.js.map +1 -1
  14. package/dist/testkit.d.ts +2 -0
  15. package/dist/testkit.js +80 -24
  16. package/dist/testkit.js.map +1 -1
  17. package/dist/tsconfig.tsbuildinfo +1 -1
  18. package/dist/utility.js +17 -7
  19. package/dist/utility.js.map +1 -1
  20. package/package.json +1 -1
  21. package/project/.sdk/model/feature/secrets.aon +1001 -0
  22. package/project/.sdk/model/target/c.aon +15 -2
  23. package/project/.sdk/model/target/clojure.aon +10 -0
  24. package/project/.sdk/model/target/cpp.aon +18 -0
  25. package/project/.sdk/model/target/csharp.aon +5 -0
  26. package/project/.sdk/model/target/elixir.aon +6 -0
  27. package/project/.sdk/model/target/java.aon +5 -0
  28. package/project/.sdk/model/target/js.aon +5 -0
  29. package/project/.sdk/model/target/kotlin.aon +7 -0
  30. package/project/.sdk/model/target/lua.aon +17 -0
  31. package/project/.sdk/model/target/ocaml.aon +48 -5
  32. package/project/.sdk/model/target/perl.aon +5 -0
  33. package/project/.sdk/model/target/php.aon +5 -0
  34. package/project/.sdk/model/target/rb.aon +5 -0
  35. package/project/.sdk/model/target/rust.aon +5 -0
  36. package/project/.sdk/model/target/scala.aon +38 -2
  37. package/project/.sdk/model/target/swift.aon +10 -0
  38. package/project/.sdk/model/target/zig.aon +30 -4
  39. package/project/.sdk/src/cmp/c/Config_c.ts +338 -1
  40. package/project/.sdk/src/cmp/c/Main_c.ts +57 -2
  41. package/project/.sdk/src/cmp/c/Package_c.ts +12 -2
  42. package/project/.sdk/src/cmp/clojure/Config_clojure.ts +51 -4
  43. package/project/.sdk/src/cmp/clojure/Main_clojure.ts +43 -1
  44. package/project/.sdk/src/cmp/clojure/Package_clojure.ts +17 -1
  45. package/project/.sdk/src/cmp/clojure/featureextra_clojure.ts +130 -0
  46. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +324 -2
  47. package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +2 -0
  48. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +62 -4
  49. package/project/.sdk/src/cmp/cpp/Package_cpp.ts +10 -0
  50. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +70 -0
  51. package/project/.sdk/src/cmp/csharp/Main_csharp.ts +9 -1
  52. package/project/.sdk/src/cmp/csharp/Package_csharp.ts +25 -1
  53. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +68 -2
  54. package/project/.sdk/src/cmp/elixir/Main_elixir.ts +16 -0
  55. package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +14 -1
  56. package/project/.sdk/src/cmp/java/Config_java.ts +84 -2
  57. package/project/.sdk/src/cmp/java/Main_java.ts +10 -1
  58. package/project/.sdk/src/cmp/js/Config_js.ts +75 -8
  59. package/project/.sdk/src/cmp/js/Main_js.ts +66 -2
  60. package/project/.sdk/src/cmp/js/fragment/Config.data.fragment.js +18 -1
  61. package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +18 -1
  62. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +7 -0
  63. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +63 -1
  64. package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +32 -2
  65. package/project/.sdk/src/cmp/lua/Config_lua.ts +88 -0
  66. package/project/.sdk/src/cmp/lua/Gitignore_lua.ts +4 -0
  67. package/project/.sdk/src/cmp/lua/Main_lua.ts +93 -3
  68. package/project/.sdk/src/cmp/lua/Package_lua.ts +60 -0
  69. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +10 -0
  70. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +183 -5
  71. package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +160 -3
  72. package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +25 -1
  73. package/project/.sdk/src/cmp/perl/Config_perl.ts +129 -3
  74. package/project/.sdk/src/cmp/perl/Main_perl.ts +41 -1
  75. package/project/.sdk/src/cmp/perl/Package_perl.ts +13 -1
  76. package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +1 -0
  77. package/project/.sdk/src/cmp/php/Config_php.ts +105 -1
  78. package/project/.sdk/src/cmp/php/Gitignore_php.ts +4 -2
  79. package/project/.sdk/src/cmp/php/Main_php.ts +16 -1
  80. package/project/.sdk/src/cmp/rb/Config_rb.ts +92 -2
  81. package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +11 -1
  82. package/project/.sdk/src/cmp/rb/Main_rb.ts +7 -1
  83. package/project/.sdk/src/cmp/rust/Main_rust.ts +99 -1
  84. package/project/.sdk/src/cmp/rust/Package_rust.ts +72 -6
  85. package/project/.sdk/src/cmp/scala/Config_scala.ts +44 -0
  86. package/project/.sdk/src/cmp/scala/Main_scala.ts +68 -1
  87. package/project/.sdk/src/cmp/swift/Config_swift.ts +62 -1
  88. package/project/.sdk/src/cmp/swift/Main_swift.ts +35 -1
  89. package/project/.sdk/src/cmp/swift/Package_swift.ts +81 -11
  90. package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +58 -44
  91. package/project/.sdk/src/cmp/swift/utility_swift.ts +22 -0
  92. package/project/.sdk/src/cmp/zig/Config_zig.ts +130 -9
  93. package/project/.sdk/src/cmp/zig/Main_zig.ts +208 -4
  94. package/project/.sdk/{tm/zig/build.zig → src/cmp/zig/fragment/Build.fragment.zig} +7 -0
  95. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +25 -0
  96. package/project/.sdk/{tm/zig/root.zig → src/cmp/zig/fragment/Root.fragment.zig} +9 -0
  97. package/project/.sdk/tm/c/Makefile +58 -9
  98. package/project/.sdk/tm/c/core/sdk.h +10 -0
  99. package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +133 -0
  100. package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +39 -0
  101. package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +83 -0
  102. package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +48 -0
  103. package/project/.sdk/tm/c/feature/secrets/plugin/config.c +413 -0
  104. package/project/.sdk/tm/c/feature/secrets/plugin/config.h +24 -0
  105. package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +264 -0
  106. package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +29 -0
  107. package/project/.sdk/tm/c/feature/secrets/plugin/env.c +220 -0
  108. package/project/.sdk/tm/c/feature/secrets/plugin/env.h +19 -0
  109. package/project/.sdk/tm/c/feature/secrets/plugin/export.c +97 -0
  110. package/project/.sdk/tm/c/feature/secrets/plugin/export.h +19 -0
  111. package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +237 -0
  112. package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +17 -0
  113. package/project/.sdk/tm/c/feature/secrets/plugin/host.c +1494 -0
  114. package/project/.sdk/tm/c/feature/secrets/plugin/host.h +135 -0
  115. package/project/.sdk/tm/c/feature/secrets/plugin/order.c +288 -0
  116. package/project/.sdk/tm/c/feature/secrets/plugin/order.h +18 -0
  117. package/project/.sdk/tm/c/feature/secrets/plugin/point.c +141 -0
  118. package/project/.sdk/tm/c/feature/secrets/plugin/point.h +80 -0
  119. package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +170 -0
  120. package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +52 -0
  121. package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +98 -0
  122. package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +18 -0
  123. package/project/.sdk/tm/c/feature/secrets/plugin/types.c +150 -0
  124. package/project/.sdk/tm/c/feature/secrets/plugin/types.h +104 -0
  125. package/project/.sdk/tm/c/feature/secrets/plugin/value.c +649 -0
  126. package/project/.sdk/tm/c/feature/secrets/plugin/value.h +135 -0
  127. package/project/.sdk/tm/c/feature/secrets/plugin/version.c +151 -0
  128. package/project/.sdk/tm/c/feature/secrets/plugin/version.h +34 -0
  129. package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +281 -0
  130. package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +207 -0
  131. package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +164 -0
  132. package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +75 -0
  133. package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +141 -0
  134. package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +144 -0
  135. package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +177 -0
  136. package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +239 -0
  137. package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +599 -0
  138. package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +165 -0
  139. package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +194 -0
  140. package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +233 -0
  141. package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +125 -0
  142. package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +125 -0
  143. package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +270 -0
  144. package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +382 -0
  145. package/project/.sdk/tm/c/feature/secrets/plugins/support.h +153 -0
  146. package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +420 -0
  147. package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +86 -0
  148. package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +721 -0
  149. package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +813 -0
  150. package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +944 -0
  151. package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +490 -0
  152. package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +442 -0
  153. package/project/.sdk/tm/c/feature/secrets.c +797 -0
  154. package/project/.sdk/tm/c/feature/secrets.h +46 -0
  155. package/project/.sdk/tm/c/tests/feature/secrets/secrets_test.c +802 -0
  156. package/project/.sdk/tm/c/tests/vendor/omni/json.c +2 -2
  157. package/project/.sdk/tm/c/tests/vendor/omni/omni.h +2 -2
  158. package/project/.sdk/tm/c/tests/vendor/omni/runner.c +2 -2
  159. package/project/.sdk/tm/c/tests/vendor/omni/util.c +2 -2
  160. package/project/.sdk/tm/clojure/Makefile +3 -0
  161. package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +530 -0
  162. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/capability.clj +84 -0
  163. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/catalog.clj +44 -0
  164. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/config.clj +240 -0
  165. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/depend.clj +199 -0
  166. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/env.clj +111 -0
  167. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/export.clj +46 -0
  168. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/graph.clj +122 -0
  169. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/host.clj +1018 -0
  170. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/json.clj +106 -0
  171. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/order.clj +157 -0
  172. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/point.clj +77 -0
  173. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/ref.clj +92 -0
  174. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/resolve.clj +46 -0
  175. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/types.clj +177 -0
  176. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin/version.clj +90 -0
  177. package/project/.sdk/tm/clojure/feature/secrets/voxgig/plugin.clj +40 -0
  178. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/addr.clj +111 -0
  179. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/chain.clj +297 -0
  180. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/core.clj +192 -0
  181. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/json.clj +261 -0
  182. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/aws.clj +165 -0
  183. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/azuresecrets.clj +111 -0
  184. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/boru.clj +107 -0
  185. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/doppler.clj +61 -0
  186. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/gcpsecrets.clj +88 -0
  187. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/hashicorp.clj +115 -0
  188. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/httpjson.clj +214 -0
  189. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/infisical.clj +80 -0
  190. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/onepassword.clj +93 -0
  191. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/proc.clj +63 -0
  192. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/secretspec.clj +99 -0
  193. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/plugins/sigv4.clj +142 -0
  194. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/provider.clj +25 -0
  195. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto/providers.clj +196 -0
  196. package/project/.sdk/tm/clojure/feature/secrets/voxgig/sekreto.clj +113 -0
  197. package/project/.sdk/tm/clojure/src/sdk/core.clj +9 -0
  198. package/project/.sdk/tm/clojure/src/sdk/features.clj +13 -1
  199. package/project/.sdk/tm/clojure/test/sdk/test/feature/secrets.clj +581 -0
  200. package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +103 -14
  201. package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/json.clj +2 -2
  202. package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/runner.clj +2 -2
  203. package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/util.clj +2 -2
  204. package/project/.sdk/tm/cpp/Makefile +48 -4
  205. package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +121 -0
  206. package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +41 -0
  207. package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +60 -0
  208. package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +66 -0
  209. package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +376 -0
  210. package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +29 -0
  211. package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +230 -0
  212. package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +31 -0
  213. package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +205 -0
  214. package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +25 -0
  215. package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +84 -0
  216. package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +24 -0
  217. package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +222 -0
  218. package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +21 -0
  219. package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +1200 -0
  220. package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +250 -0
  221. package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +250 -0
  222. package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +22 -0
  223. package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +110 -0
  224. package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +79 -0
  225. package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +136 -0
  226. package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +60 -0
  227. package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +82 -0
  228. package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +25 -0
  229. package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +90 -0
  230. package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +65 -0
  231. package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +491 -0
  232. package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +120 -0
  233. package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +143 -0
  234. package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +36 -0
  235. package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +248 -0
  236. package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +30 -0
  237. package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +149 -0
  238. package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +23 -0
  239. package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +117 -0
  240. package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +27 -0
  241. package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +180 -0
  242. package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +52 -0
  243. package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +87 -0
  244. package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +23 -0
  245. package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +121 -0
  246. package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +23 -0
  247. package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +153 -0
  248. package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +23 -0
  249. package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +666 -0
  250. package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +160 -0
  251. package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +123 -0
  252. package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +23 -0
  253. package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +125 -0
  254. package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +23 -0
  255. package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +223 -0
  256. package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +47 -0
  257. package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +104 -0
  258. package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +27 -0
  259. package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +242 -0
  260. package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +58 -0
  261. package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +260 -0
  262. package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +47 -0
  263. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +476 -0
  264. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +89 -0
  265. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +312 -0
  266. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +263 -0
  267. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +354 -0
  268. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +111 -0
  269. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +613 -0
  270. package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +227 -0
  271. package/project/.sdk/tm/cpp/feature/secrets.hpp +745 -0
  272. package/project/.sdk/tm/cpp/test/feature/secrets/secrets_test.cpp +808 -0
  273. package/project/.sdk/tm/cpp/test/vendor/omni/json.hpp +2 -2
  274. package/project/.sdk/tm/cpp/test/vendor/omni/omni.hpp +2 -2
  275. package/project/.sdk/tm/cpp/test/vendor/omni/util.hpp +2 -2
  276. package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +915 -0
  277. package/project/.sdk/tm/csharp/feature/secrets/plugin/Capability.cs +196 -0
  278. package/project/.sdk/tm/csharp/feature/secrets/plugin/Catalog.cs +70 -0
  279. package/project/.sdk/tm/csharp/feature/secrets/plugin/Config.cs +505 -0
  280. package/project/.sdk/tm/csharp/feature/secrets/plugin/Definition.cs +52 -0
  281. package/project/.sdk/tm/csharp/feature/secrets/plugin/Depend.cs +285 -0
  282. package/project/.sdk/tm/csharp/feature/secrets/plugin/Entry.cs +55 -0
  283. package/project/.sdk/tm/csharp/feature/secrets/plugin/Env.cs +228 -0
  284. package/project/.sdk/tm/csharp/feature/secrets/plugin/Export.cs +102 -0
  285. package/project/.sdk/tm/csharp/feature/secrets/plugin/Graph.cs +256 -0
  286. package/project/.sdk/tm/csharp/feature/secrets/plugin/Host.cs +1379 -0
  287. package/project/.sdk/tm/csharp/feature/secrets/plugin/Inst.cs +177 -0
  288. package/project/.sdk/tm/csharp/feature/secrets/plugin/Json.cs +326 -0
  289. package/project/.sdk/tm/csharp/feature/secrets/plugin/Order.cs +295 -0
  290. package/project/.sdk/tm/csharp/feature/secrets/plugin/Plugin.cs +113 -0
  291. package/project/.sdk/tm/csharp/feature/secrets/plugin/Point.cs +177 -0
  292. package/project/.sdk/tm/csharp/feature/secrets/plugin/Refs.cs +198 -0
  293. package/project/.sdk/tm/csharp/feature/secrets/plugin/Resolve.cs +95 -0
  294. package/project/.sdk/tm/csharp/feature/secrets/plugin/Types.cs +351 -0
  295. package/project/.sdk/tm/csharp/feature/secrets/plugin/Version.cs +196 -0
  296. package/project/.sdk/tm/csharp/feature/secrets/plugins/Aws.cs +325 -0
  297. package/project/.sdk/tm/csharp/feature/secrets/plugins/AzureSecrets.cs +195 -0
  298. package/project/.sdk/tm/csharp/feature/secrets/plugins/Boru.cs +192 -0
  299. package/project/.sdk/tm/csharp/feature/secrets/plugins/Child.cs +75 -0
  300. package/project/.sdk/tm/csharp/feature/secrets/plugins/Doppler.cs +112 -0
  301. package/project/.sdk/tm/csharp/feature/secrets/plugins/GcpSecrets.cs +167 -0
  302. package/project/.sdk/tm/csharp/feature/secrets/plugins/Hashicorp.cs +221 -0
  303. package/project/.sdk/tm/csharp/feature/secrets/plugins/HttpJson.cs +213 -0
  304. package/project/.sdk/tm/csharp/feature/secrets/plugins/Infisical.cs +155 -0
  305. package/project/.sdk/tm/csharp/feature/secrets/plugins/OnePassword.cs +169 -0
  306. package/project/.sdk/tm/csharp/feature/secrets/plugins/SecretSpec.cs +172 -0
  307. package/project/.sdk/tm/csharp/feature/secrets/plugins/Sigv4.cs +240 -0
  308. package/project/.sdk/tm/csharp/feature/secrets/sekreto/Json.cs +192 -0
  309. package/project/.sdk/tm/csharp/feature/secrets/sekreto/Providers.cs +503 -0
  310. package/project/.sdk/tm/csharp/feature/secrets/sekreto/Sekreto.cs +775 -0
  311. package/project/.sdk/tm/csharp/test/feature/secrets/SecretsFeatureTest.cs +1787 -0
  312. package/project/.sdk/tm/csharp/test/vendor/omni/Runner.cs +2 -2
  313. package/project/.sdk/tm/csharp/test/vendor/omni/Util.cs +2 -2
  314. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +10 -0
  315. package/project/.sdk/tm/csharp/utility/struct/Struct.cs +1 -1
  316. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/capability.ex +118 -0
  317. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/catalog.ex +56 -0
  318. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/config.ex +320 -0
  319. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/depend.ex +205 -0
  320. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/env.ex +166 -0
  321. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/error.ex +13 -0
  322. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/export.ex +73 -0
  323. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/graph.ex +185 -0
  324. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/host.ex +1182 -0
  325. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/inst.ex +139 -0
  326. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/json.ex +132 -0
  327. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/order.ex +179 -0
  328. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/point.ex +99 -0
  329. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/ref.ex +129 -0
  330. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/resolve.ex +59 -0
  331. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/types.ex +190 -0
  332. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/version.ex +136 -0
  333. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/plugin/voxgig_plugin.ex +42 -0
  334. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/json.ex +368 -0
  335. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/aws.ex +257 -0
  336. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/azuresecrets.ex +160 -0
  337. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/boru.ex +127 -0
  338. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/doppler.ex +96 -0
  339. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/gcpsecrets.ex +124 -0
  340. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/hashicorp.ex +162 -0
  341. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/http.ex +627 -0
  342. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/httpjson.ex +134 -0
  343. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/infisical.ex +125 -0
  344. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/onepassword.ex +152 -0
  345. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/proc.ex +80 -0
  346. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/secretspec.ex +101 -0
  347. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/plugins/sigv4.ex +176 -0
  348. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/provider.ex +67 -0
  349. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/providers.ex +536 -0
  350. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets/sekreto/sekreto.ex +662 -0
  351. package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +839 -0
  352. package/project/.sdk/tm/elixir/test/feature/secrets/secrets_test.exs +1113 -0
  353. package/project/.sdk/tm/elixir/test/vendor/omni/json.ex +2 -2
  354. package/project/.sdk/tm/elixir/test/vendor/omni/runner.ex +2 -2
  355. package/project/.sdk/tm/elixir/test/vendor/omni/util.ex +2 -2
  356. package/project/.sdk/tm/go/feature/secrets/plugin/capability.go +1 -1
  357. package/project/.sdk/tm/go/feature/secrets/plugin/catalog.go +1 -1
  358. package/project/.sdk/tm/go/feature/secrets/plugin/config.go +1 -1
  359. package/project/.sdk/tm/go/feature/secrets/plugin/depend.go +1 -1
  360. package/project/.sdk/tm/go/feature/secrets/plugin/env.go +1 -1
  361. package/project/.sdk/tm/go/feature/secrets/plugin/export.go +1 -1
  362. package/project/.sdk/tm/go/feature/secrets/plugin/graph.go +1 -1
  363. package/project/.sdk/tm/go/feature/secrets/plugin/host.go +1 -1
  364. package/project/.sdk/tm/go/feature/secrets/plugin/order.go +1 -1
  365. package/project/.sdk/tm/go/feature/secrets/plugin/point.go +1 -1
  366. package/project/.sdk/tm/go/feature/secrets/plugin/ref.go +1 -1
  367. package/project/.sdk/tm/go/feature/secrets/plugin/resolve.go +1 -1
  368. package/project/.sdk/tm/go/feature/secrets/plugin/types.go +1 -1
  369. package/project/.sdk/tm/go/feature/secrets/plugin/util.go +1 -1
  370. package/project/.sdk/tm/go/feature/secrets/plugin/version.go +1 -1
  371. package/project/.sdk/tm/go/feature/secrets/plugins/aws/aws.go +2 -2
  372. package/project/.sdk/tm/go/feature/secrets/plugins/aws/sigv4.go +2 -2
  373. package/project/.sdk/tm/go/feature/secrets/plugins/azuresecrets/azuresecrets.go +2 -2
  374. package/project/.sdk/tm/go/feature/secrets/plugins/boru/boru.go +2 -2
  375. package/project/.sdk/tm/go/feature/secrets/plugins/doppler/doppler.go +2 -2
  376. package/project/.sdk/tm/go/feature/secrets/plugins/gcpsecrets/gcpsecrets.go +2 -2
  377. package/project/.sdk/tm/go/feature/secrets/plugins/hashicorp/hashicorp.go +2 -2
  378. package/project/.sdk/tm/go/feature/secrets/plugins/httpjson/httpjson.go +2 -2
  379. package/project/.sdk/tm/go/feature/secrets/plugins/infisical/infisical.go +2 -2
  380. package/project/.sdk/tm/go/feature/secrets/plugins/onepassword/onepassword.go +2 -2
  381. package/project/.sdk/tm/go/feature/secrets/plugins/secretspec/secretspec.go +2 -2
  382. package/project/.sdk/tm/go/feature/secrets/sekreto/addr.go +2 -2
  383. package/project/.sdk/tm/go/feature/secrets/sekreto/providers.go +2 -2
  384. package/project/.sdk/tm/go/feature/secrets/sekreto/sekreto.go +2 -2
  385. package/project/.sdk/tm/go/feature/secrets_feature.go +48 -10
  386. package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +239 -0
  387. package/project/.sdk/tm/go/test/omni/omni.go +2 -2
  388. package/project/.sdk/tm/go/test/omni/util.go +2 -2
  389. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +1 -1
  390. package/project/.sdk/tm/java/feature/SecretsFeature.java +791 -0
  391. package/project/.sdk/tm/java/feature/secrets/plugin/Capability.java +181 -0
  392. package/project/.sdk/tm/java/feature/secrets/plugin/Catalog.java +64 -0
  393. package/project/.sdk/tm/java/feature/secrets/plugin/Config.java +433 -0
  394. package/project/.sdk/tm/java/feature/secrets/plugin/Definition.java +54 -0
  395. package/project/.sdk/tm/java/feature/secrets/plugin/Depend.java +292 -0
  396. package/project/.sdk/tm/java/feature/secrets/plugin/Entry.java +49 -0
  397. package/project/.sdk/tm/java/feature/secrets/plugin/Env.java +204 -0
  398. package/project/.sdk/tm/java/feature/secrets/plugin/Export.java +93 -0
  399. package/project/.sdk/tm/java/feature/secrets/plugin/Graph.java +244 -0
  400. package/project/.sdk/tm/java/feature/secrets/plugin/Host.java +1196 -0
  401. package/project/.sdk/tm/java/feature/secrets/plugin/Inst.java +173 -0
  402. package/project/.sdk/tm/java/feature/secrets/plugin/Json.java +339 -0
  403. package/project/.sdk/tm/java/feature/secrets/plugin/Order.java +274 -0
  404. package/project/.sdk/tm/java/feature/secrets/plugin/Plugin.java +94 -0
  405. package/project/.sdk/tm/java/feature/secrets/plugin/PluginException.java +37 -0
  406. package/project/.sdk/tm/java/feature/secrets/plugin/Point.java +172 -0
  407. package/project/.sdk/tm/java/feature/secrets/plugin/Refs.java +171 -0
  408. package/project/.sdk/tm/java/feature/secrets/plugin/Resolve.java +89 -0
  409. package/project/.sdk/tm/java/feature/secrets/plugin/Types.java +232 -0
  410. package/project/.sdk/tm/java/feature/secrets/plugin/Version.java +198 -0
  411. package/project/.sdk/tm/java/feature/secrets/sekreto/Addr.java +142 -0
  412. package/project/.sdk/tm/java/feature/secrets/sekreto/Builtins.java +229 -0
  413. package/project/.sdk/tm/java/feature/secrets/sekreto/Json.java +302 -0
  414. package/project/.sdk/tm/java/feature/secrets/sekreto/Provider.java +21 -0
  415. package/project/.sdk/tm/java/feature/secrets/sekreto/Sekreto.java +698 -0
  416. package/project/.sdk/tm/java/feature/secrets/sekreto/Support.java +100 -0
  417. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Aws.java +294 -0
  418. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Azuresecrets.java +164 -0
  419. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Boru.java +161 -0
  420. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Doppler.java +103 -0
  421. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Gcpsecrets.java +143 -0
  422. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Hashicorp.java +186 -0
  423. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Httpjson.java +192 -0
  424. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Infisical.java +139 -0
  425. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Onepassword.java +143 -0
  426. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Proc.java +80 -0
  427. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Secretspec.java +138 -0
  428. package/project/.sdk/tm/java/feature/secrets/sekreto/plugins/Sigv4.java +243 -0
  429. package/project/.sdk/tm/java/test/feature/secrets/SecretsFeatureTest.java +1050 -0
  430. package/project/.sdk/tm/java/test/vendor/omni/Json.java +2 -2
  431. package/project/.sdk/tm/java/test/vendor/omni/Runner.java +2 -2
  432. package/project/.sdk/tm/java/test/vendor/omni/Util.java +2 -2
  433. package/project/.sdk/tm/java/utility/MakeOptions.java +7 -0
  434. package/project/.sdk/tm/java/utility/struct/Struct.java +1 -1
  435. package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +454 -0
  436. package/project/.sdk/tm/js/src/feature/secrets/plugin/capability.js +119 -0
  437. package/project/.sdk/tm/js/src/feature/secrets/plugin/catalog.js +42 -0
  438. package/project/.sdk/tm/js/src/feature/secrets/plugin/config.js +323 -0
  439. package/project/.sdk/tm/js/src/feature/secrets/plugin/depend.js +196 -0
  440. package/project/.sdk/tm/js/src/feature/secrets/plugin/env.js +120 -0
  441. package/project/.sdk/tm/js/src/feature/secrets/plugin/export.js +48 -0
  442. package/project/.sdk/tm/js/src/feature/secrets/plugin/graph.js +150 -0
  443. package/project/.sdk/tm/js/src/feature/secrets/plugin/host.js +949 -0
  444. package/project/.sdk/tm/js/src/feature/secrets/plugin/index.js +35 -0
  445. package/project/.sdk/tm/js/src/feature/secrets/plugin/order.js +165 -0
  446. package/project/.sdk/tm/js/src/feature/secrets/plugin/point.js +94 -0
  447. package/project/.sdk/tm/js/src/feature/secrets/plugin/ref.js +106 -0
  448. package/project/.sdk/tm/js/src/feature/secrets/plugin/resolve.js +59 -0
  449. package/project/.sdk/tm/js/src/feature/secrets/plugin/types.js +79 -0
  450. package/project/.sdk/tm/js/src/feature/secrets/plugin/version.js +100 -0
  451. package/project/.sdk/tm/js/src/feature/secrets/sekreto/Sekreto.js +574 -0
  452. package/project/.sdk/tm/js/src/feature/secrets/sekreto/index.js +56 -0
  453. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/aws.js +188 -0
  454. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/azuresecrets.js +135 -0
  455. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/boru.js +128 -0
  456. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/doppler.js +67 -0
  457. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/gcpsecrets.js +115 -0
  458. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/hashicorp.js +139 -0
  459. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/httpjson.js +93 -0
  460. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/infisical.js +99 -0
  461. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/onepassword.js +111 -0
  462. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/secretspec.js +105 -0
  463. package/project/.sdk/tm/js/src/feature/secrets/sekreto/plugins/sigv4.js +157 -0
  464. package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/addr.js +119 -0
  465. package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/builtin.js +39 -0
  466. package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/dotenv.js +40 -0
  467. package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/env.js +21 -0
  468. package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/file.js +42 -0
  469. package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/memory.js +18 -0
  470. package/project/.sdk/tm/js/src/feature/secrets/sekreto/provider/support.js +151 -0
  471. package/project/.sdk/tm/js/src/utility/StructUtility.js +1 -1
  472. package/project/.sdk/tm/js/test/feature/secrets/Secrets.test.js +1019 -0
  473. package/project/.sdk/tm/js/test/vendor/omni/index.js +1 -1
  474. package/project/.sdk/tm/js/test/vendor/omni/runner.js +1 -1
  475. package/project/.sdk/tm/js/test/vendor/omni/util.js +1 -1
  476. package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +684 -0
  477. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Capability.kt +117 -0
  478. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Catalog.kt +40 -0
  479. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Config.kt +322 -0
  480. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Depend.kt +227 -0
  481. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Env.kt +163 -0
  482. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Export.kt +57 -0
  483. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Graph.kt +179 -0
  484. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Host.kt +1131 -0
  485. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Json.kt +241 -0
  486. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Order.kt +180 -0
  487. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Plugin.kt +64 -0
  488. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Point.kt +124 -0
  489. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Refs.kt +125 -0
  490. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Resolve.kt +66 -0
  491. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Types.kt +171 -0
  492. package/project/.sdk/tm/kotlin/feature/secrets/plugin/Version.kt +132 -0
  493. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Json.kt +359 -0
  494. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Provider.kt +21 -0
  495. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Providers.kt +308 -0
  496. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Sekreto.kt +525 -0
  497. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Spec.kt +126 -0
  498. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/Support.kt +205 -0
  499. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Aws.kt +251 -0
  500. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Azuresecrets.kt +145 -0
  501. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Boru.kt +130 -0
  502. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Doppler.kt +77 -0
  503. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Gcpsecrets.kt +119 -0
  504. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Hashicorp.kt +156 -0
  505. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Httpjson.kt +219 -0
  506. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Infisical.kt +114 -0
  507. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Onepassword.kt +114 -0
  508. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Secretspec.kt +118 -0
  509. package/project/.sdk/tm/kotlin/feature/secrets/sekreto/plugins/Sigv4.kt +220 -0
  510. package/project/.sdk/tm/kotlin/test/feature/secrets/SecretsTest.kt +1217 -0
  511. package/project/.sdk/tm/kotlin/test/vendor/omni/Json.kt +2 -2
  512. package/project/.sdk/tm/kotlin/test/vendor/omni/Runner.kt +2 -2
  513. package/project/.sdk/tm/kotlin/test/vendor/omni/Util.kt +2 -2
  514. package/project/.sdk/tm/kotlin/utility/struct/Struct.kt +1 -1
  515. package/project/.sdk/tm/lua/Makefile +15 -4
  516. package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +806 -0
  517. package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +135 -0
  518. package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +63 -0
  519. package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +361 -0
  520. package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +232 -0
  521. package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +168 -0
  522. package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +66 -0
  523. package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +194 -0
  524. package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +1190 -0
  525. package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +169 -0
  526. package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +207 -0
  527. package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +106 -0
  528. package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +115 -0
  529. package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +78 -0
  530. package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +275 -0
  531. package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +131 -0
  532. package/project/.sdk/tm/lua/feature/secrets/plugin.lua +76 -0
  533. package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +124 -0
  534. package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +47 -0
  535. package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +315 -0
  536. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +228 -0
  537. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +137 -0
  538. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +124 -0
  539. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +212 -0
  540. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +89 -0
  541. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +129 -0
  542. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +154 -0
  543. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +248 -0
  544. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +113 -0
  545. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +449 -0
  546. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +198 -0
  547. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +126 -0
  548. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +93 -0
  549. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +269 -0
  550. package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +146 -0
  551. package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +346 -0
  552. package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +479 -0
  553. package/project/.sdk/tm/lua/feature/secrets_feature.lua +690 -0
  554. package/project/.sdk/tm/lua/test/extend_test.lua +105 -0
  555. package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +862 -0
  556. package/project/.sdk/tm/lua/test/vendor/omni/json.lua +2 -2
  557. package/project/.sdk/tm/lua/test/vendor/omni/regex.lua +2 -2
  558. package/project/.sdk/tm/lua/test/vendor/omni/runner.lua +2 -2
  559. package/project/.sdk/tm/lua/test/vendor/omni/util.lua +2 -2
  560. package/project/.sdk/tm/lua/utility/struct/regex.lua +1 -1
  561. package/project/.sdk/tm/lua/utility/struct/struct.lua +1 -1
  562. package/project/.sdk/tm/ocaml/Makefile +52 -8
  563. package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +106 -0
  564. package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +43 -0
  565. package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +349 -0
  566. package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +143 -0
  567. package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +217 -0
  568. package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +197 -0
  569. package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +89 -0
  570. package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +227 -0
  571. package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +1112 -0
  572. package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +202 -0
  573. package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +138 -0
  574. package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +126 -0
  575. package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +62 -0
  576. package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +97 -0
  577. package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +387 -0
  578. package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +139 -0
  579. package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +171 -0
  580. package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +116 -0
  581. package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +95 -0
  582. package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +149 -0
  583. package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +61 -0
  584. package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +93 -0
  585. package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +120 -0
  586. package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +421 -0
  587. package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +66 -0
  588. package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +85 -0
  589. package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +112 -0
  590. package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +97 -0
  591. package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +68 -0
  592. package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +259 -0
  593. package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +44 -0
  594. package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +358 -0
  595. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +376 -0
  596. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +647 -0
  597. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +299 -0
  598. package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +347 -0
  599. package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +453 -0
  600. package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +670 -0
  601. package/project/.sdk/tm/ocaml/test/vendor/omni/omni.ml +2 -2
  602. package/project/.sdk/tm/perl/Makefile +6 -1
  603. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Capability.pm +141 -0
  604. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Catalog.pm +65 -0
  605. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Config.pm +346 -0
  606. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Depend.pm +216 -0
  607. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Env.pm +176 -0
  608. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Export.pm +59 -0
  609. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Graph.pm +157 -0
  610. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Host.pm +1139 -0
  611. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Order.pm +187 -0
  612. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Point.pm +100 -0
  613. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Ref.pm +119 -0
  614. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Resolve.pm +71 -0
  615. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Types.pm +251 -0
  616. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin/Version.pm +122 -0
  617. package/project/.sdk/tm/perl/feature/secrets/plugin/Voxgig/Plugin.pm +70 -0
  618. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Aws.pm +263 -0
  619. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Azuresecrets.pm +159 -0
  620. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Boru.pm +180 -0
  621. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Doppler.pm +92 -0
  622. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Gcpsecrets.pm +138 -0
  623. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Hashicorp.pm +213 -0
  624. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Httpjson.pm +173 -0
  625. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Infisical.pm +132 -0
  626. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Onepassword.pm +132 -0
  627. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Proc.pm +102 -0
  628. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Secretspec.pm +132 -0
  629. package/project/.sdk/tm/perl/feature/secrets/plugins/Voxgig/Sekreto/Plugins/Sigv4.pm +190 -0
  630. package/project/.sdk/tm/perl/feature/secrets/sekreto/Voxgig/Sekreto/Addr.pm +124 -0
  631. package/project/.sdk/tm/perl/feature/secrets/sekreto/Voxgig/Sekreto/Providers.pm +301 -0
  632. package/project/.sdk/tm/perl/feature/secrets/sekreto/Voxgig/Sekreto.pm +605 -0
  633. package/project/.sdk/tm/perl/feature/secrets_feature.pm +696 -0
  634. package/project/.sdk/tm/perl/lib/Voxgig/Struct.pm +1 -1
  635. package/project/.sdk/tm/perl/t/feature/secrets/secrets.t +1004 -0
  636. package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Runner.pm +1 -1
  637. package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Util.pm +1 -1
  638. package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni.pm +1 -1
  639. package/project/.sdk/tm/php/feature/SecretsFeature.php +694 -0
  640. package/project/.sdk/tm/php/feature/secrets/plugin/Capability.php +226 -0
  641. package/project/.sdk/tm/php/feature/secrets/plugin/Catalog.php +75 -0
  642. package/project/.sdk/tm/php/feature/secrets/plugin/Config.php +444 -0
  643. package/project/.sdk/tm/php/feature/secrets/plugin/Depend.php +262 -0
  644. package/project/.sdk/tm/php/feature/secrets/plugin/Env.php +202 -0
  645. package/project/.sdk/tm/php/feature/secrets/plugin/Export.php +74 -0
  646. package/project/.sdk/tm/php/feature/secrets/plugin/Graph.php +195 -0
  647. package/project/.sdk/tm/php/feature/secrets/plugin/Host.php +1514 -0
  648. package/project/.sdk/tm/php/feature/secrets/plugin/Order.php +260 -0
  649. package/project/.sdk/tm/php/feature/secrets/plugin/Point.php +129 -0
  650. package/project/.sdk/tm/php/feature/secrets/plugin/Ref.php +169 -0
  651. package/project/.sdk/tm/php/feature/secrets/plugin/Resolve.php +79 -0
  652. package/project/.sdk/tm/php/feature/secrets/plugin/Types.php +348 -0
  653. package/project/.sdk/tm/php/feature/secrets/plugin/Version.php +163 -0
  654. package/project/.sdk/tm/php/feature/secrets/plugin/plugin.php +40 -0
  655. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/aws.php +274 -0
  656. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/azuresecrets.php +170 -0
  657. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/boru.php +163 -0
  658. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/doppler.php +106 -0
  659. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/gcpsecrets.php +144 -0
  660. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/hashicorp.php +194 -0
  661. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/httpjson.php +113 -0
  662. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/infisical.php +131 -0
  663. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/onepassword.php +150 -0
  664. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/runcmd.php +101 -0
  665. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/secretspec.php +146 -0
  666. package/project/.sdk/tm/php/feature/secrets/sekreto/plugins/sigv4.php +161 -0
  667. package/project/.sdk/tm/php/feature/secrets/sekreto/src/Addr.php +129 -0
  668. package/project/.sdk/tm/php/feature/secrets/sekreto/src/Providers.php +322 -0
  669. package/project/.sdk/tm/php/feature/secrets/sekreto/src/Sekreto.php +724 -0
  670. package/project/.sdk/tm/php/test/feature/secrets/SecretsTest.php +1165 -0
  671. package/project/.sdk/tm/php/test/vendor/omni/Runner.php +2 -2
  672. package/project/.sdk/tm/php/test/vendor/omni/Util.php +2 -2
  673. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/__init__.py +1 -1
  674. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/capability.py +1 -1
  675. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/catalog.py +1 -1
  676. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/config.py +1 -1
  677. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/depend.py +1 -1
  678. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/env.py +1 -1
  679. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/export.py +1 -1
  680. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/graph.py +1 -1
  681. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/host.py +1 -1
  682. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/order.py +1 -1
  683. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/point.py +1 -1
  684. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/ref.py +1 -1
  685. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/resolve.py +1 -1
  686. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/types.py +1 -1
  687. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/version.py +1 -1
  688. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/__init__.py +2 -2
  689. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/addr.py +1 -1
  690. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/aws.py +1 -1
  691. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/azuresecrets.py +1 -1
  692. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/boru.py +1 -1
  693. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/doppler.py +1 -1
  694. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/gcpsecrets.py +1 -1
  695. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/hashicorp.py +1 -1
  696. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/httpjson.py +1 -1
  697. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/infisical.py +1 -1
  698. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/onepassword.py +1 -1
  699. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/secretspec.py +1 -1
  700. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/sigv4.py +1 -1
  701. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/providers.py +1 -1
  702. package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/sekreto.py +1 -1
  703. package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +28 -6
  704. package/project/.sdk/tm/py/pkg/utility/voxgig_struct/voxgig_struct.py +1 -1
  705. package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +114 -0
  706. package/project/.sdk/tm/py/test/voxgig_omni/__init__.py +1 -1
  707. package/project/.sdk/tm/py/test/voxgig_omni/runner.py +1 -1
  708. package/project/.sdk/tm/py/test/voxgig_omni/util.py +1 -1
  709. package/project/.sdk/tm/rb/Makefile +7 -1
  710. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/capability.rb +118 -0
  711. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/catalog.rb +55 -0
  712. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/config.rb +315 -0
  713. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/depend.rb +197 -0
  714. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/env.rb +143 -0
  715. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/export.rb +52 -0
  716. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/graph.rb +147 -0
  717. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/host.rb +1079 -0
  718. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/order.rb +169 -0
  719. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/point.rb +93 -0
  720. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/ref.rb +112 -0
  721. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/resolve.rb +56 -0
  722. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/types.rb +99 -0
  723. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin/version.rb +106 -0
  724. package/project/.sdk/tm/rb/feature/secrets/voxgig_plugin.rb +24 -0
  725. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/addr.rb +111 -0
  726. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/aws.rb +200 -0
  727. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/azuresecrets.rb +130 -0
  728. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/boru.rb +121 -0
  729. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/doppler.rb +66 -0
  730. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/gcpsecrets.rb +107 -0
  731. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/hashicorp.rb +137 -0
  732. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/httpjson.rb +136 -0
  733. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/infisical.rb +97 -0
  734. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/onepassword.rb +104 -0
  735. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/secretspec.rb +113 -0
  736. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/plugins/sigv4.rb +127 -0
  737. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/providers.rb +210 -0
  738. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto/sekreto.rb +465 -0
  739. package/project/.sdk/tm/rb/feature/secrets/voxgig_sekreto.rb +30 -0
  740. package/project/.sdk/tm/rb/feature/secrets_feature.rb +583 -0
  741. package/project/.sdk/tm/rb/test/feature/secrets/secrets_feature_test.rb +935 -0
  742. package/project/.sdk/tm/rb/test/vendor/omni/runner.rb +2 -2
  743. package/project/.sdk/tm/rb/test/vendor/omni/util.rb +2 -2
  744. package/project/.sdk/tm/rb/test/vendor/omni/voxgig_omni.rb +2 -2
  745. package/project/.sdk/tm/rust/feature/secrets/plugin/capability.rs +130 -0
  746. package/project/.sdk/tm/rust/feature/secrets/plugin/catalog.rs +114 -0
  747. package/project/.sdk/tm/rust/feature/secrets/plugin/config.rs +416 -0
  748. package/project/.sdk/tm/rust/feature/secrets/plugin/depend.rs +245 -0
  749. package/project/.sdk/tm/rust/feature/secrets/plugin/env.rs +200 -0
  750. package/project/.sdk/tm/rust/feature/secrets/plugin/export.rs +90 -0
  751. package/project/.sdk/tm/rust/feature/secrets/plugin/graph.rs +231 -0
  752. package/project/.sdk/tm/rust/feature/secrets/plugin/host.rs +1565 -0
  753. package/project/.sdk/tm/rust/feature/secrets/plugin/mod.rs +18 -0
  754. package/project/.sdk/tm/rust/feature/secrets/plugin/order.rs +226 -0
  755. package/project/.sdk/tm/rust/feature/secrets/plugin/point.rs +137 -0
  756. package/project/.sdk/tm/rust/feature/secrets/plugin/refs.rs +148 -0
  757. package/project/.sdk/tm/rust/feature/secrets/plugin/resolve.rs +83 -0
  758. package/project/.sdk/tm/rust/feature/secrets/plugin/types.rs +132 -0
  759. package/project/.sdk/tm/rust/feature/secrets/plugin/value.rs +433 -0
  760. package/project/.sdk/tm/rust/feature/secrets/plugin/version.rs +174 -0
  761. package/project/.sdk/tm/rust/feature/secrets/plugins/aws/crypto.rs +146 -0
  762. package/project/.sdk/tm/rust/feature/secrets/plugins/aws/sigv4.rs +230 -0
  763. package/project/.sdk/tm/rust/feature/secrets/plugins/aws.rs +328 -0
  764. package/project/.sdk/tm/rust/feature/secrets/plugins/azuresecrets.rs +184 -0
  765. package/project/.sdk/tm/rust/feature/secrets/plugins/boru.rs +172 -0
  766. package/project/.sdk/tm/rust/feature/secrets/plugins/doppler.rs +102 -0
  767. package/project/.sdk/tm/rust/feature/secrets/plugins/gcpsecrets.rs +153 -0
  768. package/project/.sdk/tm/rust/feature/secrets/plugins/hashicorp.rs +228 -0
  769. package/project/.sdk/tm/rust/feature/secrets/plugins/httpjson/http.rs +537 -0
  770. package/project/.sdk/tm/rust/feature/secrets/plugins/httpjson/json.rs +300 -0
  771. package/project/.sdk/tm/rust/feature/secrets/plugins/httpjson.rs +137 -0
  772. package/project/.sdk/tm/rust/feature/secrets/plugins/infisical.rs +148 -0
  773. package/project/.sdk/tm/rust/feature/secrets/plugins/onepassword.rs +165 -0
  774. package/project/.sdk/tm/rust/feature/secrets/plugins/secretspec.rs +150 -0
  775. package/project/.sdk/tm/rust/feature/secrets/sekreto/addr.rs +144 -0
  776. package/project/.sdk/tm/rust/feature/secrets/sekreto/mod.rs +51 -0
  777. package/project/.sdk/tm/rust/feature/secrets/sekreto/providers.rs +637 -0
  778. package/project/.sdk/tm/rust/feature/secrets/sekreto/sekreto.rs +677 -0
  779. package/project/.sdk/tm/rust/feature/secrets.rs +984 -0
  780. package/project/.sdk/tm/rust/tests/feature/secrets/main.rs +1605 -0
  781. package/project/.sdk/tm/rust/tests/vendor/omni/json.rs +2 -2
  782. package/project/.sdk/tm/rust/tests/vendor/omni/mod.rs +2 -2
  783. package/project/.sdk/tm/rust/tests/vendor/omni/regex.rs +2 -2
  784. package/project/.sdk/tm/rust/tests/vendor/omni/runner.rs +2 -2
  785. package/project/.sdk/tm/rust/tests/vendor/omni/util.rs +2 -2
  786. package/project/.sdk/tm/scala/Makefile +8 -3
  787. package/project/.sdk/tm/scala/feature/SecretsFeature.scala +578 -0
  788. package/project/.sdk/tm/scala/feature/secrets/plugin/Capability.scala +96 -0
  789. package/project/.sdk/tm/scala/feature/secrets/plugin/Config.scala +270 -0
  790. package/project/.sdk/tm/scala/feature/secrets/plugin/Depend.scala +216 -0
  791. package/project/.sdk/tm/scala/feature/secrets/plugin/Env.scala +144 -0
  792. package/project/.sdk/tm/scala/feature/secrets/plugin/Export.scala +59 -0
  793. package/project/.sdk/tm/scala/feature/secrets/plugin/Graph.scala +149 -0
  794. package/project/.sdk/tm/scala/feature/secrets/plugin/Host.scala +1089 -0
  795. package/project/.sdk/tm/scala/feature/secrets/plugin/Json.scala +156 -0
  796. package/project/.sdk/tm/scala/feature/secrets/plugin/Order.scala +169 -0
  797. package/project/.sdk/tm/scala/feature/secrets/plugin/Plugin.scala +47 -0
  798. package/project/.sdk/tm/scala/feature/secrets/plugin/Point.scala +107 -0
  799. package/project/.sdk/tm/scala/feature/secrets/plugin/Refs.scala +131 -0
  800. package/project/.sdk/tm/scala/feature/secrets/plugin/Resolve.scala +53 -0
  801. package/project/.sdk/tm/scala/feature/secrets/plugin/Types.scala +110 -0
  802. package/project/.sdk/tm/scala/feature/secrets/plugin/Value.scala +157 -0
  803. package/project/.sdk/tm/scala/feature/secrets/plugin/Version.scala +136 -0
  804. package/project/.sdk/tm/scala/feature/secrets/sekreto/Json.scala +302 -0
  805. package/project/.sdk/tm/scala/feature/secrets/sekreto/Provider.scala +20 -0
  806. package/project/.sdk/tm/scala/feature/secrets/sekreto/Providers.scala +281 -0
  807. package/project/.sdk/tm/scala/feature/secrets/sekreto/Sekreto.scala +473 -0
  808. package/project/.sdk/tm/scala/feature/secrets/sekreto/Spec.scala +123 -0
  809. package/project/.sdk/tm/scala/feature/secrets/sekreto/Support.scala +216 -0
  810. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Aws.scala +225 -0
  811. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Azuresecrets.scala +124 -0
  812. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Boru.scala +104 -0
  813. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Doppler.scala +68 -0
  814. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Gcpsecrets.scala +97 -0
  815. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Hashicorp.scala +131 -0
  816. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Httpjson.scala +209 -0
  817. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Infisical.scala +98 -0
  818. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Onepassword.scala +90 -0
  819. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Secretspec.scala +95 -0
  820. package/project/.sdk/tm/scala/feature/secrets/sekreto/plugins/Sigv4.scala +214 -0
  821. package/project/.sdk/tm/scala/sdktest/feature/secrets/SecretsTestMain.scala +1042 -0
  822. package/project/.sdk/tm/scala/sdktest/vendor/omni/Json.scala +2 -2
  823. package/project/.sdk/tm/scala/sdktest/vendor/omni/Runner.scala +2 -2
  824. package/project/.sdk/tm/scala/sdktest/vendor/omni/Util.scala +2 -2
  825. package/project/.sdk/tm/scala/utility/Make.scala +7 -0
  826. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/SecretsFeature.swift +669 -0
  827. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Capability.swift +98 -0
  828. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Catalog.swift +87 -0
  829. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Config.swift +298 -0
  830. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Depend.swift +209 -0
  831. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Env.swift +191 -0
  832. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Export.swift +63 -0
  833. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Graph.swift +158 -0
  834. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Host.swift +1141 -0
  835. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Json.swift +192 -0
  836. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Order.swift +183 -0
  837. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Point.swift +130 -0
  838. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Refs.swift +141 -0
  839. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Resolve.swift +59 -0
  840. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Types.swift +125 -0
  841. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Value.swift +198 -0
  842. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugin/Version.swift +139 -0
  843. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Aws.swift +271 -0
  844. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Azuresecrets.swift +173 -0
  845. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Boru.swift +139 -0
  846. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Crypto.swift +198 -0
  847. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Doppler.swift +86 -0
  848. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Gcpsecrets.swift +129 -0
  849. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Hashicorp.swift +175 -0
  850. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Httpjson.swift +310 -0
  851. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Infisical.swift +140 -0
  852. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Onepassword.swift +118 -0
  853. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Proc.swift +105 -0
  854. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Secretspec.swift +111 -0
  855. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/plugins/Sigv4.swift +297 -0
  856. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Addr.swift +120 -0
  857. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Json.swift +452 -0
  858. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Provider.swift +25 -0
  859. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Providers.swift +689 -0
  860. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/secrets/sekreto/Sekreto.swift +755 -0
  861. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +9 -0
  862. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +30 -0
  863. package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/feature/secrets/SecretsFeatureTest.swift +1030 -0
  864. package/project/.sdk/tm/swift/Tests/vendor/omni/Json.swift +2 -2
  865. package/project/.sdk/tm/swift/Tests/vendor/omni/Runner.swift +2 -2
  866. package/project/.sdk/tm/swift/Tests/vendor/omni/Util.swift +2 -2
  867. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +35 -12
  868. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Capability.ts +1 -1
  869. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Catalog.ts +1 -1
  870. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Config.ts +1 -1
  871. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Depend.ts +1 -1
  872. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Env.ts +1 -1
  873. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Export.ts +1 -1
  874. package/project/.sdk/tm/ts/src/feature/secrets/plugin/FeatureHost.ts +1 -1
  875. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Graph.ts +1 -1
  876. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Host.ts +1 -1
  877. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Order.ts +1 -1
  878. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Point.ts +1 -1
  879. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Ref.ts +1 -1
  880. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Resolve.ts +1 -1
  881. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Types.ts +1 -1
  882. package/project/.sdk/tm/ts/src/feature/secrets/plugin/Version.ts +1 -1
  883. package/project/.sdk/tm/ts/src/feature/secrets/plugin/index.ts +1 -1
  884. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +1 -1
  885. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +1 -1
  886. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/aws.ts +1 -1
  887. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/azuresecrets.ts +1 -1
  888. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/boru.ts +1 -1
  889. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/doppler.ts +1 -1
  890. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/gcpsecrets.ts +1 -1
  891. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/hashicorp.ts +1 -1
  892. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/httpjson.ts +1 -1
  893. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/infisical.ts +1 -1
  894. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/onepassword.ts +1 -1
  895. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/secretspec.ts +1 -1
  896. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/sigv4.ts +1 -1
  897. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +1 -1
  898. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/builtin.ts +1 -1
  899. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +1 -1
  900. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +1 -1
  901. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +1 -1
  902. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +1 -1
  903. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +1 -1
  904. package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1 -1
  905. package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +73 -0
  906. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +1 -1
  907. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +1 -1
  908. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
  909. package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +137 -0
  910. package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +90 -0
  911. package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +346 -0
  912. package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +244 -0
  913. package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +201 -0
  914. package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +83 -0
  915. package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +212 -0
  916. package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +1224 -0
  917. package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +126 -0
  918. package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +228 -0
  919. package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +34 -0
  920. package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +166 -0
  921. package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +147 -0
  922. package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +74 -0
  923. package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +140 -0
  924. package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +662 -0
  925. package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +139 -0
  926. package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +294 -0
  927. package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +222 -0
  928. package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +188 -0
  929. package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +135 -0
  930. package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +170 -0
  931. package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +242 -0
  932. package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +535 -0
  933. package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +167 -0
  934. package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +202 -0
  935. package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +160 -0
  936. package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +336 -0
  937. package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +148 -0
  938. package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +259 -0
  939. package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +440 -0
  940. package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +944 -0
  941. package/project/.sdk/tm/zig/feature/secrets.zig +854 -0
  942. package/project/.sdk/tm/zig/test/feature/secrets/secrets_test.zig +891 -0
  943. package/project/.sdk/tm/zig/test/vendor/omni/omni.zig +2 -2
  944. package/project/.sdk/tm/zig/test/vendor/omni/regex.zig +2 -2
  945. package/project/.sdk/tm/zig/utility/voxgigstruct/regex.zig +2 -2
  946. package/project/.sdk/tm/zig/utility/voxgigstruct/struct.zig +2 -2
  947. package/project/sdkgen-package.json +1 -4
  948. package/src/helpers/manifest.ts +81 -1
  949. package/src/helpers/modelNames.ts +284 -0
  950. package/src/helpers/naming.ts +45 -1
  951. package/src/sdkgen.ts +11 -1
  952. package/src/testkit.ts +115 -26
  953. package/src/utility.ts +17 -7
  954. package/project/.sdk/model/target/dart.aon +0 -58
  955. package/project/.sdk/model/target/lean.aon +0 -91
  956. package/project/.sdk/model/target/seneca-provider.aon +0 -120
  957. package/project/.sdk/src/cmp/dart/Config_dart.ts +0 -176
  958. package/project/.sdk/src/cmp/dart/EntityBase_dart.ts +0 -34
  959. package/project/.sdk/src/cmp/dart/EntityOperation_dart.ts +0 -43
  960. package/project/.sdk/src/cmp/dart/EntityTypes_dart.ts +0 -207
  961. package/project/.sdk/src/cmp/dart/Entity_dart.ts +0 -87
  962. package/project/.sdk/src/cmp/dart/Gitignore_dart.ts +0 -34
  963. package/project/.sdk/src/cmp/dart/MainEntity_dart.ts +0 -33
  964. package/project/.sdk/src/cmp/dart/Main_dart.ts +0 -106
  965. package/project/.sdk/src/cmp/dart/Package_dart.ts +0 -82
  966. package/project/.sdk/src/cmp/dart/ReadmeEntity_dart.ts +0 -177
  967. package/project/.sdk/src/cmp/dart/ReadmeExamplesTest_dart.ts +0 -238
  968. package/project/.sdk/src/cmp/dart/ReadmeExplanation_dart.ts +0 -53
  969. package/project/.sdk/src/cmp/dart/ReadmeHowto_dart.ts +0 -152
  970. package/project/.sdk/src/cmp/dart/ReadmeInstall_dart.ts +0 -59
  971. package/project/.sdk/src/cmp/dart/ReadmeIntro_dart.ts +0 -65
  972. package/project/.sdk/src/cmp/dart/ReadmeModel_dart.ts +0 -151
  973. package/project/.sdk/src/cmp/dart/ReadmeOptions_dart.ts +0 -63
  974. package/project/.sdk/src/cmp/dart/ReadmeQuick_dart.ts +0 -225
  975. package/project/.sdk/src/cmp/dart/ReadmeRef_dart.ts +0 -397
  976. package/project/.sdk/src/cmp/dart/ReadmeTopHowto_dart.ts +0 -24
  977. package/project/.sdk/src/cmp/dart/ReadmeTopQuick_dart.ts +0 -106
  978. package/project/.sdk/src/cmp/dart/ReadmeTopTest_dart.ts +0 -78
  979. package/project/.sdk/src/cmp/dart/SdkError_dart.ts +0 -42
  980. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +0 -516
  981. package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +0 -628
  982. package/project/.sdk/src/cmp/dart/Test_dart.ts +0 -116
  983. package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +0 -79
  984. package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +0 -51
  985. package/project/.sdk/src/cmp/dart/fragment/Direct.test.fragment.dart +0 -25
  986. package/project/.sdk/src/cmp/dart/fragment/Entity.fragment.dart +0 -29
  987. package/project/.sdk/src/cmp/dart/fragment/Entity.test.fragment.dart +0 -28
  988. package/project/.sdk/src/cmp/dart/fragment/EntityBase.fragment.dart +0 -302
  989. package/project/.sdk/src/cmp/dart/fragment/EntityCreateOp.fragment.dart +0 -98
  990. package/project/.sdk/src/cmp/dart/fragment/EntityListOp.fragment.dart +0 -90
  991. package/project/.sdk/src/cmp/dart/fragment/EntityLoadOp.fragment.dart +0 -102
  992. package/project/.sdk/src/cmp/dart/fragment/EntityRemoveOp.fragment.dart +0 -108
  993. package/project/.sdk/src/cmp/dart/fragment/EntityUpdateOp.fragment.dart +0 -102
  994. package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +0 -338
  995. package/project/.sdk/src/cmp/dart/fragment/SdkError.fragment.dart +0 -26
  996. package/project/.sdk/src/cmp/dart/tsconfig.json +0 -15
  997. package/project/.sdk/src/cmp/dart/utility_dart.ts +0 -155
  998. package/project/.sdk/src/cmp/lean/Config_lean.ts +0 -93
  999. package/project/.sdk/src/cmp/lean/Entity_lean.ts +0 -9
  1000. package/project/.sdk/src/cmp/lean/Gitignore_lean.ts +0 -18
  1001. package/project/.sdk/src/cmp/lean/Main_lean.ts +0 -120
  1002. package/project/.sdk/src/cmp/lean/Package_lean.ts +0 -93
  1003. package/project/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +0 -29
  1004. package/project/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +0 -19
  1005. package/project/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +0 -25
  1006. package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +0 -39
  1007. package/project/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +0 -25
  1008. package/project/.sdk/src/cmp/lean/ReadmeModel_lean.ts +0 -24
  1009. package/project/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +0 -22
  1010. package/project/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +0 -50
  1011. package/project/.sdk/src/cmp/lean/ReadmeRef_lean.ts +0 -40
  1012. package/project/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +0 -16
  1013. package/project/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +0 -23
  1014. package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +0 -19
  1015. package/project/.sdk/src/cmp/lean/Test_lean.ts +0 -261
  1016. package/project/.sdk/src/cmp/lean/utility_lean.ts +0 -129
  1017. package/project/.sdk/src/cmp/seneca-provider/Extras_seneca-provider.ts +0 -4080
  1018. package/project/.sdk/src/cmp/seneca-provider/Gitignore_seneca-provider.ts +0 -161
  1019. package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +0 -1011
  1020. package/project/.sdk/tm/dart/LICENSE +0 -22
  1021. package/project/.sdk/tm/dart/Makefile +0 -50
  1022. package/project/.sdk/tm/dart/lib/Context.dart +0 -150
  1023. package/project/.sdk/tm/dart/lib/Control.dart +0 -15
  1024. package/project/.sdk/tm/dart/lib/Operation.dart +0 -31
  1025. package/project/.sdk/tm/dart/lib/Point.dart +0 -51
  1026. package/project/.sdk/tm/dart/lib/Response.dart +0 -32
  1027. package/project/.sdk/tm/dart/lib/Result.dart +0 -39
  1028. package/project/.sdk/tm/dart/lib/Spec.dart +0 -49
  1029. package/project/.sdk/tm/dart/lib/feature/audit/AuditFeature.dart +0 -103
  1030. package/project/.sdk/tm/dart/lib/feature/base/BaseFeature.dart +0 -76
  1031. package/project/.sdk/tm/dart/lib/feature/cache/CacheFeature.dart +0 -152
  1032. package/project/.sdk/tm/dart/lib/feature/clienttrack/ClienttrackFeature.dart +0 -104
  1033. package/project/.sdk/tm/dart/lib/feature/cost/CostFeature.dart +0 -446
  1034. package/project/.sdk/tm/dart/lib/feature/debug/DebugFeature.dart +0 -153
  1035. package/project/.sdk/tm/dart/lib/feature/idempotency/IdempotencyFeature.dart +0 -92
  1036. package/project/.sdk/tm/dart/lib/feature/log/LogFeature.dart +0 -85
  1037. package/project/.sdk/tm/dart/lib/feature/metrics/MetricsFeature.dart +0 -103
  1038. package/project/.sdk/tm/dart/lib/feature/netsim/NetsimFeature.dart +0 -186
  1039. package/project/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +0 -240
  1040. package/project/.sdk/tm/dart/lib/feature/proxy/ProxyFeature.dart +0 -118
  1041. package/project/.sdk/tm/dart/lib/feature/ratelimit/RatelimitFeature.dart +0 -103
  1042. package/project/.sdk/tm/dart/lib/feature/rbac/RbacFeature.dart +0 -121
  1043. package/project/.sdk/tm/dart/lib/feature/retry/RetryFeature.dart +0 -155
  1044. package/project/.sdk/tm/dart/lib/feature/streaming/StreamingFeature.dart +0 -97
  1045. package/project/.sdk/tm/dart/lib/feature/telemetry/TelemetryFeature.dart +0 -136
  1046. package/project/.sdk/tm/dart/lib/feature/test/TestFeature.dart +0 -328
  1047. package/project/.sdk/tm/dart/lib/feature/timeout/TimeoutFeature.dart +0 -104
  1048. package/project/.sdk/tm/dart/lib/utility/CleanUtility.dart +0 -12
  1049. package/project/.sdk/tm/dart/lib/utility/DoneUtility.dart +0 -18
  1050. package/project/.sdk/tm/dart/lib/utility/ErrUtility.dart +0 -46
  1051. package/project/.sdk/tm/dart/lib/utility/FeatureAddUtility.dart +0 -35
  1052. package/project/.sdk/tm/dart/lib/utility/FeatureHookUtility.dart +0 -20
  1053. package/project/.sdk/tm/dart/lib/utility/FeatureInitUtility.dart +0 -9
  1054. package/project/.sdk/tm/dart/lib/utility/FetcherUtility.dart +0 -104
  1055. package/project/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +0 -162
  1056. package/project/.sdk/tm/dart/lib/utility/MakeContextUtility.dart +0 -6
  1057. package/project/.sdk/tm/dart/lib/utility/MakeErrorUtility.dart +0 -71
  1058. package/project/.sdk/tm/dart/lib/utility/MakeFetchDefUtility.dart +0 -42
  1059. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +0 -233
  1060. package/project/.sdk/tm/dart/lib/utility/MakePointUtility.dart +0 -130
  1061. package/project/.sdk/tm/dart/lib/utility/MakeRequestUtility.dart +0 -66
  1062. package/project/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +0 -59
  1063. package/project/.sdk/tm/dart/lib/utility/MakeResultUtility.dart +0 -49
  1064. package/project/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +0 -77
  1065. package/project/.sdk/tm/dart/lib/utility/MakeUrlUtility.dart +0 -51
  1066. package/project/.sdk/tm/dart/lib/utility/ParamUtility.dart +0 -56
  1067. package/project/.sdk/tm/dart/lib/utility/PrepareAuthUtility.dart +0 -46
  1068. package/project/.sdk/tm/dart/lib/utility/PrepareBodyUtility.dart +0 -19
  1069. package/project/.sdk/tm/dart/lib/utility/PrepareHeadersUtility.dart +0 -11
  1070. package/project/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +0 -30
  1071. package/project/.sdk/tm/dart/lib/utility/PrepareParamsUtility.dart +0 -23
  1072. package/project/.sdk/tm/dart/lib/utility/PreparePathUtility.dart +0 -9
  1073. package/project/.sdk/tm/dart/lib/utility/PrepareQueryUtility.dart +0 -18
  1074. package/project/.sdk/tm/dart/lib/utility/ResultBasicUtility.dart +0 -29
  1075. package/project/.sdk/tm/dart/lib/utility/ResultBodyUtility.dart +0 -15
  1076. package/project/.sdk/tm/dart/lib/utility/ResultHeadersUtility.dart +0 -18
  1077. package/project/.sdk/tm/dart/lib/utility/StructUtility.dart +0 -127
  1078. package/project/.sdk/tm/dart/lib/utility/TransformRequestUtility.dart +0 -28
  1079. package/project/.sdk/tm/dart/lib/utility/TransformResponseUtility.dart +0 -44
  1080. package/project/.sdk/tm/dart/lib/utility/Utility.dart +0 -239
  1081. package/project/.sdk/tm/dart/lib/utility/voxgig_struct.dart +0 -2462
  1082. package/project/.sdk/tm/dart/src/feature/README.md +0 -3
  1083. package/project/.sdk/tm/dart/src/feature/telemetry/.gitkeep +0 -0
  1084. package/project/.sdk/tm/dart/src/feature/test/.gitkeep +0 -0
  1085. package/project/.sdk/tm/dart/src/feature/timeout/.gitkeep +0 -0
  1086. package/project/.sdk/tm/dart/test/custom_test.dart +0 -65
  1087. package/project/.sdk/tm/dart/test/exists_test.dart +0 -12
  1088. package/project/.sdk/tm/dart/test/feature/harness.dart +0 -329
  1089. package/project/.sdk/tm/dart/test/feature_test.dart +0 -1193
  1090. package/project/.sdk/tm/dart/test/harness.dart +0 -153
  1091. package/project/.sdk/tm/dart/test/netsim_test.dart +0 -48
  1092. package/project/.sdk/tm/dart/test/omni.dart +0 -520
  1093. package/project/.sdk/tm/dart/test/omni_smoke_test.dart +0 -143
  1094. package/project/.sdk/tm/dart/test/pipeline_test.dart +0 -608
  1095. package/project/.sdk/tm/dart/test/primary_test.dart +0 -476
  1096. package/project/.sdk/tm/dart/test/sdk-test-control.json +0 -19
  1097. package/project/.sdk/tm/dart/test/struct_test.dart +0 -468
  1098. package/project/.sdk/tm/dart/test/utility.dart +0 -198
  1099. package/project/.sdk/tm/dart/test/vendor/omni/omni.dart +0 -7
  1100. package/project/.sdk/tm/dart/test/vendor/omni/runner.dart +0 -653
  1101. package/project/.sdk/tm/dart/test/vendor/omni/util.dart +0 -232
  1102. package/project/.sdk/tm/lean/LICENSE +0 -21
  1103. package/project/.sdk/tm/lean/Makefile +0 -32
  1104. package/project/.sdk/tm/lean/VERSION +0 -1
  1105. package/project/.sdk/tm/lean/src/SdkFeature.lean +0 -171
  1106. package/project/.sdk/tm/lean/src/SdkFeatures.lean +0 -787
  1107. package/project/.sdk/tm/lean/src/SdkJson.lean +0 -134
  1108. package/project/.sdk/tm/lean/src/SdkRuntime.lean +0 -374
  1109. package/project/.sdk/tm/lean/src/SdkUtility.lean +0 -809
  1110. package/project/.sdk/tm/lean/src/VoxgigStruct.lean +0 -2880
  1111. package/project/.sdk/tm/lean/src/Vregex.lean +0 -412
  1112. package/project/.sdk/tm/lean/src/feature/README.md +0 -5
  1113. package/project/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
  1114. package/project/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
  1115. package/project/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
  1116. package/project/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
  1117. package/project/.sdk/tm/lean/src/feature/cost/.gitkeep +0 -0
  1118. package/project/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
  1119. package/project/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
  1120. package/project/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
  1121. package/project/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
  1122. package/project/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
  1123. package/project/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
  1124. package/project/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
  1125. package/project/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
  1126. package/project/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
  1127. package/project/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
  1128. package/project/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
  1129. package/project/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
  1130. package/project/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
  1131. package/project/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
  1132. package/project/.sdk/tm/lean/test/OmniResolver.lean +0 -358
  1133. package/project/.sdk/tm/lean/test/OmniSmoke.lean +0 -174
  1134. package/project/.sdk/tm/lean/test/StructCorpus.lean +0 -368
  1135. package/project/.sdk/tm/lean/test/TFeature.lean +0 -281
  1136. package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +0 -227
  1137. package/project/.sdk/tm/lean/test/vendor/omni/Omni.lean +0 -997
  1138. package/project/.sdk/tm/seneca-provider/CODE_OF_CONDUCT.md +0 -132
  1139. package/project/.sdk/tm/seneca-provider/LICENSE +0 -21
  1140. package/project/.sdk/tm/seneca-provider/Makefile +0 -15
  1141. package/project/.sdk/tm/seneca-provider/src/tsconfig.json +0 -18
  1142. package/project/.sdk/tm/seneca-provider/test/tsconfig.json +0 -16
  1143. package/project/.sdk/tm/seneca-provider/tsfmt.json +0 -3
  1144. /package/project/.sdk/tm/{dart/src/feature/audit → c/src/feature/secrets}/.gitkeep +0 -0
  1145. /package/project/.sdk/tm/{dart/src/feature/base → clojure/src/feature/secrets}/.gitkeep +0 -0
  1146. /package/project/.sdk/tm/{dart/src/feature/cache → cpp/src/feature/secrets}/.gitkeep +0 -0
  1147. /package/project/.sdk/tm/{dart/src/feature/clienttrack → csharp/src/feature/secrets}/.gitkeep +0 -0
  1148. /package/project/.sdk/tm/{dart/src/feature/cost → elixir/src/feature/secrets}/.gitkeep +0 -0
  1149. /package/project/.sdk/tm/{dart/src/feature/debug → java/src/feature/secrets}/.gitkeep +0 -0
  1150. /package/project/.sdk/tm/{dart/src/feature/idempotency → kotlin/src/feature/secrets}/.gitkeep +0 -0
  1151. /package/project/.sdk/tm/{dart/src/feature/log → lua/src/feature/secrets}/.gitkeep +0 -0
  1152. /package/project/.sdk/tm/{dart/src/feature/metrics → ocaml/src/feature/secrets}/.gitkeep +0 -0
  1153. /package/project/.sdk/tm/{dart/src/feature/netsim → perl/src/feature/secrets}/.gitkeep +0 -0
  1154. /package/project/.sdk/tm/{dart/src/feature/paging → php/src/feature/secrets}/.gitkeep +0 -0
  1155. /package/project/.sdk/tm/{dart/src/feature/proxy → rb/src/feature/secrets}/.gitkeep +0 -0
  1156. /package/project/.sdk/tm/{dart/src/feature/ratelimit → rust/src/feature/secrets}/.gitkeep +0 -0
  1157. /package/project/.sdk/tm/{dart/src/feature/rbac → scala/src/feature/secrets}/.gitkeep +0 -0
  1158. /package/project/.sdk/tm/{dart/src/feature/retry → swift/src/feature/secrets}/.gitkeep +0 -0
  1159. /package/project/.sdk/tm/{dart/src/feature/streaming → zig/src/feature/secrets}/.gitkeep +0 -0
@@ -1,4080 +0,0 @@
1
- import {
2
- cmp, each,
3
- File, Content, Folder,
4
- jsKey, jsProp,
5
- pointSegments,
6
- } from '@voxgig/sdkgen'
7
-
8
-
9
- // The rest of the seneca-provider package: its test suite, CI workflow and
10
- // README. Split out of Main only for size — everything here is driven by the
11
- // same `provider` shape Main builds from the model.
12
- //
13
- // The tests are the reason this target is worth generating at all. A provider
14
- // is thin, and the thin part is exactly where the mistakes are: a cmd that
15
- // forgets a parent path param, an entity that comes back under the wrong
16
- // canon, a 404 that should have been `null` and instead threw. All three are
17
- // checked below, offline, against the SDK's own mock transport — so a
18
- // generated provider is verified without a server.
19
-
20
-
21
- // Does this entity's load op have a real identifying param (path or
22
- // required query), e.g. GET /result?trace_id=? A paramless GET has none.
23
- function loadHasKey(ent: any): boolean {
24
- const point = (ent.op && ent.op.load && ent.op.load.points || [])[0]
25
- if (null == point) return false
26
- // apidef states which segments are variables (its ADR-003) — no brace test.
27
- const hasPathParam = pointSegments(point).some((seg: any) => null != seg.var)
28
- const hasQueryParam = (point.args && point.args.query || [])
29
- .some((q: any) => false !== q.reqd)
30
- return hasPathParam || hasQueryParam
31
- }
32
-
33
-
34
- // The name of the entity a parent path param addresses, or '' when the model
35
- // has none of that name.
36
- //
37
- // From `e.parentOf`, which Main derives PER KEY. `e.parentEntity` describes
38
- // only the FIRST parent, so an entity nested two levels deep had every one of
39
- // its parents resolved to the innermost one — addressing the wrong record, or
40
- // none.
41
- function parentName(e: any, key: string): string {
42
- const byKey = (e.parentOf || {})[key]
43
- if (null != byKey && '' !== byKey) {
44
- return String(byKey)
45
- }
46
-
47
- const f = (e.fields || []).find((f: any) => f.name === key)
48
- return (f && f.parentEntity) || ''
49
- }
50
-
51
-
52
- // The seeded id of the record a parent path param points at.
53
- function parentSeed(e: any, key: string): string {
54
- const pe = parentName(e, key)
55
-
56
- // A key naming no entity in the model still has to seed SOMETHING the
57
- // guard accepts; strip the `_id` suffix and use that.
58
- return '' !== pe ? `${pe}0` : `${key.replace(/_id$/, '')}0`
59
- }
60
-
61
-
62
- // `key: 'value', ` pairs for an entity's parent path params, ready to splice
63
- // into an object literal. Empty for a top-level entity, so the same emitter
64
- // serves both.
65
- //
66
- // OFFLINE the value is the seeded parent id, which exists because the seed put
67
- // it there. LIVE it is a local VARIABLE, emitted as ES shorthand: a real server
68
- // holds whatever records it holds, and a fixture id written into a live test is
69
- // a 404 waiting to happen. That is not hypothetical — seeding the live nested
70
- // create is exactly how the first version of this failed, with
71
- // `create: request: 404` against a parent that only ever existed in the mock.
72
- function parentPairs(e: any, live: boolean): string {
73
- return e.parents
74
- .map((p: string) => live ? `${p}, ` : `${p}: '${parentSeed(e, p)}', `)
75
- .join('')
76
- }
77
-
78
-
79
- // The entity a parent path param addresses, or null when the model has none of
80
- // that name.
81
- function parentEntityFor(provider: any, e: any, key: string): any {
82
- const name = parentName(e, key)
83
-
84
- return '' === name ? null :
85
- provider.entities.find((pe: any) => pe.name === name) || null
86
- }
87
-
88
-
89
- // Can a LIVE round-trip get hold of this entity's parent ids at all? Every
90
- // parent key must name an entity in the model, and that entity must be
91
- // listable — otherwise there is no honest way to obtain an id the server will
92
- // accept, and the test is not emitted rather than emitted and skipped.
93
- function liveParentsResolvable(provider: any, e: any): boolean {
94
- return e.parents.every((p: string) => {
95
- const pe = parentEntityFor(provider, e, p)
96
- return null != pe && pe.cmds.includes('list')
97
- })
98
- }
99
-
100
-
101
- // The lines that fetch a live parent id per parent key, plus the guard that
102
- // skips when the server has no parent record to attach to. Empty for a
103
- // top-level entity.
104
- function liveParentSetup(provider: any, e: any, ind: string): string {
105
- if (0 === e.parents.length) {
106
- return ''
107
- }
108
-
109
- return e.parents.map((p: string) => {
110
- const pe = parentEntityFor(provider, e, p)
111
- const pv = `${pe.name}Records`
112
-
113
- return `${ind} // ${e.name} records hang off ${pe.name} records, so the ${p} has to
114
- ${ind} // come FROM THE SERVER. This database is not ours to seed.
115
- ${ind} const ${pv} = await seneca
116
- ${ind} .entity('provider/${provider.lower}/${pe.name}')
117
- ${ind} .list\$()
118
-
119
- ${ind} if (0 === ${pv}.length) return t.skip('no ${pe.name} to attach a ${e.name} to')
120
-
121
- ${ind} const ${p} = ${pv}[0].${pe.idf || 'id'}
122
-
123
- `
124
- }).join('')
125
- }
126
-
127
-
128
- // A field a round-trip test can CHANGE and then assert on: the first string
129
- // field that is neither the id nor a parent path param. Without one there is
130
- // nothing an update could alter that an assertion could see, so the update leg
131
- // is dropped rather than asserted vacuously.
132
- function mutableField(e: any): string {
133
- const f = (e.fields || []).find((f: any) =>
134
- f.name !== e.idf && 'id' !== f.name &&
135
- !e.parents.includes(f.name) && 'string' === f.kind)
136
-
137
- return f ? f.name : ''
138
- }
139
-
140
-
141
- // A create -> load -> update -> remove round-trip for one entity.
142
- //
143
- // Emitted for any entity declaring BOTH save and remove, in both modes: once
144
- // offline against the SDK's mock transport, once live behind the server probe.
145
- // The write path is where a provider actually breaks — a save that forgets a
146
- // parent key, an update that creates a second record instead of amending the
147
- // first — and it was covered by nothing until this existed. The hand-written
148
- // provider this target was modelled on had exactly these tests, live; dropping
149
- // them on the first regeneration left every cmd.save and cmd.remove action in
150
- // the generated plugin unexecuted by its own suite.
151
- //
152
- // The created id is never asserted to a VALUE: both the mock and a real API
153
- // assign it themselves and ignore any the SDK sends.
154
- function crudTest(provider: any, e: any, mode: 'offline' | 'live'): string {
155
- const live = 'live' === mode
156
- const pairs = parentPairs(e, live)
157
- // Seneca's key, not the API's: this test drives seneca.entity(...), whose
158
- // query and entity always spell the id `id`. The provider translates to
159
- // whatever the API calls it.
160
- const idf = 'id'
161
- const mut = mutableField(e)
162
-
163
- const ind = live ? ' ' : ' '
164
- const mk = live ? 'makeSeneca(liveOpts())' : 'makeSeneca()'
165
- const setup = live ? liveParentSetup(provider, e, ind) : ''
166
-
167
- const made = 0 < e.fields.filter((f: any) =>
168
- f.name !== idf && 'id' !== f.name && !e.parents.includes(f.name)).length ?
169
- seedLiteral(e, 'crud') : ''
170
-
171
- return `${ind}it('${e.name}-crud', async (${live ? 't' : ''}) => {
172
- ${live ? `${ind} if (!live) return t.skip(noServer())\n` : ''}${ind} const seneca = await ${mk}
173
- ${ind} const ent = seneca.entity('provider/${provider.lower}/${e.name}')
174
-
175
- ${setup}${ind} // Seneca's convention: an entity WITHOUT an id is a create. The API
176
- ${ind} // assigns the id itself, so the saved record comes back with one it chose.
177
- ${ind} const made = await ent.make$({ ${pairs}${made} }).save$()
178
-
179
- ${ind} assert.ok(null != made.${idf})
180
- ${ind} assert.equal(
181
- ${ind} made.canon\$({ string: true }),
182
- ${ind} 'provider/${provider.lower}/${e.name}',
183
- ${ind} )
184
-
185
- ${ind} const id = made.${idf}
186
-
187
- ${ind} try {
188
- ${ind} const loaded = await ent.load\$({ ${pairs}${idf}: id })
189
- ${ind} assert.equal(loaded.${idf}, id)
190
- ${
191
- '' === mut ? '' :
192
- `
193
- ${ind} // An entity CARRYING an id is an update, not a second create.
194
- ${ind} loaded.${mut} = 'crud-${mut}-2'
195
- ${ind} const updated = await loaded.save\$()
196
-
197
- ${ind} assert.equal(updated.${idf}, id)
198
- ${ind} assert.equal(updated.${mut}, 'crud-${mut}-2')
199
-
200
- ${ind} const reloaded = await ent.load\$({ ${pairs}${idf}: id })
201
- ${ind} assert.equal(reloaded.${mut}, 'crud-${mut}-2')
202
- `}${ind} }
203
- ${ind} finally {
204
- ${ind} // Always clean up. The mock and the server both hold data for the
205
- ${ind} // process lifetime, so a leaked record changes what later tests see.
206
- ${ind} await ent.remove\$({ ${pairs}${idf}: id })
207
- ${ind} }
208
-
209
- ${ind} // remove is real: the record is gone, and reading it is an ordinary
210
- ${ind} // not-found rather than an error.
211
- ${ind} assert.equal(await ent.load\$({ ${pairs}${idf}: id }), null)
212
- ${ind}})
213
-
214
- `
215
- }
216
-
217
-
218
- // A source literal for one field, by kind.
219
- //
220
- // `$ARRAY` and `$OBJECT` are in the model's sentinel vocabulary and used to
221
- // fall through to the string branch, so a list field came out as
222
- // `tags: 'quick-tags'` — a type-incorrect body that a validating server
223
- // rejects, and a fixture that quietly stopped exercising non-scalar payloads.
224
- function fieldLiteral(f: any, tag: string): string {
225
- switch (f.kind) {
226
- case 'number': return '12345'
227
- case 'boolean': return 'true'
228
- case 'array': return '[]'
229
- case 'object': return '{}'
230
- default: return `'${tag}-${f.name}'`
231
- }
232
- }
233
-
234
-
235
- // `name: 'value'` pairs for an entity's own (non-id, non-parent) required
236
- // fields, tagged with `tag` so a test record is recognisable in a store it
237
- // shares with the seed.
238
- function seedLiteral(e: any, tag: string): string {
239
- return (e.fields || [])
240
- .filter((f: any) =>
241
- f.name !== e.idf && 'id' !== f.name && !e.parents.includes(f.name))
242
- .map((f: any) => `${jsKey(f.name)}: ${fieldLiteral(f, tag)}`)
243
- .join(', ')
244
- }
245
-
246
-
247
- // A plausible seed record for an entity: its required fields, given values
248
- // that read as data rather than as `string`.
249
- function seedRecord(e: any, idx: number): Record<string, any> {
250
- const out: Record<string, any> = {}
251
-
252
- for (const f of e.fields) {
253
- if ('id' === f.name || f.name === e.idf) {
254
- out[f.name] = `${e.name}${idx}`
255
- }
256
- else if (e.parents.includes(f.name)) {
257
- // A nested entity's parent id must match a record the parent seeds, or
258
- // the offline store answers nothing and every nested test reads as a
259
- // false pass. Reuses parentSeed's fallback rather than f.parentEntity
260
- // directly: when no entity in the model shares this key's name (the
261
- // common case for a scoping param like `user_id` with no `user`
262
- // entity, or a same-named response field that means something else
263
- // entirely, like GitHub's `owner`), f.parentEntity is '' and seeding
264
- // '0' desynced the record from every query built against the SAME
265
- // key via parentSeed (parentPairs, crudTest, ...) — 0 results, or a
266
- // seeded field asserted against the wrong literal.
267
- out[f.name] = parentSeed(e, f.name)
268
- }
269
- else if ('number' === f.kind) {
270
- out[f.name] = 100 * (idx + 1)
271
- }
272
- else if ('boolean' === f.kind) {
273
- out[f.name] = false
274
- }
275
- else if ('array' === f.kind) {
276
- out[f.name] = []
277
- }
278
- else if ('object' === f.kind) {
279
- out[f.name] = {}
280
- }
281
- else {
282
- out[f.name] = `${f.name}${idx}`
283
- }
284
- }
285
-
286
- return out
287
- }
288
-
289
-
290
- const Tests = cmp(function Tests(props: any) {
291
- const { provider } = props
292
-
293
- // The entity the suite exercises hardest: prefer one with no parent keys
294
- // (nothing to arrange) and the most cmds.
295
- const subject = [...provider.entities]
296
- .sort((a: any, b: any) =>
297
- (a.parents.length - b.parents.length) || (b.cmds.length - a.cmds.length))[0]
298
-
299
- const nested = provider.entities.filter((e: any) => 0 < e.parents.length)
300
-
301
- Folder({ name: 'test' }, () => {
302
-
303
- // The seed the offline mock transport is loaded with. Generated from the
304
- // model so it matches the shape the SDK will actually return.
305
- File({ name: 'seed.js' }, () => {
306
- Content(`/* Generated by @voxgig/sdkgen. Do not edit. */
307
- 'use strict'
308
-
309
- // Seed data for the SDK's offline mock transport, so the entity tests
310
- // exercise real code paths without a server.
311
- const SEED = {
312
- entity: {
313
- `)
314
- each(provider.entities, (e: any) => {
315
- Content(` ${e.name}: {
316
- `)
317
- each([0, 1], (i: any) => {
318
- const idx = Number(i.val$ ?? i)
319
- const rec = seedRecord(e, idx)
320
- Content(` ${e.name}${idx}: ${JSON.stringify(rec)},
321
- `)
322
- })
323
- Content(` },
324
- `)
325
- })
326
- Content(` },
327
- }
328
-
329
- module.exports = { SEED }
330
- `)
331
- })
332
-
333
-
334
- // The message-level spec seneca-msg-test drives. TypeScript, compiled to
335
- // dist-test by test/tsconfig.json — which is also why it must exist: the
336
- // shipped tsconfig has `include: ["**/*.ts"]` and tsc fails outright on a
337
- // config that matches no input.
338
- File({ name: 'basic.messages.ts' }, () => {
339
- Content(`/* Generated by @voxgig/sdkgen. Do not edit. */
340
-
341
- const Pkg = require('../package.json')
342
-
343
- const messages = {
344
- print: false,
345
- pattern: 'sys:provider,provider:${provider.lower}',
346
- allow: { missing: true },
347
-
348
- calls: [
349
- {
350
- pattern: 'get:info',
351
- out: {
352
- ok: true,
353
- name: '${provider.lower}',
354
- version: Pkg.version,
355
- },
356
- },
357
- ],
358
- }
359
-
360
- export default messages
361
-
362
- if ('undefined' !== typeof module) {
363
- module.exports = messages
364
- }
365
- `)
366
- })
367
-
368
-
369
- File({ name: `${provider.fileBase}.test.js` }, () => {
370
- Content(`/* Generated by @voxgig/sdkgen. Do not edit. */
371
- 'use strict'
372
-
373
- const { describe, it, before } = require('node:test')
374
- const assert = require('node:assert')
375
-
376
- const Seneca = require('seneca')
377
-
378
- const ${provider.pluginName} = require('../dist/${provider.fileBase}')
379
- const ${provider.pluginName}Doc = require('../dist/${provider.pluginName}-doc')
380
-
381
- const SenecaMsgTest = require('seneca-msg-test')
382
- const { Maintain } = require('@seneca/maintain')
383
-
384
- const { SEED } = require('./seed')
385
-
386
- const BasicMessages = require('../dist-test/basic.messages')
387
- ${'' === provider.liveBase ? '' : `
388
- // The live tests run against the companion test server in the SDK repo
389
- // (\`app/\`), which serves this by default. Start it with:
390
- // cd ${provider.sdkrel}/app && npm start
391
- const LIVE_BASE = process.env.${provider.ENV}_TEST_BASE || '${provider.liveBase}'
392
- `}
393
-
394
- describe('${provider.fileBase}', () => {
395
-
396
- it('happy', async () => {
397
- assert.notEqual(${provider.pluginName}, undefined)
398
- assert.notEqual(${provider.pluginName}Doc, undefined)
399
-
400
- const seneca = await makeSeneca()
401
-
402
- assert.partialDeepStrictEqual(
403
- await seneca.post('sys:provider,provider:${provider.lower},get:info'),
404
- {
405
- ok: true,
406
- name: '${provider.lower}',
407
- },
408
- )
409
- })
410
-
411
-
412
- it('messages', async () => {
413
- const seneca = await makeSeneca()
414
- await SenecaMsgTest(seneca, BasicMessages)()
415
- })
416
-
417
-
418
- it('sdk-export', async () => {
419
- const seneca = await makeSeneca()
420
- const sdk = seneca.export('${provider.pluginName}/sdk')()
421
-
422
- `)
423
- each(provider.entities, (e: any) => {
424
- Content(` assert.equal(typeof sdk.${e.acc}, 'function')
425
- `)
426
- })
427
- Content(` })
428
-
429
- `)
430
-
431
- // Every flat entity (no parent keys), not just one "subject" — a
432
- // provider with two or more flat siblings used to leave every one
433
- // but the busiest untested beyond the accessor check above. A bare
434
- // `list$()`/`load$(id)` call has no way to carry a parent key, so
435
- // entities that need one are covered by the `nested` block below
436
- // instead, with their keys filled in.
437
- const flat = provider.entities.filter((e: any) => 0 === e.parents.length)
438
-
439
- each(flat, (e: any) => {
440
- if (e.cmds.includes('list')) {
441
- Content(`
442
- it('${e.name}-list', async () => {
443
- const seneca = await makeSeneca()
444
- const list = await seneca.entity('provider/${provider.lower}/${e.name}').list$()
445
-
446
- assert.equal(list.length, 2)
447
-
448
- // Entities must come back as Seneca entities under this plugin's canon.
449
- // The SDK tags its own results with its entity marker, which must not
450
- // survive into the Seneca entity.
451
- assert.equal(
452
- list[0].canon$({ string: true }),
453
- 'provider/${provider.lower}/${e.name}',
454
- )
455
- })
456
-
457
- `)
458
- }
459
-
460
- if (e.cmds.includes('load')) {
461
- Content(`
462
- it('${e.name}-load', async () => {
463
- const seneca = await makeSeneca()
464
- const found = await seneca
465
- .entity('provider/${provider.lower}/${e.name}')
466
- .load$('${e.name}0')
467
-
468
- assert.equal(found.${e.idf || 'id'}, '${e.name}0')
469
- assert.equal(
470
- found.canon$({ string: true }),
471
- 'provider/${provider.lower}/${e.name}',
472
- )
473
- })
474
-
475
- `)
476
- // Paramless read (e.g. GET /usage): every id "misses" the same
477
- // way a hit does -- the mock has nothing to filter by -- so a
478
- // load-missing test would just assert the happy path again.
479
- if (loadHasKey(e.ent)) {
480
- Content(`
481
- // A 404 from a single-item read is an ordinary "not found" answer, not a
482
- // failure: the provider turns it into null rather than letting the SDK
483
- // throw.
484
- it('${e.name}-load-missing', async () => {
485
- const seneca = await makeSeneca()
486
- const missing = await seneca
487
- .entity('provider/${provider.lower}/${e.name}')
488
- .load$('nosuch${e.name}')
489
-
490
- assert.equal(missing, null)
491
- })
492
-
493
- `)
494
- }
495
- }
496
- })
497
-
498
- // A nested entity cannot build its path without the parent id. That is
499
- // the mistake this target exists to make impossible, so pin it.
500
- each(nested, (e: any) => {
501
- // EVERY parent key, not just the first. An entity nested two levels
502
- // deep is guarded on both, so a test supplying only the alphabetically
503
- // first tripped the second guard and failed on the code it was meant
504
- // to be exercising.
505
- const key = e.parents[0]
506
- const pairs = e.parents
507
- .map((k: string) => `${k}: '${parentSeed(e, k)}'`).join(', ')
508
-
509
- // The guard is PER OP (Main's opParents), not a blanket property of
510
- // the entity, so the op this test calls has to be one that actually
511
- // requires `key` — hardcoding `list` assumed every nested entity's
512
- // list is parent-scoped, which fails for e.g. an entity guarded on
513
- // load/update/remove but whose list is unscoped (GitHub's `repo`:
514
- // owner guards load, not list).
515
- const guardOp = ['list', 'load', 'update', 'remove']
516
- .find((op: string) => (e.opParents[op] || []).includes(key))
517
-
518
- if (null != guardOp) {
519
- const call = 'list' === guardOp ?
520
- `${guardOp}$({})` : `${guardOp}$({ id: '${e.name}0' })`
521
-
522
- Content(`
523
- it('${e.name}-needs-${key}', async () => {
524
- const seneca = await makeSeneca()
525
-
526
- await assert.rejects(
527
- () => seneca.entity('provider/${provider.lower}/${e.name}').${call},
528
- /${key} is required/,
529
- )
530
- })
531
-
532
- `)
533
- }
534
- if (e.cmds.includes('list')) {
535
- // Assert on the SEEDED RECORDS, not merely that an array came back.
536
- // `Array.isArray` is true of the empty array, so the nested-list
537
- // test passed while proving nothing: the seed puts both of this
538
- // entity's records under the same parent, so both must come back,
539
- // under this plugin's canon, still carrying the parent key that
540
- // addressed them.
541
- Content(`
542
- it('${e.name}-list', async () => {
543
- const seneca = await makeSeneca()
544
- const list = await seneca
545
- .entity('provider/${provider.lower}/${e.name}')
546
- .list$({ ${pairs} })
547
-
548
- assert.equal(list.length, 2)
549
- assert.equal(
550
- list[0].canon$({ string: true }),
551
- 'provider/${provider.lower}/${e.name}',
552
- )
553
- assert.equal(list[0].${key}, '${parentSeed(e, key)}')
554
- })
555
-
556
- `)
557
- }
558
-
559
- // Reading ONE nested record is the path that has to thread both the
560
- // parent id and the entity id through to the SDK, so cover it
561
- // separately from list.
562
- if (e.cmds.includes('load')) {
563
- Content(`
564
- it('${e.name}-load', async () => {
565
- const seneca = await makeSeneca()
566
- const found = await seneca
567
- .entity('provider/${provider.lower}/${e.name}')
568
- .load$({ ${pairs}, id: '${e.name}0' })
569
-
570
- assert.equal(found.id, '${e.name}0')
571
- assert.equal(
572
- found.canon$({ string: true }),
573
- 'provider/${provider.lower}/${e.name}',
574
- )
575
- })
576
-
577
-
578
- it('${e.name}-load-missing', async () => {
579
- const seneca = await makeSeneca()
580
- const missing = await seneca
581
- .entity('provider/${provider.lower}/${e.name}')
582
- .load$({ ${pairs}, id: 'nosuch${e.name}' })
583
-
584
- assert.equal(missing, null)
585
- })
586
-
587
- `)
588
- }
589
- })
590
-
591
- // The WRITE path, offline. Reads were covered and writes were not, so
592
- // every generated `cmd.save` and `cmd.remove` action shipped without its
593
- // own suite ever running it — including the parent-key guard on a nested
594
- // save, which is the one this target exists to get right. The mock
595
- // transport implements create/update/remove, so this needs no server.
596
- each(provider.entities, (e: any) => {
597
- if (e.cmds.includes('save') && e.cmds.includes('remove')) {
598
- Content(`
599
- ` + crudTest(provider, e, 'offline'))
600
- }
601
- })
602
-
603
- // Live tests, against the companion server in the SDK repo's `app/`.
604
- // They PROBE first and skip when nothing is listening, so the suite is
605
- // green on a machine that has never started it — a live suite that
606
- // fails when the server is absent is one nobody runs.
607
- if ('' !== provider.liveBase) {
608
- Content(`
609
- describe('live', () => {
610
- let live = false
611
-
612
- before(async () => {
613
- live = await serverUp(LIVE_BASE)
614
- })
615
-
616
- `)
617
- if (subject.cmds.includes('list')) {
618
- Content(` it('${subject.name}-list', async (t) => {
619
- if (!live) return t.skip(noServer())
620
- const seneca = await makeSeneca(liveOpts())
621
-
622
- const list = await seneca.entity('provider/${provider.lower}/${subject.name}').list$()
623
-
624
- assert.ok(Array.isArray(list))
625
- if (0 < list.length) {
626
- assert.equal(
627
- list[0].canon$({ string: true }),
628
- 'provider/${provider.lower}/${subject.name}',
629
- )
630
- }
631
- })
632
-
633
- `)
634
- }
635
- if (subject.cmds.includes('load')) {
636
- Content(` // A read of something that is not there is \`null\`, live as well as
637
- // offline: the provider's 404 handling is the same code path either way.
638
- it('${subject.name}-load-missing', async (t) => {
639
- if (!live) return t.skip(noServer())
640
- const seneca = await makeSeneca(liveOpts())
641
-
642
- assert.equal(
643
- await seneca
644
- .entity('provider/${provider.lower}/${subject.name}')
645
- .load$('nosuch${subject.name}'),
646
- null,
647
- )
648
- })
649
-
650
- `)
651
- }
652
-
653
- // The write path against a REAL server. The mock answers the shape the
654
- // SDK expects by construction; only a live run proves the request the
655
- // provider builds is one the API actually accepts — which for a nested
656
- // entity means the parent id reached the URL rather than the body.
657
- //
658
- // Emitted only when a live parent id is OBTAINABLE (see
659
- // liveParentsResolvable): against a real server the parent has to be
660
- // looked up, and an entity whose parent cannot be listed offers no
661
- // honest way to get one.
662
- each(provider.entities, (e: any) => {
663
- if (e.cmds.includes('save') && e.cmds.includes('remove') &&
664
- liveParentsResolvable(provider, e)) {
665
- Content(crudTest(provider, e, 'live'))
666
- }
667
- })
668
-
669
- Content(` })
670
-
671
- `)
672
- }
673
-
674
- // Repository hygiene, from the @seneca/maintain dependency this package
675
- // declares. Two of its checks report a fault that is not there, because
676
- // of WHERE they run rather than what they find, so each is excluded
677
- // only in the environments that break it.
678
- Content(`
679
- it('maintain', async () => {
680
- const exclude = []
681
-
682
- // check_default proves the default branch is main by looking for
683
- // [branch "main"] in .git/config. Only a branch checkout records that
684
- // section: a pull_request build checks out the merge ref, and the
685
- // publish build checks out a tag as a detached HEAD. Neither says
686
- // anything about what the default branch is, so skip rather than fail.
687
- if ('pull_request' === process.env.GITHUB_EVENT_NAME ||
688
- 'tag' === process.env.GITHUB_REF_TYPE) {
689
- exclude.push('check_default')
690
- }
691
-
692
- // url_pkgjson locates package.json by comparing process.cwd() + '/package.json'
693
- // against a path found with Filehound. On Windows those are the same file
694
- // spelt with different separators, so the url is never read.
695
- if ('win32' === process.platform) {
696
- exclude.push('url_pkgjson')
697
- }
698
-
699
- await Maintain({ exclude })
700
- })
701
-
702
- `)
703
-
704
- Content(`})
705
-
706
- `)
707
-
708
- if ('' !== provider.liveBase) {
709
- Content(`
710
- function noServer() {
711
- return 'no ${provider.lower} server at ' + LIVE_BASE
712
- }
713
-
714
-
715
- function liveOpts() {
716
- return { sdk: { base: LIVE_BASE } }
717
- }
718
-
719
-
720
- // Probe the companion test server so live tests skip cleanly when it is not
721
- // running, rather than failing the suite.
722
- async function serverUp(base) {
723
- try {
724
- const res = await fetch(base + '${provider.probePath}', {
725
- signal: AbortSignal.timeout(2000),
726
- })
727
- return res.ok
728
- }
729
- catch (e) {
730
- return false
731
- }
732
- }
733
-
734
- `)
735
- }
736
-
737
- Content(`
738
- // Default to the SDK's offline mock transport, seeded from ./seed.
739
- async function makeSeneca(pluginopts) {
740
- pluginopts = pluginopts || { test: true, testopts: SEED }
741
-
742
- const seneca = Seneca({ legacy: false })
743
- .test()
744
- .use('promisify')
745
- .use('entity')
746
- .use('env', {
747
- // Declared so the provider convention is exercised, and defaulted so
748
- // the suite runs with nothing configured.
749
- var: {
750
- $${provider.ENV}_APIKEY: '',
751
- },
752
- })
753
- .use('provider', {
754
- provider: {
755
- ${provider.lower}: {
756
- keys: {
757
- apikey: { value: '$${provider.ENV}_APIKEY' },
758
- },
759
- },
760
- },
761
- })
762
- .use(${provider.pluginName}, pluginopts)
763
-
764
- return seneca.ready()
765
- }
766
- `)
767
- })
768
- })
769
- })
770
-
771
-
772
- // --- test/live.js, test/quick.js --------------------------------------------
773
- //
774
- // Manual scripts, not part of `npm test`: they need the companion server in
775
- // the SDK repo's `app/`, which is not published. Generated because the path
776
- // to that server is knowable — it is the inverse of this target's own
777
- // `output: path` — so the instruction can be exact rather than "start the
778
- // server somehow".
779
-
780
- const Scripts = cmp(function Scripts(props: any) {
781
- const { provider } = props
782
-
783
- // Nothing to point at without a declared server.
784
- if ('' === provider.liveBase) {
785
- return
786
- }
787
-
788
- const subject = [...provider.entities]
789
- .sort((a: any, b: any) =>
790
- (a.parents.length - b.parents.length) || (b.cmds.length - a.cmds.length))[0]
791
-
792
- const senecaSetup = `const Seneca = require('seneca')
793
-
794
- const BASE = process.env.${provider.ENV}_TEST_BASE || '${provider.liveBase}'
795
-
796
- async function makeSeneca() {
797
- return Seneca({ legacy: false })
798
- .test()
799
- .use('promisify')
800
- .use('entity')
801
- .use('provider', {
802
- provider: {
803
- ${provider.lower}: {
804
- keys: {
805
- apikey: { value: '' },
806
- },
807
- },
808
- },
809
- })
810
- .use('..', { sdk: { base: BASE } })
811
- .ready()
812
- }
813
- `
814
-
815
- Folder({ name: 'test' }, () => {
816
-
817
- File({ name: 'live.js' }, () => {
818
- Content(`/* Manual script: read from a running ${provider.api} server.
819
- *
820
- * Start the companion test server from the SDK repo first:
821
- * cd ${provider.sdkrel}/app && npm start
822
- *
823
- * Then: node test/live.js
824
- */
825
-
826
- ${senecaSetup}
827
-
828
- run()
829
-
830
- async function run() {
831
- const seneca = await makeSeneca()
832
-
833
- `)
834
- // A nested entity's list needs its parent's id, so LOOK ONE UP rather
835
- // than emitting a placeholder: a script that 404s on first run teaches
836
- // nothing and reads as a broken provider.
837
- each(provider.entities.filter((e: any) => e.cmds.includes('list')), (e: any) => {
838
- if (0 === e.parents.length) {
839
- Content(` console.log('${e.name.toUpperCase()}', await seneca
840
- .entity('provider/${provider.lower}/${e.name}')
841
- .list$())
842
-
843
- `)
844
- return
845
- }
846
-
847
- const parent = provider.entities
848
- .find((p: any) => p.name === e.parentEntity && p.cmds.includes('list'))
849
- if (null == parent) {
850
- // Nothing to derive the parent id from. Say so in the script
851
- // rather than emitting a call that cannot work.
852
- Content(` // ${e.name}: needs ${e.parents.join(', ')}; no listable parent to take
853
- // one from, so supply it yourself:
854
- // await seneca.entity('provider/${provider.lower}/${e.name}')
855
- // .list$({ ${e.parents.map((k: string) => `${k}: '...'`).join(', ')} })
856
-
857
- `)
858
- return
859
- }
860
-
861
- const key = e.parents[0]
862
- Content(` const ${parent.name}s = await seneca
863
- .entity('provider/${provider.lower}/${parent.name}')
864
- .list$()
865
-
866
- if (0 < ${parent.name}s.length) {
867
- console.log('${e.name.toUpperCase()}', await seneca
868
- .entity('provider/${provider.lower}/${e.name}')
869
- .list$({ ${key}: ${parent.name}s[0].${parent.idf || 'id'} }))
870
- }
871
-
872
- `)
873
- })
874
- Content(`}
875
- `)
876
- })
877
-
878
-
879
- // The write cycle, kept separate: it MUTATES the server, so it is not
880
- // something to run by reflex. It cleans up after itself.
881
- if (subject.cmds.includes('save') && subject.cmds.includes('remove')) {
882
- const idf = subject.idf || 'id'
883
- const writable = subject.fields
884
- .filter((f: any) => f.name !== idf && f.name !== 'id')
885
- .filter((f: any) => !subject.parents.includes(f.name))
886
-
887
- const make = writable
888
- .map((f: any) => `${jsKey(f.name)}: ${fieldLiteral(f, 'quick')}`)
889
- .join(', ')
890
-
891
- File({ name: 'quick.js' }, () => {
892
- Content(`/* Manual script: exercise the full CRUD cycle against a running server.
893
- *
894
- * Start the companion test server from the SDK repo first:
895
- * cd ${provider.sdkrel}/app && npm start
896
- *
897
- * Then: node test/quick.js
898
- *
899
- * Creates and then removes a ${subject.name}, so the server is left as found.
900
- */
901
-
902
- ${senecaSetup}
903
-
904
- run()
905
-
906
- async function run() {
907
- const seneca = await makeSeneca()
908
-
909
- // Create: the API assigns the id, so none is supplied here.
910
- let ${subject.name} = await seneca
911
- .entity('provider/${provider.lower}/${subject.name}')
912
- .make$({ ${make} })
913
- .save$()
914
- console.log('CREATED', ${subject.name})
915
-
916
- const id = ${subject.name}.${idf}
917
-
918
- try {
919
- `)
920
- // Change something an assertion could SEE. A container field would be
921
- // rewritten to the same empty literal, which demonstrates nothing.
922
- const upd = writable.find((f: any) =>
923
- 'string' === f.kind || 'number' === f.kind) || null
924
-
925
- if (subject.ops.includes('update') && null != upd) {
926
- const f = upd
927
- const v = 'number' === f.kind ? '4321' : `'quick-${f.name}-2'`
928
- Content(` // Update: an entity carrying an id is an update.
929
- ${jsProp(subject.name, f.name)} = ${v}
930
- console.log('UPDATED', await ${subject.name}.save$())
931
-
932
- `)
933
- }
934
- if (subject.cmds.includes('load')) {
935
- Content(` console.log(
936
- 'LOADED',
937
- await seneca.entity('provider/${provider.lower}/${subject.name}').load$(id)
938
- )
939
-
940
- `)
941
- }
942
-
943
- // The NESTED write, which is the leg worth having a manual script
944
- // for: it is the one where the parent id has to reach the URL rather
945
- // than the body, and where a provider that forgets it reports an
946
- // opaque 404 instead of saying what is missing.
947
- //
948
- // Only for a child of the record just created — then the parent id is
949
- // `id`, already in hand, and removing the child leaves the server
950
- // exactly as found. A child of anything else would need its own
951
- // lookup, which belongs in the test suite rather than in a script
952
- // whose whole point is to be readable.
953
- const child = provider.entities.find((e: any) =>
954
- 1 === e.parents.length &&
955
- e.parentEntity === subject.name &&
956
- e.cmds.includes('save') && e.cmds.includes('remove'))
957
-
958
- if (null != child) {
959
- const ckey = child.parents[0]
960
- const cidf = child.idf || 'id'
961
- const cmake = (child.fields || [])
962
- .filter((f: any) =>
963
- f.name !== cidf && 'id' !== f.name && !child.parents.includes(f.name))
964
- .map((f: any) => `${jsKey(f.name)}: ${fieldLiteral(f, 'quick')}`)
965
- .join(', ')
966
-
967
- Content(` // ${child.name} records hang off ${subject.name} records, so this one
968
- // goes under the ${subject.name} just created — and comes back off again.
969
- const ${child.name} = await seneca
970
- .entity('provider/${provider.lower}/${child.name}')
971
- .make$({ ${ckey}: id${'' === cmake ? '' : ', ' + cmake} })
972
- .save$()
973
- console.log('${child.name.toUpperCase()} CREATED', ${child.name})
974
-
975
- await seneca
976
- .entity('provider/${provider.lower}/${child.name}')
977
- .remove$({ ${ckey}: id, ${cidf}: ${child.name}.${cidf} })
978
- console.log('${child.name.toUpperCase()} REMOVED')
979
-
980
- `)
981
- }
982
-
983
- Content(` }
984
- finally {
985
- await seneca.entity('provider/${provider.lower}/${subject.name}').remove$(id)
986
- console.log('REMOVED', id)
987
- }
988
- `)
989
- if (subject.cmds.includes('load')) {
990
- Content(`
991
- console.log(
992
- 'AFTER REMOVE (expect null)',
993
- await seneca.entity('provider/${provider.lower}/${subject.name}').load$(id)
994
- )
995
- `)
996
- }
997
- Content(`}
998
- `)
999
- })
1000
- }
1001
- })
1002
- })
1003
-
1004
-
1005
- // --- .github/workflows/build.yml --------------------------------------------
1006
-
1007
- const Workflow = cmp(function Workflow(props: any) {
1008
- const { provider } = props
1009
-
1010
- Folder({ name: '.github' }, () => {
1011
- Folder({ name: 'workflows' }, () => {
1012
- File({ name: 'build.yml' }, () => {
1013
- Content(`# Generated by @voxgig/sdkgen. Do not edit.
1014
- #
1015
- # The ${provider.api} SDK is a normal published dependency, so \`npm install\`
1016
- # is all that is needed to build and run the offline tests on every platform.
1017
- ${!provider.liveApp ? '' : `#
1018
- # The live tests additionally need the companion server, which is only
1019
- # distributed in the SDK's source repository (it is not published). That repo
1020
- # is cloned and started on Linux only, because backgrounding the server
1021
- # assumes a POSIX shell. The live tests probe for the server and skip cleanly
1022
- # when it is absent, so on Windows and macOS they simply skip.`}
1023
-
1024
- name: build
1025
-
1026
- on:
1027
- push:
1028
- branches: [main]
1029
- pull_request:
1030
- branches: [main]
1031
-
1032
- jobs:
1033
- build:
1034
- timeout-minutes: 10
1035
-
1036
- strategy:
1037
- fail-fast: false
1038
- matrix:
1039
- os: [ubuntu-latest, windows-latest, macos-latest]
1040
- node-version: [24.x]
1041
-
1042
- runs-on: \${{ matrix.os }}
1043
-
1044
- steps:
1045
- - uses: actions/checkout@v7
1046
-
1047
- - name: Use Node.js \${{ matrix.node-version }}
1048
- uses: actions/setup-node@v7
1049
- with:
1050
- node-version: \${{ matrix.node-version }}
1051
-
1052
- ${!provider.liveApp ? '' : `
1053
- # Live-test target. Failure to start degrades coverage (the live tests
1054
- # probe first and skip) rather than failing the build — which is what
1055
- # continue-on-error is for, and what its absence undid: GitHub runs a
1056
- # run: block under bash -e, so a repo whose app/ has no build script,
1057
- # or no app/ at all, went red on its first push with the comment
1058
- # above still claiming otherwise.
1059
- - name: Start the ${provider.api} test server
1060
- if: runner.os == 'Linux'
1061
- continue-on-error: true
1062
- run: |
1063
- git clone --depth 1 \\
1064
- ${provider.sdkRepoUrl}.git \\
1065
- "$RUNNER_TEMP/sdk"
1066
- cd "$RUNNER_TEMP/sdk/app"
1067
- npm install
1068
- npm run build
1069
- npm start &
1070
- for i in $(seq 1 30); do
1071
- if curl -sf ${provider.liveBase}${provider.probePath} > /dev/null; then
1072
- echo "server up"
1073
- exit 0
1074
- fi
1075
- sleep 1
1076
- done
1077
- echo "server did not start; live tests will skip"
1078
- `}
1079
- - run: npm install
1080
-
1081
- # The Seneca host framework is a PEER dependency, so the test suite needs
1082
- # it installed explicitly. --no-save keeps npm from rewriting the peer
1083
- # ranges in package.json to carets on what it happened to resolve, which
1084
- # would have the build testing a manifest the repo never authored.
1085
- - run: npm i --no-save seneca seneca-entity seneca-promisify @seneca/provider @seneca/env
1086
-
1087
- - run: npm run build --if-present
1088
- - run: npm test
1089
- `)
1090
- })
1091
-
1092
- // --- publish.yml ---------------------------------------------------
1093
- //
1094
- // Release on a `v*` tag push, via GitHub OIDC Trusted Publishing — no
1095
- // NPM_TOKEN secret anywhere. `id-token: write` lets npm exchange a
1096
- // GitHub OIDC token for a short-lived publish credential, and npm
1097
- // attaches provenance automatically.
1098
- //
1099
- // TWO THINGS ARE LOAD-BEARING AND EASY TO GET WRONG.
1100
- //
1101
- // The FILENAME. npm's trusted publisher is registered against this
1102
- // file's name, so renaming it breaks publishing until the npm-side
1103
- // configuration is changed to match. It is publish.yml deliberately.
1104
- //
1105
- // `npm install`, NOT `npm ci`. A Seneca plugin does not commit its
1106
- // lockfile (see .gitignore), so there is nothing for ci to install
1107
- // from — it fails outright. The SDK repo commits one and uses ci; this
1108
- // package cannot.
1109
- //
1110
- // The host framework is installed explicitly for the same reason
1111
- // build.yml does it: seneca and its plugins are PEER dependencies, and
1112
- // the test suite requires them directly.
1113
- File({ name: 'publish.yml' }, () => {
1114
- Content(`# Generated by @voxgig/sdkgen. Do not edit.
1115
- #
1116
- # Publishes ${provider.pkgName} to npm on a \`v*\` tag push, via GitHub OIDC
1117
- # Trusted Publishing — no NPM_TOKEN secret. The \`id-token: write\` permission
1118
- # lets npm exchange a GitHub OIDC token for a short-lived publish credential,
1119
- # and provenance is attached automatically.
1120
- #
1121
- # The trusted publisher must be registered on npmjs.com for this package
1122
- # against THIS filename (publish.yml); renaming this file breaks publishing
1123
- # until the npm-side config is updated to match.
1124
- #
1125
- # npm cannot publish a package's FIRST version this way — the settings page
1126
- # that configures a trusted publisher only exists once a version is there. So
1127
- # release ${provider.version} by hand once, configure the publisher, and every
1128
- # release after that is a tag push.
1129
- #
1130
- # Release flow: bump the version in the SDK model
1131
- # (\`main: kit: target: 'seneca-provider': publish: version\`), regenerate,
1132
- # merge, then push a v* tag.
1133
-
1134
- name: publish
1135
-
1136
- on:
1137
- push:
1138
- tags: ['v*']
1139
- workflow_dispatch:
1140
-
1141
- jobs:
1142
- publish:
1143
- name: npm publish
1144
- runs-on: ubuntu-latest
1145
- timeout-minutes: 15
1146
- permissions:
1147
- id-token: write
1148
- contents: read
1149
-
1150
- steps:
1151
- - uses: actions/checkout@v4
1152
-
1153
- - uses: actions/setup-node@v4
1154
- with:
1155
- node-version: 24.x
1156
- registry-url: 'https://registry.npmjs.org'
1157
-
1158
- # Trusted publishing requires npm >= 11.5.1.
1159
- - name: Use a trusted-publishing capable npm
1160
- run: npm install -g npm@latest
1161
-
1162
- # install, not ci: this package does not commit a lockfile.
1163
- - run: npm install
1164
-
1165
- # The Seneca host framework is a PEER dependency, so the test suite
1166
- # needs it installed explicitly.
1167
- #
1168
- # --no-save IS LOAD-BEARING. Without it npm rewrites the peer ranges in
1169
- # package.json to carets on whatever it resolved, and \`npm publish\`
1170
- # below then ships that rewritten manifest — so an authored \`>=26\`
1171
- # reaches consumers as \`^28.1.0\` and the package refuses to install for
1172
- # anyone on a newer major. The repo looks fine; only the artifact is
1173
- # narrowed. Install into node_modules, leave the manifest alone.
1174
- - run: npm i --no-save seneca seneca-entity seneca-promisify @seneca/provider @seneca/env
1175
-
1176
- - run: npm run build
1177
- - run: npm test
1178
-
1179
- # The tag must match what the manifest declares, or a tag push silently
1180
- # republishes whatever version happens to be in package.json.
1181
- - name: Check the tag matches the manifest version
1182
- run: |
1183
- TAG="\${GITHUB_REF_NAME#v}"
1184
- PKG=$(node -p "require('./package.json').version")
1185
- if [ "$TAG" != "$PKG" ]; then
1186
- echo "tag v$TAG does not match package.json $PKG"
1187
- exit 1
1188
- fi
1189
-
1190
- - name: Publish to npm
1191
- run: npm publish --access public
1192
- `)
1193
- })
1194
- })
1195
- })
1196
- })
1197
-
1198
-
1199
- // --- README.md ---------------------------------------------------------------
1200
- //
1201
- // The heading set is NOT free: @seneca/maintain checks a Seneca plugin README
1202
- // for "Quick Example", "More Examples", "Motivation", "Support", "API",
1203
- // "Contributing" and "Background", and the generated `maintain` test fails
1204
- // without them. That check is the reason to generate this file rather than
1205
- // leave it to a maintainer.
1206
-
1207
- const Readme = cmp(function Readme(props: any) {
1208
- const { provider } = props
1209
-
1210
- const subject = [...provider.entities]
1211
- .sort((a: any, b: any) =>
1212
- (a.parents.length - b.parents.length) || (b.cmds.length - a.cmds.length))[0]
1213
-
1214
- const nested = provider.entities.filter((e: any) => 0 < e.parents.length)
1215
-
1216
- File({ name: 'README.md' }, () => {
1217
- Content(`![Seneca ${provider.Name}-Provider](http://senecajs.org/files/assets/seneca-logo.png)
1218
-
1219
- > _Seneca ${provider.Name}-Provider_ is a plugin for [Seneca](http://senecajs.org)
1220
-
1221
- Provides access to the ${provider.api} API using the Seneca _provider_
1222
- convention. ${provider.api} entities are represented as Seneca entities so that
1223
- they can be accessed using the Seneca entity API and messages.
1224
-
1225
- Requests are handled by the [${provider.api} SDK](${provider.sdkRepoUrl}),
1226
- which is generated from the API's OpenAPI specification. This plugin is
1227
- generated from the same specification by
1228
- [@voxgig/sdkgen](https://github.com/voxgig/sdkgen) — do not edit it by hand,
1229
- change the model and regenerate.
1230
-
1231
- See [seneca-entity](https://github.com/senecajs/seneca-entity) and the [Seneca Data
1232
- Entities
1233
- Tutorial](https://senecajs.org/docs/tutorials/understanding-data-entities.html)
1234
- for more details on the Seneca entity API.
1235
-
1236
- [![build](${provider.repoUrl}/actions/workflows/build.yml/badge.svg)](${provider.repoUrl}/actions/workflows/build.yml)
1237
-
1238
- | This open source module is sponsored and supported by [${provider.publisher}](${provider.publisherUrl}). |
1239
- | --- |
1240
-
1241
-
1242
- <!--START:SECTION:intro-->
1243
- <!--END:SECTION:intro-->
1244
-
1245
-
1246
- ## Documentation
1247
-
1248
- Full documentation lives in [\`doc/\`](doc/README.md) and follows the
1249
- [Diátaxis](https://diataxis.fr) framework:
1250
-
1251
- | Document | Purpose |
1252
- | -------- | ------- |
1253
- | [Tutorial](doc/tutorial.md) | Start here. Build a working script from an empty folder. |
1254
- | [How-to guides](doc/how-to.md) | Recipes for specific tasks. |
1255
- | [Reference](doc/reference.md) | Every pattern, entity, option and export. |
1256
- | [Explanation](doc/explanation.md) | Why the plugin is designed this way. |
1257
-
1258
-
1259
- ## Quick Example
1260
-
1261
- \`\`\`js
1262
- const Seneca = require('seneca')
1263
-
1264
- const seneca = Seneca()
1265
- .use('promisify')
1266
- .use('entity')
1267
- .use('env', { var: { $${provider.ENV}_APIKEY: '' } })
1268
- .use('provider', {
1269
- provider: {
1270
- ${provider.lower}: {
1271
- keys: { apikey: { value: '$${provider.ENV}_APIKEY' } },
1272
- },
1273
- },
1274
- })
1275
- .use('${provider.pkgName}')
1276
-
1277
- await seneca.ready()
1278
-
1279
- `)
1280
- if (subject.cmds.includes('list')) {
1281
- Content(`const ${subject.name}s = await seneca
1282
- .entity('provider/${provider.lower}/${subject.name}').list$()
1283
- `)
1284
- }
1285
- if (subject.cmds.includes('load')) {
1286
- Content(`const ${subject.name} = await seneca
1287
- .entity('provider/${provider.lower}/${subject.name}').load$('some-id')
1288
- `)
1289
- }
1290
- Content(`\`\`\`
1291
-
1292
-
1293
- ## Install
1294
-
1295
- \`\`\`sh
1296
- npm install ${provider.pkgName}
1297
- \`\`\`
1298
-
1299
- This plugin expects the Seneca host framework to be present:
1300
-
1301
- \`\`\`sh
1302
- npm install seneca seneca-entity seneca-promisify @seneca/provider @seneca/env
1303
- \`\`\`
1304
-
1305
-
1306
- ## Options
1307
-
1308
- | Option | Type | Description |
1309
- | --- | --- | --- |
1310
- | \`sdk\` | object | Passed straight to the \`${provider.sdkClass}\` constructor. Most usefully \`base\`, to point at a server. |
1311
- | \`test\` | boolean | Run the SDK in offline test mode (in-memory mock transport). |
1312
- | \`testopts\` | object | Seed and options for the mock, used only when \`test\` is true. |
1313
-
1314
-
1315
- ## Entities
1316
-
1317
- Each API entity is exposed as a Seneca entity under
1318
- \`provider/${provider.lower}/<entity>\`.
1319
-
1320
- | Seneca entity | Commands | Fields |
1321
- | --- | --- | --- |
1322
- `)
1323
- // Fields as well as commands: a reader deciding whether this plugin
1324
- // covers what they need has to know what a record CONTAINS, and the
1325
- // table used to answer only half the question.
1326
- each(provider.entities, (e: any) => {
1327
- const fields = 0 === e.fields.length ? '—' :
1328
- e.fields.map((f: any) => '`' + f.name + '`').join(', ')
1329
- Content(`| \`provider/${provider.lower}/${e.name}\` | ${e.cmds.map((c: string) => '`' + c + '$`').join(', ')} | ${fields} |
1330
- `)
1331
- })
1332
-
1333
- if (0 < nested.length) {
1334
- Content(`
1335
- ### Nested entities
1336
-
1337
- Some entities live under a parent in the API path, so every command needs the
1338
- parent's id in the query. Leaving it out throws with a message naming the
1339
- missing key, rather than failing as an opaque 404 from a half-built URL.
1340
-
1341
- `)
1342
- each(nested, (e: any) => {
1343
- Content(`- \`${e.name}\` requires \`${e.parents.join('`, `')}\`
1344
- `)
1345
- })
1346
- }
1347
-
1348
- Content(`
1349
-
1350
- ## Action Patterns
1351
-
1352
- Every message pattern this plugin registers. The entity actions are the ones
1353
- \`seneca-entity\` dispatches to when you call \`list$\` / \`load$\` / \`save$\` /
1354
- \`remove$\` on a canon below — you rarely post them by hand, but they are what
1355
- appears in a Seneca log, and a plugin that documents one of nine is a plugin
1356
- whose logs cannot be read.
1357
-
1358
- | Pattern | Description |
1359
- | --- | --- |
1360
- | \`sys:provider,provider:${provider.lower},get:info\` | Plugin and SDK version information. |
1361
- `)
1362
-
1363
- const CMD_DESC: Record<string, string> = {
1364
- list: 'List records',
1365
- load: 'Load one record',
1366
- save: 'Create or update a record',
1367
- remove: 'Remove a record',
1368
- }
1369
-
1370
- each(provider.entities, (e: any) => {
1371
- each(e.cmds, (cmd: any) => {
1372
- const c = String(cmd.val$ ?? cmd)
1373
- Content(`| \`sys:entity,cmd:${c},zone:provider,base:${provider.lower},name:${e.name}\` | ${CMD_DESC[c]}. |
1374
- `)
1375
- })
1376
- })
1377
-
1378
- Content(`
1379
-
1380
-
1381
- ## More Examples
1382
-
1383
- ### Offline testing
1384
-
1385
- The SDK ships an in-memory mock transport, so this plugin can be exercised
1386
- with no server:
1387
-
1388
- \`\`\`js
1389
- .use('${provider.pkgName}', { test: true, testopts: { entity: { ... } } })
1390
- \`\`\`
1391
-
1392
- \`testopts\` is passed straight to the SDK's test constructor; \`entity\`
1393
- seeds the mock store. See \`test/seed.js\` for the shape.
1394
-
1395
- `)
1396
- if ('' !== provider.liveBase) {
1397
- Content(`### Running against a server
1398
-
1399
- \`\`\`js
1400
- .use('${provider.pkgName}', { sdk: { base: '${provider.liveBase}' } })
1401
- \`\`\`
1402
-
1403
- The companion test server is distributed in the SDK's source repository
1404
- only. From a checkout beside this one:
1405
-
1406
- \`\`\`sh
1407
- cd ${provider.sdkrel}/app && npm start
1408
- \`\`\`
1409
-
1410
- Then \`node test/live.js\` reads from it, and \`node test/quick.js\` runs a
1411
- full create/update/load/remove cycle.
1412
-
1413
- `)
1414
- }
1415
-
1416
- Content(`
1417
- ## Motivation
1418
-
1419
- Applications rarely talk to one external service, and each service usually
1420
- arrives with its own client library, authentication style and error
1421
- conventions. That variety leaks into application code and makes it harder to
1422
- test.
1423
-
1424
- The Seneca provider convention removes the variety: every external service
1425
- becomes a Seneca entity reached with \`list$\`, \`load$\`, \`save$\` and
1426
- \`remove$\`, so application code has one shape regardless of what it talks to.
1427
-
1428
- The SDK underneath arrives at a similar conclusion from the other side — it
1429
- deliberately exposes entities rather than HTTP routes. This plugin is the
1430
- short bridge between the two.
1431
-
1432
-
1433
- ## Support
1434
-
1435
- - Issues and bugs: [GitHub issues](${provider.repoUrl}/issues)
1436
- - Seneca community: [senecajs.org](http://senecajs.org)
1437
-
1438
-
1439
- ## API
1440
-
1441
- ### Plugin export: \`${provider.pluginName}/sdk\`
1442
-
1443
- Returns the configured \`${provider.sdkClass}\` instance, for the operations
1444
- the entity API does not cover:
1445
-
1446
- \`\`\`js
1447
- const sdk = seneca.export('${provider.pluginName}/sdk')()
1448
- \`\`\`
1449
-
1450
-
1451
- ## Contributing
1452
-
1453
- This plugin is GENERATED. Changes belong in the SDK project's model and
1454
- components, not here — anything edited in this repository is overwritten by
1455
- the next generation run.
1456
-
1457
- The [Senecajs org](http://senecajs.org) encourages open participation. If you
1458
- feel you can help in any way, be it with bug reporting, documentation,
1459
- examples, extra testing, or new features, please get in touch.
1460
-
1461
-
1462
- ## Background
1463
-
1464
- Generated by [@voxgig/sdkgen](https://github.com/voxgig/sdkgen) from the
1465
- ${provider.api} API definition, against the
1466
- [${provider.sdkPkg}](https://www.npmjs.com/package/${provider.sdkPkg}) SDK.
1467
- `)
1468
- })
1469
- })
1470
-
1471
-
1472
- // --- doc/tutorial.md ---------------------------------------------------------
1473
- //
1474
- // The Diataxis TUTORIAL: an empty folder to a working script in about fifteen
1475
- // minutes. It teaches, so it is deliberately narrower than the other three
1476
- // documents — one path, no alternatives, and no decisions asked of the
1477
- // reader.
1478
- //
1479
- // Two decisions shape this component.
1480
- //
1481
- // FIRST, a tutorial must never ask the reader to invent a value. Every id in
1482
- // the script is therefore either seeded here (offline) or read back from a
1483
- // list call (live) — never a literal that only happens to exist on the
1484
- // author's machine. That is also why a declared server is not by itself
1485
- // enough to choose the live lesson: the primary entity must be listable, and
1486
- // a nested primary entity must have a listable parent, or there is no honest
1487
- // way to come by the first id. Failing that the offline lesson runs, which is
1488
- // a complete tutorial in its own right rather than an apology for a missing
1489
- // server.
1490
- //
1491
- // SECOND, the step numbers are computed rather than written, because which
1492
- // steps exist depends on which cmds the model declares. `step()` counts as it
1493
- // emits, and the prose refers to what a step did rather than to a number that
1494
- // may not be there.
1495
- //
1496
- // The offline seed reuses seedRecord() — the same function behind
1497
- // test/seed.js — so what the reader is told to paste has the shape the SDK
1498
- // really answers with.
1499
-
1500
- const DocTutorial = cmp(function DocTutorial(props: any) {
1501
- const { provider } = props
1502
-
1503
- // Entity and field names come from an API definition, so they cannot be
1504
- // assumed to be legal JavaScript identifiers.
1505
- const ident = (s: string) => String(s).replace(/[^A-Za-z0-9_$]/g, '_')
1506
- const qkey = (k: string) =>
1507
- /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(String(k)) ? String(k) : `'${k}'`
1508
- const plural = (n: string) =>
1509
- n.endsWith('s') ? `${ident(n)}List` : `${ident(n)}s`
1510
- const canon = (n: string) => `provider/${provider.lower}/${n}`
1511
- const entOf = (name: string) =>
1512
- provider.entities.find((e: any) => e.name === name) || null
1513
-
1514
- // The entity the lesson is built on: fewest parent keys to arrange, then
1515
- // most cmds to show. The same choice the tests and the README make, so all
1516
- // three talk about the same thing.
1517
- const subject = [...provider.entities]
1518
- .sort((a: any, b: any) =>
1519
- (a.parents.length - b.parents.length) || (b.cmds.length - a.cmds.length))[0]
1520
-
1521
- const idf = subject.idf || 'id'
1522
- const subjParent = 0 < subject.parents.length ?
1523
- entOf(subject.parentEntity) : null
1524
-
1525
- const hasServer = '' !== provider.liveBase
1526
-
1527
- // Can the live lesson actually be written? It needs a server AND a first id
1528
- // the script can discover for itself.
1529
- const live = hasServer &&
1530
- subject.cmds.includes('list') &&
1531
- (0 === subject.parents.length ||
1532
- (1 === subject.parents.length &&
1533
- null != subjParent && subjParent.cmds.includes('list')))
1534
-
1535
- const offline = !live
1536
-
1537
- // The nested entity the lesson finishes on. Prefer one hanging off the
1538
- // subject, so the reader recognises the parent id when it turns up.
1539
- const readable = (e: any) => e.cmds.includes('list') || e.cmds.includes('load')
1540
- const nested = provider.entities
1541
- .filter((e: any) => 0 < e.parents.length && e.name !== subject.name)
1542
- .filter(readable)
1543
- // Live, the parent id has to come from somewhere: exactly one parent key,
1544
- // and a parent that can be listed.
1545
- .filter((e: any) => offline ||
1546
- (1 === e.parents.length && null != entOf(e.parentEntity) &&
1547
- entOf(e.parentEntity).cmds.includes('list')))
1548
- const child = nested.find((e: any) => e.parentEntity === subject.name) ||
1549
- nested.find((e: any) => null != entOf(e.parentEntity)) ||
1550
- nested[0] || null
1551
- const childParent = null == child ? null : entOf(child.parentEntity)
1552
-
1553
- // The value seedRecord() gives a parent key, so a query written here finds
1554
- // the seeded record instead of quietly matching nothing.
1555
- const seedParentVal = (e: any, k: string) => {
1556
- const f = (e.fields || []).find((f: any) => f.name === k)
1557
- const pe = (null != f && '' !== f.parentEntity) ? f.parentEntity :
1558
- (k === e.parents[0] ? (e.parentEntity || '') : '')
1559
- return `${pe}0`
1560
- }
1561
-
1562
- // A seed record guaranteed to carry its id and its parent keys.
1563
- // seedRecord() emits only the fields the model marks required, and a record
1564
- // missing its parent key is invisible to the very query this lesson makes.
1565
- const demoRecord = (e: any, idx: number) => {
1566
- const rec: any = seedRecord(e, idx)
1567
- const eidf = e.idf || 'id'
1568
- if (null == rec[eidf]) {
1569
- rec[eidf] = `${e.name}${idx}`
1570
- }
1571
- for (const k of e.parents) {
1572
- if (null == rec[k]) {
1573
- rec[k] = seedParentVal(e, k)
1574
- }
1575
- }
1576
- return rec
1577
- }
1578
-
1579
- // Only the entities the lesson touches, in model order.
1580
- const seedNames = [subject.name]
1581
- if (null != subjParent) seedNames.push(subjParent.name)
1582
- if (null != child) seedNames.push(child.name)
1583
- if (null != childParent) seedNames.push(childParent.name)
1584
-
1585
- const seedLiteral = provider.entities
1586
- .filter((e: any) => seedNames.includes(e.name))
1587
- .map((e: any) =>
1588
- ` ${qkey(e.name)}: {\n` +
1589
- [0, 1].map((idx: number) =>
1590
- ` ${qkey(e.name + idx)}: ${JSON.stringify(demoRecord(e, idx))},\n`)
1591
- .join('') +
1592
- ` },\n`)
1593
- .join('')
1594
-
1595
- // Every call on the subject carries its parent keys, if it has any: live
1596
- // they come from a lookup, offline from the seed.
1597
- const subjKeys = subject.parents.map((k: string) =>
1598
- `${qkey(k)}: ${offline ? `'${seedParentVal(subject, k)}'` : ident(k)}`)
1599
- const listArg = 0 === subjKeys.length ? '' : `{ ${subjKeys.join(', ')} }`
1600
- const oneArg = (id: string) => 0 === subjKeys.length ? id :
1601
- `{ ${subjKeys.concat([`${qkey(idf)}: ${id}`]).join(', ')} }`
1602
-
1603
- const subjVar = plural(subject.name)
1604
- const subjOne = ident(subject.name)
1605
-
1606
- // Live, the first id is whatever the server answered with; offline it is
1607
- // seeded above.
1608
- const firstId = offline ? `'${subject.name}0'` : `${subjVar}[0].${idf}`
1609
-
1610
- // A nested subject needs its parent's id before anything else can run.
1611
- const subjPre = (live && null != subjParent) ?
1612
- ` // ${subject.name} records live under ${subjParent.name} records in the API,
1613
- // so every ${subject.name} call needs a ${subject.parents[0]}.
1614
- const ${plural(subjParent.name)} = await seneca
1615
- .entity('${canon(subjParent.name)}')
1616
- .list$()
1617
- const ${ident(subject.parents[0])} = ${plural(subjParent.name)}[0].${subjParent.idf || 'id'}
1618
-
1619
- ` : ''
1620
-
1621
- // Fields worth printing, and worth writing: not the id, not a parent key.
1622
- const plainFields = subject.fields.filter((f: any) =>
1623
- f.name !== idf && 'id' !== f.name && !subject.parents.includes(f.name))
1624
- const shown = plainFields.slice(0, 2)
1625
- const litval = (f: any, alt: boolean) =>
1626
- 'number' === f.kind ? (alt ? '4321' : '1234') :
1627
- 'boolean' === f.kind ? (alt ? 'true' : 'false') :
1628
- `'tutorial-${f.name}${alt ? '-2' : ''}'`
1629
-
1630
- const makeFields = subject.parents
1631
- .map((k: string) =>
1632
- `${qkey(k)}: ${offline ? `'${seedParentVal(subject, k)}'` : ident(k)}`)
1633
- .concat(plainFields.map((f: any) => `${qkey(f.name)}: ${litval(f, false)}`))
1634
-
1635
- const hasRead = subject.cmds.includes('list') || subject.cmds.includes('load')
1636
- // Creating a record with nothing in it teaches nothing, so the write step
1637
- // needs at least one field the caller actually supplies.
1638
- const canWrite = subject.cmds.includes('save') && 0 < plainFields.length
1639
- const canUpdate = canWrite && subject.ops.includes('update')
1640
- const canRemove = canWrite && subject.cmds.includes('remove')
1641
-
1642
- const cmdList = subject.cmds.map((c: string) => '`' + c + '$`').join(', ')
1643
-
1644
- // Entity names are lowercase, and some of them have to start a sentence.
1645
- const cap = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)
1646
-
1647
- // A nested SUBJECT would otherwise carry an unexplained parent key through
1648
- // every example in the lesson. Said once, before the first call.
1649
- const nestedNote = 0 === subject.parents.length ? '' :
1650
- `${cap(subject.name)} records live inside ${'' === subject.parentEntity ?
1651
- 'a parent record' : `${subject.parentEntity} records`} in the API,
1652
- and the route says so:
1653
-
1654
- \`${subject.path}\`
1655
-
1656
- The parent id there is not optional, so every ${subject.name} call
1657
- carries ${subject.parents.map((k: string) => '`' + k + '`').join(' and ')} in its query. Leave it out and the provider
1658
- names the key you missed, rather than letting a half-built URL come
1659
- back as a puzzling 404.
1660
-
1661
- `
1662
-
1663
- // The clone lands in a directory named for the repository, so the cd that
1664
- // follows can be exact rather than "wherever you put it". With no repo url
1665
- // to clone from, the only path anyone can be told is the relative one back
1666
- // to the SDK project.
1667
- const sdkRepo = String(provider.sdkRepoUrl || '')
1668
- const sdkDir = sdkRepo.replace(/\/+$/, '').split('/').pop() || 'sdk'
1669
- const appDir = '' === sdkRepo ? `${provider.sdkrel}/app` : `${sdkDir}/app`
1670
-
1671
- const getServer = '' === sdkRepo ?
1672
- `You also need a server to talk to. The SDK itself installs from npm,
1673
- but its test server does not — it ships only in the SDK's source
1674
- project, in its \`app\` folder, which is at \`${provider.sdkrel}\`
1675
- relative to this one.
1676
- ` :
1677
- `You also need a server to talk to. The SDK itself installs from npm,
1678
- but its test server does not — it ships only in the SDK's source
1679
- repository, so clone that:
1680
-
1681
- \`\`\`sh
1682
- $ git clone ${sdkRepo}.git
1683
- \`\`\`
1684
-
1685
- If you already have that checkout beside this plugin, it is at
1686
- \`${provider.sdkrel}\`, and you can skip the clone.
1687
- `
1688
-
1689
- // What a bare GET on the probe route answers with, when the model has one.
1690
- const probeEnt = '' === provider.probePath ? null :
1691
- provider.entities.find((e: any) => e.path === provider.probePath)
1692
-
1693
- // Who assigns ids and holds the data, in prose.
1694
- const source = live ? 'server' : 'store'
1695
-
1696
- // Where the nested-subject note lands: the first step that shows a call.
1697
- const noteAt = subject.cmds.includes('list') ? 'list' :
1698
- subject.cmds.includes('load') ? 'load' : 'write'
1699
-
1700
- let stepno = 0
1701
- const step = (title: string) => `## Step ${++stepno}: ${title}`
1702
-
1703
- File({ name: 'tutorial.md' }, () => {
1704
-
1705
- Content(`# Tutorial: your first ${provider.Name} query
1706
-
1707
- This tutorial takes you from an empty folder to a script that
1708
- ${canWrite ? 'reads and writes' : 'reads'} ${provider.api} data through
1709
- Seneca entities. It should take about fifteen minutes.
1710
-
1711
- You will build one script and add to it as you go.`)
1712
-
1713
- if (live) {
1714
- Content(` Everything runs
1715
- locally against a test server you start yourself, so nothing here can
1716
- affect anything outside your machine.
1717
-
1718
- You need [Node.js](https://nodejs.org) 24 or later.
1719
-
1720
- ${getServer}
1721
- ${step('Start the test server')}
1722
-
1723
- That server implements the ${provider.api} API. Build and start it:
1724
-
1725
- \`\`\`sh
1726
- $ cd ${appDir}
1727
- $ npm install
1728
- $ npm run build
1729
- $ npm start
1730
- \`\`\`
1731
-
1732
- It listens on \`${provider.liveBase}\`. Check it from another terminal:
1733
-
1734
- \`\`\`sh
1735
- $ curl ${provider.liveBase}${provider.probePath}
1736
- \`\`\`
1737
-
1738
- `)
1739
- Content(null == probeEnt ?
1740
- `You should get a JSON answer rather than a refused connection.
1741
- Leave the server running.
1742
-
1743
- ` :
1744
- `You should see a JSON array of ${probeEnt.name} records.
1745
- Leave the server running.
1746
-
1747
- `)
1748
- }
1749
- else {
1750
- Content(` Everything runs in
1751
- memory: the SDK ships an offline mode backed by a small in-memory
1752
- store, and you supply that store's contents yourself. No request leaves
1753
- your machine, so nothing here can affect anything outside it.
1754
-
1755
- You need [Node.js](https://nodejs.org) 24 or later. You do not need a
1756
- server, a network connection, or credentials.
1757
-
1758
- `)
1759
- }
1760
-
1761
- Content(`${step('Create the project')}
1762
-
1763
- `)
1764
- if (live) {
1765
- Content(`In a new terminal:
1766
-
1767
- `)
1768
- }
1769
- Content(`\`\`\`sh
1770
- $ mkdir ${provider.lower}-demo
1771
- $ cd ${provider.lower}-demo
1772
- $ npm init -y
1773
- $ npm install seneca seneca-entity seneca-promisify @seneca/provider ${provider.pkgName}
1774
- \`\`\`
1775
-
1776
- The first four are the Seneca host: the framework itself, the entity
1777
- API, the promise wrapper that makes calls awaitable, and the shared
1778
- machinery every Seneca provider is built on. The last is this plugin,
1779
- which brings the ${provider.api} SDK with it.
1780
-
1781
- ${step('Connect')}
1782
-
1783
- Create \`demo.js\`:
1784
-
1785
- \`\`\`js
1786
- const Seneca = require('seneca')
1787
-
1788
- `)
1789
-
1790
- if (offline) {
1791
- Content(`// The offline store. Each key under an entity name is that record's
1792
- // id, and each record is what the API would have answered with.
1793
- const SEED = {
1794
- entity: {
1795
- ${seedLiteral} },
1796
- }
1797
-
1798
- `)
1799
- }
1800
-
1801
- Content(`async function main() {
1802
- const seneca = await Seneca({ legacy: false })
1803
- .use('promisify')
1804
- .use('entity')
1805
- .use('provider', {
1806
- provider: {
1807
- ${qkey(provider.lower)}: {
1808
- keys: {
1809
- apikey: { value: '' },
1810
- },
1811
- },
1812
- },
1813
- })
1814
- `)
1815
- Content(live ?
1816
- ` .use('${provider.pkgName}', {
1817
- sdk: { base: '${provider.liveBase}' },
1818
- })
1819
- ` :
1820
- ` .use('${provider.pkgName}', {
1821
- test: true,
1822
- testopts: SEED,
1823
- })
1824
- `)
1825
- Content(` .ready()
1826
-
1827
- const info = await seneca.post('sys:provider,provider:${provider.lower},get:info')
1828
- console.log(info)
1829
- }
1830
-
1831
- main()
1832
- \`\`\`
1833
-
1834
- Run it:
1835
-
1836
- \`\`\`sh
1837
- $ node demo.js
1838
- \`\`\`
1839
-
1840
- You should see:
1841
-
1842
- \`\`\`js
1843
- {
1844
- ok: true,
1845
- name: '${provider.lower}',
1846
- version: '${provider.version}',
1847
- sdk: { name: '${provider.sdkPkg}', version: '${provider.sdkVersion}' },
1848
- }
1849
- \`\`\`
1850
-
1851
- Two details of that configuration are worth a moment. The \`apikey\` is
1852
- declared even though nothing here asks for credentials — an empty
1853
- value simply means no \`authorization\` header is sent. Every Seneca
1854
- provider is configured the same way, so an application that later moves
1855
- to an authenticated service changes one value rather than its shape.
1856
- And \`get:info\` is answered by the plugin itself, without calling the
1857
- API, so a reply tells you the plugin loaded and initialised before any
1858
- request goes anywhere.
1859
-
1860
- `)
1861
-
1862
- if (subject.cmds.includes('list')) {
1863
- Content(`${step(`List the ${subject.name} records`)}
1864
-
1865
- ${'list' === noteAt ? nestedNote : ''}Replace the \`console.log(info)\` line with:
1866
-
1867
- \`\`\`js
1868
- ${subjPre} const ${subjVar} = await seneca
1869
- .entity('${canon(subject.name)}')
1870
- .list$(${listArg})
1871
-
1872
- console.log('Found ' + ${subjVar}.length + ' ${subject.name} record(s):')
1873
- ${subjVar}.forEach((r) => {
1874
- console.log(' ' + r.${idf}${shown.map((f: any) => ` + ' ' + r.${f.name}`).join('')})
1875
- })
1876
- \`\`\`
1877
-
1878
- `)
1879
- Content(offline ?
1880
- `Run it again and you will see the two ${subject.name}
1881
- records you seeded, under the ids they are filed by.
1882
-
1883
- ` :
1884
- `Run it again and you will see every ${subject.name}
1885
- record the server holds.
1886
-
1887
- `)
1888
- Content(`No URL, no HTTP verb, no JSON parsing. You asked a Seneca entity for
1889
- a list, the provider turned that into an SDK call, and the SDK turned
1890
- it into a request. These are ordinary Seneca entities, so everything
1891
- you already know about the entity API applies to them.
1892
-
1893
- `)
1894
- }
1895
-
1896
- if (subject.cmds.includes('load')) {
1897
- Content(`${step(`Load one ${subject.name}`)}
1898
-
1899
- ${'load' === noteAt ? nestedNote : ''}Add:
1900
-
1901
- \`\`\`js
1902
- const one = await seneca
1903
- .entity('${canon(subject.name)}')
1904
- .load$(${oneArg(firstId)})
1905
-
1906
- console.log('loaded', one.${idf}${0 < shown.length ? `, one.${shown[0].name}` : ''})
1907
- \`\`\`
1908
-
1909
- `)
1910
- Content(subject.cmds.includes('list') ?
1911
- `\`list$\` gives you many, \`load$\` gives you one. Now ask for
1912
- something that is not there:
1913
-
1914
- ` :
1915
- `\`load$\` gives you one record by its id. Now ask for something
1916
- that is not there:
1917
-
1918
- `)
1919
- Content(`\`\`\`js
1920
- const missing = await seneca
1921
- .entity('${canon(subject.name)}')
1922
- .load$(${oneArg(`'nosuch${subject.name}'`)})
1923
-
1924
- console.log('missing =', missing) // null
1925
- \`\`\`
1926
-
1927
- You get \`null\`, not an exception. "There is no such
1928
- ${subject.name}" is an ordinary answer to a lookup, so it does not
1929
- interrupt your code.
1930
-
1931
- `)
1932
- }
1933
-
1934
- if (canWrite) {
1935
- Content(`${step('Create, change and remove')}
1936
-
1937
- ${'write' === noteAt ? nestedNote : ''}`)
1938
- Content(hasRead ?
1939
- `Everything so far has been reading. This entity accepts writes too,
1940
- so add:
1941
-
1942
- ` :
1943
- `Now write one. Add:
1944
-
1945
- `)
1946
- Content(`\`\`\`js
1947
- // Create: make$ builds an entity, save$ persists it.
1948
- let ${subjOne} = await seneca
1949
- .entity('${canon(subject.name)}')
1950
- .make$({ ${makeFields.join(', ')} })
1951
- .save$()
1952
-
1953
- console.log('created with id', ${subjOne}.${idf})
1954
- \`\`\`
1955
-
1956
- Run it, and note the id printed. It is **not** one you chose — the
1957
- ${source} assigns ids itself and ignores any you send. That is worth
1958
- knowing before you write code that assumes otherwise.
1959
-
1960
- `)
1961
-
1962
- if (canUpdate) {
1963
- Content(`Now change it. An entity that already carries an id is an update
1964
- rather than a create, and \`save$\` decides between the two on exactly
1965
- that:
1966
-
1967
- \`\`\`js
1968
- ${subjOne}.${plainFields[0].name} = ${litval(plainFields[0], true)}
1969
- ${subjOne} = await ${subjOne}.save$()
1970
-
1971
- console.log('updated:', ${subjOne}.${plainFields[0].name})
1972
- \`\`\`
1973
-
1974
- `)
1975
- }
1976
-
1977
- if (canRemove) {
1978
- Content(`And remove it, leaving the ${source} as you found it:
1979
-
1980
- \`\`\`js
1981
- await seneca
1982
- .entity('${canon(subject.name)}')
1983
- .remove$(${oneArg(`${subjOne}.${idf}`)})
1984
- \`\`\`
1985
-
1986
- `)
1987
- if (subject.cmds.includes('load')) {
1988
- Content(`Load it once more and, as before, you get \`null\`:
1989
-
1990
- \`\`\`js
1991
- console.log(
1992
- 'after remove:',
1993
- await seneca
1994
- .entity('${canon(subject.name)}')
1995
- .load$(${oneArg(`${subjOne}.${idf}`)})
1996
- ) // null
1997
- \`\`\`
1998
-
1999
- `)
2000
- }
2001
- }
2002
- else {
2003
- Content(`This entity declares no remove operation, so the record you have just
2004
- created stays where it is.
2005
-
2006
- `)
2007
- }
2008
-
2009
- Content(`Those are the only methods there are:
2010
-
2011
- ${cmdList}
2012
-
2013
- They behave the same way on every entity this plugin exposes.
2014
-
2015
- `)
2016
- }
2017
-
2018
- if (null != child) {
2019
- const ckey = child.parents[0]
2020
- const cidf = child.idf || 'id'
2021
- const cparent = null == childParent ? 'their parent' :
2022
- `${childParent.name} records`
2023
- const cop = child.cmds.includes('list') ? 'list' : 'load'
2024
-
2025
- // Live, the parent id comes from a list; offline it is seeded, so a
2026
- // literal is both shorter and exactly reproducible.
2027
- const reuse = `${subjVar}[0].${idf}`
2028
- const cval = offline ? `'${seedParentVal(child, ckey)}'` :
2029
- (null != childParent && childParent.name === subject.name &&
2030
- subject.cmds.includes('list') && 0 === subject.parents.length) ?
2031
- reuse : ident(ckey)
2032
-
2033
- const cpre = (live && reuse !== cval) ?
2034
- ` const ${plural(childParent.name)} = await seneca
2035
- .entity('${canon(childParent.name)}')
2036
- .list$()
2037
- const ${ident(ckey)} = ${plural(childParent.name)}[0].${childParent.idf || 'id'}
2038
-
2039
- ` : ''
2040
-
2041
- const cargs = child.parents.map((k: string) => k === ckey ?
2042
- `${qkey(k)}: ${cval}` : `${qkey(k)}: '${seedParentVal(child, k)}'`)
2043
-
2044
- Content(`${step(`Reach the ${child.name} records`)}
2045
-
2046
- ${cap(child.name)} records live inside ${cparent}, and the API route
2047
- says so:
2048
-
2049
- \`${child.path}\`
2050
-
2051
- The parent id in that path is not optional, so every ${child.name}
2052
- call needs a \`${ckey}\` in its query:
2053
-
2054
- \`\`\`js
2055
- `)
2056
-
2057
- if (child.cmds.includes('list')) {
2058
- Content(`${cpre} const ${plural(child.name)} = await seneca
2059
- .entity('${canon(child.name)}')
2060
- .list$({ ${cargs.join(', ')} })
2061
-
2062
- console.log('found ' + ${plural(child.name)}.length + ' ${child.name} record(s)')
2063
- `)
2064
- }
2065
- else {
2066
- Content(`${cpre} const found = await seneca
2067
- .entity('${canon(child.name)}')
2068
- .load$({ ${cargs.concat([`${qkey(cidf)}: '${child.name}0'`]).join(', ')} })
2069
-
2070
- console.log('found', found.${cidf})
2071
- `)
2072
- }
2073
-
2074
- Content(`\`\`\`
2075
-
2076
- Leave the \`${ckey}\` out and the call throws at once, naming the key it
2077
- needed, rather than letting a half-built URL come back as a puzzling
2078
- 404:
2079
-
2080
- \`\`\`js
2081
- // throws: ${provider.pkgName}: ${child.name} ${cop}: ${ckey} is required
2082
- await seneca
2083
- .entity('${canon(child.name)}')
2084
- .${'list' === cop ? 'list$()' : `load$('${child.name}0')`}
2085
- \`\`\`
2086
-
2087
- `)
2088
- }
2089
-
2090
- if (offline) {
2091
- Content(`## Talking to a real server
2092
-
2093
- The script you have just written never touched the network. To point it
2094
- at a running ${provider.api} server instead, replace the \`test\` and
2095
- \`testopts\` options with that server's base URL:
2096
-
2097
- \`\`\`js
2098
- .use('${provider.pkgName}', {
2099
- sdk: { base: '${hasServer ? provider.liveBase : 'https://api.example.com'}' },
2100
- })
2101
- \`\`\`
2102
-
2103
- Nothing else in the script changes — the entity calls are the same
2104
- calls. Your seeded ids will not exist there, so read the ids you need
2105
- from a \`list$\` first.
2106
-
2107
- `)
2108
- if (hasServer && '' !== sdkRepo) {
2109
- Content(`A test server that answers on that address is distributed in the SDK's
2110
- source repository, which is the only place it ships. Clone
2111
- \`${sdkRepo}\`, then run \`npm install\`, \`npm run build\` and
2112
- \`npm start\` in its \`app\` folder.
2113
-
2114
- `)
2115
- }
2116
- }
2117
-
2118
- Content(`## What you have learned
2119
-
2120
- You built a script that ${canWrite ? 'reads and writes' : 'reads'}
2121
- ${provider.api} data through Seneca entities,
2122
- ${live ? 'against a real server' : 'with no server involved'}. Along
2123
- the way you saw:
2124
-
2125
- - Provider configuration has the same shape even when no credentials
2126
- are needed.
2127
- - API resources are Seneca entities under \`provider/${provider.lower}/\`,
2128
- reached with the entity API you already know.
2129
- `)
2130
- if (null != child || 0 < subject.parents.length) {
2131
- Content(`- A resource nested under another in the API needs its parent's id in
2132
- every query, and says which key is missing when you forget.
2133
- `)
2134
- }
2135
- if (subject.cmds.includes('load')) {
2136
- Content(`- \`load$\` answers \`null\` for something that is not there, rather
2137
- than throwing.
2138
- `)
2139
- }
2140
- if (canWrite) {
2141
- Content(`- \`save$\` creates without an id and updates with one, and the
2142
- ${source} chooses the id.
2143
- `)
2144
- }
2145
- if (offline) {
2146
- Content(`- The offline store makes all of this runnable with nothing installed
2147
- but npm packages, which is also how you test your own code.
2148
- `)
2149
- }
2150
-
2151
- Content(`
2152
- ## Where to go next
2153
-
2154
- - To do a specific job — ${live ? 'run without a server' : 'point at a real server'}, reach the raw SDK,
2155
- test your own code — see the [how-to guides](how-to.md).
2156
- - To look up an exact pattern, field or option, see the
2157
- [reference](reference.md).
2158
- - To understand why the plugin is built this way — why entities rather
2159
- than one message per route, and what it does with the SDK's answers
2160
- — see the [explanation](explanation.md).
2161
- - For what each of these documents is for, see the
2162
- [documentation index](README.md).
2163
- `)
2164
- })
2165
- })
2166
-
2167
-
2168
- // --- doc/how-to.md ----------------------------------------------------------
2169
- //
2170
- // The task-oriented quadrant of the Diataxis set: one problem per section, for
2171
- // a reader who already has the plugin loaded. It instructs and does not
2172
- // explain — anything that starts justifying a design choice belongs in
2173
- // explanation.md and is linked to instead.
2174
- //
2175
- // Two decisions worth naming. First, the section list is built as data before
2176
- // anything is emitted, so the table of contents and the sections themselves
2177
- // are produced from the SAME guards and cannot drift: a recipe that is
2178
- // suppressed because no entity declares the cmd also loses its TOC entry.
2179
- // Second, every example id is the one `seedRecord` gives that entity, so the
2180
- // examples here and the seed in test/seed.js agree — the offline recipe can
2181
- // then be copied verbatim and the ids used in every other recipe will
2182
- // actually resolve.
2183
-
2184
- const DocHowto = cmp(function DocHowto(props: any) {
2185
- const { provider } = props
2186
-
2187
- const ents = provider.entities
2188
- const nested = ents.filter((e: any) => 0 < e.parents.length)
2189
-
2190
- // The model gives '' when the API definition declares no server. Normalise
2191
- // an absent value to the same thing, so a missing base is treated as absent
2192
- // rather than printed as a default nobody can use.
2193
- const liveBase = provider.liveBase || ''
2194
-
2195
- // The same choice the tests and the manual scripts make: fewest parent keys
2196
- // (nothing to arrange), then most cmds. Recipes prefer it, so one entity
2197
- // carries the reader through the document wherever it can.
2198
- const subject = [...ents]
2199
- .sort((a: any, b: any) =>
2200
- (a.parents.length - b.parents.length) || (b.cmds.length - a.cmds.length))[0]
2201
-
2202
- const forCmd = (cmd: string) => {
2203
- const able = ents.filter((e: any) => e.cmds.includes(cmd))
2204
- return able.find((e: any) => e === subject) ||
2205
- able.find((e: any) => 0 === e.parents.length) ||
2206
- able[0] || null
2207
- }
2208
-
2209
- const canon = (e: any) => `provider/${provider.lower}/${e.name}`
2210
- const idf = (e: any) => e.idf || 'id'
2211
-
2212
- // A parent key's example value. This MIRRORS seedRecord rather than
2213
- // inventing something more readable: the offline recipe below seeds with
2214
- // seedRecord, and an example id that does not match what was seeded turns
2215
- // every other recipe into a lookup that answers null.
2216
- const parentVal = (e: any, k: string) => {
2217
- const f = e.fields.find((f: any) => f.name === k)
2218
- return null == f ? `${k.replace(/_id$/, '')}0` : `${f.parentEntity}0`
2219
- }
2220
-
2221
- const parentArgs = (e: any) =>
2222
- e.parents.map((k: string) => `${k}: '${parentVal(e, k)}'`).join(', ')
2223
-
2224
- // A query naming ONE record. A top-level entity takes the bare id string;
2225
- // a nested one cannot, because it is identified by the whole set of keys.
2226
- const oneArgs = (e: any) => 0 === e.parents.length ?
2227
- `'${e.name}0'` : `{ ${parentArgs(e)}, ${idf(e)}: '${e.name}0' }`
2228
-
2229
- const listArgs = (e: any) =>
2230
- 0 === e.parents.length ? '' : `{ ${parentArgs(e)} }`
2231
-
2232
- // The SDK's own entity ops always take an object, even for a bare id.
2233
- const sdkLoadArgs = (e: any) => 0 === e.parents.length ?
2234
- `{ ${idf(e)}: '${e.name}0' }` :
2235
- `{ ${parentArgs(e)}, ${idf(e)}: '${e.name}0' }`
2236
-
2237
- const key = (k: string) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(k) ? k : `'${k}'`
2238
-
2239
- const literal = (rec: Record<string, any>) => {
2240
- const names = Object.keys(rec)
2241
- if (0 === names.length) {
2242
- return '{}'
2243
- }
2244
- return '{ ' + names
2245
- .map((k) => `${key(k)}: ` +
2246
- ('string' === typeof rec[k] ? `'${rec[k]}'` : String(rec[k])))
2247
- .join(', ') + ' }'
2248
- }
2249
-
2250
- // What a create sends: the seeded record without its id, because the id is
2251
- // the API's to assign. Parent keys stay — a nested write carries them in
2252
- // the data rather than the query.
2253
- const createData = (e: any) => {
2254
- const rec = seedRecord(e, 0)
2255
- delete rec[idf(e)]
2256
- delete rec.id
2257
- return rec
2258
- }
2259
-
2260
- const changeable = (e: any) => e.fields.find((f: any) =>
2261
- f.name !== idf(e) && 'id' !== f.name && !e.parents.includes(f.name))
2262
-
2263
- const newValue = (f: any) => 'number' === f.kind ? '999' :
2264
- 'boolean' === f.kind ? 'true' : `'${f.name}-changed'`
2265
-
2266
- const pathParams = (p: string) =>
2267
- (String(p).match(/\{([^}]+)\}/g) || []).map((s: string) => s.slice(1, -1))
2268
-
2269
- const eList = forCmd('list')
2270
- const eLoad = forCmd('load')
2271
- const eSave = forCmd('save')
2272
- const eRemove = forCmd('remove')
2273
-
2274
- // Sections as data, so the contents list and the sections cannot disagree.
2275
- const sections: any[] = []
2276
- const sec = (title: string, body: string) => sections.push({ title, body })
2277
-
2278
- // GitHub's heading anchors: lowercased, punctuation dropped, spaces
2279
- // hyphenated. Section titles avoid backticks and full stops so this stays
2280
- // a faithful reproduction rather than an approximation.
2281
- const anchor = (title: string) => '#' + title.toLowerCase()
2282
- .replace(/[^a-z0-9 _-]/g, '').trim().replace(/ +/g, '-')
2283
-
2284
- const NESTED_TITLE = 'Work with nested entities'
2285
- const OFFLINE_TITLE = 'Run offline, without a server'
2286
-
2287
-
2288
- if (null != eList) {
2289
- sec('List the records of an entity', `Every resource this plugin covers is a Seneca entity under
2290
- \`provider/${provider.lower}/\`, so listing one is \`list$\`:
2291
-
2292
- \`\`\`js
2293
- const ${eList.name}s = await seneca
2294
- .entity('${canon(eList)}')
2295
- .list$(${listArgs(eList)})
2296
- \`\`\`
2297
-
2298
- You get an ordinary array of Seneca entities back, so \`length\`, \`map\`
2299
- and \`data$()\` behave exactly as they do for any other store.
2300
-
2301
- Fields in the query travel to the API as match criteria. Seneca's own
2302
- directives — \`sort$\`, \`limit$\` and the rest — are stripped before the
2303
- call, because they are features of a database store and not of an HTTP
2304
- API. If you need ordering or paging, ask the API for it using fields it
2305
- recognises, or sort the returned array yourself.${0 < nested.length ? `
2306
-
2307
- An entity nested under a parent in the API path cannot be listed without
2308
- the parent's id; see [${NESTED_TITLE}](${anchor(NESTED_TITLE)}).` : ''}`)
2309
- }
2310
-
2311
-
2312
- if (null != eLoad) {
2313
- sec('Read one record by id', `\`load$\` answers a single record:
2314
-
2315
- \`\`\`js
2316
- const ${eLoad.name} = await seneca
2317
- .entity('${canon(eLoad)}')
2318
- .load$(${oneArgs(eLoad)})
2319
- \`\`\`
2320
- ${'id' === idf(eLoad) ? '' : `
2321
- The id field for \`${eLoad.name}\` is \`${idf(eLoad)}\`, so that is the
2322
- key to supply.
2323
- `}
2324
- A record that is not there comes back as \`null\`. It is not an error and
2325
- it does not throw, so test the value rather than wrapping the call:
2326
-
2327
- \`\`\`js
2328
- const missing = await seneca
2329
- .entity('${canon(eLoad)}')
2330
- .load$(${0 === eLoad.parents.length ? `'nosuch'` :
2331
- `{ ${parentArgs(eLoad)}, ${idf(eLoad)}: 'nosuch' }`})
2332
-
2333
- if (null == missing) {
2334
- // no such ${eLoad.name}
2335
- }
2336
- \`\`\`
2337
-
2338
- Everything else that can go wrong — a network failure, a 5xx, a rejected
2339
- key — does throw, so an unhandled rejection still means something is
2340
- genuinely wrong.`)
2341
- }
2342
-
2343
-
2344
- if (null != eSave) {
2345
- const created = literal(createData(eSave))
2346
-
2347
- sec('Create a record', `\`make$\` builds an entity and \`save$\` writes it. An entity with no id
2348
- is a create:
2349
-
2350
- \`\`\`js
2351
- const ${eSave.name} = await seneca
2352
- .entity('${canon(eSave)}')
2353
- .make$(${created})
2354
- .save$()
2355
-
2356
- console.log(${eSave.name}.${idf(eSave)})
2357
- \`\`\`
2358
- ${0 === eSave.parents.length ? '' : `
2359
- Note that \`${eSave.parents.join('`, `')}\` travels in the DATA for a write,
2360
- not in a query: a \`${eSave.name}\` is created inside its parent.
2361
- `}
2362
- \`save$\` resolves to the record as the API returned it, which is the only
2363
- reliable source of the id. Read it from there rather than predicting it:
2364
- what an API does with an id you supply on create is its own business, and
2365
- several ignore it entirely.`)
2366
-
2367
- const f = changeable(eSave)
2368
-
2369
- sec('Update a record', `The same call updates. \`save$\` dispatches on the id: an entity carrying
2370
- one is an update, an entity without one is a create. So the safe shape is
2371
- load, change, save:
2372
-
2373
- \`\`\`js${eSave.cmds.includes('load') ? `
2374
- const ${eSave.name} = await seneca
2375
- .entity('${canon(eSave)}')
2376
- .load$(${oneArgs(eSave)})
2377
- ` : `
2378
- const ${eSave.name} = seneca
2379
- .entity('${canon(eSave)}')
2380
- .make$(${literal(0 === eSave.parents.length ?
2381
- { [idf(eSave)]: `${eSave.name}0` } :
2382
- { ...Object.fromEntries(eSave.parents.map(
2383
- (k: string) => [k, parentVal(eSave, k)])),
2384
- [idf(eSave)]: `${eSave.name}0` })})
2385
- `}${null == f ? `
2386
- // change the fields you need
2387
- ` : `
2388
- ${eSave.name}.${f.name} = ${newValue(f)}
2389
- `}
2390
- await ${eSave.name}.save$()
2391
- \`\`\`
2392
-
2393
- Mutating the record you loaded sends it as it stood plus your change, so
2394
- you do not depend on how the API treats a request that omits fields —
2395
- some merge, some replace.`)
2396
- }
2397
-
2398
-
2399
- if (null != eRemove) {
2400
- sec('Remove a record', `\`\`\`js
2401
- await seneca
2402
- .entity('${canon(eRemove)}')
2403
- .remove$(${oneArgs(eRemove)})
2404
- \`\`\`
2405
- ${0 === eRemove.parents.length ? '' : `
2406
- As with a read, the parent keys are part of naming the record, so they go
2407
- in the query object alongside the id.
2408
- `}${eRemove.cmds.includes('load') ? `
2409
- A \`load$\` of the same id afterwards answers \`null\`.` :
2410
- `
2411
- \`remove$\` resolves once the API has accepted the removal.`}`)
2412
- }
2413
-
2414
-
2415
- if (0 < nested.length) {
2416
- // A nested entity that declares no cmds has nothing to demonstrate, so
2417
- // prefer one that does; the error example needs a command that exists.
2418
- const n = nested.find((e: any) => 0 < e.cmds.length) || nested[0]
2419
- const firstCmd = n.cmds[0] || 'list'
2420
-
2421
- sec(NESTED_TITLE, `Some resources live inside a parent, and the API path says so — the
2422
- route for \`${n.name}\` is:
2423
-
2424
- \`\`\`
2425
- ${n.path}
2426
- \`\`\`
2427
-
2428
- So a \`${n.name}\` cannot be addressed at all without its parent's id, and
2429
- the provider requires those keys on every command.
2430
-
2431
- ${nested.map((e: any) =>
2432
- `- \`${e.name}\` requires \`${e.parents.join('`, `')}\``).join('\n')}
2433
-
2434
- For reads the keys go in the query; for writes they go in the data:
2435
-
2436
- \`\`\`js${n.cmds.includes('list') ? `
2437
- await seneca.entity('${canon(n)}').list$({ ${parentArgs(n)} })
2438
- ` : ''}${n.cmds.includes('load') ? `
2439
- await seneca.entity('${canon(n)}')
2440
- .load$({ ${parentArgs(n)}, ${idf(n)}: '${n.name}0' })
2441
- ` : ''}${n.cmds.includes('save') ? `
2442
- await seneca.entity('${canon(n)}')
2443
- .make$(${literal(createData(n))})
2444
- .save$()
2445
- ` : ''}${n.cmds.includes('remove') ? `
2446
- await seneca.entity('${canon(n)}')
2447
- .remove$({ ${parentArgs(n)}, ${idf(n)}: '${n.name}0' })
2448
- ` : ''}\`\`\`
2449
-
2450
- Leave a key out and the call throws at once, naming what is missing:
2451
-
2452
- \`\`\`
2453
- ${provider.pkgName}: ${n.name} ${firstCmd}: ${n.parents[0]} is required
2454
- \`\`\`
2455
-
2456
- That is deliberate: without it the SDK would build half a URL and the
2457
- server would answer 404, which is a much harder message to act on. The
2458
- [explanation](explanation.md) covers why this is a guard rather than a
2459
- silent default.`)
2460
- }
2461
-
2462
-
2463
- {
2464
- // Seed the entity the recipes use, plus the first nested entity AND its
2465
- // parent — a child seeded under a parent that is not there lists as
2466
- // empty, which reads as a passing test that proves nothing.
2467
- const seeded: any[] = []
2468
- const add = (e: any) => {
2469
- if (null != e && !seeded.includes(e)) {
2470
- seeded.push(e)
2471
- }
2472
- }
2473
- const child = 0 < nested.length ? nested[0] : null
2474
- const parent = null == child ? null :
2475
- ents.find((p: any) => p.name === child.parentEntity)
2476
-
2477
- add(subject)
2478
- if (null != child) {
2479
- add(parent)
2480
- add(child)
2481
- }
2482
- seeded.sort((a: any, b: any) => ents.indexOf(a) - ents.indexOf(b))
2483
-
2484
- const seed = seeded.map((e: any) => ` ${e.name}: {
2485
- ` + [0, 1].map((i: number) =>
2486
- ` ${e.name}${i}: ${literal(seedRecord(e, i))},`).join('\n') + `
2487
- },`).join('\n')
2488
-
2489
- sec(OFFLINE_TITLE, `The SDK ships an in-memory mock transport. Turn it on with \`test\` and
2490
- seed it with \`testopts\`:
2491
-
2492
- \`\`\`js
2493
- .use('${provider.pkgName}', {
2494
- test: true,
2495
- testopts: {
2496
- entity: {
2497
- ${seed}
2498
- },
2499
- },
2500
- })
2501
- \`\`\`
2502
-
2503
- Records are keyed by id under their entity name, and the id inside the
2504
- record has to match the key it is filed under. Every command then works
2505
- offline, not-found included: an id you did not seed answers \`null\`,
2506
- exactly as it would against a real server.${null == parent || null == child ? '' : `
2507
-
2508
- A nested record has to point at a parent that is actually seeded: each
2509
- \`${child.name}\` above carries \`${child.parents[0]}: '${parentVal(child, child.parents[0])}'\`, and
2510
- that is a \`${parent.name}\` the seed contains. Seed a child under a parent
2511
- that is not there and its list comes back empty rather than failing —
2512
- which, in a test, reads as a pass that proves nothing.`}
2513
-
2514
- This is how this plugin's own suite runs, and it is the recommended way
2515
- to test application code that uses the provider: no server, no network,
2516
- and the same code path as production. See \`test/seed.js\`, which seeds
2517
- every entity this way.`)
2518
- }
2519
-
2520
-
2521
- sec('Point at a different server', `The \`sdk\` option is passed straight to the \`${provider.sdkClass}\`
2522
- constructor, so \`base\` chooses the host:
2523
-
2524
- \`\`\`js
2525
- .use('${provider.pkgName}', {
2526
- sdk: { base: 'https://${provider.lower}.example.com' },
2527
- })
2528
- \`\`\`
2529
-
2530
- ${'' === liveBase ?
2531
- `The API definition declares no server, so there is no default worth
2532
- relying on: set \`base\` explicitly, or run against the mock instead (see
2533
- [${OFFLINE_TITLE}](${anchor(OFFLINE_TITLE)})).` :
2534
- `The SDK's own default is \`${liveBase}\`, which is where the
2535
- companion test server listens, so local development usually needs no
2536
- \`base\` at all.`}`)
2537
-
2538
-
2539
- sec('Send an API key', `Credentials are not a plugin option: they come through the provider
2540
- convention, so that every provider in an application is configured the
2541
- same way. Declare the variable with \`env\` and set the key under this
2542
- provider's name:
2543
-
2544
- \`\`\`js
2545
- .use('env', {
2546
- var: { $${provider.ENV}_APIKEY: String },
2547
- })
2548
- .use('provider', {
2549
- provider: {
2550
- ${provider.lower}: {
2551
- keys: {
2552
- apikey: { value: '$${provider.ENV}_APIKEY' },
2553
- },
2554
- },
2555
- },
2556
- })
2557
- \`\`\`
2558
-
2559
- Every request then carries \`authorization: Bearer <apikey>\`. An absent
2560
- or empty key adds no header at all, so an API that needs no credentials
2561
- is configured in exactly the same shape with an empty value — which is
2562
- why it is worth writing even when there is nothing to send. An
2563
- application that later moves to an authenticated service then changes one
2564
- value rather than its structure.
2565
-
2566
- For a different scheme, set the header yourself. Headers supplied through
2567
- \`sdk\` win over the one the key would have set:
2568
-
2569
- \`\`\`js
2570
- .use('${provider.pkgName}', {
2571
- sdk: { headers: { 'x-api-key': process.env.${provider.ENV}_APIKEY } },
2572
- })
2573
- \`\`\``)
2574
-
2575
-
2576
- sec('Check which plugin and SDK are running', `One message, and the thing to reach for when a deployment is behaving
2577
- unexpectedly:
2578
-
2579
- \`\`\`js
2580
- const info = await seneca.post(
2581
- 'sys:provider,provider:${provider.lower},get:info')
2582
- \`\`\`
2583
-
2584
- \`\`\`js
2585
- {
2586
- ok: true,
2587
- name: '${provider.lower}',
2588
- version: '${provider.version}',
2589
- sdk: { name: '${provider.sdkPkg}', version: '${provider.sdkVersion}' },
2590
- }
2591
- \`\`\`
2592
-
2593
- \`version\` is this plugin's; \`sdk.version\` is the SDK it is running
2594
- against. That pair is what to quote in a bug report, because the two are
2595
- released separately and most surprises live in the gap between them.`)
2596
-
2597
-
2598
- {
2599
- const dpe = eList || subject
2600
- const dpath = dpe.path || provider.probePath || '/'
2601
- const dparams = pathParams(dpath)
2602
- const dval = (k: string) => (k === idf(dpe) || 'id' === k) ?
2603
- `${dpe.name}0` : `${k.replace(/_id$/, '')}0`
2604
-
2605
- sec('Reach the SDK directly', `The entity API covers the operations the API model declares. For
2606
- anything else — an endpoint with no entity behind it, a response header
2607
- you need to read — take the configured SDK client out of the plugin's
2608
- exports:
2609
-
2610
- \`\`\`js
2611
- const sdk = seneca.export('${provider.pluginName}/sdk')()
2612
- \`\`\`
2613
-
2614
- The export is a function, so call it, and it only answers after
2615
- \`seneca.ready()\` — that is when the plugin builds the client with the
2616
- resolved key.
2617
-
2618
- SDK operations resolve to SDK ENTITY instances rather than plain data, so
2619
- read the record out with \`.data()\`. The provider does this for you; here
2620
- you do it yourself:
2621
-
2622
- \`\`\`js${dpe.cmds.includes('list') ? `
2623
- const ${dpe.name}s = (await sdk.${dpe.acc}().list(${listArgs(dpe)}))
2624
- .map((r) => r.data())
2625
- ` : ''}${dpe.cmds.includes('load') ? `
2626
- const one = (await sdk.${dpe.acc}().load(${sdkLoadArgs(dpe)})).data()
2627
- ` : ''}\`\`\`
2628
-
2629
- For a route the entity model does not cover at all, \`direct\` sends a
2630
- request and hands back the raw response:
2631
-
2632
- \`\`\`js
2633
- const res = await sdk.direct({
2634
- path: '${dpath}',
2635
- method: 'GET',${0 === dparams.length ? '' : `
2636
- params: { ${dparams.map((k: string) => `${key(k)}: '${dval(k)}'`).join(', ')} },`}
2637
- })
2638
-
2639
- if (res instanceof Error) throw res
2640
- if (!res.ok) throw (res.err || new Error('status ' + res.status))
2641
-
2642
- console.log(res.data)
2643
- \`\`\`
2644
-
2645
- \`prepare()\` builds the same request without sending it, which is the
2646
- quickest way to see what the SDK would actually do — url, method, headers
2647
- and body, before anything leaves the process.
2648
-
2649
- Raw data becomes a Seneca entity again through \`data$\`:
2650
-
2651
- \`\`\`js
2652
- const ent = seneca.entity('${canon(dpe)}').data$(res.data)
2653
- \`\`\``)
2654
- }
2655
-
2656
-
2657
- sec('Develop against a local SDK checkout', `The SDK is an ordinary published dependency, so normal use needs nothing
2658
- special:
2659
-
2660
- \`\`\`sh
2661
- $ npm install
2662
- \`\`\`
2663
-
2664
- If you are changing the SDK and this plugin together, point npm at a
2665
- local checkout instead. Clone the SDK beside this repository, at the path
2666
- this project expects, and build it — it does not commit its build output:
2667
-
2668
- \`\`\`sh
2669
- $ git clone ${provider.sdkRepoUrl}.git \\
2670
- ${provider.sdkrel}
2671
- $ cd ${provider.sdkrel}/ts
2672
- $ npm install && npm run build
2673
- \`\`\`
2674
-
2675
- Then link it in, without committing the change to \`package.json\`:
2676
-
2677
- \`\`\`sh
2678
- $ npm install --no-save ${provider.sdkrel}/ts
2679
- \`\`\`
2680
-
2681
- npm creates a symlink, so a rebuild of the SDK is picked up here with no
2682
- reinstall:
2683
-
2684
- \`\`\`sh
2685
- $ ls -l node_modules/${provider.sdkPkg}
2686
- \`\`\`
2687
-
2688
- To go back to the published SDK:
2689
-
2690
- \`\`\`sh
2691
- $ rm -rf node_modules/${provider.sdkPkg} package-lock.json && npm install
2692
- \`\`\`
2693
-
2694
- Removing the lockfile matters. npm will happily keep resolving to the
2695
- link if the lockfile still records it and the local version satisfies the
2696
- range.`)
2697
-
2698
-
2699
- {
2700
- const pattern = subject.cmds.includes('load') ? `${subject.name}-load` :
2701
- subject.cmds.includes('list') ? `${subject.name}-list` : 'happy'
2702
- const skipped = subject.cmds.includes('list') ?
2703
- `${subject.name}-list` : `${subject.name}-load-missing`
2704
-
2705
- sec('Run the test suite', `\`\`\`sh
2706
- $ npm run build
2707
- $ npm test
2708
- \`\`\`
2709
-
2710
- The build comes first: the suite runs against \`dist\`, so an unbuilt
2711
- change is not the change you are testing.
2712
-
2713
- The offline tests use the SDK mock and always run.${'' === liveBase ? '' : ` The live tests
2714
- probe for a server first and skip cleanly when there is none, so a clean
2715
- checkout is green on a machine that has never started one:
2716
-
2717
- \`\`\`
2718
- ﹣ ${skipped} # no ${provider.lower} server at ${liveBase}
2719
- \`\`\``}
2720
-
2721
- Coverage, and a single test by name:
2722
-
2723
- \`\`\`sh
2724
- $ npm run test-coverage
2725
- $ TEST_PATTERN=${pattern} npm run test-some
2726
- \`\`\``)
2727
- }
2728
-
2729
-
2730
- if ('' !== liveBase) {
2731
- sec('Run the live tests against a server', `The companion test server ships only in the SDK's source repository, not
2732
- in the published package. From the checkout beside this one:
2733
-
2734
- \`\`\`sh
2735
- $ cd ${provider.sdkrel}/app
2736
- $ npm install && npm run build && npm start
2737
- \`\`\`
2738
-
2739
- Then run the suite as usual: the live tests find the server and activate
2740
- themselves.
2741
-
2742
- \`\`\`sh
2743
- $ npm test
2744
- \`\`\`
2745
-
2746
- To target a server somewhere else:
2747
-
2748
- \`\`\`sh
2749
- $ ${provider.ENV}_TEST_BASE=http://localhost:9000 npm test
2750
- \`\`\`
2751
-
2752
- The generated live tests only read, so a run leaves the server exactly as
2753
- it found it.${subject.cmds.includes('save') && subject.cmds.includes('remove') ? `
2754
-
2755
- Two manual scripts are there for poking at a running server by hand:
2756
-
2757
- \`\`\`sh
2758
- $ node test/live.js # read from each entity
2759
- $ node test/quick.js # a full write cycle, cleaning up after itself
2760
- \`\`\`` : `
2761
-
2762
- \`node test/live.js\` reads from each entity, for poking at a running
2763
- server by hand.`}`)
2764
- }
2765
-
2766
-
2767
- sec('Build and release', `\`\`\`sh
2768
- $ npm run build # tsc --build src test
2769
- $ npm run watch # the same, in watch mode
2770
- $ npm run reset # clean, install, build, test
2771
- \`\`\`
2772
-
2773
- Releasing follows the Seneca convention, in one command — clean, install,
2774
- build, test, tag from \`package.json\`, publish:
2775
-
2776
- \`\`\`sh
2777
- $ npm run repo-publish
2778
- \`\`\`
2779
-
2780
- Only \`dist\`, the TypeScript sources and the licence file are published;
2781
- the test suite and its build output stay in the repository.
2782
-
2783
- Before publishing, check that \`package.json\` still depends on the
2784
- published SDK by version range and not on a local path: a \`file:\`
2785
- dependency left behind from local development installs perfectly on your
2786
- own machine and cannot be resolved by anybody else.
2787
-
2788
- One last thing: this repository is GENERATED from the ${provider.api} API
2789
- model by [@voxgig/sdkgen](https://github.com/voxgig/sdkgen). An edit made
2790
- here survives exactly as long as the next generation run. Change the
2791
- model, or the components that build this target, and regenerate.`)
2792
-
2793
-
2794
- File({ name: 'how-to.md' }, () => {
2795
- Content(`# How-to guides
2796
-
2797
- Each guide here solves one problem, and assumes you already have a
2798
- working Seneca instance with this plugin loaded. If you do not, work
2799
- through the [tutorial](tutorial.md) first.
2800
-
2801
- These guides show what to do and leave out the reasoning — that is in the
2802
- [explanation](explanation.md), and the exact patterns, fields and options
2803
- are listed in the [reference](reference.md).
2804
-
2805
- `)
2806
-
2807
- each(sections, (s: any) => {
2808
- Content(`- [${s.title}](${anchor(s.title)})
2809
- `)
2810
- })
2811
-
2812
- each(sections, (s: any) => {
2813
- Content(`
2814
- ## ${s.title}
2815
-
2816
- ${s.body}
2817
- `)
2818
- })
2819
- })
2820
- })
2821
-
2822
-
2823
- // --- doc/reference.md ---------------------------------------------------------
2824
- //
2825
- // The Diátaxis reference: information-oriented, complete, and never teaching.
2826
- // Everything a caller can reach — options, canons, fields, patterns, exports,
2827
- // errors, environment variables, scripts — stated once, in tables, with the
2828
- // exact strings the generated source actually emits.
2829
- //
2830
- // Three facts here are easy to get wrong by copying a hand-written original.
2831
- // The guard message carries the PUBLISHED package name, because that is what
2832
- // Main interpolates (`${provider.pkgName}: <entity> <cmd>: <key> is required`).
2833
- // The `sdk` block of the get:info response carries the SDK's PACKAGE name, not
2834
- // its slug. And an entity whose id field is not literally `id` cannot be read
2835
- // with the `load$('x')` short form at all — Seneca turns that into `{id: 'x'}`,
2836
- // which the generated action does not look at — so the object form is
2837
- // documented for those entities instead of the string form.
2838
- //
2839
- // Nothing here assumes CRUD: every table row is conditional on the cmds and
2840
- // ops the model actually declares, so an API offering only create, or only
2841
- // reads, documents only what it has.
2842
-
2843
- const DocReference = cmp(function DocReference(props: any) {
2844
- const { provider } = props
2845
-
2846
- // The entity used for worked examples: the same choice the tests and README
2847
- // make, so all three documents show the same entity.
2848
- const subject = [...provider.entities]
2849
- .sort((a: any, b: any) =>
2850
- (a.parents.length - b.parents.length) || (b.cmds.length - a.cmds.length))[0]
2851
-
2852
- const nested = provider.entities.filter((e: any) => 0 < e.parents.length)
2853
- const saving = provider.entities.filter((e: any) => e.cmds.includes('save'))
2854
-
2855
- // Entities whose API offers BOTH create and update: only for those does
2856
- // `save$` dispatch on the id. Where it offers one, saying otherwise is wrong.
2857
- const dispatch = saving.filter((e: any) =>
2858
- e.ops.includes('create') && e.ops.includes('update'))
2859
- const oneway = saving.filter((e: any) => !dispatch.includes(e))
2860
-
2861
- const anyCmd = (c: string) => provider.entities.some((e: any) => e.cmds.includes(c))
2862
- const anyOp = (o: string) => saving.some((e: any) => e.ops.includes(o))
2863
-
2864
- const live = '' !== provider.liveBase
2865
-
2866
- // test/quick.js is emitted only when the subject entity can be created and
2867
- // removed again — see the Scripts cmp — so only document it when it exists.
2868
- const quick = live && subject.cmds.includes('save') && subject.cmds.includes('remove')
2869
-
2870
- const canon = (e: any) => `provider/${provider.lower}/${e.name}`
2871
-
2872
- const cmdList = (e: any) => e.cmds.map((c: string) => '`' + c + '$`').join(', ')
2873
-
2874
- const keys = (list: string[]) => '`' + list.join('`, `') + '`'
2875
-
2876
- // The same list as prose: `a`, `b` and `c`.
2877
- const keysAnd = (list: string[]) => 1 === list.length ? keys(list) :
2878
- `${keys(list.slice(0, -1))} and \`${list[list.length - 1]}\``
2879
-
2880
- // A query literal for the docs: parent keys first, then whatever else the
2881
- // command needs.
2882
- const query = (e: any, extra: string[]) =>
2883
- `{ ${[...e.parents, ...extra].map((k: string) => `${k}: '...'`).join(', ')} }`
2884
-
2885
- // How a single record is addressed. The `load$('x')` short form only works
2886
- // when the id field is literally `id`.
2887
- const oneArg = (e: any) => 0 < e.parents.length ? query(e, [e.idf]) :
2888
- 'id' === e.idf ? `'...'` : query(e, [e.idf])
2889
-
2890
- // The required-key phrasing, which has to read correctly for one key as
2891
- // well as several.
2892
- const reqd = (list: string[]) =>
2893
- 1 === list.length ? `${keys(list)} **required**` :
2894
- 2 === list.length ? `\`${list[0]}\` and \`${list[1]}\`, both **required**` :
2895
- `${keys(list)}, all **required**`
2896
-
2897
- // The example used in the error block: whichever command the subject has.
2898
- const errCall = subject.cmds.includes('list') ?
2899
- `list$(${0 < subject.parents.length ? query(subject, []) : ''})` :
2900
- subject.cmds.includes('load') ? `load$(${oneArg(subject)})` :
2901
- subject.cmds.includes('remove') ? `remove$(${oneArg(subject)})` :
2902
- 'make$({ ... }).save$()'
2903
-
2904
- File({ name: 'reference.md' }, () => {
2905
- Content(`# Reference
2906
-
2907
- Complete description of the interface exposed by
2908
- \`${provider.pkgName}\` version ${provider.version}.
2909
-
2910
- This document describes the machinery and assumes you know what you are
2911
- looking for. To learn the plugin, start with the [tutorial](tutorial.md);
2912
- for recipes, see the [how-to guides](how-to.md); for the reasoning behind
2913
- the design, see the [explanation](explanation.md). The package overview is
2914
- the [README](../README.md), and the document index is [here](README.md).
2915
-
2916
- - [Requirements](#requirements)
2917
- - [Registration](#registration)
2918
- - [Options](#options)
2919
- - [Entities](#entities)
2920
- - [Action patterns](#action-patterns)
2921
- - [Plugin exports](#plugin-exports)
2922
- - [Errors](#errors)
2923
- - [Authentication keys](#authentication-keys)
2924
- - [Environment variables](#environment-variables)
2925
- - [Package scripts](#package-scripts)
2926
-
2927
- ## Requirements
2928
-
2929
- | Item | Value |
2930
- | ---- | ----- |
2931
- | Node.js | \`>=24\` |
2932
- | Module format | CommonJS |
2933
- | SDK | [\`${provider.sdkPkg}\`](https://www.npmjs.com/package/${provider.sdkPkg}) \`^${provider.sdkVersion}\` |
2934
-
2935
- The SDK is an ordinary published dependency, installed by \`npm install\`
2936
- like any other.
2937
- `)
2938
-
2939
- if (live) {
2940
- Content(`
2941
- The companion **test server** used by the live tests is a separate matter:
2942
- it ships only in the SDK's [source repository](${provider.sdkRepoUrl}) under
2943
- \`app/\`, and is not published. It is needed only to run the live tests —
2944
- see the [how-to guides](how-to.md).
2945
- `)
2946
- }
2947
-
2948
- Content(`
2949
- ### Peer dependencies
2950
-
2951
- All must be present in the host application. The accepted version ranges are
2952
- declared in this package's \`package.json\`.
2953
-
2954
- | Package | Purpose |
2955
- | ------- | ------- |
2956
- | \`seneca\` | The host framework. The plugin runs inside the host's instance, never its own. |
2957
- | \`seneca-entity\` | The entity API the canons below are served through. |
2958
- | \`seneca-promisify\` | The promise-returning message API. |
2959
- | \`@seneca/provider\` | The provider convention, including \`provider/entityBuilder\`. |
2960
- | \`@seneca/env\` | Resolves \`$\`-prefixed key values from the environment. |
2961
-
2962
- ## Registration
2963
-
2964
- The plugin name is \`${provider.pluginName}\`. It must be registered after
2965
- \`entity\`, \`promisify\` and \`provider\`:
2966
-
2967
- \`\`\`js
2968
- Seneca({ legacy: false })
2969
- .use('promisify')
2970
- .use('entity')
2971
- .use('provider', { ... })
2972
- `)
2973
-
2974
- if (live) {
2975
- Content(` .use('${provider.pkgName}', { sdk: { base: '${provider.liveBase}' } })
2976
- \`\`\`
2977
- `)
2978
- }
2979
- else {
2980
- Content(` .use('${provider.pkgName}', { sdk: { base: BASE } })
2981
- \`\`\`
2982
-
2983
- The ${provider.api} definition declares no server, so there is no default
2984
- base URL: \`BASE\` is the URL of the API you are talking to, and it must be
2985
- supplied through the \`sdk\` option.
2986
- `)
2987
- }
2988
-
2989
- Content(`
2990
- The SDK client is constructed during plugin startup and is not available
2991
- until \`seneca.ready()\` resolves.
2992
-
2993
- ## Options
2994
-
2995
- | Option | Type | Default | Effect |
2996
- | ------ | ---- | ------- | ------ |
2997
- | \`sdk\` | object | \`{}\` | Passed straight to the \`${provider.sdkClass}\` constructor. Most usefully \`base\`. |
2998
- | \`test\` | boolean | \`false\` | Run the SDK against its in-memory mock transport instead of HTTP. |
2999
- | \`testopts\` | object | \`{}\` | Test-feature options, used only when \`test\` is true. \`{entity: {...}}\` seeds the mock. |
3000
-
3001
- ### \`sdk\`
3002
-
3003
- Any option the \`${provider.sdkClass}\` constructor accepts:
3004
-
3005
- | Key | Effect |
3006
- | --- | ------ |
3007
- | \`base\` | Base URL for API requests. ${live ?
3008
- `The SDK's own default is \`${provider.liveBase}\`.` :
3009
- 'There is no default: this API declares no server, so it must be set.'} |
3010
- | \`prefix\` / \`suffix\` | URL fragments placed around the path. |
3011
- | \`headers\` | Headers sent on every request. These win over the \`authorization\` header the provider adds from a configured key. |
3012
- | \`system\` | System overrides, e.g. a custom \`fetch\`. |
3013
-
3014
- ### \`test\` and \`testopts\`
3015
-
3016
- \`\`\`js
3017
- .use('${provider.pkgName}', {
3018
- test: true,
3019
- testopts: {
3020
- entity: {
3021
- `)
3022
- each(provider.entities, (e: any) => {
3023
- Content(` ${e.name}: { ${e.name}0: ${JSON.stringify(seedRecord(e, 0))} },
3024
- `)
3025
- })
3026
- Content(` },
3027
- },
3028
- })
3029
- \`\`\`
3030
-
3031
- Mock records are keyed by id under their entity name. In this mode no
3032
- network calls are made, and an unseeded id produces the same not-found
3033
- behaviour as a live server. This package's own \`test/seed.js\` is generated
3034
- in exactly this shape.
3035
- `)
3036
-
3037
- if (0 < nested.length) {
3038
- Content(`
3039
- A nested record's parent key must name a record the parent entity also
3040
- seeds: the mock resolves the path literally, so an unmatched parent id
3041
- yields nothing rather than an error.
3042
- `)
3043
- }
3044
-
3045
- Content(`
3046
- ## Entities
3047
-
3048
- The plugin registers ${1 === provider.entities.length ?
3049
- 'one entity canon' : `${provider.entities.length} entity canons`}.
3050
- A canon carries only the commands its API operations support — an entity the
3051
- API offers no delete for has no \`remove$\` — so the tables below are the
3052
- whole of what each one answers.
3053
-
3054
- | Seneca canon | SDK accessor | Route | Id field | Parent keys | Commands |
3055
- | ------------ | ------------ | ----- | -------- | ----------- | -------- |
3056
- `)
3057
- each(provider.entities, (e: any) => {
3058
- Content(`| \`${canon(e)}\` | \`sdk.${e.acc}()\` | \`${e.path}\` | \`${e.idf}\` | ${0 < e.parents.length ?
3059
- keys(e.parents) : '—'} | ${cmdList(e)} |
3060
- `)
3061
- })
3062
-
3063
- each(provider.entities, (e: any) => {
3064
- const hasCreate = e.ops.includes('create')
3065
- const hasUpdate = e.ops.includes('update')
3066
-
3067
- Content(`
3068
- ### \`${canon(e)}\`
3069
-
3070
- Backed by \`sdk.${e.acc}()\`, whose results are \`${e.cls}\` instances; the
3071
- provider hands Seneca the plain record from \`.data()\`.
3072
- `)
3073
-
3074
- if (0 < e.parents.length) {
3075
- Content(`
3076
- \`${e.name}\` is nested under \`${e.path}\` in the API, so **every**
3077
- \`${e.name}\` command requires ${keysAnd(e.parents)}. Omitting one throws —
3078
- \`${provider.pkgName}: ${e.name} <cmd>: ${e.parents[0]} is required\` —
3079
- before any request is made, rather than issuing one that would 404.
3080
- `)
3081
- }
3082
-
3083
- Content(`
3084
- | Command | Query / data | Returns |
3085
- | ------- | ------------ | ------- |
3086
- `)
3087
- if (e.cmds.includes('list')) {
3088
- Content(`| \`list$(q)\` | ${0 < e.parents.length ?
3089
- `${reqd(e.parents)}, plus optional match fields` :
3090
- 'optional match fields'} | Array of \`${e.name}\` entities. |
3091
- `)
3092
- }
3093
- if (e.cmds.includes('load')) {
3094
- Content(`| \`load$(q)\` | ${reqd([...e.parents, e.idf])} | One \`${e.name}\`, or \`null\` if not found. |
3095
- `)
3096
- }
3097
- if (e.cmds.includes('save')) {
3098
- Content(`| \`save$()\` | entity data${0 < e.parents.length ?
3099
- `, including ${keysAnd(e.parents)}` : ''} | ${hasCreate && hasUpdate ?
3100
- `Created or updated \`${e.name}\`.` : hasCreate ?
3101
- `Created \`${e.name}\`; the API declares no update operation.` :
3102
- `Updated \`${e.name}\`; the API declares no create operation.`} |
3103
- `)
3104
- }
3105
- if (e.cmds.includes('remove')) {
3106
- Content(`| \`remove$(q)\` | ${reqd([...e.parents, e.idf])} | \`null\`. |
3107
- `)
3108
- }
3109
-
3110
- // The `load$('x')` short form sets `id`, which an entity keyed by
3111
- // anything else never reads. Nested entities need the object form for
3112
- // their parent keys anyway, so this only needs saying for top-level ones.
3113
- const shortForm = 0 === e.parents.length && 'id' !== e.idf ?
3114
- e.cmds.filter((c: string) => 'load' === c || 'remove' === c) : []
3115
-
3116
- if (0 < shortForm.length) {
3117
- Content(`
3118
- This entity is keyed by \`${e.idf}\` rather than \`id\`, so the short
3119
- ${1 === shortForm.length ? 'form' : 'forms'} ${shortForm
3120
- .map((c: string) => `\`${c}$('...')\``).join(' and ')} ${1 === shortForm.length ?
3121
- 'does' : 'do'} not address it: Seneca reads a bare string as
3122
- \`{id: '...'}\`, which is not a key this entity uses. Pass
3123
- \`{ ${e.idf}: '...' }\` instead.
3124
- `)
3125
- }
3126
-
3127
- if (0 === e.fields.length) {
3128
- Content(`
3129
- The API definition declares no required fields for this entity; whatever it
3130
- returns is passed through unchanged.
3131
- `)
3132
- }
3133
- else {
3134
- Content(`
3135
- Required fields, as declared by the API definition. Optional fields the API
3136
- also defines are passed through unchanged in both directions.
3137
-
3138
- | Field | Type | Notes |
3139
- | ----- | ---- | ----- |
3140
- `)
3141
- each(e.fields, (f: any) => {
3142
- Content(`| \`${f.name}\` | ${f.kind} | ${f.name === e.idf ? 'Id field.' :
3143
- e.parents.includes(f.name) ? ('' === f.parentEntity ?
3144
- 'Parent key. Required by every command.' :
3145
- `Parent key: the id of a \`${f.parentEntity}\`. Required by every command.`) : ''} |
3146
- `)
3147
- })
3148
- }
3149
-
3150
- if (e.cmds.includes('list') || e.cmds.includes('load')) {
3151
- Content(`
3152
- \`\`\`js
3153
- `)
3154
- if (e.cmds.includes('list')) {
3155
- Content(`const ${e.name}s = await seneca
3156
- .entity('${canon(e)}')
3157
- .list$(${0 < e.parents.length ? query(e, []) : ''})
3158
- `)
3159
- }
3160
- if (e.cmds.includes('load')) {
3161
- Content(`const ${e.name} = await seneca
3162
- .entity('${canon(e)}')
3163
- .load$(${oneArg(e)})
3164
- `)
3165
- }
3166
- Content(`\`\`\`
3167
- `)
3168
- }
3169
- })
3170
-
3171
- if (0 < dispatch.length) {
3172
- // The dispatching entity to show it with: the subject when it qualifies,
3173
- // otherwise the first that does.
3174
- const s = dispatch.includes(subject) ? subject : dispatch[0]
3175
- const writable = s.fields
3176
- .filter((f: any) => f.name !== s.idf && f.name !== 'id')
3177
- .filter((f: any) => !s.parents.includes(f.name))
3178
- const value = (f: any, alt: boolean) => 'number' === f.kind ?
3179
- (alt ? '4321' : '1234') : 'boolean' === f.kind ?
3180
- (alt ? 'true' : 'false') : `'${f.name}${alt ? '-changed' : '-value'}'`
3181
- const make = [
3182
- ...s.parents.map((k: string) => `${k}: '...'`),
3183
- ...writable.map((f: any) => `${f.name}: ${value(f, false)}`),
3184
- ].join(', ')
3185
-
3186
- Content(`
3187
- ### Create versus update
3188
-
3189
- \`save$\` follows the Seneca convention: an entity **without** an id is
3190
- created, an entity **with** one is updated. The provider dispatches on the
3191
- id field, so the same call does both.
3192
-
3193
- \`\`\`js
3194
- // Create — no ${s.idf}.
3195
- const ${s.name} = await seneca
3196
- .entity('${canon(s)}')
3197
- .make$({ ${make} })
3198
- .save$()
3199
-
3200
- // Update — ${s.idf} present.
3201
- ${0 < writable.length ? `${s.name}.${writable[0].name} = ${value(writable[0], true)}
3202
- ` : ''}await ${s.name}.save$()
3203
- \`\`\`
3204
-
3205
- Whether a client-supplied id survives a create is a property of the API, not
3206
- of this plugin: many assign the id themselves and ignore the one sent. Read
3207
- the id back off the returned entity rather than assuming the one you set.
3208
- `)
3209
- }
3210
-
3211
- if (0 < oneway.length) {
3212
- if (0 === dispatch.length) {
3213
- Content(`
3214
- ### Create versus update
3215
-
3216
- \`save$\` normally dispatches on the id: an entity without one is created,
3217
- an entity with one is updated.
3218
- `)
3219
- }
3220
-
3221
- Content(`
3222
- ${1 === oneway.length ?
3223
- 'This entity supports only one half of that pair, so `save$` does not' :
3224
- 'These entities support only one half of that pair, so `save$` does not'}
3225
- dispatch for ${1 === oneway.length ? 'it' : 'them'}:
3226
-
3227
- | Canon | Behaviour of \`save$\` |
3228
- | ----- | -------------------- |
3229
- `)
3230
- each(oneway, (e: any) => {
3231
- Content(`| \`${canon(e)}\` | Always ${e.ops.includes('create') ? 'creates' : 'updates'}; the API declares no ${e.ops.includes('create') ? 'update' : 'create'} operation. |
3232
- `)
3233
- })
3234
- }
3235
-
3236
- Content(`
3237
- ### Command to SDK operation
3238
-
3239
- | Seneca command | SDK call | Notes |
3240
- | -------------- | -------- | ----- |
3241
- `)
3242
- if (anyCmd('list')) {
3243
- Content(`| \`list$(q)\` | \`.list(q)\` | Query keys are passed through as match fields. |
3244
- `)
3245
- }
3246
- if (anyCmd('load')) {
3247
- Content(`| \`load$(q)\` | \`.load({ ...keys })\` | Only the keys the route needs are sent. |
3248
- `)
3249
- }
3250
- if (anyOp('create')) {
3251
- Content(`| \`save$()\` on an entity with no id | \`.create(data)\` | Data is the entity's own fields, without Seneca metadata. |
3252
- `)
3253
- }
3254
- if (anyOp('update')) {
3255
- Content(`| \`save$()\` on an entity with an id | \`.update(data)\` | |
3256
- `)
3257
- }
3258
- if (anyCmd('remove')) {
3259
- Content(`| \`remove$(q)\` | \`.remove({ ...keys })\` | Resolves to \`null\` whatever the API returns. |
3260
- `)
3261
- }
3262
-
3263
- Content(`
3264
- Every SDK operation resolves to an SDK entity instance, or a list of them,
3265
- rather than raw data. The provider calls \`.data()\` on each and hands the
3266
- plain record to \`entize\`, so what comes back is an ordinary Seneca entity
3267
- under this plugin's canon, carrying none of the SDK's own markers.
3268
-
3269
- ### Query fields
3270
-
3271
- Seneca query directives — any key ending in \`$\`, such as \`sort$\` or
3272
- \`limit$\` — are stripped before the query reaches the SDK. They are
3273
- instructions to a store, not match fields for the API, and are not
3274
- otherwise supported.
3275
-
3276
- ## Action patterns
3277
-
3278
- ### \`sys:provider,provider:${provider.lower},get:info\`
3279
-
3280
- Returns metadata about the plugin and SDK. Answered locally; makes no API
3281
- call.
3282
-
3283
- \`\`\`js
3284
- await seneca.post('sys:provider,provider:${provider.lower},get:info')
3285
- \`\`\`
3286
-
3287
- \`\`\`js
3288
- {
3289
- ok: true,
3290
- name: '${provider.lower}',
3291
- version: '${provider.version}',
3292
- sdk: {
3293
- name: '${provider.sdkPkg}',
3294
- version: '${provider.sdkVersion}',
3295
- },
3296
- }
3297
- \`\`\`
3298
-
3299
- Both versions are read at runtime from the respective \`package.json\`, so
3300
- they describe what is installed rather than what was generated.
3301
-
3302
- ### Entity patterns
3303
-
3304
- Registered by \`@seneca/provider\`. Normally reached through the entity API
3305
- rather than posted directly.
3306
-
3307
- | Pattern |
3308
- | ------- |
3309
- `)
3310
- each(provider.entities, (e: any) => {
3311
- Content(e.cmds
3312
- .map((c: string) =>
3313
- `| \`sys:entity,zone:provider,base:${provider.lower},name:${e.name},cmd:${c}\` |\n`)
3314
- .join(''))
3315
- })
3316
-
3317
- Content(`
3318
- ### Inherited from \`@seneca/provider\`
3319
-
3320
- | Pattern | Purpose |
3321
- | ------- | ------- |
3322
- | \`sys:provider,get:key\` | Fetch one named key for a provider. |
3323
- | \`sys:provider,get:keymap\` | Fetch all keys for a provider. |
3324
- | \`sys:provider,list:provider\` | List registered providers and their key names. |
3325
-
3326
- ## Plugin exports
3327
-
3328
- ### \`${provider.pluginName}/sdk\`
3329
-
3330
- A function returning the configured \`${provider.sdkClass}\` instance.
3331
-
3332
- \`\`\`js
3333
- const sdk = seneca.export('${provider.pluginName}/sdk')()
3334
- `)
3335
- if (subject.ops.includes('list')) {
3336
- Content(`
3337
- // Every SDK operation resolves to an SDK entity (or a list of them),
3338
- // not raw data; \`.data()\` gives the plain record.
3339
- const ${subject.name}s = (await sdk.${subject.acc}().list()).map((e) => e.data())
3340
- `)
3341
- }
3342
- if ('' !== provider.probePath) {
3343
- Content(`
3344
- // \`direct\` reaches endpoints outside the entity model.
3345
- const res = await sdk.direct({ path: '${provider.probePath}', method: 'GET' })
3346
- `)
3347
- }
3348
- Content(`\`\`\`
3349
-
3350
- Available only after \`seneca.ready()\`. Use it for SDK features the entity
3351
- API does not surface — notably \`direct()\` and \`prepare()\` for endpoints
3352
- the entity model does not cover.
3353
-
3354
- ## Errors
3355
-
3356
- | Situation | Behaviour |
3357
- | --------- | --------- |
3358
- `)
3359
- if (anyCmd('load')) {
3360
- Content(`| \`load$\` for a non-existent id | Resolves to \`null\`. |
3361
- `)
3362
- }
3363
- if (anyCmd('remove')) {
3364
- Content(`| \`remove$\` for a non-existent id | Resolves to \`null\`; not an error. |
3365
- `)
3366
- }
3367
- if (0 < nested.length) {
3368
- Content(`| A nested entity command missing a parent key | Throws before any request is made. |
3369
- `)
3370
- }
3371
- if (anyCmd('list') || anyCmd('save')) {
3372
- Content(`| A 404 from \`${[anyCmd('list') ? 'list$' : '', anyCmd('save') ? 'save$' : ''].filter((s: string) => '' !== s).join('` or `')}\` | Thrown. Only single-record reads and removes map a 404 to \`null\`. |
3373
- `)
3374
- }
3375
- Content(`| Any other non-2xx response | Thrown as raised by the SDK. |
3376
- | A request that never got a response | Thrown, with \`status\` \`-1\`. |
3377
-
3378
- SDK errors are \`${provider.Name}Error\` instances carrying
3379
- \`is${provider.Name}Error: true\`, a \`code\` (e.g. \`request_status\`), the
3380
- HTTP \`status\` at the top level (\`-1\` when the request never got a
3381
- response), a \`notFound\` flag, and a \`ctx\` holding the request context and
3382
- its \`result\` — \`status\`, \`statusText\`, \`headers\` and \`body\`. The
3383
- \`null\`-on-missing behaviour is triggered by \`err.notFound\`, not by
3384
- inspecting the status at the call site.
3385
-
3386
- \`\`\`js
3387
- try {
3388
- await seneca.entity('${canon(subject)}').${errCall}
3389
- }
3390
- catch (err) {
3391
- console.error(err.code, err.status, err.notFound)
3392
- }
3393
- \`\`\`
3394
- `)
3395
-
3396
- if (0 < nested.length) {
3397
- Content(`
3398
- The missing-parent-key guard is this plugin's own, thrown before the SDK is
3399
- called at all. Its message names the entity, the command and the key:
3400
-
3401
- | Entity | Message |
3402
- | ------ | ------- |
3403
- `)
3404
- each(nested, (e: any) => {
3405
- Content(e.parents
3406
- .map((k: string) =>
3407
- `| \`${e.name}\` | \`${provider.pkgName}: ${e.name} <cmd>: ${k} is required\` |\n`)
3408
- .join(''))
3409
- })
3410
- Content(`
3411
- where \`<cmd>\` is the command that was called. A key counts as missing if
3412
- it is absent, \`null\` or the empty string.
3413
- `)
3414
- }
3415
-
3416
- Content(`
3417
- ## Authentication keys
3418
-
3419
- The plugin follows the provider convention: if an \`apikey\` key is
3420
- configured and non-empty, it is sent as \`authorization: Bearer <apikey>\`
3421
- on every request. If the provider is not registered, or the key is absent or
3422
- empty, no header is added and startup proceeds normally — an API that needs
3423
- no credential exercises the same path.
3424
-
3425
- \`\`\`js
3426
- .use('provider', {
3427
- provider: {
3428
- ${provider.lower}: {
3429
- keys: {
3430
- apikey: { value: '$${provider.ENV}_APIKEY' },
3431
- },
3432
- },
3433
- },
3434
- })
3435
- \`\`\`
3436
-
3437
- The key is read once, during \`seneca.prepare()\`, by posting
3438
- \`sys:provider,get:keymap,provider:${provider.lower}\`. An \`authorization\`
3439
- header supplied through the \`sdk.headers\` option takes precedence over it.
3440
-
3441
- ## Environment variables
3442
-
3443
- The plugin never reads the environment itself. These are the variables the
3444
- surrounding convention and tooling resolve:
3445
-
3446
- | Variable | Read by | Purpose |
3447
- | -------- | ------- | ------- |
3448
- | \`$${provider.ENV}_APIKEY\` | \`@seneca/env\` | Supplies the \`apikey\` value when the key is declared as \`'$${provider.ENV}_APIKEY'\`, as above. |
3449
- `)
3450
- if (live) {
3451
- Content(`| \`$${provider.ENV}_TEST_BASE\` | The test suite and the manual scripts | Base URL for the live tests. Defaults to \`${provider.liveBase}\`. |
3452
- `)
3453
- }
3454
-
3455
- Content(`
3456
- ## Package scripts
3457
-
3458
- | Script | Action |
3459
- | ------ | ------ |
3460
- | \`npm run build\` | \`tsc --build src test\` — compiles to \`dist\` and \`dist-test\`. |
3461
- | \`npm run watch\` | The same, in watch mode. |
3462
- | \`npm test\` | Runs the \`node:test\` suite. |
3463
- | \`npm run test-some\` | Runs tests matching \`$TEST_PATTERN\`. |
3464
- | \`npm run test-watch\` | Test suite in watch mode. |
3465
- | \`npm run test-coverage\` | Test suite with Node's built-in coverage. |
3466
- | \`npm run clean\` | Removes \`node_modules\`, \`dist\`, \`dist-test\`, \`.tsbuildinfo\`, lockfiles. |
3467
- | \`npm run reset\` | \`clean\`, then install, build and test. |
3468
- | \`npm run repo-tag\` | Commits, tags and pushes \`v<version>\` taken from \`package.json\`. |
3469
- | \`npm run repo-publish\` | Clean install, then \`repo-publish-quick\`. |
3470
- | \`npm run repo-publish-quick\` | Build, test, tag, and publish to npm. |
3471
-
3472
- ### Repository layout
3473
-
3474
- | Path | Contents |
3475
- | ---- | -------- |
3476
- | \`src/\` | TypeScript source, with its own \`tsconfig.json\`. |
3477
- | \`test/\` | Test suite (\`.js\`, run by \`node:test\`) and TypeScript fixtures. |
3478
- | \`dist/\` | Compiled source. Committed; published. |
3479
- | \`dist-test/\` | Compiled test fixtures. Committed; **not** published. |
3480
- | \`.tsbuildinfo/\` | Incremental build cache. Not committed. |
3481
- | \`doc/\` | This documentation. |
3482
-
3483
- This repository is generated by
3484
- [@voxgig/sdkgen](https://github.com/voxgig/sdkgen) from the ${provider.api}
3485
- API definition. Anything edited here is overwritten by the next generation
3486
- run; changes belong in the model.
3487
- `)
3488
-
3489
- if (live) {
3490
- const listable = provider.entities
3491
- .filter((e: any) => e.cmds.includes('list'))
3492
- .map((e: any) => e.name)
3493
-
3494
- Content(`
3495
- ### Manual scripts
3496
-
3497
- Not part of \`npm test\`: they need the companion test server, which is
3498
- distributed only in the SDK's source repository.
3499
-
3500
- | Script | Purpose |
3501
- | ------ | ------ |
3502
- `)
3503
- Content(`| \`node test/live.js\` | ${0 < listable.length ?
3504
- `Read ${listable.join(', ')} from a running server.` :
3505
- 'Reads from a running server; no entity here supports `list$`, so it does nothing.'} |
3506
- `)
3507
- if (quick) {
3508
- Content(`| \`node test/quick.js\` | Exercise the full write cycle on \`${subject.name}\`, cleaning up after itself. |
3509
- `)
3510
- }
3511
- Content(`
3512
- ${quick ? 'Both scripts target' : 'It targets'} \`$${provider.ENV}_TEST_BASE\`, defaulting to
3513
- \`${provider.liveBase}\`.
3514
- `)
3515
- }
3516
- })
3517
- })
3518
-
3519
-
3520
- // --- doc/explanation.md ------------------------------------------------------
3521
- //
3522
- // The understanding-oriented corner of the Diátaxis set: the document someone
3523
- // opens when the plugin surprised them. It DISCUSSES and never instructs, so
3524
- // nothing here is a step and nothing here is a table — those belong in
3525
- // tutorial.md, how-to.md and reference.md.
3526
- //
3527
- // The hard part of generating this one is that its subject is design reasoning,
3528
- // most of which is true of EVERY provider this target emits (the entityBuilder
3529
- // convention, the four-cmds-to-five-ops join, the .data() hop, the 404
3530
- // translation) and only some of which depends on the model (whether any entity
3531
- // is nested, whether writes exist at all, whether the API declares a server).
3532
- // So the invariant prose is written once and the model-dependent sections are
3533
- // guarded — an API with no nesting gets no nesting section rather than a
3534
- // section explaining that it has none.
3535
-
3536
- const DocExplanation = cmp(function DocExplanation(props: any) {
3537
- const { provider } = props
3538
-
3539
- // The entity used as the worked example throughout: fewest parent keys
3540
- // (nothing to arrange around it) and the most cmds. Same choice the Tests
3541
- // and Readme cmps make, so the documents agree on what they talk about.
3542
- const subject = [...provider.entities]
3543
- .sort((a: any, b: any) =>
3544
- (a.parents.length - b.parents.length) || (b.cmds.length - a.cmds.length))[0]
3545
-
3546
- const nested = provider.entities.filter((e: any) => 0 < e.parents.length)
3547
- const writable = provider.entities.filter((e: any) => e.cmds.includes('save'))
3548
- const loadable = provider.entities.filter((e: any) => e.cmds.includes('load'))
3549
- const removable = provider.entities.filter((e: any) => e.cmds.includes('remove'))
3550
-
3551
- // Entities where `save` has nothing to dispatch on, because the API offers
3552
- // only one of create/update. Worth naming: their `save$` ignores the id
3553
- // rule the rest of this document explains.
3554
- const onesided = writable.filter((e: any) =>
3555
- !(e.ops.includes('create') && e.ops.includes('update')))
3556
-
3557
- const code = (s: string) => '`' + s + '`'
3558
- const list = (names: string[]) => names.map(code).join(', ')
3559
-
3560
- // What the offline suite actually covers, so the prose does not claim a
3561
- // `load` test for an entity that has no load.
3562
- const covered: string[] = []
3563
- if (subject.cmds.includes('list')) covered.push('list')
3564
- if (subject.cmds.includes('load')) covered.push('load', 'the not-found answer')
3565
- if (0 < nested.length) covered.push('the nested-entity rules')
3566
- const coveredPhrase = 0 < covered.length ? ` — ${covered.join(', ')} —` : ''
3567
-
3568
- const othersSentence = 1 < provider.entities.length ?
3569
- 'The other entities carry whatever their own operations support; the\n' +
3570
- '[reference](reference.md) lists them all.' :
3571
- 'It is the only entity this API declares, and the\n' +
3572
- '[reference](reference.md) spells its commands out.'
3573
-
3574
- // The nesting section names the parent entity when the model knows it, and
3575
- // falls back to the path params when a parent key points at nothing declared.
3576
- const n = nested[0]
3577
- const nestLead = null == n ? '' :
3578
- '' !== n.parentEntity ?
3579
- `The API nests ${code(n.name)} under ${code(n.parentEntity)}: a ` +
3580
- `${code(n.name)}'s URL contains its ${code(n.parentEntity)}.` :
3581
- `The API nests ${code(n.name)} under a parent resource: a ` +
3582
- `${code(n.name)}'s URL contains ${list(n.parents)}.`
3583
-
3584
- File({ name: 'explanation.md' }, () => {
3585
- Content(`# Explanation
3586
-
3587
- This document discusses why \`${provider.pkgName}\` is built the way it is.
3588
- It does not tell you how to do anything — for that see the
3589
- [tutorial](tutorial.md) and the [how-to guides](how-to.md), and for the exact
3590
- patterns, entities and options, the [reference](reference.md). The whole set is
3591
- indexed in [doc/README.md](README.md).
3592
-
3593
-
3594
- ## The provider convention
3595
-
3596
- Seneca applications talk to the outside world through *providers*. A provider
3597
- is a plugin that makes a third-party API look like a Seneca data source, so
3598
- application code uses the entity API it already knows instead of learning a
3599
- client library per service.
3600
-
3601
- The payoff is uniformity. An application reading from ${provider.api}, a
3602
- payment processor and a CRM uses one access pattern for all three:
3603
-
3604
- \`\`\`js
3605
- await seneca.entity('provider/${provider.lower}/${subject.name}').list$()
3606
- await seneca.entity('provider/stripe/charge').list$()
3607
- \`\`\`
3608
-
3609
- Because these are ordinary Seneca entities, everything built on the entity API
3610
- — logging, tracing, message interception, test doubles — applies to remote
3611
- calls without any special support for HTTP.
3612
-
3613
-
3614
- ## What entityBuilder buys
3615
-
3616
- The convention is more than a naming scheme. \`@seneca/provider\` exports
3617
- \`provider/entityBuilder\`, and this plugin hands it exactly one thing: a map
3618
- from entity name to a small set of cmd actions. Recognising the
3619
- \`provider/${provider.lower}/\` canon, registering the \`role:entity\` messages
3620
- that sit behind \`list$\`, \`load$\`, \`save$\` and \`remove$\`, and turning
3621
- whatever an action returns into an entity of the right canon — none of that is
3622
- written here. It arrives with the convention.
3623
-
3624
- What remains is a handful of async functions, each a few lines long, whose
3625
- whole job is to call the SDK and hand the result back through the \`entize\`
3626
- function entityBuilder supplies. That thinness is the point rather than an
3627
- accident of effort: a provider that is nearly all glue can be read at a glance,
3628
- generated in full, and regenerated when the API moves. Cleverness added here is
3629
- cleverness that has to be maintained against a moving target.
3630
-
3631
-
3632
- ## Two layers of the same idea
3633
-
3634
- This provider is unusual among Seneca providers in that the thing it wraps is
3635
- *already* entity-shaped. The ${provider.api} SDK exposes accessors like
3636
- \`client.${subject.acc}()\` — carrying
3637
- ${list(subject.ops)} —
3638
- rather than raw HTTP routes, for much the same reason Seneca does. A small,
3639
- uniform surface is easier for people and for agents to reason about than a set
3640
- of URL templates.
3641
-
3642
- So the provider is mostly a translation between two entity models that already
3643
- agree on the important things. Where they *disagree* is where this plugin has
3644
- to do real work, and each disagreement is discussed below.
3645
-
3646
-
3647
- ## Where the SDK and Seneca disagree
3648
-
3649
- ### Four commands, five operations
3650
-
3651
- Seneca's store commands are \`list\`, \`load\`, \`save\` and \`remove\`. The
3652
- SDK's operations are \`list\`, \`load\`, \`create\`, \`update\` and \`remove\`.
3653
- Four of the five line up. \`save\` is the join, and it dispatches on the id: an
3654
- entity carrying one is an update, an entity without one is a create.
3655
-
3656
- `)
3657
-
3658
- if (0 < writable.length) {
3659
- Content(`That is Seneca's convention rather than this plugin's invention, and it is a
3660
- good one. Exposing create and update separately would push the HTTP verb back
3661
- into application code — the caller who loaded a record, changed a field and
3662
- called \`save$\` would have to know whether that becomes a POST or a PUT. The
3663
- presence of the id already answers the question. Asking the caller to answer it
3664
- again only adds a way to be wrong.
3665
-
3666
- `)
3667
- }
3668
-
3669
- Content(`Which commands exist at all is decided per entity, from the operations the API
3670
- declares, rather than from an assumption that everything is CRUD.
3671
- \`${subject.name}\` carries
3672
- ${list(subject.cmds.map((c: string) => c + '$'))}.
3673
- ${othersSentence}
3674
- An entity whose API has no create and no update simply has no \`save$\`, which
3675
- is a better answer than a \`save$\` that exists and then fails at the HTTP
3676
- layer.
3677
-
3678
- `)
3679
-
3680
- if (0 < onesided.length) {
3681
- Content(`Where an entity declares only one of create and update there is nothing to
3682
- dispatch on, and \`save$\` means that operation whether an id is present or not:
3683
-
3684
- `)
3685
- each(onesided, (e: any) => {
3686
- Content(`- \`${e.name}\`: \`save$\` always ${e.ops.includes('create') ? 'creates' : 'updates'}
3687
- `)
3688
- })
3689
- Content(`
3690
- `)
3691
- }
3692
-
3693
- Content(`### Entity instances versus plain data
3694
-
3695
- Every SDK operation resolves to an SDK entity instance, never to raw data:
3696
- \`list\` to a list of them, and each single-record operation to one. The record
3697
- is absorbed into the instance and read back through \`.data()\`.${0 < removable.length ?
3698
- ` A removed\nentity is the same instance, marked deleted, still holding what it held.` : ''}
3699
-
3700
- Seneca's \`entize\` wants plain data, so the provider takes the \`.data()\` hop
3701
- on everything the SDK hands back, before it goes anywhere near an entity. That
3702
- is the whole of the \`plain\` helper in the source, and it is the only place in
3703
- the plugin that knows the SDK deals in instances at all.
3704
-
3705
- The hop earns its keep for a second reason. An SDK instance carries its own
3706
- serialisation marker, and that marker must not survive into a Seneca entity:
3707
- Seneca reads \`entity$\` on a data object as the *canon*. A marker landing on
3708
- that key would be taken as a canon, and the record would come back under the
3709
- wrong one — or under none. The SDK namespaces its marker so the collision
3710
- cannot happen by accident, but normalising at this boundary is still the right
3711
- call. It is what makes the data plain, and it keeps the provider independent of
3712
- whatever the SDK decides to carry alongside a record.
3713
-
3714
- `)
3715
-
3716
- if (0 < loadable.length) {
3717
- Content(`### Missing things
3718
-
3719
- \`load$\` for an id that does not exist resolves to \`null\`. Only a 404 is
3720
- translated this way; every other failure propagates.
3721
-
3722
- "This thing does not exist" is an ordinary answer to a lookup, not a failure of
3723
- the lookup. It is usually a branch in the caller's logic, and forcing every call
3724
- site into a \`try\`/\`catch\` to express that branch makes the common path noisy.
3725
- A malformed request, a rejected credential or an unreachable server means
3726
- something else entirely: the question could not be asked, and that should
3727
- interrupt rather than quietly look like an empty result.
3728
-
3729
- The SDK does not draw this line — it throws for any non-2xx — so the provider
3730
- asks the thrown error, which reports \`notFound\` and the HTTP \`status\` at the
3731
- top level. That coupling to the SDK's error shape is a deliberate and narrow
3732
- one, and it is why the shape is written down in the
3733
- [reference](reference.md).
3734
-
3735
- `)
3736
-
3737
- if (0 < removable.length) {
3738
- Content(`\`remove$\` is treated the same way and for the same reason: removing something
3739
- that is already gone leaves the caller with what the caller wanted.
3740
-
3741
- `)
3742
- }
3743
- }
3744
- else if (0 < removable.length) {
3745
- Content(`### Missing things
3746
-
3747
- Nothing here reads a single record by id, but \`remove$\` still has to decide
3748
- what "it was not there" means, and it treats a 404 as an ordinary outcome rather
3749
- than a failure: the record is gone, which is what the caller asked for. Every
3750
- other failure — a malformed request, a rejected credential, an unreachable
3751
- server — means the question could not be asked at all, and propagates.
3752
-
3753
- The SDK does not draw that line; it throws for any non-2xx. So the provider asks
3754
- the thrown error, which reports \`notFound\` and the HTTP \`status\` at the top
3755
- level. That coupling to the SDK's error shape is a deliberate and narrow one,
3756
- and it is why the shape is written down in the [reference](reference.md).
3757
-
3758
- `)
3759
- }
3760
-
3761
- if (0 < nested.length) {
3762
- Content(`### Nesting
3763
-
3764
- ${nestLead}
3765
- Seneca's entity model is flat — a canon has no notion of a parent.
3766
-
3767
- The gap is bridged by putting the parent id in the query, which is why
3768
- \`${n.parents[0]}\` is required on every \`${n.name}\` command${n.cmds.includes('load') ?
3769
- `, and why\n\`${n.name}\` \`load$\` takes an object rather than a bare id string` : ''}.
3770
- This is inherited from the API's URL structure — \`${n.path}\` — rather than
3771
- chosen here.
3772
-
3773
- The provider checks for \`${n.parents[0]}\` itself and throws a named error
3774
- rather than letting the request go out. Without the check, the SDK builds a URL
3775
- with a missing segment and the server answers 404${n.cmds.includes('load') ?
3776
- `, and that 404 is\nindistinguishable from "that ${n.name} does not exist" — which the provider\nwould then dutifully translate to \`null\`. A forgotten argument would look\nexactly like an empty result` :
3777
- ` — an opaque failure that says\nnothing about the argument that was left out`}. Failing early turns a confusing
3778
- wrong answer into an obvious mistake.
3779
-
3780
- `)
3781
- if (1 < nested.length) {
3782
- Content(`The same applies to every nested entity here —
3783
- ${list(nested.map((e: any) => e.name))} — each guarded on its own keys.
3784
-
3785
- `)
3786
- }
3787
- }
3788
-
3789
- Content(`### Query directives
3790
-
3791
- Seneca store queries can carry directives such as \`sort$\` and \`limit$\`.
3792
- These are instructions to a *store*, and the API has no equivalent, so the
3793
- provider strips any key ending in \`$\` before the query becomes an API match.
3794
-
3795
- Passing them through would be worse than dropping them: the SDK would forward
3796
- them as ordinary match fields, and the API would either ignore them or reject
3797
- the request outright. Dropping them is imperfect too — a caller who writes
3798
- \`list$({ sort$: 'name' })\` gets unsorted results and no complaint — but it is
3799
- the behaviour least likely to produce a wrong answer, and the limitation is
3800
- documented rather than hidden. Sorting and limiting belong on the caller's
3801
- side, or in the API's own query fields where it has them.
3802
-
3803
-
3804
- `)
3805
-
3806
- if (0 < writable.length) {
3807
- Content(`## Why writes are supported here
3808
-
3809
- The read-only question is worth asking of every provider, and the answer here
3810
- follows from the API rather than from taste.
3811
-
3812
- Writes map cleanly onto entities only when the API's notion of "save" is
3813
- unambiguous. For a CMS with draft states, localised fields and a separate
3814
- publish step, \`save$\` would have to pick one interpretation and would mislead
3815
- whoever guessed differently. Here the write operations are plain whole-record
3816
- ones, so \`save$\` can mean exactly one thing for each of
3817
- ${list(writable.map((e: any) => e.name))}, and the store surface those
3818
- operations support is implemented in full.
3819
-
3820
- One wrinkle does not map cleanly. Seneca's model lets a caller choose an id;
3821
- many APIs assign ids themselves and ignore any id sent on create. The provider
3822
- does not try to paper over that, because it cannot make a server honour an id
3823
- it did not issue. Code that predicts the id of a record it is about to create
3824
- will be wrong on such an API, and the remedy is to read the id back from what
3825
- \`save$\` returns rather than to guess it beforehand.
3826
-
3827
-
3828
- `)
3829
- }
3830
- else {
3831
- Content(`## Why this provider only reads
3832
-
3833
- Every entity here exposes reads alone. That is not a policy decision taken in
3834
- the plugin: the cmd map is built from the operations the API declares, and none
3835
- of these entities declares a create or an update. A \`save$\` that existed only
3836
- to fail at the HTTP layer would be worse than no \`save$\` at all — the absence
3837
- is the honest signal, and it appears in the entity table in the
3838
- [reference](reference.md).
3839
-
3840
- If the API grows write operations, they arrive here by regeneration rather than
3841
- by hand. Nothing about the mapping is waiting to be written.
3842
-
3843
-
3844
- `)
3845
- }
3846
-
3847
- Content(`## Credentials, whether or not the API needs them
3848
-
3849
- At startup the plugin asks \`@seneca/provider\` for the keymap of
3850
- \`${provider.lower}\` and sends the \`apikey\` as a bearer token when one is
3851
- configured.
3852
-
3853
- The key is *optional*. Absent, unconfigured and empty all mean "send no
3854
- header", and none of them is an error. For an API that needs no credential this
3855
- looks like ceremony, and it is worth keeping anyway: the shape of a Seneca
3856
- application should not depend on whether a particular service happens to need a
3857
- key. An application that moves from an open endpoint to an authenticated
3858
- deployment then changes one configuration value rather than restructuring how
3859
- the plugin loads — and a provider that demanded a key from an API that has none
3860
- would force every user to invent a fake one.
3861
-
3862
-
3863
- ## Depending on a published SDK
3864
-
3865
- The SDK is an ordinary published dependency: \`${provider.sdkPkg}\` at
3866
- \`^${provider.sdkVersion}\`, resolved by npm like anything else.
3867
-
3868
- The alternative is vendoring — copying the generated client into this
3869
- repository. That is tempting, since both artefacts come from the same model and
3870
- change together. It is also wrong. It makes a second copy of something that is
3871
- regenerated whenever the API moves, and it puts this plugin's release cycle in
3872
- charge of the API's. As a dependency, the SDK carries its own semantic version:
3873
- when the API changes, the SDK is versioned, and this plugin either follows the
3874
- range or pins until it is ready. Keeping them separable also matters to the
3875
- people who use the SDK with no Seneca anywhere in sight.
3876
-
3877
- One consequence of depending on generated code is worth stating plainly. The
3878
- SDK is regenerated as the API model changes, so its surface can shift in ways a
3879
- hand-written library's would not. That argues for keeping this plugin thin, and
3880
- for pinning behaviour in tests. Everything this plugin knows about the SDK's
3881
- shapes is concentrated in three small functions — the \`.data()\` hop, the query
3882
- cleaner and the not-found translation — plus the construction of the client, so
3883
- an SDK change is absorbed in one place and surfaces as a failing offline test
3884
- rather than as a surprise in production.
3885
-
3886
- `)
3887
-
3888
- if ('' === provider.liveBase) {
3889
- Content(`The API definition declares no server, so this plugin has no default host: the
3890
- base URL arrives through the \`sdk.base\` option, supplied by whoever configures
3891
- the plugin for a particular deployment. The tests therefore run entirely
3892
- against the SDK's mock transport, which is the one host that is always
3893
- available.
3894
-
3895
-
3896
- `)
3897
- }
3898
- else {
3899
- Content(`The distinction that does survive is between the SDK and its **test server**.
3900
- The SDK is published; the server is not, and ships only in
3901
- [the SDK's source repository](${provider.sdkRepoUrl}). So the offline tests need
3902
- nothing but \`npm install\`, while the live tests need a clone. That asymmetry
3903
- is why the live tests probe for the server and skip rather than fail: the common
3904
- case is a contributor who has the dependency but not the repository.
3905
-
3906
-
3907
- `)
3908
- }
3909
-
3910
- Content(`## A generated plugin
3911
-
3912
- Nothing in this repository is hand-written. The plugin source, its tests, its CI
3913
- workflow, its manifest and these documents are all emitted by
3914
- [@voxgig/sdkgen](https://github.com/voxgig/sdkgen) from the ${provider.api} API
3915
- model — the same model the SDK is generated from, which is why the two cannot
3916
- disagree about entity names, id fields, or which operations exist.
3917
-
3918
- There is one blunt consequence for anyone reading the code and reaching for an
3919
- edit: the edit will not survive. The next generation run overwrites this
3920
- repository, without a merge and without a warning. A fix applied here is a fix
3921
- that has to be applied again, silently, forever.
3922
-
3923
- The source of truth is the SDK project's model — \`${provider.sdkrel}\` from
3924
- here, if both are checked out — together with the sdkgen component that emits
3925
- this target. A change to *what* the API offers belongs in the model; a change to
3926
- *how* the provider expresses it belongs in the component. Both are versioned,
3927
- both regenerate every provider built this way rather than just this one, and
3928
- both are where a fix is worth making. See
3929
- [Contributing](../README.md#contributing).
3930
-
3931
-
3932
- ## How the tests are arranged
3933
-
3934
- The suite runs offline by default. It needs no credentials and no network.
3935
-
3936
- The **offline** tests use the SDK's own mock transport, reached through this
3937
- plugin's own options:
3938
-
3939
- \`\`\`js
3940
- .use('${provider.pkgName}', {
3941
- test: true,
3942
- testopts: { entity: { ${subject.name}: { '${subject.name}0': { ... } } } },
3943
- })
3944
- \`\`\`
3945
-
3946
- This is better than the usual provider-testing compromise. Rather than checking
3947
- only that the plugin loads and answers
3948
- \`sys:provider,provider:${provider.lower},get:info\`, the tests exercise the
3949
- entity commands themselves${coveredPhrase}
3950
- through the real code path, from a Seneca entity call down to the transport and
3951
- back. The only thing replaced is the socket. And because the mock belongs to the
3952
- SDK, it stays honest as the SDK changes: a regeneration that alters a return
3953
- shape breaks a test here rather than someone's production run.
3954
-
3955
- Seeding the mock is not decoration either. The seed is generated from the same
3956
- model as the entities, so the records the tests read carry the fields the API
3957
- would really return${0 < nested.length ? `, and a nested record's parent id
3958
- names a parent record that exists — otherwise the nested tests would read an
3959
- empty store and pass without proving anything` : ''}.
3960
-
3961
- `)
3962
-
3963
- if ('' !== provider.liveBase) {
3964
- Content(`The **live** tests point at the companion server in the SDK repository and probe
3965
- it before running, skipping with a stated reason when nothing answers. So a
3966
- contributor who has just cloned this repository gets a meaningful result
3967
- immediately, and a more thorough one after starting the server.
3968
-
3969
- Skipping is deliberate, and preferred over quietly returning early. An early
3970
- \`return\` reports a test as *passed*, which makes an unconfigured checkout look
3971
- as though it verified the integration when it verified nothing at all. A skip is
3972
- honest about coverage, and the summary count shows how much did not run.
3973
-
3974
- `)
3975
-
3976
- if (subject.cmds.includes('save') && subject.cmds.includes('remove')) {
3977
- Content(`The manual scripts in \`test/\` that write to a live server remove what they
3978
- create, in a \`finally\` block, so the server is left as it was found. A run that
3979
- leaks a record changes the result of the next one, which is how a suite becomes
3980
- order-dependent and then flaky.
3981
- `)
3982
- }
3983
- }
3984
- })
3985
- })
3986
-
3987
-
3988
- // --- doc/ --------------------------------------------------------------------
3989
- //
3990
- // The Diátaxis documentation set: an index plus the four quadrants.
3991
- //
3992
- // WHY THIS IS GENERATED AT ALL. Every other sdkgen target emits a single
3993
- // README, and for a language SDK that is the right amount: the SDK's real
3994
- // reference is its types. A Seneca provider has no types a reader can browse —
3995
- // its whole interface is message patterns and entity canons, which exist only
3996
- // in prose. The provider this target was modelled on carried 1100 lines of
3997
- // hand-written documentation for exactly that reason, and the first
3998
- // regeneration left all of it orphaned: the README's link table was gone and
3999
- // nothing emitted the files it had pointed at.
4000
- //
4001
- // Everything here is derived from the same `provider` shape the source and the
4002
- // tests are built from, so the docs cannot describe an entity the plugin does
4003
- // not expose, or a cmd it does not implement — the drift that makes
4004
- // hand-written provider docs untrustworthy after the second API change.
4005
-
4006
- const DocIndex = cmp(function DocIndex(props: any) {
4007
- const { provider } = props
4008
-
4009
- File({ name: 'README.md' }, () => {
4010
- Content(`# Documentation
4011
-
4012
- The documentation for \`${provider.pkgName}\` follows the
4013
- [Diátaxis](https://diataxis.fr) framework. Each document serves one purpose,
4014
- and that purpose decides what belongs in it. If you are unsure where to look,
4015
- use the table below.
4016
-
4017
- | Document | Purpose | Read it when |
4018
- | -------- | ------- | ------------ |
4019
- | [Tutorial](tutorial.md) | Learning-oriented. A lesson that takes you from nothing to a working script. | You have never used this plugin and want to see it work. |
4020
- | [How-to guides](how-to.md) | Task-oriented. Recipes that solve one problem each. | You know what you want to do and need the steps. |
4021
- | [Reference](reference.md) | Information-oriented. A complete, factual description of the interface. | You need to look up a message pattern, entity field, or option. |
4022
- | [Explanation](explanation.md) | Understanding-oriented. The reasoning behind the design. | You want to know *why* it works this way, or you are debugging something surprising. |
4023
-
4024
- ## The distinction that matters most
4025
-
4026
- The tutorial and the how-to guides look alike — both are sequences of steps —
4027
- but they are not interchangeable.
4028
-
4029
- The **tutorial** is a lesson. It is safe to follow, it produces a result you
4030
- can see, and it asks you to make no decisions. Its job is to build confidence,
4031
- so it deliberately avoids alternatives and edge cases.
4032
-
4033
- A **how-to guide** assumes competence. It answers "how do I list every record
4034
- of a collection?", and it assumes you already have a working Seneca instance.
4035
- Its job is to get a task done, so it omits the explanation.
4036
-
4037
- Likewise **reference** describes the machinery and nothing else — it never
4038
- teaches. **Explanation** discusses and gives context — it never instructs.
4039
-
4040
- ## These documents are generated
4041
-
4042
- This plugin, and this documentation with it, is generated by
4043
- [@voxgig/sdkgen](https://github.com/voxgig/sdkgen) from the ${provider.api} API
4044
- definition held in the [SDK project](${provider.sdkRepoUrl}). An edit made here
4045
- is lost on the next regeneration.
4046
-
4047
- Something genuinely specific to this API — a quirk of its authentication, a
4048
- rate limit worth warning about — belongs in the model the generator reads, not
4049
- in the output it writes. Everything else belongs in the generator's own
4050
- components, where fixing it once fixes every provider.
4051
- `)
4052
- })
4053
- })
4054
-
4055
-
4056
- // The whole `doc/` folder. One cmp so Main names the documentation once, and
4057
- // so the folder is opened in a single place — the four quadrant components
4058
- // emit a File each and know nothing about where they sit.
4059
- const Docs = cmp(function Docs(props: any) {
4060
- const { provider } = props
4061
-
4062
- Folder({ name: 'doc' }, () => {
4063
- DocIndex({ provider })
4064
- DocTutorial({ provider })
4065
- DocHowto({ provider })
4066
- DocReference({ provider })
4067
- DocExplanation({ provider })
4068
- })
4069
- })
4070
-
4071
-
4072
- export {
4073
- Tests,
4074
- Scripts,
4075
- Workflow,
4076
- Readme,
4077
- Docs,
4078
- seedRecord,
4079
- parentSeed,
4080
- }