@replanejs/sdk 0.9.2 → 0.9.4

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
@@ -4,12 +4,12 @@
4
4
  [![License](https://img.shields.io/github/license/replane-dev/replane-javascript)](https://github.com/replane-dev/replane-javascript/blob/main/LICENSE)
5
5
  [![Community](https://img.shields.io/badge/discussions-join-blue?logo=github)](https://github.com/orgs/replane-dev/discussions)
6
6
 
7
+ > **Tip:** Get started instantly with [Replane Cloud](https://cloud.replane.dev) — no infrastructure required.
8
+
7
9
  Small TypeScript client for watching configuration values from a Replane API with realtime updates and context-based override evaluation.
8
10
 
9
11
  Part of the Replane project: [replane-dev/replane](https://github.com/replane-dev/replane).
10
12
 
11
- > Status: early. Minimal surface area on purpose. Expect small breaking tweaks until 0.1.x.
12
-
13
13
  ## Why it exists
14
14
 
15
15
  You need: given a token + config name + optional context -> watch the value with realtime updates. This package does only that:
@@ -53,14 +53,17 @@ interface PasswordRequirements {
53
53
  // Create the client with optional constructor options
54
54
  const replane = new Replane<Configs>({
55
55
  context: {
56
- environment: "production",
56
+ // example context
57
+ userId: "user-123",
58
+ plan: "premium",
59
+ region: "us-east",
57
60
  },
58
61
  });
59
62
 
60
63
  // Connect to the server
61
64
  await replane.connect({
62
65
  sdkKey: process.env.REPLANE_SDK_KEY!,
63
- baseUrl: "https://replane.my-hosting.com",
66
+ baseUrl: "https://cloud.replane.dev", // or your self-hosted URL
64
67
  });
65
68
 
66
69
  // Get a config value (knows about latest updates via SSE)
@@ -79,9 +82,8 @@ const { minLength } = passwordReqs; // TypeScript knows this is PasswordRequirem
79
82
  // With context for override evaluation
80
83
  const enabled = replane.get("billing-enabled", {
81
84
  context: {
82
- userId: "user-123",
83
- plan: "premium",
84
- region: "us-east",
85
+ plan: "free",
86
+ deviceType: "mobile",
85
87
  },
86
88
  });
87
89
 
@@ -158,7 +160,7 @@ interface Configs {
158
160
  const replane = new Replane<Configs>();
159
161
  await replane.connect({
160
162
  sdkKey: "your-sdk-key",
161
- baseUrl: "https://replane.my-host.com",
163
+ baseUrl: "https://cloud.replane.dev", // or your self-hosted URL
162
164
  });
163
165
 
164
166
  // Get value without context - TypeScript knows this is boolean
@@ -176,31 +178,14 @@ const maxConnections = replane.get("max-connections", { default: 10 });
176
178
  replane.disconnect();
177
179
  ```
178
180
 
179
- ### `replane.subscribe(callback)` or `replane.subscribe(configName, callback)`
180
-
181
- Subscribe to config changes and receive real-time updates when configs are modified.
181
+ ### `replane.subscribe(configName, callback)`
182
182
 
183
- **Two overloads:**
184
-
185
- 1. **Subscribe to all config changes:**
186
-
187
- ```ts
188
- const unsubscribe = replane.subscribe((config) => {
189
- console.log(`Config ${config.name} changed to:`, config.value);
190
- });
191
- ```
192
-
193
- 2. **Subscribe to a specific config:**
194
- ```ts
195
- const unsubscribe = replane.subscribe("billing-enabled", (config) => {
196
- console.log(`billing-enabled changed to:`, config.value);
197
- });
198
- ```
183
+ Subscribe to a specific config's changes and receive real-time updates when it is modified.
199
184
 
200
185
  Parameters:
201
186
 
202
- - `callback` (function) – Function called when any config changes. Receives an object with `{ name, value }`.
203
- - `configName` (K extends keyof T) – Optional. If provided, only changes to this specific config will trigger the callback.
187
+ - `configName` (K extends keyof T) – The config to watch for changes.
188
+ - `callback` (function) – Function called when the config changes. Receives an object with `{ name, value }`.
204
189
 
205
190
  Returns a function to unsubscribe from the config changes.
206
191
 
@@ -215,12 +200,7 @@ interface Configs {
215
200
  const replane = new Replane<Configs>();
216
201
  await replane.connect({
217
202
  sdkKey: "your-sdk-key",
218
- baseUrl: "https://replane.my-host.com",
219
- });
220
-
221
- // Subscribe to all config changes
222
- const unsubscribeAll = replane.subscribe((config) => {
223
- console.log(`Config ${config.name} updated:`, config.value);
203
+ baseUrl: "https://cloud.replane.dev", // or your self-hosted URL
224
204
  });
225
205
 
226
206
  // Subscribe to a specific config
@@ -230,7 +210,6 @@ const unsubscribeFeature = replane.subscribe("feature-flag", (config) => {
230
210
  });
231
211
 
232
212
  // Later: unsubscribe when done
233
- unsubscribeAll();
234
213
  unsubscribeFeature();
235
214
 
236
215
  // Clean up when done
@@ -448,30 +427,18 @@ await replane.connect({
448
427
  baseUrl: "https://replane.my-host.com",
449
428
  });
450
429
 
451
- // Subscribe to all config changes
452
- const unsubscribeAll = replane.subscribe((config) => {
453
- console.log(`Config ${config.name} changed:`, config.value);
454
-
455
- // React to specific config changes
456
- if (config.name === "feature-flag") {
457
- console.log("Feature flag updated:", config.value);
458
- }
459
- });
460
-
461
- // Subscribe to a specific config only
430
+ // Subscribe to specific configs
462
431
  const unsubscribeFeature = replane.subscribe("feature-flag", (config) => {
463
432
  console.log("Feature flag changed:", config.value);
464
433
  // config.value is automatically typed as boolean
465
434
  });
466
435
 
467
- // Subscribe to multiple specific configs
468
436
  const unsubscribeMaxUsers = replane.subscribe("max-users", (config) => {
469
437
  console.log("Max users changed:", config.value);
470
438
  // config.value is automatically typed as number
471
439
  });
472
440
 
473
441
  // Cleanup
474
- unsubscribeAll();
475
442
  unsubscribeFeature();
476
443
  unsubscribeMaxUsers();
477
444
  replane.disconnect();
package/dist/index.cjs CHANGED
@@ -482,11 +482,14 @@ function castToContextType(expectedValue, contextValue) {
482
482
 
483
483
  //#endregion
484
484
  //#region src/version.ts
485
- const VERSION = "0.9.2";
485
+ const VERSION = "0.9.4";
486
486
  const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;
487
487
 
488
488
  //#endregion
489
489
  //#region src/client.ts
490
+ function asReplaneHandle(replane) {
491
+ return replane;
492
+ }
490
493
  /**
491
494
  * The Replane client for managing dynamic configuration.
492
495
  *
@@ -526,12 +529,31 @@ const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;
526
529
  * ```
527
530
  */
528
531
  var Replane = class {
532
+ constructor(options = {}) {
533
+ asReplaneHandle(this)._replane = new ReplaneImpl(options);
534
+ }
535
+ connect(options) {
536
+ return asReplaneHandle(this)._replane.connect(options);
537
+ }
538
+ disconnect() {
539
+ asReplaneHandle(this)._replane.disconnect();
540
+ }
541
+ get(configName, options) {
542
+ return asReplaneHandle(this)._replane.get(configName, options);
543
+ }
544
+ subscribe(configName, callback) {
545
+ return asReplaneHandle(this)._replane.subscribe(configName, callback);
546
+ }
547
+ getSnapshot() {
548
+ return asReplaneHandle(this)._replane.getSnapshot();
549
+ }
550
+ };
551
+ var ReplaneImpl = class {
529
552
  configs;
530
553
  context;
531
554
  logger;
532
555
  storage = null;
533
556
  configSubscriptions = new Map();
534
- clientSubscriptions = new Set();
535
557
  /**
536
558
  * Create a new Replane client.
537
559
  *
@@ -647,29 +669,28 @@ var Replane = class {
647
669
  return config.value;
648
670
  }
649
671
  }
650
- subscribe(callbackOrConfigName, callbackOrUndefined) {
651
- let configName = void 0;
652
- let callback;
653
- if (typeof callbackOrConfigName === "function") callback = callbackOrConfigName;
654
- else {
655
- configName = callbackOrConfigName;
656
- if (callbackOrUndefined === void 0) throw new Error("callback is required when config name is provided");
657
- callback = callbackOrUndefined;
658
- }
659
- const originalCallback = callback;
660
- callback = (...args) => {
661
- originalCallback(...args);
672
+ /**
673
+ * Subscribe to a specific config's changes.
674
+ *
675
+ * @param configName - The config to watch
676
+ * @param callback - Function called when the config changes
677
+ * @returns Unsubscribe function
678
+ *
679
+ * @example
680
+ * ```typescript
681
+ * const unsubscribe = client.subscribe('myConfig', (change) => {
682
+ * console.log(`myConfig changed to ${change.value}`);
683
+ * });
684
+ * ```
685
+ */
686
+ subscribe(configName, callback) {
687
+ const wrappedCallback = (config) => {
688
+ callback(config);
662
689
  };
663
- if (configName === void 0) {
664
- this.clientSubscriptions.add(callback);
665
- return () => {
666
- this.clientSubscriptions.delete(callback);
667
- };
668
- }
669
690
  if (!this.configSubscriptions.has(configName)) this.configSubscriptions.set(configName, new Set());
670
- this.configSubscriptions.get(configName).add(callback);
691
+ this.configSubscriptions.get(configName).add(wrappedCallback);
671
692
  return () => {
672
- this.configSubscriptions.get(configName)?.delete(callback);
693
+ this.configSubscriptions.get(configName)?.delete(wrappedCallback);
673
694
  if (this.configSubscriptions.get(configName)?.size === 0) this.configSubscriptions.delete(configName);
674
695
  };
675
696
  }
@@ -745,7 +766,6 @@ var Replane = class {
745
766
  name: config.name,
746
767
  value: config.value
747
768
  };
748
- for (const callback of this.clientSubscriptions) callback(change);
749
769
  for (const callback of this.configSubscriptions.get(config.name) ?? []) callback(change);
750
770
  }
751
771
  }
package/dist/index.d.cts CHANGED
@@ -63,10 +63,7 @@ interface GetConfigOptions<T> {
63
63
  /**
64
64
  * Helper type for mapping configs to their names and values
65
65
  */
66
- type MapConfig<T extends object> = { [K in keyof T]: {
67
- name: K;
68
- value: T[K];
69
- } }[keyof T];
66
+
70
67
  /**
71
68
  * Serializable snapshot of the client state.
72
69
  * Can be used to restore a client on the client-side from server-fetched configs.
@@ -204,132 +201,15 @@ interface ConnectOptions {
204
201
  * ```
205
202
  */
206
203
  declare class Replane<T extends object = Record<string, unknown>> {
207
- private configs;
208
- private context;
209
- private logger;
210
- private storage;
211
- private configSubscriptions;
212
- private clientSubscriptions;
213
- /**
214
- * Create a new Replane client.
215
- *
216
- * The client is usable immediately after construction with defaults or snapshot data.
217
- * Call `connect()` to establish a real-time connection for live updates.
218
- *
219
- * @param options - Configuration options
220
- */
221
204
  constructor(options?: ReplaneOptions<T>);
222
- /**
223
- * Connect to the Replane server for real-time config updates.
224
- *
225
- * This method establishes an SSE connection to receive live config updates.
226
- * If already connected, it will disconnect first and reconnect with new options.
227
- *
228
- * @param options - Connection options including baseUrl and sdkKey
229
- * @returns Promise that resolves when the initial connection is established
230
- * @throws {ReplaneError} If connection times out and no defaults are available
231
- *
232
- * @example
233
- * ```typescript
234
- * await client.connect({
235
- * baseUrl: 'https://app.replane.dev',
236
- * sdkKey: 'rp_xxx'
237
- * });
238
- * ```
239
- */
240
205
  connect(options: ConnectOptions): Promise<void>;
241
- /**
242
- * Disconnect from the Replane server.
243
- *
244
- * Stops the SSE connection and cleans up resources.
245
- * The client remains usable with cached config values.
246
- * Can call `connect()` again to reconnect.
247
- */
248
206
  disconnect(): void;
249
- /**
250
- * Get a config value by name.
251
- *
252
- * Evaluates any overrides based on the client context and per-call context.
253
- *
254
- * @param configName - The name of the config to retrieve
255
- * @param options - Optional settings for this call
256
- * @returns The config value
257
- * @throws {ReplaneError} If config not found and no default provided
258
- *
259
- * @example
260
- * ```typescript
261
- * // Simple get
262
- * const value = client.get('myConfig');
263
- *
264
- * // With default fallback
265
- * const value = client.get('myConfig', { default: 'fallback' });
266
- *
267
- * // With per-call context for override evaluation
268
- * const value = client.get('myConfig', { context: { userId: '123' } });
269
- * ```
270
- */
271
207
  get<K extends keyof T>(configName: K, options?: GetConfigOptions<T[K]>): T[K];
272
- /**
273
- * Subscribe to config changes.
274
- *
275
- * @param callback - Function called when any config changes
276
- * @returns Unsubscribe function
277
- *
278
- * @example
279
- * ```typescript
280
- * const unsubscribe = client.subscribe((change) => {
281
- * console.log(`Config ${change.name} changed to ${change.value}`);
282
- * });
283
- *
284
- * // Later: stop listening
285
- * unsubscribe();
286
- * ```
287
- */
288
- subscribe(callback: (config: MapConfig<T>) => void): () => void;
289
- /**
290
- * Subscribe to a specific config's changes.
291
- *
292
- * @param configName - The config to watch
293
- * @param callback - Function called when the config changes
294
- * @returns Unsubscribe function
295
- *
296
- * @example
297
- * ```typescript
298
- * const unsubscribe = client.subscribe('myConfig', (change) => {
299
- * console.log(`myConfig changed to ${change.value}`);
300
- * });
301
- * ```
302
- */
303
208
  subscribe<K extends keyof T>(configName: K, callback: (config: {
304
209
  name: K;
305
210
  value: T[K];
306
211
  }) => void): () => void;
307
- /**
308
- * Get a serializable snapshot of the current client state.
309
- *
310
- * Useful for SSR/hydration scenarios where you want to pass
311
- * configs from server to client.
312
- *
313
- * @returns Snapshot object that can be serialized to JSON
314
- *
315
- * @example
316
- * ```typescript
317
- * // On server
318
- * const snapshot = client.getSnapshot();
319
- * const json = JSON.stringify(snapshot);
320
- *
321
- * // On client
322
- * const client = new Replane({ snapshot: JSON.parse(json) });
323
- * ```
324
- */
325
212
  getSnapshot(): ReplaneSnapshot<T>;
326
- /**
327
- * Check if the client is currently connected.
328
- */
329
- get isConnected(): boolean;
330
- private startStreaming;
331
- private processConfigUpdates;
332
- private toFinalOptions;
333
213
  }
334
214
  //# sourceMappingURL=client.d.ts.map
335
215
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/client-types.ts","../src/client.ts","../src/error.ts","../src/snapshot.ts"],"sourcesContent":[],"mappings":";;UAqBU,iBAAA;;ECfE,QAAA,EAAA,MAAA;EAKK,KAAA,EAAA,OAAA;AAUjB;UDaU,qBAAA,CCbuB;EAAA,QAIrB,EAAA,cAAA;EAAc,QAKd,EAAA,MAAA;EAAC,cAAA,EAAA,MAAA;EAMD,YAAS,EAAA,MAAA;EAAA,IAAA,EAAA,MAAA;;UDMX,YAAA,CCJA;EAAC,QACA,EAAA,KAAA;EAAC,UAAC,EDKC,iBCLD,EAAA;;AAEJ,UDMC,WAAA,CCND;EAMQ,QAAA,EAAA,IAAA;EAAe,UAAA,EDElB,iBCFkB,EAAA;;UDKtB,YAAA,CCHC;EAAK,QAAA,EAAA,KAAA;EAUC,SAAA,EDLJ,iBCKkB;;AAIpB,KDNC,iBAAA,GACR,iBCKO,GDJP,qBCIO,GDHP,YCGO,GDFP,WCEO,GDDP,YCCO;AAKC,UDJK,gBAAA,CCIL;EAAc,IAaV,EAAA,MAAA;EAAC,UAAI,EDfP,iBCeO,EAAA;EAAC,KAAC,EAAA,OAAA;;;;AD9DjB;AAEqB;AAaI;AAUA;AAQrB,KC9CE,cAAA,GAAiB,MDgDhB,CAAA,MAAA,EAAA,MAAiB,GAAA,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,SAAA,CAAA;AAG9B;;;AAEI,UChDa,aAAA,CDgDb;EAAqB,KACrB,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAY,IACZ,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAW,IACX,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAY,KAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;AAEhB;;;;AC1DY,UAeK,gBAfY,CAAA,CAAA,CAAA,CAAM;EAKlB;AAUjB;;EAAiC,OAIrB,CAAA,EAAA,cAAA;EAAc;AAKb;AAMb;;EAAqB,OACP,CAAA,EAPF,CAOE;;;;;AAIL,KALG,SAKH,CAAA,UAAA,MAAA,CAAA,GAAA,QAMQ,MAVH,CAUG,GAAA;EAAe,IAAA,EATtB,CASsB;EAKjB,KAAA,EAbJ,CAaI,CAbF,CAaE,CAAA;AAAgB,CAAA,EAHf,CAAA,MARR,CAQQ,CAAA;AAUhB;;;;AAsBgB,UAlCC,eAkCD,CAAA,WAAA,MAAA,GAAA,MAAA,CAAA,CAAA;EAAC;EAAK,OAAC,EAhCZ,KAgCY,CAAA;IAMM,IAAA,EAAA,MAAA;IAAhB,KAAA,EAAA,OAAA;IAAe,SAAA,EAnCb,gBAmCa,EAAA;EAMX,CAAA,CAAA;;;;ACtCjB;AAAoB,UDIH,cCJG,CAAA,UAAA,MAAA,CAAA,CAAA;EAAA;;;EAgBiB,MAqDZ,CAAA,ED7Dd,aC6Dc;EAAc;;;;EAgE4B,OAAC,CAAA,EDxHxD,cCwHwD;EAAC;;;;;;;;;;;EAgInC,QAAjB,CAAA,EAAA,QAAe,MD3OhB,CC2OgB,ID3OX,CC2OW,CD3OT,CC2OS,CAAA;;;ACzThC;AAgBA;aFoEa,gBAAgB;;;AGjF7B;;AAAoF,UHuFnE,cAAA,CGvFmE;EAAC;;AAAF;AAwDnF;;;;;EAE4B,OAAjB,EAAA,MAAA;EAAe;AAAhB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBHqES;;;;;;;;;;;;ADlHb;AAEqB;AAaI;AAUA;AAKA;AAQ/B;;;;;;;AAKgB;AAEhB;;;;AC1DA;AAKA;AAUA;;;;AASa;AAMb;;;;;;;AAKS;AAMT;;;;AAEgB;AAUhB;AAA+B,cCJlB,ODIkB,CAAA,UAAA,MAAA,GCJS,MDIT,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA;EAAA,QAIpB,OAAA;EAAa,QAKZ,OAAA;EAAc,QAaV,MAAA;EAAC,QAAI,OAAA;EAAC,QAAC,mBAAA;EAAC,QAMK,mBAAA;EAAC;AAAF;AAM5B;;;;ACtCA;;EAAoB,WAAoB,CAAA,OAAA,CAAA,EAgBjB,cAhBiB,CAgBF,CAhBE,CAAA;EAAM;;;;;;;;;;;;;;;;;;EAoMF,OAiEX,CAAA,OAAA,EAhMR,cAgMQ,CAAA,EAhMS,OAgMT,CAAA,IAAA,CAAA;EAAC;AAAF;;;;ACzThC;AAgBA;;;;ACbA;;;;;AAAmF;AAwDnF;;;;;;;AAEU;;;;;;;sBF4HY,eAAe,aAAY,iBAAiB,EAAE,MAAW,EAAE;;;;;;;;;;;;;;;;;+BA6ClD,UAAU;;;;;;;;;;;;;;;4BAgBb,eACZ;UACe;WAAU,EAAE;;;;;;;;;;;;;;;;;;;;iBAiE1B,gBAAgB;;;;;;;;;;;;;AFzS3B;AAEqB;AAqBjB,aGvCE,gBAAA;EH4CF,QAAA,GAAA,WAAW;EAKX,OAAA,GAAA,SAAY;EAKV,YAAA,GAAA,eAAiB;EAAA,SAAA,GAAA,YAAA;EAAA,SACzB,GAAA,WAAA;EAAiB,WACjB,GAAA,cAAA;EAAqB,WACrB,GAAA,cAAA;EAAY,MACZ,GAAA,QAAA;EAAW,cACX,GAAA,iBAAA;EAAY,OAAA,GAAA,SAAA;AAEhB;;;;AC1DY,cEaC,YAAA,SAAqB,KAAK,CFbJ;EAKlB,IAAA,EAAA,MAAA;EAUA,WAAA,CAAA,MAAA,EAAgB;IAAA,OAAA,EAAA,MAAA;IAIrB,IAAA,EAAA,MAAA;IAKA,KAAA,CAAA,EAAA,OAAA;EAAC,CAAA;AAMb;;;;ADjBM;AAEqB;AAaI;AAarB,UIzCO,yBJ2CH,CAAA,UAAiB,MAAA,CAAA,SI3CsC,cJ2CtC,CI3CqD,CJ2CrD,CAAA,CAAA;EAGrB;AAKV;;;;EAEyB,WACrB,CAAA,EAAA,MAAA;EAAY;;AAEA;EAEC,UAAA,EIhDH,cJgDmB,GAAA,IAEnB;;;;AC5Dd;AAKA;AAUA;;;;AASa;AAMb;;;;;;AAKQ,iBGqBc,kBHrBd,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,EGsBG,yBHtBH,CGsB6B,CHtB7B,CAAA,CAAA,EGuBL,OHvBK,CGuBG,eHvBH,CGuBmB,CHvBnB,CAAA,CAAA;AAAC;AAMT"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/client-types.ts","../src/client.ts","../src/error.ts","../src/snapshot.ts"],"sourcesContent":[],"mappings":";;UAqBU,iBAAA;;ECfE,QAAA,EAAA,MAAA;EAKK,KAAA,EAAA,OAAA;AAUjB;UDaU,qBAAA,CCbuB;EAAA,QAIrB,EAAA,cAAA;EAAc,QAKd,EAAA,MAAA;EAAC,cAAA,EAAA,MAAA;EAiBI,YAAA,EAAA,MAAe;EAAA,IAAA,EAAA,MAAA;;UDLtB,YAAA,CCOC;EAAK,QAAA,EAAA,KAAA;EAUC,UAAA,EDfH,iBCeiB,EAAA;;UDZrB,WAAA,CCgBC;EAAa,QAKZ,EAAA,IAAA;EAAc,UAaV,EDhCF,iBCgCE,EAAA;;UD7BN,YAAA,CC6Ba;EAAC,QAMK,EAAA,KAAA;EAAC,SAAjB,EDjCA,iBCiCA;AAAe;AAMX,KDpCL,iBAAA,GACR,iBC2EoB,GD1EpB,qBC0EoB,GDzEpB,YCyEoB,GDxEpB,WCwEoB,GDvEpB,YCuEoB;UDrEP,gBAAA;;cAEH;EEHD,KAAA,EAAA,OAAO;;;;AF5Cd;AAEqB;AAaI;AAUA;AAQrB,KC9CE,cAAA,GAAiB,MDgDhB,CAAA,MAAA,EAAA,MAAiB,GAAA,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,SAAA,CAAA;AAG9B;;;AAEI,UChDa,aAAA,CDgDb;EAAqB,KACrB,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAY,IACZ,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAW,IACX,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;EAAY,KAAA,CAAA,GAAA,IAAA,EAAA,OAAA,EAAA,CAAA,EAAA,IAAA;AAEhB;;;;AC1DY,UAeK,gBAfY,CAAA,CAAA,CAAA,CAAM;EAKlB;AAUjB;;EAAiC,OAIrB,CAAA,EAAA,cAAA;EAAc;AAKb;AAiBb;;EAAgC,OAKjB,CAAA,EAtBH,CAsBG;;AAHC;AAUhB;;;;AA4B4B;AAM5B;;UA9CiB;;ECgBJ,OAAA,EDdF,KCcS,CAAA;IAAA,IAAA,EAAA,MAAA;IAAoB,KAAA,EAAA,OAAA;IACF,SAAA,EDZvB,gBCYuB,EAAA;EAAC,CAAA,CAAA;;;;;AAS4B,UDdlD,cCckD,CAAA,UAAA,MAAA,CAAA,CAAA;EAAC;;;EAAQ,MAAC,CAAA,EDVlE,aCUkE;EAAC;;;;EAKpC,OAAC,CAAA,EDV/B,cCU+B;EAAC;;AAIZ;;;;AC/EhC;AAgBA;;;;ECbiB,QAAA,CAAA,EAAA,QAAyB,MH2E1B,CG3E0B,IH2ErB,CG3EqB,CH2EnB,CG3EmB,CAAA,EAAA;EAA2C;;AAAF;AAwDnF;EAAwC,QAAA,CAAA,EHyB3B,eGzB2B,CHyBX,CGzBW,CAAA;;;;;AAErC,UH6Bc,cAAA,CG7Bd;EAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mBHqES;;;;;;;;;;;;ADlHb;AAEqB;AAaI;AAUA;AAKA;AAQ/B;;;;;;;AAKgB;AAEhB;;;;AC1DA;AAKA;AAUA;;;;AASa;AAiBb;;;;AAEgB;AAUhB;;;;;;;;;AA4B4B,cCxBf,ODwBe,CAAA,UAAA,MAAA,GCxBY,MDwBZ,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA;EAMX,WAAA,CAAA,OAwCO,CAxCO,EC7BR,cDqEC,CCrEc,CDqEd,CAAA;mBClEL,iBAAiB;;sBAMd,eAAe,aAAa,iBAAiB,EAAE,MAAM,EAAE;EAVhE,SAAA,CAAO,UAAA,MAaQ,CAbR,CAAA,CAAA,UAAA,EAcJ,CAdI,EAAA,QAAA,EAAA,CAAA,MAAA,EAAA;IAAA,IAAA,EAeW,CAfX;IAAoB,KAAA,EAeC,CAfD,CAeG,CAfH,CAAA;EAAM,CAAA,EACR,GAAA,IAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAAC,WAAhB,CAAA,CAAA,EAkBN,eAlBM,CAkBU,CAlBV,CAAA;;;;;;AF7CjB;AAEqB;AAqBjB,aGvCE,gBAAA;EH4CF,QAAA,GAAA,WAAW;EAKX,OAAA,GAAA,SAAY;EAKV,YAAA,GAAA,eAAiB;EAAA,SAAA,GAAA,YAAA;EAAA,SACzB,GAAA,WAAA;EAAiB,WACjB,GAAA,cAAA;EAAqB,WACrB,GAAA,cAAA;EAAY,MACZ,GAAA,QAAA;EAAW,cACX,GAAA,iBAAA;EAAY,OAAA,GAAA,SAAA;AAEhB;;;;AC1DY,cEaC,YAAA,SAAqB,KAAK,CFbJ;EAKlB,IAAA,EAAA,MAAA;EAUA,WAAA,CAAA,MAAA,EAAgB;IAAA,OAAA,EAAA,MAAA;IAIrB,IAAA,EAAA,MAAA;IAKA,KAAA,CAAA,EAAA,OAAA;EAAC,CAAA;AAiBb;;;;AD5BM;AAEqB;AAaI;AAarB,UIzCO,yBJ2CH,CAAA,UAAiB,MAAA,CAAA,SI3CsC,cJ2CtC,CI3CqD,CJ2CrD,CAAA,CAAA;EAGrB;AAKV;;;;EAEyB,WACrB,CAAA,EAAA,MAAA;EAAY;;AAEA;EAEC,UAAA,EIhDH,cJgDmB,GAAA,IAEnB;;;;AC5Dd;AAKA;AAUA;;;;AASa;AAiBb;;;;AAEgB;AAUhB;AAA+B,iBGGT,kBHHS,CAAA,UAAA,MAAA,CAAA,CAAA,OAAA,EGIpB,yBHJoB,CGIM,CHJN,CAAA,CAAA,EGK5B,OHL4B,CGKpB,eHLoB,CGKJ,CHLI,CAAA,CAAA"}
package/dist/index.d.ts CHANGED
@@ -63,10 +63,7 @@ interface GetConfigOptions<T> {
63
63
  /**
64
64
  * Helper type for mapping configs to their names and values
65
65
  */
66
- type MapConfig<T extends object> = { [K in keyof T]: {
67
- name: K;
68
- value: T[K];
69
- } }[keyof T];
66
+
70
67
  /**
71
68
  * Serializable snapshot of the client state.
72
69
  * Can be used to restore a client on the client-side from server-fetched configs.
@@ -204,132 +201,15 @@ interface ConnectOptions {
204
201
  * ```
205
202
  */
206
203
  declare class Replane<T extends object = Record<string, unknown>> {
207
- private configs;
208
- private context;
209
- private logger;
210
- private storage;
211
- private configSubscriptions;
212
- private clientSubscriptions;
213
- /**
214
- * Create a new Replane client.
215
- *
216
- * The client is usable immediately after construction with defaults or snapshot data.
217
- * Call `connect()` to establish a real-time connection for live updates.
218
- *
219
- * @param options - Configuration options
220
- */
221
204
  constructor(options?: ReplaneOptions<T>);
222
- /**
223
- * Connect to the Replane server for real-time config updates.
224
- *
225
- * This method establishes an SSE connection to receive live config updates.
226
- * If already connected, it will disconnect first and reconnect with new options.
227
- *
228
- * @param options - Connection options including baseUrl and sdkKey
229
- * @returns Promise that resolves when the initial connection is established
230
- * @throws {ReplaneError} If connection times out and no defaults are available
231
- *
232
- * @example
233
- * ```typescript
234
- * await client.connect({
235
- * baseUrl: 'https://app.replane.dev',
236
- * sdkKey: 'rp_xxx'
237
- * });
238
- * ```
239
- */
240
205
  connect(options: ConnectOptions): Promise<void>;
241
- /**
242
- * Disconnect from the Replane server.
243
- *
244
- * Stops the SSE connection and cleans up resources.
245
- * The client remains usable with cached config values.
246
- * Can call `connect()` again to reconnect.
247
- */
248
206
  disconnect(): void;
249
- /**
250
- * Get a config value by name.
251
- *
252
- * Evaluates any overrides based on the client context and per-call context.
253
- *
254
- * @param configName - The name of the config to retrieve
255
- * @param options - Optional settings for this call
256
- * @returns The config value
257
- * @throws {ReplaneError} If config not found and no default provided
258
- *
259
- * @example
260
- * ```typescript
261
- * // Simple get
262
- * const value = client.get('myConfig');
263
- *
264
- * // With default fallback
265
- * const value = client.get('myConfig', { default: 'fallback' });
266
- *
267
- * // With per-call context for override evaluation
268
- * const value = client.get('myConfig', { context: { userId: '123' } });
269
- * ```
270
- */
271
207
  get<K extends keyof T>(configName: K, options?: GetConfigOptions<T[K]>): T[K];
272
- /**
273
- * Subscribe to config changes.
274
- *
275
- * @param callback - Function called when any config changes
276
- * @returns Unsubscribe function
277
- *
278
- * @example
279
- * ```typescript
280
- * const unsubscribe = client.subscribe((change) => {
281
- * console.log(`Config ${change.name} changed to ${change.value}`);
282
- * });
283
- *
284
- * // Later: stop listening
285
- * unsubscribe();
286
- * ```
287
- */
288
- subscribe(callback: (config: MapConfig<T>) => void): () => void;
289
- /**
290
- * Subscribe to a specific config's changes.
291
- *
292
- * @param configName - The config to watch
293
- * @param callback - Function called when the config changes
294
- * @returns Unsubscribe function
295
- *
296
- * @example
297
- * ```typescript
298
- * const unsubscribe = client.subscribe('myConfig', (change) => {
299
- * console.log(`myConfig changed to ${change.value}`);
300
- * });
301
- * ```
302
- */
303
208
  subscribe<K extends keyof T>(configName: K, callback: (config: {
304
209
  name: K;
305
210
  value: T[K];
306
211
  }) => void): () => void;
307
- /**
308
- * Get a serializable snapshot of the current client state.
309
- *
310
- * Useful for SSR/hydration scenarios where you want to pass
311
- * configs from server to client.
312
- *
313
- * @returns Snapshot object that can be serialized to JSON
314
- *
315
- * @example
316
- * ```typescript
317
- * // On server
318
- * const snapshot = client.getSnapshot();
319
- * const json = JSON.stringify(snapshot);
320
- *
321
- * // On client
322
- * const client = new Replane({ snapshot: JSON.parse(json) });
323
- * ```
324
- */
325
212
  getSnapshot(): ReplaneSnapshot<T>;
326
- /**
327
- * Check if the client is currently connected.
328
- */
329
- get isConnected(): boolean;
330
- private startStreaming;
331
- private processConfigUpdates;
332
- private toFinalOptions;
333
213
  }
334
214
  //# sourceMappingURL=client.d.ts.map
335
215
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/client-types.ts","../src/client.ts","../src/error.ts","../src/snapshot.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/client-types.ts","../src/client.ts","../src/error.ts","../src/snapshot.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -481,11 +481,14 @@ function castToContextType(expectedValue, contextValue) {
481
481
 
482
482
  //#endregion
483
483
  //#region src/version.ts
484
- const VERSION = "0.9.2";
484
+ const VERSION = "0.9.4";
485
485
  const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;
486
486
 
487
487
  //#endregion
488
488
  //#region src/client.ts
489
+ function asReplaneHandle(replane) {
490
+ return replane;
491
+ }
489
492
  /**
490
493
  * The Replane client for managing dynamic configuration.
491
494
  *
@@ -525,12 +528,31 @@ const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;
525
528
  * ```
526
529
  */
527
530
  var Replane = class {
531
+ constructor(options = {}) {
532
+ asReplaneHandle(this)._replane = new ReplaneImpl(options);
533
+ }
534
+ connect(options) {
535
+ return asReplaneHandle(this)._replane.connect(options);
536
+ }
537
+ disconnect() {
538
+ asReplaneHandle(this)._replane.disconnect();
539
+ }
540
+ get(configName, options) {
541
+ return asReplaneHandle(this)._replane.get(configName, options);
542
+ }
543
+ subscribe(configName, callback) {
544
+ return asReplaneHandle(this)._replane.subscribe(configName, callback);
545
+ }
546
+ getSnapshot() {
547
+ return asReplaneHandle(this)._replane.getSnapshot();
548
+ }
549
+ };
550
+ var ReplaneImpl = class {
528
551
  configs;
529
552
  context;
530
553
  logger;
531
554
  storage = null;
532
555
  configSubscriptions = new Map();
533
- clientSubscriptions = new Set();
534
556
  /**
535
557
  * Create a new Replane client.
536
558
  *
@@ -646,29 +668,28 @@ var Replane = class {
646
668
  return config.value;
647
669
  }
648
670
  }
649
- subscribe(callbackOrConfigName, callbackOrUndefined) {
650
- let configName = void 0;
651
- let callback;
652
- if (typeof callbackOrConfigName === "function") callback = callbackOrConfigName;
653
- else {
654
- configName = callbackOrConfigName;
655
- if (callbackOrUndefined === void 0) throw new Error("callback is required when config name is provided");
656
- callback = callbackOrUndefined;
657
- }
658
- const originalCallback = callback;
659
- callback = (...args) => {
660
- originalCallback(...args);
671
+ /**
672
+ * Subscribe to a specific config's changes.
673
+ *
674
+ * @param configName - The config to watch
675
+ * @param callback - Function called when the config changes
676
+ * @returns Unsubscribe function
677
+ *
678
+ * @example
679
+ * ```typescript
680
+ * const unsubscribe = client.subscribe('myConfig', (change) => {
681
+ * console.log(`myConfig changed to ${change.value}`);
682
+ * });
683
+ * ```
684
+ */
685
+ subscribe(configName, callback) {
686
+ const wrappedCallback = (config) => {
687
+ callback(config);
661
688
  };
662
- if (configName === void 0) {
663
- this.clientSubscriptions.add(callback);
664
- return () => {
665
- this.clientSubscriptions.delete(callback);
666
- };
667
- }
668
689
  if (!this.configSubscriptions.has(configName)) this.configSubscriptions.set(configName, new Set());
669
- this.configSubscriptions.get(configName).add(callback);
690
+ this.configSubscriptions.get(configName).add(wrappedCallback);
670
691
  return () => {
671
- this.configSubscriptions.get(configName)?.delete(callback);
692
+ this.configSubscriptions.get(configName)?.delete(wrappedCallback);
672
693
  if (this.configSubscriptions.get(configName)?.size === 0) this.configSubscriptions.delete(configName);
673
694
  };
674
695
  }
@@ -744,7 +765,6 @@ var Replane = class {
744
765
  name: config.name,
745
766
  value: config.value
746
767
  };
747
- for (const callback of this.clientSubscriptions) callback(change);
748
768
  for (const callback of this.configSubscriptions.get(config.name) ?? []) callback(change);
749
769
  }
750
770
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["params: { message: string; code: string; cause?: unknown }","ms: number","averageDelay: number","signals: Array<AbortSignal | undefined | null>","input: string | URL | Request","init: RequestInit","timeoutMs: number","fetchFn: typeof fetch","response: Response","message: string","body: unknown","params: FetchSseOptions","dataLines: string[]","comment: string | null","options: StartReplicationStreamOptions","error: unknown","options: StartReplicationStreamOptions & { onConnect?: () => void }","inactivityTimer: ReturnType<typeof setTimeout> | null","options: ConnectFinalOptions","path: string","input: string","baseValue: T","overrides: RenderedOverride[]","context: ReplaneContext","logger: ReplaneLogger","overrideResult: EvaluationResult","condition: RenderedCondition","contextValue","value: never","message: string","expectedValue: unknown","contextValue: unknown","options: ReplaneOptions<T>","initialConfigs: ConfigDto[]","options: ConnectOptions","configName: K","options: GetConfigOptions<T[K]>","callbackOrConfigName: K | ((config: MapConfig<T>) => void)","callbackOrUndefined?: (config: { name: K; value: T[K] }) => void","configName: keyof T | undefined","callback: (config: MapConfig<T>) => void","options: ConnectFinalOptions","clientReady: Deferred<void>","updatedConfigs: ConfigDto[]","options: ConnectOptions","cacheKey: string","keepAliveMs: number","options: GetReplaneSnapshotOptions<T>","client","entry: CachedClient"],"sources":["../src/error.ts","../src/utils.ts","../src/sse.ts","../src/storage.ts","../src/hash.ts","../src/evaluation.ts","../src/version.ts","../src/client.ts","../src/snapshot.ts"],"sourcesContent":["/**\n * Error codes for ReplaneError\n */\nexport enum ReplaneErrorCode {\n NotFound = \"not_found\",\n Timeout = \"timeout\",\n NetworkError = \"network_error\",\n AuthError = \"auth_error\",\n Forbidden = \"forbidden\",\n ServerError = \"server_error\",\n ClientError = \"client_error\",\n Closed = \"closed\",\n NotInitialized = \"not_initialized\",\n Unknown = \"unknown\",\n}\n\n/**\n * Custom error class for Replane SDK errors\n */\nexport class ReplaneError extends Error {\n code: string;\n\n constructor(params: { message: string; code: string; cause?: unknown }) {\n super(params.message, { cause: params.cause });\n this.name = \"ReplaneError\";\n this.code = params.code;\n }\n}\n","/**\n * Returns a promise that resolves after the specified delay\n *\n * @param ms - Delay in milliseconds\n */\nexport async function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Returns a promise that resolves after a delay with jitter.\n * The actual delay is the average delay ± 10% (jitter = averageDelay/5).\n *\n * @param averageDelay - The average delay in milliseconds\n */\nexport async function retryDelay(averageDelay: number): Promise<void> {\n const jitter = averageDelay / 5;\n const delayMs = averageDelay + Math.random() * jitter - jitter / 2;\n\n await delay(delayMs);\n}\n\n/**\n * Combines multiple abort signals into one.\n * When any of the input signals is aborted, the combined signal will also be aborted.\n *\n * @param signals - Array of AbortSignal instances (can contain undefined/null)\n * @returns An object containing the combined signal and a cleanup function\n */\nexport function combineAbortSignals(signals: Array<AbortSignal | undefined | null>): {\n signal: AbortSignal;\n cleanUpSignals: () => void;\n} {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n cleanUpSignals();\n };\n\n const cleanUpSignals = () => {\n for (const s of signals) {\n s?.removeEventListener(\"abort\", onAbort);\n }\n };\n\n for (const s of signals) {\n s?.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n if (signals.some((s) => s?.aborted)) {\n onAbort();\n }\n\n return { signal: controller.signal, cleanUpSignals };\n}\n\n/**\n * A deferred promise that can be resolved or rejected from outside.\n * Useful for coordinating async operations.\n */\nexport class Deferred<T> {\n public readonly promise: Promise<T>;\n public resolve!: (value: T) => void;\n public reject!: (error: unknown) => void;\n\n constructor() {\n this.promise = new Promise((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\n","import { ReplaneError, ReplaneErrorCode } from \"./error\";\nimport { combineAbortSignals } from \"./utils\";\n\nconst SSE_DATA_PREFIX = \"data:\";\n\n/**\n * Parsed SSE event\n */\nexport type SseEvent = { type: \"comment\"; comment: string } | { type: \"data\"; data: string };\n\n/**\n * Options for fetchSse\n */\nexport interface FetchSseOptions {\n fetchFn: typeof fetch;\n url: string;\n timeoutMs: number;\n body?: string;\n headers?: Record<string, string>;\n method?: string;\n signal?: AbortSignal;\n onConnect?: () => void;\n}\n\n/**\n * Fetch with timeout support\n */\nexport async function fetchWithTimeout(\n input: string | URL | Request,\n init: RequestInit,\n timeoutMs: number,\n fetchFn: typeof fetch\n): Promise<Response> {\n if (!fetchFn) {\n throw new Error(\"Global fetch is not available. Provide options.fetchFn.\");\n }\n if (!timeoutMs) return fetchFn(input, init);\n\n const timeoutController = new AbortController();\n const t = setTimeout(() => timeoutController.abort(), timeoutMs);\n // Note: We intentionally don't call cleanUpSignals() here because for streaming\n // responses (like SSE), the connection remains open after the response headers\n // are received. The abort signal needs to remain connected so that close() can\n // propagate the abort through the signal chain.\n const { signal } = combineAbortSignals([init.signal, timeoutController.signal]);\n try {\n return await fetchFn(input, {\n ...init,\n signal,\n });\n } finally {\n clearTimeout(t);\n }\n}\n\n/**\n * Ensures the response is successful, throwing ReplaneError if not\n */\nexport async function ensureSuccessfulResponse(response: Response, message: string): Promise<void> {\n if (response.status === 404) {\n throw new ReplaneError({\n message: `Not found: ${message}`,\n code: ReplaneErrorCode.NotFound,\n });\n }\n\n if (response.status === 401) {\n throw new ReplaneError({\n message: `Unauthorized access: ${message}`,\n code: ReplaneErrorCode.AuthError,\n });\n }\n\n if (response.status === 403) {\n throw new ReplaneError({\n message: `Forbidden access: ${message}`,\n code: ReplaneErrorCode.Forbidden,\n });\n }\n\n if (!response.ok) {\n let body: unknown;\n try {\n body = await response.text();\n } catch {\n body = \"<unable to read response body>\";\n }\n\n const code =\n response.status >= 500\n ? ReplaneErrorCode.ServerError\n : response.status >= 400\n ? ReplaneErrorCode.ClientError\n : ReplaneErrorCode.Unknown;\n\n throw new ReplaneError({\n message: `Fetch response isn't successful (${message}): ${response.status} ${response.statusText} - ${body}`,\n code,\n });\n }\n}\n\n/**\n * Fetches a Server-Sent Events (SSE) stream and yields parsed events.\n *\n * @param params - Options for the SSE fetch\n * @yields SseEvent objects containing either data or comment events\n */\nexport async function* fetchSse(params: FetchSseOptions): AsyncGenerator<SseEvent> {\n const abortController = new AbortController();\n const { signal, cleanUpSignals } = params.signal\n ? combineAbortSignals([params.signal, abortController.signal])\n : { signal: abortController.signal, cleanUpSignals: () => {} };\n\n try {\n const res = await fetchWithTimeout(\n params.url,\n {\n method: params.method ?? \"GET\",\n headers: { Accept: \"text/event-stream\", ...(params.headers ?? {}) },\n body: params.body,\n signal,\n },\n params.timeoutMs,\n params.fetchFn\n );\n\n await ensureSuccessfulResponse(res, `SSE ${params.url}`);\n const responseContentType = res.headers.get(\"content-type\") ?? \"\";\n\n if (!responseContentType.includes(\"text/event-stream\")) {\n throw new ReplaneError({\n message: `Expected text/event-stream, got \"${responseContentType}\"`,\n code: ReplaneErrorCode.ServerError,\n });\n }\n\n if (!res.body) {\n throw new ReplaneError({\n message: `Failed to fetch SSE ${params.url}: body is empty`,\n code: ReplaneErrorCode.Unknown,\n });\n }\n\n if (params.onConnect) {\n params.onConnect();\n }\n\n const decoded = res.body.pipeThrough(new TextDecoderStream());\n const reader = decoded.getReader();\n\n let buffer = \"\";\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += value!;\n\n // Split on blank line; handle both \\n\\n and \\r\\n\\r\\n\n const frames = buffer.split(/\\r?\\n\\r?\\n/);\n buffer = frames.pop() ?? \"\";\n\n for (const frame of frames) {\n // Parse lines inside a single SSE event frame\n const dataLines: string[] = [];\n let comment: string | null = null;\n\n for (const rawLine of frame.split(/\\r?\\n/)) {\n if (!rawLine) continue;\n if (rawLine.startsWith(\":\")) {\n // comment/keepalive\n comment = rawLine.slice(1);\n continue;\n }\n\n if (rawLine.startsWith(SSE_DATA_PREFIX)) {\n // Keep leading space after \"data:\" if present per spec\n const line = rawLine.slice(SSE_DATA_PREFIX.length).replace(/^\\s/, \"\");\n dataLines.push(line);\n }\n }\n\n if (dataLines.length) {\n const data = dataLines.join(\"\\n\");\n yield { type: \"data\", data };\n } else if (comment !== null) {\n yield { type: \"comment\", comment };\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch {\n // ignore error\n }\n abortController.abort();\n }\n } finally {\n cleanUpSignals();\n }\n}\n","import type { ReplicationStreamRecord, StartReplicationStreamBody } from \"./types\";\nimport type { ConnectFinalOptions, ReplaneLogger } from \"./client-types\";\nimport { fetchSse } from \"./sse\";\nimport { combineAbortSignals, retryDelay } from \"./utils\";\n\nconst SUPPORTED_REPLICATION_STREAM_RECORD_TYPES = Object.keys({\n config_change: true,\n init: true,\n} satisfies Record<ReplicationStreamRecord[\"type\"], true>);\n\n/**\n * Options for starting a replication stream\n */\nexport interface StartReplicationStreamOptions extends ConnectFinalOptions {\n // getBody is a function to get the latest configs when we are trying\n // to reestablish the replication stream\n getBody: () => StartReplicationStreamBody;\n signal?: AbortSignal;\n logger: ReplaneLogger;\n}\n\n/**\n * Interface for storage implementations\n */\nexport interface ReplaneStorage {\n startReplicationStream(\n options: StartReplicationStreamOptions\n ): AsyncIterable<ReplicationStreamRecord>;\n disconnect(): void;\n}\n\n/**\n * Remote storage implementation that connects to the Replane server\n * and streams config updates via SSE.\n */\nexport class ReplaneRemoteStorage implements ReplaneStorage {\n private disconnectController = new AbortController();\n\n /**\n * Start a replication stream that yields config updates.\n * This method never throws - it retries on failure with exponential backoff.\n */\n async *startReplicationStream(\n options: StartReplicationStreamOptions\n ): AsyncIterable<ReplicationStreamRecord> {\n const { signal, cleanUpSignals } = combineAbortSignals([\n this.disconnectController.signal,\n options.signal,\n ]);\n try {\n let failedAttempts = 0;\n while (!signal.aborted) {\n try {\n for await (const event of this.startReplicationStreamImpl({\n ...options,\n signal,\n onConnect: () => {\n failedAttempts = 0;\n },\n })) {\n yield event;\n }\n } catch (error: unknown) {\n failedAttempts++;\n const retryDelayMs = Math.min(options.retryDelayMs * 2 ** (failedAttempts - 1), 10_000);\n if (!signal.aborted) {\n options.logger.error(\n `Failed to fetch project events, retrying in ${retryDelayMs}ms...`,\n error\n );\n await retryDelay(retryDelayMs);\n }\n }\n }\n } finally {\n cleanUpSignals();\n }\n }\n\n private async *startReplicationStreamImpl(\n options: StartReplicationStreamOptions & { onConnect?: () => void }\n ): AsyncIterable<ReplicationStreamRecord> {\n // Create an abort controller for inactivity timeout\n const inactivityAbortController = new AbortController();\n const { signal: combinedSignal, cleanUpSignals } = options.signal\n ? combineAbortSignals([options.signal, inactivityAbortController.signal])\n : { signal: inactivityAbortController.signal, cleanUpSignals: () => {} };\n\n let inactivityTimer: ReturnType<typeof setTimeout> | null = null;\n\n const resetInactivityTimer = () => {\n if (inactivityTimer) clearTimeout(inactivityTimer);\n inactivityTimer = setTimeout(() => {\n inactivityAbortController.abort();\n }, options.inactivityTimeoutMs);\n };\n\n try {\n const rawEvents = fetchSse({\n fetchFn: options.fetchFn,\n headers: {\n Authorization: this.getAuthHeader(options),\n \"Content-Type\": \"application/json\",\n \"User-Agent\": options.agent,\n },\n body: JSON.stringify(options.getBody()),\n timeoutMs: options.requestTimeoutMs,\n method: \"POST\",\n signal: combinedSignal,\n url: this.getApiEndpoint(`/sdk/v1/replication/stream`, options),\n onConnect: () => {\n resetInactivityTimer();\n options.onConnect?.();\n },\n });\n\n for await (const sseEvent of rawEvents) {\n resetInactivityTimer();\n\n if (sseEvent.type === \"comment\") continue;\n\n const event = JSON.parse(sseEvent.data);\n if (\n typeof event === \"object\" &&\n event !== null &&\n \"type\" in event &&\n typeof event.type === \"string\" &&\n (SUPPORTED_REPLICATION_STREAM_RECORD_TYPES as unknown as string[]).includes(event.type)\n ) {\n yield event as ReplicationStreamRecord;\n }\n }\n } finally {\n if (inactivityTimer) clearTimeout(inactivityTimer);\n cleanUpSignals();\n }\n }\n\n /**\n * Disconnect the storage and abort any active connections\n */\n disconnect(): void {\n this.disconnectController.abort();\n // Reset the controller for potential reconnection\n this.disconnectController = new AbortController();\n }\n\n private getAuthHeader(options: ConnectFinalOptions): string {\n return `Bearer ${options.sdkKey}`;\n }\n\n private getApiEndpoint(path: string, options: ConnectFinalOptions): string {\n return `${options.baseUrl}/api${path}`;\n }\n}\n","/**\n * FNV-1a 32-bit hash function\n *\n * FNV (Fowler–Noll–Vo) is a non-cryptographic hash function known for its\n * speed and good distribution. This implementation uses the FNV-1a variant\n * which XORs before multiplying for better avalanche characteristics.\n *\n * @param input - The string to hash\n * @returns A 32-bit unsigned integer hash value\n */\nexport function fnv1a32(input: string): number {\n // Convert string to bytes (UTF-8)\n const encoder = new TextEncoder();\n const bytes = encoder.encode(input);\n\n // FNV-1a core\n let hash = 0x811c9dc5 >>> 0; // 2166136261, force uint32\n\n for (let i = 0; i < bytes.length; i++) {\n hash ^= bytes[i]; // XOR with byte\n hash = Math.imul(hash, 0x01000193) >>> 0; // * 16777619 mod 2^32\n }\n\n return hash >>> 0; // ensure unsigned 32-bit\n}\n\n/**\n * Convert FNV-1a hash to [0, 1) for bucketing.\n *\n * This is useful for percentage-based segmentation where you need\n * to deterministically assign a value to a bucket based on a string input.\n *\n * @param input - The string to hash\n * @returns A number in the range [0, 1)\n */\nexport function fnv1a32ToUnit(input: string): number {\n const h = fnv1a32(input);\n return h / 2 ** 32; // double in [0, 1)\n}\n","import type { RenderedCondition, RenderedOverride } from \"./types\";\nimport type { ReplaneContext, ReplaneLogger } from \"./client-types\";\nimport { fnv1a32ToUnit } from \"./hash\";\n\n/**\n * Result of evaluating a condition\n */\nexport type EvaluationResult = \"matched\" | \"not_matched\" | \"unknown\";\n\n/**\n * Evaluate config overrides based on context.\n * Returns the first matching override's value, or the base value if no override matches.\n *\n * @param baseValue - The default value to return if no override matches\n * @param overrides - Array of overrides to evaluate\n * @param context - The context to evaluate conditions against\n * @param logger - Logger for warnings\n * @returns The evaluated value\n */\nexport function evaluateOverrides<T>(\n baseValue: T,\n overrides: RenderedOverride[],\n context: ReplaneContext,\n logger: ReplaneLogger\n): T {\n // Find first matching override\n for (const override of overrides) {\n // All conditions must match (implicit AND)\n let overrideResult: EvaluationResult = \"matched\";\n const results = override.conditions.map((c) => evaluateCondition(c, context, logger));\n // AND: false > unknown > true\n if (results.some((r) => r === \"not_matched\")) {\n overrideResult = \"not_matched\";\n } else if (results.some((r) => r === \"unknown\")) {\n overrideResult = \"unknown\";\n }\n\n // Only use override if all conditions matched (not unknown)\n if (overrideResult === \"matched\") {\n return override.value as T;\n }\n }\n\n return baseValue;\n}\n\n/**\n * Evaluate a single condition against a context.\n *\n * @param condition - The condition to evaluate\n * @param context - The context to evaluate against\n * @param logger - Logger for warnings\n * @returns The evaluation result\n */\nexport function evaluateCondition(\n condition: RenderedCondition,\n context: ReplaneContext,\n logger: ReplaneLogger\n): EvaluationResult {\n const operator = condition.operator;\n\n // Composite conditions\n if (operator === \"and\") {\n const results = condition.conditions.map((c) => evaluateCondition(c, context, logger));\n // AND: false > unknown > true\n if (results.some((r) => r === \"not_matched\")) return \"not_matched\";\n if (results.some((r) => r === \"unknown\")) return \"unknown\";\n return \"matched\";\n }\n\n if (operator === \"or\") {\n const results = condition.conditions.map((c) => evaluateCondition(c, context, logger));\n // OR: true > unknown > false\n if (results.some((r) => r === \"matched\")) return \"matched\";\n if (results.some((r) => r === \"unknown\")) return \"unknown\";\n return \"not_matched\";\n }\n\n if (operator === \"not\") {\n const result = evaluateCondition(condition.condition, context, logger);\n if (result === \"matched\") return \"not_matched\";\n if (result === \"not_matched\") return \"matched\";\n return \"unknown\"; // NOT unknown = unknown\n }\n\n // Segmentation\n if (operator === \"segmentation\") {\n const contextValue = context[condition.property];\n if (contextValue === undefined || contextValue === null) {\n return \"unknown\";\n }\n\n // FNV-1a hash to bucket [0, 100)\n const hashInput = String(contextValue) + condition.seed;\n const unitValue = fnv1a32ToUnit(hashInput);\n return unitValue >= condition.fromPercentage / 100 && unitValue < condition.toPercentage / 100\n ? \"matched\"\n : \"not_matched\";\n }\n\n // Property-based conditions\n const property = condition.property;\n const contextValue = context[property];\n const expectedValue = condition.value;\n\n if (contextValue === undefined) {\n return \"unknown\";\n }\n\n // Type casting\n const castedValue = castToContextType(expectedValue, contextValue);\n\n switch (operator) {\n case \"equals\":\n return contextValue === castedValue ? \"matched\" : \"not_matched\";\n\n case \"in\":\n if (!Array.isArray(castedValue)) return \"unknown\";\n return castedValue.includes(contextValue) ? \"matched\" : \"not_matched\";\n\n case \"not_in\":\n if (!Array.isArray(castedValue)) return \"unknown\";\n return !castedValue.includes(contextValue) ? \"matched\" : \"not_matched\";\n\n case \"less_than\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue < castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue < castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n case \"less_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue <= castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue <= castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n case \"greater_than\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue > castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue > castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n case \"greater_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue >= castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue >= castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n default:\n warnNever(operator, logger, `Unexpected operator: ${operator}`);\n return \"unknown\";\n }\n}\n\n/**\n * Helper to warn about exhaustive check failures\n */\nfunction warnNever(value: never, logger: ReplaneLogger, message: string): void {\n logger.warn(message, { value });\n}\n\n/**\n * Cast expected value to match context value type.\n * This enables loose matching between different types (e.g., \"25\" matches 25).\n *\n * @param expectedValue - The value from the condition\n * @param contextValue - The value from the context\n * @returns The expected value cast to match the context value's type\n */\nexport function castToContextType(expectedValue: unknown, contextValue: unknown): unknown {\n if (typeof contextValue === \"number\") {\n if (typeof expectedValue === \"string\") {\n const num = Number(expectedValue);\n return isNaN(num) ? expectedValue : num;\n }\n return expectedValue;\n }\n\n if (typeof contextValue === \"boolean\") {\n if (typeof expectedValue === \"string\") {\n if (expectedValue === \"true\") return true;\n if (expectedValue === \"false\") return false;\n }\n if (typeof expectedValue === \"number\") {\n return expectedValue !== 0;\n }\n return expectedValue;\n }\n\n if (typeof contextValue === \"string\") {\n if (typeof expectedValue === \"number\" || typeof expectedValue === \"boolean\") {\n return String(expectedValue);\n }\n return expectedValue;\n }\n\n return expectedValue;\n}\n","// Auto-generated - do not edit manually\nexport const VERSION = \"0.9.2\";\nexport const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;\n","import type { ConfigDto } from \"./types\";\nimport type {\n ReplaneContext,\n ReplaneLogger,\n GetConfigOptions,\n MapConfig,\n ReplaneSnapshot,\n ReplaneOptions,\n ConnectOptions,\n ConnectFinalOptions,\n} from \"./client-types\";\nimport { ReplaneRemoteStorage } from \"./storage\";\nimport { ReplaneError, ReplaneErrorCode } from \"./error\";\nimport { evaluateOverrides } from \"./evaluation\";\nimport { Deferred } from \"./utils\";\nimport { DEFAULT_AGENT } from \"./version\";\n\n/**\n * The Replane client for managing dynamic configuration.\n *\n * @example\n * ```typescript\n * // Create client with defaults\n * const client = new Replane({\n * defaults: { myConfig: 'defaultValue' }\n * });\n *\n * // Use immediately (returns defaults)\n * const value = client.get('myConfig');\n *\n * // Connect for real-time updates\n * await client.connect({\n * baseUrl: 'https://app.replane.dev',\n * sdkKey: 'your-sdk-key'\n * });\n * ```\n *\n * @example\n * ```typescript\n * // SSR/Hydration: Create from snapshot\n * const client = new Replane({\n * snapshot: serverSnapshot\n * });\n * await client.connect({ baseUrl, sdkKey });\n * ```\n *\n * @example\n * ```typescript\n * // In-memory mode (no connection)\n * const client = new Replane({\n * defaults: { feature: true, limit: 100 }\n * });\n * // Don't call connect() - works entirely in-memory\n * ```\n */\nexport class Replane<T extends object = Record<string, unknown>> {\n private configs: Map<string, ConfigDto>;\n private context: ReplaneContext;\n private logger: ReplaneLogger;\n private storage: ReplaneRemoteStorage | null = null;\n private configSubscriptions = new Map<keyof T, Set<(config: MapConfig<T>) => void>>();\n private clientSubscriptions = new Set<(config: MapConfig<T>) => void>();\n\n /**\n * Create a new Replane client.\n *\n * The client is usable immediately after construction with defaults or snapshot data.\n * Call `connect()` to establish a real-time connection for live updates.\n *\n * @param options - Configuration options\n */\n constructor(options: ReplaneOptions<T> = {}) {\n this.logger = options.logger ?? console;\n this.context = { ...(options.context ?? {}) };\n\n // Initialize configs from snapshot or defaults\n const initialConfigs: ConfigDto[] = [];\n\n // Add snapshot configs first (they take precedence)\n if (options.snapshot) {\n for (const config of options.snapshot.configs) {\n initialConfigs.push({\n name: config.name,\n value: config.value,\n overrides: config.overrides,\n });\n }\n }\n\n // Add defaults (only if not already in snapshot)\n if (options.defaults) {\n const snapshotNames = new Set(initialConfigs.map((c) => c.name));\n for (const [name, value] of Object.entries(options.defaults)) {\n if (value !== undefined && !snapshotNames.has(name)) {\n initialConfigs.push({\n name,\n value,\n overrides: [],\n });\n }\n }\n }\n\n this.configs = new Map(initialConfigs.map((config) => [config.name, config]));\n }\n\n /**\n * Connect to the Replane server for real-time config updates.\n *\n * This method establishes an SSE connection to receive live config updates.\n * If already connected, it will disconnect first and reconnect with new options.\n *\n * @param options - Connection options including baseUrl and sdkKey\n * @returns Promise that resolves when the initial connection is established\n * @throws {ReplaneError} If connection times out and no defaults are available\n *\n * @example\n * ```typescript\n * await client.connect({\n * baseUrl: 'https://app.replane.dev',\n * sdkKey: 'rp_xxx'\n * });\n * ```\n */\n async connect(options: ConnectOptions): Promise<void> {\n // Disconnect if already connected\n this.disconnect();\n\n const finalOptions = this.toFinalOptions(options);\n this.storage = new ReplaneRemoteStorage();\n\n const clientReady = new Deferred<void>();\n\n // Start streaming in background\n this.startStreaming(finalOptions, clientReady);\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n this.disconnect();\n clientReady.reject(\n new ReplaneError({\n message: \"Replane client connection timed out\",\n code: ReplaneErrorCode.Timeout,\n })\n );\n }, finalOptions.connectTimeoutMs);\n\n clientReady.promise.finally(() => clearTimeout(timeoutId));\n\n await clientReady.promise;\n }\n\n /**\n * Disconnect from the Replane server.\n *\n * Stops the SSE connection and cleans up resources.\n * The client remains usable with cached config values.\n * Can call `connect()` again to reconnect.\n */\n disconnect(): void {\n if (this.storage) {\n this.storage.disconnect();\n this.storage = null;\n }\n }\n\n /**\n * Get a config value by name.\n *\n * Evaluates any overrides based on the client context and per-call context.\n *\n * @param configName - The name of the config to retrieve\n * @param options - Optional settings for this call\n * @returns The config value\n * @throws {ReplaneError} If config not found and no default provided\n *\n * @example\n * ```typescript\n * // Simple get\n * const value = client.get('myConfig');\n *\n * // With default fallback\n * const value = client.get('myConfig', { default: 'fallback' });\n *\n * // With per-call context for override evaluation\n * const value = client.get('myConfig', { context: { userId: '123' } });\n * ```\n */\n get<K extends keyof T>(configName: K, options: GetConfigOptions<T[K]> = {}): T[K] {\n const config = this.configs.get(String(configName));\n\n if (config === undefined) {\n if (\"default\" in options) {\n return options.default as T[K];\n }\n throw new ReplaneError({\n message: `Config not found: ${String(configName)}`,\n code: ReplaneErrorCode.NotFound,\n });\n }\n\n try {\n return evaluateOverrides<T[K]>(\n config.value as T[K],\n config.overrides,\n { ...this.context, ...(options?.context ?? {}) },\n this.logger\n );\n } catch (error) {\n this.logger.error(\n `Replane: error evaluating overrides for config ${String(configName)}:`,\n error\n );\n return config.value as T[K];\n }\n }\n\n /**\n * Subscribe to config changes.\n *\n * @param callback - Function called when any config changes\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = client.subscribe((change) => {\n * console.log(`Config ${change.name} changed to ${change.value}`);\n * });\n *\n * // Later: stop listening\n * unsubscribe();\n * ```\n */\n subscribe(callback: (config: MapConfig<T>) => void): () => void;\n\n /**\n * Subscribe to a specific config's changes.\n *\n * @param configName - The config to watch\n * @param callback - Function called when the config changes\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = client.subscribe('myConfig', (change) => {\n * console.log(`myConfig changed to ${change.value}`);\n * });\n * ```\n */\n subscribe<K extends keyof T>(\n configName: K,\n callback: (config: { name: K; value: T[K] }) => void\n ): () => void;\n\n subscribe<K extends keyof T>(\n callbackOrConfigName: K | ((config: MapConfig<T>) => void),\n callbackOrUndefined?: (config: { name: K; value: T[K] }) => void\n ): () => void {\n let configName: keyof T | undefined = undefined;\n let callback: (config: MapConfig<T>) => void;\n\n if (typeof callbackOrConfigName === \"function\") {\n callback = callbackOrConfigName;\n } else {\n configName = callbackOrConfigName as keyof T;\n if (callbackOrUndefined === undefined) {\n throw new Error(\"callback is required when config name is provided\");\n }\n // Type assertion is safe: MapConfig<T> is a union that includes { name: K, value: T[K] }\n callback = callbackOrUndefined as (config: MapConfig<T>) => void;\n }\n\n // Wrap the callback to ensure that we have a unique reference\n const originalCallback = callback;\n callback = (...args: Parameters<typeof callback>) => {\n originalCallback(...args);\n };\n\n if (configName === undefined) {\n this.clientSubscriptions.add(callback);\n return () => {\n this.clientSubscriptions.delete(callback);\n };\n }\n\n if (!this.configSubscriptions.has(configName)) {\n this.configSubscriptions.set(configName, new Set());\n }\n this.configSubscriptions.get(configName)!.add(callback);\n\n return () => {\n this.configSubscriptions.get(configName)?.delete(callback);\n if (this.configSubscriptions.get(configName)?.size === 0) {\n this.configSubscriptions.delete(configName);\n }\n };\n }\n\n /**\n * Get a serializable snapshot of the current client state.\n *\n * Useful for SSR/hydration scenarios where you want to pass\n * configs from server to client.\n *\n * @returns Snapshot object that can be serialized to JSON\n *\n * @example\n * ```typescript\n * // On server\n * const snapshot = client.getSnapshot();\n * const json = JSON.stringify(snapshot);\n *\n * // On client\n * const client = new Replane({ snapshot: JSON.parse(json) });\n * ```\n */\n getSnapshot(): ReplaneSnapshot<T> {\n return {\n configs: [...this.configs.values()].map((config) => ({\n name: config.name,\n value: config.value,\n overrides: config.overrides.map((override) => ({\n name: override.name,\n conditions: override.conditions,\n value: override.value,\n })),\n })),\n };\n }\n\n /**\n * Check if the client is currently connected.\n */\n get isConnected(): boolean {\n return this.storage !== null;\n }\n\n private async startStreaming(\n options: ConnectFinalOptions,\n clientReady: Deferred<void>\n ): Promise<void> {\n if (!this.storage) return;\n\n try {\n const replicationStream = this.storage.startReplicationStream({\n ...options,\n logger: this.logger,\n getBody: () => ({\n currentConfigs: [...this.configs.values()].map((config) => ({\n name: config.name,\n overrides: config.overrides,\n value: config.value,\n })),\n requiredConfigs: [],\n }),\n });\n\n for await (const event of replicationStream) {\n const updatedConfigs: ConfigDto[] =\n event.type === \"config_change\" ? [event.config] : event.configs;\n this.processConfigUpdates(updatedConfigs);\n clientReady.resolve();\n }\n } catch (error) {\n this.logger.error(\"Replane: error in SSE connection:\", error);\n clientReady.reject(error);\n throw error;\n }\n }\n\n private processConfigUpdates(updatedConfigs: ConfigDto[]): void {\n for (const config of updatedConfigs) {\n this.configs.set(config.name, {\n name: config.name,\n overrides: config.overrides,\n value: config.value,\n });\n\n const change = { name: config.name as keyof T, value: config.value as T[keyof T] };\n\n for (const callback of this.clientSubscriptions) {\n callback(change);\n }\n for (const callback of this.configSubscriptions.get(config.name as keyof T) ?? []) {\n callback(change);\n }\n }\n }\n\n private toFinalOptions(options: ConnectOptions): ConnectFinalOptions {\n return {\n sdkKey: options.sdkKey,\n baseUrl: (options.baseUrl ?? \"\").replace(/\\/+$/, \"\"),\n fetchFn:\n options.fetchFn ??\n // some browsers require binding the fetch function to window\n globalThis.fetch.bind(globalThis),\n requestTimeoutMs: options.requestTimeoutMs ?? 2000,\n connectTimeoutMs: options.connectTimeoutMs ?? 5000,\n inactivityTimeoutMs: options.inactivityTimeoutMs ?? 30_000,\n retryDelayMs: options.retryDelayMs ?? 200,\n agent: options.agent ?? DEFAULT_AGENT,\n };\n }\n}\n","import { Replane } from \"./client\";\nimport type { ConnectOptions, ReplaneSnapshot, ReplaneOptions } from \"./client-types\";\n\n/**\n * Options for getReplaneSnapshot with caching support.\n */\nexport interface GetReplaneSnapshotOptions<T extends object> extends ReplaneOptions<T> {\n /**\n * Cache TTL in milliseconds. When set, the client is cached and reused\n * for instant subsequent calls within this duration.\n * @default 60_000 (1 minute)\n */\n keepAliveMs?: number;\n /**\n * Connection options. If null, the snapshot will be created from defaults only.\n */\n connection: ConnectOptions | null;\n}\n\ninterface CachedClient {\n client: Replane<object>;\n timeoutId: TimeoutId;\n}\n\ninterface PendingConnection {\n promise: Promise<Replane<object>>;\n}\n\nconst clientCache = new Map<string, CachedClient>();\nconst pendingConnections = new Map<string, PendingConnection>();\n\nfunction getCacheKey(options: ConnectOptions): string {\n return `${options.baseUrl}:${options.sdkKey}`;\n}\n\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nfunction setupCleanupTimeout(cacheKey: string, keepAliveMs: number): TimeoutId {\n return setTimeout(() => {\n const cached = clientCache.get(cacheKey);\n if (cached) {\n cached.client.disconnect();\n clientCache.delete(cacheKey);\n }\n }, keepAliveMs);\n}\n\n/**\n * Creates a Replane client and returns a snapshot.\n * Useful for SSR/SSG scenarios where you need to fetch config once\n * and pass it to the client.\n *\n * @example\n * ```ts\n * const snapshot = await getReplaneSnapshot({\n * connection: {\n * baseUrl: process.env.REPLANE_BASE_URL!,\n * sdkKey: process.env.REPLANE_SDK_KEY!,\n * },\n * });\n * ```\n */\nexport async function getReplaneSnapshot<T extends object>(\n options: GetReplaneSnapshotOptions<T>\n): Promise<ReplaneSnapshot<T>> {\n const {\n keepAliveMs = 60_000,\n logger,\n context,\n defaults,\n connection: connectionOptions,\n } = options;\n\n if (!connectionOptions) {\n return {\n configs: [\n ...Object.entries(defaults ?? {}).map(([name, value]) => ({\n name,\n value,\n overrides: [],\n })),\n ],\n };\n }\n\n const cacheKey = getCacheKey(connectionOptions);\n const cached = clientCache.get(cacheKey);\n\n // Return from cache if valid\n if (cached) {\n clearTimeout(cached.timeoutId);\n cached.timeoutId = setupCleanupTimeout(cacheKey, keepAliveMs);\n return cached.client.getSnapshot() as ReplaneSnapshot<T>;\n }\n\n // Check for pending connection (for concurrent requests)\n const pending = pendingConnections.get(cacheKey);\n if (pending) {\n const client = await pending.promise;\n return client.getSnapshot() as ReplaneSnapshot<T>;\n }\n\n // Create new client and connect\n const client = new Replane<T>({\n logger,\n context,\n defaults,\n });\n\n // Store pending connection promise\n const connectionPromise = client\n .connect(connectionOptions)\n .then(() => client as unknown as Replane<object>);\n pendingConnections.set(cacheKey, { promise: connectionPromise });\n\n try {\n await connectionPromise;\n\n // Cache the connected client\n const entry: CachedClient = {\n client: client as unknown as Replane<object>,\n timeoutId: setupCleanupTimeout(cacheKey, keepAliveMs),\n };\n clientCache.set(cacheKey, entry);\n\n return client.getSnapshot();\n } finally {\n pendingConnections.delete(cacheKey);\n }\n}\n\n/**\n * Clears the client cache used by getReplaneSnapshot.\n * Useful for testing or when you need to force re-initialization.\n */\nexport function clearSnapshotCache(): void {\n for (const cached of clientCache.values()) {\n clearTimeout(cached.timeoutId);\n cached.client.disconnect();\n }\n clientCache.clear();\n pendingConnections.clear();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["params: { message: string; code: string; cause?: unknown }","ms: number","averageDelay: number","signals: Array<AbortSignal | undefined | null>","input: string | URL | Request","init: RequestInit","timeoutMs: number","fetchFn: typeof fetch","response: Response","message: string","body: unknown","params: FetchSseOptions","dataLines: string[]","comment: string | null","options: StartReplicationStreamOptions","error: unknown","options: StartReplicationStreamOptions & { onConnect?: () => void }","inactivityTimer: ReturnType<typeof setTimeout> | null","options: ConnectFinalOptions","path: string","input: string","baseValue: T","overrides: RenderedOverride[]","context: ReplaneContext","logger: ReplaneLogger","overrideResult: EvaluationResult","condition: RenderedCondition","contextValue","value: never","message: string","expectedValue: unknown","contextValue: unknown","replane: Replane<T>","options: ReplaneOptions<T>","options: ConnectOptions","configName: K","options?: GetConfigOptions<T[K]>","callback: (config: { name: K; value: T[K] }) => void","initialConfigs: ConfigDto[]","options: GetConfigOptions<T[K]>","config: MapConfig<T>","options: ConnectFinalOptions","clientReady: Deferred<void>","updatedConfigs: ConfigDto[]","options: ConnectOptions","cacheKey: string","keepAliveMs: number","options: GetReplaneSnapshotOptions<T>","client","entry: CachedClient"],"sources":["../src/error.ts","../src/utils.ts","../src/sse.ts","../src/storage.ts","../src/hash.ts","../src/evaluation.ts","../src/version.ts","../src/client.ts","../src/snapshot.ts"],"sourcesContent":["/**\n * Error codes for ReplaneError\n */\nexport enum ReplaneErrorCode {\n NotFound = \"not_found\",\n Timeout = \"timeout\",\n NetworkError = \"network_error\",\n AuthError = \"auth_error\",\n Forbidden = \"forbidden\",\n ServerError = \"server_error\",\n ClientError = \"client_error\",\n Closed = \"closed\",\n NotInitialized = \"not_initialized\",\n Unknown = \"unknown\",\n}\n\n/**\n * Custom error class for Replane SDK errors\n */\nexport class ReplaneError extends Error {\n code: string;\n\n constructor(params: { message: string; code: string; cause?: unknown }) {\n super(params.message, { cause: params.cause });\n this.name = \"ReplaneError\";\n this.code = params.code;\n }\n}\n","/**\n * Returns a promise that resolves after the specified delay\n *\n * @param ms - Delay in milliseconds\n */\nexport async function delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Returns a promise that resolves after a delay with jitter.\n * The actual delay is the average delay ± 10% (jitter = averageDelay/5).\n *\n * @param averageDelay - The average delay in milliseconds\n */\nexport async function retryDelay(averageDelay: number): Promise<void> {\n const jitter = averageDelay / 5;\n const delayMs = averageDelay + Math.random() * jitter - jitter / 2;\n\n await delay(delayMs);\n}\n\n/**\n * Combines multiple abort signals into one.\n * When any of the input signals is aborted, the combined signal will also be aborted.\n *\n * @param signals - Array of AbortSignal instances (can contain undefined/null)\n * @returns An object containing the combined signal and a cleanup function\n */\nexport function combineAbortSignals(signals: Array<AbortSignal | undefined | null>): {\n signal: AbortSignal;\n cleanUpSignals: () => void;\n} {\n const controller = new AbortController();\n const onAbort = () => {\n controller.abort();\n cleanUpSignals();\n };\n\n const cleanUpSignals = () => {\n for (const s of signals) {\n s?.removeEventListener(\"abort\", onAbort);\n }\n };\n\n for (const s of signals) {\n s?.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n if (signals.some((s) => s?.aborted)) {\n onAbort();\n }\n\n return { signal: controller.signal, cleanUpSignals };\n}\n\n/**\n * A deferred promise that can be resolved or rejected from outside.\n * Useful for coordinating async operations.\n */\nexport class Deferred<T> {\n public readonly promise: Promise<T>;\n public resolve!: (value: T) => void;\n public reject!: (error: unknown) => void;\n\n constructor() {\n this.promise = new Promise((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\n","import { ReplaneError, ReplaneErrorCode } from \"./error\";\nimport { combineAbortSignals } from \"./utils\";\n\nconst SSE_DATA_PREFIX = \"data:\";\n\n/**\n * Parsed SSE event\n */\nexport type SseEvent = { type: \"comment\"; comment: string } | { type: \"data\"; data: string };\n\n/**\n * Options for fetchSse\n */\nexport interface FetchSseOptions {\n fetchFn: typeof fetch;\n url: string;\n timeoutMs: number;\n body?: string;\n headers?: Record<string, string>;\n method?: string;\n signal?: AbortSignal;\n onConnect?: () => void;\n}\n\n/**\n * Fetch with timeout support\n */\nexport async function fetchWithTimeout(\n input: string | URL | Request,\n init: RequestInit,\n timeoutMs: number,\n fetchFn: typeof fetch\n): Promise<Response> {\n if (!fetchFn) {\n throw new Error(\"Global fetch is not available. Provide options.fetchFn.\");\n }\n if (!timeoutMs) return fetchFn(input, init);\n\n const timeoutController = new AbortController();\n const t = setTimeout(() => timeoutController.abort(), timeoutMs);\n // Note: We intentionally don't call cleanUpSignals() here because for streaming\n // responses (like SSE), the connection remains open after the response headers\n // are received. The abort signal needs to remain connected so that close() can\n // propagate the abort through the signal chain.\n const { signal } = combineAbortSignals([init.signal, timeoutController.signal]);\n try {\n return await fetchFn(input, {\n ...init,\n signal,\n });\n } finally {\n clearTimeout(t);\n }\n}\n\n/**\n * Ensures the response is successful, throwing ReplaneError if not\n */\nexport async function ensureSuccessfulResponse(response: Response, message: string): Promise<void> {\n if (response.status === 404) {\n throw new ReplaneError({\n message: `Not found: ${message}`,\n code: ReplaneErrorCode.NotFound,\n });\n }\n\n if (response.status === 401) {\n throw new ReplaneError({\n message: `Unauthorized access: ${message}`,\n code: ReplaneErrorCode.AuthError,\n });\n }\n\n if (response.status === 403) {\n throw new ReplaneError({\n message: `Forbidden access: ${message}`,\n code: ReplaneErrorCode.Forbidden,\n });\n }\n\n if (!response.ok) {\n let body: unknown;\n try {\n body = await response.text();\n } catch {\n body = \"<unable to read response body>\";\n }\n\n const code =\n response.status >= 500\n ? ReplaneErrorCode.ServerError\n : response.status >= 400\n ? ReplaneErrorCode.ClientError\n : ReplaneErrorCode.Unknown;\n\n throw new ReplaneError({\n message: `Fetch response isn't successful (${message}): ${response.status} ${response.statusText} - ${body}`,\n code,\n });\n }\n}\n\n/**\n * Fetches a Server-Sent Events (SSE) stream and yields parsed events.\n *\n * @param params - Options for the SSE fetch\n * @yields SseEvent objects containing either data or comment events\n */\nexport async function* fetchSse(params: FetchSseOptions): AsyncGenerator<SseEvent> {\n const abortController = new AbortController();\n const { signal, cleanUpSignals } = params.signal\n ? combineAbortSignals([params.signal, abortController.signal])\n : { signal: abortController.signal, cleanUpSignals: () => {} };\n\n try {\n const res = await fetchWithTimeout(\n params.url,\n {\n method: params.method ?? \"GET\",\n headers: { Accept: \"text/event-stream\", ...(params.headers ?? {}) },\n body: params.body,\n signal,\n },\n params.timeoutMs,\n params.fetchFn\n );\n\n await ensureSuccessfulResponse(res, `SSE ${params.url}`);\n const responseContentType = res.headers.get(\"content-type\") ?? \"\";\n\n if (!responseContentType.includes(\"text/event-stream\")) {\n throw new ReplaneError({\n message: `Expected text/event-stream, got \"${responseContentType}\"`,\n code: ReplaneErrorCode.ServerError,\n });\n }\n\n if (!res.body) {\n throw new ReplaneError({\n message: `Failed to fetch SSE ${params.url}: body is empty`,\n code: ReplaneErrorCode.Unknown,\n });\n }\n\n if (params.onConnect) {\n params.onConnect();\n }\n\n const decoded = res.body.pipeThrough(new TextDecoderStream());\n const reader = decoded.getReader();\n\n let buffer = \"\";\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += value!;\n\n // Split on blank line; handle both \\n\\n and \\r\\n\\r\\n\n const frames = buffer.split(/\\r?\\n\\r?\\n/);\n buffer = frames.pop() ?? \"\";\n\n for (const frame of frames) {\n // Parse lines inside a single SSE event frame\n const dataLines: string[] = [];\n let comment: string | null = null;\n\n for (const rawLine of frame.split(/\\r?\\n/)) {\n if (!rawLine) continue;\n if (rawLine.startsWith(\":\")) {\n // comment/keepalive\n comment = rawLine.slice(1);\n continue;\n }\n\n if (rawLine.startsWith(SSE_DATA_PREFIX)) {\n // Keep leading space after \"data:\" if present per spec\n const line = rawLine.slice(SSE_DATA_PREFIX.length).replace(/^\\s/, \"\");\n dataLines.push(line);\n }\n }\n\n if (dataLines.length) {\n const data = dataLines.join(\"\\n\");\n yield { type: \"data\", data };\n } else if (comment !== null) {\n yield { type: \"comment\", comment };\n }\n }\n }\n } finally {\n try {\n await reader.cancel();\n } catch {\n // ignore error\n }\n abortController.abort();\n }\n } finally {\n cleanUpSignals();\n }\n}\n","import type { ReplicationStreamRecord, StartReplicationStreamBody } from \"./types\";\nimport type { ConnectFinalOptions, ReplaneLogger } from \"./client-types\";\nimport { fetchSse } from \"./sse\";\nimport { combineAbortSignals, retryDelay } from \"./utils\";\n\nconst SUPPORTED_REPLICATION_STREAM_RECORD_TYPES = Object.keys({\n config_change: true,\n init: true,\n} satisfies Record<ReplicationStreamRecord[\"type\"], true>);\n\n/**\n * Options for starting a replication stream\n */\nexport interface StartReplicationStreamOptions extends ConnectFinalOptions {\n // getBody is a function to get the latest configs when we are trying\n // to reestablish the replication stream\n getBody: () => StartReplicationStreamBody;\n signal?: AbortSignal;\n logger: ReplaneLogger;\n}\n\n/**\n * Interface for storage implementations\n */\nexport interface ReplaneStorage {\n startReplicationStream(\n options: StartReplicationStreamOptions\n ): AsyncIterable<ReplicationStreamRecord>;\n disconnect(): void;\n}\n\n/**\n * Remote storage implementation that connects to the Replane server\n * and streams config updates via SSE.\n */\nexport class ReplaneRemoteStorage implements ReplaneStorage {\n private disconnectController = new AbortController();\n\n /**\n * Start a replication stream that yields config updates.\n * This method never throws - it retries on failure with exponential backoff.\n */\n async *startReplicationStream(\n options: StartReplicationStreamOptions\n ): AsyncIterable<ReplicationStreamRecord> {\n const { signal, cleanUpSignals } = combineAbortSignals([\n this.disconnectController.signal,\n options.signal,\n ]);\n try {\n let failedAttempts = 0;\n while (!signal.aborted) {\n try {\n for await (const event of this.startReplicationStreamImpl({\n ...options,\n signal,\n onConnect: () => {\n failedAttempts = 0;\n },\n })) {\n yield event;\n }\n } catch (error: unknown) {\n failedAttempts++;\n const retryDelayMs = Math.min(options.retryDelayMs * 2 ** (failedAttempts - 1), 10_000);\n if (!signal.aborted) {\n options.logger.error(\n `Failed to fetch project events, retrying in ${retryDelayMs}ms...`,\n error\n );\n await retryDelay(retryDelayMs);\n }\n }\n }\n } finally {\n cleanUpSignals();\n }\n }\n\n private async *startReplicationStreamImpl(\n options: StartReplicationStreamOptions & { onConnect?: () => void }\n ): AsyncIterable<ReplicationStreamRecord> {\n // Create an abort controller for inactivity timeout\n const inactivityAbortController = new AbortController();\n const { signal: combinedSignal, cleanUpSignals } = options.signal\n ? combineAbortSignals([options.signal, inactivityAbortController.signal])\n : { signal: inactivityAbortController.signal, cleanUpSignals: () => {} };\n\n let inactivityTimer: ReturnType<typeof setTimeout> | null = null;\n\n const resetInactivityTimer = () => {\n if (inactivityTimer) clearTimeout(inactivityTimer);\n inactivityTimer = setTimeout(() => {\n inactivityAbortController.abort();\n }, options.inactivityTimeoutMs);\n };\n\n try {\n const rawEvents = fetchSse({\n fetchFn: options.fetchFn,\n headers: {\n Authorization: this.getAuthHeader(options),\n \"Content-Type\": \"application/json\",\n \"User-Agent\": options.agent,\n },\n body: JSON.stringify(options.getBody()),\n timeoutMs: options.requestTimeoutMs,\n method: \"POST\",\n signal: combinedSignal,\n url: this.getApiEndpoint(`/sdk/v1/replication/stream`, options),\n onConnect: () => {\n resetInactivityTimer();\n options.onConnect?.();\n },\n });\n\n for await (const sseEvent of rawEvents) {\n resetInactivityTimer();\n\n if (sseEvent.type === \"comment\") continue;\n\n const event = JSON.parse(sseEvent.data);\n if (\n typeof event === \"object\" &&\n event !== null &&\n \"type\" in event &&\n typeof event.type === \"string\" &&\n (SUPPORTED_REPLICATION_STREAM_RECORD_TYPES as unknown as string[]).includes(event.type)\n ) {\n yield event as ReplicationStreamRecord;\n }\n }\n } finally {\n if (inactivityTimer) clearTimeout(inactivityTimer);\n cleanUpSignals();\n }\n }\n\n /**\n * Disconnect the storage and abort any active connections\n */\n disconnect(): void {\n this.disconnectController.abort();\n // Reset the controller for potential reconnection\n this.disconnectController = new AbortController();\n }\n\n private getAuthHeader(options: ConnectFinalOptions): string {\n return `Bearer ${options.sdkKey}`;\n }\n\n private getApiEndpoint(path: string, options: ConnectFinalOptions): string {\n return `${options.baseUrl}/api${path}`;\n }\n}\n","/**\n * FNV-1a 32-bit hash function\n *\n * FNV (Fowler–Noll–Vo) is a non-cryptographic hash function known for its\n * speed and good distribution. This implementation uses the FNV-1a variant\n * which XORs before multiplying for better avalanche characteristics.\n *\n * @param input - The string to hash\n * @returns A 32-bit unsigned integer hash value\n */\nexport function fnv1a32(input: string): number {\n // Convert string to bytes (UTF-8)\n const encoder = new TextEncoder();\n const bytes = encoder.encode(input);\n\n // FNV-1a core\n let hash = 0x811c9dc5 >>> 0; // 2166136261, force uint32\n\n for (let i = 0; i < bytes.length; i++) {\n hash ^= bytes[i]; // XOR with byte\n hash = Math.imul(hash, 0x01000193) >>> 0; // * 16777619 mod 2^32\n }\n\n return hash >>> 0; // ensure unsigned 32-bit\n}\n\n/**\n * Convert FNV-1a hash to [0, 1) for bucketing.\n *\n * This is useful for percentage-based segmentation where you need\n * to deterministically assign a value to a bucket based on a string input.\n *\n * @param input - The string to hash\n * @returns A number in the range [0, 1)\n */\nexport function fnv1a32ToUnit(input: string): number {\n const h = fnv1a32(input);\n return h / 2 ** 32; // double in [0, 1)\n}\n","import type { RenderedCondition, RenderedOverride } from \"./types\";\nimport type { ReplaneContext, ReplaneLogger } from \"./client-types\";\nimport { fnv1a32ToUnit } from \"./hash\";\n\n/**\n * Result of evaluating a condition\n */\nexport type EvaluationResult = \"matched\" | \"not_matched\" | \"unknown\";\n\n/**\n * Evaluate config overrides based on context.\n * Returns the first matching override's value, or the base value if no override matches.\n *\n * @param baseValue - The default value to return if no override matches\n * @param overrides - Array of overrides to evaluate\n * @param context - The context to evaluate conditions against\n * @param logger - Logger for warnings\n * @returns The evaluated value\n */\nexport function evaluateOverrides<T>(\n baseValue: T,\n overrides: RenderedOverride[],\n context: ReplaneContext,\n logger: ReplaneLogger\n): T {\n // Find first matching override\n for (const override of overrides) {\n // All conditions must match (implicit AND)\n let overrideResult: EvaluationResult = \"matched\";\n const results = override.conditions.map((c) => evaluateCondition(c, context, logger));\n // AND: false > unknown > true\n if (results.some((r) => r === \"not_matched\")) {\n overrideResult = \"not_matched\";\n } else if (results.some((r) => r === \"unknown\")) {\n overrideResult = \"unknown\";\n }\n\n // Only use override if all conditions matched (not unknown)\n if (overrideResult === \"matched\") {\n return override.value as T;\n }\n }\n\n return baseValue;\n}\n\n/**\n * Evaluate a single condition against a context.\n *\n * @param condition - The condition to evaluate\n * @param context - The context to evaluate against\n * @param logger - Logger for warnings\n * @returns The evaluation result\n */\nexport function evaluateCondition(\n condition: RenderedCondition,\n context: ReplaneContext,\n logger: ReplaneLogger\n): EvaluationResult {\n const operator = condition.operator;\n\n // Composite conditions\n if (operator === \"and\") {\n const results = condition.conditions.map((c) => evaluateCondition(c, context, logger));\n // AND: false > unknown > true\n if (results.some((r) => r === \"not_matched\")) return \"not_matched\";\n if (results.some((r) => r === \"unknown\")) return \"unknown\";\n return \"matched\";\n }\n\n if (operator === \"or\") {\n const results = condition.conditions.map((c) => evaluateCondition(c, context, logger));\n // OR: true > unknown > false\n if (results.some((r) => r === \"matched\")) return \"matched\";\n if (results.some((r) => r === \"unknown\")) return \"unknown\";\n return \"not_matched\";\n }\n\n if (operator === \"not\") {\n const result = evaluateCondition(condition.condition, context, logger);\n if (result === \"matched\") return \"not_matched\";\n if (result === \"not_matched\") return \"matched\";\n return \"unknown\"; // NOT unknown = unknown\n }\n\n // Segmentation\n if (operator === \"segmentation\") {\n const contextValue = context[condition.property];\n if (contextValue === undefined || contextValue === null) {\n return \"unknown\";\n }\n\n // FNV-1a hash to bucket [0, 100)\n const hashInput = String(contextValue) + condition.seed;\n const unitValue = fnv1a32ToUnit(hashInput);\n return unitValue >= condition.fromPercentage / 100 && unitValue < condition.toPercentage / 100\n ? \"matched\"\n : \"not_matched\";\n }\n\n // Property-based conditions\n const property = condition.property;\n const contextValue = context[property];\n const expectedValue = condition.value;\n\n if (contextValue === undefined) {\n return \"unknown\";\n }\n\n // Type casting\n const castedValue = castToContextType(expectedValue, contextValue);\n\n switch (operator) {\n case \"equals\":\n return contextValue === castedValue ? \"matched\" : \"not_matched\";\n\n case \"in\":\n if (!Array.isArray(castedValue)) return \"unknown\";\n return castedValue.includes(contextValue) ? \"matched\" : \"not_matched\";\n\n case \"not_in\":\n if (!Array.isArray(castedValue)) return \"unknown\";\n return !castedValue.includes(contextValue) ? \"matched\" : \"not_matched\";\n\n case \"less_than\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue < castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue < castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n case \"less_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue <= castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue <= castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n case \"greater_than\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue > castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue > castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n case \"greater_than_or_equal\":\n if (typeof contextValue === \"number\" && typeof castedValue === \"number\") {\n return contextValue >= castedValue ? \"matched\" : \"not_matched\";\n }\n if (typeof contextValue === \"string\" && typeof castedValue === \"string\") {\n return contextValue >= castedValue ? \"matched\" : \"not_matched\";\n }\n return \"not_matched\";\n\n default:\n warnNever(operator, logger, `Unexpected operator: ${operator}`);\n return \"unknown\";\n }\n}\n\n/**\n * Helper to warn about exhaustive check failures\n */\nfunction warnNever(value: never, logger: ReplaneLogger, message: string): void {\n logger.warn(message, { value });\n}\n\n/**\n * Cast expected value to match context value type.\n * This enables loose matching between different types (e.g., \"25\" matches 25).\n *\n * @param expectedValue - The value from the condition\n * @param contextValue - The value from the context\n * @returns The expected value cast to match the context value's type\n */\nexport function castToContextType(expectedValue: unknown, contextValue: unknown): unknown {\n if (typeof contextValue === \"number\") {\n if (typeof expectedValue === \"string\") {\n const num = Number(expectedValue);\n return isNaN(num) ? expectedValue : num;\n }\n return expectedValue;\n }\n\n if (typeof contextValue === \"boolean\") {\n if (typeof expectedValue === \"string\") {\n if (expectedValue === \"true\") return true;\n if (expectedValue === \"false\") return false;\n }\n if (typeof expectedValue === \"number\") {\n return expectedValue !== 0;\n }\n return expectedValue;\n }\n\n if (typeof contextValue === \"string\") {\n if (typeof expectedValue === \"number\" || typeof expectedValue === \"boolean\") {\n return String(expectedValue);\n }\n return expectedValue;\n }\n\n return expectedValue;\n}\n","// Auto-generated - do not edit manually\nexport const VERSION = \"0.9.4\";\nexport const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;\n","import type { ConfigDto } from \"./types\";\nimport type {\n ReplaneContext,\n ReplaneLogger,\n GetConfigOptions,\n MapConfig,\n ReplaneSnapshot,\n ReplaneOptions,\n ConnectOptions,\n ConnectFinalOptions,\n} from \"./client-types\";\nimport { ReplaneRemoteStorage } from \"./storage\";\nimport { ReplaneError, ReplaneErrorCode } from \"./error\";\nimport { evaluateOverrides } from \"./evaluation\";\nimport { Deferred } from \"./utils\";\nimport { DEFAULT_AGENT } from \"./version\";\n\ninterface ReplaneHandle<T extends object> {\n _replane: ReplaneImpl<T>;\n}\n\nfunction asReplaneHandle<T extends object>(replane: Replane<T>): ReplaneHandle<T> {\n return replane as unknown as ReplaneHandle<T>;\n}\n\n/**\n * The Replane client for managing dynamic configuration.\n *\n * @example\n * ```typescript\n * // Create client with defaults\n * const client = new Replane({\n * defaults: { myConfig: 'defaultValue' }\n * });\n *\n * // Use immediately (returns defaults)\n * const value = client.get('myConfig');\n *\n * // Connect for real-time updates\n * await client.connect({\n * baseUrl: 'https://app.replane.dev',\n * sdkKey: 'your-sdk-key'\n * });\n * ```\n *\n * @example\n * ```typescript\n * // SSR/Hydration: Create from snapshot\n * const client = new Replane({\n * snapshot: serverSnapshot\n * });\n * await client.connect({ baseUrl, sdkKey });\n * ```\n *\n * @example\n * ```typescript\n * // In-memory mode (no connection)\n * const client = new Replane({\n * defaults: { feature: true, limit: 100 }\n * });\n * // Don't call connect() - works entirely in-memory\n * ```\n */\nexport class Replane<T extends object = Record<string, unknown>> {\n constructor(options: ReplaneOptions<T> = {}) {\n asReplaneHandle(this)._replane = new ReplaneImpl<T>(options);\n }\n connect(options: ConnectOptions): Promise<void> {\n return asReplaneHandle(this)._replane.connect(options);\n }\n disconnect(): void {\n asReplaneHandle(this)._replane.disconnect();\n }\n get<K extends keyof T>(configName: K, options?: GetConfigOptions<T[K]>): T[K] {\n return asReplaneHandle(this)._replane.get(configName, options);\n }\n subscribe<K extends keyof T>(\n configName: K,\n callback: (config: { name: K; value: T[K] }) => void\n ): () => void {\n return asReplaneHandle(this)._replane.subscribe(configName, callback);\n }\n getSnapshot(): ReplaneSnapshot<T> {\n return asReplaneHandle(this)._replane.getSnapshot();\n }\n}\n\n// we declare ReplaneImpl separately to avoid exposing private properties\n// otherwise function with the following signature\n// function f(client: Replane<T>): void;\n// would expect client to have all private properties of Replane\nclass ReplaneImpl<T extends object = Record<string, unknown>> {\n private configs: Map<string, ConfigDto>;\n private context: ReplaneContext;\n private logger: ReplaneLogger;\n private storage: ReplaneRemoteStorage | null = null;\n private configSubscriptions = new Map<keyof T, Set<(config: MapConfig<T>) => void>>();\n\n /**\n * Create a new Replane client.\n *\n * The client is usable immediately after construction with defaults or snapshot data.\n * Call `connect()` to establish a real-time connection for live updates.\n *\n * @param options - Configuration options\n */\n constructor(options: ReplaneOptions<T> = {}) {\n this.logger = options.logger ?? console;\n this.context = { ...(options.context ?? {}) };\n\n // Initialize configs from snapshot or defaults\n const initialConfigs: ConfigDto[] = [];\n\n // Add snapshot configs first (they take precedence)\n if (options.snapshot) {\n for (const config of options.snapshot.configs) {\n initialConfigs.push({\n name: config.name,\n value: config.value,\n overrides: config.overrides,\n });\n }\n }\n\n // Add defaults (only if not already in snapshot)\n if (options.defaults) {\n const snapshotNames = new Set(initialConfigs.map((c) => c.name));\n for (const [name, value] of Object.entries(options.defaults)) {\n if (value !== undefined && !snapshotNames.has(name)) {\n initialConfigs.push({\n name,\n value,\n overrides: [],\n });\n }\n }\n }\n\n this.configs = new Map(initialConfigs.map((config) => [config.name, config]));\n }\n\n /**\n * Connect to the Replane server for real-time config updates.\n *\n * This method establishes an SSE connection to receive live config updates.\n * If already connected, it will disconnect first and reconnect with new options.\n *\n * @param options - Connection options including baseUrl and sdkKey\n * @returns Promise that resolves when the initial connection is established\n * @throws {ReplaneError} If connection times out and no defaults are available\n *\n * @example\n * ```typescript\n * await client.connect({\n * baseUrl: 'https://app.replane.dev',\n * sdkKey: 'rp_xxx'\n * });\n * ```\n */\n async connect(options: ConnectOptions): Promise<void> {\n // Disconnect if already connected\n this.disconnect();\n\n const finalOptions = this.toFinalOptions(options);\n this.storage = new ReplaneRemoteStorage();\n\n const clientReady = new Deferred<void>();\n\n // Start streaming in background\n this.startStreaming(finalOptions, clientReady);\n\n // Set up timeout\n const timeoutId = setTimeout(() => {\n this.disconnect();\n clientReady.reject(\n new ReplaneError({\n message: \"Replane client connection timed out\",\n code: ReplaneErrorCode.Timeout,\n })\n );\n }, finalOptions.connectTimeoutMs);\n\n clientReady.promise.finally(() => clearTimeout(timeoutId));\n\n await clientReady.promise;\n }\n\n /**\n * Disconnect from the Replane server.\n *\n * Stops the SSE connection and cleans up resources.\n * The client remains usable with cached config values.\n * Can call `connect()` again to reconnect.\n */\n disconnect(): void {\n if (this.storage) {\n this.storage.disconnect();\n this.storage = null;\n }\n }\n\n /**\n * Get a config value by name.\n *\n * Evaluates any overrides based on the client context and per-call context.\n *\n * @param configName - The name of the config to retrieve\n * @param options - Optional settings for this call\n * @returns The config value\n * @throws {ReplaneError} If config not found and no default provided\n *\n * @example\n * ```typescript\n * // Simple get\n * const value = client.get('myConfig');\n *\n * // With default fallback\n * const value = client.get('myConfig', { default: 'fallback' });\n *\n * // With per-call context for override evaluation\n * const value = client.get('myConfig', { context: { userId: '123' } });\n * ```\n */\n get<K extends keyof T>(configName: K, options: GetConfigOptions<T[K]> = {}): T[K] {\n const config = this.configs.get(String(configName));\n\n if (config === undefined) {\n if (\"default\" in options) {\n return options.default as T[K];\n }\n throw new ReplaneError({\n message: `Config not found: ${String(configName)}`,\n code: ReplaneErrorCode.NotFound,\n });\n }\n\n try {\n return evaluateOverrides<T[K]>(\n config.value as T[K],\n config.overrides,\n { ...this.context, ...(options?.context ?? {}) },\n this.logger\n );\n } catch (error) {\n this.logger.error(\n `Replane: error evaluating overrides for config ${String(configName)}:`,\n error\n );\n return config.value as T[K];\n }\n }\n\n /**\n * Subscribe to a specific config's changes.\n *\n * @param configName - The config to watch\n * @param callback - Function called when the config changes\n * @returns Unsubscribe function\n *\n * @example\n * ```typescript\n * const unsubscribe = client.subscribe('myConfig', (change) => {\n * console.log(`myConfig changed to ${change.value}`);\n * });\n * ```\n */\n subscribe<K extends keyof T>(\n configName: K,\n callback: (config: { name: K; value: T[K] }) => void\n ): () => void {\n // Wrap the callback to ensure that we have a unique reference\n // Type assertion is safe: MapConfig<T> is a union that includes { name: K, value: T[K] }\n const wrappedCallback = (config: MapConfig<T>) => {\n callback(config as { name: K; value: T[K] });\n };\n\n if (!this.configSubscriptions.has(configName)) {\n this.configSubscriptions.set(configName, new Set());\n }\n this.configSubscriptions.get(configName)!.add(wrappedCallback);\n\n return () => {\n this.configSubscriptions.get(configName)?.delete(wrappedCallback);\n if (this.configSubscriptions.get(configName)?.size === 0) {\n this.configSubscriptions.delete(configName);\n }\n };\n }\n\n /**\n * Get a serializable snapshot of the current client state.\n *\n * Useful for SSR/hydration scenarios where you want to pass\n * configs from server to client.\n *\n * @returns Snapshot object that can be serialized to JSON\n *\n * @example\n * ```typescript\n * // On server\n * const snapshot = client.getSnapshot();\n * const json = JSON.stringify(snapshot);\n *\n * // On client\n * const client = new Replane({ snapshot: JSON.parse(json) });\n * ```\n */\n getSnapshot(): ReplaneSnapshot<T> {\n return {\n configs: [...this.configs.values()].map((config) => ({\n name: config.name,\n value: config.value,\n overrides: config.overrides.map((override) => ({\n name: override.name,\n conditions: override.conditions,\n value: override.value,\n })),\n })),\n };\n }\n\n /**\n * Check if the client is currently connected.\n */\n get isConnected(): boolean {\n return this.storage !== null;\n }\n\n private async startStreaming(\n options: ConnectFinalOptions,\n clientReady: Deferred<void>\n ): Promise<void> {\n if (!this.storage) return;\n\n try {\n const replicationStream = this.storage.startReplicationStream({\n ...options,\n logger: this.logger,\n getBody: () => ({\n currentConfigs: [...this.configs.values()].map((config) => ({\n name: config.name,\n overrides: config.overrides,\n value: config.value,\n })),\n requiredConfigs: [],\n }),\n });\n\n for await (const event of replicationStream) {\n const updatedConfigs: ConfigDto[] =\n event.type === \"config_change\" ? [event.config] : event.configs;\n this.processConfigUpdates(updatedConfigs);\n clientReady.resolve();\n }\n } catch (error) {\n this.logger.error(\"Replane: error in SSE connection:\", error);\n clientReady.reject(error);\n throw error;\n }\n }\n\n private processConfigUpdates(updatedConfigs: ConfigDto[]): void {\n for (const config of updatedConfigs) {\n this.configs.set(config.name, {\n name: config.name,\n overrides: config.overrides,\n value: config.value,\n });\n\n const change = { name: config.name as keyof T, value: config.value as T[keyof T] };\n\n for (const callback of this.configSubscriptions.get(config.name as keyof T) ?? []) {\n callback(change);\n }\n }\n }\n\n private toFinalOptions(options: ConnectOptions): ConnectFinalOptions {\n return {\n sdkKey: options.sdkKey,\n baseUrl: (options.baseUrl ?? \"\").replace(/\\/+$/, \"\"),\n fetchFn:\n options.fetchFn ??\n // some browsers require binding the fetch function to window\n globalThis.fetch.bind(globalThis),\n requestTimeoutMs: options.requestTimeoutMs ?? 2000,\n connectTimeoutMs: options.connectTimeoutMs ?? 5000,\n inactivityTimeoutMs: options.inactivityTimeoutMs ?? 30_000,\n retryDelayMs: options.retryDelayMs ?? 200,\n agent: options.agent ?? DEFAULT_AGENT,\n };\n }\n}\n","import { Replane } from \"./client\";\nimport type { ConnectOptions, ReplaneSnapshot, ReplaneOptions } from \"./client-types\";\n\n/**\n * Options for getReplaneSnapshot with caching support.\n */\nexport interface GetReplaneSnapshotOptions<T extends object> extends ReplaneOptions<T> {\n /**\n * Cache TTL in milliseconds. When set, the client is cached and reused\n * for instant subsequent calls within this duration.\n * @default 60_000 (1 minute)\n */\n keepAliveMs?: number;\n /**\n * Connection options. If null, the snapshot will be created from defaults only.\n */\n connection: ConnectOptions | null;\n}\n\ninterface CachedClient {\n client: Replane<object>;\n timeoutId: TimeoutId;\n}\n\ninterface PendingConnection {\n promise: Promise<Replane<object>>;\n}\n\nconst clientCache = new Map<string, CachedClient>();\nconst pendingConnections = new Map<string, PendingConnection>();\n\nfunction getCacheKey(options: ConnectOptions): string {\n return `${options.baseUrl}:${options.sdkKey}`;\n}\n\ntype TimeoutId = ReturnType<typeof setTimeout>;\n\nfunction setupCleanupTimeout(cacheKey: string, keepAliveMs: number): TimeoutId {\n return setTimeout(() => {\n const cached = clientCache.get(cacheKey);\n if (cached) {\n cached.client.disconnect();\n clientCache.delete(cacheKey);\n }\n }, keepAliveMs);\n}\n\n/**\n * Creates a Replane client and returns a snapshot.\n * Useful for SSR/SSG scenarios where you need to fetch config once\n * and pass it to the client.\n *\n * @example\n * ```ts\n * const snapshot = await getReplaneSnapshot({\n * connection: {\n * baseUrl: process.env.REPLANE_BASE_URL!,\n * sdkKey: process.env.REPLANE_SDK_KEY!,\n * },\n * });\n * ```\n */\nexport async function getReplaneSnapshot<T extends object>(\n options: GetReplaneSnapshotOptions<T>\n): Promise<ReplaneSnapshot<T>> {\n const {\n keepAliveMs = 60_000,\n logger,\n context,\n defaults,\n connection: connectionOptions,\n } = options;\n\n if (!connectionOptions) {\n return {\n configs: [\n ...Object.entries(defaults ?? {}).map(([name, value]) => ({\n name,\n value,\n overrides: [],\n })),\n ],\n };\n }\n\n const cacheKey = getCacheKey(connectionOptions);\n const cached = clientCache.get(cacheKey);\n\n // Return from cache if valid\n if (cached) {\n clearTimeout(cached.timeoutId);\n cached.timeoutId = setupCleanupTimeout(cacheKey, keepAliveMs);\n return cached.client.getSnapshot() as ReplaneSnapshot<T>;\n }\n\n // Check for pending connection (for concurrent requests)\n const pending = pendingConnections.get(cacheKey);\n if (pending) {\n const client = await pending.promise;\n return client.getSnapshot() as ReplaneSnapshot<T>;\n }\n\n // Create new client and connect\n const client = new Replane<T>({\n logger,\n context,\n defaults,\n });\n\n // Store pending connection promise\n const connectionPromise = client\n .connect(connectionOptions)\n .then(() => client as unknown as Replane<object>);\n pendingConnections.set(cacheKey, { promise: connectionPromise });\n\n try {\n await connectionPromise;\n\n // Cache the connected client\n const entry: CachedClient = {\n client: client as unknown as Replane<object>,\n timeoutId: setupCleanupTimeout(cacheKey, keepAliveMs),\n };\n clientCache.set(cacheKey, entry);\n\n return client.getSnapshot();\n } finally {\n pendingConnections.delete(cacheKey);\n }\n}\n\n/**\n * Clears the client cache used by getReplaneSnapshot.\n * Useful for testing or when you need to force re-initialization.\n */\nexport function clearSnapshotCache(): void {\n for (const cached of clientCache.values()) {\n clearTimeout(cached.timeoutId);\n cached.client.disconnect();\n }\n clientCache.clear();\n pendingConnections.clear();\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@replanejs/sdk",
3
- "version": "0.9.2",
3
+ "version": "0.9.4",
4
4
  "description": "Dynamic configuration SDK for browser and server environments (Node.js, Deno, Bun). Powered by Replane.",
5
5
  "type": "module",
6
6
  "license": "MIT",