@voxgig/sdkgen 3.4.6 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/voxgig-sdkgen +78 -10
- package/dist/action/action.d.ts +5 -2
- package/dist/action/action.js +113 -6
- package/dist/action/action.js.map +1 -1
- package/dist/action/check.d.ts +15 -0
- package/dist/action/check.js +347 -0
- package/dist/action/check.js.map +1 -0
- package/dist/action/dispatch.d.ts +8 -0
- package/dist/action/dispatch.js +74 -0
- package/dist/action/dispatch.js.map +1 -0
- package/dist/action/docs.d.ts +4 -0
- package/dist/action/docs.js +236 -0
- package/dist/action/docs.js.map +1 -0
- package/dist/action/doctor.d.ts +5 -2
- package/dist/action/doctor.js +526 -74
- package/dist/action/doctor.js.map +1 -1
- package/dist/action/feature.js +96 -17
- package/dist/action/feature.js.map +1 -1
- package/dist/action/kind.d.ts +30 -0
- package/dist/action/kind.js +258 -0
- package/dist/action/kind.js.map +1 -0
- package/dist/action/package.d.ts +25 -0
- package/dist/action/package.js +852 -0
- package/dist/action/package.js.map +1 -0
- package/dist/action/resolve.d.ts +18 -0
- package/dist/action/resolve.js +280 -0
- package/dist/action/resolve.js.map +1 -0
- package/dist/action/target.d.ts +7 -1
- package/dist/action/target.js +240 -97
- package/dist/action/target.js.map +1 -1
- package/dist/cmp/Docs.d.ts +4 -0
- package/dist/cmp/Docs.js +98 -0
- package/dist/cmp/Docs.js.map +1 -0
- package/dist/cmp/ExternalDocs.d.ts +2 -0
- package/dist/cmp/ExternalDocs.js +43 -0
- package/dist/cmp/ExternalDocs.js.map +1 -0
- package/dist/cmp/ReadmeTop.js +5 -1
- package/dist/cmp/ReadmeTop.js.map +1 -1
- package/dist/helpers/definition.d.ts +5 -0
- package/dist/helpers/definition.js +72 -0
- package/dist/helpers/definition.js.map +1 -0
- package/dist/helpers/featureSource.d.ts +8 -2
- package/dist/helpers/featureSource.js +61 -12
- package/dist/helpers/featureSource.js.map +1 -1
- package/dist/helpers/junk.d.ts +11 -0
- package/dist/helpers/junk.js +149 -0
- package/dist/helpers/junk.js.map +1 -0
- package/dist/helpers/manifest.d.ts +50 -0
- package/dist/helpers/manifest.js +328 -0
- package/dist/helpers/manifest.js.map +1 -0
- package/dist/helpers/modelcheck.d.ts +22 -0
- package/dist/helpers/modelcheck.js +203 -0
- package/dist/helpers/modelcheck.js.map +1 -0
- package/dist/helpers/semver.d.ts +4 -0
- package/dist/helpers/semver.js +174 -0
- package/dist/helpers/semver.js.map +1 -0
- package/dist/helpers/shipped.d.ts +4 -0
- package/dist/helpers/shipped.js +36 -0
- package/dist/helpers/shipped.js.map +1 -0
- package/dist/helpers/stdrep.d.ts +9 -1
- package/dist/helpers/stdrep.js +22 -0
- package/dist/helpers/stdrep.js.map +1 -1
- package/dist/sdkgen.d.ts +15 -3
- package/dist/sdkgen.js +233 -67
- package/dist/sdkgen.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types.d.ts +10 -2
- package/dist/utility.js +17 -0
- package/dist/utility.js.map +1 -1
- package/model/sdkgen.aontu +69 -0
- package/package.json +3 -2
- package/project/.sdk/model/feature/audit.aontu +1 -0
- package/project/.sdk/model/feature/cache.aontu +1 -0
- package/project/.sdk/model/feature/clienttrack.aontu +1 -0
- package/project/.sdk/model/feature/debug.aontu +1 -0
- package/project/.sdk/model/feature/idempotency.aontu +1 -0
- package/project/.sdk/model/feature/log.aontu +1 -0
- package/project/.sdk/model/feature/metrics.aontu +1 -0
- package/project/.sdk/model/feature/netsim.aontu +1 -0
- package/project/.sdk/model/feature/paging.aontu +1 -0
- package/project/.sdk/model/feature/proxy.aontu +1 -0
- package/project/.sdk/model/feature/ratelimit.aontu +1 -0
- package/project/.sdk/model/feature/rbac.aontu +1 -0
- package/project/.sdk/model/feature/retry.aontu +1 -0
- package/project/.sdk/model/feature/streaming.aontu +1 -0
- package/project/.sdk/model/feature/telemetry.aontu +1 -0
- package/project/.sdk/model/feature/test.aontu +1 -0
- package/project/.sdk/model/feature/timeout.aontu +1 -0
- package/project/.sdk/model/target/c.aontu +1 -0
- package/project/.sdk/model/target/clojure.aontu +1 -0
- package/project/.sdk/model/target/cpp.aontu +1 -0
- package/project/.sdk/model/target/dart.aontu +1 -0
- package/project/.sdk/model/target/elixir.aontu +1 -0
- package/project/.sdk/model/target/go-cli.aontu +1 -0
- package/project/.sdk/model/target/go-mcp.aontu +1 -0
- package/project/.sdk/model/target/go.aontu +1 -0
- package/project/.sdk/model/target/haskell.aontu +1 -0
- package/project/.sdk/model/target/java.aontu +1 -0
- package/project/.sdk/model/target/js.aontu +1 -0
- package/project/.sdk/model/target/kotlin.aontu +1 -0
- package/project/.sdk/model/target/lean.aontu +1 -0
- package/project/.sdk/model/target/lua.aontu +1 -0
- package/project/.sdk/model/target/ocaml.aontu +1 -0
- package/project/.sdk/model/target/perl.aontu +1 -0
- package/project/.sdk/model/target/php.aontu +1 -0
- package/project/.sdk/model/target/py-data.aontu +1 -0
- package/project/.sdk/model/target/py.aontu +1 -0
- package/project/.sdk/model/target/rb.aontu +1 -0
- package/project/.sdk/model/target/rust.aontu +1 -0
- package/project/.sdk/model/target/scala.aontu +1 -0
- package/project/.sdk/model/target/seneca-provider.aontu +1 -0
- package/project/.sdk/model/target/zig.aontu +1 -0
- package/project/.sdk/src/cmp/c/Config_c.ts +44 -0
- package/project/.sdk/src/cmp/c/fragment/Main.fragment.c +3 -1
- package/project/.sdk/src/cmp/clojure/Config_clojure.ts +37 -42
- package/project/.sdk/src/cmp/clojure/utility_clojure.ts +42 -0
- package/project/.sdk/src/cmp/cpp/Config_cpp.ts +18 -0
- package/project/.sdk/src/cmp/csharp/Config_csharp.ts +125 -52
- package/project/.sdk/src/cmp/csharp/fragment/Main.fragment.cs +3 -1
- package/project/.sdk/src/cmp/csharp/utility_csharp.ts +94 -6
- package/project/.sdk/src/cmp/dart/Config_dart.ts +56 -3
- package/project/.sdk/src/cmp/dart/fragment/Config.data.fragment.dart +74 -0
- package/project/.sdk/src/cmp/dart/fragment/Config.fragment.dart +6 -9
- package/project/.sdk/src/cmp/dart/fragment/Main.fragment.dart +16 -5
- package/project/.sdk/src/cmp/dart/utility_dart.ts +18 -0
- package/project/.sdk/src/cmp/elixir/Config_elixir.ts +128 -13
- package/project/.sdk/src/cmp/elixir/fragment/Main.fragment.ex +3 -1
- package/project/.sdk/src/cmp/elixir/utility_elixir.ts +6 -0
- package/project/.sdk/src/cmp/haskell/Config_haskell.ts +35 -52
- package/project/.sdk/src/cmp/haskell/Package_haskell.ts +1 -0
- package/project/.sdk/src/cmp/haskell/Test_haskell.ts +1 -1
- package/project/.sdk/src/cmp/haskell/utility_haskell.ts +15 -5
- package/project/.sdk/src/cmp/java/Config_java.ts +21 -0
- package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +15 -0
- package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +42 -39
- package/project/.sdk/src/cmp/perl/Config_perl.ts +15 -0
- package/project/.sdk/src/cmp/perl/fragment/Main.fragment.pm +3 -1
- package/project/.sdk/src/cmp/rust/Config_rust.ts +44 -0
- package/project/.sdk/src/cmp/rust/fragment/Main.fragment.rs +4 -2
- package/project/.sdk/src/cmp/scala/Config_scala.ts +15 -0
- package/project/.sdk/src/cmp/swift/Config_swift.ts +17 -0
- package/project/.sdk/src/cmp/swift/fragment/Main.fragment.swift +3 -1
- package/project/.sdk/src/cmp/zig/Config_zig.ts +44 -0
- package/project/.sdk/src/cmp/zig/TestEntity_zig.ts +9 -2
- package/project/.sdk/src/cmp/zig/fragment/Main.fragment.zig +31 -18
- package/project/.sdk/tm/c/core/sdk.h +1 -0
- package/project/.sdk/tm/c/utility/make_options.c +5 -1
- package/project/.sdk/tm/clojure/src/sdk/core.clj +51 -3
- package/project/.sdk/tm/cpp/core/types.hpp +4 -1
- package/project/.sdk/tm/cpp/utility/pipeline.hpp +5 -1
- package/project/.sdk/tm/csharp/test/PipelineTest.cs +15 -0
- package/project/.sdk/tm/csharp/utility/MakeOptions.cs +67 -1
- package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +43 -0
- package/project/.sdk/tm/elixir/lib/projectname/utility.ex +58 -2
- package/project/.sdk/tm/haskell/{test/TestJson.hs → src/SdkJson.hs} +8 -4
- package/project/.sdk/tm/haskell/src/SdkRuntime.hs +55 -2
- package/project/.sdk/tm/haskell/test/Runner.hs +1 -1
- package/project/.sdk/tm/haskell/test/StructCorpus.hs +1 -1
- package/project/.sdk/tm/java/core/SdkClient.java +3 -1
- package/project/.sdk/tm/java/utility/MakeOptions.java +5 -1
- package/project/.sdk/tm/kotlin/core/SdkClient.kt +3 -1
- package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +5 -1
- package/project/.sdk/tm/ocaml/Makefile +3 -2
- package/project/.sdk/tm/ocaml/sdk_json.ml +106 -0
- package/project/.sdk/tm/ocaml/sdk_runtime.ml +56 -0
- package/project/.sdk/tm/ocaml/test/struct_corpus.ml +4 -86
- package/project/.sdk/tm/perl/utility/make_options.pm +5 -1
- package/project/.sdk/tm/rust/utility/make_options.rs +79 -1
- package/project/.sdk/tm/scala/core/SdkClient.scala +3 -1
- package/project/.sdk/tm/scala/utility/Make.scala +5 -1
- package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +5 -1
- package/project/.sdk/tm/zig/core/utility.zig +85 -1
- package/project/.sdk/tm/zig/test/gotcha_test.zig +8 -8
- package/project/sdkgen-package.json +57 -0
- package/src/action/action.ts +136 -6
- package/src/action/check.ts +495 -0
- package/src/action/dispatch.ts +105 -0
- package/src/action/docs.ts +310 -0
- package/src/action/doctor.ts +692 -81
- package/src/action/feature.ts +109 -17
- package/src/action/kind.ts +375 -0
- package/src/action/package.ts +1173 -0
- package/src/action/resolve.ts +362 -0
- package/src/action/target.ts +290 -108
- package/src/cmp/Docs.ts +125 -0
- package/src/cmp/ExternalDocs.ts +54 -0
- package/src/cmp/ReadmeTop.ts +6 -1
- package/src/helpers/definition.ts +82 -0
- package/src/helpers/featureSource.ts +83 -13
- package/src/helpers/junk.ts +168 -0
- package/src/helpers/manifest.ts +474 -0
- package/src/helpers/modelcheck.ts +262 -0
- package/src/helpers/semver.ts +211 -0
- package/src/helpers/shipped.ts +41 -0
- package/src/helpers/stdrep.ts +70 -0
- package/src/sdkgen.ts +294 -66
- package/src/types.ts +33 -3
- package/src/utility.ts +17 -0
|
@@ -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
|
|
|
@@ -98,18 +98,96 @@ pub fn make_options_util(ctx: &Rc<Context>) -> Value {
|
|
|
98
98
|
]),
|
|
99
99
|
),
|
|
100
100
|
("clean", jo(vec![("keys", Value::str("key,token,id"))])),
|
|
101
|
+
// Server-variable values for a templated base URL (OpenAPI server
|
|
102
|
+
// variables): {name} placeholders in "base" are substituted from this
|
|
103
|
+
// map at construction. Spec defaults arrive via the generated config;
|
|
104
|
+
// user values override them. Mirrors go's make_options optspec.
|
|
105
|
+
("server", jo(vec![("`$CHILD`", Value::str(""))])),
|
|
101
106
|
]);
|
|
102
107
|
|
|
103
108
|
// Preserve system.fetch before merge/validate (validation strips it).
|
|
104
109
|
let sys_fetch = getpath(&["system", "fetch"], &opts);
|
|
105
110
|
|
|
106
|
-
|
|
111
|
+
// CLONE the config side: `config` is a per-thread singleton
|
|
112
|
+
// (core::config::shared_config) and merge uses its nested maps as merge
|
|
113
|
+
// TARGETS, so without this one client's options (headers, server, ...) are
|
|
114
|
+
// written into the shared config and inherited by every client after it.
|
|
115
|
+
let merged = vs::merge(
|
|
116
|
+
&ja(vec![Value::empty_map(), vs::clone(&cfgopts), opts.clone()]), None);
|
|
107
117
|
if let Ok(validated) = vs::validate(&merged, &optspec, None) {
|
|
108
118
|
if let Value::Map(_) = validated {
|
|
109
119
|
opts = validated;
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
122
|
|
|
123
|
+
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
124
|
+
// Every placeholder must resolve to a non-empty value: from options.server
|
|
125
|
+
// (user), else the Config default. A placeholder that resolves to "" is a
|
|
126
|
+
// construction ERROR in live mode - the URL cannot work - but in test mode
|
|
127
|
+
// substitutes the deterministic value "test-<name>" so offline tests need
|
|
128
|
+
// no configuration. The SDK constructor has no error return, so a missing
|
|
129
|
+
// required variable PANICS: construction-time misconfiguration.
|
|
130
|
+
//
|
|
131
|
+
// Scanned by hand rather than with vs::re_replace, whose replacement is a
|
|
132
|
+
// fixed string and cannot vary per placeholder.
|
|
133
|
+
if let Value::Str(base) = getp(&opts, "base") {
|
|
134
|
+
if base.contains('{') {
|
|
135
|
+
let testmode = matches!(getpath(&["test", "active"], &opts), Value::Bool(true))
|
|
136
|
+
|| matches!(
|
|
137
|
+
getpath(&["feature", "test", "active"], &opts),
|
|
138
|
+
Value::Bool(true)
|
|
139
|
+
);
|
|
140
|
+
let server = getp(&opts, "server");
|
|
141
|
+
let sdkname = match getpath(&["main", "name"], &config) {
|
|
142
|
+
Value::Str(s) if !s.is_empty() => s,
|
|
143
|
+
_ => "SDK".to_string(),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
let mut resolved = String::with_capacity(base.len());
|
|
147
|
+
let bytes: Vec<char> = base.chars().collect();
|
|
148
|
+
let mut i = 0usize;
|
|
149
|
+
while i < bytes.len() {
|
|
150
|
+
if '{' != bytes[i] {
|
|
151
|
+
resolved.push(bytes[i]);
|
|
152
|
+
i += 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// A placeholder only when it closes and the name is
|
|
156
|
+
// [A-Za-z0-9_]+; anything else is literal text.
|
|
157
|
+
let mut j = i + 1;
|
|
158
|
+
while j < bytes.len() && (bytes[j].is_ascii_alphanumeric() || '_' == bytes[j]) {
|
|
159
|
+
j += 1;
|
|
160
|
+
}
|
|
161
|
+
if j >= bytes.len() || '}' != bytes[j] || j == i + 1 {
|
|
162
|
+
resolved.push(bytes[i]);
|
|
163
|
+
i += 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
let name: String = bytes[i + 1..j].iter().collect();
|
|
167
|
+
let val = match getp(&server, &name) {
|
|
168
|
+
Value::Str(s) => s,
|
|
169
|
+
_ => String::new(),
|
|
170
|
+
};
|
|
171
|
+
if val.is_empty() {
|
|
172
|
+
if testmode {
|
|
173
|
+
resolved.push_str(&format!("test-{}", name));
|
|
174
|
+
} else {
|
|
175
|
+
panic!(
|
|
176
|
+
"{}: the server variable '{}' is required: the API base \
|
|
177
|
+
URL is '{}' - pass server: {{ \"{}\": \"...\" }} in the \
|
|
178
|
+
SDK options",
|
|
179
|
+
sdkname, name, base, name
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
resolved.push_str(&val);
|
|
184
|
+
}
|
|
185
|
+
i = j + 1;
|
|
186
|
+
}
|
|
187
|
+
setp(&opts, "base", Value::str(&resolved));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
113
191
|
// Restore system.fetch.
|
|
114
192
|
if !sys_fetch.is_noval() {
|
|
115
193
|
let sys = getp(&opts, "system");
|
|
@@ -16,7 +16,9 @@ abstract class SdkClient(options0: JMap[String, Object]) {
|
|
|
16
16
|
protected var rootctx: Context = null
|
|
17
17
|
|
|
18
18
|
locally {
|
|
19
|
-
|
|
19
|
+
// The process-wide config (sdkgen rung L2): read-only on the request path,
|
|
20
|
+
// so every client shares one rather than rebuilding it.
|
|
21
|
+
val config = Config.sharedConfig()
|
|
20
22
|
|
|
21
23
|
val ctxmap = new LinkedHashMap[String, Object]()
|
|
22
24
|
ctxmap.put("client", this)
|
|
@@ -216,7 +216,11 @@ object MakeOptions {
|
|
|
216
216
|
|
|
217
217
|
val mergeList = new ArrayList[Object]()
|
|
218
218
|
mergeList.add(new LinkedHashMap[String, Object]())
|
|
219
|
-
|
|
219
|
+
// CLONE the config side: `config` is a process-wide singleton
|
|
220
|
+
// (Config.sharedConfig) and merge uses its nested maps as merge TARGETS, so
|
|
221
|
+
// without this one client's options (headers, server, ...) are written into
|
|
222
|
+
// the shared config and inherited by every client constructed afterwards.
|
|
223
|
+
mergeList.add(Struct.clone(cfgopts))
|
|
220
224
|
mergeList.add(opts)
|
|
221
225
|
val merged = Struct.merge(mergeList)
|
|
222
226
|
|
|
@@ -98,7 +98,11 @@ func makeOptionsUtil(_ ctx: Context) -> VMap {
|
|
|
98
98
|
// validation reshapes the system block).
|
|
99
99
|
let sysFetch = gpath(opts, "system", "fetch")
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
// CLONE the config side: `config` is a process-wide singleton
|
|
102
|
+
// (SdkConfig.sharedConfig) and merge uses its nested maps as merge TARGETS,
|
|
103
|
+
// so without this one client's options (headers, server, ...) are written
|
|
104
|
+
// into the shared config and inherited by every client built afterwards.
|
|
105
|
+
let merged = merge(.list([.map(VMap()), clone(.map(cfgopts)), .map(opts)]))
|
|
102
106
|
let validated = validate(merged, optspec)
|
|
103
107
|
let result = validated.asMap ?? VMap()
|
|
104
108
|
|
|
@@ -35,6 +35,14 @@ fn fmt(comptime f: []const u8, args: anytype) []const u8 {
|
|
|
35
35
|
return std.fmt.allocPrint(h.A(), f, args) catch "";
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Boolean-or-absent: an option that is unset, null or a non-boolean is false.
|
|
39
|
+
fn is_true(v: Value) bool {
|
|
40
|
+
return switch (v) {
|
|
41
|
+
.bool => |b| b,
|
|
42
|
+
else => false,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
// ============================================================================
|
|
39
47
|
// Utility bundle
|
|
40
48
|
// ============================================================================
|
|
@@ -368,17 +376,93 @@ pub fn make_options_util(ctx: *Context) Value {
|
|
|
368
376
|
.{ "entity", h.jo(&.{.{ "`$OPEN`", h.vbool(true) }}) },
|
|
369
377
|
}) },
|
|
370
378
|
.{ "clean", h.jo(&.{.{ "keys", h.vstr("key,token,id") }}) },
|
|
379
|
+
// Server-variable values for a templated base URL (OpenAPI server
|
|
380
|
+
// variables): {name} placeholders in "base" are substituted from this
|
|
381
|
+
// map at construction. Spec defaults arrive via the generated config;
|
|
382
|
+
// user values override them. Mirrors go's make_options optspec.
|
|
383
|
+
.{ "server", h.jo(&.{.{ "`$CHILD`", h.vstr("") }}) },
|
|
371
384
|
});
|
|
372
385
|
|
|
373
386
|
// Preserve system.fetch before merge/validate (validation strips it).
|
|
374
387
|
const sys_fetch = h.getpath(&.{ "system", "fetch" }, opts);
|
|
375
388
|
|
|
376
|
-
|
|
389
|
+
// CLONE the config side: `config` is a process-wide singleton
|
|
390
|
+
// (config.shared_config) and merge uses its nested maps as merge TARGETS,
|
|
391
|
+
// so without this one client's options (headers, server, ...) are written
|
|
392
|
+
// into the shared config and inherited by every client built after it.
|
|
393
|
+
const merged = vs.merge(h.A(), h.ja(&.{ h.omap(), h.clone(cfgopts), opts }), vs.MAXDEPTH) catch opts;
|
|
377
394
|
const vres = vs.validate(h.A(), merged, optspec) catch null;
|
|
378
395
|
if (vres) |vr| {
|
|
379
396
|
if (vr.err == null and vr.out == .object) opts = vr.out;
|
|
380
397
|
}
|
|
381
398
|
|
|
399
|
+
// Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
|
|
400
|
+
// Every placeholder must resolve to a non-empty value: from options.server
|
|
401
|
+
// (user), else the Config default. A placeholder that resolves to "" is a
|
|
402
|
+
// construction ERROR in live mode - the URL cannot work - but in test mode
|
|
403
|
+
// substitutes the deterministic value "test-<name>" so offline tests need
|
|
404
|
+
// no configuration. The SDK constructor has no error return, so a missing
|
|
405
|
+
// required variable PANICS: construction-time misconfiguration.
|
|
406
|
+
//
|
|
407
|
+
// Scanned by hand: a placeholder is `{` followed by [A-Za-z0-9_]+ and `}`;
|
|
408
|
+
// anything else is literal text, so a stray brace in a URL is left alone.
|
|
409
|
+
switch (h.getp(opts, "base")) {
|
|
410
|
+
.string => |base| {
|
|
411
|
+
if (null != std.mem.indexOfScalar(u8, base, '{')) {
|
|
412
|
+
const testmode =
|
|
413
|
+
is_true(h.getpath(&.{ "test", "active" }, opts)) or
|
|
414
|
+
is_true(h.getpath(&.{ "feature", "test", "active" }, opts));
|
|
415
|
+
const server = h.getp(opts, "server");
|
|
416
|
+
const sdkname: []const u8 = switch (h.getpath(&.{ "main", "name" }, config)) {
|
|
417
|
+
.string => |s| if (0 < s.len) s else "SDK",
|
|
418
|
+
else => "SDK",
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
var out = std.ArrayList(u8).init(h.A());
|
|
422
|
+
var i: usize = 0;
|
|
423
|
+
while (i < base.len) {
|
|
424
|
+
if ('{' != base[i]) {
|
|
425
|
+
out.append(base[i]) catch {};
|
|
426
|
+
i += 1;
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
var j = i + 1;
|
|
430
|
+
while (j < base.len and (std.ascii.isAlphanumeric(base[j]) or '_' == base[j])) {
|
|
431
|
+
j += 1;
|
|
432
|
+
}
|
|
433
|
+
if (j >= base.len or '}' != base[j] or j == i + 1) {
|
|
434
|
+
out.append(base[i]) catch {};
|
|
435
|
+
i += 1;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
const name = base[i + 1 .. j];
|
|
439
|
+
const val: []const u8 = switch (h.getp(server, name)) {
|
|
440
|
+
.string => |s| s,
|
|
441
|
+
else => "",
|
|
442
|
+
};
|
|
443
|
+
if (0 == val.len) {
|
|
444
|
+
if (testmode) {
|
|
445
|
+
out.appendSlice("test-") catch {};
|
|
446
|
+
out.appendSlice(name) catch {};
|
|
447
|
+
} else {
|
|
448
|
+
std.debug.panic(
|
|
449
|
+
"{s}: the server variable '{s}' is required: the API " ++
|
|
450
|
+
"base URL is '{s}' - pass .server = .{{ .{s} = \"...\" }} " ++
|
|
451
|
+
"in the SDK options",
|
|
452
|
+
.{ sdkname, name, base, name },
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
} else {
|
|
456
|
+
out.appendSlice(val) catch {};
|
|
457
|
+
}
|
|
458
|
+
i = j + 1;
|
|
459
|
+
}
|
|
460
|
+
h.setp(opts, "base", h.vstr(out.toOwnedSlice() catch base));
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
else => {},
|
|
464
|
+
}
|
|
465
|
+
|
|
382
466
|
// Restore system.fetch.
|
|
383
467
|
if (!h.is_noval(sys_fetch)) {
|
|
384
468
|
const sysv = h.getp(opts, "system");
|
|
@@ -100,7 +100,7 @@ test "gotcha8: custom utility callable survives" {
|
|
|
100
100
|
const fn_val = h.callable(@ptrCast(&probe_dummy), probeCall);
|
|
101
101
|
const opts = h.jo(&.{.{ "utility", h.jo(&.{.{ "probe", fn_val }}) }});
|
|
102
102
|
const client = sdk.SDK.new(opts);
|
|
103
|
-
const stored = h.getp(client.
|
|
103
|
+
const stored = h.getp(client.sdkUtility.custom, "probe");
|
|
104
104
|
try testing.expect(stored == .function);
|
|
105
105
|
const r = h.call_vfn(stored, h.vstr("x"));
|
|
106
106
|
try testing.expect(r == .string and std.mem.eql(u8, r.string, "x"));
|
|
@@ -139,10 +139,10 @@ test "gotcha3: many features" {
|
|
|
139
139
|
|
|
140
140
|
test "gotcha2: make_point surfaces out.point error" {
|
|
141
141
|
const client = sdk.new();
|
|
142
|
-
const ctx = client.
|
|
142
|
+
const ctx = client.sdkUtility.make_context(.{ .opname = "list" }, client.get_root_ctx());
|
|
143
143
|
const e = ctx.make_error("rbac_denied", "denied");
|
|
144
144
|
ctx.out_set("point", sdk.OutVal{ .err = e });
|
|
145
|
-
try testing.expectError(error.Sdk, client.
|
|
145
|
+
try testing.expectError(error.Sdk, client.sdkUtility.make_point(ctx));
|
|
146
146
|
try testing.expect(ctx.pending_err != null);
|
|
147
147
|
try testing.expect(std.mem.eql(u8, ctx.pending_err.?.code, "rbac_denied"));
|
|
148
148
|
}
|
|
@@ -152,8 +152,8 @@ test "gotcha2: make_point surfaces out.point error" {
|
|
|
152
152
|
test "gotcha4: featureAdd before ordering" {
|
|
153
153
|
const client = sdk.new();
|
|
154
154
|
const ctx = client.get_root_ctx();
|
|
155
|
-
client.
|
|
156
|
-
client.
|
|
155
|
+
client.sdkUtility.feature_add(ctx, OrderFeat.make("aaa", vnull()));
|
|
156
|
+
client.sdkUtility.feature_add(ctx, OrderFeat.make("bbb", h.jo(&.{.{ "__before__", h.vstr("aaa") }})));
|
|
157
157
|
var ai: usize = 999;
|
|
158
158
|
var bi: usize = 999;
|
|
159
159
|
for (client.features.items, 0..) |f, i| {
|
|
@@ -176,7 +176,7 @@ test "gotcha6: netsim offline error code" {
|
|
|
176
176
|
});
|
|
177
177
|
const client = sdk.SDK.new(opts);
|
|
178
178
|
const ctx = client.get_root_ctx();
|
|
179
|
-
const r = client.
|
|
179
|
+
const r = client.sdkUtility.fetch(ctx, "http://x", h.jo(&.{.{ "url", h.vstr("http://x") }}));
|
|
180
180
|
try testing.expectError(error.Sdk, r);
|
|
181
181
|
try testing.expect(std.mem.eql(u8, ctx.pending_err.?.code, "netsim_offline"));
|
|
182
182
|
}
|
|
@@ -192,7 +192,7 @@ test "gotcha6: netsim conn error code" {
|
|
|
192
192
|
});
|
|
193
193
|
const client = sdk.SDK.new(opts);
|
|
194
194
|
const ctx = client.get_root_ctx();
|
|
195
|
-
const r = client.
|
|
195
|
+
const r = client.sdkUtility.fetch(ctx, "http://x", h.jo(&.{.{ "url", h.vstr("http://x") }}));
|
|
196
196
|
try testing.expectError(error.Sdk, r);
|
|
197
197
|
try testing.expect(std.mem.eql(u8, ctx.pending_err.?.code, "netsim_conn"));
|
|
198
198
|
}
|
|
@@ -215,7 +215,7 @@ test "gotcha6: netsim seeded latency is deterministic" {
|
|
|
215
215
|
const ctx = client.get_root_ctx();
|
|
216
216
|
var i: usize = 0;
|
|
217
217
|
while (i < 4) : (i += 1) {
|
|
218
|
-
_ = client.
|
|
218
|
+
_ = client.sdkUtility.fetch(ctx, "http://x", h.jo(&.{.{ "url", h.vstr("http://x") }})) catch {};
|
|
219
219
|
}
|
|
220
220
|
return sleep_log.toOwnedSlice() catch &.{};
|
|
221
221
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sdkgen": {
|
|
3
|
+
"package": 1
|
|
4
|
+
},
|
|
5
|
+
"name": "@voxgig/sdkgen",
|
|
6
|
+
"version": "3.5.0",
|
|
7
|
+
"provides": {
|
|
8
|
+
"target": [
|
|
9
|
+
"c",
|
|
10
|
+
"clojure",
|
|
11
|
+
"cpp",
|
|
12
|
+
"csharp",
|
|
13
|
+
"dart",
|
|
14
|
+
"elixir",
|
|
15
|
+
"go",
|
|
16
|
+
"go-cli",
|
|
17
|
+
"go-mcp",
|
|
18
|
+
"haskell",
|
|
19
|
+
"java",
|
|
20
|
+
"js",
|
|
21
|
+
"kotlin",
|
|
22
|
+
"lean",
|
|
23
|
+
"lua",
|
|
24
|
+
"ocaml",
|
|
25
|
+
"perl",
|
|
26
|
+
"php",
|
|
27
|
+
"py",
|
|
28
|
+
"py-data",
|
|
29
|
+
"rb",
|
|
30
|
+
"rust",
|
|
31
|
+
"scala",
|
|
32
|
+
"seneca-provider",
|
|
33
|
+
"swift",
|
|
34
|
+
"ts",
|
|
35
|
+
"zig"
|
|
36
|
+
],
|
|
37
|
+
"feature": [
|
|
38
|
+
"audit",
|
|
39
|
+
"cache",
|
|
40
|
+
"clienttrack",
|
|
41
|
+
"debug",
|
|
42
|
+
"idempotency",
|
|
43
|
+
"log",
|
|
44
|
+
"metrics",
|
|
45
|
+
"netsim",
|
|
46
|
+
"paging",
|
|
47
|
+
"proxy",
|
|
48
|
+
"ratelimit",
|
|
49
|
+
"rbac",
|
|
50
|
+
"retry",
|
|
51
|
+
"streaming",
|
|
52
|
+
"telemetry",
|
|
53
|
+
"test",
|
|
54
|
+
"timeout"
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/action/action.ts
CHANGED
|
@@ -12,6 +12,40 @@ import type {
|
|
|
12
12
|
} from '../types'
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
const indexEntry = (name: string) => `@"${name}.aontu"`
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// An index line that is an ACTIVE include, and the name it includes — or
|
|
19
|
+
// undefined for a blank line, a comment, or anything else.
|
|
20
|
+
//
|
|
21
|
+
// Parsed rather than compared as a string, because both spellings around an
|
|
22
|
+
// include are legal aontu and mean opposite things:
|
|
23
|
+
//
|
|
24
|
+
// @"go.aontu" -> active, name 'go'
|
|
25
|
+
// @"go.aontu" # pinned -> active, name 'go' (trailing comment)
|
|
26
|
+
// @"go.aontu" -> active, name 'go' (indented)
|
|
27
|
+
// # @"go.aontu" -> NOT active
|
|
28
|
+
//
|
|
29
|
+
// A substring test (what this used to be) reads the commented-out form as
|
|
30
|
+
// present, so `target add go` on a project that had switched the target off
|
|
31
|
+
// by hand appended nothing and reported success while the target stayed
|
|
32
|
+
// absent from the model. A whole-line equality test fixes that but then
|
|
33
|
+
// misses the trailing-comment form, and appends a SECOND active include.
|
|
34
|
+
const INDEX_ENTRY_RE = /^\s*@"([^"]+)\.aontu"\s*(?:#.*)?$/
|
|
35
|
+
|
|
36
|
+
function indexEntryName(line: string): string | undefined {
|
|
37
|
+
const m = line.match(INDEX_ENTRY_RE)
|
|
38
|
+
return null == m ? undefined : m[1]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
// Is this name already included by the index?
|
|
43
|
+
function hasIndexEntry(content: string, name: string): boolean {
|
|
44
|
+
return content.split('\n')
|
|
45
|
+
.some((line: string) => indexEntryName(line) === name)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
15
49
|
// Append `@"<name>.aontu"` import lines for each name not already present in
|
|
16
50
|
// the index content. Checking against the accumulating result (not the
|
|
17
51
|
// original) means duplicate names in the same call are added at most once.
|
|
@@ -19,9 +53,8 @@ function appendIndexEntries(content: string, names: string[]): string {
|
|
|
19
53
|
let out = content
|
|
20
54
|
|
|
21
55
|
for (const n of names) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
out += '\n' + entry
|
|
56
|
+
if (!hasIndexEntry(out, n)) {
|
|
57
|
+
out += '\n' + indexEntry(n)
|
|
25
58
|
}
|
|
26
59
|
}
|
|
27
60
|
|
|
@@ -29,6 +62,25 @@ function appendIndexEntries(content: string, names: string[]): string {
|
|
|
29
62
|
}
|
|
30
63
|
|
|
31
64
|
|
|
65
|
+
// Drop the `@"<name>.aontu"` line for each name — the inverse of
|
|
66
|
+
// appendIndexEntries, matching line-exact for the same reasons.
|
|
67
|
+
//
|
|
68
|
+
// Nothing calls this yet: a `remove` action is the fast-follow this exists
|
|
69
|
+
// for (see docs/design/sdkgen-packages.md), and it is written here beside its
|
|
70
|
+
// inverse so the two cannot drift on how an entry is recognised.
|
|
71
|
+
function removeIndexEntries(content: string, names: string[]): string {
|
|
72
|
+
const drop = new Set(names)
|
|
73
|
+
|
|
74
|
+
return content
|
|
75
|
+
.split('\n')
|
|
76
|
+
.filter((line: string) => {
|
|
77
|
+
const name = indexEntryName(line)
|
|
78
|
+
return undefined === name || !drop.has(name)
|
|
79
|
+
})
|
|
80
|
+
.join('\n')
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
|
|
32
84
|
const UpdateIndex = cmp(function UpdateIndex(props: any) {
|
|
33
85
|
Content(appendIndexEntries(props.content, props.names))
|
|
34
86
|
})
|
|
@@ -45,7 +97,20 @@ function parseAddNames(args: any[]): string[] {
|
|
|
45
97
|
}
|
|
46
98
|
|
|
47
99
|
|
|
48
|
-
|
|
100
|
+
// The current index file for each kind, which `UpdateIndex` appends to.
|
|
101
|
+
//
|
|
102
|
+
// `seed` IS THE UPGRADE PATH. A project scaffolded before a kind existed has
|
|
103
|
+
// no `model/<kind>/<kind>-index.aontu` — every project alive today is in
|
|
104
|
+
// exactly that position for `docs` — and reading it unguarded made the FIRST
|
|
105
|
+
// `docs add` in any existing project fail on ENOENT before it wrote anything.
|
|
106
|
+
//
|
|
107
|
+
// Seeded per call rather than defaulted for every kind: a missing
|
|
108
|
+
// `target-index.aontu` in a scaffolded project is a broken project, and
|
|
109
|
+
// quietly recreating it would hide that. A kind the project has never used is
|
|
110
|
+
// a different thing, and only its own action knows which case it is in.
|
|
111
|
+
function loadContent(
|
|
112
|
+
actx: ActionContext, which: string | string[], seed?: Record<string, string>,
|
|
113
|
+
) {
|
|
49
114
|
which = Array.isArray(which) ? which : [which]
|
|
50
115
|
|
|
51
116
|
const content: any = {}
|
|
@@ -55,8 +120,9 @@ function loadContent(actx: ActionContext, which: string | string[]) {
|
|
|
55
120
|
|
|
56
121
|
which.map((w: string) => {
|
|
57
122
|
const indexfile = Path.join(modelfolder, w, w + '-index.aontu')
|
|
58
|
-
|
|
59
|
-
content[`${w}_index`] =
|
|
123
|
+
|
|
124
|
+
content[`${w}_index`] = (null != seed?.[w] && !fs.existsSync(indexfile)) ?
|
|
125
|
+
seed[w] : fs.readFileSync(indexfile, 'utf8')
|
|
60
126
|
})
|
|
61
127
|
|
|
62
128
|
return content
|
|
@@ -64,9 +130,73 @@ function loadContent(actx: ActionContext, which: string | string[]) {
|
|
|
64
130
|
|
|
65
131
|
|
|
66
132
|
|
|
133
|
+
// ENSURE THE PROJECT'S OWN MODEL INCLUDES A KIND'S INDEX.
|
|
134
|
+
//
|
|
135
|
+
// `model/sdk.aontu` is written once, by create-sdkgen, and includes the
|
|
136
|
+
// indexes of the kinds that existed then. So a project scaffolded before a
|
|
137
|
+
// kind existed — which is every project alive today, for `docs` — never
|
|
138
|
+
// includes its index, and the item's model file is an orphan: it is on disk,
|
|
139
|
+
// `<kind>-index.aontu` includes it, and NOTHING includes that. `main.kit.docs`
|
|
140
|
+
// is then absent from the compiled model, so `package list`, `package update`
|
|
141
|
+
// and `doctor` cannot see the item at all.
|
|
142
|
+
//
|
|
143
|
+
// Appending one include line is additive and idempotent, and it is the only
|
|
144
|
+
// way an existing project can adopt a new kind without hand-editing. The
|
|
145
|
+
// entry is parsed, not substring-matched, for the reason `hasIndexEntry`
|
|
146
|
+
// documents: a commented-out include means the project switched it OFF, and
|
|
147
|
+
// re-adding it would override that.
|
|
148
|
+
function ensureModelInclude(actx: ActionContext, kind: string): boolean {
|
|
149
|
+
const fs = actx.fs()
|
|
150
|
+
const url = actx.url
|
|
151
|
+
|
|
152
|
+
if (!fs.existsSync(url)) {
|
|
153
|
+
// Said out loud rather than skipped silently: without the include the
|
|
154
|
+
// item is invisible to the next model compile, and a quiet no-op here
|
|
155
|
+
// would look exactly like success.
|
|
156
|
+
actx.log.warn({
|
|
157
|
+
point: 'model-include-absent', kind, file: url,
|
|
158
|
+
note: url + ' not found, so ' + indexEntry(kind + '/' + kind + '-index') +
|
|
159
|
+
' could not be added — add it by hand, or nothing will see any ' +
|
|
160
|
+
kind + ' item'
|
|
161
|
+
})
|
|
162
|
+
return false
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const name = kind + '/' + kind + '-index'
|
|
166
|
+
const content = String(fs.readFileSync(url, 'utf8'))
|
|
167
|
+
|
|
168
|
+
if (hasIndexEntry(content, name)) {
|
|
169
|
+
return false
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (actx.opts?.dryrun) {
|
|
173
|
+
actx.log.info({
|
|
174
|
+
point: 'model-include-dryrun', kind, file: url, entry: indexEntry(name),
|
|
175
|
+
note: '** DRY RUN ** would add ' + indexEntry(name) + ' to ' + url
|
|
176
|
+
})
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fs.writeFileSync(url,
|
|
181
|
+
content + (content.endsWith('\n') ? '' : '\n') +
|
|
182
|
+
indexEntry(name) + '\n')
|
|
183
|
+
|
|
184
|
+
actx.log.info({
|
|
185
|
+
point: 'model-include-added', kind, file: url, entry: indexEntry(name),
|
|
186
|
+
note: url + ': added ' + indexEntry(name) +
|
|
187
|
+
' — without it the project model never sees any ' + kind + ' item'
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
67
194
|
export {
|
|
68
195
|
UpdateIndex,
|
|
196
|
+
ensureModelInclude,
|
|
69
197
|
appendIndexEntries,
|
|
198
|
+
removeIndexEntries,
|
|
199
|
+
hasIndexEntry,
|
|
70
200
|
parseAddNames,
|
|
71
201
|
loadContent
|
|
72
202
|
}
|