@voxgig/sdkgen 4.19.0 → 4.20.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 (112) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/AgentGuideContent.js +1 -1
  3. package/dist/sdkgen.d.ts +2 -2
  4. package/dist/sdkgen.js +6 -3
  5. package/dist/sdkgen.js.map +1 -1
  6. package/dist/testkit.js +2 -2
  7. package/dist/testkit.js.map +1 -1
  8. package/dist/tsconfig.tsbuildinfo +1 -1
  9. package/dist/utility.d.ts +4 -1
  10. package/dist/utility.js +60 -0
  11. package/dist/utility.js.map +1 -1
  12. package/model/sdkgen.aon +10 -0
  13. package/package.json +1 -1
  14. package/project/.sdk/model/feature/feature-index.aon +20 -20
  15. package/project/.sdk/src/cmp/c/Config_c.ts +15 -0
  16. package/project/.sdk/src/cmp/c/Main_c.ts +15 -0
  17. package/project/.sdk/src/cmp/c/PrepareAuth_c.ts +371 -0
  18. package/project/.sdk/src/cmp/clojure/Config_clojure.ts +47 -1
  19. package/project/.sdk/src/cmp/clojure/Main_clojure.ts +11 -1
  20. package/project/.sdk/src/cmp/clojure/PrepareAuth_clojure.ts +374 -0
  21. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +40 -0
  22. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +17 -0
  23. package/project/.sdk/src/cmp/cpp/PrepareAuth_cpp.ts +441 -0
  24. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +42 -1
  25. package/project/.sdk/src/cmp/csharp/Main_csharp.ts +18 -0
  26. package/project/.sdk/src/cmp/csharp/PrepareAuth_csharp.ts +392 -0
  27. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +36 -1
  28. package/project/.sdk/src/cmp/elixir/Main_elixir.ts +15 -0
  29. package/project/.sdk/src/cmp/elixir/PrepareAuth_elixir.ts +418 -0
  30. package/project/.sdk/src/cmp/go/Config_go.ts +12 -1
  31. package/project/.sdk/src/cmp/go/Main_go.ts +14 -0
  32. package/project/.sdk/src/cmp/go/PrepareAuth_go.ts +322 -0
  33. package/project/.sdk/src/cmp/java/Config_java.ts +20 -1
  34. package/project/.sdk/src/cmp/java/Main_java.ts +14 -0
  35. package/project/.sdk/src/cmp/java/PrepareAuth_java.ts +298 -0
  36. package/project/.sdk/src/cmp/js/Config_js.ts +12 -1
  37. package/project/.sdk/src/cmp/js/Main_js.ts +6 -0
  38. package/project/.sdk/src/cmp/js/PrepareAuth_js.ts +244 -0
  39. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +23 -1
  40. package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +20 -0
  41. package/project/.sdk/src/cmp/kotlin/PrepareAuth_kotlin.ts +440 -0
  42. package/project/.sdk/src/cmp/lua/Config_lua.ts +15 -1
  43. package/project/.sdk/src/cmp/lua/Main_lua.ts +11 -0
  44. package/project/.sdk/src/cmp/lua/PrepareAuth_lua.ts +364 -0
  45. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +31 -1
  46. package/project/.sdk/src/cmp/ocaml/Main_ocaml.ts +19 -0
  47. package/project/.sdk/src/cmp/ocaml/PrepareAuth_ocaml.ts +454 -0
  48. package/project/.sdk/src/cmp/perl/Config_perl.ts +30 -0
  49. package/project/.sdk/src/cmp/perl/Main_perl.ts +14 -0
  50. package/project/.sdk/src/cmp/perl/PrepareAuth_perl.ts +387 -0
  51. package/project/.sdk/src/cmp/php/Config_php.ts +12 -1
  52. package/project/.sdk/src/cmp/php/Main_php.ts +7 -0
  53. package/project/.sdk/src/cmp/php/PrepareAuth_php.ts +325 -0
  54. package/project/.sdk/src/cmp/py/Config_py.ts +12 -1
  55. package/project/.sdk/src/cmp/py/Main_py.ts +12 -0
  56. package/project/.sdk/src/cmp/py/PrepareAuth_py.ts +389 -0
  57. package/project/.sdk/src/cmp/rb/Config_rb.ts +18 -1
  58. package/project/.sdk/src/cmp/rb/Main_rb.ts +10 -0
  59. package/project/.sdk/src/cmp/rb/PrepareAuth_rb.ts +305 -0
  60. package/project/.sdk/src/cmp/rust/Config_rust.ts +41 -1
  61. package/project/.sdk/src/cmp/rust/Main_rust.ts +16 -0
  62. package/project/.sdk/src/cmp/rust/PrepareAuth_rust.ts +410 -0
  63. package/project/.sdk/src/cmp/scala/Config_scala.ts +21 -1
  64. package/project/.sdk/src/cmp/scala/Main_scala.ts +14 -0
  65. package/project/.sdk/src/cmp/scala/PrepareAuth_scala.ts +415 -0
  66. package/project/.sdk/src/cmp/swift/Config_swift.ts +37 -1
  67. package/project/.sdk/src/cmp/swift/Main_swift.ts +10 -0
  68. package/project/.sdk/src/cmp/swift/PrepareAuth_swift.ts +439 -0
  69. package/project/.sdk/src/cmp/ts/Config_ts.ts +12 -1
  70. package/project/.sdk/src/cmp/ts/Main_ts.ts +5 -0
  71. package/project/.sdk/src/cmp/ts/PrepareAuth_ts.ts +237 -0
  72. package/project/.sdk/src/cmp/zig/Config_zig.ts +33 -1
  73. package/project/.sdk/src/cmp/zig/Main_zig.ts +16 -0
  74. package/project/.sdk/src/cmp/zig/PrepareAuth_zig.ts +545 -0
  75. package/project/.sdk/tm/clojure/src/sdk/core.clj +31 -20
  76. package/project/.sdk/tm/cpp/utility/pipeline.hpp +36 -40
  77. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +11 -0
  78. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -32
  79. package/project/.sdk/tm/java/utility/MakeOptions.java +13 -1
  80. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +14 -1
  81. package/project/.sdk/tm/kotlin/utility/Prepare.kt +13 -46
  82. package/project/.sdk/tm/ocaml/Makefile +11 -1
  83. package/project/.sdk/tm/ocaml/sdk_helpers.ml +15 -0
  84. package/project/.sdk/tm/ocaml/sdk_runtime.ml +31 -25
  85. package/project/.sdk/tm/ocaml/test/feature/secrets/t_secrets.ml +125 -12
  86. package/project/.sdk/tm/ocaml/test/t_pipeline.ml +64 -6
  87. package/project/.sdk/tm/perl/utility/make_options.pm +10 -1
  88. package/project/.sdk/tm/php/utility/MakeOptions.php +8 -1
  89. package/project/.sdk/tm/rust/utility/make_options.rs +13 -1
  90. package/project/.sdk/tm/scala/utility/Make.scala +14 -1
  91. package/project/.sdk/tm/scala/utility/Prepare.scala +16 -40
  92. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +30 -3
  93. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/Prepare.swift +21 -39
  94. package/project/.sdk/tm/zig/core/utility.zig +37 -45
  95. package/project/.sdk/tm/zig/test/pipeline_test.zig +34 -0
  96. package/project/sdkgen-package.json +1 -1
  97. package/src/cmp/AgentGuideContent.ts +1 -1
  98. package/src/sdkgen.ts +4 -1
  99. package/src/testkit.ts +2 -2
  100. package/src/utility.ts +75 -0
  101. package/project/.sdk/tm/c/utility/prepare_auth.c +0 -65
  102. package/project/.sdk/tm/csharp/utility/PrepareAuth.cs +0 -54
  103. package/project/.sdk/tm/go/utility/prepare_auth.go +0 -59
  104. package/project/.sdk/tm/java/utility/PrepareAuth.java +0 -66
  105. package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +0 -49
  106. package/project/.sdk/tm/lua/utility/prepare_auth.lua +0 -52
  107. package/project/.sdk/tm/perl/utility/prepare_auth.pm +0 -56
  108. package/project/.sdk/tm/php/utility/PrepareAuth.php +0 -45
  109. package/project/.sdk/tm/py/pkg/utility/prepare_auth.py +0 -46
  110. package/project/.sdk/tm/rb/utility/prepare_auth.rb +0 -35
  111. package/project/.sdk/tm/rust/utility/prepare_auth.rs +0 -65
  112. package/project/.sdk/tm/ts/src/utility/PrepareAuthUtility.ts +0 -78
@@ -0,0 +1,387 @@
1
+
2
+ import {
3
+ Content,
4
+ File,
5
+ Folder,
6
+ cmp,
7
+ isAuthSuppressed,
8
+ isHttpBasicAuth,
9
+ resolveAuthIn,
10
+ resolveAuthName,
11
+ } from '@voxgig/sdkgen'
12
+
13
+
14
+ import {
15
+ perlStringLiteral,
16
+ } from './utility_perl'
17
+
18
+
19
+ // WHERE THE CREDENTIAL GOES IS A FACT ABOUT THE API, so it is generated
20
+ // rather than templated. The perl peer of cmp/ts/PrepareAuth_ts.ts.
21
+ //
22
+ // This was a static file at `tm/perl/utility/prepare_auth.pm` that opened
23
+ // with `my $HEADER_AUTH = 'authorization';` and never looked further.
24
+ // apidef has always resolved the scheme's `in` and `name` into
25
+ // `main.kit.info.security` — joplin's says `in: "query", name: "token"` —
26
+ // and generation dropped both. The result was an SDK that sent a header
27
+ // the API does not read and never sent the query parameter it does, so it
28
+ // could not authenticate at all. Four repos in the cedar fleet shipped
29
+ // that way: joplin (`token`), pipedrive (`api_token`), trello (`key`),
30
+ // lm-umbrella (`apiKey`).
31
+ //
32
+ // A template cannot fix this, because the three placements need three
33
+ // different bodies and a template has to pick one. A component emits the
34
+ // branch this API actually uses and nothing else — no dead query code in
35
+ // a bearer-token SDK, and no runtime `if` on a value that is fixed at
36
+ // generation time.
37
+ const PrepareAuth = cmp(async function PrepareAuth(props: any) {
38
+ const { model } = props.ctx$
39
+
40
+ // `!isAuthSuppressed`, NOT `isAuthActive`. The latter is also false when
41
+ // the SPEC merely declares no security scheme (`main.kit.info.auth:
42
+ // false`), and those SDKs still carry a credential: optspec always
43
+ // declares `apikey` and makeOptions fills `options.auth` from its
44
+ // defaults, so the runtime guard never fired and they have always sent
45
+ // it. Only an explicit `main.kit.config.auth.active: false` means "no
46
+ // credential, ever", which is what isAuthSuppressed reads.
47
+ const active = !isAuthSuppressed(model)
48
+ const where = resolveAuthIn(model)
49
+ const name = resolveAuthName(model)
50
+ const basic = isHttpBasicAuth(model)
51
+
52
+ // EXACTLY ONE folder, and it is opened HERE.
53
+ //
54
+ // Unlike ts — whose Main opens `Folder({name:'src'})` around Config,
55
+ // SdkError and this component — perl's Main calls PrepareAuth at the
56
+ // TOP LEVEL, with no folder open. The template's own path says what the
57
+ // layout is: `tm/perl/utility/prepare_auth.pm`, copied by Main's
58
+ // blanket `Copy({from:'tm/perl'})` (which excludes only `src/`), lands
59
+ // at `<sdk>/utility/prepare_auth.pm`. perl has no `src/` tree at all —
60
+ // Main writes `lib/`, `config.pm` and `features.pm` straight into the
61
+ // root. So this component must open `utility` and nothing above it:
62
+ // opening a second folder would write `src/utility/` or `./utility/`,
63
+ // a path nothing requires, while the copied file kept being loaded.
64
+ Folder({ name: 'utility' }, () => {
65
+ File({ name: 'prepare_auth.pm' }, () => {
66
+ Content(render({ Name: model.const.Name, active, where, name, basic }))
67
+ })
68
+ })
69
+ })
70
+
71
+
72
+ type AuthSpec = {
73
+ Name: string,
74
+ active: boolean,
75
+ where: string,
76
+ name: string,
77
+ basic: boolean,
78
+ }
79
+
80
+
81
+ // PERL KEEPS ITS HEADER KEYS LOWERCASE, and here that is load-bearing
82
+ // rather than cosmetic. The template's constant was `'authorization'`,
83
+ // and two other generated perl files address that exact spelling:
84
+ // `feature/secrets_feature.pm` rewrites `$headers->{'authorization'}` at
85
+ // the transport seam (it rebuilds the header the way prepare_auth does,
86
+ // so the two must not drift), and `t/pipeline.t` asserts on
87
+ // `$ctx->{spec}{headers}{authorization}`. Emitting the resolved name
88
+ // verbatim would give every default SDK `Authorization` and silently
89
+ // split those three into two different hash keys.
90
+ //
91
+ // Header field names are case-insensitive on the wire (and HTTP/2
92
+ // requires them lowercase), so lowercasing costs nothing and keeps a
93
+ // header-based SDK behaving exactly as it did. A QUERY PARAMETER and a
94
+ // COOKIE name are case-sensitive, and go in verbatim.
95
+ function credName(spec: AuthSpec): string {
96
+ return 'header' === spec.where ? spec.name.toLowerCase() : spec.name
97
+ }
98
+
99
+
100
+ function render(spec: AuthSpec): string {
101
+ return spec.active ? renderActive(spec) : renderInactive(spec)
102
+ }
103
+
104
+
105
+ // NO AUTH AT ALL. A public API's SDK gets a prepare_auth that is honest
106
+ // about it rather than one that deletes a header nobody set.
107
+ function renderInactive(spec: AuthSpec): string {
108
+ return `# ${spec.Name} SDK utility: prepare_auth
109
+
110
+ use strict;
111
+ use warnings;
112
+
113
+ use File::Basename ();
114
+ use Cwd ();
115
+
116
+ my $__dir;
117
+ BEGIN { $__dir = File::Basename::dirname(Cwd::abs_path(__FILE__)) }
118
+ require(Cwd::abs_path("$__dir/../lib/Voxgig/Struct.pm"));
119
+ require(Cwd::abs_path("$__dir/../core/helpers.pm"));
120
+
121
+ package ${spec.Name}Utilities;
122
+
123
+ our %REGISTRY;
124
+
125
+ # This API declares no authentication, so there is no credential to
126
+ # place. The entry stays in the registry because make_spec calls it
127
+ # unconditionally, and still guards the spec so the auth_no_spec contract
128
+ # holds for every SDK.
129
+ $REGISTRY{prepare_auth} = sub {
130
+ my ($ctx) = @_;
131
+ my $spec = $ctx->{spec};
132
+ return (undef, $ctx->make_error('auth_no_spec',
133
+ 'Expected context spec property to be defined.')) unless $spec;
134
+
135
+ return ($spec, undef);
136
+ };
137
+
138
+ 1;
139
+ `
140
+ }
141
+
142
+
143
+ function renderActive(spec: AuthSpec): string {
144
+ const cred = perlStringLiteral(credName(spec))
145
+
146
+ // HTTP Basic is header-only by definition: the scheme is
147
+ // `Authorization: Basic base64(user:pass)`. It cannot be expressed as a
148
+ // query parameter or a cookie, so the branch — and the core module it
149
+ // needs — are emitted only where they can mean something.
150
+ const wantBasic = spec.basic && 'header' === spec.where
151
+
152
+ const use64 = wantBasic ? `use MIME::Base64 ();\n` : ''
153
+ const secretConst = wantBasic ?
154
+ `my $OPTION_SECRET = 'secret';\n` : ''
155
+
156
+ return `# ${spec.Name} SDK utility: prepare_auth
157
+
158
+ use strict;
159
+ use warnings;
160
+
161
+ use File::Basename ();
162
+ use Cwd ();
163
+ ${use64}
164
+ my $__dir;
165
+ BEGIN { $__dir = File::Basename::dirname(Cwd::abs_path(__FILE__)) }
166
+ require(Cwd::abs_path("$__dir/../lib/Voxgig/Struct.pm"));
167
+ require(Cwd::abs_path("$__dir/../core/helpers.pm"));
168
+
169
+ package ${spec.Name}Utilities;
170
+
171
+ our %REGISTRY;
172
+
173
+ ${placementNote(spec)}my $CRED_NAME = ${cred};
174
+ my $OPTION_APIKEY = 'apikey';
175
+ ${secretConst}my $NOT_FOUND = '__NOTFOUND__';
176
+
177
+ $REGISTRY{prepare_auth} = sub {
178
+ my ($ctx) = @_;
179
+ my $spec = $ctx->{spec};
180
+ return (undef, $ctx->make_error('auth_no_spec',
181
+ 'Expected context spec property to be defined.')) unless $spec;
182
+
183
+ ${bag(spec)} my $options = $ctx->{client}->options_map;
184
+
185
+ # Public APIs that need no auth omit the options.auth block entirely.
186
+ ${noCredNote(spec)} if (!defined ${spec.Name}Helpers::gp($options, 'auth')) {
187
+ ${clear(spec, ' ')} return ($spec, undef);
188
+ }
189
+
190
+ my $apikey = Voxgig::Struct::getprop($options, $OPTION_APIKEY, $NOT_FOUND);
191
+ ${basicBlock(spec, wantBasic)}${credBlock(spec)}
192
+ return ($spec, undef);
193
+ };
194
+
195
+ 1;
196
+ `
197
+ }
198
+
199
+
200
+ // The missing-credential test, verbatim from the template so the four
201
+ // "no credential" shapes it distinguishes (undef, the NONE sentinel, a
202
+ // JSON null, the not-found marker or an empty string) keep behaving
203
+ // identically.
204
+ const MISSING = ` if (!defined $apikey || Voxgig::Struct::is_none($apikey)
205
+ || Voxgig::Struct::is_jnull($apikey)
206
+ || (!ref $apikey && ($apikey eq $NOT_FOUND || $apikey eq ''))) {`
207
+
208
+
209
+ // Place the credential, or clear what a previous option left behind.
210
+ //
211
+ // Header and query keep the template's if/else, because both have real
212
+ // work to do in both branches. COOKIE HAS NOTHING TO CLEAR (see clear()),
213
+ // and an `if` whose whole body is a comment is not perl anybody writes -
214
+ // so that one returns early and lets the placement run unindented.
215
+ function credBlock(spec: AuthSpec): string {
216
+ if ('cookie' === spec.where) {
217
+ return `
218
+ ${MISSING}
219
+ return ($spec, undef);
220
+ }
221
+
222
+ ${place(spec).replace(/^ {4}/gm, ' ')}`
223
+ }
224
+
225
+ return `
226
+ ${MISSING}
227
+ ${clear(spec, ' ')} }
228
+ else {
229
+ ${place(spec)} }
230
+ `
231
+ }
232
+
233
+
234
+ // For the cookie placement, where the reason there is no `delete` has to
235
+ // be stated where the delete would have been.
236
+ function noCredNote(spec: AuthSpec): string {
237
+ if ('cookie' !== spec.where) {
238
+ return ''
239
+ }
240
+
241
+ return ` # Nothing of OURS to remove either way: this SDK appends its pair to
242
+ # whatever cookie header the caller set and never stores one, and
243
+ # prepare_headers rebuilds that header from options on every request.
244
+ `
245
+ }
246
+
247
+
248
+ // A one-line reminder, in the generated file, of what the model said —
249
+ // so a reader of a joplin SDK is not left wondering why prepare_auth
250
+ // touches the query string.
251
+ function placementNote(spec: AuthSpec): string {
252
+ if ('query' === spec.where) {
253
+ return `# This API carries its credential as a QUERY PARAMETER (the spec's\n` +
254
+ `# securityScheme says in: query), not as a header.\n`
255
+ }
256
+ if ('cookie' === spec.where) {
257
+ return `# This API carries its credential as a COOKIE (the spec's\n` +
258
+ `# securityScheme says in: cookie), not as a header.\n`
259
+ }
260
+ return ''
261
+ }
262
+
263
+
264
+ // The bag the credential lands in. Cookies ride the header bag, because a
265
+ // cookie IS a header.
266
+ function bag(spec: AuthSpec): string {
267
+ if ('query' === spec.where) {
268
+ // make_spec fills spec.query from prepare_query BEFORE prepare_auth
269
+ // runs, so the bag is normally already there. A spec handed in
270
+ // without one still has to receive the credential, and assigning
271
+ // through an undef lexical would autovivify a hash the spec never
272
+ // sees - so the bag is created ON THE SPEC.
273
+ return ` my $query = $spec->{query};
274
+ unless (Voxgig::Struct::ismap($query)) {
275
+ $query = {};
276
+ $spec->{query} = $query;
277
+ }
278
+
279
+ `
280
+ }
281
+
282
+ // No blank line after it: this is the template's own line, in the
283
+ // template's own place, so a header-based SDK's prepare_auth.pm stays
284
+ // what it was.
285
+ return ` my $headers = $spec->{headers};
286
+ `
287
+ }
288
+
289
+
290
+ function clear(spec: AuthSpec, ind: string): string {
291
+ if ('query' === spec.where) {
292
+ return `${ind}delete $query->{$CRED_NAME};\n`
293
+ }
294
+
295
+ if ('cookie' === spec.where) {
296
+ // NOTHING TO CLEAR. The header case deletes its header because the
297
+ // caller's own `options.headers` may carry a stale `authorization`
298
+ // that must not go out when auth is suppressed. A cookie is
299
+ // different: this SDK APPENDS its pair to whatever cookie header the
300
+ // caller set, and prepare_headers hands it a fresh clone of that
301
+ // header on every request - so there is never a pair of ours left
302
+ // behind, and surgically removing one pair from the caller's own
303
+ // cookie header would be deleting something they put there
304
+ // deliberately. noCredNote() says so in the generated file.
305
+ return ''
306
+ }
307
+
308
+ return `${ind}delete $headers->{$CRED_NAME};\n`
309
+ }
310
+
311
+
312
+ function place(spec: AuthSpec): string {
313
+ if ('query' === spec.where) {
314
+ // NO PREFIX IN A QUERY STRING. `?token=Bearer%20abc` is not a thing
315
+ // any API reads; the prefix is a header convention and is dropped
316
+ // here deliberately rather than silently concatenated.
317
+ return ` # NO PREFIX IN A QUERY STRING: \`?token=Bearer%20abc\` is not a thing
318
+ # any API reads. The prefix is a header convention, and is dropped
319
+ # here deliberately rather than silently concatenated. make_url
320
+ # url-encodes both the name and the value.
321
+ $query->{$CRED_NAME} = (!ref $apikey) ? "$apikey" : '';
322
+ `
323
+ }
324
+
325
+ if ('cookie' === spec.where) {
326
+ return ` my $apikey_val = (!ref $apikey) ? "$apikey" : '';
327
+ my $pair = "$CRED_NAME=$apikey_val";
328
+ # APPEND, never clobber: the caller's own cookie header may already
329
+ # carry a session or consent pair that the API needs alongside this
330
+ # credential.
331
+ my $cookie = $headers->{'cookie'};
332
+ $cookie = '' unless defined $cookie && !ref $cookie;
333
+ $headers->{'cookie'} = ('' eq $cookie) ? $pair : "$cookie; $pair";
334
+ `
335
+ }
336
+
337
+ return ` my $auth_prefix = ${spec.Name}Helpers::gpath($options, 'auth.prefix');
338
+ $auth_prefix = '' unless defined $auth_prefix && !ref $auth_prefix;
339
+ my $apikey_val = (!ref $apikey) ? "$apikey" : '';
340
+ # Empty prefix (raw apiKey credential) must not add a leading space.
341
+ $headers->{$CRED_NAME} =
342
+ ('' eq $auth_prefix) ? $apikey_val : "$auth_prefix $apikey_val";
343
+ `
344
+ }
345
+
346
+
347
+ function basicBlock(spec: AuthSpec, wantBasic: boolean): string {
348
+ if (!wantBasic) {
349
+ return ''
350
+ }
351
+
352
+ return `
353
+ # True HTTP Basic Auth needs TWO credentials, base64-joined - a single
354
+ # token in the header (the branch below) can never authenticate against
355
+ # an API that actually checks \`Authorization: Basic base64(user:pass)\`.
356
+ if (${spec.Name}Helpers::is_true(${spec.Name}Helpers::gpath($options, 'auth.basic'))) {
357
+ my $secret = Voxgig::Struct::getprop($options, $OPTION_SECRET, $NOT_FOUND);
358
+
359
+ my $no_apikey = !defined $apikey || Voxgig::Struct::is_none($apikey)
360
+ || Voxgig::Struct::is_jnull($apikey)
361
+ || (!ref $apikey && ($apikey eq $NOT_FOUND || $apikey eq ''));
362
+ my $no_secret = !defined $secret || Voxgig::Struct::is_none($secret)
363
+ || Voxgig::Struct::is_jnull($secret)
364
+ || (!ref $secret && ($secret eq $NOT_FOUND || $secret eq ''));
365
+
366
+ if ($no_apikey || $no_secret) {
367
+ delete $headers->{$CRED_NAME};
368
+ }
369
+ else {
370
+ my $auth_prefix = ${spec.Name}Helpers::gpath($options, 'auth.prefix');
371
+ $auth_prefix = '' unless defined $auth_prefix && !ref $auth_prefix;
372
+ # '' as the eol: encode_base64 wraps at 76 columns by default, and a
373
+ # newline inside a header value is not a header value.
374
+ my $b64 = MIME::Base64::encode_base64("$apikey:$secret", '');
375
+ $headers->{$CRED_NAME} =
376
+ ('' eq $auth_prefix) ? $b64 : "$auth_prefix $b64";
377
+ }
378
+
379
+ return ($spec, undef);
380
+ }
381
+ `
382
+ }
383
+
384
+
385
+ export {
386
+ PrepareAuth
387
+ }
@@ -14,6 +14,8 @@ import {
14
14
  isAuthActive,
15
15
  isConfigData,
16
16
  rawStringLiteral,
17
+ resolveAuthIn,
18
+ resolveAuthName,
17
19
  resolveAuthPrefix,
18
20
  serverVariables,
19
21
  targetFeatures,
@@ -121,6 +123,13 @@ const Config = cmp(async function Config(props: any) {
121
123
  const authActive = isAuthActive(model)
122
124
  // config.auth.prefix override -> spec-derived info.security.prefix -> 'Bearer'
123
125
  const authPrefix = resolveAuthPrefix(model)
126
+ // `in` and `name` travel with the prefix now. They were resolved by
127
+ // apidef all along and dropped here, so an apiKey-in-query API got an
128
+ // Authorization header it does not read. Emitted only when they differ
129
+ // from the defaults, so a header/Authorization SDK is byte-identical to
130
+ // what it generated before.
131
+ const authIn = resolveAuthIn(model)
132
+ const authName = resolveAuthName(model)
124
133
 
125
134
  let baseUrl = ''
126
135
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
@@ -137,7 +146,9 @@ const Config = cmp(async function Config(props: any) {
137
146
 
138
147
  const authBlock = authActive
139
148
  ? ` "auth" => [
140
- "prefix" => "${authPrefix}",
149
+ "prefix" => "${authPrefix}",${'header' === authIn ? '' : `
150
+ "in" => ${phps(authIn)},`}${'Authorization' === authName ? '' : `
151
+ "name" => ${phps(authName)},`}
141
152
  ],\n`
142
153
  : ''
143
154
 
@@ -23,6 +23,7 @@ import {
23
23
 
24
24
  import { Package } from './Package_php'
25
25
  import { Config } from './Config_php'
26
+ import { PrepareAuth } from './PrepareAuth_php'
26
27
  import { Gitignore } from './Gitignore_php'
27
28
  import { MainEntity } from './MainEntity_php'
28
29
  import { EntityTypes } from './EntityTypes_php'
@@ -105,6 +106,12 @@ const Main = cmp(async function Main(props: any) {
105
106
  Config({ target })
106
107
  })
107
108
 
109
+ // GENERATED, NOT COPIED. Where the credential goes is a fact about the
110
+ // API, and tm/ can only hold one answer. The component opens `utility/`
111
+ // itself, because nothing is open here: Main writes into the target root.
112
+ // See PrepareAuth_php.
113
+ PrepareAuth({ target })
114
+
108
115
  // Generate typed models (types/<Sdk>Types.php) — classmap-autoloaded.
109
116
  EntityTypes({ target })
110
117