@voxgig/sdkgen 4.8.2 → 4.8.3
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/doctor.d.ts +1 -0
- package/dist/action/doctor.js +50 -2
- package/dist/action/doctor.js.map +1 -1
- package/dist/helpers/naming.js +4 -2
- package/dist/helpers/naming.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/model/sdkgen.aon +21 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +120 -18
- package/project/.sdk/model/target/c.aon +10 -0
- package/project/.sdk/model/target/clojure.aon +13 -0
- package/project/.sdk/model/target/cpp.aon +10 -0
- package/project/.sdk/model/target/csharp.aon +9 -0
- package/project/.sdk/model/target/dart.aon +10 -0
- package/project/.sdk/model/target/elixir.aon +9 -0
- package/project/.sdk/model/target/go.aon +13 -0
- package/project/.sdk/model/target/java.aon +9 -0
- package/project/.sdk/model/target/js.aon +7 -0
- package/project/.sdk/model/target/kotlin.aon +9 -0
- package/project/.sdk/model/target/lean.aon +17 -0
- package/project/.sdk/model/target/lua.aon +8 -0
- package/project/.sdk/model/target/ocaml.aon +9 -0
- package/project/.sdk/model/target/perl.aon +8 -0
- package/project/.sdk/model/target/php.aon +8 -0
- package/project/.sdk/model/target/py.aon +13 -0
- package/project/.sdk/model/target/rb.aon +8 -0
- package/project/.sdk/model/target/rust.aon +10 -0
- package/project/.sdk/model/target/scala.aon +21 -2
- package/project/.sdk/model/target/swift.aon +18 -0
- package/project/.sdk/model/target/ts.aon +13 -0
- package/project/.sdk/src/cmp/clojure/Package_clojure.ts +9 -2
- package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +3 -0
- package/project/.sdk/src/cmp/dart/Test_dart.ts +2 -0
- package/project/.sdk/src/cmp/elixir/Package_elixir.ts +4 -1
- package/project/.sdk/src/cmp/go/Config_go.ts +59 -2
- package/project/.sdk/src/cmp/go/Main_go.ts +8 -2
- package/project/.sdk/src/cmp/java/Test_java.ts +6 -5
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +6 -5
- package/project/.sdk/src/cmp/lean/Package_lean.ts +23 -0
- package/project/.sdk/src/cmp/lean/ReadmeInstall_lean.ts +1 -0
- package/project/.sdk/src/cmp/lean/ReadmeTopTest_lean.ts +1 -0
- package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +7 -3
- package/project/.sdk/src/cmp/py/Config_py.ts +82 -1
- package/project/.sdk/src/cmp/py/Main_py.ts +44 -0
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +2 -0
- package/project/.sdk/src/cmp/swift/Main_swift.ts +10 -0
- package/project/.sdk/src/cmp/swift/Package_swift.ts +13 -1
- package/project/.sdk/src/cmp/ts/Config_ts.ts +42 -17
- package/project/.sdk/src/cmp/ts/fragment/Config.data.fragment.ts +13 -1
- package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +13 -1
- package/project/.sdk/tm/c/Makefile +38 -3
- package/project/.sdk/tm/c/tests/omni_resolver.h +361 -0
- package/project/.sdk/tm/c/tests/omni_smoke_test.c +237 -0
- package/project/.sdk/tm/c/tests/primary_corpus_test.c +205 -284
- package/project/.sdk/tm/c/tests/struct_corpus_test.c +292 -212
- package/project/.sdk/tm/c/tests/vendor/omni/json.c +811 -0
- package/project/.sdk/tm/c/tests/vendor/omni/omni.h +240 -0
- package/project/.sdk/tm/c/tests/vendor/omni/runner.c +940 -0
- package/project/.sdk/tm/c/tests/vendor/omni/util.c +351 -0
- package/project/.sdk/tm/c/utility/struct/utility.c +7 -2
- package/project/.sdk/tm/clojure/test/sdk/test/omni.clj +401 -0
- package/project/.sdk/tm/clojure/test/sdk/test/omni_smoke.clj +81 -0
- package/project/.sdk/tm/clojure/test/sdk/test/primary.clj +92 -58
- package/project/.sdk/tm/clojure/test/sdk/test/struct_corpus.clj +122 -149
- package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +8 -3
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/json.clj +145 -0
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/runner.clj +445 -0
- package/project/.sdk/tm/clojure/test/vendor/omni/voxgig/omni/util.clj +145 -0
- package/project/.sdk/tm/cpp/Makefile +5 -1
- package/project/.sdk/tm/cpp/test/omni_resolver.hpp +473 -0
- package/project/.sdk/tm/cpp/test/omni_smoke_test.cpp +209 -0
- package/project/.sdk/tm/cpp/test/primary_utility_test.cpp +243 -184
- package/project/.sdk/tm/cpp/test/runner_support.hpp +68 -181
- package/project/.sdk/tm/cpp/test/struct_corpus_test.cpp +183 -56
- package/project/.sdk/tm/cpp/test/vendor/omni/json.hpp +406 -0
- package/project/.sdk/tm/cpp/test/vendor/omni/omni.hpp +751 -0
- package/project/.sdk/tm/cpp/test/vendor/omni/util.hpp +256 -0
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +7 -1
- package/project/.sdk/tm/csharp/test/OmniResolver.cs +491 -0
- package/project/.sdk/tm/csharp/test/OmniSmokeTest.cs +106 -0
- package/project/.sdk/tm/csharp/test/PrimaryUtilityTest.cs +299 -316
- package/project/.sdk/tm/csharp/test/Runner.cs +104 -269
- package/project/.sdk/tm/csharp/test/StructUtilityTest.cs +535 -724
- package/project/.sdk/tm/csharp/test/vendor/omni/Runner.cs +864 -0
- package/project/.sdk/tm/csharp/test/vendor/omni/Util.cs +347 -0
- package/project/.sdk/tm/csharp/utility/MakeSpec.cs +3 -1
- package/project/.sdk/tm/csharp/utility/PrepareMethod.cs +6 -1
- package/project/.sdk/tm/csharp/utility/struct/Struct.cs +21 -5
- package/project/.sdk/tm/dart/test/omni.dart +520 -0
- package/project/.sdk/tm/dart/test/omni_smoke_test.dart +143 -0
- package/project/.sdk/tm/dart/test/primary_test.dart +86 -28
- package/project/.sdk/tm/dart/test/struct_test.dart +438 -11
- package/project/.sdk/tm/dart/test/vendor/omni/omni.dart +7 -0
- package/project/.sdk/tm/dart/test/vendor/omni/runner.dart +653 -0
- package/project/.sdk/tm/dart/test/vendor/omni/util.dart +232 -0
- package/project/.sdk/tm/elixir/Makefile +11 -3
- package/project/.sdk/tm/elixir/test/omni_smoke_test.exs +117 -0
- package/project/.sdk/tm/elixir/test/primary_utility_test.exs +341 -16
- package/project/.sdk/tm/elixir/test/struct_corpus_test.exs +554 -9
- package/project/.sdk/tm/elixir/test/support/omni.ex +377 -0
- package/project/.sdk/tm/elixir/test/vendor/omni/json.ex +122 -0
- package/project/.sdk/tm/elixir/test/vendor/omni/runner.ex +623 -0
- package/project/.sdk/tm/elixir/test/vendor/omni/util.ex +187 -0
- package/project/.sdk/tm/go/core/context.go +32 -16
- package/project/.sdk/tm/go/feature/secrets/plugin/capability.go +261 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/catalog.go +78 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/config.go +652 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/depend.go +250 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/env.go +194 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/export.go +80 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/graph.go +238 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/host.go +1575 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/order.go +248 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/point.go +184 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/ref.go +123 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/resolve.go +79 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/types.go +296 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/util.go +87 -0
- package/project/.sdk/tm/go/feature/secrets/plugin/version.go +161 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/aws/aws.go +292 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/aws/sigv4.go +201 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/azuresecrets/azuresecrets.go +187 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/boru/boru.go +184 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/doppler/doppler.go +114 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/gcpsecrets/gcpsecrets.go +156 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/hashicorp/hashicorp.go +209 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/httpjson/httpjson.go +241 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/infisical/infisical.go +151 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/onepassword/onepassword.go +152 -0
- package/project/.sdk/tm/go/feature/secrets/plugins/secretspec/secretspec.go +143 -0
- package/project/.sdk/tm/go/feature/secrets/sekreto/addr.go +120 -0
- package/project/.sdk/tm/go/feature/secrets/sekreto/providers.go +439 -0
- package/project/.sdk/tm/go/feature/secrets/sekreto/sekreto.go +685 -0
- package/project/.sdk/tm/go/feature/secrets_feature.go +675 -0
- package/project/.sdk/tm/go/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +799 -0
- package/project/.sdk/tm/go/test/omni/omni.go +897 -0
- package/project/.sdk/tm/go/test/omni/util.go +360 -0
- package/project/.sdk/tm/go/test/omniresolver_test.go +748 -0
- package/project/.sdk/tm/go/test/omnismoke_test.go +112 -0
- package/project/.sdk/tm/go/test/primary_utility_test.go +123 -191
- package/project/.sdk/tm/go/test/struct_utility_test.go +323 -16
- package/project/.sdk/tm/go/test/testsupport_test.go +294 -0
- package/project/.sdk/tm/go/utility/make_options.go +8 -1
- package/project/.sdk/tm/go/utility/prepare_method.go +5 -1
- package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +2 -2
- package/project/.sdk/tm/java/test/OmniResolver.java +355 -0
- package/project/.sdk/tm/java/test/OmniSmokeTest.java +99 -0
- package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +254 -280
- package/project/.sdk/tm/java/test/RunnerSupport.java +9 -194
- package/project/.sdk/tm/java/test/StructCorpusTest.java +119 -42
- package/project/.sdk/tm/java/test/vendor/omni/Json.java +287 -0
- package/project/.sdk/tm/java/test/vendor/omni/Runner.java +851 -0
- package/project/.sdk/tm/java/test/vendor/omni/Util.java +322 -0
- package/project/.sdk/tm/java/utility/MakeSpec.java +3 -1
- package/project/.sdk/tm/java/utility/PrepareMethod.java +5 -5
- package/project/.sdk/tm/java/utility/struct/Struct.java +17 -4
- package/project/.sdk/tm/js/src/utility/StructUtility.js +3 -3
- package/project/.sdk/tm/js/test/omni.js +251 -0
- package/project/.sdk/tm/js/test/omni.test.js +105 -0
- package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +6 -2
- package/project/.sdk/tm/js/test/utility/StructUtility.test.js +27 -2
- package/project/.sdk/tm/js/test/vendor/omni/index.js +77 -0
- package/project/.sdk/tm/js/test/vendor/omni/runner.js +613 -0
- package/project/.sdk/tm/js/test/vendor/omni/util.js +212 -0
- package/project/.sdk/tm/kotlin/core/Utility.kt +1 -1
- package/project/.sdk/tm/kotlin/test/OmniResolver.kt +343 -0
- package/project/.sdk/tm/kotlin/test/OmniSmokeTest.kt +100 -0
- package/project/.sdk/tm/kotlin/test/PrimaryUtilityTest.kt +244 -262
- package/project/.sdk/tm/kotlin/test/RunnerSupport.kt +9 -178
- package/project/.sdk/tm/kotlin/test/StructCorpusTest.kt +121 -32
- package/project/.sdk/tm/kotlin/test/vendor/omni/Json.kt +264 -0
- package/project/.sdk/tm/kotlin/test/vendor/omni/Runner.kt +615 -0
- package/project/.sdk/tm/kotlin/test/vendor/omni/Util.kt +139 -0
- package/project/.sdk/tm/kotlin/utility/MakeSpecUrl.kt +6 -3
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +6 -6
- package/project/.sdk/tm/kotlin/utility/struct/Struct.kt +18 -2
- package/project/.sdk/tm/lean/Makefile +12 -5
- package/project/.sdk/tm/lean/src/SdkUtility.lean +14 -1
- package/project/.sdk/tm/lean/test/OmniResolver.lean +358 -0
- package/project/.sdk/tm/lean/test/OmniSmoke.lean +174 -0
- package/project/.sdk/tm/lean/test/StructCorpus.lean +245 -574
- package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +155 -291
- package/project/.sdk/tm/lean/test/vendor/omni/Omni.lean +997 -0
- package/project/.sdk/tm/lua/test/omni.lua +456 -0
- package/project/.sdk/tm/lua/test/omni_smoke_test.lua +103 -0
- package/project/.sdk/tm/lua/test/primary_utility_test.lua +250 -509
- package/project/.sdk/tm/lua/test/struct_utility_test.lua +161 -70
- package/project/.sdk/tm/lua/test/vendor/omni/json.lua +268 -0
- package/project/.sdk/tm/lua/test/vendor/omni/regex.lua +385 -0
- package/project/.sdk/tm/lua/test/vendor/omni/runner.lua +641 -0
- package/project/.sdk/tm/lua/test/vendor/omni/util.lua +282 -0
- package/project/.sdk/tm/lua/utility/prepare_method.lua +5 -1
- package/project/.sdk/tm/lua/utility/struct/regex.lua +663 -0
- package/project/.sdk/tm/lua/utility/struct/struct.lua +660 -420
- package/project/.sdk/tm/ocaml/Makefile +37 -12
- package/project/.sdk/tm/ocaml/test/omni_resolver.ml +284 -0
- package/project/.sdk/tm/ocaml/test/omni_smoke_test.ml +169 -0
- package/project/.sdk/tm/ocaml/test/primary_utility_test.ml +84 -100
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +225 -201
- package/project/.sdk/tm/ocaml/test/vendor/omni/omni.ml +1157 -0
- package/project/.sdk/tm/perl/lib/Voxgig/Struct.pm +88 -12
- package/project/.sdk/tm/perl/t/omni.pm +647 -0
- package/project/.sdk/tm/perl/t/omni_smoke.t +87 -0
- package/project/.sdk/tm/perl/t/primary_utility.t +189 -487
- package/project/.sdk/tm/perl/t/struct_utility.t +455 -389
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Runner.pm +649 -0
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni/Util.pm +253 -0
- package/project/.sdk/tm/perl/t/vendor/omni/Voxgig/Omni.pm +24 -0
- package/project/.sdk/tm/perl/utility/prepare_method.pm +7 -2
- package/project/.sdk/tm/php/test/Omni.php +691 -0
- package/project/.sdk/tm/php/test/OmniSmokeTest.php +93 -0
- package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +210 -571
- package/project/.sdk/tm/php/test/StructUtilityTest.php +320 -342
- package/project/.sdk/tm/php/test/vendor/omni/Runner.php +644 -0
- package/project/.sdk/tm/php/test/vendor/omni/Util.php +283 -0
- package/project/.sdk/tm/php/utility/MakeSpec.php +10 -3
- package/project/.sdk/tm/php/utility/PrepareBody.php +7 -1
- package/project/.sdk/tm/php/utility/PrepareMethod.php +6 -2
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/__init__.py +36 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/capability.py +141 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/catalog.py +50 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/config.py +333 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/depend.py +198 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/env.py +147 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/export.py +52 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/graph.py +139 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/host.py +1053 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/order.py +171 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/point.py +95 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/ref.py +116 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/resolve.py +59 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/types.py +87 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_plugin/version.py +112 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/__init__.py +74 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/addr.py +116 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/aws.py +192 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/azuresecrets.py +120 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/boru.py +140 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/doppler.py +62 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/gcpsecrets.py +101 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/hashicorp.py +152 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/httpjson.py +115 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/infisical.py +92 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/onepassword.py +101 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/secretspec.py +119 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/plugins/sigv4.py +125 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/providers.py +211 -0
- package/project/.sdk/tm/py/pkg/feature/secrets/voxgig_sekreto/sekreto.py +455 -0
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +467 -0
- package/project/.sdk/tm/py/pkg/utility/prepare_method.py +5 -1
- package/project/.sdk/tm/py/pkg/utility/voxgig_struct/__init__.py +0 -2
- package/project/.sdk/tm/py/pkg/utility/voxgig_struct/voxgig_struct.py +780 -529
- package/project/.sdk/tm/py/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/py/test/feature/__init__.py +0 -0
- package/project/.sdk/tm/py/test/feature/secrets/__init__.py +0 -0
- package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +337 -0
- package/project/.sdk/tm/py/test/omni.py +415 -0
- package/project/.sdk/tm/py/test/test_omni_smoke.py +74 -0
- package/project/.sdk/tm/py/test/test_primary_utility.py +144 -668
- package/project/.sdk/tm/py/test/test_struct_utility.py +74 -27
- package/project/.sdk/tm/py/test/voxgig_omni/__init__.py +62 -0
- package/project/.sdk/tm/py/test/voxgig_omni/runner.py +566 -0
- package/project/.sdk/tm/py/test/voxgig_omni/util.py +237 -0
- package/project/.sdk/tm/rb/test/omni.rb +505 -0
- package/project/.sdk/tm/rb/test/omni_smoke_test.rb +72 -0
- package/project/.sdk/tm/rb/test/primary_utility_test.rb +174 -560
- package/project/.sdk/tm/rb/test/struct_utility_test.rb +13 -7
- package/project/.sdk/tm/rb/test/vendor/omni/runner.rb +493 -0
- package/project/.sdk/tm/rb/test/vendor/omni/util.rb +199 -0
- package/project/.sdk/tm/rb/test/vendor/omni/voxgig_omni.rb +15 -0
- package/project/.sdk/tm/rb/utility/prepare_method.rb +5 -1
- package/project/.sdk/tm/rust/tests/common/mod.rs +19 -242
- package/project/.sdk/tm/rust/tests/omni_resolver/mod.rs +888 -0
- package/project/.sdk/tm/rust/tests/omni_smoke_test.rs +286 -0
- package/project/.sdk/tm/rust/tests/primary_utility_test.rs +318 -312
- package/project/.sdk/tm/rust/tests/struct_utility_test.rs +66 -28
- package/project/.sdk/tm/rust/tests/vendor/omni/json.rs +412 -0
- package/project/.sdk/tm/rust/tests/vendor/omni/mod.rs +38 -0
- package/project/.sdk/tm/rust/tests/vendor/omni/regex.rs +392 -0
- package/project/.sdk/tm/rust/tests/vendor/omni/runner.rs +1001 -0
- package/project/.sdk/tm/rust/tests/vendor/omni/util.rs +161 -0
- package/project/.sdk/tm/rust/utility/prepare_method.rs +5 -1
- package/project/.sdk/tm/scala/Makefile +16 -6
- package/project/.sdk/tm/scala/sdktest/OmniResolver.scala +497 -0
- package/project/.sdk/tm/scala/sdktest/OmniSmoke.scala +192 -0
- package/project/.sdk/tm/scala/sdktest/PrimaryCorpus.scala +47 -19
- package/project/.sdk/tm/scala/sdktest/StructCorpus.scala +0 -0
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Json.scala +229 -0
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Runner.scala +552 -0
- package/project/.sdk/tm/scala/sdktest/vendor/omni/Util.scala +104 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Constants.swift +10 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Inject.swift +28 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Injection.swift +5 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Minor.swift +4 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/Struct/Validate.swift +15 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +6 -1
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/OmniResolver.swift +474 -0
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/OmniSmokeTest.swift +107 -0
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/PrimaryUtilityTest.swift +141 -178
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/Runner.swift +17 -113
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/StructUtilityTest.swift +295 -446
- package/project/.sdk/tm/swift/Tests/vendor/omni/Json.swift +364 -0
- package/project/.sdk/tm/swift/Tests/vendor/omni/Runner.swift +730 -0
- package/project/.sdk/tm/swift/Tests/vendor/omni/Util.swift +162 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +7 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Capability.ts +148 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Catalog.ts +55 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Config.ts +344 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Depend.ts +205 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Env.ts +138 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Export.ts +46 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/FeatureHost.ts +349 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Graph.ts +181 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Host.ts +1132 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Order.ts +177 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Point.ts +119 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Ref.ts +102 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Resolve.ts +61 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Types.ts +112 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/Version.ts +100 -0
- package/project/.sdk/tm/ts/src/feature/secrets/plugin/index.ts +37 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +163 -30
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +27 -22
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/aws.ts +26 -36
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/azuresecrets.ts +19 -21
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/boru.ts +36 -17
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/doppler.ts +14 -21
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/gcpsecrets.ts +27 -27
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/hashicorp.ts +25 -39
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/httpjson.ts +99 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/infisical.ts +14 -16
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{provider → plugins}/onepassword.ts +15 -22
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/plugins/secretspec.ts +98 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/{Sigv4.ts → plugins/sigv4.ts} +7 -6
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +97 -23
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/builtin.ts +37 -0
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +4 -17
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +4 -16
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +11 -25
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +6 -23
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +106 -39
- package/project/.sdk/tm/ts/src/utility/StructUtility.ts +3 -3
- package/project/.sdk/tm/ts/test/omni.ts +104 -32
- package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +27 -0
- package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +32 -5
- package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +2 -2
- package/project/.sdk/tm/ts/test/vendor/omni/index.ts +2 -2
- package/project/.sdk/tm/zig/test/primary_utility_test.zig +1 -1
- package/project/.sdk/tm/zig/test/struct_runner.zig +5 -4
- package/project/sdkgen-package.json +1 -1
- package/src/action/doctor.ts +66 -2
- package/src/helpers/naming.ts +4 -2
- package/project/.sdk/tm/c/tests/runner.h +0 -274
- package/project/.sdk/tm/cpp/test/struct_runner.hpp +0 -314
- package/project/.sdk/tm/csharp/test/StructRunner.cs +0 -276
- package/project/.sdk/tm/dart/test/runner.dart +0 -406
- package/project/.sdk/tm/dart/test/struct_corpus.dart +0 -531
- package/project/.sdk/tm/elixir/test/support/struct_corpus.ex +0 -1088
- package/project/.sdk/tm/go/test/runner_test.go +0 -629
- package/project/.sdk/tm/go/test/struct_runner_test.go +0 -1094
- package/project/.sdk/tm/java/test/StructRunner.java +0 -281
- package/project/.sdk/tm/js/test/runner.js +0 -387
- package/project/.sdk/tm/kotlin/test/StructRunner.kt +0 -177
- package/project/.sdk/tm/lua/test/struct_runner.lua +0 -602
- package/project/.sdk/tm/ocaml/test/corpus_runner.ml +0 -185
- package/project/.sdk/tm/perl/t/struct_runner.pm +0 -275
- package/project/.sdk/tm/php/test/StructRunner.php +0 -275
- package/project/.sdk/tm/py/test/struct_runner.py +0 -411
- package/project/.sdk/tm/rb/test/struct_runner.rb +0 -309
- package/project/.sdk/tm/rust/tests/struct_runner/mod.rs +0 -424
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +0 -90
- package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +0 -66
- package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +0 -188
|
@@ -0,0 +1,1053 @@
|
|
|
1
|
+
# VENDORED: @voxgig/plugin 0.1.6 (python/voxgig_plugin/host.py)
|
|
2
|
+
# Source: https://github.com/voxgig/plugin @ 8d8968afc0a2008fbd795b41ab166307d989f02a [tag: sdk-20260904-1610-0]
|
|
3
|
+
# License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
4
|
+
"""The host: the lifecycle state machine (section 5), extension points
|
|
5
|
+
(section 6), and resource capture (section 8).
|
|
6
|
+
|
|
7
|
+
TWO RULES SHAPE EVERY METHOD BELOW.
|
|
8
|
+
|
|
9
|
+
Transitions are SEQUENTIAL (section 5.2). One at a time, in call order,
|
|
10
|
+
never interleaved; a transition triggered from inside a lifecycle callback
|
|
11
|
+
is `plugin_reentrant`. A hard rule, because it is the only way the
|
|
12
|
+
semantics can be identical in Go, in Ruby and in single-threaded
|
|
13
|
+
JavaScript.
|
|
14
|
+
|
|
15
|
+
Reconciliation is EAGER (section 18's portability budget). A transition
|
|
16
|
+
settles by running the state machine to a fixed point, not by suspending
|
|
17
|
+
on a promise. Every port must be able to do the same, and fourteen of them
|
|
18
|
+
will not have JavaScript's event loop.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from .types import fail
|
|
22
|
+
from .ref import canon_ref, try_ref, format_ref, parse_ref
|
|
23
|
+
from .catalog import make_catalog
|
|
24
|
+
from .order import resolve_order
|
|
25
|
+
from .point import emit as fanout, compose, provider as pickone
|
|
26
|
+
from .export import resolve_export
|
|
27
|
+
from .capability import resolve_capability
|
|
28
|
+
from .config import normalize_config, resolve_options
|
|
29
|
+
from .depend import checkcycle, gatesactivation, requirements, restartsonloss
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def make_host(options=None):
|
|
33
|
+
return Host(options)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Inst:
|
|
37
|
+
"""What a definition's callbacks see. Deliberately not the internal
|
|
38
|
+
record: a plugin that could reach `status` could also write it."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, host, entry):
|
|
41
|
+
self._host = host
|
|
42
|
+
self._entry = entry
|
|
43
|
+
self.ref = entry['ref']
|
|
44
|
+
self.name = parse_ref(entry['ref'])['name']
|
|
45
|
+
self.tag = parse_ref(entry['ref'])['tag']
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def options(self):
|
|
49
|
+
return self._entry['options']
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def state(self):
|
|
53
|
+
return self._entry['state']
|
|
54
|
+
|
|
55
|
+
def host(self):
|
|
56
|
+
return self._host
|
|
57
|
+
|
|
58
|
+
def release(self, fn):
|
|
59
|
+
"""Foreign resources the host did not hand out are registered
|
|
60
|
+
explicitly (section 8.3); host calls are recorded automatically.
|
|
61
|
+
|
|
62
|
+
SYMMETRIC WITH `acquire`, and it has to be: `open` counts the
|
|
63
|
+
resources CURRENTLY HELD, so an entry that is registered and then
|
|
64
|
+
unwound must leave the count where it found it.
|
|
65
|
+
"""
|
|
66
|
+
# Section 8.3: "`inst.release` outside `activate` is
|
|
67
|
+
# `plugin_release_scope`". `_intransition` is true in `define`
|
|
68
|
+
# too, and a scope entry registered there is never unwound.
|
|
69
|
+
if 'activate' != self._host._phase:
|
|
70
|
+
fail('plugin_release_scope', 'release called outside activate')
|
|
71
|
+
host = self._host
|
|
72
|
+
done = [False]
|
|
73
|
+
|
|
74
|
+
def unwrap():
|
|
75
|
+
if not done[0]:
|
|
76
|
+
done[0] = True
|
|
77
|
+
host._open -= 1
|
|
78
|
+
fn()
|
|
79
|
+
|
|
80
|
+
self._entry['scope'].append(unwrap)
|
|
81
|
+
host._open += 1
|
|
82
|
+
|
|
83
|
+
def acquire(self):
|
|
84
|
+
"""The synthetic counter the driver owns, so "what is open" is
|
|
85
|
+
data rather than an assertion each port words differently.
|
|
86
|
+
|
|
87
|
+
Returns its own release, so a plugin can hand one back early. The
|
|
88
|
+
scope still holds the entry and unwinding it twice is a no-op -
|
|
89
|
+
releasing early must not make teardown wrong.
|
|
90
|
+
"""
|
|
91
|
+
# Section 8.1: resources are "acquired during `activate` - the
|
|
92
|
+
# scope's actual job". Same reason as `release` above.
|
|
93
|
+
if 'activate' != self._host._phase:
|
|
94
|
+
fail('plugin_release_scope', 'acquire called outside activate')
|
|
95
|
+
host = self._host
|
|
96
|
+
done = [False]
|
|
97
|
+
|
|
98
|
+
def rel():
|
|
99
|
+
if not done[0]:
|
|
100
|
+
done[0] = True
|
|
101
|
+
host._open -= 1
|
|
102
|
+
|
|
103
|
+
self._entry['scope'].append(rel)
|
|
104
|
+
host._open += 1
|
|
105
|
+
return rel
|
|
106
|
+
|
|
107
|
+
def bind(self, point, fn, band=None):
|
|
108
|
+
"""Bind into a host point. Declared in `define`; the host inserts
|
|
109
|
+
it only after `activate` returns successfully (section 8.1), which
|
|
110
|
+
is why a failing activate leaves no live binding behind.
|
|
111
|
+
|
|
112
|
+
Section 12 has carried `plugin_bind_scope` - "binding declared
|
|
113
|
+
outside `define`" - since before anything raised it. The guard
|
|
114
|
+
was the half nobody wrote, so a binding added from `activate`
|
|
115
|
+
went live without being part of the loaded definition, and a
|
|
116
|
+
deactivate/activate cycle appended it again.
|
|
117
|
+
"""
|
|
118
|
+
if 'define' != self._host._phase:
|
|
119
|
+
fail('plugin_bind_scope', 'bind called outside define: ' + point,
|
|
120
|
+
{'ref': self.ref, 'point': point})
|
|
121
|
+
if point not in self._host._points:
|
|
122
|
+
fail('plugin_point_unknown', 'no such point: ' + point,
|
|
123
|
+
{'point': point})
|
|
124
|
+
self._entry['bindings'].append(
|
|
125
|
+
{'ref': self.ref, 'point': point, 'fn': fn, 'band': band or 0})
|
|
126
|
+
|
|
127
|
+
def export(self, key, value):
|
|
128
|
+
"""Published for other plugins and for the application (section
|
|
129
|
+
11)."""
|
|
130
|
+
self._entry['exports'][key] = value
|
|
131
|
+
|
|
132
|
+
def provides(self, prov):
|
|
133
|
+
"""What this instance can do for others (section 11.1)."""
|
|
134
|
+
self._entry['provides'].append(prov)
|
|
135
|
+
|
|
136
|
+
def position(self, point):
|
|
137
|
+
"""Where this binding landed (section 6.6) - the plugin-side
|
|
138
|
+
counterpart to a host pin. Station found that a plugin can need to
|
|
139
|
+
KNOW it is in the right place: its middleware must sit immediately
|
|
140
|
+
outside the base transport or its "wire truth" events are fiction.
|
|
141
|
+
|
|
142
|
+
THE HOST DOES NOT POLICE THIS; it just makes the fact available. A
|
|
143
|
+
plugin that requires a position it did not get fails loudly rather
|
|
144
|
+
than reporting nonsense - and that is the plugin's call, because
|
|
145
|
+
only it knows what its position means. Verification tells a plugin
|
|
146
|
+
it was misplaced; a pin (section 7) stops the misplacement from
|
|
147
|
+
being expressible at all. The two are not substitutes.
|
|
148
|
+
"""
|
|
149
|
+
return self._host.positionof(self.ref, point)
|
|
150
|
+
|
|
151
|
+
def nest(self, nestopts=None):
|
|
152
|
+
"""AN INSTANCE MAY ITSELF BE A HOST (section 6.5), and THE OUTER
|
|
153
|
+
ONE OWNS THE INNER ONE'S LIFETIME. Registering the teardown in the
|
|
154
|
+
instance scope is what makes that true rather than aspirational:
|
|
155
|
+
the inner host closes when the outer instance deactivates, in the
|
|
156
|
+
same reverse unwind as every other resource."""
|
|
157
|
+
if not self._host._intransition:
|
|
158
|
+
fail('plugin_release_scope',
|
|
159
|
+
'nest called outside a lifecycle callback')
|
|
160
|
+
inner = Host(nestopts)
|
|
161
|
+
self._entry['scope'].append(lambda: inner.close())
|
|
162
|
+
self._entry['inner'] = inner
|
|
163
|
+
return inner
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class Host:
|
|
167
|
+
def __init__(self, options=None):
|
|
168
|
+
self._opts = options or {}
|
|
169
|
+
self._dependency = self._opts.get('dependency') or 'restart'
|
|
170
|
+
# Set for the duration of a bulk teardown, so `_held` knows this
|
|
171
|
+
# is a coordinated operation rather than an ad-hoc deactivation.
|
|
172
|
+
self._coordinated = False
|
|
173
|
+
|
|
174
|
+
self.catalog = self._opts.get('catalog') or make_catalog()
|
|
175
|
+
self._reserved = self._opts.get('reserved') or []
|
|
176
|
+
self._points = self._opts.get('points') or {}
|
|
177
|
+
|
|
178
|
+
self._inst = {}
|
|
179
|
+
self._log = []
|
|
180
|
+
# Section 14: the lifecycle event record. `seq` distinguishes ONE
|
|
181
|
+
# INCARNATION of stripe$test from the next, which is the whole
|
|
182
|
+
# reason it is not `pos` (section 4 rule 4).
|
|
183
|
+
self._events = []
|
|
184
|
+
self._seqn = 0
|
|
185
|
+
self._open = 0
|
|
186
|
+
self._intransition = False
|
|
187
|
+
# WHICH callback is running, not merely that one is. Section 8.1
|
|
188
|
+
# puts resource capture in `activate` and 8.3 says `release`
|
|
189
|
+
# outside `activate` is `plugin_release_scope` - and
|
|
190
|
+
# `_intransition` alone cannot tell `activate` from `define`, so
|
|
191
|
+
# it admitted an acquire in `define` whose scope `unload` would
|
|
192
|
+
# never unwind.
|
|
193
|
+
self._phase = None
|
|
194
|
+
|
|
195
|
+
# --- observation --------------------------------------------------
|
|
196
|
+
|
|
197
|
+
def list(self):
|
|
198
|
+
"""Introspection NEVER advances the state (section 5.2). A status
|
|
199
|
+
page must not be a way to accidentally import twenty packages."""
|
|
200
|
+
return {r: self._inst[r]['status'] for r in sorted(self._inst)}
|
|
201
|
+
|
|
202
|
+
def instance(self, ref):
|
|
203
|
+
return self._inst.get(canon_ref(ref))
|
|
204
|
+
|
|
205
|
+
def trace(self):
|
|
206
|
+
return list(self._events)
|
|
207
|
+
|
|
208
|
+
def observable(self, result=None):
|
|
209
|
+
return {
|
|
210
|
+
'status': self.list(),
|
|
211
|
+
'open': self._open,
|
|
212
|
+
'log': list(self._log),
|
|
213
|
+
'result': None if None is result else result,
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
# --- the state machine --------------------------------------------
|
|
217
|
+
|
|
218
|
+
def _guard(self):
|
|
219
|
+
if self._intransition:
|
|
220
|
+
fail('plugin_reentrant',
|
|
221
|
+
'transition attempted from inside a lifecycle callback')
|
|
222
|
+
|
|
223
|
+
def _need(self, ref):
|
|
224
|
+
r = canon_ref(ref)
|
|
225
|
+
entry = self._inst.get(r)
|
|
226
|
+
if not entry:
|
|
227
|
+
fail('plugin_not_loaded', 'no such instance: ' + r, {'ref': r})
|
|
228
|
+
return entry
|
|
229
|
+
|
|
230
|
+
def _checkreserved(self, ref):
|
|
231
|
+
if 0 == len(self._reserved):
|
|
232
|
+
return
|
|
233
|
+
if parse_ref(ref)['name'] in self._reserved:
|
|
234
|
+
fail('plugin_ref_reserved', 'ref is reserved by the host: ' + ref,
|
|
235
|
+
{'ref': ref})
|
|
236
|
+
|
|
237
|
+
def _run(self, entry, callback, at):
|
|
238
|
+
fn = entry['def'].get(callback)
|
|
239
|
+
self._log.append(entry['ref'] + ':' + at)
|
|
240
|
+
self._events.append({'ref': entry['ref'], 'event': at,
|
|
241
|
+
'seq': entry['seq'], 'status': entry['status']})
|
|
242
|
+
if not callable(fn):
|
|
243
|
+
return
|
|
244
|
+
self._intransition = True
|
|
245
|
+
self._phase = at
|
|
246
|
+
try:
|
|
247
|
+
fn(Inst(self, entry))
|
|
248
|
+
except Exception as err:
|
|
249
|
+
# Section 12: `plugin_define_failed` and its three siblings
|
|
250
|
+
# are "a callback raised; wraps the cause". AN ERROR THAT
|
|
251
|
+
# ALREADY CARRIES A CODE KEEPS IT - the code is the error's
|
|
252
|
+
# identity, and a plugin raising `store_unreachable` must not
|
|
253
|
+
# have it rewritten. Only a code-less error is wrapped, which
|
|
254
|
+
# is the ordinary case for a callback that let a library
|
|
255
|
+
# error escape.
|
|
256
|
+
if getattr(err, 'code', None):
|
|
257
|
+
raise
|
|
258
|
+
fail('plugin_' + at + '_failed',
|
|
259
|
+
entry['ref'] + ' raised in ' + at + ': ' + str(err),
|
|
260
|
+
{'ref': entry['ref'], 'cause': str(err)})
|
|
261
|
+
finally:
|
|
262
|
+
self._intransition = False
|
|
263
|
+
self._phase = None
|
|
264
|
+
|
|
265
|
+
def autotag(self, name):
|
|
266
|
+
"""AUTO-TAGGING IS EXPLICIT (section 4 rule 3).
|
|
267
|
+
`declare('stripe', {'tag': '?'})` assigns the LOWEST UNUSED
|
|
268
|
+
POSITIVE INTEGER tag and returns the assigned pair. Without `'?'`,
|
|
269
|
+
a collision is an error.
|
|
270
|
+
|
|
271
|
+
It needs a host because it must know what is already declared,
|
|
272
|
+
which is why it cannot live in the pure `ref` section - the
|
|
273
|
+
correction P1.7 made to section 15.3.
|
|
274
|
+
"""
|
|
275
|
+
n = 1
|
|
276
|
+
while True:
|
|
277
|
+
cand = format_ref(name, str(n))
|
|
278
|
+
if cand not in self._inst:
|
|
279
|
+
return cand
|
|
280
|
+
n += 1
|
|
281
|
+
|
|
282
|
+
def declare(self, ref, spec=None):
|
|
283
|
+
spec = spec or {}
|
|
284
|
+
if '?' == spec.get('tag'):
|
|
285
|
+
ref = self.autotag(parse_ref(canon_ref(ref))['name'])
|
|
286
|
+
r = canon_ref(ref)
|
|
287
|
+
if not spec.get('hostowned'):
|
|
288
|
+
self._checkreserved(r)
|
|
289
|
+
defname = spec.get('definition') or parse_ref(r)['name']
|
|
290
|
+
definition = self.catalog.get(defname)
|
|
291
|
+
if not definition:
|
|
292
|
+
fail('plugin_unknown_definition', 'not in catalog: ' + defname,
|
|
293
|
+
{'name': defname})
|
|
294
|
+
|
|
295
|
+
existing = self._inst.get(r)
|
|
296
|
+
if existing:
|
|
297
|
+
# Section 4 rule 1: a pair addresses at most one instance.
|
|
298
|
+
# Re-declaring the SAME definition is the idempotent case; a
|
|
299
|
+
# different one is a duplicate, not a silent overwrite
|
|
300
|
+
# (seneca) and not an impossibility (sdkgen).
|
|
301
|
+
if existing['def']['name'] != definition['name']:
|
|
302
|
+
fail('plugin_ref_duplicate',
|
|
303
|
+
'instance already declared: ' + r, {'ref': r})
|
|
304
|
+
return existing
|
|
305
|
+
|
|
306
|
+
entry = {
|
|
307
|
+
'ref': r, 'def': definition, 'status': 'declared',
|
|
308
|
+
'pos': len(self._inst) if None is spec.get('pos') else spec['pos'],
|
|
309
|
+
'seq': self._seqn,
|
|
310
|
+
'options': {} if None is spec.get('options') else spec['options'],
|
|
311
|
+
'state': {},
|
|
312
|
+
# Section 11.4's ALWAYS-RELUCTANT rebinding made concrete: the
|
|
313
|
+
# provider ref this instance's activation actually chose, per
|
|
314
|
+
# requirement name. "A satisfied requirement is not re-bound
|
|
315
|
+
# while it stays satisfied" is a statement about a REMEMBERED
|
|
316
|
+
# choice - re-ranking on every question silently re-points a
|
|
317
|
+
# live consumer at any better newcomer, and then losing the
|
|
318
|
+
# provider it was really using does not restart it.
|
|
319
|
+
'selected': {},
|
|
320
|
+
'order': spec.get('order'), 'unmet': [], 'scope': [],
|
|
321
|
+
'bindings': [], 'exports': {}, 'provides': [], 'inner': None,
|
|
322
|
+
}
|
|
323
|
+
self._seqn += 1
|
|
324
|
+
self._inst[r] = entry
|
|
325
|
+
return entry
|
|
326
|
+
|
|
327
|
+
def hostdeclare(self, ref, spec=None):
|
|
328
|
+
"""Section 9.1: a host that reserves a name MUST still be able to
|
|
329
|
+
declare the instance it reserved - "The host declares those
|
|
330
|
+
instances itself, after the user merge, and always wins."
|
|
331
|
+
|
|
332
|
+
THE BOUNDARY IS BY METHOD, NOT BY CALLER, and that is a real
|
|
333
|
+
limit: no language here can tell the embedding host from a plugin
|
|
334
|
+
holding the same host object. What reservation protects is
|
|
335
|
+
CONFIGURATION - documents, overlays, `VOXGIG_PLUGIN_*`,
|
|
336
|
+
construction options and ordinary declare/load/options - and all
|
|
337
|
+
of that still checks.
|
|
338
|
+
"""
|
|
339
|
+
self._guard()
|
|
340
|
+
spec = dict(spec or {})
|
|
341
|
+
spec['hostowned'] = True
|
|
342
|
+
return self.declare(ref, spec)
|
|
343
|
+
|
|
344
|
+
def load(self, ref, spec=None):
|
|
345
|
+
self._guard()
|
|
346
|
+
spec = spec or {}
|
|
347
|
+
entry = self.declare(ref, spec)
|
|
348
|
+
if 'declared' != entry['status']:
|
|
349
|
+
return entry # idempotent in the trivial direction
|
|
350
|
+
# PRESENCE, NOT TRUTHINESS. An empty map is falsy in Python and
|
|
351
|
+
# truthy in JavaScript, so `if spec.get('options')` refused to
|
|
352
|
+
# apply `options: {}` - which is how a caller CLEARS what
|
|
353
|
+
# `declare` set, and which every other port did.
|
|
354
|
+
if None is not spec.get('options'):
|
|
355
|
+
entry['options'] = spec['options']
|
|
356
|
+
try:
|
|
357
|
+
self._run(entry, 'define', 'define')
|
|
358
|
+
except Exception:
|
|
359
|
+
entry['status'] = 'failed'
|
|
360
|
+
raise
|
|
361
|
+
entry['status'] = 'loaded'
|
|
362
|
+
|
|
363
|
+
# AT LOAD, and before anything runs: a cycle through
|
|
364
|
+
# restart-causing requirements does not settle, and the only safe
|
|
365
|
+
# time to report a non-terminating reconcile is before it starts
|
|
366
|
+
# (section 11.3). `provides` is populated by `define`, which has
|
|
367
|
+
# just run, so this is the first moment the graph is complete.
|
|
368
|
+
try:
|
|
369
|
+
checkcycle(self._graphnodes())
|
|
370
|
+
except Exception:
|
|
371
|
+
entry['status'] = 'failed'
|
|
372
|
+
raise
|
|
373
|
+
return entry
|
|
374
|
+
|
|
375
|
+
def _graphnodes(self):
|
|
376
|
+
"""The requirement graph as plain data, for the pure detector."""
|
|
377
|
+
return [
|
|
378
|
+
{'ref': r,
|
|
379
|
+
'provides': [p['name'] for p in self._inst[r]['provides']],
|
|
380
|
+
'requires': requirements(self._inst[r]['options'])}
|
|
381
|
+
for r in sorted(self._inst)
|
|
382
|
+
]
|
|
383
|
+
|
|
384
|
+
def activate(self, ref):
|
|
385
|
+
self._guard()
|
|
386
|
+
entry = self._need(ref)
|
|
387
|
+
if 'live' == entry['status']:
|
|
388
|
+
return entry # no-op returning success
|
|
389
|
+
if 'failed' == entry['status']:
|
|
390
|
+
fail('plugin_bad_state', 'instance has failed: ' + entry['ref'],
|
|
391
|
+
{'ref': entry['ref']})
|
|
392
|
+
# Section 9.6: `active: false` bars the instance from running, and
|
|
393
|
+
# the bar is on the INSTANCE rather than on the apply that set it.
|
|
394
|
+
# `ready` reaches this through `activate`, so one guard covers
|
|
395
|
+
# both verbs the design names.
|
|
396
|
+
if entry.get('barred'):
|
|
397
|
+
fail('plugin_inactive',
|
|
398
|
+
'instance is barred by active: false: ' + entry['ref'],
|
|
399
|
+
{'ref': entry['ref']})
|
|
400
|
+
if 'declared' == entry['status']:
|
|
401
|
+
self.load(entry['ref'])
|
|
402
|
+
|
|
403
|
+
# A declared requirement that is not live means `pending`:
|
|
404
|
+
# activation is a STANDING REQUEST, not a one-shot event.
|
|
405
|
+
unmet = self._unmetof(entry)
|
|
406
|
+
if 0 < len(unmet):
|
|
407
|
+
entry['unmet'] = unmet
|
|
408
|
+
entry['status'] = 'pending'
|
|
409
|
+
return entry
|
|
410
|
+
|
|
411
|
+
try:
|
|
412
|
+
self._run(entry, 'activate', 'activate')
|
|
413
|
+
except Exception:
|
|
414
|
+
# Unwind whatever the partial activation captured, in reverse.
|
|
415
|
+
self._unwind(entry)
|
|
416
|
+
entry['status'] = 'failed'
|
|
417
|
+
raise
|
|
418
|
+
# Section 11.4: THE SELECTION IS MADE HERE, once, and remembered.
|
|
419
|
+
# Every later question - the cascade, `hold`, `unmet` - reads it
|
|
420
|
+
# back rather than re-ranking, which is what "always-reluctant"
|
|
421
|
+
# means.
|
|
422
|
+
for req in requirements(entry['options']):
|
|
423
|
+
self._chosen(entry, req, True)
|
|
424
|
+
entry['status'] = 'live'
|
|
425
|
+
self._reconcile()
|
|
426
|
+
return entry
|
|
427
|
+
|
|
428
|
+
def deactivate(self, ref):
|
|
429
|
+
self._guard()
|
|
430
|
+
entry = self._need(ref)
|
|
431
|
+
if entry['status'] in ('loaded', 'declared'):
|
|
432
|
+
return entry
|
|
433
|
+
|
|
434
|
+
# Section 5.2: `unload` is THE ONLY TRANSITION OUT OF `failed`.
|
|
435
|
+
if 'failed' == entry['status']:
|
|
436
|
+
fail('plugin_bad_state', 'instance has failed: ' + entry['ref'],
|
|
437
|
+
{'ref': entry['ref']})
|
|
438
|
+
|
|
439
|
+
if 'pending' == entry['status']:
|
|
440
|
+
# DEACTIVATING A PENDING INSTANCE RUNS NO CALLBACK (section
|
|
441
|
+
# 5.2). It never reached activate, so it holds no scope and no
|
|
442
|
+
# live bindings; running the definition's deactivate there
|
|
443
|
+
# would be teardown without matching setup, which plugins are
|
|
444
|
+
# not written to survive and which could fail an instance that
|
|
445
|
+
# had done nothing wrong. It cannot fail.
|
|
446
|
+
entry['status'] = 'loaded'
|
|
447
|
+
entry['unmet'] = []
|
|
448
|
+
return entry
|
|
449
|
+
|
|
450
|
+
self._held(entry)
|
|
451
|
+
self._cascade(entry, {})
|
|
452
|
+
|
|
453
|
+
try:
|
|
454
|
+
self._run(entry, 'deactivate', 'deactivate')
|
|
455
|
+
except Exception:
|
|
456
|
+
self._unwind(entry)
|
|
457
|
+
entry['status'] = 'failed'
|
|
458
|
+
raise
|
|
459
|
+
self._releasecheck(entry, self._unwind(entry))
|
|
460
|
+
entry['status'] = 'loaded'
|
|
461
|
+
self._reconcile()
|
|
462
|
+
return entry
|
|
463
|
+
|
|
464
|
+
def unload(self, ref):
|
|
465
|
+
self._guard()
|
|
466
|
+
entry = self._need(ref)
|
|
467
|
+
if entry['status'] in ('live', 'pending'):
|
|
468
|
+
if 'live' == entry['status']:
|
|
469
|
+
self._held(entry)
|
|
470
|
+
self._cascade(entry, {})
|
|
471
|
+
try:
|
|
472
|
+
self._run(entry, 'deactivate', 'deactivate')
|
|
473
|
+
except Exception:
|
|
474
|
+
# Section 5.2: ANY failure during a transition lands
|
|
475
|
+
# the instance in `failed`, with the scope STILL FULLY
|
|
476
|
+
# UNWOUND - and the instance STAYS REGISTERED, because
|
|
477
|
+
# `failed` is a state an operator has to be able to
|
|
478
|
+
# see.
|
|
479
|
+
self._unwind(entry)
|
|
480
|
+
entry['status'] = 'failed'
|
|
481
|
+
raise
|
|
482
|
+
self._releasecheck(entry, self._unwind(entry))
|
|
483
|
+
entry['status'] = 'loaded'
|
|
484
|
+
if entry['status'] in ('loaded', 'failed'):
|
|
485
|
+
try:
|
|
486
|
+
self._run(entry, 'close', 'close')
|
|
487
|
+
finally:
|
|
488
|
+
del self._inst[entry['ref']]
|
|
489
|
+
return
|
|
490
|
+
del self._inst[entry['ref']]
|
|
491
|
+
|
|
492
|
+
def ready(self, ref):
|
|
493
|
+
"""Runs the whole forward path in one call (section 5.1). Section
|
|
494
|
+
15.2's verb list omits this; 5.1 defines it and 15.3's `declare`
|
|
495
|
+
row requires the corpus to pin it, so the list was incomplete
|
|
496
|
+
rather than excluding it (DOCS.md section 4.2)."""
|
|
497
|
+
self._guard()
|
|
498
|
+
r = canon_ref(ref)
|
|
499
|
+
if r not in self._inst:
|
|
500
|
+
self.declare(r)
|
|
501
|
+
if 'declared' == self._inst[r]['status']:
|
|
502
|
+
self.load(r)
|
|
503
|
+
return self.activate(r)
|
|
504
|
+
|
|
505
|
+
def _unwind(self, entry):
|
|
506
|
+
"""Bindings go live only when activation succeeds (section 8.1),
|
|
507
|
+
so the teardown is the exact inverse: reverse order, always.
|
|
508
|
+
|
|
509
|
+
Returns the errors the scope raised. Section 8.3: "A failing
|
|
510
|
+
release does not stop the rest. Every entry runs, in reverse
|
|
511
|
+
order, whatever any of them does; the errors are collected and
|
|
512
|
+
raised as one `plugin_release_failed`."
|
|
513
|
+
"""
|
|
514
|
+
# A selection belongs to ONE activation (section 11.4). Leaving
|
|
515
|
+
# `live` by any door drops it, so the next activation ranks
|
|
516
|
+
# afresh - keeping it would make a consumer prefer a provider it
|
|
517
|
+
# never actually ran against.
|
|
518
|
+
entry['selected'] = {}
|
|
519
|
+
errors = []
|
|
520
|
+
for i in range(len(entry['scope']) - 1, -1, -1):
|
|
521
|
+
try:
|
|
522
|
+
entry['scope'][i]()
|
|
523
|
+
except Exception as err:
|
|
524
|
+
errors.append(err)
|
|
525
|
+
entry['scope'] = []
|
|
526
|
+
return errors
|
|
527
|
+
|
|
528
|
+
def _releasecheck(self, entry, errors):
|
|
529
|
+
"""Section 8.3: "A failed release ends the instance in `failed`,
|
|
530
|
+
exactly as a failed callback does (5.2) - a release that raised
|
|
531
|
+
may have leaked, and an instance that may be holding resources it
|
|
532
|
+
cannot account for must not be reactivated."
|
|
533
|
+
"""
|
|
534
|
+
if 0 == len(errors):
|
|
535
|
+
return
|
|
536
|
+
entry['status'] = 'failed'
|
|
537
|
+
causes = [str(e) for e in errors]
|
|
538
|
+
fail('plugin_release_failed',
|
|
539
|
+
'release failed for ' + entry['ref'] + ': ' + '; '.join(causes),
|
|
540
|
+
{'ref': entry['ref'], 'cause': causes})
|
|
541
|
+
|
|
542
|
+
def _unmetof(self, entry):
|
|
543
|
+
"""A REQUIREMENT IS ON A CAPABILITY, not on a ref (section 11.1) -
|
|
544
|
+
it is a dependency on something that can do the job, and which
|
|
545
|
+
instance is doing it is exactly the configuration detail a plugin
|
|
546
|
+
must not care about. A bare string is shorthand for `{name}`.
|
|
547
|
+
|
|
548
|
+
A ref satisfies too, because a host that genuinely needs a
|
|
549
|
+
specific instance should not have to invent a capability for it.
|
|
550
|
+
"""
|
|
551
|
+
return [
|
|
552
|
+
req['name'] for req in requirements(entry['options'])
|
|
553
|
+
if gatesactivation(req) and 0 == len(self._providersof(req))
|
|
554
|
+
]
|
|
555
|
+
|
|
556
|
+
def _boundproviders(self, entry):
|
|
557
|
+
"""The instance currently SELECTED for each of this one's
|
|
558
|
+
restart-causing requirements. A BINDING IS TO AN INSTANCE, not to
|
|
559
|
+
a capability (section 11.1), and that is what decides behaviour
|
|
560
|
+
when the bound provider leaves while another match remains: the
|
|
561
|
+
selected one going away restarts a `static` consumer even though a
|
|
562
|
+
survivor is available. It is not silently re-pointed - `static` is
|
|
563
|
+
the plugin saying in writing that it cannot survive a provider
|
|
564
|
+
swap, and a survivor being available does not make the swap
|
|
565
|
+
survivable."""
|
|
566
|
+
out = []
|
|
567
|
+
for req in requirements(entry['options']):
|
|
568
|
+
if not restartsonloss(req):
|
|
569
|
+
continue
|
|
570
|
+
ref = self._chosen(entry, req, False)
|
|
571
|
+
if None is not ref and ref not in out:
|
|
572
|
+
out.append(ref)
|
|
573
|
+
return out
|
|
574
|
+
|
|
575
|
+
def _consumersof(self, ref):
|
|
576
|
+
"""Live instances whose selected provider is `ref` and which would
|
|
577
|
+
be restarted by losing it."""
|
|
578
|
+
return [
|
|
579
|
+
r for r in sorted(self._inst)
|
|
580
|
+
if r != ref and 'live' == self._inst[r]['status']
|
|
581
|
+
and ref in self._boundproviders(self._inst[r])
|
|
582
|
+
]
|
|
583
|
+
|
|
584
|
+
def _chosen(self, entry, req, remember):
|
|
585
|
+
"""Section 11.4's always-reluctant selection, and the ONE place a
|
|
586
|
+
provider is picked for a live instance.
|
|
587
|
+
|
|
588
|
+
If this instance already selected a provider for `req` and that
|
|
589
|
+
provider is STILL a candidate, it keeps it - a better-ranked
|
|
590
|
+
newcomer does not take it. `remember` is false for the questions
|
|
591
|
+
asked ABOUT an instance rather than BY it: introspection must not
|
|
592
|
+
create a binding.
|
|
593
|
+
"""
|
|
594
|
+
cands = self._providersof(req)
|
|
595
|
+
if 0 == len(cands):
|
|
596
|
+
return None
|
|
597
|
+
held = entry['selected'].get(req['name'])
|
|
598
|
+
if None is not held and any(c['ref'] == held for c in cands):
|
|
599
|
+
return held
|
|
600
|
+
if remember:
|
|
601
|
+
entry['selected'][req['name']] = cands[0]['ref']
|
|
602
|
+
return cands[0]['ref']
|
|
603
|
+
|
|
604
|
+
def _holdersof(self, ref):
|
|
605
|
+
"""Section 11.3's `hold` asks a DIFFERENT question from the
|
|
606
|
+
cascade, and reading it off `_consumersof` answered the
|
|
607
|
+
cascade's.
|
|
608
|
+
|
|
609
|
+
The cascade wants the edges that RESTART - mandatory-static and
|
|
610
|
+
optional-static - because a restart is what it performs. `hold`
|
|
611
|
+
says "deactivating a REQUIRED instance is
|
|
612
|
+
`plugin_dependency_held`", and required is cardinality:
|
|
613
|
+
`gatesactivation`, not `restartsonloss`. The two sets differ in
|
|
614
|
+
both directions and each difference was a real bug.
|
|
615
|
+
|
|
616
|
+
A MANDATORY-DYNAMIC consumer was excluded, so the strictest
|
|
617
|
+
policy let a provider go that a live consumer could not do
|
|
618
|
+
without - `dynamic` promises survival of a SWAP, and under
|
|
619
|
+
`hold` there is no swap, so the consumer falls back to
|
|
620
|
+
`pending`.
|
|
621
|
+
|
|
622
|
+
An OPTIONAL-STATIC consumer was included, so `hold` refused a
|
|
623
|
+
deactivation on behalf of an instance that had said in writing
|
|
624
|
+
it does not need the thing.
|
|
625
|
+
"""
|
|
626
|
+
out = []
|
|
627
|
+
for r in sorted(self._inst):
|
|
628
|
+
consumer = self._inst[r]
|
|
629
|
+
if r == ref or 'live' != consumer['status']:
|
|
630
|
+
continue
|
|
631
|
+
for req in requirements(consumer['options']):
|
|
632
|
+
if not gatesactivation(req):
|
|
633
|
+
continue
|
|
634
|
+
if self._chosen(consumer, req, False) == ref:
|
|
635
|
+
out.append(r)
|
|
636
|
+
break
|
|
637
|
+
return out
|
|
638
|
+
|
|
639
|
+
def _providersof(self, req):
|
|
640
|
+
cands = []
|
|
641
|
+
# ASK WHETHER THE NAME IS A REF, do not assume it. A requirement
|
|
642
|
+
# name is a CAPABILITY name first (section 11.1) and capability
|
|
643
|
+
# names are free-form, so `2fa` is a legal one that no ref could
|
|
644
|
+
# be called. A bare try/except around `canon_ref` did the same job
|
|
645
|
+
# here, but only here - and it read as defensive rather than as
|
|
646
|
+
# the rule, so `dependencycycle` and `graph.candidates` did not
|
|
647
|
+
# get it. `try_ref` is that rule, named, and used in all three.
|
|
648
|
+
want = try_ref(req['name'])
|
|
649
|
+
for ref in sorted(self._inst):
|
|
650
|
+
target = self._inst[ref]
|
|
651
|
+
if 'live' != target['status']:
|
|
652
|
+
continue
|
|
653
|
+
# A ref satisfies directly.
|
|
654
|
+
if ref == want:
|
|
655
|
+
cands.append({'ref': ref, 'pos': target['pos'],
|
|
656
|
+
'provides': {'name': req['name']}})
|
|
657
|
+
continue
|
|
658
|
+
for prov in target['provides']:
|
|
659
|
+
if prov.get('name') == req['name']:
|
|
660
|
+
cands.append({'ref': ref, 'pos': target['pos'],
|
|
661
|
+
'provides': prov})
|
|
662
|
+
return resolve_capability(req, cands)
|
|
663
|
+
|
|
664
|
+
def _cascade(self, provider, seen):
|
|
665
|
+
"""CONSUMERS GO DOWN FIRST, NOT AFTERWARDS (section 11.3).
|
|
666
|
+
|
|
667
|
+
The cascade is part of the provider's own deactivation and runs
|
|
668
|
+
BEFORE the provider's `deactivate` callback and scope unwind, so a
|
|
669
|
+
consumer's teardown can still call the thing it depends on -
|
|
670
|
+
flushing a buffer to the store it is about to lose is exactly what
|
|
671
|
+
a `deactivate` callback is for, and a cascade that fired after the
|
|
672
|
+
provider was already gone would make that impossible.
|
|
673
|
+
|
|
674
|
+
Order: consumers deepest-first, then the provider. `unload` and
|
|
675
|
+
`close` inherit it, UNDER EITHER DEPENDENCY POLICY, which is what
|
|
676
|
+
makes apply's reverse-load-order teardown safe even when a
|
|
677
|
+
document happens to list a consumer before its provider.
|
|
678
|
+
"""
|
|
679
|
+
if seen.get(provider['ref']):
|
|
680
|
+
return
|
|
681
|
+
seen[provider['ref']] = True
|
|
682
|
+
|
|
683
|
+
for ref in self._consumersof(provider['ref']):
|
|
684
|
+
consumer = self._inst[ref]
|
|
685
|
+
if 'live' != consumer['status']:
|
|
686
|
+
continue
|
|
687
|
+
self._cascade(consumer, seen) # deepest-first
|
|
688
|
+
bad = False
|
|
689
|
+
try:
|
|
690
|
+
self._run(consumer, 'deactivate', 'deactivate')
|
|
691
|
+
except Exception:
|
|
692
|
+
bad = True
|
|
693
|
+
errors = self._unwind(consumer)
|
|
694
|
+
if bad or 0 < len(errors):
|
|
695
|
+
# Section 5.2: ANY failure during a transition lands the
|
|
696
|
+
# instance in `failed`. Marking it `pending` handed it
|
|
697
|
+
# straight back to `_reconcile`, which would activate it
|
|
698
|
+
# again the moment the provider returned.
|
|
699
|
+
consumer['status'] = 'failed'
|
|
700
|
+
continue
|
|
701
|
+
consumer['status'] = 'pending'
|
|
702
|
+
consumer['unmet'] = self._unmetof(consumer)
|
|
703
|
+
|
|
704
|
+
def _held(self, entry):
|
|
705
|
+
"""The hold check is A GUARD ON AD-HOC DEACTIVATION, NOT ON
|
|
706
|
+
COORDINATED TEARDOWN. In a bulk operation that is removing the
|
|
707
|
+
holders too - `close()`, or an `apply` plan whose own steps
|
|
708
|
+
deactivate them - it is suspended for exactly those holders, and
|
|
709
|
+
the teardown still runs consumers before providers.
|
|
710
|
+
|
|
711
|
+
Otherwise `close()` under `hold` would raise on the first provider
|
|
712
|
+
it reached whenever a document happened to list a consumer after
|
|
713
|
+
it, which is the policy refusing to allow the one teardown it has
|
|
714
|
+
no reason to object to.
|
|
715
|
+
"""
|
|
716
|
+
if 'hold' != self._dependency:
|
|
717
|
+
return
|
|
718
|
+
if self._coordinated:
|
|
719
|
+
return
|
|
720
|
+
holders = self._holdersof(entry['ref'])
|
|
721
|
+
if 0 == len(holders):
|
|
722
|
+
return
|
|
723
|
+
fail('plugin_dependency_held',
|
|
724
|
+
'instance is required by live consumers: ' + entry['ref'],
|
|
725
|
+
{'ref': entry['ref'], 'holders': holders})
|
|
726
|
+
|
|
727
|
+
def _reconcile(self):
|
|
728
|
+
"""EAGER reconciliation: run to a fixed point rather than
|
|
729
|
+
scheduling.
|
|
730
|
+
|
|
731
|
+
Two directions, and both are the reason `pending` exists.
|
|
732
|
+
Activation is a STANDING REQUEST, not a one-shot event: a pending
|
|
733
|
+
instance whose requirement arrives activates without being asked
|
|
734
|
+
again, and a LIVE instance whose requirement is lost goes back to
|
|
735
|
+
pending - recursively, through its own consumers.
|
|
736
|
+
"""
|
|
737
|
+
moved = True
|
|
738
|
+
rounds = 0
|
|
739
|
+
while moved:
|
|
740
|
+
moved = False
|
|
741
|
+
rounds += 1
|
|
742
|
+
if 1000 < rounds:
|
|
743
|
+
break
|
|
744
|
+
|
|
745
|
+
# Losses first, so a cascade settles in one pass rather than
|
|
746
|
+
# alternating with re-activations.
|
|
747
|
+
for ref in sorted(self._inst):
|
|
748
|
+
entry = self._inst[ref]
|
|
749
|
+
if 'live' != entry['status']:
|
|
750
|
+
continue
|
|
751
|
+
lost = [q for q in requirements(entry['options'])
|
|
752
|
+
if gatesactivation(q)
|
|
753
|
+
and 0 == len(self._providersof(q))]
|
|
754
|
+
if 0 == len(lost):
|
|
755
|
+
continue
|
|
756
|
+
# POLICY IS PER REQUIREMENT, not per instance (section
|
|
757
|
+
# 11.3): only the definition that has the requirement
|
|
758
|
+
# knows what it can cope with, and one instance may hold
|
|
759
|
+
# both a `static` and a `dynamic` one. A `dynamic`
|
|
760
|
+
# requirement whose provider is gone leaves the consumer
|
|
761
|
+
# LIVE and notified; it is a statement about surviving a
|
|
762
|
+
# swap, so it does not restart here.
|
|
763
|
+
if not any(restartsonloss(q) for q in lost):
|
|
764
|
+
continue
|
|
765
|
+
bad = False
|
|
766
|
+
try:
|
|
767
|
+
self._run(entry, 'deactivate', 'deactivate')
|
|
768
|
+
except Exception:
|
|
769
|
+
bad = True
|
|
770
|
+
errors = self._unwind(entry)
|
|
771
|
+
if bad or 0 < len(errors):
|
|
772
|
+
entry['status'] = 'failed'
|
|
773
|
+
moved = True
|
|
774
|
+
continue
|
|
775
|
+
entry['status'] = 'pending'
|
|
776
|
+
entry['unmet'] = self._unmetof(entry)
|
|
777
|
+
moved = True
|
|
778
|
+
|
|
779
|
+
for ref in sorted(self._inst):
|
|
780
|
+
entry = self._inst[ref]
|
|
781
|
+
if 'pending' != entry['status']:
|
|
782
|
+
continue
|
|
783
|
+
if 0 < len(self._unmetof(entry)):
|
|
784
|
+
continue
|
|
785
|
+
try:
|
|
786
|
+
self._run(entry, 'activate', 'activate')
|
|
787
|
+
entry['status'] = 'live'
|
|
788
|
+
entry['unmet'] = []
|
|
789
|
+
moved = True
|
|
790
|
+
except Exception:
|
|
791
|
+
self._unwind(entry)
|
|
792
|
+
entry['status'] = 'failed'
|
|
793
|
+
moved = True
|
|
794
|
+
|
|
795
|
+
# --- ordering -----------------------------------------------------
|
|
796
|
+
|
|
797
|
+
def order(self, point=None):
|
|
798
|
+
# Sorted by declaration SEQUENCE, which is what makes the
|
|
799
|
+
# section 7 sort's fall-through deterministic in a language whose
|
|
800
|
+
# maps have no insertion order. Section 7 breaks ties by `pos`;
|
|
801
|
+
# two instances CAN share one - `declare` defaults `pos` to the
|
|
802
|
+
# registry size, so an unload followed by a fresh declare reuses
|
|
803
|
+
# a surviving instance's - and past that this was falling through
|
|
804
|
+
# to dict order. `seq` is that order, made explicit.
|
|
805
|
+
live = [r for r in self._inst if 'live' == self._inst[r]['status']]
|
|
806
|
+
live.sort(key=lambda r: self._inst[r]['seq'])
|
|
807
|
+
bindings = [
|
|
808
|
+
{'ref': r, 'pos': self._inst[r]['pos'],
|
|
809
|
+
'order': self._inst[r]['order']}
|
|
810
|
+
for r in live
|
|
811
|
+
]
|
|
812
|
+
spec = self._points.get(point) if point else None
|
|
813
|
+
return resolve_order(bindings, spec.get('pin') if spec else None)
|
|
814
|
+
|
|
815
|
+
# --- points -------------------------------------------------------
|
|
816
|
+
|
|
817
|
+
def _bound(self, point):
|
|
818
|
+
"""Live bindings on a point, in resolved order. Recomputed on any
|
|
819
|
+
change to the live set (section 7) rather than cached at startup -
|
|
820
|
+
the bug a host discovers only when something deactivates in
|
|
821
|
+
production."""
|
|
822
|
+
out = []
|
|
823
|
+
for ref in self.order(point):
|
|
824
|
+
entry = self._inst[ref]
|
|
825
|
+
# The band is the INSTANCE's ordering block (section 7),
|
|
826
|
+
# stamped by the host. A plugin passing its own would be
|
|
827
|
+
# ranking itself above the order its document declared.
|
|
828
|
+
block = entry['order'] or {}
|
|
829
|
+
band = block.get('band')
|
|
830
|
+
band = band if isinstance(band, int) and not isinstance(band, bool) else 0
|
|
831
|
+
for b in entry['bindings']:
|
|
832
|
+
if b['point'] == point:
|
|
833
|
+
out.append(dict(b, band=band))
|
|
834
|
+
return out
|
|
835
|
+
|
|
836
|
+
def _pointspec(self, point, want):
|
|
837
|
+
spec = self._points.get(point)
|
|
838
|
+
if None is spec:
|
|
839
|
+
fail('plugin_point_unknown', 'no such point: ' + point,
|
|
840
|
+
{'point': point})
|
|
841
|
+
kind = spec.get('kind')
|
|
842
|
+
if 'hook' == want:
|
|
843
|
+
# A point with no declared kind is a hook, which is what makes
|
|
844
|
+
# `{}` the minimal point declaration.
|
|
845
|
+
if kind and 'hook' != kind:
|
|
846
|
+
fail('plugin_point_kind', 'point is not a hook: ' + point,
|
|
847
|
+
{'point': point, 'kind': kind})
|
|
848
|
+
return spec
|
|
849
|
+
if kind != want:
|
|
850
|
+
fail('plugin_point_kind',
|
|
851
|
+
'point is not a ' + want + ': ' + point,
|
|
852
|
+
{'point': point, 'kind': kind})
|
|
853
|
+
return spec
|
|
854
|
+
|
|
855
|
+
def emit(self, point, arg=None):
|
|
856
|
+
spec = self._pointspec(point, 'hook')
|
|
857
|
+
return fanout(self._bound(point), spec.get('mode') or 'emit', arg)
|
|
858
|
+
|
|
859
|
+
def call(self, point, *args):
|
|
860
|
+
spec = self._pointspec(point, 'chain')
|
|
861
|
+
base = spec.get('base') or (lambda *a: a[0] if 0 < len(a) else None)
|
|
862
|
+
return compose(self._bound(point), base)(*args)
|
|
863
|
+
|
|
864
|
+
def provider(self, point, *args):
|
|
865
|
+
spec = self._pointspec(point, 'provider')
|
|
866
|
+
pick = pickone(self._bound(point), spec)
|
|
867
|
+
if not pick['winner']:
|
|
868
|
+
return spec.get('default')
|
|
869
|
+
return pick['winner']['fn'](*args)
|
|
870
|
+
|
|
871
|
+
def shadowed(self, point):
|
|
872
|
+
"""The losers are VISIBLE rather than silently ignored (section
|
|
873
|
+
6.3)."""
|
|
874
|
+
spec = self._points.get(point)
|
|
875
|
+
if None is spec:
|
|
876
|
+
return []
|
|
877
|
+
return pickone(self._bound(point), spec)['shadowed']
|
|
878
|
+
|
|
879
|
+
def exports(self, spec):
|
|
880
|
+
allof = []
|
|
881
|
+
for ref in sorted(self._inst):
|
|
882
|
+
entry = self._inst[ref]
|
|
883
|
+
# Exports of a `loaded` (not live) instance are VISIBLE
|
|
884
|
+
# (section 11).
|
|
885
|
+
if entry['status'] in ('declared', 'failed'):
|
|
886
|
+
continue
|
|
887
|
+
for key in sorted(entry['exports']):
|
|
888
|
+
allof.append({'ref': ref, 'key': key,
|
|
889
|
+
'value': entry['exports'][key]})
|
|
890
|
+
return resolve_export(spec, allof)
|
|
891
|
+
|
|
892
|
+
def capability(self, name):
|
|
893
|
+
"""The live providers of a capability, best-first (section
|
|
894
|
+
11.1)."""
|
|
895
|
+
cands = []
|
|
896
|
+
for ref in sorted(self._inst):
|
|
897
|
+
entry = self._inst[ref]
|
|
898
|
+
if 'live' != entry['status']:
|
|
899
|
+
continue
|
|
900
|
+
for prov in entry['provides']:
|
|
901
|
+
if prov.get('name') == name:
|
|
902
|
+
cands.append({'ref': ref, 'pos': entry['pos'],
|
|
903
|
+
'provides': prov})
|
|
904
|
+
return [c['ref'] for c in resolve_capability({'name': name}, cands)]
|
|
905
|
+
|
|
906
|
+
# --- documents ----------------------------------------------------
|
|
907
|
+
|
|
908
|
+
def apply(self, doc, profile=None):
|
|
909
|
+
"""Section 9.6: "load what is missing, UNLOAD WHAT IS GONE, patch
|
|
910
|
+
what changed, and move activation state to match", with the
|
|
911
|
+
stated ordering - "deactivations and unloads first (reverse load
|
|
912
|
+
order), then loads, then activations in load order".
|
|
913
|
+
|
|
914
|
+
FOUR PHASES, NOT ONE INTERLEAVED LOOP. An earlier draft walked
|
|
915
|
+
the document once, which never looked at instances the new
|
|
916
|
+
document had DROPPED - so an integration removed from a config
|
|
917
|
+
reload stayed live with its bindings and resources.
|
|
918
|
+
"""
|
|
919
|
+
self._guard()
|
|
920
|
+
profile = profile or self._opts.get('profile')
|
|
921
|
+
norm = normalize_config({
|
|
922
|
+
'doc': doc, 'profile': profile,
|
|
923
|
+
'keys': self._opts.get('keys'), 'reserved': self._reserved,
|
|
924
|
+
})
|
|
925
|
+
|
|
926
|
+
want = norm['order']
|
|
927
|
+
defaults = self._opts.get('defaults') or {}
|
|
928
|
+
optionsof = {}
|
|
929
|
+
for ref in want:
|
|
930
|
+
optionsof[ref] = resolve_options({
|
|
931
|
+
'ref': ref, 'doc': doc, 'profile': profile,
|
|
932
|
+
'shape': self._shapeof(ref),
|
|
933
|
+
'hostdefaults': defaults.get(parse_ref(ref)['name']),
|
|
934
|
+
})
|
|
935
|
+
|
|
936
|
+
def wantlive(ref):
|
|
937
|
+
"""Should this ref be LIVE after the apply? False for a ref
|
|
938
|
+
the document declares lazy or inactive AND for one it does
|
|
939
|
+
not name at all - which is what makes "unload what is gone"
|
|
940
|
+
and "unload what was toggled off" one rule rather than two."""
|
|
941
|
+
ent = norm['instance'].get(ref)
|
|
942
|
+
return None is not ent and ent['active'] and 'eager' == ent['start']
|
|
943
|
+
|
|
944
|
+
# --- phase 1: deactivations and unloads, REVERSE load order ---
|
|
945
|
+
drop = [r for r in self._inst
|
|
946
|
+
if 'declared' != self._inst[r]['status'] and not wantlive(r)]
|
|
947
|
+
# Highest `pos` first, ref-descending for a tie, so a consumer
|
|
948
|
+
# declared after its provider goes down first.
|
|
949
|
+
drop.sort(key=lambda r: (-self._inst[r]['pos'],
|
|
950
|
+
tuple(-ord(c) for c in r)))
|
|
951
|
+
for ref in drop:
|
|
952
|
+
self.unload(ref)
|
|
953
|
+
|
|
954
|
+
# --- phase 2: declare and patch EVERYTHING, in load order -----
|
|
955
|
+
for ref in want:
|
|
956
|
+
ent = norm['instance'][ref]
|
|
957
|
+
# NO OPTIONS HERE, and the omission is the fix rather than
|
|
958
|
+
# an oversight. `declare` ADOPTS the options map it is handed
|
|
959
|
+
# as the instance's own, so passing the resolved map made
|
|
960
|
+
# target and source THE SAME MAP in the refill below - which
|
|
961
|
+
# cleared its own source and left a first-time instance with
|
|
962
|
+
# no options at all. `declare` makes its own empty map and
|
|
963
|
+
# the refill fills it, so both paths are now one path.
|
|
964
|
+
self.declare(ref, {'order': ent.get('order'), 'pos': ent['pos']})
|
|
965
|
+
# The bar is REASSERTED ON EVERY APPLY, in both directions - a
|
|
966
|
+
# document that turns the instance back on clears it, which is
|
|
967
|
+
# the whole point of a config switch.
|
|
968
|
+
self._inst[ref]['barred'] = not ent['active']
|
|
969
|
+
# REFILL rather than REBIND. A definition's callbacks close
|
|
970
|
+
# over the options map they were handed at `define`.
|
|
971
|
+
refill(self._inst[ref]['options'], optionsof[ref])
|
|
972
|
+
self._inst[ref]['order'] = ent.get('order')
|
|
973
|
+
self._inst[ref]['pos'] = ent['pos']
|
|
974
|
+
|
|
975
|
+
# --- phase 3: loads, in load order ----------------------------
|
|
976
|
+
# ONLY THE EAGER, ACTIVE ONES: "a document of twenty lazy
|
|
977
|
+
# instances is twenty map entries and no executed code" (9.6).
|
|
978
|
+
for ref in want:
|
|
979
|
+
if wantlive(ref):
|
|
980
|
+
self.load(ref)
|
|
981
|
+
|
|
982
|
+
# --- phase 4: activations, in load order ----------------------
|
|
983
|
+
for ref in want:
|
|
984
|
+
if wantlive(ref):
|
|
985
|
+
self.activate(ref)
|
|
986
|
+
|
|
987
|
+
def _shapeof(self, ref):
|
|
988
|
+
definition = self.catalog.get(parse_ref(ref)['name'])
|
|
989
|
+
return definition.get('shape') if definition else None
|
|
990
|
+
|
|
991
|
+
def options(self, ref, patch):
|
|
992
|
+
self._guard()
|
|
993
|
+
entry = self._need(ref)
|
|
994
|
+
previous = dict(entry['options'])
|
|
995
|
+
refill(entry['options'], resolve_options({
|
|
996
|
+
'ref': entry['ref'], 'shape': self._shapeof(entry['ref']),
|
|
997
|
+
'doc': {}, 'patch': dict(previous, **(patch or {})),
|
|
998
|
+
}))
|
|
999
|
+
if 'live' == entry['status']:
|
|
1000
|
+
reconfigure = entry['def'].get('reconfigure')
|
|
1001
|
+
if callable(reconfigure):
|
|
1002
|
+
self._intransition = True
|
|
1003
|
+
try:
|
|
1004
|
+
reconfigure(Inst(self, entry), entry['options'], previous)
|
|
1005
|
+
finally:
|
|
1006
|
+
self._intransition = False
|
|
1007
|
+
else:
|
|
1008
|
+
# Always correct and sometimes expensive; `reconfigure`
|
|
1009
|
+
# exists to make the common case cheap (section 9.4).
|
|
1010
|
+
self.deactivate(entry['ref'])
|
|
1011
|
+
self.activate(entry['ref'])
|
|
1012
|
+
|
|
1013
|
+
def close(self):
|
|
1014
|
+
# A bulk teardown removing the holders too, so `hold` is suspended
|
|
1015
|
+
# for exactly those holders (section 11.3) - while the
|
|
1016
|
+
# consumers-first cascade still runs, which is the half that
|
|
1017
|
+
# matters.
|
|
1018
|
+
self._coordinated = True
|
|
1019
|
+
try:
|
|
1020
|
+
for ref in reversed(sorted(self._inst)):
|
|
1021
|
+
self.unload(ref)
|
|
1022
|
+
finally:
|
|
1023
|
+
self._coordinated = False
|
|
1024
|
+
|
|
1025
|
+
def positionof(self, ref, point):
|
|
1026
|
+
"""The same record section 6.6 gives a plugin about itself,
|
|
1027
|
+
reachable from outside for the corpus. A plugin asks via
|
|
1028
|
+
`inst.position(point)`."""
|
|
1029
|
+
entry = self._inst.get(canon_ref(ref))
|
|
1030
|
+
if not entry:
|
|
1031
|
+
fail('plugin_not_loaded', 'no such instance: ' + ref,
|
|
1032
|
+
{'ref': ref})
|
|
1033
|
+
ranked = self.order(point)
|
|
1034
|
+
index = ranked.index(entry['ref']) if entry['ref'] in ranked else -1
|
|
1035
|
+
return {
|
|
1036
|
+
'index': index, 'count': len(ranked),
|
|
1037
|
+
# Section 6.2 composes b1(b2(b3(base))) with the FIRST binding
|
|
1038
|
+
# OUTERMOST, so these are not index 0 and index count-1 the
|
|
1039
|
+
# other way round. Getting this backwards is the exact error
|
|
1040
|
+
# the positional pin vocabulary exists to prevent.
|
|
1041
|
+
'outermost': 0 == index,
|
|
1042
|
+
'innermost': index == len(ranked) - 1,
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
def define(self, definition):
|
|
1046
|
+
self.catalog.add(definition)
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
def refill(target, source):
|
|
1050
|
+
"""Empty the target and refill it, so callers holding the reference
|
|
1051
|
+
see the new values."""
|
|
1052
|
+
target.clear()
|
|
1053
|
+
target.update(source or {})
|