nestjs-infisical 1.0.13 → 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/dist/index.d.ts +2 -2
- package/dist/index.js +3 -16
- package/dist/infisical.http.d.ts +7 -0
- package/dist/infisical.http.js +36 -0
- package/dist/infisical.logger.d.ts +1 -0
- package/dist/infisical.logger.js +9 -0
- package/dist/infisical.types.d.ts +9 -5
- package/dist/load-infisical.d.ts +2 -0
- package/dist/load-infisical.js +65 -0
- package/package.json +11 -4
- package/readme.md +127 -138
- package/dist/infisical.loader.d.ts +0 -9
- package/dist/infisical.loader.js +0 -63
- package/dist/infisical.module.d.ts +0 -6
- package/dist/infisical.module.js +0 -99
- package/dist/infisical.service.d.ts +0 -2
- package/dist/infisical.service.js +0 -45
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export { loadInfisical } from './load-infisical';
|
|
2
|
+
export type { InfisicalOptions } from './infisical.types';
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,5 @@
|
|
|
1
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
exports.loadInfisical = void 0;
|
|
4
|
+
var load_infisical_1 = require("./load-infisical");
|
|
5
|
+
Object.defineProperty(exports, "loadInfisical", { enumerable: true, get: function () { return load_infisical_1.loadInfisical; } });
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchInfisicalSecrets = fetchInfisicalSecrets;
|
|
4
|
+
const infisical_logger_1 = require("./infisical.logger");
|
|
5
|
+
async function fetchInfisicalSecrets(options) {
|
|
6
|
+
const controller = new AbortController();
|
|
7
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
8
|
+
try {
|
|
9
|
+
(0, infisical_logger_1.debugLog)(options.debug, 'Calling Infisical HTTP API');
|
|
10
|
+
const url = new URL(`${options.baseUrl}/api/v3/secrets/raw`);
|
|
11
|
+
url.searchParams.set('projectId', options.projectId);
|
|
12
|
+
url.searchParams.set('environment', options.environment);
|
|
13
|
+
const response = await fetch(url.toString(), {
|
|
14
|
+
method: 'GET',
|
|
15
|
+
headers: {
|
|
16
|
+
Authorization: `Bearer ${options.token}`,
|
|
17
|
+
Accept: 'application/json',
|
|
18
|
+
},
|
|
19
|
+
signal: controller.signal,
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
const text = await response.text();
|
|
23
|
+
throw new Error(`Infisical API ${response.status}: ${text}`);
|
|
24
|
+
}
|
|
25
|
+
const json = (await response.json());
|
|
26
|
+
const secrets = {};
|
|
27
|
+
json.secrets.map((item) => {
|
|
28
|
+
secrets[item.secretKey] = item.secretValue;
|
|
29
|
+
return item;
|
|
30
|
+
});
|
|
31
|
+
return secrets;
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function debugLog(enabled: boolean | undefined, message: string): void;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { DotenvConfigOptions } from 'dotenv';
|
|
2
|
-
|
|
3
|
-
export interface InfisicalModuleOptions {
|
|
2
|
+
export interface InfisicalOptions {
|
|
4
3
|
baseUrl?: string;
|
|
5
4
|
token?: string;
|
|
6
5
|
projectId?: string;
|
|
@@ -10,7 +9,12 @@ export interface InfisicalModuleOptions {
|
|
|
10
9
|
failFast?: boolean;
|
|
11
10
|
debug?: boolean;
|
|
12
11
|
}
|
|
13
|
-
export interface
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export interface InfisicalSecretItem {
|
|
13
|
+
id: string;
|
|
14
|
+
environment: string;
|
|
15
|
+
type: string;
|
|
16
|
+
secretKey: string;
|
|
17
|
+
secretValue: string;
|
|
18
|
+
createdAt: string;
|
|
19
|
+
updatedAt: string;
|
|
16
20
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadInfisical = loadInfisical;
|
|
7
|
+
const dotenv_1 = __importDefault(require("dotenv"));
|
|
8
|
+
const infisical_http_1 = require("./infisical.http");
|
|
9
|
+
const infisical_logger_1 = require("./infisical.logger");
|
|
10
|
+
const LOG_PREFIX = '[nestjs-infisical]';
|
|
11
|
+
async function loadInfisical(options = {}) {
|
|
12
|
+
const { dotenv: dotenvOptions, override = true, failFast = true, debug = false, } = options;
|
|
13
|
+
// 1️⃣ Load dotenv FIRST
|
|
14
|
+
if (dotenvOptions !== false) {
|
|
15
|
+
(0, infisical_logger_1.debugLog)(debug, 'Loading dotenv configuration');
|
|
16
|
+
dotenv_1.default.config(dotenvOptions);
|
|
17
|
+
}
|
|
18
|
+
// 2️⃣ Resolve config (options → env)
|
|
19
|
+
const resolved = {
|
|
20
|
+
baseUrl: options.baseUrl ??
|
|
21
|
+
process.env.INFISICAL_BASE_URL ??
|
|
22
|
+
'https://app.infisical.com',
|
|
23
|
+
token: options.token ?? process.env.INFISICAL_TOKEN,
|
|
24
|
+
projectId: options.projectId ?? process.env.INFISICAL_PROJECT_ID,
|
|
25
|
+
environment: options.environment ?? process.env.INFISICAL_ENVIRONMENT,
|
|
26
|
+
};
|
|
27
|
+
const providedCount = Object.values(resolved).filter(Boolean).length;
|
|
28
|
+
(0, infisical_logger_1.debugLog)(debug, `Infisical config resolved: ${providedCount === 0
|
|
29
|
+
? 'none'
|
|
30
|
+
: providedCount === 4
|
|
31
|
+
? 'complete'
|
|
32
|
+
: 'partial'}`);
|
|
33
|
+
// 3️⃣ No config → silently skip
|
|
34
|
+
if (providedCount === 0) {
|
|
35
|
+
(0, infisical_logger_1.debugLog)(debug, 'No Infisical configuration provided. Skipping.');
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// 4️⃣ Partial config → warn & skip
|
|
39
|
+
if (providedCount !== 4) {
|
|
40
|
+
console.warn(`${LOG_PREFIX} Partial Infisical configuration detected. Secrets will not be loaded.`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
(0, infisical_logger_1.debugLog)(debug, 'Fetching Infisical secrets');
|
|
45
|
+
const secrets = await (0, infisical_http_1.fetchInfisicalSecrets)({
|
|
46
|
+
baseUrl: resolved.baseUrl,
|
|
47
|
+
token: resolved.token.trim(),
|
|
48
|
+
projectId: resolved.projectId,
|
|
49
|
+
environment: resolved.environment,
|
|
50
|
+
debug,
|
|
51
|
+
});
|
|
52
|
+
for (const [key, value] of Object.entries(secrets)) {
|
|
53
|
+
if (override || process.env[key] === undefined) {
|
|
54
|
+
process.env[key] = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
(0, infisical_logger_1.debugLog)(debug, `Loaded ${Object.keys(secrets).length} secrets from Infisical`);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
if (failFast) {
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
console.warn(`${LOG_PREFIX} Failed to load Infisical secrets. Continuing without them.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestjs-infisical",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "CLI-free Infisical HTTP integration for NestJS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"prepublishOnly": "npm run build"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"
|
|
17
|
-
"dotenv": "^16.4.0",
|
|
16
|
+
"dotenv": "^16.4.5",
|
|
18
17
|
"typescript": "^5.9.3"
|
|
19
18
|
},
|
|
20
19
|
"peerDependencies": {
|
|
@@ -33,5 +32,13 @@
|
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"@types/node": "^25.0.3"
|
|
36
|
-
}
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"nestjs",
|
|
38
|
+
"infisical",
|
|
39
|
+
"secrets",
|
|
40
|
+
"configuration",
|
|
41
|
+
"dotenv",
|
|
42
|
+
"http"
|
|
43
|
+
]
|
|
37
44
|
}
|
package/readme.md
CHANGED
|
@@ -1,216 +1,205 @@
|
|
|
1
1
|
# nestjs-infisical
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
CLI-free Infisical secrets loader for NestJS applications.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
This package loads secrets from Infisical **before** your NestJS application boots,
|
|
6
|
+
injects them into `process.env`, and then hands control back to NestJS.
|
|
7
|
+
It is intentionally boring, deterministic, and production-safe.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## Why
|
|
11
|
+
## Why this package exists
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Earlier versions attempted to integrate Infisical directly inside NestJS modules.
|
|
14
|
+
While technically possible, this approach is **not deterministic** because:
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
- NestJS does not guarantee global ordering of module side-effects
|
|
17
|
+
- Async work inside modules can interleave with bootstrap
|
|
18
|
+
- Debugging startup order becomes extremely difficult
|
|
16
19
|
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
- No runtime mutation
|
|
20
|
-
- Deterministic startup behavior
|
|
21
|
-
- Works in Docker, CI, and production
|
|
22
|
-
- Pure HTTP API usage
|
|
20
|
+
Secrets loading is **global, side-effectful, and order-sensitive**.
|
|
21
|
+
The correct place for it is **before NestJS starts**.
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
This package now follows the same pattern used by:
|
|
24
|
+
- AWS Parameter Store loaders
|
|
25
|
+
- Vault integrations
|
|
26
|
+
- Remote config systems
|
|
27
|
+
- Feature flag bootstrappers
|
|
25
28
|
|
|
26
29
|
---
|
|
27
30
|
|
|
28
|
-
##
|
|
31
|
+
## Architecture (Current)
|
|
32
|
+
|
|
33
|
+
Startup flow:
|
|
34
|
+
|
|
35
|
+
1. Optional dotenv load
|
|
36
|
+
2. Infisical HTTP API call
|
|
37
|
+
3. Inject secrets into `process.env`
|
|
38
|
+
4. NestJS application bootstrap
|
|
39
|
+
5. `@nestjs/config` reads from `process.env`
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- Allows `@nestjs/config` to consume them normally
|
|
34
|
-
- Runs **only during application bootstrap**
|
|
41
|
+
Diagram:
|
|
42
|
+
|
|
43
|
+
dotenv → Infisical HTTP API → process.env → NestJS → ConfigModule
|
|
35
44
|
|
|
36
45
|
---
|
|
37
46
|
|
|
38
|
-
##
|
|
47
|
+
## What changed from v1 to v2
|
|
39
48
|
|
|
40
|
-
|
|
49
|
+
### Old approach (v1 – deprecated)
|
|
41
50
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
↓
|
|
47
|
-
process.env
|
|
48
|
-
↓
|
|
49
|
-
@nestjs/config
|
|
50
|
-
```
|
|
51
|
+
- Dynamic NestJS module
|
|
52
|
+
- Async logic inside providers
|
|
53
|
+
- Relied on NestJS lifecycle ordering
|
|
54
|
+
- Hard to debug, non-deterministic
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
### New approach (v2 – current)
|
|
57
|
+
|
|
58
|
+
- Explicit async loader
|
|
59
|
+
- Called from `main.ts`
|
|
60
|
+
- Fully deterministic
|
|
61
|
+
- No NestJS lifecycle coupling
|
|
62
|
+
- Easier to reason about and debug
|
|
53
63
|
|
|
54
64
|
---
|
|
55
65
|
|
|
56
66
|
## Installation
|
|
57
67
|
|
|
58
|
-
```
|
|
68
|
+
```
|
|
59
69
|
npm install nestjs-infisical
|
|
60
70
|
```
|
|
61
71
|
|
|
62
|
-
Node.js
|
|
72
|
+
Node.js version requirement:
|
|
73
|
+
|
|
74
|
+
- Node 18 or newer
|
|
63
75
|
|
|
64
76
|
---
|
|
65
77
|
|
|
66
|
-
## Basic (
|
|
78
|
+
## Basic usage (recommended)
|
|
79
|
+
|
|
80
|
+
### main.ts
|
|
67
81
|
|
|
68
82
|
```ts
|
|
69
|
-
import {
|
|
70
|
-
import {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
import { loadInfisical } from 'nestjs-infisical';
|
|
84
|
+
import { NestFactory } from '@nestjs/core';
|
|
85
|
+
import { AppModule } from './app.module';
|
|
86
|
+
|
|
87
|
+
async function bootstrap() {
|
|
88
|
+
await loadInfisical({
|
|
89
|
+
debug: true
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const app = await NestFactory.create(AppModule);
|
|
93
|
+
await app.listen(3000);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
bootstrap();
|
|
83
97
|
```
|
|
84
98
|
|
|
85
|
-
|
|
99
|
+
That is all you need.
|
|
86
100
|
|
|
87
101
|
---
|
|
88
102
|
|
|
89
|
-
##
|
|
103
|
+
## Configuration resolution order
|
|
90
104
|
|
|
91
|
-
|
|
92
|
-
import { Module } from '@nestjs/common';
|
|
93
|
-
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
94
|
-
import { InfisicalModule } from 'nestjs-infisical';
|
|
95
|
-
|
|
96
|
-
@Module({
|
|
97
|
-
imports: [
|
|
98
|
-
ConfigModule.forRoot(),
|
|
99
|
-
InfisicalModule.forRootAsync({
|
|
100
|
-
imports: [ConfigModule],
|
|
101
|
-
inject: [ConfigService],
|
|
102
|
-
useFactory: (config: ConfigService) => ({
|
|
103
|
-
baseUrl: config.get('INFISICAL_BASE_URL'),
|
|
104
|
-
token: config.get('INFISICAL_TOKEN'),
|
|
105
|
-
projectId: config.get('INFISICAL_PROJECT_ID'),
|
|
106
|
-
environment: config.get('INFISICAL_ENVIRONMENT'),
|
|
107
|
-
}),
|
|
108
|
-
}),
|
|
109
|
-
],
|
|
110
|
-
})
|
|
111
|
-
export class AppModule {}
|
|
112
|
-
```
|
|
105
|
+
Configuration is resolved in this order:
|
|
113
106
|
|
|
114
|
-
|
|
107
|
+
1. dotenv (unless disabled)
|
|
108
|
+
2. Explicit options passed to `loadInfisical`
|
|
109
|
+
3. Environment variables (`process.env`)
|
|
115
110
|
|
|
116
|
-
|
|
111
|
+
Required environment variables:
|
|
117
112
|
|
|
118
|
-
|
|
113
|
+
- `INFISICAL_BASE_URL` (optional, defaults to Infisical Cloud)
|
|
114
|
+
- `INFISICAL_TOKEN`
|
|
115
|
+
- `INFISICAL_PROJECT_ID`
|
|
116
|
+
- `INFISICAL_ENVIRONMENT`
|
|
119
117
|
|
|
120
|
-
|
|
118
|
+
---
|
|
121
119
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
## Zero-config usage
|
|
121
|
+
|
|
122
|
+
If all configuration is present in `.env`:
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
INFISICAL_TOKEN=xxx
|
|
126
|
+
INFISICAL_PROJECT_ID=yyy
|
|
127
|
+
INFISICAL_ENVIRONMENT=dev
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
You can simply call:
|
|
131
131
|
|
|
132
132
|
```ts
|
|
133
|
-
|
|
134
|
-
dotenv: false,
|
|
135
|
-
});
|
|
133
|
+
await loadInfisical();
|
|
136
134
|
```
|
|
137
135
|
|
|
138
136
|
---
|
|
139
137
|
|
|
140
|
-
##
|
|
138
|
+
## Mixed configuration example
|
|
141
139
|
|
|
142
140
|
```ts
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
environment?: string;
|
|
148
|
-
dotenv?: DotenvConfigOptions | false;
|
|
149
|
-
override?: boolean; // default: true
|
|
150
|
-
failFast?: boolean; // default: true
|
|
151
|
-
}
|
|
141
|
+
await loadInfisical({
|
|
142
|
+
environment: 'production',
|
|
143
|
+
debug: true
|
|
144
|
+
});
|
|
152
145
|
```
|
|
153
146
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
- `true` → Infisical secrets overwrite existing environment variables
|
|
157
|
-
- `false` → Existing environment variables are preserved
|
|
158
|
-
|
|
159
|
-
### failFast
|
|
160
|
-
|
|
161
|
-
- `true` → Application startup fails if Infisical API fails
|
|
162
|
-
- `false` → Logs a warning and continues startup
|
|
147
|
+
Missing values are automatically read from `process.env`.
|
|
163
148
|
|
|
164
149
|
---
|
|
165
150
|
|
|
166
|
-
##
|
|
151
|
+
## Disable dotenv (Docker / Kubernetes)
|
|
167
152
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
153
|
+
```ts
|
|
154
|
+
await loadInfisical({
|
|
155
|
+
dotenv: false
|
|
156
|
+
});
|
|
157
|
+
```
|
|
171
158
|
|
|
172
|
-
|
|
159
|
+
---
|
|
173
160
|
|
|
174
|
-
|
|
161
|
+
## Failure behavior
|
|
175
162
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
163
|
+
- `failFast: true` (default)
|
|
164
|
+
- Application crashes if Infisical fails
|
|
165
|
+
- `failFast: false`
|
|
166
|
+
- Logs warning and continues without secrets
|
|
179
167
|
|
|
180
|
-
|
|
168
|
+
---
|
|
181
169
|
|
|
182
|
-
|
|
170
|
+
## What this package does NOT do
|
|
183
171
|
|
|
184
|
-
-
|
|
185
|
-
-
|
|
172
|
+
- No Infisical CLI usage
|
|
173
|
+
- No secret rotation
|
|
174
|
+
- No background polling
|
|
175
|
+
- No caching
|
|
176
|
+
- No retries
|
|
177
|
+
- No NestJS module mutation
|
|
178
|
+
- No decorators
|
|
179
|
+
- No health checks
|
|
186
180
|
|
|
187
181
|
---
|
|
188
182
|
|
|
189
|
-
##
|
|
183
|
+
## Compatibility with @nestjs/config
|
|
190
184
|
|
|
191
|
-
|
|
185
|
+
Because secrets are loaded **before** NestJS starts,
|
|
186
|
+
`@nestjs/config` works automatically without any integration.
|
|
192
187
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
- Add retries or backoff
|
|
199
|
-
- Add decorators
|
|
200
|
-
- Mutate NestJS internals
|
|
201
|
-
- Run after bootstrap
|
|
188
|
+
```ts
|
|
189
|
+
ConfigModule.forRoot({
|
|
190
|
+
isGlobal: true
|
|
191
|
+
});
|
|
192
|
+
```
|
|
202
193
|
|
|
203
|
-
|
|
194
|
+
All secrets are already present in `process.env`.
|
|
204
195
|
|
|
205
|
-
|
|
196
|
+
---
|
|
206
197
|
|
|
207
|
-
|
|
208
|
-
- Deterministic
|
|
209
|
-
- Boring
|
|
210
|
-
- Predictable
|
|
211
|
-
- `process.env` is the only contract
|
|
198
|
+
## Versioning note
|
|
212
199
|
|
|
213
|
-
|
|
200
|
+
This architecture change is released as **v2.x**.
|
|
201
|
+
If you were using the old module-based integration,
|
|
202
|
+
migrate by moving Infisical loading to `main.ts`.
|
|
214
203
|
|
|
215
204
|
---
|
|
216
205
|
|
package/dist/infisical.loader.js
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadInfisicalSecrets = loadInfisicalSecrets;
|
|
4
|
-
const LOG_PREFIX = '[nestjs-infisical]';
|
|
5
|
-
function debugLog(enabled, message) {
|
|
6
|
-
if (enabled) {
|
|
7
|
-
console.log(`${LOG_PREFIX} ${message}`);
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
async function loadInfisicalSecrets(options) {
|
|
11
|
-
const controller = new AbortController();
|
|
12
|
-
const timeout = setTimeout(() => controller.abort(), 5000); // 5s timeout
|
|
13
|
-
try {
|
|
14
|
-
debugLog(options.debug, 'Fetching secrets from Infisical');
|
|
15
|
-
debugLog(options.debug, `baseUrl=${options.baseUrl}, projectId=${options.projectId}, environment=${options.environment}`);
|
|
16
|
-
const url = new URL(`${options.baseUrl}/api/v3/secrets/raw`);
|
|
17
|
-
url.searchParams.set('projectId', options.projectId);
|
|
18
|
-
url.searchParams.set('environment', options.environment);
|
|
19
|
-
const response = await fetch(url.toString(), {
|
|
20
|
-
method: 'GET',
|
|
21
|
-
headers: {
|
|
22
|
-
Authorization: `Bearer ${options.token}`,
|
|
23
|
-
Accept: 'application/json',
|
|
24
|
-
},
|
|
25
|
-
signal: controller.signal,
|
|
26
|
-
});
|
|
27
|
-
if (!response.ok) {
|
|
28
|
-
const text = await response.text();
|
|
29
|
-
throw new Error(`Infisical API error ${response.status}: ${text}`);
|
|
30
|
-
}
|
|
31
|
-
const body = await response.json();
|
|
32
|
-
const secrets = body?.secrets ?? {};
|
|
33
|
-
debugLog(options.debug, `Fetched ${Object.keys(secrets).length} secrets from Infisical`);
|
|
34
|
-
debugLog(options.debug, `Secret keys fetched: ${Object.keys(secrets).join(', ')}`);
|
|
35
|
-
for (const [key, value] of Object.entries(secrets)) {
|
|
36
|
-
const exists = process.env[key] !== undefined;
|
|
37
|
-
if (options.override || !exists) {
|
|
38
|
-
debugLog(options.debug, exists
|
|
39
|
-
? `Overwriting env var: ${key}`
|
|
40
|
-
: `Setting env var: ${key}`);
|
|
41
|
-
process.env[key] = value;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
debugLog(options.debug, `Skipping existing env var: ${key}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
catch (err) {
|
|
49
|
-
console.error('Error while loading Infisical secrets', err);
|
|
50
|
-
if (err.name === 'AbortError') {
|
|
51
|
-
console.error(`${LOG_PREFIX} Infisical request timed out after 5s`);
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
console.error(`${LOG_PREFIX} Error loading secrets from Infisical`, err);
|
|
55
|
-
}
|
|
56
|
-
if (options.failFast) {
|
|
57
|
-
throw err;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
finally {
|
|
61
|
-
clearTimeout(timeout);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { DynamicModule } from '@nestjs/common';
|
|
2
|
-
import { InfisicalModuleAsyncOptions, InfisicalModuleOptions } from './infisical.types';
|
|
3
|
-
export declare class InfisicalModule {
|
|
4
|
-
static forRoot(options?: InfisicalModuleOptions): DynamicModule;
|
|
5
|
-
static forRootAsync(options: InfisicalModuleAsyncOptions): DynamicModule;
|
|
6
|
-
}
|
package/dist/infisical.module.js
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
|
-
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
|
-
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
5
|
-
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
6
|
-
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
7
|
-
var _, done = false;
|
|
8
|
-
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
9
|
-
var context = {};
|
|
10
|
-
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
11
|
-
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
12
|
-
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
13
|
-
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
14
|
-
if (kind === "accessor") {
|
|
15
|
-
if (result === void 0) continue;
|
|
16
|
-
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
17
|
-
if (_ = accept(result.get)) descriptor.get = _;
|
|
18
|
-
if (_ = accept(result.set)) descriptor.set = _;
|
|
19
|
-
if (_ = accept(result.init)) initializers.unshift(_);
|
|
20
|
-
}
|
|
21
|
-
else if (_ = accept(result)) {
|
|
22
|
-
if (kind === "field") initializers.unshift(_);
|
|
23
|
-
else descriptor[key] = _;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
|
-
done = true;
|
|
28
|
-
};
|
|
29
|
-
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
-
var useValue = arguments.length > 2;
|
|
31
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
-
}
|
|
34
|
-
return useValue ? value : void 0;
|
|
35
|
-
};
|
|
36
|
-
var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
|
|
37
|
-
if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
|
|
38
|
-
return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
|
|
39
|
-
};
|
|
40
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
-
exports.InfisicalModule = void 0;
|
|
42
|
-
const common_1 = require("@nestjs/common");
|
|
43
|
-
const infisical_service_1 = require("./infisical.service");
|
|
44
|
-
let InfisicalModule = (() => {
|
|
45
|
-
let _classDecorators = [(0, common_1.Module)({})];
|
|
46
|
-
let _classDescriptor;
|
|
47
|
-
let _classExtraInitializers = [];
|
|
48
|
-
let _classThis;
|
|
49
|
-
var InfisicalModule = _classThis = class {
|
|
50
|
-
static forRoot(options = {}) {
|
|
51
|
-
const resolved = {
|
|
52
|
-
baseUrl: options.baseUrl ?? process.env.INFISICAL_BASE_URL,
|
|
53
|
-
token: options.token ?? process.env.INFISICAL_TOKEN,
|
|
54
|
-
projectId: options.projectId ?? process.env.INFISICAL_PROJECT_ID,
|
|
55
|
-
environment: options.environment ?? process.env.INFISICAL_ENVIRONMENT,
|
|
56
|
-
dotenv: options.dotenv,
|
|
57
|
-
override: options.override ?? true,
|
|
58
|
-
failFast: options.failFast ?? true,
|
|
59
|
-
};
|
|
60
|
-
return {
|
|
61
|
-
module: InfisicalModule,
|
|
62
|
-
providers: [
|
|
63
|
-
{
|
|
64
|
-
provide: 'INFISICAL_BOOTSTRAP',
|
|
65
|
-
useFactory: async () => {
|
|
66
|
-
await (0, infisical_service_1.initializeInfisical)(resolved);
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
],
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
static forRootAsync(options) {
|
|
73
|
-
return {
|
|
74
|
-
module: InfisicalModule,
|
|
75
|
-
imports: options.imports,
|
|
76
|
-
providers: [
|
|
77
|
-
{
|
|
78
|
-
provide: 'INFISICAL_BOOTSTRAP',
|
|
79
|
-
inject: options.inject ?? [],
|
|
80
|
-
useFactory: async (...args) => {
|
|
81
|
-
const resolved = await options.useFactory(...args);
|
|
82
|
-
await (0, infisical_service_1.initializeInfisical)(resolved);
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
],
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
__setFunctionName(_classThis, "InfisicalModule");
|
|
90
|
-
(() => {
|
|
91
|
-
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
92
|
-
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
93
|
-
InfisicalModule = _classThis = _classDescriptor.value;
|
|
94
|
-
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
95
|
-
__runInitializers(_classThis, _classExtraInitializers);
|
|
96
|
-
})();
|
|
97
|
-
return InfisicalModule = _classThis;
|
|
98
|
-
})();
|
|
99
|
-
exports.InfisicalModule = InfisicalModule;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.initializeInfisical = initializeInfisical;
|
|
7
|
-
const infisical_loader_1 = require("./infisical.loader");
|
|
8
|
-
const dotenv_1 = __importDefault(require("dotenv"));
|
|
9
|
-
const LOG_PREFIX = '[nestjs-infisical]';
|
|
10
|
-
function debugLog(enabled, message) {
|
|
11
|
-
if (enabled) {
|
|
12
|
-
console.log(`${LOG_PREFIX} ${message}`);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
async function initializeInfisical(options) {
|
|
16
|
-
const { dotenv: dotenvOptions, baseUrl, token, projectId, environment, override = true, failFast = true, debug = false } = options;
|
|
17
|
-
if (dotenvOptions !== false) {
|
|
18
|
-
debugLog(debug, 'Loading dotenv configuration');
|
|
19
|
-
dotenv_1.default.config(dotenvOptions);
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
debugLog(debug, 'Dotenv disabled');
|
|
23
|
-
}
|
|
24
|
-
const provided = [baseUrl, token, projectId, environment].filter(Boolean).length;
|
|
25
|
-
debugLog(debug, `Infisical config resolved: ${provided === 0 ? 'none' : provided === 4 ? 'complete' : 'partial'}`);
|
|
26
|
-
if (provided === 0) {
|
|
27
|
-
debugLog(debug, 'No Infisical configuration provided');
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (provided !== 4) {
|
|
31
|
-
console.warn(`${LOG_PREFIX} Partial Infisical configuration detected. Secrets will not be loaded.`);
|
|
32
|
-
debugLog(debug, `baseUrl=${!!baseUrl}, token=${!!token}, projectId=${!!projectId}, environment=${!!environment}`);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
debugLog(debug, 'Loading Infisical secrets');
|
|
36
|
-
await (0, infisical_loader_1.loadInfisicalSecrets)({
|
|
37
|
-
baseUrl: baseUrl,
|
|
38
|
-
token: token,
|
|
39
|
-
projectId: projectId,
|
|
40
|
-
environment: environment,
|
|
41
|
-
override,
|
|
42
|
-
failFast,
|
|
43
|
-
debug
|
|
44
|
-
});
|
|
45
|
-
}
|