@voxgig/sdkgen 4.6.0 → 4.8.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.
Files changed (144) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Feature.js +5 -0
  3. package/dist/cmp/Feature.js.map +1 -1
  4. package/dist/cmp/TestControl.d.ts +4 -0
  5. package/dist/cmp/TestControl.js +51 -0
  6. package/dist/cmp/TestControl.js.map +1 -0
  7. package/dist/helpers/featureSource.d.ts +3 -1
  8. package/dist/helpers/featureSource.js +105 -0
  9. package/dist/helpers/featureSource.js.map +1 -1
  10. package/dist/helpers/opShape.js +8 -11
  11. package/dist/helpers/opShape.js.map +1 -1
  12. package/dist/helpers/pointPath.d.ts +10 -0
  13. package/dist/helpers/pointPath.js +54 -0
  14. package/dist/helpers/pointPath.js.map +1 -0
  15. package/dist/sdkgen.d.ts +6 -3
  16. package/dist/sdkgen.js +14 -4
  17. package/dist/sdkgen.js.map +1 -1
  18. package/dist/tsconfig.tsbuildinfo +1 -1
  19. package/dist/utility.d.ts +2 -1
  20. package/dist/utility.js +78 -1
  21. package/dist/utility.js.map +1 -1
  22. package/model/sdkgen.aon +58 -0
  23. package/package.json +2 -2
  24. package/project/.sdk/model/feature/secrets.aon +82 -0
  25. package/project/.sdk/src/cmp/c/Config_c.ts +7 -8
  26. package/project/.sdk/src/cmp/c/TestDirect_c.ts +4 -3
  27. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
  28. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +4 -3
  29. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
  30. package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
  31. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +34 -9
  32. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
  33. package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
  34. package/project/.sdk/src/cmp/dart/Config_dart.ts +7 -8
  35. package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
  36. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +28 -10
  37. package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
  38. package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
  39. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +9 -8
  40. package/project/.sdk/src/cmp/go/Config_go.ts +1 -7
  41. package/project/.sdk/src/cmp/go/Main_go.ts +2 -1
  42. package/project/.sdk/src/cmp/go/TestDirect_go.ts +5 -4
  43. package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
  44. package/project/.sdk/src/cmp/java/Config_java.ts +7 -7
  45. package/project/.sdk/src/cmp/java/Main_java.ts +2 -1
  46. package/project/.sdk/src/cmp/java/TestDirect_java.ts +5 -4
  47. package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
  48. package/project/.sdk/src/cmp/js/Config_js.ts +7 -8
  49. package/project/.sdk/src/cmp/js/Main_js.ts +2 -1
  50. package/project/.sdk/src/cmp/js/TestDirect_js.ts +34 -8
  51. package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
  52. package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
  53. package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
  54. package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
  55. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +7 -7
  56. package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
  57. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +5 -4
  58. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
  59. package/project/.sdk/src/cmp/lean/Config_lean.ts +9 -8
  60. package/project/.sdk/src/cmp/lua/Config_lua.ts +1 -7
  61. package/project/.sdk/src/cmp/lua/Main_lua.ts +2 -1
  62. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +32 -7
  63. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
  64. package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
  65. package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
  66. package/project/.sdk/src/cmp/perl/Main_perl.ts +2 -1
  67. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +29 -7
  68. package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
  69. package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
  70. package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
  71. package/project/.sdk/src/cmp/php/Config_php.ts +1 -7
  72. package/project/.sdk/src/cmp/php/Main_php.ts +2 -1
  73. package/project/.sdk/src/cmp/php/TestDirect_php.ts +28 -8
  74. package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
  75. package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
  76. package/project/.sdk/src/cmp/py/Config_py.ts +1 -7
  77. package/project/.sdk/src/cmp/py/Main_py.ts +2 -1
  78. package/project/.sdk/src/cmp/py/TestDirect_py.ts +5 -4
  79. package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
  80. package/project/.sdk/src/cmp/rb/Config_rb.ts +1 -7
  81. package/project/.sdk/src/cmp/rb/Main_rb.ts +2 -1
  82. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +28 -8
  83. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
  84. package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
  85. package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
  86. package/project/.sdk/src/cmp/rust/Main_rust.ts +2 -1
  87. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +35 -7
  88. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
  89. package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
  90. package/project/.sdk/src/cmp/scala/Config_scala.ts +9 -7
  91. package/project/.sdk/src/cmp/scala/Main_scala.ts +2 -1
  92. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +4 -3
  93. package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
  94. package/project/.sdk/src/cmp/seneca-provider/Extras_seneca-provider.ts +3 -1
  95. package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +9 -5
  96. package/project/.sdk/src/cmp/ts/Config_ts.ts +57 -16
  97. package/project/.sdk/src/cmp/ts/Main_ts.ts +10 -2
  98. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +15 -7
  99. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +11 -3
  100. package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
  101. package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
  102. package/project/.sdk/tm/dart/test/utility.dart +39 -0
  103. package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
  104. package/project/.sdk/tm/js/test/utility.js +91 -1
  105. package/project/.sdk/tm/lua/test/runner.lua +40 -0
  106. package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
  107. package/project/.sdk/tm/perl/t/runner.pm +30 -0
  108. package/project/.sdk/tm/php/test/Runner.php +36 -0
  109. package/project/.sdk/tm/rb/test/runner.rb +25 -0
  110. package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
  111. package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
  112. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +8 -2
  113. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +29 -5
  114. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +30 -21
  115. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
  116. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
  117. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
  118. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
  119. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
  120. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
  121. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
  122. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
  123. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
  124. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
  125. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
  126. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
  127. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
  128. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
  129. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
  130. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
  131. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +15 -13
  132. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +25 -19
  133. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +1 -1
  134. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
  135. package/project/.sdk/tm/zig/core/utility.zig +1 -1
  136. package/project/sdkgen-package.json +1 -1
  137. package/src/cmp/Feature.ts +5 -0
  138. package/src/cmp/TestControl.ts +64 -0
  139. package/src/helpers/featureSource.ts +130 -0
  140. package/src/helpers/opShape.ts +9 -14
  141. package/src/helpers/pointPath.ts +88 -0
  142. package/src/sdkgen.ts +18 -0
  143. package/src/utility.ts +92 -1
  144. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +0 -1221
@@ -153,6 +153,51 @@ public static class TestRunner
153
153
  return _testControl;
154
154
  }
155
155
 
156
+ // LiveClientOptions returns the extra SDK options every LIVE client is
157
+ // constructed with, read from sdk-test-control.json `test.client.options`.
158
+ //
159
+ // The generated live client knows two things: the base URL (from the
160
+ // spec) and the credential (from the environment). Everything else about
161
+ // how a particular API wants to be talked to - which features to switch
162
+ // on, and with what settings - is a property of THAT API, known to the
163
+ // project and to nothing in the toolchain.
164
+ //
165
+ // Merged UNDER the generated fields, so the suite's own
166
+ // base/apikey/server values win: this ADDS to the live client, it does
167
+ // not redirect it.
168
+ //
169
+ // Reserved fields are stripped HERE rather than at each merge site: the
170
+ // generated dictionary only names a field when the model calls for one,
171
+ // so a "base" in this block would face no competing value and would
172
+ // silently redirect the whole suite - credential included - to another
173
+ // host.
174
+ private static readonly string[] LiveReserved =
175
+ { "base", "prefix", "suffix", "server", "apikey", "secret" };
176
+
177
+ public static Dictionary<string, object?> LiveClientOptions()
178
+ {
179
+ var ctrl = LoadTestControl();
180
+ if (!ctrl.TryGetValue("test", out var testRaw) ||
181
+ testRaw is not Dictionary<string, object?> test ||
182
+ !test.TryGetValue("client", out var clientRaw) ||
183
+ clientRaw is not Dictionary<string, object?> client ||
184
+ !client.TryGetValue("options", out var optsRaw) ||
185
+ optsRaw is not Dictionary<string, object?> opts)
186
+ {
187
+ return new Dictionary<string, object?>();
188
+ }
189
+
190
+ var out_ = new Dictionary<string, object?>();
191
+ foreach (var kv in opts)
192
+ {
193
+ if (Array.IndexOf(LiveReserved, kv.Key) < 0)
194
+ {
195
+ out_[kv.Key] = kv.Value;
196
+ }
197
+ }
198
+ return out_;
199
+ }
200
+
156
201
  // IsControlSkipped checks sdk-test-control.json for a skip entry.
157
202
  // Returns (skip, reason).
158
203
  public static (bool, string) IsControlSkipped(string kind, string name, string mode)
@@ -143,6 +143,45 @@ bool skipIfMissingIds(T t, Map setup, List<String> requiredKeys) {
143
143
 
144
144
  // Per-test live pacing delay (ms). Read from sdk-test-control.json
145
145
  // `test.live.delayMs`; defaults to 500ms if absent or invalid.
146
+ // Extra SDK options every LIVE client is constructed with, read from
147
+ // sdk-test-control.json `test.client.options`.
148
+ //
149
+ // The generated live client knows two things: the base URL (from the spec)
150
+ // and the credential (from the environment). Everything else about how a
151
+ // particular API wants to be talked to - which features to switch on, and
152
+ // with what settings - is a property of THAT API, known to the project and
153
+ // to nothing in the toolchain.
154
+ //
155
+ // Merged UNDER the generated fields, so the suite's own base/apikey/server
156
+ // values win: this ADDS to the live client, it does not redirect it.
157
+ //
158
+ // Reserved fields are stripped HERE rather than at each merge site: the
159
+ // generated map only names a field when the model calls for one, so a
160
+ // 'base' in this block would face no competing value and would silently
161
+ // redirect the whole suite - credential included - to another host.
162
+ const List<String> liveReserved = [
163
+ 'base', 'prefix', 'suffix', 'server', 'apikey', 'secret'
164
+ ];
165
+
166
+ Map<String, dynamic> liveClientOptions() {
167
+ final ctrl = loadTestControl();
168
+ final t = ctrl['test'];
169
+ if (t is! Map) return <String, dynamic>{};
170
+ final c = t['client'];
171
+ if (c is! Map) return <String, dynamic>{};
172
+ final opts = c['options'];
173
+ if (opts is! Map) return <String, dynamic>{};
174
+
175
+ final out = <String, dynamic>{};
176
+ opts.forEach((k, v) {
177
+ if (!liveReserved.contains(k)) {
178
+ out['$k'] = v;
179
+ }
180
+ });
181
+ return out;
182
+ }
183
+
184
+
146
185
  int liveDelayMs() {
147
186
  final ctrl = loadTestControl();
148
187
  final t = ctrl['test'];
@@ -0,0 +1,19 @@
1
+ {
2
+ "version": 1,
3
+ "_doc": "Per-SDK test control. Lists tests/operations to skip in unit and live modes; tunes per-test pacing. Edit by hand. Loaded by js/test/utility.js.",
4
+ "test": {
5
+ "skip": {
6
+ "live": {
7
+ "direct": [],
8
+ "entityOp": []
9
+ },
10
+ "unit": {
11
+ "direct": [],
12
+ "entityOp": []
13
+ }
14
+ },
15
+ "live": {
16
+ "delayMs": 500
17
+ }
18
+ }
19
+ }
@@ -5,6 +5,10 @@
5
5
  * for creating test data, transformations, validations, and environment overrides.
6
6
  */
7
7
 
8
+ const Fs = require('node:fs')
9
+ const Path = require('node:path')
10
+
11
+
8
12
  // Creates a new step data structure within the data model
9
13
  function makeStepData(dm, stepname) {
10
14
  dm.s[stepname] = {
@@ -62,11 +66,97 @@ function envOverride(m) {
62
66
  }
63
67
 
64
68
 
69
+ // Loads sdk-test-control.json (cached). Returns an empty-skip object if
70
+ // the file is missing or unparsable so tests never crash on a bad config.
71
+ let _testControlCache = null
72
+
73
+ function loadTestControl() {
74
+ if (_testControlCache) return _testControlCache
75
+ const ctrlPath = Path.resolve(__dirname, '../test/sdk-test-control.json')
76
+ try {
77
+ _testControlCache = JSON.parse(Fs.readFileSync(ctrlPath, 'utf8'))
78
+ }
79
+ catch {
80
+ _testControlCache = {
81
+ version: 1,
82
+ test: { skip: { live: { direct: [], entityOp: [] }, unit: { direct: [], entityOp: [] } } }
83
+ }
84
+ }
85
+ return _testControlCache
86
+ }
87
+
88
+
89
+ // Extra SDK options every LIVE client is constructed with, read from
90
+ // sdk-test-control.json `test.client.options`.
91
+ //
92
+ // The generated live client knows two things: the base URL (from the spec)
93
+ // and the credential (from the environment). Everything else about how a
94
+ // particular API wants to be talked to - which features to switch on, and
95
+ // with what settings - is a property of THAT API, known to the project and
96
+ // to nothing in the toolchain. So it lives in the file the project owns and
97
+ // describes, where it can be read and reviewed. Secrets still come from the
98
+ // environment (the providers this block names read them).
99
+ //
100
+ // Merged UNDER the generated fields, so the suite's own base/apikey/server
101
+ // values win - this adds to the live client, it does not redirect it.
102
+ //
103
+ // That contract is enforced HERE rather than left to each merge site: the
104
+ // generated object only names a field when the model calls for one, so a
105
+ // `base` in this block would face no competing value and would silently
106
+ // redirect the whole suite - credential included - to another host. The
107
+ // reserved fields are stripped once, where the block is read, so every
108
+ // caller gets the same guarantee whether or not it happens to emit them.
109
+ const LIVE_RESERVED = ['base', 'prefix', 'suffix', 'server', 'apikey', 'secret']
110
+
111
+ function liveClientOptions() {
112
+ const ctrl = loadTestControl()
113
+ const opts = ctrl && ctrl.test && ctrl.test.client && ctrl.test.client.options
114
+
115
+ if (null == opts || 'object' !== typeof opts) {
116
+ return {}
117
+ }
118
+
119
+ const out = {}
120
+ for (const key of Object.keys(opts)) {
121
+ if (!LIVE_RESERVED.includes(key)) {
122
+ out[key] = opts[key]
123
+ }
124
+ }
125
+
126
+ return out
127
+ }
128
+
129
+
130
+ // Per-test live pacing delay (ms). Read from sdk-test-control.json
131
+ // `test.live.delayMs`; defaults to 500ms if absent or invalid.
132
+ function liveDelayMs() {
133
+ const ctrl = loadTestControl()
134
+ const v = ctrl && ctrl.test && ctrl.test.live && ctrl.test.live.delayMs
135
+ return ('number' === typeof v && v >= 0) ? v : 500
136
+ }
137
+
138
+
139
+
140
+ // afterEach hook helper for live pacing. Generated tests register this via
141
+ // `afterEach(liveDelay(<envVar>))`; it sleeps `liveDelayMs()` only when the
142
+ // SDK's *_TEST_LIVE env var is set.
143
+ function liveDelay(liveEnvVar) {
144
+ return async () => {
145
+ if ('TRUE' === process.env[liveEnvVar]) {
146
+ await new Promise(r => setTimeout(r, liveDelayMs()))
147
+ }
148
+ }
149
+ }
150
+
65
151
  module.exports = {
66
152
  makeStepData,
67
153
  makeMatch,
68
154
  makeReqdata,
69
155
  makeValid,
70
156
  makeCtrl,
71
- envOverride
157
+ envOverride,
158
+ loadTestControl,
159
+ liveClientOptions,
160
+ liveDelayMs,
161
+ liveDelay
72
162
  }
@@ -146,6 +146,46 @@ function runner.is_control_skipped(kind, name, mode)
146
146
  end
147
147
 
148
148
 
149
+ -- Extra SDK options every LIVE client is constructed with, read from
150
+ -- sdk-test-control.json `test.client.options`.
151
+ --
152
+ -- The generated live client knows two things: the base URL (from the spec)
153
+ -- and the credential (from the environment). Everything else about how a
154
+ -- particular API wants to be talked to - which features to switch on, and
155
+ -- with what settings - is a property of THAT API, known to the project and
156
+ -- to nothing in the toolchain.
157
+ --
158
+ -- Merged UNDER the generated fields, so the suite's own base/apikey/server
159
+ -- values win: this ADDS to the live client, it does not redirect it.
160
+ --
161
+ -- Reserved fields are stripped HERE rather than at each merge site: the
162
+ -- generated table only names a field when the model calls for one, so a
163
+ -- "base" in this block would face no competing value and would silently
164
+ -- redirect the whole suite - credential included - to another host.
165
+ local LIVE_RESERVED = {
166
+ base = true, prefix = true, suffix = true,
167
+ server = true, apikey = true, secret = true,
168
+ }
169
+
170
+ function runner.live_client_options()
171
+ local ctrl = runner.load_test_control()
172
+ local test = ctrl.test
173
+ if type(test) ~= "table" then return {} end
174
+ local client = test.client
175
+ if type(client) ~= "table" then return {} end
176
+ local opts = client.options
177
+ if type(opts) ~= "table" then return {} end
178
+
179
+ local out = {}
180
+ for k, v in pairs(opts) do
181
+ if not LIVE_RESERVED[k] then
182
+ out[k] = v
183
+ end
184
+ end
185
+ return out
186
+ end
187
+
188
+
149
189
  -- Per-test live pacing delay (ms); default 500.
150
190
  function runner.live_delay_ms()
151
191
  local ctrl = runner.load_test_control()
@@ -93,14 +93,24 @@ let make_client ?(server : server option) ?(mode = "test") ?(base = "http://api.
93
93
  rootctx.c_op <- new_operation (jo [("name", Str "root"); ("entity", Str "_")]);
94
94
  client.cl_rootctx <- Some rootctx;
95
95
  List.iter (fun (name, opts) ->
96
- if has_feature name then begin
97
- let ftr = Sdk_config.make_feature name in
96
+ (* `netsim` is a test-only network simulator implemented in
97
+ Sdk_features, not a model feature, so it never appears in the
98
+ generated config and has_feature rejects it. Construct it directly:
99
+ without this every netsim-driven test (retry backoff, timeout,
100
+ and the metrics/telemetry/audit error paths) silently runs against
101
+ an unsimulated transport and sees a plain 200. *)
102
+ let ftr =
103
+ if name = "netsim" then Some (Sdk_features.netsim_feature ())
104
+ else if has_feature name then Some (Sdk_config.make_feature name)
105
+ else None in
106
+ match ftr with
107
+ | None -> ()
108
+ | Some ftr ->
98
109
  let fopts = jo [("active", Bool true)] in
99
110
  (match opts with Map _ -> List.iter (fun k -> setp fopts k (getp opts k)) (keysof opts) | _ -> ());
100
111
  setp (getp client.cl_options "feature") name fopts;
101
112
  ftr.f_init rootctx fopts;
102
- client.cl_features <- client.cl_features @ [ftr]
103
- end) features;
113
+ client.cl_features <- client.cl_features @ [ftr]) features;
104
114
  feature_hook_util rootctx "PostConstruct";
105
115
  { h_base = base; h_headers = hheaders; h_client = client; h_utility = utility; h_rootctx = rootctx }
106
116
 
@@ -129,6 +129,36 @@ sub is_control_skipped {
129
129
  return (0, undef);
130
130
  }
131
131
 
132
+ # Extra SDK options every LIVE client is constructed with, read from
133
+ # sdk-test-control.json `test.client.options`.
134
+ #
135
+ # The generated live client knows two things: the base URL (from the spec)
136
+ # and the credential (from the environment). Everything else about how a
137
+ # particular API wants to be talked to - which features to switch on, and
138
+ # with what settings - is a property of THAT API, known to the project and
139
+ # to nothing in the toolchain.
140
+ #
141
+ # Merged UNDER the generated fields, so the suite's own base/apikey/server
142
+ # values win: this ADDS to the live client, it does not redirect it.
143
+ #
144
+ # Reserved fields are stripped HERE rather than at each merge site: the
145
+ # generated hash only names a field when the model calls for one, so a
146
+ # "base" in this block would face no competing value and would silently
147
+ # redirect the whole suite - credential included - to another host.
148
+ my %LIVE_RESERVED = map { $_ => 1 }
149
+ qw(base prefix suffix server apikey secret);
150
+
151
+ sub live_client_options {
152
+ my $ctrl = load_test_control();
153
+ my $opts = ProjectNameHelpers::gpath($ctrl, 'test.client.options');
154
+ return {} unless Voxgig::Struct::ismap($opts);
155
+ my %out;
156
+ for my $k (keys %$opts) {
157
+ $out{$k} = $opts->{$k} unless $LIVE_RESERVED{$k};
158
+ }
159
+ return \%out;
160
+ }
161
+
132
162
  # Per-test live pacing delay (ms); default 500.
133
163
  sub live_delay_ms {
134
164
  my $ctrl = load_test_control();
@@ -138,6 +138,42 @@ class ProjectNameTestRunner
138
138
  return [false, null];
139
139
  }
140
140
 
141
+ /**
142
+ * Extra SDK options every LIVE client is constructed with, read from
143
+ * sdk-test-control.json `test.client.options`.
144
+ *
145
+ * The generated live client knows two things: the base URL (from the
146
+ * spec) and the credential (from the environment). Everything else
147
+ * about how a particular API wants to be talked to - which features to
148
+ * switch on, and with what settings - is a property of THAT API, known
149
+ * to the project and to nothing in the toolchain.
150
+ *
151
+ * Merged UNDER the generated fields, so the suite's own
152
+ * base/apikey/server values win: this ADDS to the live client, it does
153
+ * not redirect it.
154
+ *
155
+ * Reserved fields are stripped HERE rather than at each merge site: the
156
+ * generated array only names a field when the model calls for one, so a
157
+ * "base" in this block would face no competing value and would silently
158
+ * redirect the whole suite - credential included - to another host.
159
+ */
160
+ public static function live_client_options(): array
161
+ {
162
+ $ctrl = self::load_test_control();
163
+ $opts = $ctrl['test']['client']['options'] ?? null;
164
+ if (!is_array($opts)) {
165
+ return [];
166
+ }
167
+ $reserved = ['base', 'prefix', 'suffix', 'server', 'apikey', 'secret'];
168
+ $out = [];
169
+ foreach ($opts as $k => $v) {
170
+ if (!in_array($k, $reserved, true)) {
171
+ $out[$k] = $v;
172
+ }
173
+ }
174
+ return $out;
175
+ }
176
+
141
177
  /** Per-test live pacing delay (ms); default 500. */
142
178
  public static function live_delay_ms(): int
143
179
  {
@@ -101,6 +101,31 @@ module ProjectNameTestRunner
101
101
  [false, nil]
102
102
  end
103
103
 
104
+ # Extra SDK options every LIVE client is constructed with, read from
105
+ # sdk-test-control.json `test.client.options`.
106
+ #
107
+ # The generated live client knows two things: the base URL (from the spec)
108
+ # and the credential (from the environment). Everything else about how a
109
+ # particular API wants to be talked to - which features to switch on, and
110
+ # with what settings - is a property of THAT API, known to the project and
111
+ # to nothing in the toolchain.
112
+ #
113
+ # Merged UNDER the generated fields, so the suite's own base/apikey/server
114
+ # values win: this ADDS to the live client, it does not redirect it.
115
+ #
116
+ # Reserved fields are stripped HERE rather than at each merge site: the
117
+ # generated hash only names a field when the model calls for one, so a
118
+ # "base" in this block would face no competing value and would silently
119
+ # redirect the whole suite - credential included - to another host.
120
+ LIVE_RESERVED = %w[base prefix suffix server apikey secret].freeze
121
+
122
+ def self.live_client_options
123
+ ctrl = load_test_control
124
+ opts = ctrl.dig('test', 'client', 'options')
125
+ return {} unless opts.is_a?(Hash)
126
+ opts.reject { |k, _v| LIVE_RESERVED.include?(k) }
127
+ end
128
+
104
129
  # Per-test live pacing delay (ms); default 500.
105
130
  def self.live_delay_ms
106
131
  ctrl = load_test_control
@@ -93,6 +93,38 @@ pub fn is_control_skipped(kind: &str, name: &str, mode: &str) -> (bool, String)
93
93
  (false, String::new())
94
94
  }
95
95
 
96
+ /// Extra SDK options every LIVE client is constructed with, read from
97
+ /// sdk-test-control.json `test.client.options`.
98
+ ///
99
+ /// The generated live client knows two things: the base URL (from the spec)
100
+ /// and the credential (from the environment). Everything else about how a
101
+ /// particular API wants to be talked to - which features to switch on, and
102
+ /// with what settings - is a property of THAT API, known to the project and
103
+ /// to nothing in the toolchain.
104
+ ///
105
+ /// Merged UNDER the generated fields, so the suite's own base/apikey/server
106
+ /// values win: this ADDS to the live client, it does not redirect it.
107
+ ///
108
+ /// Reserved fields are stripped HERE rather than at each merge site: the
109
+ /// generated map only names a field when the model calls for one, so a
110
+ /// "base" in this block would face no competing value and would silently
111
+ /// redirect the whole suite - credential included - to another host.
112
+ pub const LIVE_RESERVED: [&str; 6] =
113
+ ["base", "prefix", "suffix", "server", "apikey", "secret"];
114
+
115
+ pub fn live_client_options() -> Value {
116
+ let ctrl = load_test_control();
117
+ let out = Value::empty_map();
118
+ if let Value::Map(m) = get_spec(&ctrl, &["test", "client", "options"]) {
119
+ for (k, v) in m.borrow().iter() {
120
+ if !LIVE_RESERVED.contains(&k.as_str()) {
121
+ setp(&out, k, v.clone());
122
+ }
123
+ }
124
+ }
125
+ out
126
+ }
127
+
96
128
  /// Configured per-test live delay in ms; default 500.
97
129
  pub fn live_delay_ms() -> i64 {
98
130
  let ctrl = load_test_control();
@@ -78,7 +78,7 @@ pub fn make_options_util(ctx: &Rc<Context>) -> Value {
78
78
  ("base", Value::str("http://localhost:8000")),
79
79
  ("prefix", Value::str("")),
80
80
  ("suffix", Value::str("")),
81
- ("auth", jo(vec![("prefix", Value::str(""))])),
81
+ ("auth", jo(vec![("prefix", Value::str("")), ("basic", Value::Bool(false))])),
82
82
  ("headers", jo(vec![("`$CHILD`", Value::str("`$STRING`"))])),
83
83
  (
84
84
  "allow",
@@ -1,5 +1,5 @@
1
1
  // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/Sekreto.ts)
2
- // Source: https://github.com/voxgig/sekreto @ a8c293be1b6c33d65223b2b2275797c241b1a1f1
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
3
  // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
4
  // sekreto: one interface for secrets, wherever they live.
5
5
  //
@@ -11,7 +11,13 @@
11
11
  // This file is CANONICAL. Every other port is a translation of it, and
12
12
  // spec/sekreto.json is the behavioural contract they all run.
13
13
 
14
- import { Provider, ProviderSpec, makeprovider } from './Providers'
14
+ // The REGISTRY, never './Providers'. Importing the barrel here is what
15
+ // made every provider kind reachable from the core: the chain needs
16
+ // `makeprovider`, and the barrel's edge dragged AWS request signing and
17
+ // seven HTTP clients in behind it. The registry knows only the kinds
18
+ // something actually imported. See docs/design/plugin-providers.md.
19
+ import { Provider, ProviderSpec } from './provider/support'
20
+ import { makeprovider } from './provider/Registry'
15
21
 
16
22
  /** A secret name: dot-separated lowercase segments, e.g. `api.token`. */
17
23
  export type Name = string
@@ -1,5 +1,5 @@
1
1
  // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/Sigv4.ts)
2
- // Source: https://github.com/voxgig/sekreto @ a8c293be1b6c33d65223b2b2275797c241b1a1f1
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
3
  // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
4
  // AWS Signature Version 4, hand-rolled.
5
5
  //
@@ -14,7 +14,28 @@
14
14
  // carry known-answer cases that all ten ports must reproduce bit-for-bit,
15
15
  // and lets the integration mock recompute the signature server-side.
16
16
 
17
- import { createHash, createHmac } from 'node:crypto'
17
+ // node:crypto is loaded ON FIRST USE, not at import time.
18
+ //
19
+ // Providers.ts imports sigv4 for the AWS providers, and Sekreto.ts imports
20
+ // Providers — so a top-level `node:crypto` here put it in the module graph
21
+ // of every sekreto consumer, including one that never signs anything.
22
+ // Deferring keeps this module pure until an AWS provider actually runs.
23
+ //
24
+ // require(), not `await import()`: these helpers are synchronous and are
25
+ // called from synchronous signing code. The package is CommonJS.
26
+ function crypto(): typeof import('node:crypto') {
27
+ try {
28
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
29
+ return require('node:crypto')
30
+ } catch (err: any) {
31
+ throw new Error(
32
+ 'sekreto: AWS request signing needs node:crypto, which this runtime ' +
33
+ 'does not provide: ' +
34
+ err.message,
35
+ { cause: err },
36
+ )
37
+ }
38
+ }
18
39
 
19
40
  export type Sigv4Input = {
20
41
  method: string
@@ -39,11 +60,11 @@ export type Sigv4Input = {
39
60
  export type Sigv4Output = Record<string, string>
40
61
 
41
62
  function sha256hex(text: string): string {
42
- return createHash('sha256').update(text, 'utf8').digest('hex')
63
+ return crypto().createHash('sha256').update(text, 'utf8').digest('hex')
43
64
  }
44
65
 
45
66
  function hmac(key: Buffer | string, text: string): Buffer {
46
- return createHmac('sha256', key).update(text, 'utf8').digest()
67
+ return crypto().createHmac('sha256', key).update(text, 'utf8').digest()
47
68
  }
48
69
 
49
70
  /** RFC 3986 escaping, which is stricter than encodeURIComponent: AWS
@@ -128,7 +149,10 @@ export function sigv4(input: Sigv4Input): Sigv4Output {
128
149
  const kregion = hmac(kdate, input.region)
129
150
  const kservice = hmac(kregion, input.service)
130
151
  const ksigning = hmac(kservice, 'aws4_request')
131
- const signature = createHmac('sha256', ksigning).update(stringtosign, 'utf8').digest('hex')
152
+ const signature = crypto()
153
+ .createHmac('sha256', ksigning)
154
+ .update(stringtosign, 'utf8')
155
+ .digest('hex')
132
156
 
133
157
  const out: Sigv4Output = {
134
158
  authorization:
@@ -1,5 +1,5 @@
1
1
  // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/index.ts)
2
- // Source: https://github.com/voxgig/sekreto @ a8c293be1b6c33d65223b2b2275797c241b1a1f1
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
3
  // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
4
  // @voxgig/sekreto - one interface for secrets, wherever they live.
5
5
 
@@ -18,25 +18,34 @@ export {
18
18
 
19
19
  export type { Name, SekretoOptions } from './Sekreto'
20
20
 
21
- export {
22
- awsparamsprovider,
23
- awssecretsprovider,
24
- azuresecretsprovider,
25
- boruprovider,
26
- checkaddr,
27
- dopplerprovider,
28
- dotenvprovider,
29
- envprovider,
30
- fileprovider,
31
- gcpsecretsprovider,
32
- hashicorpprovider,
33
- infisicalprovider,
34
- makeprovider,
35
- memoryprovider,
36
- onepasswordprovider,
37
- } from './Providers'
21
+ // THE CORE SURFACE. Deliberately does NOT re-export the eleven provider
22
+ // kinds that need something of their runtime: pulling one through this
23
+ // file would make all of them reachable and put AWS request signing in
24
+ // every build, which is the thing the split removes.
25
+ //
26
+ // `env` and `memory` are here because they import nothing at all, and a
27
+ // chain with nowhere to read from is not usable or testable.
28
+ //
29
+ // Everything else registers itself when its module is imported:
30
+ //
31
+ // import '@voxgig/sekreto/provider/dotenv'
32
+ //
33
+ // or, for the old all-in behaviour, `from '@voxgig/sekreto/Providers'`.
34
+ // See docs/design/plugin-providers.md.
35
+ export { envprovider } from './provider/env'
36
+ export { memoryprovider } from './provider/memory'
37
+
38
+ // A pure validator, no platform dependency - kept on the core surface
39
+ // because callers validate an address before configuring a provider.
40
+ export { checkaddr } from './provider/addr'
41
+
42
+ export { makeprovider, register, registered, kinds } from './provider/Registry'
43
+ export type { ProviderDefinition } from './provider/Registry'
38
44
 
39
- export type { Provider, ProviderSpec } from './Providers'
45
+ export type { Provider, ProviderSpec } from './provider/support'
40
46
 
41
- export { sigv4 } from './Sigv4'
42
- export type { Sigv4Input, Sigv4Output } from './Sigv4'
47
+ // `sigv4` is NOT on the core surface: it is the node:crypto edge, and
48
+ // only the two aws providers use it. Import it from the module that
49
+ // needs it - `@voxgig/sekreto/provider/aws` - or from the full-set
50
+ // barrel. Re-exporting it here would put request signing in every
51
+ // build again, which is the thing the split removes.