@rolloutctrl/js-sdk 0.0.1
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/dist/index.d.mts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +382 -0
- package/dist/index.mjs +353 -0
- package/package.json +40 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { FeatureFlagEnvironment, Action, EvaluationContext, EvaluationResult } from '@rolloutctrl/evaluator';
|
|
2
|
+
|
|
3
|
+
interface FlagConfig extends FeatureFlagEnvironment {
|
|
4
|
+
key: string;
|
|
5
|
+
}
|
|
6
|
+
interface Configuration {
|
|
7
|
+
projectId: string;
|
|
8
|
+
environmentId: string;
|
|
9
|
+
version: number;
|
|
10
|
+
flags: FlagConfig[];
|
|
11
|
+
actions: Action[];
|
|
12
|
+
}
|
|
13
|
+
type VariantResult = EvaluationResult['variant'];
|
|
14
|
+
interface Evaluator {
|
|
15
|
+
isEnabled(flagKey: string, context?: EvaluationContext): boolean;
|
|
16
|
+
evaluate(flagKey: string, context?: EvaluationContext): EvaluationResult;
|
|
17
|
+
getVariant(flagKey: string, context?: EvaluationContext): VariantResult;
|
|
18
|
+
can(actionKey: string, context?: EvaluationContext): boolean;
|
|
19
|
+
}
|
|
20
|
+
interface StorageAdapter {
|
|
21
|
+
get(): Promise<Configuration | null>;
|
|
22
|
+
set(configuration: Configuration): Promise<void>;
|
|
23
|
+
clear(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
interface RolloutCtrlOptions {
|
|
26
|
+
sdkKey: string;
|
|
27
|
+
apiUrl?: string;
|
|
28
|
+
refreshInterval?: number;
|
|
29
|
+
requestTimeout?: number;
|
|
30
|
+
bootstrap?: Configuration;
|
|
31
|
+
storage?: StorageAdapter;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ClientEventMap = {
|
|
35
|
+
ready: [];
|
|
36
|
+
updated: [configuration: Configuration];
|
|
37
|
+
error: [error: Error];
|
|
38
|
+
shutdown: [];
|
|
39
|
+
};
|
|
40
|
+
declare class RolloutCtrlClient {
|
|
41
|
+
private readonly emitter;
|
|
42
|
+
private readonly repository;
|
|
43
|
+
private readonly evaluatorManager;
|
|
44
|
+
private readonly updateProvider;
|
|
45
|
+
private readonly subscribers;
|
|
46
|
+
private isInitialized;
|
|
47
|
+
private readonly readyPromise;
|
|
48
|
+
private resolveReady;
|
|
49
|
+
private rejectReady;
|
|
50
|
+
constructor(options: RolloutCtrlOptions);
|
|
51
|
+
private initialize;
|
|
52
|
+
on<K extends keyof ClientEventMap>(event: K, listener: (...args: ClientEventMap[K]) => void): this;
|
|
53
|
+
off<K extends keyof ClientEventMap>(event: K, listener: (...args: ClientEventMap[K]) => void): this;
|
|
54
|
+
ready(): Promise<void>;
|
|
55
|
+
close(): Promise<void>;
|
|
56
|
+
subscribe(listener: () => void): () => void;
|
|
57
|
+
getVersion(): number;
|
|
58
|
+
getConfiguration(): Configuration;
|
|
59
|
+
getEvaluator(): Evaluator;
|
|
60
|
+
isEnabled(flagKey: string, context?: EvaluationContext): boolean;
|
|
61
|
+
evaluate(flagKey: string, context?: EvaluationContext): EvaluationResult;
|
|
62
|
+
getVariant(flagKey: string, context?: EvaluationContext): VariantResult;
|
|
63
|
+
can(actionKey: string, context?: EvaluationContext): boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare function createEvaluator(configuration: Configuration): Evaluator;
|
|
67
|
+
declare class EvaluatorManager {
|
|
68
|
+
private evaluator;
|
|
69
|
+
update(configuration: Configuration): void;
|
|
70
|
+
get(): Evaluator;
|
|
71
|
+
isReady(): boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface ConfigurationRepository {
|
|
75
|
+
initialize(): Promise<void>;
|
|
76
|
+
initializeWith(configuration: Configuration): void;
|
|
77
|
+
refresh(): Promise<boolean>;
|
|
78
|
+
getVersion(): number;
|
|
79
|
+
getConfiguration(): Configuration;
|
|
80
|
+
subscribe(listener: (configuration: Configuration) => void): () => void;
|
|
81
|
+
}
|
|
82
|
+
declare class HttpConfigurationRepository implements ConfigurationRepository {
|
|
83
|
+
private readonly apiUrl;
|
|
84
|
+
private readonly sdkKey;
|
|
85
|
+
private readonly requestTimeout;
|
|
86
|
+
private configuration;
|
|
87
|
+
private version;
|
|
88
|
+
private readonly listeners;
|
|
89
|
+
constructor(apiUrl: string, sdkKey: string, requestTimeout: number);
|
|
90
|
+
initialize(): Promise<void>;
|
|
91
|
+
initializeWith(configuration: Configuration): void;
|
|
92
|
+
refresh(): Promise<boolean>;
|
|
93
|
+
getVersion(): number;
|
|
94
|
+
getConfiguration(): Configuration;
|
|
95
|
+
subscribe(listener: (configuration: Configuration) => void): () => void;
|
|
96
|
+
private notify;
|
|
97
|
+
private fetchBootstrap;
|
|
98
|
+
private fetchVersion;
|
|
99
|
+
private request;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface UpdateProvider {
|
|
103
|
+
start(onRefresh: () => void): void;
|
|
104
|
+
stop(): void;
|
|
105
|
+
}
|
|
106
|
+
declare class PollingUpdateProvider implements UpdateProvider {
|
|
107
|
+
private readonly interval;
|
|
108
|
+
private timer;
|
|
109
|
+
constructor(interval: number);
|
|
110
|
+
start(onRefresh: () => void): void;
|
|
111
|
+
stop(): void;
|
|
112
|
+
}
|
|
113
|
+
declare class SseUpdateProvider implements UpdateProvider {
|
|
114
|
+
private readonly apiUrl;
|
|
115
|
+
private readonly sdkKey;
|
|
116
|
+
private eventSource;
|
|
117
|
+
constructor(apiUrl: string, sdkKey: string);
|
|
118
|
+
start(onRefresh: () => void): void;
|
|
119
|
+
stop(): void;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare class LocalStorageAdapter implements StorageAdapter {
|
|
123
|
+
private readonly key;
|
|
124
|
+
constructor(key?: string);
|
|
125
|
+
get(): Promise<Configuration | null>;
|
|
126
|
+
set(configuration: Configuration): Promise<void>;
|
|
127
|
+
clear(): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { type Configuration, type ConfigurationRepository, type Evaluator, EvaluatorManager, type FlagConfig, HttpConfigurationRepository, LocalStorageAdapter, PollingUpdateProvider, RolloutCtrlClient, type RolloutCtrlOptions, SseUpdateProvider, type StorageAdapter, type UpdateProvider, type VariantResult, createEvaluator };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { FeatureFlagEnvironment, Action, EvaluationContext, EvaluationResult } from '@rolloutctrl/evaluator';
|
|
2
|
+
|
|
3
|
+
interface FlagConfig extends FeatureFlagEnvironment {
|
|
4
|
+
key: string;
|
|
5
|
+
}
|
|
6
|
+
interface Configuration {
|
|
7
|
+
projectId: string;
|
|
8
|
+
environmentId: string;
|
|
9
|
+
version: number;
|
|
10
|
+
flags: FlagConfig[];
|
|
11
|
+
actions: Action[];
|
|
12
|
+
}
|
|
13
|
+
type VariantResult = EvaluationResult['variant'];
|
|
14
|
+
interface Evaluator {
|
|
15
|
+
isEnabled(flagKey: string, context?: EvaluationContext): boolean;
|
|
16
|
+
evaluate(flagKey: string, context?: EvaluationContext): EvaluationResult;
|
|
17
|
+
getVariant(flagKey: string, context?: EvaluationContext): VariantResult;
|
|
18
|
+
can(actionKey: string, context?: EvaluationContext): boolean;
|
|
19
|
+
}
|
|
20
|
+
interface StorageAdapter {
|
|
21
|
+
get(): Promise<Configuration | null>;
|
|
22
|
+
set(configuration: Configuration): Promise<void>;
|
|
23
|
+
clear(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
interface RolloutCtrlOptions {
|
|
26
|
+
sdkKey: string;
|
|
27
|
+
apiUrl?: string;
|
|
28
|
+
refreshInterval?: number;
|
|
29
|
+
requestTimeout?: number;
|
|
30
|
+
bootstrap?: Configuration;
|
|
31
|
+
storage?: StorageAdapter;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ClientEventMap = {
|
|
35
|
+
ready: [];
|
|
36
|
+
updated: [configuration: Configuration];
|
|
37
|
+
error: [error: Error];
|
|
38
|
+
shutdown: [];
|
|
39
|
+
};
|
|
40
|
+
declare class RolloutCtrlClient {
|
|
41
|
+
private readonly emitter;
|
|
42
|
+
private readonly repository;
|
|
43
|
+
private readonly evaluatorManager;
|
|
44
|
+
private readonly updateProvider;
|
|
45
|
+
private readonly subscribers;
|
|
46
|
+
private isInitialized;
|
|
47
|
+
private readonly readyPromise;
|
|
48
|
+
private resolveReady;
|
|
49
|
+
private rejectReady;
|
|
50
|
+
constructor(options: RolloutCtrlOptions);
|
|
51
|
+
private initialize;
|
|
52
|
+
on<K extends keyof ClientEventMap>(event: K, listener: (...args: ClientEventMap[K]) => void): this;
|
|
53
|
+
off<K extends keyof ClientEventMap>(event: K, listener: (...args: ClientEventMap[K]) => void): this;
|
|
54
|
+
ready(): Promise<void>;
|
|
55
|
+
close(): Promise<void>;
|
|
56
|
+
subscribe(listener: () => void): () => void;
|
|
57
|
+
getVersion(): number;
|
|
58
|
+
getConfiguration(): Configuration;
|
|
59
|
+
getEvaluator(): Evaluator;
|
|
60
|
+
isEnabled(flagKey: string, context?: EvaluationContext): boolean;
|
|
61
|
+
evaluate(flagKey: string, context?: EvaluationContext): EvaluationResult;
|
|
62
|
+
getVariant(flagKey: string, context?: EvaluationContext): VariantResult;
|
|
63
|
+
can(actionKey: string, context?: EvaluationContext): boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare function createEvaluator(configuration: Configuration): Evaluator;
|
|
67
|
+
declare class EvaluatorManager {
|
|
68
|
+
private evaluator;
|
|
69
|
+
update(configuration: Configuration): void;
|
|
70
|
+
get(): Evaluator;
|
|
71
|
+
isReady(): boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface ConfigurationRepository {
|
|
75
|
+
initialize(): Promise<void>;
|
|
76
|
+
initializeWith(configuration: Configuration): void;
|
|
77
|
+
refresh(): Promise<boolean>;
|
|
78
|
+
getVersion(): number;
|
|
79
|
+
getConfiguration(): Configuration;
|
|
80
|
+
subscribe(listener: (configuration: Configuration) => void): () => void;
|
|
81
|
+
}
|
|
82
|
+
declare class HttpConfigurationRepository implements ConfigurationRepository {
|
|
83
|
+
private readonly apiUrl;
|
|
84
|
+
private readonly sdkKey;
|
|
85
|
+
private readonly requestTimeout;
|
|
86
|
+
private configuration;
|
|
87
|
+
private version;
|
|
88
|
+
private readonly listeners;
|
|
89
|
+
constructor(apiUrl: string, sdkKey: string, requestTimeout: number);
|
|
90
|
+
initialize(): Promise<void>;
|
|
91
|
+
initializeWith(configuration: Configuration): void;
|
|
92
|
+
refresh(): Promise<boolean>;
|
|
93
|
+
getVersion(): number;
|
|
94
|
+
getConfiguration(): Configuration;
|
|
95
|
+
subscribe(listener: (configuration: Configuration) => void): () => void;
|
|
96
|
+
private notify;
|
|
97
|
+
private fetchBootstrap;
|
|
98
|
+
private fetchVersion;
|
|
99
|
+
private request;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
interface UpdateProvider {
|
|
103
|
+
start(onRefresh: () => void): void;
|
|
104
|
+
stop(): void;
|
|
105
|
+
}
|
|
106
|
+
declare class PollingUpdateProvider implements UpdateProvider {
|
|
107
|
+
private readonly interval;
|
|
108
|
+
private timer;
|
|
109
|
+
constructor(interval: number);
|
|
110
|
+
start(onRefresh: () => void): void;
|
|
111
|
+
stop(): void;
|
|
112
|
+
}
|
|
113
|
+
declare class SseUpdateProvider implements UpdateProvider {
|
|
114
|
+
private readonly apiUrl;
|
|
115
|
+
private readonly sdkKey;
|
|
116
|
+
private eventSource;
|
|
117
|
+
constructor(apiUrl: string, sdkKey: string);
|
|
118
|
+
start(onRefresh: () => void): void;
|
|
119
|
+
stop(): void;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare class LocalStorageAdapter implements StorageAdapter {
|
|
123
|
+
private readonly key;
|
|
124
|
+
constructor(key?: string);
|
|
125
|
+
get(): Promise<Configuration | null>;
|
|
126
|
+
set(configuration: Configuration): Promise<void>;
|
|
127
|
+
clear(): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { type Configuration, type ConfigurationRepository, type Evaluator, EvaluatorManager, type FlagConfig, HttpConfigurationRepository, LocalStorageAdapter, PollingUpdateProvider, RolloutCtrlClient, type RolloutCtrlOptions, SseUpdateProvider, type StorageAdapter, type UpdateProvider, type VariantResult, createEvaluator };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
EvaluatorManager: () => EvaluatorManager,
|
|
24
|
+
HttpConfigurationRepository: () => HttpConfigurationRepository,
|
|
25
|
+
LocalStorageAdapter: () => LocalStorageAdapter,
|
|
26
|
+
PollingUpdateProvider: () => PollingUpdateProvider,
|
|
27
|
+
RolloutCtrlClient: () => RolloutCtrlClient,
|
|
28
|
+
SseUpdateProvider: () => SseUpdateProvider,
|
|
29
|
+
createEvaluator: () => createEvaluator
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/emitter.ts
|
|
34
|
+
var TinyEmitter = class {
|
|
35
|
+
constructor() {
|
|
36
|
+
this._listeners = /* @__PURE__ */ new Map();
|
|
37
|
+
}
|
|
38
|
+
on(event, listener) {
|
|
39
|
+
let set = this._listeners.get(event);
|
|
40
|
+
if (!set) {
|
|
41
|
+
set = /* @__PURE__ */ new Set();
|
|
42
|
+
this._listeners.set(event, set);
|
|
43
|
+
}
|
|
44
|
+
set.add(listener);
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
off(event, listener) {
|
|
48
|
+
this._listeners.get(event)?.delete(listener);
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
emit(event, ...args) {
|
|
52
|
+
const listeners = this._listeners.get(event);
|
|
53
|
+
if (listeners) {
|
|
54
|
+
for (const listener of listeners) {
|
|
55
|
+
listener(...args);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
removeAllListeners() {
|
|
60
|
+
this._listeners.clear();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// src/evaluator.ts
|
|
65
|
+
var import_evaluator = require("@rolloutctrl/evaluator");
|
|
66
|
+
function createEvaluator(configuration) {
|
|
67
|
+
const flagIndex = new Map(configuration.flags.map((f) => [f.key, f]));
|
|
68
|
+
const actionIndex = new Map(configuration.actions.map((a) => [a.key, a]));
|
|
69
|
+
return {
|
|
70
|
+
isEnabled(flagKey, context = {}) {
|
|
71
|
+
const flag = flagIndex.get(flagKey);
|
|
72
|
+
if (!flag) return false;
|
|
73
|
+
return (0, import_evaluator.evaluateFeatureFlag)(flag, context).enabled;
|
|
74
|
+
},
|
|
75
|
+
evaluate(flagKey, context = {}) {
|
|
76
|
+
const flag = flagIndex.get(flagKey);
|
|
77
|
+
if (!flag) {
|
|
78
|
+
return { enabled: false, reason: import_evaluator.EvaluationReason.DISABLED_FLAG };
|
|
79
|
+
}
|
|
80
|
+
return (0, import_evaluator.evaluateFeatureFlag)(flag, context);
|
|
81
|
+
},
|
|
82
|
+
getVariant(flagKey, context = {}) {
|
|
83
|
+
const flag = flagIndex.get(flagKey);
|
|
84
|
+
if (!flag) return void 0;
|
|
85
|
+
return (0, import_evaluator.evaluateFeatureFlag)(flag, context).variant;
|
|
86
|
+
},
|
|
87
|
+
can(actionKey, context = {}) {
|
|
88
|
+
const action = actionIndex.get(actionKey);
|
|
89
|
+
if (!action) return false;
|
|
90
|
+
return (0, import_evaluator.evaluateAction)(action, context).effect === "ALLOW";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
var EvaluatorManager = class {
|
|
95
|
+
constructor() {
|
|
96
|
+
this.evaluator = null;
|
|
97
|
+
}
|
|
98
|
+
update(configuration) {
|
|
99
|
+
this.evaluator = createEvaluator(configuration);
|
|
100
|
+
}
|
|
101
|
+
get() {
|
|
102
|
+
if (!this.evaluator) {
|
|
103
|
+
throw new Error("RolloutCtrlClient is not ready. Await client.ready() first.");
|
|
104
|
+
}
|
|
105
|
+
return this.evaluator;
|
|
106
|
+
}
|
|
107
|
+
isReady() {
|
|
108
|
+
return this.evaluator !== null;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/repository.ts
|
|
113
|
+
var HttpConfigurationRepository = class {
|
|
114
|
+
constructor(apiUrl, sdkKey, requestTimeout) {
|
|
115
|
+
this.apiUrl = apiUrl;
|
|
116
|
+
this.sdkKey = sdkKey;
|
|
117
|
+
this.requestTimeout = requestTimeout;
|
|
118
|
+
this.configuration = null;
|
|
119
|
+
this.version = 0;
|
|
120
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
121
|
+
}
|
|
122
|
+
async initialize() {
|
|
123
|
+
const config = await this.fetchBootstrap();
|
|
124
|
+
this.configuration = config;
|
|
125
|
+
this.version = config.version;
|
|
126
|
+
this.notify(config);
|
|
127
|
+
}
|
|
128
|
+
initializeWith(configuration) {
|
|
129
|
+
this.configuration = configuration;
|
|
130
|
+
this.version = configuration.version;
|
|
131
|
+
this.notify(configuration);
|
|
132
|
+
}
|
|
133
|
+
async refresh() {
|
|
134
|
+
try {
|
|
135
|
+
const versionResponse = await this.fetchVersion();
|
|
136
|
+
if (versionResponse.version === this.version) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
const config = await this.fetchBootstrap();
|
|
140
|
+
this.configuration = config;
|
|
141
|
+
this.version = config.version;
|
|
142
|
+
this.notify(config);
|
|
143
|
+
return true;
|
|
144
|
+
} catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
getVersion() {
|
|
149
|
+
return this.version;
|
|
150
|
+
}
|
|
151
|
+
getConfiguration() {
|
|
152
|
+
if (!this.configuration) {
|
|
153
|
+
throw new Error("Configuration not initialized. Call initialize() first.");
|
|
154
|
+
}
|
|
155
|
+
return this.configuration;
|
|
156
|
+
}
|
|
157
|
+
subscribe(listener) {
|
|
158
|
+
this.listeners.add(listener);
|
|
159
|
+
return () => {
|
|
160
|
+
this.listeners.delete(listener);
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
notify(configuration) {
|
|
164
|
+
for (const listener of this.listeners) {
|
|
165
|
+
listener(configuration);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async fetchBootstrap() {
|
|
169
|
+
return this.request(`${this.apiUrl}/sdk/bootstrap`);
|
|
170
|
+
}
|
|
171
|
+
async fetchVersion() {
|
|
172
|
+
return this.request(`${this.apiUrl}/sdk/version`);
|
|
173
|
+
}
|
|
174
|
+
async request(url) {
|
|
175
|
+
const controller = new AbortController();
|
|
176
|
+
const timeout = setTimeout(() => controller.abort(), this.requestTimeout);
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetch(url, {
|
|
179
|
+
headers: {
|
|
180
|
+
Authorization: `Bearer ${this.sdkKey}`,
|
|
181
|
+
"Content-Type": "application/json"
|
|
182
|
+
},
|
|
183
|
+
signal: controller.signal
|
|
184
|
+
});
|
|
185
|
+
if (!response.ok) {
|
|
186
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
187
|
+
}
|
|
188
|
+
return response.json();
|
|
189
|
+
} finally {
|
|
190
|
+
clearTimeout(timeout);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// src/update-provider.ts
|
|
196
|
+
var PollingUpdateProvider = class {
|
|
197
|
+
constructor(interval) {
|
|
198
|
+
this.interval = interval;
|
|
199
|
+
this.timer = null;
|
|
200
|
+
}
|
|
201
|
+
start(onRefresh) {
|
|
202
|
+
this.stop();
|
|
203
|
+
this.timer = setInterval(onRefresh, this.interval);
|
|
204
|
+
}
|
|
205
|
+
stop() {
|
|
206
|
+
if (this.timer !== null) {
|
|
207
|
+
clearInterval(this.timer);
|
|
208
|
+
this.timer = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var SseUpdateProvider = class {
|
|
213
|
+
constructor(apiUrl, sdkKey) {
|
|
214
|
+
this.apiUrl = apiUrl;
|
|
215
|
+
this.sdkKey = sdkKey;
|
|
216
|
+
this.eventSource = null;
|
|
217
|
+
}
|
|
218
|
+
start(onRefresh) {
|
|
219
|
+
this.stop();
|
|
220
|
+
if (typeof EventSource === "undefined") {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const url = `${this.apiUrl}/sdk/stream?key=${encodeURIComponent(this.sdkKey)}`;
|
|
224
|
+
this.eventSource = new EventSource(url);
|
|
225
|
+
this.eventSource.addEventListener("configuration.updated", () => {
|
|
226
|
+
onRefresh();
|
|
227
|
+
});
|
|
228
|
+
this.eventSource.onerror = () => {
|
|
229
|
+
this.stop();
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
stop() {
|
|
233
|
+
if (this.eventSource !== null) {
|
|
234
|
+
this.eventSource.close();
|
|
235
|
+
this.eventSource = null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
// src/client.ts
|
|
241
|
+
var DEFAULT_API_URL = "https://rolloutctrl.io/api";
|
|
242
|
+
var DEFAULT_REFRESH_INTERVAL = 3e4;
|
|
243
|
+
var DEFAULT_REQUEST_TIMEOUT = 5e3;
|
|
244
|
+
var RolloutCtrlClient = class {
|
|
245
|
+
constructor(options) {
|
|
246
|
+
this.emitter = new TinyEmitter();
|
|
247
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
248
|
+
this.isInitialized = false;
|
|
249
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
250
|
+
const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL;
|
|
251
|
+
const requestTimeout = options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT;
|
|
252
|
+
this.repository = new HttpConfigurationRepository(apiUrl, options.sdkKey, requestTimeout);
|
|
253
|
+
this.evaluatorManager = new EvaluatorManager();
|
|
254
|
+
this.updateProvider = new PollingUpdateProvider(refreshInterval);
|
|
255
|
+
this.repository.subscribe((configuration) => {
|
|
256
|
+
this.evaluatorManager.update(configuration);
|
|
257
|
+
options.storage?.set(configuration).catch(() => {
|
|
258
|
+
});
|
|
259
|
+
if (this.isInitialized) {
|
|
260
|
+
for (const listener of this.subscribers) {
|
|
261
|
+
listener();
|
|
262
|
+
}
|
|
263
|
+
this.emitter.emit("updated", configuration);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
267
|
+
this.resolveReady = resolve;
|
|
268
|
+
this.rejectReady = reject;
|
|
269
|
+
});
|
|
270
|
+
void this.initialize(options);
|
|
271
|
+
}
|
|
272
|
+
async initialize(options) {
|
|
273
|
+
try {
|
|
274
|
+
if (options.bootstrap) {
|
|
275
|
+
this.repository.initializeWith(options.bootstrap);
|
|
276
|
+
} else if (options.storage) {
|
|
277
|
+
const cached = await options.storage.get();
|
|
278
|
+
if (cached) {
|
|
279
|
+
this.repository.initializeWith(cached);
|
|
280
|
+
void this.repository.refresh();
|
|
281
|
+
} else {
|
|
282
|
+
await this.repository.initialize();
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
await this.repository.initialize();
|
|
286
|
+
}
|
|
287
|
+
this.isInitialized = true;
|
|
288
|
+
this.updateProvider.start(() => {
|
|
289
|
+
void this.repository.refresh();
|
|
290
|
+
});
|
|
291
|
+
this.emitter.emit("ready");
|
|
292
|
+
this.resolveReady();
|
|
293
|
+
} catch (error) {
|
|
294
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
295
|
+
this.emitter.emit("error", err);
|
|
296
|
+
this.rejectReady(err);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
on(event, listener) {
|
|
300
|
+
this.emitter.on(event, listener);
|
|
301
|
+
return this;
|
|
302
|
+
}
|
|
303
|
+
off(event, listener) {
|
|
304
|
+
this.emitter.off(event, listener);
|
|
305
|
+
return this;
|
|
306
|
+
}
|
|
307
|
+
ready() {
|
|
308
|
+
return this.readyPromise;
|
|
309
|
+
}
|
|
310
|
+
async close() {
|
|
311
|
+
this.updateProvider.stop();
|
|
312
|
+
this.subscribers.clear();
|
|
313
|
+
this.emitter.emit("shutdown");
|
|
314
|
+
this.emitter.removeAllListeners();
|
|
315
|
+
}
|
|
316
|
+
subscribe(listener) {
|
|
317
|
+
this.subscribers.add(listener);
|
|
318
|
+
return () => {
|
|
319
|
+
this.subscribers.delete(listener);
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
getVersion() {
|
|
323
|
+
return this.repository.getVersion();
|
|
324
|
+
}
|
|
325
|
+
getConfiguration() {
|
|
326
|
+
return this.repository.getConfiguration();
|
|
327
|
+
}
|
|
328
|
+
getEvaluator() {
|
|
329
|
+
return this.evaluatorManager.get();
|
|
330
|
+
}
|
|
331
|
+
isEnabled(flagKey, context) {
|
|
332
|
+
return this.getEvaluator().isEnabled(flagKey, context);
|
|
333
|
+
}
|
|
334
|
+
evaluate(flagKey, context) {
|
|
335
|
+
return this.getEvaluator().evaluate(flagKey, context);
|
|
336
|
+
}
|
|
337
|
+
getVariant(flagKey, context) {
|
|
338
|
+
return this.getEvaluator().getVariant(flagKey, context);
|
|
339
|
+
}
|
|
340
|
+
can(actionKey, context) {
|
|
341
|
+
return this.getEvaluator().can(actionKey, context);
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// src/storage.ts
|
|
346
|
+
var DEFAULT_STORAGE_KEY = "rolloutctrl:config";
|
|
347
|
+
var LocalStorageAdapter = class {
|
|
348
|
+
constructor(key = DEFAULT_STORAGE_KEY) {
|
|
349
|
+
this.key = key;
|
|
350
|
+
}
|
|
351
|
+
async get() {
|
|
352
|
+
if (typeof localStorage === "undefined") return null;
|
|
353
|
+
try {
|
|
354
|
+
const raw = localStorage.getItem(this.key);
|
|
355
|
+
if (!raw) return null;
|
|
356
|
+
return JSON.parse(raw);
|
|
357
|
+
} catch {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async set(configuration) {
|
|
362
|
+
if (typeof localStorage === "undefined") return;
|
|
363
|
+
try {
|
|
364
|
+
localStorage.setItem(this.key, JSON.stringify(configuration));
|
|
365
|
+
} catch {
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async clear() {
|
|
369
|
+
if (typeof localStorage === "undefined") return;
|
|
370
|
+
localStorage.removeItem(this.key);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
374
|
+
0 && (module.exports = {
|
|
375
|
+
EvaluatorManager,
|
|
376
|
+
HttpConfigurationRepository,
|
|
377
|
+
LocalStorageAdapter,
|
|
378
|
+
PollingUpdateProvider,
|
|
379
|
+
RolloutCtrlClient,
|
|
380
|
+
SseUpdateProvider,
|
|
381
|
+
createEvaluator
|
|
382
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
// src/emitter.ts
|
|
2
|
+
var TinyEmitter = class {
|
|
3
|
+
constructor() {
|
|
4
|
+
this._listeners = /* @__PURE__ */ new Map();
|
|
5
|
+
}
|
|
6
|
+
on(event, listener) {
|
|
7
|
+
let set = this._listeners.get(event);
|
|
8
|
+
if (!set) {
|
|
9
|
+
set = /* @__PURE__ */ new Set();
|
|
10
|
+
this._listeners.set(event, set);
|
|
11
|
+
}
|
|
12
|
+
set.add(listener);
|
|
13
|
+
return this;
|
|
14
|
+
}
|
|
15
|
+
off(event, listener) {
|
|
16
|
+
this._listeners.get(event)?.delete(listener);
|
|
17
|
+
return this;
|
|
18
|
+
}
|
|
19
|
+
emit(event, ...args) {
|
|
20
|
+
const listeners = this._listeners.get(event);
|
|
21
|
+
if (listeners) {
|
|
22
|
+
for (const listener of listeners) {
|
|
23
|
+
listener(...args);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
removeAllListeners() {
|
|
28
|
+
this._listeners.clear();
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/evaluator.ts
|
|
33
|
+
import {
|
|
34
|
+
evaluateFeatureFlag,
|
|
35
|
+
evaluateAction,
|
|
36
|
+
EvaluationReason
|
|
37
|
+
} from "@rolloutctrl/evaluator";
|
|
38
|
+
function createEvaluator(configuration) {
|
|
39
|
+
const flagIndex = new Map(configuration.flags.map((f) => [f.key, f]));
|
|
40
|
+
const actionIndex = new Map(configuration.actions.map((a) => [a.key, a]));
|
|
41
|
+
return {
|
|
42
|
+
isEnabled(flagKey, context = {}) {
|
|
43
|
+
const flag = flagIndex.get(flagKey);
|
|
44
|
+
if (!flag) return false;
|
|
45
|
+
return evaluateFeatureFlag(flag, context).enabled;
|
|
46
|
+
},
|
|
47
|
+
evaluate(flagKey, context = {}) {
|
|
48
|
+
const flag = flagIndex.get(flagKey);
|
|
49
|
+
if (!flag) {
|
|
50
|
+
return { enabled: false, reason: EvaluationReason.DISABLED_FLAG };
|
|
51
|
+
}
|
|
52
|
+
return evaluateFeatureFlag(flag, context);
|
|
53
|
+
},
|
|
54
|
+
getVariant(flagKey, context = {}) {
|
|
55
|
+
const flag = flagIndex.get(flagKey);
|
|
56
|
+
if (!flag) return void 0;
|
|
57
|
+
return evaluateFeatureFlag(flag, context).variant;
|
|
58
|
+
},
|
|
59
|
+
can(actionKey, context = {}) {
|
|
60
|
+
const action = actionIndex.get(actionKey);
|
|
61
|
+
if (!action) return false;
|
|
62
|
+
return evaluateAction(action, context).effect === "ALLOW";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
var EvaluatorManager = class {
|
|
67
|
+
constructor() {
|
|
68
|
+
this.evaluator = null;
|
|
69
|
+
}
|
|
70
|
+
update(configuration) {
|
|
71
|
+
this.evaluator = createEvaluator(configuration);
|
|
72
|
+
}
|
|
73
|
+
get() {
|
|
74
|
+
if (!this.evaluator) {
|
|
75
|
+
throw new Error("RolloutCtrlClient is not ready. Await client.ready() first.");
|
|
76
|
+
}
|
|
77
|
+
return this.evaluator;
|
|
78
|
+
}
|
|
79
|
+
isReady() {
|
|
80
|
+
return this.evaluator !== null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/repository.ts
|
|
85
|
+
var HttpConfigurationRepository = class {
|
|
86
|
+
constructor(apiUrl, sdkKey, requestTimeout) {
|
|
87
|
+
this.apiUrl = apiUrl;
|
|
88
|
+
this.sdkKey = sdkKey;
|
|
89
|
+
this.requestTimeout = requestTimeout;
|
|
90
|
+
this.configuration = null;
|
|
91
|
+
this.version = 0;
|
|
92
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
93
|
+
}
|
|
94
|
+
async initialize() {
|
|
95
|
+
const config = await this.fetchBootstrap();
|
|
96
|
+
this.configuration = config;
|
|
97
|
+
this.version = config.version;
|
|
98
|
+
this.notify(config);
|
|
99
|
+
}
|
|
100
|
+
initializeWith(configuration) {
|
|
101
|
+
this.configuration = configuration;
|
|
102
|
+
this.version = configuration.version;
|
|
103
|
+
this.notify(configuration);
|
|
104
|
+
}
|
|
105
|
+
async refresh() {
|
|
106
|
+
try {
|
|
107
|
+
const versionResponse = await this.fetchVersion();
|
|
108
|
+
if (versionResponse.version === this.version) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const config = await this.fetchBootstrap();
|
|
112
|
+
this.configuration = config;
|
|
113
|
+
this.version = config.version;
|
|
114
|
+
this.notify(config);
|
|
115
|
+
return true;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
getVersion() {
|
|
121
|
+
return this.version;
|
|
122
|
+
}
|
|
123
|
+
getConfiguration() {
|
|
124
|
+
if (!this.configuration) {
|
|
125
|
+
throw new Error("Configuration not initialized. Call initialize() first.");
|
|
126
|
+
}
|
|
127
|
+
return this.configuration;
|
|
128
|
+
}
|
|
129
|
+
subscribe(listener) {
|
|
130
|
+
this.listeners.add(listener);
|
|
131
|
+
return () => {
|
|
132
|
+
this.listeners.delete(listener);
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
notify(configuration) {
|
|
136
|
+
for (const listener of this.listeners) {
|
|
137
|
+
listener(configuration);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async fetchBootstrap() {
|
|
141
|
+
return this.request(`${this.apiUrl}/sdk/bootstrap`);
|
|
142
|
+
}
|
|
143
|
+
async fetchVersion() {
|
|
144
|
+
return this.request(`${this.apiUrl}/sdk/version`);
|
|
145
|
+
}
|
|
146
|
+
async request(url) {
|
|
147
|
+
const controller = new AbortController();
|
|
148
|
+
const timeout = setTimeout(() => controller.abort(), this.requestTimeout);
|
|
149
|
+
try {
|
|
150
|
+
const response = await fetch(url, {
|
|
151
|
+
headers: {
|
|
152
|
+
Authorization: `Bearer ${this.sdkKey}`,
|
|
153
|
+
"Content-Type": "application/json"
|
|
154
|
+
},
|
|
155
|
+
signal: controller.signal
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
159
|
+
}
|
|
160
|
+
return response.json();
|
|
161
|
+
} finally {
|
|
162
|
+
clearTimeout(timeout);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// src/update-provider.ts
|
|
168
|
+
var PollingUpdateProvider = class {
|
|
169
|
+
constructor(interval) {
|
|
170
|
+
this.interval = interval;
|
|
171
|
+
this.timer = null;
|
|
172
|
+
}
|
|
173
|
+
start(onRefresh) {
|
|
174
|
+
this.stop();
|
|
175
|
+
this.timer = setInterval(onRefresh, this.interval);
|
|
176
|
+
}
|
|
177
|
+
stop() {
|
|
178
|
+
if (this.timer !== null) {
|
|
179
|
+
clearInterval(this.timer);
|
|
180
|
+
this.timer = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
var SseUpdateProvider = class {
|
|
185
|
+
constructor(apiUrl, sdkKey) {
|
|
186
|
+
this.apiUrl = apiUrl;
|
|
187
|
+
this.sdkKey = sdkKey;
|
|
188
|
+
this.eventSource = null;
|
|
189
|
+
}
|
|
190
|
+
start(onRefresh) {
|
|
191
|
+
this.stop();
|
|
192
|
+
if (typeof EventSource === "undefined") {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const url = `${this.apiUrl}/sdk/stream?key=${encodeURIComponent(this.sdkKey)}`;
|
|
196
|
+
this.eventSource = new EventSource(url);
|
|
197
|
+
this.eventSource.addEventListener("configuration.updated", () => {
|
|
198
|
+
onRefresh();
|
|
199
|
+
});
|
|
200
|
+
this.eventSource.onerror = () => {
|
|
201
|
+
this.stop();
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
stop() {
|
|
205
|
+
if (this.eventSource !== null) {
|
|
206
|
+
this.eventSource.close();
|
|
207
|
+
this.eventSource = null;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/client.ts
|
|
213
|
+
var DEFAULT_API_URL = "https://rolloutctrl.io/api";
|
|
214
|
+
var DEFAULT_REFRESH_INTERVAL = 3e4;
|
|
215
|
+
var DEFAULT_REQUEST_TIMEOUT = 5e3;
|
|
216
|
+
var RolloutCtrlClient = class {
|
|
217
|
+
constructor(options) {
|
|
218
|
+
this.emitter = new TinyEmitter();
|
|
219
|
+
this.subscribers = /* @__PURE__ */ new Set();
|
|
220
|
+
this.isInitialized = false;
|
|
221
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
222
|
+
const refreshInterval = options.refreshInterval ?? DEFAULT_REFRESH_INTERVAL;
|
|
223
|
+
const requestTimeout = options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT;
|
|
224
|
+
this.repository = new HttpConfigurationRepository(apiUrl, options.sdkKey, requestTimeout);
|
|
225
|
+
this.evaluatorManager = new EvaluatorManager();
|
|
226
|
+
this.updateProvider = new PollingUpdateProvider(refreshInterval);
|
|
227
|
+
this.repository.subscribe((configuration) => {
|
|
228
|
+
this.evaluatorManager.update(configuration);
|
|
229
|
+
options.storage?.set(configuration).catch(() => {
|
|
230
|
+
});
|
|
231
|
+
if (this.isInitialized) {
|
|
232
|
+
for (const listener of this.subscribers) {
|
|
233
|
+
listener();
|
|
234
|
+
}
|
|
235
|
+
this.emitter.emit("updated", configuration);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
239
|
+
this.resolveReady = resolve;
|
|
240
|
+
this.rejectReady = reject;
|
|
241
|
+
});
|
|
242
|
+
void this.initialize(options);
|
|
243
|
+
}
|
|
244
|
+
async initialize(options) {
|
|
245
|
+
try {
|
|
246
|
+
if (options.bootstrap) {
|
|
247
|
+
this.repository.initializeWith(options.bootstrap);
|
|
248
|
+
} else if (options.storage) {
|
|
249
|
+
const cached = await options.storage.get();
|
|
250
|
+
if (cached) {
|
|
251
|
+
this.repository.initializeWith(cached);
|
|
252
|
+
void this.repository.refresh();
|
|
253
|
+
} else {
|
|
254
|
+
await this.repository.initialize();
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
await this.repository.initialize();
|
|
258
|
+
}
|
|
259
|
+
this.isInitialized = true;
|
|
260
|
+
this.updateProvider.start(() => {
|
|
261
|
+
void this.repository.refresh();
|
|
262
|
+
});
|
|
263
|
+
this.emitter.emit("ready");
|
|
264
|
+
this.resolveReady();
|
|
265
|
+
} catch (error) {
|
|
266
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
267
|
+
this.emitter.emit("error", err);
|
|
268
|
+
this.rejectReady(err);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
on(event, listener) {
|
|
272
|
+
this.emitter.on(event, listener);
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
275
|
+
off(event, listener) {
|
|
276
|
+
this.emitter.off(event, listener);
|
|
277
|
+
return this;
|
|
278
|
+
}
|
|
279
|
+
ready() {
|
|
280
|
+
return this.readyPromise;
|
|
281
|
+
}
|
|
282
|
+
async close() {
|
|
283
|
+
this.updateProvider.stop();
|
|
284
|
+
this.subscribers.clear();
|
|
285
|
+
this.emitter.emit("shutdown");
|
|
286
|
+
this.emitter.removeAllListeners();
|
|
287
|
+
}
|
|
288
|
+
subscribe(listener) {
|
|
289
|
+
this.subscribers.add(listener);
|
|
290
|
+
return () => {
|
|
291
|
+
this.subscribers.delete(listener);
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
getVersion() {
|
|
295
|
+
return this.repository.getVersion();
|
|
296
|
+
}
|
|
297
|
+
getConfiguration() {
|
|
298
|
+
return this.repository.getConfiguration();
|
|
299
|
+
}
|
|
300
|
+
getEvaluator() {
|
|
301
|
+
return this.evaluatorManager.get();
|
|
302
|
+
}
|
|
303
|
+
isEnabled(flagKey, context) {
|
|
304
|
+
return this.getEvaluator().isEnabled(flagKey, context);
|
|
305
|
+
}
|
|
306
|
+
evaluate(flagKey, context) {
|
|
307
|
+
return this.getEvaluator().evaluate(flagKey, context);
|
|
308
|
+
}
|
|
309
|
+
getVariant(flagKey, context) {
|
|
310
|
+
return this.getEvaluator().getVariant(flagKey, context);
|
|
311
|
+
}
|
|
312
|
+
can(actionKey, context) {
|
|
313
|
+
return this.getEvaluator().can(actionKey, context);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/storage.ts
|
|
318
|
+
var DEFAULT_STORAGE_KEY = "rolloutctrl:config";
|
|
319
|
+
var LocalStorageAdapter = class {
|
|
320
|
+
constructor(key = DEFAULT_STORAGE_KEY) {
|
|
321
|
+
this.key = key;
|
|
322
|
+
}
|
|
323
|
+
async get() {
|
|
324
|
+
if (typeof localStorage === "undefined") return null;
|
|
325
|
+
try {
|
|
326
|
+
const raw = localStorage.getItem(this.key);
|
|
327
|
+
if (!raw) return null;
|
|
328
|
+
return JSON.parse(raw);
|
|
329
|
+
} catch {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async set(configuration) {
|
|
334
|
+
if (typeof localStorage === "undefined") return;
|
|
335
|
+
try {
|
|
336
|
+
localStorage.setItem(this.key, JSON.stringify(configuration));
|
|
337
|
+
} catch {
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
async clear() {
|
|
341
|
+
if (typeof localStorage === "undefined") return;
|
|
342
|
+
localStorage.removeItem(this.key);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
export {
|
|
346
|
+
EvaluatorManager,
|
|
347
|
+
HttpConfigurationRepository,
|
|
348
|
+
LocalStorageAdapter,
|
|
349
|
+
PollingUpdateProvider,
|
|
350
|
+
RolloutCtrlClient,
|
|
351
|
+
SseUpdateProvider,
|
|
352
|
+
createEvaluator
|
|
353
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rolloutctrl/js-sdk",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "RolloutCtrl JavaScript SDK for browser applications",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean",
|
|
21
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
22
|
+
"lint": "tsc --noEmit",
|
|
23
|
+
"test": "jest",
|
|
24
|
+
"test:watch": "jest --watch"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@rolloutctrl/evaluator": "workspace:*"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/jest": "^29.5.0",
|
|
31
|
+
"jest": "^29.7.0",
|
|
32
|
+
"jest-environment-jsdom": "^29.7.0",
|
|
33
|
+
"ts-jest": "^29.1.0",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5.4.0"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
}
|
|
40
|
+
}
|