{"_id":"@alvarorestrepo/envshare-sdk","_rev":"5-ccb49522f57b2772be3f811a93672b37","name":"@alvarorestrepo/envshare-sdk","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@alvarorestrepo/envshare-sdk","version":"0.1.0","keywords":["envshare","env","environment-variables","secrets","sdk"],"license":"MIT","_id":"@alvarorestrepo/envshare-sdk@0.1.0","maintainers":[{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"}],"dist":{"shasum":"ae158003125fcd89fab5b16b6379c0b007c9bce1","tarball":"https://registry.npmjs.org/@alvarorestrepo/envshare-sdk/-/envshare-sdk-0.1.0.tgz","fileCount":8,"integrity":"sha512-ug9e8o+3a8IFuoNj7wivKfUCNsGABbdpt4CiKw7j+T3Y0vVxMLcBQN/0u2fHlUcJNbfYwKXeDRcak3VgoR6ENA==","signatures":[{"sig":"MEUCIQD4Cl4sZ0Pkaki9WGdmrmV8qk/QONqW9j3pYBZHe1lO0gIgYFaT5R2qLqF4stmUFEZShnOJZSJ2oJAqqHwRFAcOSWM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57460},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./preload":{"types":"./dist/preload.d.ts","import":"./dist/preload.mjs"}},"gitHead":"d5c76ee4af0acfb744e3c936cf29ac9304a87738","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:watch":"vitest"},"_npmUser":{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"},"_npmVersion":"10.9.4","description":"EnvShare SDK — Runtime environment variable injection","directories":{},"_nodeVersion":"22.22.1","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.1.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/envshare-sdk_0.1.0_1774027978459_0.8413812769266564","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@alvarorestrepo/envshare-sdk","version":"0.1.1","keywords":["envshare","env","environment-variables","secrets","sdk"],"license":"MIT","_id":"@alvarorestrepo/envshare-sdk@0.1.1","maintainers":[{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"}],"dist":{"shasum":"c45c13c588dd75f59fd7d5984513bebf0c270370","tarball":"https://registry.npmjs.org/@alvarorestrepo/envshare-sdk/-/envshare-sdk-0.1.1.tgz","fileCount":8,"integrity":"sha512-dxJPjfNDCrrLPDa3EFOjVlkHpr5I8GiFxzRV5xdVgX+loz2ZPrM6FT4mEhgBYqph9+49Jd8MtsuCo29jE4tqjQ==","signatures":[{"sig":"MEUCIBFZdBBpiHPC0i1bM2966xaAtco0yE0WsHjbJgm2ciWAAiEAqRdm5AjhiT4AtuB0Tt6aznXZXeu3AknvEAlBctYZ6Zk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":57524},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./preload":{"types":"./dist/preload.d.ts","import":"./dist/preload.mjs"}},"gitHead":"d5c76ee4af0acfb744e3c936cf29ac9304a87738","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:watch":"vitest"},"_npmUser":{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"},"_npmVersion":"10.9.4","description":"EnvShare SDK — Runtime environment variable injection","directories":{},"_nodeVersion":"22.22.1","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.1.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/envshare-sdk_0.1.1_1774028447769_0.2545836984572507","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@alvarorestrepo/envshare-sdk","version":"0.1.2","keywords":["envshare","env","environment-variables","secrets","sdk"],"license":"MIT","_id":"@alvarorestrepo/envshare-sdk@0.1.2","maintainers":[{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"}],"dist":{"shasum":"ed47c228416f71a9ea22013f75526db9f92e6b11","tarball":"https://registry.npmjs.org/@alvarorestrepo/envshare-sdk/-/envshare-sdk-0.1.2.tgz","fileCount":8,"integrity":"sha512-Kw5pmSh6vLyjKseDutJT6KTIilwDQ2P3B3/C8Mbk5kZLw8IuGvuk86VjPslmqjoWTJX3CdeglAFIzGpfJdBewA==","signatures":[{"sig":"MEUCIB2nWC+fWIFArVJpZ8PxjVGoPEwSMa+8WAsrqZcU0ocuAiEAtcWUf7y/le+/HFJPY0fqniaVdvBkN59BKeYbl3+gVDk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":73751},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./preload":{"types":"./dist/preload.d.ts","import":"./dist/preload.mjs"}},"gitHead":"da292e96ead5bd99e74e77c1d38c6635b7323cc3","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:watch":"vitest"},"_npmUser":{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"},"_npmVersion":"10.9.4","description":"EnvShare SDK — Runtime environment variable injection","directories":{},"_nodeVersion":"22.22.1","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.1.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/envshare-sdk_0.1.2_1774028770394_0.16976262381615737","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@alvarorestrepo/envshare-sdk","version":"0.1.3","keywords":["envshare","env","environment-variables","secrets","sdk"],"license":"MIT","_id":"@alvarorestrepo/envshare-sdk@0.1.3","maintainers":[{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"}],"dist":{"shasum":"4f558e6d1cc29ed636ca79a4d8a9958d24c9b46f","tarball":"https://registry.npmjs.org/@alvarorestrepo/envshare-sdk/-/envshare-sdk-0.1.3.tgz","fileCount":8,"integrity":"sha512-dwIf3gAXFKsCobknkIG4geR3GoCY3QcQIE9GQ0uUJBZOMNSruCIOqWfGRqqktP+Nu9ZJe/B3OL8gLnUCdhs3uA==","signatures":[{"sig":"MEQCIFAKNLjFWD37+KhiyarHxujEVMTz37OjMsh5mGkAlJd/AiBTDwXdH0LXuDPq7qv/r2PLWwzqJqRVI4OJtaXwlLSBzA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81946},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./preload":{"types":"./dist/preload.d.ts","import":"./dist/preload.mjs"}},"gitHead":"0c713dd1cc0996b13853fe3f4da098aa022dfd2b","scripts":{"lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:watch":"vitest"},"_npmUser":{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"},"_npmVersion":"10.9.4","description":"EnvShare SDK — Runtime environment variable injection","directories":{},"_nodeVersion":"22.22.1","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.1.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/envshare-sdk_0.1.3_1774036049227_0.5181663981862916","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alvarorestrepo/envshare-sdk","version":"0.2.0","description":"EnvShare SDK — Runtime environment variable injection","type":"module","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.cjs"},"./preload":{"types":"./dist/preload.d.ts","import":"./dist/preload.mjs"}},"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.ts","engines":{"node":">=18.0.0"},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","lint":"tsc --noEmit"},"devDependencies":{"@types/react":"^19.2.14","tsup":"^8.4.0","typescript":"^5.7.0","vitest":"^3.1.0"},"keywords":["envshare","env","environment-variables","secrets","sdk"],"peerDependencies":{"react":">=18.0.0"},"peerDependenciesMeta":{"react":{"optional":true}},"license":"MIT","_id":"@alvarorestrepo/envshare-sdk@0.2.0","gitHead":"e89202095e668783a758e88537fe65b1b435396e","_nodeVersion":"22.22.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-bKpqr8cy5yE0eFOZMXTnUOZAbasD6zcCXID+67PBtFhgduRSevx8bxINqGltLG+Fh0bwV2EHjn1Kte3OfUhBKw==","shasum":"27d53ba0876c3adac23fbe9e84cb4932df5bcdfa","tarball":"https://registry.npmjs.org/@alvarorestrepo/envshare-sdk/-/envshare-sdk-0.2.0.tgz","fileCount":8,"unpackedSize":95635,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDX3YpOK6oLcO5TBbS9rZ7HY1ZgAyNuMFDhCVg2hqquPAiEA2Ytf8g3SyLn40VqY2xMzPMxegilggxP3xtuu9j/BgA0="}]},"_npmUser":{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"},"directories":{},"maintainers":[{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/envshare-sdk_0.2.0_1774046914894_0.4122014129115239"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-20T17:32:58.319Z","modified":"2026-03-20T22:48:35.171Z","0.1.0":"2026-03-20T17:32:58.638Z","0.1.1":"2026-03-20T17:40:47.926Z","0.1.2":"2026-03-20T17:46:10.543Z","0.1.3":"2026-03-20T19:47:29.399Z","0.2.0":"2026-03-20T22:48:35.051Z"},"license":"MIT","keywords":["envshare","env","environment-variables","secrets","sdk"],"description":"EnvShare SDK — Runtime environment variable injection","maintainers":[{"name":"goodevpro","email":"alvarorestrepoz@gmail.com"}],"readme":"# @alvarorestrepo/envshare-sdk\n\nRuntime environment variable injection for Node.js — no `.env` files on disk.\n\n[![npm version](https://img.shields.io/npm/v/@alvarorestrepo/envshare-sdk.svg)](https://www.npmjs.com/package/@alvarorestrepo/envshare-sdk)\n[![license](https://img.shields.io/npm/l/@alvarorestrepo/envshare-sdk.svg)](https://github.com/GooDevPro/envShare/blob/main/LICENSE)\n[![node](https://img.shields.io/node/v/@alvarorestrepo/envshare-sdk.svg)](https://nodejs.org/)\n\n---\n\n## The Problem\n\nEvery team has been here:\n\n- **`.env` files get committed by accident** — one `git add .` and your production database credentials are in the repo history forever.\n- **Sharing secrets over Slack or email** — \"hey can you send me the staging API key?\" Copy-paste. Screenshot. Forwarded. Zero control.\n- **No audit trail** — who accessed the production secrets? When? From where? Nobody knows.\n- **Variables drift out of sync** — one developer updates a key locally, forgets to tell the team. Three hours of debugging later: \"oh, the API key rotated.\"\n- **No per-environment access control** — the intern has the same production credentials as the lead engineer.\n\n`.env` files were designed for local convenience. They were never meant to be a secrets management system.\n\n---\n\n## The Solution — How EnvShare Works\n\nEnvShare replaces `.env` files with a centralized, encrypted, access-controlled platform. The SDK fetches your variables at runtime and injects them directly into `process.env` — nothing is ever written to disk.\n\n```\n┌─────────────────────────────────────────────────────────┐\n│                   EnvShare Platform                      │\n│              envshared.vercel.app                         │\n│                                                          │\n│  ┌─────────┐  ┌─────────┐  ┌─────────┐                  │\n│  │   Dev   │  │ Staging │  │  Prod   │  ← Environments  │\n│  │ 12 vars │  │ 12 vars │  │ 12 vars │                  │\n│  └────┬────┘  └────┬────┘  └────┬────┘                  │\n│       │            │            │                         │\n│  ┌────┴────────────┴────────────┴────┐                   │\n│  │     AES-256-GCM Encryption        │                   │\n│  │     Per-project derived keys      │                   │\n│  └────────────────┬──────────────────┘                   │\n│                   │                                       │\n│  ┌────────────────┴──────────────────┐                   │\n│  │        REST API + Auth            │                   │\n│  │  API Key + IP Allowlist + Audit   │                   │\n│  └────────────────┬──────────────────┘                   │\n└───────────────────┼──────────────────────────────────────┘\n                    │\n          ┌─────────┴─────────┐\n          │  @alvarorestrepo/  │\n          │  envshare-sdk      │\n          │                    │\n          │  • init()          │\n          │  • ETag caching    │\n          │  • Auto-retry      │\n          │  • Stale fallback  │\n          └─────────┬─────────┘\n                    │\n                    ▼\n            ┌──────────────┐\n            │  Your App    │\n            │              │\n            │ process.env  │\n            │ .DATABASE_URL│\n            │ .API_SECRET  │\n            │ .REDIS_URL   │\n            └──────────────┘\n```\n\nYour app calls `init()` once at startup. The SDK authenticates with your API key, verifies your IP is in the allowlist, decrypts the variables server-side, and injects them into `process.env`. From that point on, your code reads `process.env.DATABASE_URL` exactly like it would with a `.env` file — except the secret was never on disk.\n\n---\n\n## ⚠️ Prerequisites — Read This First\n\n**Before your SDK calls will work, you MUST configure both an API key and the IP allowlist on the platform.**\n\nEnvShare uses a **two-layer security model**. Both layers must pass for the SDK to receive variables:\n\n1. **API Key** — validates you have access to the **project**\n2. **IP Allowlist** — validates your machine/server has access to the specific **environment**\n\n### Setup Steps\n\n1. Go to [envshared.vercel.app](https://envshared.vercel.app) and create an account\n2. Create a **project** (e.g., `my-app`)\n3. Add your **environments** (development, staging, production)\n4. Add your **environment variables** — they are encrypted with AES-256-GCM before storage\n5. Go to **Settings → API Keys** → Generate a key (copy it — it's shown only once)\n6. Go to **Settings → IP Configuration** → Add the IPs of your machines/servers:\n   - **Local development**: your public IP — find it at [ifconfig.me](https://ifconfig.me)\n   - **CI/CD runners**: your provider's IP ranges (e.g., [GitHub Actions IP ranges](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#ip-addresses))\n   - **Production servers**: your server's static IP or load balancer IP\n7. **Wait for IP approval** — if your role is Developer, an Owner or Admin must approve your IP request\n8. Now your SDK calls will work\n\n### How the Two Layers Work\n\n```\nYour Machine (IP: 203.0.113.42)\n       │\n       ▼\n  ┌──────────────┐\n  │  API Key     │──→ \"Do you have access to this PROJECT?\"\n  │  Validation  │     ✅ Valid key, not revoked\n  └──────┬───────┘\n         │\n         ▼\n  ┌──────────────┐\n  │ IP Allowlist │──→ \"Is your IP approved for this ENVIRONMENT?\"\n  │    Check     │     ✅ 203.0.113.42 is in the allowlist\n  └──────┬───────┘\n         │\n         ▼\n  ┌──────────────┐\n  │  Decrypt &   │──→ Variables decrypted with AES-256-GCM\n  │   Respond    │     Returned to your app\n  └──────────────┘\n```\n\n> **Important:** If the IP allowlist for an environment is empty, access is **DENIED** — not open. This is by design. You must explicitly add at least one IP to access any environment.\n\n### Common Errors During Setup\n\n| Error | Meaning | Fix |\n|-------|---------|-----|\n| `AuthError` (401) | API key is invalid or revoked | Generate a new key in Settings → API Keys |\n| `ForbiddenError` (403) | Your IP is not in the allowlist | Add your IP in Settings → IP Configuration |\n| `NotFoundError` (404) | Project or environment doesn't exist | Check spelling — slugs are case-sensitive |\n\n---\n\n## Quick Start\n\n### Installation\n\n```bash\nnpm install @alvarorestrepo/envshare-sdk\n```\n\n### Option 1: Programmatic (recommended)\n\n```typescript\nimport { init } from '@alvarorestrepo/envshare-sdk';\n\nawait init({\n  apiKey: process.env.ENVSHARE_API_KEY!,\n  project: 'my-app',\n  environment: 'production',\n});\n\n// process.env now has all your variables\nconsole.log(process.env.DATABASE_URL);\nconsole.log(process.env.API_SECRET);\n```\n\n### Option 2: Preload (zero code changes)\n\nSet the required environment variables:\n\n```bash\nexport ENVSHARE_API_KEY=es_live_xxxxx\nexport ENVSHARE_PROJECT=my-app\nexport ENVSHARE_ENVIRONMENT=production\n```\n\nRun your app with `--import`:\n\n```bash\nnode --import @alvarorestrepo/envshare-sdk/preload app.js\n```\n\nVariables are fetched and injected into `process.env` before your application code runs.\n\n---\n\n## Configuration Reference\n\n| Option | Type | Default | Env Var Fallback | Description |\n|--------|------|---------|------------------|-------------|\n| `apiKey` | `string` | — | `ENVSHARE_API_KEY` | API key (starts with `es_live_`) |\n| `project` | `string` | — | `ENVSHARE_PROJECT` | Project slug |\n| `environment` | `string` | — | `ENVSHARE_ENVIRONMENT` | Environment name (`development`, `staging`, `production`) |\n| `apiUrl` | `string` | `https://envshared.vercel.app` | `ENVSHARE_API_URL` | Base URL of the EnvShare API |\n| `cacheTtl` | `number` | `300_000` (5 min) | — | Cache TTL in milliseconds |\n| `maxStale` | `number` | `3_600_000` (1 hr) | — | Stale cache window in milliseconds |\n| `timeout` | `number` | `10_000` (10s) | — | Request timeout in milliseconds |\n| `maxRetries` | `number` | `3` | — | Max retry attempts for retryable errors |\n| `inject` | `boolean` | `true` | — | Inject variables into `process.env` |\n| `logger` | `Logger` | `noopLogger` | — | Logger instance (silent by default) |\n\nFull example:\n\n```typescript\nimport { init } from '@alvarorestrepo/envshare-sdk';\n\nawait init({\n  apiKey: 'es_live_xxxxx',\n  project: 'my-app',\n  environment: 'production',\n  apiUrl: 'https://envshared.vercel.app',\n  cacheTtl: 300_000,\n  maxStale: 3_600_000,\n  timeout: 10_000,\n  maxRetries: 3,\n  inject: true,\n});\n```\n\n---\n\n## API Reference\n\n### `init(config): Promise<FetchResult>`\n\nInitialize the SDK, fetch variables from the EnvShare API, and optionally inject them into `process.env`.\n\n```typescript\nconst result = await init({\n  apiKey: 'es_live_xxxxx',\n  project: 'my-app',\n  environment: 'production',\n});\n\nconsole.log(result.variables);  // { DATABASE_URL: '...', API_SECRET: '...' }\nconsole.log(result.fromCache);  // false (first call is always fresh)\nconsole.log(result.etag);       // \"abc123\" (used for cache revalidation)\n```\n\n**Returns:** `{ variables: Record<string, string>, fromCache: boolean, etag: string | null }`\n\n### `fetch(): Promise<FetchResult>`\n\nFetch variables without reinitializing. Uses in-memory cache and ETag revalidation to minimize network requests.\n\n```typescript\nconst result = await fetch();\n// If called within cacheTtl, returns cached data without hitting the network\n```\n\n### `clear(): void`\n\nClear the in-memory cache, remove all injected variables from `process.env`, and reset the SDK state. Useful for testing or graceful shutdown.\n\n```typescript\nclear();\n// Cache purged, process.env cleaned, SDK reset\n```\n\n### `isInitialized(): boolean`\n\nCheck whether the SDK has been initialized.\n\n```typescript\nif (!isInitialized()) {\n  await init({ ... });\n}\n```\n\n---\n\n## Caching & Resilience\n\nThe SDK uses an in-memory cache with ETag revalidation and stale-while-revalidate semantics. This means your app stays resilient even if the EnvShare API is temporarily unavailable.\n\n- **Zero dependencies** — uses native `fetch` (Node 18+)\n- **ETag revalidation** — only downloads variables when they've actually changed (HTTP 304)\n- **Stale fallback** — if the API is down, the SDK returns the last known good values\n- **Automatic retry** — retryable errors (network, timeout, rate limit) are retried with exponential backoff\n\n```\nRequest Flow:\n\n  fetch() called\n       │\n       ▼\n  ┌──────────┐  YES   ┌───────────┐\n  │ Cache    ├───────→│ Return    │\n  │ fresh?   │        │ cached    │\n  └────┬─────┘        └───────────┘\n       │ NO\n       ▼\n  ┌──────────┐  200   ┌───────────┐\n  │ Call API ├───────→│ Update    │\n  │ w/ ETag  │        │ cache     │\n  └────┬─────┘        └───────────┘\n       │ 304\n       ▼\n  ┌──────────┐        ┌───────────┐\n  │ Not      ├───────→│ Refresh   │\n  │ Modified │        │ TTL       │\n  └────┬─────┘        └───────────┘\n       │ ERROR\n       ▼\n  ┌──────────┐  YES   ┌───────────┐\n  │ Stale    ├───────→│ Return    │\n  │ cache?   │        │ stale     │\n  └────┬─────┘        └───────────┘\n       │ NO\n       ▼\n  ┌──────────┐\n  │  THROW   │\n  │  Error   │\n  └──────────┘\n```\n\n**Cache timeline:**\n\n```\n├── cacheTtl (5 min default) ──┤── maxStale (1 hr default) ──┤\n│        FRESH                 │         STALE                │  EXPIRED\n│   Return instantly           │   Try API, fallback to cache │  Must fetch\n```\n\n---\n\n## Error Handling\n\nAll SDK errors extend `EnvShareError`, so you can catch all SDK errors in one block or handle specific types:\n\n```typescript\nimport {\n  init,\n  EnvShareError,\n  AuthError,\n  ForbiddenError,\n  NetworkError,\n  RateLimitError,\n  NotFoundError,\n  ConfigError,\n  TimeoutError,\n} from '@alvarorestrepo/envshare-sdk';\n\ntry {\n  await init({\n    apiKey: process.env.ENVSHARE_API_KEY!,\n    project: 'my-app',\n    environment: 'production',\n  });\n} catch (error) {\n  if (error instanceof AuthError) {\n    // 401 — API key is invalid, revoked, or expired\n    console.error('Invalid API key. Generate a new one at envshared.vercel.app');\n  } else if (error instanceof ForbiddenError) {\n    // 403 — Your IP is not in the allowlist for this environment\n    console.error('IP not allowed. Add your IP in Settings → IP Configuration');\n  } else if (error instanceof NotFoundError) {\n    // 404 — Project or environment doesn't exist\n    console.error('Project or environment not found. Check the slug spelling.');\n  } else if (error instanceof RateLimitError) {\n    // 429 — Too many requests\n    console.error(`Rate limited. Retry after ${error.retryAfter}s`);\n  } else if (error instanceof NetworkError) {\n    // DNS failure, connection refused, etc.\n    console.error('Cannot reach EnvShare API');\n  } else if (error instanceof TimeoutError) {\n    console.error('Request timed out');\n  } else if (error instanceof ConfigError) {\n    console.error('Invalid SDK configuration');\n  }\n}\n```\n\n### Error Types\n\n| Error | Code | HTTP | Retryable | Cause |\n|-------|------|------|-----------|-------|\n| `ConfigError` | `CONFIG_ERROR` | — | No | Invalid or missing configuration |\n| `AuthError` | `AUTH_ERROR` | 401 | No | Invalid, revoked, or expired API key |\n| `ForbiddenError` | `FORBIDDEN_ERROR` | 403 | No | IP not in allowlist for this environment |\n| `NotFoundError` | `NOT_FOUND_ERROR` | 404 | No | Project or environment not found |\n| `RateLimitError` | `RATE_LIMIT_ERROR` | 429 | Yes | Too many requests (has `retryAfter` property) |\n| `NetworkError` | `NETWORK_ERROR` | — | Yes | Connection/DNS failure |\n| `TimeoutError` | `TIMEOUT_ERROR` | — | Yes | Request exceeded timeout |\n\nRetryable errors are automatically retried up to `maxRetries` times with exponential backoff before throwing.\n\n---\n\n## CI/CD Examples\n\n### GitHub Actions\n\n```yaml\nsteps:\n  - name: Install dependencies\n    run: npm ci\n\n  - name: Start app with EnvShare\n    env:\n      ENVSHARE_API_KEY: ${{ secrets.ENVSHARE_API_KEY }}\n      ENVSHARE_PROJECT: my-app\n      ENVSHARE_ENVIRONMENT: staging\n    run: node --import @alvarorestrepo/envshare-sdk/preload app.js\n```\n\n> **Note:** You must add your GitHub Actions runner IPs to the IP allowlist for the target environment. GitHub publishes their runner IP ranges in their [documentation](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#ip-addresses). Alternatively, use a self-hosted runner with a static IP.\n\n### Docker\n\n```dockerfile\nFROM node:22-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --production\nCOPY . .\n\nENV ENVSHARE_PROJECT=my-app\nENV ENVSHARE_ENVIRONMENT=production\n\n# Pass ENVSHARE_API_KEY at runtime, not build time:\n# docker run -e ENVSHARE_API_KEY=es_live_xxxxx my-app\nCMD [\"node\", \"--import\", \"@alvarorestrepo/envshare-sdk/preload\", \"app.js\"]\n```\n\n> **Note:** Never bake `ENVSHARE_API_KEY` into a Docker image. Pass it at runtime via `-e` or your orchestrator's secrets management (ECS task definition, Kubernetes secret, etc.).\n\n### Programmatic in CI\n\nIf you prefer programmatic initialization over preload:\n\n```typescript\n// bootstrap.ts — runs before your app\nimport { init } from '@alvarorestrepo/envshare-sdk';\n\nawait init({\n  apiKey: process.env.ENVSHARE_API_KEY!,\n  project: process.env.ENVSHARE_PROJECT!,\n  environment: process.env.ENVSHARE_ENVIRONMENT!,\n});\n\n// Now start your app\nawait import('./app.js');\n```\n\n---\n\n## 🔒 Security Model\n\nEnvShare is built with defense-in-depth. Multiple security layers protect your secrets:\n\n### Encryption at Rest\n\nAll environment variables are encrypted with **AES-256-GCM** before being stored in the database. Each project uses a unique encryption key derived via **HKDF** (HMAC-based Key Derivation Function) from a master key. Even if the database is compromised, the variables are unreadable without the master key.\n\n### API Key Security\n\nAPI keys are **SHA-256 hashed** before storage. The raw key (starting with `es_live_`) is shown exactly once when generated — it is never stored or retrievable again. If a key is compromised, it can be revoked instantly from the platform.\n\n### IP Allowlist\n\nEvery environment has an independent IP allowlist. A valid API key alone is not enough — the request must also originate from an approved IP address. This prevents stolen API keys from being used outside your infrastructure.\n\n### Role-Based Access Control\n\n| Role | Can manage variables | Can manage API keys | Can approve IPs | Can invite members |\n|------|---------------------|--------------------|-----------------|--------------------|\n| Owner | Yes | Yes | Yes | Yes |\n| Admin | Yes | Yes | Yes | Yes |\n| Developer | Yes | No | No (must request) | No |\n\nDevelopers can request IP access, but an Owner or Admin must approve it.\n\n### Audit Logging\n\nEvery API access, key generation, key revocation, IP approval, and variable change is recorded in an audit log with:\n- Who performed the action\n- When it happened\n- From which IP address\n- What was affected\n\n### Rate Limiting\n\nThe API enforces rate limits per IP address and per API key to prevent abuse and brute-force attacks.\n\n---\n\nAll of this is managed through the [EnvShare Platform](https://envshared.vercel.app). Sign up to get started.\n\n---\n\n## Comparison: `.env` Files vs EnvShare SDK\n\n| Feature | `.env` files | EnvShare SDK |\n|---------|-------------|--------------|\n| Secrets on disk | Yes (risk) | Never |\n| Accidental git commits | Common risk | Impossible |\n| Sharing secrets | Slack/email | Encrypted platform |\n| Audit trail | None | Full audit log |\n| Access control | None | Per-environment IP allowlist |\n| Auto-sync across team | Manual | Automatic on restart |\n| Encryption at rest | No | AES-256-GCM |\n| Revoke access | Change all passwords | Revoke key instantly |\n| Per-environment isolation | One `.env` per environment, manually managed | Built-in, platform-managed |\n| Zero dependencies | Requires `dotenv` | Native `fetch` (Node 18+) |\n\n---\n\n## Features\n\n- **Zero dependencies** — uses native `fetch` (Node 18+)\n- **In-memory cache** with ETag revalidation\n- **Stale-while-revalidate** for resilience when the API is down\n- **Automatic retry** with exponential backoff for transient errors\n- **TypeScript-first** with full type definitions\n- **ESM + CommonJS** dual publish\n- **Preload mode** — zero-code setup with `--import`\n\n---\n\n## EnvBridge — Runtime Next.js Support\n\n### The Problem: `NEXT_PUBLIC_*` and Build-Time Replacement\n\nNext.js performs **static text replacement** on `process.env.NEXT_PUBLIC_*` references at build time. The reference itself is removed from your code:\n\n```js\n// What you write\nconst url = process.env.NEXT_PUBLIC_API_URL;\n\n// What Next.js outputs after build (the variable reference is GONE)\nconst url = \"https://api.example.com\";\n```\n\nThe EnvShare SDK injects variables into `process.env` at **runtime** — but by the time your app runs on the client, those `NEXT_PUBLIC_*` references have already been replaced with whatever value was available at build time (or `undefined` if nothing was set).\n\n```\nBUILD TIME (next build)                    RUNTIME (node server.js)\n─────────────────────────                  ────────────────────────\nprocess.env.NEXT_PUBLIC_API_URL            EnvShare SDK injects\n    → replaced with literal \"...\"              → process.env.NEXT_PUBLIC_API_URL = \"https://...\"\n    → original reference is GONE               → but client code already has the literal\n```\n\n**Result:** Server Components work fine (they read `process.env` at runtime). Client Components get stale build-time values.\n\n### The Solution: EnvBridge + `env()`\n\nThe SDK provides three exports to solve this:\n\n| Export | Type | Purpose |\n|--------|------|---------|\n| `EnvBridge` | React Server Component | Reads `NEXT_PUBLIC_*` from runtime `process.env` and injects them into `window.__ENVSHARE` via a `<script>` tag |\n| `env(key)` | Function | Universal accessor — reads from `process.env` on the server, from `window.__ENVSHARE` on the client |\n| `envRequired(key)` | Function | Same as `env()` but throws a descriptive error if the variable is not defined |\n\n### Setup\n\n**Step 1: Add `EnvBridge` to your root layout**\n\n```tsx\n// app/layout.tsx\nimport { EnvBridge } from '@alvarorestrepo/envshare-sdk';\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html>\n      <head>\n        <EnvBridge />\n      </head>\n      <body>{children}</body>\n    </html>\n  );\n}\n```\n\n**Step 2: Use `env()` instead of `process.env` in Client Components**\n\n```tsx\n'use client';\nimport { env } from '@alvarorestrepo/envshare-sdk';\n\nexport function ApiStatus() {\n  const apiUrl = env('NEXT_PUBLIC_API_URL');\n  return <p>API: {apiUrl}</p>;\n}\n```\n\nThat's it. `EnvBridge` renders at the server, `env()` reads the injected values on the client.\n\n### Usage Examples\n\n**Basic usage in a Client Component:**\n\n```tsx\n'use client';\nimport { env } from '@alvarorestrepo/envshare-sdk';\n\nexport function Analytics() {\n  const trackingId = env('NEXT_PUBLIC_TRACKING_ID');\n  const apiUrl = env('NEXT_PUBLIC_API_URL');\n\n  // Both values come from EnvShare runtime injection,\n  // not from build-time replacement\n  return <script src={`https://analytics.example.com/${trackingId}`} />;\n}\n```\n\n**Using `envRequired()` for mandatory variables:**\n\n```tsx\n'use client';\nimport { envRequired } from '@alvarorestrepo/envshare-sdk';\n\nexport function PaymentForm() {\n  // Throws with a helpful error if NEXT_PUBLIC_STRIPE_KEY is not set:\n  // \"[envshare] Required environment variable \"NEXT_PUBLIC_STRIPE_KEY\" is not defined.\n  //  Make sure EnvShare SDK is loaded and EnvBridge is in your root layout.\"\n  const stripeKey = envRequired('NEXT_PUBLIC_STRIPE_KEY');\n\n  return <div data-stripe-key={stripeKey}>...</div>;\n}\n```\n\n**Server Components — just use `process.env` directly:**\n\n```tsx\n// app/dashboard/page.tsx (Server Component — no 'use client')\nexport default async function DashboardPage() {\n  // Server Components read process.env at runtime — no EnvBridge needed\n  const apiUrl = process.env.NEXT_PUBLIC_API_URL;\n  const secret = process.env.DATABASE_URL; // non-public vars too\n\n  const data = await fetch(apiUrl + '/stats');\n  // ...\n}\n```\n\nYou can also use `env()` in Server Components — it reads `process.env` on the server, so it works the same way:\n\n```tsx\nimport { env } from '@alvarorestrepo/envshare-sdk';\n\nexport default async function DashboardPage() {\n  const apiUrl = env('NEXT_PUBLIC_API_URL'); // reads process.env on server\n  // ...\n}\n```\n\n**Custom prefix:**\n\nIf your app uses a different prefix convention, pass it to `EnvBridge`:\n\n```tsx\n<EnvBridge prefix=\"MY_APP_\" />\n```\n\nThis will collect all `MY_APP_*` variables from `process.env` and inject them into `window.__ENVSHARE`. The `env()` helper reads from `window.__ENVSHARE` for keys starting with `NEXT_PUBLIC_` by default — for custom prefixes, access `window.__ENVSHARE` directly on the client or use `env()` on the server.\n\n**TypeScript usage:**\n\nThe `env()` and `envRequired()` functions are fully typed:\n\n```typescript\nimport { env, envRequired } from '@alvarorestrepo/envshare-sdk';\n\n// env() returns string | undefined\nconst apiUrl: string | undefined = env('NEXT_PUBLIC_API_URL');\n\n// envRequired() returns string (throws if undefined)\nconst stripeKey: string = envRequired('NEXT_PUBLIC_STRIPE_KEY');\n\n// Type-safe wrapper for your app's env vars\nfunction getConfig() {\n  return {\n    apiUrl: envRequired('NEXT_PUBLIC_API_URL'),\n    analyticsId: env('NEXT_PUBLIC_ANALYTICS_ID') ?? 'default',\n    debug: env('NEXT_PUBLIC_DEBUG') === 'true',\n  } as const;\n}\n```\n\n### How It Works\n\n```\n1. Server startup\n   ┌────────────────────────────────┐\n   │  EnvShare SDK init()           │\n   │  → fetches vars from API      │\n   │  → injects into process.env   │\n   └──────────────┬─────────────────┘\n                  │\n2. SSR render (every request)\n   ┌──────────────┴─────────────────┐\n   │  <EnvBridge /> (Server Component)│\n   │  → reads NEXT_PUBLIC_* from    │\n   │    process.env at runtime      │\n   │  → renders <script> tag with   │\n   │    JSON-serialized values      │\n   └──────────────┬─────────────────┘\n                  │\n3. Client hydration\n   ┌──────────────┴─────────────────┐\n   │  Browser executes <script>     │\n   │  → window.__ENVSHARE = {...}   │\n   └──────────────┬─────────────────┘\n                  │\n4. Client Components\n   ┌──────────────┴─────────────────┐\n   │  env('NEXT_PUBLIC_API_URL')    │\n   │  → reads window.__ENVSHARE     │\n   │  → returns runtime value       │\n   └────────────────────────────────┘\n```\n\n### Comparison\n\n| Approach | Server Components | Client Components | No extra HTTP | Immediate |\n|----------|:-:|:-:|:-:|:-:|\n| `process.env` (build-time) | ✅ | ❌ runtime vars | ✅ | ✅ |\n| EnvBridge + `env()` | ✅ | ✅ | ✅ | ✅ |\n| API route | ✅ | ✅ | ❌ | ❌ |\n\n### Important Notes\n\n- **Replace `process.env.NEXT_PUBLIC_*` with `env('NEXT_PUBLIC_*')` in Client Components.** This is the only change needed — `env()` handles the server/client logic automatically.\n- **Server Components can still use `process.env` directly.** They run at request time and read the runtime values without any bridge.\n- **`EnvBridge` must be in the root layout** (`app/layout.tsx`). If it's only in a nested layout, pages outside that layout won't have access to the injected values.\n- **React 18+ is required** for `EnvBridge` (it's a React Server Component). React is an optional peer dependency of the SDK.\n- **Don't put secrets in `NEXT_PUBLIC_*` variables.** `EnvBridge` serializes values as JSON in the HTML — they are visible in the page source. This is the same behavior as Next.js build-time replacement. Only use `NEXT_PUBLIC_*` for values that are safe to expose to the browser.\n\n---\n\n## Requirements\n\n- **Node.js** >= 18.0.0\n- An [EnvShare](https://envshared.vercel.app) account with:\n  - At least one project and environment\n  - An API key\n  - Your IP(s) added to the allowlist\n\n---\n\n## Links\n\n- **Platform**: [envshared.vercel.app](https://envshared.vercel.app)\n- **GitHub**: [github.com/GooDevPro/envShare](https://github.com/GooDevPro/envShare)\n- **npm**: [@alvarorestrepo/envshare-sdk](https://www.npmjs.com/package/@alvarorestrepo/envshare-sdk)\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}