@replanejs/sdk 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +11 -29
- package/dist/index.d.cts +9 -22
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +9 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
<img alt="Replane Screenshot" src="https://raw.githubusercontent.com/replane-dev/replane/main/public/replane-window-screenshot-light-with-border-v2.jpg">
|
|
15
15
|
</picture>
|
|
16
16
|
|
|
17
|
-
[Replane](https://github.com/replane-dev/replane) is a dynamic configuration manager. Store feature flags,
|
|
17
|
+
[Replane](https://github.com/replane-dev/replane) is a dynamic configuration manager that lets you tweak your software without running scripts or building your own admin panel. Store feature flags, rate limits, UI text, log level, rollout percentage, and more. Delegate editing to teammates and share config across services. No redeploys needed.
|
|
18
18
|
|
|
19
19
|
## Why Dynamic Configuration?
|
|
20
20
|
|
package/dist/index.cjs
CHANGED
|
@@ -496,7 +496,7 @@ function castToContextType(expectedValue, contextValue) {
|
|
|
496
496
|
|
|
497
497
|
//#endregion
|
|
498
498
|
//#region src/version.ts
|
|
499
|
-
const VERSION = "1.0.
|
|
499
|
+
const VERSION = "1.0.2";
|
|
500
500
|
const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;
|
|
501
501
|
|
|
502
502
|
//#endregion
|
|
@@ -897,7 +897,7 @@ function asHandle(client) {
|
|
|
897
897
|
* @example
|
|
898
898
|
* ```typescript
|
|
899
899
|
* // Basic usage
|
|
900
|
-
* const client = new
|
|
900
|
+
* const client = new InMemoryReplane({
|
|
901
901
|
* defaults: { "feature-enabled": true, "rate-limit": 100 },
|
|
902
902
|
* });
|
|
903
903
|
* expect(client.get("feature-enabled")).toBe(true);
|
|
@@ -907,7 +907,7 @@ function asHandle(client) {
|
|
|
907
907
|
* expect(client.get("feature-enabled")).toBe(false);
|
|
908
908
|
*
|
|
909
909
|
* // With overrides
|
|
910
|
-
* client.
|
|
910
|
+
* client.set("rate-limit", 100, {
|
|
911
911
|
* overrides: [{
|
|
912
912
|
* name: "premium-users",
|
|
913
913
|
* conditions: [{ operator: "equals", property: "plan", value: "premium" }],
|
|
@@ -920,9 +920,9 @@ function asHandle(client) {
|
|
|
920
920
|
*
|
|
921
921
|
* @typeParam T - Type definition for config keys and values
|
|
922
922
|
*/
|
|
923
|
-
var
|
|
923
|
+
var InMemoryReplane = class {
|
|
924
924
|
constructor(options = {}) {
|
|
925
|
-
asHandle(this)._impl = new
|
|
925
|
+
asHandle(this)._impl = new InMemoryReplaneImpl(options);
|
|
926
926
|
}
|
|
927
927
|
/**
|
|
928
928
|
* Get a config value by name.
|
|
@@ -956,21 +956,6 @@ var InMemoryReplaneClient = class {
|
|
|
956
956
|
return asHandle(this)._impl.getSnapshot();
|
|
957
957
|
}
|
|
958
958
|
/**
|
|
959
|
-
* Set a config value (simple form without overrides).
|
|
960
|
-
*
|
|
961
|
-
* @param name - Config name
|
|
962
|
-
* @param value - Config value
|
|
963
|
-
*
|
|
964
|
-
* @example
|
|
965
|
-
* ```typescript
|
|
966
|
-
* client.set("feature-enabled", true);
|
|
967
|
-
* client.set("rate-limit", 500);
|
|
968
|
-
* ```
|
|
969
|
-
*/
|
|
970
|
-
set(name, value) {
|
|
971
|
-
asHandle(this)._impl.set(name, value);
|
|
972
|
-
}
|
|
973
|
-
/**
|
|
974
959
|
* Set a config with optional overrides.
|
|
975
960
|
*
|
|
976
961
|
* @param name - Config name
|
|
@@ -979,7 +964,7 @@ var InMemoryReplaneClient = class {
|
|
|
979
964
|
*
|
|
980
965
|
* @example
|
|
981
966
|
* ```typescript
|
|
982
|
-
* client.
|
|
967
|
+
* client.set("rate-limit", 100, {
|
|
983
968
|
* overrides: [{
|
|
984
969
|
* name: "premium-users",
|
|
985
970
|
* conditions: [
|
|
@@ -990,8 +975,8 @@ var InMemoryReplaneClient = class {
|
|
|
990
975
|
* });
|
|
991
976
|
* ```
|
|
992
977
|
*/
|
|
993
|
-
|
|
994
|
-
asHandle(this)._impl.
|
|
978
|
+
set(name, value, options) {
|
|
979
|
+
asHandle(this)._impl.set(name, value, options);
|
|
995
980
|
}
|
|
996
981
|
/**
|
|
997
982
|
* Delete a config.
|
|
@@ -1026,7 +1011,7 @@ var InMemoryReplaneClient = class {
|
|
|
1026
1011
|
return asHandle(this)._impl.keys();
|
|
1027
1012
|
}
|
|
1028
1013
|
};
|
|
1029
|
-
var
|
|
1014
|
+
var InMemoryReplaneImpl = class {
|
|
1030
1015
|
configs;
|
|
1031
1016
|
context;
|
|
1032
1017
|
logger;
|
|
@@ -1085,10 +1070,7 @@ var InMemoryReplaneClientImpl = class {
|
|
|
1085
1070
|
}))
|
|
1086
1071
|
})) };
|
|
1087
1072
|
}
|
|
1088
|
-
set(name, value) {
|
|
1089
|
-
this.setConfig(name, value);
|
|
1090
|
-
}
|
|
1091
|
-
setConfig(name, value, options) {
|
|
1073
|
+
set(name, value, options) {
|
|
1092
1074
|
const overrides = options?.overrides ?? [];
|
|
1093
1075
|
const config = {
|
|
1094
1076
|
name: String(name),
|
|
@@ -1126,7 +1108,7 @@ var InMemoryReplaneClientImpl = class {
|
|
|
1126
1108
|
};
|
|
1127
1109
|
|
|
1128
1110
|
//#endregion
|
|
1129
|
-
exports.
|
|
1111
|
+
exports.InMemoryReplane = InMemoryReplane;
|
|
1130
1112
|
exports.Replane = Replane;
|
|
1131
1113
|
exports.ReplaneError = ReplaneError;
|
|
1132
1114
|
exports.ReplaneErrorCode = ReplaneErrorCode;
|
package/dist/index.d.cts
CHANGED
|
@@ -288,9 +288,9 @@ interface SetConfigOptions {
|
|
|
288
288
|
overrides?: Override[];
|
|
289
289
|
}
|
|
290
290
|
/**
|
|
291
|
-
* Options for
|
|
291
|
+
* Options for InMemoryReplane constructor.
|
|
292
292
|
*/
|
|
293
|
-
interface
|
|
293
|
+
interface InMemoryReplaneOptions<T extends object> {
|
|
294
294
|
/**
|
|
295
295
|
* Optional logger (defaults to console).
|
|
296
296
|
*/
|
|
@@ -322,7 +322,7 @@ interface InMemoryReplaneClientOptions<T extends object> {
|
|
|
322
322
|
* @example
|
|
323
323
|
* ```typescript
|
|
324
324
|
* // Basic usage
|
|
325
|
-
* const client = new
|
|
325
|
+
* const client = new InMemoryReplane({
|
|
326
326
|
* defaults: { "feature-enabled": true, "rate-limit": 100 },
|
|
327
327
|
* });
|
|
328
328
|
* expect(client.get("feature-enabled")).toBe(true);
|
|
@@ -332,7 +332,7 @@ interface InMemoryReplaneClientOptions<T extends object> {
|
|
|
332
332
|
* expect(client.get("feature-enabled")).toBe(false);
|
|
333
333
|
*
|
|
334
334
|
* // With overrides
|
|
335
|
-
* client.
|
|
335
|
+
* client.set("rate-limit", 100, {
|
|
336
336
|
* overrides: [{
|
|
337
337
|
* name: "premium-users",
|
|
338
338
|
* conditions: [{ operator: "equals", property: "plan", value: "premium" }],
|
|
@@ -345,8 +345,8 @@ interface InMemoryReplaneClientOptions<T extends object> {
|
|
|
345
345
|
*
|
|
346
346
|
* @typeParam T - Type definition for config keys and values
|
|
347
347
|
*/
|
|
348
|
-
declare class
|
|
349
|
-
constructor(options?:
|
|
348
|
+
declare class InMemoryReplane<T extends object = Record<string, unknown>> {
|
|
349
|
+
constructor(options?: InMemoryReplaneOptions<T>);
|
|
350
350
|
/**
|
|
351
351
|
* Get a config value by name.
|
|
352
352
|
*
|
|
@@ -375,19 +375,6 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
375
375
|
* @returns Snapshot object that can be serialized to JSON
|
|
376
376
|
*/
|
|
377
377
|
getSnapshot(): ReplaneSnapshot<T>;
|
|
378
|
-
/**
|
|
379
|
-
* Set a config value (simple form without overrides).
|
|
380
|
-
*
|
|
381
|
-
* @param name - Config name
|
|
382
|
-
* @param value - Config value
|
|
383
|
-
*
|
|
384
|
-
* @example
|
|
385
|
-
* ```typescript
|
|
386
|
-
* client.set("feature-enabled", true);
|
|
387
|
-
* client.set("rate-limit", 500);
|
|
388
|
-
* ```
|
|
389
|
-
*/
|
|
390
|
-
set<K extends keyof T>(name: K, value: T[K]): void;
|
|
391
378
|
/**
|
|
392
379
|
* Set a config with optional overrides.
|
|
393
380
|
*
|
|
@@ -397,7 +384,7 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
397
384
|
*
|
|
398
385
|
* @example
|
|
399
386
|
* ```typescript
|
|
400
|
-
* client.
|
|
387
|
+
* client.set("rate-limit", 100, {
|
|
401
388
|
* overrides: [{
|
|
402
389
|
* name: "premium-users",
|
|
403
390
|
* conditions: [
|
|
@@ -408,7 +395,7 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
408
395
|
* });
|
|
409
396
|
* ```
|
|
410
397
|
*/
|
|
411
|
-
|
|
398
|
+
set<K extends keyof T>(name: K, value: T[K], options?: SetConfigOptions): void;
|
|
412
399
|
/**
|
|
413
400
|
* Delete a config.
|
|
414
401
|
*
|
|
@@ -436,5 +423,5 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
436
423
|
}
|
|
437
424
|
//# sourceMappingURL=in-memory.d.ts.map
|
|
438
425
|
//#endregion
|
|
439
|
-
export { type Condition, type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions,
|
|
426
|
+
export { type Condition, type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, InMemoryReplane, type InMemoryReplaneOptions, type Override, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, type ReplaneSnapshot, type SetConfigOptions, getReplaneSnapshot };
|
|
440
427
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -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","../src/in-memory.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,SCeiB,EAAA;;UDZrB,WAAA,CCgBC;EAAa,QAKZ,EAAA,IAAA;EAAc,UAaV,EDhCF,SCgCE,EAAA;;UD7BN,YAAA,CC6Ba;EAAC,QAMK,EAAA,KAAA;EAAC,SAAjB,EDjCA,SCiCA;AAAe;AAMX,KDpCL,SAAA,GACR,iBC2EoB,GD1EpB,qBC0EoB,GDzEpB,YCyEoB,GDxEpB,WCwEoB,GDvEpB,YCuEoB;UDrEP,QAAA;;cAEH;EEID,KAAA,EAAA,OAAO;;;;AFnDd;AAEqB;AAaI;AAUR;AAQb,KC9CE,cAAA,GAAiB,MDgDhB,CAAA,MAAS,EAAA,MAAA,GAAA,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,SAAA,CAAA;AAGtB;;;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,
|
|
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","../src/in-memory.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -288,9 +288,9 @@ interface SetConfigOptions {
|
|
|
288
288
|
overrides?: Override[];
|
|
289
289
|
}
|
|
290
290
|
/**
|
|
291
|
-
* Options for
|
|
291
|
+
* Options for InMemoryReplane constructor.
|
|
292
292
|
*/
|
|
293
|
-
interface
|
|
293
|
+
interface InMemoryReplaneOptions<T extends object> {
|
|
294
294
|
/**
|
|
295
295
|
* Optional logger (defaults to console).
|
|
296
296
|
*/
|
|
@@ -322,7 +322,7 @@ interface InMemoryReplaneClientOptions<T extends object> {
|
|
|
322
322
|
* @example
|
|
323
323
|
* ```typescript
|
|
324
324
|
* // Basic usage
|
|
325
|
-
* const client = new
|
|
325
|
+
* const client = new InMemoryReplane({
|
|
326
326
|
* defaults: { "feature-enabled": true, "rate-limit": 100 },
|
|
327
327
|
* });
|
|
328
328
|
* expect(client.get("feature-enabled")).toBe(true);
|
|
@@ -332,7 +332,7 @@ interface InMemoryReplaneClientOptions<T extends object> {
|
|
|
332
332
|
* expect(client.get("feature-enabled")).toBe(false);
|
|
333
333
|
*
|
|
334
334
|
* // With overrides
|
|
335
|
-
* client.
|
|
335
|
+
* client.set("rate-limit", 100, {
|
|
336
336
|
* overrides: [{
|
|
337
337
|
* name: "premium-users",
|
|
338
338
|
* conditions: [{ operator: "equals", property: "plan", value: "premium" }],
|
|
@@ -345,8 +345,8 @@ interface InMemoryReplaneClientOptions<T extends object> {
|
|
|
345
345
|
*
|
|
346
346
|
* @typeParam T - Type definition for config keys and values
|
|
347
347
|
*/
|
|
348
|
-
declare class
|
|
349
|
-
constructor(options?:
|
|
348
|
+
declare class InMemoryReplane<T extends object = Record<string, unknown>> {
|
|
349
|
+
constructor(options?: InMemoryReplaneOptions<T>);
|
|
350
350
|
/**
|
|
351
351
|
* Get a config value by name.
|
|
352
352
|
*
|
|
@@ -375,19 +375,6 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
375
375
|
* @returns Snapshot object that can be serialized to JSON
|
|
376
376
|
*/
|
|
377
377
|
getSnapshot(): ReplaneSnapshot<T>;
|
|
378
|
-
/**
|
|
379
|
-
* Set a config value (simple form without overrides).
|
|
380
|
-
*
|
|
381
|
-
* @param name - Config name
|
|
382
|
-
* @param value - Config value
|
|
383
|
-
*
|
|
384
|
-
* @example
|
|
385
|
-
* ```typescript
|
|
386
|
-
* client.set("feature-enabled", true);
|
|
387
|
-
* client.set("rate-limit", 500);
|
|
388
|
-
* ```
|
|
389
|
-
*/
|
|
390
|
-
set<K extends keyof T>(name: K, value: T[K]): void;
|
|
391
378
|
/**
|
|
392
379
|
* Set a config with optional overrides.
|
|
393
380
|
*
|
|
@@ -397,7 +384,7 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
397
384
|
*
|
|
398
385
|
* @example
|
|
399
386
|
* ```typescript
|
|
400
|
-
* client.
|
|
387
|
+
* client.set("rate-limit", 100, {
|
|
401
388
|
* overrides: [{
|
|
402
389
|
* name: "premium-users",
|
|
403
390
|
* conditions: [
|
|
@@ -408,7 +395,7 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
408
395
|
* });
|
|
409
396
|
* ```
|
|
410
397
|
*/
|
|
411
|
-
|
|
398
|
+
set<K extends keyof T>(name: K, value: T[K], options?: SetConfigOptions): void;
|
|
412
399
|
/**
|
|
413
400
|
* Delete a config.
|
|
414
401
|
*
|
|
@@ -436,5 +423,5 @@ declare class InMemoryReplaneClient<T extends object = Record<string, unknown>>
|
|
|
436
423
|
}
|
|
437
424
|
//# sourceMappingURL=in-memory.d.ts.map
|
|
438
425
|
//#endregion
|
|
439
|
-
export { type Condition, type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions,
|
|
426
|
+
export { type Condition, type ConnectOptions, type GetConfigOptions, type GetReplaneSnapshotOptions, InMemoryReplane, type InMemoryReplaneOptions, type Override, Replane, type ReplaneContext, ReplaneError, ReplaneErrorCode, type ReplaneLogger, type ReplaneOptions, type ReplaneSnapshot, type SetConfigOptions, getReplaneSnapshot };
|
|
440
427
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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","../src/in-memory.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,SCeiB,EAAA;;UDZrB,WAAA,CCgBC;EAAa,QAKZ,EAAA,IAAA;EAAc,UAaV,EDhCF,SCgCE,EAAA;;UD7BN,YAAA,CC6Ba;EAAC,QAMK,EAAA,KAAA;EAAC,SAAjB,EDjCA,SCiCA;AAAe;AAMX,KDpCL,SAAA,GACR,iBC2EoB,GD1EpB,qBC0EoB,GDzEpB,YCyEoB,GDxEpB,WCwEoB,GDvEpB,YCuEoB;UDrEP,QAAA;;cAEH;EEID,KAAA,EAAA,OAAO;;;;AFnDd;AAEqB;AAaI;AAUR;AAQb,KC9CE,cAAA,GAAiB,MDgDhB,CAAA,MAAS,EAAA,MAAA,GAAA,MAAA,GAAA,OAAA,GAAA,IAAA,GAAA,SAAA,CAAA;AAGtB;;;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,CAAA;EAKZ;AAUjB;;EAAiC,OAIrB,CAAA,EAAA,cAAA;EAAc;AAKb;AAiBb;;EAAgC,OAKjB,CAAA,EAtBH,CAsBG;;AAHC;AAUhB;;;;AA4B4B;AAM5B;;UA9CiB;;ECuBJ,OAAA,EDrBF,KCqBS,CAAA;IAAA,IAAA,EAAA,MAAA;IAAoB,KAAA,EAAA,OAAA;IACF,SAAA,EDnBvB,QCmBuB,EAAA;EAAC,CAAA,CAAA;;;;;AAS4B,UDrBlD,cCqBkD,CAAA,UAAA,MAAA,CAAA,CAAA;EAAC;;;EAAQ,MAAC,CAAA,EDjBlE,aCiBkE;EAAC;;;;EAKpC,OAAC,CAAA,EDjB/B,cCiB+B;EAAC;;AAIZ;;;;ACtFhC;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;;;;ACvBV;AAQA;;;EAIwB,OAKZ,EAAA,MAAA;EAAc;;;AAYF;AA+CxB;
|
|
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","../src/in-memory.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -495,7 +495,7 @@ function castToContextType(expectedValue, contextValue) {
|
|
|
495
495
|
|
|
496
496
|
//#endregion
|
|
497
497
|
//#region src/version.ts
|
|
498
|
-
const VERSION = "1.0.
|
|
498
|
+
const VERSION = "1.0.2";
|
|
499
499
|
const DEFAULT_AGENT = `replane-js-sdk/${VERSION}`;
|
|
500
500
|
|
|
501
501
|
//#endregion
|
|
@@ -896,7 +896,7 @@ function asHandle(client) {
|
|
|
896
896
|
* @example
|
|
897
897
|
* ```typescript
|
|
898
898
|
* // Basic usage
|
|
899
|
-
* const client = new
|
|
899
|
+
* const client = new InMemoryReplane({
|
|
900
900
|
* defaults: { "feature-enabled": true, "rate-limit": 100 },
|
|
901
901
|
* });
|
|
902
902
|
* expect(client.get("feature-enabled")).toBe(true);
|
|
@@ -906,7 +906,7 @@ function asHandle(client) {
|
|
|
906
906
|
* expect(client.get("feature-enabled")).toBe(false);
|
|
907
907
|
*
|
|
908
908
|
* // With overrides
|
|
909
|
-
* client.
|
|
909
|
+
* client.set("rate-limit", 100, {
|
|
910
910
|
* overrides: [{
|
|
911
911
|
* name: "premium-users",
|
|
912
912
|
* conditions: [{ operator: "equals", property: "plan", value: "premium" }],
|
|
@@ -919,9 +919,9 @@ function asHandle(client) {
|
|
|
919
919
|
*
|
|
920
920
|
* @typeParam T - Type definition for config keys and values
|
|
921
921
|
*/
|
|
922
|
-
var
|
|
922
|
+
var InMemoryReplane = class {
|
|
923
923
|
constructor(options = {}) {
|
|
924
|
-
asHandle(this)._impl = new
|
|
924
|
+
asHandle(this)._impl = new InMemoryReplaneImpl(options);
|
|
925
925
|
}
|
|
926
926
|
/**
|
|
927
927
|
* Get a config value by name.
|
|
@@ -955,21 +955,6 @@ var InMemoryReplaneClient = class {
|
|
|
955
955
|
return asHandle(this)._impl.getSnapshot();
|
|
956
956
|
}
|
|
957
957
|
/**
|
|
958
|
-
* Set a config value (simple form without overrides).
|
|
959
|
-
*
|
|
960
|
-
* @param name - Config name
|
|
961
|
-
* @param value - Config value
|
|
962
|
-
*
|
|
963
|
-
* @example
|
|
964
|
-
* ```typescript
|
|
965
|
-
* client.set("feature-enabled", true);
|
|
966
|
-
* client.set("rate-limit", 500);
|
|
967
|
-
* ```
|
|
968
|
-
*/
|
|
969
|
-
set(name, value) {
|
|
970
|
-
asHandle(this)._impl.set(name, value);
|
|
971
|
-
}
|
|
972
|
-
/**
|
|
973
958
|
* Set a config with optional overrides.
|
|
974
959
|
*
|
|
975
960
|
* @param name - Config name
|
|
@@ -978,7 +963,7 @@ var InMemoryReplaneClient = class {
|
|
|
978
963
|
*
|
|
979
964
|
* @example
|
|
980
965
|
* ```typescript
|
|
981
|
-
* client.
|
|
966
|
+
* client.set("rate-limit", 100, {
|
|
982
967
|
* overrides: [{
|
|
983
968
|
* name: "premium-users",
|
|
984
969
|
* conditions: [
|
|
@@ -989,8 +974,8 @@ var InMemoryReplaneClient = class {
|
|
|
989
974
|
* });
|
|
990
975
|
* ```
|
|
991
976
|
*/
|
|
992
|
-
|
|
993
|
-
asHandle(this)._impl.
|
|
977
|
+
set(name, value, options) {
|
|
978
|
+
asHandle(this)._impl.set(name, value, options);
|
|
994
979
|
}
|
|
995
980
|
/**
|
|
996
981
|
* Delete a config.
|
|
@@ -1025,7 +1010,7 @@ var InMemoryReplaneClient = class {
|
|
|
1025
1010
|
return asHandle(this)._impl.keys();
|
|
1026
1011
|
}
|
|
1027
1012
|
};
|
|
1028
|
-
var
|
|
1013
|
+
var InMemoryReplaneImpl = class {
|
|
1029
1014
|
configs;
|
|
1030
1015
|
context;
|
|
1031
1016
|
logger;
|
|
@@ -1084,10 +1069,7 @@ var InMemoryReplaneClientImpl = class {
|
|
|
1084
1069
|
}))
|
|
1085
1070
|
})) };
|
|
1086
1071
|
}
|
|
1087
|
-
set(name, value) {
|
|
1088
|
-
this.setConfig(name, value);
|
|
1089
|
-
}
|
|
1090
|
-
setConfig(name, value, options) {
|
|
1072
|
+
set(name, value, options) {
|
|
1091
1073
|
const overrides = options?.overrides ?? [];
|
|
1092
1074
|
const config = {
|
|
1093
1075
|
name: String(name),
|
|
@@ -1125,5 +1107,5 @@ var InMemoryReplaneClientImpl = class {
|
|
|
1125
1107
|
};
|
|
1126
1108
|
|
|
1127
1109
|
//#endregion
|
|
1128
|
-
export {
|
|
1110
|
+
export { InMemoryReplane, Replane, ReplaneError, ReplaneErrorCode, getReplaneSnapshot };
|
|
1129
1111
|
//# sourceMappingURL=index.js.map
|
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: Override[]","context: ReplaneContext","logger: ReplaneLogger","overrideResult: EvaluationResult","condition: Condition","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","autoGeneratedContext: ReplaneContext","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","client: InMemoryReplaneClient<T>","options: InMemoryReplaneClientOptions<T>","configName: K","options?: GetConfigOptions<T[K]>","callback: (config: { name: K; value: T[K] }) => void","name: K","value: T[K]","options?: SetConfigOptions","initialConfigs: ConfigDto[]","options: GetConfigOptions<T[K]>","config: MapConfig<T>","overrides: Override[]","config: ConfigDto"],"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","../src/in-memory.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 * Generates a random UUID using the Web Crypto API.\n * Falls back to a simple implementation if crypto.randomUUID is not available.\n *\n * @returns A random UUID string\n */\nexport function generateClientId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n // Fallback for older environments\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\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 { Condition, Override } 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: Override[],\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: Condition,\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 = \"1.0.0\";\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, generateClientId } from \"./utils\";\nimport { DEFAULT_AGENT } from \"./version\";\n\n/**\n * The context key for the auto-generated client ID.\n * This key is automatically set by the SDK and can be used for segmentation.\n * User-provided values for this key take precedence over the auto-generated value.\n */\nexport const REPLANE_CLIENT_ID_KEY = \"replaneClientId\";\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\n // Generate replaneClientId and set it as base context.\n // User-provided context values take precedence (merged on top).\n const autoGeneratedContext: ReplaneContext = {\n [REPLANE_CLIENT_ID_KEY]: generateClientId(),\n };\n this.context = { ...autoGeneratedContext, ...(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 if (event.type === \"init\") {\n this.processConfigUpdates(event.configs);\n this.logger.info(`Replane: initialized with ${event.configs.length} config(s)`);\n } else {\n this.processConfigUpdates([event.config]);\n this.logger.info(`Replane: config \"${event.config.name}\" updated`);\n }\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","/**\n * Testing utilities for the Replane JavaScript SDK.\n *\n * This module provides an in-memory client for testing applications that use\n * Replane without requiring a real server connection.\n *\n * @example\n * ```typescript\n * import { InMemoryReplaneClient } from \"@replane/sdk/testing\";\n *\n * // Create client with initial configs\n * const client = new InMemoryReplaneClient({\n * defaults: {\n * \"feature-enabled\": true,\n * \"rate-limit\": 100,\n * },\n * });\n *\n * // Use in tests\n * expect(client.get(\"feature-enabled\")).toBe(true);\n *\n * // Update config at runtime\n * client.set(\"feature-enabled\", false);\n * expect(client.get(\"feature-enabled\")).toBe(false);\n * ```\n */\n\nimport type { Override, ConfigDto } from \"./types\";\nimport type {\n ReplaneContext,\n ReplaneLogger,\n GetConfigOptions,\n MapConfig,\n ReplaneSnapshot,\n} from \"./client-types\";\nimport { ReplaneError, ReplaneErrorCode } from \"./error\";\nimport { evaluateOverrides } from \"./evaluation\";\n\n/**\n * Options for setting a config with overrides.\n */\nexport interface SetConfigOptions {\n /** Override rules for context-based value selection */\n overrides?: Override[];\n}\n\n/**\n * Options for InMemoryReplaneClient constructor.\n */\nexport interface InMemoryReplaneClientOptions<T extends object> {\n /**\n * Optional logger (defaults to console).\n */\n logger?: ReplaneLogger;\n /**\n * Default context for all config evaluations.\n * Can be overridden per-request in `client.get()`.\n */\n context?: ReplaneContext;\n /**\n * Initial config values.\n * @example\n * {\n * defaults: {\n * \"feature-enabled\": true,\n * \"rate-limit\": 100,\n * },\n * }\n */\n defaults?: {\n [K in keyof T]?: T[K];\n };\n}\n\ninterface InMemoryReplaneClientHandle<T extends object> {\n _impl: InMemoryReplaneClientImpl<T>;\n}\n\nfunction asHandle<T extends object>(\n client: InMemoryReplaneClient<T>\n): InMemoryReplaneClientHandle<T> {\n return client as unknown as InMemoryReplaneClientHandle<T>;\n}\n\n/**\n * An in-memory Replane client for testing.\n *\n * This client provides the same interface as `Replane` but stores\n * all configs in memory. It's useful for unit tests where you don't want\n * to connect to a real Replane server.\n *\n * @example\n * ```typescript\n * // Basic usage\n * const client = new InMemoryReplaneClient({\n * defaults: { \"feature-enabled\": true, \"rate-limit\": 100 },\n * });\n * expect(client.get(\"feature-enabled\")).toBe(true);\n *\n * // Update config at runtime\n * client.set(\"feature-enabled\", false);\n * expect(client.get(\"feature-enabled\")).toBe(false);\n *\n * // With overrides\n * client.setConfig(\"rate-limit\", 100, {\n * overrides: [{\n * name: \"premium-users\",\n * conditions: [{ operator: \"equals\", property: \"plan\", value: \"premium\" }],\n * value: 1000,\n * }],\n * });\n * expect(client.get(\"rate-limit\")).toBe(100);\n * expect(client.get(\"rate-limit\", { context: { plan: \"premium\" } })).toBe(1000);\n * ```\n *\n * @typeParam T - Type definition for config keys and values\n */\nexport class InMemoryReplaneClient<T extends object = Record<string, unknown>> {\n constructor(options: InMemoryReplaneClientOptions<T> = {}) {\n asHandle(this)._impl = new InMemoryReplaneClientImpl<T>(options);\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 get<K extends keyof T>(configName: K, options?: GetConfigOptions<T[K]>): T[K] {\n return asHandle(this)._impl.get(configName, options);\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 subscribe<K extends keyof T>(\n configName: K,\n callback: (config: { name: K; value: T[K] }) => void\n ): () => void {\n return asHandle(this)._impl.subscribe(configName, callback);\n }\n\n /**\n * Get a serializable snapshot of the current client state.\n *\n * @returns Snapshot object that can be serialized to JSON\n */\n getSnapshot(): ReplaneSnapshot<T> {\n return asHandle(this)._impl.getSnapshot();\n }\n\n /**\n * Set a config value (simple form without overrides).\n *\n * @param name - Config name\n * @param value - Config value\n *\n * @example\n * ```typescript\n * client.set(\"feature-enabled\", true);\n * client.set(\"rate-limit\", 500);\n * ```\n */\n set<K extends keyof T>(name: K, value: T[K]): void {\n asHandle(this)._impl.set(name, value);\n }\n\n /**\n * Set a config with optional overrides.\n *\n * @param name - Config name\n * @param value - Base config value\n * @param options - Optional settings including overrides\n *\n * @example\n * ```typescript\n * client.setConfig(\"rate-limit\", 100, {\n * overrides: [{\n * name: \"premium-users\",\n * conditions: [\n * { operator: \"in\", property: \"plan\", value: [\"pro\", \"enterprise\"] }\n * ],\n * value: 1000,\n * }],\n * });\n * ```\n */\n setConfig<K extends keyof T>(name: K, value: T[K], options?: SetConfigOptions): void {\n asHandle(this)._impl.setConfig(name, value, options);\n }\n\n /**\n * Delete a config.\n *\n * @param name - Config name to delete\n * @returns True if config was deleted, false if it didn't exist\n */\n delete<K extends keyof T>(name: K): boolean {\n return asHandle(this)._impl.delete(name);\n }\n\n /**\n * Clear all configs.\n */\n clear(): void {\n asHandle(this)._impl.clear();\n }\n\n /**\n * Check if a config exists.\n *\n * @param name - Config name to check\n * @returns True if config exists\n */\n has<K extends keyof T>(name: K): boolean {\n return asHandle(this)._impl.has(name);\n }\n\n /**\n * Get all config names.\n *\n * @returns Array of config names\n */\n keys(): (keyof T)[] {\n return asHandle(this)._impl.keys();\n }\n}\n\n// Implementation class to hide internal details from the public API\nclass InMemoryReplaneClientImpl<T extends object = Record<string, unknown>> {\n private configs: Map<string, ConfigDto>;\n private context: ReplaneContext;\n private logger: ReplaneLogger;\n private configSubscriptions = new Map<keyof T, Set<(config: MapConfig<T>) => void>>();\n\n constructor(options: InMemoryReplaneClientOptions<T> = {}) {\n this.logger = options.logger ?? console;\n this.context = options.context ?? {};\n\n // Initialize configs from defaults\n const initialConfigs: ConfigDto[] = [];\n\n if (options.defaults) {\n for (const [name, value] of Object.entries(options.defaults)) {\n if (value !== undefined) {\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 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 subscribe<K extends keyof T>(\n configName: K,\n callback: (config: { name: K; value: T[K] }) => void\n ): () => void {\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 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 set<K extends keyof T>(name: K, value: T[K]): void {\n this.setConfig(name, value);\n }\n\n setConfig<K extends keyof T>(name: K, value: T[K], options?: SetConfigOptions): void {\n const overrides: Override[] = options?.overrides ?? [];\n\n const config: ConfigDto = {\n name: String(name),\n value,\n overrides,\n };\n\n this.configs.set(String(name), config);\n\n // Notify subscribers\n this.notifySubscribers(name, value);\n }\n\n delete<K extends keyof T>(name: K): boolean {\n const existed = this.configs.has(String(name));\n this.configs.delete(String(name));\n return existed;\n }\n\n clear(): void {\n this.configs.clear();\n }\n\n has<K extends keyof T>(name: K): boolean {\n return this.configs.has(String(name));\n }\n\n keys(): (keyof T)[] {\n return [...this.configs.keys()] as (keyof T)[];\n }\n\n private notifySubscribers<K extends keyof T>(name: K, value: T[K]): void {\n const change = { name, value };\n for (const callback of this.configSubscriptions.get(name) ?? []) {\n try {\n callback(change as MapConfig<T>);\n } catch (error) {\n this.logger.error(`Replane: error in subscription callback for ${String(name)}:`, error);\n }\n }\n }\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: Override[]","context: ReplaneContext","logger: ReplaneLogger","overrideResult: EvaluationResult","condition: Condition","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","autoGeneratedContext: ReplaneContext","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","client: InMemoryReplane<T>","options: InMemoryReplaneOptions<T>","configName: K","options?: GetConfigOptions<T[K]>","callback: (config: { name: K; value: T[K] }) => void","name: K","value: T[K]","options?: SetConfigOptions","initialConfigs: ConfigDto[]","options: GetConfigOptions<T[K]>","config: MapConfig<T>","overrides: Override[]","config: ConfigDto"],"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","../src/in-memory.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 * Generates a random UUID using the Web Crypto API.\n * Falls back to a simple implementation if crypto.randomUUID is not available.\n *\n * @returns A random UUID string\n */\nexport function generateClientId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n // Fallback for older environments\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === \"x\" ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\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 { Condition, Override } 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: Override[],\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: Condition,\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 = \"1.0.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, generateClientId } from \"./utils\";\nimport { DEFAULT_AGENT } from \"./version\";\n\n/**\n * The context key for the auto-generated client ID.\n * This key is automatically set by the SDK and can be used for segmentation.\n * User-provided values for this key take precedence over the auto-generated value.\n */\nexport const REPLANE_CLIENT_ID_KEY = \"replaneClientId\";\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\n // Generate replaneClientId and set it as base context.\n // User-provided context values take precedence (merged on top).\n const autoGeneratedContext: ReplaneContext = {\n [REPLANE_CLIENT_ID_KEY]: generateClientId(),\n };\n this.context = { ...autoGeneratedContext, ...(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 if (event.type === \"init\") {\n this.processConfigUpdates(event.configs);\n this.logger.info(`Replane: initialized with ${event.configs.length} config(s)`);\n } else {\n this.processConfigUpdates([event.config]);\n this.logger.info(`Replane: config \"${event.config.name}\" updated`);\n }\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","/**\n * Testing utilities for the Replane JavaScript SDK.\n *\n * This module provides an in-memory client for testing applications that use\n * Replane without requiring a real server connection.\n *\n * @example\n * ```typescript\n * import { InMemoryReplane } from \"@replane/sdk/testing\";\n *\n * // Create client with initial configs\n * const client = new InMemoryReplane({\n * defaults: {\n * \"feature-enabled\": true,\n * \"rate-limit\": 100,\n * },\n * });\n *\n * // Use in tests\n * expect(client.get(\"feature-enabled\")).toBe(true);\n *\n * // Update config at runtime\n * client.set(\"feature-enabled\", false);\n * expect(client.get(\"feature-enabled\")).toBe(false);\n * ```\n */\n\nimport type { Override, ConfigDto } from \"./types\";\nimport type {\n ReplaneContext,\n ReplaneLogger,\n GetConfigOptions,\n MapConfig,\n ReplaneSnapshot,\n} from \"./client-types\";\nimport { ReplaneError, ReplaneErrorCode } from \"./error\";\nimport { evaluateOverrides } from \"./evaluation\";\n\n/**\n * Options for setting a config with overrides.\n */\nexport interface SetConfigOptions {\n /** Override rules for context-based value selection */\n overrides?: Override[];\n}\n\n/**\n * Options for InMemoryReplane constructor.\n */\nexport interface InMemoryReplaneOptions<T extends object> {\n /**\n * Optional logger (defaults to console).\n */\n logger?: ReplaneLogger;\n /**\n * Default context for all config evaluations.\n * Can be overridden per-request in `client.get()`.\n */\n context?: ReplaneContext;\n /**\n * Initial config values.\n * @example\n * {\n * defaults: {\n * \"feature-enabled\": true,\n * \"rate-limit\": 100,\n * },\n * }\n */\n defaults?: {\n [K in keyof T]?: T[K];\n };\n}\n\ninterface InMemoryReplaneHandle<T extends object> {\n _impl: InMemoryReplaneImpl<T>;\n}\n\nfunction asHandle<T extends object>(\n client: InMemoryReplane<T>\n): InMemoryReplaneHandle<T> {\n return client as unknown as InMemoryReplaneHandle<T>;\n}\n\n/**\n * An in-memory Replane client for testing.\n *\n * This client provides the same interface as `Replane` but stores\n * all configs in memory. It's useful for unit tests where you don't want\n * to connect to a real Replane server.\n *\n * @example\n * ```typescript\n * // Basic usage\n * const client = new InMemoryReplane({\n * defaults: { \"feature-enabled\": true, \"rate-limit\": 100 },\n * });\n * expect(client.get(\"feature-enabled\")).toBe(true);\n *\n * // Update config at runtime\n * client.set(\"feature-enabled\", false);\n * expect(client.get(\"feature-enabled\")).toBe(false);\n *\n * // With overrides\n * client.set(\"rate-limit\", 100, {\n * overrides: [{\n * name: \"premium-users\",\n * conditions: [{ operator: \"equals\", property: \"plan\", value: \"premium\" }],\n * value: 1000,\n * }],\n * });\n * expect(client.get(\"rate-limit\")).toBe(100);\n * expect(client.get(\"rate-limit\", { context: { plan: \"premium\" } })).toBe(1000);\n * ```\n *\n * @typeParam T - Type definition for config keys and values\n */\nexport class InMemoryReplane<T extends object = Record<string, unknown>> {\n constructor(options: InMemoryReplaneOptions<T> = {}) {\n asHandle(this)._impl = new InMemoryReplaneImpl<T>(options);\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 get<K extends keyof T>(configName: K, options?: GetConfigOptions<T[K]>): T[K] {\n return asHandle(this)._impl.get(configName, options);\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 subscribe<K extends keyof T>(\n configName: K,\n callback: (config: { name: K; value: T[K] }) => void\n ): () => void {\n return asHandle(this)._impl.subscribe(configName, callback);\n }\n\n /**\n * Get a serializable snapshot of the current client state.\n *\n * @returns Snapshot object that can be serialized to JSON\n */\n getSnapshot(): ReplaneSnapshot<T> {\n return asHandle(this)._impl.getSnapshot();\n }\n\n /**\n * Set a config with optional overrides.\n *\n * @param name - Config name\n * @param value - Base config value\n * @param options - Optional settings including overrides\n *\n * @example\n * ```typescript\n * client.set(\"rate-limit\", 100, {\n * overrides: [{\n * name: \"premium-users\",\n * conditions: [\n * { operator: \"in\", property: \"plan\", value: [\"pro\", \"enterprise\"] }\n * ],\n * value: 1000,\n * }],\n * });\n * ```\n */\n set<K extends keyof T>(name: K, value: T[K], options?: SetConfigOptions): void {\n asHandle(this)._impl.set(name, value, options);\n }\n\n /**\n * Delete a config.\n *\n * @param name - Config name to delete\n * @returns True if config was deleted, false if it didn't exist\n */\n delete<K extends keyof T>(name: K): boolean {\n return asHandle(this)._impl.delete(name);\n }\n\n /**\n * Clear all configs.\n */\n clear(): void {\n asHandle(this)._impl.clear();\n }\n\n /**\n * Check if a config exists.\n *\n * @param name - Config name to check\n * @returns True if config exists\n */\n has<K extends keyof T>(name: K): boolean {\n return asHandle(this)._impl.has(name);\n }\n\n /**\n * Get all config names.\n *\n * @returns Array of config names\n */\n keys(): (keyof T)[] {\n return asHandle(this)._impl.keys();\n }\n}\n\n// Implementation class to hide internal details from the public API\nclass InMemoryReplaneImpl<T extends object = Record<string, unknown>> {\n private configs: Map<string, ConfigDto>;\n private context: ReplaneContext;\n private logger: ReplaneLogger;\n private configSubscriptions = new Map<keyof T, Set<(config: MapConfig<T>) => void>>();\n\n constructor(options: InMemoryReplaneOptions<T> = {}) {\n this.logger = options.logger ?? console;\n this.context = options.context ?? {};\n\n // Initialize configs from defaults\n const initialConfigs: ConfigDto[] = [];\n\n if (options.defaults) {\n for (const [name, value] of Object.entries(options.defaults)) {\n if (value !== undefined) {\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 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 subscribe<K extends keyof T>(\n configName: K,\n callback: (config: { name: K; value: T[K] }) => void\n ): () => void {\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 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 set<K extends keyof T>(name: K, value: T[K], options?: SetConfigOptions): void {\n const overrides: Override[] = options?.overrides ?? [];\n\n const config: ConfigDto = {\n name: String(name),\n value,\n overrides,\n };\n\n this.configs.set(String(name), config);\n\n // Notify subscribers\n this.notifySubscribers(name, value);\n }\n\n delete<K extends keyof T>(name: K): boolean {\n const existed = this.configs.has(String(name));\n this.configs.delete(String(name));\n return existed;\n }\n\n clear(): void {\n this.configs.clear();\n }\n\n has<K extends keyof T>(name: K): boolean {\n return this.configs.has(String(name));\n }\n\n keys(): (keyof T)[] {\n return [...this.configs.keys()] as (keyof T)[];\n }\n\n private notifySubscribers<K extends keyof T>(name: K, value: T[K]): void {\n const change = { name, value };\n for (const callback of this.configSubscriptions.get(name) ?? []) {\n try {\n callback(change as MapConfig<T>);\n } catch (error) {\n this.logger.error(`Replane: error in subscription callback for ${String(name)}:`, error);\n }\n }\n }\n}\n"],"mappings":";;;;AAGA,IAAY,gEAAL;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACD;;;;AAKD,IAAa,eAAb,cAAkC,MAAM;CACtC;CAEA,YAAYA,QAA4D;AACtE,QAAM,OAAO,SAAS,EAAE,OAAO,OAAO,MAAO,EAAC;AAC9C,OAAK,OAAO;AACZ,OAAK,OAAO,OAAO;CACpB;AACF;;;;;;;;;;ACrBD,SAAgB,mBAA2B;AACzC,YAAW,WAAW,sBAAsB,OAAO,eAAe,WAChE,QAAO,OAAO,YAAY;AAI5B,QAAO,uCAAuC,QAAQ,SAAS,CAAC,MAAM;EACpE,MAAM,IAAK,KAAK,QAAQ,GAAG,KAAM;EACjC,MAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,SAAO,EAAE,SAAS,GAAG;CACtB,EAAC;AACH;;;;;;AAOD,eAAsB,MAAMC,IAA2B;AACrD,QAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG;AACxD;;;;;;;AAQD,eAAsB,WAAWC,cAAqC;CACpE,MAAM,SAAS,eAAe;CAC9B,MAAM,UAAU,eAAe,KAAK,QAAQ,GAAG,SAAS,SAAS;AAEjE,OAAM,MAAM,QAAQ;AACrB;;;;;;;;AASD,SAAgB,oBAAoBC,SAGlC;CACA,MAAM,aAAa,IAAI;CACvB,MAAM,UAAU,MAAM;AACpB,aAAW,OAAO;AAClB,kBAAgB;CACjB;CAED,MAAM,iBAAiB,MAAM;AAC3B,OAAK,MAAM,KAAK,QACd,IAAG,oBAAoB,SAAS,QAAQ;CAE3C;AAED,MAAK,MAAM,KAAK,QACd,IAAG,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAM,EAAC;AAGvD,KAAI,QAAQ,KAAK,CAAC,MAAM,GAAG,QAAQ,CACjC,UAAS;AAGX,QAAO;EAAE,QAAQ,WAAW;EAAQ;CAAgB;AACrD;;;;;AAMD,IAAa,WAAb,MAAyB;CACvB,AAAgB;CAChB,AAAO;CACP,AAAO;CAEP,cAAc;AACZ,OAAK,UAAU,IAAI,QAAQ,CAAC,SAAS,WAAW;AAC9C,QAAK,UAAU;AACf,QAAK,SAAS;EACf;CACF;AACF;;;;ACvFD,MAAM,kBAAkB;;;;AAwBxB,eAAsB,iBACpBC,OACAC,MACAC,WACAC,SACmB;AACnB,MAAK,QACH,OAAM,IAAI,MAAM;AAElB,MAAK,UAAW,QAAO,QAAQ,OAAO,KAAK;CAE3C,MAAM,oBAAoB,IAAI;CAC9B,MAAM,IAAI,WAAW,MAAM,kBAAkB,OAAO,EAAE,UAAU;CAKhE,MAAM,EAAE,QAAQ,GAAG,oBAAoB,CAAC,KAAK,QAAQ,kBAAkB,MAAO,EAAC;AAC/E,KAAI;AACF,SAAO,MAAM,QAAQ,OAAO;GAC1B,GAAG;GACH;EACD,EAAC;CACH,UAAS;AACR,eAAa,EAAE;CAChB;AACF;;;;AAKD,eAAsB,yBAAyBC,UAAoBC,SAAgC;AACjG,KAAI,SAAS,WAAW,IACtB,OAAM,IAAI,aAAa;EACrB,UAAU,aAAa,QAAQ;EAC/B,MAAM,iBAAiB;CACxB;AAGH,KAAI,SAAS,WAAW,IACtB,OAAM,IAAI,aAAa;EACrB,UAAU,uBAAuB,QAAQ;EACzC,MAAM,iBAAiB;CACxB;AAGH,KAAI,SAAS,WAAW,IACtB,OAAM,IAAI,aAAa;EACrB,UAAU,oBAAoB,QAAQ;EACtC,MAAM,iBAAiB;CACxB;AAGH,MAAK,SAAS,IAAI;EAChB,IAAIC;AACJ,MAAI;AACF,UAAO,MAAM,SAAS,MAAM;EAC7B,QAAO;AACN,UAAO;EACR;EAED,MAAM,OACJ,SAAS,UAAU,MACf,iBAAiB,cACjB,SAAS,UAAU,MACjB,iBAAiB,cACjB,iBAAiB;AAEzB,QAAM,IAAI,aAAa;GACrB,UAAU,mCAAmC,QAAQ,KAAK,SAAS,OAAO,GAAG,SAAS,WAAW,KAAK,KAAK;GAC3G;EACD;CACF;AACF;;;;;;;AAQD,gBAAuB,SAASC,QAAmD;CACjF,MAAM,kBAAkB,IAAI;CAC5B,MAAM,EAAE,QAAQ,gBAAgB,GAAG,OAAO,SACtC,oBAAoB,CAAC,OAAO,QAAQ,gBAAgB,MAAO,EAAC,GAC5D;EAAE,QAAQ,gBAAgB;EAAQ,gBAAgB,MAAM,CAAE;CAAE;AAEhE,KAAI;EACF,MAAM,MAAM,MAAM,iBAChB,OAAO,KACP;GACE,QAAQ,OAAO,UAAU;GACzB,SAAS;IAAE,QAAQ;IAAqB,GAAI,OAAO,WAAW,CAAE;GAAG;GACnE,MAAM,OAAO;GACb;EACD,GACD,OAAO,WACP,OAAO,QACR;AAED,QAAM,yBAAyB,MAAM,MAAM,OAAO,IAAI,EAAE;EACxD,MAAM,sBAAsB,IAAI,QAAQ,IAAI,eAAe,IAAI;AAE/D,OAAK,oBAAoB,SAAS,oBAAoB,CACpD,OAAM,IAAI,aAAa;GACrB,UAAU,mCAAmC,oBAAoB;GACjE,MAAM,iBAAiB;EACxB;AAGH,OAAK,IAAI,KACP,OAAM,IAAI,aAAa;GACrB,UAAU,sBAAsB,OAAO,IAAI;GAC3C,MAAM,iBAAiB;EACxB;AAGH,MAAI,OAAO,UACT,QAAO,WAAW;EAGpB,MAAM,UAAU,IAAI,KAAK,YAAY,IAAI,oBAAoB;EAC7D,MAAM,SAAS,QAAQ,WAAW;EAElC,IAAI,SAAS;AAEb,MAAI;AACF,UAAO,MAAM;IACX,MAAM,EAAE,OAAO,MAAM,GAAG,MAAM,OAAO,MAAM;AAC3C,QAAI,KAAM;AACV,cAAU;IAGV,MAAM,SAAS,OAAO,MAAM,aAAa;AACzC,aAAS,OAAO,KAAK,IAAI;AAEzB,SAAK,MAAM,SAAS,QAAQ;KAE1B,MAAMC,YAAsB,CAAE;KAC9B,IAAIC,UAAyB;AAE7B,UAAK,MAAM,WAAW,MAAM,MAAM,QAAQ,EAAE;AAC1C,WAAK,QAAS;AACd,UAAI,QAAQ,WAAW,IAAI,EAAE;AAE3B,iBAAU,QAAQ,MAAM,EAAE;AAC1B;MACD;AAED,UAAI,QAAQ,WAAW,gBAAgB,EAAE;OAEvC,MAAM,OAAO,QAAQ,MAAM,gBAAgB,OAAO,CAAC,QAAQ,OAAO,GAAG;AACrE,iBAAU,KAAK,KAAK;MACrB;KACF;AAED,SAAI,UAAU,QAAQ;MACpB,MAAM,OAAO,UAAU,KAAK,KAAK;AACjC,YAAM;OAAE,MAAM;OAAQ;MAAM;KAC7B,WAAU,YAAY,KACrB,OAAM;MAAE,MAAM;MAAW;KAAS;IAErC;GACF;EACF,UAAS;AACR,OAAI;AACF,UAAM,OAAO,QAAQ;GACtB,QAAO,CAEP;AACD,mBAAgB,OAAO;EACxB;CACF,UAAS;AACR,kBAAgB;CACjB;AACF;;;;ACrMD,MAAM,4CAA4C,OAAO,KAAK;CAC5D,eAAe;CACf,MAAM;AACP,EAAyD;;;;;AA2B1D,IAAa,uBAAb,MAA4D;CAC1D,AAAQ,uBAAuB,IAAI;;;;;CAMnC,OAAO,uBACLC,SACwC;EACxC,MAAM,EAAE,QAAQ,gBAAgB,GAAG,oBAAoB,CACrD,KAAK,qBAAqB,QAC1B,QAAQ,MACT,EAAC;AACF,MAAI;GACF,IAAI,iBAAiB;AACrB,WAAQ,OAAO,QACb,KAAI;AACF,eAAW,MAAM,SAAS,KAAK,2BAA2B;KACxD,GAAG;KACH;KACA,WAAW,MAAM;AACf,uBAAiB;KAClB;IACF,EAAC,CACA,OAAM;GAET,SAAQC,OAAgB;AACvB;IACA,MAAM,eAAe,KAAK,IAAI,QAAQ,eAAe,MAAM,iBAAiB,IAAI,IAAO;AACvF,SAAK,OAAO,SAAS;AACnB,aAAQ,OAAO,OACZ,8CAA8C,aAAa,QAC5D,MACD;AACD,WAAM,WAAW,aAAa;IAC/B;GACF;EAEJ,UAAS;AACR,mBAAgB;EACjB;CACF;CAED,OAAe,2BACbC,SACwC;EAExC,MAAM,4BAA4B,IAAI;EACtC,MAAM,EAAE,QAAQ,gBAAgB,gBAAgB,GAAG,QAAQ,SACvD,oBAAoB,CAAC,QAAQ,QAAQ,0BAA0B,MAAO,EAAC,GACvE;GAAE,QAAQ,0BAA0B;GAAQ,gBAAgB,MAAM,CAAE;EAAE;EAE1E,IAAIC,kBAAwD;EAE5D,MAAM,uBAAuB,MAAM;AACjC,OAAI,gBAAiB,cAAa,gBAAgB;AAClD,qBAAkB,WAAW,MAAM;AACjC,8BAA0B,OAAO;GAClC,GAAE,QAAQ,oBAAoB;EAChC;AAED,MAAI;GACF,MAAM,YAAY,SAAS;IACzB,SAAS,QAAQ;IACjB,SAAS;KACP,eAAe,KAAK,cAAc,QAAQ;KAC1C,gBAAgB;KAChB,cAAc,QAAQ;IACvB;IACD,MAAM,KAAK,UAAU,QAAQ,SAAS,CAAC;IACvC,WAAW,QAAQ;IACnB,QAAQ;IACR,QAAQ;IACR,KAAK,KAAK,gBAAgB,6BAA6B,QAAQ;IAC/D,WAAW,MAAM;AACf,2BAAsB;AACtB,aAAQ,aAAa;IACtB;GACF,EAAC;AAEF,cAAW,MAAM,YAAY,WAAW;AACtC,0BAAsB;AAEtB,QAAI,SAAS,SAAS,UAAW;IAEjC,MAAM,QAAQ,KAAK,MAAM,SAAS,KAAK;AACvC,eACS,UAAU,YACjB,UAAU,QACV,UAAU,gBACH,MAAM,SAAS,YACtB,AAAC,0CAAkE,SAAS,MAAM,KAAK,CAEvF,OAAM;GAET;EACF,UAAS;AACR,OAAI,gBAAiB,cAAa,gBAAgB;AAClD,mBAAgB;EACjB;CACF;;;;CAKD,aAAmB;AACjB,OAAK,qBAAqB,OAAO;AAEjC,OAAK,uBAAuB,IAAI;CACjC;CAED,AAAQ,cAAcC,SAAsC;AAC1D,UAAQ,SAAS,QAAQ,OAAO;CACjC;CAED,AAAQ,eAAeC,MAAcD,SAAsC;AACzE,UAAQ,EAAE,QAAQ,QAAQ,MAAM,KAAK;CACtC;AACF;;;;;;;;;;;;;;AChJD,SAAgB,QAAQE,OAAuB;CAE7C,MAAM,UAAU,IAAI;CACpB,MAAM,QAAQ,QAAQ,OAAO,MAAM;CAGnC,IAAI,OAAO;AAEX,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAQ,MAAM;AACd,SAAO,KAAK,KAAK,MAAM,SAAW,KAAK;CACxC;AAED,QAAO,SAAS;AACjB;;;;;;;;;;AAWD,SAAgB,cAAcA,OAAuB;CACnD,MAAM,IAAI,QAAQ,MAAM;AACxB,QAAO,IAAI,KAAK;AACjB;;;;;;;;;;;;;;ACnBD,SAAgB,kBACdC,WACAC,WACAC,SACAC,QACG;AAEH,MAAK,MAAM,YAAY,WAAW;EAEhC,IAAIC,iBAAmC;EACvC,MAAM,UAAU,SAAS,WAAW,IAAI,CAAC,MAAM,kBAAkB,GAAG,SAAS,OAAO,CAAC;AAErF,MAAI,QAAQ,KAAK,CAAC,MAAM,MAAM,cAAc,CAC1C,kBAAiB;WACR,QAAQ,KAAK,CAAC,MAAM,MAAM,UAAU,CAC7C,kBAAiB;AAInB,MAAI,mBAAmB,UACrB,QAAO,SAAS;CAEnB;AAED,QAAO;AACR;;;;;;;;;AAUD,SAAgB,kBACdC,WACAH,SACAC,QACkB;CAClB,MAAM,WAAW,UAAU;AAG3B,KAAI,aAAa,OAAO;EACtB,MAAM,UAAU,UAAU,WAAW,IAAI,CAAC,MAAM,kBAAkB,GAAG,SAAS,OAAO,CAAC;AAEtF,MAAI,QAAQ,KAAK,CAAC,MAAM,MAAM,cAAc,CAAE,QAAO;AACrD,MAAI,QAAQ,KAAK,CAAC,MAAM,MAAM,UAAU,CAAE,QAAO;AACjD,SAAO;CACR;AAED,KAAI,aAAa,MAAM;EACrB,MAAM,UAAU,UAAU,WAAW,IAAI,CAAC,MAAM,kBAAkB,GAAG,SAAS,OAAO,CAAC;AAEtF,MAAI,QAAQ,KAAK,CAAC,MAAM,MAAM,UAAU,CAAE,QAAO;AACjD,MAAI,QAAQ,KAAK,CAAC,MAAM,MAAM,UAAU,CAAE,QAAO;AACjD,SAAO;CACR;AAED,KAAI,aAAa,OAAO;EACtB,MAAM,SAAS,kBAAkB,UAAU,WAAW,SAAS,OAAO;AACtE,MAAI,WAAW,UAAW,QAAO;AACjC,MAAI,WAAW,cAAe,QAAO;AACrC,SAAO;CACR;AAGD,KAAI,aAAa,gBAAgB;EAC/B,MAAMG,iBAAe,QAAQ,UAAU;AACvC,MAAIA,6BAA8BA,mBAAiB,KACjD,QAAO;EAIT,MAAM,YAAY,OAAOA,eAAa,GAAG,UAAU;EACnD,MAAM,YAAY,cAAc,UAAU;AAC1C,SAAO,aAAa,UAAU,iBAAiB,OAAO,YAAY,UAAU,eAAe,MACvF,YACA;CACL;CAGD,MAAM,WAAW,UAAU;CAC3B,MAAM,eAAe,QAAQ;CAC7B,MAAM,gBAAgB,UAAU;AAEhC,KAAI,wBACF,QAAO;CAIT,MAAM,cAAc,kBAAkB,eAAe,aAAa;AAElE,SAAQ,UAAR;EACE,KAAK,SACH,QAAO,iBAAiB,cAAc,YAAY;EAEpD,KAAK;AACH,QAAK,MAAM,QAAQ,YAAY,CAAE,QAAO;AACxC,UAAO,YAAY,SAAS,aAAa,GAAG,YAAY;EAE1D,KAAK;AACH,QAAK,MAAM,QAAQ,YAAY,CAAE,QAAO;AACxC,WAAQ,YAAY,SAAS,aAAa,GAAG,YAAY;EAE3D,KAAK;AACH,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,eAAe,cAAc,YAAY;AAElD,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,eAAe,cAAc,YAAY;AAElD,UAAO;EAET,KAAK;AACH,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,gBAAgB,cAAc,YAAY;AAEnD,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,gBAAgB,cAAc,YAAY;AAEnD,UAAO;EAET,KAAK;AACH,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,eAAe,cAAc,YAAY;AAElD,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,eAAe,cAAc,YAAY;AAElD,UAAO;EAET,KAAK;AACH,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,gBAAgB,cAAc,YAAY;AAEnD,cAAW,iBAAiB,mBAAmB,gBAAgB,SAC7D,QAAO,gBAAgB,cAAc,YAAY;AAEnD,UAAO;EAET;AACE,aAAU,UAAU,SAAS,uBAAuB,SAAS,EAAE;AAC/D,UAAO;CACV;AACF;;;;AAKD,SAAS,UAAUC,OAAcJ,QAAuBK,SAAuB;AAC7E,QAAO,KAAK,SAAS,EAAE,MAAO,EAAC;AAChC;;;;;;;;;AAUD,SAAgB,kBAAkBC,eAAwBC,cAAgC;AACxF,YAAW,iBAAiB,UAAU;AACpC,aAAW,kBAAkB,UAAU;GACrC,MAAM,MAAM,OAAO,cAAc;AACjC,UAAO,MAAM,IAAI,GAAG,gBAAgB;EACrC;AACD,SAAO;CACR;AAED,YAAW,iBAAiB,WAAW;AACrC,aAAW,kBAAkB,UAAU;AACrC,OAAI,kBAAkB,OAAQ,QAAO;AACrC,OAAI,kBAAkB,QAAS,QAAO;EACvC;AACD,aAAW,kBAAkB,SAC3B,QAAO,kBAAkB;AAE3B,SAAO;CACR;AAED,YAAW,iBAAiB,UAAU;AACpC,aAAW,kBAAkB,mBAAmB,kBAAkB,UAChE,QAAO,OAAO,cAAc;AAE9B,SAAO;CACR;AAED,QAAO;AACR;;;;AChND,MAAa,UAAU;AACvB,MAAa,iBAAiB,iBAAiB,QAAQ;;;;;;;;;ACoBvD,MAAa,wBAAwB;AAMrC,SAAS,gBAAkCC,SAAuC;AAChF,QAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCD,IAAa,UAAb,MAAiE;CAC/D,YAAYC,UAA6B,CAAE,GAAE;AAC3C,kBAAgB,KAAK,CAAC,WAAW,IAAI,YAAe;CACrD;CACD,QAAQC,SAAwC;AAC9C,SAAO,gBAAgB,KAAK,CAAC,SAAS,QAAQ,QAAQ;CACvD;CACD,aAAmB;AACjB,kBAAgB,KAAK,CAAC,SAAS,YAAY;CAC5C;CACD,IAAuBC,YAAeC,SAAwC;AAC5E,SAAO,gBAAgB,KAAK,CAAC,SAAS,IAAI,YAAY,QAAQ;CAC/D;CACD,UACED,YACAE,UACY;AACZ,SAAO,gBAAgB,KAAK,CAAC,SAAS,UAAU,YAAY,SAAS;CACtE;CACD,cAAkC;AAChC,SAAO,gBAAgB,KAAK,CAAC,SAAS,aAAa;CACpD;AACF;AAMD,IAAM,cAAN,MAA8D;CAC5D,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,UAAuC;CAC/C,AAAQ,sBAAsB,IAAI;;;;;;;;;CAUlC,YAAYJ,UAA6B,CAAE,GAAE;AAC3C,OAAK,SAAS,QAAQ,UAAU;EAIhC,MAAMK,uBAAuC,GAC1C,wBAAwB,kBAAkB,CAC5C;AACD,OAAK,UAAU;GAAE,GAAG;GAAsB,GAAI,QAAQ,WAAW,CAAE;EAAG;EAGtE,MAAMC,iBAA8B,CAAE;AAGtC,MAAI,QAAQ,SACV,MAAK,MAAM,UAAU,QAAQ,SAAS,QACpC,gBAAe,KAAK;GAClB,MAAM,OAAO;GACb,OAAO,OAAO;GACd,WAAW,OAAO;EACnB,EAAC;AAKN,MAAI,QAAQ,UAAU;GACpB,MAAM,gBAAgB,IAAI,IAAI,eAAe,IAAI,CAAC,MAAM,EAAE,KAAK;AAC/D,QAAK,MAAM,CAAC,MAAM,MAAM,IAAI,OAAO,QAAQ,QAAQ,SAAS,CAC1D,KAAI,qBAAwB,cAAc,IAAI,KAAK,CACjD,gBAAe,KAAK;IAClB;IACA;IACA,WAAW,CAAE;GACd,EAAC;EAGP;AAED,OAAK,UAAU,IAAI,IAAI,eAAe,IAAI,CAAC,WAAW,CAAC,OAAO,MAAM,MAAO,EAAC;CAC7E;;;;;;;;;;;;;;;;;;;CAoBD,MAAM,QAAQL,SAAwC;AAEpD,OAAK,YAAY;EAEjB,MAAM,eAAe,KAAK,eAAe,QAAQ;AACjD,OAAK,UAAU,IAAI;EAEnB,MAAM,cAAc,IAAI;AAGxB,OAAK,eAAe,cAAc,YAAY;EAG9C,MAAM,YAAY,WAAW,MAAM;AACjC,QAAK,YAAY;AACjB,eAAY,OACV,IAAI,aAAa;IACf,SAAS;IACT,MAAM,iBAAiB;GACxB,GACF;EACF,GAAE,aAAa,iBAAiB;AAEjC,cAAY,QAAQ,QAAQ,MAAM,aAAa,UAAU,CAAC;AAE1D,QAAM,YAAY;CACnB;;;;;;;;CASD,aAAmB;AACjB,MAAI,KAAK,SAAS;AAChB,QAAK,QAAQ,YAAY;AACzB,QAAK,UAAU;EAChB;CACF;;;;;;;;;;;;;;;;;;;;;;;CAwBD,IAAuBC,YAAeK,UAAkC,CAAE,GAAQ;EAChF,MAAM,SAAS,KAAK,QAAQ,IAAI,OAAO,WAAW,CAAC;AAEnD,MAAI,mBAAsB;AACxB,OAAI,aAAa,QACf,QAAO,QAAQ;AAEjB,SAAM,IAAI,aAAa;IACrB,UAAU,oBAAoB,OAAO,WAAW,CAAC;IACjD,MAAM,iBAAiB;GACxB;EACF;AAED,MAAI;AACF,UAAO,kBACL,OAAO,OACP,OAAO,WACP;IAAE,GAAG,KAAK;IAAS,GAAI,SAAS,WAAW,CAAE;GAAG,GAChD,KAAK,OACN;EACF,SAAQ,OAAO;AACd,QAAK,OAAO,OACT,iDAAiD,OAAO,WAAW,CAAC,IACrE,MACD;AACD,UAAO,OAAO;EACf;CACF;;;;;;;;;;;;;;;CAgBD,UACEL,YACAE,UACY;EAGZ,MAAM,kBAAkB,CAACI,WAAyB;AAChD,YAAS,OAAmC;EAC7C;AAED,OAAK,KAAK,oBAAoB,IAAI,WAAW,CAC3C,MAAK,oBAAoB,IAAI,YAAY,IAAI,MAAM;AAErD,OAAK,oBAAoB,IAAI,WAAW,CAAE,IAAI,gBAAgB;AAE9D,SAAO,MAAM;AACX,QAAK,oBAAoB,IAAI,WAAW,EAAE,OAAO,gBAAgB;AACjE,OAAI,KAAK,oBAAoB,IAAI,WAAW,EAAE,SAAS,EACrD,MAAK,oBAAoB,OAAO,WAAW;EAE9C;CACF;;;;;;;;;;;;;;;;;;;CAoBD,cAAkC;AAChC,SAAO,EACL,SAAS,CAAC,GAAG,KAAK,QAAQ,QAAQ,AAAC,EAAC,IAAI,CAAC,YAAY;GACnD,MAAM,OAAO;GACb,OAAO,OAAO;GACd,WAAW,OAAO,UAAU,IAAI,CAAC,cAAc;IAC7C,MAAM,SAAS;IACf,YAAY,SAAS;IACrB,OAAO,SAAS;GACjB,GAAE;EACJ,GAAE,CACJ;CACF;;;;CAKD,IAAI,cAAuB;AACzB,SAAO,KAAK,YAAY;CACzB;CAED,MAAc,eACZC,SACAC,aACe;AACf,OAAK,KAAK,QAAS;AAEnB,MAAI;GACF,MAAM,oBAAoB,KAAK,QAAQ,uBAAuB;IAC5D,GAAG;IACH,QAAQ,KAAK;IACb,SAAS,OAAO;KACd,gBAAgB,CAAC,GAAG,KAAK,QAAQ,QAAQ,AAAC,EAAC,IAAI,CAAC,YAAY;MAC1D,MAAM,OAAO;MACb,WAAW,OAAO;MAClB,OAAO,OAAO;KACf,GAAE;KACH,iBAAiB,CAAE;IACpB;GACF,EAAC;AAEF,cAAW,MAAM,SAAS,mBAAmB;AAC3C,QAAI,MAAM,SAAS,QAAQ;AACzB,UAAK,qBAAqB,MAAM,QAAQ;AACxC,UAAK,OAAO,MAAM,4BAA4B,MAAM,QAAQ,OAAO,YAAY;IAChF,OAAM;AACL,UAAK,qBAAqB,CAAC,MAAM,MAAO,EAAC;AACzC,UAAK,OAAO,MAAM,mBAAmB,MAAM,OAAO,KAAK,WAAW;IACnE;AACD,gBAAY,SAAS;GACtB;EACF,SAAQ,OAAO;AACd,QAAK,OAAO,MAAM,qCAAqC,MAAM;AAC7D,eAAY,OAAO,MAAM;AACzB,SAAM;EACP;CACF;CAED,AAAQ,qBAAqBC,gBAAmC;AAC9D,OAAK,MAAM,UAAU,gBAAgB;AACnC,QAAK,QAAQ,IAAI,OAAO,MAAM;IAC5B,MAAM,OAAO;IACb,WAAW,OAAO;IAClB,OAAO,OAAO;GACf,EAAC;GAEF,MAAM,SAAS;IAAE,MAAM,OAAO;IAAiB,OAAO,OAAO;GAAqB;AAElF,QAAK,MAAM,YAAY,KAAK,oBAAoB,IAAI,OAAO,KAAgB,IAAI,CAAE,EAC/E,UAAS,OAAO;EAEnB;CACF;CAED,AAAQ,eAAeV,SAA8C;AACnE,SAAO;GACL,QAAQ,QAAQ;GAChB,SAAS,CAAC,QAAQ,WAAW,IAAI,QAAQ,QAAQ,GAAG;GACpD,SACE,QAAQ,WAER,WAAW,MAAM,KAAK,WAAW;GACnC,kBAAkB,QAAQ,oBAAoB;GAC9C,kBAAkB,QAAQ,oBAAoB;GAC9C,qBAAqB,QAAQ,uBAAuB;GACpD,cAAc,QAAQ,gBAAgB;GACtC,OAAO,QAAQ,SAAS;EACzB;CACF;AACF;;;;AC7XD,MAAM,cAAc,IAAI;AACxB,MAAM,qBAAqB,IAAI;AAE/B,SAAS,YAAYW,SAAiC;AACpD,SAAQ,EAAE,QAAQ,QAAQ,GAAG,QAAQ,OAAO;AAC7C;AAID,SAAS,oBAAoBC,UAAkBC,aAAgC;AAC7E,QAAO,WAAW,MAAM;EACtB,MAAM,SAAS,YAAY,IAAI,SAAS;AACxC,MAAI,QAAQ;AACV,UAAO,OAAO,YAAY;AAC1B,eAAY,OAAO,SAAS;EAC7B;CACF,GAAE,YAAY;AAChB;;;;;;;;;;;;;;;;AAiBD,eAAsB,mBACpBC,SAC6B;CAC7B,MAAM,EACJ,cAAc,KACd,QACA,SACA,UACA,YAAY,mBACb,GAAG;AAEJ,MAAK,kBACH,QAAO,EACL,SAAS,CACP,GAAG,OAAO,QAAQ,YAAY,CAAE,EAAC,CAAC,IAAI,CAAC,CAAC,MAAM,MAAM,MAAM;EACxD;EACA;EACA,WAAW,CAAE;CACd,GAAE,AACJ,EACF;CAGH,MAAM,WAAW,YAAY,kBAAkB;CAC/C,MAAM,SAAS,YAAY,IAAI,SAAS;AAGxC,KAAI,QAAQ;AACV,eAAa,OAAO,UAAU;AAC9B,SAAO,YAAY,oBAAoB,UAAU,YAAY;AAC7D,SAAO,OAAO,OAAO,aAAa;CACnC;CAGD,MAAM,UAAU,mBAAmB,IAAI,SAAS;AAChD,KAAI,SAAS;EACX,MAAMC,WAAS,MAAM,QAAQ;AAC7B,SAAO,SAAO,aAAa;CAC5B;CAGD,MAAM,SAAS,IAAI,QAAW;EAC5B;EACA;EACA;CACD;CAGD,MAAM,oBAAoB,OACvB,QAAQ,kBAAkB,CAC1B,KAAK,MAAM,OAAqC;AACnD,oBAAmB,IAAI,UAAU,EAAE,SAAS,kBAAmB,EAAC;AAEhE,KAAI;AACF,QAAM;EAGN,MAAMC,QAAsB;GAClB;GACR,WAAW,oBAAoB,UAAU,YAAY;EACtD;AACD,cAAY,IAAI,UAAU,MAAM;AAEhC,SAAO,OAAO,aAAa;CAC5B,UAAS;AACR,qBAAmB,OAAO,SAAS;CACpC;AACF;;;;ACnDD,SAAS,SACPC,QAC0B;AAC1B,QAAO;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCD,IAAa,kBAAb,MAAyE;CACvE,YAAYC,UAAqC,CAAE,GAAE;AACnD,WAAS,KAAK,CAAC,QAAQ,IAAI,oBAAuB;CACnD;;;;;;;;;;;CAYD,IAAuBC,YAAeC,SAAwC;AAC5E,SAAO,SAAS,KAAK,CAAC,MAAM,IAAI,YAAY,QAAQ;CACrD;;;;;;;;CASD,UACED,YACAE,UACY;AACZ,SAAO,SAAS,KAAK,CAAC,MAAM,UAAU,YAAY,SAAS;CAC5D;;;;;;CAOD,cAAkC;AAChC,SAAO,SAAS,KAAK,CAAC,MAAM,aAAa;CAC1C;;;;;;;;;;;;;;;;;;;;;CAsBD,IAAuBC,MAASC,OAAaC,SAAkC;AAC7E,WAAS,KAAK,CAAC,MAAM,IAAI,MAAM,OAAO,QAAQ;CAC/C;;;;;;;CAQD,OAA0BF,MAAkB;AAC1C,SAAO,SAAS,KAAK,CAAC,MAAM,OAAO,KAAK;CACzC;;;;CAKD,QAAc;AACZ,WAAS,KAAK,CAAC,MAAM,OAAO;CAC7B;;;;;;;CAQD,IAAuBA,MAAkB;AACvC,SAAO,SAAS,KAAK,CAAC,MAAM,IAAI,KAAK;CACtC;;;;;;CAOD,OAAoB;AAClB,SAAO,SAAS,KAAK,CAAC,MAAM,MAAM;CACnC;AACF;AAGD,IAAM,sBAAN,MAAsE;CACpE,AAAQ;CACR,AAAQ;CACR,AAAQ;CACR,AAAQ,sBAAsB,IAAI;CAElC,YAAYJ,UAAqC,CAAE,GAAE;AACnD,OAAK,SAAS,QAAQ,UAAU;AAChC,OAAK,UAAU,QAAQ,WAAW,CAAE;EAGpC,MAAMO,iBAA8B,CAAE;AAEtC,MAAI,QAAQ,UACV;QAAK,MAAM,CAAC,MAAM,MAAM,IAAI,OAAO,QAAQ,QAAQ,SAAS,CAC1D,KAAI,iBACF,gBAAe,KAAK;IAClB;IACA;IACA,WAAW,CAAE;GACd,EAAC;EAEL;AAGH,OAAK,UAAU,IAAI,IAAI,eAAe,IAAI,CAAC,WAAW,CAAC,OAAO,MAAM,MAAO,EAAC;CAC7E;CAED,IAAuBN,YAAeO,UAAkC,CAAE,GAAQ;EAChF,MAAM,SAAS,KAAK,QAAQ,IAAI,OAAO,WAAW,CAAC;AAEnD,MAAI,mBAAsB;AACxB,OAAI,aAAa,QACf,QAAO,QAAQ;AAEjB,SAAM,IAAI,aAAa;IACrB,UAAU,oBAAoB,OAAO,WAAW,CAAC;IACjD,MAAM,iBAAiB;GACxB;EACF;AAED,MAAI;AACF,UAAO,kBACL,OAAO,OACP,OAAO,WACP;IAAE,GAAG,KAAK;IAAS,GAAI,SAAS,WAAW,CAAE;GAAG,GAChD,KAAK,OACN;EACF,SAAQ,OAAO;AACd,QAAK,OAAO,OACT,iDAAiD,OAAO,WAAW,CAAC,IACrE,MACD;AACD,UAAO,OAAO;EACf;CACF;CAED,UACEP,YACAE,UACY;EACZ,MAAM,kBAAkB,CAACM,WAAyB;AAChD,YAAS,OAAmC;EAC7C;AAED,OAAK,KAAK,oBAAoB,IAAI,WAAW,CAC3C,MAAK,oBAAoB,IAAI,YAAY,IAAI,MAAM;AAErD,OAAK,oBAAoB,IAAI,WAAW,CAAE,IAAI,gBAAgB;AAE9D,SAAO,MAAM;AACX,QAAK,oBAAoB,IAAI,WAAW,EAAE,OAAO,gBAAgB;AACjE,OAAI,KAAK,oBAAoB,IAAI,WAAW,EAAE,SAAS,EACrD,MAAK,oBAAoB,OAAO,WAAW;EAE9C;CACF;CAED,cAAkC;AAChC,SAAO,EACL,SAAS,CAAC,GAAG,KAAK,QAAQ,QAAQ,AAAC,EAAC,IAAI,CAAC,YAAY;GACnD,MAAM,OAAO;GACb,OAAO,OAAO;GACd,WAAW,OAAO,UAAU,IAAI,CAAC,cAAc;IAC7C,MAAM,SAAS;IACf,YAAY,SAAS;IACrB,OAAO,SAAS;GACjB,GAAE;EACJ,GAAE,CACJ;CACF;CAED,IAAuBL,MAASC,OAAaC,SAAkC;EAC7E,MAAMI,YAAwB,SAAS,aAAa,CAAE;EAEtD,MAAMC,SAAoB;GACxB,MAAM,OAAO,KAAK;GAClB;GACA;EACD;AAED,OAAK,QAAQ,IAAI,OAAO,KAAK,EAAE,OAAO;AAGtC,OAAK,kBAAkB,MAAM,MAAM;CACpC;CAED,OAA0BP,MAAkB;EAC1C,MAAM,UAAU,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC;AAC9C,OAAK,QAAQ,OAAO,OAAO,KAAK,CAAC;AACjC,SAAO;CACR;CAED,QAAc;AACZ,OAAK,QAAQ,OAAO;CACrB;CAED,IAAuBA,MAAkB;AACvC,SAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC;CACtC;CAED,OAAoB;AAClB,SAAO,CAAC,GAAG,KAAK,QAAQ,MAAM,AAAC;CAChC;CAED,AAAQ,kBAAqCA,MAASC,OAAmB;EACvE,MAAM,SAAS;GAAE;GAAM;EAAO;AAC9B,OAAK,MAAM,YAAY,KAAK,oBAAoB,IAAI,KAAK,IAAI,CAAE,EAC7D,KAAI;AACF,YAAS,OAAuB;EACjC,SAAQ,OAAO;AACd,QAAK,OAAO,OAAO,8CAA8C,OAAO,KAAK,CAAC,IAAI,MAAM;EACzF;CAEJ;AACF"}
|