@voxgig/sdkgen 4.5.3 → 4.7.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.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/action/check.js +15 -0
- package/dist/action/check.js.map +1 -1
- package/dist/cmp/AgentGuide.js +1 -1
- package/dist/cmp/AgentGuide.js.map +1 -1
- package/dist/cmp/AgentGuideContent.d.ts +1 -1
- package/dist/cmp/AgentGuideContent.js +8 -2
- package/dist/cmp/AgentGuideContent.js.map +1 -1
- package/dist/cmp/Feature.js +22 -0
- package/dist/cmp/Feature.js.map +1 -1
- package/dist/cmp/FeatureDocs.d.ts +1 -1
- package/dist/cmp/FeatureDocs.js +6 -1
- package/dist/cmp/FeatureDocs.js.map +1 -1
- package/dist/cmp/ReadmeExplanation.js +3 -1
- package/dist/cmp/ReadmeExplanation.js.map +1 -1
- package/dist/cmp/ReadmeFeatures.js +1 -1
- package/dist/cmp/ReadmeFeatures.js.map +1 -1
- package/dist/cmp/ReadmeRefFeatures.js +1 -1
- package/dist/cmp/ReadmeRefFeatures.js.map +1 -1
- package/dist/cmp/TestControl.d.ts +4 -0
- package/dist/cmp/TestControl.js +51 -0
- package/dist/cmp/TestControl.js.map +1 -0
- package/dist/helpers/applicability.d.ts +6 -0
- package/dist/helpers/applicability.js +103 -0
- package/dist/helpers/applicability.js.map +1 -0
- package/dist/helpers/collectDeps.js +4 -2
- package/dist/helpers/collectDeps.js.map +1 -1
- package/dist/helpers/featureSource.d.ts +3 -1
- package/dist/helpers/featureSource.js +105 -0
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/serverVars.d.ts +2 -1
- package/dist/helpers/serverVars.js +17 -0
- package/dist/helpers/serverVars.js.map +1 -1
- package/dist/sdkgen.d.ts +5 -3
- package/dist/sdkgen.js +16 -4
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +2 -1
- package/dist/utility.js +59 -1
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aon +97 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/feature-index.aon +1 -0
- package/project/.sdk/model/feature/secrets.aon +200 -0
- package/project/.sdk/model/target/ts.aon +8 -1
- package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
- package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
- package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -6
- package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
- package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
- package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
- package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +23 -6
- package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
- package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
- package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
- package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
- package/project/.sdk/src/cmp/go/Main_go.ts +8 -3
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
- package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
- package/project/.sdk/src/cmp/java/Main_java.ts +7 -2
- package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
- package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
- package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
- package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
- package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
- package/project/.sdk/src/cmp/js/Main_js.ts +7 -2
- package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +30 -5
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
- package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
- package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
- package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
- package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
- package/project/.sdk/src/cmp/lua/Main_lua.ts +7 -2
- package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +28 -4
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
- package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
- package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
- package/project/.sdk/src/cmp/perl/Main_perl.ts +7 -2
- package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -4
- package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
- package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
- package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
- package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
- package/project/.sdk/src/cmp/php/Main_php.ts +7 -2
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +24 -5
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
- package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
- package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +7 -2
- package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
- package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
- package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/Main_rb.ts +7 -2
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +24 -5
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
- package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
- package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
- package/project/.sdk/src/cmp/rust/Main_rust.ts +7 -2
- package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +31 -4
- package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
- package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
- package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
- package/project/.sdk/src/cmp/scala/Main_scala.ts +7 -2
- package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
- package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
- package/project/.sdk/src/cmp/ts/Config_ts.ts +55 -9
- package/project/.sdk/src/cmp/ts/Main_ts.ts +67 -3
- package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
- package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +75 -8
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +36 -5
- package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
- package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
- package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
- package/project/.sdk/tm/c/utility/make_options.c +22 -0
- package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
- package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
- package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
- package/project/.sdk/tm/dart/test/utility.dart +39 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
- package/project/.sdk/tm/go/core/context.go +1 -1
- package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
- package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
- package/project/.sdk/tm/go/feature/test_feature.go +12 -5
- package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
- package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
- package/project/.sdk/tm/go/test/runner_test.go +46 -0
- package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
- package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
- package/project/.sdk/tm/go/utility/fetcher.go +2 -2
- package/project/.sdk/tm/go/utility/make_options.go +24 -5
- package/project/.sdk/tm/go/utility/make_point.go +1 -1
- package/project/.sdk/tm/go/utility/make_spec.go +17 -1
- package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
- package/project/.sdk/tm/go/utility/transform_request.go +29 -1
- package/project/.sdk/tm/go/utility/transform_response.go +20 -1
- package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
- package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
- package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
- package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
- package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
- package/project/.sdk/tm/js/test/utility.js +91 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
- package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
- package/project/.sdk/tm/lua/test/runner.lua +40 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
- package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
- package/project/.sdk/tm/perl/t/runner.pm +30 -0
- package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
- package/project/.sdk/tm/php/test/Runner.php +36 -0
- package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
- package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
- package/project/.sdk/tm/py/test/runner.py +32 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
- package/project/.sdk/tm/rb/test/runner.rb +25 -0
- package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
- package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +26 -1
- package/project/.sdk/tm/scala/utility/Make.scala +17 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +387 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +175 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +51 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
- package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
- package/project/.sdk/tm/ts/test/omni.ts +86 -0
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
- package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
- package/project/.sdk/tm/ts/test/utility.ts +122 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +694 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +244 -0
- package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
- package/project/.sdk/tm/zig/core/utility.zig +24 -1
- package/project/sdkgen-package.json +2 -1
- package/src/action/check.ts +18 -0
- package/src/cmp/AgentGuide.ts +1 -1
- package/src/cmp/AgentGuideContent.ts +9 -2
- package/src/cmp/Feature.ts +24 -0
- package/src/cmp/FeatureDocs.ts +7 -1
- package/src/cmp/ReadmeExplanation.ts +4 -1
- package/src/cmp/ReadmeFeatures.ts +1 -1
- package/src/cmp/ReadmeRefFeatures.ts +1 -1
- package/src/cmp/TestControl.ts +64 -0
- package/src/helpers/applicability.ts +123 -0
- package/src/helpers/collectDeps.ts +5 -1
- package/src/helpers/featureSource.ts +130 -0
- package/src/helpers/serverVars.ts +19 -0
- package/src/sdkgen.ts +16 -1
- package/src/utility.ts +71 -1
- package/project/.sdk/tm/ts/test/runner.ts +0 -425
|
@@ -19,7 +19,7 @@ const ReadmeRefFeatures = cmp(function ReadmeRefFeatures(props: any) {
|
|
|
19
19
|
const { target, ctx$ } = props
|
|
20
20
|
const { model } = ctx$
|
|
21
21
|
|
|
22
|
-
const features: FeatureDoc[] = featureDocs(model)
|
|
22
|
+
const features: FeatureDoc[] = featureDocs(model, target)
|
|
23
23
|
|
|
24
24
|
if (0 === features.length) {
|
|
25
25
|
return
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import Path from 'node:path'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
cmp,
|
|
5
|
+
File,
|
|
6
|
+
Fragment,
|
|
7
|
+
} from 'jostraca'
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const TEST_CONTROL_FILE = 'sdk-test-control.json'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// Keep the blanket per-target `Copy({ from: 'tm/<lang>' })` from restoring the
|
|
14
|
+
// template default over a project's edited control file. One regex for every
|
|
15
|
+
// target: the file sits under `test/`, `t/`, `tests/` or `sdktest/` depending
|
|
16
|
+
// on the language, and the name is what identifies it either way.
|
|
17
|
+
const TEST_CONTROL_EXCLUDE = new RegExp('(^|/)' + TEST_CONTROL_FILE + '$')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
// `sdk-test-control.json` is the ONE generated file a project is meant to edit
|
|
21
|
+
// by hand: it names the tests to skip, the live pacing, and the extra client
|
|
22
|
+
// options a live run needs (`test.client.options`).
|
|
23
|
+
//
|
|
24
|
+
// It shipped as part of each target's blanket `Copy({ from: 'tm/<lang>' })`,
|
|
25
|
+
// and a Copy has no per-file "leave it alone if it already exists" — so every
|
|
26
|
+
// `npm run generate` overwrote whatever the project had put there. The
|
|
27
|
+
// documented workaround, editing the template master under `.sdk/tm/`, is
|
|
28
|
+
// worse: `doctor` reports it as drift and the next `target add <lang>` reverts
|
|
29
|
+
// it.
|
|
30
|
+
//
|
|
31
|
+
// So it is emitted HERE instead, write-once: `File({ exclude: true })` returns
|
|
32
|
+
// early when the file already exists (FileOp), which is exactly the semantics
|
|
33
|
+
// the file is documented to have. A project without one yet gets the
|
|
34
|
+
// template's default; a project that has edited one keeps its edits across
|
|
35
|
+
// both `generate` and `target add`.
|
|
36
|
+
//
|
|
37
|
+
// The content still comes from the target's own template, so there remains
|
|
38
|
+
// exactly one copy of the default on disk.
|
|
39
|
+
//
|
|
40
|
+
// The template is addressed the SAME way the Copy this replaces addressed it:
|
|
41
|
+
// `tm/<lang>/...` relative to the working directory, which is the project's
|
|
42
|
+
// `.sdk`. Resolved to absolute here because Fragment resolves a RELATIVE
|
|
43
|
+
// `from` against the OUTPUT folder instead — a different base, and the one
|
|
44
|
+
// place these two could silently disagree.
|
|
45
|
+
const TestControl = cmp(function TestControl(props: {
|
|
46
|
+
target: { name: string }
|
|
47
|
+
dir: string
|
|
48
|
+
ctx$?: any
|
|
49
|
+
}) {
|
|
50
|
+
const { target, dir } = props
|
|
51
|
+
|
|
52
|
+
File({ name: TEST_CONTROL_FILE, exclude: true }, () => {
|
|
53
|
+
Fragment({
|
|
54
|
+
from: Path.resolve('tm', target.name, dir, TEST_CONTROL_FILE)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
TestControl,
|
|
62
|
+
TEST_CONTROL_FILE,
|
|
63
|
+
TEST_CONTROL_EXCLUDE,
|
|
64
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Which features a target can actually take.
|
|
2
|
+
//
|
|
3
|
+
// THE ONE PLACE THIS RULE LIVES. `Feature` (what source is copied), the
|
|
4
|
+
// per-language `Config_<lang>` and `Main_<lang>` components (what the
|
|
5
|
+
// generated registry, imports and metadata contain) and `configDefinition`
|
|
6
|
+
// (the embedded config) must all reach the same answer for one
|
|
7
|
+
// (feature, target) pair. They previously each read the raw feature map,
|
|
8
|
+
// which is how a feature with no source for a target still reached that
|
|
9
|
+
// target's imports.
|
|
10
|
+
//
|
|
11
|
+
// The shape is `docs/design/feature-tags.md`: a feature declares what it
|
|
12
|
+
// NEEDS, a target declares what it PROVIDES, and the feature applies when
|
|
13
|
+
// `needs` is a subset of `provides`. Both default to empty, so a feature
|
|
14
|
+
// that needs nothing applies everywhere — every feature that existed
|
|
15
|
+
// before this gate keeps its behaviour with no model edit, which is what
|
|
16
|
+
// makes the change additive.
|
|
17
|
+
//
|
|
18
|
+
// A named target list was the cheaper design and fails on time: it has to
|
|
19
|
+
// be edited for targets the feature's author has never heard of, so a
|
|
20
|
+
// published package starves every target added after its release. A tag is
|
|
21
|
+
// a claim about the feature that stays true as the target set grows.
|
|
22
|
+
|
|
23
|
+
import { KIT, getModelPath } from '@voxgig/apidef'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// Tags are a MAP keyed by tag name, `{ sekreto: true }` — see the schema
|
|
27
|
+
// note on `needs`. A map because aontu unifies maps by key, so a package
|
|
28
|
+
// can add one tag without restating the others; and because a defaulted
|
|
29
|
+
// list disjunction in this position hangs the unifier on a real project's
|
|
30
|
+
// model. A tag counts only when its value is true, so a tag can be turned
|
|
31
|
+
// off by overriding it rather than by deleting the key.
|
|
32
|
+
//
|
|
33
|
+
// A list is still accepted, for a hand-written model that used the shape
|
|
34
|
+
// docs/design/feature-tags.md first proposed.
|
|
35
|
+
function tags(val: any): string[] {
|
|
36
|
+
if (Array.isArray(val)) {
|
|
37
|
+
return val.filter((t: any) => 'string' === typeof t)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (null == val || 'object' !== typeof val) {
|
|
41
|
+
return []
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return Object.keys(val).filter((name: string) => true === val[name])
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// Does this feature apply to this target?
|
|
49
|
+
function featureApplies(feature: any, target: any): boolean {
|
|
50
|
+
const needs = tags(feature && feature.needs)
|
|
51
|
+
|
|
52
|
+
// The common case, and the reason this is additive: no needs, applies
|
|
53
|
+
// anywhere. Checked first so a target with no `provides` is unaffected.
|
|
54
|
+
if (0 === needs.length) {
|
|
55
|
+
return true
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const provides = tags(target && target.provides)
|
|
59
|
+
|
|
60
|
+
return needs.every((need: string) => provides.includes(need))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// The features that apply to `target`, in the same map shape
|
|
65
|
+
// `getModelPath(model, 'main.<kit>.feature')` returns — already
|
|
66
|
+
// active-filtered by getModelPath, then gated by the tags.
|
|
67
|
+
//
|
|
68
|
+
// `target` may be the target object or its name; components hold the
|
|
69
|
+
// object, `configDefinition` is only given the name.
|
|
70
|
+
function targetFeatures(model: any, target: any): Record<string, any> {
|
|
71
|
+
const feature = getModelPath(model, `main.${KIT}.feature`, { required: false }) || {}
|
|
72
|
+
|
|
73
|
+
const t = 'string' === typeof target ?
|
|
74
|
+
(getModelPath(model, `main.${KIT}.target`, { required: false }) || {})[target] :
|
|
75
|
+
target
|
|
76
|
+
|
|
77
|
+
// A name that names no target gates nothing: a caller that cannot say
|
|
78
|
+
// which target it is generating for must not silently lose features.
|
|
79
|
+
if (null == t) {
|
|
80
|
+
return feature
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Keyed off the map, not off `f.name`: the key is what every consumer
|
|
84
|
+
// indexes by, and rebuilding from a field would drop an entry whose
|
|
85
|
+
// model never set one. Order is not preserved on purpose — consumers
|
|
86
|
+
// re-sort with `each`, which is what keeps output byte-stable.
|
|
87
|
+
const applies: Record<string, any> = {}
|
|
88
|
+
for (const [name, f] of Object.entries(feature as Record<string, any>)) {
|
|
89
|
+
if (featureApplies(f, t)) {
|
|
90
|
+
applies[name] = f
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return applies
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
// THE CLOSED VOCABULARY. The schema comment says applicability tags are a
|
|
99
|
+
// closed set; without this, `provides: &: boolean` accepts any key, so a
|
|
100
|
+
// typo (`sekrreto`) compiles cleanly and silently makes the feature apply
|
|
101
|
+
// NOWHERE — the worst failure shape, because the feature simply vanishes
|
|
102
|
+
// with no diagnostic.
|
|
103
|
+
//
|
|
104
|
+
// Extended by adding a tag here AND documenting it in model/sdkgen.aon.
|
|
105
|
+
const TAGS = [
|
|
106
|
+
// A vendored sekreto port lives in this target's feature container.
|
|
107
|
+
'sekreto',
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
// The tags named by `needs`/`provides` that are not in the vocabulary.
|
|
112
|
+
function unknownTags(val: any): string[] {
|
|
113
|
+
return tags(val).filter((t: string) => !TAGS.includes(t))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
export {
|
|
118
|
+
featureApplies,
|
|
119
|
+
targetFeatures,
|
|
120
|
+
tags as featureTags,
|
|
121
|
+
unknownTags,
|
|
122
|
+
TAGS,
|
|
123
|
+
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
// need extra fields like `dep.replace` (go module replace directives) or
|
|
12
12
|
// `dep.kind` (prod/dev/peer).
|
|
13
13
|
|
|
14
|
+
import { targetFeatures } from './applicability'
|
|
15
|
+
|
|
14
16
|
import { each } from 'jostraca'
|
|
15
17
|
import { KIT, getModelPath } from '@voxgig/apidef'
|
|
16
18
|
|
|
@@ -30,7 +32,9 @@ function collectDeps(
|
|
|
30
32
|
log?: any,
|
|
31
33
|
): DepEntry[] {
|
|
32
34
|
const out: DepEntry[] = []
|
|
33
|
-
|
|
35
|
+
// Gated: a feature that does not apply to this target must not flow a
|
|
36
|
+
// dependency into its generated manifest.
|
|
37
|
+
const feature = targetFeatures(model, targetName)
|
|
34
38
|
|
|
35
39
|
// Deduplicate by package name. Two features can require the same package
|
|
36
40
|
// (or a feature and the target itself can), and every Package_<lang>.ts
|
|
@@ -260,6 +260,134 @@ function srcFeatureExcludes(model: any): RegExp[] {
|
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
|
|
263
|
+
// The same rule as srcFeatureExcludes, ONE LEVEL DEEPER: a feature's
|
|
264
|
+
// declared-but-inactive PLUGINS.
|
|
265
|
+
//
|
|
266
|
+
// A plugin is a feature's optional part — the `secrets` feature over
|
|
267
|
+
// sekreto has one per provider kind, and each carries its own platform
|
|
268
|
+
// cost (node:crypto for AWS request signing, fetch for seven HTTP vault
|
|
269
|
+
// clients). A project whose chain is `[dotenv, env]` should carry neither,
|
|
270
|
+
// and before the plugin trim it carried both, because every kind was
|
|
271
|
+
// reachable from a single import.
|
|
272
|
+
//
|
|
273
|
+
// Layout is `src/feature/<feature>/plugin/<plugin>/`, so a plugin's tree
|
|
274
|
+
// is excluded exactly as a feature's is. Only declared-but-inactive
|
|
275
|
+
// plugins are excluded: a directory the model never mentions is left
|
|
276
|
+
// alone, matching srcFeatureExcludes' treatment of `base`.
|
|
277
|
+
//
|
|
278
|
+
// Note this trims a plugin belonging to an ACTIVE feature. An inactive
|
|
279
|
+
// feature's whole tree — plugins included — is already gone via
|
|
280
|
+
// srcFeatureExcludes, so walking its plugins here would be redundant, and
|
|
281
|
+
// asking the model for them would fail on the `only_active` filter anyway.
|
|
282
|
+
// One feature's inactive-plugin patterns.
|
|
283
|
+
//
|
|
284
|
+
// Separate from pluginExcludes(model) because the two COPIES that need it
|
|
285
|
+
// see different things: Main copies the whole target tree and has the
|
|
286
|
+
// model, while the Feature component copies one feature's tree and has
|
|
287
|
+
// only that feature. Feature's copy is the one that actually matters —
|
|
288
|
+
// it runs for every ACTIVE feature, so without this an active feature's
|
|
289
|
+
// plugins were all copied regardless, and Main's exclude never saw them.
|
|
290
|
+
function pluginExcludesFor(model: any, fname: string): RegExp[] {
|
|
291
|
+
if (null == model || null == fname) {
|
|
292
|
+
return []
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// `only_active: false`, and this is the whole subtlety. The feature
|
|
296
|
+
// object a component is handed has ALREADY been filtered, so its
|
|
297
|
+
// `plugin` map contains only the ACTIVE plugins — and a function looking
|
|
298
|
+
// there for something inactive finds nothing and excludes nothing. That
|
|
299
|
+
// is exactly the trap srcFeatureExcludes documents, hit again one level
|
|
300
|
+
// down: the first cut of this read the filtered feature and silently
|
|
301
|
+
// trimmed nothing while every test passed.
|
|
302
|
+
const plugins = getModelPath(model,
|
|
303
|
+
`main.${KIT}.feature.${fname}.plugin`,
|
|
304
|
+
{ required: false, only_active: false }) || {}
|
|
305
|
+
|
|
306
|
+
const out: RegExp[] = []
|
|
307
|
+
const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
308
|
+
|
|
309
|
+
for (const pname of Object.keys(plugins).sort()) {
|
|
310
|
+
const plugin = plugins[pname]
|
|
311
|
+
if (false !== plugin?.active) continue
|
|
312
|
+
|
|
313
|
+
const declared = plugin.path || []
|
|
314
|
+
|
|
315
|
+
if (0 < declared.length) {
|
|
316
|
+
for (const one of declared) {
|
|
317
|
+
// FEATURE-RELATIVE, because jostraca tests a Copy's candidates
|
|
318
|
+
// against the path RELATIVE TO THAT COPY'S ROOT — `sekreto/
|
|
319
|
+
// provider/aws.ts`, not `src/feature/secrets/sekreto/provider/
|
|
320
|
+
// aws.ts`. The Feature component's Copy is rooted at
|
|
321
|
+
// `tm/<target>/src/feature/<feature>`, so a full declared path
|
|
322
|
+
// never matches and the trim silently does nothing.
|
|
323
|
+
//
|
|
324
|
+
// Worth stating because it is invisible: an exclude that matches
|
|
325
|
+
// nothing is indistinguishable from no exclude at all, and both
|
|
326
|
+
// the patterns and the model were correct while nothing was
|
|
327
|
+
// trimmed. Main's copy IS rooted at the target root, which is why
|
|
328
|
+
// pluginExcludes below keeps the full path.
|
|
329
|
+
const rel = String(one).replace(
|
|
330
|
+
new RegExp('^src/feature/' + esc(fname) + '/'), '')
|
|
331
|
+
const pat = esc(rel)
|
|
332
|
+
out.push(new RegExp('(^|/)' + pat.replace(/\\\/$/, '') +
|
|
333
|
+
(/\/$/.test(rel) ? '/' : '$')))
|
|
334
|
+
}
|
|
335
|
+
continue
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
out.push(new RegExp('(^|/)plugin/' + esc(pname) + '/'))
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return out
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
function pluginExcludes(model: any): RegExp[] {
|
|
346
|
+
const active = getModelPath(model, `main.${KIT}.feature`,
|
|
347
|
+
{ required: false }) || {}
|
|
348
|
+
|
|
349
|
+
const out: RegExp[] = []
|
|
350
|
+
const esc = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
351
|
+
|
|
352
|
+
for (const fname of Object.keys(active)) {
|
|
353
|
+
const all = getModelPath(model,
|
|
354
|
+
`main.${KIT}.feature.${fname}.plugin`,
|
|
355
|
+
{ required: false, only_active: false }) || {}
|
|
356
|
+
const on = getModelPath(model,
|
|
357
|
+
`main.${KIT}.feature.${fname}.plugin`,
|
|
358
|
+
{ required: false }) || {}
|
|
359
|
+
|
|
360
|
+
for (const pname of Object.keys(all)) {
|
|
361
|
+
if (null != on[pname]) continue
|
|
362
|
+
|
|
363
|
+
// DECLARED paths first. A plugin's files are often not free to
|
|
364
|
+
// move — sekreto's provider modules are vendored, and both the
|
|
365
|
+
// vendoring guard and their own relative imports pin them at
|
|
366
|
+
// upstream's directory depth — so a plugin says which paths it
|
|
367
|
+
// owns rather than being assumed to own a directory.
|
|
368
|
+
const declared = all[pname].path || []
|
|
369
|
+
|
|
370
|
+
if (0 < declared.length) {
|
|
371
|
+
for (const one of declared) {
|
|
372
|
+
const pat = esc(String(one))
|
|
373
|
+
// A trailing slash means a folder and everything under it;
|
|
374
|
+
// anything else matches that path exactly, as featureExcludes
|
|
375
|
+
// does for a file source.
|
|
376
|
+
out.push(new RegExp('(^|/)' + pat.replace(/\\\/$/, '') +
|
|
377
|
+
(/\/$/.test(String(one)) ? '/' : '$')))
|
|
378
|
+
}
|
|
379
|
+
continue
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
out.push(new RegExp(
|
|
383
|
+
'(^|/)src/feature/' + esc(fname) + '/plugin/' + esc(pname) + '/'))
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return out
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
|
|
263
391
|
export type {
|
|
264
392
|
FeatureSource,
|
|
265
393
|
FeatureEntry,
|
|
@@ -275,4 +403,6 @@ export {
|
|
|
275
403
|
featureExcludes,
|
|
276
404
|
fullsetExcludes,
|
|
277
405
|
srcFeatureExcludes,
|
|
406
|
+
pluginExcludes,
|
|
407
|
+
pluginExcludesFor,
|
|
278
408
|
}
|
|
@@ -88,6 +88,24 @@ function serverVariables(model: any): ServerVar[] {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
// The environment variable a generated LIVE test reads one server variable
|
|
92
|
+
// from: `<PROJ>_SERVER_<NAME>`, e.g. ELEMENTDEMO_SERVER_ACCOUNT_ID.
|
|
93
|
+
//
|
|
94
|
+
// A live client CANNOT BE CONSTRUCTED without every required server
|
|
95
|
+
// variable — makeOptions raises rather than issue requests to a URL with a
|
|
96
|
+
// literal `{account_id}` in it — so an SDK whose spec templates its server
|
|
97
|
+
// URL had no runnable live suite at all until the generated tests could be
|
|
98
|
+
// told the values. The apikey has had `<PROJ>_APIKEY` since the beginning;
|
|
99
|
+
// this is the same idea for the other half of "where do I point".
|
|
100
|
+
//
|
|
101
|
+
// One variable per NAME rather than one JSON blob: the names come from the
|
|
102
|
+
// spec, they are few, and a shell export per name is what a CI secret store
|
|
103
|
+
// and a .env file both hold naturally.
|
|
104
|
+
function serverVarEnv(projenvname: string, name: string): string {
|
|
105
|
+
return projenvname + '_SERVER_' + String(name).toUpperCase()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
91
109
|
// Does the model's server URL contain any {name} placeholders at all?
|
|
92
110
|
function hasServerVariables(model: any): boolean {
|
|
93
111
|
const info = model?.main?.kit?.info || model?.main?.KIT?.info
|
|
@@ -100,6 +118,7 @@ function hasServerVariables(model: any): boolean {
|
|
|
100
118
|
export {
|
|
101
119
|
serverVariables,
|
|
102
120
|
hasServerVariables,
|
|
121
|
+
serverVarEnv,
|
|
103
122
|
}
|
|
104
123
|
|
|
105
124
|
export type { ServerVar }
|
package/src/sdkgen.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { SdkGenError, requirePath, isAuthActive, resolveAuthPrefix, isHttpBasicA
|
|
|
22
22
|
configReprSetting, configDefinition, clean, rawStringLiteral } from './utility'
|
|
23
23
|
|
|
24
24
|
import { Main } from './cmp/Main'
|
|
25
|
+
import { featureApplies, targetFeatures, featureTags, unknownTags, TAGS } from './helpers/applicability'
|
|
25
26
|
import { ExternalTarget } from './cmp/ExternalTarget'
|
|
26
27
|
import { Docs, DocsItem } from './cmp/Docs'
|
|
27
28
|
import { ExternalDocs } from './cmp/ExternalDocs'
|
|
@@ -39,6 +40,7 @@ import { License } from './cmp/License'
|
|
|
39
40
|
import { Security } from './cmp/Security'
|
|
40
41
|
import { Changelog } from './cmp/Changelog'
|
|
41
42
|
import { Test } from './cmp/Test'
|
|
43
|
+
import { TestControl, TEST_CONTROL_EXCLUDE } from './cmp/TestControl'
|
|
42
44
|
import { ReadmeInstall } from './cmp/ReadmeInstall'
|
|
43
45
|
import { ReadmeQuick } from './cmp/ReadmeQuick'
|
|
44
46
|
import { ReadmeErrors } from './cmp/ReadmeErrors'
|
|
@@ -61,7 +63,7 @@ import type { DepEntry } from './helpers/collectDeps'
|
|
|
61
63
|
import { canonToType, canonToDtype, canonKey, canonScalarKey } from './helpers/canonType'
|
|
62
64
|
import { OP_SUFFIX, opTypeName, opParams, ownPoint, opActions, entityActions, entityPath, opRequestShape, entityIdField, entityDataIdField, entityOps, entityPrimaryOp, pickExampleEntity, entityClassName, entityTypeCollisions, warnEntityTypeCollisions, deriveEntityNames, entityCollection } from './helpers/opShape'
|
|
63
65
|
import { isReservedName, safeVarName, exampleVarName, phpEntityAccessor, entityCacheField, isRbCoreConstant, isRbSdkConstant, rbSafeTypeName, isSwiftSdkType, swiftSafeTypeName, isPhpReservedType, isPhpSdkClass, phpSafeTypeName, isTsReservedType, tsSafeTypeName, jsProp, jsOptProp, jsKey } from './helpers/naming'
|
|
64
|
-
import { serverVariables, hasServerVariables } from './helpers/serverVars'
|
|
66
|
+
import { serverVariables, hasServerVariables, serverVarEnv } from './helpers/serverVars'
|
|
65
67
|
import { primaryOpCall, idLiteral, matchArg, dataArg, litFor } from './helpers/opExample'
|
|
66
68
|
import type { ExampleLang } from './helpers/opExample'
|
|
67
69
|
import { liveStrict } from './helpers/testPolicy'
|
|
@@ -72,6 +74,8 @@ import {
|
|
|
72
74
|
featureExcludes,
|
|
73
75
|
fullsetExcludes,
|
|
74
76
|
srcFeatureExcludes,
|
|
77
|
+
pluginExcludes,
|
|
78
|
+
pluginExcludesFor,
|
|
75
79
|
} from './helpers/featureSource'
|
|
76
80
|
import type { FeatureSource } from './helpers/featureSource'
|
|
77
81
|
import { stationLibrary } from './helpers/station'
|
|
@@ -1071,6 +1075,8 @@ export {
|
|
|
1071
1075
|
Entity,
|
|
1072
1076
|
Feature,
|
|
1073
1077
|
Test,
|
|
1078
|
+
TestControl,
|
|
1079
|
+
TEST_CONTROL_EXCLUDE,
|
|
1074
1080
|
Readme,
|
|
1075
1081
|
ReadmeTop,
|
|
1076
1082
|
AgentGuideTop,
|
|
@@ -1153,6 +1159,7 @@ export {
|
|
|
1153
1159
|
tsSafeTypeName,
|
|
1154
1160
|
serverVariables,
|
|
1155
1161
|
hasServerVariables,
|
|
1162
|
+
serverVarEnv,
|
|
1156
1163
|
liveStrict,
|
|
1157
1164
|
primaryOpCall,
|
|
1158
1165
|
idLiteral,
|
|
@@ -1165,8 +1172,16 @@ export {
|
|
|
1165
1172
|
featureExcludes,
|
|
1166
1173
|
fullsetExcludes,
|
|
1167
1174
|
srcFeatureExcludes,
|
|
1175
|
+
pluginExcludes,
|
|
1176
|
+
pluginExcludesFor,
|
|
1168
1177
|
stationLibrary,
|
|
1169
1178
|
|
|
1179
|
+
featureApplies,
|
|
1180
|
+
targetFeatures,
|
|
1181
|
+
featureTags,
|
|
1182
|
+
unknownTags,
|
|
1183
|
+
TAGS,
|
|
1184
|
+
|
|
1170
1185
|
definitionPath,
|
|
1171
1186
|
definitionFolder,
|
|
1172
1187
|
definitionNames,
|
package/src/utility.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { JostracaResult, each } from 'jostraca'
|
|
|
5
5
|
|
|
6
6
|
import { KIT, getModelPath } from '@voxgig/apidef'
|
|
7
7
|
|
|
8
|
+
import { targetFeatures } from './helpers/applicability'
|
|
9
|
+
|
|
8
10
|
import { serverVariables } from './helpers/serverVars'
|
|
9
11
|
import { packageVersion } from './helpers/packageMeta'
|
|
10
12
|
|
|
@@ -80,6 +82,34 @@ function isHttpBasicAuth(model: any): boolean {
|
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
|
|
85
|
+
// The API's ACCESS-TOKEN EXCHANGE, as apidef recorded it from the spec
|
|
86
|
+
// (main.kit.info.security.exchange): where the token endpoint lives, and the
|
|
87
|
+
// field names it sends and answers with. Null when the spec describes none,
|
|
88
|
+
// which is the common case.
|
|
89
|
+
//
|
|
90
|
+
// These are FACTS ABOUT THE API, so they belong in the generated config
|
|
91
|
+
// rather than in a project's hand-written model: an SDK whose spec says the
|
|
92
|
+
// exchange is at `auth/token` should not need to be told so again. A feature
|
|
93
|
+
// opts in to receiving them with `spec: { authexchange: '<options-key>' }`;
|
|
94
|
+
// configDefinition does the overlay.
|
|
95
|
+
//
|
|
96
|
+
// Deliberately NOT included: `active`. Whether a client performs the
|
|
97
|
+
// exchange is the project's decision (some resolve a static credential
|
|
98
|
+
// through the same feature on an API that also has a token endpoint), so
|
|
99
|
+
// apidef records only what the exchange IS, never that it should run.
|
|
100
|
+
function resolveAuthExchange(model: any): Record<string, any> | null {
|
|
101
|
+
const security = getModelPath(model, `main.${KIT}.info.security`,
|
|
102
|
+
{ only_active: false, required: false })
|
|
103
|
+
|
|
104
|
+
const exchange = security?.exchange
|
|
105
|
+
if (null == exchange || 'object' !== typeof exchange) {
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return exchange
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
|
|
83
113
|
function requirePath(ctx$: any, path: string, flags?: { ignore?: boolean }): any {
|
|
84
114
|
const fullpath = resolvePath(ctx$, path)
|
|
85
115
|
const ignore = null == flags?.ignore ? false : flags.ignore
|
|
@@ -116,6 +146,7 @@ export {
|
|
|
116
146
|
requirePath,
|
|
117
147
|
isAuthActive,
|
|
118
148
|
resolveAuthPrefix,
|
|
149
|
+
resolveAuthExchange,
|
|
119
150
|
isHttpBasicAuth,
|
|
120
151
|
SdkGenError,
|
|
121
152
|
CONFIG_DATA_THRESHOLD,
|
|
@@ -299,6 +330,15 @@ function rawStringLiteral(s: string): string {
|
|
|
299
330
|
}
|
|
300
331
|
|
|
301
332
|
|
|
333
|
+
// The closed vocabulary of spec-derived facts a feature may ask for by
|
|
334
|
+
// declaring `spec: { <fact>: <options-key> }`. Closed, and resolved through
|
|
335
|
+
// one function each, so a feature cannot reach arbitrarily into the model
|
|
336
|
+
// and a fact's shape is defined in exactly one place.
|
|
337
|
+
const SPEC_FACTS: Record<string, (model: any) => any> = {
|
|
338
|
+
authexchange: resolveAuthExchange,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
|
|
302
342
|
// THE CANONICAL CONFIG OBJECT, and the JSON the threshold is measured on.
|
|
303
343
|
//
|
|
304
344
|
// Every target builds its config from this one function, so the literal a
|
|
@@ -312,7 +352,14 @@ function rawStringLiteral(s: string): string {
|
|
|
312
352
|
// across runs exactly like the literal it replaces.
|
|
313
353
|
function configDefinition(model: any, targetname?: string): { def: any, json: string } {
|
|
314
354
|
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
315
|
-
|
|
355
|
+
|
|
356
|
+
// Gated by the target when one is named, so the embedded config cannot
|
|
357
|
+
// advertise a feature this target has no implementation for — the exact
|
|
358
|
+
// hybrid state the applicability gate exists to prevent. With no
|
|
359
|
+
// targetname (a caller that cannot say which target it is building) the
|
|
360
|
+
// helper returns every active feature, i.e. the old behaviour.
|
|
361
|
+
const feature = targetFeatures(model, targetname)
|
|
362
|
+
|
|
316
363
|
const headers = getModelPath(model, `main.${KIT}.config.headers`) || {}
|
|
317
364
|
|
|
318
365
|
const authActive = isAuthActive(model)
|
|
@@ -352,6 +399,29 @@ function configDefinition(model: any, targetname?: string): { def: any, json: st
|
|
|
352
399
|
if (null != f.transport && '' !== f.transport) {
|
|
353
400
|
fdef.transport = String(f.transport)
|
|
354
401
|
}
|
|
402
|
+
|
|
403
|
+
// SPEC-DERIVED OPTIONS. A feature declares `spec: { <fact>: <options
|
|
404
|
+
// key> }` to receive facts apidef recorded from the OpenAPI spec — the
|
|
405
|
+
// same declare-never-infer rule `needs` and `transport` follow, and for
|
|
406
|
+
// the same reason: the alternative is this function knowing feature
|
|
407
|
+
// names, which is exactly the coupling the generic feature loop exists
|
|
408
|
+
// to avoid.
|
|
409
|
+
//
|
|
410
|
+
// The fact overlays the feature's DECLARED DEFAULTS, and rightly: a
|
|
411
|
+
// default like `path: 'auth/token'` is a generic guess, while the spec
|
|
412
|
+
// states where this API's endpoint actually is. A project that needs
|
|
413
|
+
// something else still overrides at runtime through `options.feature`,
|
|
414
|
+
// which beats the embedded config either way.
|
|
415
|
+
for (const factname of Object.keys(f.spec || {}).sort()) {
|
|
416
|
+
const optkey = f.spec[factname]
|
|
417
|
+
const fact = SPEC_FACTS[factname]?.(model)
|
|
418
|
+
if (null == fact || null == optkey || '' === optkey) {
|
|
419
|
+
continue
|
|
420
|
+
}
|
|
421
|
+
fdef.options = { ...(fdef.options || {}) }
|
|
422
|
+
fdef.options[optkey] = { ...(fdef.options[optkey] || {}), ...fact }
|
|
423
|
+
}
|
|
424
|
+
|
|
355
425
|
featureDefs[f.name] = fdef
|
|
356
426
|
})
|
|
357
427
|
|