@voxgig/sdkgen 4.6.0 → 4.8.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 (144) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Feature.js +5 -0
  3. package/dist/cmp/Feature.js.map +1 -1
  4. package/dist/cmp/TestControl.d.ts +4 -0
  5. package/dist/cmp/TestControl.js +51 -0
  6. package/dist/cmp/TestControl.js.map +1 -0
  7. package/dist/helpers/featureSource.d.ts +3 -1
  8. package/dist/helpers/featureSource.js +105 -0
  9. package/dist/helpers/featureSource.js.map +1 -1
  10. package/dist/helpers/opShape.js +8 -11
  11. package/dist/helpers/opShape.js.map +1 -1
  12. package/dist/helpers/pointPath.d.ts +10 -0
  13. package/dist/helpers/pointPath.js +54 -0
  14. package/dist/helpers/pointPath.js.map +1 -0
  15. package/dist/sdkgen.d.ts +6 -3
  16. package/dist/sdkgen.js +14 -4
  17. package/dist/sdkgen.js.map +1 -1
  18. package/dist/tsconfig.tsbuildinfo +1 -1
  19. package/dist/utility.d.ts +2 -1
  20. package/dist/utility.js +78 -1
  21. package/dist/utility.js.map +1 -1
  22. package/model/sdkgen.aon +58 -0
  23. package/package.json +2 -2
  24. package/project/.sdk/model/feature/secrets.aon +82 -0
  25. package/project/.sdk/src/cmp/c/Config_c.ts +7 -8
  26. package/project/.sdk/src/cmp/c/TestDirect_c.ts +4 -3
  27. package/project/.sdk/src/cmp/cpp/Main_cpp.ts +2 -1
  28. package/project/.sdk/src/cmp/cpp/TestDirect_cpp.ts +4 -3
  29. package/project/.sdk/src/cmp/cpp/Test_cpp.ts +5 -1
  30. package/project/.sdk/src/cmp/csharp/Main_csharp.ts +2 -1
  31. package/project/.sdk/src/cmp/csharp/TestDirect_csharp.ts +34 -9
  32. package/project/.sdk/src/cmp/csharp/TestEntity_csharp.ts +29 -5
  33. package/project/.sdk/src/cmp/csharp/Test_csharp.ts +5 -1
  34. package/project/.sdk/src/cmp/dart/Config_dart.ts +7 -8
  35. package/project/.sdk/src/cmp/dart/Main_dart.ts +2 -1
  36. package/project/.sdk/src/cmp/dart/TestDirect_dart.ts +28 -10
  37. package/project/.sdk/src/cmp/dart/TestEntity_dart.ts +28 -7
  38. package/project/.sdk/src/cmp/dart/Test_dart.ts +5 -1
  39. package/project/.sdk/src/cmp/elixir/Config_elixir.ts +9 -8
  40. package/project/.sdk/src/cmp/go/Config_go.ts +1 -7
  41. package/project/.sdk/src/cmp/go/Main_go.ts +2 -1
  42. package/project/.sdk/src/cmp/go/TestDirect_go.ts +5 -4
  43. package/project/.sdk/src/cmp/go/Test_go.ts +5 -1
  44. package/project/.sdk/src/cmp/java/Config_java.ts +7 -7
  45. package/project/.sdk/src/cmp/java/Main_java.ts +2 -1
  46. package/project/.sdk/src/cmp/java/TestDirect_java.ts +5 -4
  47. package/project/.sdk/src/cmp/java/Test_java.ts +5 -1
  48. package/project/.sdk/src/cmp/js/Config_js.ts +7 -8
  49. package/project/.sdk/src/cmp/js/Main_js.ts +2 -1
  50. package/project/.sdk/src/cmp/js/TestDirect_js.ts +34 -8
  51. package/project/.sdk/src/cmp/js/TestEntity_js.ts +35 -5
  52. package/project/.sdk/src/cmp/js/Test_js.ts +5 -1
  53. package/project/.sdk/src/cmp/js/fragment/Direct.test.fragment.js +7 -1
  54. package/project/.sdk/src/cmp/js/fragment/Entity.test.fragment.js +7 -1
  55. package/project/.sdk/src/cmp/kotlin/Config_kotlin.ts +7 -7
  56. package/project/.sdk/src/cmp/kotlin/Main_kotlin.ts +2 -1
  57. package/project/.sdk/src/cmp/kotlin/TestDirect_kotlin.ts +5 -4
  58. package/project/.sdk/src/cmp/kotlin/Test_kotlin.ts +5 -1
  59. package/project/.sdk/src/cmp/lean/Config_lean.ts +9 -8
  60. package/project/.sdk/src/cmp/lua/Config_lua.ts +1 -7
  61. package/project/.sdk/src/cmp/lua/Main_lua.ts +2 -1
  62. package/project/.sdk/src/cmp/lua/TestDirect_lua.ts +32 -7
  63. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +24 -4
  64. package/project/.sdk/src/cmp/lua/Test_lua.ts +5 -1
  65. package/project/.sdk/src/cmp/ocaml/Gitignore_ocaml.ts +1 -0
  66. package/project/.sdk/src/cmp/perl/Main_perl.ts +2 -1
  67. package/project/.sdk/src/cmp/perl/TestDirect_perl.ts +29 -7
  68. package/project/.sdk/src/cmp/perl/TestEntity_perl.ts +24 -4
  69. package/project/.sdk/src/cmp/perl/Test_perl.ts +5 -1
  70. package/project/.sdk/src/cmp/perl/utility_perl.ts +10 -0
  71. package/project/.sdk/src/cmp/php/Config_php.ts +1 -7
  72. package/project/.sdk/src/cmp/php/Main_php.ts +2 -1
  73. package/project/.sdk/src/cmp/php/TestDirect_php.ts +28 -8
  74. package/project/.sdk/src/cmp/php/TestEntity_php.ts +28 -4
  75. package/project/.sdk/src/cmp/php/Test_php.ts +5 -1
  76. package/project/.sdk/src/cmp/py/Config_py.ts +1 -7
  77. package/project/.sdk/src/cmp/py/Main_py.ts +2 -1
  78. package/project/.sdk/src/cmp/py/TestDirect_py.ts +5 -4
  79. package/project/.sdk/src/cmp/py/Test_py.ts +5 -1
  80. package/project/.sdk/src/cmp/rb/Config_rb.ts +1 -7
  81. package/project/.sdk/src/cmp/rb/Main_rb.ts +2 -1
  82. package/project/.sdk/src/cmp/rb/TestDirect_rb.ts +28 -8
  83. package/project/.sdk/src/cmp/rb/TestEntity_rb.ts +24 -4
  84. package/project/.sdk/src/cmp/rb/Test_rb.ts +5 -1
  85. package/project/.sdk/src/cmp/rb/utility_rb.ts +3 -1
  86. package/project/.sdk/src/cmp/rust/Main_rust.ts +2 -1
  87. package/project/.sdk/src/cmp/rust/TestDirect_rust.ts +35 -7
  88. package/project/.sdk/src/cmp/rust/TestEntity_rust.ts +37 -4
  89. package/project/.sdk/src/cmp/rust/Test_rust.ts +5 -1
  90. package/project/.sdk/src/cmp/scala/Config_scala.ts +9 -7
  91. package/project/.sdk/src/cmp/scala/Main_scala.ts +2 -1
  92. package/project/.sdk/src/cmp/scala/TestDirect_scala.ts +4 -3
  93. package/project/.sdk/src/cmp/scala/Test_scala.ts +5 -1
  94. package/project/.sdk/src/cmp/seneca-provider/Extras_seneca-provider.ts +3 -1
  95. package/project/.sdk/src/cmp/seneca-provider/Main_seneca-provider.ts +9 -5
  96. package/project/.sdk/src/cmp/ts/Config_ts.ts +57 -16
  97. package/project/.sdk/src/cmp/ts/Main_ts.ts +10 -2
  98. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +15 -7
  99. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +11 -3
  100. package/project/.sdk/src/cmp/ts/Test_ts.ts +5 -1
  101. package/project/.sdk/tm/csharp/test/Runner.cs +45 -0
  102. package/project/.sdk/tm/dart/test/utility.dart +39 -0
  103. package/project/.sdk/tm/js/test/sdk-test-control.json +19 -0
  104. package/project/.sdk/tm/js/test/utility.js +91 -1
  105. package/project/.sdk/tm/lua/test/runner.lua +40 -0
  106. package/project/.sdk/tm/ocaml/test/harness.ml +14 -4
  107. package/project/.sdk/tm/perl/t/runner.pm +30 -0
  108. package/project/.sdk/tm/php/test/Runner.php +36 -0
  109. package/project/.sdk/tm/rb/test/runner.rb +25 -0
  110. package/project/.sdk/tm/rust/tests/common/mod.rs +32 -0
  111. package/project/.sdk/tm/rust/utility/make_options.rs +1 -1
  112. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sekreto.ts +8 -2
  113. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Sigv4.ts +29 -5
  114. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/index.ts +30 -21
  115. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/Registry.ts +90 -0
  116. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/addr.ts +48 -0
  117. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/aws.ts +219 -0
  118. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/azuresecrets.ts +144 -0
  119. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/boru.ts +113 -0
  120. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/doppler.ts +77 -0
  121. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/dotenv.ts +51 -0
  122. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/env.ts +31 -0
  123. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/file.ts +54 -0
  124. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/gcpsecrets.ts +118 -0
  125. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/hashicorp.ts +159 -0
  126. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/http.ts +66 -0
  127. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/infisical.ts +107 -0
  128. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/memory.ts +33 -0
  129. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/onepassword.ts +120 -0
  130. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/provider/support.ts +176 -0
  131. package/project/.sdk/tm/ts/test/vendor/omni/Runner.ts +15 -13
  132. package/project/.sdk/tm/ts/test/vendor/omni/Util.ts +25 -19
  133. package/project/.sdk/tm/ts/test/vendor/omni/compat.ts +1 -1
  134. package/project/.sdk/tm/ts/test/vendor/omni/index.ts +1 -1
  135. package/project/.sdk/tm/zig/core/utility.zig +1 -1
  136. package/project/sdkgen-package.json +1 -1
  137. package/src/cmp/Feature.ts +5 -0
  138. package/src/cmp/TestControl.ts +64 -0
  139. package/src/helpers/featureSource.ts +130 -0
  140. package/src/helpers/opShape.ts +9 -14
  141. package/src/helpers/pointPath.ts +88 -0
  142. package/src/sdkgen.ts +18 -0
  143. package/src/utility.ts +92 -1
  144. package/project/.sdk/tm/ts/src/feature/secrets/sekreto/Providers.ts +0 -1221
@@ -0,0 +1,51 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/dotenv.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import { ProviderSpec, Provider, SekretoError, envkey, nodemod, parsedotenv } from './support'
7
+
8
+ export function dotenvprovider(file: string, prefix?: string): Provider {
9
+ let values: Record<string, string> | undefined
10
+
11
+ const load = () => {
12
+ if (undefined === values) {
13
+ try {
14
+ const { readFileSync } = nodemod<typeof import('node:fs')>('node:fs')
15
+ values = parsedotenv(readFileSync(file, 'utf8'))
16
+ } catch (err: any) {
17
+ // An absent file - or an absent directory - means "no secrets
18
+ // here", exactly like fileprovider. Anything else (permission
19
+ // denied, an unreadable mount) is a store that could not answer,
20
+ // and swallowing it would fall through to a weaker store.
21
+ if ('ENOENT' === err.code || 'ENOTDIR' === err.code) {
22
+ values = {}
23
+ } else {
24
+ throw new SekretoError(
25
+ 'sekreto: dotenv provider cannot read ' + file + ': ' + err.message,
26
+ )
27
+ }
28
+ }
29
+ }
30
+ return values
31
+ }
32
+
33
+ return {
34
+ lookup: (name: string) => load()[envkey(name, prefix)],
35
+ describe: () => 'dotenv:' + file,
36
+ }
37
+ }
38
+
39
+ /** Literal values, keyed like environment variables. The spec uses this
40
+ * to test chain behaviour without touching the outside world. */
41
+
42
+
43
+ // Registering at import is what makes this module's presence the only
44
+ // thing that decides whether the kind exists in a build.
45
+ import { register } from './Registry'
46
+
47
+ register({
48
+ name: 'dotenv',
49
+ needs: ['fs'],
50
+ define: (spec: ProviderSpec) => dotenvprovider(spec.file || '.env', spec.prefix),
51
+ })
@@ -0,0 +1,31 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/env.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import { ProviderSpec, Provider, envkey } from './support'
7
+
8
+ export function envprovider(prefix?: string, source?: Record<string, any>): Provider {
9
+ const env = source || process.env
10
+
11
+ return {
12
+ lookup: (name: string) => {
13
+ const value = env[envkey(name, prefix)]
14
+ return undefined === value || null === value ? undefined : String(value)
15
+ },
16
+ describe: () => 'env' + (prefix ? ':' + prefix : ''),
17
+ }
18
+ }
19
+
20
+ /** A `.env` file, read once, keyed exactly like the environment. */
21
+
22
+
23
+ // Registering at import is what makes this module's presence the only
24
+ // thing that decides whether the kind exists in a build.
25
+ import { register } from './Registry'
26
+
27
+ register({
28
+ name: 'env',
29
+ needs: [],
30
+ define: (spec: ProviderSpec) => envprovider(spec.prefix),
31
+ })
@@ -0,0 +1,54 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/file.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import {
7
+ ProviderSpec, Provider, SekretoError, envkey, flatname, nodemod,
8
+ } from './support'
9
+
10
+ export function fileprovider(dir: string, prefix?: string): Provider {
11
+ return {
12
+ lookup: (name: string) => {
13
+ const { join } = nodemod<typeof import('node:path')>('node:path')
14
+ const file = join(dir, envkey(name, prefix))
15
+
16
+ let text: string
17
+ try {
18
+ const { readFileSync } = nodemod<typeof import('node:fs')>('node:fs')
19
+ text = readFileSync(file, 'utf8')
20
+ } catch (err: any) {
21
+ // An absent file - or an absent directory - means "no secrets
22
+ // here", exactly like a missing .env. Anything else (permission
23
+ // denied, an unreadable mount) is a store that could not answer.
24
+ if ('ENOENT' === err.code || 'ENOTDIR' === err.code) {
25
+ return undefined
26
+ }
27
+ throw new SekretoError('sekreto: file provider cannot read ' + file + ': ' + err.message)
28
+ }
29
+
30
+ return text.replace(/\r?\n$/, '')
31
+ },
32
+ describe: () => 'file:' + dir,
33
+ }
34
+ }
35
+
36
+ /** Refuse to send a secret-bearing credential in the clear.
37
+ *
38
+ * A vault API is HTTPS in any real deployment; plaintext is a dev-mode
39
+ * convenience. Sending a token over http to anything but the local
40
+ * machine puts both the token and the secret it fetches on the wire for
41
+ * anyone on the path, so sekreto will not do it. Loopback stays allowed:
42
+ * that is `vault server -dev`, `boru vault serve`, and this repo's own
43
+ * test harness. */
44
+
45
+
46
+ // Registering at import is what makes this module's presence the only
47
+ // thing that decides whether the kind exists in a build.
48
+ import { register } from './Registry'
49
+
50
+ register({
51
+ name: 'file',
52
+ needs: ['fs'],
53
+ define: (spec: ProviderSpec) => fileprovider(spec.dir || '', spec.prefix),
54
+ })
@@ -0,0 +1,118 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/gcpsecrets.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import { ProviderSpec, Provider, SekretoError, flatname, nodemod } from './support'
7
+ import { checkaddr } from './addr'
8
+ import { fetchjson } from './http'
9
+
10
+ export function gcpsecretsprovider(options?: {
11
+ project?: string
12
+ token?: string
13
+ addr?: string
14
+ metadataaddr?: string
15
+ }): Provider {
16
+ const opts = options || {}
17
+
18
+ // A configured token is kept forever; a metadata-server token carries
19
+ // expires_in and is renewed shortly before it runs out.
20
+ let livetoken: string | undefined
21
+ let renewat = Infinity
22
+
23
+ const metadataaddr = (): string => {
24
+ if (opts.metadataaddr) {
25
+ return opts.metadataaddr
26
+ }
27
+ const host = process.env.GCE_METADATA_HOST
28
+ return host ? 'http://' + host : 'http://metadata.google.internal'
29
+ }
30
+
31
+ const login = async (): Promise<string> => {
32
+ const configured = opts.token || process.env.GOOGLE_OAUTH_ACCESS_TOKEN
33
+ if (configured) {
34
+ return configured
35
+ }
36
+
37
+ const url =
38
+ metadataaddr().replace(/\/$/, '') +
39
+ '/computeMetadata/v1/instance/service-accounts/default/token'
40
+
41
+ const res = await fetchjson('GET', url, { 'Metadata-Flavor': 'Google' })
42
+
43
+ const got = res.body && res.body.access_token
44
+ if (200 !== res.status || !got) {
45
+ throw new SekretoError('sekreto: gcp: no token and metadata server did not answer')
46
+ }
47
+
48
+ const expires = Number(res.body.expires_in)
49
+ renewat = 0 < expires ? Date.now() + Math.max(expires - 60, 1) * 1000 : Infinity
50
+
51
+ return String(got)
52
+ }
53
+
54
+ return {
55
+ lookup: async (name: string) => {
56
+ const project = opts.project || ''
57
+ if ('' === project) {
58
+ throw new SekretoError('sekreto: gcp: no project')
59
+ }
60
+
61
+ const addr = opts.addr || 'https://secretmanager.googleapis.com'
62
+ checkaddr(addr)
63
+
64
+ if (undefined === livetoken || Date.now() >= renewat) {
65
+ livetoken = await login()
66
+ }
67
+
68
+ const url =
69
+ addr.replace(/\/$/, '') +
70
+ '/v1/projects/' +
71
+ project +
72
+ '/secrets/' +
73
+ flatname(name, '_') +
74
+ '/versions/latest:access'
75
+
76
+ const res = await fetchjson('GET', url, { authorization: 'Bearer ' + livetoken })
77
+
78
+ if (404 === res.status) {
79
+ return undefined
80
+ }
81
+
82
+ if (200 !== res.status) {
83
+ throw new SekretoError('sekreto: gcp error: ' + res.status + ': ' + url)
84
+ }
85
+
86
+ const data = res.body && res.body.payload && res.body.payload.data
87
+ if ('string' !== typeof data) {
88
+ return undefined
89
+ }
90
+
91
+ return Buffer.from(data, 'base64').toString('utf8')
92
+ },
93
+ describe: () => 'gcpsecrets:' + (opts.project || ''),
94
+ }
95
+ }
96
+
97
+ /** Azure Key Vault.
98
+ *
99
+ * `api.token` reads secret `api-token` (dots flattened to `-`; Key
100
+ * Vault names allow nothing else), current version. The token comes
101
+ * from config, then a client-credentials login when tenant/clientid/
102
+ * clientsecret are given, then the IMDS managed-identity endpoint - so
103
+ * on Azure's own platform no credential configuration is needed.
104
+ *
105
+ * As with GCP, the IMDS call is plain http to a link-local host by
106
+ * platform design and carries no credential; the login and vault
107
+ * addresses are `checkaddr`-guarded. */
108
+
109
+
110
+ // Registering at import is what makes this module's presence the only
111
+ // thing that decides whether the kind exists in a build.
112
+ import { register } from './Registry'
113
+
114
+ register({
115
+ name: 'gcpsecrets',
116
+ needs: ['fetch'],
117
+ define: (spec: ProviderSpec) => gcpsecretsprovider(spec),
118
+ })
@@ -0,0 +1,159 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/hashicorp.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import { ProviderSpec, Provider, SekretoError, checkname, nodemod, vaultref } from './support'
7
+ import { checkaddr } from './addr'
8
+ import { fetchjson } from './http'
9
+
10
+ export function hashicorpprovider(
11
+ addr: string,
12
+ token: string,
13
+ options?: {
14
+ mount?: string
15
+ kv?: number
16
+ vaultnamespace?: string
17
+ auth?: ProviderSpec['auth']
18
+ },
19
+ ): Provider {
20
+ const opts = options || {}
21
+ const usemount = opts.mount || 'secret'
22
+ const kv = opts.kv || 2
23
+
24
+ // A version typo like kv: 3 must not quietly behave as v2 and turn
25
+ // its 404s into misses; there is nothing safe to assume it meant.
26
+ if (1 !== kv && 2 !== kv) {
27
+ throw new SekretoError('sekreto: hashicorp: unsupported kv version: ' + String(kv))
28
+ }
29
+
30
+ // The working token: a configured token is kept forever, a logged-in
31
+ // token is renewed shortly before its lease runs out - a long-running
32
+ // process must not keep presenting a token the vault already expired.
33
+ let livetoken: string | undefined = '' === token ? undefined : token
34
+ let renewat = Infinity
35
+
36
+ const baseheaders = (): Record<string, string> => {
37
+ const headers: Record<string, string> = {}
38
+ if (opts.vaultnamespace) {
39
+ headers['X-Vault-Namespace'] = opts.vaultnamespace
40
+ }
41
+ return headers
42
+ }
43
+
44
+ const login = async (): Promise<string> => {
45
+ const auth = opts.auth
46
+ if (!auth) {
47
+ throw new SekretoError('sekreto: hashicorp: no token and no auth method')
48
+ }
49
+
50
+ const mount = auth.mount || auth.method
51
+ const url = addr.replace(/\/$/, '') + '/v1/auth/' + mount + '/login'
52
+
53
+ let body: any
54
+ if ('kubernetes' === auth.method) {
55
+ let jwt = auth.jwt
56
+ if (undefined === jwt) {
57
+ const file = auth.jwtfile || '/var/run/secrets/kubernetes.io/serviceaccount/token'
58
+ try {
59
+ const { readFileSync } = nodemod<typeof import('node:fs')>('node:fs')
60
+ jwt = readFileSync(file, 'utf8').trim()
61
+ } catch (err: any) {
62
+ throw new SekretoError('sekreto: hashicorp: cannot read jwt file ' + file)
63
+ }
64
+ }
65
+ body = { role: auth.role || '', jwt }
66
+ } else if ('approle' === auth.method) {
67
+ body = { role_id: auth.roleid || '', secret_id: auth.secretid || '' }
68
+ } else {
69
+ throw new SekretoError('sekreto: hashicorp: unknown auth method: ' + String(auth.method))
70
+ }
71
+
72
+ const res = await fetchjson('POST', url, baseheaders(), JSON.stringify(body))
73
+
74
+ const got = res.body && res.body.auth && res.body.auth.client_token
75
+ if (200 !== res.status || !got) {
76
+ throw new SekretoError('sekreto: hashicorp login failed: ' + res.status + ': ' + url)
77
+ }
78
+
79
+ const lease = Number(res.body.auth.lease_duration)
80
+ renewat = 0 < lease ? Date.now() + Math.max(lease - 60, 1) * 1000 : Infinity
81
+
82
+ return String(got)
83
+ }
84
+
85
+ return {
86
+ lookup: async (name: string) => {
87
+ checkaddr(addr)
88
+
89
+ if (undefined === livetoken || Date.now() >= renewat) {
90
+ livetoken = await login()
91
+ }
92
+
93
+ const ref = vaultref(name)
94
+ const base = addr.replace(/\/$/, '') + '/v1/' + usemount
95
+ const url = 1 === kv ? base + '/' + ref.path : base + '/data/' + ref.path
96
+
97
+ const headers = baseheaders()
98
+ headers['X-Vault-Token'] = livetoken
99
+
100
+ const res = await fetchjson('GET', url, headers)
101
+
102
+ if (404 === res.status) {
103
+ return undefined
104
+ }
105
+
106
+ if (200 !== res.status) {
107
+ throw new SekretoError('sekreto: hashicorp error: ' + res.status + ': ' + url)
108
+ }
109
+
110
+ const data =
111
+ 1 === kv ? res.body && res.body.data : res.body && res.body.data && res.body.data.data
112
+
113
+ const value = data ? data[ref.field] : undefined
114
+ return undefined === value || null === value ? undefined : String(value)
115
+ },
116
+ describe: () => 'hashicorp:' + addr + '/' + usemount,
117
+ }
118
+ }
119
+
120
+ /** A boru vault (https://github.com/boru-lang/boru).
121
+ *
122
+ * Two ways in, both boru's own.
123
+ *
124
+ * With no `addr`, the CLI: `boru vault get --reveal <alias>` prints the
125
+ * secret on stdout and nothing else. The passphrase is read by boru
126
+ * itself from `BORU_VAULT_PASSPHRASE`; sekreto never accepts it as
127
+ * config and never puts it on a command line, where it would show up in
128
+ * the process table.
129
+ *
130
+ * With an `addr`, boru's wire protocol: `boru vault serve` publishes a
131
+ * read-only, HashiCorp-shaped provision API (boru's
132
+ * design/VAULT-WIRE-PROTOCOL.0.md), authenticated by a capability token
133
+ * from `boru vault grant`. A sekreto name is already a valid boru
134
+ * alias, and boru aliases keep their dots, so `api.token` is the single
135
+ * path segment `api.token` - not the `api`/`token` split a HashiCorp KV
136
+ * gets. The value is the `value` field. A 404 is a miss; anything else
137
+ * the server refuses (a revoked capability, a sealed vault) is an
138
+ * error.
139
+ *
140
+ * boru's `vault proxy` and `vault mcp` remain out of bounds: they are a
141
+ * credential *broker*, built precisely so the caller never receives the
142
+ * credential. `vault serve` is the provision endpoint, built to hand
143
+ * the value back - that is the one sekreto uses. */
144
+
145
+
146
+ // Registering at import is what makes this module's presence the only
147
+ // thing that decides whether the kind exists in a build.
148
+ import { register } from './Registry'
149
+
150
+ register({
151
+ name: 'hashicorp',
152
+ needs: ['fetch', 'fs'],
153
+ define: (spec: ProviderSpec) => hashicorpprovider(spec.addr || '', spec.token || '', {
154
+ mount: spec.mount,
155
+ kv: spec.kv,
156
+ vaultnamespace: spec.vaultnamespace,
157
+ auth: spec.auth,
158
+ }),
159
+ })
@@ -0,0 +1,66 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/http.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import { SekretoError } from './support'
7
+ import { checkaddr } from './addr'
8
+
9
+ // Moved here with fetchjson, its only consumer.
10
+ const HTTP_TIMEOUT_MS = 10000
11
+
12
+ export async function fetchjson(
13
+ method: string,
14
+ url: string,
15
+ headers: Record<string, string>,
16
+ body?: string,
17
+ ): Promise<{ status: number; body: any }> {
18
+ let res: Response
19
+ try {
20
+ res = await fetch(url, {
21
+ method,
22
+ headers,
23
+ body,
24
+ // A vault API never legitimately redirects, and a followed redirect
25
+ // carries X-Vault-Token to the redirect's host (and can downgrade
26
+ // https to http), which checkaddr - it only validates the configured
27
+ // address - cannot see. Refuse to follow one.
28
+ redirect: 'error',
29
+ // Bound the wait so an accepted-but-silent endpoint cannot hang the
30
+ // caller (and the app's startup) forever.
31
+ signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
32
+ })
33
+ } catch (err: any) {
34
+ throw new SekretoError('sekreto: cannot reach ' + url.split('?')[0] + ': ' + err.message)
35
+ }
36
+
37
+ let parsed: any = undefined
38
+ try {
39
+ parsed = await res.json()
40
+ } catch (err: any) {
41
+ // A success status promised JSON; a body that does not parse means
42
+ // the store could not answer coherently, and treating it as a miss
43
+ // would fall through to a weaker store. Error statuses may carry
44
+ // any body - they are decided on status alone.
45
+ if (200 === res.status) {
46
+ throw new SekretoError('sekreto: malformed response from ' + url.split('?')[0])
47
+ }
48
+ }
49
+
50
+ return { status: res.status, body: parsed }
51
+ }
52
+
53
+ /** HashiCorp Vault.
54
+ *
55
+ * KV v2 (the default): `api.token` reads `{addr}/v1/{mount}/data/api`
56
+ * and takes the `token` field of `data.data`. KV v1 (`kv: 1`) reads
57
+ * `{addr}/v1/{mount}/api` and takes the field of `data`. A 404 means
58
+ * "not here" - a miss - so a vault can sit in a chain with fallbacks.
59
+ *
60
+ * A Vault Enterprise namespace rides the X-Vault-Namespace header, on
61
+ * logins as well as reads.
62
+ *
63
+ * Instead of being handed a token, the provider can log in: Kubernetes
64
+ * auth (the pod's service-account JWT, from its conventional path) or
65
+ * AppRole. A failed login is an error, never a miss - it means this
66
+ * store could not answer at all. */
@@ -0,0 +1,107 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/infisical.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import { ProviderSpec, Provider, SekretoError, envkey } from './support'
7
+ import { checkaddr } from './addr'
8
+ import { fetchjson } from './http'
9
+
10
+ export function infisicalprovider(options?: {
11
+ addr?: string
12
+ token?: string
13
+ clientid?: string
14
+ clientsecret?: string
15
+ project?: string
16
+ environment?: string
17
+ path?: string
18
+ }): Provider {
19
+ const opts = options || {}
20
+
21
+ // A configured token is kept forever; a universal-auth token carries
22
+ // expiresIn and is renewed shortly before it runs out.
23
+ let livetoken: string | undefined
24
+ let renewat = Infinity
25
+
26
+ const login = async (addr: string): Promise<string> => {
27
+ if (opts.token) {
28
+ return opts.token
29
+ }
30
+
31
+ if (!opts.clientid || !opts.clientsecret) {
32
+ throw new SekretoError('sekreto: infisical: no token and no client credentials')
33
+ }
34
+
35
+ const res = await fetchjson(
36
+ 'POST',
37
+ addr + '/api/v1/auth/universal-auth/login',
38
+ { 'content-type': 'application/json' },
39
+ JSON.stringify({ clientId: opts.clientid, clientSecret: opts.clientsecret }),
40
+ )
41
+
42
+ const got = res.body && res.body.accessToken
43
+ if (200 !== res.status || !got) {
44
+ throw new SekretoError('sekreto: infisical login failed: ' + res.status)
45
+ }
46
+
47
+ const expires = Number(res.body.expiresIn)
48
+ renewat = 0 < expires ? Date.now() + Math.max(expires - 60, 1) * 1000 : Infinity
49
+
50
+ return String(got)
51
+ }
52
+
53
+ return {
54
+ lookup: async (name: string) => {
55
+ const addr = (opts.addr || 'https://app.infisical.com').replace(/\/$/, '')
56
+ checkaddr(addr)
57
+
58
+ const project = opts.project || ''
59
+ const environment = opts.environment || ''
60
+ if ('' === project || '' === environment) {
61
+ throw new SekretoError('sekreto: infisical: no project/environment')
62
+ }
63
+
64
+ if (undefined === livetoken || Date.now() >= renewat) {
65
+ livetoken = await login(addr)
66
+ }
67
+
68
+ const url =
69
+ addr +
70
+ '/api/v3/secrets/raw/' +
71
+ envkey(name) +
72
+ '?workspaceId=' +
73
+ encodeURIComponent(project) +
74
+ '&environment=' +
75
+ encodeURIComponent(environment) +
76
+ '&secretPath=' +
77
+ encodeURIComponent(opts.path || '/')
78
+
79
+ const res = await fetchjson('GET', url, { authorization: 'Bearer ' + livetoken })
80
+
81
+ if (404 === res.status) {
82
+ return undefined
83
+ }
84
+
85
+ if (200 !== res.status) {
86
+ throw new SekretoError('sekreto: infisical error: ' + res.status)
87
+ }
88
+
89
+ const value = res.body && res.body.secret && res.body.secret.secretValue
90
+ return undefined === value || null === value ? undefined : String(value)
91
+ },
92
+ describe: () => 'infisical:' + (opts.project || '') + '/' + (opts.environment || ''),
93
+ }
94
+ }
95
+
96
+ /** Build a provider from its declarative form. */
97
+
98
+
99
+ // Registering at import is what makes this module's presence the only
100
+ // thing that decides whether the kind exists in a build.
101
+ import { register } from './Registry'
102
+
103
+ register({
104
+ name: 'infisical',
105
+ needs: ['fetch'],
106
+ define: (spec: ProviderSpec) => infisicalprovider(spec),
107
+ })
@@ -0,0 +1,33 @@
1
+ // VENDORED: @voxgig/sekreto 0.1.2 (typescript/src/provider/memory.ts)
2
+ // Source: https://github.com/voxgig/sekreto @ 65009cb5758850db767785ab666e71895f86086b
3
+ // License: MIT (c) voxgig - see repository LICENSE. Do not edit: resync from upstream.
4
+ /* Copyright (c) 2025 Voxgig Ltd, MIT License */
5
+
6
+ import { ProviderSpec, Provider, envkey } from './support'
7
+
8
+ export function memoryprovider(values: Record<string, string>, prefix?: string): Provider {
9
+ return {
10
+ lookup: (name: string) => values[envkey(name, prefix)],
11
+ describe: () => 'memory' + (prefix ? ':' + prefix : ''),
12
+ }
13
+ }
14
+
15
+ /** A directory of one-secret-per-file entries, keyed like the
16
+ * environment: `api.token` reads `<dir>/API_TOKEN`.
17
+ *
18
+ * This is the shape of a mounted Kubernetes Secret, a Docker or Swarm
19
+ * secret, and a systemd credentials directory, so those all work with no
20
+ * further configuration. One trailing newline is stripped - tools that
21
+ * write these files disagree about it, and a newline is never part of a
22
+ * secret on purpose. */
23
+
24
+
25
+ // Registering at import is what makes this module's presence the only
26
+ // thing that decides whether the kind exists in a build.
27
+ import { register } from './Registry'
28
+
29
+ register({
30
+ name: 'memory',
31
+ needs: [],
32
+ define: (spec: ProviderSpec) => memoryprovider(spec.values || {}, spec.prefix),
33
+ })