@voxgig/sdkgen 3.4.7 → 3.5.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 +78 -10
- package/dist/action/action.d.ts +5 -2
- package/dist/action/action.js +113 -6
- package/dist/action/action.js.map +1 -1
- package/dist/action/check.d.ts +15 -0
- package/dist/action/check.js +347 -0
- package/dist/action/check.js.map +1 -0
- package/dist/action/dispatch.d.ts +8 -0
- package/dist/action/dispatch.js +74 -0
- package/dist/action/dispatch.js.map +1 -0
- package/dist/action/docs.d.ts +4 -0
- package/dist/action/docs.js +236 -0
- package/dist/action/docs.js.map +1 -0
- package/dist/action/doctor.d.ts +5 -2
- package/dist/action/doctor.js +526 -74
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/feature.js +96 -17
- package/dist/action/feature.js.map +1 -1
- package/dist/action/kind.d.ts +30 -0
- package/dist/action/kind.js +258 -0
- package/dist/action/kind.js.map +1 -0
- package/dist/action/package.d.ts +25 -0
- package/dist/action/package.js +852 -0
- package/dist/action/package.js.map +1 -0
- package/dist/action/resolve.d.ts +18 -0
- package/dist/action/resolve.js +280 -0
- package/dist/action/resolve.js.map +1 -0
- package/dist/action/target.d.ts +7 -1
- package/dist/action/target.js +240 -97
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Docs.d.ts +4 -0
- package/dist/cmp/Docs.js +98 -0
- package/dist/cmp/Docs.js.map +1 -0
- package/dist/cmp/ExternalDocs.d.ts +2 -0
- package/dist/cmp/ExternalDocs.js +43 -0
- package/dist/cmp/ExternalDocs.js.map +1 -0
- package/dist/helpers/definition.d.ts +5 -0
- package/dist/helpers/definition.js +72 -0
- package/dist/helpers/definition.js.map +1 -0
- package/dist/helpers/featureSource.d.ts +8 -2
- package/dist/helpers/featureSource.js +61 -12
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/junk.d.ts +11 -0
- package/dist/helpers/junk.js +149 -0
- package/dist/helpers/junk.js.map +1 -0
- package/dist/helpers/manifest.d.ts +50 -0
- package/dist/helpers/manifest.js +328 -0
- package/dist/helpers/manifest.js.map +1 -0
- package/dist/helpers/modelcheck.d.ts +22 -0
- package/dist/helpers/modelcheck.js +203 -0
- package/dist/helpers/modelcheck.js.map +1 -0
- package/dist/helpers/semver.d.ts +4 -0
- package/dist/helpers/semver.js +174 -0
- package/dist/helpers/semver.js.map +1 -0
- package/dist/helpers/shipped.d.ts +4 -0
- package/dist/helpers/shipped.js +36 -0
- package/dist/helpers/shipped.js.map +1 -0
- package/dist/helpers/stdrep.d.ts +9 -1
- package/dist/helpers/stdrep.js +22 -0
- package/dist/helpers/stdrep.js.map +1 -1
- package/dist/sdkgen.d.ts +15 -3
- package/dist/sdkgen.js +233 -67
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/utility.js +17 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +69 -0
- package/package.json +3 -2
- package/project/.sdk/model/feature/audit.aontu +1 -0
- package/project/.sdk/model/feature/cache.aontu +1 -0
- package/project/.sdk/model/feature/clienttrack.aontu +1 -0
- package/project/.sdk/model/feature/debug.aontu +1 -0
- package/project/.sdk/model/feature/idempotency.aontu +1 -0
- package/project/.sdk/model/feature/log.aontu +1 -0
- package/project/.sdk/model/feature/metrics.aontu +1 -0
- package/project/.sdk/model/feature/netsim.aontu +1 -0
- package/project/.sdk/model/feature/paging.aontu +1 -0
- package/project/.sdk/model/feature/proxy.aontu +1 -0
- package/project/.sdk/model/feature/ratelimit.aontu +1 -0
- package/project/.sdk/model/feature/rbac.aontu +1 -0
- package/project/.sdk/model/feature/retry.aontu +1 -0
- package/project/.sdk/model/feature/streaming.aontu +1 -0
- package/project/.sdk/model/feature/telemetry.aontu +1 -0
- package/project/.sdk/model/feature/test.aontu +1 -0
- package/project/.sdk/model/feature/timeout.aontu +1 -0
- package/project/.sdk/model/target/c.aontu +1 -0
- package/project/.sdk/model/target/clojure.aontu +1 -0
- package/project/.sdk/model/target/cpp.aontu +1 -0
- package/project/.sdk/model/target/dart.aontu +1 -0
- package/project/.sdk/model/target/elixir.aontu +1 -0
- package/project/.sdk/model/target/go-cli.aontu +1 -0
- package/project/.sdk/model/target/go-mcp.aontu +1 -0
- package/project/.sdk/model/target/go.aontu +1 -0
- package/project/.sdk/model/target/haskell.aontu +1 -0
- package/project/.sdk/model/target/java.aontu +1 -0
- package/project/.sdk/model/target/js.aontu +1 -0
- package/project/.sdk/model/target/kotlin.aontu +1 -0
- package/project/.sdk/model/target/lean.aontu +1 -0
- package/project/.sdk/model/target/lua.aontu +1 -0
- package/project/.sdk/model/target/ocaml.aontu +1 -0
- package/project/.sdk/model/target/perl.aontu +1 -0
- package/project/.sdk/model/target/php.aontu +1 -0
- package/project/.sdk/model/target/py-data.aontu +1 -0
- package/project/.sdk/model/target/py.aontu +1 -0
- package/project/.sdk/model/target/rb.aontu +1 -0
- package/project/.sdk/model/target/rust.aontu +1 -0
- package/project/.sdk/model/target/scala.aontu +1 -0
- package/project/.sdk/model/target/seneca-provider.aontu +1 -0
- package/project/.sdk/model/target/zig.aontu +1 -0
- package/project/.sdk/src/cmp/c/Config_c.ts +44 -0
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +3 -1
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +37 -42
- package/project/.sdk/src/cmp/clojure/utility_clojure.ts +42 -0
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +18 -0
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +125 -52
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +3 -1
- package/project/.sdk/src/cmp/csharp/utility_csharp.ts +94 -6
- package/project/.sdk/src/cmp/dart/Config_dart.ts +43 -5
- package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +74 -0
- package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +6 -9
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +16 -5
- package/project/.sdk/src/cmp/dart/utility_dart.ts +18 -0
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +128 -13
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +3 -1
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +6 -0
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +35 -52
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +1 -0
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +1 -1
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +15 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +21 -0
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +15 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +42 -39
- package/project/.sdk/src/cmp/perl/Config_perl.ts +15 -0
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +44 -0
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +4 -2
- package/project/.sdk/src/cmp/scala/Config_scala.ts +15 -0
- package/project/.sdk/src/cmp/swift/Config_swift.ts +17 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +3 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +44 -0
- package/project/.sdk/src/cmp/zig/TestEntity_zig.ts +9 -2
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +3 -1
- package/project/.sdk/tm/c/core/sdk.h +1 -0
- package/project/.sdk/tm/c/utility/make_options.c +5 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +51 -3
- package/project/.sdk/tm/cpp/core/types.hpp +4 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +5 -1
- package/project/.sdk/tm/csharp/test/PipelineTest.cs +15 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +67 -1
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +43 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +58 -2
- package/project/.sdk/tm/haskell/{test/TestJson.hs → src/SdkJson.hs} +8 -4
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +55 -2
- package/project/.sdk/tm/haskell/test/Runner.hs +1 -1
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +1 -1
- package/project/.sdk/tm/java/core/SdkClient.java +3 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +5 -1
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +3 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +5 -1
- package/project/.sdk/tm/ocaml/Makefile +3 -2
- package/project/.sdk/tm/ocaml/sdk_json.ml +106 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +56 -0
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +4 -86
- package/project/.sdk/tm/perl/utility/make_options.pm +5 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +79 -1
- package/project/.sdk/tm/scala/core/SdkClient.scala +3 -1
- package/project/.sdk/tm/scala/utility/Make.scala +5 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +5 -1
- package/project/.sdk/tm/zig/core/utility.zig +85 -1
- package/project/sdkgen-package.json +57 -0
- package/src/action/action.ts +136 -6
- package/src/action/check.ts +495 -0
- package/src/action/dispatch.ts +105 -0
- package/src/action/docs.ts +310 -0
- package/src/action/doctor.ts +692 -81
- package/src/action/feature.ts +109 -17
- package/src/action/kind.ts +375 -0
- package/src/action/package.ts +1173 -0
- package/src/action/resolve.ts +362 -0
- package/src/action/target.ts +290 -108
- package/src/cmp/Docs.ts +125 -0
- package/src/cmp/ExternalDocs.ts +54 -0
- package/src/helpers/definition.ts +82 -0
- package/src/helpers/featureSource.ts +83 -13
- package/src/helpers/junk.ts +168 -0
- package/src/helpers/manifest.ts +474 -0
- package/src/helpers/modelcheck.ts +262 -0
- package/src/helpers/semver.ts +211 -0
- package/src/helpers/shipped.ts +41 -0
- package/src/helpers/stdrep.ts +70 -0
- package/src/sdkgen.ts +294 -66
- package/src/types.ts +33 -3
- package/src/utility.ts +17 -0
|
@@ -3,8 +3,11 @@ import {
|
|
|
3
3
|
Content,
|
|
4
4
|
File,
|
|
5
5
|
cmp,
|
|
6
|
+
configDefinition,
|
|
7
|
+
configReprSetting,
|
|
6
8
|
each,
|
|
7
9
|
isAuthActive,
|
|
10
|
+
isConfigData,
|
|
8
11
|
resolveAuthPrefix,
|
|
9
12
|
} from '@voxgig/sdkgen'
|
|
10
13
|
|
|
@@ -17,7 +20,6 @@ import {
|
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
import {
|
|
20
|
-
clean,
|
|
21
23
|
formatOcamlValue,
|
|
22
24
|
ocamlString,
|
|
23
25
|
} from './utility_ocaml'
|
|
@@ -32,52 +34,52 @@ const Config = cmp(async function Config(props: any) {
|
|
|
32
34
|
|
|
33
35
|
const model: Model = ctx$.model
|
|
34
36
|
|
|
35
|
-
const entity = getModelPath(model, `main.${KIT}.entity`)
|
|
36
37
|
const feature = getModelPath(model, `main.${KIT}.feature`)
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
// The canonical config OBJECT and its JSON, from the shared helper. Both
|
|
40
|
+
// representations render from the same `def`, so they cannot describe
|
|
41
|
+
// different configs - and this target picks up `options.server` (the OpenAPI
|
|
42
|
+
// server-variable defaults), which the hand-rolled build here omitted.
|
|
43
|
+
const { def: config, json: configJson } = configDefinition(model)
|
|
44
|
+
const asData = isConfigData(configJson, configReprSetting(model))
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
const authPrefix = resolveAuthPrefix(model)
|
|
46
|
+
File({ name: 'sdk_config.' + target.ext }, () => {
|
|
42
47
|
|
|
43
|
-
|
|
44
|
-
|
|
48
|
+
// ABOVE THE THRESHOLD: emit the model as DATA.
|
|
49
|
+
//
|
|
50
|
+
// The literal is one nested expression the compiler type-checks as a
|
|
51
|
+
// single item; a string is one token, and `Sdk_json.json_read` builds the
|
|
52
|
+
// same value tree at runtime. That reader used to live only in the corpus
|
|
53
|
+
// test harness - this rung is why it now lives in the runtime, and the
|
|
54
|
+
// harness uses it from there rather than keeping a second copy.
|
|
55
|
+
//
|
|
56
|
+
// No number-type question: `json_read` yields `Num (float)` and
|
|
57
|
+
// `formatOcamlValue` emits `(Num (5.))`, so both branches agree.
|
|
58
|
+
if (asData) {
|
|
59
|
+
Content(`(* Generated API configuration (mirrors go core/config.go).
|
|
60
|
+
*
|
|
61
|
+
* THE API MODEL, EMBEDDED AS DATA (sdkgen rung L1). Emitted only above a size
|
|
62
|
+
* threshold, or when main.kit.config.repr pins it: for a small model the
|
|
63
|
+
* literal is smaller and far easier to read when debugging.
|
|
64
|
+
*
|
|
65
|
+
* make_config () — the embedded API model as a voxgig struct value.
|
|
66
|
+
* make_feature name — the N-feature-safe factory the client uses. *)
|
|
45
67
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
open Voxgig_struct
|
|
69
|
+
open Sdk_types
|
|
70
|
+
open Sdk_helpers
|
|
71
|
+
open Sdk_features
|
|
50
72
|
|
|
51
|
-
|
|
52
|
-
each(entity, (ent: any) => {
|
|
53
|
-
entityOptions[ent.name] = {}
|
|
54
|
-
})
|
|
73
|
+
let config_data = "${ocamlString(configJson)}"
|
|
55
74
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
headers,
|
|
59
|
-
entity: entityOptions,
|
|
60
|
-
}
|
|
61
|
-
if (authActive) {
|
|
62
|
-
options.auth = { prefix: authPrefix }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const entityConfig = Object.values(entity || {}).reduce((a: any, n: any) => (
|
|
66
|
-
a[n.name] = clean({
|
|
67
|
-
fields: n.fields,
|
|
68
|
-
name: n.name,
|
|
69
|
-
op: n.op,
|
|
70
|
-
relations: n.relations,
|
|
71
|
-
}, true), a), {})
|
|
72
|
-
|
|
73
|
-
const config = {
|
|
74
|
-
main: { name: model.const.Name },
|
|
75
|
-
feature: featureConfig,
|
|
76
|
-
options,
|
|
77
|
-
entity: entityConfig,
|
|
78
|
-
}
|
|
75
|
+
let make_config () : value =
|
|
76
|
+
Sdk_json.json_read config_data
|
|
79
77
|
|
|
80
|
-
|
|
78
|
+
let make_feature (name : string) : feature =
|
|
79
|
+
match name with
|
|
80
|
+
`)
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
81
83
|
|
|
82
84
|
Content(`(* Generated API configuration (mirrors go core/config.go).
|
|
83
85
|
*
|
|
@@ -95,6 +97,7 @@ let make_config () : value =
|
|
|
95
97
|
let make_feature (name : string) : feature =
|
|
96
98
|
match name with
|
|
97
99
|
`)
|
|
100
|
+
}
|
|
98
101
|
|
|
99
102
|
each(feature, (f: any) => {
|
|
100
103
|
if (f.name !== 'base') {
|
|
@@ -110,6 +110,21 @@ sub make_config {
|
|
|
110
110
|
return Voxgig::Struct::parse_json($CONFIG_JSON);
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
# SHARED CONFIG (sdkgen rung L2).
|
|
114
|
+
#
|
|
115
|
+
# The SDK reads the config on every request and never writes to it, so one
|
|
116
|
+
# instance is shared by every client rather than rebuilt per client - the
|
|
117
|
+
# difference between parsing the embedded JSON once and once per client.
|
|
118
|
+
#
|
|
119
|
+
# The returned structure is SHARED: treat it as read-only. Callers that need to
|
|
120
|
+
# mutate should use make_config, which always parses a fresh copy.
|
|
121
|
+
my $SHARED_CONFIG;
|
|
122
|
+
|
|
123
|
+
sub shared_config {
|
|
124
|
+
$SHARED_CONFIG = make_config() unless defined $SHARED_CONFIG;
|
|
125
|
+
return $SHARED_CONFIG;
|
|
126
|
+
}
|
|
127
|
+
|
|
113
128
|
sub make_feature {
|
|
114
129
|
my ($name) = @_;
|
|
115
130
|
require(Cwd::abs_path("$__dir/features.pm"));
|
|
@@ -41,7 +41,9 @@ sub new {
|
|
|
41
41
|
my $utility = ProjectNameUtility->new;
|
|
42
42
|
$self->{_utility} = $utility;
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
# The process-wide config (sdkgen rung L2): read-only on the request path,
|
|
45
|
+
# so every client shares one rather than rebuilding it.
|
|
46
|
+
my $config = ProjectNameConfig::shared_config();
|
|
45
47
|
|
|
46
48
|
$self->{_rootctx} = $utility->{make_context}->({
|
|
47
49
|
'client' => $self,
|
|
@@ -89,6 +89,28 @@ pub fn make_config() -> Value {
|
|
|
89
89
|
json_parse(CONFIG_DATA).expect("${model.const.Name}: embedded config is not valid JSON")
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
// SHARED CONFIG (sdkgen rung L2).
|
|
93
|
+
//
|
|
94
|
+
// The SDK reads the config on every request and never writes to it, so one
|
|
95
|
+
// instance is shared by every client rather than rebuilt per client. Above the
|
|
96
|
+
// size threshold make_config re-parses the whole embedded JSON, so this is the
|
|
97
|
+
// difference between parsing the model once and once per client.
|
|
98
|
+
//
|
|
99
|
+
// THREAD-LOCAL, not a global: Value is Rc/RefCell-backed and so is neither
|
|
100
|
+
// Send nor Sync. One config per thread is the widest scope that is sound here,
|
|
101
|
+
// and the clone is an Rc bump, not a deep copy.
|
|
102
|
+
thread_local! {
|
|
103
|
+
static SHARED_CONFIG: Value = make_config();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// The per-thread config, built once on first use.
|
|
107
|
+
///
|
|
108
|
+
/// The returned Value SHARES its nodes: treat it as read-only. Callers that
|
|
109
|
+
/// need to mutate should use make_config, which always returns a fresh copy.
|
|
110
|
+
pub fn shared_config() -> Value {
|
|
111
|
+
SHARED_CONFIG.with(|c| c.clone())
|
|
112
|
+
}
|
|
113
|
+
|
|
92
114
|
pub fn make_feature(name: &str) -> FeatureRef {
|
|
93
115
|
match name {
|
|
94
116
|
`)
|
|
@@ -107,6 +129,28 @@ pub fn make_config() -> Value {
|
|
|
107
129
|
${formatRustValue(config, 1)}
|
|
108
130
|
}
|
|
109
131
|
|
|
132
|
+
// SHARED CONFIG (sdkgen rung L2).
|
|
133
|
+
//
|
|
134
|
+
// The SDK reads the config on every request and never writes to it, so one
|
|
135
|
+
// instance is shared by every client rather than rebuilt per client. Above the
|
|
136
|
+
// size threshold make_config re-parses the whole embedded JSON, so this is the
|
|
137
|
+
// difference between parsing the model once and once per client.
|
|
138
|
+
//
|
|
139
|
+
// THREAD-LOCAL, not a global: Value is Rc/RefCell-backed and so is neither
|
|
140
|
+
// Send nor Sync. One config per thread is the widest scope that is sound here,
|
|
141
|
+
// and the clone is an Rc bump, not a deep copy.
|
|
142
|
+
thread_local! {
|
|
143
|
+
static SHARED_CONFIG: Value = make_config();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// The per-thread config, built once on first use.
|
|
147
|
+
///
|
|
148
|
+
/// The returned Value SHARES its nodes: treat it as read-only. Callers that
|
|
149
|
+
/// need to mutate should use make_config, which always returns a fresh copy.
|
|
150
|
+
pub fn shared_config() -> Value {
|
|
151
|
+
SHARED_CONFIG.with(|c| c.clone())
|
|
152
|
+
}
|
|
153
|
+
|
|
110
154
|
pub fn make_feature(name: &str) -> FeatureRef {
|
|
111
155
|
match name {
|
|
112
156
|
`)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
use std::cell::RefCell;
|
|
4
4
|
use std::rc::Rc;
|
|
5
5
|
|
|
6
|
-
use crate::core::config::{
|
|
6
|
+
use crate::core::config::{make_feature, shared_config};
|
|
7
7
|
use crate::core::context::{Context, CtxSpec};
|
|
8
8
|
use crate::core::error::ProjectNameError;
|
|
9
9
|
use crate::core::helpers::{call_json, get_bool, get_str, getp, getpath, jo, setp, to_int, to_map};
|
|
@@ -31,7 +31,9 @@ impl ProjectNameSDK {
|
|
|
31
31
|
rootctx: RefCell::new(None),
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
// The per-thread config (sdkgen rung L2): read-only on the request
|
|
35
|
+
// path, so every client shares one rather than rebuilding it.
|
|
36
|
+
let config = shared_config();
|
|
35
37
|
|
|
36
38
|
let rootctx = sdk.utility.make_context(
|
|
37
39
|
CtxSpec {
|
|
@@ -93,6 +93,21 @@ object Config {
|
|
|
93
93
|
def makeConfig(): JMap[String, Object] =
|
|
94
94
|
Json.parse(configJson()).asInstanceOf[JMap[String, Object]]
|
|
95
95
|
|
|
96
|
+
// SHARED CONFIG (sdkgen rung L2).
|
|
97
|
+
//
|
|
98
|
+
// The SDK reads the config on every request and never writes to it, so one
|
|
99
|
+
// instance is shared by every client rather than rebuilt per client - the
|
|
100
|
+
// difference between parsing the embedded JSON once and once per client.
|
|
101
|
+
//
|
|
102
|
+
// A lazy val is initialised once, on first use, under a lock, so
|
|
103
|
+
// concurrent first calls build it exactly once.
|
|
104
|
+
//
|
|
105
|
+
// The returned map is SHARED: treat it as read-only. Callers that need to
|
|
106
|
+
// mutate should use makeConfig, which always parses a fresh copy.
|
|
107
|
+
private lazy val sharedConfigVal: JMap[String, Object] = makeConfig()
|
|
108
|
+
|
|
109
|
+
def sharedConfig(): JMap[String, Object] = sharedConfigVal
|
|
110
|
+
|
|
96
111
|
def makeFeature(name: String): Feature = name match {
|
|
97
112
|
`)
|
|
98
113
|
|
|
@@ -101,6 +101,23 @@ ${configJson}
|
|
|
101
101
|
return (try? JSON.parse(json))?.asMap ?? VMap()
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// SHARED CONFIG (sdkgen rung L2).
|
|
105
|
+
//
|
|
106
|
+
// The SDK reads the config on every request and never writes to it, so one
|
|
107
|
+
// instance is shared by every client rather than rebuilt per client - the
|
|
108
|
+
// difference between parsing the embedded JSON once and once per client.
|
|
109
|
+
//
|
|
110
|
+
// A static let in an enum is lazy and initialised exactly once, thread-safe
|
|
111
|
+
// via swift_once.
|
|
112
|
+
//
|
|
113
|
+
// The result is SHARED: treat it as read-only. Callers that need to mutate
|
|
114
|
+
// should use makeConfig, which always parses a fresh copy.
|
|
115
|
+
private static let sharedConfigVal: VMap = makeConfig()
|
|
116
|
+
|
|
117
|
+
public static func sharedConfig() -> VMap {
|
|
118
|
+
return sharedConfigVal
|
|
119
|
+
}
|
|
120
|
+
|
|
104
121
|
public static func makeFeature(_ name: String) -> BaseFeature {
|
|
105
122
|
switch name {
|
|
106
123
|
`)
|
|
@@ -12,7 +12,9 @@ public final class ProjectNameSDK {
|
|
|
12
12
|
public init(_ optionsIn: VMap? = nil) {
|
|
13
13
|
utility = Utility()
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
// The process-wide config (sdkgen rung L2): read-only on the request path,
|
|
16
|
+
// so every client shares one rather than rebuilding it.
|
|
17
|
+
let config = SdkConfig.sharedConfig()
|
|
16
18
|
|
|
17
19
|
var ctxmap: [String: Any?] = [
|
|
18
20
|
"client": self,
|
|
@@ -79,6 +79,28 @@ pub fn make_config() Value {
|
|
|
79
79
|
@panic("${model.const.Name}: embedded config is not valid JSON");
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// SHARED CONFIG (sdkgen rung L2).
|
|
83
|
+
//
|
|
84
|
+
// The SDK reads the config on every request and never writes to it, so one
|
|
85
|
+
// instance is shared by every client rather than rebuilt per client. Above the
|
|
86
|
+
// size threshold make_config re-parses the whole embedded JSON, so this is the
|
|
87
|
+
// difference between parsing the model once and once per client.
|
|
88
|
+
//
|
|
89
|
+
// Value nodes are arena-allocated and reference-stable, so the shared value is
|
|
90
|
+
// genuinely one structure, not a copy.
|
|
91
|
+
var shared_config_val: ?Value = null;
|
|
92
|
+
|
|
93
|
+
/// The process-wide config, built once on first use.
|
|
94
|
+
///
|
|
95
|
+
/// The returned Value SHARES its nodes: treat it as read-only. Callers that
|
|
96
|
+
/// need to mutate should use make_config, which always returns a fresh copy.
|
|
97
|
+
pub fn shared_config() Value {
|
|
98
|
+
if (shared_config_val) |c| return c;
|
|
99
|
+
const c = make_config();
|
|
100
|
+
shared_config_val = c;
|
|
101
|
+
return c;
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
pub fn make_feature(name: []const u8) Feature {
|
|
83
105
|
`)
|
|
84
106
|
}
|
|
@@ -96,6 +118,28 @@ pub fn make_config() Value {
|
|
|
96
118
|
return ${formatZigValue(config, 1)};
|
|
97
119
|
}
|
|
98
120
|
|
|
121
|
+
// SHARED CONFIG (sdkgen rung L2).
|
|
122
|
+
//
|
|
123
|
+
// The SDK reads the config on every request and never writes to it, so one
|
|
124
|
+
// instance is shared by every client rather than rebuilt per client. Above the
|
|
125
|
+
// size threshold make_config re-parses the whole embedded JSON, so this is the
|
|
126
|
+
// difference between parsing the model once and once per client.
|
|
127
|
+
//
|
|
128
|
+
// Value nodes are arena-allocated and reference-stable, so the shared value is
|
|
129
|
+
// genuinely one structure, not a copy.
|
|
130
|
+
var shared_config_val: ?Value = null;
|
|
131
|
+
|
|
132
|
+
/// The process-wide config, built once on first use.
|
|
133
|
+
///
|
|
134
|
+
/// The returned Value SHARES its nodes: treat it as read-only. Callers that
|
|
135
|
+
/// need to mutate should use make_config, which always returns a fresh copy.
|
|
136
|
+
pub fn shared_config() Value {
|
|
137
|
+
if (shared_config_val) |c| return c;
|
|
138
|
+
const c = make_config();
|
|
139
|
+
shared_config_val = c;
|
|
140
|
+
return c;
|
|
141
|
+
}
|
|
142
|
+
|
|
99
143
|
pub fn make_feature(name: []const u8) Feature {
|
|
100
144
|
`)
|
|
101
145
|
}
|
|
@@ -26,8 +26,15 @@ test "${method}_load_smoke" {
|
|
|
26
26
|
const e = testsdk.${method}(vnull());
|
|
27
27
|
const res = e.load(h.jo(&.{.{ "id", h.vstr("t01") }}), vnull());
|
|
28
28
|
switch (res) {
|
|
29
|
-
.ok => |
|
|
30
|
-
|
|
29
|
+
.ok => |ent| {
|
|
30
|
+
// EVERY operation resolves to the ENTITY, not the record: the
|
|
31
|
+
// payload of EntResult.ok is the entity pointer, and the record is
|
|
32
|
+
// reached through data(). Destructuring it as a Value was a
|
|
33
|
+
// compile error ("expected type 'struct.JsonValue', found
|
|
34
|
+
// '*entity.<name>.<Name>Entity'"), so no generated zig SDK with a
|
|
35
|
+
// loadable entity could build its own test suite.
|
|
36
|
+
const rec = ent.asEntity().data(null);
|
|
37
|
+
try std.testing.expect(std.mem.eql(u8, h.get_str(rec, "id") orelse "", "t01"));
|
|
31
38
|
},
|
|
32
39
|
.err => |er| {
|
|
33
40
|
std.debug.print("${method} load failed: {s}\\n", .{er.msg});
|
|
@@ -49,7 +49,9 @@ pub const ProjectNameSDK = struct {
|
|
|
49
49
|
.rootctx = null,
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
// The process-wide config (sdkgen rung L2): read-only on the request
|
|
53
|
+
// path, so every client shares one rather than rebuilding it.
|
|
54
|
+
const cfg = config.shared_config();
|
|
53
55
|
|
|
54
56
|
const rootctx = sdk.sdkUtility.make_context(CtxSpec{
|
|
55
57
|
.client = sdk,
|
|
@@ -445,6 +445,7 @@ voxgig_value* sdk_graphql(ProjectNameSDK* sdk, const char* query,
|
|
|
445
445
|
|
|
446
446
|
// Generated config (core/config.c).
|
|
447
447
|
voxgig_value* make_config(void);
|
|
448
|
+
voxgig_value* shared_config(void);
|
|
448
449
|
Feature* make_feature(const char* name);
|
|
449
450
|
|
|
450
451
|
// ===========================================================================
|
|
@@ -82,7 +82,11 @@ voxgig_value* make_options_util(Context* ctx) {
|
|
|
82
82
|
// Preserve system.fetch before merge/validate (validation strips it).
|
|
83
83
|
voxgig_value* sys_fetch = getpath2(opts, "system", "fetch");
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
/* CLONE the config side, do not v_share it: `config` is a process-wide
|
|
86
|
+
* singleton (shared_config) and merge uses its nested maps as merge TARGETS,
|
|
87
|
+
* so sharing lets one client's options (headers, server, ...) be written into
|
|
88
|
+
* the shared config and inherited by every client built afterwards. */
|
|
89
|
+
voxgig_value* mergelist = clist(3, v_map(), voxgig_clone(cfgopts), v_share(opts));
|
|
86
90
|
voxgig_value* merged = voxgig_merge(mergelist, VOXGIG_MAXDEPTH);
|
|
87
91
|
voxgig_value* validated = voxgig_validate(merged, optspec, NULL);
|
|
88
92
|
if (voxgig_is_map(validated)) {
|
|
@@ -80,7 +80,16 @@
|
|
|
80
80
|
(aset pos 0 (inc (aget pos 0))))
|
|
81
81
|
(let [tok (subs s start (aget pos 0))]
|
|
82
82
|
(if (or (.contains tok ".") (.contains tok "e") (.contains tok "E"))
|
|
83
|
-
(Double/parseDouble tok)
|
|
83
|
+
(Double/parseDouble tok)
|
|
84
|
+
;; A JSON integer may exceed the signed 64-bit range - an
|
|
85
|
+
;; OpenAPI `default`/`example` can carry any integer. The
|
|
86
|
+
;; Clojure reader gives the literal branch a BigInt for
|
|
87
|
+
;; such a token, so parseLong alone would make the two
|
|
88
|
+
;; representations disagree by throwing on input the
|
|
89
|
+
;; literal accepts.
|
|
90
|
+
(try (Long/parseLong tok)
|
|
91
|
+
(catch NumberFormatException _
|
|
92
|
+
(bigint (java.math.BigInteger. tok))))))))]
|
|
84
93
|
(parse-val))))
|
|
85
94
|
|
|
86
95
|
;; ---------------------------------------------------------------------------
|
|
@@ -928,11 +937,50 @@
|
|
|
928
937
|
"utility" (vs/jm)
|
|
929
938
|
"system" (vs/jm)
|
|
930
939
|
"test" (vs/jm "active" false "entity" (vs/jm "`$OPEN`" true))
|
|
931
|
-
"clean" (vs/jm "keys" "key,token,id")
|
|
940
|
+
"clean" (vs/jm "keys" "key,token,id")
|
|
941
|
+
;; Server-variable values for a templated base URL (OpenAPI
|
|
942
|
+
;; server variables): {name} placeholders in "base" are
|
|
943
|
+
;; substituted from this map at construction. Spec defaults
|
|
944
|
+
;; arrive via the generated config; user values override
|
|
945
|
+
;; them. Mirrors go's make_options optspec.
|
|
946
|
+
"server" (vs/jm "`$CHILD`" ""))
|
|
932
947
|
sys-fetch (vs/getpath opts0 "system.fetch")
|
|
933
948
|
merged (vs/merge (vs/jt (vs/jm) cfgopts opts0))
|
|
934
949
|
validated (vs/validate merged optspec)
|
|
935
|
-
opts (if (vs/ismap validated) validated (vs/jm))
|
|
950
|
+
opts (if (vs/ismap validated) validated (vs/jm))
|
|
951
|
+
;; Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
952
|
+
;; Every placeholder must resolve to a non-empty value: from
|
|
953
|
+
;; options["server"] (user), else the config default. A placeholder
|
|
954
|
+
;; that resolves to "" is a construction ERROR in live mode - the URL
|
|
955
|
+
;; cannot work - but in test mode substitutes the deterministic value
|
|
956
|
+
;; "test-<name>" so offline tests need no configuration.
|
|
957
|
+
;;
|
|
958
|
+
;; Accepting `server` in the option spec without this is a trap: the
|
|
959
|
+
;; option validates and is then ignored, so a templated URL still
|
|
960
|
+
;; reaches the wire with a literal {name} in it.
|
|
961
|
+
base (vs/getprop opts "base")
|
|
962
|
+
_ (when (and (string? base) (.contains ^String base "{"))
|
|
963
|
+
(let [testmode (or (true? (vs/getpath opts "test.active"))
|
|
964
|
+
(true? (vs/getpath opts "feature.test.active")))
|
|
965
|
+
server (let [s (vs/getprop opts "server")]
|
|
966
|
+
(if (vs/ismap s) s (vs/jm)))
|
|
967
|
+
sdkname (let [n (vs/getpath config "main.name")]
|
|
968
|
+
(if (and (string? n) (seq n)) n "SDK"))
|
|
969
|
+
resolved (clojure.string/replace
|
|
970
|
+
base #"\{([A-Za-z0-9_]+)\}"
|
|
971
|
+
(fn [[_ name]]
|
|
972
|
+
(let [v (vs/getprop server name)
|
|
973
|
+
v (if (string? v) v "")]
|
|
974
|
+
(cond
|
|
975
|
+
(seq v) v
|
|
976
|
+
testmode (str "test-" name)
|
|
977
|
+
:else
|
|
978
|
+
(throw (IllegalArgumentException.
|
|
979
|
+
(str sdkname ": the server variable '" name
|
|
980
|
+
"' is required: the API base URL is '" base
|
|
981
|
+
"' - pass {\"server\" {\"" name "\" \"...\"}}"
|
|
982
|
+
" in the SDK options")))))))]
|
|
983
|
+
(vs/setprop opts "base" resolved)))]
|
|
936
984
|
(when sys-fetch
|
|
937
985
|
(when (not (vs/ismap (vs/getprop opts "system"))) (.put ^java.util.Map opts "system" (vs/jm)))
|
|
938
986
|
(.put ^java.util.Map (vs/getprop opts "system") "fetch" sys-fetch))
|
|
@@ -53,6 +53,7 @@ using OpMapPtr = std::shared_ptr<OpMap>;
|
|
|
53
53
|
|
|
54
54
|
// Generated (core/config.hpp) — the embedded API config + feature factory.
|
|
55
55
|
inline Value makeConfig();
|
|
56
|
+
inline const Value& sharedConfig();
|
|
56
57
|
inline FeaturePtr makeFeature(const std::string& name);
|
|
57
58
|
|
|
58
59
|
// Defined in utility/pipeline.hpp; wires the Utility function fields.
|
|
@@ -781,7 +782,9 @@ inline SdkErrorPtr Context::makeError(const std::string& code, const std::string
|
|
|
781
782
|
inline SdkClient::SdkClient(const Value& options_) {
|
|
782
783
|
utility = std::make_shared<Utility>();
|
|
783
784
|
|
|
784
|
-
|
|
785
|
+
// The process-wide config (sdkgen rung L2): read-only on the request path,
|
|
786
|
+
// so every client shares one rather than rebuilding it.
|
|
787
|
+
Value config = sharedConfig();
|
|
785
788
|
|
|
786
789
|
CtxSpec cs;
|
|
787
790
|
cs.client = this;
|
|
@@ -1095,7 +1095,11 @@ inline Value makeOptions(CtxPtr ctx) {
|
|
|
1095
1095
|
// dropped by validate).
|
|
1096
1096
|
Value sysFetch = Struct::getpath(opts, {"system", "fetch"});
|
|
1097
1097
|
|
|
1098
|
-
|
|
1098
|
+
// CLONE the config side: `config` is a process-wide singleton (sharedConfig)
|
|
1099
|
+
// and merge uses its nested maps as merge TARGETS, so without this one
|
|
1100
|
+
// client's options (headers, server, ...) are written into the shared config
|
|
1101
|
+
// and inherited by every client constructed afterwards.
|
|
1102
|
+
Value mergeList = vlist({vmap(), Struct::clone(cfgopts), opts});
|
|
1099
1103
|
Value merged = Struct::merge(mergeList);
|
|
1100
1104
|
|
|
1101
1105
|
Value vopts = vmap();
|
|
@@ -150,6 +150,21 @@ public class PipelineTest
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
public object? Match(object? match = null) => null;
|
|
153
|
+
|
|
154
|
+
// The deletion half of the entity contract, mirroring
|
|
155
|
+
// ProjectNameEntityBase: `remove` marks the instance, which keeps the
|
|
156
|
+
// data it held but is no longer a live record.
|
|
157
|
+
private bool _deleted = false;
|
|
158
|
+
|
|
159
|
+
public void MarkDeleted()
|
|
160
|
+
{
|
|
161
|
+
this._deleted = true;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
public bool Deleted()
|
|
165
|
+
{
|
|
166
|
+
return this._deleted;
|
|
167
|
+
}
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
// --- MakeResponse -------------------------------------------------------
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
// ProjectName SDK utility: makeOptions - merge, validate and derive the
|
|
2
2
|
// client options.
|
|
3
3
|
|
|
4
|
+
using System.Text.RegularExpressions;
|
|
5
|
+
|
|
4
6
|
using Voxgig.Struct;
|
|
5
7
|
|
|
6
8
|
namespace ProjectNameSdk.Util;
|
|
7
9
|
|
|
8
10
|
public static partial class SdkUtility
|
|
9
11
|
{
|
|
12
|
+
// {name} placeholders in a templated server URL (OpenAPI server
|
|
13
|
+
// variables). Compiled once: MakeOptions runs per client construction.
|
|
14
|
+
private static readonly Regex ServerVarRe =
|
|
15
|
+
new(@"\{([A-Za-z0-9_]+)\}", RegexOptions.Compiled);
|
|
16
|
+
|
|
17
|
+
|
|
10
18
|
internal static Dictionary<string, object?> MakeOptionsUtil(Context ctx)
|
|
11
19
|
{
|
|
12
20
|
var options = ctx.Options ?? new Dictionary<string, object?>();
|
|
@@ -73,6 +81,14 @@ public static partial class SdkUtility
|
|
|
73
81
|
{
|
|
74
82
|
["`$CHILD`"] = "`$STRING`",
|
|
75
83
|
},
|
|
84
|
+
// OpenAPI server-variable defaults, carried by the generated
|
|
85
|
+
// config whenever the spec's server URL is templated. Accepted
|
|
86
|
+
// here so validation does not reject the SDK's own config; the
|
|
87
|
+
// {name} substitution into base is a separate concern.
|
|
88
|
+
["server"] = new Dictionary<string, object?>
|
|
89
|
+
{
|
|
90
|
+
["`$CHILD`"] = "",
|
|
91
|
+
},
|
|
76
92
|
["allow"] = new Dictionary<string, object?>
|
|
77
93
|
{
|
|
78
94
|
["method"] = "GET,PUT,POST,PATCH,DELETE,OPTIONS",
|
|
@@ -118,12 +134,62 @@ public static partial class SdkUtility
|
|
|
118
134
|
var merged = StructUtils.Merge(new List<object?>
|
|
119
135
|
{
|
|
120
136
|
new Dictionary<string, object?>(),
|
|
121
|
-
|
|
137
|
+
// CLONE the config side. `config` is a process-wide singleton
|
|
138
|
+
// (SdkConfig.SharedConfig), and Merge uses its nested maps as
|
|
139
|
+
// merge TARGETS - without this, one client's options (headers,
|
|
140
|
+
// server, ...) are written into the shared config and inherited by
|
|
141
|
+
// every client constructed afterwards.
|
|
142
|
+
StructUtils.Clone(cfgopts),
|
|
122
143
|
opts,
|
|
123
144
|
});
|
|
124
145
|
var validated = StructUtils.Validate(merged, optspec);
|
|
125
146
|
opts = validated as Dictionary<string, object?> ?? new Dictionary<string, object?>();
|
|
126
147
|
|
|
148
|
+
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
149
|
+
// Every placeholder must resolve to a non-empty value: from
|
|
150
|
+
// options.server (user), else the Config default. A placeholder that
|
|
151
|
+
// resolves to "" is a construction ERROR in live mode - the URL cannot
|
|
152
|
+
// work - but in test mode substitutes the deterministic value
|
|
153
|
+
// "test-<name>" so offline tests need no configuration. The SDK
|
|
154
|
+
// constructor has no error return, so a missing required variable
|
|
155
|
+
// THROWS: this is construction-time misconfiguration.
|
|
156
|
+
if (opts.TryGetValue("base", out var baseRaw) && baseRaw is string baseUrl &&
|
|
157
|
+
baseUrl.Contains('{'))
|
|
158
|
+
{
|
|
159
|
+
var testmode =
|
|
160
|
+
StructUtils.GetPath(opts, StructUtils.Jt("test", "active")) is bool ta && ta ||
|
|
161
|
+
StructUtils.GetPath(opts, StructUtils.Jt("feature", "test", "active"))
|
|
162
|
+
is bool fa && fa;
|
|
163
|
+
|
|
164
|
+
var server = opts.TryGetValue("server", out var sv) &&
|
|
165
|
+
sv is Dictionary<string, object?> svm
|
|
166
|
+
? svm : new Dictionary<string, object?>();
|
|
167
|
+
|
|
168
|
+
var sdkname = StructUtils.GetPath(config, StructUtils.Jt("main", "name"))
|
|
169
|
+
is string mn && mn != "" ? mn : "SDK";
|
|
170
|
+
|
|
171
|
+
opts["base"] = ServerVarRe.Replace(baseUrl, m =>
|
|
172
|
+
{
|
|
173
|
+
var name = m.Groups[1].Value;
|
|
174
|
+
var val = server.TryGetValue(name, out var v) && v is string s ? s : "";
|
|
175
|
+
if (val == "")
|
|
176
|
+
{
|
|
177
|
+
if (testmode)
|
|
178
|
+
{
|
|
179
|
+
return "test-" + name;
|
|
180
|
+
}
|
|
181
|
+
throw new ProjectNameError("server_var_required",
|
|
182
|
+
sdkname + ": the server variable '" + name + "' is required: " +
|
|
183
|
+
"the API base URL is '" + baseUrl + "' - pass " +
|
|
184
|
+
"new Dictionary<string, object?> { [\"server\"] = new " +
|
|
185
|
+
"Dictionary<string, object?> { [\"" + name + "\"] = \"...\" } } " +
|
|
186
|
+
"in the SDK options",
|
|
187
|
+
ctx);
|
|
188
|
+
}
|
|
189
|
+
return val;
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
127
193
|
// Restore system.fetch.
|
|
128
194
|
if (sysFetch != null)
|
|
129
195
|
{
|