@keldra/sdk 0.1.0 → 0.1.2

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
@@ -2,6 +2,12 @@
2
2
 
3
3
  TypeScript SDK for the Keldra relay API.
4
4
 
5
+ ## Security
6
+
7
+ - Use this SDK with a server-side API key only.
8
+ - Never ship `kk_...` keys in browser/client bundles.
9
+ - Keep `KELDRA_API_KEY` in backend environment variables.
10
+
5
11
  ## Install
6
12
 
7
13
  ```bash
@@ -26,7 +32,7 @@ npm install @keldra/sdk viem
26
32
  ```ts
27
33
  import { KeldraClient } from "@keldra/sdk";
28
34
 
29
- const client = KeldraClient.create("kk_your_api_key");
35
+ const client = KeldraClient.fromEnv();
30
36
  const result = await client.relay("ethereum", signedTxHex);
31
37
  const limits = await client.limits();
32
38
  const usage = await client.usage("2026-02-01", "2026-02-20");
@@ -35,6 +41,49 @@ console.log(result.relayId, result.status, result.txHash);
35
41
  console.log(limits.tier, usage.totals.relays_submitted);
36
42
  ```
37
43
 
44
+ ## API Key From .env
45
+
46
+ Use environment variables on your backend:
47
+
48
+ ```env
49
+ KELDRA_API_KEY=kk_your_api_key
50
+ KELDRA_GATEWAY_URL=https://relay.keldra.io
51
+ ```
52
+
53
+ Then initialize directly:
54
+
55
+ ```ts
56
+ import { KeldraClient } from "@keldra/sdk";
57
+
58
+ const client = KeldraClient.fromEnv();
59
+ ```
60
+
61
+ ## Backend Proxy Example (Next.js)
62
+
63
+ Keep Keldra calls on your server route:
64
+
65
+ ```ts
66
+ // app/api/relay/route.ts
67
+ import { NextRequest, NextResponse } from "next/server";
68
+ import { KeldraClient } from "@keldra/sdk";
69
+
70
+ const client = KeldraClient.fromEnv();
71
+
72
+ export async function POST(req: NextRequest) {
73
+ const body = await req.json();
74
+ const { chain, signedTx } = body as { chain: "ethereum"; signedTx: string };
75
+
76
+ if (!chain || !signedTx) {
77
+ return NextResponse.json({ error: "chain and signedTx are required" }, { status: 400 });
78
+ }
79
+
80
+ const relay = await client.relay(chain, signedTx);
81
+ return NextResponse.json(relay);
82
+ }
83
+ ```
84
+
85
+ Frontend calls your backend endpoint, not Keldra directly.
86
+
38
87
  ## Encrypted Transport
39
88
 
40
89
  ```ts
@@ -1,5 +1,6 @@
1
1
  import { K as KeldraClientConfig, D as DelayProfile, E as EncryptFn, C as Chain, h as RelayResult, g as RelayResponse, i as RelayStatusResponse, H as HealthResponse, b as ChainsResponse, M as MeLimitsResponse, d as MeUsageResponse } from './types-CL-VpP9K.cjs';
2
2
 
3
+ type EnvMap = Record<string, string | undefined>;
3
4
  declare class KeldraClient {
4
5
  private readonly http;
5
6
  private readonly gatewayUrl;
@@ -11,6 +12,10 @@ declare class KeldraClient {
11
12
  private readonly encryptFn?;
12
13
  constructor(config: KeldraClientConfig);
13
14
  static create(apiKey: string): KeldraClient;
15
+ static fromEnv(env?: EnvMap, options?: {
16
+ apiKeyEnv?: string;
17
+ gatewayUrlEnv?: string;
18
+ }): KeldraClient;
14
19
  static builder(): KeldraClientBuilder;
15
20
  relay(chain: Chain, signedTx: string): Promise<RelayResult>;
16
21
  submit(chain: Chain, signedTx: string): Promise<RelayResponse>;
@@ -1,5 +1,6 @@
1
1
  import { K as KeldraClientConfig, D as DelayProfile, E as EncryptFn, C as Chain, h as RelayResult, g as RelayResponse, i as RelayStatusResponse, H as HealthResponse, b as ChainsResponse, M as MeLimitsResponse, d as MeUsageResponse } from './types-CL-VpP9K.js';
2
2
 
3
+ type EnvMap = Record<string, string | undefined>;
3
4
  declare class KeldraClient {
4
5
  private readonly http;
5
6
  private readonly gatewayUrl;
@@ -11,6 +12,10 @@ declare class KeldraClient {
11
12
  private readonly encryptFn?;
12
13
  constructor(config: KeldraClientConfig);
13
14
  static create(apiKey: string): KeldraClient;
15
+ static fromEnv(env?: EnvMap, options?: {
16
+ apiKeyEnv?: string;
17
+ gatewayUrlEnv?: string;
18
+ }): KeldraClient;
14
19
  static builder(): KeldraClientBuilder;
15
20
  relay(chain: Chain, signedTx: string): Promise<RelayResult>;
16
21
  submit(chain: Chain, signedTx: string): Promise<RelayResponse>;
@@ -1,5 +1,5 @@
1
1
  import { Signer } from 'ethers';
2
- import { K as KeldraClient } from '../client-Bm1hg0EA.cjs';
2
+ import { K as KeldraClient } from '../client-COg_GErM.cjs';
3
3
  import { C as Chain, g as RelayResponse } from '../types-CL-VpP9K.cjs';
4
4
 
5
5
  interface KeldraBroadcasterOptions {
@@ -1,5 +1,5 @@
1
1
  import { Signer } from 'ethers';
2
- import { K as KeldraClient } from '../client-03PUOnb6.js';
2
+ import { K as KeldraClient } from '../client-q45X0E5a.js';
3
3
  import { C as Chain, g as RelayResponse } from '../types-CL-VpP9K.js';
4
4
 
5
5
  interface KeldraBroadcasterOptions {
package/dist/index.cjs CHANGED
@@ -164,6 +164,11 @@ var DEFAULT_GATEWAY_URL = "http://localhost:3400";
164
164
  var DEFAULT_TIMEOUT_MS = 3e5;
165
165
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
166
166
  var MAX_POLL_INTERVAL_MS = 15e3;
167
+ var DEFAULT_API_KEY_ENV = "KELDRA_API_KEY";
168
+ var DEFAULT_GATEWAY_ENV = "KELDRA_GATEWAY_URL";
169
+ function isBrowserRuntime() {
170
+ return typeof window !== "undefined" && typeof document !== "undefined";
171
+ }
167
172
  var KeldraClient = class _KeldraClient {
168
173
 
169
174
 
@@ -177,6 +182,11 @@ var KeldraClient = class _KeldraClient {
177
182
  if (!config.apiKey) {
178
183
  throw KeldraError.config("apiKey is required");
179
184
  }
185
+ if (isBrowserRuntime() && config.apiKey.startsWith("kk_")) {
186
+ throw KeldraError.config(
187
+ "Do not use a Keldra API key in browser code. Move SDK calls to your backend."
188
+ );
189
+ }
180
190
  this.http = new HttpClient(config.apiKey);
181
191
  this.gatewayUrl = (_nullishCoalesce(config.gatewayUrl, () => ( DEFAULT_GATEWAY_URL))).replace(
182
192
  /\/$/,
@@ -192,6 +202,23 @@ var KeldraClient = class _KeldraClient {
192
202
  static create(apiKey) {
193
203
  return new _KeldraClient({ apiKey });
194
204
  }
205
+ static fromEnv(env = _nullishCoalesce(_optionalChain([globalThis, 'access', _2 => _2.process, 'optionalAccess', _3 => _3.env]), () => ( {})), options) {
206
+ if (isBrowserRuntime()) {
207
+ throw KeldraError.config(
208
+ "KeldraClient.fromEnv() is server-only. Load KELDRA_API_KEY on your backend."
209
+ );
210
+ }
211
+ const apiKeyName = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _4 => _4.apiKeyEnv]), () => ( DEFAULT_API_KEY_ENV));
212
+ const gatewayUrlName = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _5 => _5.gatewayUrlEnv]), () => ( DEFAULT_GATEWAY_ENV));
213
+ const apiKey = env[apiKeyName];
214
+ if (!apiKey) {
215
+ throw KeldraError.config(`${apiKeyName} is required`);
216
+ }
217
+ return new _KeldraClient({
218
+ apiKey,
219
+ gatewayUrl: _nullishCoalesce(env[gatewayUrlName], () => ( DEFAULT_GATEWAY_URL))
220
+ });
221
+ }
195
222
  static builder() {
196
223
  return new KeldraClientBuilder();
197
224
  }
@@ -253,7 +280,7 @@ var KeldraClient = class _KeldraClient {
253
280
  }
254
281
  interval = Math.min(interval * 2, MAX_POLL_INTERVAL_MS);
255
282
  }
256
- throw KeldraError.timeout(relayId, _nullishCoalesce(_optionalChain([lastStatus, 'optionalAccess', _2 => _2.status]), () => ( "queued")));
283
+ throw KeldraError.timeout(relayId, _nullishCoalesce(_optionalChain([lastStatus, 'optionalAccess', _6 => _6.status]), () => ( "queued")));
257
284
  }
258
285
  async health() {
259
286
  return this.http.get(`${this.gatewayUrl}/v1/health`);
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- export { K as KeldraClient, a as KeldraClientBuilder } from './client-Bm1hg0EA.cjs';
1
+ export { K as KeldraClient, a as KeldraClientBuilder } from './client-COg_GErM.cjs';
2
2
  import { R as RelayStatus } from './types-CL-VpP9K.cjs';
3
3
  export { C as Chain, a as ChainConfig, b as ChainsResponse, D as DelayProfile, E as EncryptFn, H as HealthResponse, c as HealthStats, K as KeldraClientConfig, M as MeLimitsResponse, d as MeUsageResponse, N as NoiseKeyResponse, e as RelayOptions, f as RelayRequest, g as RelayResponse, h as RelayResult, i as RelayStatusResponse, T as TERMINAL_STATUSES, U as UsageDailyRow, j as UsageTotals } from './types-CL-VpP9K.cjs';
4
4
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { K as KeldraClient, a as KeldraClientBuilder } from './client-03PUOnb6.js';
1
+ export { K as KeldraClient, a as KeldraClientBuilder } from './client-q45X0E5a.js';
2
2
  import { R as RelayStatus } from './types-CL-VpP9K.js';
3
3
  export { C as Chain, a as ChainConfig, b as ChainsResponse, D as DelayProfile, E as EncryptFn, H as HealthResponse, c as HealthStats, K as KeldraClientConfig, M as MeLimitsResponse, d as MeUsageResponse, N as NoiseKeyResponse, e as RelayOptions, f as RelayRequest, g as RelayResponse, h as RelayResult, i as RelayStatusResponse, T as TERMINAL_STATUSES, U as UsageDailyRow, j as UsageTotals } from './types-CL-VpP9K.js';
4
4
 
package/dist/index.js CHANGED
@@ -164,6 +164,11 @@ var DEFAULT_GATEWAY_URL = "http://localhost:3400";
164
164
  var DEFAULT_TIMEOUT_MS = 3e5;
165
165
  var DEFAULT_POLL_INTERVAL_MS = 2e3;
166
166
  var MAX_POLL_INTERVAL_MS = 15e3;
167
+ var DEFAULT_API_KEY_ENV = "KELDRA_API_KEY";
168
+ var DEFAULT_GATEWAY_ENV = "KELDRA_GATEWAY_URL";
169
+ function isBrowserRuntime() {
170
+ return typeof window !== "undefined" && typeof document !== "undefined";
171
+ }
167
172
  var KeldraClient = class _KeldraClient {
168
173
  http;
169
174
  gatewayUrl;
@@ -177,6 +182,11 @@ var KeldraClient = class _KeldraClient {
177
182
  if (!config.apiKey) {
178
183
  throw KeldraError.config("apiKey is required");
179
184
  }
185
+ if (isBrowserRuntime() && config.apiKey.startsWith("kk_")) {
186
+ throw KeldraError.config(
187
+ "Do not use a Keldra API key in browser code. Move SDK calls to your backend."
188
+ );
189
+ }
180
190
  this.http = new HttpClient(config.apiKey);
181
191
  this.gatewayUrl = (config.gatewayUrl ?? DEFAULT_GATEWAY_URL).replace(
182
192
  /\/$/,
@@ -192,6 +202,23 @@ var KeldraClient = class _KeldraClient {
192
202
  static create(apiKey) {
193
203
  return new _KeldraClient({ apiKey });
194
204
  }
205
+ static fromEnv(env = globalThis.process?.env ?? {}, options) {
206
+ if (isBrowserRuntime()) {
207
+ throw KeldraError.config(
208
+ "KeldraClient.fromEnv() is server-only. Load KELDRA_API_KEY on your backend."
209
+ );
210
+ }
211
+ const apiKeyName = options?.apiKeyEnv ?? DEFAULT_API_KEY_ENV;
212
+ const gatewayUrlName = options?.gatewayUrlEnv ?? DEFAULT_GATEWAY_ENV;
213
+ const apiKey = env[apiKeyName];
214
+ if (!apiKey) {
215
+ throw KeldraError.config(`${apiKeyName} is required`);
216
+ }
217
+ return new _KeldraClient({
218
+ apiKey,
219
+ gatewayUrl: env[gatewayUrlName] ?? DEFAULT_GATEWAY_URL
220
+ });
221
+ }
195
222
  static builder() {
196
223
  return new KeldraClientBuilder();
197
224
  }
@@ -1,5 +1,5 @@
1
1
  import { Transport, Chain as Chain$1, Account, WalletClient } from 'viem';
2
- import { K as KeldraClient } from '../client-Bm1hg0EA.cjs';
2
+ import { K as KeldraClient } from '../client-COg_GErM.cjs';
3
3
  import { C as Chain } from '../types-CL-VpP9K.cjs';
4
4
 
5
5
  interface KeldraViemOptions {
@@ -1,5 +1,5 @@
1
1
  import { Transport, Chain as Chain$1, Account, WalletClient } from 'viem';
2
- import { K as KeldraClient } from '../client-03PUOnb6.js';
2
+ import { K as KeldraClient } from '../client-q45X0E5a.js';
3
3
  import { C as Chain } from '../types-CL-VpP9K.js';
4
4
 
5
5
  interface KeldraViemOptions {
package/package.json CHANGED
@@ -1,17 +1,10 @@
1
1
  {
2
2
  "name": "@keldra/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "TypeScript SDK for Keldra relay API",
5
5
  "license": "MIT",
6
6
  "author": "Keldra",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/jperth86/keldra_relay.git"
10
- },
11
7
  "homepage": "https://keldra.network",
12
- "bugs": {
13
- "url": "https://github.com/jperth86/keldra_relay/issues"
14
- },
15
8
  "keywords": [
16
9
  "keldra",
17
10
  "relay",
@@ -33,23 +26,51 @@
33
26
  },
34
27
  "exports": {
35
28
  ".": {
36
- "import": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
37
- "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" }
29
+ "import": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "require": {
34
+ "types": "./dist/index.d.cts",
35
+ "default": "./dist/index.cjs"
36
+ }
38
37
  },
39
38
  "./crypto": {
40
- "import": { "types": "./dist/crypto/index.d.ts", "default": "./dist/crypto/index.js" },
41
- "require": { "types": "./dist/crypto/index.d.cts", "default": "./dist/crypto/index.cjs" }
39
+ "import": {
40
+ "types": "./dist/crypto/index.d.ts",
41
+ "default": "./dist/crypto/index.js"
42
+ },
43
+ "require": {
44
+ "types": "./dist/crypto/index.d.cts",
45
+ "default": "./dist/crypto/index.cjs"
46
+ }
42
47
  },
43
48
  "./ethers": {
44
- "import": { "types": "./dist/ethers/index.d.ts", "default": "./dist/ethers/index.js" },
45
- "require": { "types": "./dist/ethers/index.d.cts", "default": "./dist/ethers/index.cjs" }
49
+ "import": {
50
+ "types": "./dist/ethers/index.d.ts",
51
+ "default": "./dist/ethers/index.js"
52
+ },
53
+ "require": {
54
+ "types": "./dist/ethers/index.d.cts",
55
+ "default": "./dist/ethers/index.cjs"
56
+ }
46
57
  },
47
58
  "./viem": {
48
- "import": { "types": "./dist/viem/index.d.ts", "default": "./dist/viem/index.js" },
49
- "require": { "types": "./dist/viem/index.d.cts", "default": "./dist/viem/index.cjs" }
59
+ "import": {
60
+ "types": "./dist/viem/index.d.ts",
61
+ "default": "./dist/viem/index.js"
62
+ },
63
+ "require": {
64
+ "types": "./dist/viem/index.d.cts",
65
+ "default": "./dist/viem/index.cjs"
66
+ }
50
67
  }
51
68
  },
52
- "files": ["dist", "README.md", "LICENSE"],
69
+ "files": [
70
+ "dist",
71
+ "README.md",
72
+ "LICENSE"
73
+ ],
53
74
  "scripts": {
54
75
  "build": "tsup",
55
76
  "test": "vitest run",
@@ -65,11 +86,21 @@
65
86
  "viem": "^2.0.0"
66
87
  },
67
88
  "peerDependenciesMeta": {
68
- "@stablelib/blake2s": { "optional": true },
69
- "@stablelib/chacha20poly1305": { "optional": true },
70
- "@stablelib/x25519": { "optional": true },
71
- "ethers": { "optional": true },
72
- "viem": { "optional": true }
89
+ "@stablelib/blake2s": {
90
+ "optional": true
91
+ },
92
+ "@stablelib/chacha20poly1305": {
93
+ "optional": true
94
+ },
95
+ "@stablelib/x25519": {
96
+ "optional": true
97
+ },
98
+ "ethers": {
99
+ "optional": true
100
+ },
101
+ "viem": {
102
+ "optional": true
103
+ }
73
104
  },
74
105
  "devDependencies": {
75
106
  "@stablelib/blake2s": "^2.0.0",