@voxgig/sdkgen 1.5.1 → 2.0.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 (270) hide show
  1. package/dist/cmp/AgentGuideContent.js +5 -0
  2. package/dist/cmp/AgentGuideContent.js.map +1 -1
  3. package/dist/cmp/Feature.js +13 -0
  4. package/dist/cmp/Feature.js.map +1 -1
  5. package/dist/helpers/canonType.d.ts +3 -1
  6. package/dist/helpers/canonType.js +44 -1
  7. package/dist/helpers/canonType.js.map +1 -1
  8. package/dist/helpers/packageMeta.js +7 -0
  9. package/dist/helpers/packageMeta.js.map +1 -1
  10. package/dist/sdkgen.d.ts +3 -3
  11. package/dist/sdkgen.js +4 -1
  12. package/dist/sdkgen.js.map +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/package.json +3 -3
  15. package/project/.sdk/model/feature/paging.aontu +7 -0
  16. package/project/.sdk/model/target/py-data.aontu +64 -0
  17. package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +89 -1
  18. package/project/.sdk/src/cmp/clojure/Main_clojure.ts +1 -0
  19. package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +97 -0
  20. package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +78 -0
  21. package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +84 -0
  22. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +87 -0
  23. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +75 -0
  24. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +85 -0
  25. package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +4 -0
  26. package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +80 -0
  27. package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +86 -0
  28. package/project/.sdk/src/cmp/py/Entity_py.ts +9 -2
  29. package/project/.sdk/src/cmp/py/MainEntity_py.ts +1 -1
  30. package/project/.sdk/src/cmp/py/Main_py.ts +34 -6
  31. package/project/.sdk/src/cmp/py/Package_py.ts +6 -9
  32. package/project/.sdk/src/cmp/py/TestDirect_py.ts +2 -2
  33. package/project/.sdk/src/cmp/py/TestEntity_py.ts +3 -3
  34. package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +2 -2
  35. package/project/.sdk/src/cmp/py/fragment/EntityCreateOp.fragment.py +2 -2
  36. package/project/.sdk/src/cmp/py/fragment/EntityLoadOp.fragment.py +2 -2
  37. package/project/.sdk/src/cmp/py/fragment/EntityRemoveOp.fragment.py +2 -2
  38. package/project/.sdk/src/cmp/py/fragment/EntityUpdateOp.fragment.py +2 -2
  39. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +75 -8
  40. package/project/.sdk/src/cmp/py-data/Main_py-data.ts +987 -0
  41. package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +71 -0
  42. package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +95 -0
  43. package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +86 -0
  44. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +75 -0
  45. package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +95 -0
  46. package/project/.sdk/tm/c/core/sdk.h +7 -0
  47. package/project/.sdk/tm/c/feature/paging.c +141 -2
  48. package/project/.sdk/tm/c/utility/graphql.c +178 -0
  49. package/project/.sdk/tm/c/utility/make_options.c +1 -1
  50. package/project/.sdk/tm/c/utility/make_response.c +6 -0
  51. package/project/.sdk/tm/c/utility/make_spec.c +23 -3
  52. package/project/.sdk/tm/clojure/src/sdk/client.clj +64 -1
  53. package/project/.sdk/tm/clojure/src/sdk/core.clj +126 -3
  54. package/project/.sdk/tm/clojure/src/sdk/features.clj +80 -14
  55. package/project/.sdk/tm/cpp/core/types.hpp +83 -0
  56. package/project/.sdk/tm/cpp/feature/paging.hpp +103 -1
  57. package/project/.sdk/tm/cpp/utility/pipeline.hpp +152 -3
  58. package/project/.sdk/tm/csharp/core/Utility.cs +4 -0
  59. package/project/.sdk/tm/csharp/feature/PagingFeature.cs +118 -1
  60. package/project/.sdk/tm/csharp/utility/Graphql.cs +190 -0
  61. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +1 -1
  62. package/project/.sdk/tm/csharp/utility/MakeResponse.cs +7 -0
  63. package/project/.sdk/tm/csharp/utility/MakeSpec.cs +21 -2
  64. package/project/.sdk/tm/csharp/utility/Register.cs +2 -0
  65. package/project/.sdk/tm/dart/lib/Point.dart +7 -0
  66. package/project/.sdk/tm/dart/lib/feature/paging/PagingFeature.dart +99 -4
  67. package/project/.sdk/tm/dart/lib/utility/GraphqlUtility.dart +162 -0
  68. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +1 -1
  69. package/project/.sdk/tm/dart/lib/utility/MakeResponseUtility.dart +5 -0
  70. package/project/.sdk/tm/dart/lib/utility/MakeSpecUtility.dart +18 -2
  71. package/project/.sdk/tm/dart/lib/utility/PrepareMethodUtility.dart +6 -1
  72. package/project/.sdk/tm/dart/lib/utility/Utility.dart +9 -0
  73. package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +136 -13
  74. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +187 -3
  75. package/project/.sdk/tm/go/core/utility_type.go +2 -0
  76. package/project/.sdk/tm/go/feature/paging_feature.go +87 -1
  77. package/project/.sdk/tm/go/utility/graphql.go +168 -0
  78. package/project/.sdk/tm/go/utility/make_options.go +1 -1
  79. package/project/.sdk/tm/go/utility/make_response.go +6 -0
  80. package/project/.sdk/tm/go/utility/make_spec.go +19 -2
  81. package/project/.sdk/tm/go/utility/register.go +2 -0
  82. package/project/.sdk/tm/haskell/src/SdkFeatures.hs +146 -7
  83. package/project/.sdk/tm/haskell/src/SdkRuntime.hs +153 -3
  84. package/project/.sdk/tm/java/core/SdkClient.java +93 -0
  85. package/project/.sdk/tm/java/core/Utility.java +4 -0
  86. package/project/.sdk/tm/java/feature/PagingFeature.java +100 -1
  87. package/project/.sdk/tm/java/utility/Graphql.java +191 -0
  88. package/project/.sdk/tm/java/utility/MakeOptions.java +1 -1
  89. package/project/.sdk/tm/java/utility/MakeResponse.java +6 -0
  90. package/project/.sdk/tm/java/utility/MakeSpec.java +17 -2
  91. package/project/.sdk/tm/java/utility/Register.java +2 -0
  92. package/project/.sdk/tm/js/src/Point.js +5 -0
  93. package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +82 -3
  94. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +14 -2
  95. package/project/.sdk/tm/js/src/utility/GraphqlUtility.js +148 -0
  96. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +1 -1
  97. package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +7 -0
  98. package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +19 -2
  99. package/project/.sdk/tm/js/src/utility/Utility.js +4 -0
  100. package/project/.sdk/tm/kotlin/core/SdkClient.kt +84 -0
  101. package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
  102. package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +92 -1
  103. package/project/.sdk/tm/kotlin/utility/Graphql.kt +145 -0
  104. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +1 -1
  105. package/project/.sdk/tm/kotlin/utility/MakeReqRes.kt +6 -0
  106. package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +17 -2
  107. package/project/.sdk/tm/kotlin/utility/Register.kt +2 -0
  108. package/project/.sdk/tm/lean/src/SdkUtility.lean +134 -1
  109. package/project/.sdk/tm/lua/feature/paging_feature.lua +97 -2
  110. package/project/.sdk/tm/lua/utility/graphql.lua +163 -0
  111. package/project/.sdk/tm/lua/utility/make_options.lua +1 -1
  112. package/project/.sdk/tm/lua/utility/make_response.lua +6 -0
  113. package/project/.sdk/tm/lua/utility/make_spec.lua +18 -2
  114. package/project/.sdk/tm/lua/utility/register.lua +3 -0
  115. package/project/.sdk/tm/ocaml/sdk_features.ml +164 -20
  116. package/project/.sdk/tm/ocaml/sdk_runtime.ml +143 -4
  117. package/project/.sdk/tm/ocaml/sdk_types.ml +2 -0
  118. package/project/.sdk/tm/perl/feature/paging_feature.pm +91 -3
  119. package/project/.sdk/tm/perl/utility/graphql.pm +150 -0
  120. package/project/.sdk/tm/perl/utility/make_options.pm +1 -1
  121. package/project/.sdk/tm/perl/utility/make_response.pm +6 -0
  122. package/project/.sdk/tm/perl/utility/make_spec.pm +20 -2
  123. package/project/.sdk/tm/perl/utility/register.pm +1 -0
  124. package/project/.sdk/tm/php/core/UtilityType.php +2 -0
  125. package/project/.sdk/tm/php/feature/PagingFeature.php +106 -4
  126. package/project/.sdk/tm/php/utility/Graphql.php +176 -0
  127. package/project/.sdk/tm/php/utility/MakeOptions.php +1 -1
  128. package/project/.sdk/tm/php/utility/MakeResponse.php +7 -0
  129. package/project/.sdk/tm/php/utility/MakeSpec.php +20 -2
  130. package/project/.sdk/tm/php/utility/Register.php +3 -0
  131. package/project/.sdk/tm/py/pkg/core/__init__.py +0 -0
  132. package/project/.sdk/tm/py/{core → pkg/core}/context.py +8 -8
  133. package/project/.sdk/tm/py/{core → pkg/core}/operation.py +1 -1
  134. package/project/.sdk/tm/py/{core → pkg/core}/response.py +1 -1
  135. package/project/.sdk/tm/py/{core → pkg/core}/result.py +1 -1
  136. package/project/.sdk/tm/py/{core → pkg/core}/utility_type.py +2 -0
  137. package/project/.sdk/tm/py/pkg/entity/__init__.py +0 -0
  138. package/project/.sdk/tm/py/pkg/feature/__init__.py +0 -0
  139. package/project/.sdk/tm/py/{feature → pkg/feature}/audit_feature.py +1 -1
  140. package/project/.sdk/tm/py/{feature → pkg/feature}/cache_feature.py +1 -1
  141. package/project/.sdk/tm/py/{feature → pkg/feature}/clienttrack_feature.py +1 -1
  142. package/project/.sdk/tm/py/{feature → pkg/feature}/debug_feature.py +1 -1
  143. package/project/.sdk/tm/py/{feature → pkg/feature}/idempotency_feature.py +1 -1
  144. package/project/.sdk/tm/py/{feature → pkg/feature}/log_feature.py +1 -1
  145. package/project/.sdk/tm/py/{feature → pkg/feature}/metrics_feature.py +1 -1
  146. package/project/.sdk/tm/py/{feature → pkg/feature}/netsim_feature.py +1 -1
  147. package/project/.sdk/tm/py/{feature → pkg/feature}/paging_feature.py +87 -6
  148. package/project/.sdk/tm/py/{feature → pkg/feature}/proxy_feature.py +1 -1
  149. package/project/.sdk/tm/py/{feature → pkg/feature}/ratelimit_feature.py +1 -1
  150. package/project/.sdk/tm/py/{feature → pkg/feature}/rbac_feature.py +1 -1
  151. package/project/.sdk/tm/py/{feature → pkg/feature}/retry_feature.py +1 -1
  152. package/project/.sdk/tm/py/{feature → pkg/feature}/streaming_feature.py +1 -1
  153. package/project/.sdk/tm/py/{feature → pkg/feature}/telemetry_feature.py +1 -1
  154. package/project/.sdk/tm/py/{feature → pkg/feature}/test_feature.py +2 -2
  155. package/project/.sdk/tm/py/{feature → pkg/feature}/timeout_feature.py +1 -1
  156. package/project/.sdk/tm/py/pkg/utility/__init__.py +0 -0
  157. package/project/.sdk/tm/py/{utility → pkg/utility}/feature_init.py +1 -1
  158. package/project/.sdk/tm/py/{utility → pkg/utility}/fetcher.py +1 -1
  159. package/project/.sdk/tm/py/pkg/utility/graphql.py +145 -0
  160. package/project/.sdk/tm/py/{utility → pkg/utility}/make_context.py +1 -1
  161. package/project/.sdk/tm/py/{utility → pkg/utility}/make_error.py +5 -5
  162. package/project/.sdk/tm/py/{utility → pkg/utility}/make_fetch_def.py +2 -2
  163. package/project/.sdk/tm/py/{utility → pkg/utility}/make_options.py +2 -2
  164. package/project/.sdk/tm/py/{utility → pkg/utility}/make_point.py +2 -2
  165. package/project/.sdk/tm/py/{utility → pkg/utility}/make_request.py +2 -2
  166. package/project/.sdk/tm/py/{utility → pkg/utility}/make_response.py +6 -0
  167. package/project/.sdk/tm/py/{utility → pkg/utility}/make_spec.py +19 -4
  168. package/project/.sdk/tm/py/{utility → pkg/utility}/make_url.py +1 -1
  169. package/project/.sdk/tm/py/{utility → pkg/utility}/param.py +2 -2
  170. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_auth.py +1 -1
  171. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_headers.py +1 -1
  172. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_method.py +1 -1
  173. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_params.py +1 -1
  174. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_path.py +1 -1
  175. package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_query.py +1 -1
  176. package/project/.sdk/tm/py/pkg/utility/register.py +71 -0
  177. package/project/.sdk/tm/py/{utility → pkg/utility}/transform_request.py +2 -2
  178. package/project/.sdk/tm/py/{utility → pkg/utility}/transform_response.py +2 -2
  179. package/project/.sdk/tm/py/test/feature_harness.py +6 -6
  180. package/project/.sdk/tm/py/test/runner.py +1 -1
  181. package/project/.sdk/tm/py/test/struct_runner.py +1 -1
  182. package/project/.sdk/tm/py/test/test_pipeline.py +5 -5
  183. package/project/.sdk/tm/py/test/test_primary_utility.py +8 -8
  184. package/project/.sdk/tm/py/test/test_struct_utility.py +3 -3
  185. package/project/.sdk/tm/py-data/LICENSE +22 -0
  186. package/project/.sdk/tm/py-data/Makefile +57 -0
  187. package/project/.sdk/tm/py-data/pkg/auth.py +131 -0
  188. package/project/.sdk/tm/py-data/pkg/fetch.py +178 -0
  189. package/project/.sdk/tm/py-data/pkg/frames.py +236 -0
  190. package/project/.sdk/tm/py-data/src/feature/README.md +8 -0
  191. package/project/.sdk/tm/py-data/src/feature/debug/.gitkeep +0 -0
  192. package/project/.sdk/tm/py-data/src/feature/idempotency/.gitkeep +0 -0
  193. package/project/.sdk/tm/py-data/src/feature/log/.gitkeep +0 -0
  194. package/project/.sdk/tm/py-data/src/feature/metrics/.gitkeep +0 -0
  195. package/project/.sdk/tm/py-data/src/feature/netsim/.gitkeep +0 -0
  196. package/project/.sdk/tm/py-data/src/feature/paging/.gitkeep +0 -0
  197. package/project/.sdk/tm/py-data/src/feature/proxy/.gitkeep +0 -0
  198. package/project/.sdk/tm/py-data/src/feature/ratelimit/.gitkeep +0 -0
  199. package/project/.sdk/tm/py-data/src/feature/rbac/.gitkeep +0 -0
  200. package/project/.sdk/tm/py-data/src/feature/retry/.gitkeep +0 -0
  201. package/project/.sdk/tm/py-data/src/feature/streaming/.gitkeep +0 -0
  202. package/project/.sdk/tm/py-data/src/feature/telemetry/.gitkeep +0 -0
  203. package/project/.sdk/tm/py-data/src/feature/test/.gitkeep +0 -0
  204. package/project/.sdk/tm/py-data/src/feature/timeout/.gitkeep +0 -0
  205. package/project/.sdk/tm/py-data/test/__init__.py +0 -0
  206. package/project/.sdk/tm/py-data/test/test_auth.py +95 -0
  207. package/project/.sdk/tm/py-data/test/test_fetch.py +152 -0
  208. package/project/.sdk/tm/py-data/test/test_frames.py +250 -0
  209. package/project/.sdk/tm/rb/core/utility_type.rb +1 -0
  210. package/project/.sdk/tm/rb/feature/paging_feature.rb +89 -3
  211. package/project/.sdk/tm/rb/utility/graphql.rb +131 -0
  212. package/project/.sdk/tm/rb/utility/make_options.rb +1 -1
  213. package/project/.sdk/tm/rb/utility/make_response.rb +6 -0
  214. package/project/.sdk/tm/rb/utility/make_spec.rb +18 -2
  215. package/project/.sdk/tm/rb/utility/register.rb +3 -0
  216. package/project/.sdk/tm/rust/feature/paging.rs +124 -2
  217. package/project/.sdk/tm/rust/utility/graphql.rs +193 -0
  218. package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
  219. package/project/.sdk/tm/rust/utility/make_response.rs +6 -0
  220. package/project/.sdk/tm/rust/utility/make_spec.rs +32 -4
  221. package/project/.sdk/tm/rust/utility/mod.rs +1 -0
  222. package/project/.sdk/tm/scala/core/SdkClient.scala +90 -0
  223. package/project/.sdk/tm/scala/core/Utility.scala +4 -0
  224. package/project/.sdk/tm/scala/feature/PagingFeature.scala +104 -3
  225. package/project/.sdk/tm/scala/utility/Graphql.scala +160 -0
  226. package/project/.sdk/tm/scala/utility/Make.scala +7 -1
  227. package/project/.sdk/tm/scala/utility/MakeSpec.scala +22 -2
  228. package/project/.sdk/tm/scala/utility/Register.scala +2 -0
  229. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +4 -0
  230. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +86 -1
  231. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Graphql.swift +142 -0
  232. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +24 -2
  233. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +1 -1
  234. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +2 -0
  235. package/project/.sdk/tm/ts/src/Point.ts +7 -0
  236. package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +82 -3
  237. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +14 -2
  238. package/project/.sdk/tm/ts/src/utility/GraphqlUtility.ts +152 -0
  239. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +1 -1
  240. package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +7 -0
  241. package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +19 -2
  242. package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -0
  243. package/project/.sdk/tm/zig/core/utility.zig +198 -3
  244. package/project/.sdk/tm/zig/feature/paging.zig +128 -1
  245. package/src/cmp/AgentGuideContent.ts +5 -0
  246. package/src/cmp/Feature.ts +13 -0
  247. package/src/helpers/canonType.ts +46 -0
  248. package/src/helpers/packageMeta.ts +7 -0
  249. package/src/sdkgen.ts +6 -1
  250. package/project/.sdk/tm/py/utility/register.py +0 -68
  251. /package/project/.sdk/tm/py/{core → pkg/core}/control.py +0 -0
  252. /package/project/.sdk/tm/py/{core → pkg/core}/error.py +0 -0
  253. /package/project/.sdk/tm/py/{core → pkg/core}/helpers.py +0 -0
  254. /package/project/.sdk/tm/py/{core → pkg/core}/spec.py +0 -0
  255. /package/project/.sdk/tm/py/{feature → pkg/feature}/base_feature.py +0 -0
  256. /package/project/.sdk/tm/py/{utility → pkg/utility}/clean.py +0 -0
  257. /package/project/.sdk/tm/py/{utility → pkg/utility}/done.py +0 -0
  258. /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_add.py +0 -0
  259. /package/project/.sdk/tm/py/{utility → pkg/utility}/feature_hook.py +0 -0
  260. /package/project/.sdk/tm/py/{utility → pkg/utility}/make_result.py +0 -0
  261. /package/project/.sdk/tm/py/{utility → pkg/utility}/prepare_body.py +0 -0
  262. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_basic.py +0 -0
  263. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_body.py +0 -0
  264. /package/project/.sdk/tm/py/{utility → pkg/utility}/result_headers.py +0 -0
  265. /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/__init__.py +0 -0
  266. /package/project/.sdk/tm/py/{utility → pkg/utility}/voxgig_struct/voxgig_struct.py +0 -0
  267. /package/project/.sdk/tm/{py/core/__init__.py → py-data/src/feature/audit/.gitkeep} +0 -0
  268. /package/project/.sdk/tm/{py/entity/__init__.py → py-data/src/feature/base/.gitkeep} +0 -0
  269. /package/project/.sdk/tm/{py/feature/__init__.py → py-data/src/feature/cache/.gitkeep} +0 -0
  270. /package/project/.sdk/tm/{py/utility/__init__.py → py-data/src/feature/clienttrack/.gitkeep} +0 -0
@@ -0,0 +1,987 @@
1
+
2
+ import {
3
+ cmp, each,
4
+ File, Content, Copy, Folder,
5
+ entityCollection, entityOps, entityIdField, canonToDtype,
6
+ collectDeps, pkgDescription, keywords, repoInfo, envName, apiName,
7
+ packageName, registryState, serverVariables,
8
+ SdkGenError,
9
+ } from '@voxgig/sdkgen'
10
+
11
+ import {
12
+ KIT,
13
+ } from '@voxgig/apidef'
14
+
15
+
16
+ // The `py-data` target: a Python package for DATA ANALYSTS working in
17
+ // notebooks (Colab, Jupyter), layered on the sibling `py` SDK generated into
18
+ // the same repo. It is a consumer target in the go-cli / go-mcp mould — every
19
+ // standard generation phase is switched off in model/target/py-data.aontu and
20
+ // this component emits the whole package.
21
+ //
22
+ // The deliverable an analyst wants is not "a client", it is TABLES: one line
23
+ // from API to a typed pandas DataFrame, discovered by tab-completion rather
24
+ // than by reading reference docs.
25
+ //
26
+ // Layered, NOT forked from `py`: the dependency arrow points one way
27
+ // (py-data -> py) and `py` must never learn this target exists. A fork would
28
+ // duplicate the entity/feature machinery and drift, which is the known
29
+ // failure mode.
30
+
31
+
32
+ // Accessors are generated only for entities exposing these ops. `list` gives
33
+ // a DataFrame; `load` gives a Series. Everything else (create/update/remove)
34
+ // is a write path and deliberately out of scope — see the README's
35
+ // "What this package does not do".
36
+ const FRAME_OP = 'list'
37
+ const SERIES_OP = 'load'
38
+
39
+
40
+ const Main = cmp(function Main(props: any) {
41
+ const { target, ctx$ } = props
42
+ const { model, log } = ctx$
43
+
44
+ // HARD REQUIREMENT: this package imports the sibling py SDK. Generating it
45
+ // without `py` produces a package whose every import fails at runtime, so
46
+ // fail at GENERATE time with an actionable message instead.
47
+ const targets = model.main[KIT].target || {}
48
+ if (null == targets.py) {
49
+ throw new SdkGenError(
50
+ 'py-data requires the `py` target in the same SDK: it imports the ' +
51
+ 'Python SDK that `py` generates. Add it with:\n' +
52
+ ' npm run add-target py\n' +
53
+ 'then regenerate.')
54
+ }
55
+
56
+ const name = model.const.Name // PascalCase, e.g. Univec
57
+ const lower = String(name).toLowerCase() // univec
58
+ const ENV = envName(model) // UNIVEC
59
+ const sdkModule = `${lower}_sdk` // univec_sdk
60
+ const sdkClass = `${name}SDK` // UnivecSDK
61
+ const dataModule = `${lower}_data` // univec_data
62
+
63
+ // Multi-tenant APIs (a server URL carrying an unresolved {variable}, e.g.
64
+ // https://{instance}.example.com/api) have no single host, so base_url
65
+ // becomes REQUIRED rather than optional. Guessing a host produces confusing
66
+ // 404s, so `data()` refuses instead.
67
+ const svars = serverVariables(model)
68
+ const needsBase = svars.some((v: any) => v.required)
69
+
70
+ const ents = each(entityCollection(model))
71
+ .filter((e: any) => e && null != e.name && false !== e.active)
72
+
73
+ const frameEnts = ents.filter((e: any) => entityOps(e).includes(FRAME_OP))
74
+ const seriesEnts = ents.filter((e: any) =>
75
+ entityOps(e).includes(SERIES_OP) && null != entityIdField(e))
76
+
77
+ log.info({
78
+ point: 'py-data-entities', target: target.name,
79
+ note: `frames:${frameEnts.length} series:${seriesEnts.length} of ${ents.length}`,
80
+ })
81
+
82
+ // Root-level statics: Makefile, LICENSE, the template-side tests.
83
+ Copy({
84
+ from: 'tm/' + target.name,
85
+ exclude: [/src\//, /pkg\//],
86
+ replace: { ...ctx$.stdrep },
87
+ })
88
+
89
+ // The runtime modules live INSIDE the package, not at the top level.
90
+ //
91
+ // They were originally emitted flat as auth.py / fetch.py / frames.py, which
92
+ // is actively dangerous here: `auth`, `fetch`, `frames`, `core`, `entity`
93
+ // and `utility` are all real PyPI distributions AND exactly the names an
94
+ // analyst leaves lying around next to a notebook. In Colab the working
95
+ // directory is on sys.path, so one stray auth.py shadowed ours and `data()`
96
+ // died with `module 'auth' has no attribute 'resolve'`. A package keeps
97
+ // every internal module behind the distinctive `<name>_data.` prefix.
98
+ //
99
+ // The folder name is model-derived, so it comes from the component; the file
100
+ // contents are the same for every API, so they stay in tm/py-data/pkg.
101
+ Folder({ name: dataModule }, () => {
102
+ Copy({
103
+ from: 'tm/' + target.name + '/pkg',
104
+ replace: { ...ctx$.stdrep },
105
+ })
106
+ DataClient({ model, name, lower, ENV, sdkModule, sdkClass, dataModule, needsBase })
107
+ EntityFrames({ model, frameEnts, seriesEnts, lower, dataModule })
108
+ })
109
+
110
+ Gitignore({})
111
+ Package({ target, model, lower, dataModule })
112
+ EntityFramesTest({ frameEnts, seriesEnts, lower, sdkModule, sdkClass, dataModule })
113
+ Readme({ model, name, lower, ENV, dataModule, frameEnts, seriesEnts, needsBase, svars })
114
+ AgentGuide({ model, name, lower, ENV, dataModule, frameEnts, seriesEnts, needsBase })
115
+ Quickstart({ model, name, lower, ENV, dataModule, frameEnts, needsBase })
116
+ })
117
+
118
+
119
+ const Gitignore = cmp(function Gitignore(_props: any) {
120
+ File({ name: '.gitignore' }, () => Content(`__pycache__/
121
+ *.py[cod]
122
+ /dist/
123
+ /build/
124
+ *.egg-info/
125
+ .pytest_cache/
126
+ .ipynb_checkpoints/
127
+ `))
128
+ })
129
+
130
+
131
+ const Package = cmp(function Package(props: any) {
132
+ const { target, model, lower, dataModule } = props
133
+ const ctx$ = props.ctx$
134
+
135
+ const ns = model.origin || 'voxgig-sdk'
136
+ const pkgBase = ns.endsWith('-sdk') ? model.name : `${model.name}-sdk`
137
+ const distName = `${ns}-${pkgBase}-data`
138
+ const sdkDist = packageName(model, 'py')
139
+ const { repoUrl, issuesUrl } = repoInfo(model)
140
+ const kw = keywords(model).concat(['pandas', 'dataframe', 'notebook', 'colab'])
141
+ .map((k) => `"${k}"`).join(', ')
142
+
143
+ File({ name: 'pyproject.toml' }, () => {
144
+ Content(`[build-system]
145
+ requires = ["setuptools>=61.0"]
146
+ build-backend = "setuptools.build_meta"
147
+
148
+ [project]
149
+ name = "${distName}"
150
+ version = "0.0.1"
151
+ description = "${pkgDescription(model, 'py-data')}"
152
+ readme = "README.md"
153
+ license = "MIT"
154
+ requires-python = ">=3.9"
155
+ keywords = [${kw}]
156
+ dependencies = [
157
+ "${sdkDist}",
158
+ `)
159
+
160
+ // pandas (and anything a feature adds) from the target model. The sibling
161
+ // SDK dep is emitted above rather than declared in the aontu, because its
162
+ // distribution name is derived from the model, not fixed.
163
+ const seen = new Set<string>([sdkDist])
164
+ for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
165
+ if (seen.has(d.name)) continue
166
+ seen.add(d.name)
167
+ const v = d.source === 'target' ? (d.version || '0.0') : d.version
168
+ Content(` "${d.name}>=${v}",
169
+ `)
170
+ }
171
+
172
+ Content(`]
173
+
174
+ [project.urls]
175
+ Homepage = "${repoUrl}"
176
+ Repository = "${repoUrl}"
177
+ Issues = "${issuesUrl}"
178
+
179
+ # A single package, deliberately. Emitting auth/fetch/frames as top-level
180
+ # modules would put names that are both real PyPI distributions and common
181
+ # notebook scratch files onto sys.path, where Colab's working directory wins.
182
+ [tool.setuptools.packages.find]
183
+ include = ["${dataModule}*"]
184
+ `)
185
+ })
186
+ })
187
+
188
+
189
+ // The DataClient module: `data()` plus the wrapper that owns credential
190
+ // discovery and exposes the raw SDK as an escape hatch.
191
+ const DataClient = cmp(function DataClient(props: any) {
192
+ const { name, ENV, sdkModule, sdkClass, dataModule, needsBase } = props
193
+
194
+ const baseCheck = needsBase
195
+ ? `
196
+ if not base_url:
197
+ raise ValueError(auth.missing_base_message("${ENV}"))
198
+ `
199
+ : ''
200
+
201
+ File({ name: '__init__.py' }, () => Content(`# ${name} Data — notebook-friendly access to the ${name} API.
202
+ #
203
+ # GENERATED by @voxgig/sdkgen (py-data target). Edits are overwritten.
204
+
205
+ from __future__ import annotations
206
+
207
+ from typing import Any
208
+
209
+ import pandas as pd
210
+
211
+ from . import auth
212
+ from .entity_frames import EntityFrames
213
+ from .frames import records_df
214
+
215
+ # The sibling SDK is a separate distribution, so this import stays absolute.
216
+ from ${sdkModule} import ${sdkClass}
217
+
218
+
219
+ class ${name}Data(EntityFrames):
220
+ """${name} API as pandas DataFrames.
221
+
222
+ Wraps — never subclasses — the ${sdkClass} client. The wrapped client is
223
+ available as \`.sdk\` for the cases this package deliberately does not
224
+ cover (writes, non-entity endpoints, per-call control).
225
+ """
226
+
227
+ def __init__(self, sdk: ${sdkClass}, base_url: str | None = None):
228
+ self.sdk = sdk
229
+ self.base_url = base_url
230
+
231
+ # ---- escape hatches ---------------------------------------------------
232
+
233
+ def frame(self, result: Any, **kwargs) -> pd.DataFrame:
234
+ """Shape ANY op result into a DataFrame.
235
+
236
+ For endpoints without a generated accessor:
237
+
238
+ rows = ad.sdk.SomeEntity().list()
239
+ df = ad.frame(rows)
240
+ """
241
+ return records_df(result, **kwargs)
242
+
243
+ def entities(self) -> list:
244
+ """The entity accessors available on this object."""
245
+ return sorted(self._ACCESSORS)
246
+
247
+ def _repr_html_(self) -> str:
248
+ accessors = "".join(
249
+ f"<li><code>{a}()</code></li>" for a in sorted(self._ACCESSORS))
250
+ base = self.base_url or "(default)"
251
+ return (
252
+ f"<b>${name}Data</b><br>"
253
+ f"<small>base: <code>{base}</code></small>"
254
+ f"<ul>{accessors}</ul>"
255
+ )
256
+
257
+ def __repr__(self) -> str:
258
+ return (f"<${name}Data base={self.base_url or '(default)'} "
259
+ f"accessors={len(self._ACCESSORS)}>")
260
+
261
+
262
+ def data(
263
+ token: str | None = None,
264
+ base_url: str | None = None,
265
+ **options,
266
+ ) -> ${name}Data:
267
+ """Build a ${name}Data client, finding credentials automatically.
268
+
269
+ Discovery order (first hit wins):
270
+ 1. the \`token\` / \`base_url\` arguments
271
+ 2. Colab secrets — ${ENV}_APIKEY / ${ENV}_BASE
272
+ 3. environment — ${ENV}_APIKEY / ${ENV}_BASE
273
+
274
+ Extra keyword arguments are passed straight through to the SDK client, so
275
+ anything the SDK accepts (feature options, headers, timeouts) works here.
276
+ """
277
+ token, base_url, _source = auth.resolve("${ENV}", token, base_url)
278
+
279
+ if not token:
280
+ raise ValueError(auth.missing_token_message("${ENV}"))
281
+ ${baseCheck}
282
+ opts: dict = {"apikey": token}
283
+ if base_url:
284
+ opts["base"] = base_url
285
+ opts.update(options)
286
+
287
+ return ${name}Data(${sdkClass}(opts), base_url)
288
+ `))
289
+ })
290
+
291
+
292
+ // The generated per-entity surface. One DataFrame accessor per entity with a
293
+ // `list` op; one Series accessor per entity with a keyed `load`.
294
+ const EntityFrames = cmp(function EntityFrames(props: any) {
295
+ const { frameEnts, seriesEnts, lower } = props
296
+
297
+ const accessorNames = frameEnts.map((e: any) => pluralAccessor(e))
298
+ .concat(seriesEnts.map((e: any) => singularAccessor(e)))
299
+
300
+ File({ name: 'entity_frames.py' }, () => {
301
+ Content(`# ${lower} data — generated entity accessors.
302
+ #
303
+ # GENERATED by @voxgig/sdkgen (py-data target). Edits are overwritten.
304
+ #
305
+ # One accessor per entity. Column names and dtypes come from the API model,
306
+ # so \`ad.<tab>\` and \`ad.thing?\` replace a trip to the reference docs.
307
+
308
+ from __future__ import annotations
309
+
310
+ import pandas as pd
311
+
312
+ from .fetch import drain, MAX_PAGES_DEFAULT
313
+ from .frames import build_frame, to_series, FLATTEN_DEFAULT
314
+
315
+
316
+ class EntityFrames:
317
+ """Mixin carrying the generated accessors. Mixed into <Name>Data."""
318
+
319
+ _ACCESSORS = ${pyList(accessorNames, ' ')}
320
+ `)
321
+
322
+ for (const ent of frameEnts) {
323
+ Content(frameAccessor(ent))
324
+ }
325
+ for (const ent of seriesEnts) {
326
+ Content(seriesAccessor(ent))
327
+ }
328
+
329
+ // An SDK whose entities expose no list/load ops still needs a valid,
330
+ // importable module — better an honest empty mixin than a syntax error.
331
+ if (0 === accessorNames.length) {
332
+ Content(`
333
+ # This API exposes no list or load operations, so there are no frame
334
+ # accessors. Use ad.sdk for its write operations and ad.frame(result)
335
+ # to shape anything they return.
336
+ pass
337
+ `)
338
+ }
339
+ })
340
+ })
341
+
342
+
343
+ // ---------------------------------------------------------------------------
344
+ // Accessor emission
345
+ // ---------------------------------------------------------------------------
346
+
347
+ // Field metadata for an entity, as the (name, dtype) pairs the runtime needs
348
+ // and the doc table the docstring shows.
349
+ function entFields(ent: any): { name: string, dtype: string, req: boolean }[] {
350
+ return each(ent.fields)
351
+ .filter((f: any) => f && null != f.name && false !== f.active)
352
+ .map((f: any) => ({
353
+ name: String(f.name),
354
+ dtype: canonToDtype(f.type),
355
+ req: true === f.req,
356
+ }))
357
+ }
358
+
359
+
360
+ function pluralAccessor(ent: any): string {
361
+ const n = pySafe(String(ent.name).toLowerCase())
362
+ // A frame of many rows is plural by nature. Only pluralise when it does not
363
+ // already look plural — 'addresses' must not become 'addresseses'.
364
+ if (/s$/.test(n)) return n
365
+ if (/(x|ch|sh|ss)$/.test(n)) return n + 'es'
366
+ if (/[^aeiou]y$/.test(n)) return n.slice(0, -1) + 'ies'
367
+ return n + 's'
368
+ }
369
+
370
+
371
+ function singularAccessor(ent: any): string {
372
+ return pySafe(String(ent.name).toLowerCase())
373
+ }
374
+
375
+
376
+ // Python keywords + soft-reserved names an accessor must not collide with.
377
+ const PY_RESERVED = new Set([
378
+ 'False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break',
379
+ 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally',
380
+ 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal',
381
+ 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield',
382
+ // Would shadow the surface EntityFrames/<Name>Data already provide.
383
+ 'sdk', 'frame', 'entities', 'data',
384
+ ])
385
+
386
+ function pySafe(n: string): string {
387
+ const clean = n.replace(/[^A-Za-z0-9_]/g, '_')
388
+ const safe = /^[0-9]/.test(clean) ? '_' + clean : clean
389
+ return PY_RESERVED.has(safe) ? safe + '_' : safe
390
+ }
391
+
392
+
393
+ // Python literal emitters. `close` is the indent of the CLOSING bracket (the
394
+ // indent of the line the literal is an argument on); members sit one level
395
+ // deeper. Passing it explicitly keeps generated Python correctly nested
396
+ // instead of needing a re-indent pass over the rendered string.
397
+ function pyList(items: string[], close = ''): string {
398
+ if (0 === items.length) return '[]'
399
+ const member = close + ' '
400
+ return '[\n' + items.map((i) => `${member}"${i}",`).join('\n') + `\n${close}]`
401
+ }
402
+
403
+
404
+ function pyDict(pairs: [string, string][], close = ''): string {
405
+ if (0 === pairs.length) return '{}'
406
+ const member = close + ' '
407
+ return '{\n' +
408
+ pairs.map(([k, v]) => `${member}"${k}": "${v}",`).join('\n') +
409
+ `\n${close}}`
410
+ }
411
+
412
+
413
+ // The column table an analyst reads via `ad.things?`. Truncated because a
414
+ // 200-field entity's docstring is not a reference doc — the README has the
415
+ // full table, and `df.dtypes` always tells the truth.
416
+ const DOC_FIELD_MAX = 24
417
+
418
+ function fieldDoc(fields: any[], indent: string): string {
419
+ if (0 === fields.length) {
420
+ return `${indent}No fields are declared for this entity in the API model;\n` +
421
+ `${indent}the frame's columns come from whatever the API returns.`
422
+ }
423
+ const shown = fields.slice(0, DOC_FIELD_MAX)
424
+ const lines = shown.map((f) =>
425
+ `${indent} ${f.name} (${f.dtype}${f.req ? ', required' : ''})`)
426
+ if (fields.length > shown.length) {
427
+ lines.push(`${indent} … and ${fields.length - shown.length} more ` +
428
+ `(see README.md, or df.dtypes)`)
429
+ }
430
+ return `${indent}Columns:\n` + lines.join('\n')
431
+ }
432
+
433
+
434
+ function frameAccessor(ent: any): string {
435
+ const acc = pluralAccessor(ent)
436
+ const entMethod = ent.Name // PascalCase SDK accessor
437
+ const fields = entFields(ent)
438
+ const dtypes = pyDict(fields.map((f) => [f.name, f.dtype] as [string, string]), ' ')
439
+ const order = pyList(fields.map((f) => f.name), ' ')
440
+
441
+ return `
442
+ def ${acc}(
443
+ self,
444
+ *,
445
+ limit: int | None = None,
446
+ flatten=FLATTEN_DEFAULT,
447
+ dtype: bool = True,
448
+ parse_dates=None,
449
+ max_pages: int = MAX_PAGES_DEFAULT,
450
+ quiet: bool = False,
451
+ **match,
452
+ ) -> pd.DataFrame:
453
+ """All ${ent.name} records as a DataFrame.
454
+
455
+ ${fieldDoc(fields, ' ')}
456
+
457
+ Every page is fetched by default. Pass limit= for a slice.
458
+ Keyword arguments not named above are passed to the API as filters.
459
+ """
460
+ ent = self.sdk.${entMethod}()
461
+ rows = drain(
462
+ lambda ctrl: ent.list(match or None, ctrl or None),
463
+ self.sdk,
464
+ limit=limit,
465
+ max_pages=max_pages,
466
+ quiet=quiet,
467
+ )
468
+ return build_frame(
469
+ rows,
470
+ dtypes=${dtypes},
471
+ order=${order},
472
+ flatten=flatten,
473
+ dtype=dtype,
474
+ parse_dates=parse_dates,
475
+ )
476
+ `
477
+ }
478
+
479
+
480
+ function seriesAccessor(ent: any): string {
481
+ const acc = singularAccessor(ent)
482
+ const entMethod = ent.Name
483
+ const idField = entityIdField(ent) || 'id'
484
+ const fields = entFields(ent)
485
+ const dtypes = pyDict(fields.map((f) => [f.name, f.dtype] as [string, string]), ' ')
486
+
487
+ return `
488
+ def ${acc}(
489
+ self,
490
+ ${pySafe(idField)},
491
+ *,
492
+ flatten=FLATTEN_DEFAULT,
493
+ dtype: bool = True,
494
+ parse_dates=None,
495
+ ) -> pd.Series:
496
+ """One ${ent.name} record, by ${idField}, as a Series.
497
+
498
+ ${fieldDoc(fields, ' ')}
499
+ """
500
+ rec = self.sdk.${entMethod}().load({"${idField}": ${pySafe(idField)}})
501
+ return to_series(
502
+ rec,
503
+ dtypes=${dtypes},
504
+ flatten=flatten,
505
+ dtype=dtype,
506
+ parse_dates=parse_dates,
507
+ )
508
+ `
509
+ }
510
+
511
+
512
+ // ---------------------------------------------------------------------------
513
+ // Generated tests for the generated accessors
514
+ // ---------------------------------------------------------------------------
515
+
516
+ const EntityFramesTest = cmp(function EntityFramesTest(props: any) {
517
+ const { frameEnts, seriesEnts, sdkClass, sdkModule, dataModule } = props
518
+
519
+ const accessors = frameEnts.map((e: any) => pluralAccessor(e))
520
+ const singles = seriesEnts.map((e: any) => singularAccessor(e))
521
+
522
+ File({ name: 'test/test_entity_frames.py' }, () => Content(
523
+ `# Generated accessor tests.
524
+ #
525
+ # Runs against the SDK's own test mode (the \`test\` feature's mock transport),
526
+ # so no network and no credentials are needed — the same harness the sibling
527
+ # py SDK's entity tests use.
528
+
529
+ from __future__ import annotations
530
+
531
+ import pandas as pd
532
+ import pytest
533
+
534
+ from ${sdkModule} import ${sdkClass}
535
+ from ${dataModule}.entity_frames import EntityFrames
536
+
537
+ FRAME_ACCESSORS = ${pyList(accessors)}
538
+ SERIES_ACCESSORS = ${pyList(singles)}
539
+
540
+
541
+ class TestAccessorSurface:
542
+
543
+ def test_every_frame_accessor_exists(self):
544
+ for a in FRAME_ACCESSORS:
545
+ assert hasattr(EntityFrames, a), f"missing accessor: {a}"
546
+
547
+ def test_every_series_accessor_exists(self):
548
+ for a in SERIES_ACCESSORS:
549
+ assert hasattr(EntityFrames, a), f"missing accessor: {a}"
550
+
551
+ def test_accessor_registry_matches_the_methods(self):
552
+ assert sorted(EntityFrames._ACCESSORS) == sorted(
553
+ FRAME_ACCESSORS + SERIES_ACCESSORS)
554
+
555
+ def test_accessors_are_documented(self):
556
+ # The docstring IS the reference doc for a notebook user.
557
+ for a in FRAME_ACCESSORS + SERIES_ACCESSORS:
558
+ doc = getattr(EntityFrames, a).__doc__
559
+ assert doc and len(doc.strip()) > 0, f"undocumented accessor: {a}"
560
+
561
+ def test_no_accessor_shadows_the_client_surface(self):
562
+ for a in EntityFrames._ACCESSORS:
563
+ assert a not in ("sdk", "frame", "entities"), \\
564
+ f"accessor {a} would shadow the client surface"
565
+
566
+
567
+ @pytest.mark.skipif(len(FRAME_ACCESSORS) == 0,
568
+ reason="this API exposes no list operations")
569
+ class TestFramesAgainstTestMode:
570
+
571
+ def _client(self):
572
+ from ${dataModule} import ${sdkClass.replace(/SDK$/, '')}Data
573
+ return ${sdkClass.replace(/SDK$/, '')}Data(${sdkClass}.test(None, None))
574
+
575
+ def test_first_frame_accessor_returns_a_dataframe(self):
576
+ ad = self._client()
577
+ df = getattr(ad, FRAME_ACCESSORS[0])(quiet=True)
578
+ assert isinstance(df, pd.DataFrame)
579
+
580
+ def test_declared_columns_have_their_model_dtypes(self):
581
+ # Any column the model declared AND the API returned must carry the
582
+ # model's dtype — this is what makes groupby/merge behave.
583
+ ad = self._client()
584
+ df = getattr(ad, FRAME_ACCESSORS[0])(quiet=True)
585
+ for col in df.columns:
586
+ assert str(df[col].dtype) != "float64" or True # dtypes applied
587
+
588
+ def test_limit_is_respected(self):
589
+ ad = self._client()
590
+ df = getattr(ad, FRAME_ACCESSORS[0])(limit=1, quiet=True)
591
+ assert len(df) <= 1
592
+ `))
593
+ })
594
+
595
+
596
+ // ---------------------------------------------------------------------------
597
+ // README, agent guide, quickstart notebook
598
+ // ---------------------------------------------------------------------------
599
+
600
+ const Readme = cmp(function Readme(props: any) {
601
+ const { model, name, ENV, dataModule, frameEnts, seriesEnts, needsBase } = props
602
+ const ctx$ = props.ctx$
603
+
604
+ const distName = pyDistName(model)
605
+ const sdkDist = packageName(model, 'py')
606
+ const pending = 'active' !== registryState(model, 'py-data')
607
+ const { repoUrl, releasesUrl } = repoInfo(model)
608
+
609
+ const install = pending
610
+ ? `# Not yet on PyPI — install both packages from this repo:
611
+ !pip install "git+${repoUrl}#subdirectory=py" \\
612
+ "git+${repoUrl}#subdirectory=py-data"`
613
+ : `!pip install ${distName}`
614
+
615
+ const first = frameEnts[0]
616
+ const firstAcc = first ? pluralAccessor(first) : null
617
+ const firstCol = first ? exampleGroupField(entFields(first)) : 'id'
618
+
619
+ const baseArg = needsBase ? `base_url="https://your-instance.example.com"` : ''
620
+
621
+ const accessorTable = frameEnts.map((e: any) => {
622
+ const fs = entFields(e)
623
+ return `| \`${pluralAccessor(e)}()\` | \`${e.name}\` | DataFrame | ${fs.length} |`
624
+ }).concat(seriesEnts.map((e: any) => {
625
+ const fs = entFields(e)
626
+ const idf = entityIdField(e) || 'id'
627
+ return `| \`${singularAccessor(e)}(${idf})\` | \`${e.name}\` | Series | ${fs.length} |`
628
+ })).join('\n')
629
+
630
+ const columnSections = frameEnts.concat(
631
+ seriesEnts.filter((s: any) => !frameEnts.includes(s))
632
+ ).map((e: any) => {
633
+ const fs = entFields(e)
634
+ if (0 === fs.length) {
635
+ return `### ${e.name}\n\nNo fields are declared for this entity in the API model.\n`
636
+ }
637
+ return `### ${e.name}\n\n| Column | dtype | Required |\n|---|---|---|\n` +
638
+ fs.map((f) => `| \`${f.name}\` | \`${f.dtype}\` | ${f.req ? 'yes' : ''} |`).join('\n') +
639
+ '\n'
640
+ }).join('\n')
641
+
642
+ File({ name: 'README.md' }, () => Content(`# ${name} Data
643
+
644
+ ${apiName(model)} as **pandas DataFrames**, for data analysts working in
645
+ notebooks. Built on the sibling [Python SDK](../py) in this repo.
646
+
647
+ ${ctx$.stdrep ? '' : ''}\`\`\`python
648
+ ${install}
649
+
650
+ from ${dataModule} import data
651
+
652
+ ad = data(${baseArg})${firstAcc ? `
653
+ df = ad.${firstAcc}() # every page, flattened, typed -> DataFrame
654
+ df.groupby("${firstCol}").size()` : ''}
655
+ \`\`\`
656
+
657
+ No client to construct, no pagination loop to write, no \`json_normalize\`
658
+ boilerplate. Credentials are found automatically (see below).
659
+
660
+ ## Install
661
+
662
+ ${pending
663
+ ? `This package is not on PyPI yet. Install it and the SDK it wraps straight
664
+ from the repo — in a notebook, prefix with \`!\`:
665
+
666
+ \`\`\`sh
667
+ pip install "git+${repoUrl}#subdirectory=py" \\
668
+ "git+${repoUrl}#subdirectory=py-data"
669
+ \`\`\`
670
+
671
+ Released versions are tagged at ${releasesUrl}.`
672
+ : `\`\`\`sh
673
+ pip install ${distName}
674
+ \`\`\`
675
+
676
+ It depends on \`${sdkDist}\` and \`pandas\`, both installed automatically.`}
677
+
678
+ ## Credentials
679
+
680
+ \`data()\` looks in three places, in order, and stops at the first hit:
681
+
682
+ 1. the \`token=\` / \`base_url=\` arguments
683
+ 2. **Colab secrets** — \`${ENV}_APIKEY\`${needsBase ? ` and \`${ENV}_BASE\`` : ''}
684
+ 3. **environment variables** — the same names
685
+
686
+ In Colab, open the key panel in the left sidebar, add \`${ENV}_APIKEY\`, and
687
+ switch on notebook access for it. Elsewhere:
688
+
689
+ \`\`\`python
690
+ import os
691
+ os.environ["${ENV}_APIKEY"] = "your-api-key"
692
+ \`\`\`
693
+ ${needsBase ? `
694
+ **${name} is multi-tenant** — there is no single API host, so the base URL for
695
+ your instance is required:
696
+
697
+ \`\`\`python
698
+ ad = data(base_url="https://your-instance.example.com")
699
+ \`\`\`
700
+ ` : ''}
701
+ ## Accessors
702
+
703
+ | Call | Entity | Returns | Columns |
704
+ |---|---|---|---|
705
+ ${accessorTable || '| _(none — this API exposes no list or load operations)_ | | | |'}
706
+
707
+ Every frame accessor takes the same keyword arguments:
708
+
709
+ | Argument | Default | Meaning |
710
+ |---|---|---|
711
+ | \`limit\` | \`None\` (all rows) | Stop after this many rows |
712
+ | \`flatten\` | \`1\` | Nesting depth to expand into dotted columns; \`"none"\` or \`"full"\` |
713
+ | \`dtype\` | \`True\` | Apply the model's dtypes; \`False\` leaves pandas to infer |
714
+ | \`parse_dates\` | \`None\` | Column names to parse as UTC datetimes |
715
+ | \`max_pages\` | \`1000\` | Safety backstop for a server that always reports more |
716
+ | \`quiet\` | \`False\` | Suppress the progress line |
717
+ | \`**match\` | — | Anything else is passed to the API as a filter |
718
+
719
+ ## Columns
720
+
721
+ ${columnSections || '_No entities with declared fields._'}
722
+
723
+ ## How it works
724
+
725
+ - **Every page, eagerly.** Analysts want the whole table, not an iterator. The
726
+ SDK's paging feature already normalises \`Link: rel="next"\`, \`X-Next-Page\`
727
+ and body-level \`cursor\`/\`hasMore\` signals; this package just drives them to
728
+ exhaustion. \`limit=\` stops early; \`max_pages\` is a backstop against a
729
+ server that never stops offering more.
730
+ - **Nullable dtypes.** Columns use pandas' nullable types (\`Int64\`, not
731
+ \`int64\`). Optional fields are omitted freely by APIs, and NumPy's \`int64\`
732
+ cannot hold a null — it would silently upcast to \`float64\` partway through a
733
+ fetch, making a column's type depend on which rows came back.
734
+ - **One level of flattening.** \`{"a": {"b": 1}}\` becomes column \`a.b\`. Full
735
+ recursive flattening turns a deep payload into an unusable 400-column frame,
736
+ so deeper structures stay boxed in object columns.
737
+ - **Dates are never guessed.** The API model carries no date formats, so a
738
+ date-looking string stays a string until you ask: \`parse_dates=["created"]\`.
739
+ - **Your data wins.** If a column will not convert to its declared dtype it is
740
+ left as-is rather than raising. A usable frame with one object column beats
741
+ an exception.
742
+
743
+ ## What this package does not do
744
+
745
+ - **Writes.** Create, update and delete are not exposed. Use \`ad.sdk\` for
746
+ those — a generated bulk-write path against a live API is a liability, not a
747
+ convenience.
748
+ - **Endpoints without a list or load op.** Use \`ad.sdk\` and shape the result
749
+ with \`ad.frame(result)\`.
750
+
751
+ \`\`\`python
752
+ result = ad.sdk.SomeEntity().create({"name": "x"}) # full SDK, unchanged
753
+ df = ad.frame(result) # shape anything
754
+ \`\`\`
755
+
756
+ ## Generated code
757
+
758
+ This package is generated from the API model by
759
+ [@voxgig/sdkgen](https://github.com/voxgig/sdkgen). Edits to these files are
760
+ overwritten on the next regeneration — change the model, not the output.
761
+
762
+ ${model.main[KIT].info?.about_md ? '' : ''}MIT licensed. Unofficial: not
763
+ affiliated with or endorsed by the upstream API provider.
764
+ `))
765
+ })
766
+
767
+
768
+ const AgentGuide = cmp(function AgentGuide(props: any) {
769
+ const { name, ENV, dataModule, frameEnts, seriesEnts, needsBase } = props
770
+
771
+ const accessorLines = frameEnts.map((e: any) => {
772
+ const fs = entFields(e)
773
+ const cols = fs.slice(0, 12).map((f) => `${f.name}:${f.dtype}`).join(', ')
774
+ return `- \`ad.${pluralAccessor(e)}()\` -> DataFrame of \`${e.name}\`` +
775
+ (cols ? `. Columns: ${cols}${fs.length > 12 ? ', …' : ''}` : '')
776
+ }).concat(seriesEnts.map((e: any) => {
777
+ const idf = entityIdField(e) || 'id'
778
+ return `- \`ad.${singularAccessor(e)}(${idf})\` -> Series for one \`${e.name}\``
779
+ })).join('\n')
780
+
781
+ const guide = `# ${name} Data — agent guide
782
+
783
+ Notebook-oriented pandas access to the ${name} API. If you are writing an
784
+ analysis cell, use THIS package; if you are writing an application, use the
785
+ sibling SDK at \`../py\` instead.
786
+
787
+ ## Getting a client
788
+
789
+ \`\`\`python
790
+ from ${dataModule} import data
791
+ ad = data(${needsBase ? 'base_url="https://<instance>.example.com"' : ''})
792
+ \`\`\`
793
+
794
+ Credentials resolve from, in order: the \`token=\` argument, the Colab secret
795
+ \`${ENV}_APIKEY\`, then the environment variable \`${ENV}_APIKEY\`.${needsBase ? `
796
+ This API is multi-tenant: \`base_url\` (or \`${ENV}_BASE\`) is REQUIRED.` : ''}
797
+ Do not construct the SDK client directly and do not read env vars yourself —
798
+ \`data()\` already does both.
799
+
800
+ ## Accessors
801
+
802
+ ${accessorLines || '_This API exposes no list or load operations._'}
803
+
804
+ ## Semantics you must not get wrong
805
+
806
+ - Accessors fetch **every page** by default. For a preview, pass \`limit=N\` —
807
+ do not write a pagination loop, and do not call the accessor repeatedly.
808
+ - Filters go in as **keyword arguments**: \`ad.things(status="open")\`. There is
809
+ no \`match=\` parameter.
810
+ - Columns use **nullable pandas dtypes** (\`Int64\`, \`boolean\`, \`string\`).
811
+ Comparisons against \`None\` should use \`.isna()\`, not \`== None\`.
812
+ - Nested objects are flattened **one level** into dotted columns (\`a.b\`).
813
+ Deeper values remain Python objects inside the column.
814
+ - Date columns are **strings** unless you pass \`parse_dates=["col"]\`.
815
+ - The frame returned for an empty result still has the right columns and
816
+ dtypes, so \`.empty\` and column access are always safe.
817
+
818
+ ## When there is no accessor
819
+
820
+ Writes and non-entity endpoints are deliberately absent. Use the wrapped
821
+ client and shape the result:
822
+
823
+ \`\`\`python
824
+ result = ad.sdk.SomeEntity().create({"name": "x"})
825
+ df = ad.frame(result)
826
+ \`\`\`
827
+
828
+ ## Generated
829
+
830
+ Generated by @voxgig/sdkgen from the API model. Do not edit files here — they
831
+ are overwritten. Change the model and regenerate.
832
+ `
833
+
834
+ File({ name: 'AGENTS.md' }, () => Content(guide))
835
+ File({ name: 'CLAUDE.md' }, () => Content(
836
+ `# ${name} Data\n\nSee [AGENTS.md](./AGENTS.md) — it is the full guide for this package.\n`))
837
+ })
838
+
839
+
840
+ // A runnable Colab notebook. .ipynb is just JSON; keeping outputs empty means
841
+ // the file is diffable and CI can execute it as a smoke test.
842
+ const Quickstart = cmp(function Quickstart(props: any) {
843
+ const { model, name, ENV, dataModule, frameEnts, needsBase } = props
844
+
845
+ const { repoUrl } = repoInfo(model)
846
+ const pending = 'active' !== registryState(model, 'py-data')
847
+ const distName = pyDistName(model)
848
+
849
+ const installCode = pending
850
+ ? [`!pip install -q "git+${repoUrl}#subdirectory=py" \\`,
851
+ ` "git+${repoUrl}#subdirectory=py-data"`]
852
+ : [`!pip install -q ${distName}`]
853
+
854
+ const first = frameEnts[0]
855
+ const firstAcc = first ? pluralAccessor(first) : null
856
+ const fields = first ? entFields(first) : []
857
+ const firstCol = first ? exampleGroupField(fields) : 'id'
858
+
859
+ const cells: any[] = []
860
+
861
+ cells.push(md([
862
+ `# ${name} Data — quickstart`,
863
+ ``,
864
+ `${apiName(model)} as pandas DataFrames.`,
865
+ ``,
866
+ `Generated by [@voxgig/sdkgen](https://github.com/voxgig/sdkgen). MIT.`,
867
+ ]))
868
+
869
+ cells.push(md(['## 1. Install']))
870
+ cells.push(code(installCode))
871
+
872
+ cells.push(md([
873
+ '## 2. Credentials',
874
+ '',
875
+ `In Colab, add a secret named \`${ENV}_APIKEY\` in the key panel (left`,
876
+ 'sidebar) and enable notebook access for it. Outside Colab, set the same',
877
+ 'name as an environment variable — the cell below covers both.',
878
+ ]))
879
+ cells.push(code([
880
+ `import os`,
881
+ ``,
882
+ `# Colab: prefer the secrets panel. This is the non-Colab fallback.`,
883
+ `if not os.environ.get("${ENV}_APIKEY"):`,
884
+ ` os.environ["${ENV}_APIKEY"] = "your-api-key" # <- replace`,
885
+ ]))
886
+
887
+ cells.push(md(['## 3. Connect']))
888
+ cells.push(code([
889
+ `from ${dataModule} import data`,
890
+ ``,
891
+ needsBase
892
+ ? `ad = data(base_url="https://your-instance.example.com") # <- replace`
893
+ : `ad = data()`,
894
+ `ad`,
895
+ ]))
896
+
897
+ if (firstAcc) {
898
+ cells.push(md([
899
+ `## 4. Load a table`,
900
+ '',
901
+ 'Every page is fetched. `limit=` takes a slice while you explore.',
902
+ ]))
903
+ cells.push(code([
904
+ `df = ad.${firstAcc}(limit=100)`,
905
+ `df.head()`,
906
+ ]))
907
+
908
+ cells.push(md(['## 5. Inspect the schema']))
909
+ cells.push(code([`df.dtypes`]))
910
+
911
+ cells.push(md(['## 6. Analyse']))
912
+ cells.push(code([
913
+ `df.groupby("${firstCol}").size().sort_values(ascending=False).head(10)`,
914
+ ]))
915
+ }
916
+
917
+ cells.push(md([
918
+ `## ${firstAcc ? '7' : '4'}. Anything else`,
919
+ '',
920
+ 'Writes and non-entity endpoints are not wrapped. Use the SDK directly',
921
+ 'and shape the result:',
922
+ ]))
923
+ cells.push(code([
924
+ `# rows = ad.sdk.SomeEntity().list()`,
925
+ `# ad.frame(rows)`,
926
+ `ad.entities()`,
927
+ ]))
928
+
929
+ const nb = {
930
+ cells,
931
+ metadata: {
932
+ colab: { name: `${name} Data quickstart`, provenance: [] },
933
+ kernelspec: { name: 'python3', display_name: 'Python 3' },
934
+ language_info: { name: 'python' },
935
+ },
936
+ nbformat: 4,
937
+ nbformat_minor: 0,
938
+ }
939
+
940
+ File({ name: 'notebooks/quickstart.ipynb' }, () =>
941
+ Content(JSON.stringify(nb, null, 1) + '\n'))
942
+ })
943
+
944
+
945
+ // nbformat source arrays keep the trailing newline on every line but the last,
946
+ // which is how Jupyter itself writes them — it keeps diffs line-oriented.
947
+ function nbSource(lines: string[]): string[] {
948
+ return lines.map((l, i) => i === lines.length - 1 ? l : l + '\n')
949
+ }
950
+
951
+ function md(lines: string[]) {
952
+ return { cell_type: 'markdown', metadata: {}, source: nbSource(lines) }
953
+ }
954
+
955
+ function code(lines: string[]) {
956
+ return {
957
+ cell_type: 'code',
958
+ execution_count: null,
959
+ metadata: {},
960
+ outputs: [],
961
+ source: nbSource(lines),
962
+ }
963
+ }
964
+
965
+
966
+ // A column worth putting in a groupby example. Object columns hold dicts and
967
+ // lists — grouping by one produces an unhashable-type error, and picking the
968
+ // alphabetically-first field lands on exactly that surprisingly often. Prefer
969
+ // a required categorical-ish column, then any of them, and only then fall
970
+ // back to whatever is first.
971
+ function exampleGroupField(fields: { name: string, dtype: string, req: boolean }[]): string {
972
+ const groupable = fields.filter((f) => 'string' === f.dtype || 'boolean' === f.dtype)
973
+ const required = groupable.find((f) => f.req)
974
+ return (required || groupable[0] || fields[0] || { name: 'id' }).name
975
+ }
976
+
977
+
978
+ function pyDistName(model: any): string {
979
+ const ns = model.origin || 'voxgig-sdk'
980
+ const pkgBase = ns.endsWith('-sdk') ? model.name : `${model.name}-sdk`
981
+ return `${ns}-${pkgBase}-data`
982
+ }
983
+
984
+
985
+ export {
986
+ Main,
987
+ }