@sentroy-co/client-sdk 2.6.4 → 2.9.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/AGENTS.md +138 -2
- package/README.md +44 -1
- package/bin/sentroy.js +4 -0
- package/dist/cli/dotenv.d.ts +35 -0
- package/dist/cli/dotenv.d.ts.map +1 -0
- package/dist/cli/dotenv.js +125 -0
- package/dist/cli/dotenv.js.map +1 -0
- package/dist/cli/env.d.ts +11 -0
- package/dist/cli/env.d.ts.map +1 -0
- package/dist/cli/env.js +331 -0
- package/dist/cli/env.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +105 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/vault/index.d.ts +73 -0
- package/dist/vault/index.d.ts.map +1 -0
- package/dist/vault/index.js +169 -0
- package/dist/vault/index.js.map +1 -0
- package/dist/vault/react.d.ts +27 -0
- package/dist/vault/react.d.ts.map +1 -0
- package/dist/vault/react.js +73 -0
- package/dist/vault/react.js.map +1 -0
- package/package.json +23 -3
- package/src/cli/dotenv.ts +146 -0
- package/src/cli/env.ts +402 -0
- package/src/cli/index.ts +122 -0
- package/src/vault/index.ts +198 -0
- package/src/vault/react.tsx +125 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `@sentroy-co/client-sdk/vault` — Sentroy Env Vault server-side client.
|
|
4
|
+
*
|
|
5
|
+
* Bootstrap pattern:
|
|
6
|
+
* `SENTROY_ENV_API_KEY` (process.env) → tek dış env. Fonksiyonlar
|
|
7
|
+
* `getEnv("KEY")` ya da `getEnvOrThrow("KEY")` çağrılırken in-memory
|
|
8
|
+
* cache'den döner; ilk çağrıda Sentroy core'a HTTP fetch yapar ve
|
|
9
|
+
* o token scope'undaki TÜM env'leri (public + private) çeker.
|
|
10
|
+
*
|
|
11
|
+
* Cache stratejisi:
|
|
12
|
+
* • Default TTL 5 dk; refresh deadline aşıldığında bir sonraki
|
|
13
|
+
* `getEnv` çağrısında re-fetch tetiklenir.
|
|
14
|
+
* • `await refreshEnvCache()` manuel invalidation — webhook ya da
|
|
15
|
+
* SIGHUP-style restart sinyaline bağlanabilir.
|
|
16
|
+
* • `setEnvCacheTTL(seconds)` runtime'da TTL değiştirme.
|
|
17
|
+
*
|
|
18
|
+
* Hata politikası: bootstrap fail (token yok / network down / 401)
|
|
19
|
+
* → `getEnv` her çağrıda undefined döner; `getEnvOrThrow` exception
|
|
20
|
+
* atar. Process startup'ında `await preloadEnv()` çağırırsanız
|
|
21
|
+
* eksik env'leri erkenden yakalarsınız.
|
|
22
|
+
*
|
|
23
|
+
* **NOT**: Bu modül `Sentroy` ana client'ından (mail/storage REST
|
|
24
|
+
* resource'ları) bağımsızdır. Vault token'ları (stk_env_*) ile mail/
|
|
25
|
+
* storage token'ları (stk_*) farklı namespace'tedir; tek client'ta
|
|
26
|
+
* birleştirmek ergonomiyi bozardı.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.configureEnvClient = configureEnvClient;
|
|
30
|
+
exports.setEnvCacheTTL = setEnvCacheTTL;
|
|
31
|
+
exports.refreshEnvCache = refreshEnvCache;
|
|
32
|
+
exports.preloadEnv = preloadEnv;
|
|
33
|
+
exports.getEnv = getEnv;
|
|
34
|
+
exports.getEnvOrThrow = getEnvOrThrow;
|
|
35
|
+
exports.getAllEnvs = getAllEnvs;
|
|
36
|
+
exports.getPublicEnvs = getPublicEnvs;
|
|
37
|
+
const DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
38
|
+
const DEFAULT_BASE_URL = "https://sentroy.com";
|
|
39
|
+
let resolvedBaseUrl = DEFAULT_BASE_URL;
|
|
40
|
+
let resolvedApiKey;
|
|
41
|
+
let cacheTtlMs = DEFAULT_TTL_MS;
|
|
42
|
+
let fetchTimeoutMs = 5000;
|
|
43
|
+
let cache = null;
|
|
44
|
+
let pendingRefresh = null;
|
|
45
|
+
function readEnv(name) {
|
|
46
|
+
if (typeof process === "undefined")
|
|
47
|
+
return undefined;
|
|
48
|
+
return process.env?.[name];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* One-time client config — Sentroy app'lerinde modül seviyesinde çağrılır,
|
|
52
|
+
* default'lara güvenilirse hiç çağrılmasına gerek yok.
|
|
53
|
+
*/
|
|
54
|
+
function configureEnvClient(options = {}) {
|
|
55
|
+
if (options.baseUrl)
|
|
56
|
+
resolvedBaseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
57
|
+
else
|
|
58
|
+
resolvedBaseUrl = (readEnv("NEXT_PUBLIC_SENTROY_ENV_API_URL") ||
|
|
59
|
+
readEnv("SENTROY_ENV_API_URL") ||
|
|
60
|
+
readEnv("NEXT_PUBLIC_CORE_APP_URL") ||
|
|
61
|
+
DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
62
|
+
resolvedApiKey = options.apiKey ?? readEnv("SENTROY_ENV_API_KEY");
|
|
63
|
+
if (options.ttlSeconds)
|
|
64
|
+
cacheTtlMs = options.ttlSeconds * 1000;
|
|
65
|
+
if (options.timeoutMs)
|
|
66
|
+
fetchTimeoutMs = options.timeoutMs;
|
|
67
|
+
}
|
|
68
|
+
/** TTL'i runtime'da değiştir (örn. development için kısa, prod için uzun). */
|
|
69
|
+
function setEnvCacheTTL(seconds) {
|
|
70
|
+
cacheTtlMs = seconds * 1000;
|
|
71
|
+
}
|
|
72
|
+
/** Cache'i invalidate et — webhook ya da admin-driven manual refresh için. */
|
|
73
|
+
async function refreshEnvCache() {
|
|
74
|
+
cache = null;
|
|
75
|
+
await ensureCache();
|
|
76
|
+
}
|
|
77
|
+
/** Process start'ında erkenden tetikle — eksik env'i fail-fast yakalar. */
|
|
78
|
+
async function preloadEnv() {
|
|
79
|
+
await ensureCache();
|
|
80
|
+
}
|
|
81
|
+
async function fetchVariables() {
|
|
82
|
+
if (!resolvedApiKey) {
|
|
83
|
+
// Lazy bootstrap — configureEnvClient çağrılmadıysa env'den oku.
|
|
84
|
+
configureEnvClient();
|
|
85
|
+
}
|
|
86
|
+
if (!resolvedApiKey) {
|
|
87
|
+
throw new Error("@sentroy-co/client-sdk/vault: SENTROY_ENV_API_KEY is not set. " +
|
|
88
|
+
"Set it on the platform (Coolify env) or call configureEnvClient({ apiKey: ... }) at boot.");
|
|
89
|
+
}
|
|
90
|
+
const url = `${resolvedBaseUrl}/api/env-vault/fetch`;
|
|
91
|
+
const res = await fetch(url, {
|
|
92
|
+
headers: { Authorization: `Bearer ${resolvedApiKey}` },
|
|
93
|
+
signal: AbortSignal.timeout(fetchTimeoutMs),
|
|
94
|
+
cache: "no-store",
|
|
95
|
+
});
|
|
96
|
+
if (!res.ok) {
|
|
97
|
+
throw new Error(`env-vault fetch failed: ${res.status} ${res.statusText} (url=${url})`);
|
|
98
|
+
}
|
|
99
|
+
const json = (await res.json());
|
|
100
|
+
if (!json.data)
|
|
101
|
+
throw new Error("env-vault fetch: malformed response");
|
|
102
|
+
const map = new Map();
|
|
103
|
+
for (const v of json.data.variables)
|
|
104
|
+
map.set(v.key, v);
|
|
105
|
+
return {
|
|
106
|
+
fetchedAt: Date.now(),
|
|
107
|
+
variables: map,
|
|
108
|
+
project: json.data.project,
|
|
109
|
+
environment: json.data.environment,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function ensureCache() {
|
|
113
|
+
const now = Date.now();
|
|
114
|
+
if (cache && now - cache.fetchedAt < cacheTtlMs)
|
|
115
|
+
return cache;
|
|
116
|
+
if (pendingRefresh) {
|
|
117
|
+
await pendingRefresh;
|
|
118
|
+
if (cache)
|
|
119
|
+
return cache;
|
|
120
|
+
}
|
|
121
|
+
pendingRefresh = (async () => {
|
|
122
|
+
try {
|
|
123
|
+
cache = await fetchVariables();
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
pendingRefresh = null;
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
await pendingRefresh;
|
|
130
|
+
if (!cache)
|
|
131
|
+
throw new Error("env-vault: cache hydrate failed");
|
|
132
|
+
return cache;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Async — env yoksa undefined. Bu fonksiyon TÜM env'leri (server+public)
|
|
136
|
+
* gizler, çünkü `process.env` fallback yok; sadece vault'ta kayıtlı
|
|
137
|
+
* olanlar dönder. Token bootstrap fail ederse exception atar.
|
|
138
|
+
*/
|
|
139
|
+
async function getEnv(key) {
|
|
140
|
+
const c = await ensureCache();
|
|
141
|
+
return c.variables.get(key)?.value;
|
|
142
|
+
}
|
|
143
|
+
/** Eksik env'i hemen patlatır — config-validation pattern'inde kullanışlı. */
|
|
144
|
+
async function getEnvOrThrow(key) {
|
|
145
|
+
const v = await getEnv(key);
|
|
146
|
+
if (v === undefined) {
|
|
147
|
+
throw new Error(`env-vault: required variable ${key} is not defined (project=${cache?.project ?? "?"}, env=${cache?.environment ?? "?"})`);
|
|
148
|
+
}
|
|
149
|
+
return v;
|
|
150
|
+
}
|
|
151
|
+
/** Tüm env'leri map olarak döner (dump için kullanışlı). */
|
|
152
|
+
async function getAllEnvs() {
|
|
153
|
+
const c = await ensureCache();
|
|
154
|
+
const out = {};
|
|
155
|
+
for (const [k, v] of c.variables)
|
|
156
|
+
out[k] = v.value;
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
/** Sadece public (`public: true`) env'ler — SSR helper için. */
|
|
160
|
+
async function getPublicEnvs() {
|
|
161
|
+
const c = await ensureCache();
|
|
162
|
+
const out = {};
|
|
163
|
+
for (const [k, v] of c.variables) {
|
|
164
|
+
if (v.public)
|
|
165
|
+
out[k] = v.value;
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vault/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
interface EnvProviderProps {
|
|
3
|
+
/** Server-side fetched public envs — SSR'da inject edilir. */
|
|
4
|
+
envs: Record<string, string>;
|
|
5
|
+
/** Public refresh endpoint URL — default `/api/env-vault/public`. */
|
|
6
|
+
refreshUrl?: string;
|
|
7
|
+
/** Bearer token — public endpoint için. Default `process.env.NEXT_PUBLIC_SENTROY_ENV_API_KEY`. */
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/** Refresh interval ms; 0 ise polling kapalı. Default 5 dk. */
|
|
10
|
+
refreshIntervalMs?: number;
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
export declare function EnvProvider({ envs: initialEnvs, refreshUrl, apiKey, refreshIntervalMs, children, }: EnvProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
/**
|
|
15
|
+
* `useEnv("KEY")` — provider'ın hydrate ettiği env değerini döner.
|
|
16
|
+
* Yoksa undefined; çağıran fallback verir (`useEnv("X") ?? "default"`).
|
|
17
|
+
*/
|
|
18
|
+
export declare function useEnv(key: string): string | undefined;
|
|
19
|
+
/** Tüm public env'leri Record olarak döner. */
|
|
20
|
+
export declare function useAllEnvs(): Record<string, string>;
|
|
21
|
+
/** Manuel refresh tetikleme (örn. admin "config updated" notification sonrası). */
|
|
22
|
+
export declare function useEnvRefresh(): {
|
|
23
|
+
refresh: () => Promise<void>;
|
|
24
|
+
loading: boolean;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=react.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/vault/react.tsx"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AAgCd,UAAU,gBAAgB;IACxB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kGAAkG;IAClG,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,+DAA+D;IAC/D,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAID,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EAAE,WAAW,EACjB,UAAoC,EACpC,MAAM,EACN,iBAA+C,EAC/C,QAAQ,GACT,EAAE,gBAAgB,2CA4ClB;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGtD;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEnD;AAED,mFAAmF;AACnF,wBAAgB,aAAa,IAAI;IAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAGlF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use client";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.EnvProvider = EnvProvider;
|
|
5
|
+
exports.useEnv = useEnv;
|
|
6
|
+
exports.useAllEnvs = useAllEnvs;
|
|
7
|
+
exports.useEnvRefresh = useEnvRefresh;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
const react_1 = require("react");
|
|
10
|
+
const EnvContext = (0, react_1.createContext)({
|
|
11
|
+
envs: {},
|
|
12
|
+
loading: false,
|
|
13
|
+
refresh: async () => { },
|
|
14
|
+
});
|
|
15
|
+
const DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1000;
|
|
16
|
+
function EnvProvider({ envs: initialEnvs, refreshUrl = "/api/env-vault/public", apiKey, refreshIntervalMs = DEFAULT_REFRESH_INTERVAL_MS, children, }) {
|
|
17
|
+
const [envs, setEnvs] = (0, react_1.useState)(initialEnvs);
|
|
18
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
19
|
+
const effectiveKey = apiKey ??
|
|
20
|
+
(typeof process !== "undefined"
|
|
21
|
+
? process.env?.NEXT_PUBLIC_SENTROY_ENV_API_KEY
|
|
22
|
+
: undefined);
|
|
23
|
+
async function refresh() {
|
|
24
|
+
if (!effectiveKey)
|
|
25
|
+
return; // bootstrap yoksa polling no-op
|
|
26
|
+
setLoading(true);
|
|
27
|
+
try {
|
|
28
|
+
const res = await fetch(refreshUrl, {
|
|
29
|
+
headers: { Authorization: `Bearer ${effectiveKey}` },
|
|
30
|
+
cache: "no-store",
|
|
31
|
+
});
|
|
32
|
+
if (!res.ok)
|
|
33
|
+
return;
|
|
34
|
+
const json = (await res.json());
|
|
35
|
+
const next = {};
|
|
36
|
+
for (const v of json.data?.variables ?? [])
|
|
37
|
+
next[v.key] = v.value;
|
|
38
|
+
setEnvs(next);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// network error — keep previous envs, fail-soft
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
setLoading(false);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
(0, react_1.useEffect)(() => {
|
|
48
|
+
if (!refreshIntervalMs || refreshIntervalMs <= 0)
|
|
49
|
+
return;
|
|
50
|
+
const id = setInterval(refresh, refreshIntervalMs);
|
|
51
|
+
return () => clearInterval(id);
|
|
52
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
53
|
+
}, [refreshIntervalMs, effectiveKey]);
|
|
54
|
+
return ((0, jsx_runtime_1.jsx)(EnvContext.Provider, { value: { envs, loading, refresh }, children: children }));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `useEnv("KEY")` — provider'ın hydrate ettiği env değerini döner.
|
|
58
|
+
* Yoksa undefined; çağıran fallback verir (`useEnv("X") ?? "default"`).
|
|
59
|
+
*/
|
|
60
|
+
function useEnv(key) {
|
|
61
|
+
const ctx = (0, react_1.useContext)(EnvContext);
|
|
62
|
+
return ctx.envs[key];
|
|
63
|
+
}
|
|
64
|
+
/** Tüm public env'leri Record olarak döner. */
|
|
65
|
+
function useAllEnvs() {
|
|
66
|
+
return (0, react_1.useContext)(EnvContext).envs;
|
|
67
|
+
}
|
|
68
|
+
/** Manuel refresh tetikleme (örn. admin "config updated" notification sonrası). */
|
|
69
|
+
function useEnvRefresh() {
|
|
70
|
+
const ctx = (0, react_1.useContext)(EnvContext);
|
|
71
|
+
return { refresh: ctx.refresh, loading: ctx.loading };
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/vault/react.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentroy-co/client-sdk",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "TypeScript SDK for the Sentroy platform —
|
|
3
|
+
"version": "2.9.0",
|
|
4
|
+
"description": "TypeScript SDK + CLI for the Sentroy platform — mail, storage, env vault + React components.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"sentroy": "./bin/sentroy.js"
|
|
9
|
+
},
|
|
7
10
|
"exports": {
|
|
8
11
|
".": {
|
|
9
12
|
"types": "./dist/index.d.ts",
|
|
@@ -24,11 +27,22 @@
|
|
|
24
27
|
"types": "./dist/react/crop/index.d.ts",
|
|
25
28
|
"import": "./dist/react/crop/index.js",
|
|
26
29
|
"require": "./dist/react/crop/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./vault": {
|
|
32
|
+
"types": "./dist/vault/index.d.ts",
|
|
33
|
+
"import": "./dist/vault/index.js",
|
|
34
|
+
"require": "./dist/vault/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./vault/react": {
|
|
37
|
+
"types": "./dist/vault/react.d.ts",
|
|
38
|
+
"import": "./dist/vault/react.js",
|
|
39
|
+
"require": "./dist/vault/react.js"
|
|
27
40
|
}
|
|
28
41
|
},
|
|
29
42
|
"files": [
|
|
30
43
|
"dist",
|
|
31
44
|
"src",
|
|
45
|
+
"bin",
|
|
32
46
|
"AGENTS.md"
|
|
33
47
|
],
|
|
34
48
|
"scripts": {
|
|
@@ -43,7 +57,13 @@
|
|
|
43
57
|
"mail",
|
|
44
58
|
"typescript",
|
|
45
59
|
"react",
|
|
46
|
-
"media-manager"
|
|
60
|
+
"media-manager",
|
|
61
|
+
"env",
|
|
62
|
+
"vault",
|
|
63
|
+
"secrets",
|
|
64
|
+
"config",
|
|
65
|
+
"cli",
|
|
66
|
+
"dotenv"
|
|
47
67
|
],
|
|
48
68
|
"repository": {
|
|
49
69
|
"type": "git",
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal .env parser + serializer used by the CLI.
|
|
3
|
+
*
|
|
4
|
+
* Format conventions (mirrors apps/core/components/admin/env-vault-content.tsx
|
|
5
|
+
* developer mode):
|
|
6
|
+
* - blank line resets pending description/public flag
|
|
7
|
+
* - `# @public` on its own line marks the next variable as browser-readable
|
|
8
|
+
* - `# any other text` becomes the next variable's description
|
|
9
|
+
* - `KEY=value` (unquoted)
|
|
10
|
+
* - `KEY="value with spaces"` (double-quoted; supports \n, \", \\ escapes)
|
|
11
|
+
* - `KEY='single quotes'` (single-quoted; literal)
|
|
12
|
+
* - `export KEY=value` (export prefix stripped)
|
|
13
|
+
*
|
|
14
|
+
* Anything else is reported as a parse error with the offending line number.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface DotenvEntry {
|
|
18
|
+
key: string
|
|
19
|
+
value: string
|
|
20
|
+
public: boolean
|
|
21
|
+
description: string | null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DotenvParseResult {
|
|
25
|
+
entries: DotenvEntry[]
|
|
26
|
+
errors: { line: number; message: string }[]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/
|
|
30
|
+
|
|
31
|
+
export function parseDotenv(text: string): DotenvParseResult {
|
|
32
|
+
const lines = text.split(/\r?\n/)
|
|
33
|
+
const entries: DotenvEntry[] = []
|
|
34
|
+
const errors: DotenvParseResult["errors"] = []
|
|
35
|
+
const seen = new Set<string>()
|
|
36
|
+
|
|
37
|
+
let pendingDescription: string[] = []
|
|
38
|
+
let pendingPublic = false
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < lines.length; i++) {
|
|
41
|
+
const raw = lines[i] ?? ""
|
|
42
|
+
const line = raw.trim()
|
|
43
|
+
|
|
44
|
+
if (line === "") {
|
|
45
|
+
pendingDescription = []
|
|
46
|
+
pendingPublic = false
|
|
47
|
+
continue
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (line.startsWith("#")) {
|
|
51
|
+
const body = line.slice(1).trim()
|
|
52
|
+
if (body === "@public") {
|
|
53
|
+
pendingPublic = true
|
|
54
|
+
} else if (body) {
|
|
55
|
+
pendingDescription.push(body)
|
|
56
|
+
}
|
|
57
|
+
continue
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const work = line.startsWith("export ") ? line.slice(7).trimStart() : line
|
|
61
|
+
const eq = work.indexOf("=")
|
|
62
|
+
if (eq <= 0) {
|
|
63
|
+
errors.push({
|
|
64
|
+
line: i + 1,
|
|
65
|
+
message: "invalid syntax (expected KEY=value)",
|
|
66
|
+
})
|
|
67
|
+
pendingDescription = []
|
|
68
|
+
pendingPublic = false
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const key = work.slice(0, eq).trim()
|
|
73
|
+
let value = work.slice(eq + 1)
|
|
74
|
+
|
|
75
|
+
if (!KEY_PATTERN.test(key)) {
|
|
76
|
+
errors.push({
|
|
77
|
+
line: i + 1,
|
|
78
|
+
message: "key must match [A-Z_][A-Z0-9_]*",
|
|
79
|
+
})
|
|
80
|
+
pendingDescription = []
|
|
81
|
+
pendingPublic = false
|
|
82
|
+
continue
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const trimmed = value.trim()
|
|
86
|
+
if (
|
|
87
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
88
|
+
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
89
|
+
) {
|
|
90
|
+
value = trimmed.slice(1, -1)
|
|
91
|
+
if (trimmed.startsWith('"')) {
|
|
92
|
+
value = value
|
|
93
|
+
.replace(/\\n/g, "\n")
|
|
94
|
+
.replace(/\\"/g, '"')
|
|
95
|
+
.replace(/\\\\/g, "\\")
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
value = trimmed
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (seen.has(key)) {
|
|
102
|
+
errors.push({ line: i + 1, message: `duplicate key ${key}` })
|
|
103
|
+
pendingDescription = []
|
|
104
|
+
pendingPublic = false
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
seen.add(key)
|
|
108
|
+
|
|
109
|
+
entries.push({
|
|
110
|
+
key,
|
|
111
|
+
value,
|
|
112
|
+
public: pendingPublic,
|
|
113
|
+
description:
|
|
114
|
+
pendingDescription.length > 0 ? pendingDescription.join(" ") : null,
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
pendingDescription = []
|
|
118
|
+
pendingPublic = false
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { entries, errors }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Inverse of parseDotenv — emit a .env document that round-trips through it.
|
|
126
|
+
* Quotes values that contain whitespace or shell-special characters.
|
|
127
|
+
*/
|
|
128
|
+
export function serializeDotenv(entries: DotenvEntry[]): string {
|
|
129
|
+
const blocks: string[] = []
|
|
130
|
+
for (const e of entries) {
|
|
131
|
+
const parts: string[] = []
|
|
132
|
+
if (e.description) parts.push(`# ${e.description}`)
|
|
133
|
+
if (e.public) parts.push("# @public")
|
|
134
|
+
const value = e.value
|
|
135
|
+
const needsQuote = /[\s"'#$`\\]/.test(value) || value === ""
|
|
136
|
+
const escaped = needsQuote
|
|
137
|
+
? `"${value
|
|
138
|
+
.replace(/\\/g, "\\\\")
|
|
139
|
+
.replace(/"/g, '\\"')
|
|
140
|
+
.replace(/\n/g, "\\n")}"`
|
|
141
|
+
: value
|
|
142
|
+
parts.push(`${e.key}=${escaped}`)
|
|
143
|
+
blocks.push(parts.join("\n"))
|
|
144
|
+
}
|
|
145
|
+
return blocks.join("\n\n") + (blocks.length > 0 ? "\n" : "")
|
|
146
|
+
}
|