Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 26 additions & 26 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
name: Release

on:
push:
branches: [main]
push:
branches: [main]

permissions:
contents: write
issues: write
pull-requests: write
id-token: write
contents: write
issues: write
pull-requests: write
id-token: write

jobs:
release:
runs-on: blacksmith-2vcpu-ubuntu-2404-arm
if: github.repository == 'enkryptify/sdk'
release:
runs-on: blacksmith-2vcpu-ubuntu-2404-arm
if: github.repository == 'enkryptify/sdk'

steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0

- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061

- uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm

- run: pnpm install --frozen-lockfile
- run: pnpm install --frozen-lockfile

- name: Build
run: pnpm build
- name: Build
run: pnpm build

# - name: Release
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# run: npx semantic-release
# - name: Release
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# run: npx semantic-release
38 changes: 19 additions & 19 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
{
"plugins": ["typescript", "import", "unicorn"],
"categories": {
"correctness": "error",
"suspicious": "warn"
},
"rules": {
"no-console": "warn",
"eqeqeq": "error",
"typescript/consistent-type-imports": "error"
},
"overrides": [
{
"files": ["tests/**/*.ts"],
"rules": {
"no-console": "off"
}
}
],
"ignorePatterns": ["dist", "node_modules", "coverage"]
"plugins": ["typescript", "import", "unicorn"],
"categories": {
"correctness": "error",
"suspicious": "warn"
},
"rules": {
"no-console": "warn",
"eqeqeq": "error",
"typescript/consistent-type-imports": "error"
},
"overrides": [
{
"files": ["tests/**/*.ts"],
"rules": {
"no-console": "off"
}
}
],
"ignorePatterns": ["dist", "node_modules", "coverage"]
}
28 changes: 14 additions & 14 deletions .releaserc.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md", "package.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
[
"@semantic-release/git",
{
"assets": ["CHANGELOG.md", "package.json"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
]
}
20 changes: 10 additions & 10 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,22 @@ Thank you for your interest in contributing! Please read our [Code of Conduct](C

1. Clone the repository:

```bash
git clone https://github.com/enkryptify/sdk.git
cd sdk
```
```bash
git clone https://github.com/enkryptify/sdk.git
cd sdk
```

2. Install dependencies:

```bash
pnpm install
```
```bash
pnpm install
```

3. Verify your setup:

```bash
pnpm check && pnpm lint && pnpm test && pnpm build
```
```bash
pnpm check && pnpm lint && pnpm test && pnpm build
```

## Development Workflow

Expand Down
179 changes: 160 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,183 @@ npm install @enkryptify/sdk
yarn add @enkryptify/sdk
```

## Usage
## Quick Start

```typescript
import Enkryptify from "@enkryptify/sdk";

const client = new Enkryptify({
apiKey: "your-api-key",
workspaceId: "your-workspace-id",
projectId: "your-project-id",
environment: "production",
auth: Enkryptify.fromEnv(),
workspace: "my-workspace",
project: "my-project",
environment: "env-id",
});

const dbUrl = await client.get("DATABASE_URL");
console.log(dbUrl);
```

## Usage

### Preloading Secrets

When caching is enabled (the default), you can preload all secrets up front. This makes subsequent `get()` and `getFromCache()` calls instant.

```typescript
const client = new Enkryptify({
auth: Enkryptify.fromEnv(),
workspace: "my-workspace",
project: "my-project",
environment: "env-id",
});

await client.preload();

// Synchronous — no API call needed
const dbHost = client.getFromCache("DB_HOST");
const dbPort = client.getFromCache("DB_PORT");
```

### Eager Caching

By default `cache.eager` is `true`. This means the first `get()` call fetches _all_ secrets and caches them, so subsequent calls are served from the cache without additional API requests.

```typescript
// First call fetches all secrets from the API
const dbHost = await client.get("DB_HOST");

// Second call is served from cache — no API call
const dbPort = await client.get("DB_PORT");
```

Set `cache.eager` to `false` to fetch secrets individually:

```typescript
const client = new Enkryptify({
auth: Enkryptify.fromEnv(),
workspace: "my-workspace",
project: "my-project",
environment: "env-id",
cache: { eager: false },
});

// Each call fetches only the requested secret
const dbHost = await client.get("DB_HOST");
const dbPort = await client.get("DB_PORT");
```

### Bypassing the Cache

Pass `{ cache: false }` to always fetch a fresh value from the API:

```typescript
const secret = await client.get("ROTATING_KEY", { cache: false });
```

### Strict vs Non-Strict Mode

By default, `get()` throws a `SecretNotFoundError` when a key doesn't exist. Disable strict mode to return an empty string instead:

```typescript
const client = new Enkryptify({
auth: Enkryptify.fromEnv(),
workspace: "my-workspace",
project: "my-project",
environment: "env-id",
options: { strict: false },
});

const value = await client.get("MAYBE_MISSING"); // "" if not found
```

### Personal Values

When `usePersonalValues` is `true` (the default), the SDK prefers your personal override for a secret. If no personal value exists, it falls back to the shared value.

```typescript
const client = new Enkryptify({
auth: Enkryptify.fromEnv(),
workspace: "my-workspace",
project: "my-project",
environment: "env-id",
options: { usePersonalValues: false }, // always use shared values
});
```

### Cleanup

Destroy the client when you're done to clear all cached secrets from memory:

```typescript
client.destroy();
```

## Configuration

| Option | Type | Default | Description |
| --------------------------- | ---------------------------------------- | ------------------------------ | ------------------------------------------------ |
| `auth` | `EnkryptifyAuthProvider` | _required_ | Auth provider created via `Enkryptify.fromEnv()` |
| `workspace` | `string` | _required_ | Workspace slug or ID |
| `project` | `string` | _required_ | Project slug or ID |
| `environment` | `string` | _required_ | Environment ID |
| `baseUrl` | `string` | `"https://api.enkryptify.com"` | API base URL |
| `options.strict` | `boolean` | `true` | Throw on missing secrets |
| `options.usePersonalValues` | `boolean` | `true` | Prefer personal secret values |
| `cache.enabled` | `boolean` | `true` | Enable in-memory caching |
| `cache.ttl` | `number` | `-1` | Cache TTL in ms (`-1` = never expire) |
| `cache.eager` | `boolean` | `true` | Fetch all secrets on first `get()` |
| `logger.level` | `"debug" \| "info" \| "warn" \| "error"` | `"info"` | Minimum log level |

## API Reference

### `new Enkryptify(config)`
### `Enkryptify.fromEnv(): EnkryptifyAuthProvider`

Creates an auth provider by reading the `ENKRYPTIFY_TOKEN` environment variable.

### `client.get(key, options?): Promise<string>`

Fetches a secret by key. Uses the cache when available, otherwise calls the API.

Creates a new Enkryptify client.
- `key` — the secret name
- `options.cache` — set to `false` to bypass the cache (default: `true`)

| Parameter | Type | Description |
| ------------- | -------- | ------------------------------------- |
| `apiKey` | `string` | Your Enkryptify API key |
| `workspaceId` | `string` | The workspace ID |
| `projectId` | `string` | The project ID |
| `environment` | `string` | The environment (e.g. `"production"`) |
### `client.getFromCache(key): string`

### `client.get(key): Promise<string>`
Returns a secret from the cache synchronously. Throws if the key is not cached or caching is disabled.

Fetches a secret by key. Throws `EnkryptifyError` if the secret is not found.
### `client.preload(): Promise<void>`

### `EnkryptifyError`
Fetches all secrets and populates the cache. Throws if caching is disabled.

### `client.destroy(): void`

Clears the cache and marks the client as destroyed. All subsequent method calls will throw.

## Error Handling

The SDK provides specific error classes so you can handle different failure modes:

```typescript
import Enkryptify, { SecretNotFoundError, AuthenticationError, ApiError } from "@enkryptify/sdk";

try {
const value = await client.get("MY_SECRET");
} catch (error) {
if (error instanceof SecretNotFoundError) {
// Secret doesn't exist in the project/environment
} else if (error instanceof AuthenticationError) {
// Token is invalid or expired (HTTP 401/403)
} else if (error instanceof ApiError) {
// Other API error (500, network issues, etc.)
}
}
```

Custom error class for all SDK errors.
| Error Class | When |
| --------------------- | ----------------------------------------------- |
| `EnkryptifyError` | Base class for all SDK errors |
| `SecretNotFoundError` | Secret key not found in the project/environment |
| `AuthenticationError` | HTTP 401 or 403 from the API |
| `ApiError` | Any other non-OK HTTP response |

## Development

Expand All @@ -79,7 +220,7 @@ pnpm lint
pnpm format

# Typecheck
pnpm check
pnpm typecheck
Comment on lines 222 to +223
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Keep the documented typecheck command consistent.

This now diverges from CONTRIBUTING.md, which still tells contributors to run pnpm check. One of the two docs is stale, so following both instructions will fail.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 222 - 223, The README's "Typecheck" section currently
shows the command "pnpm typecheck" which conflicts with CONTRIBUTING.md's "pnpm
check"; decide which command is canonical and make them consistent—e.g., change
the README's "pnpm typecheck" line to "pnpm check" (or update CONTRIBUTING.md
instead) and also verify the corresponding npm script exists in package.json
(script name "check" or "typecheck") so both docs match the actual script.

```

## Contributing
Expand Down
Loading