@biocomputingup/infisical-secrets-loader 1.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/LICENSE +21 -0
- package/README.md +212 -0
- package/dist/errors/error.d.ts +23 -0
- package/dist/errors/error.js +41 -0
- package/dist/helpers/mapSecretsToObjects.d.ts +11 -0
- package/dist/helpers/mapSecretsToObjects.js +58 -0
- package/dist/helpers/validate.d.ts +2 -0
- package/dist/helpers/validate.js +18 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +16 -0
- package/dist/logger/logger.d.ts +15 -0
- package/dist/logger/logger.js +53 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +60 -0
- package/dist/types/types.d.ts +6 -0
- package/dist/types/types.js +11 -0
- package/dist/validators/loadConfig.schema.d.ts +25 -0
- package/dist/validators/loadConfig.schema.js +51 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mehdi Zoubiri
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Infisical Secrets Loader
|
|
2
|
+
|
|
3
|
+
A lightweight Node.js library to load secrets from [Infisical](https://infisical.com) and transform them into a structured JavaScript object. Designed to work seamlessly with NestJS config modules.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @biocomputingup/infisical-secrets-loader
|
|
9
|
+
# or
|
|
10
|
+
yarn add @biocomputingup/infisical-secrets-loader
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Requirements:** Node.js 20+
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { loadSecretsFromInfisical, LogLevel } from '@biocomputingup/infisical-secrets-loader';
|
|
19
|
+
|
|
20
|
+
const config = await loadSecretsFromInfisical({
|
|
21
|
+
baseUrl: 'https://infisical.example.com',
|
|
22
|
+
environment: 'production',
|
|
23
|
+
projectId: '550e8400-e29b-41d4-a716-446655440000',
|
|
24
|
+
auth: {
|
|
25
|
+
clientId: 'your-client-id',
|
|
26
|
+
clientSecret: 'your-client-secret',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
console.log(config); // { secret1: 'value1', secret2: 'value2', ... }
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
### LoadConfig Shape
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
interface LoadConfig {
|
|
39
|
+
// Required
|
|
40
|
+
baseUrl: string; // Infisical instance URL (e.g., https://infisical.example.com)
|
|
41
|
+
environment: string; // Environment name (e.g., 'development', 'production')
|
|
42
|
+
projectId: string; // UUID of your Infisical project
|
|
43
|
+
auth: {
|
|
44
|
+
clientId: string; // Infisical machine identity client ID
|
|
45
|
+
clientSecret: string; // Infisical machine identity client secret
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Optional
|
|
49
|
+
mode?: 'rootOnly' | 'flat' | 'nested'; // Secret retrieval mode (default: 'rootOnly')
|
|
50
|
+
logger?: Logger; // Custom logger (default: console)
|
|
51
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error'; // Log level (default: 'debug')
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Retrieval Modes
|
|
56
|
+
|
|
57
|
+
Secrets are organized in Infisical using paths. This library offers three modes to transform them:
|
|
58
|
+
|
|
59
|
+
### `rootOnly` (default)
|
|
60
|
+
Returns only secrets in the root directory, ignoring secrets in subfolders.
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
Infisical:
|
|
64
|
+
/API_KEY
|
|
65
|
+
/db/HOST
|
|
66
|
+
/db/PORT
|
|
67
|
+
|
|
68
|
+
Result: { API_KEY: '...' }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### `flat`
|
|
72
|
+
Flattens the entire secret structure into a single-level object. <span style="color:FireBrick">**Last secret loaded wins on key collision.**</span>.
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
Result: { API_KEY: '...', HOST: '...', PORT: '...' }
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `nested`
|
|
79
|
+
Preserves the folder structure using nested objects.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
Result: {
|
|
83
|
+
API_KEY: '...',
|
|
84
|
+
db: { HOST: '...', PORT: '...' }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Error Handling
|
|
89
|
+
|
|
90
|
+
The library throws typed errors for different failure scenarios:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import {
|
|
94
|
+
InfisicalError,
|
|
95
|
+
InfisicalAuthError,
|
|
96
|
+
InfisicalConfigError,
|
|
97
|
+
InfisicalFetchError,
|
|
98
|
+
} from '@biocomputingup/infisical-secrets-loader';
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const secrets = await loadSecretsFromInfisical(config);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
if (err instanceof InfisicalAuthError) {
|
|
104
|
+
console.error('Authentication failed:', err.message);
|
|
105
|
+
} else if (err instanceof InfisicalConfigError) {
|
|
106
|
+
console.error('Invalid configuration:', err.details?.errors);
|
|
107
|
+
} else if (err instanceof InfisicalFetchError) {
|
|
108
|
+
console.error('Failed to fetch secrets:', err.cause);
|
|
109
|
+
} else if (err instanceof InfisicalError) {
|
|
110
|
+
console.error('Infisical error:', err.code, err.details);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Error Types
|
|
116
|
+
|
|
117
|
+
- **InfisicalAuthError**: Authentication with Infisical failed
|
|
118
|
+
- **InfisicalConfigError**: Invalid configuration passed to `loadSecretsFromInfisical`
|
|
119
|
+
- **InfisicalFetchError**: Failed to fetch secrets from Infisical
|
|
120
|
+
- **InfisicalParseError**: Failed to parse secrets response
|
|
121
|
+
|
|
122
|
+
All errors extend `InfisicalError` with properties:
|
|
123
|
+
- `code`: Unique error code (e.g., 'INFISICAL_AUTH_ERROR')
|
|
124
|
+
- `cause`: Original error if available
|
|
125
|
+
- `details`: Additional context (e.g., validation errors)
|
|
126
|
+
|
|
127
|
+
## Logging
|
|
128
|
+
|
|
129
|
+
Customize logging behavior by passing a logger and log level:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { LogLevel } from '@biocomputingup/infisical-secrets-loader';
|
|
133
|
+
|
|
134
|
+
const secrets = await loadSecretsFromInfisical({
|
|
135
|
+
// ... config
|
|
136
|
+
logLevel: LogLevel.info,
|
|
137
|
+
logger: console, // or your custom logger
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Log Levels:** `debug`, `info`, `warn`, `error`, `silent`
|
|
142
|
+
|
|
143
|
+
Custom loggers must implement:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
interface Logger {
|
|
147
|
+
debug: (message: string, meta?: Record<string, unknown>) => void;
|
|
148
|
+
info: (message: string, meta?: Record<string, unknown>) => void;
|
|
149
|
+
warn: (message: string, meta?: Record<string, unknown>) => void;
|
|
150
|
+
error: (message: string, meta?: Record<string, unknown>) => void;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## NestJS Integration
|
|
155
|
+
|
|
156
|
+
Use with NestJS ConfigModule:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { Module } from '@nestjs/common';
|
|
160
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
161
|
+
import { loadSecretsFromInfisical } from '@biocomputingup/infisical-secrets-loader';
|
|
162
|
+
|
|
163
|
+
@Module({
|
|
164
|
+
imports: [
|
|
165
|
+
ConfigModule.forRoot({
|
|
166
|
+
load: [
|
|
167
|
+
async () => {
|
|
168
|
+
return await loadSecretsFromInfisical({
|
|
169
|
+
baseUrl: process.env.INFISICAL_BASE_URL!,
|
|
170
|
+
environment: process.env.INFISICAL_ENVIRONMENT!,
|
|
171
|
+
projectId: process.env.INFISICAL_PROJECT_ID!,
|
|
172
|
+
auth: {
|
|
173
|
+
clientId: process.env.INFISICAL_CLIENT_ID!,
|
|
174
|
+
clientSecret: process.env.INFISICAL_CLIENT_SECRET!,
|
|
175
|
+
},
|
|
176
|
+
mode: 'nested',
|
|
177
|
+
});
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
}),
|
|
181
|
+
],
|
|
182
|
+
})
|
|
183
|
+
export class AppModule {}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Exported Types & Utilities
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Main function
|
|
190
|
+
export { loadSecretsFromInfisical };
|
|
191
|
+
|
|
192
|
+
// Error classes
|
|
193
|
+
export {
|
|
194
|
+
InfisicalError,
|
|
195
|
+
InfisicalAuthError,
|
|
196
|
+
InfisicalConfigError,
|
|
197
|
+
InfisicalFetchError,
|
|
198
|
+
InfisicalParseError,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Logger utilities
|
|
202
|
+
export { LogLevel, createLogger };
|
|
203
|
+
export type { Logger };
|
|
204
|
+
|
|
205
|
+
// Types
|
|
206
|
+
export type { LoadConfig };
|
|
207
|
+
export { TRetrievalMode };
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type InfisicalErrorOptions = {
|
|
2
|
+
code: string;
|
|
3
|
+
cause?: unknown;
|
|
4
|
+
details?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
export declare class InfisicalError extends Error {
|
|
7
|
+
readonly code: string;
|
|
8
|
+
readonly cause?: unknown;
|
|
9
|
+
readonly details?: Record<string, unknown>;
|
|
10
|
+
constructor(message: string, options: InfisicalErrorOptions);
|
|
11
|
+
}
|
|
12
|
+
export declare class InfisicalAuthError extends InfisicalError {
|
|
13
|
+
constructor(message: string, options?: Omit<InfisicalErrorOptions, "code">);
|
|
14
|
+
}
|
|
15
|
+
export declare class InfisicalConfigError extends InfisicalError {
|
|
16
|
+
constructor(message: string, options?: Omit<InfisicalErrorOptions, "code">);
|
|
17
|
+
}
|
|
18
|
+
export declare class InfisicalFetchError extends InfisicalError {
|
|
19
|
+
constructor(message: string, options?: Omit<InfisicalErrorOptions, "code">);
|
|
20
|
+
}
|
|
21
|
+
export declare class InfisicalParseError extends InfisicalError {
|
|
22
|
+
constructor(message: string, options?: Omit<InfisicalErrorOptions, "code">);
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InfisicalParseError = exports.InfisicalFetchError = exports.InfisicalConfigError = exports.InfisicalAuthError = exports.InfisicalError = void 0;
|
|
4
|
+
class InfisicalError extends Error {
|
|
5
|
+
constructor(message, options) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "InfisicalError";
|
|
8
|
+
this.code = options.code;
|
|
9
|
+
this.cause = options.cause;
|
|
10
|
+
this.details = options.details;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.InfisicalError = InfisicalError;
|
|
14
|
+
class InfisicalAuthError extends InfisicalError {
|
|
15
|
+
constructor(message, options = {}) {
|
|
16
|
+
super(message, { ...options, code: "INFISICAL_AUTH_ERROR" });
|
|
17
|
+
this.name = "InfisicalAuthError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.InfisicalAuthError = InfisicalAuthError;
|
|
21
|
+
class InfisicalConfigError extends InfisicalError {
|
|
22
|
+
constructor(message, options = {}) {
|
|
23
|
+
super(message, { ...options, code: "INFISICAL_CONFIG_ERROR" });
|
|
24
|
+
this.name = "InfisicalConfigError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.InfisicalConfigError = InfisicalConfigError;
|
|
28
|
+
class InfisicalFetchError extends InfisicalError {
|
|
29
|
+
constructor(message, options = {}) {
|
|
30
|
+
super(message, { ...options, code: "INFISICAL_FETCH_ERROR" });
|
|
31
|
+
this.name = "InfisicalFetchError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.InfisicalFetchError = InfisicalFetchError;
|
|
35
|
+
class InfisicalParseError extends InfisicalError {
|
|
36
|
+
constructor(message, options = {}) {
|
|
37
|
+
super(message, { ...options, code: "INFISICAL_PARSE_ERROR" });
|
|
38
|
+
this.name = "InfisicalParseError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.InfisicalParseError = InfisicalParseError;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TRetrievalMode } from "../types/types";
|
|
2
|
+
type SecretLike = {
|
|
3
|
+
secretKey: string;
|
|
4
|
+
secretValue: string;
|
|
5
|
+
secretPath?: string;
|
|
6
|
+
};
|
|
7
|
+
type ExtractKeyValuesOptions = {
|
|
8
|
+
mode?: TRetrievalMode;
|
|
9
|
+
};
|
|
10
|
+
export declare function mapSecretsToObject(secrets: SecretLike[], options?: ExtractKeyValuesOptions): Record<string, unknown>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mapSecretsToObject = mapSecretsToObject;
|
|
4
|
+
const types_1 = require("../types/types");
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
function mergeObjects(target, source) {
|
|
9
|
+
for (const [key, value] of Object.entries(source)) {
|
|
10
|
+
const existingValue = target[key];
|
|
11
|
+
if (isRecord(existingValue) && isRecord(value)) {
|
|
12
|
+
target[key] = mergeObjects(existingValue, value);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
target[key] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return target;
|
|
19
|
+
}
|
|
20
|
+
function normalizeSecretPath(secretPath) {
|
|
21
|
+
if (!secretPath) {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
const trimmed = secretPath.trim();
|
|
25
|
+
if (!trimmed) {
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
const cleaned = trimmed.replace(/^\/+|\/+$/g, "");
|
|
29
|
+
if (!cleaned) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return cleaned.split("/").filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
function mapSecretsToObject(secrets, options = {}) {
|
|
35
|
+
const result = {};
|
|
36
|
+
for (const secret of secrets) {
|
|
37
|
+
const { secretKey, secretValue } = secret;
|
|
38
|
+
if (options.mode !== types_1.TRetrievalMode.nested) {
|
|
39
|
+
result[secretKey] = secretValue;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const pathSegments = normalizeSecretPath(secret.secretPath ?? null);
|
|
43
|
+
if (pathSegments.length === 0) {
|
|
44
|
+
result[secretKey] = secretValue;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
let nestedObject = {
|
|
48
|
+
[secretKey]: secretValue,
|
|
49
|
+
};
|
|
50
|
+
for (let index = pathSegments.length - 1; index >= 0; index -= 1) {
|
|
51
|
+
nestedObject = {
|
|
52
|
+
[pathSegments[index]]: nestedObject,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
mergeObjects(result, nestedObject);
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateInput = validateInput;
|
|
4
|
+
const error_1 = require("../errors/error");
|
|
5
|
+
const loadConfig_schema_1 = require("../validators/loadConfig.schema");
|
|
6
|
+
function validateInput(config) {
|
|
7
|
+
const result = loadConfig_schema_1.LoadConfigSchema.safeParse(config);
|
|
8
|
+
if (!result.success) {
|
|
9
|
+
const errorMessages = result.error.issues
|
|
10
|
+
.map((err) => `${err.path.join(".")} : ${err.message}`)
|
|
11
|
+
.join("; ");
|
|
12
|
+
throw new error_1.InfisicalConfigError(`Invalid configuration: ${errorMessages}`, {
|
|
13
|
+
details: {
|
|
14
|
+
errors: errorMessages,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { InfisicalError, InfisicalAuthError, InfisicalConfigError, InfisicalFetchError, InfisicalParseError, } from "./errors/error";
|
|
2
|
+
export { LogLevel, createLogger } from "./logger/logger";
|
|
3
|
+
export type { Logger } from "./logger/logger";
|
|
4
|
+
export { TRetrievalMode } from "./types/types";
|
|
5
|
+
export { loadSecretsFromInfisical } from "./main";
|
|
6
|
+
export type { LoadConfig } from "./validators/loadConfig.schema";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadSecretsFromInfisical = exports.TRetrievalMode = exports.createLogger = exports.LogLevel = exports.InfisicalParseError = exports.InfisicalFetchError = exports.InfisicalConfigError = exports.InfisicalAuthError = exports.InfisicalError = void 0;
|
|
4
|
+
var error_1 = require("./errors/error");
|
|
5
|
+
Object.defineProperty(exports, "InfisicalError", { enumerable: true, get: function () { return error_1.InfisicalError; } });
|
|
6
|
+
Object.defineProperty(exports, "InfisicalAuthError", { enumerable: true, get: function () { return error_1.InfisicalAuthError; } });
|
|
7
|
+
Object.defineProperty(exports, "InfisicalConfigError", { enumerable: true, get: function () { return error_1.InfisicalConfigError; } });
|
|
8
|
+
Object.defineProperty(exports, "InfisicalFetchError", { enumerable: true, get: function () { return error_1.InfisicalFetchError; } });
|
|
9
|
+
Object.defineProperty(exports, "InfisicalParseError", { enumerable: true, get: function () { return error_1.InfisicalParseError; } });
|
|
10
|
+
var logger_1 = require("./logger/logger");
|
|
11
|
+
Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return logger_1.LogLevel; } });
|
|
12
|
+
Object.defineProperty(exports, "createLogger", { enumerable: true, get: function () { return logger_1.createLogger; } });
|
|
13
|
+
var types_1 = require("./types/types");
|
|
14
|
+
Object.defineProperty(exports, "TRetrievalMode", { enumerable: true, get: function () { return types_1.TRetrievalMode; } });
|
|
15
|
+
var main_1 = require("./main");
|
|
16
|
+
Object.defineProperty(exports, "loadSecretsFromInfisical", { enumerable: true, get: function () { return main_1.loadSecretsFromInfisical; } });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const LogLevel: {
|
|
2
|
+
readonly silent: "silent";
|
|
3
|
+
readonly error: "error";
|
|
4
|
+
readonly warn: "warn";
|
|
5
|
+
readonly info: "info";
|
|
6
|
+
readonly debug: "debug";
|
|
7
|
+
};
|
|
8
|
+
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];
|
|
9
|
+
export type Logger = {
|
|
10
|
+
debug: (message: string, meta?: Record<string, unknown>) => void;
|
|
11
|
+
info: (message: string, meta?: Record<string, unknown>) => void;
|
|
12
|
+
warn: (message: string, meta?: Record<string, unknown>) => void;
|
|
13
|
+
error: (message: string, meta?: Record<string, unknown>) => void;
|
|
14
|
+
};
|
|
15
|
+
export declare function createLogger(level?: LogLevel, baseLogger?: Logger): Logger;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LogLevel = void 0;
|
|
4
|
+
exports.createLogger = createLogger;
|
|
5
|
+
exports.LogLevel = {
|
|
6
|
+
silent: "silent",
|
|
7
|
+
error: "error",
|
|
8
|
+
warn: "warn",
|
|
9
|
+
info: "info",
|
|
10
|
+
debug: "debug",
|
|
11
|
+
};
|
|
12
|
+
const logLevelPriority = {
|
|
13
|
+
silent: 0,
|
|
14
|
+
error: 1,
|
|
15
|
+
warn: 2,
|
|
16
|
+
info: 3,
|
|
17
|
+
debug: 4,
|
|
18
|
+
};
|
|
19
|
+
function shouldLog(current, target) {
|
|
20
|
+
return logLevelPriority[target] <= logLevelPriority[current];
|
|
21
|
+
}
|
|
22
|
+
function createLogger(level = exports.LogLevel.info, baseLogger = console) {
|
|
23
|
+
const prefix = "[infisical-secrets-loader]";
|
|
24
|
+
const logWithMeta = (logFn, message, meta) => {
|
|
25
|
+
if (meta === undefined) {
|
|
26
|
+
logFn(message);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
logFn(message, meta);
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
debug: (message, meta) => {
|
|
33
|
+
if (shouldLog(level, exports.LogLevel.debug)) {
|
|
34
|
+
logWithMeta(baseLogger.debug, `${prefix}[DEBUG] ${message}`, meta);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
info: (message, meta) => {
|
|
38
|
+
if (shouldLog(level, exports.LogLevel.info)) {
|
|
39
|
+
logWithMeta(baseLogger.info, `${prefix}[INFO] ${message}`, meta);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
warn: (message, meta) => {
|
|
43
|
+
if (shouldLog(level, exports.LogLevel.warn)) {
|
|
44
|
+
logWithMeta(baseLogger.warn, `${prefix}[WARN] ${message}`, meta);
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
error: (message, meta) => {
|
|
48
|
+
if (shouldLog(level, exports.LogLevel.error)) {
|
|
49
|
+
logWithMeta(baseLogger.error, `${prefix}[ERROR] ${message}`, meta);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
package/dist/main.d.ts
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadSecretsFromInfisical = loadSecretsFromInfisical;
|
|
4
|
+
const sdk_1 = require("@infisical/sdk");
|
|
5
|
+
const mapSecretsToObjects_1 = require("./helpers/mapSecretsToObjects");
|
|
6
|
+
const error_1 = require("./errors/error");
|
|
7
|
+
const logger_1 = require("./logger/logger");
|
|
8
|
+
const types_1 = require("./types/types");
|
|
9
|
+
const validate_1 = require("./helpers/validate");
|
|
10
|
+
async function loadSecretsFromInfisical(config, skipInputValidation = false) {
|
|
11
|
+
const { baseUrl, environment, auth, projectId, mode = types_1.TRetrievalMode.rootOnly, logger, logLevel = logger_1.LogLevel.debug, } = config;
|
|
12
|
+
const activeLogger = (0, logger_1.createLogger)(logLevel, logger ?? console);
|
|
13
|
+
if (!skipInputValidation) {
|
|
14
|
+
activeLogger.debug("Validating input configuration");
|
|
15
|
+
(0, validate_1.validateInput)(config);
|
|
16
|
+
activeLogger.debug("Input configuration is valid");
|
|
17
|
+
}
|
|
18
|
+
const client = new sdk_1.InfisicalSDK({
|
|
19
|
+
siteUrl: baseUrl,
|
|
20
|
+
});
|
|
21
|
+
try {
|
|
22
|
+
activeLogger.info("Authenticating with Infisical");
|
|
23
|
+
await client.auth().universalAuth.login({
|
|
24
|
+
clientId: auth.clientId,
|
|
25
|
+
clientSecret: auth.clientSecret,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
activeLogger.error("Error authenticating with Infisical", {
|
|
30
|
+
error: err,
|
|
31
|
+
});
|
|
32
|
+
throw new error_1.InfisicalAuthError("Authentication failed", {
|
|
33
|
+
cause: err,
|
|
34
|
+
details: { baseUrl },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
activeLogger.info(`Successfully authenticated with Infisical instance at ${baseUrl}`);
|
|
38
|
+
try {
|
|
39
|
+
activeLogger.info("Fetching secrets", {
|
|
40
|
+
environment,
|
|
41
|
+
projectId: projectId,
|
|
42
|
+
});
|
|
43
|
+
const loadRecursive = mode !== types_1.TRetrievalMode.rootOnly;
|
|
44
|
+
const allSecrets = await client.secrets().listSecrets({
|
|
45
|
+
environment,
|
|
46
|
+
projectId: projectId,
|
|
47
|
+
expandSecretReferences: false,
|
|
48
|
+
viewSecretValue: true,
|
|
49
|
+
includeImports: false,
|
|
50
|
+
recursive: loadRecursive,
|
|
51
|
+
});
|
|
52
|
+
return (0, mapSecretsToObjects_1.mapSecretsToObject)(allSecrets.secrets, { mode });
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
activeLogger.error("Error fetching secrets", { error: err });
|
|
56
|
+
throw new error_1.InfisicalFetchError("Failed to fetch secrets", {
|
|
57
|
+
cause: err,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TRetrievalMode = void 0;
|
|
4
|
+
exports.TRetrievalMode = {
|
|
5
|
+
// return secrets only in the root directory, ignore all secrets in subfolders
|
|
6
|
+
rootOnly: "rootOnly",
|
|
7
|
+
// ignore file structure and return secrets in a flat key-value pair format
|
|
8
|
+
flat: "flat",
|
|
9
|
+
// return secrets in a nested format that reflects the folder structure in Infisical
|
|
10
|
+
nested: "nested",
|
|
11
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import * as zod from "zod";
|
|
2
|
+
import { Logger } from "../logger/logger";
|
|
3
|
+
export declare const LogLevels: readonly ["debug", "info", "warn", "error"];
|
|
4
|
+
export declare const LoadConfigSchema: zod.ZodObject<{
|
|
5
|
+
baseUrl: zod.ZodURL;
|
|
6
|
+
environment: zod.ZodString;
|
|
7
|
+
auth: zod.ZodObject<{
|
|
8
|
+
clientId: zod.ZodString;
|
|
9
|
+
clientSecret: zod.ZodString;
|
|
10
|
+
}, zod.z.core.$strip>;
|
|
11
|
+
projectId: zod.ZodUUID;
|
|
12
|
+
mode: zod.ZodOptional<zod.ZodEnum<{
|
|
13
|
+
readonly rootOnly: "rootOnly";
|
|
14
|
+
readonly flat: "flat";
|
|
15
|
+
readonly nested: "nested";
|
|
16
|
+
}>>;
|
|
17
|
+
logger: zod.ZodOptional<zod.ZodCustom<Logger, Logger>>;
|
|
18
|
+
logLevel: zod.ZodOptional<zod.ZodEnum<{
|
|
19
|
+
error: "error";
|
|
20
|
+
warn: "warn";
|
|
21
|
+
info: "info";
|
|
22
|
+
debug: "debug";
|
|
23
|
+
}>>;
|
|
24
|
+
}, zod.z.core.$strip>;
|
|
25
|
+
export type LoadConfig = zod.infer<typeof LoadConfigSchema>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LoadConfigSchema = exports.LogLevels = void 0;
|
|
37
|
+
const zod = __importStar(require("zod"));
|
|
38
|
+
const types_1 = require("../types/types");
|
|
39
|
+
exports.LogLevels = ["debug", "info", "warn", "error"];
|
|
40
|
+
exports.LoadConfigSchema = zod.object({
|
|
41
|
+
baseUrl: zod.url(),
|
|
42
|
+
environment: zod.string().trim().min(1),
|
|
43
|
+
auth: zod.object({
|
|
44
|
+
clientId: zod.string().trim().min(1),
|
|
45
|
+
clientSecret: zod.string().trim().min(1),
|
|
46
|
+
}),
|
|
47
|
+
projectId: zod.uuid(),
|
|
48
|
+
mode: zod.enum(types_1.TRetrievalMode).optional(),
|
|
49
|
+
logger: zod.custom().optional(),
|
|
50
|
+
logLevel: zod.enum(exports.LogLevels).optional(),
|
|
51
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@biocomputingup/infisical-secrets-loader",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A small library to load secrets from infisical into a JSON shaped object to be injected into your config service. Designed to work with NestJS config module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -p tsconfig.build.json",
|
|
12
|
+
"watch": "tsc --watch",
|
|
13
|
+
"prepublishOnly": "npm run build",
|
|
14
|
+
"test": "jest",
|
|
15
|
+
"test:file": "jest --runTestsByPath"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/Mehdi-SK/infisical-secrets-loader.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"infisical",
|
|
23
|
+
"nestjs",
|
|
24
|
+
"secrets management"
|
|
25
|
+
],
|
|
26
|
+
"author": "Mehdi Zoubiri",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/Mehdi-SK/infisical-secrets-loader/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/Mehdi-SK/infisical-secrets-loader#readme",
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/jest": "^30.0.0",
|
|
34
|
+
"@types/node": "^25.6.0",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^8.59.0",
|
|
36
|
+
"@typescript-eslint/parser": "^8.59.0",
|
|
37
|
+
"dotenv": "^17.4.2",
|
|
38
|
+
"eslint": "^10.2.1",
|
|
39
|
+
"jest": "^30.4.2",
|
|
40
|
+
"ts-jest": "^29.4.9",
|
|
41
|
+
"typescript": "^6.0.3"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@infisical/sdk": "^5.0.1",
|
|
48
|
+
"zod": "^4.4.3"
|
|
49
|
+
}
|
|
50
|
+
}
|