@voxgig/sdkgen 1.3.17 → 1.3.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/cmp/Entity.js +5 -1
- package/dist/cmp/Entity.js.map +1 -1
- package/dist/cmp/Feature.js +2 -1
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/helpers/canonType.js +8 -3
- package/dist/helpers/canonType.js.map +1 -1
- package/dist/helpers/collectDeps.d.ts +1 -1
- package/dist/helpers/collectDeps.js +36 -13
- package/dist/helpers/collectDeps.js.map +1 -1
- package/dist/helpers/naming.d.ts +3 -1
- package/dist/helpers/naming.js +53 -0
- package/dist/helpers/naming.js.map +1 -1
- package/dist/helpers/opShape.d.ts +3 -1
- package/dist/helpers/opShape.js +87 -8
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -3
- package/dist/sdkgen.js +6 -2
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +15 -10
- package/project/.sdk/model/target/cpp.aontu +4 -4
- package/project/.sdk/model/target/go-cli.aontu +17 -17
- package/project/.sdk/model/target/go-mcp.aontu +11 -11
- package/project/.sdk/model/target/go.aontu +1 -1
- package/project/.sdk/model/target/java.aontu +4 -4
- package/project/.sdk/model/target/kotlin.aontu +4 -4
- package/project/.sdk/model/target/lean.aontu +51 -0
- package/project/.sdk/model/target/scala.aontu +4 -4
- package/project/.sdk/src/cmp/c/EntityTypes_c.ts +3 -4
- package/project/.sdk/src/cmp/c/Entity_c.ts +3 -3
- package/project/.sdk/src/cmp/c/ReadmeExamplesTest_c.ts +24 -7
- package/project/.sdk/src/cmp/c/TestDirect_c.ts +3 -3
- package/project/.sdk/src/cmp/c/TestEntity_c.ts +1 -1
- package/project/.sdk/src/cmp/clojure/Entity_clojure.ts +1 -1
- package/project/.sdk/src/cmp/clojure/ReadmeExamplesTest_clojure.ts +19 -8
- package/project/.sdk/src/cmp/cpp/EntityTypes_cpp.ts +2 -2
- package/project/.sdk/src/cmp/cpp/Entity_cpp.ts +3 -3
- package/project/.sdk/src/cmp/cpp/MainEntity_cpp.ts +2 -2
- package/project/.sdk/src/cmp/cpp/ReadmeExamplesTest_cpp.ts +136 -0
- package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +3 -3
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -0
- package/project/.sdk/src/cmp/csharp/EntityTypes_csharp.ts +2 -3
- package/project/.sdk/src/cmp/csharp/Entity_csharp.ts +3 -3
- package/project/.sdk/src/cmp/csharp/MainEntity_csharp.ts +2 -2
- package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
- package/project/.sdk/src/cmp/csharp/ReadmeExamplesTest_csharp.ts +13 -1
- package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +3 -3
- package/project/.sdk/src/cmp/dart/EntityTypes_dart.ts +2 -3
- package/project/.sdk/src/cmp/dart/Entity_dart.ts +3 -3
- package/project/.sdk/src/cmp/dart/MainEntity_dart.ts +2 -2
- package/project/.sdk/src/cmp/dart/Main_dart.ts +3 -3
- package/project/.sdk/src/cmp/dart/Package_dart.ts +1 -1
- package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +4 -4
- package/project/.sdk/src/cmp/elixir/EntityTypes_elixir.ts +2 -3
- package/project/.sdk/src/cmp/elixir/Entity_elixir.ts +1 -1
- package/project/.sdk/src/cmp/elixir/ReadmeExamplesTest_elixir.ts +21 -3
- package/project/.sdk/src/cmp/go/Config_go.ts +4 -1
- package/project/.sdk/src/cmp/go/EntityTypes_go.ts +110 -18
- package/project/.sdk/src/cmp/go/Entity_go.ts +3 -3
- package/project/.sdk/src/cmp/go/Main_go.ts +7 -6
- package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +3 -3
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
- package/project/.sdk/src/cmp/go/utility_go.ts +25 -0
- package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +9 -1
- package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +9 -1
- package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +28 -18
- package/project/.sdk/src/cmp/java/EntityTypes_java.ts +2 -3
- package/project/.sdk/src/cmp/java/Entity_java.ts +3 -3
- package/project/.sdk/src/cmp/java/MainEntity_java.ts +2 -2
- package/project/.sdk/src/cmp/java/Package_java.ts +1 -1
- package/project/.sdk/src/cmp/java/ReadmeExamplesTest_java.ts +23 -1
- package/project/.sdk/src/cmp/java/TestDirect_java.ts +3 -3
- package/project/.sdk/src/cmp/js/EntityTypes_js.ts +2 -3
- package/project/.sdk/src/cmp/js/Entity_js.ts +3 -3
- package/project/.sdk/src/cmp/js/MainEntity_js.ts +2 -2
- package/project/.sdk/src/cmp/js/Main_js.ts +2 -2
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +4 -4
- package/project/.sdk/src/cmp/kotlin/EntityTypes_kotlin.ts +2 -3
- package/project/.sdk/src/cmp/kotlin/Entity_kotlin.ts +3 -3
- package/project/.sdk/src/cmp/kotlin/MainEntity_kotlin.ts +2 -2
- package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +1 -1
- package/project/.sdk/src/cmp/kotlin/ReadmeExamplesTest_kotlin.ts +27 -2
- package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +3 -3
- package/project/.sdk/src/cmp/lean/Config_lean.ts +88 -0
- package/project/.sdk/src/cmp/lean/Entity_lean.ts +9 -0
- package/project/.sdk/src/cmp/lean/Gitignore_lean.ts +18 -0
- package/project/.sdk/src/cmp/lean/Main_lean.ts +120 -0
- package/project/.sdk/src/cmp/lean/Package_lean.ts +68 -0
- package/project/.sdk/src/cmp/lean/ReadmeEntity_lean.ts +29 -0
- package/project/.sdk/src/cmp/lean/ReadmeExplanation_lean.ts +19 -0
- package/project/.sdk/src/cmp/lean/ReadmeHowto_lean.ts +25 -0
- package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +38 -0
- package/project/.sdk/src/cmp/lean/ReadmeIntro_lean.ts +25 -0
- package/project/.sdk/src/cmp/lean/ReadmeModel_lean.ts +24 -0
- package/project/.sdk/src/cmp/lean/ReadmeOptions_lean.ts +22 -0
- package/project/.sdk/src/cmp/lean/ReadmeQuick_lean.ts +50 -0
- package/project/.sdk/src/cmp/lean/ReadmeRef_lean.ts +16 -0
- package/project/.sdk/src/cmp/lean/ReadmeTopHowto_lean.ts +16 -0
- package/project/.sdk/src/cmp/lean/ReadmeTopQuick_lean.ts +23 -0
- package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +18 -0
- package/project/.sdk/src/cmp/lean/Test_lean.ts +207 -0
- package/project/.sdk/src/cmp/lean/utility_lean.ts +78 -0
- package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +2 -3
- package/project/.sdk/src/cmp/lua/Entity_lua.ts +3 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -1
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +3 -3
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +1 -1
- package/project/.sdk/src/cmp/ocaml/ReadmeExamplesTest_ocaml.ts +29 -14
- package/project/.sdk/src/cmp/perl/Entity_perl.ts +3 -3
- package/project/.sdk/src/cmp/perl/MainEntity_perl.ts +2 -2
- package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
- package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +3 -3
- package/project/.sdk/src/cmp/php/EntityTypes_php.ts +2 -3
- package/project/.sdk/src/cmp/php/Entity_php.ts +3 -3
- package/project/.sdk/src/cmp/php/MainEntity_php.ts +2 -2
- package/project/.sdk/src/cmp/php/Package_php.ts +1 -1
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +3 -3
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +1 -1
- package/project/.sdk/src/cmp/py/EntityTypes_py.ts +2 -3
- package/project/.sdk/src/cmp/py/Entity_py.ts +3 -3
- package/project/.sdk/src/cmp/py/MainEntity_py.ts +2 -2
- package/project/.sdk/src/cmp/py/Main_py.ts +2 -2
- package/project/.sdk/src/cmp/py/Package_py.ts +1 -1
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +3 -3
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +1 -1
- package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +15 -5
- package/project/.sdk/src/cmp/rb/Entity_rb.ts +3 -3
- package/project/.sdk/src/cmp/rb/MainEntity_rb.ts +2 -2
- package/project/.sdk/src/cmp/rb/Package_rb.ts +2 -2
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +3 -3
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +1 -1
- package/project/.sdk/src/cmp/rust/EntityBase_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/EntityTypes_rust.ts +3 -4
- package/project/.sdk/src/cmp/rust/Entity_rust.ts +3 -3
- package/project/.sdk/src/cmp/rust/MainEntity_rust.ts +2 -2
- package/project/.sdk/src/cmp/rust/Package_rust.ts +1 -1
- package/project/.sdk/src/cmp/rust/ReadmeExamplesTest_rust.ts +22 -10
- package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +3 -3
- package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +2 -3
- package/project/.sdk/src/cmp/scala/Entity_scala.ts +3 -3
- package/project/.sdk/src/cmp/scala/MainEntity_scala.ts +2 -2
- package/project/.sdk/src/cmp/scala/ReadmeExamplesTest_scala.ts +22 -3
- package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +3 -3
- package/project/.sdk/src/cmp/scala/Test_scala.ts +1 -1
- package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +3 -4
- package/project/.sdk/src/cmp/swift/Entity_swift.ts +3 -3
- package/project/.sdk/src/cmp/swift/MainEntity_swift.ts +2 -2
- package/project/.sdk/src/cmp/swift/Package_swift.ts +1 -1
- package/project/.sdk/src/cmp/swift/ReadmeExamplesTest_swift.ts +18 -1
- package/project/.sdk/src/cmp/swift/TestDirect_swift.ts +1 -1
- package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +2 -3
- package/project/.sdk/src/cmp/ts/Entity_ts.ts +3 -3
- package/project/.sdk/src/cmp/ts/MainEntity_ts.ts +2 -2
- package/project/.sdk/src/cmp/ts/Main_ts.ts +2 -2
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +4 -4
- package/project/.sdk/src/cmp/zig/Entity_zig.ts +3 -3
- package/project/.sdk/src/cmp/zig/MainEntity_zig.ts +2 -2
- package/project/.sdk/src/cmp/zig/Package_zig.ts +10 -2
- package/project/.sdk/tm/cpp/test/primary_utility_test.cpp +8 -16
- package/project/.sdk/tm/csharp/test/PrimaryUtilityTest.cs +10 -28
- package/project/.sdk/tm/dart/test/primary_test.dart +7 -0
- package/project/.sdk/tm/go/feature/test_feature.go +33 -1
- package/project/.sdk/tm/go/test/primary_utility_test.go +41 -42
- package/project/.sdk/tm/go/test/runner_test.go +30 -1
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +7 -24
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +18 -1
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +8 -0
- package/project/.sdk/tm/kotlin/test/PrimaryUtilityTest.kt +7 -20
- package/project/.sdk/tm/lean/LICENSE +21 -0
- package/project/.sdk/tm/lean/VERSION +1 -0
- package/project/.sdk/tm/lean/src/SdkFeature.lean +171 -0
- package/project/.sdk/tm/lean/src/SdkFeatures.lean +602 -0
- package/project/.sdk/tm/lean/src/SdkJson.lean +134 -0
- package/project/.sdk/tm/lean/src/SdkRuntime.lean +356 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +617 -0
- package/project/.sdk/tm/lean/src/VoxgigStruct.lean +2880 -0
- package/project/.sdk/tm/lean/src/Vregex.lean +412 -0
- package/project/.sdk/tm/lean/src/feature/README.md +5 -0
- package/project/.sdk/tm/lean/src/feature/audit/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/cache/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/clienttrack/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/debug/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/idempotency/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/metrics/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/netsim/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/paging/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/proxy/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/ratelimit/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/rbac/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/retry/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/streaming/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/telemetry/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/lean/src/feature/timeout/.gitkeep +0 -0
- package/project/.sdk/tm/lean/test/StructCorpus.lean +697 -0
- package/project/.sdk/tm/lean/test/TFeature.lean +268 -0
- package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +349 -0
- package/project/.sdk/tm/lua/test/primary_utility_test.lua +10 -8
- package/project/.sdk/tm/perl/t/primary_utility.t +8 -17
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +9 -7
- package/project/.sdk/tm/py/feature/test_feature.py +28 -1
- package/project/.sdk/tm/py/test/test_primary_utility.py +13 -18
- package/project/.sdk/tm/rb/feature/test_feature.rb +24 -1
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +7 -8
- package/project/.sdk/tm/rust/tests/common/mod.rs +37 -1
- package/project/.sdk/tm/rust/tests/primary_utility_test.rs +12 -26
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +7 -8
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +18 -1
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +65 -21
- package/src/cmp/Entity.ts +5 -1
- package/src/cmp/Feature.ts +2 -1
- package/src/helpers/canonType.ts +8 -3
- package/src/helpers/collectDeps.ts +39 -12
- package/src/helpers/naming.ts +59 -0
- package/src/helpers/opShape.ts +99 -12
- package/src/sdkgen.ts +7 -3
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Content,
|
|
3
|
+
File,
|
|
4
|
+
Folder,
|
|
5
|
+
cmp,
|
|
6
|
+
each,
|
|
7
|
+
} from '@voxgig/sdkgen'
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
KIT,
|
|
11
|
+
Model,
|
|
12
|
+
getModelPath,
|
|
13
|
+
} from '@voxgig/apidef'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// True when an op has at least one endpoint point that needs no path params
|
|
17
|
+
// (a top-level operation the live smoke test can call without an id).
|
|
18
|
+
function hasParamFreePoint(op: any): boolean {
|
|
19
|
+
if (null == op) return false
|
|
20
|
+
const points = op.points || []
|
|
21
|
+
for (const pt of points) {
|
|
22
|
+
const params = ((pt.args || {}).params) || []
|
|
23
|
+
if (params.length === 0) return true
|
|
24
|
+
}
|
|
25
|
+
return false
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// Synthesize a create payload from the entity's required non-id fields.
|
|
30
|
+
function synthData(fields: any): any {
|
|
31
|
+
const o: any = {}
|
|
32
|
+
each(fields, (f: any) => {
|
|
33
|
+
if (f.req && f.name !== 'id') {
|
|
34
|
+
const t = String(f.type || '').toLowerCase()
|
|
35
|
+
o[f.name] =
|
|
36
|
+
(t.includes('number') || t.includes('integer') || t.includes('decimal')) ? 42
|
|
37
|
+
: t.includes('bool') ? true
|
|
38
|
+
: 'leantest'
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
return o
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
// The generated `test/Runner.lean` exe runs TWO lanes:
|
|
46
|
+
//
|
|
47
|
+
// - OFFLINE (always): a test-mode client seeded from the entity's generated
|
|
48
|
+
// `<Entity>TestData.json` answers operations from an in-memory store, so
|
|
49
|
+
// entity behaviour (list/load/create/load-back/remove) is verified with no
|
|
50
|
+
// server — the same guarantee the `test` feature gives the other targets.
|
|
51
|
+
// - LIVE (only when SDK_TEST_BASE is set): the same flow against a real
|
|
52
|
+
// server, exercising the curl transport end to end.
|
|
53
|
+
const Test = cmp(async function Test(props: any) {
|
|
54
|
+
const ctx$ = props.ctx$
|
|
55
|
+
const target = props.target
|
|
56
|
+
const model: Model = ctx$.model
|
|
57
|
+
|
|
58
|
+
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
59
|
+
|
|
60
|
+
// Per-entity OFFLINE (test-mode) blocks: seeded store, no server.
|
|
61
|
+
let offline = ''
|
|
62
|
+
each(entity, (e: any) => {
|
|
63
|
+
const ns = e.name.charAt(0).toUpperCase() + e.name.slice(1)
|
|
64
|
+
const Name = e.Name || (e.name.charAt(0).toUpperCase() + e.name.slice(1))
|
|
65
|
+
const ops = e.op || {}
|
|
66
|
+
if (!ops.list && !ops.load) return
|
|
67
|
+
offline += ` -- ${e.name}: offline (test-mode) entity behaviour
|
|
68
|
+
(do
|
|
69
|
+
let seedPath := "../.sdk/test/entity/${e.name}/${Name}TestData.json"
|
|
70
|
+
if !(← System.FilePath.pathExists seedPath) then
|
|
71
|
+
IO.println s!"skip - ${e.name}: no seed at {seedPath}"
|
|
72
|
+
else do
|
|
73
|
+
let seed ← SdkJson.jsonRead (← IO.FS.readFile seedPath)
|
|
74
|
+
let tclient ← Sdk.testSdk0 seed
|
|
75
|
+
let existing ← SdkRuntime.gp (← SdkRuntime.gp seed "existing") "${e.name}"
|
|
76
|
+
let ids ← keysof existing
|
|
77
|
+
-- list returns every seeded entity
|
|
78
|
+
let items ← ${ns}.list tclient (← emptyMap) (← emptyMap)
|
|
79
|
+
let n ← (match items with | .list lid => do pure (← listItems lid).size | _ => pure 0)
|
|
80
|
+
if n == ids.size then pass s!"${e.name}.list offline -> {n} seeded"
|
|
81
|
+
else fail s!"${e.name}.list offline: got {n}, want {ids.size}"
|
|
82
|
+
-- load the first seeded entity by id
|
|
83
|
+
if ids.size > 0 then do
|
|
84
|
+
let wid := ids[0]!
|
|
85
|
+
let m ← newMap #[("id", .str wid)]
|
|
86
|
+
let got ← ${ns}.load tclient m (← emptyMap)
|
|
87
|
+
let gid ← SdkRuntime.gpS got "id"
|
|
88
|
+
if gid == wid then pass s!"${e.name}.load offline (id={wid})"
|
|
89
|
+
else fail s!"${e.name}.load offline: got {gid}, want {wid}"
|
|
90
|
+
-- create -> load back -> remove, all in the store
|
|
91
|
+
let newmap ← SdkRuntime.gp (← SdkRuntime.gp seed "new") "${e.name}"
|
|
92
|
+
let nks ← keysof newmap
|
|
93
|
+
if nks.size > 0 then do
|
|
94
|
+
let payload ← SdkRuntime.gp newmap nks[0]!
|
|
95
|
+
let created ← ${ns}.create tclient payload (← emptyMap)
|
|
96
|
+
let cid ← SdkRuntime.gpS created "id"
|
|
97
|
+
if cid == "" then fail "${e.name}.create offline returned no id" else do
|
|
98
|
+
let m2 ← newMap #[("id", .str cid)]
|
|
99
|
+
let back ← ${ns}.load tclient m2 (← emptyMap)
|
|
100
|
+
if (← SdkRuntime.gpS back "id") == cid then
|
|
101
|
+
pass s!"${e.name}.create/load offline (id={cid})"
|
|
102
|
+
else fail s!"${e.name}.create/load offline mismatch"
|
|
103
|
+
let _ ← ${ns}.remove tclient m2 (← emptyMap)
|
|
104
|
+
let gone ← ${ns}.load tclient m2 (← emptyMap)
|
|
105
|
+
match gone with
|
|
106
|
+
| .map _ => fail s!"${e.name}.remove offline: still present"
|
|
107
|
+
| _ => pass s!"${e.name}.remove offline (id={cid})")
|
|
108
|
+
`
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Per-entity LIVE test blocks.
|
|
112
|
+
let blocks = ''
|
|
113
|
+
each(entity, (e: any) => {
|
|
114
|
+
const ns = e.name.charAt(0).toUpperCase() + e.name.slice(1)
|
|
115
|
+
const listOp = (e.op || {}).list
|
|
116
|
+
const createOp = (e.op || {}).create
|
|
117
|
+
const removeOp = (e.op || {}).remove
|
|
118
|
+
|
|
119
|
+
if (hasParamFreePoint(listOp)) {
|
|
120
|
+
blocks += ` -- ${e.name}: list smoke
|
|
121
|
+
(do
|
|
122
|
+
let items ← ${ns}.list client (← emptyMap) (← emptyMap)
|
|
123
|
+
match items with
|
|
124
|
+
| .list lid => pass s!"${e.name}.list -> {(← listItems lid).size} items"
|
|
125
|
+
| _ => fail "${e.name}.list did not return a list")
|
|
126
|
+
`
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Round-trip when create is param-free (creatable without an id); load and
|
|
130
|
+
// remove then use the id returned by create.
|
|
131
|
+
const loadOp = (e.op || {}).load
|
|
132
|
+
if (hasParamFreePoint(createOp) && loadOp && removeOp) {
|
|
133
|
+
const data = JSON.stringify(synthData(e.fields))
|
|
134
|
+
blocks += ` -- ${e.name}: create / load / remove round-trip
|
|
135
|
+
(do
|
|
136
|
+
let d ← SdkJson.jsonRead ${JSON.stringify(data)}
|
|
137
|
+
let created ← ${ns}.create client d (← emptyMap)
|
|
138
|
+
let nid ← SdkRuntime.gpS created "id"
|
|
139
|
+
if nid == "" then fail "${e.name}.create returned no id" else do
|
|
140
|
+
let m ← newMap #[("id", .str nid)]
|
|
141
|
+
let back ← ${ns}.load client m (← emptyMap)
|
|
142
|
+
let bid ← SdkRuntime.gpS back "id"
|
|
143
|
+
if bid == nid then pass s!"${e.name}.create/load round-trip (id={nid})"
|
|
144
|
+
else fail s!"${e.name}.load mismatch: {bid} != {nid}"
|
|
145
|
+
let _ ← ${ns}.remove client m (← emptyMap)
|
|
146
|
+
pass s!"${e.name}.remove (id={nid})")
|
|
147
|
+
`
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// The live lane nests two levels deeper than the offline lane.
|
|
152
|
+
const liveBlocks = blocks.split('\n').map((l) => l ? ' ' + l : l).join('\n')
|
|
153
|
+
|
|
154
|
+
Folder({ name: 'test' }, () => {
|
|
155
|
+
File({ name: 'Runner.' + target.ext }, () => {
|
|
156
|
+
Content(`-- ${model.const.Name} SDK test runner (generated by @voxgig/sdkgen).
|
|
157
|
+
--
|
|
158
|
+
-- OFFLINE lane (always): a test-mode client seeded from the generated entity
|
|
159
|
+
-- test data answers operations from an in-memory store — no server needed.
|
|
160
|
+
-- LIVE lane (only when SDK_TEST_BASE is set): the same flow over real HTTP.
|
|
161
|
+
|
|
162
|
+
import VoxgigStruct
|
|
163
|
+
import SdkJson
|
|
164
|
+
import SdkRuntime
|
|
165
|
+
import SdkClient
|
|
166
|
+
|
|
167
|
+
open VoxgigStruct
|
|
168
|
+
|
|
169
|
+
initialize npass : IO.Ref Nat ← IO.mkRef 0
|
|
170
|
+
initialize nfail : IO.Ref Nat ← IO.mkRef 0
|
|
171
|
+
|
|
172
|
+
def pass (msg : String) : SIO Unit := do
|
|
173
|
+
npass.modify (· + 1)
|
|
174
|
+
IO.println s!"ok - {msg}"
|
|
175
|
+
|
|
176
|
+
def fail (msg : String) : SIO Unit := do
|
|
177
|
+
nfail.modify (· + 1)
|
|
178
|
+
IO.println s!"FAIL - {msg}"
|
|
179
|
+
|
|
180
|
+
def defaultBase : String := "http://localhost:8901"
|
|
181
|
+
|
|
182
|
+
def main : IO UInt32 := do
|
|
183
|
+
let liveBase ← IO.getEnv "SDK_TEST_BASE"
|
|
184
|
+
let ctx ← mkCtx
|
|
185
|
+
let offline : SIO Unit := do
|
|
186
|
+
${offline} offline.run ctx
|
|
187
|
+
match liveBase with
|
|
188
|
+
| none => IO.println "skip - live lane (set SDK_TEST_BASE to enable)"
|
|
189
|
+
| some base => do
|
|
190
|
+
let live : SIO Unit := do
|
|
191
|
+
let opts ← newMap #[("base", .str base)]
|
|
192
|
+
let client ← Sdk.newSdk opts
|
|
193
|
+
${liveBlocks} live.run ctx
|
|
194
|
+
let p ← npass.get
|
|
195
|
+
let f ← nfail.get
|
|
196
|
+
IO.println ""
|
|
197
|
+
IO.println s!"PASS {p} FAIL {f}"
|
|
198
|
+
if f > 0 then return 1 else return 0
|
|
199
|
+
`)
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
export {
|
|
206
|
+
Test
|
|
207
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as Path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
clone,
|
|
5
|
+
walk,
|
|
6
|
+
} from '@voxgig/struct'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
function projectPath(suffix?: string): string {
|
|
10
|
+
return Path.normalize(Path.join(__dirname, '../../..', suffix ?? ''))
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// A Lean 4 string literal. Lean strings are UTF-8, so non-ASCII passes through;
|
|
15
|
+
// only the structural characters need escaping. (Backticks are ordinary in Lean
|
|
16
|
+
// strings, so the config's `body`-style transform exprs embed verbatim.)
|
|
17
|
+
function leanString(s: string): string {
|
|
18
|
+
let out = '"'
|
|
19
|
+
for (const ch of String(s)) {
|
|
20
|
+
if (ch === '"') out += '\\"'
|
|
21
|
+
else if (ch === '\\') out += '\\\\'
|
|
22
|
+
else if (ch === '\n') out += '\\n'
|
|
23
|
+
else if (ch === '\t') out += '\\t'
|
|
24
|
+
else if (ch === '\r') out += '\\r'
|
|
25
|
+
else out += ch
|
|
26
|
+
}
|
|
27
|
+
return out + '"'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
// Lean reserved words that cannot be bare identifiers.
|
|
32
|
+
const LEAN_RESERVED = new Set<string>([
|
|
33
|
+
'do', 'let', 'fun', 'match', 'with', 'if', 'then', 'else', 'by', 'end',
|
|
34
|
+
'namespace', 'section', 'open', 'import', 'def', 'partial', 'mutual',
|
|
35
|
+
'structure', 'inductive', 'class', 'instance', 'where', 'deriving', 'return',
|
|
36
|
+
'try', 'catch', 'for', 'in', 'while', 'have', 'show', 'from', 'set_option',
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
// A collision-free lower-camel Lean identifier for a model name.
|
|
41
|
+
function leanVarName(name: string): string {
|
|
42
|
+
let s = String(name).replace(/[^a-zA-Z0-9_]/g, '_')
|
|
43
|
+
if (s.length === 0) {
|
|
44
|
+
s = 'x'
|
|
45
|
+
}
|
|
46
|
+
s = s.charAt(0).toLowerCase() + s.slice(1)
|
|
47
|
+
if (!/^[a-z_]/.test(s)) {
|
|
48
|
+
s = 'e_' + s
|
|
49
|
+
}
|
|
50
|
+
return LEAN_RESERVED.has(s) ? s + '_' : s
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// The lowercase-hyphenated package name (used for the lake package + repo).
|
|
55
|
+
function pkgName(model: any): string {
|
|
56
|
+
const org = (model.origin || 'voxgig-sdk').replace(/-sdk$/, '')
|
|
57
|
+
return `${org}-${model.name}-sdk`.toLowerCase().replace(/[^a-z0-9-]/g, '-')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
// Remove `$`-suffixed model annotation keys (so the embedded config is clean).
|
|
62
|
+
function clean(o: any) {
|
|
63
|
+
return walk(clone(o), (k: any, v: any, p: any) => {
|
|
64
|
+
if (null != k && k.endsWith('$')) {
|
|
65
|
+
delete p[k]
|
|
66
|
+
}
|
|
67
|
+
return v
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
clean,
|
|
74
|
+
leanString,
|
|
75
|
+
leanVarName,
|
|
76
|
+
pkgName,
|
|
77
|
+
projectPath,
|
|
78
|
+
}
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
File, Content,
|
|
22
22
|
} from '@voxgig/sdkgen'
|
|
23
23
|
|
|
24
|
-
import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
|
|
24
|
+
import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
|
|
25
25
|
|
|
26
26
|
import {
|
|
27
27
|
KIT,
|
|
@@ -53,11 +53,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
|
|
|
53
53
|
// filter, so inactive entities still get class files referencing these
|
|
54
54
|
// typed names. Filter on `name` (always present), NOT `active` — parity
|
|
55
55
|
// with the go emitter's fix.
|
|
56
|
-
const entityList =
|
|
56
|
+
const entityList = deriveEntityNames(entity)
|
|
57
57
|
// Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
|
|
58
58
|
// entity not yet named (e.g. a fieldless placeholder) would otherwise read
|
|
59
59
|
// `Name = undefined` below. Parity with the go emitter's fix.
|
|
60
|
-
entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
|
|
61
60
|
|
|
62
61
|
// Surface duplicate generated type names (two entities with the same
|
|
63
62
|
// PascalCase Name) — they would redeclare a type in statically-typed
|
|
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
|
|
|
4
4
|
import {
|
|
5
5
|
cmp, each, camelify, names,
|
|
6
6
|
File, Content, Folder, Fragment, Line, FeatureHook, Slot,
|
|
7
|
-
entityClassName,
|
|
7
|
+
entityClassName, entityCollection,
|
|
8
8
|
} from '@voxgig/sdkgen'
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -24,7 +24,7 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
24
24
|
// data-type name. The class is a module-local table (snake-cased file name),
|
|
25
25
|
// so this is used only for the local identifier — kept uniform with the other
|
|
26
26
|
// languages.
|
|
27
|
-
const entityColl =
|
|
27
|
+
const entityColl = entityCollection(model)
|
|
28
28
|
const cls = entityClassName(entity, entityColl)
|
|
29
29
|
|
|
30
30
|
const entrep = {
|
|
@@ -40,7 +40,7 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
40
40
|
|
|
41
41
|
File({ name: entity.name + '_entity.' + target.ext }, () => {
|
|
42
42
|
|
|
43
|
-
const opnames = Object.keys(entity.op)
|
|
43
|
+
const opnames = Object.keys(entity.op || {})
|
|
44
44
|
|
|
45
45
|
const opfrags =
|
|
46
46
|
(['load', 'list', 'create', 'update', 'remove']
|
|
@@ -57,7 +57,7 @@ dependencies = {
|
|
|
57
57
|
`)
|
|
58
58
|
|
|
59
59
|
const seen = new Set<string>(['lua', 'dkjson'])
|
|
60
|
-
for (const d of collectDeps(model, target.name, target.deps)) {
|
|
60
|
+
for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
|
|
61
61
|
if (seen.has(d.name)) continue
|
|
62
62
|
seen.add(d.name)
|
|
63
63
|
const v = d.source === 'target' ? (d.version || '0.0') : d.version
|
|
@@ -70,7 +70,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
70
70
|
? `\n apikey = env["${PROJECTNAME}_APIKEY"],`
|
|
71
71
|
: ''
|
|
72
72
|
|
|
73
|
-
const opnames = Object.keys(entity.op)
|
|
73
|
+
const opnames = Object.keys(entity.op || {})
|
|
74
74
|
const hasLoad = opnames.includes('load')
|
|
75
75
|
const hasList = opnames.includes('list')
|
|
76
76
|
|
|
@@ -78,8 +78,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
78
78
|
return
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const loadOp = entity.op
|
|
82
|
-
const listOp = entity.op
|
|
81
|
+
const loadOp = entity.op?.load
|
|
82
|
+
const listOp = entity.op?.list
|
|
83
83
|
|
|
84
84
|
const loadPoint = loadOp?.points?.[0]
|
|
85
85
|
const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
|
|
@@ -50,7 +50,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
50
50
|
// it only applies to entities that declare a list op. Others (e.g. Batch =
|
|
51
51
|
// create/load) have no list endpoint — make_point errors and the stream
|
|
52
52
|
// yields nothing — so skip the stream test for them.
|
|
53
|
-
const hasList = !!(entity.op && (entity.op as any)
|
|
53
|
+
const hasList = !!(entity.op && (entity.op as any)?.list)
|
|
54
54
|
|
|
55
55
|
const basicflow: ModelEntityFlow | undefined =
|
|
56
56
|
getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
|
|
@@ -45,8 +45,11 @@ let read_file (path : string) : string option =
|
|
|
45
45
|
|
|
46
46
|
let split_lines (s : string) : string list = String.split_on_char '\\n' s
|
|
47
47
|
|
|
48
|
-
(* Extract the body of every \`\`\`ocaml ... \`\`\` fenced block from markdown.
|
|
49
|
-
|
|
48
|
+
(* Extract the body of every \`\`\`ocaml ... \`\`\` fenced block from markdown.
|
|
49
|
+
* Returns (blocks, unclosed) where unclosed is true when a fence was opened
|
|
50
|
+
* but never closed before EOF — a malformed doc that must fail rather than
|
|
51
|
+
* silently drop the unterminated block. *)
|
|
52
|
+
let extract_ocaml_blocks (md : string) : string list * bool =
|
|
50
53
|
let blocks = ref [] in
|
|
51
54
|
let cur = ref [] in
|
|
52
55
|
let in_block = ref false in
|
|
@@ -61,23 +64,31 @@ let extract_ocaml_blocks (md : string) : string list =
|
|
|
61
64
|
end
|
|
62
65
|
else cur := line :: !cur)
|
|
63
66
|
(split_lines md);
|
|
64
|
-
List.rev !blocks
|
|
67
|
+
(List.rev !blocks, !in_block)
|
|
65
68
|
|
|
66
|
-
(* Balanced (), [], {} outside of string literals
|
|
67
|
-
*
|
|
69
|
+
(* Balanced (), [], {} outside of string literals, with each closer matching
|
|
70
|
+
* the most-recent opener (so "([)]" is rejected, not just counted). Naive
|
|
71
|
+
* quote toggle: the generated snippets never embed an escaped quote inside a
|
|
72
|
+
* string. *)
|
|
68
73
|
let is_balanced (block : string) : bool =
|
|
69
74
|
let in_str = ref false in
|
|
70
|
-
let
|
|
75
|
+
let stack = ref [] in
|
|
71
76
|
let ok = ref true in
|
|
72
77
|
String.iter (fun c ->
|
|
73
|
-
if
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
if !ok then
|
|
79
|
+
if c = '"' then in_str := not !in_str
|
|
80
|
+
else if not !in_str then
|
|
81
|
+
match c with
|
|
82
|
+
| '(' -> stack := ')' :: !stack
|
|
83
|
+
| '[' -> stack := ']' :: !stack
|
|
84
|
+
| '{' -> stack := '}' :: !stack
|
|
85
|
+
| ')' | ']' | '}' ->
|
|
86
|
+
(match !stack with
|
|
87
|
+
| top :: rest when top = c -> stack := rest
|
|
88
|
+
| _ -> ok := false)
|
|
89
|
+
| _ -> ())
|
|
79
90
|
block;
|
|
80
|
-
!ok && !
|
|
91
|
+
!ok && !stack = [] && not !in_str
|
|
81
92
|
|
|
82
93
|
let docs = [
|
|
83
94
|
("root README", "../README.md");
|
|
@@ -92,13 +103,17 @@ let () =
|
|
|
92
103
|
match read_file path with
|
|
93
104
|
| None -> () (* doc not generated in this run — tolerate *)
|
|
94
105
|
| Some src ->
|
|
106
|
+
let (blocks, unclosed) = extract_ocaml_blocks src in
|
|
107
|
+
(* A fence opened but never closed is a malformed doc — fail
|
|
108
|
+
* rather than silently skipping the unterminated block. *)
|
|
109
|
+
check (label ^ " ocaml fence closed") (not unclosed);
|
|
95
110
|
List.iteri (fun i block ->
|
|
96
111
|
check (label ^ " block " ^ string_of_int (i + 1) ^ " non-empty")
|
|
97
112
|
(trim block <> "");
|
|
98
113
|
check (label ^ " block " ^ string_of_int (i + 1) ^ " balanced delimiters")
|
|
99
114
|
(is_balanced block);
|
|
100
115
|
incr total)
|
|
101
|
-
|
|
116
|
+
blocks)
|
|
102
117
|
docs;
|
|
103
118
|
check "at least one ocaml code block across the docs" (!total > 0))
|
|
104
119
|
`)
|
|
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
|
|
|
4
4
|
import {
|
|
5
5
|
cmp, camelify, names,
|
|
6
6
|
File, Folder, Fragment,
|
|
7
|
-
entityClassName,
|
|
7
|
+
entityClassName, entityCollection,
|
|
8
8
|
} from '@voxgig/sdkgen'
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -22,7 +22,7 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
22
22
|
// Collision-free entity CLASS name (see entityClassName): normally
|
|
23
23
|
// `<Name>Entity`, disambiguated when it would clash with another entity's
|
|
24
24
|
// data-type name. The snake-cased source-file name is unaffected.
|
|
25
|
-
const entityColl =
|
|
25
|
+
const entityColl = entityCollection(model)
|
|
26
26
|
const cls = entityClassName(entity, entityColl)
|
|
27
27
|
|
|
28
28
|
const entrep = {
|
|
@@ -38,7 +38,7 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
38
38
|
|
|
39
39
|
File({ name: entity.name + '_entity.pm' }, () => {
|
|
40
40
|
|
|
41
|
-
const opnames = Object.keys(entity.op)
|
|
41
|
+
const opnames = Object.keys(entity.op || {})
|
|
42
42
|
|
|
43
43
|
const opfrags =
|
|
44
44
|
(['load', 'list', 'create', 'update', 'remove']
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import { cmp, Content, entityClassName } from '@voxgig/sdkgen'
|
|
3
|
+
import { cmp, Content, entityClassName, entityCollection } from '@voxgig/sdkgen'
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
KIT,
|
|
@@ -14,7 +14,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
|
|
|
14
14
|
|
|
15
15
|
// Return the collision-free class (entityClassName); the accessor METHOD
|
|
16
16
|
// name (entity.Name) is unchanged so callers still write $client->{Name}.
|
|
17
|
-
const cls = entityClassName(entity,
|
|
17
|
+
const cls = entityClassName(entity, entityCollection(model))
|
|
18
18
|
|
|
19
19
|
Content(`
|
|
20
20
|
# Canonical facade: $client->${entity.Name}->list / ->load({ 'id' => ... })
|
|
@@ -27,7 +27,7 @@ const Package = cmp(async function Package(props: any) {
|
|
|
27
27
|
// (e.g. "Voxgig::SDK::Solar" -> dist voxgig-sdk-solar).
|
|
28
28
|
const Name = model.const.Name
|
|
29
29
|
|
|
30
|
-
const deps = collectDeps(model, target.name, target.deps)
|
|
30
|
+
const deps = collectDeps(model, target.name, target.deps, ctx$.log)
|
|
31
31
|
const prereq = deps
|
|
32
32
|
.map((d: any) => ` '${d.name}' => '${d.version || '0'}',`)
|
|
33
33
|
.join('\n')
|
|
@@ -80,7 +80,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
80
80
|
? `\n 'apikey' => $env->{'${PROJECTNAME}_APIKEY'},`
|
|
81
81
|
: ''
|
|
82
82
|
|
|
83
|
-
const opnames = Object.keys(entity.op)
|
|
83
|
+
const opnames = Object.keys(entity.op || {})
|
|
84
84
|
const hasLoad = opnames.includes('load')
|
|
85
85
|
const hasList = opnames.includes('list')
|
|
86
86
|
|
|
@@ -88,8 +88,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
88
88
|
return
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
const loadOp = entity.op
|
|
92
|
-
const listOp = entity.op
|
|
91
|
+
const loadOp = entity.op?.load
|
|
92
|
+
const listOp = entity.op?.list
|
|
93
93
|
|
|
94
94
|
const loadPoint = loadOp?.points?.[0]
|
|
95
95
|
const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
File, Content, Folder,
|
|
30
30
|
} from '@voxgig/sdkgen'
|
|
31
31
|
|
|
32
|
-
import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
|
|
32
|
+
import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
|
|
33
33
|
|
|
34
34
|
import {
|
|
35
35
|
KIT,
|
|
@@ -79,11 +79,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
|
|
|
79
79
|
// filter, so inactive entities still get class files referencing these
|
|
80
80
|
// typed names. Filter on `name` (always present), NOT `active` — parity
|
|
81
81
|
// with the go emitter's fix.
|
|
82
|
-
const entityList =
|
|
82
|
+
const entityList = deriveEntityNames(entity)
|
|
83
83
|
// Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
|
|
84
84
|
// entity not yet named (e.g. a fieldless placeholder) would otherwise read
|
|
85
85
|
// `Name = undefined` below. Parity with the go emitter's fix.
|
|
86
|
-
entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
|
|
87
86
|
|
|
88
87
|
// Surface duplicate generated type names (two entities with the same
|
|
89
88
|
// PascalCase Name) — they would redeclare a type in statically-typed
|
|
@@ -4,7 +4,7 @@ import * as Path from 'node:path'
|
|
|
4
4
|
import {
|
|
5
5
|
cmp, each, camelify, names,
|
|
6
6
|
File, Content, Folder, Fragment, Line, FeatureHook, Slot,
|
|
7
|
-
entityClassName,
|
|
7
|
+
entityClassName, entityCollection,
|
|
8
8
|
} from '@voxgig/sdkgen'
|
|
9
9
|
|
|
10
10
|
import {
|
|
@@ -22,7 +22,7 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
22
22
|
// Collision-free entity CLASS name (see entityClassName): normally
|
|
23
23
|
// `<Name>Entity`, disambiguated when it would clash with another entity's
|
|
24
24
|
// data-type name. The snake-cased source-file name is unaffected.
|
|
25
|
-
const entityColl =
|
|
25
|
+
const entityColl = entityCollection(model)
|
|
26
26
|
const cls = entityClassName(entity, entityColl)
|
|
27
27
|
|
|
28
28
|
const entrep = {
|
|
@@ -38,7 +38,7 @@ const Entity = cmp(function Entity(props: any) {
|
|
|
38
38
|
|
|
39
39
|
File({ name: entity.name + '_entity.' + target.ext }, () => {
|
|
40
40
|
|
|
41
|
-
const opnames = Object.keys(entity.op)
|
|
41
|
+
const opnames = Object.keys(entity.op || {})
|
|
42
42
|
|
|
43
43
|
const opfrags =
|
|
44
44
|
(['load', 'list', 'create', 'update', 'remove']
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
import { cmp, Content, entityClassName } from '@voxgig/sdkgen'
|
|
3
|
+
import { cmp, Content, entityClassName, entityCollection } from '@voxgig/sdkgen'
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
KIT,
|
|
@@ -20,7 +20,7 @@ const MainEntity = cmp(async function MainEntity(props: any) {
|
|
|
20
20
|
|
|
21
21
|
// Collision-free entity CLASS name (entityClassName); the accessor METHOD name
|
|
22
22
|
// (below) is unchanged so callers still write $client->${entity.Name}().
|
|
23
|
-
const cls = entityClassName(entity,
|
|
23
|
+
const cls = entityClassName(entity, entityCollection(model))
|
|
24
24
|
|
|
25
25
|
// Canonical facade method name is the PascalCase entity Name
|
|
26
26
|
// (`$client->${entity.Name}()`). PHP method names are case-insensitive, so
|
|
@@ -49,7 +49,7 @@ const Package = cmp(async function Package(props: any) {
|
|
|
49
49
|
"require": {
|
|
50
50
|
"php": ">=8.2"`)
|
|
51
51
|
|
|
52
|
-
for (const d of collectDeps(model, target.name, target.deps)) {
|
|
52
|
+
for (const d of collectDeps(model, target.name, target.deps, ctx$.log)) {
|
|
53
53
|
const v = d.source === 'target' ? (d.version || '0.0') : d.version
|
|
54
54
|
Content(`,
|
|
55
55
|
"${d.name}": "^${v}"`)
|
|
@@ -70,7 +70,7 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
70
70
|
? `\n "apikey" => $env["${PROJECTNAME}_APIKEY"],`
|
|
71
71
|
: ''
|
|
72
72
|
|
|
73
|
-
const opnames = Object.keys(entity.op)
|
|
73
|
+
const opnames = Object.keys(entity.op || {})
|
|
74
74
|
const hasLoad = opnames.includes('load')
|
|
75
75
|
const hasList = opnames.includes('list')
|
|
76
76
|
|
|
@@ -78,8 +78,8 @@ const TestDirect = cmp(function TestDirect(props: any) {
|
|
|
78
78
|
return
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const loadOp = entity.op
|
|
82
|
-
const listOp = entity.op
|
|
81
|
+
const loadOp = entity.op?.load
|
|
82
|
+
const listOp = entity.op?.list
|
|
83
83
|
|
|
84
84
|
const loadPoint = loadOp?.points?.[0]
|
|
85
85
|
const loadPath = loadPoint ? normalizePathParams(loadPoint.parts || [], loadPoint?.args?.params || [], loadPoint?.rename?.param) : ''
|
|
@@ -53,7 +53,7 @@ const TestEntity = cmp(function TestEntity(props: any) {
|
|
|
53
53
|
// it only applies to entities that declare a list op. Others (e.g. Batch =
|
|
54
54
|
// create/load) have no list endpoint — make_point errors and the stream
|
|
55
55
|
// yields nothing — so skip the stream test for them.
|
|
56
|
-
const hasList = !!(entity.op && (entity.op as any)
|
|
56
|
+
const hasList = !!(entity.op && (entity.op as any)?.list)
|
|
57
57
|
|
|
58
58
|
const basicflow: ModelEntityFlow | undefined =
|
|
59
59
|
getModelPath(model, `main.${KIT}.flow.Basic${nom(entity, 'Name')}Flow`)
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
File, Content,
|
|
37
37
|
} from '@voxgig/sdkgen'
|
|
38
38
|
|
|
39
|
-
import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions } from '@voxgig/sdkgen'
|
|
39
|
+
import { canonToType, opTypeName, opRequestShape, warnEntityTypeCollisions , deriveEntityNames } from '@voxgig/sdkgen'
|
|
40
40
|
|
|
41
41
|
import {
|
|
42
42
|
KIT,
|
|
@@ -142,11 +142,10 @@ const EntityTypes = cmp(function EntityTypes(props: any) {
|
|
|
142
142
|
// filter, so inactive entities still get class files referencing these
|
|
143
143
|
// typed names. Filter on `name` (always present), NOT `active` — parity
|
|
144
144
|
// with the go emitter's fix.
|
|
145
|
-
const entityList =
|
|
145
|
+
const entityList = deriveEntityNames(entity)
|
|
146
146
|
// Derive the PascalCase Name up-front — it is set LAZILY by names(), so an
|
|
147
147
|
// entity not yet named (e.g. a fieldless placeholder) would otherwise read
|
|
148
148
|
// `Name = undefined` below. Parity with the go emitter's fix.
|
|
149
|
-
entityList.forEach((e: any) => { if (null == e.Name) names(e, e.name) })
|
|
150
149
|
|
|
151
150
|
// Surface duplicate generated type names (two entities with the same
|
|
152
151
|
// PascalCase Name) — they would redeclare a type in statically-typed
|