@voxgig/sdkgen 4.5.2 → 4.6.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 (176) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/action/check.js +15 -0
  3. package/dist/action/check.js.map +1 -1
  4. package/dist/cmp/AgentGuide.js +1 -1
  5. package/dist/cmp/AgentGuide.js.map +1 -1
  6. package/dist/cmp/AgentGuideContent.d.ts +1 -1
  7. package/dist/cmp/AgentGuideContent.js +8 -2
  8. package/dist/cmp/AgentGuideContent.js.map +1 -1
  9. package/dist/cmp/Feature.js +17 -0
  10. package/dist/cmp/Feature.js.map +1 -1
  11. package/dist/cmp/FeatureDocs.d.ts +1 -1
  12. package/dist/cmp/FeatureDocs.js +6 -1
  13. package/dist/cmp/FeatureDocs.js.map +1 -1
  14. package/dist/cmp/ReadmeExplanation.js +3 -1
  15. package/dist/cmp/ReadmeExplanation.js.map +1 -1
  16. package/dist/cmp/ReadmeFeatures.js +1 -1
  17. package/dist/cmp/ReadmeFeatures.js.map +1 -1
  18. package/dist/cmp/ReadmeRefFeatures.js +1 -1
  19. package/dist/cmp/ReadmeRefFeatures.js.map +1 -1
  20. package/dist/cmp/ReadmeTop.js +1 -1
  21. package/dist/helpers/applicability.d.ts +6 -0
  22. package/dist/helpers/applicability.js +103 -0
  23. package/dist/helpers/applicability.js.map +1 -0
  24. package/dist/helpers/collectDeps.js +4 -2
  25. package/dist/helpers/collectDeps.js.map +1 -1
  26. package/dist/helpers/serverVars.d.ts +2 -1
  27. package/dist/helpers/serverVars.js +17 -0
  28. package/dist/helpers/serverVars.js.map +1 -1
  29. package/dist/sdkgen.d.ts +3 -2
  30. package/dist/sdkgen.js +9 -2
  31. package/dist/sdkgen.js.map +1 -1
  32. package/dist/tsconfig.tsbuildinfo +1 -1
  33. package/dist/utility.js +7 -1
  34. package/dist/utility.js.map +1 -1
  35. package/model/sdkgen.aon +39 -0
  36. package/package.json +1 -1
  37. package/project/.sdk/model/feature/feature-index.aon +1 -0
  38. package/project/.sdk/model/feature/secrets.aon +118 -0
  39. package/project/.sdk/model/target/ts.aon +8 -1
  40. package/project/.sdk/src/cmp/c/Config_c.ts +5 -1
  41. package/project/.sdk/src/cmp/cpp/Config_cpp.ts +5 -1
  42. package/project/.sdk/src/cmp/csharp/Config_csharp.ts +5 -1
  43. package/project/.sdk/src/cmp/dart/Config_dart.ts +5 -1
  44. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +5 -1
  45. package/project/.sdk/src/cmp/elixir/Main_elixir.ts +5 -1
  46. package/project/.sdk/src/cmp/go/Config_go.ts +5 -1
  47. package/project/.sdk/src/cmp/go/Main_go.ts +6 -2
  48. package/project/.sdk/src/cmp/go/ReadmeQuick_go.ts +16 -3
  49. package/project/.sdk/src/cmp/go/ReadmeTopQuick_go.ts +16 -3
  50. package/project/.sdk/src/cmp/go/TestDirect_go.ts +28 -4
  51. package/project/.sdk/src/cmp/go/TestEntity_go.ts +43 -10
  52. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +5 -5
  53. package/project/.sdk/src/cmp/java/Config_java.ts +22 -1
  54. package/project/.sdk/src/cmp/java/Main_java.ts +5 -1
  55. package/project/.sdk/src/cmp/java/ReadmeQuick_java.ts +17 -3
  56. package/project/.sdk/src/cmp/java/ReadmeTopQuick_java.ts +17 -3
  57. package/project/.sdk/src/cmp/java/TestDirect_java.ts +23 -4
  58. package/project/.sdk/src/cmp/java/TestEntity_java.ts +29 -4
  59. package/project/.sdk/src/cmp/js/Config_js.ts +5 -1
  60. package/project/.sdk/src/cmp/js/Main_js.ts +5 -1
  61. package/project/.sdk/src/cmp/js/Package_js.ts +4 -1
  62. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +5 -1
  63. package/project/.sdk/src/cmp/lean/Config_lean.ts +5 -1
  64. package/project/.sdk/src/cmp/lua/Config_lua.ts +5 -1
  65. package/project/.sdk/src/cmp/lua/Main_lua.ts +5 -1
  66. package/project/.sdk/src/cmp/lua/Package_lua.ts +4 -1
  67. package/project/.sdk/src/cmp/ocaml/Config_ocaml.ts +5 -1
  68. package/project/.sdk/src/cmp/perl/Main_perl.ts +5 -1
  69. package/project/.sdk/src/cmp/php/Config_php.ts +5 -1
  70. package/project/.sdk/src/cmp/php/Main_php.ts +5 -1
  71. package/project/.sdk/src/cmp/py/Config_py.ts +5 -1
  72. package/project/.sdk/src/cmp/py/Main_py.ts +5 -1
  73. package/project/.sdk/src/cmp/py/ReadmeQuick_py.ts +16 -3
  74. package/project/.sdk/src/cmp/py/ReadmeTopQuick_py.ts +16 -3
  75. package/project/.sdk/src/cmp/py/TestDirect_py.ts +24 -5
  76. package/project/.sdk/src/cmp/py/TestEntity_py.ts +23 -3
  77. package/project/.sdk/src/cmp/rb/Config_rb.ts +5 -1
  78. package/project/.sdk/src/cmp/rb/Main_rb.ts +5 -1
  79. package/project/.sdk/src/cmp/rust/Config_rust.ts +5 -1
  80. package/project/.sdk/src/cmp/rust/Main_rust.ts +5 -1
  81. package/project/.sdk/src/cmp/scala/Config_scala.ts +5 -1
  82. package/project/.sdk/src/cmp/scala/Main_scala.ts +5 -1
  83. package/project/.sdk/src/cmp/swift/Config_swift.ts +5 -1
  84. package/project/.sdk/src/cmp/ts/Config_ts.ts +5 -1
  85. package/project/.sdk/src/cmp/ts/Main_ts.ts +57 -1
  86. package/project/.sdk/src/cmp/ts/Package_ts.ts +4 -1
  87. package/project/.sdk/src/cmp/ts/ReadmeModel_ts.ts +15 -3
  88. package/project/.sdk/src/cmp/ts/ReadmeQuick_ts.ts +18 -5
  89. package/project/.sdk/src/cmp/ts/ReadmeTopQuick_ts.ts +19 -5
  90. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +67 -7
  91. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +27 -4
  92. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +9 -2
  93. package/project/.sdk/src/cmp/ts/fragment/Entity.test.fragment.ts +9 -2
  94. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +7 -0
  95. package/project/.sdk/src/cmp/zig/Config_zig.ts +5 -1
  96. package/project/.sdk/tm/c/utility/make_options.c +22 -0
  97. package/project/.sdk/tm/clojure/src/sdk/core.clj +18 -1
  98. package/project/.sdk/tm/cpp/utility/pipeline.hpp +23 -0
  99. package/project/.sdk/tm/csharp/test/CustomUtilityTest.cs +15 -0
  100. package/project/.sdk/tm/csharp/utility/MakeOptions.cs +23 -0
  101. package/project/.sdk/tm/dart/lib/utility/MakeOptionsUtility.dart +22 -0
  102. package/project/.sdk/tm/elixir/lib/projectname/utility.ex +19 -0
  103. package/project/.sdk/tm/go/core/context.go +1 -1
  104. package/project/.sdk/tm/go/feature/cost_feature.go +1 -1
  105. package/project/.sdk/tm/go/feature/paging_feature.go +5 -5
  106. package/project/.sdk/tm/go/feature/test_feature.go +12 -5
  107. package/project/.sdk/tm/go/test/custom_utility_test.go +15 -0
  108. package/project/.sdk/tm/go/test/feature_corpus_test.go +7 -0
  109. package/project/.sdk/tm/go/test/runner_test.go +46 -0
  110. package/project/.sdk/tm/go/test/struct_runner_test.go +7 -7
  111. package/project/.sdk/tm/go/test/struct_utility_test.go +63 -27
  112. package/project/.sdk/tm/go/utility/fetcher.go +2 -2
  113. package/project/.sdk/tm/go/utility/make_options.go +24 -5
  114. package/project/.sdk/tm/go/utility/make_point.go +1 -1
  115. package/project/.sdk/tm/go/utility/make_spec.go +17 -1
  116. package/project/.sdk/tm/go/utility/prepare_auth.go +1 -1
  117. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +370 -307
  118. package/project/.sdk/tm/go/utility/transform_request.go +29 -1
  119. package/project/.sdk/tm/go/utility/transform_response.go +20 -1
  120. package/project/.sdk/tm/java/test/CustomUtilityTest.java +10 -0
  121. package/project/.sdk/tm/java/test/FeatureCorpusTest.java +7 -0
  122. package/project/.sdk/tm/java/test/PrimaryUtilityTest.java +6 -0
  123. package/project/.sdk/tm/java/test/RunnerSupport.java +43 -0
  124. package/project/.sdk/tm/java/utility/MakeOptions.java +84 -1
  125. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +24 -0
  126. package/project/.sdk/tm/js/src/utility/StructUtility.js +2076 -1837
  127. package/project/.sdk/tm/js/test/feature/Corpus.test.js +9 -0
  128. package/project/.sdk/tm/kotlin/utility/MakeOptions.kt +21 -0
  129. package/project/.sdk/tm/lean/src/SdkUtility.lean +12 -1
  130. package/project/.sdk/tm/ocaml/sdk_runtime.ml +11 -0
  131. package/project/.sdk/tm/perl/utility/make_options.pm +18 -0
  132. package/project/.sdk/tm/php/test/FeatureCorpusTest.php +10 -1
  133. package/project/.sdk/tm/php/utility/MakeOptions.php +24 -0
  134. package/project/.sdk/tm/py/pkg/utility/make_options.py +20 -0
  135. package/project/.sdk/tm/py/test/runner.py +32 -0
  136. package/project/.sdk/tm/py/test/test_feature_corpus.py +10 -1
  137. package/project/.sdk/tm/rb/test/feature_corpus_test.rb +9 -1
  138. package/project/.sdk/tm/rb/utility/make_options.rb +17 -0
  139. package/project/.sdk/tm/rust/utility/make_options.rs +25 -0
  140. package/project/.sdk/tm/scala/utility/Make.scala +17 -0
  141. package/project/.sdk/tm/swift/Sources/ProjectNameSDK/utility/MakeOptions.swift +21 -0
  142. package/project/.sdk/tm/ts/src/feature/secrets/SecretsFeature.ts +419 -0
  143. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +1221 -0
  144. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +381 -0
  145. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +151 -0
  146. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +42 -0
  147. package/project/.sdk/tm/ts/src/utility/MakeOptionsUtility.ts +18 -0
  148. package/project/.sdk/tm/ts/src/utility/StructUtility.ts +1321 -710
  149. package/project/.sdk/tm/ts/test/feature/Corpus.test.ts +9 -0
  150. package/project/.sdk/tm/ts/test/feature/secrets/Secrets.test.ts +764 -0
  151. package/project/.sdk/tm/ts/test/omni.test.ts +84 -0
  152. package/project/.sdk/tm/ts/test/omni.ts +86 -0
  153. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +6 -2
  154. package/project/.sdk/tm/ts/test/utility/StructUtility.test.ts +2 -2
  155. package/project/.sdk/tm/ts/test/utility.ts +122 -0
  156. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +692 -0
  157. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +238 -0
  158. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +188 -0
  159. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +38 -0
  160. package/project/.sdk/tm/zig/core/utility.zig +23 -0
  161. package/project/sdkgen-package.json +2 -1
  162. package/src/action/check.ts +18 -0
  163. package/src/cmp/AgentGuide.ts +1 -1
  164. package/src/cmp/AgentGuideContent.ts +9 -2
  165. package/src/cmp/Feature.ts +19 -0
  166. package/src/cmp/FeatureDocs.ts +7 -1
  167. package/src/cmp/ReadmeExplanation.ts +4 -1
  168. package/src/cmp/ReadmeFeatures.ts +1 -1
  169. package/src/cmp/ReadmeRefFeatures.ts +1 -1
  170. package/src/cmp/ReadmeTop.ts +1 -1
  171. package/src/helpers/applicability.ts +123 -0
  172. package/src/helpers/collectDeps.ts +5 -1
  173. package/src/helpers/serverVars.ts +19 -0
  174. package/src/sdkgen.ts +9 -1
  175. package/src/utility.ts +10 -1
  176. package/project/.sdk/tm/ts/test/runner.ts +0 -425
@@ -0,0 +1,381 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/Sekreto.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ a8c293be1b6c33d65223b2b2275797c241b1a1f1
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ // sekreto: one interface for secrets, wherever they live.
5
+ //
6
+ // A Sekreto is an ordered chain of providers. `get` asks each in turn and
7
+ // returns the first hit, so an app can be configured from environment
8
+ // variables in development and a vault in production without changing a
9
+ // line of its own code.
10
+ //
11
+ // This file is CANONICAL. Every other port is a translation of it, and
12
+ // spec/sekreto.json is the behavioural contract they all run.
13
+
14
+ import { Provider, ProviderSpec, makeprovider } from './Providers'
15
+
16
+ /** A secret name: dot-separated lowercase segments, e.g. `api.token`. */
17
+ export type Name = string
18
+
19
+ export type SekretoOptions = {
20
+ /** The provider chain, in resolution order. */
21
+ providers?: (Provider | ProviderSpec)[]
22
+ /** Cache resolved values (default: true). */
23
+ cache?: boolean
24
+ }
25
+
26
+ /** Anything sekreto refuses to do: a bad name, a missing secret, a
27
+ * provider that could not be reached. */
28
+ export class SekretoError extends Error {
29
+ constructor(message: string) {
30
+ super(message)
31
+ this.name = 'SekretoError'
32
+ }
33
+ }
34
+
35
+ const NAMEPART = /^[a-z0-9_]+$/
36
+
37
+ /** Is this a well-formed secret name? */
38
+ export function validname(name: any): boolean {
39
+ if ('string' !== typeof name || 0 === name.length) {
40
+ return false
41
+ }
42
+
43
+ const parts = name.split('.')
44
+
45
+ for (const part of parts) {
46
+ if (!NAMEPART.test(part)) {
47
+ return false
48
+ }
49
+ }
50
+
51
+ return true
52
+ }
53
+
54
+ export function checkname(name: any): string {
55
+ if (!validname(name)) {
56
+ throw new SekretoError('sekreto: invalid name: ' + String(null == name ? '' : name))
57
+ }
58
+ return name
59
+ }
60
+
61
+ /** The environment-variable key for a name: `api.token` -> `API_TOKEN`. */
62
+ export function envkey(name: Name, prefix?: string): string {
63
+ checkname(name)
64
+ return (prefix || '') + name.split('.').join('_').toUpperCase()
65
+ }
66
+
67
+ /** Where a name lives in a KV vault: `api.token` -> `api` / `token`.
68
+ *
69
+ * A single-segment name has no path of its own, so it becomes a secret of
70
+ * that name with the conventional field `value`. */
71
+ export function vaultref(name: Name): { path: string; field: string } {
72
+ checkname(name)
73
+
74
+ const parts = name.split('.')
75
+
76
+ if (1 === parts.length) {
77
+ return { path: parts[0], field: 'value' }
78
+ }
79
+
80
+ return { path: parts.slice(0, -1).join('/'), field: parts[parts.length - 1] }
81
+ }
82
+
83
+ /** A name flattened to one segment: `api.token` -> `api_token` (GCP
84
+ * Secret Manager, `_`) or `api-token` (Azure Key Vault, `-`).
85
+ *
86
+ * Those stores have no path hierarchy and reject dots in ids, so the
87
+ * dots become the store's conventional separator. With `-` as the
88
+ * separator, underscores flatten too: Azure Key Vault's alphabet is
89
+ * letters, digits and hyphens only, and a valid sekreto name like
90
+ * `with_underscore` must still be representable there. (The resulting
91
+ * `.`/`_` collision mirrors the documented envkey behaviour, where
92
+ * both already map to `_`.) */
93
+ export function flatname(name: Name, sep: string): string {
94
+ checkname(name)
95
+ const flat = name.split('.').join(sep)
96
+ return '-' === sep ? flat.split('_').join('-') : flat
97
+ }
98
+
99
+ /** The AWS SSM Parameter Store name for a name: dots become the path
100
+ * hierarchy, rooted at `/` (or at a prefix): `db.pass.main` ->
101
+ * `/db/pass/main`, or `/app/db/pass/main` under prefix `/app`. */
102
+ export function awsparam(name: Name, prefix?: string): string {
103
+ checkname(name)
104
+
105
+ let base = prefix || ''
106
+ if ('' !== base && !base.startsWith('/')) {
107
+ base = '/' + base
108
+ }
109
+ base = base.replace(/\/$/, '')
110
+
111
+ return base + '/' + name.split('.').join('/')
112
+ }
113
+
114
+ /** Parse `.env` text into a map of raw keys to values.
115
+ *
116
+ * Deliberately small: `KEY=value`, optional `export`, `#` comments on their
117
+ * own line, and single- or double-quoted values (double quotes also
118
+ * unescape `\n`, `\r`, `\t` and `\\`). A line with no `=` is skipped. */
119
+ export function parsedotenv(text: string): Record<string, string> {
120
+ const out: Record<string, string> = {}
121
+
122
+ if ('string' !== typeof text) {
123
+ return out
124
+ }
125
+
126
+ for (const rawline of text.split('\n')) {
127
+ const line = rawline.replace(/\r$/, '').trim()
128
+
129
+ if (0 === line.length || line.startsWith('#')) {
130
+ continue
131
+ }
132
+
133
+ const body = line.startsWith('export ') ? line.slice(7).trim() : line
134
+
135
+ const eq = body.indexOf('=')
136
+ if (0 >= eq) {
137
+ continue
138
+ }
139
+
140
+ const key = body.slice(0, eq).trim()
141
+ let value = body.slice(eq + 1).trim()
142
+
143
+ if (2 <= value.length && value.startsWith('"') && value.endsWith('"')) {
144
+ value = unescape(value.slice(1, -1))
145
+ } else if (2 <= value.length && value.startsWith("'") && value.endsWith("'")) {
146
+ value = value.slice(1, -1)
147
+ }
148
+
149
+ out[key] = value
150
+ }
151
+
152
+ return out
153
+ }
154
+
155
+ function unescape(text: string): string {
156
+ let out = ''
157
+
158
+ for (let index = 0; index < text.length; index++) {
159
+ if ('\\' === text[index] && index + 1 < text.length) {
160
+ const next = text[index + 1]
161
+ index++
162
+ if ('n' === next) {
163
+ out += '\n'
164
+ } else if ('r' === next) {
165
+ out += '\r'
166
+ } else if ('t' === next) {
167
+ out += '\t'
168
+ } else if ('\\' === next) {
169
+ out += '\\'
170
+ } else if ('"' === next) {
171
+ out += '"'
172
+ } else {
173
+ out += '\\' + next
174
+ }
175
+ } else {
176
+ out += text[index]
177
+ }
178
+ }
179
+
180
+ return out
181
+ }
182
+
183
+ /** Replace known secret values in text with `[redacted]`.
184
+ *
185
+ * Only values of four characters or more are replaced: shorter ones are
186
+ * too likely to appear in ordinary text, and redacting them would make
187
+ * logs unreadable without making them safer. */
188
+ export function redact(text: string, values: string[]): string {
189
+ let out = 'string' === typeof text ? text : ''
190
+
191
+ for (const value of values || []) {
192
+ if ('string' !== typeof value || 4 > value.length) {
193
+ continue
194
+ }
195
+ out = out.split(value).join('[redacted]')
196
+ }
197
+
198
+ return out
199
+ }
200
+
201
+ /** One provider in the chain, under the store name it answers to. */
202
+ type Entry = { store: string; provider: Provider }
203
+
204
+ /** One resolved value. Kept as a list rather than a map so that the store
205
+ * a value came from stays attached, and so redaction order is stable. */
206
+ type Cached = { store: string; name: Name; value: string }
207
+
208
+ /** The store name a provider answers to when its spec does not say.
209
+ *
210
+ * `describe()` opens with the provider's kind - `vault:...`, `dotenv:...`,
211
+ * plain `env` - so the kind is the natural default, and a custom provider
212
+ * gets a sensible name without having to implement anything extra. */
213
+ function storename(provider: Provider, spec?: ProviderSpec): string {
214
+ if (spec && spec.name) {
215
+ return spec.name
216
+ }
217
+ return provider.describe().split(':')[0]
218
+ }
219
+
220
+ /** The secrets facade: a chain of providers plus a cache.
221
+ *
222
+ * Two ways to read. `get` is transparent - it walks the chain and takes
223
+ * the first hit, and the caller never learns which store answered. `getfrom`
224
+ * is directed - it names the store, and only that store is asked. Use the
225
+ * first for ordinary configuration, the second when *which* store holds a
226
+ * secret is part of what you mean. */
227
+ export class Sekreto {
228
+ private entries: Entry[]
229
+ private docache: boolean
230
+ private cache: Cached[]
231
+ // Every value ever resolved, for redact(). Kept independently of the
232
+ // read cache so that redaction still works when cache is off - otherwise
233
+ // `cache: false` would silently disable redact() and leak secrets to logs.
234
+ private seen: string[]
235
+
236
+ constructor(options?: SekretoOptions) {
237
+ const opts = options || {}
238
+
239
+ this.entries = (opts.providers || []).map((entry) => {
240
+ if ('function' === typeof (entry as Provider).lookup) {
241
+ const provider = entry as Provider
242
+ return { store: storename(provider), provider }
243
+ }
244
+
245
+ const spec = entry as ProviderSpec
246
+ const provider = makeprovider(spec)
247
+ return { store: storename(provider, spec), provider }
248
+ })
249
+
250
+ this.docache = false === opts.cache ? false : true
251
+ this.cache = []
252
+ this.seen = []
253
+ }
254
+
255
+ /** The secret, or a SekretoError if no provider has it. */
256
+ async get(name: Name): Promise<string> {
257
+ const found = await this.try(name)
258
+
259
+ if (undefined === found) {
260
+ throw new SekretoError('sekreto: unknown secret: ' + name)
261
+ }
262
+
263
+ return found
264
+ }
265
+
266
+ /** The secret, or undefined if no provider has it. */
267
+ async try(name: Name): Promise<string | undefined> {
268
+ return this.resolve('', name, this.entries)
269
+ }
270
+
271
+ /** The secret from one named store, or a SekretoError if that store does
272
+ * not have it. */
273
+ async getfrom(store: string, name: Name): Promise<string> {
274
+ const found = await this.tryfrom(store, name)
275
+
276
+ if (undefined === found) {
277
+ throw new SekretoError('sekreto: unknown secret: ' + store + ':' + name)
278
+ }
279
+
280
+ return found
281
+ }
282
+
283
+ /** The secret from one named store, or undefined if that store does not
284
+ * have it.
285
+ *
286
+ * Naming a store that is not in the chain is an error, not a miss: `try`
287
+ * already means "this store may not have it", so it cannot also mean
288
+ * "this store may not exist" without hiding a typo. */
289
+ async tryfrom(store: string, name: Name): Promise<string | undefined> {
290
+ const matching = this.entries.filter((entry) => entry.store === store)
291
+
292
+ if (0 === matching.length) {
293
+ throw new SekretoError('sekreto: unknown store: ' + store)
294
+ }
295
+
296
+ return this.resolve(store, name, matching)
297
+ }
298
+
299
+ private async resolve(store: string, name: Name, entries: Entry[]): Promise<string | undefined> {
300
+ checkname(name)
301
+
302
+ if (this.docache) {
303
+ const hit = this.cache.find((entry) => entry.store === store && entry.name === name)
304
+ if (undefined !== hit) {
305
+ return hit.value
306
+ }
307
+ }
308
+
309
+ for (const entry of entries) {
310
+ const found = await entry.provider.lookup(name)
311
+
312
+ if (undefined !== found && null !== found) {
313
+ if (this.docache) {
314
+ this.cache.push({ store, name, value: found })
315
+ }
316
+ this.seen.push(found)
317
+ return found
318
+ }
319
+ }
320
+
321
+ return undefined
322
+ }
323
+
324
+ /** Does any provider have this secret? */
325
+ async has(name: Name): Promise<boolean> {
326
+ return undefined !== (await this.try(name))
327
+ }
328
+
329
+ /** Does this named store have this secret? */
330
+ async hasin(store: string, name: Name): Promise<boolean> {
331
+ return undefined !== (await this.tryfrom(store, name))
332
+ }
333
+
334
+ /** Every named secret at once. Missing ones are an error. */
335
+ async all(names: Name[]): Promise<Record<string, string>> {
336
+ const out: Record<string, string> = {}
337
+
338
+ for (const name of names) {
339
+ out[name] = await this.get(name)
340
+ }
341
+
342
+ return out
343
+ }
344
+
345
+ /** A description of each provider, in resolution order. */
346
+ sources(): string[] {
347
+ return this.entries.map((entry) => entry.provider.describe())
348
+ }
349
+
350
+ /** The name of each store that can be named by `getfrom`, in resolution
351
+ * order and without repeats. */
352
+ stores(): string[] {
353
+ const out: string[] = []
354
+
355
+ for (const entry of this.entries) {
356
+ if (!out.includes(entry.store)) {
357
+ out.push(entry.store)
358
+ }
359
+ }
360
+
361
+ return out
362
+ }
363
+
364
+ /** Replace every value this Sekreto has resolved with `[redacted]`.
365
+ *
366
+ * Works whether or not caching is enabled: the redaction list is kept
367
+ * independently of the read cache. */
368
+ redact(text: string): string {
369
+ return redact(text, this.seen)
370
+ }
371
+
372
+ /** Drop cached values, so the next `get` asks the providers again. */
373
+ refresh(): void {
374
+ this.cache = []
375
+ }
376
+ }
377
+
378
+ /** Make a Sekreto from options. */
379
+ export function sekreto(options?: SekretoOptions): Sekreto {
380
+ return new Sekreto(options)
381
+ }
@@ -0,0 +1,151 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/Sigv4.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ a8c293be1b6c33d65223b2b2275797c241b1a1f1
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ // AWS Signature Version 4, hand-rolled.
5
+ //
6
+ // The AWS providers need exactly one thing from the AWS SDK - request
7
+ // signing - and taking the SDK for it would break the no-dependency rule
8
+ // that keeps ten ports honest. SigV4 is a stable, published algorithm
9
+ // built from HMAC-SHA256, which every port's standard library (or, for
10
+ // Rust, a small in-tree implementation) already has.
11
+ //
12
+ // `sigv4` is pure: the caller passes the timestamp, so the same input
13
+ // yields the same signature everywhere. That is what lets the shared spec
14
+ // carry known-answer cases that all ten ports must reproduce bit-for-bit,
15
+ // and lets the integration mock recompute the signature server-side.
16
+
17
+ import { createHash, createHmac } from 'node:crypto'
18
+
19
+ export type Sigv4Input = {
20
+ method: string
21
+ /** Full request URL; the host, path and query are signed. */
22
+ url: string
23
+ /** Extra headers to sign, e.g. content-type and x-amz-target. */
24
+ headers?: Record<string, string>
25
+ body?: string
26
+ service: string
27
+ region: string
28
+ keyid: string
29
+ secret: string
30
+ /** STS session token; signed as x-amz-security-token when present. */
31
+ session?: string
32
+ /** The signing moment, `YYYYMMDDTHHMMSSZ`. Passed in, never sampled,
33
+ * so the function stays pure. */
34
+ datetime: string
35
+ }
36
+
37
+ /** The headers to attach to the request: authorization, x-amz-date, and
38
+ * x-amz-security-token when a session token was given. */
39
+ export type Sigv4Output = Record<string, string>
40
+
41
+ function sha256hex(text: string): string {
42
+ return createHash('sha256').update(text, 'utf8').digest('hex')
43
+ }
44
+
45
+ function hmac(key: Buffer | string, text: string): Buffer {
46
+ return createHmac('sha256', key).update(text, 'utf8').digest()
47
+ }
48
+
49
+ /** RFC 3986 escaping, which is stricter than encodeURIComponent: AWS
50
+ * wants `!`, `'`, `(`, `)` and `*` escaped too. */
51
+ function uriescape(text: string): string {
52
+ return encodeURIComponent(text).replace(
53
+ /[!'()*]/g,
54
+ (ch) => '%' + ch.charCodeAt(0).toString(16).toUpperCase(),
55
+ )
56
+ }
57
+
58
+ /** The canonical query string: each pair RFC 3986-escaped, sorted by
59
+ * escaped key then escaped value. */
60
+ function canonicalquery(query: string): string {
61
+ if ('' === query) {
62
+ return ''
63
+ }
64
+
65
+ const pairs = query.split('&').map((pair) => {
66
+ const eq = pair.indexOf('=')
67
+ const key = -1 === eq ? pair : pair.slice(0, eq)
68
+ const value = -1 === eq ? '' : pair.slice(eq + 1)
69
+ return [uriescape(decodeURIComponent(key)), uriescape(decodeURIComponent(value))]
70
+ })
71
+
72
+ pairs.sort((left, right) => {
73
+ if (left[0] !== right[0]) {
74
+ return left[0] < right[0] ? -1 : 1
75
+ }
76
+ return left[1] < right[1] ? -1 : left[1] > right[1] ? 1 : 0
77
+ })
78
+
79
+ return pairs.map((pair) => pair[0] + '=' + pair[1]).join('&')
80
+ }
81
+
82
+ /** Sign one request. Returns the headers to attach. */
83
+ export function sigv4(input: Sigv4Input): Sigv4Output {
84
+ const url = new URL(input.url)
85
+
86
+ const date = input.datetime.slice(0, 8)
87
+ const body = input.body || ''
88
+
89
+ // Every header that will be signed: the caller's extras, plus host and
90
+ // x-amz-date (and the session token when present), lower-cased and
91
+ // trimmed the way the canonical form requires.
92
+ // Canonical header values are trimmed AND internally collapsed -
93
+ // AWS folds sequential whitespace to one space before signing, so a
94
+ // header like "a b" must sign as "a b" or the service refuses it.
95
+ const headers: Record<string, string> = {}
96
+ for (const [key, value] of Object.entries(input.headers || {})) {
97
+ headers[key.toLowerCase()] = String(value).trim().replace(/\s+/g, ' ')
98
+ }
99
+ headers['host'] = url.host
100
+ headers['x-amz-date'] = input.datetime
101
+ if (input.session) {
102
+ headers['x-amz-security-token'] = input.session
103
+ }
104
+
105
+ const names = Object.keys(headers).sort()
106
+ const canonicalheaders = names.map((name) => name + ':' + headers[name] + '\n').join('')
107
+ const signedheaders = names.join(';')
108
+
109
+ const canonicalrequest = [
110
+ input.method.toUpperCase(),
111
+ url.pathname || '/',
112
+ canonicalquery(url.search.replace(/^\?/, '')),
113
+ canonicalheaders,
114
+ signedheaders,
115
+ sha256hex(body),
116
+ ].join('\n')
117
+
118
+ const scope = date + '/' + input.region + '/' + input.service + '/aws4_request'
119
+
120
+ const stringtosign = [
121
+ 'AWS4-HMAC-SHA256',
122
+ input.datetime,
123
+ scope,
124
+ sha256hex(canonicalrequest),
125
+ ].join('\n')
126
+
127
+ const kdate = hmac('AWS4' + input.secret, date)
128
+ const kregion = hmac(kdate, input.region)
129
+ const kservice = hmac(kregion, input.service)
130
+ const ksigning = hmac(kservice, 'aws4_request')
131
+ const signature = createHmac('sha256', ksigning).update(stringtosign, 'utf8').digest('hex')
132
+
133
+ const out: Sigv4Output = {
134
+ authorization:
135
+ 'AWS4-HMAC-SHA256 Credential=' +
136
+ input.keyid +
137
+ '/' +
138
+ scope +
139
+ ', SignedHeaders=' +
140
+ signedheaders +
141
+ ', Signature=' +
142
+ signature,
143
+ 'x-amz-date': input.datetime,
144
+ }
145
+
146
+ if (input.session) {
147
+ out['x-amz-security-token'] = input.session
148
+ }
149
+
150
+ return out
151
+ }
@@ -0,0 +1,42 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/index.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ a8c293be1b6c33d65223b2b2275797c241b1a1f1
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ // @voxgig/sekreto - one interface for secrets, wherever they live.
5
+
6
+ export {
7
+ Sekreto,
8
+ SekretoError,
9
+ awsparam,
10
+ envkey,
11
+ flatname,
12
+ parsedotenv,
13
+ redact,
14
+ sekreto,
15
+ validname,
16
+ vaultref,
17
+ } from './Sekreto'
18
+
19
+ export type { Name, SekretoOptions } from './Sekreto'
20
+
21
+ export {
22
+ awsparamsprovider,
23
+ awssecretsprovider,
24
+ azuresecretsprovider,
25
+ boruprovider,
26
+ checkaddr,
27
+ dopplerprovider,
28
+ dotenvprovider,
29
+ envprovider,
30
+ fileprovider,
31
+ gcpsecretsprovider,
32
+ hashicorpprovider,
33
+ infisicalprovider,
34
+ makeprovider,
35
+ memoryprovider,
36
+ onepasswordprovider,
37
+ } from './Providers'
38
+
39
+ export type { Provider, ProviderSpec } from './Providers'
40
+
41
+ export { sigv4 } from './Sigv4'
42
+ export type { Sigv4Input, Sigv4Output } from './Sigv4'
@@ -14,6 +14,19 @@ function makeOptions(ctx: Context) {
14
14
 
15
15
  let opts = { ...(options || {}) }
16
16
 
17
+ // EXPLICIT AUTH SUPPRESSION, captured BEFORE merge and validate.
18
+ //
19
+ // `auth: null` is the documented way to disable auth outright, and
20
+ // prepareAuth honours it. But struct 0.3.2's getprop treats a stored
21
+ // null as "no value", so validate fills in the optspec's `auth`
22
+ // default and the suppression silently became "use default auth" —
23
+ // transmitting a credential the caller explicitly asked not to send.
24
+ // (0.0.10 rejected `auth: null` outright, so nothing depended on the
25
+ // old behaviour, and nothing catches the new one.)
26
+ //
27
+ // Suppliedness cannot be recovered after validate, hence here.
28
+ const authSuppressed = null === (options || {}).auth
29
+
17
30
  // Feature add-order. `options.feature` may be given as an ordered ARRAY of
18
31
  // { name, active, ...opts } entries (the array position IS the order in
19
32
  // which features are added), or as a { name: {opts} } map. Normalize an
@@ -111,6 +124,11 @@ function makeOptions(ctx: Context) {
111
124
 
112
125
  opts = validate(opts, optspec)
113
126
 
127
+ // Restore the suppression the optspec default would otherwise erase.
128
+ if (authSuppressed) {
129
+ opts.auth = null
130
+ }
131
+
114
132
  // Resolve a templated base URL (e.g. https://{tenant_id}.hanko.io).
115
133
  // Every placeholder must resolve to a non-empty value: from
116
134
  // options.server (user), else the Config default. A placeholder that