@voxgig/sdkgen 3.4.7 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +78 -10
- package/dist/action/action.d.ts +5 -2
- package/dist/action/action.js +113 -6
- package/dist/action/action.js.map +1 -1
- package/dist/action/check.d.ts +15 -0
- package/dist/action/check.js +347 -0
- package/dist/action/check.js.map +1 -0
- package/dist/action/dispatch.d.ts +8 -0
- package/dist/action/dispatch.js +74 -0
- package/dist/action/dispatch.js.map +1 -0
- package/dist/action/docs.d.ts +4 -0
- package/dist/action/docs.js +236 -0
- package/dist/action/docs.js.map +1 -0
- package/dist/action/doctor.d.ts +5 -2
- package/dist/action/doctor.js +526 -74
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/feature.js +96 -17
- package/dist/action/feature.js.map +1 -1
- package/dist/action/kind.d.ts +30 -0
- package/dist/action/kind.js +258 -0
- package/dist/action/kind.js.map +1 -0
- package/dist/action/package.d.ts +25 -0
- package/dist/action/package.js +852 -0
- package/dist/action/package.js.map +1 -0
- package/dist/action/resolve.d.ts +18 -0
- package/dist/action/resolve.js +280 -0
- package/dist/action/resolve.js.map +1 -0
- package/dist/action/target.d.ts +7 -1
- package/dist/action/target.js +240 -97
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Docs.d.ts +4 -0
- package/dist/cmp/Docs.js +98 -0
- package/dist/cmp/Docs.js.map +1 -0
- package/dist/cmp/ExternalDocs.d.ts +2 -0
- package/dist/cmp/ExternalDocs.js +43 -0
- package/dist/cmp/ExternalDocs.js.map +1 -0
- package/dist/helpers/definition.d.ts +5 -0
- package/dist/helpers/definition.js +72 -0
- package/dist/helpers/definition.js.map +1 -0
- package/dist/helpers/featureSource.d.ts +8 -2
- package/dist/helpers/featureSource.js +61 -12
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/junk.d.ts +11 -0
- package/dist/helpers/junk.js +149 -0
- package/dist/helpers/junk.js.map +1 -0
- package/dist/helpers/manifest.d.ts +50 -0
- package/dist/helpers/manifest.js +328 -0
- package/dist/helpers/manifest.js.map +1 -0
- package/dist/helpers/modelcheck.d.ts +22 -0
- package/dist/helpers/modelcheck.js +203 -0
- package/dist/helpers/modelcheck.js.map +1 -0
- package/dist/helpers/semver.d.ts +4 -0
- package/dist/helpers/semver.js +174 -0
- package/dist/helpers/semver.js.map +1 -0
- package/dist/helpers/shipped.d.ts +4 -0
- package/dist/helpers/shipped.js +36 -0
- package/dist/helpers/shipped.js.map +1 -0
- package/dist/helpers/stdrep.d.ts +9 -1
- package/dist/helpers/stdrep.js +22 -0
- package/dist/helpers/stdrep.js.map +1 -1
- package/dist/sdkgen.d.ts +15 -3
- package/dist/sdkgen.js +233 -67
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/utility.js +17 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +69 -0
- package/package.json +3 -2
- package/project/.sdk/model/feature/audit.aontu +1 -0
- package/project/.sdk/model/feature/cache.aontu +1 -0
- package/project/.sdk/model/feature/clienttrack.aontu +1 -0
- package/project/.sdk/model/feature/debug.aontu +1 -0
- package/project/.sdk/model/feature/idempotency.aontu +1 -0
- package/project/.sdk/model/feature/log.aontu +1 -0
- package/project/.sdk/model/feature/metrics.aontu +1 -0
- package/project/.sdk/model/feature/netsim.aontu +1 -0
- package/project/.sdk/model/feature/paging.aontu +1 -0
- package/project/.sdk/model/feature/proxy.aontu +1 -0
- package/project/.sdk/model/feature/ratelimit.aontu +1 -0
- package/project/.sdk/model/feature/rbac.aontu +1 -0
- package/project/.sdk/model/feature/retry.aontu +1 -0
- package/project/.sdk/model/feature/streaming.aontu +1 -0
- package/project/.sdk/model/feature/telemetry.aontu +1 -0
- package/project/.sdk/model/feature/test.aontu +1 -0
- package/project/.sdk/model/feature/timeout.aontu +1 -0
- package/project/.sdk/model/target/c.aontu +1 -0
- package/project/.sdk/model/target/clojure.aontu +1 -0
- package/project/.sdk/model/target/cpp.aontu +1 -0
- package/project/.sdk/model/target/dart.aontu +1 -0
- package/project/.sdk/model/target/elixir.aontu +1 -0
- package/project/.sdk/model/target/go-cli.aontu +1 -0
- package/project/.sdk/model/target/go-mcp.aontu +1 -0
- package/project/.sdk/model/target/go.aontu +1 -0
- package/project/.sdk/model/target/haskell.aontu +1 -0
- package/project/.sdk/model/target/java.aontu +1 -0
- package/project/.sdk/model/target/js.aontu +1 -0
- package/project/.sdk/model/target/kotlin.aontu +1 -0
- package/project/.sdk/model/target/lean.aontu +1 -0
- package/project/.sdk/model/target/lua.aontu +1 -0
- package/project/.sdk/model/target/ocaml.aontu +1 -0
- package/project/.sdk/model/target/perl.aontu +1 -0
- package/project/.sdk/model/target/php.aontu +1 -0
- package/project/.sdk/model/target/py-data.aontu +1 -0
- package/project/.sdk/model/target/py.aontu +1 -0
- package/project/.sdk/model/target/rb.aontu +1 -0
- package/project/.sdk/model/target/rust.aontu +1 -0
- package/project/.sdk/model/target/scala.aontu +1 -0
- package/project/.sdk/model/target/seneca-provider.aontu +1 -0
- package/project/.sdk/model/target/zig.aontu +1 -0
- package/project/.sdk/src/cmp/c/Config_c.ts +44 -0
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +3 -1
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +37 -42
- package/project/.sdk/src/cmp/clojure/utility_clojure.ts +42 -0
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +18 -0
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +125 -52
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +3 -1
- package/project/.sdk/src/cmp/csharp/utility_csharp.ts +94 -6
- package/project/.sdk/src/cmp/dart/Config_dart.ts +43 -5
- package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +74 -0
- package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +6 -9
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +16 -5
- package/project/.sdk/src/cmp/dart/utility_dart.ts +18 -0
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +128 -13
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +3 -1
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +6 -0
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +35 -52
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +1 -0
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +1 -1
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +15 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +21 -0
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +15 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +42 -39
- package/project/.sdk/src/cmp/perl/Config_perl.ts +15 -0
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +44 -0
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +4 -2
- package/project/.sdk/src/cmp/scala/Config_scala.ts +15 -0
- package/project/.sdk/src/cmp/swift/Config_swift.ts +17 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +3 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +44 -0
- package/project/.sdk/src/cmp/zig/TestEntity_zig.ts +9 -2
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +3 -1
- package/project/.sdk/tm/c/core/sdk.h +1 -0
- package/project/.sdk/tm/c/utility/make_options.c +5 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +51 -3
- package/project/.sdk/tm/cpp/core/types.hpp +4 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +5 -1
- package/project/.sdk/tm/csharp/test/PipelineTest.cs +15 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +67 -1
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +43 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +58 -2
- package/project/.sdk/tm/haskell/{test/TestJson.hs → src/SdkJson.hs} +8 -4
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +55 -2
- package/project/.sdk/tm/haskell/test/Runner.hs +1 -1
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +1 -1
- package/project/.sdk/tm/java/core/SdkClient.java +3 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +5 -1
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +3 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +5 -1
- package/project/.sdk/tm/ocaml/Makefile +3 -2
- package/project/.sdk/tm/ocaml/sdk_json.ml +106 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +56 -0
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +4 -86
- package/project/.sdk/tm/perl/utility/make_options.pm +5 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +79 -1
- package/project/.sdk/tm/scala/core/SdkClient.scala +3 -1
- package/project/.sdk/tm/scala/utility/Make.scala +5 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +5 -1
- package/project/.sdk/tm/zig/core/utility.zig +85 -1
- package/project/sdkgen-package.json +57 -0
- package/src/action/action.ts +136 -6
- package/src/action/check.ts +495 -0
- package/src/action/dispatch.ts +105 -0
- package/src/action/docs.ts +310 -0
- package/src/action/doctor.ts +692 -81
- package/src/action/feature.ts +109 -17
- package/src/action/kind.ts +375 -0
- package/src/action/package.ts +1173 -0
- package/src/action/resolve.ts +362 -0
- package/src/action/target.ts +290 -108
- package/src/cmp/Docs.ts +125 -0
- package/src/cmp/ExternalDocs.ts +54 -0
- package/src/helpers/definition.ts +82 -0
- package/src/helpers/featureSource.ts +83 -13
- package/src/helpers/junk.ts +168 -0
- package/src/helpers/manifest.ts +474 -0
- package/src/helpers/modelcheck.ts +262 -0
- package/src/helpers/semver.ts +211 -0
- package/src/helpers/shipped.ts +41 -0
- package/src/helpers/stdrep.ts +70 -0
- package/src/sdkgen.ts +294 -66
- package/src/types.ts +33 -3
- package/src/utility.ts +17 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import '../ProjectNameError.dart';
|
|
2
|
+
|
|
1
3
|
import 'voxgig_struct.dart' as vs;
|
|
2
4
|
|
|
3
5
|
import 'FetcherUtility.dart';
|
|
@@ -55,6 +57,11 @@ dynamic makeOptions(dynamic ctx) {
|
|
|
55
57
|
'clean': {
|
|
56
58
|
'keys': 'key,token,id',
|
|
57
59
|
},
|
|
60
|
+
// Server-variable values for a templated base URL (OpenAPI server
|
|
61
|
+
// variables): {name} placeholders in "base" are substituted from this
|
|
62
|
+
// map at construction. Spec defaults arrive via the generated config;
|
|
63
|
+
// user values override them. Mirrors go's make_options optspec.
|
|
64
|
+
'server': {'`\$CHILD`': ''},
|
|
58
65
|
};
|
|
59
66
|
|
|
60
67
|
// Dart specific: preserve the (function-valued) system.fetch across
|
|
@@ -95,6 +102,42 @@ dynamic makeOptions(dynamic ctx) {
|
|
|
95
102
|
|
|
96
103
|
opts = vs.validate(opts, optspec);
|
|
97
104
|
|
|
105
|
+
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
106
|
+
// Every placeholder must resolve to a non-empty value: from options.server
|
|
107
|
+
// (user), else the Config default. A placeholder that resolves to '' is a
|
|
108
|
+
// construction ERROR in live mode — the URL cannot work — but in test mode
|
|
109
|
+
// substitutes the deterministic value `test-<name>` so offline tests need no
|
|
110
|
+
// configuration. The SDK constructor has no error return, so a missing
|
|
111
|
+
// required variable THROWS: construction-time misconfiguration.
|
|
112
|
+
final baseVal = vs.getprop(opts, 'base');
|
|
113
|
+
if (baseVal is String && baseVal.contains('{')) {
|
|
114
|
+
final testmode = true == vs.getpath(opts, 'test.active') ||
|
|
115
|
+
true == vs.getpath(opts, 'feature.test.active');
|
|
116
|
+
final server = vs.getprop(opts, 'server') ?? {};
|
|
117
|
+
final sdkname = (vs.getpath(config, 'main.name') ?? 'SDK').toString();
|
|
118
|
+
|
|
119
|
+
opts['base'] = baseVal.replaceAllMapped(
|
|
120
|
+
RegExp(r'\{([A-Za-z0-9_]+)\}'),
|
|
121
|
+
(m) {
|
|
122
|
+
final name = m.group(1) as String;
|
|
123
|
+
final raw = vs.getprop(server, name);
|
|
124
|
+
final val = raw is String ? raw : '';
|
|
125
|
+
if ('' == val) {
|
|
126
|
+
if (testmode) {
|
|
127
|
+
return 'test-' + name;
|
|
128
|
+
}
|
|
129
|
+
throw ProjectNameError(
|
|
130
|
+
'server_var_required',
|
|
131
|
+
"$sdkname: the server variable '$name' is required: the API base "
|
|
132
|
+
"URL is '$baseVal' — pass { 'server': { '$name': '...' } } in the "
|
|
133
|
+
'SDK options',
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
return val;
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
98
141
|
final sys = vs.getprop(opts, 'system');
|
|
99
142
|
if (sys is Map) {
|
|
100
143
|
sys['fetch'] = sysFetch;
|
|
@@ -263,15 +263,71 @@ defmodule ProjectName.Utility do
|
|
|
263
263
|
"utility" => %{},
|
|
264
264
|
"system" => %{},
|
|
265
265
|
"test" => %{"active" => false, "entity" => %{"`$OPEN`" => true}},
|
|
266
|
-
"clean" => %{"keys" => "key,token,id"}
|
|
266
|
+
"clean" => %{"keys" => "key,token,id"},
|
|
267
|
+
# Server-variable values for a templated base URL (OpenAPI server
|
|
268
|
+
# variables): {name} placeholders in "base" are substituted from this
|
|
269
|
+
# map at construction. Spec defaults arrive via the generated config;
|
|
270
|
+
# user values override them. Mirrors go's make_options optspec - elixir
|
|
271
|
+
# was the only target validating options that did not accept it, so a
|
|
272
|
+
# spec with a templated server URL failed validation outright.
|
|
273
|
+
"server" => %{"`$CHILD`" => ""}
|
|
267
274
|
})
|
|
268
275
|
|
|
269
276
|
sys_fetch = S.getpath(opts0, "system.fetch")
|
|
270
277
|
|
|
271
|
-
|
|
278
|
+
# CLONE the config side: `config` is a process-wide singleton
|
|
279
|
+
# (ProjectName.Config.shared_config) and merge uses its nested nodes as
|
|
280
|
+
# merge TARGETS, so without this one client's options (headers, server,
|
|
281
|
+
# ...) are written into the shared config and inherited by every client
|
|
282
|
+
# constructed afterwards.
|
|
283
|
+
merged = S.merge(S.jt([S.jm([]), S.clone(cfgopts), opts0]))
|
|
272
284
|
validated = S.validate(merged, optspec)
|
|
273
285
|
opts = if S.ismap(validated), do: validated, else: S.jm([])
|
|
274
286
|
|
|
287
|
+
# Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
288
|
+
# Every placeholder must resolve to a non-empty value: from options.server
|
|
289
|
+
# (user), else the Config default. A placeholder that resolves to "" is a
|
|
290
|
+
# construction ERROR in live mode - the URL cannot work - but in test mode
|
|
291
|
+
# substitutes the deterministic value "test-<name>" so offline tests need
|
|
292
|
+
# no configuration. The SDK constructor has no error return, so a missing
|
|
293
|
+
# required variable RAISES: construction-time misconfiguration.
|
|
294
|
+
base = S.getprop(opts, "base")
|
|
295
|
+
|
|
296
|
+
if is_binary(base) and String.contains?(base, "{") do
|
|
297
|
+
testmode =
|
|
298
|
+
true == S.getpath(opts, "test.active") or
|
|
299
|
+
true == S.getpath(opts, "feature.test.active")
|
|
300
|
+
|
|
301
|
+
server = H.or_(S.getprop(opts, "server"), S.jm([]))
|
|
302
|
+
mn = S.getpath(config, "main.name")
|
|
303
|
+
sdkname = if is_binary(mn) and mn != "", do: mn, else: "SDK"
|
|
304
|
+
|
|
305
|
+
resolved =
|
|
306
|
+
Regex.replace(~r/\{([A-Za-z0-9_]+)\}/, base, fn _match, name ->
|
|
307
|
+
val = S.getprop(server, name)
|
|
308
|
+
val = if is_binary(val), do: val, else: ""
|
|
309
|
+
|
|
310
|
+
cond do
|
|
311
|
+
val != "" ->
|
|
312
|
+
val
|
|
313
|
+
|
|
314
|
+
testmode ->
|
|
315
|
+
"test-" <> name
|
|
316
|
+
|
|
317
|
+
true ->
|
|
318
|
+
raise ProjectName.Error,
|
|
319
|
+
code: "server_var_required",
|
|
320
|
+
sdk: "ProjectName",
|
|
321
|
+
msg:
|
|
322
|
+
"#{sdkname}: the server variable '#{name}' is required: the API " <>
|
|
323
|
+
"base URL is '#{base}' - pass %{\"server\" => %{\"#{name}\" => " <>
|
|
324
|
+
"\"...\"}} in the SDK options"
|
|
325
|
+
end
|
|
326
|
+
end)
|
|
327
|
+
|
|
328
|
+
S.setprop(opts, "base", resolved)
|
|
329
|
+
end
|
|
330
|
+
|
|
275
331
|
if sys_fetch != nil do
|
|
276
332
|
sysnode = S.getprop(opts, "system")
|
|
277
333
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
-- Self-contained JSON reader building struct `Value` nodes directly (no
|
|
2
|
-
-- third-party dependency beyond the `array` boot library).
|
|
3
|
-
--
|
|
4
|
-
--
|
|
2
|
+
-- third-party dependency beyond the `array` boot library). Uses O(1) array
|
|
3
|
+
-- indexing so large corpora parse in linear time.
|
|
4
|
+
--
|
|
5
|
+
-- Lives in src/ rather than test/ because SdkConfig needs it: above a size
|
|
6
|
+
-- threshold the API model is embedded as a JSON string constant and parsed
|
|
7
|
+
-- once at load (sdkgen rung L1) instead of being built as a CV literal. It is
|
|
8
|
+
-- still what the struct corpus runner and the entity-fixture tests use.
|
|
5
9
|
|
|
6
10
|
{-# LANGUAGE LambdaCase #-}
|
|
7
11
|
|
|
8
|
-
module
|
|
12
|
+
module SdkJson (jsonRead) where
|
|
9
13
|
|
|
10
14
|
import Data.Array (Array, listArray, (!))
|
|
11
15
|
import Data.Char (chr)
|
|
@@ -13,7 +13,7 @@ module SdkRuntime where
|
|
|
13
13
|
import Control.Exception (throwIO)
|
|
14
14
|
import Control.Monad (forM_, when)
|
|
15
15
|
import Data.Bits ((.&.))
|
|
16
|
-
import Data.Char (toUpper)
|
|
16
|
+
import Data.Char (isAlphaNum, toUpper)
|
|
17
17
|
import Data.IORef
|
|
18
18
|
import qualified Data.Map.Strict as Map
|
|
19
19
|
import System.IO.Unsafe (unsafePerformIO)
|
|
@@ -1016,6 +1016,11 @@ optSpecValue :: IO Value
|
|
|
1016
1016
|
optSpecValue = do
|
|
1017
1017
|
auth <- jo [("prefix", VStr "")]
|
|
1018
1018
|
hdrs <- jo [("`$CHILD`", VStr "`$STRING`")]
|
|
1019
|
+
-- OpenAPI server-variable defaults, carried by the generated config whenever
|
|
1020
|
+
-- the spec's server URL is templated. Accepted here so validation does not
|
|
1021
|
+
-- reject the SDK's own config; the {name} substitution into base is a
|
|
1022
|
+
-- separate concern.
|
|
1023
|
+
srv <- jo [("`$CHILD`", VStr "")]
|
|
1019
1024
|
allow <- jo [("method", VStr "GET,PUT,POST,PATCH,DELETE,OPTIONS"), ("op", VStr "create,update,load,list,remove,command,direct,graphql")]
|
|
1020
1025
|
entChild <- do a <- emptyMap; jo [("`$OPEN`", VBool True), ("active", VBool False), ("alias", a)]
|
|
1021
1026
|
ent <- jo [("`$CHILD`", entChild)]
|
|
@@ -1027,7 +1032,7 @@ optSpecValue = do
|
|
|
1027
1032
|
test <- jo [("active", VBool False), ("entity", testEnt)]
|
|
1028
1033
|
clean <- jo [("keys", VStr "key,token,id")]
|
|
1029
1034
|
jo [ ("apikey", VStr ""), ("base", VStr "http://localhost:8000"), ("prefix", VStr ""), ("suffix", VStr "")
|
|
1030
|
-
, ("auth", auth), ("headers", hdrs), ("allow", allow), ("entity", ent), ("feature", feat)
|
|
1035
|
+
, ("auth", auth), ("headers", hdrs), ("server", srv), ("allow", allow), ("entity", ent), ("feature", feat)
|
|
1031
1036
|
, ("utility", utilm), ("system", sysm), ("test", test), ("clean", clean) ]
|
|
1032
1037
|
|
|
1033
1038
|
makeOptionsUtil :: Context -> IO Value
|
|
@@ -1079,6 +1084,54 @@ makeOptionsUtil ctx = do
|
|
|
1079
1084
|
merged <- merge mlist
|
|
1080
1085
|
validated <- validate INone merged optspec
|
|
1081
1086
|
opts <- case validated of VMap _ -> pure validated; _ -> emptyMap
|
|
1087
|
+
-- Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
1088
|
+
-- Every placeholder must resolve to a non-empty value: from options.server
|
|
1089
|
+
-- (user), else the Config default. A placeholder that resolves to "" is a
|
|
1090
|
+
-- construction ERROR in live mode - the URL cannot work - but in test mode
|
|
1091
|
+
-- substitutes the deterministic value "test-<name>" so offline tests need no
|
|
1092
|
+
-- configuration. The SDK constructor has no error return, so a missing
|
|
1093
|
+
-- required variable THROWS: construction-time misconfiguration.
|
|
1094
|
+
baseV <- getp opts "base"
|
|
1095
|
+
case baseV of
|
|
1096
|
+
VStr base | '{' `elem` base -> do
|
|
1097
|
+
taV <- getpathS opts "test.active"
|
|
1098
|
+
faV <- getpathS opts "feature.test.active"
|
|
1099
|
+
-- Value has no Eq instance: match the constructor.
|
|
1100
|
+
let isTrue v = case v of VBool b -> b; _ -> False
|
|
1101
|
+
testmode = isTrue taV || isTrue faV
|
|
1102
|
+
server <- getp opts "server"
|
|
1103
|
+
mnV <- getpathS config "main.name"
|
|
1104
|
+
let sdkname = case mnV of VStr s | not (null s) -> s; _ -> "SDK"
|
|
1105
|
+
|
|
1106
|
+
-- Scanned by hand: a placeholder is `{` followed by [A-Za-z0-9_]+ and
|
|
1107
|
+
-- `}`; anything else is literal text, so a stray brace is left alone.
|
|
1108
|
+
let resolve [] = pure []
|
|
1109
|
+
resolve ('{' : rest) =
|
|
1110
|
+
let (name, tail') = span (\c -> isAlphaNum c || '_' == c) rest
|
|
1111
|
+
in case tail' of
|
|
1112
|
+
('}' : more) | not (null name) -> do
|
|
1113
|
+
valV <- case server of VMap _ -> getp server name; _ -> pure VNoval
|
|
1114
|
+
let val = case valV of VStr s -> s; _ -> ""
|
|
1115
|
+
sub <- if not (null val)
|
|
1116
|
+
then pure val
|
|
1117
|
+
else if testmode
|
|
1118
|
+
then pure ("test-" ++ name)
|
|
1119
|
+
else do
|
|
1120
|
+
e <- jo [ ("code", VStr "server_var_required")
|
|
1121
|
+
, ("msg", VStr (sdkname ++ ": the server variable '" ++
|
|
1122
|
+
name ++ "' is required: the API base URL is '" ++
|
|
1123
|
+
base ++ "' - pass server = { \"" ++ name ++
|
|
1124
|
+
"\": \"...\" } in the SDK options"))
|
|
1125
|
+
, ("sdk", VStr "ProjectName") ]
|
|
1126
|
+
throwIO (SdkException e)
|
|
1127
|
+
more' <- resolve more
|
|
1128
|
+
pure (sub ++ more')
|
|
1129
|
+
_ -> do r <- resolve rest; pure ('{' : r)
|
|
1130
|
+
resolve (c : rest) = do r <- resolve rest; pure (c : r)
|
|
1131
|
+
resolved <- resolve base
|
|
1132
|
+
setp opts "base" (VStr resolved)
|
|
1133
|
+
_ -> pure ()
|
|
1134
|
+
|
|
1082
1135
|
when (not (isNoval sysFetch)) $ do
|
|
1083
1136
|
sys <- getp opts "system"
|
|
1084
1137
|
case sys of
|
|
@@ -27,7 +27,9 @@ public abstract class SdkClient {
|
|
|
27
27
|
protected SdkClient(Map<String, Object> options) {
|
|
28
28
|
this.utility = new Utility();
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
// The process-wide config (sdkgen rung L2): read-only on the request path,
|
|
31
|
+
// so every client shares one rather than rebuilding it.
|
|
32
|
+
Map<String, Object> config = Config.sharedConfig();
|
|
31
33
|
|
|
32
34
|
Map<String, Object> ctxmap = new LinkedHashMap<>();
|
|
33
35
|
ctxmap.put("client", this);
|
|
@@ -93,7 +93,11 @@ final class MakeOptions {
|
|
|
93
93
|
|
|
94
94
|
List<Object> mergeList = new ArrayList<>();
|
|
95
95
|
mergeList.add(new LinkedHashMap<String, Object>());
|
|
96
|
-
|
|
96
|
+
// CLONE the config side: `config` is a process-wide singleton
|
|
97
|
+
// (Config.sharedConfig) and merge uses its nested maps as merge TARGETS, so
|
|
98
|
+
// without this one client's options (headers, server, ...) are written into
|
|
99
|
+
// the shared config and inherited by every client constructed afterwards.
|
|
100
|
+
mergeList.add(Struct.clone(cfgopts));
|
|
97
101
|
mergeList.add(opts);
|
|
98
102
|
Object merged = Struct.merge(mergeList);
|
|
99
103
|
|
|
@@ -21,7 +21,9 @@ abstract class SdkClient(sdkopts: MutableMap<String, Any?>?) {
|
|
|
21
21
|
protected val rootctx: Context
|
|
22
22
|
|
|
23
23
|
init {
|
|
24
|
-
|
|
24
|
+
// The process-wide config (sdkgen rung L2): read-only on the request path,
|
|
25
|
+
// so every client shares one rather than rebuilding it.
|
|
26
|
+
val config = Config.sharedConfig()
|
|
25
27
|
|
|
26
28
|
val ctxmap = linkedMapOf<String, Any?>()
|
|
27
29
|
ctxmap["client"] = this
|
|
@@ -84,7 +84,11 @@ fun makeOptions(ctx: Context): MutableMap<String, Any?> {
|
|
|
84
84
|
|
|
85
85
|
val mergeList = mutableListOf<Any?>()
|
|
86
86
|
mergeList.add(linkedMapOf<String, Any?>())
|
|
87
|
-
|
|
87
|
+
// CLONE the config side: `config` is a process-wide singleton
|
|
88
|
+
// (Config.sharedConfig) and merge uses its nested maps as merge TARGETS, so
|
|
89
|
+
// without this one client's options (headers, server, ...) are written into
|
|
90
|
+
// the shared config and inherited by every client constructed afterwards.
|
|
91
|
+
mergeList.add(Struct.clone(cfgopts))
|
|
88
92
|
mergeList.add(opts)
|
|
89
93
|
val merged = Struct.merge(mergeList)
|
|
90
94
|
|
|
@@ -14,7 +14,7 @@ INC = -I utility -I . -I test
|
|
|
14
14
|
# The shared JSON corpus lives in the sibling .sdk workspace.
|
|
15
15
|
CORPUS = ../.sdk/test/test.json
|
|
16
16
|
|
|
17
|
-
RUNTIME = utility/vregex.ml utility/voxgig_struct.ml \
|
|
17
|
+
RUNTIME = utility/vregex.ml utility/voxgig_struct.ml sdk_json.ml \
|
|
18
18
|
sdk_types.ml sdk_helpers.ml sdk_runtime.ml sdk_features.ml
|
|
19
19
|
|
|
20
20
|
ENTITIES = $(sort $(wildcard sdk_entity_*.ml))
|
|
@@ -33,7 +33,8 @@ SDK_TEST = $(RUNTIME) sdk_config.ml $(ENTITIES) sdk_client.ml sdk_error.ml \
|
|
|
33
33
|
test/t_pipeline.ml test/t_feature.ml test/custom_utility.ml \
|
|
34
34
|
$(ENTITY_TESTS) $(DOC_TESTS) test/t_main.ml
|
|
35
35
|
|
|
36
|
-
CORPUS_SRC = utility/vregex.ml utility/voxgig_struct.ml
|
|
36
|
+
CORPUS_SRC = utility/vregex.ml utility/voxgig_struct.ml sdk_json.ml \
|
|
37
|
+
test/struct_corpus.ml
|
|
37
38
|
|
|
38
39
|
.PHONY: test test-sdk test-corpus build lint clean
|
|
39
40
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
(* JSON text -> voxgig struct value.
|
|
2
|
+
*
|
|
3
|
+
* A dependency-free reader, so the SDK can parse JSON with no opam packages -
|
|
4
|
+
* the same position every other target is in (elixir's ProjectName.Json,
|
|
5
|
+
* clojure's sdk.core/json-parse, the json_parse in c/rust/zig).
|
|
6
|
+
*
|
|
7
|
+
* It lives in the RUNTIME rather than the test tree because the generated
|
|
8
|
+
* config uses it: above a size threshold the API model is emitted as a JSON
|
|
9
|
+
* string constant and parsed here (sdkgen rung L1) instead of as a literal the
|
|
10
|
+
* compiler has to walk. `test/struct_corpus.ml` uses this same function rather
|
|
11
|
+
* than its own copy, so the corpus and the SDK cannot disagree about what a
|
|
12
|
+
* given JSON text means.
|
|
13
|
+
*
|
|
14
|
+
* Numbers are read as Num (float), which is exactly what formatOcamlValue
|
|
15
|
+
* emits for the literal form - so the two representations cannot disagree
|
|
16
|
+
* about the type of a whole number.
|
|
17
|
+
*)
|
|
18
|
+
|
|
19
|
+
open Voxgig_struct
|
|
20
|
+
|
|
21
|
+
(* ---------------- JSON reader -> value ---------------- *)
|
|
22
|
+
|
|
23
|
+
let json_read (s : string) : value =
|
|
24
|
+
let n = String.length s in
|
|
25
|
+
let pos = ref 0 in
|
|
26
|
+
let peek () = if !pos < n then Some s.[!pos] else None in
|
|
27
|
+
let adv () = incr pos in
|
|
28
|
+
let skip_ws () =
|
|
29
|
+
while !pos < n && (match s.[!pos] with ' ' | '\t' | '\n' | '\r' -> true | _ -> false) do incr pos done
|
|
30
|
+
in
|
|
31
|
+
let rec pval () =
|
|
32
|
+
skip_ws ();
|
|
33
|
+
match peek () with
|
|
34
|
+
| Some '{' -> pobj ()
|
|
35
|
+
| Some '[' -> parr ()
|
|
36
|
+
| Some '"' -> Str (pstr ())
|
|
37
|
+
| Some 't' -> pos := !pos + 4; Bool true
|
|
38
|
+
| Some 'f' -> pos := !pos + 5; Bool false
|
|
39
|
+
| Some 'n' -> pos := !pos + 4; Null
|
|
40
|
+
| _ -> pnum ()
|
|
41
|
+
and pobj () =
|
|
42
|
+
adv (); skip_ws ();
|
|
43
|
+
if peek () = Some '}' then (adv (); empty_map ())
|
|
44
|
+
else begin
|
|
45
|
+
let m = empty_map () in
|
|
46
|
+
let rec loop () =
|
|
47
|
+
skip_ws ();
|
|
48
|
+
let k = pstr () in
|
|
49
|
+
skip_ws (); adv (); (* : *)
|
|
50
|
+
let v = pval () in
|
|
51
|
+
ignore (setprop m (Str k) v);
|
|
52
|
+
skip_ws ();
|
|
53
|
+
let c = (match peek () with Some c -> adv (); c | None -> '}') in
|
|
54
|
+
if c = ',' then loop () else m
|
|
55
|
+
in loop ()
|
|
56
|
+
end
|
|
57
|
+
and parr () =
|
|
58
|
+
adv (); skip_ws ();
|
|
59
|
+
if peek () = Some ']' then (adv (); empty_list ())
|
|
60
|
+
else begin
|
|
61
|
+
let acc = ref [] in
|
|
62
|
+
let rec loop () =
|
|
63
|
+
let v = pval () in
|
|
64
|
+
acc := v :: !acc;
|
|
65
|
+
skip_ws ();
|
|
66
|
+
let c = (match peek () with Some c -> adv (); c | None -> ']') in
|
|
67
|
+
if c = ',' then loop () else lst (List.rev !acc)
|
|
68
|
+
in loop ()
|
|
69
|
+
end
|
|
70
|
+
and pstr () =
|
|
71
|
+
adv ();
|
|
72
|
+
let b = Buffer.create 16 in
|
|
73
|
+
let rec loop () =
|
|
74
|
+
let c = s.[!pos] in adv ();
|
|
75
|
+
if c = '"' then Buffer.contents b
|
|
76
|
+
else if c = '\\' then begin
|
|
77
|
+
let e = s.[!pos] in adv ();
|
|
78
|
+
(match e with
|
|
79
|
+
| '"' -> Buffer.add_char b '"' | '\\' -> Buffer.add_char b '\\'
|
|
80
|
+
| '/' -> Buffer.add_char b '/' | 'n' -> Buffer.add_char b '\n'
|
|
81
|
+
| 't' -> Buffer.add_char b '\t' | 'r' -> Buffer.add_char b '\r'
|
|
82
|
+
| 'b' -> Buffer.add_char b '\b' | 'f' -> Buffer.add_char b '\012'
|
|
83
|
+
| 'u' ->
|
|
84
|
+
let hex = String.sub s !pos 4 in pos := !pos + 4;
|
|
85
|
+
let code = int_of_string ("0x" ^ hex) in
|
|
86
|
+
if code < 128 then Buffer.add_char b (Char.chr code)
|
|
87
|
+
else if code < 2048 then begin
|
|
88
|
+
Buffer.add_char b (Char.chr (0xC0 lor (code lsr 6)));
|
|
89
|
+
Buffer.add_char b (Char.chr (0x80 lor (code land 0x3F)))
|
|
90
|
+
end else begin
|
|
91
|
+
Buffer.add_char b (Char.chr (0xE0 lor (code lsr 12)));
|
|
92
|
+
Buffer.add_char b (Char.chr (0x80 lor ((code lsr 6) land 0x3F)));
|
|
93
|
+
Buffer.add_char b (Char.chr (0x80 lor (code land 0x3F)))
|
|
94
|
+
end
|
|
95
|
+
| c -> Buffer.add_char b c);
|
|
96
|
+
loop ()
|
|
97
|
+
end else (Buffer.add_char b c; loop ())
|
|
98
|
+
in loop ()
|
|
99
|
+
and pnum () =
|
|
100
|
+
let start = !pos in
|
|
101
|
+
while !pos < n && (match s.[!pos] with
|
|
102
|
+
| '0'..'9' | '-' | '+' | '.' | 'e' | 'E' -> true | _ -> false) do incr pos done;
|
|
103
|
+
let tok = String.sub s start (!pos - start) in
|
|
104
|
+
Num (float_of_string tok)
|
|
105
|
+
in
|
|
106
|
+
pval ()
|
|
@@ -905,6 +905,11 @@ let opt_spec_value () : value =
|
|
|
905
905
|
("system", empty_map ());
|
|
906
906
|
("test", jo [("active", Bool false); ("entity", jo [("`$OPEN`", Bool true)])]);
|
|
907
907
|
("clean", jo [("keys", Str "key,token,id")]);
|
|
908
|
+
(* Server-variable values for a templated base URL (OpenAPI server
|
|
909
|
+
* variables): {name} placeholders in "base" are substituted from this
|
|
910
|
+
* map at construction. Spec defaults arrive via the generated config;
|
|
911
|
+
* user values override them. Mirrors go's make_options optspec. *)
|
|
912
|
+
("server", jo [("`$CHILD`", Str "")]);
|
|
908
913
|
]
|
|
909
914
|
|
|
910
915
|
let make_options_util (ctx : ctx) : value =
|
|
@@ -946,6 +951,57 @@ let make_options_util (ctx : ctx) : value =
|
|
|
946
951
|
let merged = merge (ja [empty_map (); cfgopts; opts]) in
|
|
947
952
|
let validated = validate merged optspec in
|
|
948
953
|
let opts = match validated with Map _ as m -> m | _ -> empty_map () in
|
|
954
|
+
(* Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
955
|
+
Every placeholder must resolve to a non-empty value: from options.server
|
|
956
|
+
(user), else the Config default. A placeholder that resolves to "" is a
|
|
957
|
+
construction ERROR in live mode - the URL cannot work - but in test mode
|
|
958
|
+
substitutes the deterministic value "test-<name>" so offline tests need no
|
|
959
|
+
configuration. The SDK constructor has no error return, so a missing
|
|
960
|
+
required variable RAISES: construction-time misconfiguration.
|
|
961
|
+
|
|
962
|
+
Scanned by hand: a placeholder is `{` followed by [A-Za-z0-9_]+ and `}`;
|
|
963
|
+
anything else is literal text, so a stray brace is left alone. *)
|
|
964
|
+
(match getp opts "base" with
|
|
965
|
+
| Str base when String.contains base '{' ->
|
|
966
|
+
let is_true v = match v with Bool b -> b | _ -> false in
|
|
967
|
+
let testmode =
|
|
968
|
+
is_true (getpath_s opts "test.active")
|
|
969
|
+
|| is_true (getpath_s opts "feature.test.active") in
|
|
970
|
+
let server = match getp opts "server" with Map _ as m -> m | _ -> empty_map () in
|
|
971
|
+
let sdkname =
|
|
972
|
+
match getpath_s config "main.name" with Str s when s <> "" -> s | _ -> "SDK" in
|
|
973
|
+
let namechar c =
|
|
974
|
+
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|
|
975
|
+
|| (c >= '0' && c <= '9') || c = '_' in
|
|
976
|
+
let buf = Buffer.create (String.length base) in
|
|
977
|
+
let n = String.length base in
|
|
978
|
+
let i = ref 0 in
|
|
979
|
+
while !i < n do
|
|
980
|
+
if base.[!i] <> '{' then begin
|
|
981
|
+
Buffer.add_char buf base.[!i];
|
|
982
|
+
incr i
|
|
983
|
+
end else begin
|
|
984
|
+
let j = ref (!i + 1) in
|
|
985
|
+
while !j < n && namechar base.[!j] do incr j done;
|
|
986
|
+
if !j >= n || base.[!j] <> '}' || !j = !i + 1 then begin
|
|
987
|
+
Buffer.add_char buf base.[!i];
|
|
988
|
+
incr i
|
|
989
|
+
end else begin
|
|
990
|
+
let name = String.sub base (!i + 1) (!j - !i - 1) in
|
|
991
|
+
let value = match getp server name with Str s -> s | _ -> "" in
|
|
992
|
+
if value <> "" then Buffer.add_string buf value
|
|
993
|
+
else if testmode then Buffer.add_string buf ("test-" ^ name)
|
|
994
|
+
else
|
|
995
|
+
raise (Sdk_error_exc (ctx_make_error ctx "server_var_required"
|
|
996
|
+
(sdkname ^ ": the server variable '" ^ name ^ "' is required: the API "
|
|
997
|
+
^ "base URL is '" ^ base ^ "' - pass ~server:[(\"" ^ name
|
|
998
|
+
^ "\", \"...\")] in the SDK options")));
|
|
999
|
+
i := !j + 1
|
|
1000
|
+
end
|
|
1001
|
+
end
|
|
1002
|
+
done;
|
|
1003
|
+
setp opts "base" (Str (Buffer.contents buf))
|
|
1004
|
+
| _ -> ());
|
|
949
1005
|
(if not (is_noval sys_fetch) then
|
|
950
1006
|
match getp opts "system" with
|
|
951
1007
|
| Map _ as sys -> setp sys "fetch" sys_fetch
|
|
@@ -8,92 +8,10 @@ let nullmark = "__NULL__"
|
|
|
8
8
|
let undefmark = "__UNDEF__"
|
|
9
9
|
let existsmark = "__EXISTS__"
|
|
10
10
|
|
|
11
|
-
(*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let pos = ref 0 in
|
|
16
|
-
let peek () = if !pos < n then Some s.[!pos] else None in
|
|
17
|
-
let adv () = incr pos in
|
|
18
|
-
let skip_ws () =
|
|
19
|
-
while !pos < n && (match s.[!pos] with ' ' | '\t' | '\n' | '\r' -> true | _ -> false) do incr pos done
|
|
20
|
-
in
|
|
21
|
-
let rec pval () =
|
|
22
|
-
skip_ws ();
|
|
23
|
-
match peek () with
|
|
24
|
-
| Some '{' -> pobj ()
|
|
25
|
-
| Some '[' -> parr ()
|
|
26
|
-
| Some '"' -> Str (pstr ())
|
|
27
|
-
| Some 't' -> pos := !pos + 4; Bool true
|
|
28
|
-
| Some 'f' -> pos := !pos + 5; Bool false
|
|
29
|
-
| Some 'n' -> pos := !pos + 4; Null
|
|
30
|
-
| _ -> pnum ()
|
|
31
|
-
and pobj () =
|
|
32
|
-
adv (); skip_ws ();
|
|
33
|
-
if peek () = Some '}' then (adv (); empty_map ())
|
|
34
|
-
else begin
|
|
35
|
-
let m = empty_map () in
|
|
36
|
-
let rec loop () =
|
|
37
|
-
skip_ws ();
|
|
38
|
-
let k = pstr () in
|
|
39
|
-
skip_ws (); adv (); (* : *)
|
|
40
|
-
let v = pval () in
|
|
41
|
-
ignore (setprop m (Str k) v);
|
|
42
|
-
skip_ws ();
|
|
43
|
-
let c = (match peek () with Some c -> adv (); c | None -> '}') in
|
|
44
|
-
if c = ',' then loop () else m
|
|
45
|
-
in loop ()
|
|
46
|
-
end
|
|
47
|
-
and parr () =
|
|
48
|
-
adv (); skip_ws ();
|
|
49
|
-
if peek () = Some ']' then (adv (); empty_list ())
|
|
50
|
-
else begin
|
|
51
|
-
let acc = ref [] in
|
|
52
|
-
let rec loop () =
|
|
53
|
-
let v = pval () in
|
|
54
|
-
acc := v :: !acc;
|
|
55
|
-
skip_ws ();
|
|
56
|
-
let c = (match peek () with Some c -> adv (); c | None -> ']') in
|
|
57
|
-
if c = ',' then loop () else lst (List.rev !acc)
|
|
58
|
-
in loop ()
|
|
59
|
-
end
|
|
60
|
-
and pstr () =
|
|
61
|
-
adv ();
|
|
62
|
-
let b = Buffer.create 16 in
|
|
63
|
-
let rec loop () =
|
|
64
|
-
let c = s.[!pos] in adv ();
|
|
65
|
-
if c = '"' then Buffer.contents b
|
|
66
|
-
else if c = '\\' then begin
|
|
67
|
-
let e = s.[!pos] in adv ();
|
|
68
|
-
(match e with
|
|
69
|
-
| '"' -> Buffer.add_char b '"' | '\\' -> Buffer.add_char b '\\'
|
|
70
|
-
| '/' -> Buffer.add_char b '/' | 'n' -> Buffer.add_char b '\n'
|
|
71
|
-
| 't' -> Buffer.add_char b '\t' | 'r' -> Buffer.add_char b '\r'
|
|
72
|
-
| 'b' -> Buffer.add_char b '\b' | 'f' -> Buffer.add_char b '\012'
|
|
73
|
-
| 'u' ->
|
|
74
|
-
let hex = String.sub s !pos 4 in pos := !pos + 4;
|
|
75
|
-
let code = int_of_string ("0x" ^ hex) in
|
|
76
|
-
if code < 128 then Buffer.add_char b (Char.chr code)
|
|
77
|
-
else if code < 2048 then begin
|
|
78
|
-
Buffer.add_char b (Char.chr (0xC0 lor (code lsr 6)));
|
|
79
|
-
Buffer.add_char b (Char.chr (0x80 lor (code land 0x3F)))
|
|
80
|
-
end else begin
|
|
81
|
-
Buffer.add_char b (Char.chr (0xE0 lor (code lsr 12)));
|
|
82
|
-
Buffer.add_char b (Char.chr (0x80 lor ((code lsr 6) land 0x3F)));
|
|
83
|
-
Buffer.add_char b (Char.chr (0x80 lor (code land 0x3F)))
|
|
84
|
-
end
|
|
85
|
-
| c -> Buffer.add_char b c);
|
|
86
|
-
loop ()
|
|
87
|
-
end else (Buffer.add_char b c; loop ())
|
|
88
|
-
in loop ()
|
|
89
|
-
and pnum () =
|
|
90
|
-
let start = !pos in
|
|
91
|
-
while !pos < n && (match s.[!pos] with
|
|
92
|
-
| '0'..'9' | '-' | '+' | '.' | 'e' | 'E' -> true | _ -> false) do incr pos done;
|
|
93
|
-
let tok = String.sub s start (!pos - start) in
|
|
94
|
-
Num (float_of_string tok)
|
|
95
|
-
in
|
|
96
|
-
pval ()
|
|
11
|
+
(* The JSON reader now lives in the runtime (sdk_json.ml) so the SDK's
|
|
12
|
+
* generated config can use it; the corpus runs against that same function
|
|
13
|
+
* rather than a second copy that could drift from it. *)
|
|
14
|
+
let json_read = Sdk_json.json_read
|
|
97
15
|
|
|
98
16
|
(* ---------------- fixJSON / equality ---------------- *)
|
|
99
17
|
|
|
@@ -82,7 +82,11 @@ $REGISTRY{make_options} = sub {
|
|
|
82
82
|
|
|
83
83
|
my $sys_fetch = ProjectNameHelpers::gpath($opts, 'system.fetch');
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
# CLONE the config side: `config` is a process-wide singleton
|
|
86
|
+
# (ProjectNameConfig::shared_config) and merge uses its nested hashes as merge
|
|
87
|
+
# TARGETS, so without this one client's options (headers, server, ...) are
|
|
88
|
+
# written into the shared config and inherited by every client after it.
|
|
89
|
+
my $merged = Voxgig::Struct::merge([{}, Voxgig::Struct::clone($cfgopts), $opts]);
|
|
86
90
|
my $validated = Voxgig::Struct::validate($merged, $optspec);
|
|
87
91
|
$opts = Voxgig::Struct::ismap($validated) ? $validated : {};
|
|
88
92
|
|