@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.
Files changed (86) hide show
  1. package/dist/cmp/Readme.js +2 -0
  2. package/dist/cmp/Readme.js.map +1 -1
  3. package/dist/cmp/ReadmeUnions.d.ts +3 -0
  4. package/dist/cmp/ReadmeUnions.js +74 -0
  5. package/dist/cmp/ReadmeUnions.js.map +1 -0
  6. package/dist/tsconfig.tsbuildinfo +1 -1
  7. package/package.json +1 -1
  8. package/project/.sdk/src/cmp/c/Config_c.ts +1 -1
  9. package/project/.sdk/src/cmp/c/utility_c.ts +57 -6
  10. package/project/.sdk/src/cmp/clojure/Config_clojure.ts +1 -1
  11. package/project/.sdk/src/cmp/clojure/utility_clojure.ts +57 -6
  12. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +1 -1
  13. package/project/.sdk/src/cmp/cpp/utility_cpp.ts +57 -6
  14. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +1 -1
  15. package/project/.sdk/src/cmp/csharp/utility_csharp.ts +57 -6
  16. package/project/.sdk/src/cmp/dart/Config_dart.ts +1 -1
  17. package/project/.sdk/src/cmp/dart/utility_dart.ts +57 -6
  18. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +1 -1
  19. package/project/.sdk/src/cmp/elixir/utility_elixir.ts +57 -6
  20. package/project/.sdk/src/cmp/go/Config_go.ts +27 -2
  21. package/project/.sdk/src/cmp/go/Main_go.ts +1 -0
  22. package/project/.sdk/src/cmp/go/TestEntity_go.ts +1 -1
  23. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +1 -1
  24. package/project/.sdk/src/cmp/go/utility_go.ts +57 -6
  25. package/project/.sdk/src/cmp/haskell/Config_haskell.ts +1 -1
  26. package/project/.sdk/src/cmp/haskell/utility_haskell.ts +57 -6
  27. package/project/.sdk/src/cmp/java/Config_java.ts +1 -1
  28. package/project/.sdk/src/cmp/java/utility_java.ts +57 -6
  29. package/project/.sdk/src/cmp/js/Config_js.ts +1 -1
  30. package/project/.sdk/src/cmp/js/utility_js.ts +57 -6
  31. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +1 -1
  32. package/project/.sdk/src/cmp/kotlin/utility_kotlin.ts +57 -6
  33. package/project/.sdk/src/cmp/lean/Config_lean.ts +1 -1
  34. package/project/.sdk/src/cmp/lean/utility_lean.ts +57 -6
  35. package/project/.sdk/src/cmp/lua/Config_lua.ts +30 -1
  36. package/project/.sdk/src/cmp/lua/Package_lua.ts +1 -0
  37. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +1 -1
  38. package/project/.sdk/src/cmp/lua/fragment/Main.fragment.lua +1 -1
  39. package/project/.sdk/src/cmp/lua/utility_lua.ts +57 -6
  40. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +1 -1
  41. package/project/.sdk/src/cmp/ocaml/utility_ocaml.ts +57 -6
  42. package/project/.sdk/src/cmp/perl/Config_perl.ts +1 -1
  43. package/project/.sdk/src/cmp/perl/utility_perl.ts +57 -6
  44. package/project/.sdk/src/cmp/php/Config_php.ts +24 -1
  45. package/project/.sdk/src/cmp/php/TestEntity_php.ts +1 -1
  46. package/project/.sdk/src/cmp/php/fragment/Entity.fragment.php +22 -0
  47. package/project/.sdk/src/cmp/php/fragment/Main.fragment.php +1 -1
  48. package/project/.sdk/src/cmp/php/utility_php.ts +57 -6
  49. package/project/.sdk/src/cmp/py/Config_py.ts +24 -1
  50. package/project/.sdk/src/cmp/py/TestEntity_py.ts +2 -2
  51. package/project/.sdk/src/cmp/py/fragment/Main.fragment.py +2 -2
  52. package/project/.sdk/src/cmp/py/utility_py.ts +57 -6
  53. package/project/.sdk/src/cmp/rb/Config_rb.ts +15 -1
  54. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +1 -1
  55. package/project/.sdk/src/cmp/rb/fragment/Main.fragment.rb +1 -1
  56. package/project/.sdk/src/cmp/rb/utility_rb.ts +57 -6
  57. package/project/.sdk/src/cmp/rust/Config_rust.ts +1 -1
  58. package/project/.sdk/src/cmp/rust/utility_rust.ts +57 -6
  59. package/project/.sdk/src/cmp/scala/Config_scala.ts +1 -1
  60. package/project/.sdk/src/cmp/scala/utility_scala.ts +57 -6
  61. package/project/.sdk/src/cmp/swift/Config_swift.ts +1 -1
  62. package/project/.sdk/src/cmp/swift/utility_swift.ts +57 -6
  63. package/project/.sdk/src/cmp/ts/Config_ts.ts +1 -1
  64. package/project/.sdk/src/cmp/ts/utility_ts.ts +57 -6
  65. package/project/.sdk/src/cmp/zig/Config_zig.ts +1 -1
  66. package/project/.sdk/src/cmp/zig/utility_zig.ts +57 -6
  67. package/project/.sdk/tm/c/core/point.c +2 -1
  68. package/project/.sdk/tm/csharp/core/Point.cs +3 -1
  69. package/project/.sdk/tm/dart/lib/Point.dart +2 -1
  70. package/project/.sdk/tm/go/test/feature_harness_test.go +1 -1
  71. package/project/.sdk/tm/go/utility/make_options.go +5 -1
  72. package/project/.sdk/tm/java/core/Point.java +3 -1
  73. package/project/.sdk/tm/js/src/Point.js +3 -1
  74. package/project/.sdk/tm/kotlin/core/Point.kt +3 -1
  75. package/project/.sdk/tm/lua/utility/make_options.lua +5 -1
  76. package/project/.sdk/tm/php/test/FeatureTest.php +1 -1
  77. package/project/.sdk/tm/py/pkg/utility/make_options.py +5 -1
  78. package/project/.sdk/tm/py/test/feature_harness.py +2 -2
  79. package/project/.sdk/tm/rb/test/feature_test.rb +1 -1
  80. package/project/.sdk/tm/rb/utility/make_options.rb +5 -1
  81. package/project/.sdk/tm/scala/core/Point.scala +2 -1
  82. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/core/Point.swift +3 -1
  83. package/project/.sdk/tm/ts/src/Point.ts +3 -1
  84. package/project/.sdk/tm/zig/core/point.zig +2 -1
  85. package/src/cmp/Readme.ts +2 -0
  86. package/src/cmp/ReadmeUnions.ts +90 -0
@@ -61,7 +61,7 @@ const Config = cmp(async function Config(props: any) {
61
61
  name: ent.name,
62
62
  op: ent.op,
63
63
  relations: ent.relations,
64
- })
64
+ }, true)
65
65
  })
66
66
 
67
67
  const options: any = {
@@ -57,13 +57,64 @@ function swiftString(val: string): string {
57
57
 
58
58
 
59
59
  // Strip model-internal keys (ending in `$`) from a cloned value graph.
60
- function clean(o: any) {
61
- return walk(clone(o), (k: any, v: any, p: any) => {
62
- if (null != k && k.endsWith('$')) {
63
- delete p[k]
60
+ // Emission-time normalisation of a model subtree (L0).
61
+ //
62
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
63
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
64
+ // default the runtime already assumes when the key is absent, which is pure
65
+ // payload — see CONFIG_DEFAULT.
66
+ //
67
+ // Rebuilds the tree rather than mutating during a walk. The previous
68
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
69
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
70
+ // the delete was undone on the way out and the helper silently did nothing.
71
+ // Returning `undefined` from the callback does not fix it either: setprop
72
+ // stores undefined rather than removing the key, which then emits as a null.
73
+ //
74
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
75
+ // `active` means something different in feature config, where absent reads as
76
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
77
+ // disable the feature.
78
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
79
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
80
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
81
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
82
+ const MODEL_META = ['index$', 'key$', 'val$']
83
+
84
+ // Keys whose value IS the default the runtime already assumes when the key is
85
+ // absent, so emitting them is pure payload.
86
+ const CONFIG_DEFAULT: Record<string, any> = {
87
+ active: true,
88
+ req: false,
89
+ reqd: false,
90
+ }
91
+
92
+ // Subtrees carrying user payload rather than schema. An active:true inside an
93
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
94
+ // keys and everything below them is passed through untouched.
95
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
96
+
97
+ function clean(o: any, dropDefaults?: boolean): any {
98
+ const prune = (node: any, defaults: boolean): any => {
99
+ if (Array.isArray(node)) {
100
+ return node.map((n: any) => prune(n, defaults))
101
+ }
102
+ if (null != node && 'object' === typeof node) {
103
+ const out: any = {}
104
+ for (const k of Object.keys(node)) {
105
+ if (MODEL_META.includes(k)) {
106
+ continue
107
+ }
108
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
109
+ continue
110
+ }
111
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
112
+ }
113
+ return out
64
114
  }
65
- return v
66
- })
115
+ return node
116
+ }
117
+ return prune(o, true === dropDefaults)
67
118
  }
68
119
 
69
120
 
@@ -104,7 +104,7 @@ const Config = cmp(async function Config(props: any) {
104
104
  name: n.name,
105
105
  op: n.op,
106
106
  relations: n.relations,
107
- }), a), {}), { margin: 2 }).trim(),
107
+ }, true), a), {}), { margin: 2 }).trim(),
108
108
  }
109
109
  })
110
110
  })
@@ -104,13 +104,64 @@ function formatJson(obj: any, flags?: { line?: boolean, margin?: number }): stri
104
104
  }
105
105
 
106
106
 
107
- function clean(o: any) {
108
- return walk(clone(o), (k: any, v: any, p: any) => {
109
- if (null != k && k.endsWith('$')) {
110
- delete p[k]
107
+ // Emission-time normalisation of a model subtree (L0).
108
+ //
109
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
110
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
111
+ // default the runtime already assumes when the key is absent, which is pure
112
+ // payload — see CONFIG_DEFAULT.
113
+ //
114
+ // Rebuilds the tree rather than mutating during a walk. The previous
115
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
116
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
117
+ // the delete was undone on the way out and the helper silently did nothing.
118
+ // Returning `undefined` from the callback does not fix it either: setprop
119
+ // stores undefined rather than removing the key, which then emits as a null.
120
+ //
121
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
122
+ // `active` means something different in feature config, where absent reads as
123
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
124
+ // disable the feature.
125
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
126
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
127
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
128
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
129
+ const MODEL_META = ['index$', 'key$', 'val$']
130
+
131
+ // Keys whose value IS the default the runtime already assumes when the key is
132
+ // absent, so emitting them is pure payload.
133
+ const CONFIG_DEFAULT: Record<string, any> = {
134
+ active: true,
135
+ req: false,
136
+ reqd: false,
137
+ }
138
+
139
+ // Subtrees carrying user payload rather than schema. An active:true inside an
140
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
141
+ // keys and everything below them is passed through untouched.
142
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
143
+
144
+ function clean(o: any, dropDefaults?: boolean): any {
145
+ const prune = (node: any, defaults: boolean): any => {
146
+ if (Array.isArray(node)) {
147
+ return node.map((n: any) => prune(n, defaults))
111
148
  }
112
- return v
113
- })
149
+ if (null != node && 'object' === typeof node) {
150
+ const out: any = {}
151
+ for (const k of Object.keys(node)) {
152
+ if (MODEL_META.includes(k)) {
153
+ continue
154
+ }
155
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
156
+ continue
157
+ }
158
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
159
+ }
160
+ return out
161
+ }
162
+ return node
163
+ }
164
+ return prune(o, true === dropDefaults)
114
165
  }
115
166
 
116
167
 
@@ -67,7 +67,7 @@ const Config = cmp(async function Config(props: any) {
67
67
  name: n.name,
68
68
  op: n.op,
69
69
  relations: n.relations,
70
- }), a), {})
70
+ }, true), a), {})
71
71
 
72
72
  const config = {
73
73
  main: { name: model.const.Name },
@@ -77,13 +77,64 @@ function formatZigValue(val: any, indent: number = 0): string {
77
77
 
78
78
 
79
79
  // Deep-remove meta keys (`foo$`) from a model subtree (twin of go's clean).
80
- function clean(o: any) {
81
- return walk(clone(o), (k: any, v: any, p: any) => {
82
- if (null != k && k.endsWith('$')) {
83
- delete p[k]
80
+ // Emission-time normalisation of a model subtree (L0).
81
+ //
82
+ // Always drops jostraca's iteration metadata (`$`-suffixed keys: index$,
83
+ // key$, val$). With `dropDefaults`, also drops keys whose value IS the
84
+ // default the runtime already assumes when the key is absent, which is pure
85
+ // payload — see CONFIG_DEFAULT.
86
+ //
87
+ // Rebuilds the tree rather than mutating during a walk. The previous
88
+ // implementation walked a clone calling `delete p[k]`, but walk() assigns its
89
+ // callback's result back over the child (`setprop(out, ckey, walk(...))`), so
90
+ // the delete was undone on the way out and the helper silently did nothing.
91
+ // Returning `undefined` from the callback does not fix it either: setprop
92
+ // stores undefined rather than removing the key, which then emits as a null.
93
+ //
94
+ // `dropDefaults` is opt-in and must be passed ONLY for the entity subtree.
95
+ // `active` means something different in feature config, where absent reads as
96
+ // INACTIVE (see feature_init) — dropping `active: true` there would silently
97
+ // disable the feature.
98
+ // jostraca's iteration metadata, injected by each()/names() while it walks the
99
+ // model. Listed explicitly rather than matched by trailing-dollar suffix: a
100
+ // trailing dollar is not exclusive to jostraca -- Seneca uses entity$ as real
101
+ // data -- so a blanket suffix match can silently drop a legitimate API field.
102
+ const MODEL_META = ['index$', 'key$', 'val$']
103
+
104
+ // Keys whose value IS the default the runtime already assumes when the key is
105
+ // absent, so emitting them is pure payload.
106
+ const CONFIG_DEFAULT: Record<string, any> = {
107
+ active: true,
108
+ req: false,
109
+ reqd: false,
110
+ }
111
+
112
+ // Subtrees carrying user payload rather than schema. An active:true inside an
113
+ // OpenAPI example is DATA, not a default, so default-pruning stops at these
114
+ // keys and everything below them is passed through untouched.
115
+ const PAYLOAD_KEYS = ['default', 'example', 'examples']
116
+
117
+ function clean(o: any, dropDefaults?: boolean): any {
118
+ const prune = (node: any, defaults: boolean): any => {
119
+ if (Array.isArray(node)) {
120
+ return node.map((n: any) => prune(n, defaults))
121
+ }
122
+ if (null != node && 'object' === typeof node) {
123
+ const out: any = {}
124
+ for (const k of Object.keys(node)) {
125
+ if (MODEL_META.includes(k)) {
126
+ continue
127
+ }
128
+ if (defaults && k in CONFIG_DEFAULT && CONFIG_DEFAULT[k] === node[k]) {
129
+ continue
130
+ }
131
+ out[k] = prune(node[k], defaults && !PAYLOAD_KEYS.includes(k))
132
+ }
133
+ return out
84
134
  }
85
- return v
86
- })
135
+ return node
136
+ }
137
+ return prune(o, true === dropDefaults)
87
138
  }
88
139
 
89
140
 
@@ -40,7 +40,8 @@ Point* point_new(voxgig_value* altmap) {
40
40
  p->orig = dup_str(orig ? orig : "");
41
41
 
42
42
  p->select = to_map(getp(altmap, "select"));
43
- bool active = false;
43
+ // Absent in config means ACTIVE: emission drops `active: true` as a default (sdkgen L0), so only an explicit `active: false` turns a point off.
44
+ bool active = true;
44
45
  get_bool(altmap, "active", &active);
45
46
  p->active = active;
46
47
  p->relations = getp(altmap, "relations");
@@ -13,7 +13,9 @@ public class Point
13
13
  public List<object?> Parts = new();
14
14
  public List<object?>? Params;
15
15
  public Dictionary<string, object?>? Select;
16
- public bool Active;
16
+ // Absent in config means ACTIVE: emission drops `active: true` as a
17
+ // default (sdkgen L0), so only an explicit `active: false` turns a point off.
18
+ public bool Active = true;
17
19
  public List<object?>? Relations;
18
20
  public Dictionary<string, object?> Alias = new();
19
21
  public Dictionary<string, object?> Transform = new();
@@ -28,7 +28,8 @@ class Point {
28
28
  parts = vs.getprop(altmap, 'parts', []);
29
29
  params = vs.getprop(altmap, 'params', []);
30
30
  select = vs.getprop(altmap, 'select');
31
- active = vs.getprop(altmap, 'active', false);
31
+ // Absent in config means ACTIVE: emission drops `active: true` as a default (sdkgen L0), so only an explicit `active: false` turns a point off.
32
+ active = vs.getprop(altmap, 'active', true);
32
33
  relations = vs.getprop(altmap, 'relations', []);
33
34
  alias = vs.getprop(altmap, 'alias', {});
34
35
  transform = vs.getprop(altmap, 'transform', {'req': null, 'res': null});
@@ -25,7 +25,7 @@ import (
25
25
 
26
26
  // fhHasFeature is true when this SDK was generated with the named feature.
27
27
  func fhHasFeature(name string) bool {
28
- config := sdk.MakeConfig()
28
+ config := sdk.SharedConfig()
29
29
  fm, _ := config["feature"].(map[string]any)
30
30
  return fm != nil && fm[name] != nil
31
31
  }
@@ -127,7 +127,11 @@ func makeOptionsUtil(ctx *core.Context) map[string]any {
127
127
  sysFetch = sf
128
128
  }
129
129
 
130
- merged := vs.Merge([]any{map[string]any{}, cfgopts, opts})
130
+ // Clone the config side before merging: `config` is a process-wide
131
+ // singleton (see core.SharedConfig), and Merge would otherwise use its
132
+ // nested maps as merge TARGETS — one instance's options (server, headers,
133
+ // ...) would contaminate every instance constructed after it.
134
+ merged := vs.Merge([]any{map[string]any{}, vs.Clone(cfgopts), opts})
131
135
  validated, _ := vs.Validate(merged, optspec)
132
136
  opts = validated.(map[string]any)
133
137
 
@@ -18,7 +18,9 @@ public class Point {
18
18
  public List<Object> parts = new ArrayList<>();
19
19
  public List<Object> params;
20
20
  public Map<String, Object> select;
21
- public boolean active = false;
21
+ // Absent in config means ACTIVE: emission drops `active: true` as a
22
+ // default (sdkgen L0), so only an explicit `active: false` turns a point off.
23
+ public boolean active = true;
22
24
  public List<Object> relations;
23
25
  public Map<String, Object> alias = new LinkedHashMap<>();
24
26
  public Map<String, Object> transform = new LinkedHashMap<>();
@@ -16,7 +16,9 @@ class Point {
16
16
  this.parts = getprop(altmap, 'parts', [])
17
17
  this.params = getprop(altmap, 'params', [])
18
18
  this.select = getprop(altmap, 'select')
19
- this.active = getprop(altmap, 'active', false)
19
+ // Absent in config means ACTIVE: emission drops `active: true` as a default
20
+ // (sdkgen L0), so only an explicit `active: false` turns a point off.
21
+ this.active = getprop(altmap, 'active', true)
20
22
  this.relations = getprop(altmap, 'relations', [])
21
23
  this.alias = getprop(altmap, 'alias', {})
22
24
  this.transform = getprop(altmap, 'transform', { req: undefined, res: undefined })
@@ -13,7 +13,9 @@ class Point(pointmap: Map<String, Any?>?) {
13
13
  var parts: MutableList<Any?> = mutableListOf()
14
14
  var params: MutableList<Any?>? = null
15
15
  var select: MutableMap<String, Any?>? = null
16
- var active: Boolean = false
16
+ // Absent in config means ACTIVE: emission drops `active: true` as a
17
+ // default (sdkgen L0), so only an explicit `active: false` turns a point off.
18
+ var active: Boolean = true
17
19
  var relations: MutableList<Any?>? = null
18
20
  var alias: MutableMap<String, Any?> = linkedMapOf()
19
21
  var transform: MutableMap<String, Any?> = linkedMapOf()
@@ -104,7 +104,11 @@ local function make_options_util(ctx)
104
104
  -- Preserve system.fetch before merge/validate.
105
105
  local sys_fetch = vs.getpath(opts, "system.fetch")
106
106
 
107
- local merged = vs.merge({ {}, cfgopts, opts })
107
+ -- Clone the config side before merging: `config` is a per-Lua-state
108
+ -- singleton (see config_shared), and merge would otherwise use its nested
109
+ -- tables as merge TARGETS — one instance's options (server, headers, ...)
110
+ -- would contaminate every instance constructed after it.
111
+ local merged = vs.merge({ {}, vs.clone(cfgopts), opts })
108
112
  local validated = vs.validate(merged, optspec)
109
113
  if type(validated) ~= "table" then
110
114
  validated = {}
@@ -149,7 +149,7 @@ class FtHarness
149
149
  public static function hasFeature(string $name): bool
150
150
  {
151
151
  if (self::$feature_config === null) {
152
- $config = ProjectNameConfig::make_config();
152
+ $config = ProjectNameConfig::shared_config();
153
153
  $f = $config['feature'] ?? [];
154
154
  self::$feature_config = is_array($f) ? $f : [];
155
155
  }
@@ -96,7 +96,11 @@ def make_options_util(ctx):
96
96
  # Preserve system.fetch before merge/validate.
97
97
  sys_fetch = vs.getpath(opts, "system.fetch")
98
98
 
99
- merged = vs.merge([{}, cfgopts, opts])
99
+ # Clone the config side before merging: `config` is a process-wide
100
+ # singleton (see config.shared_config), and merge would otherwise use its
101
+ # nested dicts as merge TARGETS — one instance's options (server, headers,
102
+ # ...) would contaminate every instance constructed after it.
103
+ merged = vs.merge([{}, vs.clone(cfgopts), opts])
100
104
  validated = vs.validate(merged, optspec)
101
105
  if not isinstance(validated, dict):
102
106
  validated = {}
@@ -14,7 +14,7 @@ from __future__ import annotations
14
14
 
15
15
  from urllib.parse import quote
16
16
 
17
- from projectname_sdk.config import make_config
17
+ from projectname_sdk.config import shared_config
18
18
  from projectname_sdk.features import _make_feature
19
19
  from projectname_sdk.core.control import ProjectNameControl
20
20
  from projectname_sdk.core.error import ProjectNameError
@@ -24,7 +24,7 @@ from projectname_sdk.core.spec import ProjectNameSpec
24
24
 
25
25
  # True when this SDK was generated with the named feature.
26
26
  def has_feature(name):
27
- feature = make_config().get("feature")
27
+ feature = shared_config().get("feature")
28
28
  return isinstance(feature, dict) and feature.get(name) is not None
29
29
 
30
30
 
@@ -15,7 +15,7 @@ require_relative "../ProjectName_sdk"
15
15
  module ProjectNameFeatureHarness
16
16
  # True when this SDK was generated with the named feature.
17
17
  def self.has_feature?(name)
18
- f = ProjectNameConfig.make_config["feature"]
18
+ f = ProjectNameConfig.shared_config["feature"]
19
19
  f.is_a?(Hash) && !f[name].nil?
20
20
  end
21
21
 
@@ -61,7 +61,11 @@ module ProjectNameUtilities
61
61
 
62
62
  sys_fetch = VoxgigStruct.getpath(opts, "system.fetch")
63
63
 
64
- merged = VoxgigStruct.merge([{}, cfgopts, opts])
64
+ # Clone the config side before merging: `config` is a process-wide
65
+ # singleton (see Config.shared_config), and merge would otherwise use its
66
+ # nested hashes as merge TARGETS — one instance's options (server,
67
+ # headers, ...) would contaminate every instance constructed after it.
68
+ merged = VoxgigStruct.merge([{}, VoxgigStruct.clone(cfgopts), opts])
65
69
  validated = VoxgigStruct.validate(merged, optspec)
66
70
  opts = validated.is_a?(Hash) ? validated : {}
67
71
 
@@ -13,7 +13,8 @@ class Point(pointmap: JMap[String, Object]) {
13
13
  var parts: JList[Object] = new ArrayList[Object]()
14
14
  var params: JList[Object] = null
15
15
  var select: JMap[String, Object] = null
16
- var active: Boolean = false
16
+ // Absent in config means ACTIVE: emission drops `active: true` as a default (sdkgen L0), so only an explicit `active: false` turns a point off.
17
+ var active: Boolean = true
17
18
  var relations: JList[Object] = null
18
19
  var alias: JMap[String, Object] = new LinkedHashMap[String, Object]()
19
20
  var transform: JMap[String, Object] = new LinkedHashMap[String, Object]()
@@ -10,7 +10,9 @@ public final class Point {
10
10
  public var parts: VList = VList()
11
11
  public var params: VList? = nil
12
12
  public var select: VMap? = nil
13
- public var active: Bool = false
13
+ // Absent in config means ACTIVE: emission drops `active: true` as a
14
+ // default (sdkgen L0), so only an explicit `active: false` turns a point off.
15
+ public var active: Bool = true
14
16
  public var relations: VList? = nil
15
17
  public var alias: VMap = VMap()
16
18
  public var transform: VMap = VMap()
@@ -30,7 +30,9 @@ class Point {
30
30
  this.parts = getprop(altmap, 'parts', [])
31
31
  this.params = getprop(altmap, 'params', [])
32
32
  this.select = getprop(altmap, 'select')
33
- this.active = getprop(altmap, 'active', false)
33
+ // Absent in config means ACTIVE: emission drops `active: true` as a default
34
+ // (sdkgen L0), so only an explicit `active: false` turns a point off.
35
+ this.active = getprop(altmap, 'active', true)
34
36
  this.relations = getprop(altmap, 'relations', [])
35
37
  this.alias = getprop(altmap, 'alias', {})
36
38
  this.transform = getprop(altmap, 'transform', { req: undefined, res: undefined })
@@ -12,7 +12,8 @@ pub const Point = struct {
12
12
  parts: Value,
13
13
  params: Value,
14
14
  select: Value,
15
- active: bool = false,
15
+ // Absent in config means ACTIVE: emission drops `active: true` as a default (sdkgen L0), so only an explicit `active: false` turns a point off.
16
+ active: bool = true,
16
17
  relations: Value,
17
18
  alias: Value,
18
19
  transform: Value,
package/src/cmp/Readme.ts CHANGED
@@ -9,6 +9,7 @@ import { ReadmeErrors } from './ReadmeErrors'
9
9
  import { ReadmeModel } from './ReadmeModel'
10
10
  import { ReadmeOptions } from './ReadmeOptions'
11
11
  import { ReadmeEntity } from './ReadmeEntity'
12
+ import { ReadmeUnions } from './ReadmeUnions'
12
13
  import { ReadmeHowto } from './ReadmeHowto'
13
14
  import { ReadmeExplanation } from './ReadmeExplanation'
14
15
  import { ReadmeRef } from './ReadmeRef'
@@ -28,6 +29,7 @@ const Readme = cmp(function Readme(props: any) {
28
29
  ReadmeModel({ target })
29
30
  ReadmeOptions({ target })
30
31
  ReadmeEntity({ target })
32
+ ReadmeUnions({ target })
31
33
  ReadmeExplanation({ target })
32
34
 
33
35
  Content(`
@@ -0,0 +1,90 @@
1
+ import { cmp, each, Content } from 'jostraca'
2
+
3
+ import {
4
+ KIT,
5
+ getModelPath
6
+ } from '../types'
7
+
8
+
9
+ // Fields whose type could not be narrowed because the API definition describes
10
+ // them with an UNTAGGED union: `oneOf`/`anyOf` branches carrying no
11
+ // `discriminator`, so the specification never states which variant a given
12
+ // value is. apidef records the widest such union it finds beneath each field
13
+ // (see ModelField.union); this section reports them.
14
+ //
15
+ // Without this, those fields read as a modelling failure — a `groups` field
16
+ // typed as a bare array looks like the generator gave up, when in fact the
17
+ // definition offers nothing to narrow it to. Saying so is the difference
18
+ // between an SDK that looks unfinished and one that documents the API it was
19
+ // given.
20
+ //
21
+ // Two-branch unions are NOT reported: `oneOf: [string, number]` is a routine
22
+ // either/or, an open type for it surprises nobody, and listing every one would
23
+ // bury the cases that matter.
24
+ const MIN_REPORTED_BRANCHES = 3
25
+
26
+
27
+ const ReadmeUnions = cmp(function ReadmeUnions(props: any) {
28
+ const { ctx$ } = props
29
+ const { model } = ctx$
30
+
31
+ const entity = getModelPath(model, `main.${KIT}.entity`)
32
+
33
+ const rows: { entity: string, field: string, branches: number, depth: number }[] = []
34
+
35
+ each(entity, (ent: any) => {
36
+ if (false === ent.active) {
37
+ return
38
+ }
39
+ each(ent.fields, (field: any) => {
40
+ const union = field?.union
41
+ if (null == union || union.branches < MIN_REPORTED_BRANCHES) {
42
+ return
43
+ }
44
+ rows.push({
45
+ entity: ent.name,
46
+ field: field.name,
47
+ branches: union.branches,
48
+ depth: union.depth,
49
+ })
50
+ })
51
+ })
52
+
53
+ if (0 === rows.length) {
54
+ return
55
+ }
56
+
57
+ // Widest first: the worst offender is the one a reader needs to see.
58
+ rows.sort((a, b) => b.branches - a.branches || a.entity.localeCompare(b.entity) ||
59
+ a.field.localeCompare(b.field))
60
+
61
+ const plural = 1 === rows.length ? 'field is' : 'fields are'
62
+
63
+ Content(`
64
+ ## Open types
65
+
66
+ ${rows.length} ${plural} carried as open values rather than typed structures.
67
+ This follows from the API definition, not from a gap in this SDK: the
68
+ definition describes ${1 === rows.length ? 'it' : 'them'} with untagged unions —
69
+ \`oneOf\`/\`anyOf\` branches with no \`discriminator\` — so it never states which
70
+ variant a given value is. Nothing can select a branch reliably, so the SDK
71
+ passes the value through unchanged rather than assert a shape the API does not
72
+ guarantee.
73
+
74
+ | Entity | Field | Variants | Nesting |
75
+ | --- | --- | --- | --- |
76
+ ${rows.map((r) =>
77
+ `| \`${r.entity}\` | \`${r.field}\` | ${r.branches} | ` +
78
+ `${r.depth} ${1 === r.depth ? 'level' : 'levels'} |`).join('\n')}
79
+
80
+ These values round-trip unchanged — read them, modify them, send them back. If
81
+ the API adds a \`discriminator\` to the definition, regenerating will type them.
82
+ Every other field is typed normally.
83
+ `)
84
+ })
85
+
86
+
87
+ export {
88
+ ReadmeUnions,
89
+ MIN_REPORTED_BRANCHES,
90
+ }