@getflaggy/sdk 0.1.0
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/chunk-HBGNXCEV.mjs +281 -0
- package/dist/chunk-HBGNXCEV.mjs.map +1 -0
- package/dist/index.cjs +307 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +80 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.mjs +7 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react.cjs +397 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +24 -0
- package/dist/react.d.ts +24 -0
- package/dist/react.mjs +95 -0
- package/dist/react.mjs.map +1 -0
- package/package.json +62 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Possible types a flag value can take */
|
|
2
|
+
type FlagValue = boolean | string | number | Record<string, unknown>;
|
|
3
|
+
/** Evaluation context sent to the server */
|
|
4
|
+
type FlaggyContext = Record<string, unknown>;
|
|
5
|
+
/** A single evaluated flag result from the server */
|
|
6
|
+
interface EvaluatedFlag {
|
|
7
|
+
flag_key: string;
|
|
8
|
+
value: FlagValue;
|
|
9
|
+
match: boolean;
|
|
10
|
+
reason: string;
|
|
11
|
+
}
|
|
12
|
+
/** Response from POST /api/v1/evaluate */
|
|
13
|
+
interface EvaluateResponse {
|
|
14
|
+
flag_key: string;
|
|
15
|
+
value: FlagValue;
|
|
16
|
+
match: boolean;
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
/** Response from POST /api/v1/evaluate/batch */
|
|
20
|
+
interface BatchEvaluateResponse {
|
|
21
|
+
results: EvaluatedFlag[];
|
|
22
|
+
}
|
|
23
|
+
/** SSE event data for flag changes */
|
|
24
|
+
interface FlagChangeEvent {
|
|
25
|
+
type: 'flag_updated' | 'flag_deleted' | 'flag_created';
|
|
26
|
+
key: string;
|
|
27
|
+
}
|
|
28
|
+
/** Configuration for FlaggyClient */
|
|
29
|
+
interface FlaggyClientOptions {
|
|
30
|
+
serverUrl: string;
|
|
31
|
+
apiKey: string;
|
|
32
|
+
context?: FlaggyContext;
|
|
33
|
+
/** Whether to open an SSE connection for live updates. Default: true */
|
|
34
|
+
enableStreaming?: boolean;
|
|
35
|
+
/** Initial retry delay for SSE reconnection in ms. Default: 1000 */
|
|
36
|
+
sseRetryDelay?: number;
|
|
37
|
+
/** Max retry delay for SSE reconnection in ms. Default: 30000 */
|
|
38
|
+
sseMaxRetryDelay?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Listener for flag value changes */
|
|
41
|
+
type FlagChangeListener = (key: string, value: FlagValue) => void;
|
|
42
|
+
/** Listener for readiness state */
|
|
43
|
+
type ReadyListener = () => void;
|
|
44
|
+
/** Listener for errors */
|
|
45
|
+
type ErrorListener = (error: Error) => void;
|
|
46
|
+
|
|
47
|
+
type EventMap = {
|
|
48
|
+
change: FlagChangeListener;
|
|
49
|
+
ready: ReadyListener;
|
|
50
|
+
error: ErrorListener;
|
|
51
|
+
};
|
|
52
|
+
declare class FlaggyClient {
|
|
53
|
+
private readonly serverUrl;
|
|
54
|
+
private readonly apiKey;
|
|
55
|
+
private readonly enableStreaming;
|
|
56
|
+
private readonly sseRetryDelay;
|
|
57
|
+
private readonly sseMaxRetryDelay;
|
|
58
|
+
private context;
|
|
59
|
+
private cache;
|
|
60
|
+
private _ready;
|
|
61
|
+
private _error;
|
|
62
|
+
private sseManager;
|
|
63
|
+
private contextAbortController;
|
|
64
|
+
private listeners;
|
|
65
|
+
constructor(options: FlaggyClientOptions);
|
|
66
|
+
get ready(): boolean;
|
|
67
|
+
get error(): Error | null;
|
|
68
|
+
initialize(): Promise<void>;
|
|
69
|
+
getFlag<T extends FlagValue>(key: string, defaultValue: T): T;
|
|
70
|
+
setContext(context: FlaggyContext): Promise<void>;
|
|
71
|
+
on<E extends keyof EventMap>(event: E, listener: EventMap[E]): () => void;
|
|
72
|
+
destroy(): void;
|
|
73
|
+
private startSSE;
|
|
74
|
+
private handleSSEEvent;
|
|
75
|
+
private applyBatchResult;
|
|
76
|
+
private emit;
|
|
77
|
+
private fetchApi;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { type BatchEvaluateResponse, type ErrorListener, type EvaluateResponse, type EvaluatedFlag, type FlagChangeEvent, type FlagChangeListener, type FlagValue, FlaggyClient, type FlaggyClientOptions, type FlaggyContext, type ReadyListener };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/** Possible types a flag value can take */
|
|
2
|
+
type FlagValue = boolean | string | number | Record<string, unknown>;
|
|
3
|
+
/** Evaluation context sent to the server */
|
|
4
|
+
type FlaggyContext = Record<string, unknown>;
|
|
5
|
+
/** A single evaluated flag result from the server */
|
|
6
|
+
interface EvaluatedFlag {
|
|
7
|
+
flag_key: string;
|
|
8
|
+
value: FlagValue;
|
|
9
|
+
match: boolean;
|
|
10
|
+
reason: string;
|
|
11
|
+
}
|
|
12
|
+
/** Response from POST /api/v1/evaluate */
|
|
13
|
+
interface EvaluateResponse {
|
|
14
|
+
flag_key: string;
|
|
15
|
+
value: FlagValue;
|
|
16
|
+
match: boolean;
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
/** Response from POST /api/v1/evaluate/batch */
|
|
20
|
+
interface BatchEvaluateResponse {
|
|
21
|
+
results: EvaluatedFlag[];
|
|
22
|
+
}
|
|
23
|
+
/** SSE event data for flag changes */
|
|
24
|
+
interface FlagChangeEvent {
|
|
25
|
+
type: 'flag_updated' | 'flag_deleted' | 'flag_created';
|
|
26
|
+
key: string;
|
|
27
|
+
}
|
|
28
|
+
/** Configuration for FlaggyClient */
|
|
29
|
+
interface FlaggyClientOptions {
|
|
30
|
+
serverUrl: string;
|
|
31
|
+
apiKey: string;
|
|
32
|
+
context?: FlaggyContext;
|
|
33
|
+
/** Whether to open an SSE connection for live updates. Default: true */
|
|
34
|
+
enableStreaming?: boolean;
|
|
35
|
+
/** Initial retry delay for SSE reconnection in ms. Default: 1000 */
|
|
36
|
+
sseRetryDelay?: number;
|
|
37
|
+
/** Max retry delay for SSE reconnection in ms. Default: 30000 */
|
|
38
|
+
sseMaxRetryDelay?: number;
|
|
39
|
+
}
|
|
40
|
+
/** Listener for flag value changes */
|
|
41
|
+
type FlagChangeListener = (key: string, value: FlagValue) => void;
|
|
42
|
+
/** Listener for readiness state */
|
|
43
|
+
type ReadyListener = () => void;
|
|
44
|
+
/** Listener for errors */
|
|
45
|
+
type ErrorListener = (error: Error) => void;
|
|
46
|
+
|
|
47
|
+
type EventMap = {
|
|
48
|
+
change: FlagChangeListener;
|
|
49
|
+
ready: ReadyListener;
|
|
50
|
+
error: ErrorListener;
|
|
51
|
+
};
|
|
52
|
+
declare class FlaggyClient {
|
|
53
|
+
private readonly serverUrl;
|
|
54
|
+
private readonly apiKey;
|
|
55
|
+
private readonly enableStreaming;
|
|
56
|
+
private readonly sseRetryDelay;
|
|
57
|
+
private readonly sseMaxRetryDelay;
|
|
58
|
+
private context;
|
|
59
|
+
private cache;
|
|
60
|
+
private _ready;
|
|
61
|
+
private _error;
|
|
62
|
+
private sseManager;
|
|
63
|
+
private contextAbortController;
|
|
64
|
+
private listeners;
|
|
65
|
+
constructor(options: FlaggyClientOptions);
|
|
66
|
+
get ready(): boolean;
|
|
67
|
+
get error(): Error | null;
|
|
68
|
+
initialize(): Promise<void>;
|
|
69
|
+
getFlag<T extends FlagValue>(key: string, defaultValue: T): T;
|
|
70
|
+
setContext(context: FlaggyContext): Promise<void>;
|
|
71
|
+
on<E extends keyof EventMap>(event: E, listener: EventMap[E]): () => void;
|
|
72
|
+
destroy(): void;
|
|
73
|
+
private startSSE;
|
|
74
|
+
private handleSSEEvent;
|
|
75
|
+
private applyBatchResult;
|
|
76
|
+
private emit;
|
|
77
|
+
private fetchApi;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export { type BatchEvaluateResponse, type ErrorListener, type EvaluateResponse, type EvaluatedFlag, type FlagChangeEvent, type FlagChangeListener, type FlagValue, FlaggyClient, type FlaggyClientOptions, type FlaggyContext, type ReadyListener };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/react.cjs
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
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/react/index.ts
|
|
21
|
+
var react_exports = {};
|
|
22
|
+
__export(react_exports, {
|
|
23
|
+
FlaggyProvider: () => FlaggyProvider,
|
|
24
|
+
useFlag: () => useFlag,
|
|
25
|
+
useFlaggy: () => useFlaggy
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(react_exports);
|
|
28
|
+
|
|
29
|
+
// src/react/FlaggyProvider.tsx
|
|
30
|
+
var import_react2 = require("react");
|
|
31
|
+
|
|
32
|
+
// src/sse.ts
|
|
33
|
+
var SSEManager = class {
|
|
34
|
+
constructor(options) {
|
|
35
|
+
this.abortController = null;
|
|
36
|
+
this.retryCount = 0;
|
|
37
|
+
this.retryTimeout = null;
|
|
38
|
+
this.destroyed = false;
|
|
39
|
+
this.url = options.url;
|
|
40
|
+
this.apiKey = options.apiKey;
|
|
41
|
+
this.onEvent = options.onEvent;
|
|
42
|
+
this.onError = options.onError;
|
|
43
|
+
this.retryDelay = options.retryDelay ?? 1e3;
|
|
44
|
+
this.maxRetryDelay = options.maxRetryDelay ?? 3e4;
|
|
45
|
+
}
|
|
46
|
+
connect() {
|
|
47
|
+
if (this.destroyed) return;
|
|
48
|
+
this.abortController = new AbortController();
|
|
49
|
+
fetch(this.url, {
|
|
50
|
+
headers: {
|
|
51
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
52
|
+
Accept: "text/event-stream"
|
|
53
|
+
},
|
|
54
|
+
signal: this.abortController.signal
|
|
55
|
+
}).then((response) => {
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
throw new Error(`SSE connection failed: ${response.status}`);
|
|
58
|
+
}
|
|
59
|
+
if (!response.body) {
|
|
60
|
+
throw new Error("SSE response has no body");
|
|
61
|
+
}
|
|
62
|
+
this.retryCount = 0;
|
|
63
|
+
this.readStream(response.body);
|
|
64
|
+
}).catch((err) => {
|
|
65
|
+
if (this.destroyed) return;
|
|
66
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
67
|
+
this.onError(err instanceof Error ? err : new Error(String(err)));
|
|
68
|
+
this.reconnect();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
destroy() {
|
|
72
|
+
this.destroyed = true;
|
|
73
|
+
this.abortController?.abort();
|
|
74
|
+
this.abortController = null;
|
|
75
|
+
if (this.retryTimeout) {
|
|
76
|
+
clearTimeout(this.retryTimeout);
|
|
77
|
+
this.retryTimeout = null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async readStream(body) {
|
|
81
|
+
const reader = body.getReader();
|
|
82
|
+
const decoder = new TextDecoder();
|
|
83
|
+
let buffer = "";
|
|
84
|
+
let currentEvent = "";
|
|
85
|
+
let currentData = "";
|
|
86
|
+
try {
|
|
87
|
+
while (true) {
|
|
88
|
+
const { done, value } = await reader.read();
|
|
89
|
+
if (done) break;
|
|
90
|
+
buffer += decoder.decode(value, { stream: true });
|
|
91
|
+
const lines = buffer.split("\n");
|
|
92
|
+
buffer = lines.pop() ?? "";
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
if (line.startsWith("event:")) {
|
|
95
|
+
currentEvent = line.slice(6).trim();
|
|
96
|
+
} else if (line.startsWith("data:")) {
|
|
97
|
+
currentData = line.slice(5).trim();
|
|
98
|
+
} else if (line === "") {
|
|
99
|
+
if (currentData) {
|
|
100
|
+
this.handleEvent(currentEvent, currentData);
|
|
101
|
+
}
|
|
102
|
+
currentEvent = "";
|
|
103
|
+
currentData = "";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
if (this.destroyed) return;
|
|
109
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
110
|
+
this.onError(err instanceof Error ? err : new Error(String(err)));
|
|
111
|
+
} finally {
|
|
112
|
+
reader.releaseLock();
|
|
113
|
+
}
|
|
114
|
+
if (!this.destroyed) {
|
|
115
|
+
this.reconnect();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
handleEvent(eventType, data) {
|
|
119
|
+
try {
|
|
120
|
+
const parsed = JSON.parse(data);
|
|
121
|
+
if (eventType && !parsed.type) {
|
|
122
|
+
parsed.type = eventType;
|
|
123
|
+
}
|
|
124
|
+
this.onEvent(parsed);
|
|
125
|
+
} catch {
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
reconnect() {
|
|
129
|
+
if (this.destroyed) return;
|
|
130
|
+
const delay = this.getBackoffDelay();
|
|
131
|
+
this.retryCount++;
|
|
132
|
+
this.retryTimeout = setTimeout(() => {
|
|
133
|
+
this.retryTimeout = null;
|
|
134
|
+
this.connect();
|
|
135
|
+
}, delay);
|
|
136
|
+
}
|
|
137
|
+
getBackoffDelay() {
|
|
138
|
+
const delay = this.retryDelay * Math.pow(2, this.retryCount);
|
|
139
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
140
|
+
return Math.min(delay + jitter, this.maxRetryDelay);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// src/client.ts
|
|
145
|
+
var FlaggyClient = class {
|
|
146
|
+
constructor(options) {
|
|
147
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
148
|
+
this._ready = false;
|
|
149
|
+
this._error = null;
|
|
150
|
+
this.sseManager = null;
|
|
151
|
+
this.contextAbortController = null;
|
|
152
|
+
this.listeners = {
|
|
153
|
+
change: /* @__PURE__ */ new Set(),
|
|
154
|
+
ready: /* @__PURE__ */ new Set(),
|
|
155
|
+
error: /* @__PURE__ */ new Set()
|
|
156
|
+
};
|
|
157
|
+
this.serverUrl = options.serverUrl.replace(/\/$/, "");
|
|
158
|
+
this.apiKey = options.apiKey;
|
|
159
|
+
this.context = options.context ?? {};
|
|
160
|
+
this.enableStreaming = options.enableStreaming ?? true;
|
|
161
|
+
this.sseRetryDelay = options.sseRetryDelay ?? 1e3;
|
|
162
|
+
this.sseMaxRetryDelay = options.sseMaxRetryDelay ?? 3e4;
|
|
163
|
+
}
|
|
164
|
+
get ready() {
|
|
165
|
+
return this._ready;
|
|
166
|
+
}
|
|
167
|
+
get error() {
|
|
168
|
+
return this._error;
|
|
169
|
+
}
|
|
170
|
+
async initialize() {
|
|
171
|
+
try {
|
|
172
|
+
const response = await this.fetchApi(
|
|
173
|
+
"/api/v1/evaluate/batch",
|
|
174
|
+
{ context: this.context }
|
|
175
|
+
);
|
|
176
|
+
this.applyBatchResult(response);
|
|
177
|
+
this._ready = true;
|
|
178
|
+
this.emit("ready");
|
|
179
|
+
} catch (err) {
|
|
180
|
+
this._error = err instanceof Error ? err : new Error(String(err));
|
|
181
|
+
this.emit("error", this._error);
|
|
182
|
+
}
|
|
183
|
+
if (this.enableStreaming) {
|
|
184
|
+
this.startSSE();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
getFlag(key, defaultValue) {
|
|
188
|
+
if (!this._ready || !this.cache.has(key)) {
|
|
189
|
+
return defaultValue;
|
|
190
|
+
}
|
|
191
|
+
return this.cache.get(key);
|
|
192
|
+
}
|
|
193
|
+
async setContext(context) {
|
|
194
|
+
this.context = context;
|
|
195
|
+
this.contextAbortController?.abort();
|
|
196
|
+
const controller = new AbortController();
|
|
197
|
+
this.contextAbortController = controller;
|
|
198
|
+
try {
|
|
199
|
+
const response = await this.fetchApi(
|
|
200
|
+
"/api/v1/evaluate/batch",
|
|
201
|
+
{ context },
|
|
202
|
+
controller.signal
|
|
203
|
+
);
|
|
204
|
+
if (controller.signal.aborted) return;
|
|
205
|
+
this.applyBatchResult(response);
|
|
206
|
+
} catch (err) {
|
|
207
|
+
if (err instanceof Error && err.name === "AbortError") return;
|
|
208
|
+
this._error = err instanceof Error ? err : new Error(String(err));
|
|
209
|
+
this.emit("error", this._error);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
on(event, listener) {
|
|
213
|
+
this.listeners[event].add(listener);
|
|
214
|
+
return () => {
|
|
215
|
+
this.listeners[event].delete(listener);
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
destroy() {
|
|
219
|
+
this.sseManager?.destroy();
|
|
220
|
+
this.sseManager = null;
|
|
221
|
+
this.contextAbortController?.abort();
|
|
222
|
+
this.contextAbortController = null;
|
|
223
|
+
this.listeners.change.clear();
|
|
224
|
+
this.listeners.ready.clear();
|
|
225
|
+
this.listeners.error.clear();
|
|
226
|
+
}
|
|
227
|
+
startSSE() {
|
|
228
|
+
this.sseManager = new SSEManager({
|
|
229
|
+
url: `${this.serverUrl}/api/v1/stream`,
|
|
230
|
+
apiKey: this.apiKey,
|
|
231
|
+
onEvent: (event) => this.handleSSEEvent(event),
|
|
232
|
+
onError: (err) => this.emit("error", err),
|
|
233
|
+
retryDelay: this.sseRetryDelay,
|
|
234
|
+
maxRetryDelay: this.sseMaxRetryDelay
|
|
235
|
+
});
|
|
236
|
+
this.sseManager.connect();
|
|
237
|
+
}
|
|
238
|
+
async handleSSEEvent(event) {
|
|
239
|
+
if (event.type === "flag_deleted") {
|
|
240
|
+
if (this.cache.has(event.key)) {
|
|
241
|
+
this.cache.delete(event.key);
|
|
242
|
+
this.emit("change", event.key, void 0);
|
|
243
|
+
}
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
try {
|
|
247
|
+
const response = await this.fetchApi(
|
|
248
|
+
"/api/v1/evaluate",
|
|
249
|
+
{ flag_key: event.key, context: this.context }
|
|
250
|
+
);
|
|
251
|
+
const oldValue = this.cache.get(event.key);
|
|
252
|
+
this.cache.set(event.key, response.value);
|
|
253
|
+
if (oldValue !== response.value) {
|
|
254
|
+
this.emit("change", event.key, response.value);
|
|
255
|
+
}
|
|
256
|
+
} catch {
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
applyBatchResult(response) {
|
|
260
|
+
const newCache = /* @__PURE__ */ new Map();
|
|
261
|
+
for (const flag of response.results) {
|
|
262
|
+
newCache.set(flag.flag_key, flag.value);
|
|
263
|
+
}
|
|
264
|
+
for (const [key, newValue] of newCache) {
|
|
265
|
+
const oldValue = this.cache.get(key);
|
|
266
|
+
if (oldValue !== newValue) {
|
|
267
|
+
this.emit("change", key, newValue);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
for (const key of this.cache.keys()) {
|
|
271
|
+
if (!newCache.has(key)) {
|
|
272
|
+
this.emit("change", key, void 0);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
this.cache = newCache;
|
|
276
|
+
}
|
|
277
|
+
emit(event, ...args) {
|
|
278
|
+
if (event === "change") {
|
|
279
|
+
for (const listener of this.listeners.change) {
|
|
280
|
+
listener(args[0], args[1]);
|
|
281
|
+
}
|
|
282
|
+
} else if (event === "ready") {
|
|
283
|
+
for (const listener of this.listeners.ready) {
|
|
284
|
+
listener();
|
|
285
|
+
}
|
|
286
|
+
} else if (event === "error") {
|
|
287
|
+
for (const listener of this.listeners.error) {
|
|
288
|
+
listener(args[0]);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
async fetchApi(path, body, signal) {
|
|
293
|
+
const response = await fetch(`${this.serverUrl}${path}`, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers: {
|
|
296
|
+
"Content-Type": "application/json",
|
|
297
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
298
|
+
},
|
|
299
|
+
body: JSON.stringify(body),
|
|
300
|
+
signal
|
|
301
|
+
});
|
|
302
|
+
if (!response.ok) {
|
|
303
|
+
throw new Error(`Flaggy API error: ${response.status} ${response.statusText}`);
|
|
304
|
+
}
|
|
305
|
+
return response.json();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// src/react/context.ts
|
|
310
|
+
var import_react = require("react");
|
|
311
|
+
var FlaggyReactContext = (0, import_react.createContext)(null);
|
|
312
|
+
|
|
313
|
+
// src/react/FlaggyProvider.tsx
|
|
314
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
315
|
+
function FlaggyProvider({
|
|
316
|
+
serverUrl,
|
|
317
|
+
apiKey,
|
|
318
|
+
context,
|
|
319
|
+
enableStreaming,
|
|
320
|
+
onError,
|
|
321
|
+
children
|
|
322
|
+
}) {
|
|
323
|
+
const clientRef = (0, import_react2.useRef)(null);
|
|
324
|
+
const [ready, setReady] = (0, import_react2.useState)(false);
|
|
325
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
326
|
+
const [, setVersion] = (0, import_react2.useState)(0);
|
|
327
|
+
(0, import_react2.useEffect)(() => {
|
|
328
|
+
const client = new FlaggyClient({
|
|
329
|
+
serverUrl,
|
|
330
|
+
apiKey,
|
|
331
|
+
context,
|
|
332
|
+
enableStreaming
|
|
333
|
+
});
|
|
334
|
+
clientRef.current = client;
|
|
335
|
+
setReady(false);
|
|
336
|
+
setError(null);
|
|
337
|
+
const unsubReady = client.on("ready", () => setReady(true));
|
|
338
|
+
const unsubError = client.on("error", (err) => {
|
|
339
|
+
setError(err);
|
|
340
|
+
onError?.(err);
|
|
341
|
+
});
|
|
342
|
+
const unsubChange = client.on("change", () => {
|
|
343
|
+
setVersion((v) => v + 1);
|
|
344
|
+
});
|
|
345
|
+
client.initialize();
|
|
346
|
+
return () => {
|
|
347
|
+
unsubReady();
|
|
348
|
+
unsubError();
|
|
349
|
+
unsubChange();
|
|
350
|
+
client.destroy();
|
|
351
|
+
clientRef.current = null;
|
|
352
|
+
};
|
|
353
|
+
}, [serverUrl, apiKey]);
|
|
354
|
+
const contextKey = context ? JSON.stringify(context) : "";
|
|
355
|
+
(0, import_react2.useEffect)(() => {
|
|
356
|
+
if (clientRef.current && context && clientRef.current.ready) {
|
|
357
|
+
clientRef.current.setContext(context);
|
|
358
|
+
}
|
|
359
|
+
}, [contextKey]);
|
|
360
|
+
const value = (0, import_react2.useMemo)(
|
|
361
|
+
() => clientRef.current ? { client: clientRef.current, ready, error } : null,
|
|
362
|
+
[ready, error]
|
|
363
|
+
);
|
|
364
|
+
if (!value) return null;
|
|
365
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlaggyReactContext.Provider, { value, children });
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/react/useFlag.ts
|
|
369
|
+
var import_react3 = require("react");
|
|
370
|
+
function useFlag(key, defaultValue) {
|
|
371
|
+
const ctx = (0, import_react3.useContext)(FlaggyReactContext);
|
|
372
|
+
if (!ctx) {
|
|
373
|
+
return defaultValue;
|
|
374
|
+
}
|
|
375
|
+
return ctx.client.getFlag(key, defaultValue);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/react/useFlaggy.ts
|
|
379
|
+
var import_react4 = require("react");
|
|
380
|
+
function useFlaggy() {
|
|
381
|
+
const ctx = (0, import_react4.useContext)(FlaggyReactContext);
|
|
382
|
+
if (!ctx) {
|
|
383
|
+
throw new Error("[flaggy] useFlaggy() must be used within a <FlaggyProvider>.");
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
client: ctx.client,
|
|
387
|
+
ready: ctx.ready,
|
|
388
|
+
error: ctx.error
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
392
|
+
0 && (module.exports = {
|
|
393
|
+
FlaggyProvider,
|
|
394
|
+
useFlag,
|
|
395
|
+
useFlaggy
|
|
396
|
+
});
|
|
397
|
+
//# sourceMappingURL=react.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/react/index.ts","../src/react/FlaggyProvider.tsx","../src/sse.ts","../src/client.ts","../src/react/context.ts","../src/react/useFlag.ts","../src/react/useFlaggy.ts"],"sourcesContent":["export { FlaggyProvider } from './FlaggyProvider';\nexport type { FlaggyProviderProps } from './FlaggyProvider';\nexport { useFlag } from './useFlag';\nexport { useFlaggy } from './useFlaggy';\nexport type { FlaggyContext as FlaggyEvalContext, FlagValue } from '../types';\n","import { useEffect, useRef, useState, useMemo, type ReactNode } from 'react';\nimport { FlaggyClient } from '../client';\nimport { FlaggyReactContext } from './context';\nimport type { FlaggyContext } from '../types';\n\nexport interface FlaggyProviderProps {\n serverUrl: string;\n apiKey: string;\n context?: FlaggyContext;\n enableStreaming?: boolean;\n /** Called when an error occurs (init failure, SSE error, etc.) */\n onError?: (error: Error) => void;\n children: ReactNode;\n}\n\nexport function FlaggyProvider({\n serverUrl,\n apiKey,\n context,\n enableStreaming,\n onError,\n children,\n}: FlaggyProviderProps) {\n const clientRef = useRef<FlaggyClient | null>(null);\n const [ready, setReady] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n const [, setVersion] = useState(0);\n\n // Create and initialize client when serverUrl or apiKey change\n useEffect(() => {\n const client = new FlaggyClient({\n serverUrl,\n apiKey,\n context,\n enableStreaming,\n });\n clientRef.current = client;\n setReady(false);\n setError(null);\n\n const unsubReady = client.on('ready', () => setReady(true));\n const unsubError = client.on('error', (err) => {\n setError(err);\n onError?.(err);\n });\n const unsubChange = client.on('change', () => {\n setVersion((v) => v + 1);\n });\n\n client.initialize();\n\n return () => {\n unsubReady();\n unsubError();\n unsubChange();\n client.destroy();\n clientRef.current = null;\n };\n }, [serverUrl, apiKey]);\n\n // Update context when it changes (deep comparison via JSON.stringify)\n const contextKey = context ? JSON.stringify(context) : '';\n useEffect(() => {\n if (clientRef.current && context && clientRef.current.ready) {\n clientRef.current.setContext(context);\n }\n }, [contextKey]);\n\n const value = useMemo(\n () =>\n clientRef.current\n ? { client: clientRef.current, ready, error }\n : null,\n [ready, error],\n );\n\n if (!value) return null;\n\n return (\n <FlaggyReactContext.Provider value={value}>\n {children}\n </FlaggyReactContext.Provider>\n );\n}\n","import type { FlagChangeEvent } from './types';\n\nexport interface SSEManagerOptions {\n url: string;\n apiKey: string;\n onEvent: (event: FlagChangeEvent) => void;\n onError: (error: Error) => void;\n retryDelay?: number;\n maxRetryDelay?: number;\n}\n\nexport class SSEManager {\n private abortController: AbortController | null = null;\n private retryCount = 0;\n private retryTimeout: ReturnType<typeof setTimeout> | null = null;\n private destroyed = false;\n\n private readonly url: string;\n private readonly apiKey: string;\n private readonly onEvent: (event: FlagChangeEvent) => void;\n private readonly onError: (error: Error) => void;\n private readonly retryDelay: number;\n private readonly maxRetryDelay: number;\n\n constructor(options: SSEManagerOptions) {\n this.url = options.url;\n this.apiKey = options.apiKey;\n this.onEvent = options.onEvent;\n this.onError = options.onError;\n this.retryDelay = options.retryDelay ?? 1000;\n this.maxRetryDelay = options.maxRetryDelay ?? 30_000;\n }\n\n connect(): void {\n if (this.destroyed) return;\n\n this.abortController = new AbortController();\n\n fetch(this.url, {\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: 'text/event-stream',\n },\n signal: this.abortController.signal,\n })\n .then((response) => {\n if (!response.ok) {\n throw new Error(`SSE connection failed: ${response.status}`);\n }\n if (!response.body) {\n throw new Error('SSE response has no body');\n }\n\n this.retryCount = 0;\n this.readStream(response.body);\n })\n .catch((err: unknown) => {\n if (this.destroyed) return;\n if (err instanceof Error && err.name === 'AbortError') return;\n\n this.onError(err instanceof Error ? err : new Error(String(err)));\n this.reconnect();\n });\n }\n\n destroy(): void {\n this.destroyed = true;\n this.abortController?.abort();\n this.abortController = null;\n if (this.retryTimeout) {\n clearTimeout(this.retryTimeout);\n this.retryTimeout = null;\n }\n }\n\n private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let currentEvent = '';\n let currentData = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split('\\n');\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('event:')) {\n currentEvent = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n currentData = line.slice(5).trim();\n } else if (line === '') {\n // Empty line = end of event\n if (currentData) {\n this.handleEvent(currentEvent, currentData);\n }\n currentEvent = '';\n currentData = '';\n }\n }\n }\n } catch (err: unknown) {\n if (this.destroyed) return;\n if (err instanceof Error && err.name === 'AbortError') return;\n\n this.onError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n reader.releaseLock();\n }\n\n // Stream ended — reconnect if not destroyed\n if (!this.destroyed) {\n this.reconnect();\n }\n }\n\n private handleEvent(eventType: string, data: string): void {\n try {\n const parsed = JSON.parse(data) as FlagChangeEvent;\n // Use the event type from the SSE field if present, otherwise from data\n if (eventType && !parsed.type) {\n parsed.type = eventType as FlagChangeEvent['type'];\n }\n this.onEvent(parsed);\n } catch {\n // Malformed event data, skip\n }\n }\n\n private reconnect(): void {\n if (this.destroyed) return;\n\n const delay = this.getBackoffDelay();\n this.retryCount++;\n this.retryTimeout = setTimeout(() => {\n this.retryTimeout = null;\n this.connect();\n }, delay);\n }\n\n private getBackoffDelay(): number {\n const delay = this.retryDelay * Math.pow(2, this.retryCount);\n const jitter = delay * 0.25 * (Math.random() * 2 - 1);\n return Math.min(delay + jitter, this.maxRetryDelay);\n }\n}\n","import { SSEManager } from './sse';\nimport type {\n FlagValue,\n FlaggyContext,\n FlaggyClientOptions,\n EvaluateResponse,\n BatchEvaluateResponse,\n FlagChangeEvent,\n FlagChangeListener,\n ReadyListener,\n ErrorListener,\n} from './types';\n\ntype EventMap = {\n change: FlagChangeListener;\n ready: ReadyListener;\n error: ErrorListener;\n};\n\nexport class FlaggyClient {\n private readonly serverUrl: string;\n private readonly apiKey: string;\n private readonly enableStreaming: boolean;\n private readonly sseRetryDelay: number;\n private readonly sseMaxRetryDelay: number;\n\n private context: FlaggyContext;\n private cache = new Map<string, FlagValue>();\n private _ready = false;\n private _error: Error | null = null;\n private sseManager: SSEManager | null = null;\n private contextAbortController: AbortController | null = null;\n\n private listeners: {\n change: Set<FlagChangeListener>;\n ready: Set<ReadyListener>;\n error: Set<ErrorListener>;\n } = {\n change: new Set(),\n ready: new Set(),\n error: new Set(),\n };\n\n constructor(options: FlaggyClientOptions) {\n this.serverUrl = options.serverUrl.replace(/\\/$/, '');\n this.apiKey = options.apiKey;\n this.context = options.context ?? {};\n this.enableStreaming = options.enableStreaming ?? true;\n this.sseRetryDelay = options.sseRetryDelay ?? 1000;\n this.sseMaxRetryDelay = options.sseMaxRetryDelay ?? 30_000;\n }\n\n get ready(): boolean {\n return this._ready;\n }\n\n get error(): Error | null {\n return this._error;\n }\n\n async initialize(): Promise<void> {\n try {\n const response = await this.fetchApi<BatchEvaluateResponse>(\n '/api/v1/evaluate/batch',\n { context: this.context },\n );\n this.applyBatchResult(response);\n this._ready = true;\n this.emit('ready');\n } catch (err: unknown) {\n this._error = err instanceof Error ? err : new Error(String(err));\n this.emit('error', this._error);\n }\n\n if (this.enableStreaming) {\n this.startSSE();\n }\n }\n\n getFlag<T extends FlagValue>(key: string, defaultValue: T): T {\n if (!this._ready || !this.cache.has(key)) {\n return defaultValue;\n }\n return this.cache.get(key) as T;\n }\n\n async setContext(context: FlaggyContext): Promise<void> {\n this.context = context;\n this.contextAbortController?.abort();\n const controller = new AbortController();\n this.contextAbortController = controller;\n\n try {\n const response = await this.fetchApi<BatchEvaluateResponse>(\n '/api/v1/evaluate/batch',\n { context },\n controller.signal,\n );\n if (controller.signal.aborted) return;\n this.applyBatchResult(response);\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') return;\n this._error = err instanceof Error ? err : new Error(String(err));\n this.emit('error', this._error);\n }\n }\n\n on<E extends keyof EventMap>(event: E, listener: EventMap[E]): () => void {\n (this.listeners[event] as Set<EventMap[E]>).add(listener);\n return () => {\n (this.listeners[event] as Set<EventMap[E]>).delete(listener);\n };\n }\n\n destroy(): void {\n this.sseManager?.destroy();\n this.sseManager = null;\n this.contextAbortController?.abort();\n this.contextAbortController = null;\n this.listeners.change.clear();\n this.listeners.ready.clear();\n this.listeners.error.clear();\n }\n\n private startSSE(): void {\n this.sseManager = new SSEManager({\n url: `${this.serverUrl}/api/v1/stream`,\n apiKey: this.apiKey,\n onEvent: (event) => this.handleSSEEvent(event),\n onError: (err) => this.emit('error', err),\n retryDelay: this.sseRetryDelay,\n maxRetryDelay: this.sseMaxRetryDelay,\n });\n this.sseManager.connect();\n }\n\n private async handleSSEEvent(event: FlagChangeEvent): Promise<void> {\n if (event.type === 'flag_deleted') {\n if (this.cache.has(event.key)) {\n this.cache.delete(event.key);\n this.emit('change', event.key, undefined as unknown as FlagValue);\n }\n return;\n }\n\n // flag_updated or flag_created — re-evaluate this flag\n try {\n const response = await this.fetchApi<EvaluateResponse>(\n '/api/v1/evaluate',\n { flag_key: event.key, context: this.context },\n );\n const oldValue = this.cache.get(event.key);\n this.cache.set(event.key, response.value);\n if (oldValue !== response.value) {\n this.emit('change', event.key, response.value);\n }\n } catch {\n // Failed to re-evaluate, keep previous cached value\n }\n }\n\n private applyBatchResult(response: BatchEvaluateResponse): void {\n const newCache = new Map<string, FlagValue>();\n for (const flag of response.results) {\n newCache.set(flag.flag_key, flag.value);\n }\n\n // Emit changes for any values that differ\n for (const [key, newValue] of newCache) {\n const oldValue = this.cache.get(key);\n if (oldValue !== newValue) {\n this.emit('change', key, newValue);\n }\n }\n\n // Emit changes for keys that were removed\n for (const key of this.cache.keys()) {\n if (!newCache.has(key)) {\n this.emit('change', key, undefined as unknown as FlagValue);\n }\n }\n\n this.cache = newCache;\n }\n\n private emit(event: 'change', key: string, value: FlagValue): void;\n private emit(event: 'ready'): void;\n private emit(event: 'error', error: Error): void;\n private emit(event: keyof EventMap, ...args: unknown[]): void {\n if (event === 'change') {\n for (const listener of this.listeners.change) {\n listener(args[0] as string, args[1] as FlagValue);\n }\n } else if (event === 'ready') {\n for (const listener of this.listeners.ready) {\n listener();\n }\n } else if (event === 'error') {\n for (const listener of this.listeners.error) {\n listener(args[0] as Error);\n }\n }\n }\n\n private async fetchApi<T>(\n path: string,\n body: unknown,\n signal?: AbortSignal,\n ): Promise<T> {\n const response = await fetch(`${this.serverUrl}${path}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal,\n });\n if (!response.ok) {\n throw new Error(`Flaggy API error: ${response.status} ${response.statusText}`);\n }\n return response.json() as Promise<T>;\n }\n}\n","import { createContext } from 'react';\nimport type { FlaggyClient } from '../client';\n\nexport interface FlaggyContextValue {\n client: FlaggyClient;\n ready: boolean;\n error: Error | null;\n}\n\nexport const FlaggyReactContext = createContext<FlaggyContextValue | null>(null);\n","import { useContext } from 'react';\nimport { FlaggyReactContext } from './context';\nimport type { FlagValue } from '../types';\n\nexport function useFlag<T extends FlagValue>(key: string, defaultValue: T): T {\n const ctx = useContext(FlaggyReactContext);\n\n if (!ctx) {\n return defaultValue;\n }\n\n return ctx.client.getFlag(key, defaultValue);\n}\n","import { useContext } from 'react';\nimport { FlaggyReactContext } from './context';\n\nexport function useFlaggy() {\n const ctx = useContext(FlaggyReactContext);\n\n if (!ctx) {\n throw new Error('[flaggy] useFlaggy() must be used within a <FlaggyProvider>.');\n }\n\n return {\n client: ctx.client,\n ready: ctx.ready,\n error: ctx.error,\n };\n}\n"],"mappings":"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","names":["import_react","import_react","import_react"]}
|
package/dist/react.d.cts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { FlaggyContext, FlagValue, FlaggyClient } from './index.cjs';
|
|
4
|
+
|
|
5
|
+
interface FlaggyProviderProps {
|
|
6
|
+
serverUrl: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
context?: FlaggyContext;
|
|
9
|
+
enableStreaming?: boolean;
|
|
10
|
+
/** Called when an error occurs (init failure, SSE error, etc.) */
|
|
11
|
+
onError?: (error: Error) => void;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
declare function FlaggyProvider({ serverUrl, apiKey, context, enableStreaming, onError, children, }: FlaggyProviderProps): react_jsx_runtime.JSX.Element | null;
|
|
15
|
+
|
|
16
|
+
declare function useFlag<T extends FlagValue>(key: string, defaultValue: T): T;
|
|
17
|
+
|
|
18
|
+
declare function useFlaggy(): {
|
|
19
|
+
client: FlaggyClient;
|
|
20
|
+
ready: boolean;
|
|
21
|
+
error: Error | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { FlagValue, FlaggyContext as FlaggyEvalContext, FlaggyProvider, type FlaggyProviderProps, useFlag, useFlaggy };
|
package/dist/react.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode } from 'react';
|
|
3
|
+
import { FlaggyContext, FlagValue, FlaggyClient } from './index.js';
|
|
4
|
+
|
|
5
|
+
interface FlaggyProviderProps {
|
|
6
|
+
serverUrl: string;
|
|
7
|
+
apiKey: string;
|
|
8
|
+
context?: FlaggyContext;
|
|
9
|
+
enableStreaming?: boolean;
|
|
10
|
+
/** Called when an error occurs (init failure, SSE error, etc.) */
|
|
11
|
+
onError?: (error: Error) => void;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
declare function FlaggyProvider({ serverUrl, apiKey, context, enableStreaming, onError, children, }: FlaggyProviderProps): react_jsx_runtime.JSX.Element | null;
|
|
15
|
+
|
|
16
|
+
declare function useFlag<T extends FlagValue>(key: string, defaultValue: T): T;
|
|
17
|
+
|
|
18
|
+
declare function useFlaggy(): {
|
|
19
|
+
client: FlaggyClient;
|
|
20
|
+
ready: boolean;
|
|
21
|
+
error: Error | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { FlagValue, FlaggyContext as FlaggyEvalContext, FlaggyProvider, type FlaggyProviderProps, useFlag, useFlaggy };
|