@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,7 +1,30 @@
|
|
|
1
|
+
-- VENDORED: @voxgig/struct 0.1.1 (lua/src/struct.lua)
|
|
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-2026 Voxgig Ltd. MIT LICENSE.
|
|
2
5
|
|
|
3
6
|
-- VERSION: @voxgig/struct 0.0.10
|
|
4
7
|
|
|
8
|
+
-- RE2-subset regex engine — pure Lua, no external deps. Lua's built-in
|
|
9
|
+
-- string.match uses Lua patterns (not regex), so for cross-port uniformity
|
|
10
|
+
-- we use this engine. See /REGEX_API.md.
|
|
11
|
+
-- Installed (namespaced) layout: voxgig/struct.lua + voxgig/struct/regex.lua.
|
|
12
|
+
local _regex_ok, _regex = pcall(require, "utility.struct.regex")
|
|
13
|
+
if not _regex_ok then
|
|
14
|
+
-- Flat dev layout / the regex-only specs, which put src/ on package.path.
|
|
15
|
+
_regex_ok, _regex = pcall(require, "regex")
|
|
16
|
+
end
|
|
17
|
+
if not _regex_ok then
|
|
18
|
+
-- When loaded from a sibling test file with package.path tweaked.
|
|
19
|
+
_regex_ok, _regex = pcall(dofile, (debug.getinfo(1, "S").source:sub(2):gsub("struct%.lua$", "regex.lua")))
|
|
20
|
+
end
|
|
21
|
+
local re_compile = _regex and _regex.re_compile or nil
|
|
22
|
+
local re_test = _regex and _regex.re_test or nil
|
|
23
|
+
local re_find = _regex and _regex.re_find or nil
|
|
24
|
+
local re_find_all = _regex and _regex.re_find_all or nil
|
|
25
|
+
local re_replace = _regex and _regex.re_replace or nil
|
|
26
|
+
local re_escape = _regex and _regex.re_escape or nil
|
|
27
|
+
|
|
5
28
|
--[[
|
|
6
29
|
Voxgig Struct
|
|
7
30
|
=============
|
|
@@ -51,7 +74,8 @@
|
|
|
51
74
|
to represent JSON null, if this ambiguity creates issues
|
|
52
75
|
(thankfully in most APIs, JSON nulls are not used). For example,
|
|
53
76
|
the unit tests use the string "__NULL__" where necessary.
|
|
54
|
-
]]
|
|
77
|
+
]]
|
|
78
|
+
----------------------------------------------------------
|
|
55
79
|
-- String constants are explicitly defined.
|
|
56
80
|
----------------------------------------------------------
|
|
57
81
|
|
|
@@ -61,61 +85,66 @@ local M_KEYPOST = 2
|
|
|
61
85
|
local M_VAL = 4
|
|
62
86
|
|
|
63
87
|
local MODENAME = {
|
|
64
|
-
[M_VAL] =
|
|
65
|
-
[M_KEYPRE] =
|
|
66
|
-
[M_KEYPOST] =
|
|
88
|
+
[M_VAL] = "val",
|
|
89
|
+
[M_KEYPRE] = "key:pre",
|
|
90
|
+
[M_KEYPOST] = "key:post",
|
|
67
91
|
}
|
|
68
92
|
|
|
69
93
|
-- Special strings.
|
|
70
|
-
local S_BKEY =
|
|
71
|
-
local S_BANNO =
|
|
72
|
-
local S_BEXACT =
|
|
73
|
-
local S_BVAL =
|
|
94
|
+
local S_BKEY = "`$KEY`"
|
|
95
|
+
local S_BANNO = "`$ANNO`"
|
|
96
|
+
local S_BEXACT = "`$EXACT`"
|
|
97
|
+
local S_BVAL = "`$VAL`"
|
|
74
98
|
|
|
75
|
-
local S_DKEY =
|
|
76
|
-
local S_DTOP =
|
|
77
|
-
local S_DERRS =
|
|
78
|
-
local S_DSPEC =
|
|
99
|
+
local S_DKEY = "$KEY"
|
|
100
|
+
local S_DTOP = "$TOP"
|
|
101
|
+
local S_DERRS = "$ERRS"
|
|
102
|
+
local S_DSPEC = "$SPEC"
|
|
79
103
|
|
|
80
104
|
-- General strings.
|
|
81
|
-
local S_list =
|
|
82
|
-
local S_base =
|
|
83
|
-
local S_boolean =
|
|
84
|
-
local S_function =
|
|
85
|
-
local S_symbol =
|
|
86
|
-
local S_instance =
|
|
87
|
-
local S_key =
|
|
88
|
-
local S_any =
|
|
89
|
-
local S_nil =
|
|
90
|
-
local S_null =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
local
|
|
97
|
-
local
|
|
98
|
-
local
|
|
105
|
+
local S_list = "list"
|
|
106
|
+
local S_base = "base"
|
|
107
|
+
local S_boolean = "boolean"
|
|
108
|
+
local S_function = "function"
|
|
109
|
+
local S_symbol = "symbol"
|
|
110
|
+
local S_instance = "instance"
|
|
111
|
+
local S_key = "key"
|
|
112
|
+
local S_any = "any"
|
|
113
|
+
local S_nil = "nil"
|
|
114
|
+
local S_null = "null"
|
|
115
|
+
|
|
116
|
+
-- JSON null sentinel value. Lua has no value distinct from nil for JSON
|
|
117
|
+
-- null, so (as the file header notes) the string "__NULL__" carries a JSON
|
|
118
|
+
-- null through code and the test harness. This is distinct from nil, which
|
|
119
|
+
-- represents an absent/undefined value (the canonical TS `undefined`/NONE).
|
|
120
|
+
local NULLMARK = "__NULL__"
|
|
121
|
+
local S_number = "number"
|
|
122
|
+
local S_object = "object"
|
|
123
|
+
local S_string = "string"
|
|
124
|
+
local S_decimal = "decimal"
|
|
125
|
+
local S_integer = "integer"
|
|
126
|
+
local S_map = "map"
|
|
127
|
+
local S_scalar = "scalar"
|
|
128
|
+
local S_node = "node"
|
|
99
129
|
|
|
100
130
|
-- Character strings.
|
|
101
|
-
local S_BT =
|
|
102
|
-
local S_CN =
|
|
103
|
-
local S_CS =
|
|
104
|
-
local S_DS =
|
|
105
|
-
local S_DT =
|
|
106
|
-
local S_FS =
|
|
107
|
-
local S_KEY =
|
|
108
|
-
local S_MT =
|
|
109
|
-
local S_OS =
|
|
110
|
-
local S_SP =
|
|
111
|
-
local S_CM =
|
|
112
|
-
local S_VIZ =
|
|
113
|
-
|
|
131
|
+
local S_BT = "`"
|
|
132
|
+
local S_CN = ":"
|
|
133
|
+
local S_CS = "]"
|
|
134
|
+
local S_DS = "$"
|
|
135
|
+
local S_DT = "."
|
|
136
|
+
local S_FS = "/"
|
|
137
|
+
local S_KEY = "KEY"
|
|
138
|
+
local S_MT = ""
|
|
139
|
+
local S_OS = "["
|
|
140
|
+
local S_SP = " "
|
|
141
|
+
local S_CM = ","
|
|
142
|
+
local S_VIZ = ": "
|
|
114
143
|
|
|
115
144
|
-- Types (bit flags)
|
|
116
145
|
-- Using explicit bit positions to match TS implementation
|
|
117
|
-
local T_any = (1 << 31) - 1
|
|
118
|
-
local T_noval = 1 << 30
|
|
146
|
+
local T_any = (1 << 31) - 1 -- All bits set
|
|
147
|
+
local T_noval = 1 << 30 -- Property absent, undefined
|
|
119
148
|
local T_boolean = 1 << 29
|
|
120
149
|
local T_decimal = 1 << 28
|
|
121
150
|
local T_integer = 1 << 27
|
|
@@ -123,7 +152,7 @@ local T_number = 1 << 26
|
|
|
123
152
|
local T_string = 1 << 25
|
|
124
153
|
local T_function = 1 << 24
|
|
125
154
|
local T_symbol = 1 << 23
|
|
126
|
-
local T_null = 1 << 22
|
|
155
|
+
local T_null = 1 << 22 -- Actual JSON null value
|
|
127
156
|
-- gap of 7
|
|
128
157
|
local T_list = 1 << 14
|
|
129
158
|
local T_map = 1 << 13
|
|
@@ -143,23 +172,57 @@ local TYPENAME = {
|
|
|
143
172
|
S_function,
|
|
144
173
|
S_symbol,
|
|
145
174
|
S_null,
|
|
146
|
-
|
|
147
|
-
|
|
175
|
+
"",
|
|
176
|
+
"",
|
|
177
|
+
"",
|
|
178
|
+
"",
|
|
179
|
+
"",
|
|
180
|
+
"",
|
|
181
|
+
"",
|
|
148
182
|
S_list,
|
|
149
183
|
S_map,
|
|
150
184
|
S_instance,
|
|
151
|
-
|
|
185
|
+
"",
|
|
186
|
+
"",
|
|
187
|
+
"",
|
|
188
|
+
"",
|
|
152
189
|
S_scalar,
|
|
153
190
|
S_node,
|
|
154
191
|
}
|
|
155
192
|
|
|
156
|
-
|
|
157
193
|
-- The standard undefined value for this language.
|
|
158
194
|
local NONE = nil
|
|
159
195
|
|
|
160
196
|
-- Private markers
|
|
161
|
-
local SKIP = { [
|
|
162
|
-
local DELETE = { [
|
|
197
|
+
local SKIP = { ["`$SKIP`"] = true }
|
|
198
|
+
local DELETE = { ["`$DELETE`"] = true }
|
|
199
|
+
|
|
200
|
+
-- The no-value: absent, as distinct from a JSON null.
|
|
201
|
+
--
|
|
202
|
+
-- Lua has one `nil` and the corpus needs two states, so this port models the
|
|
203
|
+
-- second explicitly - the same move struct/go makes with its `NOVAL`
|
|
204
|
+
-- sentinel. `typify()` and `typify(null)` are different results the corpus
|
|
205
|
+
-- pins as different (1073741824 against 4194432), and without a sentinel a
|
|
206
|
+
-- Lua port simply cannot express the first.
|
|
207
|
+
--
|
|
208
|
+
-- It is recognised AHEAD of the normal dispatch in `typify`, which is the
|
|
209
|
+
-- whole trick: a Lua sentinel is a table, so the ordinary path would class it
|
|
210
|
+
-- as a map. Everywhere else it collapses to `nil` via `denoval`, because
|
|
211
|
+
-- canonical distinguishes undefined from null in `typify` and nowhere else
|
|
212
|
+
-- (`null == val` is true for both in JavaScript).
|
|
213
|
+
local NOVAL = setmetatable({}, {
|
|
214
|
+
__tostring = function()
|
|
215
|
+
return "NOVAL"
|
|
216
|
+
end,
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
-- Collapse the no-value to nil for every function that must not see it.
|
|
220
|
+
local function denoval(val)
|
|
221
|
+
if NOVAL == val then
|
|
222
|
+
return nil
|
|
223
|
+
end
|
|
224
|
+
return val
|
|
225
|
+
end
|
|
163
226
|
|
|
164
227
|
local MAXDEPTH = 32
|
|
165
228
|
|
|
@@ -171,6 +234,7 @@ local _injecthandler
|
|
|
171
234
|
local _validatehandler
|
|
172
235
|
local _invalidTypeMsg
|
|
173
236
|
local _validation
|
|
237
|
+
local stringify
|
|
174
238
|
local ismap
|
|
175
239
|
local islist
|
|
176
240
|
local getpath
|
|
@@ -179,31 +243,47 @@ local delprop
|
|
|
179
243
|
local checkPlacement
|
|
180
244
|
local injectorArgs
|
|
181
245
|
|
|
182
|
-
|
|
183
246
|
-- Return type string for narrowest type.
|
|
184
247
|
local function typename(t)
|
|
185
248
|
-- Math.clz32 equivalent: count leading zeros in a 32-bit integer
|
|
186
249
|
local function clz32(x)
|
|
187
|
-
if x == 0 then
|
|
250
|
+
if x == 0 then
|
|
251
|
+
return 32
|
|
252
|
+
end
|
|
188
253
|
local n = 0
|
|
189
|
-
if (x & 0xFFFF0000) == 0 then
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (x &
|
|
254
|
+
if (x & 0xFFFF0000) == 0 then
|
|
255
|
+
n = n + 16
|
|
256
|
+
x = x << 16
|
|
257
|
+
end
|
|
258
|
+
if (x & 0xFF000000) == 0 then
|
|
259
|
+
n = n + 8
|
|
260
|
+
x = x << 8
|
|
261
|
+
end
|
|
262
|
+
if (x & 0xF0000000) == 0 then
|
|
263
|
+
n = n + 4
|
|
264
|
+
x = x << 4
|
|
265
|
+
end
|
|
266
|
+
if (x & 0xC0000000) == 0 then
|
|
267
|
+
n = n + 2
|
|
268
|
+
x = x << 2
|
|
269
|
+
end
|
|
270
|
+
if (x & 0x80000000) == 0 then
|
|
271
|
+
n = n + 1
|
|
272
|
+
end
|
|
194
273
|
return n
|
|
195
274
|
end
|
|
196
|
-
local idx = clz32(t) + 1
|
|
275
|
+
local idx = clz32(t) + 1 -- 1-based index
|
|
197
276
|
if idx >= 1 and idx <= #TYPENAME then
|
|
198
277
|
return TYPENAME[idx]
|
|
199
278
|
end
|
|
200
|
-
return TYPENAME[1]
|
|
279
|
+
return TYPENAME[1] -- S_any
|
|
201
280
|
end
|
|
202
281
|
|
|
203
282
|
-- Value is a node - defined, and a map (hash) or list (array).
|
|
204
283
|
-- @param val (any) The value to check
|
|
205
284
|
-- @return (boolean) True if value is a node
|
|
206
285
|
local function isnode(val)
|
|
286
|
+
val = denoval(val)
|
|
207
287
|
if val == nil then
|
|
208
288
|
return false
|
|
209
289
|
end
|
|
@@ -211,14 +291,13 @@ local function isnode(val)
|
|
|
211
291
|
return ismap(val) or islist(val)
|
|
212
292
|
end
|
|
213
293
|
|
|
214
|
-
|
|
215
294
|
-- Value is a defined map (hash) with string keys.
|
|
216
295
|
-- @param val (any) The value to check
|
|
217
296
|
-- @return (boolean) True if value is a map
|
|
218
297
|
ismap = function(val)
|
|
298
|
+
val = denoval(val)
|
|
219
299
|
-- Check if the value is a table
|
|
220
|
-
if type(val) ~= "table" or
|
|
221
|
-
(getmetatable(val) and getmetatable(val).__jsontype == "array") then
|
|
300
|
+
if type(val) ~= "table" or (getmetatable(val) and getmetatable(val).__jsontype == "array") then
|
|
222
301
|
return false
|
|
223
302
|
end
|
|
224
303
|
|
|
@@ -237,21 +316,24 @@ ismap = function(val)
|
|
|
237
316
|
return true
|
|
238
317
|
end
|
|
239
318
|
|
|
240
|
-
|
|
241
319
|
-- Value is a defined list (array) with integer keys (indexes).
|
|
242
320
|
-- @param val (any) The value to check
|
|
243
321
|
-- @return (boolean) True if value is a list
|
|
244
322
|
islist = function(val)
|
|
323
|
+
val = denoval(val)
|
|
245
324
|
-- First check metatable indicators (preferred approach)
|
|
246
|
-
if
|
|
247
|
-
|
|
248
|
-
|
|
325
|
+
if
|
|
326
|
+
getmetatable(val)
|
|
327
|
+
and (
|
|
328
|
+
(getmetatable(val).__jsontype == "array")
|
|
329
|
+
or (getmetatable(val).__jsontype and getmetatable(val).__jsontype.type == "array")
|
|
330
|
+
)
|
|
331
|
+
then
|
|
249
332
|
return true
|
|
250
333
|
end
|
|
251
334
|
|
|
252
335
|
-- Check if it's a table
|
|
253
|
-
if type(val) ~= "table" or
|
|
254
|
-
(getmetatable(val) and getmetatable(val).__jsontype == "object") then
|
|
336
|
+
if type(val) ~= "table" or (getmetatable(val) and getmetatable(val).__jsontype == "object") then
|
|
255
337
|
return false
|
|
256
338
|
end
|
|
257
339
|
|
|
@@ -271,17 +353,15 @@ islist = function(val)
|
|
|
271
353
|
return count > 0 and max == count
|
|
272
354
|
end
|
|
273
355
|
|
|
274
|
-
|
|
275
356
|
-- Value is a defined string (non-empty) or integer key.
|
|
276
357
|
-- @param key (any) The key to check
|
|
277
358
|
-- @return (boolean) True if key is valid
|
|
278
359
|
local function iskey(key)
|
|
360
|
+
key = denoval(key)
|
|
279
361
|
local keytype = type(key)
|
|
280
|
-
return (keytype == S_string and key ~= S_MT and key ~=
|
|
281
|
-
S_number
|
|
362
|
+
return (keytype == S_string and key ~= S_MT and key ~= NULLMARK) or keytype == S_number
|
|
282
363
|
end
|
|
283
364
|
|
|
284
|
-
|
|
285
365
|
-- Get a defined value. Returns alt if val is nil.
|
|
286
366
|
local function getdef(val, alt)
|
|
287
367
|
if nil == val then
|
|
@@ -290,14 +370,16 @@ local function getdef(val, alt)
|
|
|
290
370
|
return val
|
|
291
371
|
end
|
|
292
372
|
|
|
293
|
-
|
|
294
373
|
-- The integer size of the value.
|
|
295
374
|
local function size(val)
|
|
375
|
+
val = denoval(val)
|
|
296
376
|
if islist(val) then
|
|
297
377
|
return #val
|
|
298
378
|
elseif ismap(val) then
|
|
299
379
|
local count = 0
|
|
300
|
-
for _ in pairs(val) do
|
|
380
|
+
for _ in pairs(val) do
|
|
381
|
+
count = count + 1
|
|
382
|
+
end
|
|
301
383
|
return count
|
|
302
384
|
end
|
|
303
385
|
|
|
@@ -314,13 +396,13 @@ local function size(val)
|
|
|
314
396
|
end
|
|
315
397
|
end
|
|
316
398
|
|
|
317
|
-
|
|
318
399
|
-- Check for an "empty" value - nil, empty string, array, object.
|
|
319
400
|
-- @param val (any) The value to check
|
|
320
401
|
-- @return (boolean) True if value is empty
|
|
321
402
|
local function isempty(val)
|
|
322
|
-
|
|
323
|
-
if
|
|
403
|
+
val = denoval(val)
|
|
404
|
+
-- Check if the value is absent (nil) or JSON null (NULLMARK).
|
|
405
|
+
if val == nil or val == NULLMARK then
|
|
324
406
|
return true
|
|
325
407
|
end
|
|
326
408
|
|
|
@@ -338,29 +420,37 @@ local function isempty(val)
|
|
|
338
420
|
return false
|
|
339
421
|
end
|
|
340
422
|
|
|
341
|
-
|
|
342
423
|
-- Value is a function.
|
|
343
424
|
-- @param val (any) The value to check
|
|
344
425
|
-- @return (boolean) True if value is a function
|
|
345
426
|
local function isfunc(val)
|
|
346
|
-
|
|
427
|
+
val = denoval(val)
|
|
428
|
+
return type(val) == "function"
|
|
347
429
|
end
|
|
348
430
|
|
|
349
|
-
|
|
350
431
|
-- Determine the type of a value as a bit code.
|
|
351
432
|
-- @param value (any) The value to check
|
|
352
433
|
-- @return (number) The type as a bit flag
|
|
353
434
|
local function typify(value)
|
|
435
|
+
-- The no-value, checked BEFORE anything else: NOVAL is a table, so the
|
|
436
|
+
-- dispatch below would otherwise class it as a map.
|
|
437
|
+
if NOVAL == value then
|
|
438
|
+
return T_noval
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
-- Lua's nil is the representation of JSON null. Canonical TS
|
|
442
|
+
-- typify(null) === T_scalar | T_null; typify(undefined) === T_noval, which
|
|
443
|
+
-- this port spells NOVAL (above).
|
|
354
444
|
if value == nil then
|
|
355
|
-
return T_null
|
|
445
|
+
return T_scalar | T_null
|
|
356
446
|
end
|
|
357
447
|
|
|
358
448
|
local luatype = type(value)
|
|
359
449
|
|
|
360
450
|
if luatype == S_number then
|
|
361
|
-
if value ~= value then
|
|
451
|
+
if value ~= value then -- NaN check
|
|
362
452
|
return T_noval
|
|
363
|
-
elseif math.type(value) ==
|
|
453
|
+
elseif math.type(value) == "integer" or (value % 1 == 0) then
|
|
364
454
|
return T_scalar | T_number | T_integer
|
|
365
455
|
else
|
|
366
456
|
return T_scalar | T_number | T_decimal
|
|
@@ -371,7 +461,7 @@ local function typify(value)
|
|
|
371
461
|
return T_scalar | T_boolean
|
|
372
462
|
elseif luatype == S_function then
|
|
373
463
|
return T_scalar | T_function
|
|
374
|
-
elseif luatype ==
|
|
464
|
+
elseif luatype == "table" then
|
|
375
465
|
if islist(value) then
|
|
376
466
|
return T_node | T_list
|
|
377
467
|
elseif ismap(value) then
|
|
@@ -384,7 +474,6 @@ local function typify(value)
|
|
|
384
474
|
return T_any
|
|
385
475
|
end
|
|
386
476
|
|
|
387
|
-
|
|
388
477
|
-- Safely get a property of a node. Nil arguments return nil.
|
|
389
478
|
-- If the key is not found, return the alternative value, if any.
|
|
390
479
|
-- @param val (any) The parent object/table
|
|
@@ -424,7 +513,8 @@ local function getprop(val, key, alt)
|
|
|
424
513
|
end
|
|
425
514
|
end
|
|
426
515
|
|
|
427
|
-
-- Return alternative if out is nil
|
|
516
|
+
-- Return alternative if out is absent. Lua's nil is JSON null/undefined;
|
|
517
|
+
-- canonical TS `if (null == out) return alt` unifies null and undefined.
|
|
428
518
|
if out == nil then
|
|
429
519
|
return alt
|
|
430
520
|
end
|
|
@@ -432,7 +522,6 @@ local function getprop(val, key, alt)
|
|
|
432
522
|
return out
|
|
433
523
|
end
|
|
434
524
|
|
|
435
|
-
|
|
436
525
|
-- Get a list element. The key should be an integer, or a string
|
|
437
526
|
-- that can parse to an integer only. Negative integers count from the end of the list.
|
|
438
527
|
local function getelem(val, key, alt)
|
|
@@ -453,7 +542,11 @@ local function getelem(val, key, alt)
|
|
|
453
542
|
end
|
|
454
543
|
end
|
|
455
544
|
|
|
456
|
-
|
|
545
|
+
-- A nil/JSON-null slot counts as "no value" → alt. Lua arrays cannot hold
|
|
546
|
+
-- a nil hole, so the test harness carries a JSON null list element as the
|
|
547
|
+
-- NULLMARK ("__NULL__") sentinel; treat it as no-value here too.
|
|
548
|
+
-- Canonical TS: `if (null == out) return alt`.
|
|
549
|
+
if NONE == out or NULLMARK == out then
|
|
457
550
|
if NONE ~= alt and type(alt) == S_function then
|
|
458
551
|
return alt()
|
|
459
552
|
end
|
|
@@ -463,7 +556,6 @@ local function getelem(val, key, alt)
|
|
|
463
556
|
return out
|
|
464
557
|
end
|
|
465
558
|
|
|
466
|
-
|
|
467
559
|
-- Convert different types of keys to string representation.
|
|
468
560
|
-- String keys are returned as is.
|
|
469
561
|
-- Number keys are converted to strings.
|
|
@@ -472,7 +564,8 @@ end
|
|
|
472
564
|
-- @param key (any) The key to convert
|
|
473
565
|
-- @return (string) The string representation of the key
|
|
474
566
|
local function strkey(key)
|
|
475
|
-
|
|
567
|
+
key = denoval(key)
|
|
568
|
+
if key == NONE or key == NULLMARK then
|
|
476
569
|
return S_MT
|
|
477
570
|
end
|
|
478
571
|
|
|
@@ -491,18 +584,21 @@ local function strkey(key)
|
|
|
491
584
|
return S_MT
|
|
492
585
|
end
|
|
493
586
|
|
|
494
|
-
|
|
495
587
|
-- Sorted keys of a map, or indexes of a list.
|
|
496
588
|
-- @param val (any) The object or array to get keys from
|
|
497
589
|
-- @return (table) Array of keys as strings
|
|
498
590
|
local function keysof(val)
|
|
591
|
+
val = denoval(val)
|
|
592
|
+
-- Marked, not plain. An UNMARKED empty table is a map by this port's own
|
|
593
|
+
-- rules (ismap({}) is true, islist({}) is false), so an unmarked `{}` here
|
|
594
|
+
-- claims keysof returned an object where the corpus wants [].
|
|
499
595
|
if not isnode(val) then
|
|
500
|
-
return {}
|
|
596
|
+
return setmetatable({}, { __jsontype = "array" })
|
|
501
597
|
end
|
|
502
598
|
|
|
503
599
|
if ismap(val) then
|
|
504
600
|
-- For maps, collect all keys and sort them
|
|
505
|
-
local keys = {}
|
|
601
|
+
local keys = setmetatable({}, { __jsontype = "array" })
|
|
506
602
|
for k, _ in pairs(val) do
|
|
507
603
|
table.insert(keys, k)
|
|
508
604
|
end
|
|
@@ -510,7 +606,7 @@ local function keysof(val)
|
|
|
510
606
|
return keys
|
|
511
607
|
else
|
|
512
608
|
-- For lists, create array of stringified indices (0-based to match JS/Go)
|
|
513
|
-
local indexes = {}
|
|
609
|
+
local indexes = setmetatable({}, { __jsontype = "array" })
|
|
514
610
|
for i = 1, #val do
|
|
515
611
|
-- Subtract 1 to convert from Lua's 1-based to 0-based indexing
|
|
516
612
|
table.insert(indexes, tostring(i - 1))
|
|
@@ -519,7 +615,6 @@ local function keysof(val)
|
|
|
519
615
|
end
|
|
520
616
|
end
|
|
521
617
|
|
|
522
|
-
|
|
523
618
|
-- Value of property with name key in node val is defined.
|
|
524
619
|
-- @param val (any) The object to check
|
|
525
620
|
-- @param key (any) The key to check
|
|
@@ -528,16 +623,16 @@ local function haskey(val, key)
|
|
|
528
623
|
return getprop(val, key) ~= NONE
|
|
529
624
|
end
|
|
530
625
|
|
|
531
|
-
|
|
532
626
|
-- List the sorted keys of a map or list as an array of tuples of the form {key, value}
|
|
533
627
|
-- @param val (any) The object or array to convert to key-value pairs
|
|
534
628
|
-- @return (table) Array of {key, value} pairs
|
|
535
629
|
local function items(val)
|
|
630
|
+
val = denoval(val)
|
|
536
631
|
if type(val) ~= "table" then
|
|
537
|
-
return {}
|
|
632
|
+
return setmetatable({}, { __jsontype = "array" })
|
|
538
633
|
end
|
|
539
634
|
|
|
540
|
-
local result = {}
|
|
635
|
+
local result = setmetatable({}, { __jsontype = "array" })
|
|
541
636
|
|
|
542
637
|
if islist(val) then
|
|
543
638
|
-- Handle array-like tables (0-based string keys like JS Object.entries)
|
|
@@ -559,7 +654,6 @@ local function items(val)
|
|
|
559
654
|
return result
|
|
560
655
|
end
|
|
561
656
|
|
|
562
|
-
|
|
563
657
|
-- Filter item values using check function.
|
|
564
658
|
-- check receives {key, value} pairs (1-indexed: [1]=key, [2]=value).
|
|
565
659
|
-- Returns array of values where check returns true.
|
|
@@ -576,7 +670,6 @@ local function filter(val, check)
|
|
|
576
670
|
return out
|
|
577
671
|
end
|
|
578
672
|
|
|
579
|
-
|
|
580
673
|
-- Escape regular expression.
|
|
581
674
|
-- @param s (string) The string to escape
|
|
582
675
|
-- @return (string) The escaped string
|
|
@@ -586,7 +679,6 @@ local function escre(s)
|
|
|
586
679
|
return result
|
|
587
680
|
end
|
|
588
681
|
|
|
589
|
-
|
|
590
682
|
-- Escape URLs.
|
|
591
683
|
-- @param s (string) The string to escape
|
|
592
684
|
-- @return (string) The URL-encoded string
|
|
@@ -599,10 +691,9 @@ local function escurl(s)
|
|
|
599
691
|
return result
|
|
600
692
|
end
|
|
601
693
|
|
|
602
|
-
|
|
603
694
|
-- Replace a search string (all), or a pattern, in a source string.
|
|
604
695
|
local function replace(s, from, to)
|
|
605
|
-
local rs
|
|
696
|
+
local rs
|
|
606
697
|
local ts = typify(s)
|
|
607
698
|
if 0 == (T_string & ts) then
|
|
608
699
|
rs = stringify(s)
|
|
@@ -611,7 +702,7 @@ local function replace(s, from, to)
|
|
|
611
702
|
else
|
|
612
703
|
rs = stringify(s)
|
|
613
704
|
end
|
|
614
|
-
if type(from) ==
|
|
705
|
+
if type(from) == "string" then
|
|
615
706
|
-- Plain string replacement (all occurrences)
|
|
616
707
|
return (rs:gsub(escre(from), to))
|
|
617
708
|
else
|
|
@@ -620,7 +711,6 @@ local function replace(s, from, to)
|
|
|
620
711
|
end
|
|
621
712
|
end
|
|
622
713
|
|
|
623
|
-
|
|
624
714
|
-- Return a sub-array. Start and end are 0-based, end is exclusive.
|
|
625
715
|
-- For numbers: clamp between start and end-1.
|
|
626
716
|
-- For strings: substring from start to end.
|
|
@@ -643,12 +733,16 @@ local function slice(val, start, endidx, mutate)
|
|
|
643
733
|
if start ~= nil then
|
|
644
734
|
if start < 0 then
|
|
645
735
|
endidx = vlen + start
|
|
646
|
-
if endidx < 0 then
|
|
736
|
+
if endidx < 0 then
|
|
737
|
+
endidx = 0
|
|
738
|
+
end
|
|
647
739
|
start = 0
|
|
648
740
|
elseif endidx ~= nil then
|
|
649
741
|
if endidx < 0 then
|
|
650
742
|
endidx = vlen + endidx
|
|
651
|
-
if endidx < 0 then
|
|
743
|
+
if endidx < 0 then
|
|
744
|
+
endidx = 0
|
|
745
|
+
end
|
|
652
746
|
elseif vlen < endidx then
|
|
653
747
|
endidx = vlen
|
|
654
748
|
end
|
|
@@ -686,7 +780,9 @@ local function slice(val, start, endidx, mutate)
|
|
|
686
780
|
else
|
|
687
781
|
if islist(val) then
|
|
688
782
|
if mutate then
|
|
689
|
-
for i = 1, #val do
|
|
783
|
+
for i = 1, #val do
|
|
784
|
+
val[i] = nil
|
|
785
|
+
end
|
|
690
786
|
return val
|
|
691
787
|
end
|
|
692
788
|
return setmetatable({}, { __jsontype = "array" })
|
|
@@ -699,7 +795,6 @@ local function slice(val, start, endidx, mutate)
|
|
|
699
795
|
return val
|
|
700
796
|
end
|
|
701
797
|
|
|
702
|
-
|
|
703
798
|
-- Flatten nested lists to a given depth.
|
|
704
799
|
local function flatten(val, depth)
|
|
705
800
|
if not islist(val) then
|
|
@@ -710,7 +805,7 @@ local function flatten(val, depth)
|
|
|
710
805
|
setmetatable(result, { __jsontype = "array" })
|
|
711
806
|
|
|
712
807
|
for _, item in ipairs(val) do
|
|
713
|
-
if (islist(item) or (type(item) ==
|
|
808
|
+
if (islist(item) or (type(item) == "table" and next(item) == nil)) and depth > 0 then
|
|
714
809
|
local sub = flatten(item, depth - 1)
|
|
715
810
|
for _, v in ipairs(sub) do
|
|
716
811
|
table.insert(result, v)
|
|
@@ -722,14 +817,22 @@ local function flatten(val, depth)
|
|
|
722
817
|
return result
|
|
723
818
|
end
|
|
724
819
|
|
|
725
|
-
|
|
726
820
|
-- Pad a string or number.
|
|
727
821
|
-- Positive padlen = right-pad (padEnd), negative padlen = left-pad (padStart).
|
|
728
822
|
local function pad(val, padlen, padchar)
|
|
729
|
-
|
|
823
|
+
-- Mirror TS canonical: stringify(null) is "null". Lua's nil corresponds
|
|
824
|
+
-- to JSON null when input doesn't carry the runner's __NULL__ marker;
|
|
825
|
+
-- explicitly coerce here so padding works the same as in TS.
|
|
826
|
+
if val == nil then
|
|
827
|
+
val = "null"
|
|
828
|
+
else
|
|
829
|
+
val = S_string == type(val) and val or stringify(val)
|
|
830
|
+
end
|
|
730
831
|
padlen = padlen or 44
|
|
731
832
|
padchar = padchar or S_SP
|
|
732
|
-
if #padchar > 1 then
|
|
833
|
+
if #padchar > 1 then
|
|
834
|
+
padchar = padchar:sub(1, 1)
|
|
835
|
+
end
|
|
733
836
|
|
|
734
837
|
if padlen >= 0 then
|
|
735
838
|
-- Right-pad (padEnd)
|
|
@@ -746,7 +849,6 @@ local function pad(val, padlen, padchar)
|
|
|
746
849
|
return val
|
|
747
850
|
end
|
|
748
851
|
|
|
749
|
-
|
|
750
852
|
-- Delete a property from a node.
|
|
751
853
|
delprop = function(parent, key)
|
|
752
854
|
if not iskey(key) then
|
|
@@ -771,7 +873,6 @@ delprop = function(parent, key)
|
|
|
771
873
|
return parent
|
|
772
874
|
end
|
|
773
875
|
|
|
774
|
-
|
|
775
876
|
-- Build a JSON map from alternating key, value arguments.
|
|
776
877
|
local function jm(...)
|
|
777
878
|
local kv = { ... }
|
|
@@ -779,7 +880,7 @@ local function jm(...)
|
|
|
779
880
|
local o = {}
|
|
780
881
|
local i = 0
|
|
781
882
|
while i < kvsize do
|
|
782
|
-
local k = getprop(kv, i,
|
|
883
|
+
local k = getprop(kv, i, "$KEY" .. i)
|
|
783
884
|
k = S_string == type(k) and k or stringify(k)
|
|
784
885
|
o[k] = getprop(kv, i + 1, nil)
|
|
785
886
|
i = i + 2
|
|
@@ -787,7 +888,6 @@ local function jm(...)
|
|
|
787
888
|
return o
|
|
788
889
|
end
|
|
789
890
|
|
|
790
|
-
|
|
791
891
|
-- Define a JSON Array using function arguments.
|
|
792
892
|
local function jt(...)
|
|
793
893
|
local v = { ... }
|
|
@@ -800,7 +900,6 @@ local function jt(...)
|
|
|
800
900
|
return a
|
|
801
901
|
end
|
|
802
902
|
|
|
803
|
-
|
|
804
903
|
-- Concatenate strings, merging separator char as needed.
|
|
805
904
|
-- Default separator is comma. When url=true, preserve protocol slashes.
|
|
806
905
|
-- @param arr (table) Array of parts to join
|
|
@@ -826,15 +925,19 @@ local function join(arr, sep, url)
|
|
|
826
925
|
for i = 1, #arr do
|
|
827
926
|
local v = arr[i]
|
|
828
927
|
local ts = typify(v)
|
|
829
|
-
|
|
830
|
-
|
|
928
|
+
-- Canonical: keep only non-empty string parts and drop JSON nulls. Lua
|
|
929
|
+
-- arrays cannot hold a nil hole, so a JSON null list element is carried as
|
|
930
|
+
-- the placeholder string S_null ("null", null=false) or NULLMARK
|
|
931
|
+
-- ("__NULL__", null=true); both are dropped here.
|
|
932
|
+
if (0 < (T_string & ts)) and v ~= S_MT and v ~= S_null and v ~= NULLMARK then
|
|
933
|
+
table.insert(string_items, { i - 1, v }) -- 0-based index, value
|
|
831
934
|
end
|
|
832
935
|
end
|
|
833
936
|
|
|
834
937
|
-- Step 2: Process each element to clean separators
|
|
835
938
|
local processed = {}
|
|
836
939
|
for _, item in ipairs(string_items) do
|
|
837
|
-
local idx = item[1]
|
|
940
|
+
local idx = item[1] -- 0-based original index
|
|
838
941
|
local s = item[2]
|
|
839
942
|
|
|
840
943
|
if seppat ~= nil and seppat ~= S_MT then
|
|
@@ -853,8 +956,10 @@ local function join(arr, sep, url)
|
|
|
853
956
|
end
|
|
854
957
|
|
|
855
958
|
-- Collapse multiple seps between non-sep chars
|
|
856
|
-
s = s:gsub(
|
|
857
|
-
"
|
|
959
|
+
s = s:gsub(
|
|
960
|
+
"([^" .. seppat .. "])" .. seppat .. "+([^" .. seppat .. "])",
|
|
961
|
+
"%1" .. sepdef .. "%2"
|
|
962
|
+
)
|
|
858
963
|
end
|
|
859
964
|
end
|
|
860
965
|
|
|
@@ -866,19 +971,24 @@ local function join(arr, sep, url)
|
|
|
866
971
|
return table.concat(processed, sepdef)
|
|
867
972
|
end
|
|
868
973
|
|
|
869
|
-
|
|
870
974
|
-- Safely stringify a value for humans (NOT JSON!)
|
|
871
975
|
-- Strings are returned as-is (not quoted).
|
|
872
976
|
-- @param val (any) The value to stringify
|
|
873
977
|
-- @param maxlen (number) Optional maximum length for result
|
|
874
978
|
-- @param pretty (boolean) Optional pretty mode with ANSI colors
|
|
875
979
|
-- @return (string) String representation of the value
|
|
876
|
-
|
|
980
|
+
stringify = function(val, maxlen, pretty)
|
|
877
981
|
local valstr = S_MT
|
|
878
982
|
pretty = pretty and true or false
|
|
879
983
|
|
|
984
|
+
-- Absent/undefined (canonical TS NONE): empty string (or "<>" pretty).
|
|
880
985
|
if val == nil then
|
|
881
|
-
return pretty and
|
|
986
|
+
return pretty and "<>" or valstr
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
-- JSON null (canonical TS null): JSON.stringify(null) === "null".
|
|
990
|
+
if val == NULLMARK then
|
|
991
|
+
return "null"
|
|
882
992
|
end
|
|
883
993
|
|
|
884
994
|
if type(val) == S_string then
|
|
@@ -896,26 +1006,28 @@ local function stringify(val, maxlen, pretty)
|
|
|
896
1006
|
local function serialize(obj, seen)
|
|
897
1007
|
seen = seen or {}
|
|
898
1008
|
|
|
899
|
-
if type(obj) ==
|
|
900
|
-
return
|
|
1009
|
+
if type(obj) == "table" and seen[obj] then
|
|
1010
|
+
return "..."
|
|
901
1011
|
end
|
|
902
1012
|
|
|
903
1013
|
local obj_type = type(obj)
|
|
904
1014
|
|
|
905
|
-
if obj == nil then
|
|
906
|
-
return
|
|
1015
|
+
if obj == nil or obj == NULLMARK then
|
|
1016
|
+
return "null"
|
|
907
1017
|
elseif obj_type == S_number then
|
|
908
|
-
if obj ~= obj then
|
|
1018
|
+
if obj ~= obj then
|
|
1019
|
+
return "null"
|
|
1020
|
+
end -- NaN
|
|
909
1021
|
-- Use integer representation for whole numbers
|
|
910
1022
|
if obj % 1 == 0 then
|
|
911
|
-
return string.format(
|
|
1023
|
+
return string.format("%d", obj)
|
|
912
1024
|
end
|
|
913
1025
|
return tostring(obj)
|
|
914
1026
|
elseif obj_type == S_boolean then
|
|
915
1027
|
return tostring(obj)
|
|
916
1028
|
elseif obj_type == S_function then
|
|
917
|
-
return
|
|
918
|
-
elseif obj_type ~=
|
|
1029
|
+
return "null"
|
|
1030
|
+
elseif obj_type ~= "table" then
|
|
919
1031
|
return tostring(obj)
|
|
920
1032
|
end
|
|
921
1033
|
|
|
@@ -938,9 +1050,9 @@ local function stringify(val, maxlen, pretty)
|
|
|
938
1050
|
seen[obj] = nil
|
|
939
1051
|
|
|
940
1052
|
if is_arr then
|
|
941
|
-
return S_OS .. table.concat(parts,
|
|
1053
|
+
return S_OS .. table.concat(parts, ",") .. S_CS
|
|
942
1054
|
else
|
|
943
|
-
return
|
|
1055
|
+
return "{" .. table.concat(parts, ",") .. "}"
|
|
944
1056
|
end
|
|
945
1057
|
end
|
|
946
1058
|
|
|
@@ -951,31 +1063,33 @@ local function stringify(val, maxlen, pretty)
|
|
|
951
1063
|
if success then
|
|
952
1064
|
valstr = result
|
|
953
1065
|
else
|
|
954
|
-
valstr =
|
|
1066
|
+
valstr = "__STRINGIFY_FAILED__"
|
|
955
1067
|
end
|
|
956
1068
|
end
|
|
957
1069
|
|
|
958
1070
|
-- Handle maxlen
|
|
959
1071
|
if maxlen ~= nil and maxlen > -1 then
|
|
960
1072
|
if maxlen < #valstr then
|
|
961
|
-
valstr = string.sub(valstr, 1, maxlen - 3) ..
|
|
1073
|
+
valstr = string.sub(valstr, 1, maxlen - 3) .. "..."
|
|
962
1074
|
end
|
|
963
1075
|
end
|
|
964
1076
|
|
|
965
1077
|
if pretty then
|
|
966
1078
|
local c = { 81, 118, 213, 39, 208, 201, 45, 190, 129, 51, 160, 121, 226, 33, 207, 69 }
|
|
967
|
-
local r =
|
|
1079
|
+
local r = "\x1b[0m"
|
|
968
1080
|
local d = 0
|
|
969
|
-
local function cc(n)
|
|
1081
|
+
local function cc(n)
|
|
1082
|
+
return "\x1b[38;5;" .. n .. "m"
|
|
1083
|
+
end
|
|
970
1084
|
local o = cc(c[1])
|
|
971
1085
|
local t = o
|
|
972
1086
|
for i = 1, #valstr do
|
|
973
1087
|
local ch = valstr:sub(i, i)
|
|
974
|
-
if ch ==
|
|
1088
|
+
if ch == "{" or ch == S_OS then
|
|
975
1089
|
d = d + 1
|
|
976
1090
|
o = cc(c[(d % #c) + 1])
|
|
977
1091
|
t = t .. o .. ch
|
|
978
|
-
elseif ch ==
|
|
1092
|
+
elseif ch == "}" or ch == S_CS then
|
|
979
1093
|
t = t .. o .. ch
|
|
980
1094
|
d = d - 1
|
|
981
1095
|
o = cc(c[(d % #c) + 1])
|
|
@@ -989,15 +1103,14 @@ local function stringify(val, maxlen, pretty)
|
|
|
989
1103
|
return valstr
|
|
990
1104
|
end
|
|
991
1105
|
|
|
992
|
-
|
|
993
1106
|
-- Convert a value to JSON string representation (matching JSON.stringify behavior).
|
|
994
1107
|
local function jsonify(val, flags)
|
|
995
1108
|
local str = S_null
|
|
996
1109
|
|
|
997
1110
|
if val ~= nil then
|
|
998
1111
|
local ok, result = pcall(function()
|
|
999
|
-
local indent_size = getprop(flags,
|
|
1000
|
-
local offset = getprop(flags,
|
|
1112
|
+
local indent_size = getprop(flags, "indent", 2)
|
|
1113
|
+
local offset = getprop(flags, "offset", 0)
|
|
1001
1114
|
|
|
1002
1115
|
-- Recursive JSON serializer matching JSON.stringify(val, null, indent)
|
|
1003
1116
|
local function ser(v, depth)
|
|
@@ -1006,22 +1119,27 @@ local function jsonify(val, flags)
|
|
|
1006
1119
|
elseif type(v) == S_boolean then
|
|
1007
1120
|
return tostring(v)
|
|
1008
1121
|
elseif type(v) == S_number then
|
|
1009
|
-
if v ~= v then
|
|
1122
|
+
if v ~= v then
|
|
1123
|
+
return S_null
|
|
1124
|
+
end -- NaN
|
|
1010
1125
|
if v % 1 == 0 then
|
|
1011
|
-
return string.format(
|
|
1126
|
+
return string.format("%d", v)
|
|
1012
1127
|
end
|
|
1013
1128
|
return tostring(v)
|
|
1014
1129
|
elseif type(v) == S_string then
|
|
1015
1130
|
-- Escape string for JSON
|
|
1016
|
-
local escaped = v:gsub(
|
|
1017
|
-
:gsub('
|
|
1131
|
+
local escaped = v:gsub("\\", "\\\\")
|
|
1132
|
+
:gsub('"', '\\"')
|
|
1133
|
+
:gsub("\n", "\\n")
|
|
1134
|
+
:gsub("\r", "\\r")
|
|
1135
|
+
:gsub("\t", "\\t")
|
|
1018
1136
|
return '"' .. escaped .. '"'
|
|
1019
1137
|
elseif type(v) == S_function then
|
|
1020
|
-
return nil
|
|
1021
|
-
elseif type(v) ==
|
|
1138
|
+
return nil -- Functions are omitted in JSON
|
|
1139
|
+
elseif type(v) == "table" then
|
|
1022
1140
|
if islist(v) then
|
|
1023
1141
|
if #v == 0 then
|
|
1024
|
-
return
|
|
1142
|
+
return "[]"
|
|
1025
1143
|
end
|
|
1026
1144
|
local parts = {}
|
|
1027
1145
|
for i = 1, #v do
|
|
@@ -1029,35 +1147,44 @@ local function jsonify(val, flags)
|
|
|
1029
1147
|
table.insert(parts, sv or S_null)
|
|
1030
1148
|
end
|
|
1031
1149
|
if indent_size == 0 then
|
|
1032
|
-
return
|
|
1150
|
+
return "[" .. table.concat(parts, ",") .. "]"
|
|
1033
1151
|
end
|
|
1034
|
-
local pad_str = string.rep(
|
|
1035
|
-
local close_pad = string.rep(
|
|
1036
|
-
return
|
|
1037
|
-
|
|
1152
|
+
local pad_str = string.rep(" ", indent_size * (depth + 1) + offset)
|
|
1153
|
+
local close_pad = string.rep(" ", indent_size * depth + offset)
|
|
1154
|
+
return "[\n"
|
|
1155
|
+
.. pad_str
|
|
1156
|
+
.. table.concat(parts, ",\n" .. pad_str)
|
|
1157
|
+
.. "\n"
|
|
1158
|
+
.. close_pad
|
|
1159
|
+
.. "]"
|
|
1038
1160
|
else
|
|
1039
1161
|
-- Map/object
|
|
1040
1162
|
local keys_list = keysof(v)
|
|
1041
1163
|
if #keys_list == 0 then
|
|
1042
|
-
return
|
|
1164
|
+
return "{}"
|
|
1043
1165
|
end
|
|
1044
1166
|
local parts = {}
|
|
1167
|
+
local kv_sep = indent_size == 0 and '":' or '": '
|
|
1045
1168
|
for _, k in ipairs(keys_list) do
|
|
1046
1169
|
local sv = ser(v[k], depth + 1)
|
|
1047
|
-
if sv ~= nil then
|
|
1048
|
-
table.insert(parts, '"' .. k ..
|
|
1170
|
+
if sv ~= nil then -- Skip undefined values
|
|
1171
|
+
table.insert(parts, '"' .. k .. kv_sep .. sv)
|
|
1049
1172
|
end
|
|
1050
1173
|
end
|
|
1051
1174
|
if #parts == 0 then
|
|
1052
|
-
return
|
|
1175
|
+
return "{}"
|
|
1053
1176
|
end
|
|
1054
1177
|
if indent_size == 0 then
|
|
1055
|
-
return
|
|
1178
|
+
return "{" .. table.concat(parts, ",") .. "}"
|
|
1056
1179
|
end
|
|
1057
|
-
local pad_str = string.rep(
|
|
1058
|
-
local close_pad = string.rep(
|
|
1059
|
-
return
|
|
1060
|
-
|
|
1180
|
+
local pad_str = string.rep(" ", indent_size * (depth + 1) + offset)
|
|
1181
|
+
local close_pad = string.rep(" ", indent_size * depth + offset)
|
|
1182
|
+
return "{\n"
|
|
1183
|
+
.. pad_str
|
|
1184
|
+
.. table.concat(parts, ",\n" .. pad_str)
|
|
1185
|
+
.. "\n"
|
|
1186
|
+
.. close_pad
|
|
1187
|
+
.. "}"
|
|
1061
1188
|
end
|
|
1062
1189
|
end
|
|
1063
1190
|
return S_null
|
|
@@ -1073,14 +1200,13 @@ local function jsonify(val, flags)
|
|
|
1073
1200
|
if ok and result ~= nil then
|
|
1074
1201
|
str = result
|
|
1075
1202
|
else
|
|
1076
|
-
str =
|
|
1203
|
+
str = "__JSONIFY_FAILED__"
|
|
1077
1204
|
end
|
|
1078
1205
|
end
|
|
1079
1206
|
|
|
1080
1207
|
return str
|
|
1081
1208
|
end
|
|
1082
1209
|
|
|
1083
|
-
|
|
1084
1210
|
-- Build a human friendly path string.
|
|
1085
1211
|
-- @param val (any) The path as array or string
|
|
1086
1212
|
-- @param startin (number) Optional start index
|
|
@@ -1090,18 +1216,22 @@ local function pathify(val, startin, endin)
|
|
|
1090
1216
|
local pathstr = NONE
|
|
1091
1217
|
local path = NONE
|
|
1092
1218
|
|
|
1093
|
-
-- Convert input to path array
|
|
1219
|
+
-- Convert input to path array. A JSON null (NULLMARK) is a scalar value,
|
|
1220
|
+
-- not a string path, so it falls through to the <unknown-path:null> branch
|
|
1221
|
+
-- (canonical TS: pathify(null) === '<unknown-path:null>').
|
|
1094
1222
|
if islist(val) then
|
|
1095
1223
|
path = val
|
|
1224
|
+
elseif val == NULLMARK then
|
|
1225
|
+
path = NONE
|
|
1096
1226
|
elseif type(val) == S_string then
|
|
1097
1227
|
path = { val }
|
|
1098
1228
|
setmetatable(path, {
|
|
1099
|
-
__jsontype = "array"
|
|
1229
|
+
__jsontype = "array",
|
|
1100
1230
|
})
|
|
1101
1231
|
elseif type(val) == S_number then
|
|
1102
1232
|
path = { val }
|
|
1103
1233
|
setmetatable(path, {
|
|
1104
|
-
__jsontype = "array"
|
|
1234
|
+
__jsontype = "array",
|
|
1105
1235
|
})
|
|
1106
1236
|
end
|
|
1107
1237
|
|
|
@@ -1118,7 +1248,7 @@ local function pathify(val, startin, endin)
|
|
|
1118
1248
|
path = sliced
|
|
1119
1249
|
|
|
1120
1250
|
if #path == 0 then
|
|
1121
|
-
pathstr =
|
|
1251
|
+
pathstr = "<root>"
|
|
1122
1252
|
else
|
|
1123
1253
|
-- Filter valid path elements using iskey
|
|
1124
1254
|
local filtered = {}
|
|
@@ -1148,19 +1278,18 @@ local function pathify(val, startin, endin)
|
|
|
1148
1278
|
|
|
1149
1279
|
-- Handle unknown paths
|
|
1150
1280
|
if pathstr == NONE then
|
|
1151
|
-
pathstr =
|
|
1281
|
+
pathstr = "<unknown-path"
|
|
1152
1282
|
if val == NONE then
|
|
1153
1283
|
pathstr = pathstr .. S_MT
|
|
1154
1284
|
else
|
|
1155
1285
|
pathstr = pathstr .. (S_CN .. stringify(val, 47))
|
|
1156
1286
|
end
|
|
1157
|
-
pathstr = pathstr ..
|
|
1287
|
+
pathstr = pathstr .. ">"
|
|
1158
1288
|
end
|
|
1159
1289
|
|
|
1160
1290
|
return pathstr
|
|
1161
1291
|
end
|
|
1162
1292
|
|
|
1163
|
-
|
|
1164
1293
|
-- Set a value deep inside a node at a key path.
|
|
1165
1294
|
local function setpath(store, path, val, injdef)
|
|
1166
1295
|
local pathType = typify(path)
|
|
@@ -1202,7 +1331,7 @@ local function setpath(store, path, val, injdef)
|
|
|
1202
1331
|
|
|
1203
1332
|
local lastKey = getelem(parts, -1)
|
|
1204
1333
|
|
|
1205
|
-
if type(val) ==
|
|
1334
|
+
if type(val) == "table" and val["`$DELETE`"] then
|
|
1206
1335
|
delprop(parent, lastKey)
|
|
1207
1336
|
else
|
|
1208
1337
|
setprop(parent, lastKey, val)
|
|
@@ -1211,13 +1340,13 @@ local function setpath(store, path, val, injdef)
|
|
|
1211
1340
|
return parent
|
|
1212
1341
|
end
|
|
1213
1342
|
|
|
1214
|
-
|
|
1215
1343
|
-- Clone a JSON-like data structure.
|
|
1216
1344
|
-- NOTE: function value references are copied, *not* cloned.
|
|
1217
1345
|
-- @param val (any) The value to clone
|
|
1218
1346
|
-- @param flags (table) Optional flags to control cloning behavior
|
|
1219
1347
|
-- @return (any) Deep copy of the value
|
|
1220
1348
|
local function clone(val, flags)
|
|
1349
|
+
val = denoval(val)
|
|
1221
1350
|
-- Handle nil value
|
|
1222
1351
|
if val == nil then
|
|
1223
1352
|
return nil
|
|
@@ -1283,7 +1412,6 @@ local function clone(val, flags)
|
|
|
1283
1412
|
return val
|
|
1284
1413
|
end
|
|
1285
1414
|
|
|
1286
|
-
|
|
1287
1415
|
-- Safely set a property. Undefined arguments and invalid keys are ignored.
|
|
1288
1416
|
-- Returns the (possibly modified) parent.
|
|
1289
1417
|
-- If the parent is a list, and the key is negative, prepend the value.
|
|
@@ -1329,8 +1457,12 @@ setprop = function(parent, key, val)
|
|
|
1329
1457
|
return parent
|
|
1330
1458
|
end
|
|
1331
1459
|
|
|
1332
|
-
|
|
1333
1460
|
-- Walk a data structure depth first, applying a function to each value.
|
|
1461
|
+
-- The `path` argument passed to the before/after callbacks is a single
|
|
1462
|
+
-- mutable array per depth, shared across all callback invocations for the
|
|
1463
|
+
-- lifetime of this top-level walk call. Callbacks that need to store the
|
|
1464
|
+
-- path MUST clone it (e.g. via a shallow copy); the contents will otherwise
|
|
1465
|
+
-- be overwritten by subsequent visits.
|
|
1334
1466
|
-- @param val (any) The value to walk
|
|
1335
1467
|
-- @param before (function) Applied before descending into a node
|
|
1336
1468
|
-- @param after (function) Applied after descending into a node
|
|
@@ -1338,14 +1470,22 @@ end
|
|
|
1338
1470
|
-- @param key (any) Current key (for recursive calls)
|
|
1339
1471
|
-- @param parent (table) Current parent (for recursive calls)
|
|
1340
1472
|
-- @param path (table) Current path (for recursive calls)
|
|
1473
|
+
-- @param pool (table) Per-depth reusable path arrays (for recursive calls)
|
|
1341
1474
|
-- @return (any) The transformed value
|
|
1342
|
-
local function walk(val, before, after, maxdepth,
|
|
1343
|
-
|
|
1344
|
-
if
|
|
1345
|
-
|
|
1346
|
-
|
|
1475
|
+
local function walk(val, before, after, maxdepth, key, parent, path, pool)
|
|
1476
|
+
val = denoval(val)
|
|
1477
|
+
if nil == pool then
|
|
1478
|
+
pool = {}
|
|
1479
|
+
local rootPath = {}
|
|
1480
|
+
setmetatable(rootPath, { __jsontype = "array" })
|
|
1481
|
+
pool[0] = rootPath
|
|
1482
|
+
end
|
|
1483
|
+
if nil == path then
|
|
1484
|
+
path = pool[0]
|
|
1347
1485
|
end
|
|
1348
1486
|
|
|
1487
|
+
local depth = #path
|
|
1488
|
+
|
|
1349
1489
|
local out
|
|
1350
1490
|
if nil == before then
|
|
1351
1491
|
out = val
|
|
@@ -1354,18 +1494,31 @@ local function walk(val, before, after, maxdepth,
|
|
|
1354
1494
|
end
|
|
1355
1495
|
|
|
1356
1496
|
maxdepth = (maxdepth ~= nil and maxdepth >= 0) and maxdepth or MAXDEPTH
|
|
1357
|
-
if 0 == maxdepth or (
|
|
1497
|
+
if 0 == maxdepth or (0 < maxdepth and maxdepth <= depth) then
|
|
1358
1498
|
return out
|
|
1359
1499
|
end
|
|
1360
1500
|
|
|
1361
1501
|
if isnode(out) then
|
|
1502
|
+
local childDepth = depth + 1
|
|
1503
|
+
local childPath = pool[childDepth]
|
|
1504
|
+
if nil == childPath then
|
|
1505
|
+
childPath = {}
|
|
1506
|
+
setmetatable(childPath, { __jsontype = "array" })
|
|
1507
|
+
pool[childDepth] = childPath
|
|
1508
|
+
end
|
|
1509
|
+
-- Sync prefix [1..depth] from the current path. Only needed once per
|
|
1510
|
+
-- parent: siblings share the same prefix and will each overwrite slot
|
|
1511
|
+
-- [childDepth] below.
|
|
1512
|
+
for i = 1, depth do
|
|
1513
|
+
childPath[i] = path[i]
|
|
1514
|
+
end
|
|
1515
|
+
|
|
1362
1516
|
for _, item in ipairs(items(out)) do
|
|
1363
1517
|
local ckey, child = item[1], item[2]
|
|
1364
1518
|
|
|
1365
|
-
|
|
1366
|
-
setmetatable(childPath, { __jsontype = "array" })
|
|
1519
|
+
childPath[childDepth] = S_MT .. tostring(ckey)
|
|
1367
1520
|
|
|
1368
|
-
setprop(out, ckey, walk(child, before, after, maxdepth, ckey, out, childPath))
|
|
1521
|
+
setprop(out, ckey, walk(child, before, after, maxdepth, ckey, out, childPath, pool))
|
|
1369
1522
|
end
|
|
1370
1523
|
end
|
|
1371
1524
|
|
|
@@ -1376,7 +1529,6 @@ local function walk(val, before, after, maxdepth,
|
|
|
1376
1529
|
return out
|
|
1377
1530
|
end
|
|
1378
1531
|
|
|
1379
|
-
|
|
1380
1532
|
-- Merge a list of values into each other. Later values have
|
|
1381
1533
|
-- precedence. Nodes override scalars. Node kinds (list or map)
|
|
1382
1534
|
-- override each other, and do *not* merge. The first element is
|
|
@@ -1385,8 +1537,9 @@ end
|
|
|
1385
1537
|
-- @param maxdepth (number) Optional maximum depth for merge
|
|
1386
1538
|
-- @return (any) The merged result
|
|
1387
1539
|
local function merge(val, maxdepth)
|
|
1540
|
+
val = denoval(val)
|
|
1388
1541
|
local md = slice(getdef(maxdepth, MAXDEPTH), 0)
|
|
1389
|
-
local out
|
|
1542
|
+
local out
|
|
1390
1543
|
|
|
1391
1544
|
-- Handle edge cases
|
|
1392
1545
|
if not islist(val) then
|
|
@@ -1443,9 +1596,8 @@ local function merge(val, maxdepth)
|
|
|
1443
1596
|
-- NOT `a and b or c`: that idiom silently yields `c` whenever `b`
|
|
1444
1597
|
-- is falsy, and `getprop` returns exactly that for a key the
|
|
1445
1598
|
-- destination does not have. `dst[pI + 1]` then kept the PREVIOUS
|
|
1446
|
-
-- sibling's destination node, so the next key descended into it
|
|
1447
|
-
--
|
|
1448
|
-
-- `auth` and `feature` as ONE table holding the union of both.
|
|
1599
|
+
-- sibling's destination node, so the next key descended into it and
|
|
1600
|
+
-- the two ended up sharing ONE table holding the union of both.
|
|
1449
1601
|
if 0 < pI then
|
|
1450
1602
|
dst[pI + 1] = getprop(dst[pI], key)
|
|
1451
1603
|
end
|
|
@@ -1453,8 +1605,7 @@ local function merge(val, maxdepth)
|
|
|
1453
1605
|
|
|
1454
1606
|
-- Destination empty, so create node (unless override is class instance).
|
|
1455
1607
|
if NONE == tval and 0 == (T_instance & typify(bval)) then
|
|
1456
|
-
cur[pI + 1] = islist(bval) and
|
|
1457
|
-
setmetatable({}, { __jsontype = "array" }) or {}
|
|
1608
|
+
cur[pI + 1] = islist(bval) and setmetatable({}, { __jsontype = "array" }) or {}
|
|
1458
1609
|
|
|
1459
1610
|
-- Matching override and destination so continue with their values.
|
|
1460
1611
|
elseif typify(bval) == typify(tval) then
|
|
@@ -1487,14 +1638,12 @@ local function merge(val, maxdepth)
|
|
|
1487
1638
|
|
|
1488
1639
|
if 0 == md then
|
|
1489
1640
|
out = getelem(list, -1)
|
|
1490
|
-
out = islist(out) and setmetatable({}, { __jsontype = "array" })
|
|
1491
|
-
or ismap(out) and {} or out
|
|
1641
|
+
out = islist(out) and setmetatable({}, { __jsontype = "array" }) or ismap(out) and {} or out
|
|
1492
1642
|
end
|
|
1493
1643
|
|
|
1494
1644
|
return out
|
|
1495
1645
|
end
|
|
1496
1646
|
|
|
1497
|
-
|
|
1498
1647
|
-- Get a value deep inside a node using a key path.
|
|
1499
1648
|
-- @param store (table) The data store to search in
|
|
1500
1649
|
-- @param path (string|table|number) The path to the value
|
|
@@ -1511,7 +1660,7 @@ getpath = function(store, path, injdef)
|
|
|
1511
1660
|
local pos = 1
|
|
1512
1661
|
local len = #path
|
|
1513
1662
|
while pos <= len do
|
|
1514
|
-
local dotpos = path:find(
|
|
1663
|
+
local dotpos = path:find(".", pos, true)
|
|
1515
1664
|
if dotpos then
|
|
1516
1665
|
table.insert(parts, path:sub(pos, dotpos - 1))
|
|
1517
1666
|
pos = dotpos + 1
|
|
@@ -1523,14 +1672,12 @@ getpath = function(store, path, injdef)
|
|
|
1523
1672
|
if pos == 1 then
|
|
1524
1673
|
-- Empty string
|
|
1525
1674
|
parts = { S_MT }
|
|
1526
|
-
elseif pos
|
|
1527
|
-
-- Normal end
|
|
1528
|
-
else
|
|
1675
|
+
elseif pos ~= len + 1 then
|
|
1529
1676
|
-- Path ends with a dot
|
|
1530
1677
|
table.insert(parts, S_MT)
|
|
1531
1678
|
end
|
|
1532
1679
|
-- Handle trailing dot: "a." -> ["a", ""]
|
|
1533
|
-
if len > 0 and path:sub(len, len) ==
|
|
1680
|
+
if len > 0 and path:sub(len, len) == "." then
|
|
1534
1681
|
table.insert(parts, S_MT)
|
|
1535
1682
|
end
|
|
1536
1683
|
elseif type(path) == S_number then
|
|
@@ -1543,13 +1690,12 @@ getpath = function(store, path, injdef)
|
|
|
1543
1690
|
local base = getprop(injdef, S_base)
|
|
1544
1691
|
local src = getprop(store, base, store)
|
|
1545
1692
|
local numparts = #parts
|
|
1546
|
-
local dparent = getprop(injdef,
|
|
1693
|
+
local dparent = getprop(injdef, "dparent")
|
|
1547
1694
|
|
|
1548
1695
|
-- An empty path (incl empty string) just finds the store.
|
|
1549
1696
|
if path == nil or store == nil or (1 == numparts and S_MT == parts[1]) then
|
|
1550
1697
|
val = src
|
|
1551
1698
|
elseif 0 < numparts then
|
|
1552
|
-
|
|
1553
1699
|
-- Check for $ACTIONs
|
|
1554
1700
|
if 1 == numparts then
|
|
1555
1701
|
val = getprop(store, parts[1])
|
|
@@ -1559,34 +1705,34 @@ getpath = function(store, path, injdef)
|
|
|
1559
1705
|
val = src
|
|
1560
1706
|
|
|
1561
1707
|
-- Check for meta path syntax: field$=value or field$~value
|
|
1562
|
-
local m1,
|
|
1708
|
+
local m1, _, m3 = parts[1]:match("^([^$]+)%$([=~])(.+)$")
|
|
1563
1709
|
if m1 and injdef and injdef.meta then
|
|
1564
1710
|
val = getprop(injdef.meta, m1)
|
|
1565
1711
|
parts[1] = m3
|
|
1566
1712
|
end
|
|
1567
1713
|
|
|
1568
|
-
local dpath = getprop(injdef,
|
|
1714
|
+
local dpath = getprop(injdef, "dpath")
|
|
1569
1715
|
|
|
1570
1716
|
local pI = 0
|
|
1571
1717
|
while NONE ~= val and pI < numparts do
|
|
1572
|
-
local part = parts[pI + 1]
|
|
1718
|
+
local part = parts[pI + 1] -- Lua 1-based
|
|
1573
1719
|
|
|
1574
1720
|
if injdef and S_DKEY == part then
|
|
1575
1721
|
part = getprop(injdef, S_key)
|
|
1576
|
-
elseif injdef and part and #part > 5 and part:sub(1, 5) ==
|
|
1722
|
+
elseif injdef and part and #part > 5 and part:sub(1, 5) == "$GET:" then
|
|
1577
1723
|
-- $GET:path$ -> get store value, use as path part (strip trailing $)
|
|
1578
1724
|
part = stringify(getpath(src, slice(part, 5, -1)))
|
|
1579
|
-
elseif injdef and part and #part > 5 and part:sub(1, 5) ==
|
|
1725
|
+
elseif injdef and part and #part > 5 and part:sub(1, 5) == "$REF:" then
|
|
1580
1726
|
-- $REF:refpath$ -> get spec value, use as path part (strip trailing $)
|
|
1581
1727
|
part = stringify(getpath(getprop(store, S_DSPEC), slice(part, 5, -1)))
|
|
1582
|
-
elseif injdef and part and #part > 6 and part:sub(1, 6) ==
|
|
1728
|
+
elseif injdef and part and #part > 6 and part:sub(1, 6) == "$META:" then
|
|
1583
1729
|
-- $META:metapath$ -> get meta value, use as path part (strip trailing $)
|
|
1584
|
-
part = stringify(getpath(getprop(injdef,
|
|
1730
|
+
part = stringify(getpath(getprop(injdef, "meta"), slice(part, 6, -1)))
|
|
1585
1731
|
end
|
|
1586
1732
|
|
|
1587
|
-
-- $$ escapes $
|
|
1588
|
-
if part and type(part) == S_string then
|
|
1589
|
-
part = part:gsub(
|
|
1733
|
+
-- $$ escapes $ (skip the pattern gsub for the common no-"$$" segment)
|
|
1734
|
+
if part and type(part) == S_string and part:find("$$", 1, true) then
|
|
1735
|
+
part = part:gsub("%$%$", "$")
|
|
1590
1736
|
end
|
|
1591
1737
|
|
|
1592
1738
|
if S_MT == part then
|
|
@@ -1631,7 +1777,7 @@ getpath = function(store, path, injdef)
|
|
|
1631
1777
|
end
|
|
1632
1778
|
|
|
1633
1779
|
-- Injdef may provide a custom handler to modify found value.
|
|
1634
|
-
local handler = getprop(injdef,
|
|
1780
|
+
local handler = getprop(injdef, "handler")
|
|
1635
1781
|
if nil ~= injdef and isfunc(handler) then
|
|
1636
1782
|
local ref = pathify(path)
|
|
1637
1783
|
val = handler(injdef, val, ref, store)
|
|
@@ -1640,7 +1786,6 @@ getpath = function(store, path, injdef)
|
|
|
1640
1786
|
return val
|
|
1641
1787
|
end
|
|
1642
1788
|
|
|
1643
|
-
|
|
1644
1789
|
-- Injection "class" for managing injection state.
|
|
1645
1790
|
-- Methods: descend, child, setval
|
|
1646
1791
|
|
|
@@ -1672,21 +1817,37 @@ function Injection:new(val, parent)
|
|
|
1672
1817
|
return o
|
|
1673
1818
|
end
|
|
1674
1819
|
|
|
1675
|
-
|
|
1676
1820
|
function Injection:__tostring()
|
|
1677
|
-
return
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1821
|
+
return "INJ"
|
|
1822
|
+
.. S_CN
|
|
1823
|
+
.. pad(pathify(self.path, 1))
|
|
1824
|
+
.. (MODENAME[self.mode] or "")
|
|
1825
|
+
.. (self.full and "/full" or "")
|
|
1826
|
+
.. S_CN
|
|
1827
|
+
.. "key="
|
|
1828
|
+
.. self.keyI
|
|
1829
|
+
.. S_FS
|
|
1830
|
+
.. tostring(self.key)
|
|
1831
|
+
.. S_FS
|
|
1832
|
+
.. S_OS
|
|
1833
|
+
.. table.concat(self.keys, ",")
|
|
1834
|
+
.. S_CS
|
|
1835
|
+
.. " p="
|
|
1836
|
+
.. stringify(self.parent, -1, 1)
|
|
1837
|
+
.. " m="
|
|
1838
|
+
.. stringify(self.meta, -1, 1)
|
|
1839
|
+
.. " d/"
|
|
1840
|
+
.. pathify(self.dpath, 1)
|
|
1841
|
+
.. "="
|
|
1842
|
+
.. stringify(self.dparent, -1, 1)
|
|
1843
|
+
.. " r="
|
|
1844
|
+
.. stringify(getprop(getprop(self.nodes, 0), S_DTOP), -1, 1)
|
|
1685
1845
|
end
|
|
1686
1846
|
|
|
1687
|
-
|
|
1688
1847
|
function Injection:descend()
|
|
1689
|
-
if self.meta.__d == nil then
|
|
1848
|
+
if self.meta.__d == nil then
|
|
1849
|
+
self.meta.__d = 0
|
|
1850
|
+
end
|
|
1690
1851
|
self.meta.__d = self.meta.__d + 1
|
|
1691
1852
|
|
|
1692
1853
|
local parentkey = getelem(self.path, -2)
|
|
@@ -1700,7 +1861,7 @@ function Injection:descend()
|
|
|
1700
1861
|
self.dparent = getprop(self.dparent, parentkey)
|
|
1701
1862
|
|
|
1702
1863
|
local lastpart = getelem(self.dpath, -1)
|
|
1703
|
-
if lastpart ==
|
|
1864
|
+
if lastpart == "$:" .. tostring(parentkey) then
|
|
1704
1865
|
self.dpath = slice(self.dpath, -1)
|
|
1705
1866
|
else
|
|
1706
1867
|
self.dpath = flatten({ self.dpath, parentkey })
|
|
@@ -1711,9 +1872,8 @@ function Injection:descend()
|
|
|
1711
1872
|
return self.dparent
|
|
1712
1873
|
end
|
|
1713
1874
|
|
|
1714
|
-
|
|
1715
1875
|
function Injection:child(keyI, keys)
|
|
1716
|
-
local key = strkey(keys[keyI + 1])
|
|
1876
|
+
local key = strkey(keys[keyI + 1]) -- Lua 1-based
|
|
1717
1877
|
local val = self.val
|
|
1718
1878
|
|
|
1719
1879
|
local cinj = Injection:new(getprop(val, key), val)
|
|
@@ -1738,9 +1898,8 @@ function Injection:child(keyI, keys)
|
|
|
1738
1898
|
return cinj
|
|
1739
1899
|
end
|
|
1740
1900
|
|
|
1741
|
-
|
|
1742
1901
|
function Injection:setval(val, ancestor)
|
|
1743
|
-
local parent
|
|
1902
|
+
local parent
|
|
1744
1903
|
if ancestor == nil or ancestor < 2 then
|
|
1745
1904
|
if NONE == val then
|
|
1746
1905
|
self.parent = delprop(self.parent, self.key)
|
|
@@ -1760,7 +1919,6 @@ function Injection:setval(val, ancestor)
|
|
|
1760
1919
|
return parent
|
|
1761
1920
|
end
|
|
1762
1921
|
|
|
1763
|
-
|
|
1764
1922
|
-- Inject values from a data store into a node recursively.
|
|
1765
1923
|
-- @param val (any) The value to inject into
|
|
1766
1924
|
-- @param store (table) The data store
|
|
@@ -1808,7 +1966,7 @@ local function inject(val, store, injdef)
|
|
|
1808
1966
|
else
|
|
1809
1967
|
nodekeys = {}
|
|
1810
1968
|
for i = 1, #val do
|
|
1811
|
-
table.insert(nodekeys, i - 1)
|
|
1969
|
+
table.insert(nodekeys, i - 1) -- 0-based indices
|
|
1812
1970
|
end
|
|
1813
1971
|
end
|
|
1814
1972
|
|
|
@@ -1833,10 +1991,6 @@ local function inject(val, store, injdef)
|
|
|
1833
1991
|
-- Perform val mode injection
|
|
1834
1992
|
inject(childinj.val, store, childinj)
|
|
1835
1993
|
|
|
1836
|
-
-- The injection may modify child processing.
|
|
1837
|
-
nkI = childinj.keyI
|
|
1838
|
-
nodekeys = childinj.keys
|
|
1839
|
-
|
|
1840
1994
|
-- Perform key:post mode injection
|
|
1841
1995
|
childinj.mode = M_KEYPOST
|
|
1842
1996
|
_injectstr(nodekey, store, childinj)
|
|
@@ -1847,7 +2001,6 @@ local function inject(val, store, injdef)
|
|
|
1847
2001
|
|
|
1848
2002
|
nkI = nkI + 1
|
|
1849
2003
|
end
|
|
1850
|
-
|
|
1851
2004
|
elseif S_string == valtype then
|
|
1852
2005
|
inj.mode = M_VAL
|
|
1853
2006
|
val = _injectstr(val, store, inj)
|
|
@@ -1869,17 +2022,15 @@ local function inject(val, store, injdef)
|
|
|
1869
2022
|
return getprop(inj.parent, S_DTOP)
|
|
1870
2023
|
end
|
|
1871
2024
|
|
|
1872
|
-
|
|
1873
2025
|
-- Delete a key from a map or list.
|
|
1874
2026
|
local function transform_DELETE(inj)
|
|
1875
2027
|
inj:setval(NONE)
|
|
1876
2028
|
return NONE
|
|
1877
2029
|
end
|
|
1878
2030
|
|
|
1879
|
-
|
|
1880
2031
|
-- Copy value from source data.
|
|
1881
2032
|
local function transform_COPY(inj, _val)
|
|
1882
|
-
local ijname =
|
|
2033
|
+
local ijname = "COPY"
|
|
1883
2034
|
|
|
1884
2035
|
if not checkPlacement(M_VAL, ijname, T_any, inj) then
|
|
1885
2036
|
return NONE
|
|
@@ -1890,7 +2041,6 @@ local function transform_COPY(inj, _val)
|
|
|
1890
2041
|
return out
|
|
1891
2042
|
end
|
|
1892
2043
|
|
|
1893
|
-
|
|
1894
2044
|
-- As a value, inject the key of the parent node.
|
|
1895
2045
|
local function transform_KEY(inj)
|
|
1896
2046
|
local mode, path, parent = inj.mode, inj.path, inj.parent
|
|
@@ -1909,14 +2059,12 @@ local function transform_KEY(inj)
|
|
|
1909
2059
|
return getprop(getprop(parent, S_BANNO), S_KEY, getelem(path, -2))
|
|
1910
2060
|
end
|
|
1911
2061
|
|
|
1912
|
-
|
|
1913
2062
|
-- Store annotation data about a node.
|
|
1914
2063
|
local function transform_ANNO(inj)
|
|
1915
2064
|
delprop(inj.parent, S_BANNO)
|
|
1916
2065
|
return NONE
|
|
1917
2066
|
end
|
|
1918
2067
|
|
|
1919
|
-
|
|
1920
2068
|
-- Merge a list of objects into the current object.
|
|
1921
2069
|
local function transform_MERGE(inj)
|
|
1922
2070
|
local mode, key, parent = inj.mode, inj.key, inj.parent
|
|
@@ -1925,7 +2073,6 @@ local function transform_MERGE(inj)
|
|
|
1925
2073
|
|
|
1926
2074
|
if M_KEYPRE == mode then
|
|
1927
2075
|
out = key
|
|
1928
|
-
|
|
1929
2076
|
elseif M_KEYPOST == mode then
|
|
1930
2077
|
out = key
|
|
1931
2078
|
|
|
@@ -1946,7 +2093,6 @@ local function transform_MERGE(inj)
|
|
|
1946
2093
|
return out
|
|
1947
2094
|
end
|
|
1948
2095
|
|
|
1949
|
-
|
|
1950
2096
|
-- Helper: injectChild
|
|
1951
2097
|
local function injectChild(child, store, inj)
|
|
1952
2098
|
local cinj = inj
|
|
@@ -1967,11 +2113,10 @@ local function injectChild(child, store, inj)
|
|
|
1967
2113
|
return cinj
|
|
1968
2114
|
end
|
|
1969
2115
|
|
|
1970
|
-
|
|
1971
2116
|
-- Convert a node to a list.
|
|
1972
2117
|
-- Format: ['`$EACH`', '`source-path-of-node`', child-template]
|
|
1973
2118
|
local function transform_EACH(inj, _val, _ref, store)
|
|
1974
|
-
local ijname =
|
|
2119
|
+
local ijname = "EACH"
|
|
1975
2120
|
|
|
1976
2121
|
if not checkPlacement(M_VAL, ijname, T_list, inj) then
|
|
1977
2122
|
return NONE
|
|
@@ -1980,14 +2125,18 @@ local function transform_EACH(inj, _val, _ref, store)
|
|
|
1980
2125
|
-- Remove remaining keys to avoid spurious processing.
|
|
1981
2126
|
local trimmed = slice(inj.keys, 0, 1)
|
|
1982
2127
|
-- Replace keys in-place
|
|
1983
|
-
for i = #inj.keys, 1, -1 do
|
|
1984
|
-
|
|
2128
|
+
for i = #inj.keys, 1, -1 do
|
|
2129
|
+
inj.keys[i] = nil
|
|
2130
|
+
end
|
|
2131
|
+
for i, v in ipairs(trimmed) do
|
|
2132
|
+
inj.keys[i] = v
|
|
2133
|
+
end
|
|
1985
2134
|
|
|
1986
2135
|
-- Get arguments: ['`$EACH`', 'source-path', child-template]
|
|
1987
2136
|
local each_args = injectorArgs({ T_string, T_any }, slice(inj.parent, 1))
|
|
1988
2137
|
local err = each_args[1]
|
|
1989
2138
|
if NONE ~= err then
|
|
1990
|
-
table.insert(inj.errs,
|
|
2139
|
+
table.insert(inj.errs, "$" .. ijname .. ": " .. err)
|
|
1991
2140
|
return NONE
|
|
1992
2141
|
end
|
|
1993
2142
|
local srcpath = each_args[2]
|
|
@@ -1998,12 +2147,14 @@ local function transform_EACH(inj, _val, _ref, store)
|
|
|
1998
2147
|
local src = getpath(srcstore, srcpath, inj)
|
|
1999
2148
|
local srctype = typify(src)
|
|
2000
2149
|
|
|
2001
|
-
local tcur
|
|
2150
|
+
local tcur
|
|
2002
2151
|
local tval = {}
|
|
2003
2152
|
setmetatable(tval, { __jsontype = "array" })
|
|
2004
2153
|
|
|
2005
2154
|
local tkey = getelem(inj.path, -2)
|
|
2006
|
-
local target = getelem(inj.nodes, -2, function()
|
|
2155
|
+
local target = getelem(inj.nodes, -2, function()
|
|
2156
|
+
return getelem(inj.nodes, -1)
|
|
2157
|
+
end)
|
|
2007
2158
|
|
|
2008
2159
|
-- Create clones of the child template for each value of the source.
|
|
2009
2160
|
if 0 < (T_list & srctype) then
|
|
@@ -2025,7 +2176,9 @@ local function transform_EACH(inj, _val, _ref, store)
|
|
|
2025
2176
|
local srcvals = {}
|
|
2026
2177
|
setmetatable(srcvals, { __jsontype = "array" })
|
|
2027
2178
|
if islist(src) then
|
|
2028
|
-
for i = 1, #src do
|
|
2179
|
+
for i = 1, #src do
|
|
2180
|
+
table.insert(srcvals, src[i])
|
|
2181
|
+
end
|
|
2029
2182
|
elseif ismap(src) then
|
|
2030
2183
|
for _, item in ipairs(items(src)) do
|
|
2031
2184
|
table.insert(srcvals, item[2])
|
|
@@ -2042,14 +2195,14 @@ local function transform_EACH(inj, _val, _ref, store)
|
|
|
2042
2195
|
table.insert(srcparts, p)
|
|
2043
2196
|
end
|
|
2044
2197
|
end
|
|
2045
|
-
local dpath = flatten({ S_DTOP, srcparts,
|
|
2198
|
+
local dpath = flatten({ S_DTOP, srcparts, "$:" .. tostring(ckey) })
|
|
2046
2199
|
|
|
2047
2200
|
tcur = { [ckey] = srcvals }
|
|
2048
2201
|
|
|
2049
2202
|
if 1 < size(tpath) then
|
|
2050
2203
|
local pkey = getelem(inj.path, -3, S_DTOP)
|
|
2051
2204
|
tcur = { [pkey] = tcur }
|
|
2052
|
-
table.insert(dpath,
|
|
2205
|
+
table.insert(dpath, "$:" .. tostring(pkey))
|
|
2053
2206
|
end
|
|
2054
2207
|
|
|
2055
2208
|
local tinj = inj:child(0, { ckey })
|
|
@@ -2071,14 +2224,12 @@ local function transform_EACH(inj, _val, _ref, store)
|
|
|
2071
2224
|
return getelem(rval, 0)
|
|
2072
2225
|
end
|
|
2073
2226
|
|
|
2074
|
-
|
|
2075
2227
|
-- Convert a node to a map.
|
|
2076
2228
|
-- Format: { '`$PACK`':['`source-path`', child-template]}
|
|
2077
2229
|
local function transform_PACK(inj, _val, _ref, store)
|
|
2078
|
-
local
|
|
2079
|
-
inj.parent, inj.nodes
|
|
2230
|
+
local key, path, parent, nodes = inj.key, inj.path, inj.parent, inj.nodes
|
|
2080
2231
|
|
|
2081
|
-
local ijname =
|
|
2232
|
+
local ijname = "EACH"
|
|
2082
2233
|
|
|
2083
2234
|
if not checkPlacement(M_KEYPRE, ijname, T_map, inj) then
|
|
2084
2235
|
return NONE
|
|
@@ -2089,7 +2240,7 @@ local function transform_PACK(inj, _val, _ref, store)
|
|
|
2089
2240
|
local pack_args = injectorArgs({ T_string, T_any }, args)
|
|
2090
2241
|
local err = pack_args[1]
|
|
2091
2242
|
if NONE ~= err then
|
|
2092
|
-
table.insert(inj.errs,
|
|
2243
|
+
table.insert(inj.errs, "$" .. ijname .. ": " .. err)
|
|
2093
2244
|
return NONE
|
|
2094
2245
|
end
|
|
2095
2246
|
local srcpath = pack_args[2]
|
|
@@ -2167,7 +2318,7 @@ local function transform_PACK(inj, _val, _ref, store)
|
|
|
2167
2318
|
local srcnode = item[2]
|
|
2168
2319
|
local kn
|
|
2169
2320
|
if keypath == nil then
|
|
2170
|
-
kn = srcI - 1
|
|
2321
|
+
kn = srcI - 1 -- 0-based
|
|
2171
2322
|
elseif type(keypath) == S_string and keypath:sub(1, 1) == S_BT then
|
|
2172
2323
|
kn = inject(keypath, merge({ {}, store, { [S_DTOP] = srcnode } }, 1))
|
|
2173
2324
|
else
|
|
@@ -2185,14 +2336,14 @@ local function transform_PACK(inj, _val, _ref, store)
|
|
|
2185
2336
|
table.insert(srcparts, p)
|
|
2186
2337
|
end
|
|
2187
2338
|
end
|
|
2188
|
-
local dpath = flatten({ S_DTOP, srcparts,
|
|
2339
|
+
local dpath = flatten({ S_DTOP, srcparts, "$:" .. tostring(ckey) })
|
|
2189
2340
|
|
|
2190
2341
|
local tcur = { [ckey] = tsrc }
|
|
2191
2342
|
|
|
2192
2343
|
if 1 < size(tpath) then
|
|
2193
2344
|
local pkey = getelem(inj.path, -3, S_DTOP)
|
|
2194
2345
|
tcur = { [pkey] = tcur }
|
|
2195
|
-
table.insert(dpath,
|
|
2346
|
+
table.insert(dpath, "$:" .. tostring(pkey))
|
|
2196
2347
|
end
|
|
2197
2348
|
|
|
2198
2349
|
local tinj = inj:child(0, { ckey })
|
|
@@ -2213,15 +2364,13 @@ local function transform_PACK(inj, _val, _ref, store)
|
|
|
2213
2364
|
return NONE
|
|
2214
2365
|
end
|
|
2215
2366
|
|
|
2216
|
-
|
|
2217
2367
|
-- Placement labels for error messages.
|
|
2218
2368
|
local PLACEMENT = {
|
|
2219
|
-
[M_VAL] =
|
|
2369
|
+
[M_VAL] = "value",
|
|
2220
2370
|
[M_KEYPRE] = S_key,
|
|
2221
2371
|
[M_KEYPOST] = S_key,
|
|
2222
2372
|
}
|
|
2223
2373
|
|
|
2224
|
-
|
|
2225
2374
|
-- Check that a transform is used in the correct mode and parent type.
|
|
2226
2375
|
checkPlacement = function(modes, ijname, parentTypes, inj)
|
|
2227
2376
|
if 0 == (modes & inj.mode) then
|
|
@@ -2232,35 +2381,55 @@ checkPlacement = function(modes, ijname, parentTypes, inj)
|
|
|
2232
2381
|
table.insert(expected, PLACEMENT[m])
|
|
2233
2382
|
end
|
|
2234
2383
|
end
|
|
2235
|
-
table.insert(
|
|
2236
|
-
|
|
2237
|
-
|
|
2384
|
+
table.insert(
|
|
2385
|
+
inj.errs,
|
|
2386
|
+
"$"
|
|
2387
|
+
.. ijname
|
|
2388
|
+
.. ": invalid placement as "
|
|
2389
|
+
.. PLACEMENT[inj.mode]
|
|
2390
|
+
.. ", expected: "
|
|
2391
|
+
.. table.concat(expected, ",")
|
|
2392
|
+
.. "."
|
|
2393
|
+
)
|
|
2238
2394
|
return false
|
|
2239
2395
|
end
|
|
2240
2396
|
if not isempty(parentTypes) then
|
|
2241
2397
|
local ptype = typify(inj.parent)
|
|
2242
2398
|
if 0 == (parentTypes & ptype) then
|
|
2243
|
-
table.insert(
|
|
2244
|
-
|
|
2399
|
+
table.insert(
|
|
2400
|
+
inj.errs,
|
|
2401
|
+
"$"
|
|
2402
|
+
.. ijname
|
|
2403
|
+
.. ": invalid placement in parent "
|
|
2404
|
+
.. typename(ptype)
|
|
2405
|
+
.. ", expected: "
|
|
2406
|
+
.. typename(parentTypes)
|
|
2407
|
+
.. "."
|
|
2408
|
+
)
|
|
2245
2409
|
return false
|
|
2246
2410
|
end
|
|
2247
2411
|
end
|
|
2248
2412
|
return true
|
|
2249
2413
|
end
|
|
2250
2414
|
|
|
2251
|
-
|
|
2252
2415
|
-- Validate and extract typed arguments from a list.
|
|
2253
2416
|
injectorArgs = function(argTypes, args)
|
|
2254
2417
|
local numargs = size(argTypes)
|
|
2255
2418
|
local found = {}
|
|
2256
|
-
found[1] = NONE
|
|
2419
|
+
found[1] = NONE -- err slot (1-based)
|
|
2257
2420
|
for argI = 1, numargs do
|
|
2258
|
-
local arg = getprop(args, argI - 1)
|
|
2421
|
+
local arg = getprop(args, argI - 1) -- 0-based access
|
|
2259
2422
|
local argType = typify(arg)
|
|
2260
2423
|
if 0 == (argTypes[argI] & argType) then
|
|
2261
|
-
found[1] =
|
|
2262
|
-
|
|
2263
|
-
|
|
2424
|
+
found[1] = "invalid argument: "
|
|
2425
|
+
.. stringify(arg, 22)
|
|
2426
|
+
.. " ("
|
|
2427
|
+
.. typename(argType)
|
|
2428
|
+
.. " at position "
|
|
2429
|
+
.. argI
|
|
2430
|
+
.. ") is not of type: "
|
|
2431
|
+
.. typename(argTypes[argI])
|
|
2432
|
+
.. "."
|
|
2264
2433
|
break
|
|
2265
2434
|
end
|
|
2266
2435
|
found[1 + argI] = arg
|
|
@@ -2268,7 +2437,6 @@ injectorArgs = function(argTypes, args)
|
|
|
2268
2437
|
return found
|
|
2269
2438
|
end
|
|
2270
2439
|
|
|
2271
|
-
|
|
2272
2440
|
-- Transform: resolve a reference to another part of the spec.
|
|
2273
2441
|
-- Format: ['`$REF`', 'ref-path']
|
|
2274
2442
|
local function transform_REF(inj, val, _ref, store)
|
|
@@ -2295,7 +2463,7 @@ local function transform_REF(inj, val, _ref, store)
|
|
|
2295
2463
|
local hasSubRef = false
|
|
2296
2464
|
if isnode(ref) then
|
|
2297
2465
|
walk(ref, function(_k, v)
|
|
2298
|
-
if
|
|
2466
|
+
if "`$REF`" == v then
|
|
2299
2467
|
hasSubRef = true
|
|
2300
2468
|
end
|
|
2301
2469
|
return v
|
|
@@ -2308,7 +2476,7 @@ local function transform_REF(inj, val, _ref, store)
|
|
|
2308
2476
|
local tpath = slice(inj.path, -1)
|
|
2309
2477
|
local tcur = getpath(store, cpath)
|
|
2310
2478
|
local tval = getpath(store, tpath)
|
|
2311
|
-
local rval
|
|
2479
|
+
local rval
|
|
2312
2480
|
|
|
2313
2481
|
if not hasSubRef or NONE ~= tval then
|
|
2314
2482
|
local tinj = inj:child(0, { getelem(tpath, -1) })
|
|
@@ -2337,10 +2505,11 @@ local function transform_REF(inj, val, _ref, store)
|
|
|
2337
2505
|
return val
|
|
2338
2506
|
end
|
|
2339
2507
|
|
|
2340
|
-
|
|
2341
2508
|
-- Named formatters for transform_FORMAT.
|
|
2342
2509
|
local FORMATTER = {
|
|
2343
|
-
identity = function(_k, v)
|
|
2510
|
+
identity = function(_k, v)
|
|
2511
|
+
return v
|
|
2512
|
+
end,
|
|
2344
2513
|
upper = function(_k, v)
|
|
2345
2514
|
return isnode(v) and v or string.upper(tostring(v))
|
|
2346
2515
|
end,
|
|
@@ -2351,14 +2520,20 @@ local FORMATTER = {
|
|
|
2351
2520
|
return isnode(v) and v or tostring(v)
|
|
2352
2521
|
end,
|
|
2353
2522
|
number = function(_k, v)
|
|
2354
|
-
if isnode(v) then
|
|
2523
|
+
if isnode(v) then
|
|
2524
|
+
return v
|
|
2525
|
+
end
|
|
2355
2526
|
local n = tonumber(v)
|
|
2356
2527
|
return (n == nil or n ~= n) and 0 or n
|
|
2357
2528
|
end,
|
|
2358
2529
|
integer = function(_k, v)
|
|
2359
|
-
if isnode(v) then
|
|
2530
|
+
if isnode(v) then
|
|
2531
|
+
return v
|
|
2532
|
+
end
|
|
2360
2533
|
local n = tonumber(v)
|
|
2361
|
-
if n == nil or n ~= n then
|
|
2534
|
+
if n == nil or n ~= n then
|
|
2535
|
+
n = 0
|
|
2536
|
+
end
|
|
2362
2537
|
return math.floor(n)
|
|
2363
2538
|
end,
|
|
2364
2539
|
concat = function(k, v)
|
|
@@ -2366,7 +2541,7 @@ local FORMATTER = {
|
|
|
2366
2541
|
local parts = {}
|
|
2367
2542
|
for _, item in ipairs(items(v)) do
|
|
2368
2543
|
local val = item[2]
|
|
2369
|
-
table.insert(parts, isnode(val) and
|
|
2544
|
+
table.insert(parts, isnode(val) and "" or tostring(val))
|
|
2370
2545
|
end
|
|
2371
2546
|
return table.concat(parts)
|
|
2372
2547
|
end
|
|
@@ -2374,7 +2549,6 @@ local FORMATTER = {
|
|
|
2374
2549
|
end,
|
|
2375
2550
|
}
|
|
2376
2551
|
|
|
2377
|
-
|
|
2378
2552
|
-- Transform: format values using named formatters.
|
|
2379
2553
|
-- Format: ['`$FORMAT`', 'name', child]
|
|
2380
2554
|
local function transform_FORMAT(inj, _val, _ref, store)
|
|
@@ -2391,7 +2565,9 @@ local function transform_FORMAT(inj, _val, _ref, store)
|
|
|
2391
2565
|
|
|
2392
2566
|
-- Source data.
|
|
2393
2567
|
local tkey = getelem(inj.path, -2)
|
|
2394
|
-
local target = getelem(inj.nodes, -2, function()
|
|
2568
|
+
local target = getelem(inj.nodes, -2, function()
|
|
2569
|
+
return getelem(inj.nodes, -1)
|
|
2570
|
+
end)
|
|
2395
2571
|
|
|
2396
2572
|
local cinj = injectChild(child, store, inj)
|
|
2397
2573
|
local resolved = cinj.val
|
|
@@ -2399,7 +2575,7 @@ local function transform_FORMAT(inj, _val, _ref, store)
|
|
|
2399
2575
|
local formatter = (0 < (T_function & typify(name))) and name or getprop(FORMATTER, name)
|
|
2400
2576
|
|
|
2401
2577
|
if NONE == formatter then
|
|
2402
|
-
table.insert(inj.errs,
|
|
2578
|
+
table.insert(inj.errs, "$FORMAT: unknown format: " .. tostring(name) .. ".")
|
|
2403
2579
|
return NONE
|
|
2404
2580
|
end
|
|
2405
2581
|
|
|
@@ -2410,11 +2586,10 @@ local function transform_FORMAT(inj, _val, _ref, store)
|
|
|
2410
2586
|
return out
|
|
2411
2587
|
end
|
|
2412
2588
|
|
|
2413
|
-
|
|
2414
2589
|
-- Apply a function to a value.
|
|
2415
2590
|
-- Format: ['`$APPLY`', function, child]
|
|
2416
2591
|
local function transform_APPLY(inj, _val, _ref, store)
|
|
2417
|
-
local ijname =
|
|
2592
|
+
local ijname = "APPLY"
|
|
2418
2593
|
|
|
2419
2594
|
if not checkPlacement(M_VAL, ijname, T_list, inj) then
|
|
2420
2595
|
return NONE
|
|
@@ -2423,12 +2598,14 @@ local function transform_APPLY(inj, _val, _ref, store)
|
|
|
2423
2598
|
local found = injectorArgs({ T_function, T_any }, slice(inj.parent, 1))
|
|
2424
2599
|
local err, apply, child = found[1], found[2], found[3]
|
|
2425
2600
|
if NONE ~= err then
|
|
2426
|
-
table.insert(inj.errs,
|
|
2601
|
+
table.insert(inj.errs, "$" .. ijname .. ": " .. err)
|
|
2427
2602
|
return NONE
|
|
2428
2603
|
end
|
|
2429
2604
|
|
|
2430
2605
|
local tkey = getelem(inj.path, -2)
|
|
2431
|
-
local target = getelem(inj.nodes, -2, function()
|
|
2606
|
+
local target = getelem(inj.nodes, -2, function()
|
|
2607
|
+
return getelem(inj.nodes, -1)
|
|
2608
|
+
end)
|
|
2432
2609
|
|
|
2433
2610
|
local cinj = injectChild(child, store, inj)
|
|
2434
2611
|
local resolved = cinj.val
|
|
@@ -2439,7 +2616,6 @@ local function transform_APPLY(inj, _val, _ref, store)
|
|
|
2439
2616
|
return out
|
|
2440
2617
|
end
|
|
2441
2618
|
|
|
2442
|
-
|
|
2443
2619
|
-- Transform data using spec.
|
|
2444
2620
|
-- @param data (any) Source data to transform
|
|
2445
2621
|
-- @param spec (any) Transform specification
|
|
@@ -2480,51 +2656,58 @@ local function transform(data, spec, injdef)
|
|
|
2480
2656
|
{
|
|
2481
2657
|
[S_DTOP] = dataClone,
|
|
2482
2658
|
|
|
2483
|
-
[S_DSPEC] = function()
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
[
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
[
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
[
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
[
|
|
2498
|
-
[
|
|
2659
|
+
[S_DSPEC] = function()
|
|
2660
|
+
return origspec
|
|
2661
|
+
end,
|
|
2662
|
+
|
|
2663
|
+
["$BT"] = function()
|
|
2664
|
+
return S_BT
|
|
2665
|
+
end,
|
|
2666
|
+
["$DS"] = function()
|
|
2667
|
+
return S_DS
|
|
2668
|
+
end,
|
|
2669
|
+
["$WHEN"] = function()
|
|
2670
|
+
return os.date("!%Y-%m-%dT%H:%M:%S.000Z")
|
|
2671
|
+
end,
|
|
2672
|
+
|
|
2673
|
+
["$DELETE"] = transform_DELETE,
|
|
2674
|
+
["$COPY"] = transform_COPY,
|
|
2675
|
+
["$KEY"] = transform_KEY,
|
|
2676
|
+
["$ANNO"] = transform_ANNO,
|
|
2677
|
+
["$MERGE"] = transform_MERGE,
|
|
2678
|
+
["$EACH"] = transform_EACH,
|
|
2679
|
+
["$PACK"] = transform_PACK,
|
|
2680
|
+
["$REF"] = transform_REF,
|
|
2681
|
+
["$FORMAT"] = transform_FORMAT,
|
|
2682
|
+
["$APPLY"] = transform_APPLY,
|
|
2499
2683
|
},
|
|
2500
2684
|
extraTransforms,
|
|
2501
|
-
{ [
|
|
2685
|
+
{ ["$ERRS"] = errs },
|
|
2502
2686
|
}, 1)
|
|
2503
2687
|
|
|
2504
2688
|
local out = inject(spec, store, injdef)
|
|
2505
2689
|
|
|
2506
2690
|
local generr = 0 < size(errs) and not collect
|
|
2507
2691
|
if generr then
|
|
2508
|
-
error(table.concat(errs,
|
|
2692
|
+
error(table.concat(errs, " | "))
|
|
2509
2693
|
end
|
|
2510
2694
|
|
|
2511
2695
|
return out
|
|
2512
2696
|
end
|
|
2513
2697
|
|
|
2514
|
-
|
|
2515
2698
|
-- A required string value. NOTE: Rejects empty strings.
|
|
2516
2699
|
local function validate_STRING(inj)
|
|
2517
2700
|
local out = getprop(inj.dparent, inj.key)
|
|
2518
2701
|
|
|
2519
2702
|
local t = typify(out)
|
|
2520
2703
|
if 0 == (T_string & t) then
|
|
2521
|
-
local msg = _invalidTypeMsg(inj.path, S_string, t, out,
|
|
2704
|
+
local msg = _invalidTypeMsg(inj.path, S_string, t, out, "V1010")
|
|
2522
2705
|
table.insert(inj.errs, msg)
|
|
2523
2706
|
return NONE
|
|
2524
2707
|
end
|
|
2525
2708
|
|
|
2526
2709
|
if S_MT == out then
|
|
2527
|
-
local msg =
|
|
2710
|
+
local msg = "Empty string at " .. pathify(inj.path, 1)
|
|
2528
2711
|
table.insert(inj.errs, msg)
|
|
2529
2712
|
return NONE
|
|
2530
2713
|
end
|
|
@@ -2532,7 +2715,6 @@ local function validate_STRING(inj)
|
|
|
2532
2715
|
return out
|
|
2533
2716
|
end
|
|
2534
2717
|
|
|
2535
|
-
|
|
2536
2718
|
-- A generic type validator. Ref is used to determine which type to check.
|
|
2537
2719
|
local function validate_TYPE(inj, _val, ref)
|
|
2538
2720
|
local tname = slice(ref, 1):lower()
|
|
@@ -2555,27 +2737,24 @@ local function validate_TYPE(inj, _val, ref)
|
|
|
2555
2737
|
|
|
2556
2738
|
local t = typify(out)
|
|
2557
2739
|
if 0 == (t & typev) then
|
|
2558
|
-
table.insert(inj.errs, _invalidTypeMsg(inj.path, tname, t, out,
|
|
2740
|
+
table.insert(inj.errs, _invalidTypeMsg(inj.path, tname, t, out, "V1001"))
|
|
2559
2741
|
return NONE
|
|
2560
2742
|
end
|
|
2561
2743
|
|
|
2562
2744
|
return out
|
|
2563
2745
|
end
|
|
2564
2746
|
|
|
2565
|
-
|
|
2566
2747
|
-- Allow any value.
|
|
2567
2748
|
local function validate_ANY(inj)
|
|
2568
2749
|
local out = getprop(inj.dparent, inj.key)
|
|
2569
2750
|
return out
|
|
2570
2751
|
end
|
|
2571
2752
|
|
|
2572
|
-
|
|
2573
2753
|
-- Specify child values for map or list.
|
|
2574
2754
|
-- Map syntax: {'`$CHILD`': child-template }
|
|
2575
2755
|
-- List syntax: ['`$CHILD`', child-template ]
|
|
2576
2756
|
local function validate_CHILD(inj)
|
|
2577
|
-
local mode, key, parent, keys, path = inj.mode, inj.key, inj.parent,
|
|
2578
|
-
inj.keys, inj.path
|
|
2757
|
+
local mode, key, parent, keys, path = inj.mode, inj.key, inj.parent, inj.keys, inj.path
|
|
2579
2758
|
|
|
2580
2759
|
-- Map syntax.
|
|
2581
2760
|
if M_KEYPRE == mode then
|
|
@@ -2588,8 +2767,10 @@ local function validate_CHILD(inj)
|
|
|
2588
2767
|
if NONE == tval then
|
|
2589
2768
|
tval = {}
|
|
2590
2769
|
elseif not ismap(tval) then
|
|
2591
|
-
table.insert(
|
|
2592
|
-
|
|
2770
|
+
table.insert(
|
|
2771
|
+
inj.errs,
|
|
2772
|
+
_invalidTypeMsg(slice(inj.path, 0, -1), S_object, typify(tval), tval, "V0220")
|
|
2773
|
+
)
|
|
2593
2774
|
return NONE
|
|
2594
2775
|
end
|
|
2595
2776
|
|
|
@@ -2610,7 +2791,7 @@ local function validate_CHILD(inj)
|
|
|
2610
2791
|
if M_VAL == mode then
|
|
2611
2792
|
if not islist(parent) then
|
|
2612
2793
|
-- $CHILD was not inside a list.
|
|
2613
|
-
table.insert(inj.errs,
|
|
2794
|
+
table.insert(inj.errs, "Invalid $CHILD as value")
|
|
2614
2795
|
return NONE
|
|
2615
2796
|
end
|
|
2616
2797
|
|
|
@@ -2623,34 +2804,45 @@ local function validate_CHILD(inj)
|
|
|
2623
2804
|
end
|
|
2624
2805
|
|
|
2625
2806
|
if not islist(inj.dparent) then
|
|
2626
|
-
local msg =
|
|
2627
|
-
slice(inj.path, 0, -1), S_list, typify(inj.dparent), inj.dparent,
|
|
2807
|
+
local msg =
|
|
2808
|
+
_invalidTypeMsg(slice(inj.path, 0, -1), S_list, typify(inj.dparent), inj.dparent, "V0230")
|
|
2628
2809
|
table.insert(inj.errs, msg)
|
|
2629
2810
|
inj.keyI = size(parent)
|
|
2630
2811
|
return inj.dparent
|
|
2631
2812
|
end
|
|
2632
2813
|
|
|
2633
2814
|
-- Clone children and reset inj key index.
|
|
2815
|
+
-- The inject child loop will now iterate over the cloned children,
|
|
2816
|
+
-- validating them against the current list values.
|
|
2634
2817
|
for i = 1, #inj.dparent do
|
|
2635
2818
|
parent[i] = clone(childtm)
|
|
2636
2819
|
end
|
|
2637
2820
|
slice(parent, 0, #inj.dparent, true)
|
|
2638
|
-
inj.keyI = 0
|
|
2639
2821
|
|
|
2640
|
-
|
|
2641
|
-
|
|
2822
|
+
-- NOTE: modifying inj! This extends the child value loop in inject
|
|
2823
|
+
-- to cover every cloned child.
|
|
2824
|
+
for ckeyI = size(keys), size(parent) - 1 do
|
|
2825
|
+
table.insert(keys, ckeyI)
|
|
2826
|
+
end
|
|
2827
|
+
|
|
2828
|
+
-- Restart the child value loop at the first element (the loop
|
|
2829
|
+
-- increments keyI on resume) so that the first element is also
|
|
2830
|
+
-- validated against the child template.
|
|
2831
|
+
inj.keyI = -1
|
|
2832
|
+
|
|
2833
|
+
-- SKIP leaves the cloned child template in place at the first
|
|
2834
|
+
-- element so the resumed loop can validate it.
|
|
2835
|
+
return SKIP
|
|
2642
2836
|
end
|
|
2643
2837
|
|
|
2644
2838
|
return NONE
|
|
2645
2839
|
end
|
|
2646
2840
|
|
|
2647
|
-
|
|
2648
2841
|
----------------------------------------------------------
|
|
2649
2842
|
-- Forward declaration for validate to resolve lack of function hoisting
|
|
2650
2843
|
----------------------------------------------------------
|
|
2651
2844
|
local validate
|
|
2652
2845
|
|
|
2653
|
-
|
|
2654
2846
|
-- Match at least one of the specified shapes.
|
|
2655
2847
|
-- Syntax: ['`$ONE`', alt0, alt1, ...]
|
|
2656
2848
|
local function validate_ONE(inj, _val, _ref, store)
|
|
@@ -2659,9 +2851,12 @@ local function validate_ONE(inj, _val, _ref, store)
|
|
|
2659
2851
|
-- Only operate in val mode, since parent is a list.
|
|
2660
2852
|
if M_VAL == mode then
|
|
2661
2853
|
if not islist(parent) or 0 ~= keyI then
|
|
2662
|
-
table.insert(
|
|
2663
|
-
|
|
2664
|
-
|
|
2854
|
+
table.insert(
|
|
2855
|
+
inj.errs,
|
|
2856
|
+
"The $ONE validator at field "
|
|
2857
|
+
.. pathify(inj.path, 1, 1)
|
|
2858
|
+
.. " must be the first element of an array."
|
|
2859
|
+
)
|
|
2665
2860
|
return
|
|
2666
2861
|
end
|
|
2667
2862
|
|
|
@@ -2675,9 +2870,12 @@ local function validate_ONE(inj, _val, _ref, store)
|
|
|
2675
2870
|
|
|
2676
2871
|
local tvals = slice(parent, 1)
|
|
2677
2872
|
if 0 == size(tvals) then
|
|
2678
|
-
table.insert(
|
|
2679
|
-
|
|
2680
|
-
|
|
2873
|
+
table.insert(
|
|
2874
|
+
inj.errs,
|
|
2875
|
+
"The $ONE validator at field "
|
|
2876
|
+
.. pathify(inj.path, 1, 1)
|
|
2877
|
+
.. " must have at least one argument."
|
|
2878
|
+
)
|
|
2681
2879
|
return
|
|
2682
2880
|
end
|
|
2683
2881
|
|
|
@@ -2708,19 +2906,24 @@ local function validate_ONE(inj, _val, _ref, store)
|
|
|
2708
2906
|
for _, v in ipairs(tvals) do
|
|
2709
2907
|
table.insert(valdesc, stringify(v))
|
|
2710
2908
|
end
|
|
2711
|
-
local valdesc_str = table.concat(valdesc,
|
|
2712
|
-
valdesc_str = valdesc_str:gsub(
|
|
2909
|
+
local valdesc_str = table.concat(valdesc, ", ")
|
|
2910
|
+
valdesc_str = valdesc_str:gsub("`%$([A-Z]+)`", function(p1)
|
|
2713
2911
|
return string.lower(p1)
|
|
2714
2912
|
end)
|
|
2715
2913
|
|
|
2716
|
-
table.insert(
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
inj.
|
|
2914
|
+
table.insert(
|
|
2915
|
+
inj.errs,
|
|
2916
|
+
_invalidTypeMsg(
|
|
2917
|
+
inj.path,
|
|
2918
|
+
(1 < size(tvals) and "one of " or "") .. valdesc_str,
|
|
2919
|
+
typify(inj.dparent),
|
|
2920
|
+
inj.dparent,
|
|
2921
|
+
"V0210"
|
|
2922
|
+
)
|
|
2923
|
+
)
|
|
2720
2924
|
end
|
|
2721
2925
|
end
|
|
2722
2926
|
|
|
2723
|
-
|
|
2724
2927
|
-- Match exactly one of the specified values.
|
|
2725
2928
|
-- Syntax: ['`$EXACT`', val1, val2, ...]
|
|
2726
2929
|
local function validate_EXACT(inj)
|
|
@@ -2729,9 +2932,12 @@ local function validate_EXACT(inj)
|
|
|
2729
2932
|
-- Only operate in val mode, since parent is a list.
|
|
2730
2933
|
if M_VAL == mode then
|
|
2731
2934
|
if not islist(parent) or 0 ~= keyI then
|
|
2732
|
-
table.insert(
|
|
2733
|
-
|
|
2734
|
-
|
|
2935
|
+
table.insert(
|
|
2936
|
+
inj.errs,
|
|
2937
|
+
"The $EXACT validator at field "
|
|
2938
|
+
.. pathify(inj.path, 1, 1)
|
|
2939
|
+
.. " must be the first element of an array."
|
|
2940
|
+
)
|
|
2735
2941
|
return
|
|
2736
2942
|
end
|
|
2737
2943
|
|
|
@@ -2745,9 +2951,12 @@ local function validate_EXACT(inj)
|
|
|
2745
2951
|
|
|
2746
2952
|
local tvals = slice(parent, 1)
|
|
2747
2953
|
if 0 == size(tvals) then
|
|
2748
|
-
table.insert(
|
|
2749
|
-
|
|
2750
|
-
|
|
2954
|
+
table.insert(
|
|
2955
|
+
inj.errs,
|
|
2956
|
+
"The $EXACT validator at field "
|
|
2957
|
+
.. pathify(inj.path, 1, 1)
|
|
2958
|
+
.. " must have at least one argument."
|
|
2959
|
+
)
|
|
2751
2960
|
return
|
|
2752
2961
|
end
|
|
2753
2962
|
|
|
@@ -2773,19 +2982,26 @@ local function validate_EXACT(inj)
|
|
|
2773
2982
|
for _, v in ipairs(tvals) do
|
|
2774
2983
|
table.insert(valdesc, stringify(v))
|
|
2775
2984
|
end
|
|
2776
|
-
local valdesc_str = table.concat(valdesc,
|
|
2777
|
-
|
|
2778
|
-
table.insert(
|
|
2779
|
-
inj.
|
|
2780
|
-
(
|
|
2781
|
-
|
|
2782
|
-
|
|
2985
|
+
local valdesc_str = table.concat(valdesc, ", ")
|
|
2986
|
+
|
|
2987
|
+
table.insert(
|
|
2988
|
+
inj.errs,
|
|
2989
|
+
_invalidTypeMsg(
|
|
2990
|
+
inj.path,
|
|
2991
|
+
(1 < size(inj.path) and "" or "value ")
|
|
2992
|
+
.. "exactly equal to "
|
|
2993
|
+
.. (1 == size(tvals) and "" or "one of ")
|
|
2994
|
+
.. valdesc_str,
|
|
2995
|
+
typify(inj.dparent),
|
|
2996
|
+
inj.dparent,
|
|
2997
|
+
"V0110"
|
|
2998
|
+
)
|
|
2999
|
+
)
|
|
2783
3000
|
else
|
|
2784
3001
|
delprop(parent, key)
|
|
2785
3002
|
end
|
|
2786
3003
|
end
|
|
2787
3004
|
|
|
2788
|
-
|
|
2789
3005
|
-- This is the "modify" argument to inject. Use this to perform
|
|
2790
3006
|
-- generic validation. Runs *after* any special commands.
|
|
2791
3007
|
_validation = function(pval, key, parent, inj)
|
|
@@ -2818,13 +3034,13 @@ _validation = function(pval, key, parent, inj)
|
|
|
2818
3034
|
|
|
2819
3035
|
-- Type mismatch.
|
|
2820
3036
|
if ptype ~= ctype and NONE ~= pval then
|
|
2821
|
-
table.insert(inj.errs, _invalidTypeMsg(inj.path, typename(ptype), ctype, cval,
|
|
3037
|
+
table.insert(inj.errs, _invalidTypeMsg(inj.path, typename(ptype), ctype, cval, "V0010"))
|
|
2822
3038
|
return
|
|
2823
3039
|
end
|
|
2824
3040
|
|
|
2825
3041
|
if ismap(cval) then
|
|
2826
3042
|
if not ismap(pval) then
|
|
2827
|
-
table.insert(inj.errs, _invalidTypeMsg(inj.path, typename(ptype), ctype, cval,
|
|
3043
|
+
table.insert(inj.errs, _invalidTypeMsg(inj.path, typename(ptype), ctype, cval, "V0020"))
|
|
2828
3044
|
return
|
|
2829
3045
|
end
|
|
2830
3046
|
|
|
@@ -2832,7 +3048,7 @@ _validation = function(pval, key, parent, inj)
|
|
|
2832
3048
|
local pkeys = keysof(pval)
|
|
2833
3049
|
|
|
2834
3050
|
-- Empty spec object {} means object can be open (any keys).
|
|
2835
|
-
if 0 < size(pkeys) and true ~= getprop(pval,
|
|
3051
|
+
if 0 < size(pkeys) and true ~= getprop(pval, "`$OPEN`") then
|
|
2836
3052
|
local badkeys = {}
|
|
2837
3053
|
|
|
2838
3054
|
for _, ckey in ipairs(ckeys) do
|
|
@@ -2843,27 +3059,30 @@ _validation = function(pval, key, parent, inj)
|
|
|
2843
3059
|
|
|
2844
3060
|
-- Closed object, so reject extra keys not in shape.
|
|
2845
3061
|
if 0 < size(badkeys) then
|
|
2846
|
-
local msg =
|
|
2847
|
-
|
|
3062
|
+
local msg = "Unexpected keys at field "
|
|
3063
|
+
.. pathify(inj.path, 1)
|
|
3064
|
+
.. S_VIZ
|
|
3065
|
+
.. table.concat(badkeys, ", ")
|
|
2848
3066
|
table.insert(inj.errs, msg)
|
|
2849
3067
|
end
|
|
2850
3068
|
else
|
|
2851
3069
|
-- Object is open, so merge in extra keys.
|
|
2852
3070
|
merge({ pval, cval })
|
|
2853
3071
|
if isnode(pval) then
|
|
2854
|
-
delprop(pval,
|
|
3072
|
+
delprop(pval, "`$OPEN`")
|
|
2855
3073
|
end
|
|
2856
3074
|
end
|
|
2857
3075
|
elseif islist(cval) then
|
|
2858
3076
|
if not islist(pval) then
|
|
2859
|
-
table.insert(inj.errs, _invalidTypeMsg(inj.path, typename(ptype), ctype, cval,
|
|
3077
|
+
table.insert(inj.errs, _invalidTypeMsg(inj.path, typename(ptype), ctype, cval, "V0030"))
|
|
2860
3078
|
end
|
|
2861
3079
|
elseif exact then
|
|
2862
3080
|
if cval ~= pval then
|
|
2863
|
-
local pathmsg = 1 < size(inj.path)
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
3081
|
+
local pathmsg = 1 < size(inj.path) and ("at field " .. pathify(inj.path, 1) .. S_VIZ) or S_MT
|
|
3082
|
+
table.insert(
|
|
3083
|
+
inj.errs,
|
|
3084
|
+
"Value " .. pathmsg .. tostring(cval) .. " should equal " .. tostring(pval) .. "."
|
|
3085
|
+
)
|
|
2867
3086
|
end
|
|
2868
3087
|
else
|
|
2869
3088
|
-- Spec value was a default, copy over data
|
|
@@ -2871,7 +3090,6 @@ _validation = function(pval, key, parent, inj)
|
|
|
2871
3090
|
end
|
|
2872
3091
|
end
|
|
2873
3092
|
|
|
2874
|
-
|
|
2875
3093
|
-- Validate a data structure against a shape specification. The shape
|
|
2876
3094
|
-- specification follows the "by example" principle. Plain data in
|
|
2877
3095
|
-- the shape is treated as default values that also specify the
|
|
@@ -2928,7 +3146,7 @@ validate = function(data, spec, injdef)
|
|
|
2928
3146
|
-- A special top level value to collect errors.
|
|
2929
3147
|
{
|
|
2930
3148
|
["$ERRS"] = errs,
|
|
2931
|
-
}
|
|
3149
|
+
},
|
|
2932
3150
|
}, 1)
|
|
2933
3151
|
|
|
2934
3152
|
local meta = (injdef and injdef.meta) or {}
|
|
@@ -2945,17 +3163,15 @@ validate = function(data, spec, injdef)
|
|
|
2945
3163
|
local generr = (0 < size(errs) and not collect)
|
|
2946
3164
|
|
|
2947
3165
|
if generr then
|
|
2948
|
-
error(table.concat(errs,
|
|
3166
|
+
error(table.concat(errs, " | "))
|
|
2949
3167
|
end
|
|
2950
3168
|
|
|
2951
3169
|
return out
|
|
2952
3170
|
end
|
|
2953
3171
|
|
|
2954
|
-
|
|
2955
3172
|
-- Select query operators
|
|
2956
3173
|
-- ======================
|
|
2957
3174
|
|
|
2958
|
-
|
|
2959
3175
|
local function select_AND(inj, _val, _ref, store)
|
|
2960
3176
|
if M_KEYPRE == inj.mode then
|
|
2961
3177
|
local terms = getprop(inj.parent, inj.key)
|
|
@@ -2976,8 +3192,10 @@ local function select_AND(inj, _val, _ref, store)
|
|
|
2976
3192
|
})
|
|
2977
3193
|
|
|
2978
3194
|
if 0 ~= size(terrs) then
|
|
2979
|
-
table.insert(
|
|
2980
|
-
|
|
3195
|
+
table.insert(
|
|
3196
|
+
inj.errs,
|
|
3197
|
+
"AND:" .. pathify(ppath) .. S_VIZ .. stringify(point) .. " fail:" .. stringify(terms)
|
|
3198
|
+
)
|
|
2981
3199
|
end
|
|
2982
3200
|
end
|
|
2983
3201
|
|
|
@@ -2987,7 +3205,6 @@ local function select_AND(inj, _val, _ref, store)
|
|
|
2987
3205
|
end
|
|
2988
3206
|
end
|
|
2989
3207
|
|
|
2990
|
-
|
|
2991
3208
|
local function select_OR(inj, _val, _ref, store)
|
|
2992
3209
|
if M_KEYPRE == inj.mode then
|
|
2993
3210
|
local terms = getprop(inj.parent, inj.key)
|
|
@@ -3016,12 +3233,13 @@ local function select_OR(inj, _val, _ref, store)
|
|
|
3016
3233
|
end
|
|
3017
3234
|
end
|
|
3018
3235
|
|
|
3019
|
-
table.insert(
|
|
3020
|
-
|
|
3236
|
+
table.insert(
|
|
3237
|
+
inj.errs,
|
|
3238
|
+
"OR:" .. pathify(ppath) .. S_VIZ .. stringify(point) .. " fail:" .. stringify(terms)
|
|
3239
|
+
)
|
|
3021
3240
|
end
|
|
3022
3241
|
end
|
|
3023
3242
|
|
|
3024
|
-
|
|
3025
3243
|
local function select_NOT(inj, _val, _ref, store)
|
|
3026
3244
|
if M_KEYPRE == inj.mode then
|
|
3027
3245
|
local term = getprop(inj.parent, inj.key)
|
|
@@ -3041,8 +3259,10 @@ local function select_NOT(inj, _val, _ref, store)
|
|
|
3041
3259
|
})
|
|
3042
3260
|
|
|
3043
3261
|
if 0 == size(terrs) then
|
|
3044
|
-
table.insert(
|
|
3045
|
-
|
|
3262
|
+
table.insert(
|
|
3263
|
+
inj.errs,
|
|
3264
|
+
"NOT:" .. pathify(ppath) .. S_VIZ .. stringify(point) .. " fail:" .. stringify(term)
|
|
3265
|
+
)
|
|
3046
3266
|
end
|
|
3047
3267
|
|
|
3048
3268
|
local gkey = getelem(inj.path, -2)
|
|
@@ -3051,7 +3271,6 @@ local function select_NOT(inj, _val, _ref, store)
|
|
|
3051
3271
|
end
|
|
3052
3272
|
end
|
|
3053
3273
|
|
|
3054
|
-
|
|
3055
3274
|
local function select_CMP(inj, _val, ref, store)
|
|
3056
3275
|
if M_KEYPRE == inj.mode then
|
|
3057
3276
|
local term = getprop(inj.parent, inj.key)
|
|
@@ -3062,35 +3281,47 @@ local function select_CMP(inj, _val, ref, store)
|
|
|
3062
3281
|
|
|
3063
3282
|
local pass = false
|
|
3064
3283
|
|
|
3065
|
-
if
|
|
3066
|
-
pass = true
|
|
3067
|
-
elseif '$LT' == ref and point < term then
|
|
3284
|
+
if "$GT" == ref and point > term then
|
|
3068
3285
|
pass = true
|
|
3069
|
-
elseif
|
|
3286
|
+
elseif "$LT" == ref and point < term then
|
|
3070
3287
|
pass = true
|
|
3071
|
-
elseif
|
|
3288
|
+
elseif "$GTE" == ref and point >= term then
|
|
3072
3289
|
pass = true
|
|
3073
|
-
elseif
|
|
3290
|
+
elseif "$LTE" == ref and point <= term then
|
|
3074
3291
|
pass = true
|
|
3292
|
+
elseif "$LIKE" == ref then
|
|
3293
|
+
if re_test then
|
|
3294
|
+
if re_test(tostring(term), stringify(point)) then pass = true end
|
|
3295
|
+
else
|
|
3296
|
+
if stringify(point):match(term) then pass = true end
|
|
3297
|
+
end
|
|
3075
3298
|
end
|
|
3076
3299
|
|
|
3077
3300
|
if pass then
|
|
3078
3301
|
local gp = getelem(inj.nodes, -2)
|
|
3079
3302
|
setprop(gp, gkey, point)
|
|
3080
3303
|
else
|
|
3081
|
-
table.insert(
|
|
3082
|
-
|
|
3304
|
+
table.insert(
|
|
3305
|
+
inj.errs,
|
|
3306
|
+
"CMP: "
|
|
3307
|
+
.. pathify(ppath)
|
|
3308
|
+
.. S_VIZ
|
|
3309
|
+
.. stringify(point)
|
|
3310
|
+
.. " fail:"
|
|
3311
|
+
.. ref
|
|
3312
|
+
.. " "
|
|
3313
|
+
.. stringify(term)
|
|
3314
|
+
)
|
|
3083
3315
|
end
|
|
3084
3316
|
end
|
|
3085
3317
|
|
|
3086
3318
|
return NONE
|
|
3087
3319
|
end
|
|
3088
3320
|
|
|
3089
|
-
|
|
3090
3321
|
-- Select children matching a query.
|
|
3091
3322
|
local function select_fn(children, query)
|
|
3092
3323
|
if not isnode(children) then
|
|
3093
|
-
return {}
|
|
3324
|
+
return setmetatable({}, { __jsontype = "array" })
|
|
3094
3325
|
end
|
|
3095
3326
|
|
|
3096
3327
|
if ismap(children) then
|
|
@@ -3106,7 +3337,7 @@ local function select_fn(children, query)
|
|
|
3106
3337
|
end
|
|
3107
3338
|
end
|
|
3108
3339
|
|
|
3109
|
-
local results = {}
|
|
3340
|
+
local results = setmetatable({}, { __jsontype = "array" })
|
|
3110
3341
|
local injdef = {
|
|
3111
3342
|
errs = {},
|
|
3112
3343
|
meta = { [S_BEXACT] = true },
|
|
@@ -3119,14 +3350,14 @@ local function select_fn(children, query)
|
|
|
3119
3350
|
["$GTE"] = select_CMP,
|
|
3120
3351
|
["$LTE"] = select_CMP,
|
|
3121
3352
|
["$LIKE"] = select_CMP,
|
|
3122
|
-
}
|
|
3353
|
+
},
|
|
3123
3354
|
}
|
|
3124
3355
|
|
|
3125
3356
|
local q = clone(query)
|
|
3126
3357
|
|
|
3127
3358
|
walk(q, function(_k, v)
|
|
3128
3359
|
if ismap(v) then
|
|
3129
|
-
setprop(v,
|
|
3360
|
+
setprop(v, "`$OPEN`", getprop(v, "`$OPEN`", true))
|
|
3130
3361
|
end
|
|
3131
3362
|
return v
|
|
3132
3363
|
end)
|
|
@@ -3144,25 +3375,25 @@ local function select_fn(children, query)
|
|
|
3144
3375
|
return results
|
|
3145
3376
|
end
|
|
3146
3377
|
|
|
3147
|
-
|
|
3148
3378
|
-- Internal utilities
|
|
3149
3379
|
-- ==================
|
|
3150
3380
|
|
|
3151
|
-
|
|
3152
3381
|
-- Build a type validation error message.
|
|
3153
3382
|
_invalidTypeMsg = function(path, needtype, vt, v, _whence)
|
|
3154
|
-
local vs = (v == nil or v == S_null) and
|
|
3383
|
+
local vs = (v == nil or v == S_null) and "no value" or stringify(v)
|
|
3155
3384
|
local vtname = type(vt) == S_number and typename(vt) or tostring(vt)
|
|
3156
3385
|
|
|
3157
|
-
local msg =
|
|
3158
|
-
..
|
|
3159
|
-
|
|
3386
|
+
local msg = "Expected "
|
|
3387
|
+
.. (1 < #path and ("field " .. pathify(path, 1) .. " to be ") or "")
|
|
3388
|
+
.. needtype
|
|
3389
|
+
.. ", but found "
|
|
3390
|
+
.. ((v ~= nil and v ~= S_null) and (vtname .. S_VIZ) or "")
|
|
3391
|
+
.. vs
|
|
3160
3392
|
|
|
3161
|
-
msg = msg ..
|
|
3393
|
+
msg = msg .. "."
|
|
3162
3394
|
return msg
|
|
3163
3395
|
end
|
|
3164
3396
|
|
|
3165
|
-
|
|
3166
3397
|
-- Default inject handler for transforms.
|
|
3167
3398
|
_injecthandler = function(inj, val, ref, store)
|
|
3168
3399
|
local out = val
|
|
@@ -3180,10 +3411,9 @@ _injecthandler = function(inj, val, ref, store)
|
|
|
3180
3411
|
return out
|
|
3181
3412
|
end
|
|
3182
3413
|
|
|
3183
|
-
|
|
3184
3414
|
-- Validate handler - intercepts meta paths for validation.
|
|
3185
3415
|
_validatehandler = function(inj, val, ref, store)
|
|
3186
|
-
local out
|
|
3416
|
+
local out
|
|
3187
3417
|
|
|
3188
3418
|
-- Check for meta path syntax: field$=value or field$~value
|
|
3189
3419
|
local m = ref:match("^([^$]+)%$([=~])(.+)$")
|
|
@@ -3191,7 +3421,7 @@ _validatehandler = function(inj, val, ref, store)
|
|
|
3191
3421
|
|
|
3192
3422
|
if ismetapath then
|
|
3193
3423
|
local eq = ref:match("^[^$]+%$(.)") -- '=' or '~'
|
|
3194
|
-
if
|
|
3424
|
+
if "=" == eq then
|
|
3195
3425
|
inj:setval({ S_BEXACT, val })
|
|
3196
3426
|
else
|
|
3197
3427
|
inj:setval(val)
|
|
@@ -3206,7 +3436,6 @@ _validatehandler = function(inj, val, ref, store)
|
|
|
3206
3436
|
return out
|
|
3207
3437
|
end
|
|
3208
3438
|
|
|
3209
|
-
|
|
3210
3439
|
-- Inject store values into a string.
|
|
3211
3440
|
_injectstr = function(val, store, inj)
|
|
3212
3441
|
-- Can't inject into non-strings
|
|
@@ -3214,7 +3443,7 @@ _injectstr = function(val, store, inj)
|
|
|
3214
3443
|
return S_MT
|
|
3215
3444
|
end
|
|
3216
3445
|
|
|
3217
|
-
local out
|
|
3446
|
+
local out
|
|
3218
3447
|
|
|
3219
3448
|
-- Full value wrapped in backticks
|
|
3220
3449
|
-- R_INJECTION_FULL: /^`(\$[A-Z]+|[^`]*)[0-9]*`$/
|
|
@@ -3257,11 +3486,14 @@ _injectstr = function(val, store, inj)
|
|
|
3257
3486
|
return S_MT
|
|
3258
3487
|
elseif type(found) == S_string then
|
|
3259
3488
|
return found
|
|
3260
|
-
elseif type(found) ==
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3489
|
+
elseif type(found) == "table" then
|
|
3490
|
+
-- Use the in-tree jsonify (compact form) so the library proper has
|
|
3491
|
+
-- no third-party JSON dependency.
|
|
3492
|
+
local ok, result = pcall(jsonify, found, { indent = 0 })
|
|
3493
|
+
if ok and result then
|
|
3494
|
+
return result
|
|
3495
|
+
end
|
|
3496
|
+
return islist(found) and "[...]" or "{...}"
|
|
3265
3497
|
else
|
|
3266
3498
|
return tostring(found)
|
|
3267
3499
|
end
|
|
@@ -3277,9 +3509,9 @@ _injectstr = function(val, store, inj)
|
|
|
3277
3509
|
return out
|
|
3278
3510
|
end
|
|
3279
3511
|
|
|
3280
|
-
|
|
3281
3512
|
-- Define the StructUtility "class"
|
|
3282
3513
|
local StructUtility = {
|
|
3514
|
+
NOVAL = NOVAL,
|
|
3283
3515
|
clone = clone,
|
|
3284
3516
|
delprop = delprop,
|
|
3285
3517
|
escre = escre,
|
|
@@ -3319,6 +3551,13 @@ local StructUtility = {
|
|
|
3319
3551
|
validate = validate,
|
|
3320
3552
|
walk = walk,
|
|
3321
3553
|
|
|
3554
|
+
re_compile = _regex and _regex.compile or nil,
|
|
3555
|
+
re_find = re_find,
|
|
3556
|
+
re_find_all = re_find_all,
|
|
3557
|
+
re_replace = re_replace,
|
|
3558
|
+
re_test = re_test,
|
|
3559
|
+
re_escape = re_escape,
|
|
3560
|
+
|
|
3322
3561
|
SKIP = SKIP,
|
|
3323
3562
|
DELETE = DELETE,
|
|
3324
3563
|
|
|
@@ -3362,6 +3601,7 @@ end
|
|
|
3362
3601
|
|
|
3363
3602
|
return {
|
|
3364
3603
|
StructUtility = StructUtility,
|
|
3604
|
+
NOVAL = NOVAL,
|
|
3365
3605
|
clone = clone,
|
|
3366
3606
|
delprop = delprop,
|
|
3367
3607
|
escre = escre,
|