keyenv 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 KeyEnv
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,162 @@
1
+ # KeyEnv Node.js SDK
2
+
3
+ Official Node.js SDK for [KeyEnv](https://keyenv.dev) - Secure secrets management for development teams.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install keyenv
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { KeyEnv } from 'keyenv';
15
+
16
+ const client = new KeyEnv({
17
+ token: process.env.KEYENV_TOKEN!,
18
+ });
19
+
20
+ // Load secrets into process.env
21
+ await client.loadEnv('your-project-id', 'production');
22
+ console.log(process.env.DATABASE_URL);
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ### Initialize the Client
28
+
29
+ ```typescript
30
+ import { KeyEnv } from 'keyenv';
31
+
32
+ const client = new KeyEnv({
33
+ token: 'your-service-token',
34
+ });
35
+ ```
36
+
37
+ ### Export Secrets
38
+
39
+ ```typescript
40
+ // Get all secrets as an array
41
+ const secrets = await client.exportSecrets('project-id', 'production');
42
+ for (const secret of secrets) {
43
+ console.log(`${secret.key}=${secret.value}`);
44
+ }
45
+
46
+ // Get secrets as a key-value object
47
+ const env = await client.exportSecretsAsObject('project-id', 'production');
48
+ console.log(env.DATABASE_URL);
49
+
50
+ // Load directly into process.env
51
+ const count = await client.loadEnv('project-id', 'production');
52
+ console.log(`Loaded ${count} secrets`);
53
+ ```
54
+
55
+ ### Manage Secrets
56
+
57
+ ```typescript
58
+ // Get a single secret
59
+ const secret = await client.getSecret('project-id', 'production', 'DATABASE_URL');
60
+ console.log(secret.value);
61
+
62
+ // Set a secret (creates or updates)
63
+ await client.setSecret('project-id', 'production', 'API_KEY', 'sk_live_...');
64
+
65
+ // Delete a secret
66
+ await client.deleteSecret('project-id', 'production', 'OLD_KEY');
67
+ ```
68
+
69
+ ### Bulk Import
70
+
71
+ ```typescript
72
+ const result = await client.bulkImport('project-id', 'development', [
73
+ { key: 'DATABASE_URL', value: 'postgres://localhost/mydb' },
74
+ { key: 'REDIS_URL', value: 'redis://localhost:6379' },
75
+ { key: 'API_KEY', value: 'sk_test_...', description: 'Test API key' },
76
+ ], { overwrite: true });
77
+
78
+ console.log(`Created: ${result.created}, Updated: ${result.updated}`);
79
+ ```
80
+
81
+ ### Generate .env File
82
+
83
+ ```typescript
84
+ import { writeFileSync } from 'fs';
85
+
86
+ const envContent = await client.generateEnvFile('project-id', 'production');
87
+ writeFileSync('.env', envContent);
88
+ ```
89
+
90
+ ### List Projects and Environments
91
+
92
+ ```typescript
93
+ // List all projects
94
+ const projects = await client.listProjects();
95
+ for (const project of projects) {
96
+ console.log(`${project.name} (${project.id})`);
97
+ }
98
+
99
+ // Get project with environments
100
+ const project = await client.getProject('project-id');
101
+ for (const env of project.environments) {
102
+ console.log(` - ${env.name}`);
103
+ }
104
+ ```
105
+
106
+ ## Error Handling
107
+
108
+ ```typescript
109
+ import { KeyEnv, KeyEnvError } from 'keyenv';
110
+
111
+ try {
112
+ await client.getSecret('project-id', 'production', 'MISSING_KEY');
113
+ } catch (error) {
114
+ if (error instanceof KeyEnvError) {
115
+ console.error(`Error ${error.status}: ${error.message}`);
116
+ if (error.status === 404) {
117
+ console.error('Secret not found');
118
+ }
119
+ }
120
+ }
121
+ ```
122
+
123
+ ## TypeScript Support
124
+
125
+ The SDK is written in TypeScript and includes full type definitions:
126
+
127
+ ```typescript
128
+ import type { Secret, SecretWithValue, Project } from 'keyenv';
129
+ ```
130
+
131
+ ## API Reference
132
+
133
+ ### `new KeyEnv(options)`
134
+
135
+ Create a new KeyEnv client.
136
+
137
+ | Option | Type | Required | Default | Description |
138
+ |--------|------|----------|---------|-------------|
139
+ | `token` | `string` | Yes | - | Service token |
140
+ | `timeout` | `number` | No | `30000` | Request timeout (ms) |
141
+
142
+ ### Methods
143
+
144
+ | Method | Description |
145
+ |--------|-------------|
146
+ | `getCurrentUser()` | Get current user/token info |
147
+ | `listProjects()` | List all accessible projects |
148
+ | `getProject(id)` | Get project with environments |
149
+ | `listEnvironments(projectId)` | List environments in a project |
150
+ | `listSecrets(projectId, env)` | List secret keys (no values) |
151
+ | `exportSecrets(projectId, env)` | Export secrets with values |
152
+ | `exportSecretsAsObject(projectId, env)` | Export as key-value object |
153
+ | `getSecret(projectId, env, key)` | Get single secret |
154
+ | `setSecret(projectId, env, key, value)` | Create or update secret |
155
+ | `deleteSecret(projectId, env, key)` | Delete secret |
156
+ | `bulkImport(projectId, env, secrets)` | Bulk import secrets |
157
+ | `loadEnv(projectId, env)` | Load secrets into process.env |
158
+ | `generateEnvFile(projectId, env)` | Generate .env file content |
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,187 @@
1
+ /** KeyEnv client configuration options */
2
+ interface KeyEnvOptions {
3
+ /** Service token for authentication */
4
+ token: string;
5
+ /** Request timeout in milliseconds (default: 30000) */
6
+ timeout?: number;
7
+ }
8
+ /** User or service token info */
9
+ interface User {
10
+ id: string;
11
+ email?: string;
12
+ name?: string;
13
+ clerk_id?: string;
14
+ avatar_url?: string;
15
+ /** Present for service tokens */
16
+ auth_type?: 'service_token' | 'user';
17
+ /** Team ID (for service tokens) */
18
+ team_id?: string;
19
+ /** Project ID (for project-scoped service tokens) */
20
+ project_id?: string;
21
+ /** Token scopes (for service tokens) */
22
+ scopes?: string[];
23
+ created_at: string;
24
+ }
25
+ /** Project */
26
+ interface Project {
27
+ id: string;
28
+ team_id: string;
29
+ name: string;
30
+ slug: string;
31
+ description?: string;
32
+ created_at: string;
33
+ }
34
+ /** Environment */
35
+ interface Environment {
36
+ id: string;
37
+ project_id: string;
38
+ name: string;
39
+ inherits_from?: string;
40
+ created_at: string;
41
+ }
42
+ /** Project with environments */
43
+ interface ProjectWithEnvironments extends Project {
44
+ environments: Environment[];
45
+ }
46
+ /** Secret (without value) */
47
+ interface Secret {
48
+ id: string;
49
+ environment_id: string;
50
+ key: string;
51
+ type: string;
52
+ description?: string;
53
+ version: number;
54
+ created_at: string;
55
+ updated_at: string;
56
+ }
57
+ /** Secret with decrypted value */
58
+ interface SecretWithValue extends Secret {
59
+ value: string;
60
+ inherited_from?: string;
61
+ }
62
+ /** Secret history entry */
63
+ interface SecretHistory {
64
+ id: string;
65
+ secret_id: string;
66
+ value: string;
67
+ version: number;
68
+ changed_by?: string;
69
+ changed_at: string;
70
+ }
71
+ /** Bulk import request item */
72
+ interface BulkSecretItem {
73
+ key: string;
74
+ value: string;
75
+ description?: string;
76
+ }
77
+ /** Bulk import result */
78
+ interface BulkImportResult {
79
+ created: number;
80
+ updated: number;
81
+ skipped: number;
82
+ }
83
+ /** KeyEnv API error */
84
+ declare class KeyEnvError extends Error {
85
+ readonly status: number;
86
+ readonly code?: string;
87
+ readonly details?: Record<string, unknown>;
88
+ constructor(message: string, status: number, code?: string, details?: Record<string, unknown>);
89
+ }
90
+
91
+ /**
92
+ * KeyEnv API client for managing secrets
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * import { KeyEnv } from 'keyenv';
97
+ *
98
+ * const client = new KeyEnv({ token: process.env.KEYENV_TOKEN });
99
+ *
100
+ * // Export all secrets for an environment
101
+ * const secrets = await client.exportSecrets('project-id', 'production');
102
+ * ```
103
+ */
104
+ declare class KeyEnv {
105
+ private readonly token;
106
+ private readonly timeout;
107
+ constructor(options: KeyEnvOptions);
108
+ private request;
109
+ /** Get the current user or service token info */
110
+ getCurrentUser(): Promise<User>;
111
+ /** Validate the token and return user info */
112
+ validateToken(): Promise<User>;
113
+ /** List all accessible projects */
114
+ listProjects(): Promise<Project[]>;
115
+ /** Get a project by ID */
116
+ getProject(projectId: string): Promise<ProjectWithEnvironments>;
117
+ /** Create a new project */
118
+ createProject(teamId: string, name: string): Promise<Project>;
119
+ /** Delete a project */
120
+ deleteProject(projectId: string): Promise<void>;
121
+ /** List environments in a project */
122
+ listEnvironments(projectId: string): Promise<Environment[]>;
123
+ /** Create a new environment */
124
+ createEnvironment(projectId: string, name: string, inheritsFrom?: string): Promise<Environment>;
125
+ /** Delete an environment */
126
+ deleteEnvironment(projectId: string, environment: string): Promise<void>;
127
+ /** List secrets in an environment (keys and metadata only) */
128
+ listSecrets(projectId: string, environment: string): Promise<Secret[]>;
129
+ /**
130
+ * Export all secrets with their decrypted values
131
+ * @example
132
+ * ```ts
133
+ * const secrets = await client.exportSecrets('project-id', 'production');
134
+ * for (const secret of secrets) {
135
+ * process.env[secret.key] = secret.value;
136
+ * }
137
+ * ```
138
+ */
139
+ exportSecrets(projectId: string, environment: string): Promise<SecretWithValue[]>;
140
+ /**
141
+ * Export secrets as a key-value object
142
+ * @example
143
+ * ```ts
144
+ * const env = await client.exportSecretsAsObject('project-id', 'production');
145
+ * // { DATABASE_URL: '...', API_KEY: '...' }
146
+ * ```
147
+ */
148
+ exportSecretsAsObject(projectId: string, environment: string): Promise<Record<string, string>>;
149
+ /** Get a single secret with its value */
150
+ getSecret(projectId: string, environment: string, key: string): Promise<SecretWithValue>;
151
+ /** Create a new secret */
152
+ createSecret(projectId: string, environment: string, key: string, value: string, description?: string): Promise<Secret>;
153
+ /** Update a secret's value */
154
+ updateSecret(projectId: string, environment: string, key: string, value: string, description?: string): Promise<Secret>;
155
+ /** Set a secret (create or update) */
156
+ setSecret(projectId: string, environment: string, key: string, value: string, description?: string): Promise<Secret>;
157
+ /** Delete a secret */
158
+ deleteSecret(projectId: string, environment: string, key: string): Promise<void>;
159
+ /** Get secret version history */
160
+ getSecretHistory(projectId: string, environment: string, key: string): Promise<SecretHistory[]>;
161
+ /**
162
+ * Bulk import secrets
163
+ * @example
164
+ * ```ts
165
+ * await client.bulkImport('project-id', 'development', [
166
+ * { key: 'DATABASE_URL', value: 'postgres://...' },
167
+ * { key: 'API_KEY', value: 'sk_...' },
168
+ * ], { overwrite: true });
169
+ * ```
170
+ */
171
+ bulkImport(projectId: string, environment: string, secrets: BulkSecretItem[], options?: {
172
+ overwrite?: boolean;
173
+ }): Promise<BulkImportResult>;
174
+ /**
175
+ * Load secrets into process.env
176
+ * @example
177
+ * ```ts
178
+ * await client.loadEnv('project-id', 'production');
179
+ * console.log(process.env.DATABASE_URL);
180
+ * ```
181
+ */
182
+ loadEnv(projectId: string, environment: string): Promise<number>;
183
+ /** Generate .env file content from secrets */
184
+ generateEnvFile(projectId: string, environment: string): Promise<string>;
185
+ }
186
+
187
+ export { type BulkImportResult, type BulkSecretItem, type Environment, KeyEnv, KeyEnvError, type KeyEnvOptions, type Project, type ProjectWithEnvironments, type Secret, type SecretHistory, type SecretWithValue, type User };
@@ -0,0 +1,187 @@
1
+ /** KeyEnv client configuration options */
2
+ interface KeyEnvOptions {
3
+ /** Service token for authentication */
4
+ token: string;
5
+ /** Request timeout in milliseconds (default: 30000) */
6
+ timeout?: number;
7
+ }
8
+ /** User or service token info */
9
+ interface User {
10
+ id: string;
11
+ email?: string;
12
+ name?: string;
13
+ clerk_id?: string;
14
+ avatar_url?: string;
15
+ /** Present for service tokens */
16
+ auth_type?: 'service_token' | 'user';
17
+ /** Team ID (for service tokens) */
18
+ team_id?: string;
19
+ /** Project ID (for project-scoped service tokens) */
20
+ project_id?: string;
21
+ /** Token scopes (for service tokens) */
22
+ scopes?: string[];
23
+ created_at: string;
24
+ }
25
+ /** Project */
26
+ interface Project {
27
+ id: string;
28
+ team_id: string;
29
+ name: string;
30
+ slug: string;
31
+ description?: string;
32
+ created_at: string;
33
+ }
34
+ /** Environment */
35
+ interface Environment {
36
+ id: string;
37
+ project_id: string;
38
+ name: string;
39
+ inherits_from?: string;
40
+ created_at: string;
41
+ }
42
+ /** Project with environments */
43
+ interface ProjectWithEnvironments extends Project {
44
+ environments: Environment[];
45
+ }
46
+ /** Secret (without value) */
47
+ interface Secret {
48
+ id: string;
49
+ environment_id: string;
50
+ key: string;
51
+ type: string;
52
+ description?: string;
53
+ version: number;
54
+ created_at: string;
55
+ updated_at: string;
56
+ }
57
+ /** Secret with decrypted value */
58
+ interface SecretWithValue extends Secret {
59
+ value: string;
60
+ inherited_from?: string;
61
+ }
62
+ /** Secret history entry */
63
+ interface SecretHistory {
64
+ id: string;
65
+ secret_id: string;
66
+ value: string;
67
+ version: number;
68
+ changed_by?: string;
69
+ changed_at: string;
70
+ }
71
+ /** Bulk import request item */
72
+ interface BulkSecretItem {
73
+ key: string;
74
+ value: string;
75
+ description?: string;
76
+ }
77
+ /** Bulk import result */
78
+ interface BulkImportResult {
79
+ created: number;
80
+ updated: number;
81
+ skipped: number;
82
+ }
83
+ /** KeyEnv API error */
84
+ declare class KeyEnvError extends Error {
85
+ readonly status: number;
86
+ readonly code?: string;
87
+ readonly details?: Record<string, unknown>;
88
+ constructor(message: string, status: number, code?: string, details?: Record<string, unknown>);
89
+ }
90
+
91
+ /**
92
+ * KeyEnv API client for managing secrets
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * import { KeyEnv } from 'keyenv';
97
+ *
98
+ * const client = new KeyEnv({ token: process.env.KEYENV_TOKEN });
99
+ *
100
+ * // Export all secrets for an environment
101
+ * const secrets = await client.exportSecrets('project-id', 'production');
102
+ * ```
103
+ */
104
+ declare class KeyEnv {
105
+ private readonly token;
106
+ private readonly timeout;
107
+ constructor(options: KeyEnvOptions);
108
+ private request;
109
+ /** Get the current user or service token info */
110
+ getCurrentUser(): Promise<User>;
111
+ /** Validate the token and return user info */
112
+ validateToken(): Promise<User>;
113
+ /** List all accessible projects */
114
+ listProjects(): Promise<Project[]>;
115
+ /** Get a project by ID */
116
+ getProject(projectId: string): Promise<ProjectWithEnvironments>;
117
+ /** Create a new project */
118
+ createProject(teamId: string, name: string): Promise<Project>;
119
+ /** Delete a project */
120
+ deleteProject(projectId: string): Promise<void>;
121
+ /** List environments in a project */
122
+ listEnvironments(projectId: string): Promise<Environment[]>;
123
+ /** Create a new environment */
124
+ createEnvironment(projectId: string, name: string, inheritsFrom?: string): Promise<Environment>;
125
+ /** Delete an environment */
126
+ deleteEnvironment(projectId: string, environment: string): Promise<void>;
127
+ /** List secrets in an environment (keys and metadata only) */
128
+ listSecrets(projectId: string, environment: string): Promise<Secret[]>;
129
+ /**
130
+ * Export all secrets with their decrypted values
131
+ * @example
132
+ * ```ts
133
+ * const secrets = await client.exportSecrets('project-id', 'production');
134
+ * for (const secret of secrets) {
135
+ * process.env[secret.key] = secret.value;
136
+ * }
137
+ * ```
138
+ */
139
+ exportSecrets(projectId: string, environment: string): Promise<SecretWithValue[]>;
140
+ /**
141
+ * Export secrets as a key-value object
142
+ * @example
143
+ * ```ts
144
+ * const env = await client.exportSecretsAsObject('project-id', 'production');
145
+ * // { DATABASE_URL: '...', API_KEY: '...' }
146
+ * ```
147
+ */
148
+ exportSecretsAsObject(projectId: string, environment: string): Promise<Record<string, string>>;
149
+ /** Get a single secret with its value */
150
+ getSecret(projectId: string, environment: string, key: string): Promise<SecretWithValue>;
151
+ /** Create a new secret */
152
+ createSecret(projectId: string, environment: string, key: string, value: string, description?: string): Promise<Secret>;
153
+ /** Update a secret's value */
154
+ updateSecret(projectId: string, environment: string, key: string, value: string, description?: string): Promise<Secret>;
155
+ /** Set a secret (create or update) */
156
+ setSecret(projectId: string, environment: string, key: string, value: string, description?: string): Promise<Secret>;
157
+ /** Delete a secret */
158
+ deleteSecret(projectId: string, environment: string, key: string): Promise<void>;
159
+ /** Get secret version history */
160
+ getSecretHistory(projectId: string, environment: string, key: string): Promise<SecretHistory[]>;
161
+ /**
162
+ * Bulk import secrets
163
+ * @example
164
+ * ```ts
165
+ * await client.bulkImport('project-id', 'development', [
166
+ * { key: 'DATABASE_URL', value: 'postgres://...' },
167
+ * { key: 'API_KEY', value: 'sk_...' },
168
+ * ], { overwrite: true });
169
+ * ```
170
+ */
171
+ bulkImport(projectId: string, environment: string, secrets: BulkSecretItem[], options?: {
172
+ overwrite?: boolean;
173
+ }): Promise<BulkImportResult>;
174
+ /**
175
+ * Load secrets into process.env
176
+ * @example
177
+ * ```ts
178
+ * await client.loadEnv('project-id', 'production');
179
+ * console.log(process.env.DATABASE_URL);
180
+ * ```
181
+ */
182
+ loadEnv(projectId: string, environment: string): Promise<number>;
183
+ /** Generate .env file content from secrets */
184
+ generateEnvFile(projectId: string, environment: string): Promise<string>;
185
+ }
186
+
187
+ export { type BulkImportResult, type BulkSecretItem, type Environment, KeyEnv, KeyEnvError, type KeyEnvOptions, type Project, type ProjectWithEnvironments, type Secret, type SecretHistory, type SecretWithValue, type User };
package/dist/index.js ADDED
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ KeyEnv: () => KeyEnv,
24
+ KeyEnvError: () => KeyEnvError
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/types.ts
29
+ var KeyEnvError = class extends Error {
30
+ status;
31
+ code;
32
+ details;
33
+ constructor(message, status, code, details) {
34
+ super(message);
35
+ this.name = "KeyEnvError";
36
+ this.status = status;
37
+ this.code = code;
38
+ this.details = details;
39
+ }
40
+ };
41
+
42
+ // src/client.ts
43
+ var BASE_URL = "https://api.keyenv.dev";
44
+ var DEFAULT_TIMEOUT = 3e4;
45
+ var KeyEnv = class {
46
+ token;
47
+ timeout;
48
+ constructor(options) {
49
+ if (!options.token) {
50
+ throw new Error("KeyEnv token is required");
51
+ }
52
+ this.token = options.token;
53
+ this.timeout = options.timeout || DEFAULT_TIMEOUT;
54
+ }
55
+ async request(method, path, body) {
56
+ const url = `${BASE_URL}${path}`;
57
+ const controller = new AbortController();
58
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
59
+ try {
60
+ const response = await fetch(url, {
61
+ method,
62
+ headers: {
63
+ "Authorization": `Bearer ${this.token}`,
64
+ "Content-Type": "application/json",
65
+ "User-Agent": "keyenv-node/1.0.0"
66
+ },
67
+ body: body ? JSON.stringify(body) : void 0,
68
+ signal: controller.signal
69
+ });
70
+ clearTimeout(timeoutId);
71
+ if (!response.ok) {
72
+ let errorData = { error: "Unknown error" };
73
+ try {
74
+ errorData = await response.json();
75
+ } catch {
76
+ errorData = { error: response.statusText };
77
+ }
78
+ throw new KeyEnvError(errorData.error, response.status, errorData.code, errorData.details);
79
+ }
80
+ if (response.status === 204) {
81
+ return void 0;
82
+ }
83
+ return response.json();
84
+ } catch (error) {
85
+ clearTimeout(timeoutId);
86
+ if (error instanceof KeyEnvError) throw error;
87
+ if (error instanceof Error && error.name === "AbortError") {
88
+ throw new KeyEnvError("Request timeout", 408);
89
+ }
90
+ throw new KeyEnvError(error instanceof Error ? error.message : "Network error", 0);
91
+ }
92
+ }
93
+ /** Get the current user or service token info */
94
+ async getCurrentUser() {
95
+ return this.request("GET", "/api/v1/users/me");
96
+ }
97
+ /** Validate the token and return user info */
98
+ async validateToken() {
99
+ return this.getCurrentUser();
100
+ }
101
+ /** List all accessible projects */
102
+ async listProjects() {
103
+ const response = await this.request("GET", "/api/v1/projects");
104
+ return response.projects;
105
+ }
106
+ /** Get a project by ID */
107
+ async getProject(projectId) {
108
+ return this.request("GET", `/api/v1/projects/${projectId}`);
109
+ }
110
+ /** Create a new project */
111
+ async createProject(teamId, name) {
112
+ return this.request("POST", "/api/v1/projects", { team_id: teamId, name });
113
+ }
114
+ /** Delete a project */
115
+ async deleteProject(projectId) {
116
+ await this.request("DELETE", `/api/v1/projects/${projectId}`);
117
+ }
118
+ /** List environments in a project */
119
+ async listEnvironments(projectId) {
120
+ const response = await this.request(
121
+ "GET",
122
+ `/api/v1/projects/${projectId}/environments`
123
+ );
124
+ return response.environments;
125
+ }
126
+ /** Create a new environment */
127
+ async createEnvironment(projectId, name, inheritsFrom) {
128
+ return this.request(
129
+ "POST",
130
+ `/api/v1/projects/${projectId}/environments`,
131
+ { name, inherits_from: inheritsFrom }
132
+ );
133
+ }
134
+ /** Delete an environment */
135
+ async deleteEnvironment(projectId, environment) {
136
+ await this.request("DELETE", `/api/v1/projects/${projectId}/environments/${environment}`);
137
+ }
138
+ /** List secrets in an environment (keys and metadata only) */
139
+ async listSecrets(projectId, environment) {
140
+ const response = await this.request(
141
+ "GET",
142
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets`
143
+ );
144
+ return response.secrets;
145
+ }
146
+ /**
147
+ * Export all secrets with their decrypted values
148
+ * @example
149
+ * ```ts
150
+ * const secrets = await client.exportSecrets('project-id', 'production');
151
+ * for (const secret of secrets) {
152
+ * process.env[secret.key] = secret.value;
153
+ * }
154
+ * ```
155
+ */
156
+ async exportSecrets(projectId, environment) {
157
+ const response = await this.request(
158
+ "GET",
159
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/export`
160
+ );
161
+ return response.secrets;
162
+ }
163
+ /**
164
+ * Export secrets as a key-value object
165
+ * @example
166
+ * ```ts
167
+ * const env = await client.exportSecretsAsObject('project-id', 'production');
168
+ * // { DATABASE_URL: '...', API_KEY: '...' }
169
+ * ```
170
+ */
171
+ async exportSecretsAsObject(projectId, environment) {
172
+ const secrets = await this.exportSecrets(projectId, environment);
173
+ return Object.fromEntries(secrets.map((s) => [s.key, s.value]));
174
+ }
175
+ /** Get a single secret with its value */
176
+ async getSecret(projectId, environment, key) {
177
+ const response = await this.request(
178
+ "GET",
179
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`
180
+ );
181
+ return response.secret;
182
+ }
183
+ /** Create a new secret */
184
+ async createSecret(projectId, environment, key, value, description) {
185
+ const response = await this.request(
186
+ "POST",
187
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets`,
188
+ { key, value, description }
189
+ );
190
+ return response.secret;
191
+ }
192
+ /** Update a secret's value */
193
+ async updateSecret(projectId, environment, key, value, description) {
194
+ const response = await this.request(
195
+ "PUT",
196
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`,
197
+ { value, description }
198
+ );
199
+ return response.secret;
200
+ }
201
+ /** Set a secret (create or update) */
202
+ async setSecret(projectId, environment, key, value, description) {
203
+ try {
204
+ return await this.updateSecret(projectId, environment, key, value, description);
205
+ } catch (error) {
206
+ if (error instanceof KeyEnvError && error.status === 404) {
207
+ return this.createSecret(projectId, environment, key, value, description);
208
+ }
209
+ throw error;
210
+ }
211
+ }
212
+ /** Delete a secret */
213
+ async deleteSecret(projectId, environment, key) {
214
+ await this.request(
215
+ "DELETE",
216
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`
217
+ );
218
+ }
219
+ /** Get secret version history */
220
+ async getSecretHistory(projectId, environment, key) {
221
+ const response = await this.request(
222
+ "GET",
223
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}/history`
224
+ );
225
+ return response.history;
226
+ }
227
+ /**
228
+ * Bulk import secrets
229
+ * @example
230
+ * ```ts
231
+ * await client.bulkImport('project-id', 'development', [
232
+ * { key: 'DATABASE_URL', value: 'postgres://...' },
233
+ * { key: 'API_KEY', value: 'sk_...' },
234
+ * ], { overwrite: true });
235
+ * ```
236
+ */
237
+ async bulkImport(projectId, environment, secrets, options = {}) {
238
+ return this.request(
239
+ "POST",
240
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/bulk`,
241
+ { secrets, overwrite: options.overwrite ?? false }
242
+ );
243
+ }
244
+ /**
245
+ * Load secrets into process.env
246
+ * @example
247
+ * ```ts
248
+ * await client.loadEnv('project-id', 'production');
249
+ * console.log(process.env.DATABASE_URL);
250
+ * ```
251
+ */
252
+ async loadEnv(projectId, environment) {
253
+ const secrets = await this.exportSecrets(projectId, environment);
254
+ for (const secret of secrets) {
255
+ process.env[secret.key] = secret.value;
256
+ }
257
+ return secrets.length;
258
+ }
259
+ /** Generate .env file content from secrets */
260
+ async generateEnvFile(projectId, environment) {
261
+ const secrets = await this.exportSecrets(projectId, environment);
262
+ const lines = [
263
+ "# Generated by KeyEnv",
264
+ `# Environment: ${environment}`,
265
+ `# Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
266
+ ""
267
+ ];
268
+ for (const secret of secrets) {
269
+ const value = secret.value;
270
+ if (value.includes("\n") || value.includes('"') || value.includes("'") || value.includes(" ")) {
271
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
272
+ lines.push(`${secret.key}="${escaped}"`);
273
+ } else {
274
+ lines.push(`${secret.key}=${value}`);
275
+ }
276
+ }
277
+ return lines.join("\n") + "\n";
278
+ }
279
+ };
280
+ // Annotate the CommonJS export names for ESM import in node:
281
+ 0 && (module.exports = {
282
+ KeyEnv,
283
+ KeyEnvError
284
+ });
285
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/client.ts"],"sourcesContent":["export { KeyEnv } from './client.js';\nexport { KeyEnvError } from './types.js';\nexport type {\n KeyEnvOptions,\n User,\n Project,\n ProjectWithEnvironments,\n Environment,\n Secret,\n SecretWithValue,\n SecretHistory,\n BulkSecretItem,\n BulkImportResult,\n} from './types.js';\n","/** KeyEnv client configuration options */\nexport interface KeyEnvOptions {\n /** Service token for authentication */\n token: string;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n}\n\n/** User or service token info */\nexport interface User {\n id: string;\n email?: string;\n name?: string;\n clerk_id?: string;\n avatar_url?: string;\n /** Present for service tokens */\n auth_type?: 'service_token' | 'user';\n /** Team ID (for service tokens) */\n team_id?: string;\n /** Project ID (for project-scoped service tokens) */\n project_id?: string;\n /** Token scopes (for service tokens) */\n scopes?: string[];\n created_at: string;\n}\n\n/** Project */\nexport interface Project {\n id: string;\n team_id: string;\n name: string;\n slug: string;\n description?: string;\n created_at: string;\n}\n\n/** Environment */\nexport interface Environment {\n id: string;\n project_id: string;\n name: string;\n inherits_from?: string;\n created_at: string;\n}\n\n/** Project with environments */\nexport interface ProjectWithEnvironments extends Project {\n environments: Environment[];\n}\n\n/** Secret (without value) */\nexport interface Secret {\n id: string;\n environment_id: string;\n key: string;\n type: string;\n description?: string;\n version: number;\n created_at: string;\n updated_at: string;\n}\n\n/** Secret with decrypted value */\nexport interface SecretWithValue extends Secret {\n value: string;\n inherited_from?: string;\n}\n\n/** Secret history entry */\nexport interface SecretHistory {\n id: string;\n secret_id: string;\n value: string;\n version: number;\n changed_by?: string;\n changed_at: string;\n}\n\n/** Bulk import request item */\nexport interface BulkSecretItem {\n key: string;\n value: string;\n description?: string;\n}\n\n/** Bulk import result */\nexport interface BulkImportResult {\n created: number;\n updated: number;\n skipped: number;\n}\n\n/** API error response */\nexport interface ApiError {\n error: string;\n code?: string;\n details?: Record<string, unknown>;\n}\n\n/** KeyEnv API error */\nexport class KeyEnvError extends Error {\n public readonly status: number;\n public readonly code?: string;\n public readonly details?: Record<string, unknown>;\n\n constructor(message: string, status: number, code?: string, details?: Record<string, unknown>) {\n super(message);\n this.name = 'KeyEnvError';\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n","import type {\n KeyEnvOptions,\n User,\n Project,\n ProjectWithEnvironments,\n Environment,\n Secret,\n SecretWithValue,\n SecretHistory,\n BulkSecretItem,\n BulkImportResult,\n ApiError,\n} from './types.js';\nimport { KeyEnvError } from './types.js';\n\nconst BASE_URL = 'https://api.keyenv.dev';\nconst DEFAULT_TIMEOUT = 30000;\n\n/**\n * KeyEnv API client for managing secrets\n *\n * @example\n * ```ts\n * import { KeyEnv } from 'keyenv';\n *\n * const client = new KeyEnv({ token: process.env.KEYENV_TOKEN });\n *\n * // Export all secrets for an environment\n * const secrets = await client.exportSecrets('project-id', 'production');\n * ```\n */\nexport class KeyEnv {\n private readonly token: string;\n private readonly timeout: number;\n\n constructor(options: KeyEnvOptions) {\n if (!options.token) {\n throw new Error('KeyEnv token is required');\n }\n this.token = options.token;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${BASE_URL}${path}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'keyenv-node/1.0.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n let errorData: ApiError = { error: 'Unknown error' };\n try {\n errorData = await response.json() as ApiError;\n } catch {\n errorData = { error: response.statusText };\n }\n throw new KeyEnvError(errorData.error, response.status, errorData.code, errorData.details);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof KeyEnvError) throw error;\n if (error instanceof Error && error.name === 'AbortError') {\n throw new KeyEnvError('Request timeout', 408);\n }\n throw new KeyEnvError(error instanceof Error ? error.message : 'Network error', 0);\n }\n }\n\n /** Get the current user or service token info */\n async getCurrentUser(): Promise<User> {\n return this.request<User>('GET', '/api/v1/users/me');\n }\n\n /** Validate the token and return user info */\n async validateToken(): Promise<User> {\n return this.getCurrentUser();\n }\n\n /** List all accessible projects */\n async listProjects(): Promise<Project[]> {\n const response = await this.request<{ projects: Project[] }>('GET', '/api/v1/projects');\n return response.projects;\n }\n\n /** Get a project by ID */\n async getProject(projectId: string): Promise<ProjectWithEnvironments> {\n return this.request<ProjectWithEnvironments>('GET', `/api/v1/projects/${projectId}`);\n }\n\n /** Create a new project */\n async createProject(teamId: string, name: string): Promise<Project> {\n return this.request<Project>('POST', '/api/v1/projects', { team_id: teamId, name });\n }\n\n /** Delete a project */\n async deleteProject(projectId: string): Promise<void> {\n await this.request<void>('DELETE', `/api/v1/projects/${projectId}`);\n }\n\n /** List environments in a project */\n async listEnvironments(projectId: string): Promise<Environment[]> {\n const response = await this.request<{ environments: Environment[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments`\n );\n return response.environments;\n }\n\n /** Create a new environment */\n async createEnvironment(projectId: string, name: string, inheritsFrom?: string): Promise<Environment> {\n return this.request<Environment>(\n 'POST', `/api/v1/projects/${projectId}/environments`,\n { name, inherits_from: inheritsFrom }\n );\n }\n\n /** Delete an environment */\n async deleteEnvironment(projectId: string, environment: string): Promise<void> {\n await this.request<void>('DELETE', `/api/v1/projects/${projectId}/environments/${environment}`);\n }\n\n /** List secrets in an environment (keys and metadata only) */\n async listSecrets(projectId: string, environment: string): Promise<Secret[]> {\n const response = await this.request<{ secrets: Secret[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets`\n );\n return response.secrets;\n }\n\n /**\n * Export all secrets with their decrypted values\n * @example\n * ```ts\n * const secrets = await client.exportSecrets('project-id', 'production');\n * for (const secret of secrets) {\n * process.env[secret.key] = secret.value;\n * }\n * ```\n */\n async exportSecrets(projectId: string, environment: string): Promise<SecretWithValue[]> {\n const response = await this.request<{ secrets: SecretWithValue[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets/export`\n );\n return response.secrets;\n }\n\n /**\n * Export secrets as a key-value object\n * @example\n * ```ts\n * const env = await client.exportSecretsAsObject('project-id', 'production');\n * // { DATABASE_URL: '...', API_KEY: '...' }\n * ```\n */\n async exportSecretsAsObject(projectId: string, environment: string): Promise<Record<string, string>> {\n const secrets = await this.exportSecrets(projectId, environment);\n return Object.fromEntries(secrets.map((s) => [s.key, s.value]));\n }\n\n /** Get a single secret with its value */\n async getSecret(projectId: string, environment: string, key: string): Promise<SecretWithValue> {\n const response = await this.request<{ secret: SecretWithValue }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`\n );\n return response.secret;\n }\n\n /** Create a new secret */\n async createSecret(\n projectId: string, environment: string, key: string, value: string, description?: string\n ): Promise<Secret> {\n const response = await this.request<{ secret: Secret }>(\n 'POST', `/api/v1/projects/${projectId}/environments/${environment}/secrets`,\n { key, value, description }\n );\n return response.secret;\n }\n\n /** Update a secret's value */\n async updateSecret(\n projectId: string, environment: string, key: string, value: string, description?: string\n ): Promise<Secret> {\n const response = await this.request<{ secret: Secret }>(\n 'PUT', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`,\n { value, description }\n );\n return response.secret;\n }\n\n /** Set a secret (create or update) */\n async setSecret(\n projectId: string, environment: string, key: string, value: string, description?: string\n ): Promise<Secret> {\n try {\n return await this.updateSecret(projectId, environment, key, value, description);\n } catch (error) {\n if (error instanceof KeyEnvError && error.status === 404) {\n return this.createSecret(projectId, environment, key, value, description);\n }\n throw error;\n }\n }\n\n /** Delete a secret */\n async deleteSecret(projectId: string, environment: string, key: string): Promise<void> {\n await this.request<void>(\n 'DELETE', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`\n );\n }\n\n /** Get secret version history */\n async getSecretHistory(projectId: string, environment: string, key: string): Promise<SecretHistory[]> {\n const response = await this.request<{ history: SecretHistory[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}/history`\n );\n return response.history;\n }\n\n /**\n * Bulk import secrets\n * @example\n * ```ts\n * await client.bulkImport('project-id', 'development', [\n * { key: 'DATABASE_URL', value: 'postgres://...' },\n * { key: 'API_KEY', value: 'sk_...' },\n * ], { overwrite: true });\n * ```\n */\n async bulkImport(\n projectId: string, environment: string, secrets: BulkSecretItem[], options: { overwrite?: boolean } = {}\n ): Promise<BulkImportResult> {\n return this.request<BulkImportResult>(\n 'POST', `/api/v1/projects/${projectId}/environments/${environment}/secrets/bulk`,\n { secrets, overwrite: options.overwrite ?? false }\n );\n }\n\n /**\n * Load secrets into process.env\n * @example\n * ```ts\n * await client.loadEnv('project-id', 'production');\n * console.log(process.env.DATABASE_URL);\n * ```\n */\n async loadEnv(projectId: string, environment: string): Promise<number> {\n const secrets = await this.exportSecrets(projectId, environment);\n for (const secret of secrets) {\n process.env[secret.key] = secret.value;\n }\n return secrets.length;\n }\n\n /** Generate .env file content from secrets */\n async generateEnvFile(projectId: string, environment: string): Promise<string> {\n const secrets = await this.exportSecrets(projectId, environment);\n const lines = [\n '# Generated by KeyEnv',\n `# Environment: ${environment}`,\n `# Generated at: ${new Date().toISOString()}`,\n '',\n ];\n\n for (const secret of secrets) {\n const value = secret.value;\n if (value.includes('\\n') || value.includes('\"') || value.includes(\"'\") || value.includes(' ')) {\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n lines.push(`${secret.key}=\"${escaped}\"`);\n } else {\n lines.push(`${secret.key}=${value}`);\n }\n }\n\n return lines.join('\\n') + '\\n';\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.mjs ADDED
@@ -0,0 +1,257 @@
1
+ // src/types.ts
2
+ var KeyEnvError = class extends Error {
3
+ status;
4
+ code;
5
+ details;
6
+ constructor(message, status, code, details) {
7
+ super(message);
8
+ this.name = "KeyEnvError";
9
+ this.status = status;
10
+ this.code = code;
11
+ this.details = details;
12
+ }
13
+ };
14
+
15
+ // src/client.ts
16
+ var BASE_URL = "https://api.keyenv.dev";
17
+ var DEFAULT_TIMEOUT = 3e4;
18
+ var KeyEnv = class {
19
+ token;
20
+ timeout;
21
+ constructor(options) {
22
+ if (!options.token) {
23
+ throw new Error("KeyEnv token is required");
24
+ }
25
+ this.token = options.token;
26
+ this.timeout = options.timeout || DEFAULT_TIMEOUT;
27
+ }
28
+ async request(method, path, body) {
29
+ const url = `${BASE_URL}${path}`;
30
+ const controller = new AbortController();
31
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
32
+ try {
33
+ const response = await fetch(url, {
34
+ method,
35
+ headers: {
36
+ "Authorization": `Bearer ${this.token}`,
37
+ "Content-Type": "application/json",
38
+ "User-Agent": "keyenv-node/1.0.0"
39
+ },
40
+ body: body ? JSON.stringify(body) : void 0,
41
+ signal: controller.signal
42
+ });
43
+ clearTimeout(timeoutId);
44
+ if (!response.ok) {
45
+ let errorData = { error: "Unknown error" };
46
+ try {
47
+ errorData = await response.json();
48
+ } catch {
49
+ errorData = { error: response.statusText };
50
+ }
51
+ throw new KeyEnvError(errorData.error, response.status, errorData.code, errorData.details);
52
+ }
53
+ if (response.status === 204) {
54
+ return void 0;
55
+ }
56
+ return response.json();
57
+ } catch (error) {
58
+ clearTimeout(timeoutId);
59
+ if (error instanceof KeyEnvError) throw error;
60
+ if (error instanceof Error && error.name === "AbortError") {
61
+ throw new KeyEnvError("Request timeout", 408);
62
+ }
63
+ throw new KeyEnvError(error instanceof Error ? error.message : "Network error", 0);
64
+ }
65
+ }
66
+ /** Get the current user or service token info */
67
+ async getCurrentUser() {
68
+ return this.request("GET", "/api/v1/users/me");
69
+ }
70
+ /** Validate the token and return user info */
71
+ async validateToken() {
72
+ return this.getCurrentUser();
73
+ }
74
+ /** List all accessible projects */
75
+ async listProjects() {
76
+ const response = await this.request("GET", "/api/v1/projects");
77
+ return response.projects;
78
+ }
79
+ /** Get a project by ID */
80
+ async getProject(projectId) {
81
+ return this.request("GET", `/api/v1/projects/${projectId}`);
82
+ }
83
+ /** Create a new project */
84
+ async createProject(teamId, name) {
85
+ return this.request("POST", "/api/v1/projects", { team_id: teamId, name });
86
+ }
87
+ /** Delete a project */
88
+ async deleteProject(projectId) {
89
+ await this.request("DELETE", `/api/v1/projects/${projectId}`);
90
+ }
91
+ /** List environments in a project */
92
+ async listEnvironments(projectId) {
93
+ const response = await this.request(
94
+ "GET",
95
+ `/api/v1/projects/${projectId}/environments`
96
+ );
97
+ return response.environments;
98
+ }
99
+ /** Create a new environment */
100
+ async createEnvironment(projectId, name, inheritsFrom) {
101
+ return this.request(
102
+ "POST",
103
+ `/api/v1/projects/${projectId}/environments`,
104
+ { name, inherits_from: inheritsFrom }
105
+ );
106
+ }
107
+ /** Delete an environment */
108
+ async deleteEnvironment(projectId, environment) {
109
+ await this.request("DELETE", `/api/v1/projects/${projectId}/environments/${environment}`);
110
+ }
111
+ /** List secrets in an environment (keys and metadata only) */
112
+ async listSecrets(projectId, environment) {
113
+ const response = await this.request(
114
+ "GET",
115
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets`
116
+ );
117
+ return response.secrets;
118
+ }
119
+ /**
120
+ * Export all secrets with their decrypted values
121
+ * @example
122
+ * ```ts
123
+ * const secrets = await client.exportSecrets('project-id', 'production');
124
+ * for (const secret of secrets) {
125
+ * process.env[secret.key] = secret.value;
126
+ * }
127
+ * ```
128
+ */
129
+ async exportSecrets(projectId, environment) {
130
+ const response = await this.request(
131
+ "GET",
132
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/export`
133
+ );
134
+ return response.secrets;
135
+ }
136
+ /**
137
+ * Export secrets as a key-value object
138
+ * @example
139
+ * ```ts
140
+ * const env = await client.exportSecretsAsObject('project-id', 'production');
141
+ * // { DATABASE_URL: '...', API_KEY: '...' }
142
+ * ```
143
+ */
144
+ async exportSecretsAsObject(projectId, environment) {
145
+ const secrets = await this.exportSecrets(projectId, environment);
146
+ return Object.fromEntries(secrets.map((s) => [s.key, s.value]));
147
+ }
148
+ /** Get a single secret with its value */
149
+ async getSecret(projectId, environment, key) {
150
+ const response = await this.request(
151
+ "GET",
152
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`
153
+ );
154
+ return response.secret;
155
+ }
156
+ /** Create a new secret */
157
+ async createSecret(projectId, environment, key, value, description) {
158
+ const response = await this.request(
159
+ "POST",
160
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets`,
161
+ { key, value, description }
162
+ );
163
+ return response.secret;
164
+ }
165
+ /** Update a secret's value */
166
+ async updateSecret(projectId, environment, key, value, description) {
167
+ const response = await this.request(
168
+ "PUT",
169
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`,
170
+ { value, description }
171
+ );
172
+ return response.secret;
173
+ }
174
+ /** Set a secret (create or update) */
175
+ async setSecret(projectId, environment, key, value, description) {
176
+ try {
177
+ return await this.updateSecret(projectId, environment, key, value, description);
178
+ } catch (error) {
179
+ if (error instanceof KeyEnvError && error.status === 404) {
180
+ return this.createSecret(projectId, environment, key, value, description);
181
+ }
182
+ throw error;
183
+ }
184
+ }
185
+ /** Delete a secret */
186
+ async deleteSecret(projectId, environment, key) {
187
+ await this.request(
188
+ "DELETE",
189
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`
190
+ );
191
+ }
192
+ /** Get secret version history */
193
+ async getSecretHistory(projectId, environment, key) {
194
+ const response = await this.request(
195
+ "GET",
196
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}/history`
197
+ );
198
+ return response.history;
199
+ }
200
+ /**
201
+ * Bulk import secrets
202
+ * @example
203
+ * ```ts
204
+ * await client.bulkImport('project-id', 'development', [
205
+ * { key: 'DATABASE_URL', value: 'postgres://...' },
206
+ * { key: 'API_KEY', value: 'sk_...' },
207
+ * ], { overwrite: true });
208
+ * ```
209
+ */
210
+ async bulkImport(projectId, environment, secrets, options = {}) {
211
+ return this.request(
212
+ "POST",
213
+ `/api/v1/projects/${projectId}/environments/${environment}/secrets/bulk`,
214
+ { secrets, overwrite: options.overwrite ?? false }
215
+ );
216
+ }
217
+ /**
218
+ * Load secrets into process.env
219
+ * @example
220
+ * ```ts
221
+ * await client.loadEnv('project-id', 'production');
222
+ * console.log(process.env.DATABASE_URL);
223
+ * ```
224
+ */
225
+ async loadEnv(projectId, environment) {
226
+ const secrets = await this.exportSecrets(projectId, environment);
227
+ for (const secret of secrets) {
228
+ process.env[secret.key] = secret.value;
229
+ }
230
+ return secrets.length;
231
+ }
232
+ /** Generate .env file content from secrets */
233
+ async generateEnvFile(projectId, environment) {
234
+ const secrets = await this.exportSecrets(projectId, environment);
235
+ const lines = [
236
+ "# Generated by KeyEnv",
237
+ `# Environment: ${environment}`,
238
+ `# Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
239
+ ""
240
+ ];
241
+ for (const secret of secrets) {
242
+ const value = secret.value;
243
+ if (value.includes("\n") || value.includes('"') || value.includes("'") || value.includes(" ")) {
244
+ const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
245
+ lines.push(`${secret.key}="${escaped}"`);
246
+ } else {
247
+ lines.push(`${secret.key}=${value}`);
248
+ }
249
+ }
250
+ return lines.join("\n") + "\n";
251
+ }
252
+ };
253
+ export {
254
+ KeyEnv,
255
+ KeyEnvError
256
+ };
257
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/client.ts"],"sourcesContent":["/** KeyEnv client configuration options */\nexport interface KeyEnvOptions {\n /** Service token for authentication */\n token: string;\n /** Request timeout in milliseconds (default: 30000) */\n timeout?: number;\n}\n\n/** User or service token info */\nexport interface User {\n id: string;\n email?: string;\n name?: string;\n clerk_id?: string;\n avatar_url?: string;\n /** Present for service tokens */\n auth_type?: 'service_token' | 'user';\n /** Team ID (for service tokens) */\n team_id?: string;\n /** Project ID (for project-scoped service tokens) */\n project_id?: string;\n /** Token scopes (for service tokens) */\n scopes?: string[];\n created_at: string;\n}\n\n/** Project */\nexport interface Project {\n id: string;\n team_id: string;\n name: string;\n slug: string;\n description?: string;\n created_at: string;\n}\n\n/** Environment */\nexport interface Environment {\n id: string;\n project_id: string;\n name: string;\n inherits_from?: string;\n created_at: string;\n}\n\n/** Project with environments */\nexport interface ProjectWithEnvironments extends Project {\n environments: Environment[];\n}\n\n/** Secret (without value) */\nexport interface Secret {\n id: string;\n environment_id: string;\n key: string;\n type: string;\n description?: string;\n version: number;\n created_at: string;\n updated_at: string;\n}\n\n/** Secret with decrypted value */\nexport interface SecretWithValue extends Secret {\n value: string;\n inherited_from?: string;\n}\n\n/** Secret history entry */\nexport interface SecretHistory {\n id: string;\n secret_id: string;\n value: string;\n version: number;\n changed_by?: string;\n changed_at: string;\n}\n\n/** Bulk import request item */\nexport interface BulkSecretItem {\n key: string;\n value: string;\n description?: string;\n}\n\n/** Bulk import result */\nexport interface BulkImportResult {\n created: number;\n updated: number;\n skipped: number;\n}\n\n/** API error response */\nexport interface ApiError {\n error: string;\n code?: string;\n details?: Record<string, unknown>;\n}\n\n/** KeyEnv API error */\nexport class KeyEnvError extends Error {\n public readonly status: number;\n public readonly code?: string;\n public readonly details?: Record<string, unknown>;\n\n constructor(message: string, status: number, code?: string, details?: Record<string, unknown>) {\n super(message);\n this.name = 'KeyEnvError';\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n","import type {\n KeyEnvOptions,\n User,\n Project,\n ProjectWithEnvironments,\n Environment,\n Secret,\n SecretWithValue,\n SecretHistory,\n BulkSecretItem,\n BulkImportResult,\n ApiError,\n} from './types.js';\nimport { KeyEnvError } from './types.js';\n\nconst BASE_URL = 'https://api.keyenv.dev';\nconst DEFAULT_TIMEOUT = 30000;\n\n/**\n * KeyEnv API client for managing secrets\n *\n * @example\n * ```ts\n * import { KeyEnv } from 'keyenv';\n *\n * const client = new KeyEnv({ token: process.env.KEYENV_TOKEN });\n *\n * // Export all secrets for an environment\n * const secrets = await client.exportSecrets('project-id', 'production');\n * ```\n */\nexport class KeyEnv {\n private readonly token: string;\n private readonly timeout: number;\n\n constructor(options: KeyEnvOptions) {\n if (!options.token) {\n throw new Error('KeyEnv token is required');\n }\n this.token = options.token;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${BASE_URL}${path}`;\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method,\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'keyenv-node/1.0.0',\n },\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n let errorData: ApiError = { error: 'Unknown error' };\n try {\n errorData = await response.json() as ApiError;\n } catch {\n errorData = { error: response.statusText };\n }\n throw new KeyEnvError(errorData.error, response.status, errorData.code, errorData.details);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n clearTimeout(timeoutId);\n if (error instanceof KeyEnvError) throw error;\n if (error instanceof Error && error.name === 'AbortError') {\n throw new KeyEnvError('Request timeout', 408);\n }\n throw new KeyEnvError(error instanceof Error ? error.message : 'Network error', 0);\n }\n }\n\n /** Get the current user or service token info */\n async getCurrentUser(): Promise<User> {\n return this.request<User>('GET', '/api/v1/users/me');\n }\n\n /** Validate the token and return user info */\n async validateToken(): Promise<User> {\n return this.getCurrentUser();\n }\n\n /** List all accessible projects */\n async listProjects(): Promise<Project[]> {\n const response = await this.request<{ projects: Project[] }>('GET', '/api/v1/projects');\n return response.projects;\n }\n\n /** Get a project by ID */\n async getProject(projectId: string): Promise<ProjectWithEnvironments> {\n return this.request<ProjectWithEnvironments>('GET', `/api/v1/projects/${projectId}`);\n }\n\n /** Create a new project */\n async createProject(teamId: string, name: string): Promise<Project> {\n return this.request<Project>('POST', '/api/v1/projects', { team_id: teamId, name });\n }\n\n /** Delete a project */\n async deleteProject(projectId: string): Promise<void> {\n await this.request<void>('DELETE', `/api/v1/projects/${projectId}`);\n }\n\n /** List environments in a project */\n async listEnvironments(projectId: string): Promise<Environment[]> {\n const response = await this.request<{ environments: Environment[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments`\n );\n return response.environments;\n }\n\n /** Create a new environment */\n async createEnvironment(projectId: string, name: string, inheritsFrom?: string): Promise<Environment> {\n return this.request<Environment>(\n 'POST', `/api/v1/projects/${projectId}/environments`,\n { name, inherits_from: inheritsFrom }\n );\n }\n\n /** Delete an environment */\n async deleteEnvironment(projectId: string, environment: string): Promise<void> {\n await this.request<void>('DELETE', `/api/v1/projects/${projectId}/environments/${environment}`);\n }\n\n /** List secrets in an environment (keys and metadata only) */\n async listSecrets(projectId: string, environment: string): Promise<Secret[]> {\n const response = await this.request<{ secrets: Secret[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets`\n );\n return response.secrets;\n }\n\n /**\n * Export all secrets with their decrypted values\n * @example\n * ```ts\n * const secrets = await client.exportSecrets('project-id', 'production');\n * for (const secret of secrets) {\n * process.env[secret.key] = secret.value;\n * }\n * ```\n */\n async exportSecrets(projectId: string, environment: string): Promise<SecretWithValue[]> {\n const response = await this.request<{ secrets: SecretWithValue[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets/export`\n );\n return response.secrets;\n }\n\n /**\n * Export secrets as a key-value object\n * @example\n * ```ts\n * const env = await client.exportSecretsAsObject('project-id', 'production');\n * // { DATABASE_URL: '...', API_KEY: '...' }\n * ```\n */\n async exportSecretsAsObject(projectId: string, environment: string): Promise<Record<string, string>> {\n const secrets = await this.exportSecrets(projectId, environment);\n return Object.fromEntries(secrets.map((s) => [s.key, s.value]));\n }\n\n /** Get a single secret with its value */\n async getSecret(projectId: string, environment: string, key: string): Promise<SecretWithValue> {\n const response = await this.request<{ secret: SecretWithValue }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`\n );\n return response.secret;\n }\n\n /** Create a new secret */\n async createSecret(\n projectId: string, environment: string, key: string, value: string, description?: string\n ): Promise<Secret> {\n const response = await this.request<{ secret: Secret }>(\n 'POST', `/api/v1/projects/${projectId}/environments/${environment}/secrets`,\n { key, value, description }\n );\n return response.secret;\n }\n\n /** Update a secret's value */\n async updateSecret(\n projectId: string, environment: string, key: string, value: string, description?: string\n ): Promise<Secret> {\n const response = await this.request<{ secret: Secret }>(\n 'PUT', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`,\n { value, description }\n );\n return response.secret;\n }\n\n /** Set a secret (create or update) */\n async setSecret(\n projectId: string, environment: string, key: string, value: string, description?: string\n ): Promise<Secret> {\n try {\n return await this.updateSecret(projectId, environment, key, value, description);\n } catch (error) {\n if (error instanceof KeyEnvError && error.status === 404) {\n return this.createSecret(projectId, environment, key, value, description);\n }\n throw error;\n }\n }\n\n /** Delete a secret */\n async deleteSecret(projectId: string, environment: string, key: string): Promise<void> {\n await this.request<void>(\n 'DELETE', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}`\n );\n }\n\n /** Get secret version history */\n async getSecretHistory(projectId: string, environment: string, key: string): Promise<SecretHistory[]> {\n const response = await this.request<{ history: SecretHistory[] }>(\n 'GET', `/api/v1/projects/${projectId}/environments/${environment}/secrets/${key}/history`\n );\n return response.history;\n }\n\n /**\n * Bulk import secrets\n * @example\n * ```ts\n * await client.bulkImport('project-id', 'development', [\n * { key: 'DATABASE_URL', value: 'postgres://...' },\n * { key: 'API_KEY', value: 'sk_...' },\n * ], { overwrite: true });\n * ```\n */\n async bulkImport(\n projectId: string, environment: string, secrets: BulkSecretItem[], options: { overwrite?: boolean } = {}\n ): Promise<BulkImportResult> {\n return this.request<BulkImportResult>(\n 'POST', `/api/v1/projects/${projectId}/environments/${environment}/secrets/bulk`,\n { secrets, overwrite: options.overwrite ?? false }\n );\n }\n\n /**\n * Load secrets into process.env\n * @example\n * ```ts\n * await client.loadEnv('project-id', 'production');\n * console.log(process.env.DATABASE_URL);\n * ```\n */\n async loadEnv(projectId: string, environment: string): Promise<number> {\n const secrets = await this.exportSecrets(projectId, environment);\n for (const secret of secrets) {\n process.env[secret.key] = secret.value;\n }\n return secrets.length;\n }\n\n /** Generate .env file content from secrets */\n async generateEnvFile(projectId: string, environment: string): Promise<string> {\n const secrets = await this.exportSecrets(projectId, environment);\n const lines = [\n '# Generated by KeyEnv',\n `# Environment: ${environment}`,\n `# Generated at: ${new Date().toISOString()}`,\n '',\n ];\n\n for (const secret of secrets) {\n const value = secret.value;\n if (value.includes('\\n') || value.includes('\"') || value.includes(\"'\") || value.includes(' ')) {\n const escaped = value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n lines.push(`${secret.key}=\"${escaped}\"`);\n } else {\n lines.push(`${secret.key}=${value}`);\n }\n }\n\n return lines.join('\\n') + '\\n';\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "keyenv",
3
+ "version": "0.1.0",
4
+ "description": "Official Node.js SDK for KeyEnv - Secure secrets management",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/index.js",
11
+ "import": "./dist/index.mjs",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "lint": "eslint src --ext .ts",
23
+ "typecheck": "tsc --noEmit",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "keywords": [
27
+ "keyenv",
28
+ "secrets",
29
+ "environment",
30
+ "env",
31
+ "dotenv",
32
+ "secrets-management",
33
+ "configuration"
34
+ ],
35
+ "author": "KeyEnv",
36
+ "license": "MIT",
37
+ "repository": {
38
+ "type": "git",
39
+ "url": "https://github.com/keyenv/node-sdk.git"
40
+ },
41
+ "homepage": "https://keyenv.dev",
42
+ "bugs": {
43
+ "url": "https://github.com/keyenv/node-sdk/issues"
44
+ },
45
+ "engines": {
46
+ "node": ">=18"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^22.0.0",
50
+ "tsup": "^8.0.0",
51
+ "typescript": "^5.0.0",
52
+ "vitest": "^2.0.0"
53
+ }
54
+ }