@voxgig/sdkgen 3.5.0 → 3.6.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 (171) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/ExternalTarget.js +1 -1
  3. package/dist/cmp/ExternalTarget.js.map +1 -1
  4. package/dist/helpers/naming.d.ts +4 -1
  5. package/dist/helpers/naming.js +119 -5
  6. package/dist/helpers/naming.js.map +1 -1
  7. package/dist/helpers/opShape.js +72 -0
  8. package/dist/helpers/opShape.js.map +1 -1
  9. package/dist/helpers/packageMeta.d.ts +2 -1
  10. package/dist/helpers/packageMeta.js +57 -20
  11. package/dist/helpers/packageMeta.js.map +1 -1
  12. package/dist/sdkgen.d.ts +3 -3
  13. package/dist/sdkgen.js +61 -18
  14. package/dist/sdkgen.js.map +1 -1
  15. package/dist/testkit.d.ts +39 -0
  16. package/dist/testkit.js +663 -0
  17. package/dist/testkit.js.map +1 -0
  18. package/dist/tsconfig.tsbuildinfo +1 -1
  19. package/package.json +32 -8
  20. package/project/.sdk/src/cmp/c/Test_c.ts +5 -7
  21. package/project/.sdk/src/cmp/clojure/Test_clojure.ts +3 -7
  22. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -7
  23. package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
  24. package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -7
  25. package/project/.sdk/src/cmp/dart/Test_dart.ts +3 -4
  26. package/project/.sdk/src/cmp/elixir/Package_elixir.ts +1 -1
  27. package/project/.sdk/src/cmp/elixir/Test_elixir.ts +5 -6
  28. package/project/.sdk/src/cmp/go/EntityTypes_go.ts +1 -1
  29. package/project/.sdk/src/cmp/go/Entity_go.ts +1 -1
  30. package/project/.sdk/src/cmp/go/Gitignore_go.ts +6 -0
  31. package/project/.sdk/src/cmp/go/Main_go.ts +2 -2
  32. package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
  33. package/project/.sdk/src/cmp/go/ReadmeExamplesTest_go.ts +59 -6
  34. package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +1 -1
  35. package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +1 -1
  36. package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +1 -1
  37. package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +1 -1
  38. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +1 -1
  39. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +1 -1
  40. package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +1 -1
  41. package/project/.sdk/src/cmp/go/Test_go.ts +6 -8
  42. package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +6 -2
  43. package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +6 -2
  44. package/project/.sdk/src/cmp/java/Test_java.ts +4 -7
  45. package/project/.sdk/src/cmp/js/Package_js.ts +1 -1
  46. package/project/.sdk/src/cmp/js/Test_js.ts +4 -7
  47. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +4 -7
  48. package/project/.sdk/src/cmp/lean/Test_lean.ts +3 -3
  49. package/project/.sdk/src/cmp/lua/Package_lua.ts +2 -2
  50. package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -7
  51. package/project/.sdk/src/cmp/ocaml/Test_ocaml.ts +3 -7
  52. package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
  53. package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -7
  54. package/project/.sdk/src/cmp/php/EntityTypes_php.ts +13 -2
  55. package/project/.sdk/src/cmp/php/Package_php.ts +2 -2
  56. package/project/.sdk/src/cmp/php/Test_php.ts +5 -7
  57. package/project/.sdk/src/cmp/py/Package_py.ts +2 -2
  58. package/project/.sdk/src/cmp/py/Test_py.ts +5 -7
  59. package/project/.sdk/src/cmp/py-data/Main_py-data.ts +14 -19
  60. package/project/.sdk/src/cmp/rb/Package_rb.ts +3 -3
  61. package/project/.sdk/src/cmp/rb/ReadmeInstall_rb.ts +1 -1
  62. package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -7
  63. package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -7
  64. package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -2
  65. package/project/.sdk/src/cmp/seneca-provider/Gitignore_seneca-provider.ts +161 -0
  66. package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +20 -11
  67. package/project/.sdk/src/cmp/swift/Test_swift.ts +5 -7
  68. package/project/.sdk/src/cmp/ts/Package_ts.ts +1 -1
  69. package/project/.sdk/src/cmp/ts/Test_ts.ts +8 -7
  70. package/project/.sdk/src/cmp/zig/Test_zig.ts +3 -7
  71. package/project/.sdk/tm/c/utility/make_point.c +64 -3
  72. package/project/.sdk/tm/clojure/src/sdk/core.clj +49 -14
  73. package/project/.sdk/tm/cpp/utility/pipeline.hpp +55 -3
  74. package/project/.sdk/tm/csharp/feature/TestFeature.cs +36 -2
  75. package/project/.sdk/tm/csharp/utility/MakePoint.cs +65 -2
  76. package/project/.sdk/tm/dart/lib/feature/test/TestFeature.dart +34 -4
  77. package/project/.sdk/tm/dart/lib/utility/MakePointUtility.dart +48 -2
  78. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +70 -10
  79. package/project/.sdk/tm/go/feature/test_feature.go +34 -2
  80. package/project/.sdk/tm/go/utility/make_point.go +66 -2
  81. package/project/.sdk/tm/java/feature/TestFeature.java +42 -2
  82. package/project/.sdk/tm/java/utility/MakePoint.java +61 -2
  83. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +22 -3
  84. package/project/.sdk/tm/js/src/utility/MakePointUtility.js +72 -2
  85. package/project/.sdk/tm/kotlin/feature/TestFeature.kt +31 -2
  86. package/project/.sdk/tm/kotlin/utility/MakePoint.kt +58 -2
  87. package/project/.sdk/tm/lean/src/SdkUtility.lean +28 -0
  88. package/project/.sdk/tm/lua/utility/make_point.lua +65 -2
  89. package/project/.sdk/tm/ocaml/sdk_runtime.ml +40 -2
  90. package/project/.sdk/tm/perl/utility/make_point.pm +55 -2
  91. package/project/.sdk/tm/php/feature/TestFeature.php +30 -1
  92. package/project/.sdk/tm/php/utility/MakePoint.php +49 -1
  93. package/project/.sdk/tm/py/pkg/utility/make_point.py +56 -2
  94. package/project/.sdk/tm/rb/utility/make_point.rb +42 -2
  95. package/project/.sdk/tm/rust/utility/make_point.rs +69 -2
  96. package/project/.sdk/tm/scala/feature/TestFeature.scala +36 -1
  97. package/project/.sdk/tm/scala/utility/Make.scala +57 -3
  98. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +29 -1
  99. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +51 -2
  100. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +22 -3
  101. package/project/.sdk/tm/ts/src/utility/MakePointUtility.ts +72 -2
  102. package/project/.sdk/tm/zig/core/utility.zig +66 -3
  103. package/project/sdkgen-package.json +1 -2
  104. package/src/cmp/ExternalTarget.ts +1 -1
  105. package/src/helpers/naming.ts +129 -5
  106. package/src/helpers/opShape.ts +85 -0
  107. package/src/helpers/packageMeta.ts +59 -20
  108. package/src/sdkgen.ts +68 -20
  109. package/src/testkit.ts +863 -0
  110. package/testkit.d.ts +3 -0
  111. package/testkit.js +12 -0
  112. package/project/.sdk/model/target/haskell.aontu +0 -72
  113. package/project/.sdk/src/cmp/haskell/Config_haskell.ts +0 -98
  114. package/project/.sdk/src/cmp/haskell/Entity_haskell.ts +0 -13
  115. package/project/.sdk/src/cmp/haskell/Gitignore_haskell.ts +0 -27
  116. package/project/.sdk/src/cmp/haskell/Main_haskell.ts +0 -123
  117. package/project/.sdk/src/cmp/haskell/Package_haskell.ts +0 -60
  118. package/project/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +0 -186
  119. package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +0 -135
  120. package/project/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +0 -48
  121. package/project/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +0 -168
  122. package/project/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +0 -51
  123. package/project/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +0 -67
  124. package/project/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +0 -162
  125. package/project/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +0 -74
  126. package/project/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +0 -241
  127. package/project/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +0 -408
  128. package/project/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +0 -31
  129. package/project/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +0 -104
  130. package/project/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +0 -80
  131. package/project/.sdk/src/cmp/haskell/Test_haskell.ts +0 -300
  132. package/project/.sdk/src/cmp/haskell/tsconfig.json +0 -15
  133. package/project/.sdk/src/cmp/haskell/utility_haskell.ts +0 -166
  134. package/project/.sdk/tm/haskell/LICENSE +0 -21
  135. package/project/.sdk/tm/haskell/Makefile +0 -22
  136. package/project/.sdk/tm/haskell/VERSION +0 -1
  137. package/project/.sdk/tm/haskell/src/SdkFeatures.hs +0 -1440
  138. package/project/.sdk/tm/haskell/src/SdkHelpers.hs +0 -337
  139. package/project/.sdk/tm/haskell/src/SdkJson.hs +0 -111
  140. package/project/.sdk/tm/haskell/src/SdkRuntime.hs +0 -1219
  141. package/project/.sdk/tm/haskell/src/SdkTypes.hs +0 -195
  142. package/project/.sdk/tm/haskell/src/VoxgigStruct.hs +0 -2299
  143. package/project/.sdk/tm/haskell/src/Vregex.hs +0 -237
  144. package/project/.sdk/tm/haskell/src/feature/README.md +0 -5
  145. package/project/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
  146. package/project/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
  147. package/project/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
  148. package/project/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
  149. package/project/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
  150. package/project/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
  151. package/project/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
  152. package/project/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
  153. package/project/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
  154. package/project/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
  155. package/project/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
  156. package/project/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
  157. package/project/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
  158. package/project/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
  159. package/project/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
  160. package/project/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
  161. package/project/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
  162. package/project/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
  163. package/project/.sdk/tm/haskell/test/Harness.hs +0 -281
  164. package/project/.sdk/tm/haskell/test/Runner.hs +0 -47
  165. package/project/.sdk/tm/haskell/test/StructCorpus.hs +0 -449
  166. package/project/.sdk/tm/haskell/test/TCustomUtility.hs +0 -38
  167. package/project/.sdk/tm/haskell/test/TFeature.hs +0 -794
  168. package/project/.sdk/tm/haskell/test/TNetsim.hs +0 -50
  169. package/project/.sdk/tm/haskell/test/TPipeline.hs +0 -334
  170. package/project/.sdk/tm/haskell/test/TPrimaryUtility.hs +0 -386
  171. package/project/.sdk/tm/haskell/test/Testutil.hs +0 -59
@@ -1,135 +0,0 @@
1
-
2
- import { cmp, File, Content } from '@voxgig/sdkgen'
3
-
4
-
5
- // Emits test/TReadmeExamples.hs — a STRUCTURAL gate over every fenced
6
- // Haskell code block in the three docs that ship Haskell examples:
7
- // - the root README.md (top-level, multi-language quick start)
8
- // - the per-language haskell/README.md
9
- // - the per-language haskell/REFERENCE.md
10
- //
11
- // DESIGN NOTE vs the Python gate (ReadmeExamplesTest_py.ts): the Python
12
- // version parses AND executes every block in a seeded test-mode subprocess.
13
- // A faithful Haskell port would compile every snippet against the generated
14
- // SDK — a throwaway module per block plus a GHC invocation — a large,
15
- // environment-sensitive harness that cannot be authored safely without a build
16
- // to validate it. This gate instead validates that every documented Haskell
17
- // block is PRESENT, non-empty, and STRUCTURALLY well-formed (balanced
18
- // delimiters). Missing docs are tolerated (a run may generate a subset of
19
- // phases), and no blocks found records no failures — so this never breaks the
20
- // shared runner. Upgrading it to a true compile gate is left as a follow-up.
21
- //
22
- // The emitted module exposes `tests :: Counters -> IO ()`; the generated
23
- // SdkGenTests calls it, so it runs inside the standard Runner. The triple
24
- // backtick fence marker is built from its char code (toEnum 96) so this
25
- // component's Haskell source needs no backtick/backslash gymnastics.
26
- const ReadmeExamplesTest = cmp(function ReadmeExamplesTest(props: any) {
27
- const { target, ctx$: { model } } = props
28
-
29
- const Name = model.const.Name
30
-
31
- File({ name: 'TReadmeExamples.' + target.ext }, () => {
32
- Content(`-- ${Name} SDK — documentation Haskell-examples structural gate.
33
- --
34
- -- Validates that every fenced Haskell code block in the SDK docs (the root
35
- -- README.md, the per-language README.md, and REFERENCE.md) is present,
36
- -- non-empty, and has balanced delimiters. Missing docs are tolerated (a run
37
- -- may generate a subset of phases), so this gate never breaks the shared
38
- -- runner. See ReadmeExamplesTest_haskell for why this is a structural gate
39
- -- rather than a full compile harness. Generated by @voxgig/sdkgen.
40
-
41
- {-# LANGUAGE ScopedTypeVariables #-}
42
-
43
- module TReadmeExamples (tests) where
44
-
45
- import Control.Exception (SomeException, evaluate, try)
46
- import Control.Monad (forM_)
47
-
48
- import Testutil (Counters, runTest)
49
-
50
-
51
- -- True for the whitespace characters we trim: space, tab, carriage return.
52
- isWs :: Char -> Bool
53
- isWs ch = let n = fromEnum ch in n == 32 || n == 9 || n == 13
54
-
55
-
56
- -- Trim leading/trailing whitespace from a line.
57
- trimWs :: String -> String
58
- trimWs = f . f
59
- where f = reverse . dropWhile isWs
60
-
61
-
62
- -- Read a doc file, tolerating absence (returns "" when missing/unreadable).
63
- readDoc :: FilePath -> IO String
64
- readDoc path = do
65
- r <- try (do s <- readFile path; _ <- evaluate (length s); pure s)
66
- :: IO (Either SomeException String)
67
- pure (either (const "") id r)
68
-
69
-
70
- -- Extract the body of every fenced Haskell block from markdown. Returns
71
- -- (blocks, allClosed) where allClosed is False when a fence was opened but
72
- -- never closed before EOF — a malformed doc that must fail rather than pass
73
- -- silently on the unterminated block. The fence marker (three backticks) is
74
- -- built from its char code so this source stays backtick-free.
75
- haskellBlocks :: String -> ([String], Bool)
76
- haskellBlocks src = go (lines src)
77
- where
78
- fence3 = replicate 3 (toEnum 96 :: Char)
79
- fenceHs = fence3 ++ "haskell"
80
- go [] = ([], True)
81
- go (l : ls)
82
- | trimWs l == fenceHs =
83
- case break (\\x -> trimWs x == fence3) ls of
84
- (blk, []) -> ([unlines blk], False)
85
- (blk, _ : rest) -> let (bs, ok) = go rest in (unlines blk : bs, ok)
86
- | otherwise = go ls
87
-
88
-
89
- -- Balanced (), [], {} outside of double-quoted string literals, with each
90
- -- closer matching the most-recent opener via a stack (so "([)]" is rejected,
91
- -- not merely counted). The generated snippets never embed an escaped quote
92
- -- inside a string.
93
- balanced :: String -> Bool
94
- balanced = go False []
95
- where
96
- go inStr stack [] = not inStr && null stack
97
- go inStr stack (c : cs)
98
- | c == '"' = go (not inStr) stack cs
99
- | inStr = go inStr stack cs
100
- | c == '(' = go inStr (')' : stack) cs
101
- | c == '[' = go inStr (']' : stack) cs
102
- | c == '{' = go inStr ('}' : stack) cs
103
- | c == ')' || c == ']' || c == '}' =
104
- case stack of
105
- (top : rest) | top == c -> go inStr rest cs
106
- _ -> False
107
- | otherwise = go inStr stack cs
108
-
109
-
110
- -- The three documents held to the gate, tagged by human label.
111
- docs :: [(String, FilePath)]
112
- docs =
113
- [ ("root README", "../README.md")
114
- , ("${target.name} README", "README.md")
115
- , ("${target.name} REFERENCE", "REFERENCE.md")
116
- ]
117
-
118
-
119
- tests :: Counters -> IO ()
120
- tests c =
121
- forM_ docs (\\(label, path) -> do
122
- src <- readDoc path
123
- let (blocks, closed) = haskellBlocks src
124
- runTest c (label ++ " haskell fences closed") (pure closed)
125
- forM_ (zip [1 :: Int ..] blocks) (\\(i, blk) ->
126
- runTest c (label ++ " haskell block " ++ show i ++ " well-formed")
127
- (pure (not (null (trimWs blk)) && balanced blk))))
128
- `)
129
- })
130
- })
131
-
132
-
133
- export {
134
- ReadmeExamplesTest
135
- }
@@ -1,48 +0,0 @@
1
-
2
- import { cmp, Content } from '@voxgig/sdkgen'
3
-
4
-
5
- const ReadmeExplanation = cmp(function ReadmeExplanation(props: any) {
6
- const { target, ctx$: { model } } = props
7
-
8
- Content(`### Data as struct Values
9
-
10
- The Haskell SDK models every API record as the dynamic \`Value\` type (from
11
- the vendored \`VoxgigStruct\` module) rather than bespoke Haskell records.
12
- This mirrors the dynamic nature of the API and keeps the SDK flexible — no
13
- new datatypes or code generation are needed when the API schema changes.
14
-
15
- Build request maps with \`jo [(key, value)]\` and read fields back with
16
- \`getp value "field"\`; scalars are the \`VStr\` / \`VNum\` / \`VBool\`
17
- constructors, and \`VNoval\` stands for an absent property.
18
-
19
- ### Module structure
20
-
21
- \`\`\`
22
- ${target.name}/
23
- ├── src/
24
- │ ├── VoxgigStruct.hs -- vendored dependency-free struct library (Value)
25
- │ ├── Vregex.hs -- vendored regex support
26
- │ ├── SdkTypes.hs -- core types (Client, Entity, Feature)
27
- │ ├── SdkHelpers.hs -- helper functions (jo, getp, ...)
28
- │ ├── SdkRuntime.hs -- the generic operation pipeline
29
- │ ├── SdkFeatures.hs -- built-in features + makeEntity
30
- │ ├── SdkConfig.hs -- generated API configuration + feature factory
31
- │ └── SdkClient.hs -- generated public client (newSdk, entity accessors)
32
- ├── test/ -- test suites
33
- ├── Makefile -- stock-GHC build/test (no third-party deps)
34
- └── ${model.const.Name.toLowerCase()}-sdk.cabal -- package manifest (for Hackage)
35
- \`\`\`
36
-
37
- The public module (\`SdkClient\`) exports the SDK constructors (\`newSdk\`,
38
- \`testSdk\`) and one accessor per entity. Import \`VoxgigStruct\` for the
39
- \`Value\` constructors and \`SdkHelpers\` for \`jo\` / \`getp\`.
40
-
41
- `)
42
-
43
- })
44
-
45
-
46
- export {
47
- ReadmeExplanation
48
- }
@@ -1,168 +0,0 @@
1
-
2
- import { cmp, Content, isAuthActive, envName, canonKey, entityIdField, pickExampleEntity, opRequestShape } from '@voxgig/sdkgen'
3
-
4
- import {
5
- KIT,
6
- getModelPath,
7
- nom,
8
- } from '@voxgig/apidef'
9
-
10
- import { hsVarName } from './utility_haskell'
11
-
12
-
13
- // A type-correct Haskell `Value` literal for a field's canonical type.
14
- function hsLit(type: any): string {
15
- const k = canonKey(type)
16
- if ('INTEGER' === k || 'NUMBER' === k) return 'VNum 1'
17
- if ('BOOLEAN' === k) return 'VBool True'
18
- if ('ARRAY' === k || 'OBJECT' === k) return 'VNoval'
19
- return 'VStr "example"'
20
- }
21
-
22
-
23
- const ReadmeHowto = cmp(function ReadmeHowto(props: any) {
24
- const { target, ctx$: { model } } = props
25
-
26
- const entity = getModelPath(model, `main.${KIT}.entity`)
27
- // Pick an entity with a real op (prefer a read op). primaryOp is null only
28
- // when NO entity exposes any op (a direct()-only SDK).
29
- const { entity: exampleEntity, primaryOp } = pickExampleEntity(entity)
30
- const eName = exampleEntity ? nom(exampleEntity, 'Name') : 'Entity'
31
- const eFn = exampleEntity ? hsVarName(exampleEntity.name) : 'entity'
32
- const idF = exampleEntity ? entityIdField(exampleEntity) : null
33
- const isMatchOp = 'load' === primaryOp || 'remove' === primaryOp
34
- let testArgExpr = 'emptyMap'
35
- if (exampleEntity && isMatchOp) {
36
- testArgExpr = idF ? `jo [("${idF}", VStr "test01")]` : 'emptyMap'
37
- } else if (exampleEntity && ('create' === primaryOp || 'update' === primaryOp)) {
38
- const items = opRequestShape(exampleEntity, primaryOp).items
39
- .filter((it: any) => it.name !== idF && it.name !== 'id')
40
- const required = items.filter((it: any) => !it.optional)
41
- const chosen = required.length ? required : items.slice(0, 3)
42
- testArgExpr = `jo [${chosen.map((it: any) => `("${it.name}", ${hsLit(it.type)})`).join(', ')}]`
43
- }
44
-
45
- // The op-driven test-mode line, shown only when the SDK has an entity op.
46
- const testModeExample = primaryOp
47
- ? ` ent <- Sdk.${eFn} sdk VNoval
48
- arg <- ${testArgExpr}
49
- ctrl <- emptyMap
50
- -- Entity ops return the bare record and raise on error.
51
- ${eFn} <- Sdk.e${primaryOp.charAt(0).toUpperCase() + primaryOp.slice(1)} ent arg ctrl
52
- print ${eFn}`
53
- : ` args <- jo [("path", VStr "/api/resource"), ("method", VStr "GET")]
54
- result <- F.direct sdk args
55
- print result`
56
-
57
- const apikeyEnvLine = isAuthActive(model)
58
- ? `\n${envName(model)}_APIKEY=<your-key>`
59
- : ''
60
-
61
- Content(`### Make a direct HTTP request
62
-
63
- For endpoints not covered by entity accessors, use \`direct\` — it never
64
- raises and returns a result \`Value\` you branch on via its \`ok\` field:
65
-
66
- \`\`\`haskell
67
- import qualified SdkClient as Sdk
68
- import qualified SdkFeatures as F
69
- import VoxgigStruct (Value (..))
70
- import SdkHelpers (jo, getp)
71
-
72
- main :: IO ()
73
- main = do
74
- sdk <- Sdk.newSdk0
75
- params <- jo [("id", VStr "example")]
76
- args <- jo [("path", VStr "/api/resource/{id}"), ("method", VStr "GET"), ("params", params)]
77
- result <- F.direct sdk args
78
- ok <- getp result "ok"
79
- case ok of
80
- VBool True -> do
81
- status <- getp result "status" -- e.g. VNum 200
82
- body <- getp result "data" -- the response body
83
- print (status, body)
84
- _ -> do
85
- -- A non-2xx response carries status + data (the error body); a
86
- -- transport-level failure carries err instead.
87
- status <- getp result "status"
88
- err <- getp result "err"
89
- print (status, err)
90
- \`\`\`
91
-
92
- ### Prepare a request without sending it
93
-
94
- \`\`\`haskell
95
- import qualified SdkClient as Sdk
96
- import qualified SdkFeatures as F
97
- import VoxgigStruct (Value (..))
98
- import SdkHelpers (jo, getp)
99
-
100
- main :: IO ()
101
- main = do
102
- sdk <- Sdk.newSdk0
103
- params <- jo [("id", VStr "example")]
104
- args <- jo [("path", VStr "/api/resource/{id}"), ("method", VStr "DELETE"), ("params", params)]
105
- -- prepare returns the fetch definition and raises on error.
106
- fetchdef <- F.prepare sdk args
107
- url <- getp fetchdef "url"
108
- method <- getp fetchdef "method"
109
- print (url, method)
110
- \`\`\`
111
-
112
- ### Use test mode
113
-
114
- Create a mock client for unit testing — no server required:
115
-
116
- \`\`\`haskell
117
- import qualified SdkClient as Sdk
118
- import qualified SdkFeatures as F
119
- import VoxgigStruct (Value (..), emptyMap)
120
- import SdkHelpers (jo)
121
-
122
- main :: IO ()
123
- main = do
124
- sdk <- Sdk.testSdk0
125
- ${testModeExample}
126
- \`\`\`
127
-
128
- ### Use a custom fetch function
129
-
130
- Replace the HTTP transport with your own \`VFunc\` under \`system.fetch\`:
131
-
132
- \`\`\`haskell
133
- import qualified SdkClient as Sdk
134
- import VoxgigStruct (Value (..))
135
- import SdkHelpers (jo, jsonThunk)
136
-
137
- customClient :: IO Sdk.Client
138
- customClient = do
139
- let mockFetch = VFunc (\\_ _ _ _ -> do
140
- body <- jo [("id", VStr "mock01")]
141
- jo [("status", VNum 200), ("statusText", VStr "OK"), ("json", jsonThunk body)])
142
- sys <- jo [("fetch", mockFetch)]
143
- opts <- jo [("base", VStr "http://localhost:8080"), ("system", sys)]
144
- Sdk.newSdk opts
145
- \`\`\`
146
-
147
- ### Run live tests
148
-
149
- Create a \`.env.local\` file at the project root:
150
-
151
- \`\`\`
152
- ${envName(model)}_TEST_LIVE=TRUE${apikeyEnvLine}
153
- \`\`\`
154
-
155
- Then run the suite (stock GHC, no third-party dependencies):
156
-
157
- \`\`\`bash
158
- cd ${target.name} && make test
159
- \`\`\`
160
-
161
- `)
162
-
163
- })
164
-
165
-
166
- export {
167
- ReadmeHowto
168
- }
@@ -1,51 +0,0 @@
1
-
2
- import { cmp, Content, installCommand, isPublished, repoInfo } from '@voxgig/sdkgen'
3
-
4
-
5
- const ReadmeInstall = cmp(function ReadmeInstall(props: any) {
6
- const { target, ctx$ } = props
7
- const { model } = ctx$
8
-
9
- if (isPublished(model, target.name)) {
10
- Content(`\`\`\`bash
11
- ${installCommand(model, target.name)}
12
- \`\`\`
13
-
14
- Or build from a source checkout with the bundled Makefile (stock GHC, no
15
- third-party dependencies — only the GHC boot libraries):
16
-
17
- \`\`\`bash
18
- cd ${target.name} && make test
19
- \`\`\`
20
-
21
- `)
22
- return
23
- }
24
-
25
- // Publish pending: not yet on Hackage. Build from the git release tag or
26
- // from a local source checkout. The runtime is dependency-free, so no cabal
27
- // solve is needed — the Makefile drives stock GHC directly.
28
- const { releasesUrl } = repoInfo(model)
29
- Content(`This package is not yet published to Hackage. Install it from the GitHub
30
- release tag (\`${target.name}/vX.Y.Z\`, see [Releases](${releasesUrl})) or
31
- from a source checkout. The runtime has no third-party dependencies (only the
32
- GHC boot libraries: \`base\`, \`containers\`, \`array\`, \`time\`), so the
33
- bundled Makefile drives stock GHC with no cabal solve:
34
-
35
- \`\`\`bash
36
- cd ${target.name} && make test
37
- \`\`\`
38
-
39
- A \`.cabal\` file is also generated for use with \`cabal\`/\`stack\`:
40
-
41
- \`\`\`bash
42
- cd ${target.name} && cabal build
43
- \`\`\`
44
-
45
- `)
46
- })
47
-
48
-
49
- export {
50
- ReadmeInstall
51
- }
@@ -1,67 +0,0 @@
1
-
2
- import { cmp, Content } from '@voxgig/sdkgen'
3
-
4
- import {
5
- KIT,
6
- getModelPath,
7
- nom,
8
- } from '@voxgig/apidef'
9
-
10
- import { hsVarName } from './utility_haskell'
11
-
12
-
13
- const ReadmeIntro = cmp(function ReadmeIntro(props: any) {
14
- const { target, ctx$: { model } } = props
15
- const info = (model.main && model.main.kit && model.main.kit.info) || {}
16
- const tagline = info.tagline || ''
17
-
18
- Content(`# ${model.Name} ${target.title} SDK
19
-
20
- ${tagline}
21
-
22
- The ${target.title} SDK for the ${model.Name} API — an entity-oriented client following idiomatic Haskell conventions (pure functions, explicit \`IO\`, and the dependency-free vendored \`Value\` struct model).
23
-
24
- `)
25
-
26
- // Emphasise the entity-oriented design: the SDK surfaces the API as
27
- // capitalised, semantic Entities with a small, uniform verb set rather
28
- // than raw URL paths and query strings. The example entity accessor is
29
- // derived from the model, never hardcoded.
30
- const entity = getModelPath(model, `main.${KIT}.entity`)
31
- const exampleEntity = Object.values(entity || {})
32
- .find((e: any) => e.active !== false) as any
33
- const example = exampleEntity
34
- ? `for example \`${hsVarName(exampleEntity.name)} sdk VNoval\` — each`
35
- : 'each'
36
-
37
- // Model-driven op list — only the operations the active entities actually
38
- // expose (a read-only entity has just list+load); never claim
39
- // create/update/remove exist when no entity has them.
40
- const CANON_OPS = ['list', 'load', 'create', 'update', 'remove']
41
- const opSet = new Set<string>()
42
- Object.values(entity || {}).forEach((e: any) => {
43
- if (!e || e.active === false) return
44
- Object.keys(e.op || {}).forEach((o: string) => {
45
- if (e.op[o] && e.op[o].active !== false) opSet.add(o)
46
- })
47
- })
48
- const opNames = CANON_OPS.filter((o) => opSet.has(o))
49
- .concat([...opSet].filter((o) => !CANON_OPS.includes(o)))
50
- const opList = (opNames.length ? opNames : ['list', 'load'])
51
- .map((o) => '`e' + o.charAt(0).toUpperCase() + o.slice(1) + '`').join(', ')
52
-
53
- Content(`The SDK exposes the API as capitalised, semantic **Entities** — ${example}
54
- carrying a small, uniform set of operations (${opList}) instead of raw URL
55
- paths and query strings. You work with named resources and verbs, which
56
- keeps the cognitive load low.
57
-
58
- > Other languages, the CLI, and MCP server live alongside this one — see
59
- > the [top-level README](../README.md).
60
-
61
- `)
62
- })
63
-
64
-
65
- export {
66
- ReadmeIntro
67
- }
@@ -1,162 +0,0 @@
1
-
2
- import { cmp, each, Content, isAuthActive } from '@voxgig/sdkgen'
3
-
4
- import {
5
- KIT,
6
- getModelPath,
7
- } from '@voxgig/apidef'
8
-
9
- import { hsVarName } from './utility_haskell'
10
-
11
-
12
- const ReadmeModel = cmp(function ReadmeModel(props: any) {
13
- const { target, ctx$: { model } } = props
14
-
15
- const entity = getModelPath(model, `main.${KIT}.entity`)
16
- const entityList = each(entity).filter((e: any) => e.active !== false)
17
-
18
- // Model-driven op rows for the shared entity record interface: emit a row
19
- // only for operations at least one active entity actually exposes.
20
- const opUnion = new Set<string>()
21
- entityList.forEach((e: any) => Object.keys(e.op || {})
22
- .forEach((o: string) => { if (e.op[o] && e.op[o].active !== false) opUnion.add(o) }))
23
- const opRowDefs: Record<string, string> = {
24
- load: '| `eLoad` | `Value -> Value -> IO Entity` | Load a single entity by match criteria. Resolves to the entity. Raises on error. |',
25
- list: '| `eList` | `Value -> Value -> IO [Entity]` | List entities matching the criteria. Resolves to one entity per record. Raises on error. |',
26
- create: '| `eCreate` | `Value -> Value -> IO Entity` | Create a new entity. Resolves to the entity. Raises on error. |',
27
- update: '| `eUpdate` | `Value -> Value -> IO Entity` | Update an existing entity. Resolves to the entity. Raises on error. |',
28
- remove: '| `eRemove` | `Value -> Value -> IO Entity` | Remove an entity. Resolves to the entity, marked deleted. Raises on error. |',
29
- }
30
- const opRows = ['load', 'list', 'create', 'update', 'remove']
31
- .filter((o) => opUnion.has(o)).map((o) => opRowDefs[o]).join('\n')
32
-
33
- const apikeyOptionRow = isAuthActive(model)
34
- ? '| `apikey` | `String` | API key for authentication. |\n'
35
- : ''
36
-
37
- Content(`### Client constructors
38
-
39
- \`\`\`haskell
40
- import qualified SdkClient as Sdk
41
- import VoxgigStruct (Value (..))
42
- import SdkHelpers (jo)
43
-
44
- makeClient :: IO Sdk.Client
45
- makeClient = do
46
- opts <- jo [("base", VStr "https://api.example.com")]
47
- Sdk.newSdk opts
48
- \`\`\`
49
-
50
- \`newSdk :: Value -> IO Client\` constructs a client from an options map;
51
- \`newSdk0 :: IO Client\` is the no-argument convenience form.
52
-
53
- | Option (map key) | Type | Description |
54
- | --- | --- | --- |
55
- ${apikeyOptionRow}| \`base\` | \`String\` | Base URL of the API server. |
56
- | \`prefix\` | \`String\` | URL path prefix prepended to all requests. |
57
- | \`suffix\` | \`String\` | URL path suffix appended to all requests. |
58
- | \`headers\` | \`Value\` | Custom headers for all requests. |
59
- | \`feature\` | \`Value\` | Feature activation flags. |
60
- | \`system\` | \`Value\` | System overrides (e.g. custom \`fetch\` function). |
61
-
62
- ### Test client
63
-
64
- \`\`\`haskell
65
- client <- Sdk.testSdk testopts sdkopts
66
- \`\`\`
67
-
68
- \`testSdk :: Value -> Value -> IO Client\` constructs a test-mode client with
69
- mock transport (\`testSdk0 :: IO Client\` for the no-argument form). Pass
70
- \`VNoval\` for defaults.
71
-
72
- ### Client functions
73
-
74
- | Function | Signature | Description |
75
- | --- | --- | --- |
76
- | \`newSdk\` | \`Value -> IO Client\` | Construct a live client from options. |
77
- | \`newSdk0\` | \`IO Client\` | Construct a live client with defaults. |
78
- | \`testSdk\` | \`Value -> Value -> IO Client\` | Construct a test-mode client. |
79
- | \`prepare\` | \`Client -> Value -> IO Value\` | Build an HTTP request definition without sending. Raises on error. |
80
- | \`direct\` | \`Client -> Value -> IO Value\` | Build and send an HTTP request. Returns a result \`Value\` (branch on \`ok\`). |
81
- `)
82
-
83
- each(entityList, (ent: any) => {
84
- const eFn = hsVarName(ent.name)
85
- const article = /^[aeiou]/i.test(ent.Name) ? 'an' : 'a'
86
- Content(`| \`${eFn}\` | \`Client -> Value -> IO Entity\` | Create ${article} ${ent.Name} entity instance. |
87
- `)
88
- })
89
-
90
- Content(`
91
- ### Entity interface
92
-
93
- All entities share the same record interface (fields of the \`Entity\` type).
94
-
95
- | Field | Signature | Description |
96
- | --- | --- | --- |
97
- ${opRows}
98
- | \`eDataGet\` | \`IO Value\` | Get entity data. |
99
- | \`eDataSet\` | \`Value -> IO ()\` | Set entity data. |
100
- | \`eStream\` | \`String -> Value -> Value -> IO [Value]\` | Run an op as a lazy stream of items. |
101
- | \`eMake\` | \`IO Entity\` | Create a new instance with the same options. |
102
- | \`eName\` | \`String\` | The entity name. |
103
-
104
- ### Result shape
105
-
106
- Entity operations resolve to the ENTITY, not the raw record \u2014 \`eList\` to
107
- one entity per record \u2014 and raise on error. The record is reached through
108
- \`eDataGet\`, which returns the entity's data container. \`eRemove\` resolves to
109
- the entity marked deleted (\`eDeleted\`); it keeps the data it held. Wrap calls
110
- in \`Control.Exception.try\` to handle failures.
111
-
112
- The \`direct\` escape hatch never raises — it returns a result \`Value\`
113
- you branch on via its \`ok\` field (read with \`getp result "ok"\`):
114
-
115
- | Key | Type | Description |
116
- | --- | --- | --- |
117
- | \`ok\` | \`Bool\` | \`True\` if the HTTP status is 2xx. |
118
- | \`status\` | \`Int\` | HTTP status code. |
119
- | \`headers\` | \`Value\` | Response headers. |
120
- | \`data\` | \`Value\` | Parsed JSON response body. |
121
-
122
- On error, \`ok\` is \`False\` and \`err\` carries the error value.
123
-
124
- `)
125
-
126
- // Entities summary
127
- Content(`### Entities
128
-
129
- `)
130
- each(entityList, (ent: any) => {
131
- const fields = ent.fields || []
132
- const opnames = Object.keys(ent.op || {})
133
- const ops = ent.op || {}
134
- const points = each(ops).map((op: any) =>
135
- op.points ? each(op.points) : []
136
- ).flat()
137
- const path = points.length > 0 ? (points[0] as any).orig || '' : ''
138
-
139
- Content(`#### ${ent.Name}
140
-
141
- | Field | Description |
142
- | --- | --- |
143
- `)
144
- each(fields, (field: any) => {
145
- Content(`| \`${field.name}\` | ${field.short || ''} |
146
- `)
147
- })
148
-
149
- Content(`
150
- Operations: ${opnames.map((n: string) => n.charAt(0).toUpperCase() + n.slice(1)).join(', ')}.
151
-
152
- API path: \`${path}\`
153
-
154
- `)
155
- })
156
-
157
- })
158
-
159
-
160
- export {
161
- ReadmeModel
162
- }