@captainsafia/burrow 1.0.0-preview.0a24dbc → 1.0.0-preview.2be53e0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -7
- package/dist/api.d.ts +71 -8
- package/dist/api.js +140 -70
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ burrow set DATABASE_URL=postgres://localhost/mydb --path ~/projects
|
|
|
30
30
|
### Get a secret
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
burrow get API_KEY
|
|
33
|
+
burrow get API_KEY
|
|
34
34
|
burrow get API_KEY --format json
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -44,8 +44,14 @@ burrow list --format json
|
|
|
44
44
|
### Export to your shell
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
|
+
# Auto-detects your shell (bash, fish, powershell, cmd)
|
|
47
48
|
eval "$(burrow export)"
|
|
48
|
-
|
|
49
|
+
|
|
50
|
+
# Or specify a format explicitly
|
|
51
|
+
burrow export --format fish
|
|
52
|
+
burrow export --format powershell
|
|
53
|
+
burrow export --format dotenv
|
|
54
|
+
burrow export --format json
|
|
49
55
|
```
|
|
50
56
|
|
|
51
57
|
### Block inheritance
|
|
@@ -54,6 +60,14 @@ eval "$(burrow export --format shell)" && npm start
|
|
|
54
60
|
burrow unset API_KEY --path ~/projects/app/tests
|
|
55
61
|
```
|
|
56
62
|
|
|
63
|
+
### Remove a secret
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
burrow remove API_KEY --path ~/projects/app
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Unlike `unset` which blocks inheritance, `remove` deletes the entry entirely, restoring inheritance from parent directories.
|
|
70
|
+
|
|
57
71
|
## How It Works
|
|
58
72
|
|
|
59
73
|
Secrets are stored in your user profile:
|
|
@@ -76,13 +90,26 @@ import { BurrowClient } from '@captainsafia/burrow';
|
|
|
76
90
|
|
|
77
91
|
const client = new BurrowClient();
|
|
78
92
|
|
|
79
|
-
|
|
93
|
+
try {
|
|
94
|
+
await client.set('API_KEY', 'secret123', { path: '/my/project' });
|
|
95
|
+
|
|
96
|
+
const secret = await client.get('API_KEY', { cwd: '/my/project/subdir' });
|
|
97
|
+
console.log(secret?.value); // 'secret123'
|
|
98
|
+
console.log(secret?.sourcePath); // '/my/project'
|
|
80
99
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
const allSecrets = await client.list({ cwd: '/my/project' });
|
|
101
|
+
} finally {
|
|
102
|
+
client.close(); // Clean up database connection
|
|
103
|
+
}
|
|
104
|
+
```
|
|
84
105
|
|
|
85
|
-
|
|
106
|
+
Or with TypeScript's `using` declarations for automatic cleanup:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
{
|
|
110
|
+
using client = new BurrowClient();
|
|
111
|
+
await client.set('API_KEY', 'secret123');
|
|
112
|
+
} // Automatically cleaned up
|
|
86
113
|
```
|
|
87
114
|
|
|
88
115
|
## Contributing
|
package/dist/api.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface ResolvedSecret {
|
|
|
5
5
|
value: string;
|
|
6
6
|
sourcePath: string;
|
|
7
7
|
}
|
|
8
|
-
export type ExportFormat = "shell" | "dotenv" | "json";
|
|
8
|
+
export type ExportFormat = "shell" | "bash" | "fish" | "powershell" | "cmd" | "dotenv" | "json";
|
|
9
9
|
/**
|
|
10
10
|
* Configuration options for creating a BurrowClient instance.
|
|
11
11
|
*/
|
|
@@ -21,7 +21,7 @@ export interface BurrowClientOptions {
|
|
|
21
21
|
configDir?: string;
|
|
22
22
|
/**
|
|
23
23
|
* Custom filename for the secrets store.
|
|
24
|
-
* Defaults to `store.
|
|
24
|
+
* Defaults to `store.db`.
|
|
25
25
|
*/
|
|
26
26
|
storeFileName?: string;
|
|
27
27
|
/**
|
|
@@ -72,6 +72,16 @@ export interface BlockOptions {
|
|
|
72
72
|
*/
|
|
73
73
|
path?: string;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Options for the `remove` method.
|
|
77
|
+
*/
|
|
78
|
+
export interface RemoveOptions {
|
|
79
|
+
/**
|
|
80
|
+
* Directory path to remove the secret from.
|
|
81
|
+
* Defaults to the current working directory.
|
|
82
|
+
*/
|
|
83
|
+
path?: string;
|
|
84
|
+
}
|
|
75
85
|
/**
|
|
76
86
|
* Options for the `export` method.
|
|
77
87
|
*/
|
|
@@ -89,10 +99,6 @@ export interface ExportOptions {
|
|
|
89
99
|
* - `json`: Exports as a JSON object
|
|
90
100
|
*/
|
|
91
101
|
format?: ExportFormat;
|
|
92
|
-
/**
|
|
93
|
-
* Whether to show actual values (currently unused, reserved for future use).
|
|
94
|
-
*/
|
|
95
|
-
showValues?: boolean;
|
|
96
102
|
/**
|
|
97
103
|
* Whether to include source paths in JSON output.
|
|
98
104
|
* When true, JSON output includes `{ key: { value, sourcePath } }` format.
|
|
@@ -141,7 +147,7 @@ export declare class BurrowClient {
|
|
|
141
147
|
* The secret will be available to the specified directory and all its
|
|
142
148
|
* subdirectories, unless overridden or blocked at a deeper level.
|
|
143
149
|
*
|
|
144
|
-
* @param key - Environment variable name. Must match `^[A-
|
|
150
|
+
* @param key - Environment variable name. Must match `^[A-Za-z_][A-Za-z0-9_]*$`
|
|
145
151
|
* @param value - Secret value to store
|
|
146
152
|
* @param options - Set options including target path
|
|
147
153
|
* @throws Error if the key format is invalid
|
|
@@ -205,7 +211,7 @@ export declare class BurrowClient {
|
|
|
205
211
|
*
|
|
206
212
|
* A blocked key can be re-enabled by calling `set` at the same or deeper path.
|
|
207
213
|
*
|
|
208
|
-
* @param key - Environment variable name to block. Must match `^[A-
|
|
214
|
+
* @param key - Environment variable name to block. Must match `^[A-Za-z_][A-Za-z0-9_]*$`
|
|
209
215
|
* @param options - Block options including target path
|
|
210
216
|
* @throws Error if the key format is invalid
|
|
211
217
|
*
|
|
@@ -223,6 +229,33 @@ export declare class BurrowClient {
|
|
|
223
229
|
* ```
|
|
224
230
|
*/
|
|
225
231
|
block(key: string, options?: BlockOptions): Promise<void>;
|
|
232
|
+
/**
|
|
233
|
+
* Removes a secret entry entirely from the specified path.
|
|
234
|
+
*
|
|
235
|
+
* Unlike `block`, which creates a tombstone to prevent inheritance,
|
|
236
|
+
* `remove` completely deletes the secret entry. After removal, the key
|
|
237
|
+
* may still be inherited from parent directories if defined there.
|
|
238
|
+
*
|
|
239
|
+
* @param key - Environment variable name to remove. Must match `^[A-Za-z_][A-Za-z0-9_]*$`
|
|
240
|
+
* @param options - Remove options including target path
|
|
241
|
+
* @returns true if the secret was found and removed, false if it didn't exist
|
|
242
|
+
* @throws Error if the key format is invalid
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* // Set a secret
|
|
247
|
+
* await client.set('API_KEY', 'secret', { path: '/projects/myapp' });
|
|
248
|
+
*
|
|
249
|
+
* // Remove it entirely
|
|
250
|
+
* const removed = await client.remove('API_KEY', { path: '/projects/myapp' });
|
|
251
|
+
* console.log(removed); // true
|
|
252
|
+
*
|
|
253
|
+
* // Trying to remove again returns false
|
|
254
|
+
* const removedAgain = await client.remove('API_KEY', { path: '/projects/myapp' });
|
|
255
|
+
* console.log(removedAgain); // false
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
remove(key: string, options?: RemoveOptions): Promise<boolean>;
|
|
226
259
|
/**
|
|
227
260
|
* Exports resolved secrets in various formats.
|
|
228
261
|
*
|
|
@@ -274,6 +307,36 @@ export declare class BurrowClient {
|
|
|
274
307
|
* ```
|
|
275
308
|
*/
|
|
276
309
|
resolve(cwd?: string): Promise<Map<string, ResolvedSecret>>;
|
|
310
|
+
/**
|
|
311
|
+
* Closes the database connection and releases resources.
|
|
312
|
+
* After calling this method, the client instance should not be used.
|
|
313
|
+
*
|
|
314
|
+
* This method is safe to call multiple times.
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```typescript
|
|
318
|
+
* const client = new BurrowClient();
|
|
319
|
+
* try {
|
|
320
|
+
* await client.set('API_KEY', 'value');
|
|
321
|
+
* // ... do work
|
|
322
|
+
* } finally {
|
|
323
|
+
* client.close();
|
|
324
|
+
* }
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
close(): void;
|
|
328
|
+
/**
|
|
329
|
+
* Allows using the BurrowClient with `using` declarations for automatic cleanup.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* {
|
|
334
|
+
* using client = new BurrowClient();
|
|
335
|
+
* await client.set('API_KEY', 'value');
|
|
336
|
+
* } // client.close() is called automatically
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
[Symbol.dispose](): void;
|
|
277
340
|
}
|
|
278
341
|
/**
|
|
279
342
|
* Creates a new BurrowClient instance.
|
package/dist/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/storage/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import { Database } from "bun:sqlite";
|
|
3
|
+
import { chmod, mkdir } from "node:fs/promises";
|
|
3
4
|
import { join as join2 } from "node:path";
|
|
4
|
-
import { randomBytes } from "node:crypto";
|
|
5
5
|
|
|
6
6
|
// src/platform/index.ts
|
|
7
7
|
import { homedir } from "node:os";
|
|
@@ -44,18 +44,12 @@ function isWindows() {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// src/storage/index.ts
|
|
47
|
-
var
|
|
48
|
-
var DEFAULT_STORE_FILE = "store.json";
|
|
49
|
-
function createEmptyStore() {
|
|
50
|
-
return {
|
|
51
|
-
version: STORE_VERSION,
|
|
52
|
-
paths: {}
|
|
53
|
-
};
|
|
54
|
-
}
|
|
47
|
+
var DEFAULT_STORE_FILE = "store.db";
|
|
55
48
|
|
|
56
49
|
class Storage {
|
|
57
50
|
configDir;
|
|
58
51
|
storeFileName;
|
|
52
|
+
db = null;
|
|
59
53
|
constructor(options = {}) {
|
|
60
54
|
this.configDir = options.configDir ?? getConfigDir();
|
|
61
55
|
this.storeFileName = options.storeFileName ?? DEFAULT_STORE_FILE;
|
|
@@ -63,68 +57,97 @@ class Storage {
|
|
|
63
57
|
get storePath() {
|
|
64
58
|
return join2(this.configDir, this.storeFileName);
|
|
65
59
|
}
|
|
66
|
-
async
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const store = JSON.parse(content);
|
|
70
|
-
if (store.version !== STORE_VERSION) {
|
|
71
|
-
throw new Error(`Unsupported store version: ${store.version}. Expected: ${STORE_VERSION}`);
|
|
72
|
-
}
|
|
73
|
-
return store;
|
|
74
|
-
} catch (error) {
|
|
75
|
-
if (error.code === "ENOENT") {
|
|
76
|
-
return createEmptyStore();
|
|
77
|
-
}
|
|
78
|
-
throw error;
|
|
60
|
+
async ensureDb() {
|
|
61
|
+
if (this.db) {
|
|
62
|
+
return this.db;
|
|
79
63
|
}
|
|
80
|
-
}
|
|
81
|
-
async write(store) {
|
|
82
64
|
await mkdir(this.configDir, { recursive: true });
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
await
|
|
89
|
-
await rename(tempPath, this.storePath);
|
|
90
|
-
} catch (error) {
|
|
91
|
-
try {
|
|
92
|
-
await unlink(tempPath);
|
|
93
|
-
} catch {}
|
|
94
|
-
throw error;
|
|
65
|
+
if (!isWindows()) {
|
|
66
|
+
await chmod(this.configDir, 448);
|
|
67
|
+
}
|
|
68
|
+
this.db = new Database(this.storePath);
|
|
69
|
+
if (!isWindows()) {
|
|
70
|
+
await chmod(this.storePath, 384);
|
|
95
71
|
}
|
|
72
|
+
this.db.run("PRAGMA journal_mode = WAL");
|
|
73
|
+
this.db.run(`
|
|
74
|
+
CREATE TABLE IF NOT EXISTS secrets (
|
|
75
|
+
path TEXT NOT NULL,
|
|
76
|
+
key TEXT NOT NULL,
|
|
77
|
+
value TEXT,
|
|
78
|
+
updated_at TEXT NOT NULL,
|
|
79
|
+
PRIMARY KEY (path, key)
|
|
80
|
+
)
|
|
81
|
+
`);
|
|
82
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_secrets_path ON secrets (path)");
|
|
83
|
+
const versionResult = this.db.query("PRAGMA user_version").get();
|
|
84
|
+
const currentVersion = versionResult?.user_version ?? 0;
|
|
85
|
+
if (currentVersion === 0) {
|
|
86
|
+
this.db.run("PRAGMA user_version = 1");
|
|
87
|
+
} else if (currentVersion !== 1) {
|
|
88
|
+
throw new Error(`Unsupported store version: ${currentVersion}. Expected: 1`);
|
|
89
|
+
}
|
|
90
|
+
return this.db;
|
|
96
91
|
}
|
|
97
92
|
async setSecret(canonicalPath, key, value) {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
93
|
+
const db = await this.ensureDb();
|
|
94
|
+
const updatedAt = new Date().toISOString();
|
|
95
|
+
db.query(`
|
|
96
|
+
INSERT INTO secrets (path, key, value, updated_at)
|
|
97
|
+
VALUES (?, ?, ?, ?)
|
|
98
|
+
ON CONFLICT(path, key) DO UPDATE SET
|
|
99
|
+
value = excluded.value,
|
|
100
|
+
updated_at = excluded.updated_at
|
|
101
|
+
`).run(canonicalPath, key, value, updatedAt);
|
|
107
102
|
}
|
|
108
103
|
async getPathSecrets(canonicalPath) {
|
|
109
|
-
const
|
|
110
|
-
|
|
104
|
+
const db = await this.ensureDb();
|
|
105
|
+
const rows = db.query("SELECT key, value, updated_at FROM secrets WHERE path = ?").all(canonicalPath);
|
|
106
|
+
if (rows.length === 0) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const secrets = {};
|
|
110
|
+
for (const row of rows) {
|
|
111
|
+
secrets[row.key] = {
|
|
112
|
+
value: row.value,
|
|
113
|
+
updatedAt: row.updated_at
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return secrets;
|
|
111
117
|
}
|
|
112
118
|
async getAllPaths() {
|
|
113
|
-
const
|
|
114
|
-
|
|
119
|
+
const db = await this.ensureDb();
|
|
120
|
+
const rows = db.query("SELECT DISTINCT path FROM secrets").all();
|
|
121
|
+
return rows.map((row) => row.path);
|
|
122
|
+
}
|
|
123
|
+
async getAncestorPaths(canonicalPath) {
|
|
124
|
+
const db = await this.ensureDb();
|
|
125
|
+
let rows;
|
|
126
|
+
if (isWindows()) {
|
|
127
|
+
rows = db.query("SELECT DISTINCT path FROM secrets WHERE ? = path OR ? LIKE path || '\\' || '%' OR (length(path) = 3 AND path LIKE '_:\\' AND ? LIKE path || '%')").all(canonicalPath, canonicalPath, canonicalPath);
|
|
128
|
+
} else {
|
|
129
|
+
rows = db.query("SELECT DISTINCT path FROM secrets WHERE ? = path OR ? LIKE path || '/' || '%' OR path = '/'").all(canonicalPath, canonicalPath);
|
|
130
|
+
}
|
|
131
|
+
return rows.map((row) => row.path);
|
|
115
132
|
}
|
|
116
133
|
async removeKey(canonicalPath, key) {
|
|
117
|
-
const
|
|
118
|
-
|
|
134
|
+
const db = await this.ensureDb();
|
|
135
|
+
const existing = db.query("SELECT path FROM secrets WHERE path = ? AND key = ?").get(canonicalPath, key);
|
|
136
|
+
if (!existing) {
|
|
119
137
|
return false;
|
|
120
138
|
}
|
|
121
|
-
|
|
122
|
-
if (Object.keys(store.paths[canonicalPath]).length === 0) {
|
|
123
|
-
delete store.paths[canonicalPath];
|
|
124
|
-
}
|
|
125
|
-
await this.write(store);
|
|
139
|
+
db.query("DELETE FROM secrets WHERE path = ? AND key = ?").run(canonicalPath, key);
|
|
126
140
|
return true;
|
|
127
141
|
}
|
|
142
|
+
close() {
|
|
143
|
+
if (this.db) {
|
|
144
|
+
this.db.close();
|
|
145
|
+
this.db = null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
[Symbol.dispose]() {
|
|
149
|
+
this.close();
|
|
150
|
+
}
|
|
128
151
|
}
|
|
129
152
|
|
|
130
153
|
// src/core/path.ts
|
|
@@ -161,16 +184,6 @@ function normalizePath(path) {
|
|
|
161
184
|
}
|
|
162
185
|
return normalized;
|
|
163
186
|
}
|
|
164
|
-
function isAncestorOf(ancestorPath, descendantPath) {
|
|
165
|
-
if (ancestorPath === descendantPath) {
|
|
166
|
-
return true;
|
|
167
|
-
}
|
|
168
|
-
const ancestorWithSep = ancestorPath.endsWith(sep) ? ancestorPath : ancestorPath + sep;
|
|
169
|
-
if (isWindows()) {
|
|
170
|
-
return descendantPath.toLowerCase().startsWith(ancestorWithSep.toLowerCase());
|
|
171
|
-
}
|
|
172
|
-
return descendantPath.startsWith(ancestorWithSep);
|
|
173
|
-
}
|
|
174
187
|
// src/core/resolver.ts
|
|
175
188
|
class Resolver {
|
|
176
189
|
storage;
|
|
@@ -184,8 +197,7 @@ class Resolver {
|
|
|
184
197
|
async resolve(cwd) {
|
|
185
198
|
const workingDir = cwd ?? process.cwd();
|
|
186
199
|
const canonicalCwd = await canonicalize(workingDir, this.pathOptions);
|
|
187
|
-
const
|
|
188
|
-
const ancestorPaths = allPaths.filter((storedPath) => isAncestorOf(storedPath, canonicalCwd));
|
|
200
|
+
const ancestorPaths = await this.storage.getAncestorPaths(canonicalCwd);
|
|
189
201
|
ancestorPaths.sort((a, b) => {
|
|
190
202
|
if (isWindows()) {
|
|
191
203
|
return a.toLowerCase().localeCompare(b.toLowerCase());
|
|
@@ -224,7 +236,7 @@ class Resolver {
|
|
|
224
236
|
}
|
|
225
237
|
}
|
|
226
238
|
// src/core/formatter.ts
|
|
227
|
-
var ENV_KEY_PATTERN = /^[A-
|
|
239
|
+
var ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
228
240
|
function validateEnvKey(key) {
|
|
229
241
|
return ENV_KEY_PATTERN.test(key);
|
|
230
242
|
}
|
|
@@ -239,6 +251,9 @@ function escapeShellValue(value) {
|
|
|
239
251
|
function escapeDoubleQuotes(value) {
|
|
240
252
|
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
241
253
|
}
|
|
254
|
+
function escapePowerShellValue(value) {
|
|
255
|
+
return value.replace(/'/g, "''");
|
|
256
|
+
}
|
|
242
257
|
function formatShell(secrets) {
|
|
243
258
|
const lines = [];
|
|
244
259
|
const sortedKeys = Array.from(secrets.keys()).sort();
|
|
@@ -251,6 +266,42 @@ function formatShell(secrets) {
|
|
|
251
266
|
return lines.join(`
|
|
252
267
|
`);
|
|
253
268
|
}
|
|
269
|
+
function formatFish(secrets) {
|
|
270
|
+
const lines = [];
|
|
271
|
+
const sortedKeys = Array.from(secrets.keys()).sort();
|
|
272
|
+
for (const key of sortedKeys) {
|
|
273
|
+
const secret = secrets.get(key);
|
|
274
|
+
assertValidEnvKey(key);
|
|
275
|
+
const escapedValue = escapeShellValue(secret.value);
|
|
276
|
+
lines.push(`set -gx ${key} '${escapedValue}'`);
|
|
277
|
+
}
|
|
278
|
+
return lines.join(`
|
|
279
|
+
`);
|
|
280
|
+
}
|
|
281
|
+
function formatPowerShell(secrets) {
|
|
282
|
+
const lines = [];
|
|
283
|
+
const sortedKeys = Array.from(secrets.keys()).sort();
|
|
284
|
+
for (const key of sortedKeys) {
|
|
285
|
+
const secret = secrets.get(key);
|
|
286
|
+
assertValidEnvKey(key);
|
|
287
|
+
const escapedValue = escapePowerShellValue(secret.value);
|
|
288
|
+
lines.push(`$env:${key} = '${escapedValue}'`);
|
|
289
|
+
}
|
|
290
|
+
return lines.join(`
|
|
291
|
+
`);
|
|
292
|
+
}
|
|
293
|
+
function formatCmd(secrets) {
|
|
294
|
+
const lines = [];
|
|
295
|
+
const sortedKeys = Array.from(secrets.keys()).sort();
|
|
296
|
+
for (const key of sortedKeys) {
|
|
297
|
+
const secret = secrets.get(key);
|
|
298
|
+
assertValidEnvKey(key);
|
|
299
|
+
const escapedValue = secret.value.replace(/([&|<>^])/g, "^$1");
|
|
300
|
+
lines.push(`set ${key}=${escapedValue}`);
|
|
301
|
+
}
|
|
302
|
+
return lines.join(`
|
|
303
|
+
`);
|
|
304
|
+
}
|
|
254
305
|
function formatDotenv(secrets) {
|
|
255
306
|
const lines = [];
|
|
256
307
|
const sortedKeys = Array.from(secrets.keys()).sort();
|
|
@@ -288,7 +339,14 @@ function formatJson(secrets, includeSources = false) {
|
|
|
288
339
|
function format(secrets, fmt, options = {}) {
|
|
289
340
|
switch (fmt) {
|
|
290
341
|
case "shell":
|
|
342
|
+
case "bash":
|
|
291
343
|
return formatShell(secrets);
|
|
344
|
+
case "fish":
|
|
345
|
+
return formatFish(secrets);
|
|
346
|
+
case "powershell":
|
|
347
|
+
return formatPowerShell(secrets);
|
|
348
|
+
case "cmd":
|
|
349
|
+
return formatCmd(secrets);
|
|
292
350
|
case "dotenv":
|
|
293
351
|
return formatDotenv(secrets);
|
|
294
352
|
case "json":
|
|
@@ -333,6 +391,12 @@ class BurrowClient {
|
|
|
333
391
|
const canonicalPath = await canonicalize(targetPath, this.pathOptions);
|
|
334
392
|
await this.storage.setSecret(canonicalPath, key, null);
|
|
335
393
|
}
|
|
394
|
+
async remove(key, options = {}) {
|
|
395
|
+
assertValidEnvKey(key);
|
|
396
|
+
const targetPath = options.path ?? process.cwd();
|
|
397
|
+
const canonicalPath = await canonicalize(targetPath, this.pathOptions);
|
|
398
|
+
return this.storage.removeKey(canonicalPath, key);
|
|
399
|
+
}
|
|
336
400
|
async export(options = {}) {
|
|
337
401
|
const secrets = await this.resolver.resolve(options.cwd);
|
|
338
402
|
const fmt = options.format ?? "shell";
|
|
@@ -343,6 +407,12 @@ class BurrowClient {
|
|
|
343
407
|
async resolve(cwd) {
|
|
344
408
|
return this.resolver.resolve(cwd);
|
|
345
409
|
}
|
|
410
|
+
close() {
|
|
411
|
+
this.storage.close();
|
|
412
|
+
}
|
|
413
|
+
[Symbol.dispose]() {
|
|
414
|
+
this.close();
|
|
415
|
+
}
|
|
346
416
|
}
|
|
347
417
|
function createClient(options) {
|
|
348
418
|
return new BurrowClient(options);
|