@rusamer/env-guards 0.1.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 +107 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +239 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +236 -0
- package/dist/index.mjs.map +1 -0
- package/dist/keytar-F4YAPN53.node +0 -0
- package/dist/next.d.mts +6 -0
- package/dist/next.d.ts +6 -0
- package/dist/next.js +241 -0
- package/dist/next.js.map +1 -0
- package/dist/next.mjs +239 -0
- package/dist/next.mjs.map +1 -0
- package/dist/types-BxDfpIC9.d.mts +23 -0
- package/dist/types-BxDfpIC9.d.ts +23 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 EnvGod
|
|
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,107 @@
|
|
|
1
|
+
# EnvGod SDK
|
|
2
|
+
|
|
3
|
+
A secure, server-side Node.js and Next.js SDK for fetching environment bundles from the EnvGod Data Plane.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Secure by Default**: Throws if executed in a browser environment.
|
|
8
|
+
- **In-Memory Only**: Never writes secrets to disk or logs them.
|
|
9
|
+
- **Auto-Auth**: Exchanges `ENVGOD_API_KEY` for short-lived JWTs.
|
|
10
|
+
- **Smart Caching**: Caches tokens and bundles in memory until expiry.
|
|
11
|
+
- **Reliable**: Automatic retry on 401 (token expiry) and network resiliency.
|
|
12
|
+
- **Next.js Ready**: Dedicated `envgod/next` entry point with `server-only` guards.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @rusamer/envgod
|
|
18
|
+
# or
|
|
19
|
+
pnpm add @rusamer/envgod
|
|
20
|
+
# or
|
|
21
|
+
yarn add @rusamer/envgod
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Configuration
|
|
25
|
+
|
|
26
|
+
The SDK automatically reads the following environment variables:
|
|
27
|
+
|
|
28
|
+
| Variable | Description |
|
|
29
|
+
|C...|...|
|
|
30
|
+
| `ENVGOD_API_URL` | URL of the EnvGod Data Plane |
|
|
31
|
+
| `ENVGOD_API_KEY` | Your project Service Key |
|
|
32
|
+
| `ENVGOD_PROJECT` | Project ID |
|
|
33
|
+
| `ENVGOD_ENV` | Environment Name (e.g., prod) |
|
|
34
|
+
| `ENVGOD_SERVICE` | Service Name |
|
|
35
|
+
|
|
36
|
+
```env
|
|
37
|
+
ENVGOD_API_URL=https://api.example.com
|
|
38
|
+
ENVGOD_API_KEY=sk_xxx
|
|
39
|
+
ENVGOD_PROJECT=myapp
|
|
40
|
+
ENVGOD_ENV=prod
|
|
41
|
+
ENVGOD_SERVICE=web
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Node.js
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { loadEnv } from '@rusamer/envgod';
|
|
50
|
+
|
|
51
|
+
async function main() {
|
|
52
|
+
const env = await loadEnv();
|
|
53
|
+
|
|
54
|
+
console.log(process.env.MY_SECRET); // Accessed from process.env
|
|
55
|
+
console.log(env.MY_SECRET); // Or from the returned object
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
main();
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Next.js (App Router / Server Actions)
|
|
62
|
+
|
|
63
|
+
Use the Next.js specific helper to ensure server-side only execution.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// src/lib/env.ts
|
|
67
|
+
import { loadServerEnv } from '@rusamer/envgod/next';
|
|
68
|
+
|
|
69
|
+
export async function getSecrets() {
|
|
70
|
+
return loadServerEnv();
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// src/app/page.tsx
|
|
76
|
+
import { getSecrets } from '@/lib/env';
|
|
77
|
+
|
|
78
|
+
export default async function Page() {
|
|
79
|
+
const env = await getSecrets();
|
|
80
|
+
return <div>Secret length: {env.API_KEY.length}</div>;
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Security Notes
|
|
85
|
+
|
|
86
|
+
1. **Server-Only**: This SDK is designed strictly for server environments. It explicitly checks for `window` and imports `server-only` in the Next.js entrypoint.
|
|
87
|
+
2. **No Persistence**: Secrets are held in memory. Restarting the server will trigger a fresh fetch.
|
|
88
|
+
3. **Logs**: The SDK does not log secret values.
|
|
89
|
+
|
|
90
|
+
## For Maintainers
|
|
91
|
+
|
|
92
|
+
### Build
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
pnpm build
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Test
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pnpm test
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Publish
|
|
105
|
+
|
|
106
|
+
1. `npm version patch`
|
|
107
|
+
2. `npm publish`
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { L as LoadEnvOptions } from './types-BxDfpIC9.mjs';
|
|
2
|
+
export { A as AuthExchangeResponse, B as BundleResponse, E as EnvGuardsConfig } from './types-BxDfpIC9.mjs';
|
|
3
|
+
|
|
4
|
+
/** @internal For testing only */
|
|
5
|
+
declare function _resetState(): void;
|
|
6
|
+
declare function loadEnv(options?: LoadEnvOptions): Promise<Record<string, string>>;
|
|
7
|
+
|
|
8
|
+
export { LoadEnvOptions, _resetState, loadEnv };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { L as LoadEnvOptions } from './types-BxDfpIC9.js';
|
|
2
|
+
export { A as AuthExchangeResponse, B as BundleResponse, E as EnvGuardsConfig } from './types-BxDfpIC9.js';
|
|
3
|
+
|
|
4
|
+
/** @internal For testing only */
|
|
5
|
+
declare function _resetState(): void;
|
|
6
|
+
declare function loadEnv(options?: LoadEnvOptions): Promise<Record<string, string>>;
|
|
7
|
+
|
|
8
|
+
export { LoadEnvOptions, _resetState, loadEnv };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
10
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
11
|
+
}) : x)(function(x) {
|
|
12
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
|
+
});
|
|
15
|
+
var __esm = (fn, res) => function __init() {
|
|
16
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
17
|
+
};
|
|
18
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
19
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
!mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
|
|
38
|
+
// node_modules/keytar/build/Release/keytar.node
|
|
39
|
+
var keytar_default;
|
|
40
|
+
var init_keytar = __esm({
|
|
41
|
+
"node_modules/keytar/build/Release/keytar.node"() {
|
|
42
|
+
keytar_default = "./keytar-F4YAPN53.node";
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// node-file:D:\projects\envguards-sdk\node_modules\keytar\build\Release\keytar.node
|
|
47
|
+
var require_keytar = __commonJS({
|
|
48
|
+
"node-file:D:\\projects\\envguards-sdk\\node_modules\\keytar\\build\\Release\\keytar.node"(exports$1, module) {
|
|
49
|
+
init_keytar();
|
|
50
|
+
try {
|
|
51
|
+
module.exports = __require(keytar_default);
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// node_modules/keytar/lib/keytar.js
|
|
58
|
+
var require_keytar2 = __commonJS({
|
|
59
|
+
"node_modules/keytar/lib/keytar.js"(exports$1, module) {
|
|
60
|
+
var keytar = require_keytar();
|
|
61
|
+
function checkRequired(val, name) {
|
|
62
|
+
if (!val || val.length <= 0) {
|
|
63
|
+
throw new Error(name + " is required.");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
module.exports = {
|
|
67
|
+
getPassword: function(service, account) {
|
|
68
|
+
checkRequired(service, "Service");
|
|
69
|
+
checkRequired(account, "Account");
|
|
70
|
+
return keytar.getPassword(service, account);
|
|
71
|
+
},
|
|
72
|
+
setPassword: function(service, account, password) {
|
|
73
|
+
checkRequired(service, "Service");
|
|
74
|
+
checkRequired(account, "Account");
|
|
75
|
+
checkRequired(password, "Password");
|
|
76
|
+
return keytar.setPassword(service, account, password);
|
|
77
|
+
},
|
|
78
|
+
deletePassword: function(service, account) {
|
|
79
|
+
checkRequired(service, "Service");
|
|
80
|
+
checkRequired(account, "Account");
|
|
81
|
+
return keytar.deletePassword(service, account);
|
|
82
|
+
},
|
|
83
|
+
findPassword: function(service) {
|
|
84
|
+
checkRequired(service, "Service");
|
|
85
|
+
return keytar.findPassword(service);
|
|
86
|
+
},
|
|
87
|
+
findCredentials: function(service) {
|
|
88
|
+
checkRequired(service, "Service");
|
|
89
|
+
return keytar.findCredentials(service);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// src/index.ts
|
|
96
|
+
var cache = /* @__PURE__ */ new Map();
|
|
97
|
+
var pendingLoadPromise = null;
|
|
98
|
+
function _resetState() {
|
|
99
|
+
cache.clear();
|
|
100
|
+
pendingLoadPromise = null;
|
|
101
|
+
}
|
|
102
|
+
function checkBrowser() {
|
|
103
|
+
if (typeof window !== "undefined") {
|
|
104
|
+
throw new Error("[Env.Guards] Security Warning: SDK execution attempting in browser environment. This SDK is server-only.");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function fetchWithTimeout(url, init) {
|
|
108
|
+
const { timeout = 5e3, ...rest } = init;
|
|
109
|
+
const controller = new AbortController();
|
|
110
|
+
const id = setTimeout(() => controller.abort(), timeout);
|
|
111
|
+
try {
|
|
112
|
+
const res = await fetch(url, { ...rest, signal: controller.signal });
|
|
113
|
+
return res;
|
|
114
|
+
} catch (err) {
|
|
115
|
+
if (err.name === "AbortError") {
|
|
116
|
+
throw new Error(`[Env.Guards] Request timed out after ${timeout}ms`);
|
|
117
|
+
}
|
|
118
|
+
throw err;
|
|
119
|
+
} finally {
|
|
120
|
+
clearTimeout(id);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function resolveRuntimeKey(config) {
|
|
124
|
+
if (config.apiKey) return config.apiKey;
|
|
125
|
+
if (process.env.ENV_GUARDS_API_KEY) return process.env.ENV_GUARDS_API_KEY;
|
|
126
|
+
try {
|
|
127
|
+
const keytar = (await Promise.resolve().then(() => __toESM(require_keytar2()))).default;
|
|
128
|
+
const SERVICE = "env-guards";
|
|
129
|
+
const { apiUrl, org, project, env, service } = config;
|
|
130
|
+
if (apiUrl && org && project && env && service) {
|
|
131
|
+
const account = `runtime:${apiUrl}:${org}:${project}:${env}:${service}`;
|
|
132
|
+
const key = await keytar.getPassword(SERVICE, account);
|
|
133
|
+
if (key) return key;
|
|
134
|
+
}
|
|
135
|
+
} catch (err) {
|
|
136
|
+
}
|
|
137
|
+
throw new Error("[Env.Guards] Runtime key not found. Please set ENV_GUARDS_API_KEY, use `env-guards run`, or run `env-guards add-runtime-key`.");
|
|
138
|
+
}
|
|
139
|
+
function getFullConfig(options) {
|
|
140
|
+
const env = process.env;
|
|
141
|
+
return {
|
|
142
|
+
apiUrl: options?.config?.apiUrl ?? env.ENV_GUARDS_API_URL,
|
|
143
|
+
apiKey: options?.config?.apiKey ?? env.ENV_GUARDS_API_KEY,
|
|
144
|
+
org: options?.config?.org ?? env.ENV_GUARDS_ORG,
|
|
145
|
+
project: options?.config?.project ?? env.ENV_GUARDS_PROJECT,
|
|
146
|
+
env: options?.config?.env ?? env.ENV_GUARDS_ENV,
|
|
147
|
+
service: options?.config?.service ?? env.ENV_GUARDS_SERVICE
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function exchangeToken(apiUrl, runtimeKey, scope, timeout, state) {
|
|
151
|
+
const res = await fetchWithTimeout(`${apiUrl}/v1/auth/exchange`, {
|
|
152
|
+
method: "POST",
|
|
153
|
+
headers: {
|
|
154
|
+
"Content-Type": "application/json",
|
|
155
|
+
"Authorization": `Bearer ${runtimeKey}`
|
|
156
|
+
},
|
|
157
|
+
body: JSON.stringify(scope),
|
|
158
|
+
timeout
|
|
159
|
+
});
|
|
160
|
+
if (!res.ok) {
|
|
161
|
+
throw new Error(`[Env.Guards] Auth exchange failed: ${res.status} ${res.statusText}`);
|
|
162
|
+
}
|
|
163
|
+
const data = await res.json();
|
|
164
|
+
state.token = data.token;
|
|
165
|
+
state.tokenExpiresAt = new Date(data.expiresAt).getTime();
|
|
166
|
+
return data.token;
|
|
167
|
+
}
|
|
168
|
+
async function fetchBundle(apiUrl, token, timeout) {
|
|
169
|
+
const res = await fetchWithTimeout(`${apiUrl}/v1/bundle`, {
|
|
170
|
+
method: "GET",
|
|
171
|
+
headers: { "Authorization": `Bearer ${token}` },
|
|
172
|
+
timeout
|
|
173
|
+
});
|
|
174
|
+
if (res.status === 401) throw new Error("401");
|
|
175
|
+
if (!res.ok) throw new Error(`[Env.Guards] Fetch bundle failed: ${res.status} ${res.statusText}`);
|
|
176
|
+
const data = await res.json();
|
|
177
|
+
return data.values;
|
|
178
|
+
}
|
|
179
|
+
function getConfigFingerprint(apiUrl, runtimeKey, config) {
|
|
180
|
+
const keyPrefix = runtimeKey.substring(0, 12);
|
|
181
|
+
return [apiUrl, keyPrefix, config.org, config.project, config.env, config.service].join("|");
|
|
182
|
+
}
|
|
183
|
+
async function loadEnvInternal(options) {
|
|
184
|
+
checkBrowser();
|
|
185
|
+
const config = getFullConfig(options);
|
|
186
|
+
const { apiUrl, ...scope } = config;
|
|
187
|
+
if (!apiUrl) {
|
|
188
|
+
throw new Error("[Env.Guards] Missing required configuration: apiUrl is required.");
|
|
189
|
+
}
|
|
190
|
+
const runtimeKey = await resolveRuntimeKey(config);
|
|
191
|
+
const timeout = options?.timeout ?? 5e3;
|
|
192
|
+
const now = Date.now();
|
|
193
|
+
const TOKEN_SKEW_MS = 30 * 1e3;
|
|
194
|
+
const fingerprint = getConfigFingerprint(apiUrl, runtimeKey, config);
|
|
195
|
+
if (!cache.has(fingerprint)) {
|
|
196
|
+
cache.set(fingerprint, { token: null, tokenExpiresAt: null, bundle: null });
|
|
197
|
+
}
|
|
198
|
+
const state = cache.get(fingerprint);
|
|
199
|
+
let token = state.token;
|
|
200
|
+
let tokenValid = token && state.tokenExpiresAt && state.tokenExpiresAt > now + TOKEN_SKEW_MS;
|
|
201
|
+
if (!tokenValid) {
|
|
202
|
+
token = await exchangeToken(apiUrl, runtimeKey, scope, timeout, state);
|
|
203
|
+
}
|
|
204
|
+
if (state.bundle && tokenValid) {
|
|
205
|
+
Object.assign(process.env, state.bundle);
|
|
206
|
+
return state.bundle;
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
const values = await fetchBundle(apiUrl, token, timeout);
|
|
210
|
+
state.bundle = values;
|
|
211
|
+
Object.assign(process.env, values);
|
|
212
|
+
return values;
|
|
213
|
+
} catch (err) {
|
|
214
|
+
if (err.message === "401") {
|
|
215
|
+
state.token = null;
|
|
216
|
+
state.bundle = null;
|
|
217
|
+
const newToken = await exchangeToken(apiUrl, runtimeKey, scope, timeout, state);
|
|
218
|
+
const values = await fetchBundle(apiUrl, newToken, timeout);
|
|
219
|
+
state.bundle = values;
|
|
220
|
+
Object.assign(process.env, values);
|
|
221
|
+
return values;
|
|
222
|
+
}
|
|
223
|
+
throw err;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
function loadEnv(options) {
|
|
227
|
+
if (pendingLoadPromise) {
|
|
228
|
+
return pendingLoadPromise;
|
|
229
|
+
}
|
|
230
|
+
pendingLoadPromise = loadEnvInternal(options).finally(() => {
|
|
231
|
+
pendingLoadPromise = null;
|
|
232
|
+
});
|
|
233
|
+
return pendingLoadPromise;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
exports._resetState = _resetState;
|
|
237
|
+
exports.loadEnv = loadEnv;
|
|
238
|
+
//# sourceMappingURL=index.js.map
|
|
239
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["node-file:D:/projects/envguards-sdk/node_modules/keytar/build/Release/keytar.node","../node_modules/keytar/lib/keytar.js","../src/index.ts"],"names":["exports","require_keytar"],"mappings":"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","file":"index.js","sourcesContent":["\n import path from \"D:\\\\projects\\\\envguards-sdk\\\\node_modules\\\\keytar\\\\build\\\\Release\\\\keytar.node\"\n try { module.exports = require(path) }\n catch {}\n ","var keytar = require('../build/Release/keytar.node')\n\nfunction checkRequired(val, name) {\n if (!val || val.length <= 0) {\n throw new Error(name + ' is required.');\n }\n}\n\nmodule.exports = {\n getPassword: function (service, account) {\n checkRequired(service, 'Service')\n checkRequired(account, 'Account')\n\n return keytar.getPassword(service, account)\n },\n\n setPassword: function (service, account, password) {\n checkRequired(service, 'Service')\n checkRequired(account, 'Account')\n checkRequired(password, 'Password')\n\n return keytar.setPassword(service, account, password)\n },\n\n deletePassword: function (service, account) {\n checkRequired(service, 'Service')\n checkRequired(account, 'Account')\n\n return keytar.deletePassword(service, account)\n },\n\n findPassword: function (service) {\n checkRequired(service, 'Service')\n\n return keytar.findPassword(service)\n },\n\n findCredentials: function (service) {\n checkRequired(service, 'Service')\n\n return keytar.findCredentials(service)\n }\n}\n","import type { EnvGuardsConfig, LoadEnvOptions, AuthExchangeResponse, BundleResponse } from './types.js';\r\n\r\n// --- State ---\r\ninterface CacheState {\r\n token: string | null;\r\n tokenExpiresAt: number | null; // Timestamp in ms\r\n bundle: Record<string, string> | null;\r\n}\r\n\r\nconst cache = new Map<string, CacheState>();\r\nlet pendingLoadPromise: Promise<Record<string, string>> | null = null;\r\n\r\n/** @internal For testing only */\r\nexport function _resetState() {\r\n cache.clear();\r\n pendingLoadPromise = null;\r\n}\r\n\r\n// --- Helpers ---\r\n\r\nfunction checkBrowser() {\r\n if (typeof window !== 'undefined') {\r\n throw new Error('[Env.Guards] Security Warning: SDK execution attempting in browser environment. This SDK is server-only.');\r\n }\r\n}\r\n\r\nasync function fetchWithTimeout(url: string, init: RequestInit & { timeout?: number }) {\r\n const { timeout = 5000, ...rest } = init;\r\n const controller = new AbortController();\r\n const id = setTimeout(() => controller.abort(), timeout);\r\n\r\n try {\r\n const res = await fetch(url, { ...rest, signal: controller.signal });\r\n return res;\r\n } catch (err: any) {\r\n if (err.name === 'AbortError') {\r\n throw new Error(`[Env.Guards] Request timed out after ${timeout}ms`);\r\n }\r\n throw err;\r\n } finally {\r\n clearTimeout(id);\r\n }\r\n}\r\n\r\n// --- Core Logic ---\r\n\r\nasync function resolveRuntimeKey(config: EnvGuardsConfig): Promise<string> {\r\n // 1. Explicitly passed apiKey\r\n if (config.apiKey) return config.apiKey;\r\n\r\n // 2. Environment variable\r\n if (process.env.ENV_GUARDS_API_KEY) return process.env.ENV_GUARDS_API_KEY;\r\n\r\n // 3. Keytar (for local development, optional)\r\n try {\r\n const keytar = (await import('keytar')).default;\r\n const SERVICE = 'env-guards';\r\n const { apiUrl, org, project, env, service } = config;\r\n if (apiUrl && org && project && env && service) {\r\n const account = `runtime:${apiUrl}:${org}:${project}:${env}:${service}`;\r\n const key = await keytar.getPassword(SERVICE, account);\r\n if (key) return key;\r\n }\r\n } catch (err) {\r\n // Keytar is optional, so we ignore errors (e.g., native module not built).\r\n }\r\n\r\n throw new Error('[Env.Guards] Runtime key not found. Please set ENV_GUARDS_API_KEY, use `env-guards run`, or run `env-guards add-runtime-key`.');\r\n}\r\n\r\nfunction getFullConfig(options?: LoadEnvOptions): EnvGuardsConfig {\r\n const env = process.env;\r\n return {\r\n apiUrl: options?.config?.apiUrl ?? env.ENV_GUARDS_API_URL,\r\n apiKey: options?.config?.apiKey ?? env.ENV_GUARDS_API_KEY,\r\n org: options?.config?.org ?? env.ENV_GUARDS_ORG,\r\n project: options?.config?.project ?? env.ENV_GUARDS_PROJECT,\r\n env: options?.config?.env ?? env.ENV_GUARDS_ENV,\r\n service: options?.config?.service ?? env.ENV_GUARDS_SERVICE,\r\n };\r\n}\r\n\r\nasync function exchangeToken(apiUrl: string, runtimeKey: string, scope: Partial<EnvGuardsConfig>, timeout: number, state: CacheState): Promise<string> {\r\n const res = await fetchWithTimeout(`${apiUrl}/v1/auth/exchange`, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${runtimeKey}`,\r\n },\r\n body: JSON.stringify(scope),\r\n timeout,\r\n });\r\n\r\n if (!res.ok) {\r\n throw new Error(`[Env.Guards] Auth exchange failed: ${res.status} ${res.statusText}`);\r\n }\r\n\r\n const data = (await res.json()) as AuthExchangeResponse;\r\n state.token = data.token;\r\n state.tokenExpiresAt = new Date(data.expiresAt).getTime();\r\n return data.token;\r\n}\r\n\r\nasync function fetchBundle(apiUrl: string, token: string, timeout: number): Promise<Record<string, string>> {\r\n const res = await fetchWithTimeout(`${apiUrl}/v1/bundle`, {\r\n method: 'GET',\r\n headers: { 'Authorization': `Bearer ${token}` },\r\n timeout,\r\n });\r\n\r\n if (res.status === 401) throw new Error('401'); // Signal to retry\r\n if (!res.ok) throw new Error(`[Env.Guards] Fetch bundle failed: ${res.status} ${res.statusText}`);\r\n\r\n const data = (await res.json()) as BundleResponse;\r\n return data.values;\r\n}\r\n\r\nfunction getConfigFingerprint(apiUrl: string, runtimeKey: string, config: EnvGuardsConfig): string {\r\n const keyPrefix = runtimeKey.substring(0, 12); // env-guards_sk_...\r\n return [apiUrl, keyPrefix, config.org, config.project, config.env, config.service].join('|');\r\n}\r\n\r\nasync function loadEnvInternal(options?: LoadEnvOptions): Promise<Record<string, string>> {\r\n checkBrowser();\r\n const config = getFullConfig(options);\r\n const { apiUrl, ...scope } = config;\r\n\r\n if (!apiUrl) {\r\n throw new Error('[Env.Guards] Missing required configuration: apiUrl is required.');\r\n }\r\n\r\n const runtimeKey = await resolveRuntimeKey(config);\r\n const timeout = options?.timeout ?? 5000;\r\n const now = Date.now();\r\n const TOKEN_SKEW_MS = 30 * 1000;\r\n\r\n const fingerprint = getConfigFingerprint(apiUrl, runtimeKey, config);\r\n if (!cache.has(fingerprint)) {\r\n cache.set(fingerprint, { token: null, tokenExpiresAt: null, bundle: null });\r\n }\r\n const state = cache.get(fingerprint)!;\r\n\r\n let token = state.token;\r\n let tokenValid = token && state.tokenExpiresAt && state.tokenExpiresAt > (now + TOKEN_SKEW_MS);\r\n\r\n if (!tokenValid) {\r\n token = await exchangeToken(apiUrl, runtimeKey, scope, timeout, state);\r\n }\r\n\r\n if (state.bundle && tokenValid) {\r\n Object.assign(process.env, state.bundle);\r\n return state.bundle;\r\n }\r\n\r\n try {\r\n const values = await fetchBundle(apiUrl, token!, timeout);\r\n state.bundle = values;\r\n Object.assign(process.env, values);\r\n return values;\r\n } catch (err: any) {\r\n if (err.message === '401') {\r\n state.token = null;\r\n state.bundle = null;\r\n const newToken = await exchangeToken(apiUrl, runtimeKey, scope, timeout, state);\r\n const values = await fetchBundle(apiUrl, newToken, timeout);\r\n state.bundle = values;\r\n Object.assign(process.env, values);\r\n return values;\r\n }\r\n throw err;\r\n }\r\n}\r\n\r\nexport function loadEnv(options?: LoadEnvOptions): Promise<Record<string, string>> {\r\n if (pendingLoadPromise) {\r\n return pendingLoadPromise;\r\n }\r\n\r\n pendingLoadPromise = loadEnvInternal(options).finally(() => {\r\n pendingLoadPromise = null;\r\n });\r\n\r\n return pendingLoadPromise;\r\n}\r\n\r\nexport * from './types.js';\r\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
13
|
+
var __esm = (fn, res) => function __init() {
|
|
14
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
+
};
|
|
16
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
17
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
|
+
};
|
|
19
|
+
var __copyProps = (to, from, except, desc) => {
|
|
20
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
21
|
+
for (let key of __getOwnPropNames(from))
|
|
22
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
23
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
24
|
+
}
|
|
25
|
+
return to;
|
|
26
|
+
};
|
|
27
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
28
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
29
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
30
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
31
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
32
|
+
!mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
33
|
+
mod
|
|
34
|
+
));
|
|
35
|
+
|
|
36
|
+
// node_modules/keytar/build/Release/keytar.node
|
|
37
|
+
var keytar_default;
|
|
38
|
+
var init_keytar = __esm({
|
|
39
|
+
"node_modules/keytar/build/Release/keytar.node"() {
|
|
40
|
+
keytar_default = "./keytar-F4YAPN53.node";
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// node-file:D:\projects\envguards-sdk\node_modules\keytar\build\Release\keytar.node
|
|
45
|
+
var require_keytar = __commonJS({
|
|
46
|
+
"node-file:D:\\projects\\envguards-sdk\\node_modules\\keytar\\build\\Release\\keytar.node"(exports$1, module) {
|
|
47
|
+
init_keytar();
|
|
48
|
+
try {
|
|
49
|
+
module.exports = __require(keytar_default);
|
|
50
|
+
} catch {
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// node_modules/keytar/lib/keytar.js
|
|
56
|
+
var require_keytar2 = __commonJS({
|
|
57
|
+
"node_modules/keytar/lib/keytar.js"(exports$1, module) {
|
|
58
|
+
var keytar = require_keytar();
|
|
59
|
+
function checkRequired(val, name) {
|
|
60
|
+
if (!val || val.length <= 0) {
|
|
61
|
+
throw new Error(name + " is required.");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
module.exports = {
|
|
65
|
+
getPassword: function(service, account) {
|
|
66
|
+
checkRequired(service, "Service");
|
|
67
|
+
checkRequired(account, "Account");
|
|
68
|
+
return keytar.getPassword(service, account);
|
|
69
|
+
},
|
|
70
|
+
setPassword: function(service, account, password) {
|
|
71
|
+
checkRequired(service, "Service");
|
|
72
|
+
checkRequired(account, "Account");
|
|
73
|
+
checkRequired(password, "Password");
|
|
74
|
+
return keytar.setPassword(service, account, password);
|
|
75
|
+
},
|
|
76
|
+
deletePassword: function(service, account) {
|
|
77
|
+
checkRequired(service, "Service");
|
|
78
|
+
checkRequired(account, "Account");
|
|
79
|
+
return keytar.deletePassword(service, account);
|
|
80
|
+
},
|
|
81
|
+
findPassword: function(service) {
|
|
82
|
+
checkRequired(service, "Service");
|
|
83
|
+
return keytar.findPassword(service);
|
|
84
|
+
},
|
|
85
|
+
findCredentials: function(service) {
|
|
86
|
+
checkRequired(service, "Service");
|
|
87
|
+
return keytar.findCredentials(service);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// src/index.ts
|
|
94
|
+
var cache = /* @__PURE__ */ new Map();
|
|
95
|
+
var pendingLoadPromise = null;
|
|
96
|
+
function _resetState() {
|
|
97
|
+
cache.clear();
|
|
98
|
+
pendingLoadPromise = null;
|
|
99
|
+
}
|
|
100
|
+
function checkBrowser() {
|
|
101
|
+
if (typeof window !== "undefined") {
|
|
102
|
+
throw new Error("[Env.Guards] Security Warning: SDK execution attempting in browser environment. This SDK is server-only.");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function fetchWithTimeout(url, init) {
|
|
106
|
+
const { timeout = 5e3, ...rest } = init;
|
|
107
|
+
const controller = new AbortController();
|
|
108
|
+
const id = setTimeout(() => controller.abort(), timeout);
|
|
109
|
+
try {
|
|
110
|
+
const res = await fetch(url, { ...rest, signal: controller.signal });
|
|
111
|
+
return res;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
if (err.name === "AbortError") {
|
|
114
|
+
throw new Error(`[Env.Guards] Request timed out after ${timeout}ms`);
|
|
115
|
+
}
|
|
116
|
+
throw err;
|
|
117
|
+
} finally {
|
|
118
|
+
clearTimeout(id);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function resolveRuntimeKey(config) {
|
|
122
|
+
if (config.apiKey) return config.apiKey;
|
|
123
|
+
if (process.env.ENV_GUARDS_API_KEY) return process.env.ENV_GUARDS_API_KEY;
|
|
124
|
+
try {
|
|
125
|
+
const keytar = (await Promise.resolve().then(() => __toESM(require_keytar2()))).default;
|
|
126
|
+
const SERVICE = "env-guards";
|
|
127
|
+
const { apiUrl, org, project, env, service } = config;
|
|
128
|
+
if (apiUrl && org && project && env && service) {
|
|
129
|
+
const account = `runtime:${apiUrl}:${org}:${project}:${env}:${service}`;
|
|
130
|
+
const key = await keytar.getPassword(SERVICE, account);
|
|
131
|
+
if (key) return key;
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
}
|
|
135
|
+
throw new Error("[Env.Guards] Runtime key not found. Please set ENV_GUARDS_API_KEY, use `env-guards run`, or run `env-guards add-runtime-key`.");
|
|
136
|
+
}
|
|
137
|
+
function getFullConfig(options) {
|
|
138
|
+
const env = process.env;
|
|
139
|
+
return {
|
|
140
|
+
apiUrl: options?.config?.apiUrl ?? env.ENV_GUARDS_API_URL,
|
|
141
|
+
apiKey: options?.config?.apiKey ?? env.ENV_GUARDS_API_KEY,
|
|
142
|
+
org: options?.config?.org ?? env.ENV_GUARDS_ORG,
|
|
143
|
+
project: options?.config?.project ?? env.ENV_GUARDS_PROJECT,
|
|
144
|
+
env: options?.config?.env ?? env.ENV_GUARDS_ENV,
|
|
145
|
+
service: options?.config?.service ?? env.ENV_GUARDS_SERVICE
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async function exchangeToken(apiUrl, runtimeKey, scope, timeout, state) {
|
|
149
|
+
const res = await fetchWithTimeout(`${apiUrl}/v1/auth/exchange`, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
headers: {
|
|
152
|
+
"Content-Type": "application/json",
|
|
153
|
+
"Authorization": `Bearer ${runtimeKey}`
|
|
154
|
+
},
|
|
155
|
+
body: JSON.stringify(scope),
|
|
156
|
+
timeout
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
throw new Error(`[Env.Guards] Auth exchange failed: ${res.status} ${res.statusText}`);
|
|
160
|
+
}
|
|
161
|
+
const data = await res.json();
|
|
162
|
+
state.token = data.token;
|
|
163
|
+
state.tokenExpiresAt = new Date(data.expiresAt).getTime();
|
|
164
|
+
return data.token;
|
|
165
|
+
}
|
|
166
|
+
async function fetchBundle(apiUrl, token, timeout) {
|
|
167
|
+
const res = await fetchWithTimeout(`${apiUrl}/v1/bundle`, {
|
|
168
|
+
method: "GET",
|
|
169
|
+
headers: { "Authorization": `Bearer ${token}` },
|
|
170
|
+
timeout
|
|
171
|
+
});
|
|
172
|
+
if (res.status === 401) throw new Error("401");
|
|
173
|
+
if (!res.ok) throw new Error(`[Env.Guards] Fetch bundle failed: ${res.status} ${res.statusText}`);
|
|
174
|
+
const data = await res.json();
|
|
175
|
+
return data.values;
|
|
176
|
+
}
|
|
177
|
+
function getConfigFingerprint(apiUrl, runtimeKey, config) {
|
|
178
|
+
const keyPrefix = runtimeKey.substring(0, 12);
|
|
179
|
+
return [apiUrl, keyPrefix, config.org, config.project, config.env, config.service].join("|");
|
|
180
|
+
}
|
|
181
|
+
async function loadEnvInternal(options) {
|
|
182
|
+
checkBrowser();
|
|
183
|
+
const config = getFullConfig(options);
|
|
184
|
+
const { apiUrl, ...scope } = config;
|
|
185
|
+
if (!apiUrl) {
|
|
186
|
+
throw new Error("[Env.Guards] Missing required configuration: apiUrl is required.");
|
|
187
|
+
}
|
|
188
|
+
const runtimeKey = await resolveRuntimeKey(config);
|
|
189
|
+
const timeout = options?.timeout ?? 5e3;
|
|
190
|
+
const now = Date.now();
|
|
191
|
+
const TOKEN_SKEW_MS = 30 * 1e3;
|
|
192
|
+
const fingerprint = getConfigFingerprint(apiUrl, runtimeKey, config);
|
|
193
|
+
if (!cache.has(fingerprint)) {
|
|
194
|
+
cache.set(fingerprint, { token: null, tokenExpiresAt: null, bundle: null });
|
|
195
|
+
}
|
|
196
|
+
const state = cache.get(fingerprint);
|
|
197
|
+
let token = state.token;
|
|
198
|
+
let tokenValid = token && state.tokenExpiresAt && state.tokenExpiresAt > now + TOKEN_SKEW_MS;
|
|
199
|
+
if (!tokenValid) {
|
|
200
|
+
token = await exchangeToken(apiUrl, runtimeKey, scope, timeout, state);
|
|
201
|
+
}
|
|
202
|
+
if (state.bundle && tokenValid) {
|
|
203
|
+
Object.assign(process.env, state.bundle);
|
|
204
|
+
return state.bundle;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
const values = await fetchBundle(apiUrl, token, timeout);
|
|
208
|
+
state.bundle = values;
|
|
209
|
+
Object.assign(process.env, values);
|
|
210
|
+
return values;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (err.message === "401") {
|
|
213
|
+
state.token = null;
|
|
214
|
+
state.bundle = null;
|
|
215
|
+
const newToken = await exchangeToken(apiUrl, runtimeKey, scope, timeout, state);
|
|
216
|
+
const values = await fetchBundle(apiUrl, newToken, timeout);
|
|
217
|
+
state.bundle = values;
|
|
218
|
+
Object.assign(process.env, values);
|
|
219
|
+
return values;
|
|
220
|
+
}
|
|
221
|
+
throw err;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function loadEnv(options) {
|
|
225
|
+
if (pendingLoadPromise) {
|
|
226
|
+
return pendingLoadPromise;
|
|
227
|
+
}
|
|
228
|
+
pendingLoadPromise = loadEnvInternal(options).finally(() => {
|
|
229
|
+
pendingLoadPromise = null;
|
|
230
|
+
});
|
|
231
|
+
return pendingLoadPromise;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export { _resetState, loadEnv };
|
|
235
|
+
//# sourceMappingURL=index.mjs.map
|
|
236
|
+
//# sourceMappingURL=index.mjs.map
|