@voxgig/sdkgen 4.8.2 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +1 -1
- package/dist/action/doctor.d.ts +1 -0
- package/dist/action/doctor.js +50 -2
- package/dist/action/doctor.js.map +1 -1
- package/dist/helpers/naming.js +4 -2
- package/dist/helpers/naming.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/model/sdkgen.aon +21 -0
- package/package.json +1 -1
- package/project/.sdk/model/feature/secrets.aon +120 -18
- package/project/.sdk/model/target/c.aon +10 -0
- package/project/.sdk/model/target/clojure.aon +13 -0
- package/project/.sdk/model/target/cpp.aon +10 -0
- package/project/.sdk/model/target/csharp.aon +9 -0
- package/project/.sdk/model/target/dart.aon +10 -0
- package/project/.sdk/model/target/elixir.aon +9 -0
- package/project/.sdk/model/target/go.aon +13 -0
- package/project/.sdk/model/target/java.aon +9 -0
- package/project/.sdk/model/target/js.aon +7 -0
- package/project/.sdk/model/target/kotlin.aon +9 -0
- package/project/.sdk/model/target/lean.aon +17 -0
- package/project/.sdk/model/target/lua.aon +8 -0
- package/project/.sdk/model/target/ocaml.aon +9 -0
- package/project/.sdk/model/target/perl.aon +8 -0
- package/project/.sdk/model/target/php.aon +8 -0
- package/project/.sdk/model/target/py.aon +13 -0
- package/project/.sdk/model/target/rb.aon +8 -0
- package/project/.sdk/model/target/rust.aon +10 -0
- package/project/.sdk/model/target/scala.aon +21 -2
- package/project/.sdk/model/target/swift.aon +18 -0
- package/project/.sdk/model/target/ts.aon +13 -0
- package/project/.sdk/model/target/zig.aon +9 -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/src/cmp/zig/Package_zig.ts +15 -11
- package/project/.sdk/src/cmp/zig/fragment/Entity.fragment.zig +3 -3
- package/project/.sdk/src/cmp/zig/fragment/EntityListOp.fragment.zig +3 -3
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +3 -3
- package/project/.sdk/src/cmp/zig/utility_zig.ts +50 -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 +208 -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 +150 -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 +442 -0
- package/project/.sdk/tm/go/feature/secrets/sekreto/sekreto.go +714 -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 +902 -0
- package/project/.sdk/tm/go/test/omni/util.go +381 -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 +628 -0
- package/project/.sdk/tm/js/test/vendor/omni/util.js +229 -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 +76 -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 +151 -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 +90 -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 +475 -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 +580 -0
- package/project/.sdk/tm/py/test/voxgig_omni/util.py +252 -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/Makefile +15 -1
- package/project/.sdk/tm/zig/build.zig +23 -5
- package/project/.sdk/tm/zig/core/helpers.zig +52 -11
- package/project/.sdk/tm/zig/core/mem.zig +17 -1
- package/project/.sdk/tm/zig/core/utility.zig +23 -19
- package/project/.sdk/tm/zig/feature/audit.zig +2 -2
- package/project/.sdk/tm/zig/feature/cache.zig +2 -2
- package/project/.sdk/tm/zig/feature/debug.zig +5 -5
- package/project/.sdk/tm/zig/feature/netsim.zig +2 -2
- package/project/.sdk/tm/zig/feature/paging.zig +2 -2
- package/project/.sdk/tm/zig/feature/proxy.zig +15 -8
- package/project/.sdk/tm/zig/feature/streaming.zig +5 -5
- package/project/.sdk/tm/zig/feature/support.zig +3 -3
- package/project/.sdk/tm/zig/feature/telemetry.zig +5 -5
- package/project/.sdk/tm/zig/feature/test.zig +10 -5
- package/project/.sdk/tm/zig/test/fh.zig +10 -10
- package/project/.sdk/tm/zig/test/gotcha_test.zig +5 -5
- package/project/.sdk/tm/zig/test/omniresolver.zig +515 -0
- package/project/.sdk/tm/zig/test/omnismoke_test.zig +284 -0
- package/project/.sdk/tm/zig/test/pipeline_test.zig +8 -8
- package/project/.sdk/tm/zig/test/primary_utility_test.zig +145 -155
- package/project/.sdk/tm/zig/test/struct_corpus.zig +264 -187
- package/project/.sdk/tm/zig/test/vendor/omni/omni.zig +1314 -0
- package/project/.sdk/tm/zig/test/vendor/omni/regex.zig +446 -0
- package/project/.sdk/tm/zig/utility/voxgigstruct/regex.zig +7 -4
- package/project/.sdk/tm/zig/utility/voxgigstruct/struct.zig +356 -157
- 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
- package/project/.sdk/tm/zig/test/struct_runner.zig +0 -464
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
# VENDORED: @voxgig/struct 0.1.1 (python/voxgig_struct/voxgig_struct.py)
|
|
2
|
+
# Source: https://github.com/voxgig/struct @ adda9521c828147573df5a8878408a3f2193ddf4 [tag: sdk-20260907-0029-0]
|
|
3
|
+
# License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
|
|
1
4
|
# Copyright (c) 2025 Voxgig Ltd. MIT LICENSE.
|
|
2
5
|
#
|
|
3
6
|
# Voxgig Struct
|
|
@@ -34,23 +37,23 @@
|
|
|
34
37
|
# - joinurl: join parts of a url, merging forward slashes.
|
|
35
38
|
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
from datetime import datetime
|
|
39
|
-
import urllib.parse
|
|
40
|
+
import inspect
|
|
40
41
|
import json
|
|
41
|
-
import re
|
|
42
42
|
import math
|
|
43
|
-
import
|
|
43
|
+
import re
|
|
44
|
+
import urllib.parse
|
|
45
|
+
from datetime import datetime
|
|
46
|
+
from typing import Any
|
|
44
47
|
|
|
45
48
|
# Regex patterns for path processing
|
|
46
49
|
R_META_PATH = re.compile(r'^([^$]+)\$([=~])(.+)$') # Meta path syntax.
|
|
47
|
-
R_DOUBLE_DOLLAR = re.compile(r'\$\$')
|
|
50
|
+
R_DOUBLE_DOLLAR = re.compile(r'\$\$') # Double dollar escape sequence.
|
|
48
51
|
|
|
49
52
|
# Mode value for inject step.
|
|
50
|
-
S_MKEYPRE =
|
|
51
|
-
S_MKEYPOST =
|
|
52
|
-
S_MVAL =
|
|
53
|
-
S_MKEY =
|
|
53
|
+
S_MKEYPRE = 'key:pre'
|
|
54
|
+
S_MKEYPOST = 'key:post'
|
|
55
|
+
S_MVAL = 'val'
|
|
56
|
+
S_MKEY = 'key'
|
|
54
57
|
|
|
55
58
|
M_KEYPRE = 1
|
|
56
59
|
M_KEYPOST = 2
|
|
@@ -60,18 +63,18 @@ _PLACEMENT = {M_VAL: 'value', M_KEYPRE: S_MKEY, M_KEYPOST: S_MKEY}
|
|
|
60
63
|
MODENAME = {M_VAL: 'val', M_KEYPRE: 'key:pre', M_KEYPOST: 'key:post'}
|
|
61
64
|
|
|
62
65
|
# Special keys.
|
|
63
|
-
S_DKEY =
|
|
64
|
-
S_BANNO =
|
|
65
|
-
S_DTOP =
|
|
66
|
-
S_DERRS =
|
|
67
|
-
S_DSPEC =
|
|
68
|
-
S_BMETA =
|
|
69
|
-
S_BEXACT =
|
|
66
|
+
S_DKEY = '$KEY'
|
|
67
|
+
S_BANNO = '`$ANNO`'
|
|
68
|
+
S_DTOP = '$TOP'
|
|
69
|
+
S_DERRS = '$ERRS'
|
|
70
|
+
S_DSPEC = '$SPEC'
|
|
71
|
+
S_BMETA = 'meta'
|
|
72
|
+
S_BEXACT = '`$EXACT`'
|
|
70
73
|
S_BVAL = '`$VAL`'
|
|
71
74
|
S_BKEY = '`$KEY`'
|
|
72
75
|
|
|
73
76
|
# General strings.
|
|
74
|
-
S_array =
|
|
77
|
+
S_array = 'array'
|
|
75
78
|
S_integer = 'integer'
|
|
76
79
|
S_decimal = 'decimal'
|
|
77
80
|
S_map = 'map'
|
|
@@ -81,23 +84,23 @@ S_instance = 'instance'
|
|
|
81
84
|
S_node = 'node'
|
|
82
85
|
S_scalar = 'scalar'
|
|
83
86
|
S_any = 'any'
|
|
84
|
-
S_base =
|
|
85
|
-
S_boolean =
|
|
86
|
-
S_function =
|
|
87
|
-
S_number =
|
|
88
|
-
S_object =
|
|
89
|
-
S_string =
|
|
90
|
-
S_null =
|
|
91
|
-
S_key =
|
|
92
|
-
S_parent =
|
|
93
|
-
S_MT =
|
|
94
|
-
S_BT =
|
|
95
|
-
S_DS =
|
|
96
|
-
S_DT =
|
|
97
|
-
S_CM =
|
|
98
|
-
S_CN =
|
|
99
|
-
S_FS =
|
|
100
|
-
S_KEY =
|
|
87
|
+
S_base = 'base'
|
|
88
|
+
S_boolean = 'boolean'
|
|
89
|
+
S_function = 'function'
|
|
90
|
+
S_number = 'number'
|
|
91
|
+
S_object = 'object'
|
|
92
|
+
S_string = 'string'
|
|
93
|
+
S_null = 'null'
|
|
94
|
+
S_key = 'key'
|
|
95
|
+
S_parent = 'parent'
|
|
96
|
+
S_MT = ''
|
|
97
|
+
S_BT = '`'
|
|
98
|
+
S_DS = '$'
|
|
99
|
+
S_DT = '.'
|
|
100
|
+
S_CM = ','
|
|
101
|
+
S_CN = ':'
|
|
102
|
+
S_FS = '/'
|
|
103
|
+
S_KEY = 'KEY'
|
|
101
104
|
|
|
102
105
|
|
|
103
106
|
# Type bit flags (mirroring TypeScript)
|
|
@@ -131,12 +134,20 @@ TYPENAME = [
|
|
|
131
134
|
S_function,
|
|
132
135
|
'symbol',
|
|
133
136
|
S_null,
|
|
134
|
-
'',
|
|
135
|
-
'',
|
|
137
|
+
'',
|
|
138
|
+
'',
|
|
139
|
+
'',
|
|
140
|
+
'',
|
|
141
|
+
'',
|
|
142
|
+
'',
|
|
143
|
+
'',
|
|
136
144
|
S_list,
|
|
137
145
|
S_map,
|
|
138
146
|
S_instance,
|
|
139
|
-
'',
|
|
147
|
+
'',
|
|
148
|
+
'',
|
|
149
|
+
'',
|
|
150
|
+
'',
|
|
140
151
|
S_scalar,
|
|
141
152
|
S_node,
|
|
142
153
|
]
|
|
@@ -144,32 +155,48 @@ TYPENAME = [
|
|
|
144
155
|
S_VIZ = ': '
|
|
145
156
|
|
|
146
157
|
# The standard undefined value for this language.
|
|
147
|
-
UNDEF = None
|
|
158
|
+
UNDEF: Any = None
|
|
148
159
|
SKIP = {'`$SKIP`': True}
|
|
149
160
|
DELETE = {'`$DELETE`': True}
|
|
150
161
|
|
|
151
162
|
|
|
163
|
+
# Python collapses the canonical "undefined" (TS `undefined`) and JSON `null`
|
|
164
|
+
# both onto None, which loses a distinction the corpus depends on (e.g.
|
|
165
|
+
# stringify(null) is "null" but stringify(undefined) is ""). _ABSENT is a
|
|
166
|
+
# private sentinel used ONLY as a default argument value so a function can tell
|
|
167
|
+
# "no argument supplied" (undefined) apart from an explicit None (JSON null).
|
|
168
|
+
class _Absent:
|
|
169
|
+
__slots__ = ()
|
|
170
|
+
|
|
171
|
+
def __repr__(self):
|
|
172
|
+
return '<absent>'
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
_ABSENT = _Absent()
|
|
176
|
+
|
|
177
|
+
|
|
152
178
|
class Injection:
|
|
153
179
|
"""
|
|
154
180
|
Injection state used for recursive injection into JSON-like data structures.
|
|
155
181
|
"""
|
|
182
|
+
|
|
156
183
|
def __init__(
|
|
157
184
|
self,
|
|
158
|
-
mode: str,
|
|
159
|
-
full: bool,
|
|
160
|
-
keyI: int,
|
|
161
|
-
keys:
|
|
162
|
-
key: str,
|
|
163
|
-
val: Any,
|
|
164
|
-
parent: Any,
|
|
165
|
-
path:
|
|
166
|
-
nodes:
|
|
167
|
-
handler: Any,
|
|
168
|
-
errs:
|
|
169
|
-
meta:
|
|
170
|
-
base:
|
|
171
|
-
modify:
|
|
172
|
-
extra:
|
|
185
|
+
mode: str, # Injection mode: key:pre, val, key:post.
|
|
186
|
+
full: bool, # Transform escape was full key name.
|
|
187
|
+
keyI: int, # Index of parent key in list of parent keys.
|
|
188
|
+
keys: list[str], # List of parent keys.
|
|
189
|
+
key: str, # Current parent key.
|
|
190
|
+
val: Any, # Current child value.
|
|
191
|
+
parent: Any, # Current parent (in transform specification).
|
|
192
|
+
path: list[str], # Path to current node.
|
|
193
|
+
nodes: list[Any], # Stack of ancestor nodes
|
|
194
|
+
handler: Any, # Custom handler for injections.
|
|
195
|
+
errs: list[Any] = UNDEF, # Error collector.
|
|
196
|
+
meta: dict[str, Any] = UNDEF, # Custom meta data.
|
|
197
|
+
base: str | None = None, # Base key for data in store, if any.
|
|
198
|
+
modify: Any | None = None, # Modify injection output.
|
|
199
|
+
extra: Any | None = None, # Extra data for injection.
|
|
173
200
|
) -> None:
|
|
174
201
|
self.mode = mode
|
|
175
202
|
self.full = full
|
|
@@ -186,10 +213,12 @@ class Injection:
|
|
|
186
213
|
self.base = base
|
|
187
214
|
self.modify = modify
|
|
188
215
|
self.extra = extra
|
|
189
|
-
self.prior = None
|
|
216
|
+
self.prior: Injection | None = None
|
|
190
217
|
self.dparent = UNDEF
|
|
191
218
|
self.dpath = [S_DTOP]
|
|
192
|
-
self.root =
|
|
219
|
+
self.root = (
|
|
220
|
+
None # Virtual root parent; set at top level so we can return it after transforms
|
|
221
|
+
)
|
|
193
222
|
|
|
194
223
|
def descend(self):
|
|
195
224
|
if '__d' not in self.meta:
|
|
@@ -199,8 +228,8 @@ class Injection:
|
|
|
199
228
|
parentkey = getelem(self.path, -2)
|
|
200
229
|
|
|
201
230
|
if self.dparent is UNDEF:
|
|
202
|
-
if
|
|
203
|
-
self.dpath = self.dpath
|
|
231
|
+
if size(self.dpath) > 1:
|
|
232
|
+
self.dpath = [*self.dpath, parentkey]
|
|
204
233
|
else:
|
|
205
234
|
if parentkey is not None:
|
|
206
235
|
self.dparent = getprop(self.dparent, parentkey)
|
|
@@ -209,15 +238,15 @@ class Injection:
|
|
|
209
238
|
if lastpart == '$:' + str(parentkey):
|
|
210
239
|
self.dpath = slice(self.dpath, -1)
|
|
211
240
|
else:
|
|
212
|
-
self.dpath = self.dpath
|
|
241
|
+
self.dpath = [*self.dpath, parentkey]
|
|
213
242
|
|
|
214
243
|
return self.dparent
|
|
215
244
|
|
|
216
|
-
def child(self, keyI: int, keys:
|
|
245
|
+
def child(self, keyI: int, keys: list[str]) -> 'Injection':
|
|
217
246
|
"""Create a child state object with the given key index and keys."""
|
|
218
247
|
key = strkey(keys[keyI])
|
|
219
248
|
val = self.val
|
|
220
|
-
|
|
249
|
+
|
|
221
250
|
cinj = Injection(
|
|
222
251
|
mode=self.mode,
|
|
223
252
|
full=self.full,
|
|
@@ -226,28 +255,40 @@ class Injection:
|
|
|
226
255
|
key=key,
|
|
227
256
|
val=getprop(val, key),
|
|
228
257
|
parent=val,
|
|
229
|
-
path=self.path
|
|
230
|
-
nodes=self.nodes
|
|
258
|
+
path=[*self.path, key],
|
|
259
|
+
nodes=[*self.nodes, val],
|
|
231
260
|
handler=self.handler,
|
|
232
261
|
errs=self.errs,
|
|
233
262
|
meta=self.meta,
|
|
234
263
|
base=self.base,
|
|
235
|
-
modify=self.modify
|
|
264
|
+
modify=self.modify,
|
|
236
265
|
)
|
|
237
266
|
cinj.prior = self
|
|
238
267
|
cinj.dpath = self.dpath[:]
|
|
239
268
|
cinj.dparent = self.dparent
|
|
240
269
|
cinj.extra = self.extra # Preserve extra (contains transform functions)
|
|
241
270
|
cinj.root = getattr(self, 'root', None)
|
|
242
|
-
|
|
271
|
+
|
|
243
272
|
return cinj
|
|
244
273
|
|
|
245
|
-
def setval(self, val: Any, ancestor:
|
|
246
|
-
"""
|
|
274
|
+
def setval(self, val: Any, ancestor: int | None = None) -> Any:
|
|
275
|
+
"""Inject-state setter. Mirrors the canonical TS Injection.setval:
|
|
276
|
+
val of UNDEF (= None in Python — distinguishability is impossible
|
|
277
|
+
in any of the dynamic-typed ports anyway) deletes the slot, any
|
|
278
|
+
other value sets it. setprop itself remains a pure assignment per
|
|
279
|
+
the Group B rule — this delete-on-undef shortcut lives only here
|
|
280
|
+
so transform/validate injectors can signal "no value at this slot"
|
|
281
|
+
through their return value.
|
|
282
|
+
"""
|
|
247
283
|
if ancestor is None or ancestor < 2:
|
|
248
|
-
|
|
284
|
+
target = self.parent
|
|
285
|
+
key = self.key
|
|
249
286
|
else:
|
|
250
|
-
|
|
287
|
+
target = getelem(self.nodes, 0 - ancestor)
|
|
288
|
+
key = getelem(self.path, 0 - ancestor)
|
|
289
|
+
if val is None:
|
|
290
|
+
return delprop(target, key)
|
|
291
|
+
return setprop(target, key, val)
|
|
251
292
|
|
|
252
293
|
|
|
253
294
|
def getdef(val, alt):
|
|
@@ -281,9 +322,7 @@ def iskey(key: Any = UNDEF) -> bool:
|
|
|
281
322
|
return False
|
|
282
323
|
if isinstance(key, int):
|
|
283
324
|
return True
|
|
284
|
-
|
|
285
|
-
return True
|
|
286
|
-
return False
|
|
325
|
+
return isinstance(key, float)
|
|
287
326
|
|
|
288
327
|
|
|
289
328
|
def size(val: Any = UNDEF) -> int:
|
|
@@ -294,7 +333,7 @@ def size(val: Any = UNDEF) -> int:
|
|
|
294
333
|
return len(val)
|
|
295
334
|
elif ismap(val):
|
|
296
335
|
return len(val.keys())
|
|
297
|
-
|
|
336
|
+
|
|
298
337
|
if isinstance(val, str):
|
|
299
338
|
return len(val)
|
|
300
339
|
elif isinstance(val, (int, float)):
|
|
@@ -309,16 +348,17 @@ def size(val: Any = UNDEF) -> int:
|
|
|
309
348
|
|
|
310
349
|
def slice(val: Any, start: int = UNDEF, end: int = UNDEF, mutate: bool = False) -> Any:
|
|
311
350
|
"""Return a part of a list, string, or clamp a number"""
|
|
312
|
-
# Handle numbers - acts like clamp function
|
|
313
|
-
|
|
351
|
+
# Handle numbers - acts like clamp function.
|
|
352
|
+
# bool is a subclass of int in python, but `typeof true` is not
|
|
353
|
+
# "number" in the canonical TypeScript, so a boolean must fall through
|
|
354
|
+
# to the container path and be returned unchanged.
|
|
355
|
+
if isinstance(val, (int, float)) and not isinstance(val, bool):
|
|
314
356
|
if start is None:
|
|
315
357
|
start = float('-inf')
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
else:
|
|
319
|
-
end = end - 1 # TypeScript uses exclusive end, so subtract 1
|
|
358
|
+
# TypeScript uses exclusive end, so subtract 1 when bounded.
|
|
359
|
+
end = float('inf') if end is None else end - 1
|
|
320
360
|
return max(start, min(val, end))
|
|
321
|
-
|
|
361
|
+
|
|
322
362
|
if islist(val) or isinstance(val, str):
|
|
323
363
|
vlen = size(val)
|
|
324
364
|
if end is not None and start is None:
|
|
@@ -342,13 +382,13 @@ def slice(val: Any, start: int = UNDEF, end: int = UNDEF, mutate: bool = False)
|
|
|
342
382
|
if vlen < start:
|
|
343
383
|
start = vlen
|
|
344
384
|
|
|
345
|
-
if -1
|
|
385
|
+
if start > -1 and start <= end and end <= vlen:
|
|
346
386
|
if islist(val) and mutate:
|
|
347
387
|
j = start
|
|
348
388
|
for i in range(end - start):
|
|
349
389
|
val[i] = val[j]
|
|
350
390
|
j += 1
|
|
351
|
-
del val[end - start:]
|
|
391
|
+
del val[end - start :]
|
|
352
392
|
return val
|
|
353
393
|
return val[start:end]
|
|
354
394
|
else:
|
|
@@ -356,7 +396,7 @@ def slice(val: Any, start: int = UNDEF, end: int = UNDEF, mutate: bool = False)
|
|
|
356
396
|
if mutate:
|
|
357
397
|
del val[:]
|
|
358
398
|
return val if mutate else []
|
|
359
|
-
return
|
|
399
|
+
return ''
|
|
360
400
|
|
|
361
401
|
# No slice performed; return original value unchanged
|
|
362
402
|
return val
|
|
@@ -364,10 +404,13 @@ def slice(val: Any, start: int = UNDEF, end: int = UNDEF, mutate: bool = False)
|
|
|
364
404
|
|
|
365
405
|
def pad(s: Any, padding: int = UNDEF, padchar: str = UNDEF) -> str:
|
|
366
406
|
"""Pad a string to a specified length"""
|
|
367
|
-
|
|
407
|
+
# Mirror TS canonical: stringify(null) is "null", distinct from
|
|
408
|
+
# stringify(undefined) which is "". Python's UNDEF=None conflates the
|
|
409
|
+
# two; explicitly coerce JSON-null to the literal string "null" first.
|
|
410
|
+
s = 'null' if s is None else stringify(s)
|
|
368
411
|
padding = 44 if padding is UNDEF else padding
|
|
369
412
|
padchar = ' ' if padchar is UNDEF else (padchar + ' ')[0]
|
|
370
|
-
|
|
413
|
+
|
|
371
414
|
if padding > -1:
|
|
372
415
|
return s.ljust(padding, padchar)
|
|
373
416
|
else:
|
|
@@ -375,7 +418,7 @@ def pad(s: Any, padding: int = UNDEF, padchar: str = UNDEF) -> str:
|
|
|
375
418
|
|
|
376
419
|
|
|
377
420
|
def strkey(key: Any = UNDEF) -> str:
|
|
378
|
-
if
|
|
421
|
+
if key == UNDEF:
|
|
379
422
|
return S_MT
|
|
380
423
|
|
|
381
424
|
if isinstance(key, str):
|
|
@@ -395,19 +438,16 @@ def strkey(key: Any = UNDEF) -> str:
|
|
|
395
438
|
|
|
396
439
|
def isempty(val: Any = UNDEF) -> bool:
|
|
397
440
|
"Check for an 'empty' value - None, empty string, array, object."
|
|
398
|
-
if
|
|
441
|
+
if val == UNDEF:
|
|
399
442
|
return True
|
|
400
|
-
|
|
443
|
+
|
|
401
444
|
if val == S_MT:
|
|
402
445
|
return True
|
|
403
|
-
|
|
446
|
+
|
|
404
447
|
if islist(val) and len(val) == 0:
|
|
405
448
|
return True
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
return True
|
|
409
|
-
|
|
410
|
-
return False
|
|
449
|
+
|
|
450
|
+
return ismap(val) and len(val) == 0
|
|
411
451
|
|
|
412
452
|
|
|
413
453
|
def isfunc(val: Any = UNDEF) -> bool:
|
|
@@ -439,6 +479,7 @@ def typify(value: Any = _TYPIFY_NO_ARG) -> int:
|
|
|
439
479
|
return T_scalar | T_number | T_integer
|
|
440
480
|
if isinstance(value, float):
|
|
441
481
|
import math
|
|
482
|
+
|
|
442
483
|
if math.isnan(value):
|
|
443
484
|
return T_noval
|
|
444
485
|
return T_scalar | T_number | T_decimal
|
|
@@ -455,12 +496,12 @@ def typify(value: Any = _TYPIFY_NO_ARG) -> int:
|
|
|
455
496
|
|
|
456
497
|
def getelem(val: Any, key: Any, alt: Any = UNDEF) -> Any:
|
|
457
498
|
"""
|
|
458
|
-
Get a list element.
|
|
459
|
-
|
|
499
|
+
Get a list element. Group A semantics: a stored None at the slot
|
|
500
|
+
returns alt, just like an out-of-range index.
|
|
460
501
|
"""
|
|
461
502
|
out = UNDEF
|
|
462
503
|
|
|
463
|
-
if
|
|
504
|
+
if val is UNDEF or key is UNDEF:
|
|
464
505
|
return alt
|
|
465
506
|
|
|
466
507
|
if islist(val):
|
|
@@ -473,45 +514,65 @@ def getelem(val: Any, key: Any, alt: Any = UNDEF) -> Any:
|
|
|
473
514
|
except (ValueError, IndexError):
|
|
474
515
|
pass
|
|
475
516
|
|
|
476
|
-
if UNDEF
|
|
477
|
-
return alt() if
|
|
517
|
+
if out is UNDEF or out is None:
|
|
518
|
+
return alt() if (T_function & typify(alt)) > 0 else alt
|
|
478
519
|
|
|
479
520
|
return out
|
|
480
521
|
|
|
481
522
|
|
|
482
523
|
def getprop(val: Any = UNDEF, key: Any = UNDEF, alt: Any = UNDEF) -> Any:
|
|
483
524
|
"""
|
|
484
|
-
Safely get a property of a node.
|
|
485
|
-
|
|
525
|
+
Safely get a property of a node. Group A semantics (per design/UNDEF_SPEC.md):
|
|
526
|
+
a stored None is treated as "no value" and returns alt. Internal callers
|
|
527
|
+
that need to inspect the raw stored value at a slot (preserving null)
|
|
528
|
+
use _lookup.
|
|
486
529
|
"""
|
|
487
|
-
if UNDEF
|
|
488
|
-
return alt
|
|
489
|
-
|
|
490
|
-
if UNDEF == key:
|
|
530
|
+
if val is UNDEF or key is UNDEF:
|
|
491
531
|
return alt
|
|
492
532
|
|
|
493
533
|
out = alt
|
|
494
|
-
|
|
534
|
+
|
|
495
535
|
if ismap(val):
|
|
496
|
-
|
|
497
|
-
|
|
536
|
+
skey = str(key)
|
|
537
|
+
if skey in val:
|
|
538
|
+
out = val[skey]
|
|
539
|
+
|
|
498
540
|
elif islist(val):
|
|
499
541
|
try:
|
|
500
|
-
|
|
501
|
-
except:
|
|
542
|
+
ki = int(key)
|
|
543
|
+
except (ValueError, TypeError):
|
|
502
544
|
return alt
|
|
503
545
|
|
|
504
|
-
if 0 <=
|
|
505
|
-
|
|
506
|
-
else:
|
|
507
|
-
return alt
|
|
546
|
+
if 0 <= ki < len(val):
|
|
547
|
+
out = val[ki]
|
|
508
548
|
|
|
509
|
-
if
|
|
549
|
+
if out is None:
|
|
510
550
|
return alt
|
|
511
|
-
|
|
551
|
+
|
|
512
552
|
return out
|
|
513
553
|
|
|
514
554
|
|
|
555
|
+
def _lookup(val: Any, key: Any) -> Any:
|
|
556
|
+
"""Internal raw lookup: returns the literally-stored value (including
|
|
557
|
+
None for JSON null), or UNDEF when the slot doesn't exist. Used by
|
|
558
|
+
Group B callers that must distinguish a null slot from an absent one
|
|
559
|
+
while still using a unified test runner."""
|
|
560
|
+
if val is UNDEF or key is UNDEF:
|
|
561
|
+
return UNDEF
|
|
562
|
+
if ismap(val):
|
|
563
|
+
skey = str(key)
|
|
564
|
+
return val.get(skey, UNDEF)
|
|
565
|
+
if islist(val):
|
|
566
|
+
try:
|
|
567
|
+
ki = int(key)
|
|
568
|
+
except (ValueError, TypeError):
|
|
569
|
+
return UNDEF
|
|
570
|
+
if 0 <= ki < len(val):
|
|
571
|
+
return val[ki]
|
|
572
|
+
return UNDEF
|
|
573
|
+
return UNDEF
|
|
574
|
+
|
|
575
|
+
|
|
515
576
|
def keysof(val: Any = UNDEF) -> list[str]:
|
|
516
577
|
"Sorted keys of a map, or indexes of a list."
|
|
517
578
|
if not isnode(val):
|
|
@@ -523,10 +584,11 @@ def keysof(val: Any = UNDEF) -> list[str]:
|
|
|
523
584
|
|
|
524
585
|
|
|
525
586
|
def haskey(val: Any = UNDEF, key: Any = UNDEF) -> bool:
|
|
526
|
-
"Value of property with name key in node val is defined."
|
|
527
|
-
|
|
587
|
+
"Value of property with name key in node val is defined (and not None)."
|
|
588
|
+
out = getprop(val, key)
|
|
589
|
+
return out is not UNDEF and out is not None
|
|
590
|
+
|
|
528
591
|
|
|
529
|
-
|
|
530
592
|
def items(val: Any = UNDEF, apply=None):
|
|
531
593
|
"List the keys of a map or list as an array of [key, value] tuples."
|
|
532
594
|
if not isnode(val):
|
|
@@ -536,7 +598,7 @@ def items(val: Any = UNDEF, apply=None):
|
|
|
536
598
|
if apply is not None:
|
|
537
599
|
out = [apply(item) for item in out]
|
|
538
600
|
return out
|
|
539
|
-
|
|
601
|
+
|
|
540
602
|
|
|
541
603
|
def flatten(lst, depth=None):
|
|
542
604
|
if depth is None:
|
|
@@ -564,26 +626,104 @@ def filter(val, check):
|
|
|
564
626
|
|
|
565
627
|
def escre(s: Any):
|
|
566
628
|
"Escape regular expression."
|
|
567
|
-
if
|
|
568
|
-
s =
|
|
629
|
+
if s == UNDEF:
|
|
630
|
+
s = ''
|
|
569
631
|
pattern = r'([.*+?^${}()|\[\]\\])'
|
|
570
632
|
return re.sub(pattern, r'\\\1', s)
|
|
571
633
|
|
|
572
634
|
|
|
635
|
+
# ---------------------------------------------------------------------------
|
|
636
|
+
# Regex utility — uniform re_* API (see /design/REGEX_API.md). The Python port
|
|
637
|
+
# wraps the stdlib `re` module; the dialect is the RE2 subset.
|
|
638
|
+
# ---------------------------------------------------------------------------
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def re_compile(pattern, flags=0):
|
|
642
|
+
"Compile a regex (or return as-is if already a compiled pattern)."
|
|
643
|
+
if hasattr(pattern, 'pattern'):
|
|
644
|
+
return pattern
|
|
645
|
+
return re.compile(pattern, flags)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def re_find(pattern, input):
|
|
649
|
+
"First match. Returns [whole, capture1, ...] or None."
|
|
650
|
+
rx = pattern if hasattr(pattern, 'search') else re.compile(pattern)
|
|
651
|
+
m = rx.search(input)
|
|
652
|
+
if not m:
|
|
653
|
+
return None
|
|
654
|
+
return [m.group(0)] + [g if g is not None else '' for g in m.groups()]
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def re_find_all(pattern, input):
|
|
658
|
+
"All non-overlapping matches."
|
|
659
|
+
rx = pattern if hasattr(pattern, 'finditer') else re.compile(pattern)
|
|
660
|
+
out = []
|
|
661
|
+
for m in rx.finditer(input):
|
|
662
|
+
out.append([m.group(0)] + [g if g is not None else '' for g in m.groups()])
|
|
663
|
+
return out
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
def re_replace(pattern, input, replacement):
|
|
667
|
+
"Replace every match. `replacement` may be a string with $&/$1 or a callable."
|
|
668
|
+
rx = pattern if hasattr(pattern, 'sub') else re.compile(pattern)
|
|
669
|
+
if callable(replacement):
|
|
670
|
+
|
|
671
|
+
def _cb(m):
|
|
672
|
+
return replacement([m.group(0)] + [g if g is not None else '' for g in m.groups()])
|
|
673
|
+
|
|
674
|
+
return rx.sub(_cb, input)
|
|
675
|
+
# Translate $& and $1..$9 to Python's \g<0>/\1..\9
|
|
676
|
+
py_repl = []
|
|
677
|
+
i = 0
|
|
678
|
+
while i < len(replacement):
|
|
679
|
+
c = replacement[i]
|
|
680
|
+
if c == '$' and i + 1 < len(replacement):
|
|
681
|
+
nxt = replacement[i + 1]
|
|
682
|
+
if nxt == '&':
|
|
683
|
+
py_repl.append('\\g<0>')
|
|
684
|
+
i += 2
|
|
685
|
+
continue
|
|
686
|
+
if nxt.isdigit():
|
|
687
|
+
py_repl.append('\\' + nxt)
|
|
688
|
+
i += 2
|
|
689
|
+
continue
|
|
690
|
+
if nxt == '$':
|
|
691
|
+
py_repl.append('$')
|
|
692
|
+
i += 2
|
|
693
|
+
continue
|
|
694
|
+
if c == '\\':
|
|
695
|
+
py_repl.append('\\\\')
|
|
696
|
+
else:
|
|
697
|
+
py_repl.append(c)
|
|
698
|
+
i += 1
|
|
699
|
+
return rx.sub(''.join(py_repl), input)
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
def re_test(pattern, input):
|
|
703
|
+
"Boolean test."
|
|
704
|
+
rx = pattern if hasattr(pattern, 'search') else re.compile(pattern)
|
|
705
|
+
return rx.search(input) is not None
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def re_escape(s):
|
|
709
|
+
"Alias of escre."
|
|
710
|
+
return escre(s)
|
|
711
|
+
|
|
712
|
+
|
|
573
713
|
def escurl(s: Any):
|
|
574
714
|
"Escape URLs."
|
|
575
|
-
if
|
|
715
|
+
if s == UNDEF:
|
|
576
716
|
s = S_MT
|
|
577
|
-
return urllib.parse.quote(s, safe=
|
|
717
|
+
return urllib.parse.quote(s, safe='')
|
|
578
718
|
|
|
579
719
|
|
|
580
720
|
def replace(s, from_pat, to_str):
|
|
581
721
|
"Replace a search string (all), or a regexp, in a source string."
|
|
582
722
|
rs = s
|
|
583
723
|
ts = typify(s)
|
|
584
|
-
if
|
|
724
|
+
if (T_string & ts) == 0:
|
|
585
725
|
rs = stringify(s)
|
|
586
|
-
elif
|
|
726
|
+
elif ((T_noval | T_null) & ts) > 0:
|
|
587
727
|
rs = S_MT
|
|
588
728
|
else:
|
|
589
729
|
rs = stringify(s)
|
|
@@ -597,27 +737,27 @@ def join(arr, sep=UNDEF, url=UNDEF):
|
|
|
597
737
|
if not islist(arr):
|
|
598
738
|
return S_MT
|
|
599
739
|
sepdef = S_CM if sep is UNDEF or sep is None else sep
|
|
600
|
-
sepre = escre(sepdef) if
|
|
740
|
+
sepre = escre(sepdef) if size(sepdef) == 1 else UNDEF
|
|
601
741
|
|
|
602
742
|
sarr = size(arr)
|
|
603
|
-
filtered = [(i, s) for i, s in enumerate(arr)
|
|
604
|
-
if isinstance(s, str) and S_MT != s]
|
|
743
|
+
filtered = [(i, s) for i, s in enumerate(arr) if isinstance(s, str) and s != S_MT]
|
|
605
744
|
|
|
606
745
|
result = []
|
|
607
746
|
for idx, s in filtered:
|
|
608
|
-
if sepre is not UNDEF and
|
|
609
|
-
if url and
|
|
747
|
+
if sepre is not UNDEF and sepre != S_MT:
|
|
748
|
+
if url and idx == 0:
|
|
610
749
|
s = re.sub(sepre + '+$', S_MT, s)
|
|
611
750
|
result.append(s)
|
|
612
751
|
continue
|
|
613
|
-
if
|
|
752
|
+
if idx > 0:
|
|
614
753
|
s = re.sub('^' + sepre + '+', S_MT, s)
|
|
615
754
|
if idx < sarr - 1 or not url:
|
|
616
755
|
s = re.sub(sepre + '+$', S_MT, s)
|
|
617
|
-
s = re.sub(
|
|
618
|
-
|
|
756
|
+
s = re.sub(
|
|
757
|
+
'([^' + sepre + '])' + sepre + '+([^' + sepre + '])', r'\1' + sepdef + r'\2', s
|
|
758
|
+
)
|
|
619
759
|
|
|
620
|
-
if
|
|
760
|
+
if s != S_MT:
|
|
621
761
|
result.append(s)
|
|
622
762
|
|
|
623
763
|
return sepdef.join(result)
|
|
@@ -659,43 +799,48 @@ def delprop(parent: Any, key: Any):
|
|
|
659
799
|
return parent
|
|
660
800
|
|
|
661
801
|
|
|
662
|
-
def jsonify(val: Any = UNDEF, flags:
|
|
802
|
+
def jsonify(val: Any = UNDEF, flags: dict[str, Any] = UNDEF) -> str:
|
|
663
803
|
"""
|
|
664
804
|
Convert a value to a formatted JSON string.
|
|
665
805
|
In general, the behavior of JavaScript's JSON.stringify(val, null, 2) is followed.
|
|
666
806
|
"""
|
|
667
807
|
flags = flags or {}
|
|
668
|
-
|
|
808
|
+
|
|
669
809
|
if val is UNDEF:
|
|
670
810
|
return S_null
|
|
671
|
-
|
|
811
|
+
|
|
672
812
|
indent = getprop(flags, 'indent', 2)
|
|
673
|
-
|
|
813
|
+
|
|
674
814
|
try:
|
|
675
|
-
|
|
815
|
+
if indent and indent > 0:
|
|
816
|
+
# JS-style pretty print: newlines, spaces, ": " between key/value.
|
|
817
|
+
json_str = json.dumps(val, indent=indent, separators=(',', ': '))
|
|
818
|
+
else:
|
|
819
|
+
# indent=0 = compact single-line, matching the C/Rust/Lua printers.
|
|
820
|
+
json_str = json.dumps(val, separators=(',', ':'))
|
|
676
821
|
except Exception:
|
|
677
822
|
return S_null
|
|
678
|
-
|
|
823
|
+
|
|
679
824
|
if json_str is None:
|
|
680
825
|
return S_null
|
|
681
|
-
|
|
826
|
+
|
|
682
827
|
offset = getprop(flags, 'offset', 0)
|
|
683
|
-
if
|
|
828
|
+
if offset > 0:
|
|
684
829
|
lines = json_str.split('\n')
|
|
685
830
|
padded = [pad(n[1], 0 - offset - size(n[1])) for n in items(lines[1:])]
|
|
686
831
|
json_str = '{\n' + '\n'.join(padded)
|
|
687
|
-
|
|
832
|
+
|
|
688
833
|
return json_str
|
|
689
834
|
|
|
690
835
|
|
|
691
|
-
def
|
|
836
|
+
def jm(*kv: Any) -> dict[str, Any]:
|
|
692
837
|
"""
|
|
693
838
|
Define a JSON Object using function arguments.
|
|
694
839
|
Arguments are treated as key-value pairs.
|
|
695
840
|
"""
|
|
696
841
|
kvsize = len(kv)
|
|
697
842
|
o = {}
|
|
698
|
-
|
|
843
|
+
|
|
699
844
|
for i in range(0, kvsize, 2):
|
|
700
845
|
k = kv[i] if i < kvsize else f'$KEY{i}'
|
|
701
846
|
# Handle None specially to become "null" for keys
|
|
@@ -706,30 +851,25 @@ def jo(*kv: Any) -> Dict[str, Any]:
|
|
|
706
851
|
else:
|
|
707
852
|
k = stringify(k)
|
|
708
853
|
o[k] = kv[i + 1] if i + 1 < kvsize else None
|
|
709
|
-
|
|
854
|
+
|
|
710
855
|
return o
|
|
711
856
|
|
|
712
857
|
|
|
713
|
-
def
|
|
858
|
+
def jt(*v: Any) -> list[Any]:
|
|
714
859
|
"""
|
|
715
860
|
Define a JSON Array using function arguments.
|
|
716
861
|
"""
|
|
717
862
|
vsize = len(v)
|
|
718
863
|
a = [None] * vsize
|
|
719
|
-
|
|
864
|
+
|
|
720
865
|
for i in range(vsize):
|
|
721
866
|
a[i] = v[i] if i < vsize else None
|
|
722
|
-
|
|
723
|
-
return a
|
|
724
|
-
|
|
725
867
|
|
|
726
|
-
|
|
727
|
-
jm = jo
|
|
728
|
-
jt = ja
|
|
868
|
+
return a
|
|
729
869
|
|
|
730
870
|
|
|
731
871
|
def select_AND(state, _val, _ref, store):
|
|
732
|
-
if
|
|
872
|
+
if state.mode == S_MKEYPRE:
|
|
733
873
|
terms = getprop(state.parent, state.key)
|
|
734
874
|
ppath = slice(state.path, -1)
|
|
735
875
|
point = getpath(store, ppath)
|
|
@@ -739,15 +879,24 @@ def select_AND(state, _val, _ref, store):
|
|
|
739
879
|
|
|
740
880
|
for term in terms:
|
|
741
881
|
terrs = []
|
|
742
|
-
validate(
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
882
|
+
validate(
|
|
883
|
+
point,
|
|
884
|
+
term,
|
|
885
|
+
{
|
|
886
|
+
'extra': vstore,
|
|
887
|
+
'errs': terrs,
|
|
888
|
+
'meta': state.meta,
|
|
889
|
+
},
|
|
890
|
+
)
|
|
891
|
+
if len(terrs) != 0:
|
|
748
892
|
state.errs.append(
|
|
749
|
-
'AND:'
|
|
750
|
-
|
|
893
|
+
'AND:'
|
|
894
|
+
+ pathify(ppath)
|
|
895
|
+
+ '\u2a2f'
|
|
896
|
+
+ stringify(point)
|
|
897
|
+
+ ' fail:'
|
|
898
|
+
+ stringify(terms)
|
|
899
|
+
)
|
|
751
900
|
|
|
752
901
|
gkey = getelem(state.path, -2)
|
|
753
902
|
gp = getelem(state.nodes, -2)
|
|
@@ -757,7 +906,7 @@ def select_AND(state, _val, _ref, store):
|
|
|
757
906
|
|
|
758
907
|
|
|
759
908
|
def select_OR(state, _val, _ref, store):
|
|
760
|
-
if
|
|
909
|
+
if state.mode == S_MKEYPRE:
|
|
761
910
|
terms = getprop(state.parent, state.key)
|
|
762
911
|
ppath = slice(state.path, -1)
|
|
763
912
|
point = getpath(store, ppath)
|
|
@@ -767,26 +916,30 @@ def select_OR(state, _val, _ref, store):
|
|
|
767
916
|
|
|
768
917
|
for term in terms:
|
|
769
918
|
terrs = []
|
|
770
|
-
validate(
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
919
|
+
validate(
|
|
920
|
+
point,
|
|
921
|
+
term,
|
|
922
|
+
{
|
|
923
|
+
'extra': vstore,
|
|
924
|
+
'errs': terrs,
|
|
925
|
+
'meta': state.meta,
|
|
926
|
+
},
|
|
927
|
+
)
|
|
928
|
+
if len(terrs) == 0:
|
|
776
929
|
gkey = getelem(state.path, -2)
|
|
777
930
|
gp = getelem(state.nodes, -2)
|
|
778
931
|
setprop(gp, gkey, point)
|
|
779
932
|
return UNDEF
|
|
780
933
|
|
|
781
934
|
state.errs.append(
|
|
782
|
-
'OR:' + pathify(ppath) + '\
|
|
783
|
-
|
|
935
|
+
'OR:' + pathify(ppath) + '\u2a2f' + stringify(point) + ' fail:' + stringify(terms)
|
|
936
|
+
)
|
|
784
937
|
|
|
785
938
|
return UNDEF
|
|
786
939
|
|
|
787
940
|
|
|
788
941
|
def select_NOT(state, _val, _ref, store):
|
|
789
|
-
if
|
|
942
|
+
if state.mode == S_MKEYPRE:
|
|
790
943
|
term = getprop(state.parent, state.key)
|
|
791
944
|
ppath = slice(state.path, -1)
|
|
792
945
|
point = getpath(store, ppath)
|
|
@@ -795,16 +948,20 @@ def select_NOT(state, _val, _ref, store):
|
|
|
795
948
|
vstore['$TOP'] = point
|
|
796
949
|
|
|
797
950
|
terrs = []
|
|
798
|
-
validate(
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
951
|
+
validate(
|
|
952
|
+
point,
|
|
953
|
+
term,
|
|
954
|
+
{
|
|
955
|
+
'extra': vstore,
|
|
956
|
+
'errs': terrs,
|
|
957
|
+
'meta': state.meta,
|
|
958
|
+
},
|
|
959
|
+
)
|
|
803
960
|
|
|
804
|
-
if
|
|
961
|
+
if len(terrs) == 0:
|
|
805
962
|
state.errs.append(
|
|
806
|
-
'NOT:' + pathify(ppath) + '\
|
|
807
|
-
|
|
963
|
+
'NOT:' + pathify(ppath) + '\u2a2f' + stringify(point) + ' fail:' + stringify(term)
|
|
964
|
+
)
|
|
808
965
|
|
|
809
966
|
gkey = getelem(state.path, -2)
|
|
810
967
|
gp = getelem(state.nodes, -2)
|
|
@@ -814,7 +971,7 @@ def select_NOT(state, _val, _ref, store):
|
|
|
814
971
|
|
|
815
972
|
|
|
816
973
|
def select_CMP(state, _val, ref, store):
|
|
817
|
-
if
|
|
974
|
+
if state.mode == S_MKEYPRE:
|
|
818
975
|
term = getprop(state.parent, state.key)
|
|
819
976
|
gkey = getelem(state.path, -2)
|
|
820
977
|
ppath = slice(state.path, -1)
|
|
@@ -822,16 +979,16 @@ def select_CMP(state, _val, ref, store):
|
|
|
822
979
|
|
|
823
980
|
pass_test = False
|
|
824
981
|
|
|
825
|
-
if
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
elif '$LTE' == ref and point <= term:
|
|
982
|
+
if (
|
|
983
|
+
(ref == '$GT' and point > term)
|
|
984
|
+
or (ref == '$LT' and point < term)
|
|
985
|
+
or (ref == '$GTE' and point >= term)
|
|
986
|
+
or (ref == '$LTE' and point <= term)
|
|
987
|
+
):
|
|
832
988
|
pass_test = True
|
|
833
|
-
elif '$LIKE'
|
|
989
|
+
elif ref == '$LIKE':
|
|
834
990
|
import re as re_mod
|
|
991
|
+
|
|
835
992
|
if re_mod.search(term, stringify(point)):
|
|
836
993
|
pass_test = True
|
|
837
994
|
|
|
@@ -840,13 +997,20 @@ def select_CMP(state, _val, ref, store):
|
|
|
840
997
|
setprop(gp, gkey, point)
|
|
841
998
|
else:
|
|
842
999
|
state.errs.append(
|
|
843
|
-
'CMP: '
|
|
844
|
-
|
|
1000
|
+
'CMP: '
|
|
1001
|
+
+ pathify(ppath)
|
|
1002
|
+
+ '\u2a2f'
|
|
1003
|
+
+ stringify(point)
|
|
1004
|
+
+ ' fail:'
|
|
1005
|
+
+ ref
|
|
1006
|
+
+ ' '
|
|
1007
|
+
+ stringify(term)
|
|
1008
|
+
)
|
|
845
1009
|
|
|
846
1010
|
return UNDEF
|
|
847
1011
|
|
|
848
1012
|
|
|
849
|
-
def select(children: Any, query: Any) ->
|
|
1013
|
+
def select(children: Any, query: Any) -> list[Any]:
|
|
850
1014
|
"""
|
|
851
1015
|
Select children from a top-level object that match a MongoDB-style query.
|
|
852
1016
|
Supports $and, $or, and equality comparisons.
|
|
@@ -854,12 +1018,12 @@ def select(children: Any, query: Any) -> List[Any]:
|
|
|
854
1018
|
"""
|
|
855
1019
|
if not isnode(children):
|
|
856
1020
|
return []
|
|
857
|
-
|
|
1021
|
+
|
|
858
1022
|
if ismap(children):
|
|
859
1023
|
children = [setprop(v, S_DKEY, k) or v for k, v in items(children)]
|
|
860
1024
|
else:
|
|
861
1025
|
children = [setprop(n, S_DKEY, i) or n if ismap(n) else n for i, n in enumerate(children)]
|
|
862
|
-
|
|
1026
|
+
|
|
863
1027
|
results = []
|
|
864
1028
|
injdef = {
|
|
865
1029
|
'errs': [],
|
|
@@ -873,36 +1037,40 @@ def select(children: Any, query: Any) -> List[Any]:
|
|
|
873
1037
|
'$GTE': select_CMP,
|
|
874
1038
|
'$LTE': select_CMP,
|
|
875
1039
|
'$LIKE': select_CMP,
|
|
876
|
-
}
|
|
1040
|
+
},
|
|
877
1041
|
}
|
|
878
|
-
|
|
1042
|
+
|
|
879
1043
|
q = clone(query)
|
|
880
|
-
|
|
1044
|
+
|
|
881
1045
|
# Add $OPEN to all maps in the query
|
|
882
1046
|
def add_open(_k, v, _parent, _path):
|
|
883
1047
|
if ismap(v):
|
|
884
1048
|
setprop(v, '`$OPEN`', getprop(v, '`$OPEN`', True))
|
|
885
1049
|
return v
|
|
886
|
-
|
|
1050
|
+
|
|
887
1051
|
walk(q, add_open)
|
|
888
|
-
|
|
1052
|
+
|
|
889
1053
|
for child in children:
|
|
890
1054
|
injdef['errs'] = []
|
|
891
1055
|
validate(child, clone(q), injdef)
|
|
892
|
-
|
|
1056
|
+
|
|
893
1057
|
if size(injdef['errs']) == 0:
|
|
894
1058
|
results.append(child)
|
|
895
|
-
|
|
1059
|
+
|
|
896
1060
|
return results
|
|
897
1061
|
|
|
898
1062
|
|
|
899
|
-
def stringify(val: Any, maxlen: int = UNDEF, pretty: Any = None):
|
|
1063
|
+
def stringify(val: Any = _ABSENT, maxlen: int = UNDEF, pretty: Any = None):
|
|
900
1064
|
"Safely stringify a value for printing (NOT JSON!)."
|
|
901
1065
|
|
|
902
1066
|
pretty = bool(pretty)
|
|
903
1067
|
valstr = S_MT
|
|
904
1068
|
|
|
905
|
-
|
|
1069
|
+
# Canonical: stringify(undefined) is "" (or "<>" pretty), while
|
|
1070
|
+
# stringify(null) is "null". Python conflates both onto None, so we use the
|
|
1071
|
+
# _ABSENT sentinel to mean "no value supplied" (undefined). An explicit None
|
|
1072
|
+
# is JSON null and falls through to json.dumps below, which yields "null".
|
|
1073
|
+
if val is _ABSENT:
|
|
906
1074
|
return '<>' if pretty else valstr
|
|
907
1075
|
|
|
908
1076
|
if isinstance(val, str):
|
|
@@ -914,9 +1082,9 @@ def stringify(val: Any, maxlen: int = UNDEF, pretty: Any = None):
|
|
|
914
1082
|
except Exception:
|
|
915
1083
|
valstr = '__STRINGIFY_FAILED__'
|
|
916
1084
|
|
|
917
|
-
if maxlen is not UNDEF and maxlen is not None and -1
|
|
1085
|
+
if maxlen is not UNDEF and maxlen is not None and maxlen > -1:
|
|
918
1086
|
js = valstr[:maxlen]
|
|
919
|
-
valstr = (js[:maxlen - 3] + '...') if maxlen < len(valstr) else valstr
|
|
1087
|
+
valstr = (js[: maxlen - 3] + '...') if maxlen < len(valstr) else valstr
|
|
920
1088
|
|
|
921
1089
|
if pretty:
|
|
922
1090
|
colors = [81, 118, 213, 39, 208, 201, 45, 190, 129, 51, 160, 121, 226, 33, 207, 69]
|
|
@@ -941,31 +1109,28 @@ def stringify(val: Any, maxlen: int = UNDEF, pretty: Any = None):
|
|
|
941
1109
|
return valstr
|
|
942
1110
|
|
|
943
1111
|
|
|
944
|
-
def pathify(val: Any =
|
|
1112
|
+
def pathify(val: Any = _ABSENT, startin: int = UNDEF, endin: int = UNDEF) -> str:
|
|
945
1113
|
pathstr = UNDEF
|
|
946
|
-
|
|
1114
|
+
|
|
947
1115
|
# Convert input to a path array
|
|
948
|
-
path = val if islist(val) else
|
|
949
|
-
[val] if iskey(val) else \
|
|
950
|
-
UNDEF
|
|
1116
|
+
path = val if islist(val) else [val] if iskey(val) else UNDEF
|
|
951
1117
|
|
|
952
1118
|
# [val] if isinstance(val, str) else \
|
|
953
|
-
|
|
1119
|
+
# [val] if isinstance(val, (int, float)) else \
|
|
954
1120
|
|
|
955
|
-
|
|
956
1121
|
# Determine starting index and ending index
|
|
957
|
-
start = 0 if startin is UNDEF else startin if -1
|
|
958
|
-
end = 0 if endin is UNDEF else endin if -1
|
|
1122
|
+
start = 0 if startin is UNDEF else startin if startin > -1 else 0
|
|
1123
|
+
end = 0 if endin is UNDEF else endin if endin > -1 else 0
|
|
959
1124
|
|
|
960
|
-
if
|
|
961
|
-
path = path[start:len(path)-end]
|
|
1125
|
+
if path != UNDEF and start >= 0:
|
|
1126
|
+
path = path[start : len(path) - end]
|
|
962
1127
|
|
|
963
|
-
if
|
|
964
|
-
pathstr =
|
|
1128
|
+
if len(path) == 0:
|
|
1129
|
+
pathstr = '<root>'
|
|
965
1130
|
else:
|
|
966
1131
|
# Filter path parts to include only valid keys
|
|
967
1132
|
filtered_path = [p for p in path if iskey(p)]
|
|
968
|
-
|
|
1133
|
+
|
|
969
1134
|
# Map path parts: convert numbers to strings and remove any dots
|
|
970
1135
|
mapped_path = []
|
|
971
1136
|
for p in filtered_path:
|
|
@@ -973,12 +1138,15 @@ def pathify(val: Any = UNDEF, startin: int = UNDEF, endin: int = UNDEF) -> str:
|
|
|
973
1138
|
mapped_path.append(S_MT + str(int(p)))
|
|
974
1139
|
else:
|
|
975
1140
|
mapped_path.append(str(p).replace('.', S_MT))
|
|
976
|
-
|
|
1141
|
+
|
|
977
1142
|
pathstr = S_DT.join(mapped_path)
|
|
978
1143
|
|
|
979
|
-
# Handle the case where we couldn't create a path
|
|
980
|
-
|
|
981
|
-
|
|
1144
|
+
# Handle the case where we couldn't create a path.
|
|
1145
|
+
# Canonical: an absent (undefined) value renders "<unknown-path>", while an
|
|
1146
|
+
# explicit JSON null renders "<unknown-path:null>". _ABSENT distinguishes
|
|
1147
|
+
# the two (Python conflates both onto None otherwise).
|
|
1148
|
+
if pathstr is UNDEF:
|
|
1149
|
+
pathstr = f'<unknown-path{S_MT if val is _ABSENT else S_CN + stringify(val, 47)}>'
|
|
982
1150
|
|
|
983
1151
|
return pathstr
|
|
984
1152
|
|
|
@@ -988,9 +1156,18 @@ def clone(val: Any = UNDEF):
|
|
|
988
1156
|
Clone a JSON-like data structure.
|
|
989
1157
|
NOTE: function value references are copied, *not* cloned.
|
|
990
1158
|
"""
|
|
991
|
-
if
|
|
1159
|
+
if val == UNDEF:
|
|
992
1160
|
return UNDEF
|
|
993
1161
|
|
|
1162
|
+
try:
|
|
1163
|
+
# Fast path: plain JSON data round-trips directly, avoiding the two full
|
|
1164
|
+
# recursive passes (replacer + reviver) the ref-capturing path below
|
|
1165
|
+
# needs. Non-serialisable values (functions / instances) raise TypeError
|
|
1166
|
+
# and fall through to that path.
|
|
1167
|
+
return json.loads(json.dumps(val, separators=(',', ':')))
|
|
1168
|
+
except TypeError:
|
|
1169
|
+
pass
|
|
1170
|
+
|
|
994
1171
|
refs = []
|
|
995
1172
|
|
|
996
1173
|
def replacer(item):
|
|
@@ -1004,7 +1181,7 @@ def clone(val: Any = UNDEF):
|
|
|
1004
1181
|
elif hasattr(item, 'to_json'):
|
|
1005
1182
|
return item.to_json()
|
|
1006
1183
|
elif hasattr(item, '__dict__'):
|
|
1007
|
-
return item.__dict__
|
|
1184
|
+
return item.__dict__
|
|
1008
1185
|
else:
|
|
1009
1186
|
return item
|
|
1010
1187
|
|
|
@@ -1035,7 +1212,13 @@ def clone(val: Any = UNDEF):
|
|
|
1035
1212
|
def setprop(parent: Any, key: Any, val: Any):
|
|
1036
1213
|
"""
|
|
1037
1214
|
Safely set a property on a dictionary or list.
|
|
1038
|
-
|
|
1215
|
+
|
|
1216
|
+
Mirrors TS canonical: setprop SETS, including JSON null. Callers wanting
|
|
1217
|
+
to delete must use delprop. (Python's UNDEF==None previously conflated
|
|
1218
|
+
"absent" and "JSON null"; the delete-on-None behaviour silently dropped
|
|
1219
|
+
null values, breaking $ANY validation. See Injection.setval for the
|
|
1220
|
+
delete-on-undef logic.)
|
|
1221
|
+
|
|
1039
1222
|
- For lists, negative key -> prepend.
|
|
1040
1223
|
- For lists, key > len(list) -> append.
|
|
1041
1224
|
"""
|
|
@@ -1044,10 +1227,7 @@ def setprop(parent: Any, key: Any, val: Any):
|
|
|
1044
1227
|
|
|
1045
1228
|
if ismap(parent):
|
|
1046
1229
|
key = str(key)
|
|
1047
|
-
|
|
1048
|
-
parent.pop(key, None)
|
|
1049
|
-
else:
|
|
1050
|
-
parent[key] = val
|
|
1230
|
+
parent[key] = val
|
|
1051
1231
|
|
|
1052
1232
|
elif islist(parent):
|
|
1053
1233
|
try:
|
|
@@ -1055,11 +1235,8 @@ def setprop(parent: Any, key: Any, val: Any):
|
|
|
1055
1235
|
except ValueError:
|
|
1056
1236
|
return parent
|
|
1057
1237
|
|
|
1058
|
-
if
|
|
1059
|
-
|
|
1060
|
-
for pI in range(key_i, len(parent) - 1):
|
|
1061
|
-
parent[pI] = parent[pI + 1]
|
|
1062
|
-
parent.pop()
|
|
1238
|
+
if False:
|
|
1239
|
+
pass
|
|
1063
1240
|
else:
|
|
1064
1241
|
if key_i >= 0:
|
|
1065
1242
|
key_i = min(key_i, len(parent))
|
|
@@ -1077,39 +1254,68 @@ MAXDEPTH = 32
|
|
|
1077
1254
|
|
|
1078
1255
|
|
|
1079
1256
|
def walk(
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1257
|
+
val: Any,
|
|
1258
|
+
apply: Any = None,
|
|
1259
|
+
key: Any = UNDEF,
|
|
1260
|
+
parent: Any = UNDEF,
|
|
1261
|
+
path: Any = UNDEF,
|
|
1262
|
+
*,
|
|
1263
|
+
before: Any = None,
|
|
1264
|
+
after: Any = None,
|
|
1265
|
+
maxdepth: Any = None,
|
|
1266
|
+
pool: Any = UNDEF,
|
|
1089
1267
|
):
|
|
1090
1268
|
"""
|
|
1091
1269
|
Walk a data structure depth-first.
|
|
1092
1270
|
Supports before (pre-descent) and after (post-descent) callbacks.
|
|
1093
1271
|
For backward compat, `apply` is treated as the after callback.
|
|
1272
|
+
|
|
1273
|
+
The `path` argument passed to the before/after callbacks is a single
|
|
1274
|
+
mutable list per depth, shared across all callback invocations for
|
|
1275
|
+
the lifetime of this top-level walk call. Callbacks that need to
|
|
1276
|
+
store the path MUST clone it (e.g. ``path[:]`` or ``list(path)``);
|
|
1277
|
+
the contents will otherwise be overwritten by subsequent visits.
|
|
1094
1278
|
"""
|
|
1279
|
+
if pool is UNDEF:
|
|
1280
|
+
pool = [[]]
|
|
1095
1281
|
if path is UNDEF:
|
|
1096
|
-
path = []
|
|
1282
|
+
path = pool[0]
|
|
1097
1283
|
|
|
1098
1284
|
_before = before
|
|
1099
1285
|
_after = after if after is not None else apply
|
|
1100
1286
|
|
|
1287
|
+
depth = len(path)
|
|
1288
|
+
|
|
1101
1289
|
out = val if _before is None else _before(key, val, parent, path)
|
|
1102
1290
|
|
|
1103
|
-
md = maxdepth if maxdepth is not None and
|
|
1104
|
-
if
|
|
1291
|
+
md = maxdepth if maxdepth is not None and maxdepth >= 0 else MAXDEPTH
|
|
1292
|
+
if md == 0 or (md > 0 and md <= depth):
|
|
1105
1293
|
return out
|
|
1106
1294
|
|
|
1107
1295
|
if isnode(out):
|
|
1108
|
-
|
|
1296
|
+
child_depth = depth + 1
|
|
1297
|
+
# Grow pool on demand: pool[n] is a reusable path list of length n.
|
|
1298
|
+
# Appending at index i creates a list of length i, matching index.
|
|
1299
|
+
while len(pool) <= child_depth:
|
|
1300
|
+
pool.append([None] * len(pool))
|
|
1301
|
+
child_path = pool[child_depth]
|
|
1302
|
+
# Sync prefix [0..depth-1] from the current path. Only needed once
|
|
1303
|
+
# per parent: siblings share the same prefix and will each
|
|
1304
|
+
# overwrite slot [depth] below.
|
|
1305
|
+
for i in range(depth):
|
|
1306
|
+
child_path[i] = path[i]
|
|
1307
|
+
|
|
1308
|
+
for ckey, child in items(out):
|
|
1309
|
+
child_path[depth] = str(ckey)
|
|
1109
1310
|
result = walk(
|
|
1110
|
-
child,
|
|
1111
|
-
|
|
1112
|
-
|
|
1311
|
+
child,
|
|
1312
|
+
key=ckey,
|
|
1313
|
+
parent=out,
|
|
1314
|
+
path=child_path,
|
|
1315
|
+
before=_before,
|
|
1316
|
+
after=_after,
|
|
1317
|
+
maxdepth=md,
|
|
1318
|
+
pool=pool,
|
|
1113
1319
|
)
|
|
1114
1320
|
if ismap(out):
|
|
1115
1321
|
out[str(ckey)] = result
|
|
@@ -1122,7 +1328,7 @@ def walk(
|
|
|
1122
1328
|
return out
|
|
1123
1329
|
|
|
1124
1330
|
|
|
1125
|
-
def merge(objs:
|
|
1331
|
+
def merge(objs: list[Any] = UNDEF, maxdepth: Any = None) -> Any:
|
|
1126
1332
|
"""
|
|
1127
1333
|
Merge a list of values into each other. Later values have
|
|
1128
1334
|
precedence. Nodes override scalars. Node kinds (list or map)
|
|
@@ -1137,9 +1343,9 @@ def merge(objs: List[Any] = None, maxdepth: Any = None) -> Any:
|
|
|
1137
1343
|
|
|
1138
1344
|
lenlist = len(objs)
|
|
1139
1345
|
|
|
1140
|
-
if
|
|
1346
|
+
if lenlist == 0:
|
|
1141
1347
|
return UNDEF
|
|
1142
|
-
if
|
|
1348
|
+
if lenlist == 1:
|
|
1143
1349
|
return objs[0]
|
|
1144
1350
|
|
|
1145
1351
|
out = getprop(objs, 0, {})
|
|
@@ -1153,7 +1359,7 @@ def merge(objs: List[Any] = None, maxdepth: Any = None) -> Any:
|
|
|
1153
1359
|
cur = [out]
|
|
1154
1360
|
dst = [out]
|
|
1155
1361
|
|
|
1156
|
-
def before(key, val, _parent, path):
|
|
1362
|
+
def before(key, val, _parent, path, cur=cur, dst=dst):
|
|
1157
1363
|
pI = size(path)
|
|
1158
1364
|
|
|
1159
1365
|
if md <= pI:
|
|
@@ -1179,10 +1385,10 @@ def merge(objs: List[Any] = None, maxdepth: Any = None) -> Any:
|
|
|
1179
1385
|
if pI >= cur_len:
|
|
1180
1386
|
cur.extend([UNDEF] * (pI + 1 - cur_len))
|
|
1181
1387
|
|
|
1182
|
-
dst[pI] = getprop(dst[pI - 1], key) if
|
|
1388
|
+
dst[pI] = getprop(dst[pI - 1], key) if pI > 0 else dst[pI]
|
|
1183
1389
|
tval = dst[pI]
|
|
1184
1390
|
|
|
1185
|
-
if
|
|
1391
|
+
if tval == UNDEF:
|
|
1186
1392
|
cur[pI] = [] if islist(val) else {}
|
|
1187
1393
|
elif (islist(val) and islist(tval)) or (ismap(val) and ismap(tval)):
|
|
1188
1394
|
cur[pI] = tval
|
|
@@ -1192,7 +1398,7 @@ def merge(objs: List[Any] = None, maxdepth: Any = None) -> Any:
|
|
|
1192
1398
|
|
|
1193
1399
|
return val
|
|
1194
1400
|
|
|
1195
|
-
def after(key, _val, _parent, path):
|
|
1401
|
+
def after(key, _val, _parent, path, cur=cur):
|
|
1196
1402
|
cI = size(path)
|
|
1197
1403
|
if cI < 1:
|
|
1198
1404
|
return cur[0] if len(cur) > 0 else _val
|
|
@@ -1205,7 +1411,7 @@ def merge(objs: List[Any] = None, maxdepth: Any = None) -> Any:
|
|
|
1205
1411
|
|
|
1206
1412
|
out = walk(obj, before=before, after=after)
|
|
1207
1413
|
|
|
1208
|
-
if
|
|
1414
|
+
if md == 0:
|
|
1209
1415
|
out = getprop(objs, lenlist - 1, UNDEF)
|
|
1210
1416
|
out = [] if islist(out) else {} if ismap(out) else out
|
|
1211
1417
|
|
|
@@ -1226,7 +1432,7 @@ def getpath(store, path, injdef=UNDEF):
|
|
|
1226
1432
|
parts = [strkey(path)]
|
|
1227
1433
|
else:
|
|
1228
1434
|
return UNDEF
|
|
1229
|
-
|
|
1435
|
+
|
|
1230
1436
|
val = store
|
|
1231
1437
|
# Support both dict-style injdef and Injection instance
|
|
1232
1438
|
if isinstance(injdef, Injection):
|
|
@@ -1244,33 +1450,31 @@ def getpath(store, path, injdef=UNDEF):
|
|
|
1244
1450
|
|
|
1245
1451
|
src = getprop(store, base, store) if base else store
|
|
1246
1452
|
numparts = size(parts)
|
|
1247
|
-
|
|
1453
|
+
|
|
1248
1454
|
# An empty path (incl empty string) just finds the store.
|
|
1249
|
-
if path is UNDEF or store is UNDEF or (
|
|
1455
|
+
if path is UNDEF or store is UNDEF or (numparts == 1 and parts[0] == S_MT) or numparts == 0:
|
|
1250
1456
|
val = src
|
|
1251
1457
|
return val
|
|
1252
1458
|
elif numparts > 0:
|
|
1253
|
-
|
|
1254
1459
|
# Check for $ACTIONs
|
|
1255
|
-
if
|
|
1460
|
+
if numparts == 1:
|
|
1256
1461
|
val = getprop(store, parts[0])
|
|
1257
|
-
|
|
1462
|
+
|
|
1258
1463
|
if not isfunc(val):
|
|
1259
1464
|
val = src
|
|
1260
|
-
|
|
1465
|
+
|
|
1261
1466
|
# Check for meta path syntax
|
|
1262
1467
|
m = R_META_PATH.match(parts[0]) if parts[0] else None
|
|
1263
1468
|
if m and inj_meta:
|
|
1264
1469
|
val = getprop(inj_meta, m.group(1))
|
|
1265
1470
|
parts[0] = m.group(3)
|
|
1266
|
-
|
|
1267
|
-
|
|
1471
|
+
|
|
1268
1472
|
for pI in range(numparts):
|
|
1269
1473
|
if val is UNDEF:
|
|
1270
1474
|
break
|
|
1271
|
-
|
|
1475
|
+
|
|
1272
1476
|
part = parts[pI]
|
|
1273
|
-
|
|
1477
|
+
|
|
1274
1478
|
# Handle special path components
|
|
1275
1479
|
if injdef and part == S_DKEY:
|
|
1276
1480
|
part = inj_key if inj_key is not UNDEF else part
|
|
@@ -1283,55 +1487,56 @@ def getpath(store, path, injdef=UNDEF):
|
|
|
1283
1487
|
elif injdef and isinstance(part, str) and part.startswith('$META:'):
|
|
1284
1488
|
# $META:metapath$ -> get meta value, use as path part (string)
|
|
1285
1489
|
part = stringify(getpath(inj_meta, part[6:-1]))
|
|
1286
|
-
|
|
1287
|
-
# $$ escapes $ (path parts can be int e.g. list indices)
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
else:
|
|
1490
|
+
|
|
1491
|
+
# $$ escapes $ (path parts can be int e.g. list indices).
|
|
1492
|
+
# Skip the regex for the common segment with no '$$'.
|
|
1493
|
+
if not isinstance(part, str):
|
|
1291
1494
|
part = strkey(part)
|
|
1292
|
-
|
|
1495
|
+
elif '$$' in part:
|
|
1496
|
+
part = R_DOUBLE_DOLLAR.sub('$', part)
|
|
1497
|
+
|
|
1293
1498
|
if part == S_MT:
|
|
1294
1499
|
ascends = 0
|
|
1295
1500
|
while pI + 1 < len(parts) and parts[pI + 1] == S_MT:
|
|
1296
1501
|
ascends += 1
|
|
1297
1502
|
pI += 1
|
|
1298
1503
|
|
|
1299
|
-
if injdef and
|
|
1504
|
+
if injdef and ascends > 0:
|
|
1300
1505
|
if pI == len(parts) - 1:
|
|
1301
1506
|
ascends -= 1
|
|
1302
1507
|
|
|
1303
|
-
if
|
|
1508
|
+
if ascends == 0:
|
|
1304
1509
|
val = dparent
|
|
1305
1510
|
else:
|
|
1306
|
-
fullpath = flatten(
|
|
1307
|
-
|
|
1308
|
-
if ascends <= size(dpath):
|
|
1309
|
-
val = getpath(store, fullpath)
|
|
1310
|
-
else:
|
|
1311
|
-
val = UNDEF
|
|
1511
|
+
fullpath = flatten([slice(dpath, 0 - ascends), parts[pI + 1 :]])
|
|
1512
|
+
val = getpath(store, fullpath) if ascends <= size(dpath) else UNDEF
|
|
1312
1513
|
break
|
|
1313
1514
|
else:
|
|
1314
1515
|
val = dparent
|
|
1315
1516
|
else:
|
|
1316
1517
|
val = getprop(val, part)
|
|
1317
|
-
|
|
1518
|
+
|
|
1318
1519
|
# Injdef may provide a custom handler to modify found value.
|
|
1319
|
-
handler =
|
|
1520
|
+
handler = (
|
|
1521
|
+
injdef.handler
|
|
1522
|
+
if isinstance(injdef, Injection)
|
|
1523
|
+
else (getprop(injdef, 'handler') if injdef else UNDEF)
|
|
1524
|
+
)
|
|
1320
1525
|
if handler and isfunc(handler):
|
|
1321
1526
|
ref = pathify(path)
|
|
1322
1527
|
val = handler(injdef, val, ref, store)
|
|
1323
|
-
|
|
1528
|
+
|
|
1324
1529
|
return val
|
|
1325
1530
|
|
|
1326
1531
|
|
|
1327
1532
|
def setpath(store, path, val, injdef=UNDEF):
|
|
1328
1533
|
pathType = typify(path)
|
|
1329
1534
|
|
|
1330
|
-
if
|
|
1535
|
+
if (T_list & pathType) > 0:
|
|
1331
1536
|
parts = path
|
|
1332
|
-
elif
|
|
1537
|
+
elif (T_string & pathType) > 0:
|
|
1333
1538
|
parts = path.split(S_DT)
|
|
1334
|
-
elif
|
|
1539
|
+
elif (T_number & pathType) > 0:
|
|
1335
1540
|
parts = [path]
|
|
1336
1541
|
else:
|
|
1337
1542
|
return UNDEF
|
|
@@ -1345,7 +1550,7 @@ def setpath(store, path, val, injdef=UNDEF):
|
|
|
1345
1550
|
nextParent = getprop(parent, partKey)
|
|
1346
1551
|
if not isnode(nextParent):
|
|
1347
1552
|
nextPart = getelem(parts, pI + 1)
|
|
1348
|
-
nextParent = [] if
|
|
1553
|
+
nextParent = [] if (T_number & typify(nextPart)) > 0 else {}
|
|
1349
1554
|
setprop(parent, partKey, nextParent)
|
|
1350
1555
|
parent = nextParent
|
|
1351
1556
|
|
|
@@ -1361,8 +1566,6 @@ def inject(val, store, injdef=UNDEF):
|
|
|
1361
1566
|
"""
|
|
1362
1567
|
Inject values from `store` into `val` recursively, respecting backtick syntax.
|
|
1363
1568
|
"""
|
|
1364
|
-
valtype = type(val)
|
|
1365
|
-
|
|
1366
1569
|
# Reuse existing injection state during recursion; otherwise create a new one.
|
|
1367
1570
|
if isinstance(injdef, Injection):
|
|
1368
1571
|
inj = injdef
|
|
@@ -1385,7 +1588,7 @@ def inject(val, store, injdef=UNDEF):
|
|
|
1385
1588
|
base=S_DTOP,
|
|
1386
1589
|
modify=getprop(injdef, 'modify') if injdef else None,
|
|
1387
1590
|
meta=getprop(injdef, 'meta', {}),
|
|
1388
|
-
errs=getprop(store, S_DERRS, [])
|
|
1591
|
+
errs=getprop(store, S_DERRS, []),
|
|
1389
1592
|
)
|
|
1390
1593
|
inj.dparent = store
|
|
1391
1594
|
inj.dpath = [S_DTOP]
|
|
@@ -1408,9 +1611,9 @@ def inject(val, store, injdef=UNDEF):
|
|
|
1408
1611
|
# Keys are sorted alphanumerically to ensure determinism.
|
|
1409
1612
|
# Injection transforms ($FOO) are processed *after* other keys.
|
|
1410
1613
|
if ismap(val):
|
|
1411
|
-
normal_keys = [k for k in val
|
|
1614
|
+
normal_keys = [k for k in val if S_DS not in k]
|
|
1412
1615
|
normal_keys.sort()
|
|
1413
|
-
transform_keys = [k for k in val
|
|
1616
|
+
transform_keys = [k for k in val if S_DS in k]
|
|
1414
1617
|
transform_keys.sort()
|
|
1415
1618
|
nodekeys = normal_keys + transform_keys
|
|
1416
1619
|
else:
|
|
@@ -1473,7 +1676,11 @@ def inject(val, store, injdef=UNDEF):
|
|
|
1473
1676
|
inj.val = val
|
|
1474
1677
|
|
|
1475
1678
|
# Return the (possibly transform-replaced) root only at top level (prior is None).
|
|
1476
|
-
if
|
|
1679
|
+
if (
|
|
1680
|
+
getattr(inj, 'prior', None) is None
|
|
1681
|
+
and getattr(inj, 'root', None) is not None
|
|
1682
|
+
and haskey(inj.root, S_DTOP)
|
|
1683
|
+
):
|
|
1477
1684
|
return getprop(inj.root, S_DTOP)
|
|
1478
1685
|
if inj.key == S_DTOP and inj.parent is not UNDEF and haskey(inj.parent, S_DTOP):
|
|
1479
1686
|
return getprop(inj.parent, S_DTOP)
|
|
@@ -1484,7 +1691,7 @@ def inject(val, store, injdef=UNDEF):
|
|
|
1484
1691
|
# call the function passing the injection state. This is how transforms operate.
|
|
1485
1692
|
def _injecthandler(inj, val, ref, store):
|
|
1486
1693
|
out = val
|
|
1487
|
-
iscmd = isfunc(val) and (
|
|
1694
|
+
iscmd = isfunc(val) and (ref == UNDEF or (isinstance(ref, str) and ref.startswith(S_DS)))
|
|
1488
1695
|
|
|
1489
1696
|
# Only call val function if it is a special command ($NAME format).
|
|
1490
1697
|
if iscmd:
|
|
@@ -1513,7 +1720,7 @@ def transform_DELETE(inj, val, ref, store):
|
|
|
1513
1720
|
"""
|
|
1514
1721
|
Injection handler to delete a key from a map/list.
|
|
1515
1722
|
"""
|
|
1516
|
-
inj.
|
|
1723
|
+
delprop(inj.parent, inj.key)
|
|
1517
1724
|
return UNDEF
|
|
1518
1725
|
|
|
1519
1726
|
|
|
@@ -1523,7 +1730,6 @@ def transform_COPY(inj, val, ref, store):
|
|
|
1523
1730
|
"""
|
|
1524
1731
|
mode = inj.mode
|
|
1525
1732
|
key = inj.key
|
|
1526
|
-
parent = inj.parent
|
|
1527
1733
|
|
|
1528
1734
|
out = UNDEF
|
|
1529
1735
|
if mode.startswith('key'):
|
|
@@ -1574,9 +1780,8 @@ def transform_KEY(inj, val, ref, store):
|
|
|
1574
1780
|
|
|
1575
1781
|
keyspec = getprop(parent, S_BKEY)
|
|
1576
1782
|
if keyspec is not UNDEF:
|
|
1577
|
-
#
|
|
1578
|
-
|
|
1579
|
-
setprop(parent, S_BKEY, UNDEF)
|
|
1783
|
+
# Explicitly delete the marker key.
|
|
1784
|
+
delprop(parent, S_BKEY)
|
|
1580
1785
|
return getprop(inj.dparent, keyspec)
|
|
1581
1786
|
|
|
1582
1787
|
# If no explicit keyspec, and current data has a field matching this key,
|
|
@@ -1593,7 +1798,7 @@ def transform_ANNO(inj, val, ref, store):
|
|
|
1593
1798
|
Annotate node. Does nothing itself, just used by other injectors, and is removed when called.
|
|
1594
1799
|
"""
|
|
1595
1800
|
parent = inj.parent
|
|
1596
|
-
|
|
1801
|
+
delprop(parent, S_BANNO)
|
|
1597
1802
|
return UNDEF
|
|
1598
1803
|
|
|
1599
1804
|
|
|
@@ -1619,11 +1824,11 @@ def transform_MERGE(inj, val, ref, store):
|
|
|
1619
1824
|
args = args if islist(args) else [args]
|
|
1620
1825
|
|
|
1621
1826
|
# Remove the $MERGE command from a parent map.
|
|
1622
|
-
inj.
|
|
1827
|
+
delprop(inj.parent, inj.key)
|
|
1623
1828
|
|
|
1624
1829
|
# Literals in the parent have precedence, but we still merge onto
|
|
1625
1830
|
# the parent object, so that node tree references are not changed.
|
|
1626
|
-
mergelist = [parent
|
|
1831
|
+
mergelist = [parent, *args, clone(parent)]
|
|
1627
1832
|
|
|
1628
1833
|
merge(mergelist)
|
|
1629
1834
|
|
|
@@ -1666,7 +1871,7 @@ def transform_EACH(inj, val, ref, store):
|
|
|
1666
1871
|
# Source data
|
|
1667
1872
|
srcstore = getprop(store, inj.base, store)
|
|
1668
1873
|
src = getpath(srcstore, srcpath, inj)
|
|
1669
|
-
|
|
1874
|
+
|
|
1670
1875
|
# Create parallel data structures:
|
|
1671
1876
|
# source entries :: child templates
|
|
1672
1877
|
tcurrent = []
|
|
@@ -1676,26 +1881,26 @@ def transform_EACH(inj, val, ref, store):
|
|
|
1676
1881
|
target = nodes_[-2] if len(nodes_) >= 2 else nodes_[-1]
|
|
1677
1882
|
|
|
1678
1883
|
rval = []
|
|
1679
|
-
|
|
1884
|
+
|
|
1680
1885
|
if isnode(src):
|
|
1681
1886
|
if islist(src):
|
|
1682
1887
|
tval = [clone(child_template) for _ in src]
|
|
1683
1888
|
else:
|
|
1684
1889
|
# Convert dict to a list of child templates
|
|
1685
1890
|
tval = []
|
|
1686
|
-
for k
|
|
1891
|
+
for k in src:
|
|
1687
1892
|
# Keep key in meta for usage by `$KEY`
|
|
1688
1893
|
copy_child = clone(child_template)
|
|
1689
1894
|
if ismap(copy_child):
|
|
1690
1895
|
setprop(copy_child, S_BANNO, {S_KEY: k})
|
|
1691
1896
|
tval.append(copy_child)
|
|
1692
1897
|
tcurrent = list(src.values()) if ismap(src) else src
|
|
1693
|
-
|
|
1694
|
-
if
|
|
1898
|
+
|
|
1899
|
+
if size(tval) > 0:
|
|
1695
1900
|
# Build tcurrent structure matching TypeScript approach
|
|
1696
1901
|
ckey = getelem(path, -2) if len(path) >= 2 else UNDEF
|
|
1697
1902
|
tpath = path[:-1] if len(path) > 0 else []
|
|
1698
|
-
|
|
1903
|
+
|
|
1699
1904
|
# Build dpath: [S_DTOP, ...srcpath parts, '$:' + ckey]
|
|
1700
1905
|
dpath = [S_DTOP]
|
|
1701
1906
|
if isinstance(srcpath, str) and srcpath:
|
|
@@ -1704,34 +1909,34 @@ def transform_EACH(inj, val, ref, store):
|
|
|
1704
1909
|
dpath.append(part)
|
|
1705
1910
|
if ckey is not UNDEF:
|
|
1706
1911
|
dpath.append('$:' + str(ckey))
|
|
1707
|
-
|
|
1912
|
+
|
|
1708
1913
|
tcur = {ckey: tcurrent}
|
|
1709
1914
|
|
|
1710
|
-
if
|
|
1915
|
+
if size(tpath) > 1:
|
|
1711
1916
|
pkey = getelem(path, -3, S_DTOP)
|
|
1712
1917
|
tcur = {pkey: tcur}
|
|
1713
1918
|
dpath.append('$:' + str(pkey))
|
|
1714
|
-
|
|
1919
|
+
|
|
1715
1920
|
# Create child injection state
|
|
1716
1921
|
tinj = inj.child(0, [ckey] if ckey is not UNDEF else [])
|
|
1717
1922
|
tinj.path = tpath
|
|
1718
1923
|
tinj.nodes = nodes_[:-1] if len(nodes_) > 0 else []
|
|
1719
1924
|
tinj.parent = getelem(tinj.nodes, -1) if len(tinj.nodes) > 0 else UNDEF
|
|
1720
|
-
|
|
1925
|
+
|
|
1721
1926
|
if ckey is not UNDEF and tinj.parent is not UNDEF:
|
|
1722
1927
|
setprop(tinj.parent, ckey, tval)
|
|
1723
|
-
|
|
1928
|
+
|
|
1724
1929
|
tinj.val = tval
|
|
1725
1930
|
tinj.dpath = dpath
|
|
1726
1931
|
tinj.dparent = tcur
|
|
1727
|
-
|
|
1932
|
+
|
|
1728
1933
|
# Inject the entire list at once
|
|
1729
1934
|
inject(tval, store, tinj)
|
|
1730
1935
|
rval = tinj.val
|
|
1731
1936
|
|
|
1732
1937
|
setprop(target, tkey, rval)
|
|
1733
1938
|
|
|
1734
|
-
return rval[0] if islist(rval) and
|
|
1939
|
+
return rval[0] if islist(rval) and size(rval) > 0 else UNDEF
|
|
1735
1940
|
|
|
1736
1941
|
|
|
1737
1942
|
def transform_PACK(inj, val, ref, store):
|
|
@@ -1741,7 +1946,7 @@ def transform_PACK(inj, val, ref, store):
|
|
|
1741
1946
|
parent = inj.parent
|
|
1742
1947
|
nodes_ = inj.nodes
|
|
1743
1948
|
|
|
1744
|
-
if
|
|
1949
|
+
if mode != S_MKEYPRE or not isinstance(key, str) or path is UNDEF or nodes_ is UNDEF:
|
|
1745
1950
|
return UNDEF
|
|
1746
1951
|
|
|
1747
1952
|
args_val = getprop(parent, key)
|
|
@@ -1817,7 +2022,7 @@ def transform_PACK(inj, val, ref, store):
|
|
|
1817
2022
|
|
|
1818
2023
|
tcur = {ckey: tsrc}
|
|
1819
2024
|
|
|
1820
|
-
if
|
|
2025
|
+
if size(tpath) > 1:
|
|
1821
2026
|
pkey = getelem(inj.path, -3, S_DTOP)
|
|
1822
2027
|
tcur = {pkey: tcur}
|
|
1823
2028
|
dpath.append('$:' + str(pkey))
|
|
@@ -1844,7 +2049,6 @@ def transform_REF(inj, val, _ref, store):
|
|
|
1844
2049
|
Format: ['`$REF`', '`spec-path`']
|
|
1845
2050
|
"""
|
|
1846
2051
|
nodes = inj.nodes
|
|
1847
|
-
modify = inj.modify
|
|
1848
2052
|
|
|
1849
2053
|
if inj.mode != S_MVAL:
|
|
1850
2054
|
return UNDEF
|
|
@@ -1863,6 +2067,7 @@ def transform_REF(inj, val, _ref, store):
|
|
|
1863
2067
|
# Check if ref has another $REF inside
|
|
1864
2068
|
hasSubRef = False
|
|
1865
2069
|
if isnode(ref):
|
|
2070
|
+
|
|
1866
2071
|
def check_subref(k, v, parent, path):
|
|
1867
2072
|
nonlocal hasSubRef
|
|
1868
2073
|
if v == '`$REF`':
|
|
@@ -1873,8 +2078,8 @@ def transform_REF(inj, val, _ref, store):
|
|
|
1873
2078
|
|
|
1874
2079
|
tref = clone(ref)
|
|
1875
2080
|
|
|
1876
|
-
cpath = slice(inj.path, 0, len(inj.path)-3)
|
|
1877
|
-
tpath = slice(inj.path, 0, len(inj.path)-1)
|
|
2081
|
+
cpath = slice(inj.path, 0, len(inj.path) - 3)
|
|
2082
|
+
tpath = slice(inj.path, 0, len(inj.path) - 1)
|
|
1878
2083
|
tcur = getpath(store, cpath)
|
|
1879
2084
|
tval = getpath(store, tpath)
|
|
1880
2085
|
rval = UNDEF
|
|
@@ -1884,7 +2089,7 @@ def transform_REF(inj, val, _ref, store):
|
|
|
1884
2089
|
# Create child state for the next level
|
|
1885
2090
|
child_state = inj.child(0, [getelem(tpath, -1)])
|
|
1886
2091
|
child_state.path = tpath
|
|
1887
|
-
child_state.nodes = slice(inj.nodes, 0, len(inj.nodes)-1)
|
|
2092
|
+
child_state.nodes = slice(inj.nodes, 0, len(inj.nodes) - 1)
|
|
1888
2093
|
child_state.parent = getelem(nodes, -2)
|
|
1889
2094
|
child_state.val = tref
|
|
1890
2095
|
|
|
@@ -1897,7 +2102,7 @@ def transform_REF(inj, val, _ref, store):
|
|
|
1897
2102
|
|
|
1898
2103
|
# Set the value in grandparent, using setval
|
|
1899
2104
|
inj.setval(rval, 2)
|
|
1900
|
-
|
|
2105
|
+
|
|
1901
2106
|
# Handle lists by decrementing keyI
|
|
1902
2107
|
if islist(inj.parent) and inj.prior:
|
|
1903
2108
|
inj.prior.keyI -= 1
|
|
@@ -1944,26 +2149,41 @@ FORMATTER = {
|
|
|
1944
2149
|
'string': lambda _k, v, *_a: v if isnode(v) else _jsstr(v),
|
|
1945
2150
|
'number': _fmt_number,
|
|
1946
2151
|
'integer': _fmt_integer,
|
|
1947
|
-
'concat': lambda k, v, *_a:
|
|
1948
|
-
items(v, lambda n: '' if isnode(n[1]) else _jsstr(n[1])), '')
|
|
2152
|
+
'concat': lambda k, v, *_a: (
|
|
2153
|
+
join(items(v, lambda n: '' if isnode(n[1]) else _jsstr(n[1])), '')
|
|
2154
|
+
if k is None and islist(v)
|
|
2155
|
+
else v
|
|
2156
|
+
),
|
|
1949
2157
|
}
|
|
1950
2158
|
|
|
1951
2159
|
|
|
1952
2160
|
def checkPlacement(modes, ijname, parentTypes, inj):
|
|
1953
2161
|
mode_num = _MODE_TO_NUM.get(inj.mode, 0)
|
|
1954
|
-
if
|
|
2162
|
+
if (modes & mode_num) == 0:
|
|
1955
2163
|
allowed = [m for m in [M_KEYPRE, M_KEYPOST, M_VAL] if modes & m]
|
|
1956
|
-
placements = join(
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
2164
|
+
placements = join(items(allowed, lambda n: _PLACEMENT.get(n[1], '')), ',')
|
|
2165
|
+
inj.errs.append(
|
|
2166
|
+
'$'
|
|
2167
|
+
+ ijname
|
|
2168
|
+
+ ': invalid placement as '
|
|
2169
|
+
+ _PLACEMENT.get(mode_num, '')
|
|
2170
|
+
+ ', expected: '
|
|
2171
|
+
+ placements
|
|
2172
|
+
+ '.'
|
|
2173
|
+
)
|
|
1961
2174
|
return False
|
|
1962
2175
|
if not isempty(parentTypes):
|
|
1963
2176
|
ptype = typify(inj.parent)
|
|
1964
|
-
if
|
|
1965
|
-
inj.errs.append(
|
|
1966
|
-
|
|
2177
|
+
if (parentTypes & ptype) == 0:
|
|
2178
|
+
inj.errs.append(
|
|
2179
|
+
'$'
|
|
2180
|
+
+ ijname
|
|
2181
|
+
+ ': invalid placement in parent '
|
|
2182
|
+
+ typename(ptype)
|
|
2183
|
+
+ ', expected: '
|
|
2184
|
+
+ typename(parentTypes)
|
|
2185
|
+
+ '.'
|
|
2186
|
+
)
|
|
1967
2187
|
return False
|
|
1968
2188
|
return True
|
|
1969
2189
|
|
|
@@ -1975,10 +2195,18 @@ def injectorArgs(argTypes, args):
|
|
|
1975
2195
|
for argI in range(numargs):
|
|
1976
2196
|
arg = getelem(args, argI)
|
|
1977
2197
|
argType = typify(arg)
|
|
1978
|
-
if
|
|
1979
|
-
found[0] = (
|
|
1980
|
-
|
|
1981
|
-
|
|
2198
|
+
if (argTypes[argI] & argType) == 0:
|
|
2199
|
+
found[0] = (
|
|
2200
|
+
'invalid argument: '
|
|
2201
|
+
+ stringify(arg, 22)
|
|
2202
|
+
+ ' ('
|
|
2203
|
+
+ typename(argType)
|
|
2204
|
+
+ ' at position '
|
|
2205
|
+
+ str(1 + argI)
|
|
2206
|
+
+ ') is not of type: '
|
|
2207
|
+
+ typename(argTypes[argI])
|
|
2208
|
+
+ '.'
|
|
2209
|
+
)
|
|
1982
2210
|
break
|
|
1983
2211
|
found[1 + argI] = arg
|
|
1984
2212
|
return found
|
|
@@ -2002,7 +2230,7 @@ def injectChild(child, store, inj):
|
|
|
2002
2230
|
def transform_FORMAT(inj, _val, _ref, store):
|
|
2003
2231
|
slice(inj.keys, 0, 1, True)
|
|
2004
2232
|
|
|
2005
|
-
if
|
|
2233
|
+
if inj.mode != S_MVAL:
|
|
2006
2234
|
return UNDEF
|
|
2007
2235
|
|
|
2008
2236
|
name = getprop(inj.parent, 1)
|
|
@@ -2014,7 +2242,7 @@ def transform_FORMAT(inj, _val, _ref, store):
|
|
|
2014
2242
|
cinj = injectChild(child, store, inj)
|
|
2015
2243
|
resolved = cinj.val
|
|
2016
2244
|
|
|
2017
|
-
formatter = name if
|
|
2245
|
+
formatter = name if (T_function & typify(name)) > 0 else getprop(FORMATTER, name)
|
|
2018
2246
|
|
|
2019
2247
|
if formatter is UNDEF:
|
|
2020
2248
|
inj.errs.append('$FORMAT: unknown format: ' + str(name) + '.')
|
|
@@ -2038,7 +2266,7 @@ def transform_APPLY(inj, _val, _ref, store):
|
|
|
2038
2266
|
apply_fn = err_apply_child[1]
|
|
2039
2267
|
child = err_apply_child[2] if len(err_apply_child) > 2 else UNDEF
|
|
2040
2268
|
|
|
2041
|
-
if
|
|
2269
|
+
if err != UNDEF:
|
|
2042
2270
|
inj.errs.append('$' + ijname + ': ' + err)
|
|
2043
2271
|
return UNDEF
|
|
2044
2272
|
|
|
@@ -2064,23 +2292,23 @@ def transform_APPLY(inj, _val, _ref, store):
|
|
|
2064
2292
|
# Transform data using spec.
|
|
2065
2293
|
# Only operates on static JSON-like data.
|
|
2066
2294
|
# Arrays are treated as if they are objects with indices as keys.
|
|
2067
|
-
def transform(
|
|
2068
|
-
data,
|
|
2069
|
-
spec,
|
|
2070
|
-
injdef=UNDEF
|
|
2071
|
-
):
|
|
2295
|
+
def transform(data, spec, injdef=UNDEF):
|
|
2072
2296
|
# Clone the spec so that the clone can be modified in place as the transform result.
|
|
2073
2297
|
origspec = spec
|
|
2074
2298
|
spec = clone(spec)
|
|
2075
2299
|
|
|
2076
2300
|
extra = getprop(injdef, 'extra') if injdef else UNDEF
|
|
2077
2301
|
|
|
2078
|
-
collect =
|
|
2302
|
+
collect = (
|
|
2303
|
+
getprop(injdef, 'errs') is not None and getprop(injdef, 'errs') is not UNDEF
|
|
2304
|
+
if injdef
|
|
2305
|
+
else False
|
|
2306
|
+
)
|
|
2079
2307
|
errs = getprop(injdef, 'errs') if collect else []
|
|
2080
2308
|
|
|
2081
2309
|
extraTransforms = {}
|
|
2082
|
-
extraData = {}
|
|
2083
|
-
|
|
2310
|
+
extraData = {}
|
|
2311
|
+
|
|
2084
2312
|
if extra:
|
|
2085
2313
|
for k, v in items(extra):
|
|
2086
2314
|
if isinstance(k, str) and k.startswith(S_DS):
|
|
@@ -2089,10 +2317,7 @@ def transform(
|
|
|
2089
2317
|
extraData[k] = v
|
|
2090
2318
|
|
|
2091
2319
|
# Combine extra data with user data
|
|
2092
|
-
data_clone = merge([
|
|
2093
|
-
clone(extraData) if not isempty(extraData) else UNDEF,
|
|
2094
|
-
clone(data)
|
|
2095
|
-
])
|
|
2320
|
+
data_clone = merge([clone(extraData) if not isempty(extraData) else UNDEF, clone(data)])
|
|
2096
2321
|
|
|
2097
2322
|
# Top-level store used by inject
|
|
2098
2323
|
store = {
|
|
@@ -2100,19 +2325,14 @@ def transform(
|
|
|
2100
2325
|
# NOTE: to escape data that contains "`$FOO`" keys at the top level,
|
|
2101
2326
|
# place that data inside a holding map: { myholder: mydata }.
|
|
2102
2327
|
S_DTOP: data_clone,
|
|
2103
|
-
|
|
2104
2328
|
# Original spec (before clone) for $REF to resolve refpath.
|
|
2105
2329
|
S_DSPEC: lambda: origspec,
|
|
2106
|
-
|
|
2107
2330
|
# Escape backtick (this also works inside backticks).
|
|
2108
2331
|
'$BT': lambda *args, **kwargs: S_BT,
|
|
2109
|
-
|
|
2110
2332
|
# Escape dollar sign (this also works inside backticks).
|
|
2111
2333
|
'$DS': lambda *args, **kwargs: S_DS,
|
|
2112
|
-
|
|
2113
2334
|
# Insert current date and time as an ISO string.
|
|
2114
2335
|
'$WHEN': lambda *args, **kwargs: datetime.utcnow().isoformat(),
|
|
2115
|
-
|
|
2116
2336
|
'$DELETE': transform_DELETE,
|
|
2117
2337
|
'$COPY': transform_COPY,
|
|
2118
2338
|
'$KEY': transform_KEY,
|
|
@@ -2123,10 +2343,8 @@ def transform(
|
|
|
2123
2343
|
'$REF': transform_REF,
|
|
2124
2344
|
'$FORMAT': transform_FORMAT,
|
|
2125
2345
|
'$APPLY': transform_APPLY,
|
|
2126
|
-
|
|
2127
2346
|
# Custom extra transforms, if any.
|
|
2128
2347
|
**extraTransforms,
|
|
2129
|
-
|
|
2130
2348
|
S_DERRS: errs,
|
|
2131
2349
|
}
|
|
2132
2350
|
|
|
@@ -2138,7 +2356,7 @@ def transform(
|
|
|
2138
2356
|
|
|
2139
2357
|
out = inject(spec, store, injdef)
|
|
2140
2358
|
|
|
2141
|
-
generr =
|
|
2359
|
+
generr = size(errs) > 0 and not collect
|
|
2142
2360
|
if generr:
|
|
2143
2361
|
raise ValueError(join(errs, ' | '))
|
|
2144
2362
|
|
|
@@ -2149,11 +2367,11 @@ def validate_STRING(inj, _val=UNDEF, _ref=UNDEF, _store=UNDEF):
|
|
|
2149
2367
|
out = getprop(inj.dparent, inj.key)
|
|
2150
2368
|
t = typify(out)
|
|
2151
2369
|
|
|
2152
|
-
if
|
|
2370
|
+
if (T_string & t) == 0:
|
|
2153
2371
|
inj.errs.append(_invalidTypeMsg(inj.path, S_string, t, out, 'V1010'))
|
|
2154
2372
|
return UNDEF
|
|
2155
2373
|
|
|
2156
|
-
if
|
|
2374
|
+
if out == S_MT:
|
|
2157
2375
|
inj.errs.append('Empty string at ' + pathify(inj.path, 1))
|
|
2158
2376
|
return UNDEF
|
|
2159
2377
|
|
|
@@ -2170,8 +2388,9 @@ TYPE_CHECKS = {
|
|
|
2170
2388
|
S_map: lambda v: isinstance(v, dict),
|
|
2171
2389
|
S_list: lambda v: isinstance(v, list),
|
|
2172
2390
|
S_function: lambda v: callable(v) and not isinstance(v, type),
|
|
2173
|
-
S_instance: lambda v: (
|
|
2174
|
-
|
|
2391
|
+
S_instance: lambda v: (
|
|
2392
|
+
not isinstance(v, (dict, list, str, int, float, bool)) and v is not None and v is not UNDEF
|
|
2393
|
+
),
|
|
2175
2394
|
}
|
|
2176
2395
|
|
|
2177
2396
|
|
|
@@ -2183,7 +2402,7 @@ def validate_TYPE(inj, _val=UNDEF, ref=UNDEF, _store=UNDEF):
|
|
|
2183
2402
|
out = getprop(inj.dparent, inj.key)
|
|
2184
2403
|
t = typify(out)
|
|
2185
2404
|
|
|
2186
|
-
if
|
|
2405
|
+
if (t & typev) == 0:
|
|
2187
2406
|
inj.errs.append(_invalidTypeMsg(inj.path, tname, t, out, 'V1001'))
|
|
2188
2407
|
return UNDEF
|
|
2189
2408
|
|
|
@@ -2202,17 +2421,16 @@ def validate_CHILD(inj, _val=UNDEF, _ref=UNDEF, _store=UNDEF):
|
|
|
2202
2421
|
keys = inj.keys
|
|
2203
2422
|
|
|
2204
2423
|
# Map syntax.
|
|
2205
|
-
if
|
|
2424
|
+
if mode == S_MKEYPRE:
|
|
2206
2425
|
childtm = getprop(parent, key)
|
|
2207
2426
|
|
|
2208
2427
|
pkey = getelem(path, -2)
|
|
2209
2428
|
tval = getprop(inj.dparent, pkey)
|
|
2210
2429
|
|
|
2211
|
-
if
|
|
2430
|
+
if tval == UNDEF:
|
|
2212
2431
|
tval = {}
|
|
2213
2432
|
elif not ismap(tval):
|
|
2214
|
-
inj.errs.append(_invalidTypeMsg(
|
|
2215
|
-
path[:-1], S_object, typify(tval), tval, 'V0220'))
|
|
2433
|
+
inj.errs.append(_invalidTypeMsg(path[:-1], S_object, typify(tval), tval, 'V0220'))
|
|
2216
2434
|
return UNDEF
|
|
2217
2435
|
|
|
2218
2436
|
ckeys = keysof(tval)
|
|
@@ -2220,36 +2438,44 @@ def validate_CHILD(inj, _val=UNDEF, _ref=UNDEF, _store=UNDEF):
|
|
|
2220
2438
|
setprop(parent, ckey, clone(childtm))
|
|
2221
2439
|
keys.append(ckey)
|
|
2222
2440
|
|
|
2223
|
-
inj.
|
|
2441
|
+
delprop(inj.parent, inj.key)
|
|
2224
2442
|
return UNDEF
|
|
2225
2443
|
|
|
2226
2444
|
# List syntax.
|
|
2227
|
-
if
|
|
2228
|
-
|
|
2445
|
+
if mode == S_MVAL:
|
|
2229
2446
|
if not islist(parent):
|
|
2230
2447
|
inj.errs.append('Invalid $CHILD as value')
|
|
2231
2448
|
return UNDEF
|
|
2232
2449
|
|
|
2233
2450
|
childtm = getprop(parent, 1)
|
|
2234
2451
|
|
|
2235
|
-
if
|
|
2452
|
+
if inj.dparent == UNDEF:
|
|
2236
2453
|
del parent[:]
|
|
2237
2454
|
return UNDEF
|
|
2238
2455
|
|
|
2239
2456
|
if not islist(inj.dparent):
|
|
2240
|
-
msg = _invalidTypeMsg(
|
|
2241
|
-
path[:-1], S_list, typify(inj.dparent), inj.dparent, 'V0230')
|
|
2457
|
+
msg = _invalidTypeMsg(path[:-1], S_list, typify(inj.dparent), inj.dparent, 'V0230')
|
|
2242
2458
|
inj.errs.append(msg)
|
|
2243
2459
|
inj.keyI = size(parent)
|
|
2244
2460
|
return inj.dparent
|
|
2245
2461
|
|
|
2246
2462
|
for n in items(inj.dparent):
|
|
2247
2463
|
setprop(parent, n[0], clone(childtm))
|
|
2248
|
-
del parent[len(inj.dparent):]
|
|
2249
|
-
inj.keyI = 0
|
|
2464
|
+
del parent[len(inj.dparent) :]
|
|
2250
2465
|
|
|
2251
|
-
|
|
2252
|
-
|
|
2466
|
+
# NOTE: modifying inj! This extends the child value loop in inject
|
|
2467
|
+
# to cover every cloned child.
|
|
2468
|
+
for ckeyI in range(size(keys), size(parent)):
|
|
2469
|
+
keys.append(strkey(ckeyI))
|
|
2470
|
+
|
|
2471
|
+
# Restart the child value loop at the first element (the loop
|
|
2472
|
+
# increments keyI on resume) so that the first element is also
|
|
2473
|
+
# validated against the child template.
|
|
2474
|
+
inj.keyI = -1
|
|
2475
|
+
|
|
2476
|
+
# SKIP leaves the cloned child template in place at the first
|
|
2477
|
+
# element so the resumed loop can validate it.
|
|
2478
|
+
return SKIP
|
|
2253
2479
|
|
|
2254
2480
|
return UNDEF
|
|
2255
2481
|
|
|
@@ -2259,11 +2485,13 @@ def validate_ONE(inj, _val=UNDEF, _ref=UNDEF, store=UNDEF):
|
|
|
2259
2485
|
parent = inj.parent
|
|
2260
2486
|
keyI = inj.keyI
|
|
2261
2487
|
|
|
2262
|
-
if
|
|
2263
|
-
if not islist(parent) or
|
|
2264
|
-
inj.errs.append(
|
|
2265
|
-
|
|
2266
|
-
|
|
2488
|
+
if mode == S_MVAL:
|
|
2489
|
+
if not islist(parent) or keyI != 0:
|
|
2490
|
+
inj.errs.append(
|
|
2491
|
+
'The $ONE validator at field '
|
|
2492
|
+
+ pathify(inj.path, 1, 1)
|
|
2493
|
+
+ ' must be the first element of an array.'
|
|
2494
|
+
)
|
|
2267
2495
|
return None
|
|
2268
2496
|
|
|
2269
2497
|
inj.keyI = size(inj.keys)
|
|
@@ -2274,10 +2502,12 @@ def validate_ONE(inj, _val=UNDEF, _ref=UNDEF, store=UNDEF):
|
|
|
2274
2502
|
inj.key = getelem(inj.path, -1)
|
|
2275
2503
|
|
|
2276
2504
|
tvals = parent[1:]
|
|
2277
|
-
if
|
|
2278
|
-
inj.errs.append(
|
|
2279
|
-
|
|
2280
|
-
|
|
2505
|
+
if size(tvals) == 0:
|
|
2506
|
+
inj.errs.append(
|
|
2507
|
+
'The $ONE validator at field '
|
|
2508
|
+
+ pathify(inj.path, 1, 1)
|
|
2509
|
+
+ ' must have at least one argument.'
|
|
2510
|
+
)
|
|
2281
2511
|
return None
|
|
2282
2512
|
|
|
2283
2513
|
for tval in tvals:
|
|
@@ -2286,24 +2516,33 @@ def validate_ONE(inj, _val=UNDEF, _ref=UNDEF, store=UNDEF):
|
|
|
2286
2516
|
vstore = merge([{}, store], 1)
|
|
2287
2517
|
vstore[S_DTOP] = inj.dparent
|
|
2288
2518
|
|
|
2289
|
-
vcurrent = validate(
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2519
|
+
vcurrent = validate(
|
|
2520
|
+
inj.dparent,
|
|
2521
|
+
tval,
|
|
2522
|
+
{
|
|
2523
|
+
'extra': vstore,
|
|
2524
|
+
'errs': terrs,
|
|
2525
|
+
'meta': inj.meta,
|
|
2526
|
+
},
|
|
2527
|
+
)
|
|
2294
2528
|
|
|
2295
2529
|
inj.setval(vcurrent, -2)
|
|
2296
2530
|
|
|
2297
|
-
if
|
|
2531
|
+
if size(terrs) == 0:
|
|
2298
2532
|
return None
|
|
2299
2533
|
|
|
2300
2534
|
valdesc = ', '.join(stringify(n[1]) for n in items(tvals))
|
|
2301
2535
|
valdesc = re.sub(r'`\$([A-Z]+)`', lambda m: m.group(1).lower(), valdesc)
|
|
2302
2536
|
|
|
2303
|
-
inj.errs.append(
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2537
|
+
inj.errs.append(
|
|
2538
|
+
_invalidTypeMsg(
|
|
2539
|
+
inj.path,
|
|
2540
|
+
('one of ' if size(tvals) > 1 else '') + valdesc,
|
|
2541
|
+
typify(inj.dparent),
|
|
2542
|
+
inj.dparent,
|
|
2543
|
+
'V0210',
|
|
2544
|
+
)
|
|
2545
|
+
)
|
|
2307
2546
|
|
|
2308
2547
|
|
|
2309
2548
|
def validate_EXACT(inj, _val=UNDEF, _ref=UNDEF, _store=UNDEF):
|
|
@@ -2312,11 +2551,13 @@ def validate_EXACT(inj, _val=UNDEF, _ref=UNDEF, _store=UNDEF):
|
|
|
2312
2551
|
key = inj.key
|
|
2313
2552
|
keyI = inj.keyI
|
|
2314
2553
|
|
|
2315
|
-
if
|
|
2316
|
-
if not islist(parent) or
|
|
2317
|
-
inj.errs.append(
|
|
2318
|
-
|
|
2319
|
-
|
|
2554
|
+
if mode == S_MVAL:
|
|
2555
|
+
if not islist(parent) or keyI != 0:
|
|
2556
|
+
inj.errs.append(
|
|
2557
|
+
'The $EXACT validator at field '
|
|
2558
|
+
+ pathify(inj.path, 1, 1)
|
|
2559
|
+
+ ' must be the first element of an array.'
|
|
2560
|
+
)
|
|
2320
2561
|
return None
|
|
2321
2562
|
|
|
2322
2563
|
inj.keyI = size(inj.keys)
|
|
@@ -2327,10 +2568,12 @@ def validate_EXACT(inj, _val=UNDEF, _ref=UNDEF, _store=UNDEF):
|
|
|
2327
2568
|
inj.key = getelem(inj.path, -1)
|
|
2328
2569
|
|
|
2329
2570
|
tvals = parent[1:]
|
|
2330
|
-
if
|
|
2331
|
-
inj.errs.append(
|
|
2332
|
-
|
|
2333
|
-
|
|
2571
|
+
if size(tvals) == 0:
|
|
2572
|
+
inj.errs.append(
|
|
2573
|
+
'The $EXACT validator at field '
|
|
2574
|
+
+ pathify(inj.path, 1, 1)
|
|
2575
|
+
+ ' must have at least one argument.'
|
|
2576
|
+
)
|
|
2334
2577
|
return None
|
|
2335
2578
|
|
|
2336
2579
|
currentstr = None
|
|
@@ -2348,22 +2591,24 @@ def validate_EXACT(inj, _val=UNDEF, _ref=UNDEF, _store=UNDEF):
|
|
|
2348
2591
|
valdesc = ', '.join(stringify(n[1]) for n in items(tvals))
|
|
2349
2592
|
valdesc = re.sub(r'`\$([A-Z]+)`', lambda m: m.group(1).lower(), valdesc)
|
|
2350
2593
|
|
|
2351
|
-
inj.errs.append(
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2594
|
+
inj.errs.append(
|
|
2595
|
+
_invalidTypeMsg(
|
|
2596
|
+
inj.path,
|
|
2597
|
+
('' if size(inj.path) > 1 else 'value ')
|
|
2598
|
+
+ 'exactly equal to '
|
|
2599
|
+
+ ('' if size(tvals) == 1 else 'one of ')
|
|
2600
|
+
+ valdesc,
|
|
2601
|
+
typify(inj.dparent),
|
|
2602
|
+
inj.dparent,
|
|
2603
|
+
'V0110',
|
|
2604
|
+
)
|
|
2605
|
+
)
|
|
2356
2606
|
else:
|
|
2357
2607
|
delprop(parent, key)
|
|
2358
2608
|
|
|
2359
|
-
|
|
2360
|
-
def _validation(
|
|
2361
|
-
|
|
2362
|
-
key,
|
|
2363
|
-
parent,
|
|
2364
|
-
inj
|
|
2365
|
-
):
|
|
2366
|
-
if UNDEF == inj:
|
|
2609
|
+
|
|
2610
|
+
def _validation(pval, key, parent, inj):
|
|
2611
|
+
if inj == UNDEF:
|
|
2367
2612
|
return
|
|
2368
2613
|
|
|
2369
2614
|
if pval == SKIP:
|
|
@@ -2375,17 +2620,17 @@ def _validation(
|
|
|
2375
2620
|
# Current val to verify.
|
|
2376
2621
|
cval = getprop(inj.dparent, key)
|
|
2377
2622
|
|
|
2378
|
-
if
|
|
2623
|
+
if inj == UNDEF or (not exact and cval == UNDEF):
|
|
2379
2624
|
return
|
|
2380
2625
|
|
|
2381
2626
|
ptype = typify(pval)
|
|
2382
2627
|
|
|
2383
|
-
if
|
|
2628
|
+
if (T_string & ptype) > 0 and S_DS in str(pval):
|
|
2384
2629
|
return
|
|
2385
2630
|
|
|
2386
2631
|
ctype = typify(cval)
|
|
2387
2632
|
|
|
2388
|
-
if ptype != ctype and
|
|
2633
|
+
if ptype != ctype and pval != UNDEF:
|
|
2389
2634
|
inj.errs.append(_invalidTypeMsg(inj.path, typename(ptype), ctype, cval, 'V0010'))
|
|
2390
2635
|
return
|
|
2391
2636
|
|
|
@@ -2398,13 +2643,20 @@ def _validation(
|
|
|
2398
2643
|
pkeys = keysof(pval)
|
|
2399
2644
|
|
|
2400
2645
|
# Empty spec object {} means object can be open (any keys).
|
|
2401
|
-
if
|
|
2646
|
+
if len(pkeys) > 0 and getprop(pval, '`$OPEN`') is not True:
|
|
2402
2647
|
badkeys = []
|
|
2403
2648
|
for ckey in ckeys:
|
|
2649
|
+
# Literal presence: the shape DECLARES ckey even when its value
|
|
2650
|
+
# is null. Canonical uses `NONE === _lookup(pval, ckey)`; here
|
|
2651
|
+
# `haskey` is Group A (value-based) and would wrongly flag a
|
|
2652
|
+
# null-valued shape slot as unexpected — dropping records with a
|
|
2653
|
+
# null field from an open ($AND) select. Test map presence.
|
|
2404
2654
|
if str(ckey) not in pval:
|
|
2405
2655
|
badkeys.append(ckey)
|
|
2406
|
-
if
|
|
2407
|
-
msg =
|
|
2656
|
+
if size(badkeys) > 0:
|
|
2657
|
+
msg = (
|
|
2658
|
+
'Unexpected keys at field ' + pathify(inj.path, 1) + S_VIZ + join(badkeys, ', ')
|
|
2659
|
+
)
|
|
2408
2660
|
inj.errs.append(msg)
|
|
2409
2661
|
else:
|
|
2410
2662
|
# Object is open, so merge in extra keys.
|
|
@@ -2418,9 +2670,8 @@ def _validation(
|
|
|
2418
2670
|
|
|
2419
2671
|
elif exact:
|
|
2420
2672
|
if cval != pval:
|
|
2421
|
-
pathmsg = 'at field ' + pathify(inj.path, 1) + ': ' if
|
|
2422
|
-
inj.errs.append('Value ' + pathmsg + str(cval) +
|
|
2423
|
-
' should equal ' + str(pval) + '.')
|
|
2673
|
+
pathmsg = 'at field ' + pathify(inj.path, 1) + ': ' if size(inj.path) > 1 else ''
|
|
2674
|
+
inj.errs.append('Value ' + pathmsg + str(cval) + ' should equal ' + str(pval) + '.')
|
|
2424
2675
|
|
|
2425
2676
|
else:
|
|
2426
2677
|
# Spec value was a default, copy over data
|
|
@@ -2444,80 +2695,84 @@ def validate(data, spec, injdef=UNDEF):
|
|
|
2444
2695
|
|
|
2445
2696
|
collect = getprop(injdef, 'errs') is not None and getprop(injdef, 'errs') is not UNDEF
|
|
2446
2697
|
errs = getprop(injdef, 'errs') if collect else []
|
|
2447
|
-
|
|
2448
|
-
store = merge([
|
|
2449
|
-
{
|
|
2450
|
-
"$DELETE": None,
|
|
2451
|
-
"$COPY": None,
|
|
2452
|
-
"$KEY": None,
|
|
2453
|
-
"$META": None,
|
|
2454
|
-
"$MERGE": None,
|
|
2455
|
-
"$EACH": None,
|
|
2456
|
-
"$PACK": None,
|
|
2457
|
-
|
|
2458
|
-
"$STRING": validate_STRING,
|
|
2459
|
-
"$NUMBER": validate_TYPE,
|
|
2460
|
-
"$INTEGER": validate_TYPE,
|
|
2461
|
-
"$DECIMAL": validate_TYPE,
|
|
2462
|
-
"$BOOLEAN": validate_TYPE,
|
|
2463
|
-
"$NULL": validate_TYPE,
|
|
2464
|
-
"$NIL": validate_TYPE,
|
|
2465
|
-
"$MAP": validate_TYPE,
|
|
2466
|
-
"$LIST": validate_TYPE,
|
|
2467
|
-
"$FUNCTION": validate_TYPE,
|
|
2468
|
-
"$INSTANCE": validate_TYPE,
|
|
2469
|
-
"$ANY": validate_ANY,
|
|
2470
|
-
"$CHILD": validate_CHILD,
|
|
2471
|
-
"$ONE": validate_ONE,
|
|
2472
|
-
"$EXACT": validate_EXACT,
|
|
2473
|
-
},
|
|
2474
2698
|
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2699
|
+
store = merge(
|
|
2700
|
+
[
|
|
2701
|
+
{
|
|
2702
|
+
'$DELETE': None,
|
|
2703
|
+
'$COPY': None,
|
|
2704
|
+
'$KEY': None,
|
|
2705
|
+
'$META': None,
|
|
2706
|
+
'$MERGE': None,
|
|
2707
|
+
'$EACH': None,
|
|
2708
|
+
'$PACK': None,
|
|
2709
|
+
'$STRING': validate_STRING,
|
|
2710
|
+
'$NUMBER': validate_TYPE,
|
|
2711
|
+
'$INTEGER': validate_TYPE,
|
|
2712
|
+
'$DECIMAL': validate_TYPE,
|
|
2713
|
+
'$BOOLEAN': validate_TYPE,
|
|
2714
|
+
'$NULL': validate_TYPE,
|
|
2715
|
+
'$NIL': validate_TYPE,
|
|
2716
|
+
'$MAP': validate_TYPE,
|
|
2717
|
+
'$LIST': validate_TYPE,
|
|
2718
|
+
'$FUNCTION': validate_TYPE,
|
|
2719
|
+
'$INSTANCE': validate_TYPE,
|
|
2720
|
+
'$ANY': validate_ANY,
|
|
2721
|
+
'$CHILD': validate_CHILD,
|
|
2722
|
+
'$ONE': validate_ONE,
|
|
2723
|
+
'$EXACT': validate_EXACT,
|
|
2724
|
+
},
|
|
2725
|
+
({} if extra is UNDEF or extra is None else extra),
|
|
2726
|
+
{
|
|
2727
|
+
'$ERRS': errs,
|
|
2728
|
+
},
|
|
2729
|
+
],
|
|
2730
|
+
1,
|
|
2731
|
+
)
|
|
2481
2732
|
|
|
2482
2733
|
meta = getprop(injdef, 'meta', {})
|
|
2483
2734
|
setprop(meta, S_BEXACT, getprop(meta, S_BEXACT, False))
|
|
2484
2735
|
|
|
2485
|
-
out = transform(
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2736
|
+
out = transform(
|
|
2737
|
+
data,
|
|
2738
|
+
spec,
|
|
2739
|
+
{
|
|
2740
|
+
'meta': meta,
|
|
2741
|
+
'extra': store,
|
|
2742
|
+
'modify': _validation,
|
|
2743
|
+
'handler': _validatehandler,
|
|
2744
|
+
'errs': errs,
|
|
2745
|
+
},
|
|
2746
|
+
)
|
|
2492
2747
|
|
|
2493
|
-
generr =
|
|
2748
|
+
generr = len(errs) > 0 and not collect
|
|
2494
2749
|
if generr:
|
|
2495
2750
|
raise ValueError(' | '.join(errs))
|
|
2496
2751
|
|
|
2497
2752
|
return out
|
|
2498
2753
|
|
|
2499
2754
|
|
|
2500
|
-
|
|
2501
2755
|
# Internal utilities
|
|
2502
2756
|
# ==================
|
|
2503
2757
|
|
|
2758
|
+
|
|
2504
2759
|
def _validatehandler(inj, val, ref, store):
|
|
2505
2760
|
out = val
|
|
2506
|
-
|
|
2761
|
+
|
|
2507
2762
|
m = R_META_PATH.match(ref) if ref else None
|
|
2508
2763
|
ismetapath = m is not None
|
|
2509
|
-
|
|
2764
|
+
|
|
2510
2765
|
if ismetapath:
|
|
2511
2766
|
if m.group(2) == '=':
|
|
2512
2767
|
inj.setval([S_BEXACT, val])
|
|
2513
2768
|
else:
|
|
2514
2769
|
inj.setval(val)
|
|
2515
2770
|
inj.keyI = -1
|
|
2516
|
-
|
|
2771
|
+
|
|
2517
2772
|
out = SKIP
|
|
2518
2773
|
else:
|
|
2519
2774
|
out = _injecthandler(inj, val, ref, store)
|
|
2520
|
-
|
|
2775
|
+
|
|
2521
2776
|
return out
|
|
2522
2777
|
|
|
2523
2778
|
|
|
@@ -2525,8 +2780,8 @@ def _validatehandler(inj, val, ref, store):
|
|
|
2525
2780
|
# when needed by implementation language.
|
|
2526
2781
|
def _setparentprop(state, val):
|
|
2527
2782
|
setprop(state.parent, state.key, val)
|
|
2528
|
-
|
|
2529
|
-
|
|
2783
|
+
|
|
2784
|
+
|
|
2530
2785
|
# Update all references to target in state.nodes.
|
|
2531
2786
|
def _updateAncestors(_state, target, tkey, tval):
|
|
2532
2787
|
# SetProp is sufficient in Python as target reference remains consistent even for lists.
|
|
@@ -2547,53 +2802,52 @@ def _injectstr(val, store, inj=UNDEF):
|
|
|
2547
2802
|
full_re = re.compile(r'^`(\$[A-Z]+|[^`]*)[0-9]*`$')
|
|
2548
2803
|
part_re = re.compile(r'`([^`]*)`')
|
|
2549
2804
|
|
|
2550
|
-
if not isinstance(val, str) or
|
|
2805
|
+
if not isinstance(val, str) or val == S_MT:
|
|
2551
2806
|
return S_MT
|
|
2552
2807
|
|
|
2553
2808
|
out = val
|
|
2554
|
-
|
|
2809
|
+
|
|
2555
2810
|
# Pattern examples: "`a.b.c`", "`$NAME`", "`$NAME1`"
|
|
2556
2811
|
m = full_re.match(val)
|
|
2557
|
-
|
|
2812
|
+
|
|
2558
2813
|
# Full string of the val is an injection.
|
|
2559
2814
|
if m:
|
|
2560
|
-
if
|
|
2815
|
+
if inj != UNDEF:
|
|
2561
2816
|
inj.full = True
|
|
2562
2817
|
|
|
2563
2818
|
pathref = m.group(1)
|
|
2564
2819
|
|
|
2565
2820
|
# Special escapes inside injection.
|
|
2566
|
-
if
|
|
2821
|
+
if len(pathref) > 3:
|
|
2567
2822
|
pathref = pathref.replace(r'$BT', S_BT).replace(r'$DS', S_DS)
|
|
2568
2823
|
|
|
2569
2824
|
# Get the extracted path reference.
|
|
2570
2825
|
out = getpath(store, pathref, inj)
|
|
2571
2826
|
|
|
2572
2827
|
else:
|
|
2573
|
-
|
|
2574
2828
|
# Check for injections within the string.
|
|
2575
2829
|
def partial(mobj):
|
|
2576
2830
|
ref = mobj.group(1)
|
|
2577
2831
|
|
|
2578
2832
|
# Special escapes inside injection.
|
|
2579
|
-
if
|
|
2833
|
+
if len(ref) > 3:
|
|
2580
2834
|
ref = ref.replace(r'$BT', S_BT).replace(r'$DS', S_DS)
|
|
2581
|
-
|
|
2582
|
-
if
|
|
2835
|
+
|
|
2836
|
+
if inj != UNDEF:
|
|
2583
2837
|
inj.full = False
|
|
2584
2838
|
|
|
2585
2839
|
found = getpath(store, ref, inj)
|
|
2586
|
-
|
|
2840
|
+
|
|
2587
2841
|
# Ensure inject value is a string.
|
|
2588
|
-
if
|
|
2842
|
+
if found == UNDEF:
|
|
2589
2843
|
return S_MT
|
|
2590
|
-
|
|
2844
|
+
|
|
2591
2845
|
if isinstance(found, str):
|
|
2592
2846
|
# Convert test NULL marker to JSON 'null' when injecting into strings
|
|
2593
2847
|
if found == '__NULL__':
|
|
2594
2848
|
return 'null'
|
|
2595
2849
|
return found
|
|
2596
|
-
|
|
2850
|
+
|
|
2597
2851
|
if isfunc(found):
|
|
2598
2852
|
return found
|
|
2599
2853
|
|
|
@@ -2606,7 +2860,7 @@ def _injectstr(val, store, inj=UNDEF):
|
|
|
2606
2860
|
|
|
2607
2861
|
# Also call the inj handler on the entire string, providing the
|
|
2608
2862
|
# option for custom injection.
|
|
2609
|
-
if
|
|
2863
|
+
if inj != UNDEF and isfunc(inj.handler):
|
|
2610
2864
|
inj.full = True
|
|
2611
2865
|
out = inj.handler(inj, out, val, store)
|
|
2612
2866
|
|
|
@@ -2616,11 +2870,13 @@ def _injectstr(val, store, inj=UNDEF):
|
|
|
2616
2870
|
def _invalidTypeMsg(path, needtype, vt, v, _whence=None):
|
|
2617
2871
|
vs = 'no value' if v is None or v is UNDEF else stringify(v)
|
|
2618
2872
|
return (
|
|
2619
|
-
'Expected '
|
|
2620
|
-
('field ' + pathify(path, 1) + ' to be ' if
|
|
2621
|
-
str(needtype)
|
|
2622
|
-
|
|
2623
|
-
'
|
|
2873
|
+
'Expected '
|
|
2874
|
+
+ ('field ' + pathify(path, 1) + ' to be ' if size(path) > 1 else '')
|
|
2875
|
+
+ str(needtype)
|
|
2876
|
+
+ ', but found '
|
|
2877
|
+
+ (typename(vt) + S_VIZ if v is not None and v is not UNDEF else '')
|
|
2878
|
+
+ vs
|
|
2879
|
+
+ '.'
|
|
2624
2880
|
)
|
|
2625
2881
|
|
|
2626
2882
|
|
|
@@ -2648,8 +2904,6 @@ class StructUtility:
|
|
|
2648
2904
|
self.items = items
|
|
2649
2905
|
self.jm = jm
|
|
2650
2906
|
self.jt = jt
|
|
2651
|
-
self.jo = jo
|
|
2652
|
-
self.ja = ja
|
|
2653
2907
|
self.join = join
|
|
2654
2908
|
self.joinurl = joinurl
|
|
2655
2909
|
self.jsonify = jsonify
|
|
@@ -2694,11 +2948,32 @@ class StructUtility:
|
|
|
2694
2948
|
self.checkPlacement = checkPlacement
|
|
2695
2949
|
self.injectorArgs = injectorArgs
|
|
2696
2950
|
self.injectChild = injectChild
|
|
2697
|
-
|
|
2951
|
+
|
|
2698
2952
|
|
|
2699
2953
|
__all__ = [
|
|
2954
|
+
'DELETE',
|
|
2955
|
+
'MODENAME',
|
|
2956
|
+
'M_KEYPOST',
|
|
2957
|
+
'M_KEYPRE',
|
|
2958
|
+
'M_VAL',
|
|
2959
|
+
'SKIP',
|
|
2700
2960
|
'Injection',
|
|
2701
2961
|
'StructUtility',
|
|
2962
|
+
'T_any',
|
|
2963
|
+
'T_boolean',
|
|
2964
|
+
'T_decimal',
|
|
2965
|
+
'T_function',
|
|
2966
|
+
'T_instance',
|
|
2967
|
+
'T_integer',
|
|
2968
|
+
'T_list',
|
|
2969
|
+
'T_map',
|
|
2970
|
+
'T_node',
|
|
2971
|
+
'T_noval',
|
|
2972
|
+
'T_null',
|
|
2973
|
+
'T_number',
|
|
2974
|
+
'T_scalar',
|
|
2975
|
+
'T_string',
|
|
2976
|
+
'T_symbol',
|
|
2702
2977
|
'checkPlacement',
|
|
2703
2978
|
'clone',
|
|
2704
2979
|
'delprop',
|
|
@@ -2721,9 +2996,7 @@ __all__ = [
|
|
|
2721
2996
|
'ismap',
|
|
2722
2997
|
'isnode',
|
|
2723
2998
|
'items',
|
|
2724
|
-
'ja',
|
|
2725
2999
|
'jm',
|
|
2726
|
-
'jo',
|
|
2727
3000
|
'join',
|
|
2728
3001
|
'joinurl',
|
|
2729
3002
|
'jsonify',
|
|
@@ -2745,26 +3018,4 @@ __all__ = [
|
|
|
2745
3018
|
'typify',
|
|
2746
3019
|
'validate',
|
|
2747
3020
|
'walk',
|
|
2748
|
-
'SKIP',
|
|
2749
|
-
'DELETE',
|
|
2750
|
-
'T_any',
|
|
2751
|
-
'T_noval',
|
|
2752
|
-
'T_boolean',
|
|
2753
|
-
'T_decimal',
|
|
2754
|
-
'T_integer',
|
|
2755
|
-
'T_number',
|
|
2756
|
-
'T_string',
|
|
2757
|
-
'T_function',
|
|
2758
|
-
'T_symbol',
|
|
2759
|
-
'T_null',
|
|
2760
|
-
'T_list',
|
|
2761
|
-
'T_map',
|
|
2762
|
-
'T_instance',
|
|
2763
|
-
'T_scalar',
|
|
2764
|
-
'T_node',
|
|
2765
|
-
'M_KEYPRE',
|
|
2766
|
-
'M_KEYPOST',
|
|
2767
|
-
'M_VAL',
|
|
2768
|
-
'MODENAME',
|
|
2769
3021
|
]
|
|
2770
|
-
|