@voxgig/sdkgen 4.22.0 → 4.24.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 +18 -2
- package/dist/action/doctor.d.ts +1 -1
- package/dist/action/doctor.js +12 -10
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/edition.js +4 -0
- package/dist/action/edition.js.map +1 -1
- package/dist/action/feature.js +5 -1
- package/dist/action/feature.js.map +1 -1
- package/dist/action/remove.d.ts +16 -0
- package/dist/action/remove.js +345 -0
- package/dist/action/remove.js.map +1 -0
- package/dist/action/target.js +6 -1
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/ReadmeTop.js +1 -1
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/cmp/ReadmeUnions.js +1 -1
- package/dist/cmp/ReadmeUnions.js.map +1 -1
- package/dist/cmp/Test.js +1 -1
- package/dist/cmp/Test.js.map +1 -1
- package/dist/helpers/buildIdNames.d.ts +3 -1
- package/dist/helpers/buildIdNames.js +15 -7
- package/dist/helpers/buildIdNames.js.map +1 -1
- package/dist/helpers/canonSpec.js +2 -2
- package/dist/helpers/canonSpec.js.map +1 -1
- package/dist/helpers/getMatchEntries.js +2 -2
- package/dist/helpers/getMatchEntries.js.map +1 -1
- package/dist/helpers/modelNames.js +5 -1
- package/dist/helpers/modelNames.js.map +1 -1
- package/dist/helpers/opShape.js +33 -33
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/helpers/pointPath.js +5 -10
- package/dist/helpers/pointPath.js.map +1 -1
- package/dist/helpers/resolved.js +2 -29
- package/dist/helpers/resolved.js.map +1 -1
- package/dist/sdkgen.d.ts +2 -2
- package/dist/sdkgen.js +5 -3
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.js +15 -7
- package/dist/utility.js.map +1 -1
- package/package.json +3 -3
- package/project/.sdk/src/cmp/c/EntityTypes_c.ts +4 -4
- package/project/.sdk/src/cmp/c/ReadmeEntity_c.ts +3 -3
- package/project/.sdk/src/cmp/c/ReadmeModel_c.ts +3 -3
- package/project/.sdk/src/cmp/c/ReadmeRef_c.ts +5 -5
- package/project/.sdk/src/cmp/c/Schema_c.ts +1 -1
- package/project/.sdk/src/cmp/c/TestDirect_c.ts +12 -12
- package/project/.sdk/src/cmp/clojure/ReadmeEntity_clojure.ts +3 -3
- package/project/.sdk/src/cmp/clojure/ReadmeModel_clojure.ts +3 -3
- package/project/.sdk/src/cmp/clojure/ReadmeRef_clojure.ts +5 -5
- package/project/.sdk/src/cmp/clojure/Schema_clojure.ts +1 -1
- package/project/.sdk/src/cmp/cpp/EntityTypes_cpp.ts +3 -3
- package/project/.sdk/src/cmp/cpp/ReadmeEntity_cpp.ts +3 -3
- package/project/.sdk/src/cmp/cpp/ReadmeModel_cpp.ts +3 -3
- package/project/.sdk/src/cmp/cpp/ReadmeRef_cpp.ts +5 -5
- package/project/.sdk/src/cmp/cpp/Schema_cpp.ts +1 -1
- package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +14 -14
- package/project/.sdk/src/cmp/cpp/TestEntity_cpp.ts +51 -50
- package/project/.sdk/src/cmp/csharp/EntityTypes_csharp.ts +4 -4
- package/project/.sdk/src/cmp/csharp/ReadmeEntity_csharp.ts +3 -3
- package/project/.sdk/src/cmp/csharp/ReadmeModel_csharp.ts +3 -3
- package/project/.sdk/src/cmp/csharp/ReadmeRef_csharp.ts +5 -5
- package/project/.sdk/src/cmp/csharp/Schema_csharp.ts +1 -1
- package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +30 -30
- package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +66 -65
- package/project/.sdk/src/cmp/elixir/EntityTypes_elixir.ts +4 -4
- package/project/.sdk/src/cmp/elixir/ReadmeEntity_elixir.ts +3 -3
- package/project/.sdk/src/cmp/elixir/ReadmeModel_elixir.ts +3 -3
- package/project/.sdk/src/cmp/elixir/ReadmeRef_elixir.ts +4 -4
- package/project/.sdk/src/cmp/elixir/Schema_elixir.ts +1 -1
- package/project/.sdk/src/cmp/go/EntityTypes_go.ts +4 -4
- package/project/.sdk/src/cmp/go/ReadmeEntity_go.ts +3 -3
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +3 -3
- package/project/.sdk/src/cmp/go/ReadmeRef_go.ts +5 -5
- package/project/.sdk/src/cmp/go/Schema_go.ts +1 -1
- package/project/.sdk/src/cmp/go/TestDirect_go.ts +38 -38
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +75 -74
- package/project/.sdk/src/cmp/go/utility_go.ts +5 -5
- package/project/.sdk/src/cmp/java/EntityTypes_java.ts +4 -4
- package/project/.sdk/src/cmp/java/ReadmeEntity_java.ts +3 -3
- package/project/.sdk/src/cmp/java/ReadmeModel_java.ts +3 -3
- package/project/.sdk/src/cmp/java/ReadmeRef_java.ts +5 -5
- package/project/.sdk/src/cmp/java/Schema_java.ts +1 -1
- package/project/.sdk/src/cmp/java/TestDirect_java.ts +30 -30
- package/project/.sdk/src/cmp/java/TestEntity_java.ts +69 -68
- package/project/.sdk/src/cmp/js/EntityTypes_js.ts +5 -5
- package/project/.sdk/src/cmp/js/ReadmeEntity_js.ts +3 -3
- package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +3 -3
- package/project/.sdk/src/cmp/js/ReadmeRef_js.ts +5 -5
- package/project/.sdk/src/cmp/js/Schema_js.ts +1 -1
- package/project/.sdk/src/cmp/js/TestDirect_js.ts +27 -27
- package/project/.sdk/src/cmp/js/TestEntity_js.ts +66 -69
- package/project/.sdk/src/cmp/js/TestLive_js.ts +3 -3
- package/project/.sdk/src/cmp/js/utility_js.ts +5 -5
- package/project/.sdk/src/cmp/kotlin/EntityTypes_kotlin.ts +4 -4
- package/project/.sdk/src/cmp/kotlin/ReadmeEntity_kotlin.ts +3 -3
- package/project/.sdk/src/cmp/kotlin/ReadmeModel_kotlin.ts +3 -3
- package/project/.sdk/src/cmp/kotlin/ReadmeRef_kotlin.ts +5 -5
- package/project/.sdk/src/cmp/kotlin/Schema_kotlin.ts +1 -1
- package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +30 -30
- package/project/.sdk/src/cmp/kotlin/TestEntity_kotlin.ts +67 -66
- package/project/.sdk/src/cmp/lua/EntityTypes_lua.ts +5 -5
- package/project/.sdk/src/cmp/lua/ReadmeEntity_lua.ts +3 -3
- package/project/.sdk/src/cmp/lua/ReadmeModel_lua.ts +3 -3
- package/project/.sdk/src/cmp/lua/ReadmeQuick_lua.ts +4 -4
- package/project/.sdk/src/cmp/lua/ReadmeRef_lua.ts +5 -5
- package/project/.sdk/src/cmp/lua/Schema_lua.ts +1 -1
- package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +25 -25
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +66 -65
- package/project/.sdk/src/cmp/ocaml/ReadmeEntity_ocaml.ts +3 -3
- package/project/.sdk/src/cmp/ocaml/ReadmeModel_ocaml.ts +3 -3
- package/project/.sdk/src/cmp/ocaml/ReadmeRef_ocaml.ts +5 -5
- package/project/.sdk/src/cmp/ocaml/Schema_ocaml.ts +1 -1
- package/project/.sdk/src/cmp/perl/ReadmeEntity_perl.ts +3 -3
- package/project/.sdk/src/cmp/perl/ReadmeModel_perl.ts +3 -3
- package/project/.sdk/src/cmp/perl/ReadmeRef_perl.ts +5 -5
- package/project/.sdk/src/cmp/perl/Schema_perl.ts +1 -1
- package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +25 -25
- package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +60 -59
- package/project/.sdk/src/cmp/php/EntityTypes_php.ts +4 -4
- package/project/.sdk/src/cmp/php/ReadmeEntity_php.ts +3 -3
- package/project/.sdk/src/cmp/php/ReadmeModel_php.ts +3 -3
- package/project/.sdk/src/cmp/php/ReadmeQuick_php.ts +4 -4
- package/project/.sdk/src/cmp/php/ReadmeRef_php.ts +5 -5
- package/project/.sdk/src/cmp/php/Schema_php.ts +1 -1
- package/project/.sdk/src/cmp/php/TestDirect_php.ts +25 -25
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +66 -65
- package/project/.sdk/src/cmp/py/EntityTypes_py.ts +4 -4
- package/project/.sdk/src/cmp/py/ReadmeEntity_py.ts +3 -3
- package/project/.sdk/src/cmp/py/ReadmeModel_py.ts +3 -3
- package/project/.sdk/src/cmp/py/ReadmeRef_py.ts +5 -5
- package/project/.sdk/src/cmp/py/Schema_py.ts +1 -1
- package/project/.sdk/src/cmp/py/TestDirect_py.ts +25 -25
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +66 -65
- package/project/.sdk/src/cmp/py-data/Main_py-data.ts +4 -4
- package/project/.sdk/src/cmp/rb/EntityTypes_rb.ts +4 -4
- package/project/.sdk/src/cmp/rb/ReadmeEntity_rb.ts +3 -3
- package/project/.sdk/src/cmp/rb/ReadmeModel_rb.ts +3 -3
- package/project/.sdk/src/cmp/rb/ReadmeQuick_rb.ts +4 -4
- package/project/.sdk/src/cmp/rb/ReadmeRef_rb.ts +5 -5
- package/project/.sdk/src/cmp/rb/Schema_rb.ts +1 -1
- package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +25 -25
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +66 -65
- package/project/.sdk/src/cmp/rust/EntityTypes_rust.ts +4 -4
- package/project/.sdk/src/cmp/rust/ReadmeEntity_rust.ts +3 -3
- package/project/.sdk/src/cmp/rust/ReadmeModel_rust.ts +3 -3
- package/project/.sdk/src/cmp/rust/ReadmeRef_rust.ts +5 -5
- package/project/.sdk/src/cmp/rust/Schema_rust.ts +1 -1
- package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +18 -18
- package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +63 -62
- package/project/.sdk/src/cmp/scala/EntityTypes_scala.ts +4 -4
- package/project/.sdk/src/cmp/scala/ReadmeEntity_scala.ts +3 -3
- package/project/.sdk/src/cmp/scala/ReadmeModel_scala.ts +3 -3
- package/project/.sdk/src/cmp/scala/ReadmeRef_scala.ts +5 -5
- package/project/.sdk/src/cmp/scala/Schema_scala.ts +1 -1
- package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +14 -14
- package/project/.sdk/src/cmp/scala/TestEntity_scala.ts +60 -59
- package/project/.sdk/src/cmp/swift/EntityTypes_swift.ts +4 -4
- package/project/.sdk/src/cmp/swift/ReadmeEntity_swift.ts +3 -3
- package/project/.sdk/src/cmp/swift/ReadmeModel_swift.ts +3 -3
- package/project/.sdk/src/cmp/swift/ReadmeRef_swift.ts +5 -5
- package/project/.sdk/src/cmp/swift/Schema_swift.ts +1 -1
- package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +5 -5
- package/project/.sdk/src/cmp/ts/ReadmeEntity_ts.ts +3 -3
- package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +3 -3
- package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/ReadmeRef_ts.ts +5 -5
- package/project/.sdk/src/cmp/ts/Schema_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +40 -40
- package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +72 -75
- package/project/.sdk/src/cmp/ts/TestLive_ts.ts +3 -3
- package/project/.sdk/src/cmp/ts/utility_ts.ts +5 -5
- package/project/.sdk/src/cmp/zig/ReadmeEntity_zig.ts +3 -3
- package/project/.sdk/src/cmp/zig/ReadmeModel_zig.ts +3 -3
- package/project/.sdk/src/cmp/zig/ReadmeRef_zig.ts +5 -5
- package/project/.sdk/src/cmp/zig/Schema_zig.ts +1 -1
- package/project/.sdk/tm/c/core/context.c +3 -1
- package/project/.sdk/tm/c/feature/paging.c +37 -3
- package/project/.sdk/tm/c/feature/test.c +71 -31
- package/project/.sdk/tm/c/utility/prepare_query.c +2 -1
- package/project/.sdk/tm/c/utility/transform_request.c +25 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +21 -7
- package/project/.sdk/tm/clojure/src/sdk/features.clj +74 -22
- package/project/.sdk/tm/cpp/core/types.hpp +1 -1
- package/project/.sdk/tm/cpp/feature/paging.hpp +36 -3
- package/project/.sdk/tm/cpp/feature/test.hpp +58 -19
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +16 -4
- package/project/.sdk/tm/csharp/core/Context.cs +1 -1
- package/project/.sdk/tm/csharp/feature/PagingFeature.cs +58 -4
- package/project/.sdk/tm/csharp/feature/TestFeature.cs +23 -25
- package/project/.sdk/tm/csharp/utility/Fetcher.cs +19 -12
- package/project/.sdk/tm/csharp/utility/PrepareQuery.cs +1 -1
- package/project/.sdk/tm/csharp/utility/TransformRequest.cs +23 -3
- package/project/.sdk/tm/elixir/lib/projectname/context.ex +1 -1
- package/project/.sdk/tm/elixir/lib/projectname/feature/paging.ex +52 -10
- package/project/.sdk/tm/elixir/lib/projectname/feature/test.ex +58 -22
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +30 -9
- package/project/.sdk/tm/go/core/context.go +13 -13
- package/project/.sdk/tm/go/feature/paging_feature.go +40 -2
- package/project/.sdk/tm/go/feature/test_feature.go +3 -15
- package/project/.sdk/tm/go/test/feature_corpus_test.go +1 -0
- package/project/.sdk/tm/go/test/feature_test.go +54 -0
- package/project/.sdk/tm/go/utility/fetcher.go +41 -20
- package/project/.sdk/tm/go/utility/prepare_query.go +1 -1
- package/project/.sdk/tm/go/utility/transform_request.go +22 -3
- package/project/.sdk/tm/java/core/Context.java +2 -1
- package/project/.sdk/tm/java/feature/PagingFeature.java +46 -4
- package/project/.sdk/tm/java/feature/TestFeature.java +21 -19
- package/project/.sdk/tm/java/test/FeatureCorpusTest.java +3 -0
- package/project/.sdk/tm/java/test/FeatureTest.java +42 -0
- package/project/.sdk/tm/java/utility/Fetcher.java +36 -28
- package/project/.sdk/tm/java/utility/PrepareQuery.java +1 -1
- package/project/.sdk/tm/java/utility/TransformRequest.java +18 -3
- package/project/.sdk/tm/js/src/Context.js +7 -2
- package/project/.sdk/tm/js/src/feature/paging/PagingFeature.js +24 -6
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +7 -5
- package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +1 -1
- package/project/.sdk/tm/js/test/feature/Corpus.test.js +4 -0
- package/project/.sdk/tm/js/test/live-entity.js +29 -29
- package/project/.sdk/tm/js/test/live-scenarios.js +4 -4
- package/project/.sdk/tm/kotlin/core/Context.kt +1 -1
- package/project/.sdk/tm/kotlin/feature/PagingFeature.kt +36 -4
- package/project/.sdk/tm/kotlin/feature/TestFeature.kt +19 -18
- package/project/.sdk/tm/kotlin/utility/Fetcher.kt +33 -24
- package/project/.sdk/tm/kotlin/utility/Prepare.kt +1 -1
- package/project/.sdk/tm/kotlin/utility/ResultTransform.kt +18 -3
- package/project/.sdk/tm/lua/core/context.lua +8 -1
- package/project/.sdk/tm/lua/core/control.lua +3 -0
- package/project/.sdk/tm/lua/feature/paging_feature.lua +44 -5
- package/project/.sdk/tm/lua/feature/test_feature.lua +32 -14
- package/project/.sdk/tm/lua/utility/prepare_query.lua +2 -1
- package/project/.sdk/tm/lua/utility/transform_request.lua +19 -3
- package/project/.sdk/tm/ocaml/sdk_features.ml +78 -25
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +23 -8
- package/project/.sdk/tm/perl/core/context.pm +2 -1
- package/project/.sdk/tm/perl/feature/paging_feature.pm +31 -5
- package/project/.sdk/tm/perl/feature/test_feature.pm +52 -20
- package/project/.sdk/tm/perl/feature/timeout_feature.pm +17 -13
- package/project/.sdk/tm/perl/t/feature_corpus.t +14 -1
- package/project/.sdk/tm/perl/utility/fetcher.pm +15 -1
- package/project/.sdk/tm/perl/utility/prepare_query.pm +1 -0
- package/project/.sdk/tm/perl/utility/transform_request.pm +13 -3
- package/project/.sdk/tm/php/core/Context.php +5 -1
- package/project/.sdk/tm/php/core/Control.php +5 -0
- package/project/.sdk/tm/php/feature/PagingFeature.php +26 -5
- package/project/.sdk/tm/php/feature/TestFeature.php +27 -42
- package/project/.sdk/tm/php/test/FeatureCorpusTest.php +17 -1
- package/project/.sdk/tm/php/test/FeatureTest.php +44 -0
- package/project/.sdk/tm/php/utility/Fetcher.php +17 -3
- package/project/.sdk/tm/php/utility/PrepareQuery.php +1 -1
- package/project/.sdk/tm/php/utility/TransformRequest.php +15 -3
- package/project/.sdk/tm/py/pkg/core/context.py +2 -1
- package/project/.sdk/tm/py/pkg/feature/paging_feature.py +29 -4
- package/project/.sdk/tm/py/pkg/feature/test_feature.py +29 -12
- package/project/.sdk/tm/py/pkg/utility/fetcher.py +63 -65
- package/project/.sdk/tm/py/pkg/utility/prepare_query.py +2 -1
- package/project/.sdk/tm/py/pkg/utility/transform_request.py +11 -3
- package/project/.sdk/tm/py/test/test_feature.py +27 -0
- package/project/.sdk/tm/py/test/test_feature_corpus.py +11 -1
- package/project/.sdk/tm/rb/core/context.rb +1 -1
- package/project/.sdk/tm/rb/feature/paging_feature.rb +27 -5
- package/project/.sdk/tm/rb/feature/test_feature.rb +39 -11
- package/project/.sdk/tm/rb/test/feature_corpus_test.rb +12 -1
- package/project/.sdk/tm/rb/test/feature_test.rb +38 -0
- package/project/.sdk/tm/rb/utility/fetcher.rb +49 -3
- package/project/.sdk/tm/rb/utility/prepare_query.rb +1 -1
- package/project/.sdk/tm/rb/utility/transform_request.rb +10 -3
- package/project/.sdk/tm/rust/core/context.rs +2 -6
- package/project/.sdk/tm/rust/feature/paging.rs +44 -3
- package/project/.sdk/tm/rust/feature/secrets.rs +1 -1
- package/project/.sdk/tm/rust/feature/test.rs +74 -37
- package/project/.sdk/tm/rust/utility/fetcher.rs +41 -14
- package/project/.sdk/tm/rust/utility/prepare_query.rs +1 -1
- package/project/.sdk/tm/rust/utility/transform_request.rs +31 -5
- package/project/.sdk/tm/scala/core/Context.scala +2 -1
- package/project/.sdk/tm/scala/feature/PagingFeature.scala +41 -4
- package/project/.sdk/tm/scala/feature/TestFeature.scala +21 -18
- package/project/.sdk/tm/scala/utility/Fetcher.scala +34 -13
- package/project/.sdk/tm/scala/utility/Prepare.scala +1 -1
- package/project/.sdk/tm/scala/utility/Transform.scala +19 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Context.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/PagingFeature.swift +31 -4
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +22 -15
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Fetcher.swift +22 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +1 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/ResultUtil.swift +14 -3
- package/project/.sdk/tm/ts/src/Context.ts +7 -2
- package/project/.sdk/tm/ts/src/feature/paging/PagingFeature.ts +19 -3
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +3 -0
- package/project/.sdk/tm/ts/src/utility/PrepareQueryUtility.ts +1 -1
- package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +1 -0
- package/project/.sdk/tm/ts/test/live-entity.ts +26 -26
- package/project/.sdk/tm/ts/test/live-scenarios.ts +3 -3
- package/project/.sdk/tm/zig/core/context.zig +1 -1
- package/project/.sdk/tm/zig/core/utility.zig +19 -5
- package/project/.sdk/tm/zig/feature/paging.zig +34 -4
- package/project/.sdk/tm/zig/feature/test.zig +65 -30
- package/project/sdkgen-package.json +1 -1
- package/src/action/doctor.ts +12 -9
- package/src/action/edition.ts +8 -0
- package/src/action/feature.ts +7 -1
- package/src/action/remove.ts +492 -0
- package/src/action/target.ts +8 -1
- package/src/cmp/ReadmeTop.ts +1 -1
- package/src/cmp/ReadmeUnions.ts +1 -1
- package/src/cmp/Test.ts +1 -1
- package/src/helpers/buildIdNames.ts +17 -7
- package/src/helpers/canonSpec.ts +2 -2
- package/src/helpers/getMatchEntries.ts +2 -2
- package/src/helpers/modelNames.ts +6 -1
- package/src/helpers/opShape.ts +33 -33
- package/src/helpers/pointPath.ts +5 -10
- package/src/helpers/resolved.ts +3 -14
- package/src/sdkgen.ts +3 -1
- package/src/utility.ts +16 -7
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
# stamps page/limit (or a cursor) into the request query; on the way back
|
|
5
5
|
# (PreResult) it reads the server's pagination signals - a Link rel="next"
|
|
6
6
|
# header, X-Page/X-Next-Page/X-Total-Count headers, or next/cursor/
|
|
7
|
-
# nextCursor/hasMore fields in the body - and records
|
|
8
|
-
# result.paging. A per-call ctrl paging value (page or cursor) takes
|
|
7
|
+
# nextCursor/hasMore fields in the body (snake_case forms too) - and records
|
|
8
|
+
# them on result.paging and the caller's ctrl paging. A per-call ctrl paging value (page or cursor) takes
|
|
9
9
|
# priority. Parameter names ("pageParam", "limitParam", "cursorParam"),
|
|
10
10
|
# "startPage" (default 1) and page size ("limit") are configurable.
|
|
11
11
|
|
|
@@ -76,8 +76,11 @@ sub PreRequest {
|
|
|
76
76
|
$spec->{query}{$cursor_param} = $paging->{cursor};
|
|
77
77
|
}
|
|
78
78
|
elsif (!defined $spec->{query}{$page_param}) {
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
# A record written back by PreResult holds the page just fetched as
|
|
80
|
+
# "page" and the one to fetch as "nextPage", so nextPage wins.
|
|
81
|
+
my $page = defined $paging->{nextPage} ? $paging->{nextPage} : $paging->{page};
|
|
82
|
+
$spec->{query}{$page_param} = defined $page
|
|
83
|
+
? $page
|
|
81
84
|
: (defined $self->{options}{startPage} ? $self->{options}{startPage} : 1);
|
|
82
85
|
}
|
|
83
86
|
|
|
@@ -186,15 +189,27 @@ sub PreResult {
|
|
|
186
189
|
}
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
# Body-level
|
|
192
|
+
# Body-level signals; snake_case forms are read first so camelCase wins.
|
|
190
193
|
if (Voxgig::Struct::ismap($body)) {
|
|
191
194
|
my $bnext = ProjectNameHelpers::gp($body, 'next');
|
|
192
195
|
$paging->{next} = defined $paging->{next} ? $paging->{next} : $bnext
|
|
193
196
|
if defined $bnext;
|
|
197
|
+
my $bsnakecursor = ProjectNameHelpers::gp($body, 'next_cursor');
|
|
198
|
+
$paging->{cursor} = $bsnakecursor if defined $bsnakecursor;
|
|
194
199
|
my $bcursor = ProjectNameHelpers::gp($body, 'cursor');
|
|
195
200
|
$paging->{cursor} = $bcursor if defined $bcursor;
|
|
196
201
|
my $bnextcursor = ProjectNameHelpers::gp($body, 'nextCursor');
|
|
197
202
|
$paging->{cursor} = $bnextcursor if defined $bnextcursor;
|
|
203
|
+
unless (defined $paging->{nextPage}) {
|
|
204
|
+
my $np = ProjectNameHelpers::gp($body, 'nextPage');
|
|
205
|
+
$np = ProjectNameHelpers::gp($body, 'next_page') unless defined $np;
|
|
206
|
+
$paging->{nextPage} = $np if defined $np && !ref($np);
|
|
207
|
+
}
|
|
208
|
+
my $bsnakemore = $body->{has_more};
|
|
209
|
+
if (ProjectNameHelpers::is_true($bsnakemore) || ProjectNameHelpers::is_false($bsnakemore)) {
|
|
210
|
+
$paging->{hasMore} = ProjectNameHelpers::is_true($bsnakemore) ? 1 : 0;
|
|
211
|
+
$explicit_more = 1;
|
|
212
|
+
}
|
|
198
213
|
my $bhasmore = $body->{hasMore};
|
|
199
214
|
if (ProjectNameHelpers::is_true($bhasmore) || ProjectNameHelpers::is_false($bhasmore)) {
|
|
200
215
|
$paging->{hasMore} = ProjectNameHelpers::is_true($bhasmore) ? 1 : 0;
|
|
@@ -215,6 +230,17 @@ sub PreResult {
|
|
|
215
230
|
|
|
216
231
|
$result->{paging} = $paging;
|
|
217
232
|
|
|
233
|
+
if ($ctx->{ctrl}) {
|
|
234
|
+
# The Context shares the caller's paging hash by reference, so refill it
|
|
235
|
+
# in place; a fresh hash on ctrl never reaches the caller.
|
|
236
|
+
if (Voxgig::Struct::ismap($ctx->{ctrl}{paging})) {
|
|
237
|
+
%{ $ctx->{ctrl}{paging} } = %$paging;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
$ctx->{ctrl}{paging} = $paging;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
218
244
|
$self->{client}{_paging} = { 'last' => $paging };
|
|
219
245
|
return;
|
|
220
246
|
}
|
|
@@ -66,10 +66,12 @@ sub init {
|
|
|
66
66
|
my $tm = ProjectNameHelpers::gp($fctx->{point}, 'transform');
|
|
67
67
|
my $spec = ProjectNameHelpers::gp($tm, 'res');
|
|
68
68
|
return $data unless defined $spec && !ref($spec);
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
return $data unless $spec =~ /^`body\.(
|
|
72
|
-
|
|
69
|
+
# Rebuild whatever nesting the transform unwraps. Multi-segment on purpose:
|
|
70
|
+
# GraphQL ops unwrap `body.data.<field>`, not just one envelope property.
|
|
71
|
+
return $data unless $spec =~ /^`body\.(.+)`$/;
|
|
72
|
+
my $out = $data;
|
|
73
|
+
$out = { $_ => $out } for reverse split /\./, $1, -1;
|
|
74
|
+
return $out;
|
|
73
75
|
};
|
|
74
76
|
|
|
75
77
|
my $respond = sub {
|
|
@@ -152,18 +154,11 @@ sub init {
|
|
|
152
154
|
my $args = $test_self->build_args($fctx, $op, $update_match);
|
|
153
155
|
my $found = Voxgig::Struct::select($entmap, $args);
|
|
154
156
|
my $ent = ProjectNameHelpers::ge($found, 0);
|
|
155
|
-
|
|
156
|
-
for my $k (sort keys %$entmap) {
|
|
157
|
-
if (Voxgig::Struct::ismap($entmap->{$k})) {
|
|
158
|
-
$ent = $entmap->{$k};
|
|
159
|
-
last;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
157
|
+
# update miss: 404, never another record
|
|
163
158
|
return $respond->($fctx, 404, undef, { 'statusText' => 'Not found' })
|
|
164
159
|
unless ProjectNameHelpers::rb_truthy($ent);
|
|
165
|
-
if (Voxgig::Struct::ismap($ent) && $fctx->{reqdata}) {
|
|
166
|
-
$ent
|
|
160
|
+
if (Voxgig::Struct::ismap($ent) && Voxgig::Struct::ismap($fctx->{reqdata})) {
|
|
161
|
+
Voxgig::Struct::merge([$ent, $fctx->{reqdata}]);
|
|
167
162
|
}
|
|
168
163
|
Voxgig::Struct::delprop($ent, '$KEY');
|
|
169
164
|
my $out = Voxgig::Struct::clone($ent);
|
|
@@ -288,18 +283,55 @@ sub make_netsim {
|
|
|
288
283
|
};
|
|
289
284
|
}
|
|
290
285
|
|
|
286
|
+
sub _point_terminal {
|
|
287
|
+
my ($p) = @_;
|
|
288
|
+
my $parts = ProjectNameHelpers::gp($p, 'parts');
|
|
289
|
+
return 0 unless Voxgig::Struct::islist($parts) && @$parts;
|
|
290
|
+
my $last = $parts->[-1];
|
|
291
|
+
return (defined $last && !ref $last && 0 == index($last, '{')) ? 1 : 0;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
sub _point_depth {
|
|
295
|
+
my ($p) = @_;
|
|
296
|
+
my $parts = ProjectNameHelpers::gp($p, 'parts');
|
|
297
|
+
return Voxgig::Struct::islist($parts) ? scalar @$parts : 0;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# The entity's own endpoint: a terminal `{param}` marks a record route, and
|
|
301
|
+
# among equals the shallower path wins (the same rule as make_point).
|
|
302
|
+
sub _pick_point {
|
|
303
|
+
my ($points) = @_;
|
|
304
|
+
return undef unless Voxgig::Struct::islist($points) && @$points;
|
|
305
|
+
my $point = $points->[0];
|
|
306
|
+
for my $cand (@$points[1 .. $#$points]) {
|
|
307
|
+
if (_point_terminal($cand) != _point_terminal($point)) {
|
|
308
|
+
$point = $cand if _point_terminal($cand);
|
|
309
|
+
}
|
|
310
|
+
elsif (_point_depth($cand) < _point_depth($point)) {
|
|
311
|
+
$point = $cand;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return $point;
|
|
315
|
+
}
|
|
316
|
+
|
|
291
317
|
sub build_args {
|
|
292
318
|
my ($self, $ctx, $op, $args) = @_;
|
|
293
319
|
my $opname = $op->{name};
|
|
294
320
|
my $entname = $ctx->{entity}->get_name;
|
|
295
321
|
my $points = ProjectNameHelpers::gpath($ctx->{config},
|
|
296
322
|
"entity.$entname.op.$opname.points");
|
|
297
|
-
my $point =
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
my $
|
|
323
|
+
my $point = _pick_point($points);
|
|
324
|
+
|
|
325
|
+
# Path AND query: a path-only read misses a query-addressed record
|
|
326
|
+
# (e.g. GET /result?trace_id=), which has no path param at all.
|
|
327
|
+
my $reqd = [];
|
|
328
|
+
for my $kind ('params', 'query') {
|
|
329
|
+
my $args_path = ProjectNameHelpers::gpath($point, "args.$kind");
|
|
330
|
+
my $reqd_args = Voxgig::Struct::select($args_path,
|
|
331
|
+
{ 'reqd' => Voxgig::Struct::JTRUE() });
|
|
332
|
+
my $names = Voxgig::Struct::transform($reqd_args, ['`$EACH`', '', '`$KEY.name`']);
|
|
333
|
+
push @$reqd, @$names if Voxgig::Struct::islist($names);
|
|
334
|
+
}
|
|
303
335
|
|
|
304
336
|
my $qand = [];
|
|
305
337
|
my $q = { '`$AND`' => $qand };
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
# Per-request timeout. Wraps the active transport with a deadline of "ms"
|
|
4
4
|
# milliseconds (default 30000; <= 0 disables). The transport is synchronous
|
|
5
5
|
# (HTTP::Tiny), so a hanging request is interrupted with a SIGALRM timer
|
|
6
|
-
# (Time::HiRes::ualarm)
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# (Time::HiRes::ualarm) where the platform has one; where it has none
|
|
7
|
+
# (Windows) the deadline is checked once the call returns. When an
|
|
8
|
+
# injectable "now" clock is supplied the elapsed clock time is checked
|
|
9
|
+
# instead, so tests can assert the deadline deterministically. Expiry
|
|
10
|
+
# yields an error with code "timeout".
|
|
9
11
|
|
|
10
12
|
use strict;
|
|
11
13
|
use warnings;
|
|
@@ -75,24 +77,26 @@ sub with_timeout {
|
|
|
75
77
|
# Live path: interrupt a hanging synchronous transport.
|
|
76
78
|
my ($res, $err);
|
|
77
79
|
my $timed_out = 0;
|
|
78
|
-
my $
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
1;
|
|
85
|
-
};
|
|
80
|
+
my $start = Time::HiRes::time();
|
|
81
|
+
local $SIG{ALRM} = sub { $timed_out = 1; die "ProjectNameTimeout\n" };
|
|
82
|
+
my $armed = eval { Time::HiRes::ualarm(int($ms * 1000)); 1 };
|
|
83
|
+
my $ok = eval {
|
|
84
|
+
($res, $err) = $inner->($ctx, $url, $fetchdef);
|
|
85
|
+
1;
|
|
86
86
|
};
|
|
87
|
-
|
|
87
|
+
my $e = $@;
|
|
88
|
+
eval { Time::HiRes::ualarm(0) } if $armed;
|
|
88
89
|
if (!$ok) {
|
|
89
|
-
my $e = $@;
|
|
90
90
|
if ($timed_out) {
|
|
91
91
|
$self->_track($ctx, $ms);
|
|
92
92
|
return (undef, $ctx->make_error('timeout', "Request exceeded timeout of ${ms}ms"));
|
|
93
93
|
}
|
|
94
94
|
die $e;
|
|
95
95
|
}
|
|
96
|
+
if (!$armed && (Time::HiRes::time() - $start) * 1000 > $ms) {
|
|
97
|
+
$self->_track($ctx, $ms);
|
|
98
|
+
return (undef, $ctx->make_error('timeout', "Request exceeded timeout of ${ms}ms"));
|
|
99
|
+
}
|
|
96
100
|
return ($res, $err);
|
|
97
101
|
}
|
|
98
102
|
|
|
@@ -267,6 +267,15 @@ sub subset {
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
|
|
270
|
+
# Whether the SDK built the feature at all. The activity record is the
|
|
271
|
+
# wrong probe: most features create theirs on first use, so an idle client
|
|
272
|
+
# has none and every section but the eager ones read as inert.
|
|
273
|
+
sub present {
|
|
274
|
+
my ($client, $name) = @_;
|
|
275
|
+
return scalar grep { ref $_ && ($_->{name} // '') eq $name }
|
|
276
|
+
@{ $client->{features} || [] };
|
|
277
|
+
}
|
|
278
|
+
|
|
270
279
|
sub record {
|
|
271
280
|
my ($client, $name) = @_;
|
|
272
281
|
return $client->{"_$name"};
|
|
@@ -295,7 +304,11 @@ for my $name (sort keys %{ $CORPUS->{feature} || {} }) {
|
|
|
295
304
|
# Probed by ACTIVATING it: the feature defaults to inactive, so an idle
|
|
296
305
|
# client never builds it and its absence says nothing.
|
|
297
306
|
my $probe = build_client({ feature => [ { name => $name, active => 1 } ] });
|
|
298
|
-
|
|
307
|
+
unless (present($probe, $name)) {
|
|
308
|
+
# The one line every runner prints for an inert section.
|
|
309
|
+
diag("feature.$name: inert (this SDK does not generate the feature)");
|
|
310
|
+
next;
|
|
311
|
+
}
|
|
299
312
|
|
|
300
313
|
my %by_key = map { $_->{key} => $_ } @ops;
|
|
301
314
|
|
|
@@ -16,6 +16,16 @@ package ProjectNameUtilities;
|
|
|
16
16
|
|
|
17
17
|
our %REGISTRY;
|
|
18
18
|
|
|
19
|
+
# One HTTP::Tiny per (proxy, redirect) setting: a client keeps its
|
|
20
|
+
# connection alive between requests, so one per request reused nothing.
|
|
21
|
+
my %HTTP_CLIENTS;
|
|
22
|
+
|
|
23
|
+
sub _http_client {
|
|
24
|
+
my (%new_args) = @_;
|
|
25
|
+
my $key = join "\n", map { "$_=$new_args{$_}" } sort keys %new_args;
|
|
26
|
+
return $HTTP_CLIENTS{$key} //= HTTP::Tiny->new(%new_args);
|
|
27
|
+
}
|
|
28
|
+
|
|
19
29
|
our $DefaultHttpFetch = sub {
|
|
20
30
|
my ($fullurl, $fetchdef) = @_;
|
|
21
31
|
$fetchdef = {} unless defined $fetchdef;
|
|
@@ -47,8 +57,12 @@ our $DefaultHttpFetch = sub {
|
|
|
47
57
|
$new_args{max_redirect} = 0
|
|
48
58
|
if defined $fetchdef->{redirect} && !ref $fetchdef->{redirect}
|
|
49
59
|
&& 'manual' eq $fetchdef->{redirect};
|
|
60
|
+
# Both keys: HTTP::Tiny lets the environment override its generic proxy.
|
|
61
|
+
$new_args{http_proxy} = $new_args{https_proxy} = "$fetchdef->{proxy}"
|
|
62
|
+
if defined $fetchdef->{proxy} && !ref $fetchdef->{proxy}
|
|
63
|
+
&& length $fetchdef->{proxy};
|
|
50
64
|
|
|
51
|
-
my $res = eval {
|
|
65
|
+
my $res = eval { _http_client(%new_args)->request($method, $fullurl, $opts) };
|
|
52
66
|
if (!$res) {
|
|
53
67
|
my $e = defined $@ ? "$@" : 'request failed';
|
|
54
68
|
$e =~ s/\s+\z//;
|
|
@@ -30,6 +30,7 @@ $REGISTRY{prepare_query} = sub {
|
|
|
30
30
|
for my $item (@$items) {
|
|
31
31
|
my ($key, $val) = @$item;
|
|
32
32
|
next unless ProjectNameHelpers::rb_truthy($val) && defined $key && !ref $key;
|
|
33
|
+
next if '$action' eq $key;
|
|
33
34
|
next if grep { defined $_ && !ref $_ && $_ eq $key } @$params;
|
|
34
35
|
$out->{$key} = $val;
|
|
35
36
|
}
|
|
@@ -15,16 +15,26 @@ package ProjectNameUtilities;
|
|
|
15
15
|
|
|
16
16
|
our %REGISTRY;
|
|
17
17
|
|
|
18
|
+
# `$action` selects the point (see make_point); it is never an API field, so
|
|
19
|
+
# the body is a copy without it. The caller's hash is left untouched.
|
|
20
|
+
my $strip_action = sub {
|
|
21
|
+
my ($reqdata) = @_;
|
|
22
|
+
return $reqdata unless 'HASH' eq ref $reqdata && exists $reqdata->{'$action'};
|
|
23
|
+
my %body = %$reqdata;
|
|
24
|
+
delete $body{'$action'};
|
|
25
|
+
return \%body;
|
|
26
|
+
};
|
|
27
|
+
|
|
18
28
|
$REGISTRY{transform_request} = sub {
|
|
19
29
|
my ($ctx) = @_;
|
|
20
30
|
my $spec = $ctx->{spec};
|
|
21
31
|
my $point = $ctx->{point};
|
|
22
32
|
$spec->{step} = 'reqform' if $spec;
|
|
23
33
|
my $transform = ProjectNameHelpers::to_map(ProjectNameHelpers::gp($point, 'transform'));
|
|
24
|
-
return $ctx->{reqdata} unless $transform;
|
|
34
|
+
return $strip_action->($ctx->{reqdata}) unless $transform;
|
|
25
35
|
my $reqform = ProjectNameHelpers::gp($transform, 'req');
|
|
26
|
-
return $ctx->{reqdata} unless ProjectNameHelpers::rb_truthy($reqform);
|
|
27
|
-
return Voxgig::Struct::transform({ 'reqdata' => $ctx->{reqdata} }, $reqform);
|
|
36
|
+
return $strip_action->($ctx->{reqdata}) unless ProjectNameHelpers::rb_truthy($reqform);
|
|
37
|
+
return $strip_action->(Voxgig::Struct::transform({ 'reqdata' => $ctx->{reqdata} }, $reqform));
|
|
28
38
|
};
|
|
29
39
|
|
|
30
40
|
1;
|
|
@@ -55,7 +55,11 @@ class ProjectNameContext
|
|
|
55
55
|
if (array_key_exists('actor', $ctrl_raw)) {
|
|
56
56
|
$this->ctrl->actor = $ctrl_raw['actor'];
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
if (isset($ctrl_raw['paging']) && is_array($ctrl_raw['paging'])) {
|
|
59
|
+
$this->ctrl->paging = $ctrl_raw['paging'];
|
|
60
|
+
}
|
|
61
|
+
} elseif ($basectx !== null && $basectx->ctrl !== null
|
|
62
|
+
&& ProjectNameHelpers::get_ctx_prop($ctxmap, "opname") === null) {
|
|
59
63
|
$this->ctrl = $basectx->ctrl;
|
|
60
64
|
}
|
|
61
65
|
|
|
@@ -21,11 +21,16 @@ class ProjectNameControl
|
|
|
21
21
|
// passed. The shared feature corpus caught it.
|
|
22
22
|
public mixed $actor;
|
|
23
23
|
|
|
24
|
+
// Per-call paging record: read by the paging feature before the request
|
|
25
|
+
// and written back after it. Declared for the same reason as `$actor`.
|
|
26
|
+
public mixed $paging;
|
|
27
|
+
|
|
24
28
|
public function __construct(array $opts = [])
|
|
25
29
|
{
|
|
26
30
|
$this->throw_err = $opts['throw_err'] ?? null;
|
|
27
31
|
$this->err = null;
|
|
28
32
|
$this->explain = $opts['explain'] ?? null;
|
|
29
33
|
$this->actor = $opts['actor'] ?? null;
|
|
34
|
+
$this->paging = $opts['paging'] ?? null;
|
|
30
35
|
}
|
|
31
36
|
}
|
|
@@ -9,8 +9,8 @@ require_once __DIR__ . '/BaseFeature.php';
|
|
|
9
9
|
// stamps page/limit (or a cursor) into the request query; on the way back
|
|
10
10
|
// (PreResult) it reads the server's pagination signals — a `Link:
|
|
11
11
|
// rel="next"` header, `X-Page`/`X-Next-Page`/`X-Total-Count` headers, or
|
|
12
|
-
// `next`/`cursor`/`nextCursor`/`hasMore` fields in the body
|
|
13
|
-
// them on `$ctx->result->paging`. A per-call `ctrl->paging` (page or
|
|
12
|
+
// `next`/`cursor`/`nextCursor`/`hasMore` fields in the body (snake_case
|
|
13
|
+
// forms too) — and records them on `$ctx->result->paging` and `ctrl->paging`. A per-call `ctrl->paging` (page or
|
|
14
14
|
// cursor) takes priority over the stamped defaults; parameter names and
|
|
15
15
|
// page size are configurable; `startPage` defaults to 1. Mirrors
|
|
16
16
|
// ts/src/feature/paging/PagingFeature.ts.
|
|
@@ -71,8 +71,10 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
71
71
|
} elseif (($spec->query[$page_param] ?? null) === null) {
|
|
72
72
|
$start_page = is_numeric($this->options['startPage'] ?? null)
|
|
73
73
|
? $this->options['startPage'] : 1;
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
// A record written back by PreResult holds the page just fetched
|
|
75
|
+
// as "page" and the one to fetch as "nextPage", so nextPage wins.
|
|
76
|
+
$page = $paging['nextPage'] ?? $paging['page'] ?? null;
|
|
77
|
+
$spec->query[$page_param] = $page !== null ? $page : $start_page;
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
if (($this->options['limit'] ?? null) !== null
|
|
@@ -194,17 +196,30 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
194
196
|
}
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
// Body-level
|
|
199
|
+
// Body-level signals; snake_case forms are read first so camelCase wins.
|
|
198
200
|
if (is_array($body)) {
|
|
199
201
|
if (($body['next'] ?? null) !== null) {
|
|
200
202
|
$paging['next'] = $paging['next'] ?? $body['next'];
|
|
201
203
|
}
|
|
204
|
+
if (($body['next_cursor'] ?? null) !== null) {
|
|
205
|
+
$paging['cursor'] = $body['next_cursor'];
|
|
206
|
+
}
|
|
202
207
|
if (($body['cursor'] ?? null) !== null) {
|
|
203
208
|
$paging['cursor'] = $body['cursor'];
|
|
204
209
|
}
|
|
205
210
|
if (($body['nextCursor'] ?? null) !== null) {
|
|
206
211
|
$paging['cursor'] = $body['nextCursor'];
|
|
207
212
|
}
|
|
213
|
+
if ($paging['nextPage'] === null) {
|
|
214
|
+
$np = $body['nextPage'] ?? $body['next_page'] ?? null;
|
|
215
|
+
if (is_int($np) || is_float($np) || is_string($np)) {
|
|
216
|
+
$paging['nextPage'] = $np;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (is_bool($body['has_more'] ?? null)) {
|
|
220
|
+
$paging['hasMore'] = $body['has_more'];
|
|
221
|
+
$explicit_more = true;
|
|
222
|
+
}
|
|
208
223
|
if (is_bool($body['hasMore'] ?? null)) {
|
|
209
224
|
$paging['hasMore'] = $body['hasMore'];
|
|
210
225
|
$explicit_more = true;
|
|
@@ -225,6 +240,12 @@ class ProjectNamePagingFeature extends ProjectNameBaseFeature
|
|
|
225
240
|
|
|
226
241
|
$result->paging = $paging;
|
|
227
242
|
|
|
243
|
+
// PHP arrays are values, so the record lands on this call's Control
|
|
244
|
+
// rather than on an array the caller passed.
|
|
245
|
+
if ($ctx->ctrl !== null) {
|
|
246
|
+
$ctx->ctrl->paging = $paging;
|
|
247
|
+
}
|
|
248
|
+
|
|
228
249
|
$this->client->_paging = ['last' => $paging];
|
|
229
250
|
}
|
|
230
251
|
|
|
@@ -35,8 +35,8 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
35
35
|
|
|
36
36
|
$this->client->mode = 'test';
|
|
37
37
|
|
|
38
|
-
// Ensure entity ids are correct.
|
|
39
|
-
\Voxgig\Struct\Struct::walk($entity_data, function ($key, $val, $parent, $path) {
|
|
38
|
+
// Ensure entity ids are correct (walk is by value: keep its result).
|
|
39
|
+
$entity_data = \Voxgig\Struct\Struct::walk($entity_data, function ($key, $val, $parent, $path) {
|
|
40
40
|
if (count($path) === 2 && is_array($val) && $key !== null) {
|
|
41
41
|
$val['id'] = $key;
|
|
42
42
|
}
|
|
@@ -103,9 +103,8 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
103
103
|
|
|
104
104
|
// PHP-portable equivalent of TS buildArgs+select: a flat-key
|
|
105
105
|
// filter that matches by exact-equality on each provided key,
|
|
106
|
-
// with alias fallback.
|
|
107
|
-
//
|
|
108
|
-
// create), list returns all entries.
|
|
106
|
+
// with alias fallback. An empty match matches every entry, as an
|
|
107
|
+
// empty `$AND` does in select; a non-empty miss matches nothing.
|
|
109
108
|
$find_first = function (array $entmap, $match, $alias) {
|
|
110
109
|
if (!is_array($match) || empty($match)) {
|
|
111
110
|
foreach ($entmap as $e) {
|
|
@@ -158,6 +157,23 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
158
157
|
|
|
159
158
|
$alias = is_object($op) ? ($op->alias ?? null) : \Voxgig\Struct\Struct::getprop($op, 'alias');
|
|
160
159
|
|
|
160
|
+
// Every op filters through buildArgs, which keeps `id` and the
|
|
161
|
+
// point's required params and drops the rest. find_first/find_all
|
|
162
|
+
// take a flat map, so take the primary key of each $OR clause;
|
|
163
|
+
// they do their own alias fallback.
|
|
164
|
+
$query_match = function ($args) use ($fctx, $op) {
|
|
165
|
+
$flat = [];
|
|
166
|
+
foreach (($this->buildArgs($fctx, $op, $args)['$AND'] ?? []) as $clause) {
|
|
167
|
+
$ors = $clause['$OR'] ?? [];
|
|
168
|
+
if (is_array($ors) && count($ors) > 0 && is_array($ors[0])) {
|
|
169
|
+
foreach ($ors[0] as $lk => $lv) {
|
|
170
|
+
$flat[$lk] = $lv;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return $flat;
|
|
175
|
+
};
|
|
176
|
+
|
|
161
177
|
// For single-entity ops (load, remove) with an empty explicit
|
|
162
178
|
// match, fall back to the id the entity client already knows from a
|
|
163
179
|
// prior create/load (carried in $fctx->match / $fctx->data). This
|
|
@@ -176,7 +192,7 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
176
192
|
};
|
|
177
193
|
|
|
178
194
|
if ($op->name === 'load') {
|
|
179
|
-
$ent = $find_first($entmap, $resolve_match($fctx->reqmatch), $alias);
|
|
195
|
+
$ent = $find_first($entmap, $query_match($resolve_match($fctx->reqmatch)), $alias);
|
|
180
196
|
if ($ent === null) {
|
|
181
197
|
return $respond(404, null, ['statusText' => 'Not found']);
|
|
182
198
|
}
|
|
@@ -185,37 +201,7 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
185
201
|
return $respond(200, $out);
|
|
186
202
|
|
|
187
203
|
} elseif ($op->name === 'list') {
|
|
188
|
-
|
|
189
|
-
//
|
|
190
|
-
// This branch used the whole of reqmatch, while load goes
|
|
191
|
-
// through resolve_match and the TS mock runs every op through
|
|
192
|
-
// buildArgs — which keeps `id` and the point's REQUIRED params
|
|
193
|
-
// and drops the rest. buildArgs is right here in this file and
|
|
194
|
-
// its docblock says it mirrors the TS one; list simply did not
|
|
195
|
-
// call it.
|
|
196
|
-
//
|
|
197
|
-
// So a match key that is neither `id` nor a required route
|
|
198
|
-
// param matched NOTHING in php and was IGNORED in ts. trello's
|
|
199
|
-
// board_star lists on `board_id`, which is not a field of that
|
|
200
|
-
// entity (id, pos, member_id, id_board) and not a route param:
|
|
201
|
-
// php returned an empty list and BoardStarEntityTest failed on
|
|
202
|
-
// an assertion that ts, js and every other port passed.
|
|
203
|
-
//
|
|
204
|
-
// buildArgs returns a struct query; find_all takes a flat map,
|
|
205
|
-
// so take the primary key of each $OR clause. find_all already
|
|
206
|
-
// does its own alias fallback, which is what the second $OR
|
|
207
|
-
// element carries.
|
|
208
|
-
$listargs = $this->buildArgs($fctx, $op, $fctx->reqmatch);
|
|
209
|
-
$listmatch = [];
|
|
210
|
-
foreach (($listargs['$AND'] ?? []) as $clause) {
|
|
211
|
-
$ors = $clause['$OR'] ?? [];
|
|
212
|
-
if (is_array($ors) && count($ors) > 0 && is_array($ors[0])) {
|
|
213
|
-
foreach ($ors[0] as $lk => $lv) {
|
|
214
|
-
$listmatch[$lk] = $lv;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
$found = $find_all($entmap, $listmatch, $alias);
|
|
204
|
+
$found = $find_all($entmap, $query_match($fctx->reqmatch), $alias);
|
|
219
205
|
$cleaned = [];
|
|
220
206
|
foreach ($found as $e) {
|
|
221
207
|
if (is_array($e)) unset($e['$KEY']);
|
|
@@ -245,14 +231,13 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
245
231
|
if (empty($update_match)) {
|
|
246
232
|
$update_match = $resolve_match([]);
|
|
247
233
|
}
|
|
248
|
-
$ent = $find_first($entmap, $update_match, $alias);
|
|
234
|
+
$ent = $find_first($entmap, $query_match($update_match), $alias);
|
|
249
235
|
if ($ent === null) {
|
|
236
|
+
// update miss: 404, never another record
|
|
250
237
|
return $respond(404, null, ['statusText' => 'Not found']);
|
|
251
238
|
}
|
|
252
239
|
if (is_array($fctx->reqdata)) {
|
|
253
|
-
|
|
254
|
-
$ent[$k] = $v;
|
|
255
|
-
}
|
|
240
|
+
$ent = \Voxgig\Struct\Struct::merge([$ent, $fctx->reqdata]);
|
|
256
241
|
}
|
|
257
242
|
$id = is_array($ent) ? ($ent['id'] ?? null) : null;
|
|
258
243
|
if ($id !== null) {
|
|
@@ -264,7 +249,7 @@ class ProjectNameTestFeature extends ProjectNameBaseFeature
|
|
|
264
249
|
return $respond(200, $out);
|
|
265
250
|
|
|
266
251
|
} elseif ($op->name === 'remove') {
|
|
267
|
-
$ent = $find_first($entmap, $resolve_match($fctx->reqmatch), $alias);
|
|
252
|
+
$ent = $find_first($entmap, $query_match($resolve_match($fctx->reqmatch)), $alias);
|
|
268
253
|
// Remove only the first matched entity. If nothing matches,
|
|
269
254
|
// succeed as a no-op rather than erroring.
|
|
270
255
|
$id = is_array($ent) ? ($ent['id'] ?? null) : null;
|
|
@@ -267,6 +267,19 @@ class FeatureCorpusTest extends TestCase
|
|
|
267
267
|
$this->assertSame($expect, $actual, $path);
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
// Whether the SDK built the feature at all. The activity record is the
|
|
271
|
+
// wrong probe: most features create theirs on first use, so an idle
|
|
272
|
+
// client has none and every section but the eager ones read as inert.
|
|
273
|
+
private static function present($client, string $name): bool
|
|
274
|
+
{
|
|
275
|
+
foreach ($client->features ?? [] as $f) {
|
|
276
|
+
if (($f->name ?? null) === $name) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
270
283
|
private static function record($client, string $name)
|
|
271
284
|
{
|
|
272
285
|
$prop = '_' . $name;
|
|
@@ -326,7 +339,10 @@ class FeatureCorpusTest extends TestCase
|
|
|
326
339
|
// Probed by ACTIVATING it: the feature defaults to inactive, so an
|
|
327
340
|
// idle client never builds it and its absence says nothing.
|
|
328
341
|
$probe = self::buildClient(['feature' => [['name' => $name, 'active' => true]]]);
|
|
329
|
-
if (
|
|
342
|
+
if (!self::present($probe, $name)) {
|
|
343
|
+
// The one line every runner prints for an inert section.
|
|
344
|
+
fwrite(STDERR, sprintf(
|
|
345
|
+
"feature.%s: inert (this SDK does not generate the feature)\n", $name));
|
|
330
346
|
continue;
|
|
331
347
|
}
|
|
332
348
|
|
|
@@ -1104,6 +1104,50 @@ class FeatureTest extends TestCase
|
|
|
1104
1104
|
$this->assertTrue($res['result']->paging['hasMore']);
|
|
1105
1105
|
}
|
|
1106
1106
|
|
|
1107
|
+
public function test_paging_snake_case_signals_and_ctrl_write_back(): void
|
|
1108
|
+
{
|
|
1109
|
+
$this->needs('paging');
|
|
1110
|
+
$rec = new FtRecorder(function (int $n) {
|
|
1111
|
+
return FtHarness::response(200, 1 === $n
|
|
1112
|
+
? ['has_more' => true, 'next_cursor' => 'c2']
|
|
1113
|
+
: ['has_more' => false]);
|
|
1114
|
+
});
|
|
1115
|
+
$h = new FtHarness([
|
|
1116
|
+
['name' => 'paging'],
|
|
1117
|
+
], $rec->server());
|
|
1118
|
+
$res = $h->op(['op' => 'list', 'path' => '/w', 'ctrl' => ['paging' => []]]);
|
|
1119
|
+
$this->assertTrue($res['result']->paging['hasMore']);
|
|
1120
|
+
$this->assertSame('c2', $res['result']->paging['cursor']);
|
|
1121
|
+
$written = $res['ctx']->ctrl->paging;
|
|
1122
|
+
$this->assertSame('c2', $written['cursor']);
|
|
1123
|
+
$this->assertTrue($written['hasMore']);
|
|
1124
|
+
// Arrays are values here, so the written-back record is passed on by hand.
|
|
1125
|
+
$res = $h->op(['op' => 'list', 'path' => '/w', 'ctrl' => ['paging' => $written]]);
|
|
1126
|
+
$this->assertStringContainsString('cursor=c2', $rec->calls[1]['url']);
|
|
1127
|
+
$this->assertFalse($res['ctx']->ctrl->paging['hasMore']);
|
|
1128
|
+
$this->assertNull($res['ctx']->ctrl->paging['cursor']);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
public function test_paging_continues_from_written_back_next_page(): void
|
|
1132
|
+
{
|
|
1133
|
+
$this->needs('paging');
|
|
1134
|
+
$rec = new FtRecorder(function (int $n) {
|
|
1135
|
+
return FtHarness::response(200, 1 === $n ? ['next_page' => 2] : [],
|
|
1136
|
+
['x-page' => (string) $n]);
|
|
1137
|
+
});
|
|
1138
|
+
$h = new FtHarness([
|
|
1139
|
+
['name' => 'paging'],
|
|
1140
|
+
], $rec->server());
|
|
1141
|
+
$res = $h->op(['op' => 'list', 'path' => '/w']);
|
|
1142
|
+
$written = $res['ctx']->ctrl->paging;
|
|
1143
|
+
$this->assertEquals(1, $written['page']);
|
|
1144
|
+
$this->assertEquals(2, $written['nextPage']);
|
|
1145
|
+
$this->assertTrue($written['hasMore']);
|
|
1146
|
+
$res = $h->op(['op' => 'list', 'path' => '/w', 'ctrl' => ['paging' => $written]]);
|
|
1147
|
+
$this->assertStringContainsString('page=2', $rec->calls[1]['url']);
|
|
1148
|
+
$this->assertFalse($res['ctx']->ctrl->paging['hasMore']);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1107
1151
|
public function test_paging_non_list_op_is_not_paged(): void
|
|
1108
1152
|
{
|
|
1109
1153
|
$this->needs('paging');
|