hanzio 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +832 -219
- package/dist/api-wrapper/builders.d.ts +15 -3
- package/dist/api-wrapper/create-client.d.ts +1 -1
- package/dist/api-wrapper/describe.d.ts +16 -0
- package/dist/api-wrapper/endpoint.d.ts +18 -0
- package/dist/api-wrapper/errors.d.ts +40 -2
- package/dist/api-wrapper/headers.d.ts +2 -1
- package/dist/api-wrapper/index.d.ts +5 -0
- package/dist/api-wrapper/otel.d.ts +44 -0
- package/dist/api-wrapper/paginate.d.ts +12 -0
- package/dist/api-wrapper/redaction.d.ts +2 -0
- package/dist/api-wrapper/shared.d.ts +16 -2
- package/dist/api-wrapper/testing.d.ts +23 -0
- package/dist/api-wrapper/transport.d.ts +12 -10
- package/dist/api-wrapper/types.d.ts +104 -18
- package/dist/array/index.d.ts +50 -4
- package/dist/cache/createCache.d.ts +31 -0
- package/dist/cache/index.d.ts +45 -2
- package/dist/color/contrast.d.ts +40 -0
- package/dist/color/index.d.ts +2 -0
- package/dist/{math → color}/metricsSeriesColors.d.ts +14 -7
- package/dist/cool-console-log/index.d.ts +34 -5
- package/dist/crypto/encoding.d.ts +18 -0
- package/dist/crypto/hash.d.ts +33 -0
- package/dist/crypto/index.d.ts +4 -0
- package/dist/crypto/sealer.d.ts +25 -0
- package/dist/crypto/webhook.d.ts +20 -0
- package/dist/date/index.d.ts +46 -2
- package/dist/emitter/index.d.ts +21 -0
- package/dist/error/index.d.ts +12 -0
- package/dist/format/index.d.ts +102 -0
- package/dist/function/index.d.ts +60 -0
- package/dist/guard/index.d.ts +20 -0
- package/dist/i18n/index.d.ts +37 -0
- package/dist/index-0zvtcbzy.js +224 -0
- package/dist/index-181e6vfw.js +29 -0
- package/dist/index-8r2vdzjg.js +87 -0
- package/dist/index-hy7kbb99.js +157 -0
- package/dist/{src/secrets/loaders.js → index-j1ybktbr.js} +608 -405
- package/dist/index-k9yx6bne.js +89 -0
- package/dist/index-kj1phgrx.js +169 -0
- package/dist/index-mnkcfyx8.js +78 -0
- package/dist/{p-queue/index.js → index-pwt0dz63.js} +7 -11
- package/dist/index-rp5pqghz.js +157 -0
- package/dist/index-srt06xd2.js +15 -0
- package/dist/index-wjyb9bsd.js +263 -0
- package/dist/index.d.ts +7 -7
- package/dist/jwt/index.d.ts +50 -2
- package/dist/math/index.d.ts +2 -2
- package/dist/math/linearRegressionTrend.d.ts +14 -1
- package/dist/math/scale.d.ts +8 -0
- package/dist/object/index.d.ts +19 -0
- package/dist/p-queue/index.d.ts +1 -1
- package/dist/promise/index.d.ts +97 -2
- package/dist/secrets/core.d.ts +47 -0
- package/dist/secrets/deadline.d.ts +6 -0
- package/dist/secrets/environment.d.ts +9 -0
- package/dist/secrets/errors.d.ts +11 -0
- package/dist/secrets/index.d.ts +13 -25
- package/dist/secrets/infisical-cli.d.ts +6 -0
- package/dist/secrets/infisical-config.d.ts +17 -0
- package/dist/secrets/infisical-session.d.ts +4 -0
- package/dist/secrets/infisical.d.ts +8 -13
- package/dist/secrets/loaders.d.ts +5 -12
- package/dist/secrets/vite.d.ts +7 -3
- package/dist/secrets/worker-loader.d.ts +9 -0
- package/dist/secrets/worker.d.ts +3 -0
- package/dist/sitemap/index.d.ts +48 -6
- package/dist/src/api-wrapper/index.js +60 -1100
- package/dist/src/api-wrapper/testing.js +20 -0
- package/dist/{cool-console-log → src/cool-console-log}/index.js +80 -33
- package/dist/src/crypto/index.js +195 -0
- package/dist/src/format/index.js +211 -0
- package/dist/src/i18n/index.js +36 -0
- package/dist/src/index.js +863 -2809
- package/dist/src/jwt/index.js +138 -92
- package/dist/src/p-queue/index.js +9 -437
- package/dist/src/secrets/index.js +261 -1377
- package/dist/src/secrets/vite.js +6 -17
- package/dist/src/secrets/worker.js +12 -0
- package/dist/src/sitemap/index.js +183 -511
- package/dist/src/storage/index.js +119 -0
- package/dist/src/zod/index.js +347 -174
- package/dist/state/index.d.ts +21 -0
- package/dist/storage/index.d.ts +58 -0
- package/dist/string/index.d.ts +75 -2
- package/dist/typedswitch/index.d.ts +37 -26
- package/dist/types/index.d.ts +11 -0
- package/dist/url/index.d.ts +50 -1
- package/dist/zod/index.d.ts +32 -3
- package/dist/zod/introspect.d.ts +19 -0
- package/dist/zod/jsonToZodSchemaMapper.d.ts +3 -1
- package/dist/zod/zodMinMaxFilter.d.ts +22 -3
- package/dist/zod/zodToTypeString.d.ts +8 -3
- package/package.json +87 -7
- package/src/api-wrapper/actions-impl.ts +11 -72
- package/src/api-wrapper/builders.ts +84 -52
- package/src/api-wrapper/create-client.ts +246 -96
- package/src/api-wrapper/describe.ts +157 -0
- package/src/api-wrapper/endpoint.ts +26 -0
- package/src/api-wrapper/error-reporting.ts +7 -1
- package/src/api-wrapper/errors.ts +117 -2
- package/src/api-wrapper/headers.ts +19 -1
- package/src/api-wrapper/index.ts +5 -0
- package/src/api-wrapper/otel.ts +139 -0
- package/src/api-wrapper/paginate.ts +28 -0
- package/src/api-wrapper/redaction.ts +43 -0
- package/src/api-wrapper/shared.ts +21 -2
- package/src/api-wrapper/testing.ts +48 -0
- package/src/api-wrapper/transport.ts +68 -74
- package/src/api-wrapper/types.ts +186 -18
- package/src/array/index.ts +195 -42
- package/src/cache/createCache.ts +157 -0
- package/src/cache/index.ts +235 -83
- package/src/color/contrast.ts +163 -0
- package/src/color/index.ts +22 -0
- package/src/{math → color}/metricsSeriesColors.ts +47 -49
- package/src/cool-console-log/index.ts +128 -41
- package/src/crypto/encoding.ts +83 -0
- package/src/crypto/hash.ts +170 -0
- package/src/crypto/index.ts +26 -0
- package/src/crypto/sealer.ts +168 -0
- package/src/crypto/webhook.ts +86 -0
- package/src/date/index.ts +109 -34
- package/src/emitter/index.ts +82 -0
- package/src/error/index.ts +63 -0
- package/src/format/index.ts +389 -0
- package/src/function/index.ts +201 -0
- package/src/guard/index.ts +80 -0
- package/src/i18n/index.ts +125 -0
- package/src/index.ts +11 -7
- package/src/jwt/index.ts +199 -125
- package/src/math/index.ts +5 -10
- package/src/math/linearRegressionTrend.ts +33 -8
- package/src/math/scale.ts +17 -0
- package/src/object/index.ts +99 -0
- package/src/p-queue/index.ts +2 -0
- package/src/promise/index.ts +304 -9
- package/src/secrets/core.ts +348 -0
- package/src/secrets/deadline.ts +69 -0
- package/src/secrets/environment.ts +69 -0
- package/src/secrets/errors.ts +29 -0
- package/src/secrets/index.ts +79 -210
- package/src/secrets/infisical-cli.ts +99 -0
- package/src/secrets/infisical-config.ts +70 -0
- package/src/secrets/infisical-session.ts +42 -0
- package/src/secrets/infisical.ts +214 -66
- package/src/secrets/loaders.ts +24 -48
- package/src/secrets/vite.ts +44 -15
- package/src/secrets/worker-loader.ts +56 -0
- package/src/secrets/worker.ts +4 -0
- package/src/sitemap/index.ts +293 -130
- package/src/state/index.ts +133 -81
- package/src/storage/index.ts +187 -0
- package/src/string/index.ts +261 -23
- package/src/typedswitch/index.ts +134 -103
- package/src/types/index.ts +14 -0
- package/src/url/index.ts +146 -1
- package/src/zod/index.ts +105 -9
- package/src/zod/introspect.ts +103 -0
- package/src/zod/jsonToZodSchemaMapper.ts +44 -21
- package/src/zod/zodMinMaxFilter.ts +37 -13
- package/src/zod/zodToTypeString.ts +184 -181
- package/dist/api-wrapper/index.js +0 -250
- package/dist/gzip/index.d.ts +0 -2
- package/dist/gzip/index.js +0 -16
- package/dist/index.js +0 -1823
- package/dist/jwt/index.js +0 -19
- package/dist/sitemap/index.js +0 -552
- package/dist/src/gzip/index.js +0 -16
- package/dist/src/secrets/infisical.js +0 -1194
- package/dist/zod/index.js +0 -210
package/src/secrets/index.ts
CHANGED
|
@@ -1,225 +1,94 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export type DefineSecretSetOptions<SecretKey extends string> =
|
|
25
|
-
| InfisicalDefineSecretSetOptions
|
|
26
|
-
| CustomLoaderDefineSecretSetOptions<SecretKey>
|
|
27
|
-
|
|
28
|
-
export type SecretSetLoaderContext<SecretKey extends string> = {
|
|
29
|
-
readonly keys: readonly SecretKey[]
|
|
30
|
-
readonly environment: SecretEnvironment
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export type SecretSetLoader<SecretKey extends string> = (
|
|
34
|
-
context: SecretSetLoaderContext<SecretKey>
|
|
35
|
-
) => Promise<Partial<Record<SecretKey, string>>>
|
|
36
|
-
|
|
37
|
-
type ResolvedSecretSetOptions<SecretKey extends string> = {
|
|
38
|
-
readonly loader: SecretSetLoader<SecretKey>
|
|
39
|
-
readonly environment: SecretEnvironment
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export type SecretSet<SecretKey extends string> = {
|
|
43
|
-
secret: (key: SecretKey) => string
|
|
44
|
-
secrets: () => Record<SecretKey, string>
|
|
45
|
-
reload: () => Promise<SecretSet<SecretKey>>
|
|
46
|
-
}
|
|
47
|
-
|
|
1
|
+
import {
|
|
2
|
+
defineSecretSet as defineCoreSecretSet,
|
|
3
|
+
type CommonSecretSetOptions,
|
|
4
|
+
type CustomLoaderDefineSecretSetOptions,
|
|
5
|
+
type SecretSet,
|
|
6
|
+
type SecretSetLoader
|
|
7
|
+
} from './core'
|
|
8
|
+
import {
|
|
9
|
+
getProcessEnvironment,
|
|
10
|
+
resolveSecretEnvironment,
|
|
11
|
+
type SecretEnvironment
|
|
12
|
+
} from './environment'
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_INFISICAL_SITE_URL,
|
|
15
|
+
loadInfisicalSecrets,
|
|
16
|
+
type InfisicalAuthMode
|
|
17
|
+
} from './infisical'
|
|
18
|
+
import {
|
|
19
|
+
validateInfisicalOrganizationId,
|
|
20
|
+
type InfisicalProjectOptions
|
|
21
|
+
} from './infisical-config'
|
|
22
|
+
|
|
23
|
+
export * from './core'
|
|
48
24
|
export * from './infisical'
|
|
49
25
|
export * from './loaders'
|
|
50
26
|
export * from './vite'
|
|
27
|
+
export type { SecretRequestOptions } from './deadline'
|
|
28
|
+
|
|
29
|
+
export type InfisicalDefineSecretSetOptions<
|
|
30
|
+
K extends string = string,
|
|
31
|
+
Parsed = Record<K, string>
|
|
32
|
+
> = CommonSecretSetOptions<K, Parsed> &
|
|
33
|
+
InfisicalProjectOptions & {
|
|
34
|
+
readonly auth?: InfisicalAuthMode
|
|
35
|
+
readonly clientIdEnvKey?: string
|
|
36
|
+
readonly clientSecretEnvKey?: string
|
|
37
|
+
readonly loader?: undefined
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type DefineSecretSetOptions<
|
|
41
|
+
K extends string,
|
|
42
|
+
Parsed = Record<K, string>
|
|
43
|
+
> =
|
|
44
|
+
| InfisicalDefineSecretSetOptions<K, Parsed>
|
|
45
|
+
| CustomLoaderDefineSecretSetOptions<K, Parsed>
|
|
51
46
|
|
|
52
47
|
export function getSecretEnvironment(): SecretEnvironment {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
return resolveSecretEnvironment(
|
|
49
|
+
undefined,
|
|
50
|
+
getProcessEnvironment()
|
|
51
|
+
) as SecretEnvironment
|
|
57
52
|
}
|
|
58
53
|
|
|
59
54
|
export async function defineSecretSet<
|
|
60
|
-
const
|
|
55
|
+
const Keys extends readonly string[],
|
|
56
|
+
Parsed = Record<Keys[number], string>
|
|
61
57
|
>(
|
|
62
|
-
keys:
|
|
63
|
-
options: DefineSecretSetOptions<
|
|
64
|
-
): Promise<SecretSet<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
keySet,
|
|
74
|
-
cachedSecrets,
|
|
75
|
-
force,
|
|
76
|
-
...resolvedOptions
|
|
77
|
-
})
|
|
78
|
-
cachedSecrets = nextSecrets
|
|
79
|
-
return nextSecrets
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const secretSet = createSecretSet({
|
|
83
|
-
getCachedSecrets: () => cachedSecrets,
|
|
84
|
-
reload: loadSecrets
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
await loadSecrets()
|
|
88
|
-
return secretSet
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function resolveSecretSetOptions<SecretKey extends string>(
|
|
92
|
-
options: DefineSecretSetOptions<SecretKey>
|
|
93
|
-
): ResolvedSecretSetOptions<SecretKey> {
|
|
94
|
-
const environment = resolveEnvironment(options.environment)
|
|
95
|
-
const loader: SecretSetLoader<SecretKey> = options.loader
|
|
96
|
-
? options.loader
|
|
97
|
-
: (context) =>
|
|
98
|
-
loadInfisicalSecrets({
|
|
99
|
-
...context,
|
|
100
|
-
projectId: options.projectId,
|
|
101
|
-
siteUrl: options.siteUrl ?? DEFAULT_INFISICAL_SITE_URL,
|
|
102
|
-
clientIdEnvKey: options.clientIdEnvKey ?? 'INFISICAL_CLIENT_ID',
|
|
103
|
-
clientSecretEnvKey:
|
|
104
|
-
options.clientSecretEnvKey ?? 'INFISICAL_CLIENT_SECRET'
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
return {
|
|
108
|
-
loader,
|
|
109
|
-
environment
|
|
58
|
+
keys: Keys,
|
|
59
|
+
options: DefineSecretSetOptions<Keys[number], Parsed>
|
|
60
|
+
): Promise<SecretSet<Keys[number], Parsed>> {
|
|
61
|
+
let loader: SecretSetLoader<Keys[number]>
|
|
62
|
+
|
|
63
|
+
if (!options.loader) {
|
|
64
|
+
// Validate even when environment overrides satisfy every key.
|
|
65
|
+
validateInfisicalOrganizationId(options.organizationId)
|
|
66
|
+
loader = createDefaultInfisicalLoader(options)
|
|
67
|
+
} else {
|
|
68
|
+
loader = options.loader
|
|
110
69
|
}
|
|
111
|
-
}
|
|
112
70
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
force
|
|
119
|
-
}: {
|
|
120
|
-
readonly keySet: readonly SecretKey[]
|
|
121
|
-
readonly cachedSecrets: Record<SecretKey, string> | null
|
|
122
|
-
readonly loader: SecretSetLoader<SecretKey>
|
|
123
|
-
readonly environment: SecretEnvironment
|
|
124
|
-
readonly force: boolean
|
|
125
|
-
}): Promise<Record<SecretKey, string>> {
|
|
126
|
-
const processEnvSecrets = readProcessEnvSecrets(
|
|
127
|
-
keySet,
|
|
128
|
-
force ? cachedSecrets : null
|
|
71
|
+
// Supply process.env only from this Node/Bun entry point, keeping the core portable.
|
|
72
|
+
return defineCoreSecretSet(
|
|
73
|
+
keys,
|
|
74
|
+
{ ...options, loader },
|
|
75
|
+
getProcessEnvironment()
|
|
129
76
|
)
|
|
130
|
-
const keysMissingFromEnv = keySet.filter((key) => !processEnvSecrets[key])
|
|
131
|
-
|
|
132
|
-
logSecretEnvironment(environment, keySet.length)
|
|
133
|
-
|
|
134
|
-
const loaderKeys = force ? keySet : keysMissingFromEnv
|
|
135
|
-
const loadedSecrets: Partial<Record<SecretKey, string>> =
|
|
136
|
-
loaderKeys.length === 0
|
|
137
|
-
? {}
|
|
138
|
-
: await loader({
|
|
139
|
-
keys: loaderKeys,
|
|
140
|
-
environment
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
const missingKeys = keySet.filter(
|
|
144
|
-
(key) => !(processEnvSecrets[key] ?? loadedSecrets[key])
|
|
145
|
-
)
|
|
146
|
-
if (missingKeys.length > 0) {
|
|
147
|
-
throw new Error(`Missing secrets: ${missingKeys.join(', ')}`)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const nextSecrets = Object.fromEntries(
|
|
151
|
-
keySet.map((key) => [
|
|
152
|
-
key,
|
|
153
|
-
(processEnvSecrets[key] ?? loadedSecrets[key]) as string
|
|
154
|
-
])
|
|
155
|
-
) as Record<SecretKey, string>
|
|
156
|
-
|
|
157
|
-
for (const [key, value] of Object.entries(nextSecrets)) {
|
|
158
|
-
process.env[key] = value as string
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return nextSecrets
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function createSecretSet<SecretKey extends string>({
|
|
165
|
-
getCachedSecrets,
|
|
166
|
-
reload
|
|
167
|
-
}: {
|
|
168
|
-
readonly getCachedSecrets: () => Record<SecretKey, string> | null
|
|
169
|
-
readonly reload: (force?: boolean) => Promise<Record<SecretKey, string>>
|
|
170
|
-
}): SecretSet<SecretKey> {
|
|
171
|
-
const secretSet: SecretSet<SecretKey> = {
|
|
172
|
-
secret(key) {
|
|
173
|
-
const value = getCachedSecrets()?.[key] ?? process.env[key]
|
|
174
|
-
if (!value) {
|
|
175
|
-
throw new Error(`Secret ${key} not found`)
|
|
176
|
-
}
|
|
177
|
-
return value
|
|
178
|
-
},
|
|
179
|
-
secrets() {
|
|
180
|
-
const cachedSecrets = getCachedSecrets()
|
|
181
|
-
if (!cachedSecrets) {
|
|
182
|
-
throw new Error('Secrets not initialized.')
|
|
183
|
-
}
|
|
184
|
-
return { ...cachedSecrets }
|
|
185
|
-
},
|
|
186
|
-
async reload() {
|
|
187
|
-
await reload(true)
|
|
188
|
-
return secretSet
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return secretSet
|
|
193
77
|
}
|
|
194
78
|
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
):
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function readProcessEnvSecrets<SecretKey extends string>(
|
|
213
|
-
keys: readonly SecretKey[],
|
|
214
|
-
previousSecrets: Record<SecretKey, string> | null = null
|
|
215
|
-
): Partial<Record<SecretKey, string>> {
|
|
216
|
-
// On forced reloads, skip cached values so the loader can refresh them.
|
|
217
|
-
return Object.fromEntries(
|
|
218
|
-
keys
|
|
219
|
-
.filter((key) => {
|
|
220
|
-
const value = process.env[key]
|
|
221
|
-
return value && value !== previousSecrets?.[key]
|
|
222
|
-
})
|
|
223
|
-
.map((key) => [key, process.env[key] as string])
|
|
224
|
-
) as Partial<Record<SecretKey, string>>
|
|
79
|
+
function createDefaultInfisicalLoader<K extends string, Parsed>(
|
|
80
|
+
options: InfisicalDefineSecretSetOptions<K, Parsed>
|
|
81
|
+
): SecretSetLoader<K> {
|
|
82
|
+
return (context) =>
|
|
83
|
+
loadInfisicalSecrets({
|
|
84
|
+
...context,
|
|
85
|
+
projectId: options.projectId,
|
|
86
|
+
organizationId: options.organizationId,
|
|
87
|
+
siteUrl: options.siteUrl ?? DEFAULT_INFISICAL_SITE_URL,
|
|
88
|
+
secretPath: options.secretPath,
|
|
89
|
+
auth: options.auth,
|
|
90
|
+
clientIdEnvKey: options.clientIdEnvKey ?? 'INFISICAL_CLIENT_ID',
|
|
91
|
+
clientSecretEnvKey:
|
|
92
|
+
options.clientSecretEnvKey ?? 'INFISICAL_CLIENT_SECRET'
|
|
93
|
+
})
|
|
225
94
|
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { SecretRequestOptions } from './deadline'
|
|
2
|
+
import { getProcessEnvironment } from './environment'
|
|
3
|
+
import { SecretLoadError } from './errors'
|
|
4
|
+
|
|
5
|
+
/** Read the active CLI session without a shell or credential-bearing errors. */
|
|
6
|
+
export async function readInfisicalCliToken(
|
|
7
|
+
options: SecretRequestOptions & { siteUrl: string; signal: AbortSignal }
|
|
8
|
+
): Promise<string> {
|
|
9
|
+
// Import subprocess support only when the CLI is actually selected.
|
|
10
|
+
const { execFile } = await import('node:child_process').catch(() => {
|
|
11
|
+
throw new SecretLoadError(
|
|
12
|
+
'CLI',
|
|
13
|
+
'Infisical CLI fallback requires a runtime with subprocess support. Provide Infisical client credentials or a custom secret loader.'
|
|
14
|
+
)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
options.signal.throwIfAborted()
|
|
18
|
+
|
|
19
|
+
const env: Record<string, string | undefined> = {
|
|
20
|
+
...getProcessEnvironment(),
|
|
21
|
+
INFISICAL_DISABLE_UPDATE_CHECK: 'true'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Inherited machine tokens would override the CLI's signed-in user session.
|
|
25
|
+
// Remove them from the child copy without changing the parent's environment.
|
|
26
|
+
delete env.INFISICAL_TOKEN
|
|
27
|
+
delete env.INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN
|
|
28
|
+
delete env.TOKEN
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const stdout = await new Promise<string>((resolve, reject) => {
|
|
32
|
+
const child = execFile(
|
|
33
|
+
'infisical',
|
|
34
|
+
[
|
|
35
|
+
'user',
|
|
36
|
+
'get',
|
|
37
|
+
'token',
|
|
38
|
+
'--plain',
|
|
39
|
+
`--domain=${options.siteUrl}`,
|
|
40
|
+
'--silent'
|
|
41
|
+
],
|
|
42
|
+
{
|
|
43
|
+
env,
|
|
44
|
+
encoding: 'utf8',
|
|
45
|
+
shell: false,
|
|
46
|
+
timeout: options.timeoutMs ?? 30_000,
|
|
47
|
+
signal: options.signal,
|
|
48
|
+
killSignal: 'SIGKILL',
|
|
49
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
50
|
+
windowsHide: true
|
|
51
|
+
},
|
|
52
|
+
(error, output) => {
|
|
53
|
+
if (error) {
|
|
54
|
+
reject(error)
|
|
55
|
+
} else {
|
|
56
|
+
resolve(output)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// Startup cannot wait for interactive input.
|
|
62
|
+
child.stdin?.end()
|
|
63
|
+
})
|
|
64
|
+
return stdout.trim()
|
|
65
|
+
} catch (error) {
|
|
66
|
+
if (options.signal.aborted) {
|
|
67
|
+
throw options.signal.reason
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Subprocess errors can contain secret stdout/stderr, so rebuild the error.
|
|
71
|
+
const failure = error as { code?: string | number; killed?: boolean } | null
|
|
72
|
+
|
|
73
|
+
if (failure?.code === 'ENOENT') {
|
|
74
|
+
throw new SecretLoadError(
|
|
75
|
+
'CLI',
|
|
76
|
+
'Infisical CLI not found. Install infisical and ensure it is on PATH, or provide Infisical client credentials.'
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (failure?.code === 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER') {
|
|
81
|
+
throw new SecretLoadError(
|
|
82
|
+
'CLI',
|
|
83
|
+
'Infisical CLI output exceeded the 10 MiB limit.'
|
|
84
|
+
)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (failure?.killed) {
|
|
88
|
+
throw new SecretLoadError(
|
|
89
|
+
'TIMEOUT',
|
|
90
|
+
`Infisical CLI timed out after ${(options.timeoutMs ?? 30_000) / 1000} seconds or was terminated.`
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw new SecretLoadError(
|
|
95
|
+
'CLI',
|
|
96
|
+
`Infisical CLI session token failed. Run infisical login --domain=${options.siteUrl} and verify access to the configured organization, project, and environment.`
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { SecretRequestOptions } from './deadline'
|
|
2
|
+
import { SecretLoadError } from './errors'
|
|
3
|
+
|
|
4
|
+
/** Project scope shared by the public Infisical loaders. */
|
|
5
|
+
export type InfisicalProjectOptions = {
|
|
6
|
+
readonly projectId: string
|
|
7
|
+
|
|
8
|
+
/** Scope CLI user authentication; machine identities already have an organization. */
|
|
9
|
+
readonly organizationId: string
|
|
10
|
+
readonly siteUrl?: string
|
|
11
|
+
readonly secretPath?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type InfisicalScope<K extends string> = InfisicalProjectOptions &
|
|
15
|
+
SecretRequestOptions & {
|
|
16
|
+
readonly keys: readonly K[]
|
|
17
|
+
readonly environment: string
|
|
18
|
+
readonly siteUrl: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function validateInfisicalScope(options: InfisicalScope<string>): void {
|
|
22
|
+
validateInfisicalOrganizationId(options.organizationId)
|
|
23
|
+
|
|
24
|
+
let url: URL
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
url = new URL(options.siteUrl)
|
|
28
|
+
} catch {
|
|
29
|
+
throw new SecretLoadError('CONFIGURATION', 'Invalid Infisical siteUrl.')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Keep credentials and other sensitive URL components out of diagnostics.
|
|
33
|
+
if (
|
|
34
|
+
!['http:', 'https:'].includes(url.protocol) ||
|
|
35
|
+
url.username ||
|
|
36
|
+
url.password ||
|
|
37
|
+
url.search ||
|
|
38
|
+
url.hash
|
|
39
|
+
) {
|
|
40
|
+
throw new SecretLoadError(
|
|
41
|
+
'CONFIGURATION',
|
|
42
|
+
'Infisical siteUrl must be an HTTP(S) URL without credentials, query, or fragment.'
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (
|
|
47
|
+
!options.projectId.trim() ||
|
|
48
|
+
!options.environment.trim() ||
|
|
49
|
+
!(options.secretPath ?? '/').startsWith('/')
|
|
50
|
+
) {
|
|
51
|
+
throw new SecretLoadError(
|
|
52
|
+
'CONFIGURATION',
|
|
53
|
+
'Infisical requires a projectId, environment, and absolute secretPath.'
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Validate at configuration time, including for JavaScript callers and local-only loads. */
|
|
59
|
+
export function validateInfisicalOrganizationId(organizationId: unknown): void {
|
|
60
|
+
if (
|
|
61
|
+
typeof organizationId !== 'string' ||
|
|
62
|
+
!organizationId.trim() ||
|
|
63
|
+
/\s/.test(organizationId)
|
|
64
|
+
) {
|
|
65
|
+
throw new SecretLoadError(
|
|
66
|
+
'CONFIGURATION',
|
|
67
|
+
'Infisical organizationId is required and must be a nonempty ID without whitespace.'
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { SecretLoadError } from './errors'
|
|
3
|
+
|
|
4
|
+
const sessionClaimsSchema = z.object({
|
|
5
|
+
organizationId: z.string().optional(),
|
|
6
|
+
subOrganizationId: z.string().optional(),
|
|
7
|
+
exp: z.number().finite().optional()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
/** Read routing metadata only. JWT signature and permissions are verified by Infisical. */
|
|
11
|
+
export function parseInfisicalSessionToken(token: string): {
|
|
12
|
+
organizationId?: string
|
|
13
|
+
} {
|
|
14
|
+
let claims: z.infer<typeof sessionClaimsSchema>
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
// Reject empty, formatted, or multiline CLI output before making an HTTP request.
|
|
18
|
+
if (!/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(token)) {
|
|
19
|
+
throw new Error('Invalid token format')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const payload = token.split('.')[1]!
|
|
23
|
+
const json = atob(payload.replace(/-/g, '+').replace(/_/g, '/'))
|
|
24
|
+
|
|
25
|
+
claims = sessionClaimsSchema.parse(JSON.parse(json))
|
|
26
|
+
} catch {
|
|
27
|
+
throw new SecretLoadError(
|
|
28
|
+
'AUTHENTICATION',
|
|
29
|
+
'Infisical returned an invalid user session token. Run infisical login for the configured domain.'
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (claims.exp !== undefined && claims.exp <= Date.now() / 1000) {
|
|
34
|
+
throw new SecretLoadError(
|
|
35
|
+
'AUTHENTICATION',
|
|
36
|
+
'Infisical user session has expired. Run infisical login for the configured domain.'
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// For sub-organizations, the root organization claim does not identify the scope.
|
|
41
|
+
return { organizationId: claims.subOrganizationId ?? claims.organizationId }
|
|
42
|
+
}
|