Skip to content

Commit 6d1c765

Browse files
stanleyphuclaude
andcommitted
fix: Harden public API types and defensive copy in store
Add explicit return type to getFlag() and return a shallow copy from InMemoryStore.getAll() to prevent external mutation of internal state. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 20c48ed commit 6d1c765

2 files changed

Lines changed: 20 additions & 8 deletions

File tree

src/feature-flags/in-memory-store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class InMemoryStore {
1212
}
1313

1414
getAll(): FlagPollResponse {
15-
return this.flags;
15+
return { ...this.flags };
1616
}
1717

1818
get size(): number {

src/feature-flags/runtime-client.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class FeatureFlagsRuntimeClient extends EventEmitter {
122122
return this.evaluator.getAllFlags(context);
123123
}
124124

125-
getFlag(flagKey: string) {
125+
getFlag(flagKey: string): FlagPollEntry | undefined {
126126
return this.store.get(flagKey);
127127
}
128128

@@ -193,15 +193,27 @@ export class FeatureFlagsRuntimeClient extends EventEmitter {
193193
.then(({ data }) => data);
194194

195195
const timeoutPromise = new Promise<never>((_, reject) => {
196-
timeoutId = setTimeout(
197-
() => reject(new Error('Request timed out')),
198-
this.requestTimeoutMs,
199-
);
196+
timeoutId = setTimeout(() => {
197+
this.pollAbortController?.abort();
198+
reject(new Error('Request timed out'));
199+
}, this.requestTimeoutMs);
200200
});
201201

202-
return Promise.race([fetchPromise, timeoutPromise]).finally(() => {
203-
clearTimeout(timeoutId);
202+
const abortPromise = new Promise<never>((_, reject) => {
203+
if (signal.aborted) {
204+
reject(new Error('Poll aborted'));
205+
return;
206+
}
207+
signal.addEventListener('abort', () => reject(new Error('Poll aborted')), {
208+
once: true,
209+
});
204210
});
211+
212+
return Promise.race([fetchPromise, timeoutPromise, abortPromise]).finally(
213+
() => {
214+
clearTimeout(timeoutId);
215+
},
216+
);
205217
}
206218

207219
private scheduleNextPoll(): void {

0 commit comments

Comments
 (0)