@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,389 @@
1
+
2
+ import {
3
+ Content,
4
+ File,
5
+ Folder,
6
+ cmp,
7
+ isHttpBasicAuth,
8
+ resolveAuthIn,
9
+ resolveAuthName,
10
+ resolveAuthPrefix,
11
+ } from '@voxgig/sdkgen'
12
+
13
+
14
+ import {
15
+ KIT,
16
+ getModelPath,
17
+ } from '@voxgig/apidef'
18
+
19
+
20
+ // WHERE THE CREDENTIAL GOES IS A FACT ABOUT THE API, so it is generated
21
+ // rather than templated. The py port of cmp/ts/PrepareAuth_ts.ts; see that
22
+ // file for the full account.
23
+ //
24
+ // This was a static file at `tm/py/pkg/utility/prepare_auth.py` that
25
+ // hardcoded a lowercase `authorization` header. apidef has always resolved
26
+ // the scheme's `in` and `name` into `main.kit.info.security` — joplin's
27
+ // says `in: "query", name: "token"` — and generation dropped both, so the
28
+ // SDK sent a header the API does not read and never sent the query
29
+ // parameter it does.
30
+ //
31
+ // A template cannot fix this, because the three placements need three
32
+ // different bodies and a template has to pick one. A component emits the
33
+ // branch this API actually uses and nothing else — no dead query code in a
34
+ // bearer-token SDK, and no runtime `if` on a value that is fixed at
35
+ // generation time.
36
+ const PrepareAuth = cmp(async function PrepareAuth(props: any) {
37
+ const { target } = props
38
+ const { model } = props.ctx$
39
+
40
+ // FOLDER NESTING. Main_py already opened the ONE package folder
41
+ // (`<name>_sdk`) that everything the SDK owns lives in, and the blanket
42
+ // `Copy({from: 'tm/py/pkg'})` that used to bring this file in runs inside
43
+ // that same folder — which is why the template's own path,
44
+ // `tm/py/pkg/utility/prepare_auth.py`, lands at
45
+ // `<name>_sdk/utility/prepare_auth.py`. So this component opens exactly
46
+ // ONE segment, `utility`, to reproduce that path. Opening `<name>_sdk`
47
+ // again would write to `<name>_sdk/<name>_sdk/utility/`, which nothing
48
+ // imports, while register.py kept importing the stale copy.
49
+ Folder({ name: 'utility' }, () => {
50
+ File({ name: 'prepare_auth.' + target.ext }, () => {
51
+ Content(render({
52
+ // The generated package name, spelled the way Config_py spells it.
53
+ // A Copy applies ctx$.stdrep to the file it copies; Content does
54
+ // not, so the `projectname_sdk` placeholder the template carried
55
+ // has to be resolved here.
56
+ pkg: model.const.Name.toLowerCase() + '_sdk',
57
+ Name: model.const.Name,
58
+ active: isAuthActive_py(model),
59
+ where: resolveAuthIn(model),
60
+ name: resolveAuthName(model),
61
+ basic: isHttpBasicAuth(model),
62
+ // Read so the resolution is visible at generation time even though
63
+ // the emitted code takes the prefix from options at runtime (the
64
+ // secrets feature rewrites it there).
65
+ prefix: resolveAuthPrefix(model),
66
+ }))
67
+ })
68
+ })
69
+ })
70
+
71
+
72
+ type AuthSpec = {
73
+ pkg: string
74
+ Name: string
75
+ active: boolean
76
+ where: string
77
+ name: string
78
+ basic: boolean
79
+ prefix: string
80
+ }
81
+
82
+
83
+ function render(spec: AuthSpec): string {
84
+ const head = `# ${spec.Name} SDK utility: prepare_auth
85
+
86
+ from __future__ import annotations
87
+ `
88
+
89
+ // NO AUTH AT ALL - the project switched it off (see isAuthActive_py for
90
+ // why only an EXPLICIT switch counts). The SDK gets a prepare_auth that is
91
+ // honest about it rather than one that pops a header nobody set;
92
+ // voxgig_struct is not imported, because nothing here reads an option.
93
+ if (!spec.active) {
94
+ return head + `
95
+
96
+ # This SDK is configured with authentication off, so there is no credential
97
+ # to place. The function stays in the pipeline because make_spec calls it
98
+ # unconditionally.
99
+ def prepare_auth_util(ctx):
100
+ spec = ctx.spec
101
+ if spec is None:
102
+ return None, ctx.make_error("auth_no_spec",
103
+ "Expected context spec property to be defined.")
104
+
105
+ return spec, None
106
+ `
107
+ }
108
+
109
+ if ('query' === spec.where) return renderQuery(spec, head)
110
+ if ('cookie' === spec.where) return renderCookie(spec, head)
111
+
112
+ return renderHeader(spec, head)
113
+ }
114
+
115
+
116
+ // HEADER. Byte-for-byte the old template when the scheme resolves to the
117
+ // defaults (header / Authorization), so every header-based SDK regenerates
118
+ // unchanged — only the constant's VALUE moves with the model, plus the
119
+ // HTTP Basic block, which is emitted only for a basic scheme.
120
+ function renderHeader(spec: AuthSpec, head: string): string {
121
+ return head + (spec.basic ? `import base64
122
+ ` : '') + `from ${spec.pkg}.utility.voxgig_struct import voxgig_struct as vs
123
+
124
+ HEADER_AUTH = ${pystr(headerName(spec.name))}
125
+ OPTION_APIKEY = "apikey"
126
+ ` + (spec.basic ? `OPTION_SECRET = "secret"
127
+ ` : '') + `NOT_FOUND = "__NOTFOUND__"
128
+
129
+
130
+ def prepare_auth_util(ctx):
131
+ spec = ctx.spec
132
+ if spec is None:
133
+ return None, ctx.make_error("auth_no_spec",
134
+ "Expected context spec property to be defined.")
135
+
136
+ headers = spec.headers
137
+ options = ctx.client.options_map()
138
+
139
+ # Public APIs that need no auth omit the options.auth block entirely.
140
+ if options.get("auth") is None:
141
+ headers.pop(HEADER_AUTH, None)
142
+ return spec, None
143
+
144
+ apikey = vs.getprop(options, OPTION_APIKEY, NOT_FOUND)
145
+ ` + basicBlock(spec) + `
146
+ if (
147
+ (isinstance(apikey, str) and apikey == NOT_FOUND)
148
+ or apikey is None
149
+ or apikey == ""
150
+ ):
151
+ headers.pop(HEADER_AUTH, None)
152
+ else:
153
+ auth_prefix = ""
154
+ ap = vs.getpath(options, "auth.prefix")
155
+ if isinstance(ap, str):
156
+ auth_prefix = ap
157
+ apikey_val = ""
158
+ if isinstance(apikey, str):
159
+ apikey_val = apikey
160
+ # Empty prefix (raw apiKey credential) must not add a leading space.
161
+ headers[HEADER_AUTH] = (
162
+ auth_prefix + " " + apikey_val if auth_prefix else apikey_val
163
+ )
164
+
165
+ return spec, None
166
+ `
167
+ }
168
+
169
+
170
+ // HTTP Basic is header-only by definition: the scheme is
171
+ // `Authorization: Basic base64(user:pass)`. It cannot be expressed as a
172
+ // query parameter or a cookie, so the branch is emitted only where it can
173
+ // mean something — and only when the model says the scheme IS basic, so an
174
+ // ordinary bearer SDK carries no dead code.
175
+ function basicBlock(spec: AuthSpec): string {
176
+ if (!spec.basic) return ''
177
+
178
+ return `
179
+ # True HTTP Basic Auth needs TWO credentials, base64-joined - a single
180
+ # token in the header (the branch below) can never authenticate against
181
+ # an API that actually checks \`Authorization: Basic base64(user:pass)\`.
182
+ if vs.getpath(options, "auth.basic") is True:
183
+ secret = vs.getprop(options, OPTION_SECRET, NOT_FOUND)
184
+ no_apikey = (
185
+ (isinstance(apikey, str) and apikey == NOT_FOUND)
186
+ or apikey is None
187
+ or apikey == ""
188
+ )
189
+ no_secret = (
190
+ (isinstance(secret, str) and secret == NOT_FOUND)
191
+ or secret is None
192
+ or secret == ""
193
+ )
194
+
195
+ if no_apikey or no_secret:
196
+ headers.pop(HEADER_AUTH, None)
197
+ else:
198
+ auth_prefix = ""
199
+ ap = vs.getpath(options, "auth.prefix")
200
+ if isinstance(ap, str):
201
+ auth_prefix = ap
202
+ b64 = base64.b64encode(
203
+ (str(apikey) + ":" + str(secret)).encode("utf-8")
204
+ ).decode("ascii")
205
+ headers[HEADER_AUTH] = (
206
+ auth_prefix + " " + b64 if auth_prefix else b64
207
+ )
208
+
209
+ return spec, None
210
+ `
211
+ }
212
+
213
+
214
+ // QUERY. The credential is a query parameter, so it goes in spec.query and
215
+ // the headers are never touched.
216
+ function renderQuery(spec: AuthSpec, head: string): string {
217
+ return head + `from ${spec.pkg}.utility.voxgig_struct import voxgig_struct as vs
218
+
219
+ QUERY_AUTH = ${pystr(spec.name)}
220
+ OPTION_APIKEY = "apikey"
221
+ NOT_FOUND = "__NOTFOUND__"
222
+
223
+
224
+ def prepare_auth_util(ctx):
225
+ spec = ctx.spec
226
+ if spec is None:
227
+ return None, ctx.make_error("auth_no_spec",
228
+ "Expected context spec property to be defined.")
229
+
230
+ query = spec.query
231
+ options = ctx.client.options_map()
232
+
233
+ # Public APIs that need no auth omit the options.auth block entirely.
234
+ if options.get("auth") is None:
235
+ query.pop(QUERY_AUTH, None)
236
+ return spec, None
237
+
238
+ apikey = vs.getprop(options, OPTION_APIKEY, NOT_FOUND)
239
+
240
+ if (
241
+ (isinstance(apikey, str) and apikey == NOT_FOUND)
242
+ or apikey is None
243
+ or apikey == ""
244
+ ):
245
+ query.pop(QUERY_AUTH, None)
246
+ else:
247
+ apikey_val = ""
248
+ if isinstance(apikey, str):
249
+ apikey_val = apikey
250
+ # NO PREFIX IN A QUERY STRING. \`?${spec.name}=Bearer%20abc\` is not a
251
+ # thing any API reads: the prefix is a header convention, so it is
252
+ # dropped here deliberately rather than silently concatenated.
253
+ query[QUERY_AUTH] = apikey_val
254
+
255
+ return spec, None
256
+ `
257
+ }
258
+
259
+
260
+ // COOKIE. A cookie IS a header, so the credential rides the header bag -
261
+ // but the `cookie` header is SHARED with whatever cookies the caller set,
262
+ // so the pair is spliced in and out rather than the header assigned over.
263
+ function renderCookie(spec: AuthSpec, head: string): string {
264
+ return head + `from ${spec.pkg}.utility.voxgig_struct import voxgig_struct as vs
265
+
266
+ COOKIE_HEADER = "cookie"
267
+ COOKIE_AUTH = ${pystr(spec.name)}
268
+ OPTION_APIKEY = "apikey"
269
+ NOT_FOUND = "__NOTFOUND__"
270
+
271
+
272
+ def _cookies_without_cred(headers):
273
+ """The cookie header minus our own pair, every other cookie untouched."""
274
+ existing = headers.get(COOKIE_HEADER)
275
+ if not isinstance(existing, str) or existing == "":
276
+ return ""
277
+
278
+ kept = []
279
+ for part in existing.split(";"):
280
+ piece = part.strip()
281
+ if piece == "" or piece == COOKIE_AUTH or piece.startswith(COOKIE_AUTH + "="):
282
+ continue
283
+ kept.append(piece)
284
+
285
+ return "; ".join(kept)
286
+
287
+
288
+ def _apply_cookie(headers, value):
289
+ """Set (value) or remove (None) our pair, leaving the rest in place.
290
+
291
+ Splicing rather than assigning also makes this idempotent: a retried
292
+ request cannot end up with the credential in the header twice.
293
+ """
294
+ rest = _cookies_without_cred(headers)
295
+
296
+ if value is None:
297
+ if rest == "":
298
+ headers.pop(COOKIE_HEADER, None)
299
+ else:
300
+ headers[COOKIE_HEADER] = rest
301
+ return
302
+
303
+ pair = COOKIE_AUTH + "=" + value
304
+ headers[COOKIE_HEADER] = rest + "; " + pair if rest else pair
305
+
306
+
307
+ def prepare_auth_util(ctx):
308
+ spec = ctx.spec
309
+ if spec is None:
310
+ return None, ctx.make_error("auth_no_spec",
311
+ "Expected context spec property to be defined.")
312
+
313
+ headers = spec.headers
314
+ options = ctx.client.options_map()
315
+
316
+ # Public APIs that need no auth omit the options.auth block entirely.
317
+ if options.get("auth") is None:
318
+ _apply_cookie(headers, None)
319
+ return spec, None
320
+
321
+ apikey = vs.getprop(options, OPTION_APIKEY, NOT_FOUND)
322
+
323
+ if (
324
+ (isinstance(apikey, str) and apikey == NOT_FOUND)
325
+ or apikey is None
326
+ or apikey == ""
327
+ ):
328
+ _apply_cookie(headers, None)
329
+ else:
330
+ apikey_val = ""
331
+ if isinstance(apikey, str):
332
+ apikey_val = apikey
333
+ # NO PREFIX IN A COOKIE either - a cookie carries a bare
334
+ # \`name=value\` pair, not a header's scheme-prefixed credential.
335
+ _apply_cookie(headers, apikey_val)
336
+
337
+ return spec, None
338
+ `
339
+ }
340
+
341
+
342
+ // NOT `isAuthActive`, AND THE DIFFERENCE IS LOAD-BEARING. That helper is
343
+ // false whenever the SPEC declares no security scheme (`main.kit.info.auth:
344
+ // false`) — which is a statement about the DEFINITION, not a ban on ever
345
+ // sending a credential. apidef writes it for every spec with no
346
+ // securitySchemes block, GitHub's official OpenAPI included, and those SDKs
347
+ // are still expected to honour an `apikey` the caller passes; the old
348
+ // template placed the credential unconditionally, so they did.
349
+ //
350
+ // Gating the body on `isAuthActive` therefore does not just trim dead code,
351
+ // it removes working authentication from every such SDK. The generated
352
+ // suite catches it: generatedcompile's `py: auth null beats an explicit
353
+ // apikey` lane generates against a fixture that declares
354
+ // `main: kit: info: { ... auth: false }` and then requires
355
+ // `wire({apikey: 'OPTKEY01'})` to reach the transport as an authorization
356
+ // header. With the wider gate it reports
357
+ //
358
+ // FAIL: baseline broken: an ordinary apikey was not sent:
359
+ // {'called': True, 'had': False, 'val': None}
360
+ //
361
+ // So the no-op is emitted only when the PROJECT says so — `config.auth.active:
362
+ // false`, an explicit per-SDK switch nobody sets by accident. A spec that is
363
+ // merely silent keeps the credential path it has always had.
364
+ function isAuthActive_py(model: any): boolean {
365
+ const auth = getModelPath(model, `main.${KIT}.config.auth`,
366
+ { only_active: false, required: false })
367
+ return !(null != auth && false === auth.active)
368
+ }
369
+
370
+
371
+ // HTTP header names are case-insensitive, and the whole py runtime spells
372
+ // them lowercase - the debug feature's redact list, the secrets feature's
373
+ // in-place rewrite of `fetchdef["headers"]["authorization"]`, and the
374
+ // generated tests all match on the lowercase key. Lowercasing here keeps
375
+ // the default byte-identical to the old template AND keeps a custom header
376
+ // name (`X-API-Key`) findable by all of them.
377
+ function headerName(name: string): string {
378
+ return String(name).toLowerCase()
379
+ }
380
+
381
+
382
+ function pystr(s: string): string {
383
+ return '"' + String(s).replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"'
384
+ }
385
+
386
+
387
+ export {
388
+ PrepareAuth
389
+ }
@@ -13,7 +13,10 @@ import {
13
13
  each,
14
14
  isAuthActive,
15
15
  isConfigData,
16
+ isHttpBasicAuth,
16
17
  rawStringLiteral,
18
+ resolveAuthIn,
19
+ resolveAuthName,
17
20
  resolveAuthPrefix,
18
21
  serverVariables,
19
22
  targetFeatures,
@@ -118,6 +121,17 @@ const Config = cmp(async function Config(props: any) {
118
121
  const authActive = isAuthActive(model)
119
122
  // config.auth.prefix override -> spec-derived info.security.prefix -> 'Bearer'
120
123
  const authPrefix = resolveAuthPrefix(model)
124
+ // `in` and `name` travel with the prefix now. They were resolved by
125
+ // apidef all along and dropped here, so an apiKey-in-query API got an
126
+ // `authorization` header it does not read. `basic` joins them because
127
+ // PrepareAuth_rb only emits its HTTP Basic branch for a basic scheme, and
128
+ // that branch reads this option at runtime.
129
+ //
130
+ // Each is emitted ONLY when it differs from the default, so an SDK with a
131
+ // plain header/Authorization bearer scheme regenerates byte-identical.
132
+ const authBasic = isHttpBasicAuth(model)
133
+ const authIn = resolveAuthIn(model)
134
+ const authName = resolveAuthName(model)
121
135
 
122
136
  let baseUrl = ''
123
137
  try { baseUrl = getModelPath(model, `main.${KIT}.info.servers.0.url`) } catch (_e) { }
@@ -134,7 +148,10 @@ const Config = cmp(async function Config(props: any) {
134
148
 
135
149
  const authBlock = authActive
136
150
  ? ` "auth" => {
137
- "prefix" => "${authPrefix}",
151
+ "prefix" => "${authPrefix}",${authBasic ? `
152
+ "basic" => true,` : ''}${'header' === authIn ? '' : `
153
+ "in" => "${authIn}",`}${'Authorization' === authName ? '' : `
154
+ "name" => "${authName}",`}
138
155
  },\n`
139
156
  : ''
140
157
 
@@ -23,6 +23,7 @@ import {
23
23
 
24
24
  import { Package } from './Package_rb'
25
25
  import { Config } from './Config_rb'
26
+ import { PrepareAuth } from './PrepareAuth_rb'
26
27
  import { Gitignore } from './Gitignore_rb'
27
28
  import { MainEntity } from './MainEntity_rb'
28
29
  import { EntityTypes } from './EntityTypes_rb'
@@ -96,6 +97,15 @@ utility.feature_hook.call(@_rootctx, "${name}")
96
97
  Config({ target })
97
98
  })
98
99
 
100
+ // GENERATED, NOT COPIED. Where the credential goes is a fact about the
101
+ // API, and tm/ can only hold one answer. See PrepareAuth_rb.
102
+ //
103
+ // At the TARGET ROOT, not inside the `.` folder above and not inside any
104
+ // `src`: rb's tree is flat (utility/, core/, feature/ sit at the root),
105
+ // the component opens the one `utility` folder it needs, and that
106
+ // reproduces tm/rb/utility/prepare_auth.rb's old path exactly.
107
+ PrepareAuth({ target })
108
+
99
109
  // Generate typed models (<Sdk>_types.rb) — required by the main SDK file.
100
110
  EntityTypes({ target })
101
111