@voxgig/sdkgen 3.3.2 → 3.4.2
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/dist/cmp/Readme.js +2 -0
- package/dist/cmp/Readme.js.map +1 -1
- package/dist/cmp/ReadmeUnions.d.ts +3 -0
- package/dist/cmp/ReadmeUnions.js +74 -0
- package/dist/cmp/ReadmeUnions.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/project/.sdk/src/cmp/c/Config_c.ts +1 -1
- package/project/.sdk/src/cmp/c/utility_c.ts +57 -6
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +1 -1
- package/project/.sdk/src/cmp/clojure/utility_clojure.ts +57 -6
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +1 -1
- package/project/.sdk/src/cmp/cpp/utility_cpp.ts +57 -6
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +1 -1
- package/project/.sdk/src/cmp/csharp/utility_csharp.ts +57 -6
- package/project/.sdk/src/cmp/dart/Config_dart.ts +1 -1
- package/project/.sdk/src/cmp/dart/utility_dart.ts +57 -6
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +1 -1
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +57 -6
- package/project/.sdk/src/cmp/go/Config_go.ts +27 -2
- package/project/.sdk/src/cmp/go/Main_go.ts +1 -0
- package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
- package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +1 -1
- package/project/.sdk/src/cmp/go/utility_go.ts +57 -6
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +1 -1
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +57 -6
- package/project/.sdk/src/cmp/java/Config_java.ts +1 -1
- package/project/.sdk/src/cmp/java/utility_java.ts +57 -6
- package/project/.sdk/src/cmp/js/Config_js.ts +1 -1
- package/project/.sdk/src/cmp/js/utility_js.ts +57 -6
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +1 -1
- package/project/.sdk/src/cmp/kotlin/utility_kotlin.ts +57 -6
- package/project/.sdk/src/cmp/lean/Config_lean.ts +1 -1
- package/project/.sdk/src/cmp/lean/utility_lean.ts +57 -6
- package/project/.sdk/src/cmp/lua/Config_lua.ts +30 -1
- package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -0
- package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +1 -1
- package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +1 -1
- package/project/.sdk/src/cmp/lua/utility_lua.ts +57 -6
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +1 -1
- package/project/.sdk/src/cmp/ocaml/utility_ocaml.ts +57 -6
- package/project/.sdk/src/cmp/perl/Config_perl.ts +1 -1
- package/project/.sdk/src/cmp/perl/utility_perl.ts +57 -6
- package/project/.sdk/src/cmp/php/Config_php.ts +24 -1
- package/project/.sdk/src/cmp/php/TestEntity_php.ts +1 -1
- package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +22 -0
- package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +1 -1
- package/project/.sdk/src/cmp/php/utility_php.ts +57 -6
- package/project/.sdk/src/cmp/py/Config_py.ts +24 -1
- package/project/.sdk/src/cmp/py/TestEntity_py.ts +2 -2
- package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +2 -2
- package/project/.sdk/src/cmp/py/utility_py.ts +57 -6
- package/project/.sdk/src/cmp/rb/Config_rb.ts +15 -1
- package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +1 -1
- package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +1 -1
- package/project/.sdk/src/cmp/rb/utility_rb.ts +57 -6
- package/project/.sdk/src/cmp/rust/Config_rust.ts +1 -1
- package/project/.sdk/src/cmp/rust/utility_rust.ts +57 -6
- package/project/.sdk/src/cmp/scala/Config_scala.ts +1 -1
- package/project/.sdk/src/cmp/scala/utility_scala.ts +57 -6
- package/project/.sdk/src/cmp/swift/Config_swift.ts +1 -1
- package/project/.sdk/src/cmp/swift/utility_swift.ts +57 -6
- package/project/.sdk/src/cmp/ts/Config_ts.ts +1 -1
- package/project/.sdk/src/cmp/ts/utility_ts.ts +57 -6
- package/project/.sdk/src/cmp/zig/Config_zig.ts +1 -1
- package/project/.sdk/src/cmp/zig/utility_zig.ts +57 -6
- package/project/.sdk/tm/c/core/point.c +2 -1
- package/project/.sdk/tm/csharp/core/Point.cs +3 -1
- package/project/.sdk/tm/dart/lib/Point.dart +2 -1
- package/project/.sdk/tm/go/test/feature_harness_test.go +1 -1
- package/project/.sdk/tm/go/utility/make_options.go +5 -1
- package/project/.sdk/tm/java/core/Point.java +3 -1
- package/project/.sdk/tm/js/src/Point.js +3 -1
- package/project/.sdk/tm/kotlin/core/Point.kt +3 -1
- package/project/.sdk/tm/lua/utility/make_options.lua +5 -1
- package/project/.sdk/tm/php/test/FeatureTest.php +1 -1
- package/project/.sdk/tm/py/pkg/utility/make_options.py +5 -1
- package/project/.sdk/tm/py/test/feature_harness.py +2 -2
- package/project/.sdk/tm/rb/test/feature_test.rb +1 -1
- package/project/.sdk/tm/rb/utility/make_options.rb +5 -1
- package/project/.sdk/tm/scala/core/Point.scala +2 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Point.swift +3 -1
- package/project/.sdk/tm/ts/src/Point.ts +3 -1
- package/project/.sdk/tm/zig/core/point.zig +2 -1
- package/src/cmp/Readme.ts +2 -0
- package/src/cmp/ReadmeUnions.ts +90 -0
|
@@ -14,13 +14,64 @@ function projectPath(suffix?: string): string {
|
|
|
14
14
|
|
|
15
15
|
// Strip model-internal keys (ending in `$`) so generated config JSON only
|
|
16
16
|
// carries API-shaped data.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
// Emission-time normalisation of a model subtree (L0).
|
|
18
|
+
//
|
|
19
|
+
// Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
|
|
20
|
+
// key$, val$). With `dropDefaults`, also drops keys whose value IS the
|
|
21
|
+
// default the runtime already assumes when the key is absent, which is pure
|
|
22
|
+
// payload — see CONFIG_DEFAULT.
|
|
23
|
+
//
|
|
24
|
+
// Rebuilds the tree rather than mutating during a walk. The previous
|
|
25
|
+
// implementation walked a clone calling `delete p[k]`, but walk() assigns its
|
|
26
|
+
// callback's result back over the child (`setprop(out, ckey, walk(...))`), so
|
|
27
|
+
// the delete was undone on the way out and the helper silently did nothing.
|
|
28
|
+
// Returning `undefined` from the callback does not fix it either: setprop
|
|
29
|
+
// stores undefined rather than removing the key, which then emits as a null.
|
|
30
|
+
//
|
|
31
|
+
// `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
|
|
32
|
+
// `active` means something different in feature config, where absent reads as
|
|
33
|
+
// INACTIVE (see feature_init) — dropping `active: true` there would silently
|
|
34
|
+
// disable the feature.
|
|
35
|
+
// jostraca's iteration metadata, injected by each()/names() while it walks the
|
|
36
|
+
// model. Listed explicitly rather than matched by trailing-dollar suffix: a
|
|
37
|
+
// trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
|
|
38
|
+
// data -- so a blanket suffix match can silently drop a legitimate API field.
|
|
39
|
+
const MODEL_META = ['index$', 'key$', 'val$']
|
|
40
|
+
|
|
41
|
+
// Keys whose value IS the default the runtime already assumes when the key is
|
|
42
|
+
// absent, so emitting them is pure payload.
|
|
43
|
+
const CONFIG_DEFAULT: Record<string, any> = {
|
|
44
|
+
active: true,
|
|
45
|
+
req: false,
|
|
46
|
+
reqd: false,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Subtrees carrying user payload rather than schema. An active:true inside an
|
|
50
|
+
// OpenAPI example is DATA, not a default, so default-pruning stops at these
|
|
51
|
+
// keys and everything below them is passed through untouched.
|
|
52
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples']
|
|
53
|
+
|
|
54
|
+
function clean(o: any, dropDefaults?: boolean): any {
|
|
55
|
+
const prune = (node: any, defaults: boolean): any => {
|
|
56
|
+
if (Array.isArray(node)) {
|
|
57
|
+
return node.map((n: any) => prune(n, defaults))
|
|
58
|
+
}
|
|
59
|
+
if (null != node && 'object' === typeof node) {
|
|
60
|
+
const out: any = {}
|
|
61
|
+
for (const k of Object.keys(node)) {
|
|
62
|
+
if (MODEL_META.includes(k)) {
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
|
|
69
|
+
}
|
|
70
|
+
return out
|
|
21
71
|
}
|
|
22
|
-
return
|
|
23
|
-
}
|
|
72
|
+
return node
|
|
73
|
+
}
|
|
74
|
+
return prune(o, true === dropDefaults)
|
|
24
75
|
}
|
|
25
76
|
|
|
26
77
|
|
|
@@ -72,6 +72,29 @@ declare(strict_types=1);
|
|
|
72
72
|
|
|
73
73
|
class ${model.const.Name}Config
|
|
74
74
|
{
|
|
75
|
+
/** @var array<string,mixed>|null */
|
|
76
|
+
private static ?array $shared_config = null;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Return the process-wide config, built once on first use. The SDK reads
|
|
80
|
+
* the config on every request and never writes to it, so one instance is
|
|
81
|
+
* shared by every client rather than rebuilt per client.
|
|
82
|
+
*
|
|
83
|
+
* PHP arrays are copy-on-write, so callers that do mutate the result get
|
|
84
|
+
* their own copy and cannot disturb the shared one.
|
|
85
|
+
*/
|
|
86
|
+
public static function shared_config(): array
|
|
87
|
+
{
|
|
88
|
+
if (self::$shared_config === null) {
|
|
89
|
+
self::$shared_config = self::make_config();
|
|
90
|
+
}
|
|
91
|
+
return self::$shared_config;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Build a fresh, fully materialised config array. Every call rebuilds the
|
|
96
|
+
* whole structure, so prefer shared_config unless you need a private copy.
|
|
97
|
+
*/
|
|
75
98
|
public static function make_config(): array
|
|
76
99
|
{
|
|
77
100
|
return [
|
|
@@ -122,7 +145,7 @@ ${serverBlock}${authBlock} "headers" => ${formatPhpArray(headers,
|
|
|
122
145
|
name: n.name,
|
|
123
146
|
op: n.op,
|
|
124
147
|
relations: n.relations,
|
|
125
|
-
}), a), {}), 3)},
|
|
148
|
+
}, true), a), {}), 3)},
|
|
126
149
|
];
|
|
127
150
|
`)
|
|
128
151
|
}
|
|
@@ -137,7 +137,7 @@ ${hasList ? `
|
|
|
137
137
|
$this->assertCount(3, $seen);
|
|
138
138
|
|
|
139
139
|
// Inbound: streaming active -> yields each item from the feature.
|
|
140
|
-
$cfg = ${model.const.Name}Config::
|
|
140
|
+
$cfg = ${model.const.Name}Config::shared_config();
|
|
141
141
|
if (isset($cfg["feature"]) && is_array($cfg["feature"]) && isset($cfg["feature"]["streaming"])) {
|
|
142
142
|
$sdk = ${model.const.Name}SDK::test($seed, ["feature" => ["streaming" => ["active" => true]]]);
|
|
143
143
|
$got = [];
|
|
@@ -17,6 +17,7 @@ class EntyClass
|
|
|
17
17
|
private array $_data;
|
|
18
18
|
private array $_match;
|
|
19
19
|
private $_entctx;
|
|
20
|
+
private bool $_deleted = false;
|
|
20
21
|
|
|
21
22
|
public function __construct($client, ?array $entopts = null)
|
|
22
23
|
{
|
|
@@ -49,6 +50,27 @@ class EntyClass
|
|
|
49
50
|
return $this->_name;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
/**
|
|
54
|
+
* A `remove` marks the entity deleted. The instance KEEPS the data it
|
|
55
|
+
* held — a caller can still read what was removed — but it is no longer a
|
|
56
|
+
* live record. See AGENTS.md "Entity operations return ENTITIES".
|
|
57
|
+
*
|
|
58
|
+
* The remove path below already called markDeleted(); php was the one
|
|
59
|
+
* target that never declared it (cpp and swift both do), so any SDK whose
|
|
60
|
+
* entities have a `remove` op raised "Call to undefined method
|
|
61
|
+
* <Entity>::markDeleted()" the first time a remove succeeded. Nothing
|
|
62
|
+
* caught it because the fatal only fires when that path actually runs.
|
|
63
|
+
*/
|
|
64
|
+
public function markDeleted(): void
|
|
65
|
+
{
|
|
66
|
+
$this->_deleted = true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public function deleted(): bool
|
|
70
|
+
{
|
|
71
|
+
return $this->_deleted;
|
|
72
|
+
}
|
|
73
|
+
|
|
52
74
|
public function make(): self
|
|
53
75
|
{
|
|
54
76
|
$opts = $this->_entopts;
|
|
@@ -40,7 +40,7 @@ class ProjectNameSDK
|
|
|
40
40
|
$utility = new ProjectNameUtility();
|
|
41
41
|
$this->_utility = $utility;
|
|
42
42
|
|
|
43
|
-
$config = ProjectNameConfig::
|
|
43
|
+
$config = ProjectNameConfig::shared_config();
|
|
44
44
|
|
|
45
45
|
$this->_rootctx = ($utility->make_context)([
|
|
46
46
|
"client" => $this,
|
|
@@ -67,13 +67,64 @@ function formatPhpValue(val: any, indent: number = 0): string {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
// Emission-time normalisation of a model subtree (L0).
|
|
71
|
+
//
|
|
72
|
+
// Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
|
|
73
|
+
// key$, val$). With `dropDefaults`, also drops keys whose value IS the
|
|
74
|
+
// default the runtime already assumes when the key is absent, which is pure
|
|
75
|
+
// payload — see CONFIG_DEFAULT.
|
|
76
|
+
//
|
|
77
|
+
// Rebuilds the tree rather than mutating during a walk. The previous
|
|
78
|
+
// implementation walked a clone calling `delete p[k]`, but walk() assigns its
|
|
79
|
+
// callback's result back over the child (`setprop(out, ckey, walk(...))`), so
|
|
80
|
+
// the delete was undone on the way out and the helper silently did nothing.
|
|
81
|
+
// Returning `undefined` from the callback does not fix it either: setprop
|
|
82
|
+
// stores undefined rather than removing the key, which then emits as a null.
|
|
83
|
+
//
|
|
84
|
+
// `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
|
|
85
|
+
// `active` means something different in feature config, where absent reads as
|
|
86
|
+
// INACTIVE (see feature_init) — dropping `active: true` there would silently
|
|
87
|
+
// disable the feature.
|
|
88
|
+
// jostraca's iteration metadata, injected by each()/names() while it walks the
|
|
89
|
+
// model. Listed explicitly rather than matched by trailing-dollar suffix: a
|
|
90
|
+
// trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
|
|
91
|
+
// data -- so a blanket suffix match can silently drop a legitimate API field.
|
|
92
|
+
const MODEL_META = ['index$', 'key$', 'val$']
|
|
93
|
+
|
|
94
|
+
// Keys whose value IS the default the runtime already assumes when the key is
|
|
95
|
+
// absent, so emitting them is pure payload.
|
|
96
|
+
const CONFIG_DEFAULT: Record<string, any> = {
|
|
97
|
+
active: true,
|
|
98
|
+
req: false,
|
|
99
|
+
reqd: false,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Subtrees carrying user payload rather than schema. An active:true inside an
|
|
103
|
+
// OpenAPI example is DATA, not a default, so default-pruning stops at these
|
|
104
|
+
// keys and everything below them is passed through untouched.
|
|
105
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples']
|
|
106
|
+
|
|
107
|
+
function clean(o: any, dropDefaults?: boolean): any {
|
|
108
|
+
const prune = (node: any, defaults: boolean): any => {
|
|
109
|
+
if (Array.isArray(node)) {
|
|
110
|
+
return node.map((n: any) => prune(n, defaults))
|
|
74
111
|
}
|
|
75
|
-
|
|
76
|
-
|
|
112
|
+
if (null != node && 'object' === typeof node) {
|
|
113
|
+
const out: any = {}
|
|
114
|
+
for (const k of Object.keys(node)) {
|
|
115
|
+
if (MODEL_META.includes(k)) {
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
118
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
|
|
119
|
+
continue
|
|
120
|
+
}
|
|
121
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
|
|
122
|
+
}
|
|
123
|
+
return out
|
|
124
|
+
}
|
|
125
|
+
return node
|
|
126
|
+
}
|
|
127
|
+
return prune(o, true === dropDefaults)
|
|
77
128
|
}
|
|
78
129
|
|
|
79
130
|
|
|
@@ -66,7 +66,30 @@ const Config = cmp(async function Config(props: any) {
|
|
|
66
66
|
Content(`# ${model.const.Name} SDK configuration
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
_shared_config = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def shared_config():
|
|
73
|
+
"""Return the process-wide config, built once on first use.
|
|
74
|
+
|
|
75
|
+
The SDK reads the config on every request and never writes to it, so one
|
|
76
|
+
instance is shared by every client rather than rebuilt per client.
|
|
77
|
+
|
|
78
|
+
The returned dict is shared: treat it as read-only. Callers that need to
|
|
79
|
+
mutate should use make_config, which always returns a fresh copy.
|
|
80
|
+
"""
|
|
81
|
+
global _shared_config
|
|
82
|
+
if _shared_config is None:
|
|
83
|
+
_shared_config = make_config()
|
|
84
|
+
return _shared_config
|
|
85
|
+
|
|
86
|
+
|
|
69
87
|
def make_config():
|
|
88
|
+
"""Build a fresh, fully materialised config dict.
|
|
89
|
+
|
|
90
|
+
Every call rebuilds the whole structure, so prefer shared_config unless
|
|
91
|
+
you need a private copy you intend to mutate.
|
|
92
|
+
"""
|
|
70
93
|
return {
|
|
71
94
|
"main": {
|
|
72
95
|
"name": "${model.const.Name}",
|
|
@@ -100,7 +123,7 @@ ${serverBlock}${authBlock} "headers": ${formatPyDict(headers, 3)},
|
|
|
100
123
|
name: n.name,
|
|
101
124
|
op: n.op,
|
|
102
125
|
relations: n.relations,
|
|
103
|
-
}), a), {}), 2)},
|
|
126
|
+
}, true), a), {}), 2)},
|
|
104
127
|
}
|
|
105
128
|
`)
|
|
106
129
|
})
|
|
@@ -130,8 +130,8 @@ ${hasList ? `
|
|
|
130
130
|
assert len(seen) == 3
|
|
131
131
|
|
|
132
132
|
# Inbound: streaming active -> yields each item from the feature.
|
|
133
|
-
from ${model.const.Name.toLowerCase()}_sdk.config import
|
|
134
|
-
cfg =
|
|
133
|
+
from ${model.const.Name.toLowerCase()}_sdk.config import shared_config
|
|
134
|
+
cfg = shared_config()
|
|
135
135
|
if isinstance(cfg.get("feature"), dict) and "streaming" in cfg["feature"]:
|
|
136
136
|
sdk = ${model.const.Name}SDK.test(
|
|
137
137
|
seed, {"feature": {"streaming": {"active": True}}})
|
|
@@ -23,8 +23,8 @@ class ProjectNameSDK:
|
|
|
23
23
|
utility = ProjectNameUtility()
|
|
24
24
|
self._utility = utility
|
|
25
25
|
|
|
26
|
-
from projectname_sdk.config import
|
|
27
|
-
config =
|
|
26
|
+
from projectname_sdk.config import shared_config
|
|
27
|
+
config = shared_config()
|
|
28
28
|
|
|
29
29
|
self._rootctx = utility.make_context({
|
|
30
30
|
"client": self,
|
|
@@ -66,13 +66,64 @@ function formatPyValue(val: any, indent: number = 0): string {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
// Emission-time normalisation of a model subtree (L0).
|
|
70
|
+
//
|
|
71
|
+
// Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
|
|
72
|
+
// key$, val$). With `dropDefaults`, also drops keys whose value IS the
|
|
73
|
+
// default the runtime already assumes when the key is absent, which is pure
|
|
74
|
+
// payload — see CONFIG_DEFAULT.
|
|
75
|
+
//
|
|
76
|
+
// Rebuilds the tree rather than mutating during a walk. The previous
|
|
77
|
+
// implementation walked a clone calling `delete p[k]`, but walk() assigns its
|
|
78
|
+
// callback's result back over the child (`setprop(out, ckey, walk(...))`), so
|
|
79
|
+
// the delete was undone on the way out and the helper silently did nothing.
|
|
80
|
+
// Returning `undefined` from the callback does not fix it either: setprop
|
|
81
|
+
// stores undefined rather than removing the key, which then emits as a null.
|
|
82
|
+
//
|
|
83
|
+
// `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
|
|
84
|
+
// `active` means something different in feature config, where absent reads as
|
|
85
|
+
// INACTIVE (see feature_init) — dropping `active: true` there would silently
|
|
86
|
+
// disable the feature.
|
|
87
|
+
// jostraca's iteration metadata, injected by each()/names() while it walks the
|
|
88
|
+
// model. Listed explicitly rather than matched by trailing-dollar suffix: a
|
|
89
|
+
// trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
|
|
90
|
+
// data -- so a blanket suffix match can silently drop a legitimate API field.
|
|
91
|
+
const MODEL_META = ['index$', 'key$', 'val$']
|
|
92
|
+
|
|
93
|
+
// Keys whose value IS the default the runtime already assumes when the key is
|
|
94
|
+
// absent, so emitting them is pure payload.
|
|
95
|
+
const CONFIG_DEFAULT: Record<string, any> = {
|
|
96
|
+
active: true,
|
|
97
|
+
req: false,
|
|
98
|
+
reqd: false,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Subtrees carrying user payload rather than schema. An active:true inside an
|
|
102
|
+
// OpenAPI example is DATA, not a default, so default-pruning stops at these
|
|
103
|
+
// keys and everything below them is passed through untouched.
|
|
104
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples']
|
|
105
|
+
|
|
106
|
+
function clean(o: any, dropDefaults?: boolean): any {
|
|
107
|
+
const prune = (node: any, defaults: boolean): any => {
|
|
108
|
+
if (Array.isArray(node)) {
|
|
109
|
+
return node.map((n: any) => prune(n, defaults))
|
|
73
110
|
}
|
|
74
|
-
|
|
75
|
-
|
|
111
|
+
if (null != node && 'object' === typeof node) {
|
|
112
|
+
const out: any = {}
|
|
113
|
+
for (const k of Object.keys(node)) {
|
|
114
|
+
if (MODEL_META.includes(k)) {
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
|
|
118
|
+
continue
|
|
119
|
+
}
|
|
120
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
|
|
121
|
+
}
|
|
122
|
+
return out
|
|
123
|
+
}
|
|
124
|
+
return node
|
|
125
|
+
}
|
|
126
|
+
return prune(o, true === dropDefaults)
|
|
76
127
|
}
|
|
77
128
|
|
|
78
129
|
|
|
@@ -68,6 +68,20 @@ const Config = cmp(async function Config(props: any) {
|
|
|
68
68
|
Content(`# ${model.const.Name} SDK configuration
|
|
69
69
|
|
|
70
70
|
module ${model.const.Name}Config
|
|
71
|
+
# Return the process-wide config, built once on first use. The SDK reads
|
|
72
|
+
# the config on every request and never writes to it, so one instance is
|
|
73
|
+
# shared by every client rather than rebuilt per client.
|
|
74
|
+
#
|
|
75
|
+
# The returned hash is shared: treat it as read-only. Callers that need to
|
|
76
|
+
# mutate should use make_config, which always returns a fresh copy.
|
|
77
|
+
def self.shared_config
|
|
78
|
+
@shared_config ||= make_config
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Build a fresh, fully materialised config hash. Every call rebuilds the
|
|
83
|
+
# whole structure, so prefer shared_config unless you need a private copy
|
|
84
|
+
# you intend to mutate.
|
|
71
85
|
def self.make_config
|
|
72
86
|
{
|
|
73
87
|
"main" => {
|
|
@@ -102,7 +116,7 @@ ${serverBlock}${authBlock} "headers" => ${formatRubyHash(headers, 4)},
|
|
|
102
116
|
name: n.name,
|
|
103
117
|
op: n.op,
|
|
104
118
|
relations: n.relations,
|
|
105
|
-
}), a), {}), 3)},
|
|
119
|
+
}, true), a), {}), 3)},
|
|
106
120
|
}
|
|
107
121
|
end
|
|
108
122
|
|
|
@@ -117,7 +117,7 @@ ${hasList ? `
|
|
|
117
117
|
assert_equal 3, seen.length
|
|
118
118
|
|
|
119
119
|
# Inbound: streaming active -> yields each item from the feature.
|
|
120
|
-
cfg = ${model.const.Name}Config.
|
|
120
|
+
cfg = ${model.const.Name}Config.shared_config
|
|
121
121
|
if cfg["feature"].is_a?(Hash) && cfg["feature"].key?("streaming")
|
|
122
122
|
sdk = ${model.const.Name}SDK.test(seed, { "feature" => { "streaming" => { "active" => true } } })
|
|
123
123
|
got = []
|
|
@@ -66,13 +66,64 @@ function formatRubyValue(val: any, indent: number = 0): string {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
// Emission-time normalisation of a model subtree (L0).
|
|
70
|
+
//
|
|
71
|
+
// Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
|
|
72
|
+
// key$, val$). With `dropDefaults`, also drops keys whose value IS the
|
|
73
|
+
// default the runtime already assumes when the key is absent, which is pure
|
|
74
|
+
// payload — see CONFIG_DEFAULT.
|
|
75
|
+
//
|
|
76
|
+
// Rebuilds the tree rather than mutating during a walk. The previous
|
|
77
|
+
// implementation walked a clone calling `delete p[k]`, but walk() assigns its
|
|
78
|
+
// callback's result back over the child (`setprop(out, ckey, walk(...))`), so
|
|
79
|
+
// the delete was undone on the way out and the helper silently did nothing.
|
|
80
|
+
// Returning `undefined` from the callback does not fix it either: setprop
|
|
81
|
+
// stores undefined rather than removing the key, which then emits as a null.
|
|
82
|
+
//
|
|
83
|
+
// `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
|
|
84
|
+
// `active` means something different in feature config, where absent reads as
|
|
85
|
+
// INACTIVE (see feature_init) — dropping `active: true` there would silently
|
|
86
|
+
// disable the feature.
|
|
87
|
+
// jostraca's iteration metadata, injected by each()/names() while it walks the
|
|
88
|
+
// model. Listed explicitly rather than matched by trailing-dollar suffix: a
|
|
89
|
+
// trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
|
|
90
|
+
// data -- so a blanket suffix match can silently drop a legitimate API field.
|
|
91
|
+
const MODEL_META = ['index$', 'key$', 'val$']
|
|
92
|
+
|
|
93
|
+
// Keys whose value IS the default the runtime already assumes when the key is
|
|
94
|
+
// absent, so emitting them is pure payload.
|
|
95
|
+
const CONFIG_DEFAULT: Record<string, any> = {
|
|
96
|
+
active: true,
|
|
97
|
+
req: false,
|
|
98
|
+
reqd: false,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Subtrees carrying user payload rather than schema. An active:true inside an
|
|
102
|
+
// OpenAPI example is DATA, not a default, so default-pruning stops at these
|
|
103
|
+
// keys and everything below them is passed through untouched.
|
|
104
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples']
|
|
105
|
+
|
|
106
|
+
function clean(o: any, dropDefaults?: boolean): any {
|
|
107
|
+
const prune = (node: any, defaults: boolean): any => {
|
|
108
|
+
if (Array.isArray(node)) {
|
|
109
|
+
return node.map((n: any) => prune(n, defaults))
|
|
73
110
|
}
|
|
74
|
-
|
|
75
|
-
|
|
111
|
+
if (null != node && 'object' === typeof node) {
|
|
112
|
+
const out: any = {}
|
|
113
|
+
for (const k of Object.keys(node)) {
|
|
114
|
+
if (MODEL_META.includes(k)) {
|
|
115
|
+
continue
|
|
116
|
+
}
|
|
117
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
|
|
118
|
+
continue
|
|
119
|
+
}
|
|
120
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
|
|
121
|
+
}
|
|
122
|
+
return out
|
|
123
|
+
}
|
|
124
|
+
return node
|
|
125
|
+
}
|
|
126
|
+
return prune(o, true === dropDefaults)
|
|
76
127
|
}
|
|
77
128
|
|
|
78
129
|
|
|
@@ -85,13 +85,64 @@ function formatRustValue(val: any, indent: number = 0): string {
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
// Deep-remove meta keys (`foo$`) from a model subtree (twin of go's clean).
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
// Emission-time normalisation of a model subtree (L0).
|
|
89
|
+
//
|
|
90
|
+
// Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
|
|
91
|
+
// key$, val$). With `dropDefaults`, also drops keys whose value IS the
|
|
92
|
+
// default the runtime already assumes when the key is absent, which is pure
|
|
93
|
+
// payload — see CONFIG_DEFAULT.
|
|
94
|
+
//
|
|
95
|
+
// Rebuilds the tree rather than mutating during a walk. The previous
|
|
96
|
+
// implementation walked a clone calling `delete p[k]`, but walk() assigns its
|
|
97
|
+
// callback's result back over the child (`setprop(out, ckey, walk(...))`), so
|
|
98
|
+
// the delete was undone on the way out and the helper silently did nothing.
|
|
99
|
+
// Returning `undefined` from the callback does not fix it either: setprop
|
|
100
|
+
// stores undefined rather than removing the key, which then emits as a null.
|
|
101
|
+
//
|
|
102
|
+
// `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
|
|
103
|
+
// `active` means something different in feature config, where absent reads as
|
|
104
|
+
// INACTIVE (see feature_init) — dropping `active: true` there would silently
|
|
105
|
+
// disable the feature.
|
|
106
|
+
// jostraca's iteration metadata, injected by each()/names() while it walks the
|
|
107
|
+
// model. Listed explicitly rather than matched by trailing-dollar suffix: a
|
|
108
|
+
// trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
|
|
109
|
+
// data -- so a blanket suffix match can silently drop a legitimate API field.
|
|
110
|
+
const MODEL_META = ['index$', 'key$', 'val$']
|
|
111
|
+
|
|
112
|
+
// Keys whose value IS the default the runtime already assumes when the key is
|
|
113
|
+
// absent, so emitting them is pure payload.
|
|
114
|
+
const CONFIG_DEFAULT: Record<string, any> = {
|
|
115
|
+
active: true,
|
|
116
|
+
req: false,
|
|
117
|
+
reqd: false,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Subtrees carrying user payload rather than schema. An active:true inside an
|
|
121
|
+
// OpenAPI example is DATA, not a default, so default-pruning stops at these
|
|
122
|
+
// keys and everything below them is passed through untouched.
|
|
123
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples']
|
|
124
|
+
|
|
125
|
+
function clean(o: any, dropDefaults?: boolean): any {
|
|
126
|
+
const prune = (node: any, defaults: boolean): any => {
|
|
127
|
+
if (Array.isArray(node)) {
|
|
128
|
+
return node.map((n: any) => prune(n, defaults))
|
|
92
129
|
}
|
|
93
|
-
|
|
94
|
-
|
|
130
|
+
if (null != node && 'object' === typeof node) {
|
|
131
|
+
const out: any = {}
|
|
132
|
+
for (const k of Object.keys(node)) {
|
|
133
|
+
if (MODEL_META.includes(k)) {
|
|
134
|
+
continue
|
|
135
|
+
}
|
|
136
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
|
|
140
|
+
}
|
|
141
|
+
return out
|
|
142
|
+
}
|
|
143
|
+
return node
|
|
144
|
+
}
|
|
145
|
+
return prune(o, true === dropDefaults)
|
|
95
146
|
}
|
|
96
147
|
|
|
97
148
|
|
|
@@ -65,13 +65,64 @@ function scalaVarName(name: string): string {
|
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
// Strip model bookkeeping keys (ending in $) from a config subtree.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
// Emission-time normalisation of a model subtree (L0).
|
|
69
|
+
//
|
|
70
|
+
// Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
|
|
71
|
+
// key$, val$). With `dropDefaults`, also drops keys whose value IS the
|
|
72
|
+
// default the runtime already assumes when the key is absent, which is pure
|
|
73
|
+
// payload — see CONFIG_DEFAULT.
|
|
74
|
+
//
|
|
75
|
+
// Rebuilds the tree rather than mutating during a walk. The previous
|
|
76
|
+
// implementation walked a clone calling `delete p[k]`, but walk() assigns its
|
|
77
|
+
// callback's result back over the child (`setprop(out, ckey, walk(...))`), so
|
|
78
|
+
// the delete was undone on the way out and the helper silently did nothing.
|
|
79
|
+
// Returning `undefined` from the callback does not fix it either: setprop
|
|
80
|
+
// stores undefined rather than removing the key, which then emits as a null.
|
|
81
|
+
//
|
|
82
|
+
// `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
|
|
83
|
+
// `active` means something different in feature config, where absent reads as
|
|
84
|
+
// INACTIVE (see feature_init) — dropping `active: true` there would silently
|
|
85
|
+
// disable the feature.
|
|
86
|
+
// jostraca's iteration metadata, injected by each()/names() while it walks the
|
|
87
|
+
// model. Listed explicitly rather than matched by trailing-dollar suffix: a
|
|
88
|
+
// trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
|
|
89
|
+
// data -- so a blanket suffix match can silently drop a legitimate API field.
|
|
90
|
+
const MODEL_META = ['index$', 'key$', 'val$']
|
|
91
|
+
|
|
92
|
+
// Keys whose value IS the default the runtime already assumes when the key is
|
|
93
|
+
// absent, so emitting them is pure payload.
|
|
94
|
+
const CONFIG_DEFAULT: Record<string, any> = {
|
|
95
|
+
active: true,
|
|
96
|
+
req: false,
|
|
97
|
+
reqd: false,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Subtrees carrying user payload rather than schema. An active:true inside an
|
|
101
|
+
// OpenAPI example is DATA, not a default, so default-pruning stops at these
|
|
102
|
+
// keys and everything below them is passed through untouched.
|
|
103
|
+
const PAYLOAD_KEYS = ['default', 'example', 'examples']
|
|
104
|
+
|
|
105
|
+
function cleanModel(o: any, dropDefaults?: boolean): any {
|
|
106
|
+
const prune = (node: any, defaults: boolean): any => {
|
|
107
|
+
if (Array.isArray(node)) {
|
|
108
|
+
return node.map((n: any) => prune(n, defaults))
|
|
109
|
+
}
|
|
110
|
+
if (null != node && 'object' === typeof node) {
|
|
111
|
+
const out: any = {}
|
|
112
|
+
for (const k of Object.keys(node)) {
|
|
113
|
+
if (MODEL_META.includes(k)) {
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
|
|
117
|
+
continue
|
|
118
|
+
}
|
|
119
|
+
out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
|
|
120
|
+
}
|
|
121
|
+
return out
|
|
72
122
|
}
|
|
73
|
-
return
|
|
74
|
-
}
|
|
123
|
+
return node
|
|
124
|
+
}
|
|
125
|
+
return prune(o, true === dropDefaults)
|
|
75
126
|
}
|
|
76
127
|
|
|
77
128
|
|