bun-types 1.2.21-canary.20250822T140708 → 1.2.21-canary.20250824T140615
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/bun.d.ts +285 -0
- package/docs/api/fetch.md +1 -1
- package/docs/api/secrets.md +319 -0
- package/docs/api/spawn.md +1 -1
- package/docs/api/yaml.md +459 -0
- package/docs/bundler/executables.md +107 -4
- package/docs/bundler/index.md +1 -1
- package/docs/bundler/loaders.md +50 -1
- package/docs/cli/pm.md +1 -1
- package/docs/cli/publish.md +1 -1
- package/docs/guides/ecosystem/nuxt.md +1 -1
- package/docs/guides/install/add-peer.md +2 -2
- package/docs/guides/install/from-npm-install-to-bun-install.md +1 -1
- package/docs/guides/runtime/import-yaml.md +76 -0
- package/docs/guides/test/run-tests.md +3 -3
- package/docs/guides/test/snapshot.md +3 -3
- package/docs/guides/test/update-snapshots.md +1 -1
- package/docs/guides/util/version.md +1 -1
- package/docs/installation.md +4 -4
- package/docs/runtime/bun-apis.md +2 -2
- package/docs/runtime/bunfig.md +1 -0
- package/docs/runtime/debugger.md +3 -3
- package/docs/runtime/index.md +5 -2
- package/docs/runtime/loaders.md +5 -2
- package/docs/test/dom.md +1 -1
- package/extensions.d.ts +10 -0
- package/package.json +1 -1
package/bun.d.ts
CHANGED
|
@@ -1844,6 +1844,10 @@ declare module "bun" {
|
|
|
1844
1844
|
hideConsole?: boolean;
|
|
1845
1845
|
icon?: string;
|
|
1846
1846
|
title?: string;
|
|
1847
|
+
publisher?: string;
|
|
1848
|
+
version?: string;
|
|
1849
|
+
description?: string;
|
|
1850
|
+
copyright?: string;
|
|
1847
1851
|
};
|
|
1848
1852
|
}
|
|
1849
1853
|
|
|
@@ -2120,6 +2124,287 @@ declare module "bun" {
|
|
|
2120
2124
|
): string;
|
|
2121
2125
|
};
|
|
2122
2126
|
|
|
2127
|
+
/**
|
|
2128
|
+
* Securely store and retrieve sensitive credentials using the operating system's native credential storage.
|
|
2129
|
+
*
|
|
2130
|
+
* Uses platform-specific secure storage:
|
|
2131
|
+
* - **macOS**: Keychain Services
|
|
2132
|
+
* - **Linux**: libsecret (GNOME Keyring, KWallet, etc.)
|
|
2133
|
+
* - **Windows**: Windows Credential Manager
|
|
2134
|
+
*
|
|
2135
|
+
* @category Security
|
|
2136
|
+
*
|
|
2137
|
+
* @example
|
|
2138
|
+
* ```ts
|
|
2139
|
+
* import { secrets } from "bun";
|
|
2140
|
+
*
|
|
2141
|
+
* // Store a credential
|
|
2142
|
+
* await secrets.set({
|
|
2143
|
+
* service: "my-cli-tool",
|
|
2144
|
+
* name: "github-token",
|
|
2145
|
+
* value: "ghp_xxxxxxxxxxxxxxxxxxxx"
|
|
2146
|
+
* });
|
|
2147
|
+
*
|
|
2148
|
+
* // Retrieve a credential
|
|
2149
|
+
* const token = await secrets.get({
|
|
2150
|
+
* service: "my-cli-tool",
|
|
2151
|
+
* name: "github-token"
|
|
2152
|
+
* });
|
|
2153
|
+
*
|
|
2154
|
+
* if (token) {
|
|
2155
|
+
* console.log("Token found:", token);
|
|
2156
|
+
* } else {
|
|
2157
|
+
* console.log("Token not found");
|
|
2158
|
+
* }
|
|
2159
|
+
*
|
|
2160
|
+
* // Delete a credential
|
|
2161
|
+
* const deleted = await secrets.delete({
|
|
2162
|
+
* service: "my-cli-tool",
|
|
2163
|
+
* name: "github-token"
|
|
2164
|
+
* });
|
|
2165
|
+
* console.log("Deleted:", deleted); // true if deleted, false if not found
|
|
2166
|
+
* ```
|
|
2167
|
+
*
|
|
2168
|
+
* @example
|
|
2169
|
+
* ```ts
|
|
2170
|
+
* // Replace plaintext config files
|
|
2171
|
+
* import { secrets } from "bun";
|
|
2172
|
+
*
|
|
2173
|
+
* // Instead of storing in ~/.npmrc
|
|
2174
|
+
* await secrets.set({
|
|
2175
|
+
* service: "npm-registry",
|
|
2176
|
+
* name: "https://registry.npmjs.org",
|
|
2177
|
+
* value: "npm_xxxxxxxxxxxxxxxxxxxx"
|
|
2178
|
+
* });
|
|
2179
|
+
*
|
|
2180
|
+
* // Instead of storing in ~/.aws/credentials
|
|
2181
|
+
* await secrets.set({
|
|
2182
|
+
* service: "aws-cli",
|
|
2183
|
+
* name: "default",
|
|
2184
|
+
* value: process.env.AWS_SECRET_ACCESS_KEY
|
|
2185
|
+
* });
|
|
2186
|
+
*
|
|
2187
|
+
* // Load at runtime with fallback
|
|
2188
|
+
* const apiKey = await secrets.get({
|
|
2189
|
+
* service: "my-app",
|
|
2190
|
+
* name: "api-key"
|
|
2191
|
+
* }) || process.env.API_KEY;
|
|
2192
|
+
* ```
|
|
2193
|
+
*/
|
|
2194
|
+
const secrets: {
|
|
2195
|
+
/**
|
|
2196
|
+
* Retrieve a stored credential from the operating system's secure storage.
|
|
2197
|
+
*
|
|
2198
|
+
* @param options - The service and name identifying the credential
|
|
2199
|
+
* @returns The stored credential value, or null if not found
|
|
2200
|
+
*
|
|
2201
|
+
* @example
|
|
2202
|
+
* ```ts
|
|
2203
|
+
* const password = await Bun.secrets.get({
|
|
2204
|
+
* service: "my-database",
|
|
2205
|
+
* name: "admin"
|
|
2206
|
+
* });
|
|
2207
|
+
*
|
|
2208
|
+
* if (password) {
|
|
2209
|
+
* await connectToDatabase(password);
|
|
2210
|
+
* }
|
|
2211
|
+
* ```
|
|
2212
|
+
*
|
|
2213
|
+
* @example
|
|
2214
|
+
* ```ts
|
|
2215
|
+
* // Check multiple possible locations
|
|
2216
|
+
* const token =
|
|
2217
|
+
* await Bun.secrets.get({ service: "github", name: "token" }) ||
|
|
2218
|
+
* await Bun.secrets.get({ service: "gh-cli", name: "github.com" }) ||
|
|
2219
|
+
* process.env.GITHUB_TOKEN;
|
|
2220
|
+
* ```
|
|
2221
|
+
*/
|
|
2222
|
+
get(options: {
|
|
2223
|
+
/**
|
|
2224
|
+
* The service or application name.
|
|
2225
|
+
*
|
|
2226
|
+
* Use a unique identifier for your application to avoid conflicts.
|
|
2227
|
+
* Consider using reverse domain notation for production apps (e.g., "com.example.myapp").
|
|
2228
|
+
*/
|
|
2229
|
+
service: string;
|
|
2230
|
+
|
|
2231
|
+
/**
|
|
2232
|
+
* The account name, username, or resource identifier.
|
|
2233
|
+
*
|
|
2234
|
+
* This identifies the specific credential within the service.
|
|
2235
|
+
* Common patterns include usernames, email addresses, or resource URLs.
|
|
2236
|
+
*/
|
|
2237
|
+
name: string;
|
|
2238
|
+
}): Promise<string | null>;
|
|
2239
|
+
|
|
2240
|
+
/**
|
|
2241
|
+
* Store or update a credential in the operating system's secure storage.
|
|
2242
|
+
*
|
|
2243
|
+
* If a credential already exists for the given service/name combination, it will be replaced.
|
|
2244
|
+
* The credential is encrypted by the operating system and only accessible to the current user.
|
|
2245
|
+
*
|
|
2246
|
+
* @param options - The service and name identifying the credential
|
|
2247
|
+
* @param value - The secret value to store (e.g., password, API key, token)
|
|
2248
|
+
*
|
|
2249
|
+
* @example
|
|
2250
|
+
* ```ts
|
|
2251
|
+
* // Store an API key
|
|
2252
|
+
* await Bun.secrets.set({
|
|
2253
|
+
* service: "openai-api",
|
|
2254
|
+
* name: "production",
|
|
2255
|
+
* value: "sk-proj-xxxxxxxxxxxxxxxxxxxx"
|
|
2256
|
+
* });
|
|
2257
|
+
* ```
|
|
2258
|
+
*
|
|
2259
|
+
* @example
|
|
2260
|
+
* ```ts
|
|
2261
|
+
* // Update an existing credential
|
|
2262
|
+
* const newPassword = generateSecurePassword();
|
|
2263
|
+
* await Bun.secrets.set({
|
|
2264
|
+
* service: "email-server",
|
|
2265
|
+
* name: "admin@example.com",
|
|
2266
|
+
* value: newPassword
|
|
2267
|
+
* });
|
|
2268
|
+
* ```
|
|
2269
|
+
*
|
|
2270
|
+
* @example
|
|
2271
|
+
* ```ts
|
|
2272
|
+
* // Store credentials from environment variables
|
|
2273
|
+
* if (process.env.DATABASE_PASSWORD) {
|
|
2274
|
+
* await Bun.secrets.set({
|
|
2275
|
+
* service: "postgres",
|
|
2276
|
+
* name: "production",
|
|
2277
|
+
* value: process.env.DATABASE_PASSWORD
|
|
2278
|
+
* });
|
|
2279
|
+
* delete process.env.DATABASE_PASSWORD; // Remove from memory
|
|
2280
|
+
* }
|
|
2281
|
+
* ```
|
|
2282
|
+
*
|
|
2283
|
+
* @example
|
|
2284
|
+
* ```ts
|
|
2285
|
+
* // Delete a credential using empty string (equivalent to delete())
|
|
2286
|
+
* await Bun.secrets.set({
|
|
2287
|
+
* service: "my-service",
|
|
2288
|
+
* name: "api-key",
|
|
2289
|
+
* value: "" // Empty string deletes the credential
|
|
2290
|
+
* });
|
|
2291
|
+
* ```
|
|
2292
|
+
*
|
|
2293
|
+
* @example
|
|
2294
|
+
* ```ts
|
|
2295
|
+
* // Store credential with unrestricted access for CI environments
|
|
2296
|
+
* await Bun.secrets.set({
|
|
2297
|
+
* service: "github-actions",
|
|
2298
|
+
* name: "deploy-token",
|
|
2299
|
+
* value: process.env.DEPLOY_TOKEN,
|
|
2300
|
+
* allowUnrestrictedAccess: true // Allows access without user interaction on macOS
|
|
2301
|
+
* });
|
|
2302
|
+
* ```
|
|
2303
|
+
*/
|
|
2304
|
+
set(options: {
|
|
2305
|
+
/**
|
|
2306
|
+
* The service or application name.
|
|
2307
|
+
*
|
|
2308
|
+
* Use a unique identifier for your application to avoid conflicts.
|
|
2309
|
+
* Consider using reverse domain notation for production apps (e.g., "com.example.myapp").
|
|
2310
|
+
*/
|
|
2311
|
+
service: string;
|
|
2312
|
+
|
|
2313
|
+
/**
|
|
2314
|
+
* The account name, username, or resource identifier.
|
|
2315
|
+
*
|
|
2316
|
+
* This identifies the specific credential within the service.
|
|
2317
|
+
* Common patterns include usernames, email addresses, or resource URLs.
|
|
2318
|
+
*/
|
|
2319
|
+
name: string;
|
|
2320
|
+
|
|
2321
|
+
/**
|
|
2322
|
+
* The secret value to store.
|
|
2323
|
+
*
|
|
2324
|
+
* This should be a sensitive credential like a password, API key, or token.
|
|
2325
|
+
* The value is encrypted by the operating system before storage.
|
|
2326
|
+
*
|
|
2327
|
+
* Note: To delete a credential, use the delete() method or pass an empty string.
|
|
2328
|
+
* An empty string value will delete the credential if it exists.
|
|
2329
|
+
*/
|
|
2330
|
+
value: string;
|
|
2331
|
+
|
|
2332
|
+
/**
|
|
2333
|
+
* Allow unrestricted access to stored credentials on macOS.
|
|
2334
|
+
*
|
|
2335
|
+
* When true, allows all applications to access this keychain item without user interaction.
|
|
2336
|
+
* This is useful for CI environments but reduces security.
|
|
2337
|
+
*
|
|
2338
|
+
* @default false
|
|
2339
|
+
* @platform macOS - Only affects macOS keychain behavior. Ignored on other platforms.
|
|
2340
|
+
*/
|
|
2341
|
+
allowUnrestrictedAccess?: boolean;
|
|
2342
|
+
}): Promise<void>;
|
|
2343
|
+
|
|
2344
|
+
/**
|
|
2345
|
+
* Delete a stored credential from the operating system's secure storage.
|
|
2346
|
+
*
|
|
2347
|
+
* @param options - The service and name identifying the credential
|
|
2348
|
+
* @returns true if a credential was deleted, false if not found
|
|
2349
|
+
*
|
|
2350
|
+
* @example
|
|
2351
|
+
* ```ts
|
|
2352
|
+
* // Delete a single credential
|
|
2353
|
+
* const deleted = await Bun.secrets.delete({
|
|
2354
|
+
* service: "my-app",
|
|
2355
|
+
* name: "api-key"
|
|
2356
|
+
* });
|
|
2357
|
+
*
|
|
2358
|
+
* if (deleted) {
|
|
2359
|
+
* console.log("Credential removed successfully");
|
|
2360
|
+
* } else {
|
|
2361
|
+
* console.log("Credential was not found");
|
|
2362
|
+
* }
|
|
2363
|
+
* ```
|
|
2364
|
+
*
|
|
2365
|
+
* @example
|
|
2366
|
+
* ```ts
|
|
2367
|
+
* // Clean up multiple credentials
|
|
2368
|
+
* const services = ["github", "npm", "docker"];
|
|
2369
|
+
* for (const service of services) {
|
|
2370
|
+
* await Bun.secrets.delete({
|
|
2371
|
+
* service,
|
|
2372
|
+
* name: "token"
|
|
2373
|
+
* });
|
|
2374
|
+
* }
|
|
2375
|
+
* ```
|
|
2376
|
+
*
|
|
2377
|
+
* @example
|
|
2378
|
+
* ```ts
|
|
2379
|
+
* // Clean up on uninstall
|
|
2380
|
+
* if (process.argv.includes("--uninstall")) {
|
|
2381
|
+
* const deleted = await Bun.secrets.delete({
|
|
2382
|
+
* service: "my-cli-tool",
|
|
2383
|
+
* name: "config"
|
|
2384
|
+
* });
|
|
2385
|
+
* process.exit(deleted ? 0 : 1);
|
|
2386
|
+
* }
|
|
2387
|
+
* ```
|
|
2388
|
+
*/
|
|
2389
|
+
delete(options: {
|
|
2390
|
+
/**
|
|
2391
|
+
* The service or application name.
|
|
2392
|
+
*
|
|
2393
|
+
* Use a unique identifier for your application to avoid conflicts.
|
|
2394
|
+
* Consider using reverse domain notation for production apps (e.g., "com.example.myapp").
|
|
2395
|
+
*/
|
|
2396
|
+
service: string;
|
|
2397
|
+
|
|
2398
|
+
/**
|
|
2399
|
+
* The account name, username, or resource identifier.
|
|
2400
|
+
*
|
|
2401
|
+
* This identifies the specific credential within the service.
|
|
2402
|
+
* Common patterns include usernames, email addresses, or resource URLs.
|
|
2403
|
+
*/
|
|
2404
|
+
name: string;
|
|
2405
|
+
}): Promise<boolean>;
|
|
2406
|
+
};
|
|
2407
|
+
|
|
2123
2408
|
/**
|
|
2124
2409
|
* A build artifact represents a file that was generated by the bundler @see {@link Bun.build}
|
|
2125
2410
|
*
|
package/docs/api/fetch.md
CHANGED
|
@@ -336,7 +336,7 @@ This will print the request and response headers to your terminal:
|
|
|
336
336
|
```sh
|
|
337
337
|
[fetch] > HTTP/1.1 GET http://example.com/
|
|
338
338
|
[fetch] > Connection: keep-alive
|
|
339
|
-
[fetch] > User-Agent: Bun/1.2.21-canary.
|
|
339
|
+
[fetch] > User-Agent: Bun/1.2.21-canary.20250824T140615
|
|
340
340
|
[fetch] > Accept: */*
|
|
341
341
|
[fetch] > Host: example.com
|
|
342
342
|
[fetch] > Accept-Encoding: gzip, deflate, br
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
Store and retrieve sensitive credentials securely using the operating system's native credential storage APIs.
|
|
2
|
+
|
|
3
|
+
**Experimental:** This API is new and experimental. It may change in the future.
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import { secrets } from "bun";
|
|
7
|
+
|
|
8
|
+
const githubToken = await secrets.get({
|
|
9
|
+
service: "my-cli-tool",
|
|
10
|
+
name: "github-token",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!githubToken) {
|
|
14
|
+
const response = await fetch("https://api.github.com/name", {
|
|
15
|
+
headers: { "Authorization": `token ${githubToken}` },
|
|
16
|
+
});
|
|
17
|
+
console.log("Please enter your GitHub token");
|
|
18
|
+
} else {
|
|
19
|
+
await secrets.set({
|
|
20
|
+
service: "my-cli-tool",
|
|
21
|
+
name: "github-token",
|
|
22
|
+
value: prompt("Please enter your GitHub token"),
|
|
23
|
+
});
|
|
24
|
+
console.log("GitHub token stored");
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Overview
|
|
29
|
+
|
|
30
|
+
`Bun.secrets` provides a cross-platform API for managing sensitive credentials that CLI tools and development applications typically store in plaintext files like `~/.npmrc`, `~/.aws/credentials`, or `.env` files. It uses:
|
|
31
|
+
|
|
32
|
+
- **macOS**: Keychain Services
|
|
33
|
+
- **Linux**: libsecret (GNOME Keyring, KWallet, etc.)
|
|
34
|
+
- **Windows**: Windows Credential Manager
|
|
35
|
+
|
|
36
|
+
All operations are asynchronous and non-blocking, running on Bun's threadpool.
|
|
37
|
+
|
|
38
|
+
Note: in the future, we may add an additional `provider` option to make this better for production deployment secrets, but today this API is mostly useful for local development tools.
|
|
39
|
+
|
|
40
|
+
## API
|
|
41
|
+
|
|
42
|
+
### `Bun.secrets.get(options)`
|
|
43
|
+
|
|
44
|
+
Retrieve a stored credential.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { secrets } from "bun";
|
|
48
|
+
|
|
49
|
+
const password = await Bun.secrets.get({
|
|
50
|
+
service: "my-app",
|
|
51
|
+
name: "alice@example.com",
|
|
52
|
+
});
|
|
53
|
+
// Returns: string | null
|
|
54
|
+
|
|
55
|
+
// Or if you prefer without an object
|
|
56
|
+
const password = await Bun.secrets.get("my-app", "alice@example.com");
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Parameters:**
|
|
60
|
+
|
|
61
|
+
- `options.service` (string, required) - The service or application name
|
|
62
|
+
- `options.name` (string, required) - The username or account identifier
|
|
63
|
+
|
|
64
|
+
**Returns:**
|
|
65
|
+
|
|
66
|
+
- `Promise<string | null>` - The stored password, or `null` if not found
|
|
67
|
+
|
|
68
|
+
### `Bun.secrets.set(options, value)`
|
|
69
|
+
|
|
70
|
+
Store or update a credential.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { secrets } from "bun";
|
|
74
|
+
|
|
75
|
+
await secrets.set({
|
|
76
|
+
service: "my-app",
|
|
77
|
+
name: "alice@example.com",
|
|
78
|
+
value: "super-secret-password",
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Parameters:**
|
|
83
|
+
|
|
84
|
+
- `options.service` (string, required) - The service or application name
|
|
85
|
+
- `options.name` (string, required) - The username or account identifier
|
|
86
|
+
- `value` (string, required) - The password or secret to store
|
|
87
|
+
|
|
88
|
+
**Notes:**
|
|
89
|
+
|
|
90
|
+
- If a credential already exists for the given service/name combination, it will be replaced
|
|
91
|
+
- The stored value is encrypted by the operating system
|
|
92
|
+
|
|
93
|
+
### `Bun.secrets.delete(options)`
|
|
94
|
+
|
|
95
|
+
Delete a stored credential.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const deleted = await Bun.secrets.delete({
|
|
99
|
+
service: "my-app",
|
|
100
|
+
name: "alice@example.com",
|
|
101
|
+
value: "super-secret-password",
|
|
102
|
+
});
|
|
103
|
+
// Returns: boolean
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Parameters:**
|
|
107
|
+
|
|
108
|
+
- `options.service` (string, required) - The service or application name
|
|
109
|
+
- `options.name` (string, required) - The username or account identifier
|
|
110
|
+
|
|
111
|
+
**Returns:**
|
|
112
|
+
|
|
113
|
+
- `Promise<boolean>` - `true` if a credential was deleted, `false` if not found
|
|
114
|
+
|
|
115
|
+
## Examples
|
|
116
|
+
|
|
117
|
+
### Storing CLI Tool Credentials
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
// Store GitHub CLI token (instead of ~/.config/gh/hosts.yml)
|
|
121
|
+
await Bun.secrets.set({
|
|
122
|
+
service: "my-app.com",
|
|
123
|
+
name: "github-token",
|
|
124
|
+
value: "ghp_xxxxxxxxxxxxxxxxxxxx",
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Or if you prefer without an object
|
|
128
|
+
await Bun.secrets.set("my-app.com", "github-token", "ghp_xxxxxxxxxxxxxxxxxxxx");
|
|
129
|
+
|
|
130
|
+
// Store npm registry token (instead of ~/.npmrc)
|
|
131
|
+
await Bun.secrets.set({
|
|
132
|
+
service: "npm-registry",
|
|
133
|
+
name: "https://registry.npmjs.org",
|
|
134
|
+
value: "npm_xxxxxxxxxxxxxxxxxxxx",
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Retrieve for API calls
|
|
138
|
+
const token = await Bun.secrets.get({
|
|
139
|
+
service: "gh-cli",
|
|
140
|
+
name: "github.com",
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (token) {
|
|
144
|
+
const response = await fetch("https://api.github.com/name", {
|
|
145
|
+
headers: {
|
|
146
|
+
"Authorization": `token ${token}`,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Migrating from Plaintext Config Files
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
// Instead of storing in ~/.aws/credentials
|
|
156
|
+
await Bun.secrets.set({
|
|
157
|
+
service: "aws-cli",
|
|
158
|
+
name: "AWS_SECRET_ACCESS_KEY",
|
|
159
|
+
value: process.env.AWS_SECRET_ACCESS_KEY,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Instead of .env files with sensitive data
|
|
163
|
+
await Bun.secrets.set({
|
|
164
|
+
service: "my-app",
|
|
165
|
+
name: "api-key",
|
|
166
|
+
value: "sk_live_xxxxxxxxxxxxxxxxxxxx",
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Load at runtime
|
|
170
|
+
const apiKey =
|
|
171
|
+
(await Bun.secrets.get({
|
|
172
|
+
service: "my-app",
|
|
173
|
+
name: "api-key",
|
|
174
|
+
})) || process.env.API_KEY; // Fallback for CI/production
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Error Handling
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
try {
|
|
181
|
+
await Bun.secrets.set({
|
|
182
|
+
service: "my-app",
|
|
183
|
+
name: "alice",
|
|
184
|
+
value: "password123",
|
|
185
|
+
});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error("Failed to store credential:", error.message);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check if a credential exists
|
|
191
|
+
const password = await Bun.secrets.get({
|
|
192
|
+
service: "my-app",
|
|
193
|
+
name: "alice",
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
if (password === null) {
|
|
197
|
+
console.log("No credential found");
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Updating Credentials
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
// Initial password
|
|
205
|
+
await Bun.secrets.set({
|
|
206
|
+
service: "email-server",
|
|
207
|
+
name: "admin@example.com",
|
|
208
|
+
value: "old-password",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// Update to new password
|
|
212
|
+
await Bun.secrets.set({
|
|
213
|
+
service: "email-server",
|
|
214
|
+
name: "admin@example.com",
|
|
215
|
+
value: "new-password",
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// The old password is replaced
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Platform Behavior
|
|
222
|
+
|
|
223
|
+
### macOS (Keychain)
|
|
224
|
+
|
|
225
|
+
- Credentials are stored in the name's login keychain
|
|
226
|
+
- The keychain may prompt for access permission on first use
|
|
227
|
+
- Credentials persist across system restarts
|
|
228
|
+
- Accessible by the name who stored them
|
|
229
|
+
|
|
230
|
+
### Linux (libsecret)
|
|
231
|
+
|
|
232
|
+
- Requires a secret service daemon (GNOME Keyring, KWallet, etc.)
|
|
233
|
+
- Credentials are stored in the default collection
|
|
234
|
+
- May prompt for unlock if the keyring is locked
|
|
235
|
+
- The secret service must be running
|
|
236
|
+
|
|
237
|
+
### Windows (Credential Manager)
|
|
238
|
+
|
|
239
|
+
- Credentials are stored in Windows Credential Manager
|
|
240
|
+
- Visible in Control Panel → Credential Manager → Windows Credentials
|
|
241
|
+
- Persist with `CRED_PERSIST_ENTERPRISE` flag so it's scoped per user
|
|
242
|
+
- Encrypted using Windows Data Protection API
|
|
243
|
+
|
|
244
|
+
## Security Considerations
|
|
245
|
+
|
|
246
|
+
1. **Encryption**: Credentials are encrypted by the operating system's credential manager
|
|
247
|
+
2. **Access Control**: Only the name who stored the credential can retrieve it
|
|
248
|
+
3. **No Plain Text**: Passwords are never stored in plain text
|
|
249
|
+
4. **Memory Safety**: Bun zeros out password memory after use
|
|
250
|
+
5. **Process Isolation**: Credentials are isolated per name account
|
|
251
|
+
|
|
252
|
+
## Limitations
|
|
253
|
+
|
|
254
|
+
- Maximum password length varies by platform (typically 2048-4096 bytes)
|
|
255
|
+
- Service and name names should be reasonable lengths (< 256 characters)
|
|
256
|
+
- Some special characters may need escaping depending on the platform
|
|
257
|
+
- Requires appropriate system services:
|
|
258
|
+
- Linux: Secret service daemon must be running
|
|
259
|
+
- macOS: Keychain Access must be available
|
|
260
|
+
- Windows: Credential Manager service must be enabled
|
|
261
|
+
|
|
262
|
+
## Comparison with Environment Variables
|
|
263
|
+
|
|
264
|
+
Unlike environment variables, `Bun.secrets`:
|
|
265
|
+
|
|
266
|
+
- ✅ Encrypts credentials at rest (thanks to the operating system)
|
|
267
|
+
- ✅ Avoids exposing secrets in process memory dumps (memory is zeroed after its no longer needed)
|
|
268
|
+
- ✅ Survives application restarts
|
|
269
|
+
- ✅ Can be updated without restarting the application
|
|
270
|
+
- ✅ Provides name-level access control
|
|
271
|
+
- ❌ Requires OS credential service
|
|
272
|
+
- ❌ Not very useful for deployment secrets (use environment variables in production)
|
|
273
|
+
|
|
274
|
+
## Best Practices
|
|
275
|
+
|
|
276
|
+
1. **Use descriptive service names**: Match the tool or application name
|
|
277
|
+
If you're building a CLI for external use, you probably should use a UTI (Uniform Type Identifier) for the service name.
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
// Good - matches the actual tool
|
|
281
|
+
{ service: "com.docker.hub", name: "username" }
|
|
282
|
+
{ service: "com.vercel.cli", name: "team-name" }
|
|
283
|
+
|
|
284
|
+
// Avoid - too generic
|
|
285
|
+
{ service: "api", name: "key" }
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
2. **Credentials-only**: Don't store application configuration in this API
|
|
289
|
+
This API is slow, you probably still need to use a config file for some things.
|
|
290
|
+
|
|
291
|
+
3. **Use for local development tools**:
|
|
292
|
+
- ✅ CLI tools (gh, npm, docker, kubectl)
|
|
293
|
+
- ✅ Local development servers
|
|
294
|
+
- ✅ Personal API keys for testing
|
|
295
|
+
- ❌ Production servers (use proper secret management)
|
|
296
|
+
|
|
297
|
+
## TypeScript
|
|
298
|
+
|
|
299
|
+
```typescript
|
|
300
|
+
namespace Bun {
|
|
301
|
+
interface SecretsOptions {
|
|
302
|
+
service: string;
|
|
303
|
+
name: string;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
interface Secrets {
|
|
307
|
+
get(options: SecretsOptions): Promise<string | null>;
|
|
308
|
+
set(options: SecretsOptions, value: string): Promise<void>;
|
|
309
|
+
delete(options: SecretsOptions): Promise<boolean>;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const secrets: Secrets;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## See Also
|
|
317
|
+
|
|
318
|
+
- [Environment Variables](./env.md) - For deployment configuration
|
|
319
|
+
- [Bun.password](./password.md) - For password hashing and verification
|
package/docs/api/spawn.md
CHANGED
|
@@ -140,7 +140,7 @@ You can read results from the subprocess via the `stdout` and `stderr` propertie
|
|
|
140
140
|
```ts
|
|
141
141
|
const proc = Bun.spawn(["bun", "--version"]);
|
|
142
142
|
const text = await proc.stdout.text();
|
|
143
|
-
console.log(text); // => "1.2.21-canary.
|
|
143
|
+
console.log(text); // => "1.2.21-canary.20250824T140615\n"
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
Configure the output stream by passing one of the following values to `stdout/stderr`:
|