@voxgig/sdkgen 4.10.0 → 4.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +330 -0
- package/project/.sdk/model/target/c.aon +15 -2
- package/project/.sdk/model/target/cpp.aon +18 -0
- package/project/.sdk/model/target/lua.aon +17 -0
- package/project/.sdk/model/target/ocaml.aon +48 -5
- package/project/.sdk/model/target/zig.aon +30 -4
- package/project/.sdk/src/cmp/c/Config_c.ts +338 -1
- package/project/.sdk/src/cmp/c/Main_c.ts +57 -2
- package/project/.sdk/src/cmp/c/Package_c.ts +12 -2
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +324 -2
- package/project/.sdk/src/cmp/cpp/Gitignore_cpp.ts +2 -0
- package/project/.sdk/src/cmp/cpp/Main_cpp.ts +62 -4
- package/project/.sdk/src/cmp/cpp/Package_cpp.ts +10 -0
- package/project/.sdk/src/cmp/lua/Config_lua.ts +88 -0
- package/project/.sdk/src/cmp/lua/Gitignore_lua.ts +4 -0
- package/project/.sdk/src/cmp/lua/Main_lua.ts +93 -3
- package/project/.sdk/src/cmp/lua/Package_lua.ts +60 -0
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +10 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +183 -5
- package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +160 -3
- package/project/.sdk/src/cmp/ocaml/Package_ocaml.ts +25 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +130 -9
- package/project/.sdk/src/cmp/zig/Main_zig.ts +208 -4
- package/project/.sdk/{tm/zig/build.zig → src/cmp/zig/fragment/Build.fragment.zig} +7 -0
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +25 -0
- package/project/.sdk/{tm/zig/root.zig → src/cmp/zig/fragment/Root.fragment.zig} +9 -0
- package/project/.sdk/tm/c/Makefile +58 -9
- package/project/.sdk/tm/c/core/sdk.h +10 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.c +133 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/capability.h +39 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.c +83 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/catalog.h +48 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.c +413 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/config.h +24 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.c +264 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/depend.h +29 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.c +220 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/env.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.c +97 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/export.h +19 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.c +237 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/graph.h +17 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.c +1494 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/host.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.c +288 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/order.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/point.h +80 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.c +170 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/ref.h +52 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.c +98 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/resolve.h +18 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.c +150 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/types.h +104 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.c +649 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/value.h +135 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.c +151 -0
- package/project/.sdk/tm/c/feature/secrets/plugin/version.h +34 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/aws.c +281 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/azuresecrets.c +207 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/boru.c +164 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/clock.c +75 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/doppler.c +141 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/encode.c +144 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/gcpsecrets.c +177 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/hashicorp.c +239 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/httpjson.c +599 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/infisical.c +165 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/onepassword.c +194 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/proc.c +233 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/secretspec.c +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sekretoplugins.h +125 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sha256.c +270 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/sigv4.c +382 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/support.h +153 -0
- package/project/.sdk/tm/c/feature/secrets/plugins/tls.c +420 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/internal.h +86 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/json.c +721 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/providers.c +813 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.c +944 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/sekreto.h +490 -0
- package/project/.sdk/tm/c/feature/secrets/sekreto/util.c +442 -0
- package/project/.sdk/tm/c/feature/secrets.c +797 -0
- package/project/.sdk/tm/c/feature/secrets.h +46 -0
- package/project/.sdk/tm/c/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/c/tests/feature/secrets/secrets_test.c +802 -0
- package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +38 -11
- package/project/.sdk/tm/clojure/test/sdk/test/feature/secrets.clj +40 -6
- package/project/.sdk/tm/cpp/Makefile +48 -4
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/capability.hpp +41 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.cpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/catalog.hpp +66 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.cpp +376 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/config.hpp +29 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.cpp +230 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/depend.hpp +31 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.cpp +205 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/env.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.cpp +84 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/export.hpp +24 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.cpp +222 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/graph.hpp +21 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.cpp +1200 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/host.hpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.cpp +250 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/order.hpp +22 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.cpp +110 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/point.hpp +79 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.cpp +136 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/ref.hpp +60 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.cpp +82 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/resolve.hpp +25 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.cpp +90 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/types.hpp +65 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.cpp +491 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/value.hpp +120 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.cpp +143 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugin/version.hpp +36 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.cpp +248 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Aws.hpp +30 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.cpp +149 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Azuresecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.cpp +117 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Boru.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.cpp +180 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Crypto.hpp +52 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.cpp +87 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Doppler.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.cpp +121 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Gcpsecrets.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.cpp +153 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Hashicorp.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.cpp +666 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Httpjson.hpp +160 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.cpp +123 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Infisical.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.cpp +125 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Onepassword.hpp +23 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.cpp +223 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Proc.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.cpp +104 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Secretspec.hpp +27 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.cpp +242 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Sigv4.hpp +58 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.cpp +260 -0
- package/project/.sdk/tm/cpp/feature/secrets/plugins/Tls.hpp +47 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.cpp +476 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Json.hpp +89 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.cpp +312 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Provider.hpp +263 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.cpp +354 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Providers.hpp +111 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.cpp +613 -0
- package/project/.sdk/tm/cpp/feature/secrets/sekreto/Sekreto.hpp +227 -0
- package/project/.sdk/tm/cpp/feature/secrets.hpp +745 -0
- package/project/.sdk/tm/cpp/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/cpp/test/feature/secrets/secrets_test.cpp +808 -0
- package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +34 -8
- package/project/.sdk/tm/csharp/test/feature/secrets/SecretsFeatureTest.cs +85 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +17 -2
- package/project/.sdk/tm/elixir/test/feature/secrets/secrets_test.exs +41 -0
- package/project/.sdk/tm/go/feature/secrets_feature.go +32 -10
- package/project/.sdk/tm/go/test/feature/secrets/secrets_feature_test.go +116 -0
- package/project/.sdk/tm/java/feature/SecretsFeature.java +33 -8
- package/project/.sdk/tm/java/test/feature/secrets/SecretsFeatureTest.java +64 -0
- package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +34 -11
- package/project/.sdk/tm/js/test/feature/secrets/Secrets.test.js +73 -0
- package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +32 -7
- package/project/.sdk/tm/kotlin/test/feature/secrets/SecretsTest.kt +69 -0
- package/project/.sdk/tm/lua/Makefile +15 -4
- package/project/.sdk/tm/lua/feature/secrets/native/sekretonet.c +806 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/capability.lua +135 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/catalog.lua +63 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/config.lua +361 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/depend.lua +232 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/env.lua +168 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/export.lua +66 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/graph.lua +194 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/host.lua +1190 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/json.lua +169 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/order.lua +207 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/point.lua +106 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/ref.lua +115 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/resolve.lua +78 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/types.lua +275 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin/version.lua +131 -0
- package/project/.sdk/tm/lua/feature/secrets/plugin.lua +76 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/addr.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/err.lua +47 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/name.lua +315 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/aws.lua +228 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/azuresecrets.lua +137 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/boru.lua +124 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/crypto.lua +212 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/doppler.lua +89 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/gcpsecrets.lua +129 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/hashicorp.lua +154 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/httpjson.lua +248 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/infisical.lua +113 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/json.lua +449 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/net.lua +198 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/onepassword.lua +126 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/secretspec.lua +93 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/sigv4.lua +269 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/plugins/support.lua +146 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto/providers.lua +346 -0
- package/project/.sdk/tm/lua/feature/secrets/sekreto.lua +479 -0
- package/project/.sdk/tm/lua/feature/secrets_feature.lua +690 -0
- package/project/.sdk/tm/lua/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/lua/test/extend_test.lua +105 -0
- package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +862 -0
- package/project/.sdk/tm/ocaml/Makefile +52 -8
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/capability.ml +106 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/catalog.ml +43 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/config.ml +349 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/defs.ml +143 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/depend.ml +217 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/env.ml +197 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/export.ml +89 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/graph.ml +227 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/host.ml +1112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/order.ml +202 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/point.ml +138 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/ref.ml +126 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/resolve.ml +62 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/types.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/value.ml +387 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugin/version.ml +139 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/aws.ml +171 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/azuresecrets.ml +116 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/boru.ml +95 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/crypto.ml +149 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/doppler.ml +61 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/gcpsecrets.ml +93 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/hashicorp.ml +120 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/http.ml +421 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/httpjson.ml +66 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/infisical.ml +85 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/onepassword.ml +112 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/runcmd.ml +97 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/secretspec.ml +68 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/sigv4.ml +259 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls.ml +44 -0
- package/project/.sdk/tm/ocaml/feature/secrets/plugins/tls_stubs.c +358 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/json.ml +376 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/provider.ml +647 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/secret.ml +299 -0
- package/project/.sdk/tm/ocaml/feature/secrets/sekreto/sekreto.ml +347 -0
- package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +453 -0
- package/project/.sdk/tm/ocaml/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +670 -0
- package/project/.sdk/tm/perl/feature/secrets_feature.pm +33 -12
- package/project/.sdk/tm/perl/t/feature/secrets/secrets.t +47 -0
- package/project/.sdk/tm/php/feature/SecretsFeature.php +30 -6
- package/project/.sdk/tm/php/test/feature/secrets/SecretsTest.php +98 -0
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +28 -6
- package/project/.sdk/tm/py/test/feature/secrets/test_secrets.py +114 -0
- package/project/.sdk/tm/rb/feature/secrets_feature.rb +28 -6
- package/project/.sdk/tm/rb/test/feature/secrets/secrets_feature_test.rb +93 -0
- package/project/.sdk/tm/rust/feature/secrets.rs +33 -10
- package/project/.sdk/tm/rust/tests/feature/secrets/main.rs +96 -0
- package/project/.sdk/tm/scala/feature/SecretsFeature.scala +29 -7
- package/project/.sdk/tm/scala/sdktest/feature/secrets/SecretsTestMain.scala +71 -0
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +35 -12
- package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +73 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/capability.zig +137 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/catalog.zig +90 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/config.zig +346 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/depend.zig +244 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/env.zig +201 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/export.zig +83 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/graph.zig +212 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/host.zig +1224 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/inst.zig +126 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/order.zig +228 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/plugin.zig +34 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/point.zig +166 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/ref.zig +147 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/resolve.zig +74 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/types.zig +140 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/value.zig +662 -0
- package/project/.sdk/tm/zig/feature/secrets/plugin/version.zig +139 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/aws.zig +294 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/azuresecrets.zig +222 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/boru.zig +188 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/doppler.zig +135 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/gcpsecrets.zig +170 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/hashicorp.zig +242 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/httpjson.zig +535 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/infisical.zig +167 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/onepassword.zig +202 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/secretspec.zig +160 -0
- package/project/.sdk/tm/zig/feature/secrets/plugins/sigv4.zig +336 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/addr.zig +148 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/builtins.zig +259 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/provider.zig +440 -0
- package/project/.sdk/tm/zig/feature/secrets/sekreto/sekreto.zig +944 -0
- package/project/.sdk/tm/zig/feature/secrets.zig +854 -0
- package/project/.sdk/tm/zig/src/feature/secrets/.gitkeep +0 -0
- package/project/.sdk/tm/zig/test/feature/secrets/secrets_test.zig +891 -0
- package/project/sdkgen-package.json +1 -1
|
@@ -0,0 +1,1494 @@
|
|
|
1
|
+
// VENDORED: @voxgig/plugin sdk-20260908-1556-0 (c/src/host.c)
|
|
2
|
+
// Source: https://github.com/voxgig/plugin @ 48392f5e2b6d1434ee9b1a4a9a11f4480aaeb46a [tag: sdk-20260908-1556-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),
|
|
5
|
+
* and resource capture (§8). See host.h for the two rules that shape
|
|
6
|
+
* every function here. */
|
|
7
|
+
|
|
8
|
+
#include <stdio.h>
|
|
9
|
+
#include <stdlib.h>
|
|
10
|
+
#include <string.h>
|
|
11
|
+
|
|
12
|
+
#include "host.h"
|
|
13
|
+
#include "capability.h"
|
|
14
|
+
#include "config.h"
|
|
15
|
+
#include "depend.h"
|
|
16
|
+
#include "export.h"
|
|
17
|
+
#include "graph.h"
|
|
18
|
+
#include "order.h"
|
|
19
|
+
#include "ref.h"
|
|
20
|
+
|
|
21
|
+
typedef struct ScopeEntry {
|
|
22
|
+
ReleaseFn fn;
|
|
23
|
+
void *ctx;
|
|
24
|
+
bool done;
|
|
25
|
+
/* `acquire` and `release` both count toward `open`; a nested host's
|
|
26
|
+
* teardown does NOT — a teardown is not an acquisition, and the inner
|
|
27
|
+
* host keeps its own counter (`nest/open`). */
|
|
28
|
+
bool counts;
|
|
29
|
+
} ScopeEntry;
|
|
30
|
+
|
|
31
|
+
struct Inst {
|
|
32
|
+
const char *ref;
|
|
33
|
+
Definition *def;
|
|
34
|
+
const char *status;
|
|
35
|
+
double pos;
|
|
36
|
+
double seq;
|
|
37
|
+
Value *options;
|
|
38
|
+
Value *state;
|
|
39
|
+
Value *order;
|
|
40
|
+
/* §11.4's ALWAYS-RELUCTANT rebinding, made concrete: the provider ref
|
|
41
|
+
* this instance's activation actually selected, per requirement name.
|
|
42
|
+
* Recomputing the best candidate on every question silently re-points
|
|
43
|
+
* a live consumer at any better-ranked newcomer, and then losing the
|
|
44
|
+
* provider it was really using does not restart it. */
|
|
45
|
+
Value *selected;
|
|
46
|
+
/* §9.6's `active: false`. THE BAR OUTLIVES THE APPLY THAT SET IT: a
|
|
47
|
+
* flag consulted only while `apply` ran let a later direct `ready`
|
|
48
|
+
* bring the instance live, which is the config switch it exists to be
|
|
49
|
+
* silently ignored. */
|
|
50
|
+
bool barred;
|
|
51
|
+
Value *unmet;
|
|
52
|
+
ScopeEntry **scope;
|
|
53
|
+
size_t nscope;
|
|
54
|
+
size_t capscope;
|
|
55
|
+
/* Declared in `define`, inserted only when activation SUCCEEDS
|
|
56
|
+
* (§8.1). Holding them until then is what makes a failed activate
|
|
57
|
+
* leave nothing behind. */
|
|
58
|
+
Bound **bindings;
|
|
59
|
+
size_t nbindings;
|
|
60
|
+
size_t capbindings;
|
|
61
|
+
Host *inner;
|
|
62
|
+
/* Declared in `define`, and VISIBLE while merely `loaded` (§11): they
|
|
63
|
+
* are data, and hiding them would make the loaded state useless for
|
|
64
|
+
* introspection. */
|
|
65
|
+
Value *exports;
|
|
66
|
+
Value *provides;
|
|
67
|
+
Host *owner;
|
|
68
|
+
struct Inst *next;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
struct Host {
|
|
72
|
+
Catalog *catalog;
|
|
73
|
+
Value *reserved;
|
|
74
|
+
Value *keys;
|
|
75
|
+
Value *defaults;
|
|
76
|
+
Value *profile;
|
|
77
|
+
Value *points;
|
|
78
|
+
HookFn *basefns;
|
|
79
|
+
Value *basepoints;
|
|
80
|
+
const char *dependency;
|
|
81
|
+
/* Set for the duration of a bulk teardown, so `held` knows this is a
|
|
82
|
+
* coordinated operation rather than an ad-hoc deactivation. */
|
|
83
|
+
bool coordinated;
|
|
84
|
+
|
|
85
|
+
Inst *first;
|
|
86
|
+
Value *log;
|
|
87
|
+
Value *events;
|
|
88
|
+
double seqn;
|
|
89
|
+
double open;
|
|
90
|
+
bool intransition;
|
|
91
|
+
/* WHICH callback is running, not merely that one is. §8.1 puts
|
|
92
|
+
* resource capture in `activate` and §8.3 says `release` outside
|
|
93
|
+
* `activate` is `plugin_release_scope` — and a boolean alone cannot
|
|
94
|
+
* tell `activate` from `define`, so it admitted an acquire in
|
|
95
|
+
* `define` whose scope `unload` would never unwind. */
|
|
96
|
+
const char *phase;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/* ------------------------------------------------------------------ */
|
|
100
|
+
/* registry helpers */
|
|
101
|
+
/* ------------------------------------------------------------------ */
|
|
102
|
+
|
|
103
|
+
static int bytewise(const void *a, const void *b) {
|
|
104
|
+
return strcmp(*(const char *const *)a, *(const char *const *)b);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/* Every instance ref, SORTED — the deterministic walk §4 rule 4
|
|
108
|
+
* requires in a language whose containers have no inherent order. */
|
|
109
|
+
static size_t sortedrefs(Host *h, const char ***out) {
|
|
110
|
+
size_t n = 0;
|
|
111
|
+
for (Inst *e = h->first; NULL != e; e = e->next) n++;
|
|
112
|
+
const char **refs = (const char **)arena_alloc(sizeof(char *) * (n + 1));
|
|
113
|
+
size_t i = 0;
|
|
114
|
+
for (Inst *e = h->first; NULL != e; e = e->next) refs[i++] = e->ref;
|
|
115
|
+
if (1 < n) qsort((void *)refs, n, sizeof(char *), bytewise);
|
|
116
|
+
*out = refs;
|
|
117
|
+
return n;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static Inst *findinst(Host *h, const char *ref) {
|
|
121
|
+
for (Inst *e = h->first; NULL != e; e = e->next) {
|
|
122
|
+
if (0 == strcmp(e->ref, ref)) return e;
|
|
123
|
+
}
|
|
124
|
+
return NULL;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static void removeinst(Host *h, Inst *target) {
|
|
128
|
+
Inst *prev = NULL;
|
|
129
|
+
for (Inst *e = h->first; NULL != e; e = e->next) {
|
|
130
|
+
if (e == target) {
|
|
131
|
+
if (NULL == prev) h->first = e->next;
|
|
132
|
+
else prev->next = e->next;
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
prev = e;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
static size_t instcount(Host *h) {
|
|
140
|
+
size_t n = 0;
|
|
141
|
+
for (Inst *e = h->first; NULL != e; e = e->next) n++;
|
|
142
|
+
return n;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
Catalog *host_catalog(Host *h) { return h->catalog; }
|
|
146
|
+
void host_define(Host *h, Definition *def) { catalog_add(h->catalog, def); }
|
|
147
|
+
|
|
148
|
+
Host *makehost(HostOptions *opts) {
|
|
149
|
+
Host *h = (Host *)arena_alloc(sizeof(Host));
|
|
150
|
+
h->catalog = (NULL != opts && NULL != opts->catalog) ? opts->catalog : makecatalog();
|
|
151
|
+
h->reserved = (NULL != opts) ? opts->reserved : NULL;
|
|
152
|
+
h->keys = (NULL != opts) ? opts->keys : NULL;
|
|
153
|
+
h->defaults = (NULL != opts) ? opts->defaults : NULL;
|
|
154
|
+
h->profile = (NULL != opts) ? opts->profile : NULL;
|
|
155
|
+
h->points = (NULL != opts && vismap(opts->points)) ? opts->points : vmap();
|
|
156
|
+
h->basefns = (NULL != opts) ? opts->basefns : NULL;
|
|
157
|
+
h->basepoints = (NULL != opts) ? opts->basepoints : NULL;
|
|
158
|
+
h->dependency = (NULL != opts && NULL != opts->dependency) ? opts->dependency : "restart";
|
|
159
|
+
h->coordinated = false;
|
|
160
|
+
h->first = NULL;
|
|
161
|
+
h->log = vlist();
|
|
162
|
+
h->events = vlist();
|
|
163
|
+
h->seqn = 0;
|
|
164
|
+
h->open = 0;
|
|
165
|
+
h->intransition = false;
|
|
166
|
+
h->phase = NULL;
|
|
167
|
+
return h;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* ------------------------------------------------------------------ */
|
|
171
|
+
/* observation */
|
|
172
|
+
/* ------------------------------------------------------------------ */
|
|
173
|
+
|
|
174
|
+
Value *host_list(Host *h) {
|
|
175
|
+
Value *out = vmap();
|
|
176
|
+
const char **refs;
|
|
177
|
+
size_t n = sortedrefs(h, &refs);
|
|
178
|
+
for (size_t i = 0; i < n; i++) {
|
|
179
|
+
vset(out, refs[i], vstr(findinst(h, refs[i])->status));
|
|
180
|
+
}
|
|
181
|
+
return out;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
Inst *host_instance(Host *h, const char *ref) {
|
|
185
|
+
/* The VALIDATING canonicalizer, not the forgiving one: a lookup with
|
|
186
|
+
* a malformed ref is `plugin_bad_name`, not a miss
|
|
187
|
+
* (`declare/lookup#malformed`). Rust and swift both wrote this with
|
|
188
|
+
* `canon` and failed that entry. */
|
|
189
|
+
return findinst(h, canonref(vstr(ref)));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
Inst *inst_of(Host *h, const char *ref) { return host_instance(h, ref); }
|
|
193
|
+
|
|
194
|
+
Value *host_observable(Host *h, Value *result, bool hasresult) {
|
|
195
|
+
Value *out = vmap();
|
|
196
|
+
vset(out, "status", host_list(h));
|
|
197
|
+
vset(out, "open", vnum(h->open));
|
|
198
|
+
Value *log = vlist();
|
|
199
|
+
for (size_t i = 0; i < vlen(h->log); i++) vpush(log, vat(h->log, i));
|
|
200
|
+
vset(out, "log", log);
|
|
201
|
+
vset(out, "result", (!hasresult || NULL == result) ? vnull() : result);
|
|
202
|
+
return out;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* A COPY, not the live list: the canonical is `trace: () => events.slice()`, and `observable` already copies the log. Returning the live list lets a caller append to or delete from the host's own event record — application observation code fabricating or erasing lifecycle history. */
|
|
206
|
+
Value *host_trace(Host *h) {
|
|
207
|
+
Value *out = vlist();
|
|
208
|
+
for (size_t i = 0; i < vlen(h->events); i++) vpush(out, vat(h->events, i));
|
|
209
|
+
return out;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
Value *inst_status(Inst *i) { return vstr(i->status); }
|
|
213
|
+
double inst_seq(Inst *i) { return i->seq; }
|
|
214
|
+
double inst_pos(Inst *i) { return i->pos; }
|
|
215
|
+
Host *inst_inner(Inst *i) { return i->inner; }
|
|
216
|
+
const char *inst_ref(Inst *i) { return i->ref; }
|
|
217
|
+
const char *inst_name(Inst *i) { return refname(i->ref); }
|
|
218
|
+
const char *inst_tag(Inst *i) {
|
|
219
|
+
const char *cut = strchr(i->ref, '$');
|
|
220
|
+
return NULL == cut ? "" : cut + 1;
|
|
221
|
+
}
|
|
222
|
+
Value *inst_options(Inst *i) { return i->options; }
|
|
223
|
+
Value *inst_state(Inst *i) { return i->state; }
|
|
224
|
+
Host *inst_host(Inst *i) { return i->owner; }
|
|
225
|
+
|
|
226
|
+
/* ------------------------------------------------------------------ */
|
|
227
|
+
/* guards */
|
|
228
|
+
/* ------------------------------------------------------------------ */
|
|
229
|
+
|
|
230
|
+
static void guard(Host *h) {
|
|
231
|
+
if (h->intransition) {
|
|
232
|
+
fail("plugin_reentrant",
|
|
233
|
+
"transition attempted from inside a lifecycle callback", NULL);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
static Inst *need(Host *h, const char *ref) {
|
|
238
|
+
const char *r = canonref(vstr(ref));
|
|
239
|
+
Inst *e = findinst(h, r);
|
|
240
|
+
if (NULL == e) {
|
|
241
|
+
size_t sz = strlen(r) + 32;
|
|
242
|
+
char *text = (char *)arena_alloc(sz);
|
|
243
|
+
snprintf(text, sz, "no such instance: %s", r);
|
|
244
|
+
fail("plugin_not_loaded", text, details1("ref", vstr(r)));
|
|
245
|
+
}
|
|
246
|
+
return e;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
static void checkreserved(Host *h, const char *ref) {
|
|
250
|
+
if (!vislist(h->reserved) || 0 == vlen(h->reserved)) return;
|
|
251
|
+
const char *name = refname(ref);
|
|
252
|
+
for (size_t i = 0; i < vlen(h->reserved); i++) {
|
|
253
|
+
Value *r = vat(h->reserved, i);
|
|
254
|
+
if (visstr(r) && 0 == strcmp(vasstr(r), name)) {
|
|
255
|
+
size_t sz = strlen(ref) + 48;
|
|
256
|
+
char *text = (char *)arena_alloc(sz);
|
|
257
|
+
snprintf(text, sz, "ref is reserved by the host: %s", ref);
|
|
258
|
+
fail("plugin_ref_reserved", text, details1("ref", vstr(ref)));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/* ------------------------------------------------------------------ */
|
|
264
|
+
/* scope */
|
|
265
|
+
/* ------------------------------------------------------------------ */
|
|
266
|
+
|
|
267
|
+
static void pushscope(Inst *e, ReleaseFn fn, void *ctx, bool counts) {
|
|
268
|
+
if (e->nscope == e->capscope) {
|
|
269
|
+
size_t cap = 0 == e->capscope ? 8 : e->capscope * 2;
|
|
270
|
+
ScopeEntry **grown = (ScopeEntry **)arena_alloc(sizeof(ScopeEntry *) * cap);
|
|
271
|
+
for (size_t i = 0; i < e->nscope; i++) grown[i] = e->scope[i];
|
|
272
|
+
e->scope = grown;
|
|
273
|
+
e->capscope = cap;
|
|
274
|
+
}
|
|
275
|
+
ScopeEntry *s = (ScopeEntry *)arena_alloc(sizeof(ScopeEntry));
|
|
276
|
+
s->fn = fn;
|
|
277
|
+
s->ctx = ctx;
|
|
278
|
+
s->done = false;
|
|
279
|
+
s->counts = counts;
|
|
280
|
+
e->scope[e->nscope++] = s;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/* A selection belongs to ONE activation (§11.4). Leaving `live` by any
|
|
284
|
+
* door drops it, so the next activation ranks afresh — keeping it would
|
|
285
|
+
* make a consumer prefer a provider it never actually ran against.
|
|
286
|
+
*
|
|
287
|
+
* Returns the errors the scope raised. §8.3: "A failing release does
|
|
288
|
+
* not stop the rest. Every entry runs, in reverse order, whatever any
|
|
289
|
+
* of them does; the errors are collected and raised as one
|
|
290
|
+
* `plugin_release_failed`." */
|
|
291
|
+
static Value *unwind(Host *h, Inst *e) {
|
|
292
|
+
e->selected = vmap();
|
|
293
|
+
Value *errors = vlist();
|
|
294
|
+
for (size_t k = e->nscope; 0 < k; k--) {
|
|
295
|
+
ScopeEntry *s = e->scope[k - 1];
|
|
296
|
+
if (s->done) continue;
|
|
297
|
+
s->done = true;
|
|
298
|
+
if (s->counts) h->open -= 1;
|
|
299
|
+
if (NULL == s->fn) continue;
|
|
300
|
+
CatchFrame f;
|
|
301
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
302
|
+
s->fn(s->ctx);
|
|
303
|
+
PLUGIN_END(&f);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
vpush(errors, vstr(plugin_caught()->message));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
e->nscope = 0;
|
|
310
|
+
return errors;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/* §8.3: "A failed release ends the instance in `failed`, exactly as a
|
|
314
|
+
* failed callback does (§5.2) — a release that raised may have leaked,
|
|
315
|
+
* and an instance that may be holding resources it cannot account for
|
|
316
|
+
* must not be reactivated." */
|
|
317
|
+
static void releasecheck(Inst *e, Value *errors) {
|
|
318
|
+
if (0 == vlen(errors)) return;
|
|
319
|
+
e->status = "failed";
|
|
320
|
+
size_t sz = strlen(e->ref) + 64;
|
|
321
|
+
for (size_t i = 0; i < vlen(errors); i++) sz += strlen(vasstr(vat(errors, i))) + 4;
|
|
322
|
+
char *text = (char *)arena_alloc(sz);
|
|
323
|
+
size_t used = (size_t)snprintf(text, sz, "release failed for %s: ", e->ref);
|
|
324
|
+
for (size_t i = 0; i < vlen(errors); i++) {
|
|
325
|
+
used += (size_t)snprintf(text + used, sz - used, "%s%s",
|
|
326
|
+
0 < i ? "; " : "", vasstr(vat(errors, i)));
|
|
327
|
+
}
|
|
328
|
+
Value *d = vmap();
|
|
329
|
+
vset(d, "ref", vstr(e->ref));
|
|
330
|
+
vset(d, "cause", errors);
|
|
331
|
+
fail("plugin_release_failed", text, d);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/* ------------------------------------------------------------------ */
|
|
335
|
+
/* the instance api */
|
|
336
|
+
/* ------------------------------------------------------------------ */
|
|
337
|
+
|
|
338
|
+
AcquireHandle *inst_acquire(Inst *e) {
|
|
339
|
+
/* §8.1: resources are "acquired during `activate` — the scope's
|
|
340
|
+
* actual job". */
|
|
341
|
+
if (NULL == e->owner->phase || 0 != strcmp(e->owner->phase, "activate")) {
|
|
342
|
+
fail("plugin_release_scope", "acquire called outside activate", NULL);
|
|
343
|
+
}
|
|
344
|
+
pushscope(e, NULL, NULL, true);
|
|
345
|
+
e->owner->open += 1;
|
|
346
|
+
return e->scope[e->nscope - 1];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* Hand a resource back before teardown. Idempotent, and the scope keeps
|
|
350
|
+
* the entry: unwinding it again must be a no-op, or releasing early
|
|
351
|
+
* would make teardown wrong. */
|
|
352
|
+
void inst_giveback(Inst *e, AcquireHandle *handle) {
|
|
353
|
+
if (NULL == handle || handle->done) return;
|
|
354
|
+
handle->done = true;
|
|
355
|
+
if (handle->counts) e->owner->open -= 1;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
void inst_release(Inst *e, ReleaseFn fn, void *ctx) {
|
|
359
|
+
/* §8.3: "`inst.release` outside `activate` is `plugin_release_scope`".
|
|
360
|
+
* Being in a transition is true in `define` too, and a scope entry
|
|
361
|
+
* registered there is never unwound — `unload` on a merely `loaded`
|
|
362
|
+
* instance does not unwind, because a loaded instance is not supposed
|
|
363
|
+
* to hold anything. */
|
|
364
|
+
if (NULL == e->owner->phase || 0 != strcmp(e->owner->phase, "activate")) {
|
|
365
|
+
fail("plugin_release_scope", "release called outside activate", NULL);
|
|
366
|
+
}
|
|
367
|
+
/* SYMMETRIC WITH `acquire`, and it has to be: `open` counts the
|
|
368
|
+
* resources CURRENTLY HELD, so an entry that is registered and then
|
|
369
|
+
* unwound must leave the count where it found it. Incrementing on
|
|
370
|
+
* registration and never decrementing made every `release` a
|
|
371
|
+
* permanent leak in the counter. */
|
|
372
|
+
pushscope(e, fn, ctx, true);
|
|
373
|
+
e->owner->open += 1;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
void inst_bind(Inst *e, const char *point, HookFn hook, ChainFn chain,
|
|
377
|
+
void *ctx, Value *band) {
|
|
378
|
+
Host *h = e->owner;
|
|
379
|
+
/* §12's `plugin_bind_scope`: "binding declared outside `define`". §8.1
|
|
380
|
+
* puts binding declaration in `define` and insertion at a SUCCESSFUL
|
|
381
|
+
* activate, and the guard was the half that never got written — so a
|
|
382
|
+
* binding added from `activate` went live without being part of the
|
|
383
|
+
* loaded definition, and a deactivate/activate cycle appended it
|
|
384
|
+
* again. The code was in the table before anything raised it. */
|
|
385
|
+
if (NULL == h->phase || 0 != strcmp(h->phase, "define")) {
|
|
386
|
+
size_t sz = strlen(point) + 48;
|
|
387
|
+
char *text = (char *)arena_alloc(sz);
|
|
388
|
+
snprintf(text, sz, "bind called outside define: %s", point);
|
|
389
|
+
Value *d = vmap();
|
|
390
|
+
vset(d, "ref", vstr(e->ref));
|
|
391
|
+
vset(d, "point", vstr(point));
|
|
392
|
+
fail("plugin_bind_scope", text, d);
|
|
393
|
+
}
|
|
394
|
+
if (!vhas(h->points, point)) {
|
|
395
|
+
size_t sz = strlen(point) + 32;
|
|
396
|
+
char *text = (char *)arena_alloc(sz);
|
|
397
|
+
snprintf(text, sz, "no such point: %s", point);
|
|
398
|
+
fail("plugin_point_unknown", text, details1("point", vstr(point)));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (e->nbindings == e->capbindings) {
|
|
402
|
+
size_t cap = 0 == e->capbindings ? 8 : e->capbindings * 2;
|
|
403
|
+
Bound **grown = (Bound **)arena_alloc(sizeof(Bound *) * cap);
|
|
404
|
+
for (size_t i = 0; i < e->nbindings; i++) grown[i] = e->bindings[i];
|
|
405
|
+
e->bindings = grown;
|
|
406
|
+
e->capbindings = cap;
|
|
407
|
+
}
|
|
408
|
+
Bound *b = (Bound *)arena_alloc(sizeof(Bound));
|
|
409
|
+
b->ref = e->ref;
|
|
410
|
+
b->point = arena_strdup(point);
|
|
411
|
+
b->band = visnum(band) ? vasnum(band) : 0.0;
|
|
412
|
+
b->hook = hook;
|
|
413
|
+
b->chain = chain;
|
|
414
|
+
b->ctx = ctx;
|
|
415
|
+
e->bindings[e->nbindings++] = b;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
void inst_export(Inst *e, const char *key, Value *value) {
|
|
419
|
+
vset(e->exports, key, value);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
void inst_provides(Inst *e, Value *p) { vpush(e->provides, p); }
|
|
423
|
+
|
|
424
|
+
/* The scope entry carries the inner host as its context — C's stand-in
|
|
425
|
+
* for the closure every other port writes here. */
|
|
426
|
+
static void closeinner(void *ctx) { host_close((Host *)ctx); }
|
|
427
|
+
|
|
428
|
+
Host *inst_nest(Inst *e, HostOptions *opts) {
|
|
429
|
+
Host *h = e->owner;
|
|
430
|
+
if (!h->intransition) {
|
|
431
|
+
fail("plugin_release_scope", "nest called outside a lifecycle callback", NULL);
|
|
432
|
+
}
|
|
433
|
+
/* AN INSTANCE MAY ITSELF BE A HOST (§6.5), and THE OUTER ONE OWNS THE
|
|
434
|
+
* INNER ONE'S LIFETIME. Registering the teardown in the instance
|
|
435
|
+
* scope is what makes that true rather than aspirational: the inner
|
|
436
|
+
* host closes when the outer instance deactivates, in the same
|
|
437
|
+
* reverse unwind as every other resource.
|
|
438
|
+
*
|
|
439
|
+
* It does NOT count toward `open` — a teardown is not an acquisition
|
|
440
|
+
* (`nest/open`). */
|
|
441
|
+
Host *inner = makehost(opts);
|
|
442
|
+
pushscope(e, closeinner, inner, false);
|
|
443
|
+
e->inner = inner;
|
|
444
|
+
return inner;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/* ------------------------------------------------------------------ */
|
|
448
|
+
/* running a callback */
|
|
449
|
+
/* ------------------------------------------------------------------ */
|
|
450
|
+
|
|
451
|
+
static void run(Host *h, Inst *e, const char *at) {
|
|
452
|
+
LifecycleFn fn = definition_callback(e->def, at);
|
|
453
|
+
|
|
454
|
+
size_t sz = strlen(e->ref) + strlen(at) + 2;
|
|
455
|
+
char *entry = (char *)arena_alloc(sz);
|
|
456
|
+
snprintf(entry, sz, "%s:%s", e->ref, at);
|
|
457
|
+
vpush(h->log, vstr(entry));
|
|
458
|
+
|
|
459
|
+
Value *ev = vmap();
|
|
460
|
+
vset(ev, "ref", vstr(e->ref));
|
|
461
|
+
vset(ev, "event", vstr(at));
|
|
462
|
+
vset(ev, "seq", vnum(e->seq));
|
|
463
|
+
vset(ev, "status", vstr(e->status));
|
|
464
|
+
vpush(h->events, ev);
|
|
465
|
+
|
|
466
|
+
if (NULL == fn) return;
|
|
467
|
+
|
|
468
|
+
h->intransition = true;
|
|
469
|
+
h->phase = at;
|
|
470
|
+
|
|
471
|
+
CatchFrame f;
|
|
472
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
473
|
+
fn(e);
|
|
474
|
+
PLUGIN_END(&f);
|
|
475
|
+
h->intransition = false;
|
|
476
|
+
h->phase = NULL;
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
h->intransition = false;
|
|
481
|
+
h->phase = NULL;
|
|
482
|
+
|
|
483
|
+
/* §12: `plugin_define_failed` and its three siblings are "a callback
|
|
484
|
+
* raised; wraps the cause". AN ERROR THAT ALREADY CARRIES A CODE
|
|
485
|
+
* KEEPS IT — the code is the error's identity, and a plugin that
|
|
486
|
+
* raised `store_unreachable` must not have it rewritten. Only a
|
|
487
|
+
* code-less error is wrapped, which is the ordinary case for a
|
|
488
|
+
* callback that let a library error escape. */
|
|
489
|
+
if (NULL != plugin_caught()->code && '\0' != plugin_caught()->code[0] &&
|
|
490
|
+
0 != strcmp(plugin_caught()->code, "plugin_bare")) {
|
|
491
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
492
|
+
}
|
|
493
|
+
size_t tsz = strlen(e->ref) + strlen(at) + strlen(plugin_caught()->text) + 48;
|
|
494
|
+
char *text = (char *)arena_alloc(tsz);
|
|
495
|
+
snprintf(text, tsz, "%s raised in %s: %s", e->ref, at, plugin_caught()->text);
|
|
496
|
+
char *code = (char *)arena_alloc(strlen(at) + 24);
|
|
497
|
+
snprintf(code, strlen(at) + 24, "plugin_%s_failed", at);
|
|
498
|
+
Value *d = vmap();
|
|
499
|
+
vset(d, "ref", vstr(e->ref));
|
|
500
|
+
vset(d, "cause", vstr(plugin_caught()->text));
|
|
501
|
+
fail(code, text, d);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/* ------------------------------------------------------------------ */
|
|
505
|
+
/* requirements and providers */
|
|
506
|
+
/* ------------------------------------------------------------------ */
|
|
507
|
+
|
|
508
|
+
static Value *providersof(Host *h, Value *req) {
|
|
509
|
+
Value *cands = vlist();
|
|
510
|
+
/* ASK WHETHER THE NAME IS A REF, do not assume it. A requirement name
|
|
511
|
+
* is a CAPABILITY name first (§11.1) and capability names are
|
|
512
|
+
* free-form, so `2fa` and `my cap` are legal ones that no ref could
|
|
513
|
+
* be called — and `canonref` RAISES on those, which made a perfectly
|
|
514
|
+
* legal document kill the host right here. */
|
|
515
|
+
Value *rname = vget(req, "name");
|
|
516
|
+
const char *asref = visstr(rname) ? tryref(vasstr(rname)) : NULL;
|
|
517
|
+
|
|
518
|
+
const char **refs;
|
|
519
|
+
size_t n = sortedrefs(h, &refs);
|
|
520
|
+
for (size_t i = 0; i < n; i++) {
|
|
521
|
+
Inst *t = findinst(h, refs[i]);
|
|
522
|
+
if (0 != strcmp(t->status, "live")) continue;
|
|
523
|
+
/* A ref satisfies directly. */
|
|
524
|
+
if (NULL != asref && 0 == strcmp(refs[i], asref)) {
|
|
525
|
+
Value *prov = vmap();
|
|
526
|
+
vset(prov, "name", rname);
|
|
527
|
+
Value *c = vmap();
|
|
528
|
+
vset(c, "ref", vstr(refs[i]));
|
|
529
|
+
vset(c, "pos", vnum(t->pos));
|
|
530
|
+
vset(c, "provides", prov);
|
|
531
|
+
vpush(cands, c);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
for (size_t j = 0; j < vlen(t->provides); j++) {
|
|
535
|
+
Value *p = vat(t->provides, j);
|
|
536
|
+
if (vsame(vget(p, "name"), rname)) {
|
|
537
|
+
Value *c = vmap();
|
|
538
|
+
vset(c, "ref", vstr(refs[i]));
|
|
539
|
+
vset(c, "pos", vnum(t->pos));
|
|
540
|
+
vset(c, "provides", p);
|
|
541
|
+
vpush(cands, c);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return resolvecapability(req, cands);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
static Value *unmetof(Host *h, Inst *e) {
|
|
549
|
+
Value *out = vlist();
|
|
550
|
+
Value *reqs = requirements(e->options);
|
|
551
|
+
for (size_t i = 0; i < vlen(reqs); i++) {
|
|
552
|
+
Value *r = vat(reqs, i);
|
|
553
|
+
if (!gatesactivation(r)) continue;
|
|
554
|
+
if (0 == vlen(providersof(h, r))) vpush(out, vget(r, "name"));
|
|
555
|
+
}
|
|
556
|
+
return out;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/* §11.4's always-reluctant selection, and the ONE place a provider is
|
|
560
|
+
* chosen for a live instance. "A satisfied requirement is not re-bound
|
|
561
|
+
* while it stays satisfied" is a statement about a REMEMBERED choice.
|
|
562
|
+
*
|
|
563
|
+
* `remember` is false for the questions asked ABOUT an instance rather
|
|
564
|
+
* than BY it — introspection must not create a binding. */
|
|
565
|
+
static const char *chosen(Host *h, Inst *e, Value *req, bool remember) {
|
|
566
|
+
Value *cands = providersof(h, req);
|
|
567
|
+
if (0 == vlen(cands)) return NULL;
|
|
568
|
+
const char *name = vasstr(vget(req, "name"));
|
|
569
|
+
Value *heldv = vget(e->selected, name);
|
|
570
|
+
if (visstr(heldv)) {
|
|
571
|
+
for (size_t i = 0; i < vlen(cands); i++) {
|
|
572
|
+
if (0 == strcmp(vasstr(vget(vat(cands, i), "ref")), vasstr(heldv))) {
|
|
573
|
+
return vasstr(heldv);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
const char *first = vasstr(vget(vat(cands, 0), "ref"));
|
|
578
|
+
if (remember) vset(e->selected, name, vstr(first));
|
|
579
|
+
return first;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/* The instance currently SELECTED for each of this one's
|
|
583
|
+
* restart-causing requirements. A BINDING IS TO AN INSTANCE, not to a
|
|
584
|
+
* capability (§11.1): the selected one going away restarts a `static`
|
|
585
|
+
* consumer even though a survivor is available. */
|
|
586
|
+
static Value *boundproviders(Host *h, Inst *e) {
|
|
587
|
+
Value *out = vlist();
|
|
588
|
+
Value *reqs = requirements(e->options);
|
|
589
|
+
for (size_t i = 0; i < vlen(reqs); i++) {
|
|
590
|
+
Value *r = vat(reqs, i);
|
|
591
|
+
if (!restartsonloss(r)) continue;
|
|
592
|
+
const char *ref = chosen(h, e, r, false);
|
|
593
|
+
if (NULL == ref) continue;
|
|
594
|
+
bool dup = false;
|
|
595
|
+
for (size_t j = 0; j < vlen(out); j++) {
|
|
596
|
+
if (0 == strcmp(vasstr(vat(out, j)), ref)) { dup = true; break; }
|
|
597
|
+
}
|
|
598
|
+
if (!dup) vpush(out, vstr(ref));
|
|
599
|
+
}
|
|
600
|
+
return out;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
static Value *consumersof(Host *h, const char *ref) {
|
|
604
|
+
Value *out = vlist();
|
|
605
|
+
const char **refs;
|
|
606
|
+
size_t n = sortedrefs(h, &refs);
|
|
607
|
+
for (size_t i = 0; i < n; i++) {
|
|
608
|
+
if (0 == strcmp(refs[i], ref)) continue;
|
|
609
|
+
Inst *c = findinst(h, refs[i]);
|
|
610
|
+
if (0 != strcmp(c->status, "live")) continue;
|
|
611
|
+
Value *bp = boundproviders(h, c);
|
|
612
|
+
for (size_t j = 0; j < vlen(bp); j++) {
|
|
613
|
+
if (0 == strcmp(vasstr(vat(bp, j)), ref)) { vpush(out, vstr(refs[i])); break; }
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return out;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* §11.3's `hold` asks a DIFFERENT question from the cascade.
|
|
620
|
+
*
|
|
621
|
+
* The cascade wants the edges that RESTART — mandatory-static and
|
|
622
|
+
* optional-static. `hold` says "deactivating a REQUIRED instance is
|
|
623
|
+
* `plugin_dependency_held`", and `required` is CARDINALITY:
|
|
624
|
+
* `gatesactivation`, not `restartsonloss`. The two sets differ in both
|
|
625
|
+
* directions and each difference was a real bug. */
|
|
626
|
+
static Value *holdersof(Host *h, const char *ref) {
|
|
627
|
+
Value *out = vlist();
|
|
628
|
+
const char **refs;
|
|
629
|
+
size_t n = sortedrefs(h, &refs);
|
|
630
|
+
for (size_t i = 0; i < n; i++) {
|
|
631
|
+
if (0 == strcmp(refs[i], ref)) continue;
|
|
632
|
+
Inst *c = findinst(h, refs[i]);
|
|
633
|
+
if (0 != strcmp(c->status, "live")) continue;
|
|
634
|
+
Value *reqs = requirements(c->options);
|
|
635
|
+
for (size_t j = 0; j < vlen(reqs); j++) {
|
|
636
|
+
Value *req = vat(reqs, j);
|
|
637
|
+
if (!gatesactivation(req)) continue;
|
|
638
|
+
const char *sel = chosen(h, c, req, false);
|
|
639
|
+
if (NULL != sel && 0 == strcmp(sel, ref)) { vpush(out, vstr(refs[i])); break; }
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return out;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* The hold check is A GUARD ON AD-HOC DEACTIVATION, NOT ON COORDINATED
|
|
646
|
+
* TEARDOWN. In a bulk operation that is removing the holders too, it is
|
|
647
|
+
* suspended — otherwise `close()` under `hold` would raise on the first
|
|
648
|
+
* provider it reached whenever a document happened to list a consumer
|
|
649
|
+
* after it, which is the policy refusing the one teardown it has no
|
|
650
|
+
* reason to object to. */
|
|
651
|
+
static void held(Host *h, Inst *e) {
|
|
652
|
+
if (0 != strcmp(h->dependency, "hold")) return;
|
|
653
|
+
if (h->coordinated) return;
|
|
654
|
+
Value *holders = holdersof(h, e->ref);
|
|
655
|
+
if (0 == vlen(holders)) return;
|
|
656
|
+
size_t sz = strlen(e->ref) + 64;
|
|
657
|
+
char *text = (char *)arena_alloc(sz);
|
|
658
|
+
snprintf(text, sz, "instance is required by live consumers: %s", e->ref);
|
|
659
|
+
Value *d = vmap();
|
|
660
|
+
vset(d, "ref", vstr(e->ref));
|
|
661
|
+
vset(d, "holders", holders);
|
|
662
|
+
fail("plugin_dependency_held", text, d);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/* The requirement graph as plain data, for the pure detector. */
|
|
666
|
+
static Value *graphnodes(Host *h) {
|
|
667
|
+
Value *out = vlist();
|
|
668
|
+
const char **refs;
|
|
669
|
+
size_t n = sortedrefs(h, &refs);
|
|
670
|
+
for (size_t i = 0; i < n; i++) {
|
|
671
|
+
Inst *e = findinst(h, refs[i]);
|
|
672
|
+
Value *provides = vlist();
|
|
673
|
+
for (size_t j = 0; j < vlen(e->provides); j++) {
|
|
674
|
+
vpush(provides, vget(vat(e->provides, j), "name"));
|
|
675
|
+
}
|
|
676
|
+
Value *node = vmap();
|
|
677
|
+
vset(node, "ref", vstr(refs[i]));
|
|
678
|
+
vset(node, "provides", provides);
|
|
679
|
+
vset(node, "requires", requirements(e->options));
|
|
680
|
+
vpush(out, node);
|
|
681
|
+
}
|
|
682
|
+
return out;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/* ------------------------------------------------------------------ */
|
|
686
|
+
/* ordering and points */
|
|
687
|
+
/* ------------------------------------------------------------------ */
|
|
688
|
+
|
|
689
|
+
Value *host_order(Host *h, const char *point) {
|
|
690
|
+
/* Sorted by declaration SEQUENCE, which is what makes the §7 sort's
|
|
691
|
+
* fall-through deterministic in a language whose containers have no
|
|
692
|
+
* insertion order. §7 breaks ties by `pos`; two instances CAN share
|
|
693
|
+
* one — `declare` defaults `pos` to the registry size, so an unload
|
|
694
|
+
* followed by a fresh declare reuses a surviving instance's — and
|
|
695
|
+
* past that the canonical was falling through to map order. `seq` is
|
|
696
|
+
* that order, made explicit. Found by review of the go port. */
|
|
697
|
+
size_t total = instcount(h);
|
|
698
|
+
Inst **live = (Inst **)arena_alloc(sizeof(Inst *) * (total + 1));
|
|
699
|
+
size_t n = 0;
|
|
700
|
+
for (Inst *e = h->first; NULL != e; e = e->next) {
|
|
701
|
+
if (0 == strcmp(e->status, "live")) live[n++] = e;
|
|
702
|
+
}
|
|
703
|
+
for (size_t i = 0; i + 1 < n; i++) {
|
|
704
|
+
for (size_t j = i + 1; j < n; j++) {
|
|
705
|
+
if (live[j]->seq < live[i]->seq) { Inst *t = live[i]; live[i] = live[j]; live[j] = t; }
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
Value *bindings = vlist();
|
|
710
|
+
for (size_t i = 0; i < n; i++) {
|
|
711
|
+
Value *b = vmap();
|
|
712
|
+
vset(b, "ref", vstr(live[i]->ref));
|
|
713
|
+
vset(b, "pos", vnum(live[i]->pos));
|
|
714
|
+
if (!visnull(live[i]->order)) vset(b, "order", live[i]->order);
|
|
715
|
+
vpush(bindings, b);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
Value *spec = NULL == point ? NULL : vget(h->points, point);
|
|
719
|
+
return resolveorder(bindings, vismap(spec) ? vget(spec, "pin") : NULL);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
Value *host_positionof(Host *h, const char *ref, const char *point) {
|
|
723
|
+
Value *ranked = host_order(h, point);
|
|
724
|
+
const char *r = canonref(vstr(ref));
|
|
725
|
+
double index = -1;
|
|
726
|
+
for (size_t i = 0; i < vlen(ranked); i++) {
|
|
727
|
+
if (0 == strcmp(vasstr(vat(ranked, i)), r)) { index = (double)i; break; }
|
|
728
|
+
}
|
|
729
|
+
Value *out = vmap();
|
|
730
|
+
vset(out, "index", vnum(index));
|
|
731
|
+
vset(out, "count", vnum((double)vlen(ranked)));
|
|
732
|
+
/* §6.2 composes b1(b2(b3(base))) with the FIRST binding OUTERMOST, so
|
|
733
|
+
* these are not index 0 and count-1 the other way round. Getting this
|
|
734
|
+
* backwards is the exact error the positional pin vocabulary exists
|
|
735
|
+
* to prevent. */
|
|
736
|
+
vset(out, "outermost", vbool(0 == index));
|
|
737
|
+
vset(out, "innermost", vbool(index == (double)vlen(ranked) - 1));
|
|
738
|
+
return out;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
Value *inst_position(Inst *e, const char *point) {
|
|
742
|
+
return host_positionof(e->owner, e->ref, point);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/* Live bindings on a point, in resolved order. Recomputed on any change
|
|
746
|
+
* to the live set (§7) rather than cached at startup — the bug a host
|
|
747
|
+
* discovers only when something deactivates in production. */
|
|
748
|
+
static size_t boundon(Host *h, const char *point, Bound ***out) {
|
|
749
|
+
Value *ranked = host_order(h, point);
|
|
750
|
+
size_t cap = 8, n = 0;
|
|
751
|
+
Bound **list = (Bound **)arena_alloc(sizeof(Bound *) * cap);
|
|
752
|
+
for (size_t i = 0; i < vlen(ranked); i++) {
|
|
753
|
+
Inst *e = findinst(h, vasstr(vat(ranked, i)));
|
|
754
|
+
if (NULL == e) continue;
|
|
755
|
+
/* The band is the INSTANCE's ordering block (§7), stamped by the
|
|
756
|
+
* host. A plugin passing its own would be ranking itself above the
|
|
757
|
+
* order its document declared. */
|
|
758
|
+
Value *band = vget(e->order, "band");
|
|
759
|
+
double bandv = visnum(band) ? vasnum(band) : 0.0;
|
|
760
|
+
for (size_t j = 0; j < e->nbindings; j++) {
|
|
761
|
+
if (0 != strcmp(e->bindings[j]->point, point)) continue;
|
|
762
|
+
if (n == cap) {
|
|
763
|
+
cap *= 2;
|
|
764
|
+
Bound **grown = (Bound **)arena_alloc(sizeof(Bound *) * cap);
|
|
765
|
+
for (size_t k = 0; k < n; k++) grown[k] = list[k];
|
|
766
|
+
list = grown;
|
|
767
|
+
}
|
|
768
|
+
Bound *b = (Bound *)arena_alloc(sizeof(Bound));
|
|
769
|
+
*b = *e->bindings[j];
|
|
770
|
+
b->band = bandv;
|
|
771
|
+
list[n++] = b;
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
*out = list;
|
|
775
|
+
return n;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
static Value *pointspec(Host *h, const char *point) {
|
|
779
|
+
Value *spec = vget(h->points, point);
|
|
780
|
+
if (!vhas(h->points, point)) {
|
|
781
|
+
size_t sz = strlen(point) + 32;
|
|
782
|
+
char *text = (char *)arena_alloc(sz);
|
|
783
|
+
snprintf(text, sz, "no such point: %s", point);
|
|
784
|
+
fail("plugin_point_unknown", text, details1("point", vstr(point)));
|
|
785
|
+
}
|
|
786
|
+
return vismap(spec) ? spec : vmap();
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
static void checkkind(Value *spec, const char *point, const char *want) {
|
|
790
|
+
Value *kind = vget(spec, "kind");
|
|
791
|
+
const char *k = visstr(kind) ? vasstr(kind) : NULL;
|
|
792
|
+
bool ok = (NULL == k) ? (0 == strcmp(want, "hook")) : (0 == strcmp(k, want));
|
|
793
|
+
if (ok) return;
|
|
794
|
+
size_t sz = strlen(point) + strlen(want) + 40;
|
|
795
|
+
char *text = (char *)arena_alloc(sz);
|
|
796
|
+
snprintf(text, sz, "point is not a %s: %s", want, point);
|
|
797
|
+
Value *d = vmap();
|
|
798
|
+
vset(d, "point", vstr(point));
|
|
799
|
+
vset(d, "kind", NULL == k ? vnull() : vstr(k));
|
|
800
|
+
fail("plugin_point_kind", text, d);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
Value *host_emit(Host *h, const char *point, Value *arg) {
|
|
804
|
+
Value *spec = pointspec(h, point);
|
|
805
|
+
checkkind(spec, point, "hook");
|
|
806
|
+
Bound **bindings;
|
|
807
|
+
size_t n = boundon(h, point, &bindings);
|
|
808
|
+
Value *mode = vget(spec, "mode");
|
|
809
|
+
Value *errors = NULL;
|
|
810
|
+
Value *out = point_emit(bindings, n, visstr(mode) ? vasstr(mode) : "emit",
|
|
811
|
+
arg, &errors);
|
|
812
|
+
const char *m = visstr(mode) ? vasstr(mode) : "emit";
|
|
813
|
+
if (0 == strcmp(m, "emit")) return NULL;
|
|
814
|
+
if (0 == strcmp(m, "bail")) return out;
|
|
815
|
+
return errors;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
static HookFn basefor(Host *h, const char *point) {
|
|
819
|
+
if (NULL == h->basefns || !vislist(h->basepoints)) return NULL;
|
|
820
|
+
for (size_t i = 0; i < vlen(h->basepoints); i++) {
|
|
821
|
+
if (0 == strcmp(vasstr(vat(h->basepoints, i)), point)) return h->basefns[i];
|
|
822
|
+
}
|
|
823
|
+
return NULL;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
Value *host_call(Host *h, const char *point, Value *arg) {
|
|
827
|
+
Value *spec = pointspec(h, point);
|
|
828
|
+
checkkind(spec, point, "chain");
|
|
829
|
+
Bound **bindings;
|
|
830
|
+
size_t n = boundon(h, point, &bindings);
|
|
831
|
+
/* The host owns the base and a plugin cannot replace it (§6.2). One
|
|
832
|
+
* that wants to SUBSTITUTE rather than wrap binds innermost and
|
|
833
|
+
* simply does not call `next`. */
|
|
834
|
+
return point_call(bindings, n, basefor(h, point), NULL, arg);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
Value *host_provider(Host *h, const char *point, Value *arg) {
|
|
838
|
+
Value *spec = pointspec(h, point);
|
|
839
|
+
checkkind(spec, point, "provider");
|
|
840
|
+
Bound **bindings;
|
|
841
|
+
size_t n = boundon(h, point, &bindings);
|
|
842
|
+
Value *shadow = NULL;
|
|
843
|
+
Bound *winner = point_provider(bindings, n,
|
|
844
|
+
vtruthy(vget(spec, "exclusive")), &shadow);
|
|
845
|
+
if (NULL == winner) return vget(spec, "default");
|
|
846
|
+
return winner->hook(arg, winner->ctx);
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
Value *host_shadowed(Host *h, const char *point) {
|
|
850
|
+
if (!vhas(h->points, point)) return vlist();
|
|
851
|
+
Value *spec = vget(h->points, point);
|
|
852
|
+
Bound **bindings;
|
|
853
|
+
size_t n = boundon(h, point, &bindings);
|
|
854
|
+
Value *shadow = NULL;
|
|
855
|
+
point_provider(bindings, n,
|
|
856
|
+
vismap(spec) && vtruthy(vget(spec, "exclusive")), &shadow);
|
|
857
|
+
return NULL == shadow ? vlist() : shadow;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
Value *host_exports(Host *h, const char *spec) {
|
|
861
|
+
Value *all = vlist();
|
|
862
|
+
const char **refs;
|
|
863
|
+
size_t n = sortedrefs(h, &refs);
|
|
864
|
+
for (size_t i = 0; i < n; i++) {
|
|
865
|
+
Inst *e = findinst(h, refs[i]);
|
|
866
|
+
/* Exports of a `loaded` (not live) instance are VISIBLE (§11). */
|
|
867
|
+
if (0 == strcmp(e->status, "declared") || 0 == strcmp(e->status, "failed")) continue;
|
|
868
|
+
const char **keys;
|
|
869
|
+
size_t kn = vkeys(e->exports, &keys);
|
|
870
|
+
for (size_t j = 0; j < kn; j++) {
|
|
871
|
+
Value *ex = vmap();
|
|
872
|
+
vset(ex, "ref", vstr(refs[i]));
|
|
873
|
+
vset(ex, "key", vstr(keys[j]));
|
|
874
|
+
vset(ex, "value", vget(e->exports, keys[j]));
|
|
875
|
+
vpush(all, ex);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
return resolveexport(vstr(spec), all);
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
Value *host_capability(Host *h, const char *name) {
|
|
882
|
+
Value *cands = vlist();
|
|
883
|
+
const char **refs;
|
|
884
|
+
size_t n = sortedrefs(h, &refs);
|
|
885
|
+
for (size_t i = 0; i < n; i++) {
|
|
886
|
+
Inst *e = findinst(h, refs[i]);
|
|
887
|
+
if (0 != strcmp(e->status, "live")) continue;
|
|
888
|
+
for (size_t j = 0; j < vlen(e->provides); j++) {
|
|
889
|
+
Value *p = vat(e->provides, j);
|
|
890
|
+
if (visstr(vget(p, "name")) && 0 == strcmp(vasstr(vget(p, "name")), name)) {
|
|
891
|
+
Value *c = vmap();
|
|
892
|
+
vset(c, "ref", vstr(refs[i]));
|
|
893
|
+
vset(c, "pos", vnum(e->pos));
|
|
894
|
+
vset(c, "provides", p);
|
|
895
|
+
vpush(cands, c);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
Value *req = vmap();
|
|
900
|
+
vset(req, "name", vstr(name));
|
|
901
|
+
Value *ranked = resolvecapability(req, cands);
|
|
902
|
+
Value *out = vlist();
|
|
903
|
+
for (size_t i = 0; i < vlen(ranked); i++) vpush(out, vget(vat(ranked, i), "ref"));
|
|
904
|
+
return out;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/* ------------------------------------------------------------------ */
|
|
908
|
+
/* the state machine */
|
|
909
|
+
/* ------------------------------------------------------------------ */
|
|
910
|
+
|
|
911
|
+
static void reconcile(Host *h);
|
|
912
|
+
|
|
913
|
+
const char *host_autotag(Host *h, const char *name) {
|
|
914
|
+
/* AUTO-TAGGING IS EXPLICIT (§4 rule 3): the LOWEST UNUSED POSITIVE
|
|
915
|
+
* INTEGER tag. It needs a host because it must know what is already
|
|
916
|
+
* declared, which is why it cannot live in the pure `ref` section. */
|
|
917
|
+
for (int n = 1;; n++) {
|
|
918
|
+
char tag[32];
|
|
919
|
+
snprintf(tag, sizeof(tag), "%d", n);
|
|
920
|
+
const char *cand = formatref(vstr(name), vstr(tag));
|
|
921
|
+
if (NULL == findinst(h, cand)) return cand;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
Inst *host_declare(Host *h, const char *ref, DeclareSpec *spec) {
|
|
926
|
+
const char *r;
|
|
927
|
+
if (NULL != spec && NULL != spec->tag && 0 == strcmp(spec->tag, "?")) {
|
|
928
|
+
r = host_autotag(h, refname(canonref(vstr(ref))));
|
|
929
|
+
}
|
|
930
|
+
else {
|
|
931
|
+
r = canonref(vstr(ref));
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
if (NULL == spec || !spec->hostowned) checkreserved(h, r);
|
|
935
|
+
|
|
936
|
+
const char *defname = (NULL != spec && NULL != spec->definition)
|
|
937
|
+
? spec->definition : refname(r);
|
|
938
|
+
Definition *def = catalog_get(h->catalog, defname);
|
|
939
|
+
if (NULL == def) {
|
|
940
|
+
size_t sz = strlen(defname) + 32;
|
|
941
|
+
char *text = (char *)arena_alloc(sz);
|
|
942
|
+
snprintf(text, sz, "not in catalog: %s", defname);
|
|
943
|
+
fail("plugin_unknown_definition", text, details1("name", vstr(defname)));
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
Inst *existing = findinst(h, r);
|
|
947
|
+
if (NULL != existing) {
|
|
948
|
+
/* §4 rule 1: a pair addresses at most one instance. Re-declaring the
|
|
949
|
+
* SAME definition is the idempotent case; a different one is a
|
|
950
|
+
* duplicate, not a silent overwrite (seneca) and not an
|
|
951
|
+
* impossibility (sdkgen). */
|
|
952
|
+
if (0 != strcmp(existing->def->name, def->name)) {
|
|
953
|
+
size_t sz = strlen(r) + 40;
|
|
954
|
+
char *text = (char *)arena_alloc(sz);
|
|
955
|
+
snprintf(text, sz, "instance already declared: %s", r);
|
|
956
|
+
fail("plugin_ref_duplicate", text, details1("ref", vstr(r)));
|
|
957
|
+
}
|
|
958
|
+
return existing;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
Inst *e = (Inst *)arena_alloc(sizeof(Inst));
|
|
962
|
+
e->ref = arena_strdup(r);
|
|
963
|
+
e->def = def;
|
|
964
|
+
e->status = "declared";
|
|
965
|
+
e->pos = (NULL != spec && spec->haspos) ? spec->pos : (double)instcount(h);
|
|
966
|
+
e->seq = h->seqn++;
|
|
967
|
+
/* NO OPTIONS ADOPTED HERE. `apply` resolves options and hands the map
|
|
968
|
+
* over; adopting the caller's map made target and source THE SAME MAP
|
|
969
|
+
* in the refill that follows, which cleared its own source and left a
|
|
970
|
+
* first-time instance with no options at all. */
|
|
971
|
+
e->options = (NULL != spec && vismap(spec->options)) ? spec->options : vmap();
|
|
972
|
+
e->state = vmap();
|
|
973
|
+
e->order = (NULL != spec) ? spec->order : NULL;
|
|
974
|
+
e->selected = vmap();
|
|
975
|
+
e->barred = false;
|
|
976
|
+
e->unmet = vlist();
|
|
977
|
+
e->scope = NULL;
|
|
978
|
+
e->nscope = 0;
|
|
979
|
+
e->capscope = 0;
|
|
980
|
+
e->bindings = NULL;
|
|
981
|
+
e->nbindings = 0;
|
|
982
|
+
e->capbindings = 0;
|
|
983
|
+
e->inner = NULL;
|
|
984
|
+
e->exports = vmap();
|
|
985
|
+
e->provides = vlist();
|
|
986
|
+
e->owner = h;
|
|
987
|
+
e->next = NULL;
|
|
988
|
+
|
|
989
|
+
Inst **tail = &h->first;
|
|
990
|
+
while (NULL != *tail) tail = &(*tail)->next;
|
|
991
|
+
*tail = e;
|
|
992
|
+
return e;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
Inst *host_load(Host *h, const char *ref, DeclareSpec *spec) {
|
|
996
|
+
guard(h);
|
|
997
|
+
Inst *e = host_declare(h, ref, spec);
|
|
998
|
+
if (0 != strcmp(e->status, "declared")) return e; /* idempotent */
|
|
999
|
+
/* PRESENT AND NOT NULL, not merely present. Every driver builds its
|
|
1000
|
+
* command spec with all four keys and a null for each absent one, so
|
|
1001
|
+
* a presence test reads an omitted `options` as an authored empty and
|
|
1002
|
+
* wipes the real ones. */
|
|
1003
|
+
if (NULL != spec && vismap(spec->options)) e->options = spec->options;
|
|
1004
|
+
|
|
1005
|
+
CatchFrame f;
|
|
1006
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1007
|
+
run(h, e, "define");
|
|
1008
|
+
PLUGIN_END(&f);
|
|
1009
|
+
}
|
|
1010
|
+
else {
|
|
1011
|
+
e->status = "failed";
|
|
1012
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1013
|
+
}
|
|
1014
|
+
e->status = "loaded";
|
|
1015
|
+
|
|
1016
|
+
/* AT LOAD, and before anything runs: a cycle through restart-causing
|
|
1017
|
+
* requirements does not settle, and the only safe time to report a
|
|
1018
|
+
* non-terminating reconcile is before it starts (§11.3). `provides`
|
|
1019
|
+
* is populated by `define`, which has just run, so this is the first
|
|
1020
|
+
* moment the graph is complete. */
|
|
1021
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1022
|
+
checkcycle(graphnodes(h));
|
|
1023
|
+
PLUGIN_END(&f);
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
e->status = "failed";
|
|
1027
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1028
|
+
}
|
|
1029
|
+
return e;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
/* CONSUMERS GO DOWN FIRST, NOT AFTERWARDS (§11.3).
|
|
1033
|
+
*
|
|
1034
|
+
* The cascade is part of the provider's own deactivation and runs
|
|
1035
|
+
* BEFORE the provider's `deactivate` callback and scope unwind, so a
|
|
1036
|
+
* consumer's teardown can still call the thing it depends on — flushing
|
|
1037
|
+
* a buffer to the store it is about to lose is exactly what a
|
|
1038
|
+
* `deactivate` callback is for. */
|
|
1039
|
+
static void cascade(Host *h, Inst *provider, Value *seen) {
|
|
1040
|
+
if (vhas(seen, provider->ref)) return;
|
|
1041
|
+
vset(seen, provider->ref, vbool(true));
|
|
1042
|
+
|
|
1043
|
+
Value *cons = consumersof(h, provider->ref);
|
|
1044
|
+
for (size_t i = 0; i < vlen(cons); i++) {
|
|
1045
|
+
Inst *c = findinst(h, vasstr(vat(cons, i)));
|
|
1046
|
+
if (NULL == c || 0 != strcmp(c->status, "live")) continue;
|
|
1047
|
+
cascade(h, c, seen); /* deepest-first */
|
|
1048
|
+
/* VOLATILE, and this is a correctness requirement rather than a
|
|
1049
|
+
* warning to silence: C guarantees only that `volatile` locals keep
|
|
1050
|
+
* their value across a `longjmp`, so a flag set before the jump and
|
|
1051
|
+
* read after it is indeterminate without this. Every local that
|
|
1052
|
+
* straddles a PLUGIN_TRY in this file is marked, and gcc's
|
|
1053
|
+
* -Wclobbered is what finds the ones a reader would miss. */
|
|
1054
|
+
volatile bool bad = false;
|
|
1055
|
+
CatchFrame f;
|
|
1056
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1057
|
+
run(h, c, "deactivate");
|
|
1058
|
+
PLUGIN_END(&f);
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
bad = true;
|
|
1062
|
+
}
|
|
1063
|
+
Value *errors = unwind(h, c);
|
|
1064
|
+
if (bad || 0 < vlen(errors)) {
|
|
1065
|
+
/* §5.2: ANY failure during a transition lands the instance in
|
|
1066
|
+
* `failed`, and a cascaded consumer is not an exception. Marking
|
|
1067
|
+
* it `pending` instead handed it straight back to `reconcile`,
|
|
1068
|
+
* which would activate it again the moment the provider returned
|
|
1069
|
+
* — the one thing `failed` exists to stop. */
|
|
1070
|
+
c->status = "failed";
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
c->status = "pending";
|
|
1074
|
+
c->unmet = unmetof(h, c);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
Inst *host_activate(Host *h, const char *ref) {
|
|
1079
|
+
guard(h);
|
|
1080
|
+
Inst *e = need(h, ref);
|
|
1081
|
+
if (0 == strcmp(e->status, "live")) return e; /* no-op returning success */
|
|
1082
|
+
if (0 == strcmp(e->status, "failed")) {
|
|
1083
|
+
size_t sz = strlen(e->ref) + 32;
|
|
1084
|
+
char *text = (char *)arena_alloc(sz);
|
|
1085
|
+
snprintf(text, sz, "instance has failed: %s", e->ref);
|
|
1086
|
+
fail("plugin_bad_state", text, details1("ref", vstr(e->ref)));
|
|
1087
|
+
}
|
|
1088
|
+
/* §9.6: `active: false` bars the instance from running, and the bar
|
|
1089
|
+
* is on the INSTANCE rather than on the apply that set it. `ready`
|
|
1090
|
+
* reaches this through `activate`, which is why one guard covers both
|
|
1091
|
+
* verbs the design names. */
|
|
1092
|
+
if (e->barred) {
|
|
1093
|
+
size_t sz = strlen(e->ref) + 48;
|
|
1094
|
+
char *text = (char *)arena_alloc(sz);
|
|
1095
|
+
snprintf(text, sz, "instance is barred by active: false: %s", e->ref);
|
|
1096
|
+
fail("plugin_inactive", text, details1("ref", vstr(e->ref)));
|
|
1097
|
+
}
|
|
1098
|
+
if (0 == strcmp(e->status, "declared")) host_load(h, e->ref, NULL);
|
|
1099
|
+
|
|
1100
|
+
/* A declared requirement that is not live means `pending`: activation
|
|
1101
|
+
* is a STANDING REQUEST, not a one-shot event. */
|
|
1102
|
+
Value *unmet = unmetof(h, e);
|
|
1103
|
+
if (0 < vlen(unmet)) {
|
|
1104
|
+
e->unmet = unmet;
|
|
1105
|
+
e->status = "pending";
|
|
1106
|
+
return e;
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
CatchFrame f;
|
|
1110
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1111
|
+
run(h, e, "activate");
|
|
1112
|
+
PLUGIN_END(&f);
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
/* Unwind whatever the partial activation captured, in reverse. */
|
|
1116
|
+
unwind(h, e);
|
|
1117
|
+
e->status = "failed";
|
|
1118
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/* §11.4: THE SELECTION IS MADE HERE, once, and remembered. Every
|
|
1122
|
+
* later question — the cascade, `hold`, `unmet` — reads it back
|
|
1123
|
+
* rather than re-ranking, which is what "always-reluctant" means. */
|
|
1124
|
+
Value *reqs = requirements(e->options);
|
|
1125
|
+
for (size_t i = 0; i < vlen(reqs); i++) chosen(h, e, vat(reqs, i), true);
|
|
1126
|
+
e->status = "live";
|
|
1127
|
+
reconcile(h);
|
|
1128
|
+
return e;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
Inst *host_deactivate(Host *h, const char *ref) {
|
|
1132
|
+
guard(h);
|
|
1133
|
+
Inst *e = need(h, ref);
|
|
1134
|
+
if (0 == strcmp(e->status, "loaded") || 0 == strcmp(e->status, "declared")) return e;
|
|
1135
|
+
|
|
1136
|
+
/* §5.2: `unload` is THE ONLY TRANSITION OUT OF `failed`. Falling
|
|
1137
|
+
* through here ran the definition's `deactivate` on an instance that
|
|
1138
|
+
* never completed activation and, if that callback happened to
|
|
1139
|
+
* succeed, returned it to `loaded` — from where it could be activated
|
|
1140
|
+
* again, which is precisely what `failed` exists to prevent. */
|
|
1141
|
+
if (0 == strcmp(e->status, "failed")) {
|
|
1142
|
+
size_t sz = strlen(e->ref) + 32;
|
|
1143
|
+
char *text = (char *)arena_alloc(sz);
|
|
1144
|
+
snprintf(text, sz, "instance has failed: %s", e->ref);
|
|
1145
|
+
fail("plugin_bad_state", text, details1("ref", vstr(e->ref)));
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
if (0 == strcmp(e->status, "pending")) {
|
|
1149
|
+
/* DEACTIVATING A PENDING INSTANCE RUNS NO CALLBACK (§5.2). It never
|
|
1150
|
+
* reached activate, so it holds no scope and no live bindings;
|
|
1151
|
+
* running the definition's deactivate there would be teardown
|
|
1152
|
+
* without matching setup. It cannot fail. */
|
|
1153
|
+
e->status = "loaded";
|
|
1154
|
+
e->unmet = vlist();
|
|
1155
|
+
return e;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
held(h, e);
|
|
1159
|
+
cascade(h, e, vmap());
|
|
1160
|
+
|
|
1161
|
+
CatchFrame f;
|
|
1162
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1163
|
+
run(h, e, "deactivate");
|
|
1164
|
+
PLUGIN_END(&f);
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
unwind(h, e);
|
|
1168
|
+
e->status = "failed";
|
|
1169
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1170
|
+
}
|
|
1171
|
+
releasecheck(e, unwind(h, e));
|
|
1172
|
+
e->status = "loaded";
|
|
1173
|
+
reconcile(h);
|
|
1174
|
+
return e;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
void host_unload(Host *h, const char *ref) {
|
|
1178
|
+
guard(h);
|
|
1179
|
+
Inst *e = need(h, ref);
|
|
1180
|
+
if (0 == strcmp(e->status, "live") || 0 == strcmp(e->status, "pending")) {
|
|
1181
|
+
if (0 == strcmp(e->status, "live")) {
|
|
1182
|
+
held(h, e);
|
|
1183
|
+
cascade(h, e, vmap());
|
|
1184
|
+
CatchFrame f;
|
|
1185
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1186
|
+
run(h, e, "deactivate");
|
|
1187
|
+
PLUGIN_END(&f);
|
|
1188
|
+
}
|
|
1189
|
+
else {
|
|
1190
|
+
/* §5.2: ANY failure during a transition lands the instance in
|
|
1191
|
+
* `failed`, with the scope STILL FULLY UNWOUND. An earlier
|
|
1192
|
+
* draft let the raise propagate straight out of `unload`, which
|
|
1193
|
+
* left the instance `live` and its scope untouched — reporting
|
|
1194
|
+
* a failure while leaking exactly the resources the failure was
|
|
1195
|
+
* about. */
|
|
1196
|
+
unwind(h, e);
|
|
1197
|
+
e->status = "failed";
|
|
1198
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1199
|
+
}
|
|
1200
|
+
releasecheck(e, unwind(h, e));
|
|
1201
|
+
}
|
|
1202
|
+
e->status = "loaded";
|
|
1203
|
+
}
|
|
1204
|
+
if (0 == strcmp(e->status, "loaded") || 0 == strcmp(e->status, "failed")) {
|
|
1205
|
+
CatchFrame f;
|
|
1206
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1207
|
+
run(h, e, "close");
|
|
1208
|
+
PLUGIN_END(&f);
|
|
1209
|
+
removeinst(h, e);
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
removeinst(h, e);
|
|
1213
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1214
|
+
}
|
|
1215
|
+
removeinst(h, e);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
Inst *host_ready(Host *h, const char *ref) {
|
|
1219
|
+
/* Runs the whole forward path in one call (§5.1). §15.2's verb list
|
|
1220
|
+
* omits this; §5.1 defines it and §15.3's `declare` row requires the
|
|
1221
|
+
* corpus to pin it, so the list was incomplete rather than excluding
|
|
1222
|
+
* it (DOCS.md §4.2). */
|
|
1223
|
+
guard(h);
|
|
1224
|
+
const char *r = canonref(vstr(ref));
|
|
1225
|
+
if (NULL == findinst(h, r)) host_declare(h, r, NULL);
|
|
1226
|
+
if (0 == strcmp(findinst(h, r)->status, "declared")) host_load(h, r, NULL);
|
|
1227
|
+
return host_activate(h, r);
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/* EAGER reconciliation: run to a fixed point rather than scheduling.
|
|
1231
|
+
*
|
|
1232
|
+
* Two directions, and both are the reason `pending` exists. Activation
|
|
1233
|
+
* is a STANDING REQUEST, not a one-shot event: a pending instance whose
|
|
1234
|
+
* requirement arrives activates without being asked again, and a LIVE
|
|
1235
|
+
* instance whose requirement is lost goes back to pending —
|
|
1236
|
+
* recursively, through its own consumers. */
|
|
1237
|
+
static void reconcile(Host *h) {
|
|
1238
|
+
bool moved = true;
|
|
1239
|
+
int rounds = 0;
|
|
1240
|
+
while (moved) {
|
|
1241
|
+
moved = false;
|
|
1242
|
+
if (1000 < ++rounds) break;
|
|
1243
|
+
|
|
1244
|
+
/* Losses first, so a cascade settles in one pass rather than
|
|
1245
|
+
* alternating with re-activations. */
|
|
1246
|
+
const char **refs;
|
|
1247
|
+
volatile size_t n = sortedrefs(h, &refs);
|
|
1248
|
+
for (volatile size_t i = 0; i < n; i++) {
|
|
1249
|
+
Inst *e = findinst(h, refs[i]);
|
|
1250
|
+
if (NULL == e || 0 != strcmp(e->status, "live")) continue;
|
|
1251
|
+
Value *reqs = requirements(e->options);
|
|
1252
|
+
Value *lost = vlist();
|
|
1253
|
+
for (size_t j = 0; j < vlen(reqs); j++) {
|
|
1254
|
+
Value *q = vat(reqs, j);
|
|
1255
|
+
if (!gatesactivation(q)) continue;
|
|
1256
|
+
if (0 == vlen(providersof(h, q))) vpush(lost, q);
|
|
1257
|
+
}
|
|
1258
|
+
if (0 == vlen(lost)) continue;
|
|
1259
|
+
/* POLICY IS PER REQUIREMENT, not per instance (§11.3): only the
|
|
1260
|
+
* definition that has the requirement knows what it can cope
|
|
1261
|
+
* with, and one instance may hold both a `static` and a `dynamic`
|
|
1262
|
+
* one. A `dynamic` requirement whose provider is gone leaves the
|
|
1263
|
+
* consumer LIVE and notified. */
|
|
1264
|
+
bool anyrestart = false;
|
|
1265
|
+
for (size_t j = 0; j < vlen(lost); j++) {
|
|
1266
|
+
if (restartsonloss(vat(lost, j))) { anyrestart = true; break; }
|
|
1267
|
+
}
|
|
1268
|
+
if (!anyrestart) continue;
|
|
1269
|
+
|
|
1270
|
+
volatile bool bad = false;
|
|
1271
|
+
CatchFrame f;
|
|
1272
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1273
|
+
run(h, e, "deactivate");
|
|
1274
|
+
PLUGIN_END(&f);
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
bad = true;
|
|
1278
|
+
}
|
|
1279
|
+
Value *errors = unwind(h, e);
|
|
1280
|
+
if (bad || 0 < vlen(errors)) {
|
|
1281
|
+
e->status = "failed";
|
|
1282
|
+
moved = true;
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
e->status = "pending";
|
|
1286
|
+
e->unmet = unmetof(h, e);
|
|
1287
|
+
moved = true;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
n = sortedrefs(h, &refs);
|
|
1291
|
+
for (volatile size_t i = 0; i < n; i++) {
|
|
1292
|
+
Inst *e = findinst(h, refs[i]);
|
|
1293
|
+
if (NULL == e || 0 != strcmp(e->status, "pending")) continue;
|
|
1294
|
+
if (0 < vlen(unmetof(h, e))) continue;
|
|
1295
|
+
CatchFrame f;
|
|
1296
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1297
|
+
run(h, e, "activate");
|
|
1298
|
+
PLUGIN_END(&f);
|
|
1299
|
+
Value *reqs = requirements(e->options);
|
|
1300
|
+
for (size_t j = 0; j < vlen(reqs); j++) chosen(h, e, vat(reqs, j), true);
|
|
1301
|
+
e->status = "live";
|
|
1302
|
+
e->unmet = vlist();
|
|
1303
|
+
moved = true;
|
|
1304
|
+
}
|
|
1305
|
+
else {
|
|
1306
|
+
unwind(h, e);
|
|
1307
|
+
e->status = "failed";
|
|
1308
|
+
moved = true;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/* ------------------------------------------------------------------ */
|
|
1315
|
+
/* documents */
|
|
1316
|
+
/* ------------------------------------------------------------------ */
|
|
1317
|
+
|
|
1318
|
+
/* Empty the target and refill it, so callers holding the reference see
|
|
1319
|
+
* the new values. A definition's callbacks close over the options map
|
|
1320
|
+
* they were handed at `define`; replacing the reference would leave
|
|
1321
|
+
* every binding reading the values the first apply gave it. */
|
|
1322
|
+
static void refill(Value *target, Value *source) {
|
|
1323
|
+
const char **keys;
|
|
1324
|
+
size_t n = vkeys(target, &keys);
|
|
1325
|
+
for (size_t i = 0; i < n; i++) vdel(target, keys[i]);
|
|
1326
|
+
n = vkeys(source, &keys);
|
|
1327
|
+
for (size_t i = 0; i < n; i++) vset(target, keys[i], vget(source, keys[i]));
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
static Value *shapeof(Host *h, const char *ref) {
|
|
1331
|
+
Definition *d = catalog_get(h->catalog, refname(ref));
|
|
1332
|
+
return NULL == d ? NULL : d->shape;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
void host_apply(Host *h, Value *doc, Value *profile) {
|
|
1336
|
+
guard(h);
|
|
1337
|
+
Value *in = vmap();
|
|
1338
|
+
vset(in, "doc", doc);
|
|
1339
|
+
vset(in, "profile", visnull(profile) ? h->profile : profile);
|
|
1340
|
+
vset(in, "keys", h->keys);
|
|
1341
|
+
vset(in, "reserved", h->reserved);
|
|
1342
|
+
Value *norm = normalizeconfig(in);
|
|
1343
|
+
|
|
1344
|
+
Value *want = vget(norm, "order");
|
|
1345
|
+
Value *optionsof = vmap();
|
|
1346
|
+
for (size_t i = 0; i < vlen(want); i++) {
|
|
1347
|
+
const char *ref = vasstr(vat(want, i));
|
|
1348
|
+
Value *oin = vmap();
|
|
1349
|
+
vset(oin, "ref", vstr(ref));
|
|
1350
|
+
vset(oin, "doc", doc);
|
|
1351
|
+
vset(oin, "profile", visnull(profile) ? h->profile : profile);
|
|
1352
|
+
vset(oin, "shape", shapeof(h, ref));
|
|
1353
|
+
if (vismap(h->defaults)) {
|
|
1354
|
+
vset(oin, "hostdefaults", vget(h->defaults, refname(ref)));
|
|
1355
|
+
}
|
|
1356
|
+
vset(optionsof, ref, resolveoptions(oin));
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/* --- phase 1: deactivations and unloads, in REVERSE load order --- */
|
|
1360
|
+
Value *instances = vget(norm, "instance");
|
|
1361
|
+
size_t total = instcount(h);
|
|
1362
|
+
const char **drop = (const char **)arena_alloc(sizeof(char *) * (total + 1));
|
|
1363
|
+
size_t ndrop = 0;
|
|
1364
|
+
for (Inst *e = h->first; NULL != e; e = e->next) {
|
|
1365
|
+
if (0 == strcmp(e->status, "declared")) continue;
|
|
1366
|
+
Value *ent = vget(instances, e->ref);
|
|
1367
|
+
bool wantlive = vismap(ent) && vtruthy(vget(ent, "active")) &&
|
|
1368
|
+
visstr(vget(ent, "start")) && 0 == strcmp(vasstr(vget(ent, "start")), "eager");
|
|
1369
|
+
if (!wantlive) drop[ndrop++] = e->ref;
|
|
1370
|
+
}
|
|
1371
|
+
/* Reverse load order: highest `pos` first, ref-descending for a tie,
|
|
1372
|
+
* so a consumer declared after its provider goes down first. */
|
|
1373
|
+
for (size_t i = 0; i + 1 < ndrop; i++) {
|
|
1374
|
+
for (size_t j = i + 1; j < ndrop; j++) {
|
|
1375
|
+
Inst *a = findinst(h, drop[i]);
|
|
1376
|
+
Inst *b = findinst(h, drop[j]);
|
|
1377
|
+
bool swap = (b->pos > a->pos) ||
|
|
1378
|
+
(b->pos == a->pos && 0 > strcmp(drop[j], drop[i]));
|
|
1379
|
+
if (swap) { const char *t = drop[i]; drop[i] = drop[j]; drop[j] = t; }
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
for (size_t i = 0; i < ndrop; i++) host_unload(h, drop[i]);
|
|
1383
|
+
|
|
1384
|
+
/* --- phase 2: declare and patch EVERYTHING, in load order -------- */
|
|
1385
|
+
for (size_t i = 0; i < vlen(want); i++) {
|
|
1386
|
+
const char *ref = vasstr(vat(want, i));
|
|
1387
|
+
Value *ent = vget(instances, ref);
|
|
1388
|
+
DeclareSpec spec;
|
|
1389
|
+
memset(&spec, 0, sizeof(spec));
|
|
1390
|
+
spec.order = vget(ent, "order");
|
|
1391
|
+
spec.haspos = true;
|
|
1392
|
+
spec.pos = vasnum(vget(ent, "pos"));
|
|
1393
|
+
host_declare(h, ref, &spec);
|
|
1394
|
+
Inst *e = findinst(h, ref);
|
|
1395
|
+
/* The bar is REASSERTED ON EVERY APPLY, in both directions — a
|
|
1396
|
+
* document that turns the instance back on clears it, which is the
|
|
1397
|
+
* whole point of a config switch. */
|
|
1398
|
+
e->barred = !vtruthy(vget(ent, "active"));
|
|
1399
|
+
refill(e->options, vget(optionsof, ref));
|
|
1400
|
+
e->order = vget(ent, "order");
|
|
1401
|
+
e->pos = vasnum(vget(ent, "pos"));
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
/* --- phase 3: loads, in load order ------------------------------- */
|
|
1405
|
+
for (size_t i = 0; i < vlen(want); i++) {
|
|
1406
|
+
const char *ref = vasstr(vat(want, i));
|
|
1407
|
+
Value *ent = vget(instances, ref);
|
|
1408
|
+
bool wantlive = vtruthy(vget(ent, "active")) &&
|
|
1409
|
+
visstr(vget(ent, "start")) && 0 == strcmp(vasstr(vget(ent, "start")), "eager");
|
|
1410
|
+
if (wantlive) host_load(h, ref, NULL);
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
/* --- phase 4: activations, in load order ------------------------- */
|
|
1414
|
+
for (size_t i = 0; i < vlen(want); i++) {
|
|
1415
|
+
const char *ref = vasstr(vat(want, i));
|
|
1416
|
+
Value *ent = vget(instances, ref);
|
|
1417
|
+
bool wantlive = vtruthy(vget(ent, "active")) &&
|
|
1418
|
+
visstr(vget(ent, "start")) && 0 == strcmp(vasstr(vget(ent, "start")), "eager");
|
|
1419
|
+
if (wantlive) host_activate(h, ref);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
void host_setoptions(Host *h, const char *ref, Value *patch) {
|
|
1424
|
+
guard(h);
|
|
1425
|
+
Inst *e = need(h, ref);
|
|
1426
|
+
Value *previous = vclone(e->options);
|
|
1427
|
+
Value *in = vmap();
|
|
1428
|
+
vset(in, "ref", vstr(e->ref));
|
|
1429
|
+
vset(in, "shape", shapeof(h, e->ref));
|
|
1430
|
+
vset(in, "doc", vmap());
|
|
1431
|
+
vset(in, "patch", plugin_merge(previous, patch));
|
|
1432
|
+
refill(e->options, resolveoptions(in));
|
|
1433
|
+
|
|
1434
|
+
if (0 != strcmp(e->status, "live")) return;
|
|
1435
|
+
if (NULL != e->def->reconfigure) {
|
|
1436
|
+
h->intransition = true;
|
|
1437
|
+
h->phase = "reconfigure";
|
|
1438
|
+
CatchFrame f;
|
|
1439
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1440
|
+
e->def->reconfigure(e, e->options, previous);
|
|
1441
|
+
PLUGIN_END(&f);
|
|
1442
|
+
h->intransition = false;
|
|
1443
|
+
h->phase = NULL;
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
h->intransition = false;
|
|
1447
|
+
h->phase = NULL;
|
|
1448
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1449
|
+
}
|
|
1450
|
+
/* Always correct and sometimes expensive; `reconfigure` exists to
|
|
1451
|
+
* make the common case cheap (§9.4). */
|
|
1452
|
+
host_deactivate(h, e->ref);
|
|
1453
|
+
host_activate(h, e->ref);
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
void host_close(Host *h) {
|
|
1457
|
+
/* A bulk teardown removing the holders too, so `hold` is suspended
|
|
1458
|
+
* for exactly those holders (§11.3) — while the consumers-first
|
|
1459
|
+
* cascade still runs, which is the half that matters. */
|
|
1460
|
+
h->coordinated = true;
|
|
1461
|
+
size_t total = instcount(h);
|
|
1462
|
+
const char **refs = (const char **)arena_alloc(sizeof(char *) * (total + 1));
|
|
1463
|
+
size_t n = 0;
|
|
1464
|
+
for (Inst *e = h->first; NULL != e; e = e->next) refs[n++] = e->ref;
|
|
1465
|
+
/* Reverse load order. */
|
|
1466
|
+
for (size_t i = 0; i + 1 < n; i++) {
|
|
1467
|
+
for (size_t j = i + 1; j < n; j++) {
|
|
1468
|
+
Inst *a = findinst(h, refs[i]);
|
|
1469
|
+
Inst *b = findinst(h, refs[j]);
|
|
1470
|
+
if (NULL == a || NULL == b) continue;
|
|
1471
|
+
bool swap = (b->pos > a->pos) ||
|
|
1472
|
+
(b->pos == a->pos && 0 > strcmp(refs[j], refs[i]));
|
|
1473
|
+
if (swap) { const char *t = refs[i]; refs[i] = refs[j]; refs[j] = t; }
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
/* A COORDINATED FLAG THAT SURVIVES A RAISE IS A DISABLED GUARD. The
|
|
1477
|
+
* canonical wraps the teardown in `try/finally`; here an unload that
|
|
1478
|
+
* raises would skip the reset and leave the host permanently
|
|
1479
|
+
* `coordinated`, so a caller that catches the error and carries on
|
|
1480
|
+
* under `dependency: "hold"` gets ad-hoc deactivation with the holder
|
|
1481
|
+
* check silently off. Catch, reset, re-raise is C's `finally`. */
|
|
1482
|
+
volatile size_t i = 0;
|
|
1483
|
+
CatchFrame f;
|
|
1484
|
+
if (0 == PLUGIN_TRY(&f)) {
|
|
1485
|
+
for (; i < n; i++) {
|
|
1486
|
+
if (NULL != findinst(h, refs[i])) host_unload(h, refs[i]);
|
|
1487
|
+
}
|
|
1488
|
+
PLUGIN_END(&f);
|
|
1489
|
+
h->coordinated = false;
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
h->coordinated = false;
|
|
1493
|
+
fail(plugin_caught()->code, plugin_caught()->text, plugin_caught()->details);
|
|
1494
|
+
}
|