@replanejs/sdk 0.8.20 → 0.9.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
@@ -36,7 +36,7 @@ yarn add @replanejs/sdk
36
36
  > **Important:** Each SDK key is tied to a specific project. The client can only access configs from the project that the SDK key belongs to. If you need configs from multiple projects, create separate SDK keys and initialize separate clients—one per project.
37
37
 
38
38
  ```ts
39
- import { createReplaneClient } from "@replanejs/sdk";
39
+ import { Replane } from "@replanejs/sdk";
40
40
 
41
41
  // Define your config types
42
42
  interface Configs {
@@ -50,8 +50,15 @@ interface PasswordRequirements {
50
50
  requireSymbol: boolean;
51
51
  }
52
52
 
53
- const replane = await createReplaneClient<Configs>({
54
- // Each SDK key belongs to one project only
53
+ // Create the client with optional constructor options
54
+ const replane = new Replane<Configs>({
55
+ context: {
56
+ environment: "production",
57
+ },
58
+ });
59
+
60
+ // Connect to the server
61
+ await replane.connect({
55
62
  sdkKey: process.env.REPLANE_SDK_KEY!,
56
63
  baseUrl: "https://replane.my-hosting.com",
57
64
  });
@@ -83,35 +90,42 @@ if (enabled) {
83
90
  }
84
91
 
85
92
  // When done, clean up resources
86
- replane.close();
93
+ replane.disconnect();
87
94
  ```
88
95
 
89
96
  ## API
90
97
 
91
- ### `createReplaneClient<T>(options)`
98
+ ### `new Replane<T>(options?)`
92
99
 
93
- Returns a promise resolving to an object: `{ get, subscribe, close }`.
100
+ Creates a new Replane client instance.
94
101
 
95
102
  Type parameter `T` defines the shape of your configs (a mapping of config names to their value types).
96
103
 
97
- `close()` stops the configs client and cleans up resources. It is safe to call multiple times (no‑op after the first call).
104
+ The client is usable immediately after construction if you provide `defaults` or a `snapshot`. Call `connect()` to establish a server connection for real-time updates.
105
+
106
+ #### Constructor Options
107
+
108
+ - `context` (object) – default context for all config evaluations. Can be overridden per-request in `get()`. Optional.
109
+ - `defaults` (object) – default values to use before connecting or if the connection fails. These values are available immediately. Optional.
110
+ - `snapshot` (object) – restore from a previous `getSnapshot()` call. Useful for SSR/hydration scenarios. Optional.
111
+ - `logger` (object) – custom logger with `debug`, `info`, `warn`, `error` methods. Default: `console`.
112
+
113
+ ### `replane.connect(options)`
114
+
115
+ Connects to the Replane server and starts receiving real-time updates via SSE.
116
+
117
+ Returns a Promise that resolves when the connection is established and initial configs are loaded.
98
118
 
99
- #### Options
119
+ #### Connect Options
100
120
 
101
121
  - `baseUrl` (string) – Replane origin (no trailing slash needed). Required.
102
122
  - `sdkKey` (string) – SDK key for authorization. Required. **Note:** Each SDK key is tied to a specific project and can only access configs from that project. To access configs from multiple projects, create multiple SDK keys and initialize separate client instances.
103
- - `required` (object or array) – mark specific configs as required. If any required config is missing, the client will throw an error during initialization. Can be an object with boolean values or an array of config names. Optional.
104
- - `defaults` (object) – default values to use if the initial request to fetch configs fails or times out. These values are used immediately while waiting for server connection. Allows the client to start even when the API is unavailable. Optional.
105
- - `context` (object) – default context for all config evaluations. Can be overridden per-request in `get()`. Optional.
106
123
  - `fetchFn` (function) – custom fetch (e.g. `undici.fetch` or mocked fetch in tests). Optional.
107
124
  - `requestTimeoutMs` (number) – timeout for SSE requests in ms. Default: 2000.
108
- - `initializationTimeoutMs` (number) – timeout for SDK initialization in ms. Default: 5000.
125
+ - `connectTimeoutMs` (number) – timeout for initial connection in ms. Default: 5000.
109
126
  - `retryDelayMs` (number) – base delay between retries in ms. Default: 200.
110
127
  - `inactivityTimeoutMs` (number) – timeout for SSE inactivity before reconnecting in ms. Default: 30000.
111
- - `logger` (object) – custom logger with `debug`, `info`, `warn`, `error` methods. Default: `console`.
112
128
  - `agent` (string) – agent identifier sent in User-Agent header.
113
- - `onConnectionError` (function) – callback invoked when a connection error occurs during SSE streaming.
114
- - `onConnected` (function) – callback invoked when the SSE connection is successfully established.
115
129
 
116
130
  ### `replane.get<K>(name, options?)`
117
131
 
@@ -141,7 +155,8 @@ interface Configs {
141
155
  "max-connections": number;
142
156
  }
143
157
 
144
- const replane = await createReplaneClient<Configs>({
158
+ const replane = new Replane<Configs>();
159
+ await replane.connect({
145
160
  sdkKey: "your-sdk-key",
146
161
  baseUrl: "https://replane.my-host.com",
147
162
  });
@@ -158,7 +173,7 @@ const userEnabled = replane.get("billing-enabled", {
158
173
  const maxConnections = replane.get("max-connections", { default: 10 });
159
174
 
160
175
  // Clean up when done
161
- replane.close();
176
+ replane.disconnect();
162
177
  ```
163
178
 
164
179
  ### `replane.subscribe(callback)` or `replane.subscribe(configName, callback)`
@@ -197,7 +212,8 @@ interface Configs {
197
212
  "max-connections": number;
198
213
  }
199
214
 
200
- const replane = await createReplaneClient<Configs>({
215
+ const replane = new Replane<Configs>();
216
+ await replane.connect({
201
217
  sdkKey: "your-sdk-key",
202
218
  baseUrl: "https://replane.my-host.com",
203
219
  });
@@ -218,61 +234,55 @@ unsubscribeAll();
218
234
  unsubscribeFeature();
219
235
 
220
236
  // Clean up when done
221
- replane.close();
237
+ replane.disconnect();
222
238
  ```
223
239
 
224
- ### `createInMemoryReplaneClient(initialData)`
225
-
226
- Creates a client backed by an in-memory store instead of making HTTP requests. Handy for unit tests or local development where you want deterministic config values without a server.
240
+ ### In-memory client (testing)
227
241
 
228
- Parameters:
229
-
230
- - `initialData` (object) – map of config name to value.
231
-
232
- Returns the same client shape as `createReplaneClient` (`{ get, subscribe, close }`).
233
-
234
- Notes:
235
-
236
- - `get(name)` resolves to the value from `initialData`.
237
- - If a name is missing, it throws a `ReplaneError` (`Config not found: <name>`).
238
- - The client works as usual but doesn't receive SSE updates (values remain whatever is in-memory).
239
-
240
- Example:
242
+ For unit tests or local development where you want deterministic config values without a server, create a `Replane` instance with `defaults` and don't call `connect()`:
241
243
 
242
244
  ```ts
243
- import { createInMemoryReplaneClient } from "@replanejs/sdk";
245
+ import { Replane } from "@replanejs/sdk";
244
246
 
245
247
  interface Configs {
246
248
  "feature-a": boolean;
247
249
  "max-items": { value: number; ttl: number };
248
250
  }
249
251
 
250
- const replane = createInMemoryReplaneClient<Configs>({
251
- "feature-a": true,
252
- "max-items": { value: 10, ttl: 3600 },
252
+ const replane = new Replane<Configs>({
253
+ defaults: {
254
+ "feature-a": true,
255
+ "max-items": { value: 10, ttl: 3600 },
256
+ },
253
257
  });
254
258
 
259
+ // No connect() call - works purely from defaults
260
+
255
261
  const featureA = replane.get("feature-a"); // TypeScript knows this is boolean
256
262
  console.log(featureA); // true
257
263
 
258
264
  const maxItems = replane.get("max-items"); // TypeScript knows the type
259
265
  console.log(maxItems); // { value: 10, ttl: 3600 }
260
-
261
- replane.close();
262
266
  ```
263
267
 
264
- ### `replane.close()`
268
+ Notes:
269
+
270
+ - `get(name)` returns the value from `defaults`.
271
+ - If a name is missing, it throws a `ReplaneError` (`Config not found: <name>`).
272
+ - The client works as usual but doesn't receive SSE updates (values remain whatever is in-memory).
265
273
 
266
- Gracefully shuts down the Replane client and cleans up resources. Subsequent method calls is a no-op, it's safe to call multiple times. Use this in environments where you manage resource lifecycles explicitly (e.g. shutting down a server or worker).
274
+ ### `replane.disconnect()`
275
+
276
+ Gracefully shuts down the Replane client and cleans up resources. Safe to call multiple times. Use this in environments where you manage resource lifecycles explicitly (e.g. shutting down a server or worker).
267
277
 
268
278
  ```ts
269
279
  // During shutdown
270
- replane.close();
280
+ replane.disconnect();
271
281
  ```
272
282
 
273
283
  ### Errors
274
284
 
275
- `createReplaneClient` throws if the initial request to fetch configs fails with non‑2xx HTTP responses and network errors. A `ReplaneError` is thrown for HTTP failures; other errors may be thrown for network/parse issues.
285
+ `replane.connect()` throws if the initial request to fetch configs fails with non‑2xx HTTP responses and network errors. A `ReplaneError` is thrown for HTTP failures; other errors may be thrown for network/parse issues.
276
286
 
277
287
  The Replane client receives realtime updates via SSE in the background. SSE connection errors are logged and automatically retried, but don't affect `get` calls (which return the last known value).
278
288
 
@@ -295,7 +305,8 @@ interface Configs {
295
305
  layout: LayoutConfig;
296
306
  }
297
307
 
298
- const replane = await createReplaneClient<Configs>({
308
+ const replane = new Replane<Configs>();
309
+ await replane.connect({
299
310
  sdkKey: process.env.REPLANE_SDK_KEY!,
300
311
  baseUrl: "https://replane.my-host.com",
301
312
  });
@@ -311,7 +322,8 @@ interface Configs {
311
322
  "advanced-features": boolean;
312
323
  }
313
324
 
314
- const replane = await createReplaneClient<Configs>({
325
+ const replane = new Replane<Configs>();
326
+ await replane.connect({
315
327
  sdkKey: process.env.REPLANE_SDK_KEY!,
316
328
  baseUrl: "https://replane.my-host.com",
317
329
  });
@@ -336,14 +348,16 @@ interface Configs {
336
348
  "feature-flag": boolean;
337
349
  }
338
350
 
339
- const replane = await createReplaneClient<Configs>({
340
- sdkKey: process.env.REPLANE_SDK_KEY!,
341
- baseUrl: "https://replane.my-host.com",
351
+ const replane = new Replane<Configs>({
342
352
  context: {
343
353
  userId: "user-123",
344
354
  region: "us-east",
345
355
  },
346
356
  });
357
+ await replane.connect({
358
+ sdkKey: process.env.REPLANE_SDK_KEY!,
359
+ baseUrl: "https://replane.my-host.com",
360
+ });
347
361
 
348
362
  // This context is used for all configs unless overridden
349
363
  const value1 = replane.get("feature-flag"); // Uses client-level context
@@ -355,38 +369,14 @@ const value2 = replane.get("feature-flag", {
355
369
  ### Custom fetch (tests)
356
370
 
357
371
  ```ts
358
- const replane = await createReplaneClient({
372
+ const replane = new Replane();
373
+ await replane.connect({
359
374
  sdkKey: "TKN",
360
375
  baseUrl: "https://api",
361
376
  fetchFn: mockFetch,
362
377
  });
363
378
  ```
364
379
 
365
- ### Required configs
366
-
367
- ```ts
368
- interface Configs {
369
- "api-key": string;
370
- "database-url": string;
371
- "optional-feature": boolean;
372
- }
373
-
374
- const replane = await createReplaneClient<Configs>({
375
- sdkKey: process.env.REPLANE_SDK_KEY!,
376
- baseUrl: "https://replane.my-host.com",
377
- required: {
378
- "api-key": true,
379
- "database-url": true,
380
- "optional-feature": false, // Not required
381
- },
382
- });
383
-
384
- // Alternative: use an array
385
- // required: ["api-key", "database-url"]
386
-
387
- // If any required config is missing, initialization will throw
388
- ```
389
-
390
380
  ### Default configs
391
381
 
392
382
  ```ts
@@ -396,15 +386,17 @@ interface Configs {
396
386
  "timeout-ms": number;
397
387
  }
398
388
 
399
- const replane = await createReplaneClient<Configs>({
400
- sdkKey: process.env.REPLANE_SDK_KEY!,
401
- baseUrl: "https://replane.my-host.com",
389
+ const replane = new Replane<Configs>({
402
390
  defaults: {
403
391
  "feature-flag": false, // Use false if fetch fails
404
392
  "max-connections": 10, // Use 10 if fetch fails
405
393
  "timeout-ms": 5000, // Use 5s if fetch fails
406
394
  },
407
395
  });
396
+ await replane.connect({
397
+ sdkKey: process.env.REPLANE_SDK_KEY!,
398
+ baseUrl: "https://replane.my-host.com",
399
+ });
408
400
 
409
401
  // If the initial fetch fails or times out, default values are used
410
402
  // Once the client connects, it will receive realtime updates
@@ -425,12 +417,14 @@ interface ProjectBConfigs {
425
417
  }
426
418
 
427
419
  // Each project needs its own SDK key and Replane client instance
428
- const projectAConfigs = await createReplaneClient<ProjectAConfigs>({
420
+ const projectAConfigs = new Replane<ProjectAConfigs>();
421
+ await projectAConfigs.connect({
429
422
  sdkKey: process.env.PROJECT_A_SDK_KEY!,
430
423
  baseUrl: "https://replane.my-host.com",
431
424
  });
432
425
 
433
- const projectBConfigs = await createReplaneClient<ProjectBConfigs>({
426
+ const projectBConfigs = new Replane<ProjectBConfigs>();
427
+ await projectBConfigs.connect({
434
428
  sdkKey: process.env.PROJECT_B_SDK_KEY!,
435
429
  baseUrl: "https://replane.my-host.com",
436
430
  });
@@ -448,7 +442,8 @@ interface Configs {
448
442
  "max-users": number;
449
443
  }
450
444
 
451
- const replane = await createReplaneClient<Configs>({
445
+ const replane = new Replane<Configs>();
446
+ await replane.connect({
452
447
  sdkKey: process.env.REPLANE_SDK_KEY!,
453
448
  baseUrl: "https://replane.my-host.com",
454
449
  });
@@ -479,7 +474,7 @@ const unsubscribeMaxUsers = replane.subscribe("max-users", (config) => {
479
474
  unsubscribeAll();
480
475
  unsubscribeFeature();
481
476
  unsubscribeMaxUsers();
482
- replane.close();
477
+ replane.disconnect();
483
478
  ```
484
479
 
485
480
  ## Framework SDKs