@voxgig/sdkgen 4.32.1 → 4.34.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/copies.d.ts +21 -0
- package/dist/action/copies.js +146 -0
- package/dist/action/copies.js.map +1 -0
- package/dist/action/doctor.d.ts +15 -2
- package/dist/action/doctor.js +169 -23
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/edition.js +6 -2
- package/dist/action/edition.js.map +1 -1
- package/dist/action/feature.js +6 -2
- package/dist/action/feature.js.map +1 -1
- package/dist/action/package.js +28 -10
- package/dist/action/package.js.map +1 -1
- package/dist/action/remove.js +2 -0
- package/dist/action/remove.js.map +1 -1
- package/dist/action/resolve.d.ts +1 -0
- package/dist/action/resolve.js +10 -2
- package/dist/action/resolve.js.map +1 -1
- package/dist/action/target.js +6 -2
- package/dist/action/target.js.map +1 -1
- package/dist/helpers/naming.js +1 -1
- package/dist/helpers/naming.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -1
- package/dist/sdkgen.js +36 -3
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/model/sdkgen.aontu +7 -1
- package/package.json +1 -1
- package/project/.sdk/model/feature/debug.aontu +3 -1
- package/project/.sdk/src/cmp/c/PrepareAuth_c.ts +8 -28
- package/project/.sdk/src/cmp/c/TestClean_c.ts +774 -0
- package/project/.sdk/src/cmp/c/Test_c.ts +3 -0
- package/project/.sdk/src/cmp/c/fragment/Entity.fragment.c +16 -5
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +1 -1
- package/project/.sdk/src/cmp/clojure/PrepareAuth_clojure.ts +3 -0
- package/project/.sdk/src/cmp/clojure/TestClean_clojure.ts +473 -0
- package/project/.sdk/src/cmp/clojure/Test_clojure.ts +6 -0
- package/project/.sdk/src/cmp/clojure/fragment/Entity.fragment.clj +18 -8
- package/project/.sdk/src/cmp/cpp/PrepareAuth_cpp.ts +3 -0
- package/project/.sdk/src/cmp/cpp/TestClean_cpp.ts +782 -0
- package/project/.sdk/src/cmp/cpp/TestEntity_cpp.ts +1 -1
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +2 -0
- package/project/.sdk/src/cmp/csharp/PrepareAuth_csharp.ts +3 -0
- package/project/.sdk/src/cmp/csharp/TestClean_csharp.ts +887 -0
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +2 -0
- package/project/.sdk/src/cmp/csharp/fragment/EntityBase.fragment.cs +68 -4
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +8 -2
- package/project/.sdk/src/cmp/csharp/fragment/SdkError.fragment.cs +49 -0
- package/project/.sdk/src/cmp/elixir/PrepareAuth_elixir.ts +3 -0
- package/project/.sdk/src/cmp/elixir/Schema_elixir.ts +20 -10
- package/project/.sdk/src/cmp/elixir/TestClean_elixir.ts +666 -0
- package/project/.sdk/src/cmp/elixir/Test_elixir.ts +4 -0
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +1 -1
- package/project/.sdk/src/cmp/elixir/fragment/Pipeline.fragment.ex +14 -2
- package/project/.sdk/src/cmp/go/PrepareAuth_go.ts +3 -0
- package/project/.sdk/src/cmp/go/TestClean_go.ts +804 -0
- package/project/.sdk/src/cmp/go/Test_go.ts +3 -0
- package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +54 -4
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +27 -2
- package/project/.sdk/src/cmp/java/PrepareAuth_java.ts +3 -0
- package/project/.sdk/src/cmp/java/TestClean_java.ts +884 -0
- package/project/.sdk/src/cmp/java/Test_java.ts +2 -0
- package/project/.sdk/src/cmp/java/fragment/EntityBase.fragment.java +56 -11
- package/project/.sdk/src/cmp/java/fragment/SdkError.fragment.java +36 -3
- package/project/.sdk/src/cmp/js/PrepareAuth_js.ts +3 -0
- package/project/.sdk/src/cmp/js/TestClean_js.ts +458 -0
- package/project/.sdk/src/cmp/js/Test_js.ts +2 -0
- package/project/.sdk/src/cmp/js/fragment/EntityBase.fragment.js +5 -4
- package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +7 -1
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +11 -2
- package/project/.sdk/src/cmp/js/fragment/SdkError.fragment.js +17 -1
- package/project/.sdk/src/cmp/kotlin/Package_kotlin.ts +5 -0
- package/project/.sdk/src/cmp/kotlin/PrepareAuth_kotlin.ts +3 -0
- package/project/.sdk/src/cmp/kotlin/TestClean_kotlin.ts +654 -0
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +3 -0
- package/project/.sdk/src/cmp/kotlin/fragment/EntityBase.fragment.kt +44 -11
- package/project/.sdk/src/cmp/kotlin/fragment/SdkError.fragment.kt +31 -0
- package/project/.sdk/src/cmp/lua/PrepareAuth_lua.ts +3 -0
- package/project/.sdk/src/cmp/lua/TestClean_lua.ts +769 -0
- package/project/.sdk/src/cmp/lua/Test_lua.ts +3 -0
- package/project/.sdk/src/cmp/lua/fragment/Entity.fragment.lua +77 -1
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +49 -2
- package/project/.sdk/src/cmp/ocaml/Entity_ocaml.ts +3 -2
- package/project/.sdk/src/cmp/ocaml/PrepareAuth_ocaml.ts +4 -25
- package/project/.sdk/src/cmp/ocaml/TestClean_ocaml.ts +538 -0
- package/project/.sdk/src/cmp/ocaml/Test_ocaml.ts +5 -0
- package/project/.sdk/src/cmp/ocaml/fragment/RunOp.fragment.ml +16 -4
- package/project/.sdk/src/cmp/perl/PrepareAuth_perl.ts +3 -0
- package/project/.sdk/src/cmp/perl/TestClean_perl.ts +619 -0
- package/project/.sdk/src/cmp/perl/Test_perl.ts +3 -0
- package/project/.sdk/src/cmp/perl/fragment/Entity.fragment.pm +67 -5
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +13 -1
- package/project/.sdk/src/cmp/php/PrepareAuth_php.ts +3 -0
- package/project/.sdk/src/cmp/php/TestClean_php.ts +809 -0
- package/project/.sdk/src/cmp/php/Test_php.ts +2 -0
- package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +96 -55
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +15 -2
- package/project/.sdk/src/cmp/py/PrepareAuth_py.ts +3 -0
- package/project/.sdk/src/cmp/py/TestClean_py.ts +549 -0
- package/project/.sdk/src/cmp/py/Test_py.ts +3 -0
- package/project/.sdk/src/cmp/py/fragment/Entity.fragment.py +83 -55
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +19 -3
- package/project/.sdk/src/cmp/rb/PrepareAuth_rb.ts +3 -0
- package/project/.sdk/src/cmp/rb/TestClean_rb.ts +535 -0
- package/project/.sdk/src/cmp/rb/Test_rb.ts +3 -0
- package/project/.sdk/src/cmp/rb/fragment/Entity.fragment.rb +108 -46
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +12 -1
- package/project/.sdk/src/cmp/rust/PrepareAuth_rust.ts +9 -45
- package/project/.sdk/src/cmp/rust/TestClean_rust.ts +881 -0
- package/project/.sdk/src/cmp/rust/Test_rust.ts +3 -0
- package/project/.sdk/src/cmp/rust/fragment/Entity.fragment.rs +14 -6
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +17 -1
- package/project/.sdk/src/cmp/rust/fragment/SdkError.fragment.rs +34 -2
- package/project/.sdk/src/cmp/scala/PrepareAuth_scala.ts +3 -0
- package/project/.sdk/src/cmp/scala/TestClean_scala.ts +608 -0
- package/project/.sdk/src/cmp/scala/Test_scala.ts +3 -0
- package/project/.sdk/src/cmp/scala/fragment/EntityBase.fragment.scala +58 -6
- package/project/.sdk/src/cmp/swift/PrepareAuth_swift.ts +3 -0
- package/project/.sdk/src/cmp/swift/TestClean_swift.ts +671 -0
- package/project/.sdk/src/cmp/swift/Test_swift.ts +3 -0
- package/project/.sdk/src/cmp/swift/fragment/EntityBase.fragment.swift +10 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +16 -2
- package/project/.sdk/src/cmp/swift/fragment/SdkError.fragment.swift +47 -3
- package/project/.sdk/src/cmp/ts/PrepareAuth_ts.ts +3 -0
- package/project/.sdk/src/cmp/ts/TestClean_ts.ts +465 -0
- package/project/.sdk/src/cmp/ts/Test_ts.ts +2 -0
- package/project/.sdk/src/cmp/ts/fragment/EntityBase.fragment.ts +5 -4
- package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +7 -1
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +8 -2
- package/project/.sdk/src/cmp/ts/fragment/SdkError.fragment.ts +20 -2
- package/project/.sdk/src/cmp/zig/PrepareAuth_zig.ts +3 -0
- package/project/.sdk/src/cmp/zig/TestClean_zig.ts +791 -0
- package/project/.sdk/src/cmp/zig/Test_zig.ts +5 -0
- package/project/.sdk/src/cmp/zig/fragment/Build.fragment.zig +19 -0
- package/project/.sdk/src/cmp/zig/fragment/Entity.fragment.zig +14 -5
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +1 -1
- package/project/.sdk/tm/c/core/context.c +25 -0
- package/project/.sdk/tm/c/core/error.c +16 -0
- package/project/.sdk/tm/c/core/sdk.h +22 -0
- package/project/.sdk/tm/c/feature/audit.c +2 -0
- package/project/.sdk/tm/c/feature/cost.c +1 -0
- package/project/.sdk/tm/c/feature/debug.c +19 -29
- package/project/.sdk/tm/c/feature/log.c +21 -2
- package/project/.sdk/tm/c/feature/proxy.c +55 -3
- package/project/.sdk/tm/c/feature/secrets.c +7 -0
- package/project/.sdk/tm/c/feature/telemetry.c +2 -0
- package/project/.sdk/tm/c/tests/feature_test.c +2 -2
- package/project/.sdk/tm/c/utility/clean.c +530 -8
- package/project/.sdk/tm/c/utility/done.c +28 -12
- package/project/.sdk/tm/c/utility/make_error.c +4 -3
- package/project/.sdk/tm/c/utility/make_options.c +71 -45
- package/project/.sdk/tm/clojure/feature/secrets/sdk/feature/secrets.clj +15 -0
- package/project/.sdk/tm/clojure/src/sdk/client.clj +20 -4
- package/project/.sdk/tm/clojure/src/sdk/core.clj +319 -35
- package/project/.sdk/tm/clojure/src/sdk/features.clj +68 -30
- package/project/.sdk/tm/clojure/test/sdk/test/feature.clj +4 -2
- package/project/.sdk/tm/clojure/test/sdk/test/pipeline.clj +47 -20
- package/project/.sdk/tm/clojure/test/sdk/test_runner.clj +4 -0
- package/project/.sdk/tm/cpp/core/types.hpp +93 -3
- package/project/.sdk/tm/cpp/feature/audit.hpp +1 -0
- package/project/.sdk/tm/cpp/feature/cost.hpp +1 -0
- package/project/.sdk/tm/cpp/feature/debug.hpp +20 -12
- package/project/.sdk/tm/cpp/feature/log.hpp +31 -9
- package/project/.sdk/tm/cpp/feature/proxy.hpp +52 -6
- package/project/.sdk/tm/cpp/feature/secrets.hpp +13 -0
- package/project/.sdk/tm/cpp/feature/telemetry.hpp +4 -2
- package/project/.sdk/tm/cpp/test/feature_test.cpp +2 -2
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +426 -51
- package/project/.sdk/tm/csharp/core/Context.cs +32 -0
- package/project/.sdk/tm/csharp/core/Utility.cs +2 -0
- package/project/.sdk/tm/csharp/feature/AuditFeature.cs +1 -0
- package/project/.sdk/tm/csharp/feature/CostFeature.cs +7 -5
- package/project/.sdk/tm/csharp/feature/DebugFeature.cs +19 -16
- package/project/.sdk/tm/csharp/feature/FeatureOptions.cs +16 -0
- package/project/.sdk/tm/csharp/feature/LogFeature.cs +4 -1
- package/project/.sdk/tm/csharp/feature/ProxyFeature.cs +26 -5
- package/project/.sdk/tm/csharp/feature/SecretsFeature.cs +16 -0
- package/project/.sdk/tm/csharp/feature/TelemetryFeature.cs +4 -2
- package/project/.sdk/tm/csharp/test/FeatureTest.cs +2 -2
- package/project/.sdk/tm/csharp/utility/Clean.cs +440 -2
- package/project/.sdk/tm/csharp/utility/Done.cs +27 -11
- package/project/.sdk/tm/csharp/utility/MakeError.cs +19 -8
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +119 -29
- package/project/.sdk/tm/csharp/utility/MakeResponse.cs +21 -11
- package/project/.sdk/tm/csharp/utility/Register.cs +1 -0
- package/project/.sdk/tm/elixir/lib/projectname/context.ex +19 -0
- package/project/.sdk/tm/elixir/lib/projectname/entity_base.ex +22 -1
- package/project/.sdk/tm/elixir/lib/projectname/error.ex +24 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/audit.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/cost.ex +2 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature/debug.ex +13 -13
- package/project/.sdk/tm/elixir/lib/projectname/feature/log.ex +18 -5
- package/project/.sdk/tm/elixir/lib/projectname/feature/proxy.ex +19 -1
- package/project/.sdk/tm/elixir/lib/projectname/feature/secrets.ex +15 -0
- package/project/.sdk/tm/elixir/lib/projectname/feature/telemetry.ex +4 -2
- package/project/.sdk/tm/elixir/lib/projectname/feature.ex +22 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +464 -41
- package/project/.sdk/tm/elixir/test/feature_test.exs +3 -2
- package/project/.sdk/tm/go/core/context.go +42 -0
- package/project/.sdk/tm/go/core/error.go +30 -10
- package/project/.sdk/tm/go/core/utility_type.go +2 -0
- package/project/.sdk/tm/go/feature/audit_feature.go +1 -0
- package/project/.sdk/tm/go/feature/base_feature.go +12 -0
- package/project/.sdk/tm/go/feature/cost_feature.go +2 -2
- package/project/.sdk/tm/go/feature/debug_feature.go +14 -12
- package/project/.sdk/tm/go/feature/log_feature.go +13 -8
- package/project/.sdk/tm/go/feature/proxy_feature.go +26 -6
- package/project/.sdk/tm/go/feature/secrets_feature.go +19 -0
- package/project/.sdk/tm/go/feature/telemetry_feature.go +4 -2
- package/project/.sdk/tm/go/test/feature_test.go +2 -2
- package/project/.sdk/tm/go/utility/clean.go +585 -2
- package/project/.sdk/tm/go/utility/done.go +29 -9
- package/project/.sdk/tm/go/utility/make_error.go +51 -16
- package/project/.sdk/tm/go/utility/make_options.go +77 -36
- package/project/.sdk/tm/go/utility/register.go +12 -0
- package/project/.sdk/tm/java/core/Context.java +29 -0
- package/project/.sdk/tm/java/core/SdkClient.java +4 -2
- package/project/.sdk/tm/java/core/Utility.java +7 -0
- package/project/.sdk/tm/java/feature/AuditFeature.java +6 -0
- package/project/.sdk/tm/java/feature/CostFeature.java +5 -0
- package/project/.sdk/tm/java/feature/DebugFeature.java +25 -14
- package/project/.sdk/tm/java/feature/LogFeature.java +11 -4
- package/project/.sdk/tm/java/feature/ProxyFeature.java +41 -6
- package/project/.sdk/tm/java/feature/SecretsFeature.java +18 -0
- package/project/.sdk/tm/java/feature/TelemetryFeature.java +8 -2
- package/project/.sdk/tm/java/test/FeatureTest.java +2 -2
- package/project/.sdk/tm/java/utility/Clean.java +519 -1
- package/project/.sdk/tm/java/utility/Done.java +1 -1
- package/project/.sdk/tm/java/utility/MakeError.java +5 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +55 -31
- package/project/.sdk/tm/java/utility/Register.java +4 -0
- package/project/.sdk/tm/js/src/Context.js +5 -1
- package/project/.sdk/tm/js/src/feature/audit/AuditFeature.js +2 -2
- package/project/.sdk/tm/js/src/feature/cost/CostFeature.js +2 -2
- package/project/.sdk/tm/js/src/feature/debug/DebugFeature.js +13 -7
- package/project/.sdk/tm/js/src/feature/log/LogFeature.js +9 -5
- package/project/.sdk/tm/js/src/feature/proxy/ProxyFeature.js +19 -1
- package/project/.sdk/tm/js/src/feature/secrets/SecretsFeature.js +14 -0
- package/project/.sdk/tm/js/src/feature/telemetry/TelemetryFeature.js +4 -2
- package/project/.sdk/tm/js/src/utility/CleanUtility.js +294 -21
- package/project/.sdk/tm/js/src/utility/DoneUtility.js +17 -5
- package/project/.sdk/tm/js/src/utility/MakeErrorUtility.js +18 -2
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +39 -15
- package/project/.sdk/tm/js/src/utility/Utility.js +4 -2
- package/project/.sdk/tm/js/test/feature/harness.js +11 -1
- package/project/.sdk/tm/js/test/feature.test.js +9 -2
- package/project/.sdk/tm/kotlin/core/Context.kt +19 -0
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +9 -2
- package/project/.sdk/tm/kotlin/core/Utility.kt +2 -0
- package/project/.sdk/tm/kotlin/feature/AuditFeature.kt +11 -9
- package/project/.sdk/tm/kotlin/feature/CostFeature.kt +8 -5
- package/project/.sdk/tm/kotlin/feature/DebugFeature.kt +15 -22
- package/project/.sdk/tm/kotlin/feature/LogFeature.kt +8 -4
- package/project/.sdk/tm/kotlin/feature/ProxyFeature.kt +26 -2
- package/project/.sdk/tm/kotlin/feature/SecretsFeature.kt +11 -0
- package/project/.sdk/tm/kotlin/feature/TelemetryFeature.kt +4 -2
- package/project/.sdk/tm/kotlin/test/FeatureTest.kt +2 -2
- package/project/.sdk/tm/kotlin/utility/CoreUtil.kt +447 -11
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +38 -31
- package/project/.sdk/tm/kotlin/utility/Register.kt +1 -0
- package/project/.sdk/tm/lua/core/context.lua +43 -0
- package/project/.sdk/tm/lua/core/error.lua +36 -1
- package/project/.sdk/tm/lua/core/utility_type.lua +4 -0
- package/project/.sdk/tm/lua/feature/audit_feature.lua +2 -2
- package/project/.sdk/tm/lua/feature/cost_feature.lua +2 -2
- package/project/.sdk/tm/lua/feature/debug_feature.lua +21 -17
- package/project/.sdk/tm/lua/feature/log_feature.lua +2 -1
- package/project/.sdk/tm/lua/feature/proxy_feature.lua +23 -1
- package/project/.sdk/tm/lua/feature/secrets_feature.lua +18 -0
- package/project/.sdk/tm/lua/feature/telemetry_feature.lua +4 -2
- package/project/.sdk/tm/lua/test/feature/secrets/secrets_feature_test.lua +3 -2
- package/project/.sdk/tm/lua/test/feature_test.lua +10 -3
- package/project/.sdk/tm/lua/utility/clean.lua +426 -2
- package/project/.sdk/tm/lua/utility/clean_explain.lua +34 -0
- package/project/.sdk/tm/lua/utility/done.lua +1 -7
- package/project/.sdk/tm/lua/utility/make_error.lua +3 -1
- package/project/.sdk/tm/lua/utility/make_options.lua +76 -30
- package/project/.sdk/tm/lua/utility/register.lua +4 -1
- package/project/.sdk/tm/ocaml/Makefile +5 -1
- package/project/.sdk/tm/ocaml/feature/secrets_feature.ml +12 -1
- package/project/.sdk/tm/ocaml/sdk_features.ml +78 -18
- package/project/.sdk/tm/ocaml/sdk_helpers.ml +27 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +304 -26
- package/project/.sdk/tm/ocaml/sdk_types.ml +1 -0
- package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +3 -1
- package/project/.sdk/tm/ocaml/test/t_feature.ml +2 -2
- package/project/.sdk/tm/perl/core/context.pm +27 -0
- package/project/.sdk/tm/perl/core/error.pm +52 -5
- package/project/.sdk/tm/perl/feature/audit_feature.pm +2 -2
- package/project/.sdk/tm/perl/feature/cost_feature.pm +2 -2
- package/project/.sdk/tm/perl/feature/debug_feature.pm +17 -13
- package/project/.sdk/tm/perl/feature/log_feature.pm +3 -1
- package/project/.sdk/tm/perl/feature/proxy_feature.pm +21 -1
- package/project/.sdk/tm/perl/feature/secrets_feature.pm +20 -1
- package/project/.sdk/tm/perl/feature/telemetry_feature.pm +4 -2
- package/project/.sdk/tm/perl/t/feature.t +4 -2
- package/project/.sdk/tm/perl/utility/clean.pm +345 -1
- package/project/.sdk/tm/perl/utility/done.pm +24 -6
- package/project/.sdk/tm/perl/utility/make_error.pm +17 -4
- package/project/.sdk/tm/perl/utility/make_options.pm +33 -14
- package/project/.sdk/tm/php/core/Context.php +24 -1
- package/project/.sdk/tm/php/core/Error.php +34 -3
- package/project/.sdk/tm/php/core/UtilityType.php +4 -0
- package/project/.sdk/tm/php/feature/AuditFeature.php +2 -2
- package/project/.sdk/tm/php/feature/CostFeature.php +2 -2
- package/project/.sdk/tm/php/feature/DebugFeature.php +18 -9
- package/project/.sdk/tm/php/feature/LogFeature.php +5 -2
- package/project/.sdk/tm/php/feature/ProxyFeature.php +20 -1
- package/project/.sdk/tm/php/feature/SecretsFeature.php +18 -0
- package/project/.sdk/tm/php/feature/TelemetryFeature.php +4 -2
- package/project/.sdk/tm/php/test/FeatureTest.php +2 -2
- package/project/.sdk/tm/php/utility/Clean.php +438 -2
- package/project/.sdk/tm/php/utility/Done.php +14 -7
- package/project/.sdk/tm/php/utility/MakeError.php +9 -7
- package/project/.sdk/tm/php/utility/MakeOptions.php +62 -38
- package/project/.sdk/tm/php/utility/Register.php +2 -0
- package/project/.sdk/tm/py/pkg/core/context.py +17 -0
- package/project/.sdk/tm/py/pkg/core/error.py +42 -1
- package/project/.sdk/tm/py/pkg/core/utility_type.py +4 -0
- package/project/.sdk/tm/py/pkg/feature/audit_feature.py +2 -2
- package/project/.sdk/tm/py/pkg/feature/cost_feature.py +2 -2
- package/project/.sdk/tm/py/pkg/feature/debug_feature.py +17 -12
- package/project/.sdk/tm/py/pkg/feature/log_feature.py +2 -1
- package/project/.sdk/tm/py/pkg/feature/proxy_feature.py +18 -1
- package/project/.sdk/tm/py/pkg/feature/secrets_feature.py +12 -0
- package/project/.sdk/tm/py/pkg/feature/telemetry_feature.py +4 -2
- package/project/.sdk/tm/py/pkg/utility/clean.py +311 -1
- package/project/.sdk/tm/py/pkg/utility/done.py +18 -5
- package/project/.sdk/tm/py/pkg/utility/make_error.py +9 -6
- package/project/.sdk/tm/py/pkg/utility/make_options.py +64 -25
- package/project/.sdk/tm/py/pkg/utility/make_response.py +19 -13
- package/project/.sdk/tm/py/pkg/utility/register.py +4 -2
- package/project/.sdk/tm/py/test/feature_harness.py +4 -0
- package/project/.sdk/tm/py/test/test_feature.py +2 -2
- package/project/.sdk/tm/py/test/test_primary_utility.py +1 -0
- package/project/.sdk/tm/rb/core/context.rb +28 -0
- package/project/.sdk/tm/rb/core/error.rb +30 -0
- package/project/.sdk/tm/rb/core/utility_type.rb +1 -1
- package/project/.sdk/tm/rb/feature/audit_feature.rb +2 -2
- package/project/.sdk/tm/rb/feature/cost_feature.rb +2 -2
- package/project/.sdk/tm/rb/feature/debug_feature.rb +15 -12
- package/project/.sdk/tm/rb/feature/log_feature.rb +3 -1
- package/project/.sdk/tm/rb/feature/proxy_feature.rb +25 -1
- package/project/.sdk/tm/rb/feature/secrets_feature.rb +17 -0
- package/project/.sdk/tm/rb/feature/telemetry_feature.rb +4 -2
- package/project/.sdk/tm/rb/test/feature_test.rb +4 -2
- package/project/.sdk/tm/rb/utility/clean.rb +312 -1
- package/project/.sdk/tm/rb/utility/done.rb +15 -5
- package/project/.sdk/tm/rb/utility/make_error.rb +13 -4
- package/project/.sdk/tm/rb/utility/make_options.rb +31 -12
- package/project/.sdk/tm/rb/utility/register.rb +2 -0
- package/project/.sdk/tm/rust/core/context.rs +55 -0
- package/project/.sdk/tm/rust/core/utility_type.rs +17 -0
- package/project/.sdk/tm/rust/feature/audit.rs +2 -0
- package/project/.sdk/tm/rust/feature/cost.rs +3 -2
- package/project/.sdk/tm/rust/feature/debug.rs +16 -16
- package/project/.sdk/tm/rust/feature/log.rs +42 -7
- package/project/.sdk/tm/rust/feature/proxy.rs +48 -2
- package/project/.sdk/tm/rust/feature/secrets.rs +12 -0
- package/project/.sdk/tm/rust/feature/telemetry.rs +2 -0
- package/project/.sdk/tm/rust/tests/feature_test.rs +2 -2
- package/project/.sdk/tm/rust/utility/clean.rs +446 -5
- package/project/.sdk/tm/rust/utility/done.rs +22 -12
- package/project/.sdk/tm/rust/utility/make_error.rs +4 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +73 -19
- package/project/.sdk/tm/scala/Makefile +3 -1
- package/project/.sdk/tm/scala/core/Context.scala +16 -0
- package/project/.sdk/tm/scala/core/SdkClient.scala +14 -4
- package/project/.sdk/tm/scala/core/SdkError.scala +28 -0
- package/project/.sdk/tm/scala/core/Types.scala +3 -0
- package/project/.sdk/tm/scala/core/Utility.scala +2 -0
- package/project/.sdk/tm/scala/feature/AuditFeature.scala +16 -10
- package/project/.sdk/tm/scala/feature/CostFeature.scala +10 -5
- package/project/.sdk/tm/scala/feature/DebugFeature.scala +24 -21
- package/project/.sdk/tm/scala/feature/LogFeature.scala +9 -4
- package/project/.sdk/tm/scala/feature/ProxyFeature.scala +23 -2
- package/project/.sdk/tm/scala/feature/SecretsFeature.scala +14 -0
- package/project/.sdk/tm/scala/feature/TelemetryFeature.scala +9 -2
- package/project/.sdk/tm/scala/sdktest/SdkTestMain.scala +2 -2
- package/project/.sdk/tm/scala/utility/Make.scala +63 -20
- package/project/.sdk/tm/scala/utility/Misc.scala +358 -5
- package/project/.sdk/tm/scala/utility/Register.scala +1 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +29 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Utility.swift +2 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/AuditFeature.swift +10 -9
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/CostFeature.swift +7 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/DebugFeature.swift +19 -15
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/FeatureOptions.swift +16 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/LogFeature.swift +4 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/ProxyFeature.swift +20 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/SecretsFeature.swift +13 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TelemetryFeature.swift +4 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeError.swift +15 -5
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +58 -19
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Register.swift +1 -0
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +391 -4
- package/project/.sdk/tm/swift/Tests/ProjectNameSDKTests/FeatureTest.swift +2 -2
- package/project/.sdk/tm/ts/src/Context.ts +5 -1
- package/project/.sdk/tm/ts/src/feature/audit/AuditFeature.ts +2 -2
- package/project/.sdk/tm/ts/src/feature/cost/CostFeature.ts +2 -2
- package/project/.sdk/tm/ts/src/feature/debug/DebugFeature.ts +13 -7
- package/project/.sdk/tm/ts/src/feature/log/LogFeature.ts +9 -5
- package/project/.sdk/tm/ts/src/feature/proxy/ProxyFeature.ts +19 -1
- package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +12 -0
- package/project/.sdk/tm/ts/src/feature/telemetry/TelemetryFeature.ts +4 -2
- package/project/.sdk/tm/ts/src/utility/CleanUtility.ts +287 -9
- package/project/.sdk/tm/ts/src/utility/DoneUtility.ts +18 -5
- package/project/.sdk/tm/ts/src/utility/MakeErrorUtility.ts +18 -8
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +38 -16
- package/project/.sdk/tm/ts/src/utility/Utility.ts +4 -2
- package/project/.sdk/tm/ts/test/feature/harness.ts +11 -1
- package/project/.sdk/tm/ts/test/feature.test.ts +9 -2
- package/project/.sdk/tm/zig/core/context.zig +34 -0
- package/project/.sdk/tm/zig/core/error.zig +22 -1
- package/project/.sdk/tm/zig/core/utility.zig +403 -39
- package/project/.sdk/tm/zig/feature/audit.zig +4 -2
- package/project/.sdk/tm/zig/feature/cost.zig +3 -2
- package/project/.sdk/tm/zig/feature/debug.zig +20 -25
- package/project/.sdk/tm/zig/feature/log.zig +3 -1
- package/project/.sdk/tm/zig/feature/proxy.zig +47 -6
- package/project/.sdk/tm/zig/feature/secrets.zig +7 -0
- package/project/.sdk/tm/zig/feature/telemetry.zig +4 -2
- package/project/.sdk/tm/zig/test/feature_test.zig +2 -2
- package/project/sdkgen-package.json +1 -1
- package/src/action/copies.ts +219 -0
- package/src/action/doctor.ts +247 -21
- package/src/action/edition.ts +10 -2
- package/src/action/feature.ts +11 -2
- package/src/action/package.ts +31 -10
- package/src/action/remove.ts +4 -0
- package/src/action/resolve.ts +16 -3
- package/src/action/target.ts +10 -2
- package/src/helpers/naming.ts +1 -1
- package/src/sdkgen.ts +45 -5
|
@@ -9,6 +9,7 @@ import { cmp, each, Folder, entityCollection,
|
|
|
9
9
|
|
|
10
10
|
import { TestEntity } from './TestEntity_java'
|
|
11
11
|
import { TestDirect } from './TestDirect_java'
|
|
12
|
+
import { TestClean } from './TestClean_java'
|
|
12
13
|
import { ReadmeExamplesTest } from './ReadmeExamplesTest_java'
|
|
13
14
|
import { javaPackage } from './utility_java'
|
|
14
15
|
|
|
@@ -23,6 +24,7 @@ const Test = cmp(function Test(props: any) {
|
|
|
23
24
|
|
|
24
25
|
// Write-once: a project's edited control file survives regeneration.
|
|
25
26
|
TestControl({ target, dir: 'test' })
|
|
27
|
+
TestClean({ target, javapackage })
|
|
26
28
|
|
|
27
29
|
const entity = each(entityCollection(model))
|
|
28
30
|
.filter((e: any) => false !== e.active)
|
|
@@ -5,6 +5,7 @@ import java.util.Iterator;
|
|
|
5
5
|
import java.util.LinkedHashMap;
|
|
6
6
|
import java.util.List;
|
|
7
7
|
import java.util.Map;
|
|
8
|
+
import java.util.NoSuchElementException;
|
|
8
9
|
import java.util.Spliterator;
|
|
9
10
|
import java.util.Spliterators;
|
|
10
11
|
import java.util.function.BooleanSupplier;
|
|
@@ -163,15 +164,31 @@ public abstract class EntityBase implements SdkEntity {
|
|
|
163
164
|
return out;
|
|
164
165
|
}
|
|
165
166
|
catch (RuntimeException err) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
167
|
+
return unexpected(ctx, err);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// The catch path. An error already finalised by makeError (e.g. via done)
|
|
172
|
+
// must not be wrapped a second time.
|
|
173
|
+
private Object unexpected(Context ctx, RuntimeException err) {
|
|
174
|
+
if (err == ctx.ctrl.err) {
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
return this.utility.makeError.apply(ctx, err);
|
|
179
|
+
}
|
|
180
|
+
catch (RuntimeException thrown) {
|
|
181
|
+
throw thrown == ctx.ctrl.err ? thrown : cleanError(ctx, thrown);
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
|
|
185
|
+
// makeError fires PreUnexpected; an error a hook throws there escapes it,
|
|
186
|
+
// even under throw false, so it is cleaned here.
|
|
187
|
+
private RuntimeException cleanError(Context ctx, RuntimeException err) {
|
|
188
|
+
Object cleaned = this.utility.clean.apply(ctx, err);
|
|
189
|
+
return cleaned instanceof RuntimeException ? (RuntimeException) cleaned : err;
|
|
190
|
+
}
|
|
191
|
+
|
|
175
192
|
/**
|
|
176
193
|
* Streaming operations. Runs `action` through the full pipeline and
|
|
177
194
|
* returns a Stream over result items, so the streaming feature's
|
|
@@ -227,9 +244,14 @@ public abstract class EntityBase implements SdkEntity {
|
|
|
227
244
|
|
|
228
245
|
// Inbound: prefer the streaming feature's incremental iterator; else fall
|
|
229
246
|
// back to the materialised items so `stream` always yields.
|
|
230
|
-
Iterator<Object> source;
|
|
247
|
+
Iterator<Object> source = null;
|
|
231
248
|
if (ctx.result != null && ctx.result.stream != null) {
|
|
232
|
-
|
|
249
|
+
try {
|
|
250
|
+
source = ctx.result.stream.get();
|
|
251
|
+
}
|
|
252
|
+
catch (RuntimeException err) {
|
|
253
|
+
unexpected(ctx, err);
|
|
254
|
+
}
|
|
233
255
|
}
|
|
234
256
|
else {
|
|
235
257
|
List<Object> items;
|
|
@@ -246,19 +268,42 @@ public abstract class EntityBase implements SdkEntity {
|
|
|
246
268
|
source = items.iterator();
|
|
247
269
|
}
|
|
248
270
|
|
|
271
|
+
// The caller iterates after runOp has returned, so a failing source takes
|
|
272
|
+
// the catch path here. Both source calls sit in hasNext: under throw
|
|
273
|
+
// false the stream then ends rather than failing in next.
|
|
249
274
|
final Iterator<Object> src = source;
|
|
250
275
|
Iterator<Object> guarded = new Iterator<Object>() {
|
|
276
|
+
private boolean ended = src == null;
|
|
277
|
+
private boolean ready = false;
|
|
278
|
+
private Object item;
|
|
279
|
+
|
|
251
280
|
@Override
|
|
252
281
|
public boolean hasNext() {
|
|
253
|
-
if (signal != null && signal.getAsBoolean()) {
|
|
282
|
+
if (ended || (signal != null && signal.getAsBoolean())) {
|
|
254
283
|
return false;
|
|
255
284
|
}
|
|
256
|
-
|
|
285
|
+
if (!ready) {
|
|
286
|
+
try {
|
|
287
|
+
ready = src.hasNext();
|
|
288
|
+
ended = !ready;
|
|
289
|
+
item = ready ? src.next() : null;
|
|
290
|
+
}
|
|
291
|
+
catch (RuntimeException err) {
|
|
292
|
+
ended = true;
|
|
293
|
+
ready = false;
|
|
294
|
+
unexpected(ctx, err);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return ready;
|
|
257
298
|
}
|
|
258
299
|
|
|
259
300
|
@Override
|
|
260
301
|
public Object next() {
|
|
261
|
-
|
|
302
|
+
if (!hasNext()) {
|
|
303
|
+
throw new NoSuchElementException();
|
|
304
|
+
}
|
|
305
|
+
ready = false;
|
|
306
|
+
return item;
|
|
262
307
|
}
|
|
263
308
|
};
|
|
264
309
|
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
package JAVAPACKAGE.core;
|
|
2
2
|
|
|
3
|
+
import java.util.LinkedHashMap;
|
|
4
|
+
import java.util.Map;
|
|
5
|
+
|
|
6
|
+
import JAVAPACKAGE.utility.struct.Struct;
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
9
|
* ProjectName SDK error. Carries the SDK error code, the operation
|
|
5
10
|
* context, and cleaned copies of the result and spec at failure time.
|
|
@@ -9,9 +14,13 @@ public class SdkError extends RuntimeException {
|
|
|
9
14
|
public final String sdk = "ProjectName";
|
|
10
15
|
public String code;
|
|
11
16
|
public String msg;
|
|
12
|
-
public
|
|
13
|
-
public
|
|
14
|
-
|
|
17
|
+
public Object result;
|
|
18
|
+
public Object spec;
|
|
19
|
+
|
|
20
|
+
// Reachable for a debugger, invisible to a serialiser: the context holds
|
|
21
|
+
// the live spec and options, and an error is what gets logged. Private,
|
|
22
|
+
// so a reflective walk of the public fields never reaches it.
|
|
23
|
+
private transient Context ctx;
|
|
15
24
|
|
|
16
25
|
/**
|
|
17
26
|
* HTTP status of the response that caused this error, or -1 when the
|
|
@@ -32,8 +41,32 @@ public class SdkError extends RuntimeException {
|
|
|
32
41
|
this.ctx = ctx;
|
|
33
42
|
}
|
|
34
43
|
|
|
44
|
+
/** The operation context at failure time, for a debugger; never printed. */
|
|
45
|
+
public Context ctx() {
|
|
46
|
+
return this.ctx;
|
|
47
|
+
}
|
|
48
|
+
|
|
35
49
|
@Override
|
|
36
50
|
public String getMessage() {
|
|
37
51
|
return this.msg;
|
|
38
52
|
}
|
|
53
|
+
|
|
54
|
+
/** What makeError attached is already cleaned; the context is not part of the record. */
|
|
55
|
+
public Map<String, Object> toMap() {
|
|
56
|
+
Map<String, Object> out = new LinkedHashMap<>();
|
|
57
|
+
out.put("sdk", this.sdk);
|
|
58
|
+
out.put("code", this.code);
|
|
59
|
+
out.put("message", this.msg);
|
|
60
|
+
out.put("status", this.status);
|
|
61
|
+
out.put("result", this.result);
|
|
62
|
+
out.put("spec", this.spec);
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Override
|
|
67
|
+
public String toString() {
|
|
68
|
+
return "ProjectNameSDK error [" + this.code + "] " + this.msg
|
|
69
|
+
+ " (status " + this.status + ")"
|
|
70
|
+
+ (this.spec == null ? "" : " spec=" + Struct.jsonify(this.spec, Map.of("indent", 0)));
|
|
71
|
+
}
|
|
39
72
|
}
|
|
@@ -119,6 +119,9 @@ ${cookieHelper(spec.where)}
|
|
|
119
119
|
}
|
|
120
120
|
else {
|
|
121
121
|
const b64 = Buffer.from(apikey + ':' + pass).toString('base64')
|
|
122
|
+
// The joined, encoded pair is a wire form neither credential's own
|
|
123
|
+
// registration covers.
|
|
124
|
+
utility.cleanAdd(ctx, b64)
|
|
122
125
|
setprop(headers, CRED_name,
|
|
123
126
|
options.auth.prefix ? options.auth.prefix + ' ' + b64 : b64)
|
|
124
127
|
}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cmp,
|
|
3
|
+
File,
|
|
4
|
+
Content,
|
|
5
|
+
isAuthSuppressed,
|
|
6
|
+
isHttpBasicAuth,
|
|
7
|
+
resolveAuthIn,
|
|
8
|
+
resolveAuthName,
|
|
9
|
+
} from '@voxgig/sdkgen'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// The canary sweep: every credential slot holds a distinctive value, every
|
|
13
|
+
// diagnostic feature this SDK ships is switched on with a capturing sink, a
|
|
14
|
+
// real operation runs through every outcome, and every string that leaves
|
|
15
|
+
// the SDK is searched for the canaries and their encoded forms. It also
|
|
16
|
+
// proves its own sensitivity: with clean switched off the canary MUST show.
|
|
17
|
+
const TestClean = cmp(function TestClean(props: any) {
|
|
18
|
+
const { model } = props.ctx$
|
|
19
|
+
|
|
20
|
+
const auth = {
|
|
21
|
+
suppressed: isAuthSuppressed(model),
|
|
22
|
+
where: resolveAuthIn(model),
|
|
23
|
+
name: 'header' === resolveAuthIn(model)
|
|
24
|
+
? resolveAuthName(model).toLowerCase() : resolveAuthName(model),
|
|
25
|
+
basic: isHttpBasicAuth(model),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
File({ name: 'clean.test.js' }, () => Content(render(auth)))
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
function render(auth: {
|
|
33
|
+
suppressed: boolean, where: string, name: string, basic: boolean
|
|
34
|
+
}): string {
|
|
35
|
+
return `const { test, describe } = require('node:test')
|
|
36
|
+
const { ok, equal, deepStrictEqual } = require('node:assert')
|
|
37
|
+
const { inspect } = require('node:util')
|
|
38
|
+
|
|
39
|
+
const { SDK, BaseFeature } = require('..')
|
|
40
|
+
const { hasFeature } = require('./feature/harness')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
// Generated: the credential's wire placement is fixed when the SDK is built.
|
|
44
|
+
const AUTH = ${JSON.stringify(auth)}
|
|
45
|
+
|
|
46
|
+
const CANARY = {
|
|
47
|
+
apikey: 'CANARY-APIKEY-k9x2m7q4p1',
|
|
48
|
+
secret: 'CANARY-SECRET-w3e8r5t2y6',
|
|
49
|
+
header: 'CANARY-HEADER-z1x4c7v0b3',
|
|
50
|
+
value: 'CANARY-VALUE-n5m8b2v9c4',
|
|
51
|
+
config: 'CANARY-CONFIG-h6j3k8l2m5',
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const MASK = '[redacted]'
|
|
55
|
+
|
|
56
|
+
// Every form a canary can travel in.
|
|
57
|
+
const FORMS = []
|
|
58
|
+
for (const v of Object.values(CANARY)) {
|
|
59
|
+
FORMS.push(v, Buffer.from(v).toString('base64'), encodeURIComponent(v))
|
|
60
|
+
}
|
|
61
|
+
FORMS.push(Buffer.from(CANARY.apikey + ':' + CANARY.secret).toString('base64'))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
// Header maps keep the caller's spelling; the assertion should not care.
|
|
65
|
+
function header(map, name) {
|
|
66
|
+
for (const k of Object.keys(map || {})) {
|
|
67
|
+
if (k.toLowerCase() === name.toLowerCase()) return map[k]
|
|
68
|
+
}
|
|
69
|
+
return undefined
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
function leaks(text) {
|
|
74
|
+
return FORMS.filter((f) => text.includes(f))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
function forms(name, val) {
|
|
79
|
+
const out = []
|
|
80
|
+
const push = (kind, fn) => {
|
|
81
|
+
try { out.push({ name: name + ':' + kind, text: fn() }) } catch (_e) { }
|
|
82
|
+
}
|
|
83
|
+
push('json', () => JSON.stringify(val))
|
|
84
|
+
push('string', () => String(val))
|
|
85
|
+
push('inspect', () => inspect(val, { depth: 8 }))
|
|
86
|
+
if (val instanceof Error) {
|
|
87
|
+
push('message', () => String(val.message))
|
|
88
|
+
push('stack', () => String(val.stack))
|
|
89
|
+
push('props', () => inspect({ ...val }, { depth: 8 }))
|
|
90
|
+
}
|
|
91
|
+
return out
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
// Captures the serialised context from inside the pipeline: what a hook
|
|
96
|
+
// author would hand to a logger.
|
|
97
|
+
class CaptureFeature extends BaseFeature {
|
|
98
|
+
name = 'capture'
|
|
99
|
+
version = '0.0.1'
|
|
100
|
+
active = true
|
|
101
|
+
_sinks
|
|
102
|
+
constructor(sinks) { super(); this._sinks = sinks }
|
|
103
|
+
init() { }
|
|
104
|
+
PreRequest(ctx) { this._sinks.push(...forms('ctx@PreRequest', ctx)) }
|
|
105
|
+
PreResponse(ctx) { this._sinks.push(...forms('ctx@PreResponse', ctx)) }
|
|
106
|
+
// The SDK's own error as a hook reads it, which an observability feature logs.
|
|
107
|
+
PreUnexpected(ctx) {
|
|
108
|
+
this._sinks.push(...forms('ctx@PreUnexpected', ctx))
|
|
109
|
+
if (ctx.ctrl?.err instanceof Error) this._sinks.push(...forms('ctrl.err@PreUnexpected', ctx.ctrl.err))
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
function response(status, data, headers) {
|
|
115
|
+
const h = { 'content-type': 'application/json', ...(headers || {}) }
|
|
116
|
+
return {
|
|
117
|
+
status,
|
|
118
|
+
statusText: status < 400 ? 'OK' : 'ERR',
|
|
119
|
+
json: async () => data,
|
|
120
|
+
text: async () => JSON.stringify(data),
|
|
121
|
+
headers: {
|
|
122
|
+
get(key) { return h[String(key).toLowerCase()] },
|
|
123
|
+
forEach(cb) { Object.keys(h).forEach((k) => cb(h[k], k, this)) },
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const SCENARIOS = [
|
|
129
|
+
{ name: 'ok', respond: () => response(200, { id: 'i1', name: 'n1' },
|
|
130
|
+
{ 'x-session-token': 'RESP-TOKEN-a1b2c3d4e5' }) },
|
|
131
|
+
{ name: 'notfound', respond: () => response(404, { error: 'no such record' }) },
|
|
132
|
+
{ name: 'server', respond: () => response(500, { error: 'boom' }) },
|
|
133
|
+
{ name: 'transport', respond: (url) => {
|
|
134
|
+
throw new Error('socket hang up (URL was: "' + url + '")')
|
|
135
|
+
} },
|
|
136
|
+
{ name: 'notjson', respond: () => ({
|
|
137
|
+
status: 200, statusText: 'OK',
|
|
138
|
+
json: async () => { throw new Error('Unexpected token < in JSON') },
|
|
139
|
+
text: async () => '<html>',
|
|
140
|
+
headers: { get() { return undefined }, forEach() { } },
|
|
141
|
+
}) },
|
|
142
|
+
// The SDK's own error, its code quoting a registered value.
|
|
143
|
+
{ name: 'coded', respond: (_url, _fetchdef, ctx) => {
|
|
144
|
+
throw ctx.error('denied_' + CANARY.apikey, 'coded failure')
|
|
145
|
+
} },
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
function makeSdk(scenario, sinks, cleanopts, extra) {
|
|
150
|
+
const capture = (name) => (rec) => { sinks.push(...forms(name, rec)) }
|
|
151
|
+
const feature = {}
|
|
152
|
+
if (hasFeature('log')) {
|
|
153
|
+
const logger = {}
|
|
154
|
+
for (const level of ['trace', 'debug', 'info', 'warn', 'error', 'fatal']) {
|
|
155
|
+
logger[level] = capture('log.' + level)
|
|
156
|
+
}
|
|
157
|
+
feature.log = { active: true, logger }
|
|
158
|
+
}
|
|
159
|
+
if (hasFeature('debug')) feature.debug = { active: true, onEntry: capture('debug') }
|
|
160
|
+
if (hasFeature('audit')) feature.audit = { active: true, sink: capture('audit') }
|
|
161
|
+
if (hasFeature('telemetry')) feature.telemetry = { active: true, exporter: capture('telemetry') }
|
|
162
|
+
if (hasFeature('cost')) feature.cost = { active: true, sink: capture('cost') }
|
|
163
|
+
if (hasFeature('metrics')) feature.metrics = { active: true }
|
|
164
|
+
if (hasFeature('clienttrack')) feature.clienttrack = { active: true }
|
|
165
|
+
|
|
166
|
+
const opts = {
|
|
167
|
+
apikey: CANARY.apikey,
|
|
168
|
+
secret: CANARY.secret,
|
|
169
|
+
headers: { 'X-Custom-Token': CANARY.header },
|
|
170
|
+
feature,
|
|
171
|
+
extend: [new CaptureFeature(sinks), ...(extra || [])],
|
|
172
|
+
utility: {
|
|
173
|
+
fetcher: async (ctx, url, fetchdef) => scenario.respond(url, fetchdef, ctx),
|
|
174
|
+
},
|
|
175
|
+
}
|
|
176
|
+
// null builds the client with no clean block at all, as most callers do.
|
|
177
|
+
if (null !== cleanopts) opts.clean = { values: CANARY.value, ...(cleanopts || {}) }
|
|
178
|
+
return new SDK(opts)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
// The first operation that completes against a plain 200: with no
|
|
183
|
+
// arguments, else with every path parameter its points declare filled in.
|
|
184
|
+
// Resolves to { accessor, op, match }.
|
|
185
|
+
async function usableOp() {
|
|
186
|
+
const plain = new SDK({
|
|
187
|
+
apikey: CANARY.apikey,
|
|
188
|
+
utility: { fetcher: async () => response(200, { id: 'i1' }) },
|
|
189
|
+
})
|
|
190
|
+
const entities = plain._rootctx.config.entity || {}
|
|
191
|
+
const rank = (op) => (({ list: 0, load: 1 })[op] ?? 2)
|
|
192
|
+
for (const m of Object.getOwnPropertyNames(Object.getPrototypeOf(plain)).sort()) {
|
|
193
|
+
if (!/^[A-Z]/.test(m) || 'function' !== typeof plain[m]) { continue }
|
|
194
|
+
let inst
|
|
195
|
+
try { inst = plain[m]() } catch (_e) { continue }
|
|
196
|
+
if (null == inst || 'string' !== typeof inst.name || null == entities[inst.name]) { continue }
|
|
197
|
+
const opdefs = entities[inst.name].op || {}
|
|
198
|
+
for (const op of Object.keys(opdefs).sort((a, b) => rank(a) - rank(b))) {
|
|
199
|
+
const filled = {}
|
|
200
|
+
for (const point of opdefs[op].points || []) {
|
|
201
|
+
for (const p of point?.args?.params || []) {
|
|
202
|
+
if ('string' === typeof p?.name) filled[p.name] = 'p1'
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const match of [{}, filled]) {
|
|
206
|
+
try {
|
|
207
|
+
await plain[m]()[op]({ ...match }, {})
|
|
208
|
+
return { accessor: m, op, match }
|
|
209
|
+
}
|
|
210
|
+
catch (_e) { continue }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return null
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
// A feature that throws from inside the pipeline, quoting the request it
|
|
219
|
+
// saw: an error makeError never handled.
|
|
220
|
+
class ThrowFeature extends BaseFeature {
|
|
221
|
+
name = 'throwhook'
|
|
222
|
+
version = '0.0.1'
|
|
223
|
+
active = true
|
|
224
|
+
constructor(unexpected = false) { super(); this._unexpected = unexpected }
|
|
225
|
+
init() { }
|
|
226
|
+
PreResponse(ctx) {
|
|
227
|
+
throw new Error('hook saw ' + JSON.stringify(ctx.spec))
|
|
228
|
+
}
|
|
229
|
+
// Fired from the operation's catch block, before its cleaning.
|
|
230
|
+
PreUnexpected(ctx) {
|
|
231
|
+
if (this._unexpected) throw new Error('hook saw ' + JSON.stringify(ctx.spec))
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
// A stream that fails while the caller iterates it, quoting a credential.
|
|
237
|
+
class StreamThrowFeature extends BaseFeature {
|
|
238
|
+
name = 'streamthrow'
|
|
239
|
+
version = '0.0.1'
|
|
240
|
+
active = true
|
|
241
|
+
init() { }
|
|
242
|
+
PreDone(ctx) {
|
|
243
|
+
ctx.result.stream = async function* () { throw new Error('stream saw ' + CANARY.apikey) }
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
// A stream that succeeds, so the pipeline's terminal step never runs.
|
|
249
|
+
class StreamOkFeature extends BaseFeature {
|
|
250
|
+
name = 'streamok'
|
|
251
|
+
version = '0.0.1'
|
|
252
|
+
active = true
|
|
253
|
+
init() { }
|
|
254
|
+
PreDone(ctx) {
|
|
255
|
+
const items = [].concat(ctx.result.resdata ?? [])
|
|
256
|
+
ctx.result.stream = async function* () { yield* items }
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
async function drive(sdk, target, ctrl, sinks) {
|
|
262
|
+
// A caller may keep the record it passed rather than read ctrl.explain.
|
|
263
|
+
const held = ctrl.explain
|
|
264
|
+
let out = undefined
|
|
265
|
+
let err = undefined
|
|
266
|
+
try {
|
|
267
|
+
out = await sdk[target.accessor]()[target.op]({ ...target.match }, ctrl)
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
err = e
|
|
271
|
+
}
|
|
272
|
+
if (undefined !== err) sinks.push(...forms('error', err))
|
|
273
|
+
if (undefined !== out) sinks.push(...forms('result', out))
|
|
274
|
+
if (null != ctrl.explain) sinks.push(...forms('explain', ctrl.explain))
|
|
275
|
+
if (null != held && held !== ctrl.explain) sinks.push(...forms('explain:held', held))
|
|
276
|
+
return err
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
describe('clean', () => {
|
|
281
|
+
test('no credential leaves the SDK in any form', async (t) => {
|
|
282
|
+
const target = await usableOp()
|
|
283
|
+
if (null == target) {
|
|
284
|
+
return t.skip('no operation of this SDK completes against a plain 200; nothing to sweep')
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const sinks = []
|
|
288
|
+
const errors = {}
|
|
289
|
+
const explains = {}
|
|
290
|
+
|
|
291
|
+
for (const scenario of SCENARIOS) {
|
|
292
|
+
for (const variant of [
|
|
293
|
+
{ name: 'throw', ctrl: () => ({}) },
|
|
294
|
+
{ name: 'explain', ctrl: () => ({ explain: {} }) },
|
|
295
|
+
{ name: 'nothrow', ctrl: () => ({ throw: false, explain: {} }) },
|
|
296
|
+
]) {
|
|
297
|
+
const sdk = makeSdk(scenario, sinks)
|
|
298
|
+
const ctrl = variant.ctrl()
|
|
299
|
+
const err = await drive(sdk, target, ctrl, sinks)
|
|
300
|
+
const key = scenario.name + '/' + variant.name
|
|
301
|
+
if (null != err) errors[key] = err
|
|
302
|
+
if (null != ctrl.explain) explains[key] = ctrl.explain
|
|
303
|
+
sinks.push(...forms('sdk', sdk))
|
|
304
|
+
sinks.push({ name: 'sdk:spread', text: inspect({ ...sdk }, { depth: 6 }) })
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// A credential mistyped as an object is rejected by validation, whose
|
|
309
|
+
// message quotes the value it rejected; with and without a clean block.
|
|
310
|
+
for (const cleanblock of [{ clean: { values: CANARY.value } }, {}]) {
|
|
311
|
+
let rejected = undefined
|
|
312
|
+
try {
|
|
313
|
+
new SDK({ apikey: { value: CANARY.apikey }, ...cleanblock })
|
|
314
|
+
}
|
|
315
|
+
catch (e) {
|
|
316
|
+
rejected = e
|
|
317
|
+
}
|
|
318
|
+
ok(null != rejected, 'a credential mistyped as an object should be rejected')
|
|
319
|
+
sinks.push(...forms('rejected', rejected))
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// An error a feature hook throws, quoting the request, skips makeError.
|
|
323
|
+
for (const unexpected of [false, true]) {
|
|
324
|
+
const hooked = makeSdk(SCENARIOS[0], sinks, undefined, [new ThrowFeature(unexpected)])
|
|
325
|
+
const hookerr = await drive(hooked, target, { explain: {} }, sinks)
|
|
326
|
+
ok(null != hookerr, 'the throwing hook should fail the operation')
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Iterating a stream runs inside the same catch path as the operation,
|
|
330
|
+
// and the explain record the caller passed is cleaned however it ends.
|
|
331
|
+
for (const [name, extra] of [['stream', [new StreamThrowFeature()]],
|
|
332
|
+
['stream-ok', [new StreamOkFeature()]], ['stream-plain', []]]) {
|
|
333
|
+
const streamed = makeSdk(SCENARIOS[0], sinks, undefined, extra)
|
|
334
|
+
const explain = {}
|
|
335
|
+
let streamerr = undefined
|
|
336
|
+
try {
|
|
337
|
+
for await (const _item of streamed[target.accessor]().stream(
|
|
338
|
+
target.op, { reqmatch: { ...target.match } }, { ctrl: { explain } })) { }
|
|
339
|
+
}
|
|
340
|
+
catch (e) { streamerr = e }
|
|
341
|
+
ok(('stream' === name) === (null != streamerr), name + ': only the failing stream throws')
|
|
342
|
+
if (null != streamerr) sinks.push(...forms(name, streamerr))
|
|
343
|
+
ok(0 < Object.keys(explain).length, name + ': the explain record was not filled')
|
|
344
|
+
sinks.push(...forms(name + ':explain', explain))
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Most callers pass no clean block; the defaults alone must mask.
|
|
348
|
+
for (const scenario of [SCENARIOS[1], SCENARIOS[3]]) {
|
|
349
|
+
const bare = makeSdk(scenario, sinks, null)
|
|
350
|
+
await drive(bare, target, { explain: {} }, sinks)
|
|
351
|
+
sinks.push(...forms('bare', bare))
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// The generated config's own clean block is read beside the caller's,
|
|
355
|
+
// and is not changed by it.
|
|
356
|
+
const util = makeSdk(SCENARIOS[0], sinks).utility()
|
|
357
|
+
const cfgclean = { keys: 'zzsens', values: CANARY.config }
|
|
358
|
+
const built = util.makeOptions({ utility: util, config: { options: { clean: cfgclean } },
|
|
359
|
+
options: { clean: { values: CANARY.value } } })
|
|
360
|
+
const seeded = util.clean({ options: built }, 'config ' + CANARY.config + ' caller ' + CANARY.value)
|
|
361
|
+
sinks.push({ name: 'config-clean', text: seeded })
|
|
362
|
+
|
|
363
|
+
// A feature's name is not a field name: only the sensitive names inside
|
|
364
|
+
// its settings register. An entity block, of per-entity settings or seeded
|
|
365
|
+
// records keyed by entity name and id, is not read at all.
|
|
366
|
+
const featured = new SDK({ apikey: CANARY.apikey, feature: {
|
|
367
|
+
zzsecrets: { active: false, kind: 'PLAINSETTING-q8w2e4r6' },
|
|
368
|
+
zzfeat: { active: false, apitoken: 'FEATTOKEN-z9y8x7w6' },
|
|
369
|
+
test: { active: false, entity: { zztoken: { ZZTOKEN01: { note: 'PLAINRECORD-t5r3e1w9' } } } },
|
|
370
|
+
}, entity: { zztoken: { alias: { zzkey: 'PLAINALIAS-m2n4b6v8' } } } })
|
|
371
|
+
const fctx = { options: featured._options }
|
|
372
|
+
|
|
373
|
+
// The raw path returns its failure rather than throwing it.
|
|
374
|
+
const raw = await makeSdk(SCENARIOS[3], sinks).direct({ path: 'raw' })
|
|
375
|
+
ok(false === raw.ok && null != raw.err, 'a transport failure should fail direct()')
|
|
376
|
+
sinks.push(...forms('direct', raw.err))
|
|
377
|
+
|
|
378
|
+
const leaked = sinks
|
|
379
|
+
.map((s) => ({ name: s.name, found: leaks(s.text) }))
|
|
380
|
+
.filter((s) => 0 < s.found.length)
|
|
381
|
+
|
|
382
|
+
console.log('clean: swept ' + sinks.length + ' surface(s), ' + leaked.length + ' leak(s)')
|
|
383
|
+
|
|
384
|
+
equal(leaked.length, 0, 'credential leaked through: ' +
|
|
385
|
+
leaked.map((l) => l.name + ' [' + l.found.join(', ') + ']').join('; '))
|
|
386
|
+
|
|
387
|
+
// The positive half: the slot the credential travelled in is masked,
|
|
388
|
+
// and an unregistered token in a response header is masked by name.
|
|
389
|
+
const notfound = errors['notfound/throw']
|
|
390
|
+
ok(null != notfound, 'the 404 scenario must throw')
|
|
391
|
+
equal(notfound.status, 404)
|
|
392
|
+
if (!AUTH.suppressed) {
|
|
393
|
+
const spec = notfound.spec || {}
|
|
394
|
+
if ('query' === AUTH.where) {
|
|
395
|
+
equal(header(spec.query, AUTH.name), MASK)
|
|
396
|
+
}
|
|
397
|
+
else if ('cookie' === AUTH.where) {
|
|
398
|
+
ok(String(header(spec.headers, 'cookie')).includes(MASK), 'cookie: ' + header(spec.headers, 'cookie'))
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
ok(String(header(spec.headers, AUTH.name)).endsWith(MASK),
|
|
402
|
+
AUTH.name + ': ' + header(spec.headers, AUTH.name))
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
equal(header(notfound.spec.headers, 'x-custom-token'), MASK)
|
|
406
|
+
|
|
407
|
+
equal(seeded, 'config ' + MASK + ' caller ' + MASK)
|
|
408
|
+
deepStrictEqual(util.clean({ options: built }, { my_zzsens: 'x', other: 'y' }), { my_zzsens: MASK, other: 'y' })
|
|
409
|
+
deepStrictEqual(cfgclean, { keys: 'zzsens', values: CANARY.config })
|
|
410
|
+
|
|
411
|
+
equal(featured.utility().clean(fctx, 'kind PLAINSETTING-q8w2e4r6'), 'kind PLAINSETTING-q8w2e4r6')
|
|
412
|
+
equal(featured.utility().clean(fctx, 'token FEATTOKEN-z9y8x7w6'), 'token ' + MASK)
|
|
413
|
+
equal(featured.utility().clean(fctx, 'record PLAINRECORD-t5r3e1w9'), 'record PLAINRECORD-t5r3e1w9')
|
|
414
|
+
equal(featured.utility().clean(fctx, 'alias PLAINALIAS-m2n4b6v8'), 'alias PLAINALIAS-m2n4b6v8')
|
|
415
|
+
|
|
416
|
+
const coded = errors['coded/throw']
|
|
417
|
+
ok(null != coded, 'the coded scenario must throw')
|
|
418
|
+
equal(coded.code, 'denied_' + MASK)
|
|
419
|
+
|
|
420
|
+
const explained = explains['ok/explain'] || {}
|
|
421
|
+
ok(null != explained.result, 'the explain record should carry the result')
|
|
422
|
+
equal(header(explained.result.headers, 'x-session-token'), MASK)
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
test('the sweep can see a leak: clean switched off shows the credential', async (t) => {
|
|
427
|
+
const target = await usableOp()
|
|
428
|
+
if (null == target) {
|
|
429
|
+
return t.skip('no operation of this SDK completes against a plain 200; nothing to sweep')
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const sinks = []
|
|
433
|
+
const sdk = makeSdk(SCENARIOS[1], sinks, { active: false })
|
|
434
|
+
const err = await drive(sdk, target, {}, sinks)
|
|
435
|
+
ok(null != err)
|
|
436
|
+
|
|
437
|
+
// Explaining a failure must not cost it its error.
|
|
438
|
+
const explained = await drive(makeSdk(SCENARIOS[1], [], { active: false }), target, { explain: {} }, [])
|
|
439
|
+
equal(explained?.message, err.message, 'with clean off, explain lost the error')
|
|
440
|
+
|
|
441
|
+
const leaked = sinks.filter((s) => 0 < leaks(s.text).length)
|
|
442
|
+
ok(0 < leaked.length, 'with clean off, nothing showed the canary: the sweep is blind')
|
|
443
|
+
|
|
444
|
+
if (!AUTH.suppressed) {
|
|
445
|
+
const text = JSON.stringify(err.spec)
|
|
446
|
+
ok(text.includes(CANARY.apikey) ||
|
|
447
|
+
text.includes(Buffer.from(CANARY.apikey + ':' + CANARY.secret).toString('base64')),
|
|
448
|
+
'the raw spec should carry the credential when clean is off')
|
|
449
|
+
}
|
|
450
|
+
})
|
|
451
|
+
})
|
|
452
|
+
`
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
export {
|
|
457
|
+
TestClean
|
|
458
|
+
}
|
|
@@ -9,6 +9,7 @@ import { cmp, each, Folder, entityCollection,
|
|
|
9
9
|
|
|
10
10
|
import { TestLive } from './TestLive_js'
|
|
11
11
|
import { TestDefinition } from './TestDefinition_js'
|
|
12
|
+
import { TestClean } from './TestClean_js'
|
|
12
13
|
import { TestDirect } from './TestDirect_js'
|
|
13
14
|
import { TestEntity } from './TestEntity_js'
|
|
14
15
|
|
|
@@ -23,6 +24,7 @@ const Test = cmp(function Test(props: any) {
|
|
|
23
24
|
TestControl({ target, dir: 'test' })
|
|
24
25
|
TestLive({ target })
|
|
25
26
|
TestDefinition({ target })
|
|
27
|
+
TestClean({ target })
|
|
26
28
|
|
|
27
29
|
Folder({ name: 'entity' }, () => {
|
|
28
30
|
const entity = each(entityCollection(model))
|