@voxgig/sdkgen 3.5.1 → 3.7.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/cmp/AgentGuideTop.js +17 -0
- package/dist/cmp/AgentGuideTop.js.map +1 -1
- package/dist/cmp/ExternalTarget.js +1 -1
- package/dist/cmp/ExternalTarget.js.map +1 -1
- package/dist/cmp/Readme.js +2 -0
- package/dist/cmp/Readme.js.map +1 -1
- package/dist/cmp/ReadmeStation.d.ts +2 -0
- package/dist/cmp/ReadmeStation.js +57 -0
- package/dist/cmp/ReadmeStation.js.map +1 -0
- package/dist/helpers/naming.d.ts +6 -1
- package/dist/helpers/naming.js +156 -5
- package/dist/helpers/naming.js.map +1 -1
- package/dist/helpers/opShape.js +72 -0
- package/dist/helpers/opShape.js.map +1 -1
- package/dist/helpers/packageMeta.d.ts +2 -1
- package/dist/helpers/packageMeta.js +57 -20
- package/dist/helpers/packageMeta.js.map +1 -1
- package/dist/sdkgen.d.ts +3 -3
- package/dist/sdkgen.js +63 -18
- package/dist/sdkgen.js.map +1 -1
- package/dist/testkit.d.ts +39 -0
- package/dist/testkit.js +663 -0
- package/dist/testkit.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utility.d.ts +1 -1
- package/dist/utility.js +16 -2
- package/dist/utility.js.map +1 -1
- package/package.json +32 -8
- package/project/.sdk/src/cmp/c/Config_c.ts +34 -13
- package/project/.sdk/src/cmp/c/Test_c.ts +5 -7
- package/project/.sdk/src/cmp/clojure/Test_clojure.ts +3 -7
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +9 -46
- package/project/.sdk/src/cmp/cpp/Test_cpp.ts +4 -7
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +7 -1
- package/project/.sdk/src/cmp/csharp/Package_csharp.ts +1 -1
- package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -7
- package/project/.sdk/src/cmp/dart/Config_dart.ts +14 -2
- package/project/.sdk/src/cmp/dart/Test_dart.ts +3 -4
- package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +5 -0
- package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +6 -0
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +13 -4
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +12 -2
- package/project/.sdk/src/cmp/elixir/Package_elixir.ts +42 -3
- package/project/.sdk/src/cmp/elixir/Test_elixir.ts +5 -6
- package/project/.sdk/src/cmp/go/Config_go.ts +8 -1
- package/project/.sdk/src/cmp/go/EntityTypes_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Entity_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Gitignore_go.ts +6 -0
- package/project/.sdk/src/cmp/go/Main_go.ts +2 -2
- package/project/.sdk/src/cmp/go/Package_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeExamplesTest_go.ts +59 -6
- package/project/.sdk/src/cmp/go/ReadmeExplanation_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeHowto_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeInstall_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeModel_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +1 -1
- package/project/.sdk/src/cmp/go/ReadmeTopTest_go.ts +1 -1
- package/project/.sdk/src/cmp/go/Test_go.ts +6 -8
- package/project/.sdk/src/cmp/go-cli/Main_go-cli.ts +6 -2
- package/project/.sdk/src/cmp/go-mcp/Main_go-mcp.ts +6 -2
- package/project/.sdk/src/cmp/java/Config_java.ts +8 -1
- package/project/.sdk/src/cmp/java/Test_java.ts +4 -7
- package/project/.sdk/src/cmp/js/Config_js.ts +9 -1
- package/project/.sdk/src/cmp/js/Package_js.ts +1 -1
- package/project/.sdk/src/cmp/js/Test_js.ts +4 -7
- package/project/.sdk/src/cmp/js/fragment/Config.data.fragment.js +7 -0
- package/project/.sdk/src/cmp/js/fragment/Config.fragment.js +8 -0
- package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +33 -5
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +8 -1
- package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +4 -7
- package/project/.sdk/src/cmp/lean/Test_lean.ts +3 -3
- package/project/.sdk/src/cmp/lua/Config_lua.ts +11 -2
- package/project/.sdk/src/cmp/lua/Package_lua.ts +26 -3
- package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -7
- package/project/.sdk/src/cmp/ocaml/Test_ocaml.ts +3 -7
- package/project/.sdk/src/cmp/perl/Config_perl.ts +8 -60
- package/project/.sdk/src/cmp/perl/Package_perl.ts +1 -1
- package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -7
- package/project/.sdk/src/cmp/php/Config_php.ts +11 -2
- package/project/.sdk/src/cmp/php/EntityTypes_php.ts +13 -2
- package/project/.sdk/src/cmp/php/Main_php.ts +25 -0
- package/project/.sdk/src/cmp/php/Package_php.ts +2 -2
- package/project/.sdk/src/cmp/php/Test_php.ts +5 -7
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +23 -6
- package/project/.sdk/src/cmp/py/Config_py.ts +11 -2
- package/project/.sdk/src/cmp/py/Main_py.ts +17 -7
- package/project/.sdk/src/cmp/py/Package_py.ts +2 -2
- package/project/.sdk/src/cmp/py/Test_py.ts +5 -7
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +23 -6
- package/project/.sdk/src/cmp/py-data/Main_py-data.ts +14 -19
- package/project/.sdk/src/cmp/rb/Config_rb.ts +7 -1
- package/project/.sdk/src/cmp/rb/Package_rb.ts +3 -3
- package/project/.sdk/src/cmp/rb/ReadmeInstall_rb.ts +1 -1
- package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -7
- package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -2
- package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -7
- package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -2
- package/project/.sdk/src/cmp/seneca-provider/Gitignore_seneca-provider.ts +161 -0
- package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +20 -11
- package/project/.sdk/src/cmp/swift/Config_swift.ts +8 -54
- package/project/.sdk/src/cmp/swift/Package_swift.ts +52 -10
- package/project/.sdk/src/cmp/swift/Test_swift.ts +5 -7
- package/project/.sdk/src/cmp/ts/Config_ts.ts +9 -1
- package/project/.sdk/src/cmp/ts/EntityTypes_ts.ts +5 -2
- package/project/.sdk/src/cmp/ts/Entity_ts.ts +8 -2
- package/project/.sdk/src/cmp/ts/Package_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/Test_ts.ts +8 -7
- package/project/.sdk/src/cmp/ts/fragment/Config.data.fragment.ts +7 -0
- package/project/.sdk/src/cmp/ts/fragment/Config.fragment.ts +8 -0
- package/project/.sdk/src/cmp/ts/fragment/Entity.fragment.ts +1 -1
- package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +12 -4
- package/project/.sdk/src/cmp/zig/Test_zig.ts +3 -7
- package/project/.sdk/tm/c/Makefile +6 -1
- package/project/.sdk/tm/c/utility/make_options.c +12 -1
- package/project/.sdk/tm/c/utility/make_point.c +64 -3
- package/project/.sdk/tm/clojure/src/sdk/core.clj +49 -14
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +64 -4
- package/project/.sdk/tm/csharp/feature/TestFeature.cs +36 -2
- package/project/.sdk/tm/csharp/utility/Fetcher.cs +23 -5
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +13 -0
- package/project/.sdk/tm/csharp/utility/MakePoint.cs +65 -2
- package/project/.sdk/tm/dart/lib/feature/test/TestFeature.dart +34 -4
- package/project/.sdk/tm/dart/lib/utility/FetcherUtility.dart +9 -0
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +17 -0
- package/project/.sdk/tm/dart/lib/utility/MakePointUtility.dart +48 -2
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +109 -13
- package/project/.sdk/tm/elixir/test/feature_test.exs +19 -0
- package/project/.sdk/tm/elixir/test/primary_utility_test.exs +72 -0
- package/project/.sdk/tm/go/feature/test_feature.go +34 -2
- package/project/.sdk/tm/go/test/primary_utility_test.go +42 -20
- package/project/.sdk/tm/go/test/runner_test.go +1 -1
- package/project/.sdk/tm/go/utility/fetcher.go +14 -0
- package/project/.sdk/tm/go/utility/make_options.go +31 -4
- package/project/.sdk/tm/go/utility/make_point.go +66 -2
- package/project/.sdk/tm/java/feature/TestFeature.java +42 -2
- package/project/.sdk/tm/java/utility/Fetcher.java +11 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +11 -0
- package/project/.sdk/tm/java/utility/MakePoint.java +61 -2
- package/project/.sdk/tm/js/src/feature/test/TestFeature.js +22 -3
- package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +20 -2
- package/project/.sdk/tm/js/src/utility/MakePointUtility.js +72 -2
- package/project/.sdk/tm/kotlin/feature/TestFeature.kt +31 -2
- package/project/.sdk/tm/kotlin/utility/Fetcher.kt +10 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +11 -0
- package/project/.sdk/tm/kotlin/utility/MakePoint.kt +58 -2
- package/project/.sdk/tm/lean/src/SdkUtility.lean +28 -0
- package/project/.sdk/tm/lean/test/TPrimaryUtility.lean +13 -2
- package/project/.sdk/tm/lua/utility/make_options.lua +44 -0
- package/project/.sdk/tm/lua/utility/make_point.lua +65 -2
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +40 -2
- package/project/.sdk/tm/perl/utility/fetcher.pm +10 -1
- package/project/.sdk/tm/perl/utility/make_options.pm +36 -0
- package/project/.sdk/tm/perl/utility/make_point.pm +55 -2
- package/project/.sdk/tm/php/feature/TestFeature.php +30 -1
- package/project/.sdk/tm/php/test/PipelineTest.php +20 -0
- package/project/.sdk/tm/php/utility/Fetcher.php +12 -1
- package/project/.sdk/tm/php/utility/MakeOptions.php +58 -0
- package/project/.sdk/tm/php/utility/MakePoint.php +49 -1
- package/project/.sdk/tm/py/pkg/utility/fetcher.py +13 -1
- package/project/.sdk/tm/py/pkg/utility/make_options.py +23 -0
- package/project/.sdk/tm/py/pkg/utility/make_point.py +56 -2
- package/project/.sdk/tm/rb/utility/make_options.rb +18 -0
- package/project/.sdk/tm/rb/utility/make_point.rb +42 -2
- package/project/.sdk/tm/rust/tests/common/mod.rs +1 -1
- package/project/.sdk/tm/rust/tests/feature_test.rs +27 -0
- package/project/.sdk/tm/rust/utility/fetcher.rs +8 -0
- package/project/.sdk/tm/rust/utility/make_options.rs +15 -0
- package/project/.sdk/tm/rust/utility/make_point.rs +69 -2
- package/project/.sdk/tm/scala/feature/TestFeature.scala +36 -1
- package/project/.sdk/tm/scala/utility/Make.scala +57 -3
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/feature/TestFeature.swift +29 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Fetcher.swift +24 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Make.swift +51 -2
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +12 -0
- package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +22 -3
- package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +20 -2
- package/project/.sdk/tm/ts/src/utility/MakePointUtility.ts +72 -2
- package/project/.sdk/tm/ts/test/utility/Corpus.test.ts +85 -0
- package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +10 -17
- package/project/.sdk/tm/zig/core/utility.zig +66 -3
- package/project/sdkgen-package.json +1 -2
- package/src/cmp/AgentGuideTop.ts +18 -0
- package/src/cmp/ExternalTarget.ts +1 -1
- package/src/cmp/Readme.ts +2 -0
- package/src/cmp/ReadmeStation.ts +69 -0
- package/src/helpers/naming.ts +172 -5
- package/src/helpers/opShape.ts +85 -0
- package/src/helpers/packageMeta.ts +59 -20
- package/src/sdkgen.ts +70 -20
- package/src/testkit.ts +863 -0
- package/src/utility.ts +17 -2
- package/testkit.d.ts +3 -0
- package/testkit.js +12 -0
- package/project/.sdk/model/target/haskell.aontu +0 -72
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +0 -98
- package/project/.sdk/src/cmp/haskell/Entity_haskell.ts +0 -13
- package/project/.sdk/src/cmp/haskell/Gitignore_haskell.ts +0 -27
- package/project/.sdk/src/cmp/haskell/Main_haskell.ts +0 -123
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +0 -60
- package/project/.sdk/src/cmp/haskell/ReadmeEntity_haskell.ts +0 -186
- package/project/.sdk/src/cmp/haskell/ReadmeExamplesTest_haskell.ts +0 -135
- package/project/.sdk/src/cmp/haskell/ReadmeExplanation_haskell.ts +0 -48
- package/project/.sdk/src/cmp/haskell/ReadmeHowto_haskell.ts +0 -168
- package/project/.sdk/src/cmp/haskell/ReadmeInstall_haskell.ts +0 -51
- package/project/.sdk/src/cmp/haskell/ReadmeIntro_haskell.ts +0 -67
- package/project/.sdk/src/cmp/haskell/ReadmeModel_haskell.ts +0 -162
- package/project/.sdk/src/cmp/haskell/ReadmeOptions_haskell.ts +0 -74
- package/project/.sdk/src/cmp/haskell/ReadmeQuick_haskell.ts +0 -241
- package/project/.sdk/src/cmp/haskell/ReadmeRef_haskell.ts +0 -408
- package/project/.sdk/src/cmp/haskell/ReadmeTopHowto_haskell.ts +0 -31
- package/project/.sdk/src/cmp/haskell/ReadmeTopQuick_haskell.ts +0 -104
- package/project/.sdk/src/cmp/haskell/ReadmeTopTest_haskell.ts +0 -80
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +0 -300
- package/project/.sdk/src/cmp/haskell/tsconfig.json +0 -15
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +0 -166
- package/project/.sdk/tm/haskell/LICENSE +0 -21
- package/project/.sdk/tm/haskell/Makefile +0 -22
- package/project/.sdk/tm/haskell/VERSION +0 -1
- package/project/.sdk/tm/haskell/src/SdkFeatures.hs +0 -1440
- package/project/.sdk/tm/haskell/src/SdkHelpers.hs +0 -337
- package/project/.sdk/tm/haskell/src/SdkJson.hs +0 -111
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +0 -1219
- package/project/.sdk/tm/haskell/src/SdkTypes.hs +0 -195
- package/project/.sdk/tm/haskell/src/VoxgigStruct.hs +0 -2299
- package/project/.sdk/tm/haskell/src/Vregex.hs +0 -237
- package/project/.sdk/tm/haskell/src/feature/README.md +0 -5
- package/project/.sdk/tm/haskell/src/feature/audit/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/base/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/cache/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/clienttrack/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/debug/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/idempotency/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/log/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/metrics/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/netsim/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/paging/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/proxy/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/ratelimit/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/rbac/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/retry/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/streaming/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/telemetry/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/test/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/src/feature/timeout/.gitkeep +0 -0
- package/project/.sdk/tm/haskell/test/Harness.hs +0 -281
- package/project/.sdk/tm/haskell/test/Runner.hs +0 -47
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +0 -449
- package/project/.sdk/tm/haskell/test/TCustomUtility.hs +0 -38
- package/project/.sdk/tm/haskell/test/TFeature.hs +0 -794
- package/project/.sdk/tm/haskell/test/TNetsim.hs +0 -50
- package/project/.sdk/tm/haskell/test/TPipeline.hs +0 -334
- package/project/.sdk/tm/haskell/test/TPrimaryUtility.hs +0 -386
- package/project/.sdk/tm/haskell/test/Testutil.hs +0 -59
|
@@ -35,7 +35,7 @@ describe('PrimaryUtility', async () => {
|
|
|
35
35
|
// (.sdk/test/primary/<name>.aontu carries a PENDING header). Everything
|
|
36
36
|
// else MUST contribute cases.
|
|
37
37
|
const PENDING = new Set([
|
|
38
|
-
'fetcher', 'makeFetchDef', '
|
|
38
|
+
'fetcher', 'makeFetchDef', 'makeResult',
|
|
39
39
|
'featureAdd', 'featureHook', 'featureInit',
|
|
40
40
|
])
|
|
41
41
|
|
|
@@ -279,22 +279,15 @@ describe('PrimaryUtility', async () => {
|
|
|
279
279
|
})
|
|
280
280
|
|
|
281
281
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
transform: { req: undefined, res: undefined },
|
|
292
|
-
}
|
|
293
|
-
ctx.op.points = [point]
|
|
294
|
-
|
|
295
|
-
const result = utility.makePoint(ctx)
|
|
296
|
-
ok(!(result instanceof Error))
|
|
297
|
-
equal(ctx.point, point)
|
|
282
|
+
// Was one hand-written case (the single-point path) covering one of this
|
|
283
|
+
// utility's seven branches, which is how the corpus fixture came to be
|
|
284
|
+
// marked deferred as "needs a real client". It does not: Context rebuilds
|
|
285
|
+
// `op` from opname + entity + config, and `options` can be supplied
|
|
286
|
+
// literally, so allow.op, the empty-points error, exist-selection,
|
|
287
|
+
// $action selection and the invalid-$action error are all expressible.
|
|
288
|
+
// Driven from the corpus now, so every port asserts the same branches.
|
|
289
|
+
test('makePoint-basic', async () => {
|
|
290
|
+
await runsection('makePoint', utility.makePoint)
|
|
298
291
|
})
|
|
299
292
|
|
|
300
293
|
|
|
@@ -521,6 +521,29 @@ pub fn make_options_util(ctx: *Context) Value {
|
|
|
521
521
|
// make_point (gotcha #2: rbac PrePoint short-circuit)
|
|
522
522
|
// ============================================================================
|
|
523
523
|
|
|
524
|
+
// How many path segments a point has.
|
|
525
|
+
fn point_parts_len(point: Value) i64 {
|
|
526
|
+
const parts = h.getp(point, "parts");
|
|
527
|
+
if (parts != .array) return 0;
|
|
528
|
+
return @intCast(parts.array.data.items.len);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Does this point's path end in a parameter? A record route ends in the
|
|
532
|
+
// record's identifier (/boards/{id}); a cross-reference that also returns the
|
|
533
|
+
// entity ends in the relationship's name (/posts/{id}/author). That, then
|
|
534
|
+
// fewest segments, is what tells the entity's own route from a
|
|
535
|
+
// cross-reference. The same rule runs at generation time, in
|
|
536
|
+
// helpers/opShape.ts — both sides must move together.
|
|
537
|
+
fn point_terminal_param(point: Value) bool {
|
|
538
|
+
const parts = h.getp(point, "parts");
|
|
539
|
+
if (parts != .array) return false;
|
|
540
|
+
const items = parts.array.data.items;
|
|
541
|
+
if (0 == items.len) return false;
|
|
542
|
+
const last = items[items.len - 1];
|
|
543
|
+
if (last != .string) return false;
|
|
544
|
+
return 0 < last.string.len and '{' == last.string[0];
|
|
545
|
+
}
|
|
546
|
+
|
|
524
547
|
pub fn make_point_util(ctx: *Context) E!Value {
|
|
525
548
|
if (ctx.out_get("point")) |ov| {
|
|
526
549
|
switch (ov) {
|
|
@@ -560,10 +583,11 @@ pub fn make_point_util(ctx: *Context) E!Value {
|
|
|
560
583
|
const selector: Value = if (std.mem.eql(u8, op.input, "data")) ctx.data else ctx.mtch;
|
|
561
584
|
|
|
562
585
|
var point: Value = h.vnull();
|
|
586
|
+
var matched = false;
|
|
563
587
|
var i: i64 = 0;
|
|
564
588
|
while (i < plen) : (i += 1) {
|
|
565
|
-
|
|
566
|
-
const select_def = h.to_map(h.getp(
|
|
589
|
+
const cand = h.get_elem(points, h.vnum(i), h.vnull());
|
|
590
|
+
const select_def = h.to_map(h.getp(cand, "select"));
|
|
567
591
|
var found = true;
|
|
568
592
|
|
|
569
593
|
if (!h.is_noval(selector) and !h.is_noval(select_def)) {
|
|
@@ -589,7 +613,46 @@ pub fn make_point_util(ctx: *Context) E!Value {
|
|
|
589
613
|
if (!h.veq(req_action, select_action)) found = false;
|
|
590
614
|
}
|
|
591
615
|
|
|
592
|
-
if (found)
|
|
616
|
+
if (found) {
|
|
617
|
+
point = cand;
|
|
618
|
+
matched = true;
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// select.exist can list more than the params needed to pick a point
|
|
624
|
+
// (for /boards/{id} it is Trello's 17 optional query-includes), so a
|
|
625
|
+
// plain {id} call matches NOTHING. Fall back to the entity's own
|
|
626
|
+
// route rather than whichever point came last.
|
|
627
|
+
if (!matched) {
|
|
628
|
+
// A request naming an action reaches here only because that
|
|
629
|
+
// action's own point failed its exist test, so it is unbuildable
|
|
630
|
+
// whatever we pick. Refuse it BEFORE choosing a fallback: the
|
|
631
|
+
// guard below compares the chosen point's $action and would wave
|
|
632
|
+
// the request through whenever the fallback lands on the action
|
|
633
|
+
// point itself.
|
|
634
|
+
const unmatched_action = h.getp(reqselector, "$action");
|
|
635
|
+
if (!h.is_noval(unmatched_action)) {
|
|
636
|
+
return ctx.fail("point_action_invalid", fmt("Operation \"{s}\" action \"{s}\" is not valid.", .{ op.name, h.stringify(unmatched_action) }));
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// A terminal parameter marks a record route (/boards/{id}); a
|
|
640
|
+
// cross-reference ends in the relationship's name
|
|
641
|
+
// (/posts/{id}/author). Failing that, the shallower path wins.
|
|
642
|
+
// The same rule runs at generation time, in helpers/opShape.ts —
|
|
643
|
+
// both sides must move together.
|
|
644
|
+
point = h.get_elem(points, h.vnum(0), h.vnull());
|
|
645
|
+
var j: i64 = 0;
|
|
646
|
+
while (j < plen) : (j += 1) {
|
|
647
|
+
const cand = h.get_elem(points, h.vnum(j), h.vnull());
|
|
648
|
+
const cand_term = point_terminal_param(cand);
|
|
649
|
+
const best_term = point_terminal_param(point);
|
|
650
|
+
if (cand_term != best_term) {
|
|
651
|
+
if (cand_term) point = cand;
|
|
652
|
+
} else if (point_parts_len(cand) < point_parts_len(point)) {
|
|
653
|
+
point = cand;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
593
656
|
}
|
|
594
657
|
|
|
595
658
|
const req_action = h.getp(reqselector, "$action");
|
package/src/cmp/AgentGuideTop.ts
CHANGED
|
@@ -80,6 +80,24 @@ package (other languages). Each target's guide documents its features.
|
|
|
80
80
|
Content(`**Entities** (${entities.length}): `)
|
|
81
81
|
Content(entities.map((e: any) => `\`${e.Name || e.name}\``).join(', ') + `.
|
|
82
82
|
|
|
83
|
+
`)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Station paragraph (station design §9.4), only when the model
|
|
87
|
+
// carries the feature: an agent working on this repo should know
|
|
88
|
+
// the runtime story without leaving AGENTS.md.
|
|
89
|
+
if (features.some((f: any) => 'station' === f.name)) {
|
|
90
|
+
Content(`**Station**: this SDK is a
|
|
91
|
+
[voxgig/station](https://github.com/voxgig/station) plugin (the
|
|
92
|
+
\`station\` feature, off by default). Bound to an open \`Station\`,
|
|
93
|
+
the credential is resolved by sekreto under the plugin's secret name
|
|
94
|
+
and injected at the transport seam — \`options()\` and
|
|
95
|
+
\`prepare()\` output hold only a placeholder, so both are safe to
|
|
96
|
+
inspect and log. \`station.tap(...)\`/\`station.events()\` show live
|
|
97
|
+
traffic; \`station.plugins()\` lists descriptors. See the "Use with
|
|
98
|
+
Station" README section and \`src/feature/station/\` (or the
|
|
99
|
+
target's feature container) for the generated adapter.
|
|
100
|
+
|
|
83
101
|
`)
|
|
84
102
|
}
|
|
85
103
|
|
|
@@ -88,7 +88,7 @@ const ExternalTarget = cmp(function ExternalTarget(props: any) {
|
|
|
88
88
|
names(target, target.name)
|
|
89
89
|
|
|
90
90
|
if (phaseActive('entity')) {
|
|
91
|
-
each(entity
|
|
91
|
+
each(entity).filter((entity: any) => entity.active).map((entity: any) => {
|
|
92
92
|
names(entity, entity.name)
|
|
93
93
|
Entity({ target, entity })
|
|
94
94
|
})
|
package/src/cmp/Readme.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { ReadmeIntro } from './ReadmeIntro'
|
|
|
6
6
|
import { ReadmeInstall } from './ReadmeInstall'
|
|
7
7
|
import { ReadmeQuick } from './ReadmeQuick'
|
|
8
8
|
import { ReadmeErrors } from './ReadmeErrors'
|
|
9
|
+
import { ReadmeStation } from './ReadmeStation'
|
|
9
10
|
import { ReadmeModel } from './ReadmeModel'
|
|
10
11
|
import { ReadmeOptions } from './ReadmeOptions'
|
|
11
12
|
import { ReadmeEntity } from './ReadmeEntity'
|
|
@@ -24,6 +25,7 @@ const Readme = cmp(function Readme(props: any) {
|
|
|
24
25
|
ReadmeIntro({ target })
|
|
25
26
|
ReadmeInstall({ target })
|
|
26
27
|
ReadmeQuick({ target })
|
|
28
|
+
ReadmeStation({ target })
|
|
27
29
|
ReadmeErrors({ target })
|
|
28
30
|
ReadmeHowto({ target })
|
|
29
31
|
ReadmeModel({ target })
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
|
|
2
|
+
import { cmp, Content } from 'jostraca'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
KIT,
|
|
6
|
+
getModelPath
|
|
7
|
+
} from '../types'
|
|
8
|
+
|
|
9
|
+
import { envName } from '../helpers/packageMeta'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// The "Use with Station" README section (station design §9.4): rendered
|
|
13
|
+
// ONLY when the project's model carries the station feature (installed
|
|
14
|
+
// via `package add @voxgig/sdkgen-station`) — a project without it sees
|
|
15
|
+
// nothing. Documents the binding forms and the secret name; store
|
|
16
|
+
// configuration is sekreto's documentation, deliberately not restated
|
|
17
|
+
// here (one canonical source).
|
|
18
|
+
|
|
19
|
+
// Targets where station.connect(SDK) is the idiomatic binding; everything
|
|
20
|
+
// else uses inverted binding through the SDK's own constructor.
|
|
21
|
+
const CONNECT_TARGETS = ['ts', 'js', 'py', 'rb', 'php', 'lua', 'perl']
|
|
22
|
+
|
|
23
|
+
const ReadmeStation = cmp(function ReadmeStation(props: any) {
|
|
24
|
+
const { target } = props
|
|
25
|
+
const { model } = props.ctx$
|
|
26
|
+
|
|
27
|
+
const features = getModelPath(model, `main.${KIT}.feature`,
|
|
28
|
+
{ only_active: false, required: false }) || {}
|
|
29
|
+
const station = Object.values(features)
|
|
30
|
+
.find((f: any) => 'station' === f?.name)
|
|
31
|
+
if (null == station) { return }
|
|
32
|
+
|
|
33
|
+
const env = envName(model)
|
|
34
|
+
const secretname = env.toLowerCase() + '.apikey'
|
|
35
|
+
const connect = CONNECT_TARGETS.includes(target.name)
|
|
36
|
+
|
|
37
|
+
Content(`
|
|
38
|
+
## Use with Station
|
|
39
|
+
|
|
40
|
+
This SDK ships as a [voxgig/station](https://github.com/voxgig/station)
|
|
41
|
+
plugin: bind it to a local \`Station\` and outbound configuration,
|
|
42
|
+
credentials, and observability move to one place. The feature is
|
|
43
|
+
present but **off by default** — nothing changes until you bind.
|
|
44
|
+
|
|
45
|
+
${connect
|
|
46
|
+
? `Bind by passing the SDK class to the station:
|
|
47
|
+
|
|
48
|
+
1. \`station = Station.open()\` — profile, env, and proxy all defaulted.
|
|
49
|
+
2. \`client = station.connect(${model.const.Name}SDK)\` — replaces direct
|
|
50
|
+
construction.`
|
|
51
|
+
: `Bind through the constructor this SDK already has (inverted
|
|
52
|
+
binding): open a station, then construct with station-built options —
|
|
53
|
+
\`station.options()\` merges the handle, the activation entry, and the
|
|
54
|
+
correct feature order into the plain options the constructor accepts.`}
|
|
55
|
+
|
|
56
|
+
The credential comes from [sekreto](https://github.com/voxgig/sekreto)
|
|
57
|
+
under the name \`${secretname}\` — by default the \`${env}_APIKEY\`
|
|
58
|
+
environment variable this README already documents, unchanged. Point a
|
|
59
|
+
profile in \`station.json\` at a vault later and application code does
|
|
60
|
+
not change; sekreto's own documentation covers the stores. The key
|
|
61
|
+
stays out of \`options()\` and \`prepare()\` output; \`station.tap(...)\`
|
|
62
|
+
shows live traffic.
|
|
63
|
+
`)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
ReadmeStation
|
|
69
|
+
}
|
package/src/helpers/naming.ts
CHANGED
|
@@ -112,17 +112,67 @@ function isRbCoreConstant(Name: string): boolean {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
// RUBY SDK-OWNED CONSTANTS — the OTHER half of "already taken".
|
|
116
|
+
//
|
|
117
|
+
// `RB_CORE_CONSTANTS` guards names the LANGUAGE owns. This guards names the
|
|
118
|
+
// GENERATED SCAFFOLDING claims for itself, which is a distinct hazard and the
|
|
119
|
+
// one that stayed open: an entity named `Runner` produced `class Runner` in
|
|
120
|
+
// `<Sdk>_types.rb`, and the generated harness then does
|
|
121
|
+
//
|
|
122
|
+
// Runner = ProjectNameTestRunner # tm/rb/test/runner.rb
|
|
123
|
+
//
|
|
124
|
+
// so inside the test process the entity TYPE silently *is* the test runner.
|
|
125
|
+
// Ruby warns (`already initialized constant Runner`) and carries on, which is
|
|
126
|
+
// why it survived: nothing in the suite exercises the type, so `rb` passed.
|
|
127
|
+
// Reported on gitlab-sdk, which has a `Runner` entity (issue #64).
|
|
128
|
+
//
|
|
129
|
+
// EVERYTHING SHARES ONE NAMESPACE HERE, unlike swift. Swift's Tests module is
|
|
130
|
+
// separate from the SDK module, so `SWIFT_SDK_TYPES` can exclude test-only
|
|
131
|
+
// declarations. Ruby has no such split — the types file and the test files are
|
|
132
|
+
// all required into one process when the suite runs, which is exactly the
|
|
133
|
+
// reported symptom — so test-declared names belong in this set.
|
|
134
|
+
//
|
|
135
|
+
// PREFIXED DECLARATIONS ARE DELIBERATELY ABSENT. `ProjectNameUtility` and
|
|
136
|
+
// friends substitute to `<Sdk>Utility`, and a generated entity type is the
|
|
137
|
+
// bare entity name, so those cannot collide. Only the UNPREFIXED declarations
|
|
138
|
+
// are reachable.
|
|
139
|
+
//
|
|
140
|
+
// `rb-sdk-constants.test.ts` re-derives this list from the templates AND the
|
|
141
|
+
// components and fails on drift — the same discipline as
|
|
142
|
+
// `swift-sdk-types.test.ts`, and for the same reason: the first cut of that
|
|
143
|
+
// list was collected by hand and missed three names, one of them declared by
|
|
144
|
+
// a component rather than a template.
|
|
145
|
+
const RB_SDK_CONSTANTS = new Set<string>([
|
|
146
|
+
// module-level aliases the test harness defines for convenience
|
|
147
|
+
'Helpers', 'Runner', 'Vs',
|
|
148
|
+
// vendored struct library and its test scaffolding
|
|
149
|
+
'StructRunner', 'StructTestClient', 'StructUtilityTest', 'VoxgigStruct',
|
|
150
|
+
'STRUCT_TEST_JSON_FILE',
|
|
151
|
+
// the generated/templated test classes
|
|
152
|
+
'ExistsTest', 'FeatureTest', 'NetsimTest', 'PipelineTest',
|
|
153
|
+
'PrimaryUtilityTest', 'ReadmeExamplesTest', 'TestHookFeature',
|
|
154
|
+
'TestInitFeature',
|
|
155
|
+
])
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
// Does `Name` collide with a constant the generated Ruby SDK already declares?
|
|
159
|
+
function isRbSdkConstant(Name: string): boolean {
|
|
160
|
+
return RB_SDK_CONSTANTS.has(Name)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
115
164
|
// A top-level-safe Ruby constant name for a generated type: the name
|
|
116
|
-
// unchanged, unless
|
|
117
|
-
// appended (`File` -> `FileType
|
|
118
|
-
//
|
|
119
|
-
//
|
|
165
|
+
// unchanged, unless it is ALREADY TAKEN — by Ruby core, or by the SDK's own
|
|
166
|
+
// scaffolding — in which case `Type` is appended (`File` -> `FileType`,
|
|
167
|
+
// `Runner` -> `RunnerType`). Applied ONLY to the bare entity data type: the
|
|
168
|
+
// per-op type names always carry a suffix (`FileCreateData`,
|
|
169
|
+
// `FileLoadMatch`), which nothing in either set matches.
|
|
120
170
|
//
|
|
121
171
|
// The entity ACCESSOR keeps its original name (`client.File(...)`): methods
|
|
122
172
|
// and constants live in separate namespaces in Ruby, so the accessor never
|
|
123
173
|
// collided and the public surface is unchanged.
|
|
124
174
|
function rbSafeTypeName(Name: string): string {
|
|
125
|
-
return isRbCoreConstant(Name) ? Name + 'Type' : Name
|
|
175
|
+
return isRbCoreConstant(Name) || isRbSdkConstant(Name) ? Name + 'Type' : Name
|
|
126
176
|
}
|
|
127
177
|
|
|
128
178
|
|
|
@@ -166,6 +216,8 @@ const SWIFT_SDK_TYPES = new Set<string>([
|
|
|
166
216
|
'OrderedDictionary', 'Sentinel', 'Value', 'VList', 'VMap', 'WalkApply',
|
|
167
217
|
// function/callback aliases
|
|
168
218
|
'FetcherFunc', 'Formatter', 'NativeCall0', 'NativeRef', 'SystemFetch',
|
|
219
|
+
// transport plumbing
|
|
220
|
+
'ManualRedirectDelegate',
|
|
169
221
|
// features
|
|
170
222
|
'AuditFeature', 'BaseFeature', 'CacheFeature', 'ClienttrackFeature',
|
|
171
223
|
'DebugFeature', 'IdempotencyFeature', 'LogFeature', 'MetricsBucket',
|
|
@@ -195,6 +247,116 @@ function swiftSafeTypeName(Name: string): string {
|
|
|
195
247
|
}
|
|
196
248
|
|
|
197
249
|
|
|
250
|
+
// PHP RESERVED WORDS THAT CANNOT BE A CLASS NAME — a fifth hazard, and the
|
|
251
|
+
// only one of the five where the check must be CASE-INSENSITIVE.
|
|
252
|
+
//
|
|
253
|
+
// PHP's grammar refuses a reserved word where a class name is expected, so a
|
|
254
|
+
// spec with a `Namespace` entity emitted a file that does not parse at all:
|
|
255
|
+
//
|
|
256
|
+
// /** Namespace entity data model. */
|
|
257
|
+
// class Namespace
|
|
258
|
+
// {
|
|
259
|
+
// }
|
|
260
|
+
// -> PHP Parse error: syntax error, unexpected token "namespace",
|
|
261
|
+
// expecting identifier
|
|
262
|
+
//
|
|
263
|
+
// Found on gitlab-sdk (issue #64), and LATENT in the worst way: `types/` is on
|
|
264
|
+
// the composer classmap but nothing references the file, so PHP never loads it
|
|
265
|
+
// and the php lane passed for as long as the target has existed. It would
|
|
266
|
+
// fatal the moment anything required it, and the documented type is unusable
|
|
267
|
+
// either way.
|
|
268
|
+
//
|
|
269
|
+
// CASE-INSENSITIVITY IS THE WHOLE TRICK. Class and function names in PHP are
|
|
270
|
+
// case-insensitive, so `Namespace`, `NAMESPACE` and `namespace` are one
|
|
271
|
+
// identifier — and the generated name is PascalCase while the reserved word is
|
|
272
|
+
// lowercase. A case-sensitive `Set.has('Namespace')` against a lowercase list
|
|
273
|
+
// therefore matches NOTHING and reopens the bug silently. Entries are stored
|
|
274
|
+
// folded, and the lookup folds too.
|
|
275
|
+
//
|
|
276
|
+
// Taken from the PHP manual's reserved-words tables wholesale rather than
|
|
277
|
+
// grown one collision at a time: the list is fixed by the language, and
|
|
278
|
+
// picking from it by hand is how the second `Namespace` gets shipped.
|
|
279
|
+
const PHP_RESERVED_TYPES = new Set<string>([
|
|
280
|
+
// keywords
|
|
281
|
+
'abstract', 'and', 'array', 'as', 'break', 'callable', 'case', 'catch',
|
|
282
|
+
'class', 'clone', 'const', 'continue', 'declare', 'default', 'do', 'echo',
|
|
283
|
+
'else', 'elseif', 'empty', 'enddeclare', 'endfor', 'endforeach', 'endif',
|
|
284
|
+
'endswitch', 'endwhile', 'enum', 'eval', 'exit', 'extends', 'final',
|
|
285
|
+
'finally', 'fn', 'for', 'foreach', 'function', 'global', 'goto', 'if',
|
|
286
|
+
'implements', 'include', 'include_once', 'instanceof', 'insteadof',
|
|
287
|
+
'interface', 'isset', 'list', 'match', 'namespace', 'new', 'or', 'print',
|
|
288
|
+
'private', 'protected', 'public', 'readonly', 'require', 'require_once',
|
|
289
|
+
'return', 'static', 'switch', 'throw', 'trait', 'try', 'unset', 'use',
|
|
290
|
+
'var', 'while', 'xor', 'yield',
|
|
291
|
+
// compile-time constants
|
|
292
|
+
'__class__', '__dir__', '__file__', '__function__', '__line__',
|
|
293
|
+
'__method__', '__namespace__', '__trait__',
|
|
294
|
+
// reserved as type names / cannot be declared as a class
|
|
295
|
+
'bool', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null',
|
|
296
|
+
'numeric', 'object', 'parent', 'resource', 'self', 'string', 'true', 'void',
|
|
297
|
+
])
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
// Does `Name` collide with a word PHP reserves, ignoring case as PHP does?
|
|
301
|
+
function isPhpReservedType(Name: string): boolean {
|
|
302
|
+
return PHP_RESERVED_TYPES.has(String(Name).toLowerCase())
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
// A declarable PHP class name for a generated type: unchanged, unless PHP
|
|
307
|
+
// reserves the word, in which case `Type` is appended (`Namespace` ->
|
|
308
|
+
// `NamespaceType`). Mirrors rbSafeTypeName and swiftSafeTypeName deliberately
|
|
309
|
+
// — same suffix, same "only rename on an actual collision" rule, so every SDK
|
|
310
|
+
// that does not collide is byte-identical to before.
|
|
311
|
+
//
|
|
312
|
+
// Applied ONLY to the bare entity data class. Per-op type names already carry
|
|
313
|
+
// their own suffix (`NamespaceLoadData`, `NamespaceCreateData`), which no
|
|
314
|
+
// reserved word matches, and the entity ACCESSOR is a method rather than a
|
|
315
|
+
// class — PHP resolves those separately — so the public surface is unchanged.
|
|
316
|
+
function phpSafeTypeName(Name: string): string {
|
|
317
|
+
return isPhpReservedType(Name) ? Name + 'Type' : Name
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
// TypeScript GLOBAL TYPE NAMES — a fourth hazard, same shape as the Ruby and
|
|
322
|
+
// Swift ones above but for TS/JS builtins, which are in scope everywhere
|
|
323
|
+
// with no import. An entity named `Record` emitted `export interface Record
|
|
324
|
+
// {...}` and canonToType's own generic-object mapping (`Record<string,
|
|
325
|
+
// any>`) then resolved to that flat interface INSIDE THE SAME FILE instead
|
|
326
|
+
// of the builtin: `error TS2315: Type 'Record' is not generic` — on a
|
|
327
|
+
// field of the `Record` entity itself.
|
|
328
|
+
//
|
|
329
|
+
// Utility types (`Record`, `Partial`, ...) and common global constructors
|
|
330
|
+
// (`Array`, `Map`, `Promise`, ...) that a REST entity name plausibly lands
|
|
331
|
+
// on. Mirrors RB_CORE_CONSTANTS / SWIFT_SDK_TYPES: only real builtins are
|
|
332
|
+
// listed, so a non-colliding SDK stays byte-identical to before.
|
|
333
|
+
const TS_RESERVED_TYPES = new Set<string>([
|
|
334
|
+
// utility types
|
|
335
|
+
'Record', 'Partial', 'Required', 'Readonly', 'Pick', 'Omit', 'Exclude',
|
|
336
|
+
'Extract', 'NonNullable', 'Parameters', 'ReturnType', 'InstanceType',
|
|
337
|
+
'Awaited', 'ConstructorParameters',
|
|
338
|
+
// global constructors / builtins
|
|
339
|
+
'Array', 'Object', 'Map', 'Set', 'WeakMap', 'WeakSet', 'Date', 'Error',
|
|
340
|
+
'Function', 'Promise', 'RegExp', 'String', 'Number', 'Boolean', 'Symbol',
|
|
341
|
+
'ArrayBuffer', 'Proxy', 'Reflect', 'JSON', 'Math',
|
|
342
|
+
])
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
// Does `Name` collide with a TS/JS global type?
|
|
346
|
+
function isTsReservedType(Name: string): boolean {
|
|
347
|
+
return TS_RESERVED_TYPES.has(Name)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
// A collision-free TS type name for a generated type: unchanged, unless it
|
|
352
|
+
// shadows a TS/JS global, in which case `Type` is appended (`Record` ->
|
|
353
|
+
// `RecordType`). Mirrors rbSafeTypeName / swiftSafeTypeName. Applied ONLY
|
|
354
|
+
// to the bare entity data type, for the same reason as both of those.
|
|
355
|
+
function tsSafeTypeName(Name: string): string {
|
|
356
|
+
return isTsReservedType(Name) ? Name + 'Type' : Name
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
|
|
198
360
|
// Is `name` a reserved word (an illegal identifier) in the target language?
|
|
199
361
|
function isReservedName(name: string, lang: string): boolean {
|
|
200
362
|
const set = RESERVED[lang]
|
|
@@ -328,9 +490,14 @@ export {
|
|
|
328
490
|
phpEntityAccessor,
|
|
329
491
|
entityCacheField,
|
|
330
492
|
isRbCoreConstant,
|
|
493
|
+
isRbSdkConstant,
|
|
331
494
|
rbSafeTypeName,
|
|
332
495
|
isSwiftSdkType,
|
|
333
496
|
swiftSafeTypeName,
|
|
497
|
+
isPhpReservedType,
|
|
498
|
+
phpSafeTypeName,
|
|
499
|
+
isTsReservedType,
|
|
500
|
+
tsSafeTypeName,
|
|
334
501
|
jsProp,
|
|
335
502
|
jsOptProp,
|
|
336
503
|
jsKey,
|
package/src/helpers/opShape.ts
CHANGED
|
@@ -189,6 +189,72 @@ function entityPath(entity: any): string {
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
// The entity's OWN endpoint among an op's points.
|
|
193
|
+
//
|
|
194
|
+
// The other notion of canonical above — "not a `$action` route" — separates a
|
|
195
|
+
// folded-in custom action from the real op. This one separates the entity's
|
|
196
|
+
// own route from a CROSS-REFERENCE: another resource's route that happens to
|
|
197
|
+
// return this entity (`/notifications/{id}/board` for a board). Both are
|
|
198
|
+
// plain GETs with no `$action`, so the path itself is all there is to go on.
|
|
199
|
+
//
|
|
200
|
+
// Two signals, in order:
|
|
201
|
+
//
|
|
202
|
+
// 1. A record route ends in the record's identifier (`/boards/{id}`,
|
|
203
|
+
// `/accounts/{account_id}/users/{id}`); a cross-reference ends in the
|
|
204
|
+
// relationship's name (`/posts/{id}/author`). A terminal parameter is
|
|
205
|
+
// the stronger signal, and unlike depth alone it survives an entity
|
|
206
|
+
// nested more deeply than the route that points at it — depth by itself
|
|
207
|
+
// picked `/posts/{id}/author` over `/accounts/{account_id}/users/{id}`
|
|
208
|
+
// and then generated that op's required params from the wrong route.
|
|
209
|
+
// 2. Failing that, fewest path segments — the shallower route is the one
|
|
210
|
+
// the entity is named for, which is what settles a `list` op where
|
|
211
|
+
// neither route ends in an id (`/boards` over `/members/{id}/boards`).
|
|
212
|
+
//
|
|
213
|
+
// Ties keep the earlier point, so the sorted-key order of the model decides
|
|
214
|
+
// and the output stays byte-stable.
|
|
215
|
+
//
|
|
216
|
+
// The same rule runs at RUNTIME, in each language's makePoint template, when
|
|
217
|
+
// no point's `select.exist` matches. It cannot be shared with them — a
|
|
218
|
+
// template ships standalone, outside this package — so it is written twice on
|
|
219
|
+
// purpose, and both sides must move together.
|
|
220
|
+
function terminalParam(point: any): boolean {
|
|
221
|
+
const parts: any[] = (point && point.parts) || []
|
|
222
|
+
const last = 0 < parts.length ? parts[parts.length - 1] : ''
|
|
223
|
+
return 'string' === typeof last && last.startsWith('{')
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
function ownPoint(points: any[]): any {
|
|
228
|
+
let best = points[0]
|
|
229
|
+
|
|
230
|
+
for (const pt of points) {
|
|
231
|
+
if (null == pt || null == pt.parts || null == best || null == best.parts) {
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const ptterm = terminalParam(pt)
|
|
236
|
+
const bestterm = terminalParam(best)
|
|
237
|
+
|
|
238
|
+
if (ptterm !== bestterm ? ptterm : pt.parts.length < best.parts.length) {
|
|
239
|
+
best = pt
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return best
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
// Do all these points describe the same route? Then they are alternative
|
|
248
|
+
// selectors on one endpoint (the same path, chosen by different query
|
|
249
|
+
// params), not cross-references to different resources.
|
|
250
|
+
function samePath(points: any[]): boolean {
|
|
251
|
+
const first = ((points[0] && points[0].parts) || []).join('/')
|
|
252
|
+
|
|
253
|
+
return points.every((pt: any) =>
|
|
254
|
+
first === ((pt && pt.parts) || []).join('/'))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
|
|
192
258
|
function opParams(op: any): any[] {
|
|
193
259
|
let points: any[] = op && op.points ? each(op.points) : []
|
|
194
260
|
|
|
@@ -227,6 +293,25 @@ function opParams(op: any): any[] {
|
|
|
227
293
|
p.reqd = true === requiredOnAll[p.name]
|
|
228
294
|
})
|
|
229
295
|
|
|
296
|
+
// Unrelated cross-references share no param, so the intersection came out
|
|
297
|
+
// empty and NOTHING is required — which generates a `load({})` that drops
|
|
298
|
+
// the id. Fall back to the entity's own point and take its params whole.
|
|
299
|
+
//
|
|
300
|
+
// Two guards, and both are load-bearing:
|
|
301
|
+
//
|
|
302
|
+
// - only when the intersection produced nothing, so a genuine set of
|
|
303
|
+
// alternative routes with a shared param still merges (a `page_id` on
|
|
304
|
+
// every route stays required, per-route siblings stay optional);
|
|
305
|
+
// - only when the points describe DIFFERENT routes. Points on one path
|
|
306
|
+
// that differ solely by optional query params (`/users?email=` and
|
|
307
|
+
// `/users?name=`) legitimately require nothing, and are not
|
|
308
|
+
// cross-references — collapsing them to one point would drop the
|
|
309
|
+
// other's fields from the generated request type, so a caller could
|
|
310
|
+
// not express them at all.
|
|
311
|
+
if (1 < points.length && !out.some((p: any) => p.reqd) && !samePath(points)) {
|
|
312
|
+
return opParams({ points: [ownPoint(points)] })
|
|
313
|
+
}
|
|
314
|
+
|
|
230
315
|
return out
|
|
231
316
|
}
|
|
232
317
|
|