@voxgig/sdkgen 4.8.1 → 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 +2 -2
- 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,1575 @@
|
|
|
1
|
+
// VENDORED: @voxgig/plugin 0.1.6 (go/plugin/host.go)
|
|
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 (§5), extension points (§6), and
|
|
5
|
+
* resource capture (§8).
|
|
6
|
+
*
|
|
7
|
+
* TWO RULES SHAPE EVERY METHOD BELOW.
|
|
8
|
+
*
|
|
9
|
+
* Transitions are SEQUENTIAL (§5.2). One at a time, in call order, never
|
|
10
|
+
* 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 (§18's portability budget). A transition
|
|
16
|
+
* settles by running the state machine to a fixed point, not by
|
|
17
|
+
* suspending on a promise. Every port must be able to do the same, and
|
|
18
|
+
* fourteen of them will not have JavaScript's event loop. */
|
|
19
|
+
|
|
20
|
+
package plugin
|
|
21
|
+
|
|
22
|
+
import (
|
|
23
|
+
"fmt"
|
|
24
|
+
"sort"
|
|
25
|
+
"sync"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
type PointSpec = Spec
|
|
29
|
+
|
|
30
|
+
type HostOptions struct {
|
|
31
|
+
Catalog *Catalog
|
|
32
|
+
Reserved []string
|
|
33
|
+
Keys Keys
|
|
34
|
+
Defaults map[string]any
|
|
35
|
+
Profile string
|
|
36
|
+
Points map[string]Spec
|
|
37
|
+
// Dependency is §11.3. `restart` (the default) treats provider
|
|
38
|
+
// replacement as an ordinary runtime operation: deactivate the old
|
|
39
|
+
// store, activate the new one, and everything that depended on it
|
|
40
|
+
// rides through, having released the old one's resources in between.
|
|
41
|
+
//
|
|
42
|
+
// `hold` is the strict reading — deactivating a required instance is
|
|
43
|
+
// `plugin_dependency_held`, naming the holders. NOT the default,
|
|
44
|
+
// because a station that cannot swap a provider without a restart
|
|
45
|
+
// has lost the argument for having a plugin system.
|
|
46
|
+
Dependency string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Live is one instance's record. The exported half is what
|
|
50
|
+
// introspection reads; the rest is the host's.
|
|
51
|
+
type Live struct {
|
|
52
|
+
Ref string
|
|
53
|
+
Status Status
|
|
54
|
+
Pos int
|
|
55
|
+
Seq int
|
|
56
|
+
Options map[string]any
|
|
57
|
+
State map[string]any
|
|
58
|
+
// Inner is set when this instance is itself a host (§6.5).
|
|
59
|
+
Inner *Host
|
|
60
|
+
|
|
61
|
+
def Definition
|
|
62
|
+
order *OrderBlock
|
|
63
|
+
// selected is §11.4's ALWAYS-RELUCTANT rebinding made concrete: the
|
|
64
|
+
// provider ref this instance's activation actually chose, per
|
|
65
|
+
// requirement name. "A satisfied requirement is not re-bound while
|
|
66
|
+
// it stays satisfied" is a statement about a REMEMBERED choice —
|
|
67
|
+
// re-ranking on every question silently re-points a live consumer at
|
|
68
|
+
// any better newcomer, and then losing the provider it was really
|
|
69
|
+
// using does not restart it. Captured at activate, cleared on exit.
|
|
70
|
+
selected map[string]string
|
|
71
|
+
// barred is §9.6's `active: false` — "declares it and bars it: it
|
|
72
|
+
// appears in `host.list()`, and `activate` and `ready` on it fail
|
|
73
|
+
// rather than quietly doing nothing". THE BAR OUTLIVES THE APPLY
|
|
74
|
+
// THAT SET IT: a flag consulted only while `apply` ran let a later
|
|
75
|
+
// direct `Ready` bring the instance live.
|
|
76
|
+
barred bool
|
|
77
|
+
// unmet holds requirements this instance declared but has not been
|
|
78
|
+
// given.
|
|
79
|
+
unmet []string
|
|
80
|
+
// scope holds the resources the instance scope holds, newest last —
|
|
81
|
+
// unwound in REVERSE, because that is the only order in which
|
|
82
|
+
// teardown mirrors setup (§8.3).
|
|
83
|
+
scope []func()
|
|
84
|
+
// bindings are declared in `define`, inserted only when activation
|
|
85
|
+
// SUCCEEDS (§8.1). Holding them until then is what makes a failed
|
|
86
|
+
// activate leave nothing behind.
|
|
87
|
+
bindings []Bound
|
|
88
|
+
// exports are declared in `define`, and VISIBLE while merely
|
|
89
|
+
// `loaded` (§11): they are data, and hiding them would make the
|
|
90
|
+
// loaded state useless for introspection.
|
|
91
|
+
exports map[string]any
|
|
92
|
+
provides []Provided
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
type Event struct {
|
|
96
|
+
Ref string `json:"ref"`
|
|
97
|
+
Event string `json:"event"`
|
|
98
|
+
Seq int `json:"seq"`
|
|
99
|
+
Status Status `json:"status"`
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
type Observable struct {
|
|
103
|
+
Status map[string]Status `json:"status"`
|
|
104
|
+
Open int `json:"open"`
|
|
105
|
+
Log []string `json:"log"`
|
|
106
|
+
Result any `json:"result"`
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type Host struct {
|
|
110
|
+
opts HostOptions
|
|
111
|
+
catalog *Catalog
|
|
112
|
+
reserved []string
|
|
113
|
+
points map[string]Spec
|
|
114
|
+
dependency string
|
|
115
|
+
|
|
116
|
+
// coordinated is set for the duration of a bulk teardown, so `held`
|
|
117
|
+
// knows this is a coordinated operation rather than an ad-hoc
|
|
118
|
+
// deactivation.
|
|
119
|
+
coordinated bool
|
|
120
|
+
|
|
121
|
+
// §18: "a port uses its idiom (a mutex in Go/Rust/Java, the GIL
|
|
122
|
+
// where that is enough)". §5.2 makes transitions SEQUENTIAL — one at
|
|
123
|
+
// a time, in call order, never interleaved — and `intransition`
|
|
124
|
+
// cannot deliver that: it is set inside `run`, so two goroutines
|
|
125
|
+
// both pass `guard` and both run a callback. This holds for the
|
|
126
|
+
// WHOLE public transition, reconciliation included, which is the
|
|
127
|
+
// unit §5.2 names.
|
|
128
|
+
//
|
|
129
|
+
// It is NOT reentrant, and must not be: a transition attempted from
|
|
130
|
+
// inside a lifecycle callback is `plugin_reentrant`, and `enter`
|
|
131
|
+
// answers that without ever blocking. Below the door the unlocked
|
|
132
|
+
// bodies (`declare`, `load`, `activate`, …) call each other, so the
|
|
133
|
+
// lock is taken exactly once per public call.
|
|
134
|
+
mu sync.Mutex
|
|
135
|
+
|
|
136
|
+
inst map[string]*Live
|
|
137
|
+
log []string
|
|
138
|
+
// events is §14: the lifecycle event record. `seq` distinguishes ONE
|
|
139
|
+
// INCARNATION of stripe$test from the next, which is the whole
|
|
140
|
+
// reason it is not `pos` (§4 rule 4).
|
|
141
|
+
events []Event
|
|
142
|
+
seqn int
|
|
143
|
+
open int
|
|
144
|
+
intransition bool
|
|
145
|
+
// phase is WHICH callback is running, not merely that one is. §8.1
|
|
146
|
+
// puts resource capture in `activate` and §8.3 says `Release`
|
|
147
|
+
// outside `activate` is `plugin_release_scope` — and `intransition`
|
|
148
|
+
// alone cannot tell `activate` from `define`, so it admitted an
|
|
149
|
+
// Acquire in `define` whose scope `Unload` would never unwind.
|
|
150
|
+
phase string
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
func MakeHost(options HostOptions) *Host {
|
|
154
|
+
h := &Host{
|
|
155
|
+
opts: options,
|
|
156
|
+
catalog: options.Catalog,
|
|
157
|
+
reserved: options.Reserved,
|
|
158
|
+
points: options.Points,
|
|
159
|
+
dependency: or(options.Dependency, "restart"),
|
|
160
|
+
inst: map[string]*Live{},
|
|
161
|
+
log: []string{},
|
|
162
|
+
events: []Event{},
|
|
163
|
+
}
|
|
164
|
+
if nil == h.catalog {
|
|
165
|
+
h.catalog, _ = MakeCatalog()
|
|
166
|
+
}
|
|
167
|
+
if nil == h.points {
|
|
168
|
+
h.points = map[string]Spec{}
|
|
169
|
+
}
|
|
170
|
+
return h
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
func (h *Host) Catalog() *Catalog { return h.catalog }
|
|
174
|
+
|
|
175
|
+
// --- observation -----------------------------------------------------
|
|
176
|
+
|
|
177
|
+
// List is introspection, and introspection NEVER advances the state
|
|
178
|
+
// (§5.2). A status page must not be a way to accidentally import twenty
|
|
179
|
+
// packages.
|
|
180
|
+
func (h *Host) List() map[string]Status {
|
|
181
|
+
out := map[string]Status{}
|
|
182
|
+
for _, r := range sortedkeys(h.inst) {
|
|
183
|
+
out[r] = h.inst[r].Status
|
|
184
|
+
}
|
|
185
|
+
return out
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Instance is the introspection lookup. A MALFORMED REF IS
|
|
189
|
+
// `plugin_bad_name` HERE TOO: the canonical calls `canonref`, which
|
|
190
|
+
// raises, and a port that swallowed the parse failure answered the
|
|
191
|
+
// ordinary "no such instance" instead — a different answer to a
|
|
192
|
+
// different question. `nil, nil` is the well-formed-but-absent case.
|
|
193
|
+
func (h *Host) Instance(ref string) (*Live, error) {
|
|
194
|
+
r, err := CanonRef(ref)
|
|
195
|
+
if nil != err {
|
|
196
|
+
return nil, err
|
|
197
|
+
}
|
|
198
|
+
return h.inst[r], nil
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
func (h *Host) Trace() []Event { return append([]Event{}, h.events...) }
|
|
202
|
+
|
|
203
|
+
func (h *Host) Observable(result any) Observable {
|
|
204
|
+
return Observable{
|
|
205
|
+
Status: h.List(),
|
|
206
|
+
Open: h.open,
|
|
207
|
+
Log: append([]string{}, h.log...),
|
|
208
|
+
Result: result,
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// --- the state machine -----------------------------------------------
|
|
213
|
+
|
|
214
|
+
func (h *Host) guard() error {
|
|
215
|
+
if h.intransition {
|
|
216
|
+
return Fail("plugin_reentrant",
|
|
217
|
+
"transition attempted from inside a lifecycle callback", nil)
|
|
218
|
+
}
|
|
219
|
+
return nil
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// enter is the door every PUBLIC transition goes through, and the only
|
|
223
|
+
// place `h.mu` is taken. It returns the matching release.
|
|
224
|
+
//
|
|
225
|
+
// The two rules it has to serve pull in opposite directions. §5.2 wants
|
|
226
|
+
// transitions SEQUENTIAL, which is a lock; and §5.2 also wants a
|
|
227
|
+
// transition attempted from INSIDE a lifecycle callback to answer
|
|
228
|
+
// `plugin_reentrant`, which is the one caller that must not block —
|
|
229
|
+
// it is the goroutine already holding the lock, so blocking is a
|
|
230
|
+
// deadlock.
|
|
231
|
+
//
|
|
232
|
+
// TryLock separates them as far as Go allows. Taking the lock proves
|
|
233
|
+
// nothing else is in flight, so the call cannot be reentrant. Failing to
|
|
234
|
+
// take it means SOMEONE holds it, and `intransition` says whether that
|
|
235
|
+
// someone is running a callback — the only state from which a reentrant
|
|
236
|
+
// call can arise.
|
|
237
|
+
//
|
|
238
|
+
// THE RESIDUAL WINDOW IS REAL AND IS NOT PAPERED OVER: a genuinely
|
|
239
|
+
// concurrent caller that arrives while a callback is running gets
|
|
240
|
+
// `plugin_reentrant` instead of waiting, because Go exposes no goroutine
|
|
241
|
+
// identity to tell the two apart. It is a refusal with a code, not a
|
|
242
|
+
// corruption, and it is strictly better than the interleaving an
|
|
243
|
+
// unlocked host allowed; a host that needs the other answer should
|
|
244
|
+
// serialise its own calls. See AGENTS.md.
|
|
245
|
+
func (h *Host) enter() (func(), error) {
|
|
246
|
+
if h.mu.TryLock() {
|
|
247
|
+
return h.mu.Unlock, nil
|
|
248
|
+
}
|
|
249
|
+
if err := h.guard(); nil != err {
|
|
250
|
+
return nil, err
|
|
251
|
+
}
|
|
252
|
+
h.mu.Lock()
|
|
253
|
+
return h.mu.Unlock, nil
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
func (h *Host) need(ref string) (*Live, error) {
|
|
257
|
+
r, err := CanonRef(ref)
|
|
258
|
+
if nil != err {
|
|
259
|
+
return nil, err
|
|
260
|
+
}
|
|
261
|
+
e := h.inst[r]
|
|
262
|
+
if nil == e {
|
|
263
|
+
return nil, Fail("plugin_not_loaded", "no such instance: "+r,
|
|
264
|
+
map[string]any{"ref": r})
|
|
265
|
+
}
|
|
266
|
+
return e, nil
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
func (h *Host) checkreserved(ref string) error {
|
|
270
|
+
return checkreservedref(ref, h.reserved)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
func (h *Host) run(e *Live, fn func(*Inst) error, at string) error {
|
|
274
|
+
h.log = append(h.log, e.Ref+":"+at)
|
|
275
|
+
h.events = append(h.events, Event{Ref: e.Ref, Event: at, Seq: e.Seq, Status: e.Status})
|
|
276
|
+
if nil == fn {
|
|
277
|
+
return nil
|
|
278
|
+
}
|
|
279
|
+
h.intransition = true
|
|
280
|
+
h.phase = at
|
|
281
|
+
defer func() { h.intransition = false; h.phase = "" }()
|
|
282
|
+
err := fn(h.api(e))
|
|
283
|
+
if nil == err {
|
|
284
|
+
return nil
|
|
285
|
+
}
|
|
286
|
+
// §12: `plugin_define_failed` and its three siblings are "a callback
|
|
287
|
+
// raised; wraps the cause". AN ERROR THAT ALREADY CARRIES A CODE
|
|
288
|
+
// KEEPS IT — the code is the error's identity, and a plugin
|
|
289
|
+
// returning `store_unreachable` must not have it rewritten. Only a
|
|
290
|
+
// code-less error is wrapped, which is the ordinary case for a
|
|
291
|
+
// callback that let a library error through.
|
|
292
|
+
if "" != CodeOf(err) {
|
|
293
|
+
return err
|
|
294
|
+
}
|
|
295
|
+
return Fail("plugin_"+at+"_failed",
|
|
296
|
+
e.Ref+" raised in "+at+": "+err.Error(),
|
|
297
|
+
map[string]any{"ref": e.Ref, "cause": err.Error()})
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Inst is what a definition's callbacks see. Deliberately not the
|
|
301
|
+
// internal record: a plugin that could reach `Status` could also write
|
|
302
|
+
// it.
|
|
303
|
+
type Inst struct {
|
|
304
|
+
h *Host
|
|
305
|
+
e *Live
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
func (h *Host) api(e *Live) *Inst { return &Inst{h: h, e: e} }
|
|
309
|
+
|
|
310
|
+
func (i *Inst) Ref() string { return i.e.Ref }
|
|
311
|
+
func (i *Inst) Name() string { return refname(i.e.Ref) }
|
|
312
|
+
func (i *Inst) Tag() string { r, _ := ParseRef(i.e.Ref); return r.Tag }
|
|
313
|
+
func (i *Inst) Options() map[string]any { return i.e.Options }
|
|
314
|
+
func (i *Inst) State() map[string]any { return i.e.State }
|
|
315
|
+
func (i *Inst) Host() *Host { return i.h }
|
|
316
|
+
|
|
317
|
+
// Release registers a foreign resource the host did not hand out (§8.3);
|
|
318
|
+
// host calls are recorded automatically.
|
|
319
|
+
func (i *Inst) Release(fn func()) error {
|
|
320
|
+
// §8.3: "`inst.release` outside `activate` is
|
|
321
|
+
// `plugin_release_scope`". `intransition` is true in `define` too,
|
|
322
|
+
// and a scope entry registered there is never unwound.
|
|
323
|
+
if "activate" != i.h.phase {
|
|
324
|
+
return Fail("plugin_release_scope", "release called outside activate", nil)
|
|
325
|
+
}
|
|
326
|
+
// SYMMETRIC WITH Acquire, and it has to be: `open` counts the
|
|
327
|
+
// resources CURRENTLY HELD, so an entry that is registered and then
|
|
328
|
+
// unwound must leave the count where it found it.
|
|
329
|
+
done := false
|
|
330
|
+
h := i.h
|
|
331
|
+
i.e.scope = append(i.e.scope, func() {
|
|
332
|
+
if !done {
|
|
333
|
+
done = true
|
|
334
|
+
h.open -= 1
|
|
335
|
+
fn()
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
h.open += 1
|
|
339
|
+
return nil
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Acquire is the synthetic counter the driver owns, so "what is open" is
|
|
343
|
+
// data rather than an assertion each port words differently.
|
|
344
|
+
//
|
|
345
|
+
// Returns its own release, so a plugin can hand one back early. The
|
|
346
|
+
// scope still holds the entry and unwinding it twice is a no-op —
|
|
347
|
+
// releasing early must not make teardown wrong.
|
|
348
|
+
func (i *Inst) Acquire() (func(), error) {
|
|
349
|
+
// §8.1: resources are "acquired during `activate` — the scope's
|
|
350
|
+
// actual job". Same reason as `Release` above.
|
|
351
|
+
if "activate" != i.h.phase {
|
|
352
|
+
return nil, Fail("plugin_release_scope", "acquire called outside activate", nil)
|
|
353
|
+
}
|
|
354
|
+
done := false
|
|
355
|
+
h := i.h
|
|
356
|
+
rel := func() {
|
|
357
|
+
if !done {
|
|
358
|
+
done = true
|
|
359
|
+
h.open -= 1
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
i.e.scope = append(i.e.scope, rel)
|
|
363
|
+
h.open += 1
|
|
364
|
+
return rel, nil
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// Bind attaches a function to a host point. Declared in `define`; the
|
|
368
|
+
// host inserts it only after `activate` returns successfully (§8.1),
|
|
369
|
+
// which is why a failing activate leaves no live binding behind.
|
|
370
|
+
func (i *Inst) Bind(point string, fn BindFn, band int) error {
|
|
371
|
+
// §12's `plugin_bind_scope`: "binding declared outside `define`".
|
|
372
|
+
// §8.1 puts binding DECLARATION in `define` and INSERTION at a
|
|
373
|
+
// successful activate, and the guard was the half nobody wrote — so
|
|
374
|
+
// a binding added from `activate` went live without being part of
|
|
375
|
+
// the loaded definition, and a deactivate/activate cycle appended
|
|
376
|
+
// it again. The code was in the table before anything raised it.
|
|
377
|
+
if "define" != i.h.phase {
|
|
378
|
+
return Fail("plugin_bind_scope", "bind called outside define: "+point,
|
|
379
|
+
map[string]any{"ref": i.e.Ref, "point": point})
|
|
380
|
+
}
|
|
381
|
+
if _, ok := i.h.points[point]; !ok {
|
|
382
|
+
return Fail("plugin_point_unknown", "no such point: "+point,
|
|
383
|
+
map[string]any{"point": point})
|
|
384
|
+
}
|
|
385
|
+
i.e.bindings = append(i.e.bindings, Bound{Ref: i.e.Ref, Point: point, Fn: fn, Band: band})
|
|
386
|
+
return nil
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Export publishes a value for other plugins and for the application
|
|
390
|
+
// (§11).
|
|
391
|
+
func (i *Inst) Export(key string, value any) { i.e.exports[key] = value }
|
|
392
|
+
|
|
393
|
+
// Provides declares what this instance can do for others (§11.1).
|
|
394
|
+
func (i *Inst) Provides(p Provided) { i.e.provides = append(i.e.provides, p) }
|
|
395
|
+
|
|
396
|
+
type Position struct {
|
|
397
|
+
Index int `json:"index"`
|
|
398
|
+
Count int `json:"count"`
|
|
399
|
+
Outermost bool `json:"outermost"`
|
|
400
|
+
Innermost bool `json:"innermost"`
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/* Position reports where this binding landed (§6.6) — the plugin-side
|
|
404
|
+
* counterpart to a host pin. Station found that a plugin can need to
|
|
405
|
+
* KNOW it is in the right place: its middleware must sit immediately
|
|
406
|
+
* outside the base transport or its "wire truth" events are fiction.
|
|
407
|
+
*
|
|
408
|
+
* THE HOST DOES NOT POLICE THIS; it just makes the fact available. A
|
|
409
|
+
* plugin that requires a position it did not get fails loudly rather
|
|
410
|
+
* than reporting nonsense — and that is the plugin's call, because only
|
|
411
|
+
* it knows what its position means. Verification tells a plugin it was
|
|
412
|
+
* misplaced; a pin (§7) stops the misplacement from being expressible at
|
|
413
|
+
* all. The two are not substitutes. */
|
|
414
|
+
func (i *Inst) Position(point string) (Position, error) {
|
|
415
|
+
return i.h.PositionOf(i.e.Ref, point)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* Nest: AN INSTANCE MAY ITSELF BE A HOST (§6.5), and THE OUTER ONE OWNS
|
|
419
|
+
* THE INNER ONE'S LIFETIME. Registering the teardown in the instance
|
|
420
|
+
* scope is what makes that true rather than aspirational: the inner host
|
|
421
|
+
* closes when the outer instance deactivates, in the same reverse unwind
|
|
422
|
+
* as every other resource. */
|
|
423
|
+
func (i *Inst) Nest(nestopts HostOptions) (*Host, error) {
|
|
424
|
+
if !i.h.intransition {
|
|
425
|
+
return nil, Fail("plugin_release_scope", "nest called outside a lifecycle callback", nil)
|
|
426
|
+
}
|
|
427
|
+
inner := MakeHost(nestopts)
|
|
428
|
+
i.e.scope = append(i.e.scope, func() { inner.Close() })
|
|
429
|
+
i.e.Inner = inner
|
|
430
|
+
return inner, nil
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// AutoTag is EXPLICIT (§4 rule 3). `Declare("stripe", {Tag: "?"})`
|
|
434
|
+
// assigns the LOWEST UNUSED POSITIVE INTEGER tag and returns the
|
|
435
|
+
// assigned pair. Without `"?"`, a collision is an error.
|
|
436
|
+
//
|
|
437
|
+
// It needs a host because it must know what is already declared, which
|
|
438
|
+
// is why it cannot live in the pure `ref` section — the correction P1.7
|
|
439
|
+
// made to §15.3.
|
|
440
|
+
func (h *Host) AutoTag(name string) (string, error) {
|
|
441
|
+
for n := 1; ; n++ {
|
|
442
|
+
cand, err := FormatRef(name, itoa(n))
|
|
443
|
+
if nil != err {
|
|
444
|
+
return "", err
|
|
445
|
+
}
|
|
446
|
+
if _, taken := h.inst[cand]; !taken {
|
|
447
|
+
return cand, nil
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
type DeclareSpec struct {
|
|
453
|
+
Definition string
|
|
454
|
+
Options map[string]any
|
|
455
|
+
Order *OrderBlock
|
|
456
|
+
Pos *int
|
|
457
|
+
Tag string
|
|
458
|
+
// HostOwned is §9.1: "The host declares those instances itself,
|
|
459
|
+
// after the user merge, and always wins." Set ONLY by HostDeclare.
|
|
460
|
+
HostOwned bool
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
func (h *Host) Declare(ref string, spec DeclareSpec) (*Live, error) {
|
|
464
|
+
leave, err := h.enter()
|
|
465
|
+
if nil != err {
|
|
466
|
+
return nil, err
|
|
467
|
+
}
|
|
468
|
+
defer leave()
|
|
469
|
+
return h.declare(ref, spec)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// declare and its siblings are the UNLOCKED bodies. The public
|
|
473
|
+
// transitions call each other — `ready` walks declare/load/activate,
|
|
474
|
+
// `apply` walks all four — and a Go mutex is not reentrant, so the lock
|
|
475
|
+
// is taken once at the door and never again below it.
|
|
476
|
+
func (h *Host) declare(ref string, spec DeclareSpec) (*Live, error) {
|
|
477
|
+
if "?" == spec.Tag {
|
|
478
|
+
r, err := CanonRef(ref)
|
|
479
|
+
if nil != err {
|
|
480
|
+
return nil, err
|
|
481
|
+
}
|
|
482
|
+
ref, err = h.AutoTag(refname(r))
|
|
483
|
+
if nil != err {
|
|
484
|
+
return nil, err
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
r, err := CanonRef(ref)
|
|
488
|
+
if nil != err {
|
|
489
|
+
return nil, err
|
|
490
|
+
}
|
|
491
|
+
if !spec.HostOwned {
|
|
492
|
+
if err := h.checkreserved(r); nil != err {
|
|
493
|
+
return nil, err
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
defname := or(spec.Definition, refname(r))
|
|
497
|
+
def, ok := h.catalog.Get(defname)
|
|
498
|
+
if !ok {
|
|
499
|
+
return nil, Fail("plugin_unknown_definition", "not in catalog: "+defname,
|
|
500
|
+
map[string]any{"name": defname})
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if existing := h.inst[r]; nil != existing {
|
|
504
|
+
// §4 rule 1: a pair addresses at most one instance. Re-declaring
|
|
505
|
+
// the SAME definition is the idempotent case; a different one is
|
|
506
|
+
// a duplicate, not a silent overwrite (seneca) and not an
|
|
507
|
+
// impossibility (sdkgen).
|
|
508
|
+
if existing.def.Name != def.Name {
|
|
509
|
+
return nil, Fail("plugin_ref_duplicate", "instance already declared: "+r,
|
|
510
|
+
map[string]any{"ref": r})
|
|
511
|
+
}
|
|
512
|
+
return existing, nil
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
pos := len(h.inst)
|
|
516
|
+
if nil != spec.Pos {
|
|
517
|
+
pos = *spec.Pos
|
|
518
|
+
}
|
|
519
|
+
options := spec.Options
|
|
520
|
+
if nil == options {
|
|
521
|
+
options = map[string]any{}
|
|
522
|
+
}
|
|
523
|
+
e := &Live{
|
|
524
|
+
Ref: r, def: def, Status: StatusDeclared,
|
|
525
|
+
Pos: pos, Seq: h.seqn,
|
|
526
|
+
Options: options,
|
|
527
|
+
State: map[string]any{},
|
|
528
|
+
selected: map[string]string{},
|
|
529
|
+
order: spec.Order, unmet: []string{}, scope: []func(){},
|
|
530
|
+
bindings: []Bound{}, exports: map[string]any{}, provides: []Provided{},
|
|
531
|
+
}
|
|
532
|
+
h.seqn++
|
|
533
|
+
h.inst[r] = e
|
|
534
|
+
return e, nil
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// HostDeclare is §9.1's host-owned path: a host that reserves a name
|
|
538
|
+
// MUST still be able to declare the instance it reserved.
|
|
539
|
+
//
|
|
540
|
+
// THE BOUNDARY IS BY METHOD, NOT BY CALLER, and that is a real limit: no
|
|
541
|
+
// language here can tell the embedding host from a plugin holding the
|
|
542
|
+
// same host object. What reservation protects is CONFIGURATION —
|
|
543
|
+
// documents, overlays, `VOXGIG_PLUGIN_*`, construction options and
|
|
544
|
+
// ordinary Declare/Load/Options — and all of that still checks.
|
|
545
|
+
func (h *Host) HostDeclare(ref string, spec DeclareSpec) (*Live, error) {
|
|
546
|
+
leave, err := h.enter()
|
|
547
|
+
if nil != err {
|
|
548
|
+
return nil, err
|
|
549
|
+
}
|
|
550
|
+
defer leave()
|
|
551
|
+
spec.HostOwned = true
|
|
552
|
+
return h.declare(ref, spec)
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
func (h *Host) Load(ref string, spec DeclareSpec) (*Live, error) {
|
|
556
|
+
leave, err := h.enter()
|
|
557
|
+
if nil != err {
|
|
558
|
+
return nil, err
|
|
559
|
+
}
|
|
560
|
+
defer leave()
|
|
561
|
+
return h.load(ref, spec)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
func (h *Host) load(ref string, spec DeclareSpec) (*Live, error) {
|
|
565
|
+
e, err := h.declare(ref, spec)
|
|
566
|
+
if nil != err {
|
|
567
|
+
return nil, err
|
|
568
|
+
}
|
|
569
|
+
if StatusDeclared != e.Status {
|
|
570
|
+
return e, nil // idempotent in the trivial direction
|
|
571
|
+
}
|
|
572
|
+
if nil != spec.Options {
|
|
573
|
+
e.Options = spec.Options
|
|
574
|
+
}
|
|
575
|
+
if err := h.run(e, e.def.Define, "define"); nil != err {
|
|
576
|
+
e.Status = StatusFailed
|
|
577
|
+
return nil, err
|
|
578
|
+
}
|
|
579
|
+
e.Status = StatusLoaded
|
|
580
|
+
|
|
581
|
+
// AT LOAD, and before anything runs: a cycle through
|
|
582
|
+
// restart-causing requirements does not settle, and the only safe
|
|
583
|
+
// time to report a non-terminating reconcile is before it starts
|
|
584
|
+
// (§11.3). `provides` is populated by `define`, which has just run,
|
|
585
|
+
// so this is the first moment the graph is complete.
|
|
586
|
+
if err := CheckCycle(h.graphnodes()); nil != err {
|
|
587
|
+
e.Status = StatusFailed
|
|
588
|
+
return nil, err
|
|
589
|
+
}
|
|
590
|
+
return e, nil
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// graphnodes is the requirement graph as plain data, for the pure
|
|
594
|
+
// detector.
|
|
595
|
+
func (h *Host) graphnodes() []DependNode {
|
|
596
|
+
out := []DependNode{}
|
|
597
|
+
for _, r := range sortedkeys(h.inst) {
|
|
598
|
+
names := []string{}
|
|
599
|
+
for _, p := range h.inst[r].provides {
|
|
600
|
+
names = append(names, p.Name)
|
|
601
|
+
}
|
|
602
|
+
out = append(out, DependNode{
|
|
603
|
+
Ref: r, Provides: names, Requires: Requirements(h.inst[r].Options)})
|
|
604
|
+
}
|
|
605
|
+
return out
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
func (h *Host) Activate(ref string) (*Live, error) {
|
|
609
|
+
leave, err := h.enter()
|
|
610
|
+
if nil != err {
|
|
611
|
+
return nil, err
|
|
612
|
+
}
|
|
613
|
+
defer leave()
|
|
614
|
+
return h.activate(ref)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
func (h *Host) activate(ref string) (*Live, error) {
|
|
618
|
+
e, err := h.need(ref)
|
|
619
|
+
if nil != err {
|
|
620
|
+
return nil, err
|
|
621
|
+
}
|
|
622
|
+
if StatusLive == e.Status {
|
|
623
|
+
return e, nil // no-op returning success
|
|
624
|
+
}
|
|
625
|
+
if StatusFailed == e.Status {
|
|
626
|
+
return nil, Fail("plugin_bad_state", "instance has failed: "+e.Ref,
|
|
627
|
+
map[string]any{"ref": e.Ref})
|
|
628
|
+
}
|
|
629
|
+
// §9.6: `active: false` bars the instance from running, and the bar
|
|
630
|
+
// is on the INSTANCE rather than on the apply that set it. `Ready`
|
|
631
|
+
// reaches this through `activate`, so one guard covers both verbs
|
|
632
|
+
// the design names.
|
|
633
|
+
if e.barred {
|
|
634
|
+
return nil, Fail("plugin_inactive",
|
|
635
|
+
"instance is barred by active: false: "+e.Ref,
|
|
636
|
+
map[string]any{"ref": e.Ref})
|
|
637
|
+
}
|
|
638
|
+
if StatusDeclared == e.Status {
|
|
639
|
+
if _, err := h.load(e.Ref, DeclareSpec{}); nil != err {
|
|
640
|
+
return nil, err
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// A declared requirement that is not live means `pending`:
|
|
645
|
+
// activation is a STANDING REQUEST, not a one-shot event.
|
|
646
|
+
if unmet := h.unmetof(e); 0 < len(unmet) {
|
|
647
|
+
e.unmet = unmet
|
|
648
|
+
e.Status = StatusPending
|
|
649
|
+
return e, nil
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if err := h.run(e, e.def.Activate, "activate"); nil != err {
|
|
653
|
+
// Unwind whatever the partial activation captured, in reverse.
|
|
654
|
+
h.unwind(e)
|
|
655
|
+
e.Status = StatusFailed
|
|
656
|
+
return nil, err
|
|
657
|
+
}
|
|
658
|
+
// §11.4: THE SELECTION IS MADE HERE, once, and remembered. Every
|
|
659
|
+
// later question — the cascade, `hold`, `unmet` — reads it back
|
|
660
|
+
// rather than re-ranking, which is what "always-reluctant" means.
|
|
661
|
+
for _, r := range Requirements(e.Options) {
|
|
662
|
+
h.chosen(e, r, true)
|
|
663
|
+
}
|
|
664
|
+
e.Status = StatusLive
|
|
665
|
+
h.reconcile()
|
|
666
|
+
return e, nil
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
func (h *Host) Deactivate(ref string) (*Live, error) {
|
|
670
|
+
leave, err := h.enter()
|
|
671
|
+
if nil != err {
|
|
672
|
+
return nil, err
|
|
673
|
+
}
|
|
674
|
+
defer leave()
|
|
675
|
+
return h.deactivate(ref)
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
func (h *Host) deactivate(ref string) (*Live, error) {
|
|
679
|
+
e, err := h.need(ref)
|
|
680
|
+
if nil != err {
|
|
681
|
+
return nil, err
|
|
682
|
+
}
|
|
683
|
+
if StatusLoaded == e.Status || StatusDeclared == e.Status {
|
|
684
|
+
return e, nil
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
// §5.2: `Unload` is THE ONLY TRANSITION OUT OF `failed`.
|
|
688
|
+
if StatusFailed == e.Status {
|
|
689
|
+
return nil, Fail("plugin_bad_state", "instance has failed: "+e.Ref,
|
|
690
|
+
map[string]any{"ref": e.Ref})
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
if StatusPending == e.Status {
|
|
694
|
+
// DEACTIVATING A PENDING INSTANCE RUNS NO CALLBACK (§5.2). It
|
|
695
|
+
// never reached activate, so it holds no scope and no live
|
|
696
|
+
// bindings; running the definition's deactivate there would be
|
|
697
|
+
// teardown without matching setup, which plugins are not written
|
|
698
|
+
// to survive and which could fail an instance that had done
|
|
699
|
+
// nothing wrong. It cannot fail.
|
|
700
|
+
e.Status = StatusLoaded
|
|
701
|
+
e.unmet = []string{}
|
|
702
|
+
return e, nil
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if err := h.held(e); nil != err {
|
|
706
|
+
return nil, err
|
|
707
|
+
}
|
|
708
|
+
h.cascade(e, map[string]bool{})
|
|
709
|
+
|
|
710
|
+
if err := h.run(e, e.def.Deactivate, "deactivate"); nil != err {
|
|
711
|
+
h.unwind(e)
|
|
712
|
+
e.Status = StatusFailed
|
|
713
|
+
return nil, err
|
|
714
|
+
}
|
|
715
|
+
if err := h.releasecheck(e, h.unwind(e)); nil != err {
|
|
716
|
+
return nil, err
|
|
717
|
+
}
|
|
718
|
+
e.Status = StatusLoaded
|
|
719
|
+
h.reconcile()
|
|
720
|
+
return e, nil
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
func (h *Host) Unload(ref string) error {
|
|
724
|
+
leave, err := h.enter()
|
|
725
|
+
if nil != err {
|
|
726
|
+
return err
|
|
727
|
+
}
|
|
728
|
+
defer leave()
|
|
729
|
+
return h.unload(ref)
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
func (h *Host) unload(ref string) error {
|
|
733
|
+
e, err := h.need(ref)
|
|
734
|
+
if nil != err {
|
|
735
|
+
return err
|
|
736
|
+
}
|
|
737
|
+
if StatusLive == e.Status || StatusPending == e.Status {
|
|
738
|
+
if StatusLive == e.Status {
|
|
739
|
+
if err := h.held(e); nil != err {
|
|
740
|
+
return err
|
|
741
|
+
}
|
|
742
|
+
h.cascade(e, map[string]bool{})
|
|
743
|
+
if err := h.run(e, e.def.Deactivate, "deactivate"); nil != err {
|
|
744
|
+
// §5.2: ANY failure during a transition lands the
|
|
745
|
+
// instance in `failed`, with the scope STILL FULLY
|
|
746
|
+
// UNWOUND — and the instance STAYS REGISTERED, because
|
|
747
|
+
// `failed` is a state an operator has to be able to see.
|
|
748
|
+
h.unwind(e)
|
|
749
|
+
e.Status = StatusFailed
|
|
750
|
+
return err
|
|
751
|
+
}
|
|
752
|
+
if err := h.releasecheck(e, h.unwind(e)); nil != err {
|
|
753
|
+
return err
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
e.Status = StatusLoaded
|
|
757
|
+
}
|
|
758
|
+
if StatusLoaded == e.Status || StatusFailed == e.Status {
|
|
759
|
+
err := h.run(e, e.def.Close, "close")
|
|
760
|
+
delete(h.inst, e.Ref)
|
|
761
|
+
return err
|
|
762
|
+
}
|
|
763
|
+
delete(h.inst, e.Ref)
|
|
764
|
+
return nil
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// Ready runs the whole forward path in one call (§5.1). §15.2's verb
|
|
768
|
+
// list omits this; §5.1 defines it and §15.3's `declare` row requires
|
|
769
|
+
// the corpus to pin it, so the list was incomplete rather than excluding
|
|
770
|
+
// it (DOCS.md §4.2).
|
|
771
|
+
func (h *Host) Ready(ref string) (*Live, error) {
|
|
772
|
+
leave, err := h.enter()
|
|
773
|
+
if nil != err {
|
|
774
|
+
return nil, err
|
|
775
|
+
}
|
|
776
|
+
defer leave()
|
|
777
|
+
return h.ready(ref)
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
func (h *Host) ready(ref string) (*Live, error) {
|
|
781
|
+
r, err := CanonRef(ref)
|
|
782
|
+
if nil != err {
|
|
783
|
+
return nil, err
|
|
784
|
+
}
|
|
785
|
+
if _, has := h.inst[r]; !has {
|
|
786
|
+
if _, err := h.declare(r, DeclareSpec{}); nil != err {
|
|
787
|
+
return nil, err
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
if StatusDeclared == h.inst[r].Status {
|
|
791
|
+
if _, err := h.load(r, DeclareSpec{}); nil != err {
|
|
792
|
+
return nil, err
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return h.activate(r)
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// unwind: bindings go live only when activation succeeds (§8.1), so the
|
|
799
|
+
// teardown is the exact inverse: reverse order, always.
|
|
800
|
+
// unwind runs the scope in reverse and returns the errors it raised.
|
|
801
|
+
// §8.3: "A failing release does not stop the rest. Every entry runs, in
|
|
802
|
+
// reverse order, whatever any of them does; the errors are collected and
|
|
803
|
+
// raised as one `plugin_release_failed`."
|
|
804
|
+
//
|
|
805
|
+
// A Go release is `func()` and cannot return an error, so it signals
|
|
806
|
+
// failure by PANICKING — which is what a Go author's `defer f.Close()`
|
|
807
|
+
// wrapper does when it has nowhere to put the error, and which is
|
|
808
|
+
// recovered here rather than taking the host down.
|
|
809
|
+
// A selection belongs to ONE activation (§11.4). Leaving `live` by any
|
|
810
|
+
// door drops it, so the next activation ranks afresh — keeping it would
|
|
811
|
+
// make a consumer prefer a provider it never actually ran against.
|
|
812
|
+
func (h *Host) unwind(e *Live) []string {
|
|
813
|
+
e.selected = map[string]string{}
|
|
814
|
+
errors := []string{}
|
|
815
|
+
for i := len(e.scope) - 1; 0 <= i; i-- {
|
|
816
|
+
errors = append(errors, callrelease(e.scope[i])...)
|
|
817
|
+
}
|
|
818
|
+
e.scope = []func(){}
|
|
819
|
+
return errors
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
func callrelease(fn func()) (out []string) {
|
|
823
|
+
defer func() {
|
|
824
|
+
if r := recover(); nil != r {
|
|
825
|
+
out = []string{fmt.Sprint(r)}
|
|
826
|
+
}
|
|
827
|
+
}()
|
|
828
|
+
fn()
|
|
829
|
+
return nil
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// releasecheck is §8.3: "A failed release ends the instance in `failed`,
|
|
833
|
+
// exactly as a failed callback does (§5.2) — a release that raised may
|
|
834
|
+
// have leaked, and an instance that may be holding resources it cannot
|
|
835
|
+
// account for must not be reactivated."
|
|
836
|
+
func (h *Host) releasecheck(e *Live, errors []string) error {
|
|
837
|
+
if 0 == len(errors) {
|
|
838
|
+
return nil
|
|
839
|
+
}
|
|
840
|
+
e.Status = StatusFailed
|
|
841
|
+
return Fail("plugin_release_failed",
|
|
842
|
+
"release failed for "+e.Ref+": "+join(errors, "; "),
|
|
843
|
+
map[string]any{"ref": e.Ref, "cause": errors})
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/* unmetof: A REQUIREMENT IS ON A CAPABILITY, not on a ref (§11.1) — it
|
|
847
|
+
* is a dependency on something that can do the job, and which instance
|
|
848
|
+
* is doing it is exactly the configuration detail a plugin must not care
|
|
849
|
+
* about. A bare string is shorthand for `{name}`.
|
|
850
|
+
*
|
|
851
|
+
* A ref satisfies too, because a host that genuinely needs a specific
|
|
852
|
+
* instance should not have to invent a capability for it. */
|
|
853
|
+
func (h *Host) unmetof(e *Live) []string {
|
|
854
|
+
out := []string{}
|
|
855
|
+
for _, r := range Requirements(e.Options) {
|
|
856
|
+
if !GatesActivation(r) {
|
|
857
|
+
continue
|
|
858
|
+
}
|
|
859
|
+
if 0 == len(h.providersof(r)) {
|
|
860
|
+
out = append(out, r.Name)
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
return out
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
/* boundproviders: the instance currently SELECTED for each of this one's
|
|
867
|
+
* restart-causing requirements. A BINDING IS TO AN INSTANCE, not to a
|
|
868
|
+
* capability (§11.1), and that is what decides behaviour when the bound
|
|
869
|
+
* provider leaves while another match remains: the selected one going
|
|
870
|
+
* away restarts a `static` consumer even though a survivor is available.
|
|
871
|
+
* It is not silently re-pointed — `static` is the plugin saying in
|
|
872
|
+
* writing that it cannot survive a provider swap, and a survivor being
|
|
873
|
+
* available does not make the swap survivable. */
|
|
874
|
+
// chosen is §11.4's always-reluctant selection, and the ONE place a
|
|
875
|
+
// provider is picked for a live instance. If this instance already
|
|
876
|
+
// selected a provider for `req` and that provider is STILL a candidate,
|
|
877
|
+
// it keeps it — a better-ranked newcomer does not take it. `remember`
|
|
878
|
+
// is false for the questions asked ABOUT an instance rather than BY it:
|
|
879
|
+
// introspection must not create a binding.
|
|
880
|
+
func (h *Host) chosen(e *Live, req Required, remember bool) string {
|
|
881
|
+
cands := h.providersof(req)
|
|
882
|
+
if 0 == len(cands) {
|
|
883
|
+
return ""
|
|
884
|
+
}
|
|
885
|
+
if held, has := e.selected[req.Name]; has {
|
|
886
|
+
for _, c := range cands {
|
|
887
|
+
if c.Ref == held {
|
|
888
|
+
return held
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if remember {
|
|
893
|
+
e.selected[req.Name] = cands[0].Ref
|
|
894
|
+
}
|
|
895
|
+
return cands[0].Ref
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
func (h *Host) boundproviders(e *Live) []string {
|
|
899
|
+
out := []string{}
|
|
900
|
+
for _, r := range Requirements(e.Options) {
|
|
901
|
+
if !RestartsOnLoss(r) {
|
|
902
|
+
continue
|
|
903
|
+
}
|
|
904
|
+
if ref := h.chosen(e, r, false); "" != ref && !hasstring(out, ref) {
|
|
905
|
+
out = append(out, ref)
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return out
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// consumersof: live instances whose selected provider is `ref` and which
|
|
912
|
+
// would be restarted by losing it.
|
|
913
|
+
func (h *Host) consumersof(ref string) []string {
|
|
914
|
+
out := []string{}
|
|
915
|
+
for _, r := range sortedkeys(h.inst) {
|
|
916
|
+
c := h.inst[r]
|
|
917
|
+
if r != ref && StatusLive == c.Status && hasstring(h.boundproviders(c), ref) {
|
|
918
|
+
out = append(out, r)
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
return out
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// holdersof answers §11.3's `hold` question, which is a DIFFERENT
|
|
925
|
+
// question from the cascade's — and reading it off `consumersof`
|
|
926
|
+
// answered the cascade's.
|
|
927
|
+
//
|
|
928
|
+
// The cascade wants the edges that RESTART (mandatory-static and
|
|
929
|
+
// optional-static), because a restart is what it performs. `hold` says
|
|
930
|
+
// "deactivating a REQUIRED instance is `plugin_dependency_held`", and
|
|
931
|
+
// required is cardinality: GatesActivation, not RestartsOnLoss. The two
|
|
932
|
+
// sets differ in both directions and each difference was a real bug.
|
|
933
|
+
//
|
|
934
|
+
// A MANDATORY-DYNAMIC consumer was excluded, so the strictest policy
|
|
935
|
+
// let a provider go that a live consumer could not do without —
|
|
936
|
+
// `dynamic` promises survival of a SWAP, and under `hold` there is no
|
|
937
|
+
// swap, so the consumer falls back to `pending`.
|
|
938
|
+
//
|
|
939
|
+
// An OPTIONAL-STATIC consumer was included, so `hold` refused a
|
|
940
|
+
// deactivation on behalf of an instance that had said in writing it
|
|
941
|
+
// does not need the thing.
|
|
942
|
+
func (h *Host) holdersof(ref string) []string {
|
|
943
|
+
out := []string{}
|
|
944
|
+
for _, r := range sortedkeys(h.inst) {
|
|
945
|
+
c := h.inst[r]
|
|
946
|
+
if r == ref || StatusLive != c.Status {
|
|
947
|
+
continue
|
|
948
|
+
}
|
|
949
|
+
for _, req := range Requirements(c.Options) {
|
|
950
|
+
if !GatesActivation(req) {
|
|
951
|
+
continue
|
|
952
|
+
}
|
|
953
|
+
if h.chosen(c, req, false) == ref {
|
|
954
|
+
out = append(out, r)
|
|
955
|
+
break
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return out
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
func (h *Host) providersof(req Required) []Candidate {
|
|
963
|
+
cands := []Candidate{}
|
|
964
|
+
want := canon(req.Name)
|
|
965
|
+
for _, ref := range sortedkeys(h.inst) {
|
|
966
|
+
t := h.inst[ref]
|
|
967
|
+
if StatusLive != t.Status {
|
|
968
|
+
continue
|
|
969
|
+
}
|
|
970
|
+
// A ref satisfies directly.
|
|
971
|
+
if ref == want {
|
|
972
|
+
cands = append(cands, Candidate{Ref: ref, Pos: t.Pos, Provides: Provided{Name: req.Name}})
|
|
973
|
+
continue
|
|
974
|
+
}
|
|
975
|
+
for _, p := range t.provides {
|
|
976
|
+
if p.Name == req.Name {
|
|
977
|
+
cands = append(cands, Candidate{Ref: ref, Pos: t.Pos, Provides: p})
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return ResolveCapability(req, cands)
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
/* CONSUMERS GO DOWN FIRST, NOT AFTERWARDS (§11.3).
|
|
985
|
+
*
|
|
986
|
+
* The cascade is part of the provider's own deactivation and runs BEFORE
|
|
987
|
+
* the provider's `deactivate` callback and scope unwind, so a consumer's
|
|
988
|
+
* teardown can still call the thing it depends on — flushing a buffer to
|
|
989
|
+
* the store it is about to lose is exactly what a `deactivate` callback
|
|
990
|
+
* is for, and a cascade that fired after the provider was already gone
|
|
991
|
+
* would make that impossible.
|
|
992
|
+
*
|
|
993
|
+
* Order: consumers deepest-first, then the provider. `Unload` and
|
|
994
|
+
* `Close` inherit it, UNDER EITHER DEPENDENCY POLICY, which is what
|
|
995
|
+
* makes apply's reverse-load-order teardown safe even when a document
|
|
996
|
+
* happens to list a consumer before its provider. */
|
|
997
|
+
func (h *Host) cascade(provider *Live, seen map[string]bool) {
|
|
998
|
+
if seen[provider.Ref] {
|
|
999
|
+
return
|
|
1000
|
+
}
|
|
1001
|
+
seen[provider.Ref] = true
|
|
1002
|
+
|
|
1003
|
+
for _, r := range h.consumersof(provider.Ref) {
|
|
1004
|
+
c := h.inst[r]
|
|
1005
|
+
if StatusLive != c.Status {
|
|
1006
|
+
continue
|
|
1007
|
+
}
|
|
1008
|
+
h.cascade(c, seen) // deepest-first
|
|
1009
|
+
bad := nil != h.run(c, c.def.Deactivate, "deactivate")
|
|
1010
|
+
errors := h.unwind(c)
|
|
1011
|
+
if bad || 0 < len(errors) {
|
|
1012
|
+
// §5.2: ANY failure during a transition lands the instance in
|
|
1013
|
+
// `failed`. Marking it `pending` handed it straight back to
|
|
1014
|
+
// `reconcile`, which would activate it again the moment the
|
|
1015
|
+
// provider returned.
|
|
1016
|
+
c.Status = StatusFailed
|
|
1017
|
+
continue
|
|
1018
|
+
}
|
|
1019
|
+
c.Status = StatusPending
|
|
1020
|
+
c.unmet = h.unmetof(c)
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
/* held is A GUARD ON AD-HOC DEACTIVATION, NOT ON COORDINATED TEARDOWN.
|
|
1025
|
+
* In a bulk operation that is removing the holders too — `Close()`, or
|
|
1026
|
+
* an `Apply` plan whose own steps deactivate them — it is suspended for
|
|
1027
|
+
* exactly those holders, and the teardown still runs consumers before
|
|
1028
|
+
* providers.
|
|
1029
|
+
*
|
|
1030
|
+
* Otherwise `Close()` under `hold` would raise on the first provider it
|
|
1031
|
+
* reached whenever a document happened to list a consumer after it,
|
|
1032
|
+
* which is the policy refusing to allow the one teardown it has no
|
|
1033
|
+
* reason to object to. */
|
|
1034
|
+
func (h *Host) held(e *Live) error {
|
|
1035
|
+
if "hold" != h.dependency {
|
|
1036
|
+
return nil
|
|
1037
|
+
}
|
|
1038
|
+
if h.coordinated {
|
|
1039
|
+
return nil
|
|
1040
|
+
}
|
|
1041
|
+
holders := h.holdersof(e.Ref)
|
|
1042
|
+
if 0 == len(holders) {
|
|
1043
|
+
return nil
|
|
1044
|
+
}
|
|
1045
|
+
return Fail("plugin_dependency_held",
|
|
1046
|
+
"instance is required by live consumers: "+e.Ref,
|
|
1047
|
+
map[string]any{"ref": e.Ref, "holders": holders})
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/* reconcile is EAGER: run to a fixed point rather than scheduling.
|
|
1051
|
+
*
|
|
1052
|
+
* Two directions, and both are the reason `pending` exists. Activation
|
|
1053
|
+
* is a STANDING REQUEST, not a one-shot event: a pending instance whose
|
|
1054
|
+
* requirement arrives activates without being asked again, and a LIVE
|
|
1055
|
+
* instance whose requirement is lost goes back to pending —
|
|
1056
|
+
* recursively, through its own consumers. */
|
|
1057
|
+
func (h *Host) reconcile() {
|
|
1058
|
+
rounds := 0
|
|
1059
|
+
for moved := true; moved; {
|
|
1060
|
+
moved = false
|
|
1061
|
+
rounds++
|
|
1062
|
+
if 1000 < rounds {
|
|
1063
|
+
break
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// Losses first, so a cascade settles in one pass rather than
|
|
1067
|
+
// alternating with re-activations.
|
|
1068
|
+
for _, r := range sortedkeys(h.inst) {
|
|
1069
|
+
e := h.inst[r]
|
|
1070
|
+
if StatusLive != e.Status {
|
|
1071
|
+
continue
|
|
1072
|
+
}
|
|
1073
|
+
lost := []Required{}
|
|
1074
|
+
for _, q := range Requirements(e.Options) {
|
|
1075
|
+
if GatesActivation(q) && 0 == len(h.providersof(q)) {
|
|
1076
|
+
lost = append(lost, q)
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
if 0 == len(lost) {
|
|
1080
|
+
continue
|
|
1081
|
+
}
|
|
1082
|
+
// POLICY IS PER REQUIREMENT, not per instance (§11.3): only
|
|
1083
|
+
// the definition that has the requirement knows what it can
|
|
1084
|
+
// cope with, and one instance may hold both a `static` and a
|
|
1085
|
+
// `dynamic` one. A `dynamic` requirement whose provider is
|
|
1086
|
+
// gone leaves the consumer LIVE and notified; it is a
|
|
1087
|
+
// statement about surviving a swap, so it does not restart
|
|
1088
|
+
// here.
|
|
1089
|
+
restarts := false
|
|
1090
|
+
for _, q := range lost {
|
|
1091
|
+
if RestartsOnLoss(q) {
|
|
1092
|
+
restarts = true
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
if !restarts {
|
|
1096
|
+
continue
|
|
1097
|
+
}
|
|
1098
|
+
bad := nil != h.run(e, e.def.Deactivate, "deactivate")
|
|
1099
|
+
errors := h.unwind(e)
|
|
1100
|
+
if bad || 0 < len(errors) {
|
|
1101
|
+
e.Status = StatusFailed
|
|
1102
|
+
moved = true
|
|
1103
|
+
continue
|
|
1104
|
+
}
|
|
1105
|
+
e.Status = StatusPending
|
|
1106
|
+
e.unmet = h.unmetof(e)
|
|
1107
|
+
moved = true
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
for _, r := range sortedkeys(h.inst) {
|
|
1111
|
+
e := h.inst[r]
|
|
1112
|
+
if StatusPending != e.Status {
|
|
1113
|
+
continue
|
|
1114
|
+
}
|
|
1115
|
+
if 0 < len(h.unmetof(e)) {
|
|
1116
|
+
continue
|
|
1117
|
+
}
|
|
1118
|
+
if err := h.run(e, e.def.Activate, "activate"); nil != err {
|
|
1119
|
+
h.unwind(e)
|
|
1120
|
+
e.Status = StatusFailed
|
|
1121
|
+
moved = true
|
|
1122
|
+
continue
|
|
1123
|
+
}
|
|
1124
|
+
e.Status = StatusLive
|
|
1125
|
+
e.unmet = []string{}
|
|
1126
|
+
moved = true
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
// --- ordering --------------------------------------------------------
|
|
1132
|
+
|
|
1133
|
+
func (h *Host) Order(point string) ([]string, error) {
|
|
1134
|
+
// Sorted by declaration SEQUENCE, which is what makes §7's sort
|
|
1135
|
+
// deterministic here. §7 breaks ties by `pos`, and two instances CAN
|
|
1136
|
+
// share one — `Declare` defaults `Pos` to the registry size, so an
|
|
1137
|
+
// unload followed by a fresh declare reuses a surviving instance's.
|
|
1138
|
+
// Past that the canonical fell through to its map's insertion order;
|
|
1139
|
+
// a Go map has none, and sorting by REF gave the opposite answer.
|
|
1140
|
+
// `Seq` IS that order, made explicit — in the canonical too.
|
|
1141
|
+
refs := sortedkeys(h.inst)
|
|
1142
|
+
sort.SliceStable(refs, func(i, j int) bool {
|
|
1143
|
+
return h.inst[refs[i]].Seq < h.inst[refs[j]].Seq
|
|
1144
|
+
})
|
|
1145
|
+
bindings := []Binding{}
|
|
1146
|
+
for _, r := range refs {
|
|
1147
|
+
if StatusLive != h.inst[r].Status {
|
|
1148
|
+
continue
|
|
1149
|
+
}
|
|
1150
|
+
bindings = append(bindings, Binding{Ref: r, Pos: h.inst[r].Pos, Order: h.inst[r].order})
|
|
1151
|
+
}
|
|
1152
|
+
var pin Pin
|
|
1153
|
+
if "" != point {
|
|
1154
|
+
if spec, ok := h.points[point]; ok {
|
|
1155
|
+
pin = spec.Pin
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return ResolveOrder(bindings, pin)
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// --- points ----------------------------------------------------------
|
|
1162
|
+
|
|
1163
|
+
// bound returns the live bindings on a point, in resolved order.
|
|
1164
|
+
// Recomputed on any change to the live set (§7) rather than cached at
|
|
1165
|
+
// startup — the bug a host discovers only when something deactivates in
|
|
1166
|
+
// production.
|
|
1167
|
+
func (h *Host) bound(point string) ([]Bound, error) {
|
|
1168
|
+
ranked, err := h.Order(point)
|
|
1169
|
+
if nil != err {
|
|
1170
|
+
return nil, err
|
|
1171
|
+
}
|
|
1172
|
+
out := []Bound{}
|
|
1173
|
+
for _, ref := range ranked {
|
|
1174
|
+
e := h.inst[ref]
|
|
1175
|
+
// The band is the INSTANCE's ordering block (§7), stamped by the
|
|
1176
|
+
// host. A plugin passing its own would be ranking itself above
|
|
1177
|
+
// the order its document declared.
|
|
1178
|
+
band := 0
|
|
1179
|
+
if nil != e.order && nil != e.order.Band {
|
|
1180
|
+
band = *e.order.Band
|
|
1181
|
+
}
|
|
1182
|
+
for _, b := range e.bindings {
|
|
1183
|
+
if b.Point == point {
|
|
1184
|
+
stamped := b
|
|
1185
|
+
stamped.Band = band
|
|
1186
|
+
out = append(out, stamped)
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
return out, nil
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
func (h *Host) pointspec(point string, want Kind) (Spec, error) {
|
|
1194
|
+
spec, ok := h.points[point]
|
|
1195
|
+
if !ok {
|
|
1196
|
+
return Spec{}, Fail("plugin_point_unknown", "no such point: "+point,
|
|
1197
|
+
map[string]any{"point": point})
|
|
1198
|
+
}
|
|
1199
|
+
if KindHook == want {
|
|
1200
|
+
// A point with no declared kind is a hook, which is what makes
|
|
1201
|
+
// `{}` the minimal point declaration.
|
|
1202
|
+
if "" != spec.Kind && KindHook != spec.Kind {
|
|
1203
|
+
return Spec{}, Fail("plugin_point_kind", "point is not a hook: "+point,
|
|
1204
|
+
map[string]any{"point": point, "kind": string(spec.Kind)})
|
|
1205
|
+
}
|
|
1206
|
+
return spec, nil
|
|
1207
|
+
}
|
|
1208
|
+
if spec.Kind != want {
|
|
1209
|
+
return Spec{}, Fail("plugin_point_kind", "point is not a "+string(want)+": "+point,
|
|
1210
|
+
map[string]any{"point": point, "kind": string(spec.Kind)})
|
|
1211
|
+
}
|
|
1212
|
+
return spec, nil
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
func (h *Host) Emit(point string, arg any) (any, error) {
|
|
1216
|
+
spec, err := h.pointspec(point, KindHook)
|
|
1217
|
+
if nil != err {
|
|
1218
|
+
return nil, err
|
|
1219
|
+
}
|
|
1220
|
+
bindings, err := h.bound(point)
|
|
1221
|
+
if nil != err {
|
|
1222
|
+
return nil, err
|
|
1223
|
+
}
|
|
1224
|
+
mode := spec.Mode
|
|
1225
|
+
if "" == mode {
|
|
1226
|
+
mode = ModeEmit
|
|
1227
|
+
}
|
|
1228
|
+
return Emit(bindings, mode, arg)
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
func (h *Host) Call(point string, args ...any) (any, error) {
|
|
1232
|
+
spec, err := h.pointspec(point, KindChain)
|
|
1233
|
+
if nil != err {
|
|
1234
|
+
return nil, err
|
|
1235
|
+
}
|
|
1236
|
+
bindings, err := h.bound(point)
|
|
1237
|
+
if nil != err {
|
|
1238
|
+
return nil, err
|
|
1239
|
+
}
|
|
1240
|
+
base := spec.Base
|
|
1241
|
+
if nil == base {
|
|
1242
|
+
base = func(a ...any) any {
|
|
1243
|
+
if 0 == len(a) {
|
|
1244
|
+
return nil
|
|
1245
|
+
}
|
|
1246
|
+
return a[0]
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return Compose(bindings, base)(args...), nil
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
func (h *Host) Provide(point string, args ...any) (any, error) {
|
|
1253
|
+
spec, err := h.pointspec(point, KindProvider)
|
|
1254
|
+
if nil != err {
|
|
1255
|
+
return nil, err
|
|
1256
|
+
}
|
|
1257
|
+
bindings, err := h.bound(point)
|
|
1258
|
+
if nil != err {
|
|
1259
|
+
return nil, err
|
|
1260
|
+
}
|
|
1261
|
+
pick, err := Provider(bindings, spec)
|
|
1262
|
+
if nil != err {
|
|
1263
|
+
return nil, err
|
|
1264
|
+
}
|
|
1265
|
+
if nil == pick.Winner {
|
|
1266
|
+
return spec.Default, nil
|
|
1267
|
+
}
|
|
1268
|
+
return pick.Winner.Fn(args...), nil
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Shadowed makes the losers VISIBLE rather than silently ignored (§6.3).
|
|
1272
|
+
func (h *Host) Shadowed(point string) ([]string, error) {
|
|
1273
|
+
spec, ok := h.points[point]
|
|
1274
|
+
if !ok {
|
|
1275
|
+
return []string{}, nil
|
|
1276
|
+
}
|
|
1277
|
+
bindings, err := h.bound(point)
|
|
1278
|
+
if nil != err {
|
|
1279
|
+
return nil, err
|
|
1280
|
+
}
|
|
1281
|
+
pick, err := Provider(bindings, spec)
|
|
1282
|
+
if nil != err {
|
|
1283
|
+
return nil, err
|
|
1284
|
+
}
|
|
1285
|
+
return pick.Shadowed, nil
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
func (h *Host) Exports(spec string) (any, error) {
|
|
1289
|
+
all := []Exported{}
|
|
1290
|
+
for _, ref := range sortedkeys(h.inst) {
|
|
1291
|
+
e := h.inst[ref]
|
|
1292
|
+
// Exports of a `loaded` (not live) instance are VISIBLE (§11).
|
|
1293
|
+
if StatusDeclared == e.Status || StatusFailed == e.Status {
|
|
1294
|
+
continue
|
|
1295
|
+
}
|
|
1296
|
+
for _, k := range sortedkeys(e.exports) {
|
|
1297
|
+
all = append(all, Exported{Ref: ref, Key: k, Value: e.exports[k]})
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
return ResolveExport(spec, all)
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Capability lists the live providers of a capability, best-first
|
|
1304
|
+
// (§11.1).
|
|
1305
|
+
func (h *Host) Capability(name string) []string {
|
|
1306
|
+
cands := []Candidate{}
|
|
1307
|
+
for _, ref := range sortedkeys(h.inst) {
|
|
1308
|
+
e := h.inst[ref]
|
|
1309
|
+
if StatusLive != e.Status {
|
|
1310
|
+
continue
|
|
1311
|
+
}
|
|
1312
|
+
for _, p := range e.provides {
|
|
1313
|
+
if p.Name == name {
|
|
1314
|
+
cands = append(cands, Candidate{Ref: ref, Pos: e.Pos, Provides: p})
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
out := []string{}
|
|
1319
|
+
for _, c := range ResolveCapability(Required{Name: name}, cands) {
|
|
1320
|
+
out = append(out, c.Ref)
|
|
1321
|
+
}
|
|
1322
|
+
return out
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// --- documents -------------------------------------------------------
|
|
1326
|
+
|
|
1327
|
+
// Apply is §9.6: "load what is missing, UNLOAD WHAT IS GONE, patch what
|
|
1328
|
+
// changed, and move activation state to match", with the stated
|
|
1329
|
+
// ordering — "deactivations and unloads first (reverse load order), then
|
|
1330
|
+
// loads, then activations in load order".
|
|
1331
|
+
//
|
|
1332
|
+
// FOUR PHASES, NOT ONE INTERLEAVED LOOP. An earlier draft walked the
|
|
1333
|
+
// document once, which never looked at instances the new document had
|
|
1334
|
+
// DROPPED — so an integration removed from a config reload stayed live
|
|
1335
|
+
// with its bindings and resources.
|
|
1336
|
+
func (h *Host) Apply(doc any, profile string) error {
|
|
1337
|
+
leave, err := h.enter()
|
|
1338
|
+
if nil != err {
|
|
1339
|
+
return err
|
|
1340
|
+
}
|
|
1341
|
+
defer leave()
|
|
1342
|
+
return h.apply(doc, profile)
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
func (h *Host) apply(doc any, profile string) error {
|
|
1346
|
+
profile = or(profile, h.opts.Profile)
|
|
1347
|
+
norm, err := NormalizeConfig(NormalizeInput{
|
|
1348
|
+
Doc: doc, Profile: profile, Keys: h.opts.Keys, Reserved: h.reserved,
|
|
1349
|
+
})
|
|
1350
|
+
if nil != err {
|
|
1351
|
+
return err
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
want := norm.Order
|
|
1355
|
+
optionsof := map[string]map[string]any{}
|
|
1356
|
+
for _, ref := range want {
|
|
1357
|
+
options, err := ResolveOptions(ResolveInput{
|
|
1358
|
+
Ref: ref, Doc: doc, Profile: profile,
|
|
1359
|
+
Shape: h.shapeof(ref), HostDefaults: h.opts.Defaults[refname(ref)],
|
|
1360
|
+
})
|
|
1361
|
+
if nil != err {
|
|
1362
|
+
return err
|
|
1363
|
+
}
|
|
1364
|
+
optionsof[ref] = options
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
// wantlive: should this ref be LIVE after the apply? False for a ref
|
|
1368
|
+
// the document declares lazy or inactive AND for one it does not
|
|
1369
|
+
// name at all — which is what makes "unload what is gone" and
|
|
1370
|
+
// "unload what was toggled off" one rule rather than two.
|
|
1371
|
+
wantlive := func(ref string) bool {
|
|
1372
|
+
ent, has := norm.Instance[ref]
|
|
1373
|
+
return has && ent.Active && "eager" == ent.Start
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// --- phase 1: deactivations and unloads, in REVERSE load order ---
|
|
1377
|
+
drop := []string{}
|
|
1378
|
+
for _, ref := range sortedkeys(h.inst) {
|
|
1379
|
+
if StatusDeclared == h.inst[ref].Status {
|
|
1380
|
+
continue
|
|
1381
|
+
}
|
|
1382
|
+
if !wantlive(ref) {
|
|
1383
|
+
drop = append(drop, ref)
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
// Highest `pos` first, ref-descending for a tie, so a consumer
|
|
1387
|
+
// declared after its provider goes down first.
|
|
1388
|
+
sort.SliceStable(drop, func(i, j int) bool {
|
|
1389
|
+
a, b := h.inst[drop[i]], h.inst[drop[j]]
|
|
1390
|
+
if a.Pos != b.Pos {
|
|
1391
|
+
return a.Pos > b.Pos
|
|
1392
|
+
}
|
|
1393
|
+
return drop[i] > drop[j]
|
|
1394
|
+
})
|
|
1395
|
+
for _, ref := range drop {
|
|
1396
|
+
if err := h.unload(ref); nil != err {
|
|
1397
|
+
return err
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// --- phase 2: declare and patch EVERYTHING, in load order --------
|
|
1402
|
+
for _, ref := range want {
|
|
1403
|
+
ent := norm.Instance[ref]
|
|
1404
|
+
pos := ent.Pos
|
|
1405
|
+
// NO OPTIONS HERE, and the omission is the fix rather than an
|
|
1406
|
+
// oversight. `declare` ADOPTS the options map it is handed as
|
|
1407
|
+
// the instance's own, so passing the resolved map made target
|
|
1408
|
+
// and source THE SAME MAP in the refill below — which cleared
|
|
1409
|
+
// its own source and left a first-time instance with no options
|
|
1410
|
+
// at all. `declare` makes its own empty map and the refill fills
|
|
1411
|
+
// it, so both paths are now one path.
|
|
1412
|
+
if _, err := h.declare(ref, DeclareSpec{
|
|
1413
|
+
Order: ent.Order, Pos: &pos}); nil != err {
|
|
1414
|
+
return err
|
|
1415
|
+
}
|
|
1416
|
+
// The bar is REASSERTED ON EVERY APPLY, in both directions — a
|
|
1417
|
+
// document that turns the instance back on clears it, which is
|
|
1418
|
+
// the whole point of a config switch.
|
|
1419
|
+
h.inst[ref].barred = !ent.Active
|
|
1420
|
+
// REFILL rather than REBIND. A definition's callbacks close over
|
|
1421
|
+
// the options map they were handed at `define`; replacing the
|
|
1422
|
+
// reference here would leave every binding reading the values the
|
|
1423
|
+
// first apply gave it.
|
|
1424
|
+
refill(h.inst[ref].Options, optionsof[ref])
|
|
1425
|
+
h.inst[ref].order = ent.Order
|
|
1426
|
+
h.inst[ref].Pos = ent.Pos
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
// --- phase 3: loads, in load order -------------------------------
|
|
1430
|
+
// ONLY THE EAGER, ACTIVE ONES: "a document of twenty lazy instances
|
|
1431
|
+
// is twenty map entries and no executed code" (§9.6).
|
|
1432
|
+
for _, ref := range want {
|
|
1433
|
+
if wantlive(ref) {
|
|
1434
|
+
if _, err := h.load(ref, DeclareSpec{}); nil != err {
|
|
1435
|
+
return err
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
// --- phase 4: activations, in load order -------------------------
|
|
1441
|
+
for _, ref := range want {
|
|
1442
|
+
if wantlive(ref) {
|
|
1443
|
+
if _, err := h.activate(ref); nil != err {
|
|
1444
|
+
return err
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
return nil
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
func (h *Host) shapeof(ref string) any {
|
|
1452
|
+
def, ok := h.catalog.Get(refname(ref))
|
|
1453
|
+
if !ok {
|
|
1454
|
+
return nil
|
|
1455
|
+
}
|
|
1456
|
+
return def.Shape
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
func (h *Host) SetOptions(ref string, patch map[string]any) error {
|
|
1460
|
+
leave, err := h.enter()
|
|
1461
|
+
if nil != err {
|
|
1462
|
+
return err
|
|
1463
|
+
}
|
|
1464
|
+
defer leave()
|
|
1465
|
+
return h.setoptions(ref, patch)
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
func (h *Host) setoptions(ref string, patch map[string]any) error {
|
|
1469
|
+
e, err := h.need(ref)
|
|
1470
|
+
if nil != err {
|
|
1471
|
+
return err
|
|
1472
|
+
}
|
|
1473
|
+
previous := map[string]any{}
|
|
1474
|
+
for k, v := range e.Options {
|
|
1475
|
+
previous[k] = v
|
|
1476
|
+
}
|
|
1477
|
+
resolved, err := ResolveOptions(ResolveInput{
|
|
1478
|
+
Ref: e.Ref, Shape: h.shapeof(e.Ref), Doc: map[string]any{},
|
|
1479
|
+
Patch: shallowmerge(previous, patch)})
|
|
1480
|
+
if nil != err {
|
|
1481
|
+
return err
|
|
1482
|
+
}
|
|
1483
|
+
refill(e.Options, resolved)
|
|
1484
|
+
if StatusLive == e.Status {
|
|
1485
|
+
if nil != e.def.Reconfigure {
|
|
1486
|
+
h.intransition = true
|
|
1487
|
+
err := e.def.Reconfigure(h.api(e), e.Options, previous)
|
|
1488
|
+
h.intransition = false
|
|
1489
|
+
return err
|
|
1490
|
+
}
|
|
1491
|
+
// Always correct and sometimes expensive; `reconfigure` exists
|
|
1492
|
+
// to make the common case cheap (§9.4).
|
|
1493
|
+
if _, err := h.deactivate(e.Ref); nil != err {
|
|
1494
|
+
return err
|
|
1495
|
+
}
|
|
1496
|
+
if _, err := h.activate(e.Ref); nil != err {
|
|
1497
|
+
return err
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
return nil
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
// refill empties the target and refills it, so callers holding the
|
|
1504
|
+
// reference see the new values.
|
|
1505
|
+
func refill(target map[string]any, source map[string]any) {
|
|
1506
|
+
for k := range target {
|
|
1507
|
+
delete(target, k)
|
|
1508
|
+
}
|
|
1509
|
+
for k, v := range source {
|
|
1510
|
+
target[k] = v
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
func shallowmerge(a map[string]any, b map[string]any) map[string]any {
|
|
1515
|
+
out := map[string]any{}
|
|
1516
|
+
for k, v := range a {
|
|
1517
|
+
out[k] = v
|
|
1518
|
+
}
|
|
1519
|
+
for k, v := range b {
|
|
1520
|
+
out[k] = v
|
|
1521
|
+
}
|
|
1522
|
+
return out
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
func (h *Host) Close() error {
|
|
1526
|
+
leave, err := h.enter()
|
|
1527
|
+
if nil != err {
|
|
1528
|
+
return err
|
|
1529
|
+
}
|
|
1530
|
+
defer leave()
|
|
1531
|
+
return h.closeall()
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
func (h *Host) closeall() error {
|
|
1535
|
+
// A bulk teardown removing the holders too, so `hold` is suspended
|
|
1536
|
+
// for exactly those holders (§11.3) - while the consumers-first
|
|
1537
|
+
// cascade still runs, which is the half that matters.
|
|
1538
|
+
h.coordinated = true
|
|
1539
|
+
defer func() { h.coordinated = false }()
|
|
1540
|
+
refs := sortedkeys(h.inst)
|
|
1541
|
+
for i := len(refs) - 1; 0 <= i; i-- {
|
|
1542
|
+
if err := h.unload(refs[i]); nil != err {
|
|
1543
|
+
return err
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
return nil
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// PositionOf is the same record §6.6 gives a plugin about itself,
|
|
1550
|
+
// reachable from outside for the corpus. A plugin asks via
|
|
1551
|
+
// `inst.Position(point)`.
|
|
1552
|
+
func (h *Host) PositionOf(ref string, point string) (Position, error) {
|
|
1553
|
+
e := h.inst[canon(ref)]
|
|
1554
|
+
if nil == e {
|
|
1555
|
+
return Position{}, Fail("plugin_not_loaded", "no such instance: "+ref,
|
|
1556
|
+
map[string]any{"ref": ref})
|
|
1557
|
+
}
|
|
1558
|
+
ranked, err := h.Order(point)
|
|
1559
|
+
if nil != err {
|
|
1560
|
+
return Position{}, err
|
|
1561
|
+
}
|
|
1562
|
+
index := indexof(ranked, e.Ref)
|
|
1563
|
+
return Position{
|
|
1564
|
+
Index: index, Count: len(ranked),
|
|
1565
|
+
// §6.2 composes b1(b2(b3(base))) with the FIRST binding
|
|
1566
|
+
// OUTERMOST, so these are not index 0 and index count-1 the
|
|
1567
|
+
// other way round. Getting this backwards is the exact error the
|
|
1568
|
+
// positional pin vocabulary exists to prevent.
|
|
1569
|
+
Outermost: 0 == index,
|
|
1570
|
+
Innermost: index == len(ranked)-1,
|
|
1571
|
+
}, nil
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
// Define adds a definition to this host's catalog.
|
|
1575
|
+
func (h *Host) Define(def Definition) error { return h.catalog.Add(def) }
|