@elding/sdk 0.6.2 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,67 +1,95 @@
1
1
  # @elding/sdk
2
2
 
3
- Utilise tes clés API sans jamais les écrire dans ton code. Elding garde la clé
4
- hors de ton app : ton code manipule un placeholder, la vraie valeur est injectée
5
- au dernier moment et verrouillée à un seul domaine (anti-exfiltration).
3
+ Your API keys are never in your code or in a `.env` file. Elding keeps them, your code calls the API normally, and the real key is injected at the last moment.
4
+
5
+ **The same code works in dev and in prod.** You change nothing.
6
6
 
7
7
  ```bash
8
8
  npm install @elding/sdk
9
9
  ```
10
10
 
11
- ## Pour une clé API HTTP : `configure()`
11
+ > ⚠️ **Elding warning:** always use the **scoped** name `@elding/sdk`. `npm install elding` (without the `@elding/` scope) installs an unrelated third-party package, not Elding.
12
+
13
+ ## Quickstart (2 min)
14
+
15
+ ### 1. Sign in and choose your set
16
+
17
+ ```bash
18
+ npx elding login # opens the browser, signs you in
19
+ npx elding init # creates .elding.json (links this project to a set)
20
+ ```
21
+
22
+ ### 2. Write your code
12
23
 
13
- Utilise `configure()` pour **toute clé d'API HTTP** (OpenAI, Mistral, Stripe,
14
- Resend, etc.). Retourne de quoi faire un `fetch` normal, sans jamais exposer la clé.
24
+ `configure()` replaces your real key. 1st argument = the secret name in Elding, 2nd = the API domain.
15
25
 
16
26
  ```ts
27
+ import OpenAI from "openai";
17
28
  import { configure } from "@elding/sdk";
18
29
 
19
- // "MISTRAL_KEY" = nom du secret dans ton vault Elding.
20
- // 2e arg = le domaine de l'API (verrouillage anti-fuite).
21
- const { apiKey, baseURL } = await configure("MISTRAL_KEY", "https://api.mistral.ai");
30
+ const openai = new OpenAI(
31
+ await configure("OPENAI_API_KEY", "https://api.openai.com")
32
+ );
22
33
 
23
- const res = await fetch(`${baseURL}/v1/chat/completions`, {
24
- method: "POST",
25
- headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" },
26
- body: JSON.stringify({ model: "mistral-small-latest", messages: [...] }),
27
- });
34
+ // use openai normally, the real key is never in your code
28
35
  ```
29
36
 
30
- Important :
31
- - **Utilise toujours `baseURL`** dans ton `fetch`, jamais l'URL de l'API en dur.
32
- En dev, `baseURL` pointe vers le proxy local qui injecte la vraie clé.
33
- - Mets `apiKey` dans un **header** (`Authorization`, `x-api-key`…), jamais dans l'URL.
34
- - `configure()` est réservé aux **API HTTP**. Pour autre chose, voir `secret()`.
37
+ ### 3. Run
35
38
 
36
- ## Pour un secret non-HTTP : `secret()`
39
+ ```bash
40
+ npx elding proxy -- npm run dev
41
+ ```
37
42
 
38
- `DATABASE_URL`, `REDIS_URL`, `JWT_SECRET`… tout ce qui n'est pas une API HTTP.
39
- Retourne la valeur brute. Jamais dans `process.env`, effacée de la mémoire après
40
- 5 min (réglable via `ELDING_CACHE_TTL_MS`).
43
+ That's it. The key **never** enters your application.
41
44
 
42
- ```ts
43
- import { secret } from "@elding/sdk";
45
+ ## In production (Vercel, server, CI)
44
46
 
45
- const dbUrl = await secret("DATABASE_URL");
47
+ No proxy in prod. Elding fetches the key at runtime. **You don't change your code**, you just add **2 environment variables**:
48
+
49
+ ```bash
50
+ ELDING_REFRESH_TOKEN=eld_rt_... # generate it in the dashboard → API keys
51
+ ELDING_SET_ID=... # your set's id (set page)
46
52
  ```
47
53
 
48
- ## Plusieurs secrets d'un coup : `client()`
54
+ And... that's all. The same `configure("OPENAI_API_KEY", "https://api.openai.com")` works.
55
+
56
+ > The SDK reads these 2 variables automatically. Keep these exact names (`ELDING_REFRESH_TOKEN`, `ELDING_SET_ID`) and you have **no option** to pass.
57
+
58
+ ### How to get the 2 keys
59
+
60
+ | Variable | Where to find it |
61
+ |---|---|
62
+ | `ELDING_REFRESH_TOKEN` | Dashboard → **API keys** → New key (shown only once) |
63
+ | `ELDING_SET_ID` | Open your set in the dashboard, the id is in the URL |
64
+
65
+ ## The 2 functions
66
+
67
+ **`configure(name, domain)`** — for any **HTTP API** key (OpenAI, Mistral, Stripe, Resend…).
68
+ You pass it directly to the provider's SDK:
49
69
 
50
70
  ```ts
51
- import { client } from "@elding/sdk";
71
+ const openai = new OpenAI(await configure("OPENAI_API_KEY", "https://api.openai.com"));
72
+ ```
73
+
74
+ **`secret(name)`** — for everything **else** (`DATABASE_URL`, `JWT_SECRET`, `REDIS_URL`…).
75
+ Returns the raw value, wiped from memory after 5 min.
52
76
 
53
- const elding = await client();
54
- const dbUrl = elding.secret("DATABASE_URL");
77
+ ```ts
78
+ import { secret } from "@elding/sdk";
79
+ const dbUrl = await secret("DATABASE_URL");
55
80
  ```
56
81
 
57
- ## Règle simple
82
+ ## Dev vs prod, at a glance
58
83
 
59
- - Clé qui part dans une **requête HTTP** → `configure(nom, "https://api.exemple.com")`.
60
- - N'importe quel **autre secret** → `secret(nom)`.
61
- - **Jamais** de clé en dur, jamais dans `process.env`, jamais dans l'URL.
84
+ | | Dev | Prod |
85
+ |---|---|---|
86
+ | Command | `elding proxy -- npm run dev` | `npm run build && npm start` |
87
+ | Mechanism | local proxy | runtime fetch |
88
+ | To configure | `elding login` + `elding init` | `ELDING_REFRESH_TOKEN` + `ELDING_SET_ID` |
89
+ | Your code | identical | **identical** |
62
90
 
63
- ## Dev vs prod
91
+ ## Rules
64
92
 
65
- - **Dev** (`elding proxy -- npm run dev`) : la clé n'entre jamais dans ton process.
66
- - **Prod** (serverless) : la clé est récupérée du vault au runtime. Elle reste
67
- verrouillée au domaine, surveillée, révocable en un clic.
93
+ - **Never** hardcode a key, never in `process.env`, never in the URL.
94
+ - A key that goes into an HTTP request `configure(name, "https://…")`.
95
+ - Any other secret `secret(name)`.
package/dist/api.js CHANGED
@@ -8,14 +8,14 @@ const REQUEST_TIMEOUT_MS = 15_000;
8
8
  const REFRESH_TOKEN = /^eld_rt_[a-f0-9]{64}$/i;
9
9
  async function exchangeToken(refreshToken) {
10
10
  if (!REFRESH_TOKEN.test(refreshToken))
11
- throw new Error("[elding] Token local invalide.");
11
+ throw new Error("[elding] Invalid local token.");
12
12
  const body = await requestJson("/api/cli/auth/token", {
13
13
  method: "POST",
14
14
  headers: { "Content-Type": "application/json" },
15
15
  body: JSON.stringify({ refreshToken }),
16
16
  });
17
17
  if (!body.success || !body.accessToken)
18
- throw new Error(safeError(body.error) || "Échec d'authentification Elding");
18
+ throw new Error(safeError(body.error) || "Elding authentication failed");
19
19
  return body.accessToken;
20
20
  }
21
21
  async function fetchSecrets(accessToken, setId) {
@@ -23,7 +23,7 @@ async function fetchSecrets(accessToken, setId) {
23
23
  headers: { Authorization: `Bearer ${accessToken}` },
24
24
  });
25
25
  if (!body.success || !body.secrets || typeof body.secrets !== "object")
26
- throw new Error(safeError(body.error) || "Réponse invalide");
26
+ throw new Error(safeError(body.error) || "Invalid response");
27
27
  return sanitizeSecrets(body.secrets);
28
28
  }
29
29
  const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
@@ -48,9 +48,9 @@ async function requestJson(path, init) {
48
48
  });
49
49
  const body = (await res.json().catch(() => null));
50
50
  if (!res.ok)
51
- throw new Error(safeError(body?.error) || `Erreur ${res.status}`);
51
+ throw new Error(safeError(body?.error) || `Error ${res.status}`);
52
52
  if (!body || typeof body !== "object")
53
- throw new Error("[elding] Réponse invalide.");
53
+ throw new Error("[elding] Invalid response.");
54
54
  return body;
55
55
  }
56
56
  function safeError(value) {
package/dist/apiUrl.js CHANGED
@@ -28,17 +28,17 @@ function resolveBaseUrl(raw = process.env.ELDING_API_URL) {
28
28
  url = new URL(input);
29
29
  }
30
30
  catch {
31
- throw new Error("[elding] ELDING_API_URL invalide.");
31
+ throw new Error("[elding] Invalid ELDING_API_URL.");
32
32
  }
33
33
  if (url.username || url.password) {
34
- throw new Error("[elding] ELDING_API_URL ne doit pas contenir d'identifiants.");
34
+ throw new Error("[elding] ELDING_API_URL must not contain credentials.");
35
35
  }
36
36
  if (url.pathname !== "/" || url.search || url.hash) {
37
- throw new Error("[elding] ELDING_API_URL doit etre une origine seule.");
37
+ throw new Error("[elding] ELDING_API_URL must be an origin only.");
38
38
  }
39
39
  if (url.protocol === "https:")
40
40
  return url.origin;
41
41
  if (url.protocol === "http:" && isLoopbackHost(url.hostname))
42
42
  return url.origin;
43
- throw new Error("[elding] ELDING_API_URL doit utiliser HTTPS, sauf pour localhost en developpement.");
43
+ throw new Error("[elding] ELDING_API_URL must use HTTPS, except for localhost in development.");
44
44
  }
package/dist/client.js CHANGED
@@ -14,11 +14,11 @@ class EldingClient {
14
14
  // 1. Resolve setId : option explicite → .elding.json (dev) → ELDING_SET_ID (serverless/CI)
15
15
  const setId = options.setId ?? (0, config_js_1.readProjectConfig)()?.setId ?? process.env.ELDING_SET_ID?.trim();
16
16
  if (!setId)
17
- throw new Error("[elding] setId introuvable. Lancez `elding init`, passez setId en option, ou définissez ELDING_SET_ID.");
17
+ throw new Error("[elding] setId not found. Run `elding init`, pass setId as an option, or set ELDING_SET_ID.");
18
18
  // 2. Resolve refresh token
19
19
  const refreshToken = options.refreshToken ?? (0, config_js_1.readGlobalConfig)()?.refreshToken;
20
20
  if (!refreshToken)
21
- throw new Error("[elding] Token introuvable. Lancez `elding login` ou passez refreshToken en option.");
21
+ throw new Error("[elding] Token not found. Run `elding login` or pass refreshToken as an option.");
22
22
  // 3. Fetch secrets
23
23
  const accessToken = await (0, api_js_1.exchangeToken)(refreshToken);
24
24
  const secrets = await (0, api_js_1.fetchSecrets)(accessToken, setId);
@@ -33,7 +33,7 @@ class EldingClient {
33
33
  if (envValue !== undefined)
34
34
  return envValue;
35
35
  }
36
- throw new Error(`[elding] Secret "${name}" introuvable dans le set.`);
36
+ throw new Error(`[elding] Secret "${name}" not found in the set.`);
37
37
  }
38
38
  // Returns undefined instead of throwing
39
39
  secretOrUndefined(name) {
@@ -1,9 +1,10 @@
1
- import { type ClientOptions } from "./client.js";
1
+ import { EldingClient, type ClientOptions } from "./client.js";
2
2
  export type ProviderConfig = {
3
3
  apiKey: string;
4
4
  baseURL?: string;
5
5
  defaultHeaders?: Record<string, string>;
6
6
  };
7
+ export declare function getClient(options: ClientOptions): Promise<EldingClient>;
7
8
  /** Efface immédiatement les secrets gardés en mémoire par le SDK. */
8
9
  export declare function clearSecretCache(): void;
9
10
  export declare function configure(secretName: string, target: string, options?: ClientOptions): Promise<ProviderConfig>;
package/dist/configure.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getClient = getClient;
3
4
  exports.clearSecretCache = clearSecretCache;
4
5
  exports.configure = configure;
5
6
  exports.secret = secret;
@@ -60,12 +61,12 @@ function isHttpTarget(target) {
60
61
  }
61
62
  async function configure(secretName, target, options = {}) {
62
63
  if (!SECRET_NAME.test(secretName))
63
- throw new Error("[elding] Nom de secret invalide (A-Z, 0-9, _).");
64
+ throw new Error("[elding] Invalid secret name (A-Z, 0-9, _).");
64
65
  // configure() protège uniquement les API HTTP via le proxy. Pour une valeur
65
66
  // non-HTTP (DATABASE_URL, config...), le proxy ne peut rien intercepter :
66
67
  // on guide vers secret() plutot que de renvoyer une valeur faussement "protégée".
67
68
  if (!isHttpTarget(target))
68
- throw new Error(`[elding] configure() attend une API HTTP. Pour "${secretName}" (non-HTTP, ex. DATABASE_URL), utilise secret("${secretName}") — valeur brute, jamais protégée par le proxy.`);
69
+ throw new Error(`[elding] configure() expects an HTTP API. For "${secretName}" (non-HTTP, e.g. DATABASE_URL), use secret("${secretName}") — raw value, never protected by the proxy.`);
69
70
  // Mode proxy (dev) : placeholder, la vraie clé n'entre jamais dans le process.
70
71
  if ((0, proxy_js_1.isProxyActive)()) {
71
72
  const { baseURL, headers } = (0, proxy_js_1.proxyConfig)(target);
@@ -86,7 +87,7 @@ async function configure(secretName, target, options = {}) {
86
87
  */
87
88
  async function secret(name, options = {}) {
88
89
  if (!SECRET_NAME.test(name))
89
- throw new Error("[elding] Nom de secret invalide (A-Z, 0-9, _).");
90
+ throw new Error("[elding] Invalid secret name (A-Z, 0-9, _).");
90
91
  const elding = await getClient(options);
91
92
  return elding.secret(name);
92
93
  }
package/dist/index.d.ts CHANGED
@@ -1,16 +1,13 @@
1
1
  import { EldingClient, type ClientOptions } from "./client.js";
2
+ import { configure, secret } from "./configure.js";
2
3
  export { configure, secret, clearSecretCache, type ProviderConfig } from "./configure.js";
4
+ declare const elding: {
5
+ configure: typeof configure;
6
+ secret: typeof secret;
7
+ };
8
+ export default elding;
3
9
  export { EldingClient };
4
10
  export type { ClientOptions };
5
11
  export { isProxyActive } from "./proxy.js";
6
- /**
7
- * Crée un client Elding et charge tous les secrets du set configuré.
8
- *
9
- * @example
10
- * const elding = await client();
11
- * const key = elding.secret("OPENAI_API_KEY");
12
- *
13
- * @example avec options
14
- * const elding = await client({ setId: "xxx", refreshToken: "eld_rt_..." });
15
- */
12
+ /** Crée un client Elding et charge tous les secrets du set configuré. */
16
13
  export declare function client(options?: ClientOptions): Promise<EldingClient>;
package/dist/index.js CHANGED
@@ -4,25 +4,20 @@ exports.isProxyActive = exports.EldingClient = exports.clearSecretCache = export
4
4
  exports.client = client;
5
5
  const client_js_1 = require("./client.js");
6
6
  Object.defineProperty(exports, "EldingClient", { enumerable: true, get: function () { return client_js_1.EldingClient; } });
7
+ const configure_js_1 = require("./configure.js");
7
8
  // ── API principale ──
8
- var configure_js_1 = require("./configure.js");
9
- Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return configure_js_1.configure; } });
10
- Object.defineProperty(exports, "secret", { enumerable: true, get: function () { return configure_js_1.secret; } });
11
- Object.defineProperty(exports, "clearSecretCache", { enumerable: true, get: function () { return configure_js_1.clearSecretCache; } });
12
- // ── Avancé : vérifier le mode (proxy actif ou non) ──
9
+ // configure(name, "https://…") pour une clé d'API HTTP (passée au SDK provider
10
+ // ou à fetch), secret(name) pour les secrets non-HTTP (DATABASE_URL, JWT_SECRET…).
11
+ var configure_js_2 = require("./configure.js");
12
+ Object.defineProperty(exports, "configure", { enumerable: true, get: function () { return configure_js_2.configure; } });
13
+ Object.defineProperty(exports, "secret", { enumerable: true, get: function () { return configure_js_2.secret; } });
14
+ Object.defineProperty(exports, "clearSecretCache", { enumerable: true, get: function () { return configure_js_2.clearSecretCache; } });
15
+ // Objet namespace : `import elding from "@elding/sdk"; elding.configure(...)`.
16
+ const elding = { configure: configure_js_1.configure, secret: configure_js_1.secret };
17
+ exports.default = elding;
13
18
  var proxy_js_1 = require("./proxy.js");
14
19
  Object.defineProperty(exports, "isProxyActive", { enumerable: true, get: function () { return proxy_js_1.isProxyActive; } });
15
- // proxyConfig reste interne : configure() le supersede (proxy + bascule dev/prod auto).
16
- /**
17
- * Crée un client Elding et charge tous les secrets du set configuré.
18
- *
19
- * @example
20
- * const elding = await client();
21
- * const key = elding.secret("OPENAI_API_KEY");
22
- *
23
- * @example avec options
24
- * const elding = await client({ setId: "xxx", refreshToken: "eld_rt_..." });
25
- */
20
+ /** Crée un client Elding et charge tous les secrets du set configuré. */
26
21
  async function client(options) {
27
22
  return client_js_1.EldingClient.create(options);
28
23
  }
package/dist/proxy.js CHANGED
@@ -24,7 +24,7 @@ function proxyConfig(target) {
24
24
  const url = validateProxyUrl(process.env.ELDING_PROXY_URL);
25
25
  const token = process.env.ELDING_PROXY_TOKEN;
26
26
  if (!url || !token || !PROXY_TOKEN.test(token))
27
- throw new Error("[elding] Proxy non actif. Lancez via `elding proxy -- <cmd>`.");
27
+ throw new Error("[elding] Proxy not active. Run via `elding proxy -- <cmd>`.");
28
28
  const safeTarget = validateTarget(target);
29
29
  return {
30
30
  baseURL: url,
@@ -53,14 +53,14 @@ function validateProxyUrl(value) {
53
53
  url.pathname !== "/" ||
54
54
  url.search ||
55
55
  url.hash) {
56
- throw new Error("[elding] ELDING_PROXY_URL invalide.");
56
+ throw new Error("[elding] Invalid ELDING_PROXY_URL.");
57
57
  }
58
58
  return url.origin;
59
59
  }
60
60
  function validateTarget(value) {
61
61
  const url = new URL(value);
62
62
  if (url.protocol !== "https:" || url.username || url.password) {
63
- throw new Error("[elding] La cible proxy doit etre une URL HTTPS sans identifiants.");
63
+ throw new Error("[elding] The proxy target must be an HTTPS URL without credentials.");
64
64
  }
65
65
  url.hash = "";
66
66
  return url.origin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elding/sdk",
3
- "version": "0.6.2",
3
+ "version": "0.6.5",
4
4
  "description": "Elding SDK — accès aux secrets depuis le code, zéro .env",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",