@voxgig/sdkgen 3.4.6 → 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/cmp/ReadmeTop.js +5 -1
- package/dist/cmp/ReadmeTop.js.map +1 -1
- 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 +56 -3
- 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 +31 -18
- 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/.sdk/tm/zig/test/gotcha_test.zig +8 -8
- 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/cmp/ReadmeTop.ts +6 -1
- 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});
|
|
@@ -22,7 +22,20 @@ const Spec = spec_mod.Spec;
|
|
|
22
22
|
pub const ProjectNameSDK = struct {
|
|
23
23
|
mode: []const u8 = "live",
|
|
24
24
|
options: Value = .{ .null = {} },
|
|
25
|
-
utility
|
|
25
|
+
// NOT named `utility`, and NOT a name `zigVarName` can produce.
|
|
26
|
+
//
|
|
27
|
+
// Entity accessors are generated as methods on this same struct
|
|
28
|
+
// (MainEntity_zig), so an API with an entity called `utility` collided
|
|
29
|
+
// twice: Zig rejects a local binding that shadows the declaration, and
|
|
30
|
+
// `sdk.utility` resolves to the FIELD, which made the generated accessor
|
|
31
|
+
// unreachable.
|
|
32
|
+
//
|
|
33
|
+
// `zigVarName` lowercases every name it is given, so a field spelling with
|
|
34
|
+
// an uppercase letter is one no entity name can ever reach - which is why
|
|
35
|
+
// this is `sdkUtility` and not, say, `util_rt`, a name an entity called
|
|
36
|
+
// `util-rt` would map straight onto. The public reader is `get_utility()`,
|
|
37
|
+
// so this field is internal and free to be spelled this way.
|
|
38
|
+
sdkUtility: *Utility,
|
|
26
39
|
features: std.ArrayList(Feature),
|
|
27
40
|
rootctx: ?*Context = null,
|
|
28
41
|
|
|
@@ -31,22 +44,24 @@ pub const ProjectNameSDK = struct {
|
|
|
31
44
|
sdk.* = .{
|
|
32
45
|
.mode = "live",
|
|
33
46
|
.options = h.vnull(),
|
|
34
|
-
.
|
|
47
|
+
.sdkUtility = Utility.new(),
|
|
35
48
|
.features = std.ArrayList(Feature).init(h.A()),
|
|
36
49
|
.rootctx = null,
|
|
37
50
|
};
|
|
38
51
|
|
|
39
|
-
|
|
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();
|
|
40
55
|
|
|
41
|
-
const rootctx = sdk.
|
|
56
|
+
const rootctx = sdk.sdkUtility.make_context(CtxSpec{
|
|
42
57
|
.client = sdk,
|
|
43
|
-
.utility = sdk.
|
|
58
|
+
.utility = sdk.sdkUtility,
|
|
44
59
|
.config = cfg,
|
|
45
60
|
.options = options,
|
|
46
61
|
.shared = h.omap(),
|
|
47
62
|
}, null);
|
|
48
63
|
|
|
49
|
-
const opts = sdk.
|
|
64
|
+
const opts = sdk.sdkUtility.make_options(rootctx);
|
|
50
65
|
sdk.options = opts;
|
|
51
66
|
|
|
52
67
|
if (h.veq(h.getpath(&.{ "feature", "test", "active" }, opts), h.vbool(true))) {
|
|
@@ -71,7 +86,7 @@ pub const ProjectNameSDK = struct {
|
|
|
71
86
|
const fopts = h.getp(feature_opts, fname);
|
|
72
87
|
if (fopts == .object) {
|
|
73
88
|
if (h.get_bool(fopts, "active") orelse false) {
|
|
74
|
-
sdk.
|
|
89
|
+
sdk.sdkUtility.feature_add(rootctx, config.make_feature(fname));
|
|
75
90
|
}
|
|
76
91
|
}
|
|
77
92
|
}
|
|
@@ -80,9 +95,9 @@ pub const ProjectNameSDK = struct {
|
|
|
80
95
|
// Initialize features.
|
|
81
96
|
var snap = std.ArrayList(Feature).init(h.A());
|
|
82
97
|
for (sdk.features.items) |f| snap.append(f) catch {};
|
|
83
|
-
for (snap.items) |f| sdk.
|
|
98
|
+
for (snap.items) |f| sdk.sdkUtility.feature_init(rootctx, f);
|
|
84
99
|
|
|
85
|
-
sdk.
|
|
100
|
+
sdk.sdkUtility.feature_hook(rootctx, "PostConstruct");
|
|
86
101
|
|
|
87
102
|
return sdk;
|
|
88
103
|
}
|
|
@@ -92,7 +107,7 @@ pub const ProjectNameSDK = struct {
|
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
pub fn get_utility(self: *ProjectNameSDK) *Utility {
|
|
95
|
-
return Utility.copy(self.
|
|
110
|
+
return Utility.copy(self.sdkUtility);
|
|
96
111
|
}
|
|
97
112
|
|
|
98
113
|
pub fn get_root_ctx(self: *ProjectNameSDK) *Context {
|
|
@@ -100,7 +115,6 @@ pub const ProjectNameSDK = struct {
|
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
pub fn prepare(self: *ProjectNameSDK, fetchargs_in: Value) E!Value {
|
|
103
|
-
const utility = self.utility;
|
|
104
118
|
|
|
105
119
|
const fetchargs: Value = switch (fetchargs_in) {
|
|
106
120
|
.object => fetchargs_in,
|
|
@@ -112,7 +126,7 @@ pub const ProjectNameSDK = struct {
|
|
|
112
126
|
else => h.omap(),
|
|
113
127
|
};
|
|
114
128
|
|
|
115
|
-
const ctx =
|
|
129
|
+
const ctx = self.sdkUtility.make_context(CtxSpec{
|
|
116
130
|
.opname = "prepare",
|
|
117
131
|
.ctrl = ctrl,
|
|
118
132
|
}, self.get_root_ctx());
|
|
@@ -134,7 +148,7 @@ pub const ProjectNameSDK = struct {
|
|
|
134
148
|
else => h.omap(),
|
|
135
149
|
};
|
|
136
150
|
|
|
137
|
-
const headers =
|
|
151
|
+
const headers = self.sdkUtility.prepare_headers(ctx);
|
|
138
152
|
|
|
139
153
|
const specmap = h.jo(&.{
|
|
140
154
|
.{ "base", h.getp(options, "base") },
|
|
@@ -158,9 +172,9 @@ pub const ProjectNameSDK = struct {
|
|
|
158
172
|
while (it.next()) |kv| h.setp(spec.headers, kv.key_ptr.*, kv.value_ptr.*);
|
|
159
173
|
}
|
|
160
174
|
|
|
161
|
-
_ = try
|
|
175
|
+
_ = try self.sdkUtility.prepare_auth(ctx);
|
|
162
176
|
|
|
163
|
-
return
|
|
177
|
+
return self.sdkUtility.make_fetch_def(ctx);
|
|
164
178
|
}
|
|
165
179
|
|
|
166
180
|
// Raw endpoint access is operator-controllable, like every entity op.
|
|
@@ -200,7 +214,6 @@ pub const ProjectNameSDK = struct {
|
|
|
200
214
|
// a caller-supplied marker would let anyone opt straight back out of the
|
|
201
215
|
// gate by passing it.
|
|
202
216
|
fn raw_request(self: *ProjectNameSDK, fetchargs_in: Value) Value {
|
|
203
|
-
const utility = self.utility;
|
|
204
217
|
|
|
205
218
|
const fetchdef = self.prepare(fetchargs_in) catch {
|
|
206
219
|
return h.jo(&.{
|
|
@@ -218,13 +231,13 @@ pub const ProjectNameSDK = struct {
|
|
|
218
231
|
else => h.omap(),
|
|
219
232
|
};
|
|
220
233
|
|
|
221
|
-
const ctx =
|
|
234
|
+
const ctx = self.sdkUtility.make_context(CtxSpec{
|
|
222
235
|
.opname = "direct",
|
|
223
236
|
.ctrl = ctrl,
|
|
224
237
|
}, self.get_root_ctx());
|
|
225
238
|
|
|
226
239
|
const url = h.get_str(fetchdef, "url") orelse "";
|
|
227
|
-
const fetched =
|
|
240
|
+
const fetched = self.sdkUtility.fetch(ctx, url, fetchdef) catch {
|
|
228
241
|
return h.jo(&.{
|
|
229
242
|
.{ "ok", h.vbool(false) },
|
|
230
243
|
.{ "err", h.vstr(if (ctx.pending_err) |e| e.msg else "fetch failed") },
|
|
@@ -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 -------------------------------------------------------
|