Skip to content

Commit f1109bc

Browse files
committed
address pr comments
1 parent c09e1e1 commit f1109bc

File tree

9 files changed

+54
-51
lines changed

9 files changed

+54
-51
lines changed

.fernignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ src/events.test.ts
1313
src/events.ts
1414
src/index.ts
1515
src/logger.ts
16-
src/rules-engine.test.ts
1716
src/rules-engine.ts
1817
src/version.ts
1918
src/wasm/
2019
src/webhooks.ts
2120
src/wrapper.ts
21+
tests/unit/cache/local.test.ts
22+
tests/unit/datastream/datastream-client.test.ts
23+
tests/unit/datastream/websocket-client.test.ts
24+
tests/unit/rules-engine.test.ts
2225
tests/unit/webhooks.test.ts

build.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@ const sharedConfig = {
1212
sourcemap: 'external',
1313
external: [
1414
// Keep dependencies external since this is a library
15-
'url-join',
1615
'form-data',
17-
'formdata-node',
16+
'formdata-node',
1817
'node-fetch',
19-
'qs',
2018
'readable-stream'
2119
],
2220
tsconfig: './tsconfig.json',

src/cache/redis.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export interface RedisOptions extends CacheOptions {
2020
* Requires the 'redis' package to be installed: npm install redis
2121
*/
2222
export class RedisCacheProvider<T> implements CacheProvider<T> {
23-
private client: any; // Redis client type
23+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- redis client is dynamically imported
24+
private client: any;
2425
private defaultTTL: number;
2526
private keyPrefix: string;
2627
private isConnected: boolean = false;
@@ -37,10 +38,11 @@ export class RedisCacheProvider<T> implements CacheProvider<T> {
3738
private async initRedisClient(options: RedisOptions): Promise<void> {
3839
try {
3940
// Dynamically import redis so it's only loaded if actually used
41+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic import for optional dependency
4042
const redisModule = await import('redis' as any);
4143
const { createClient } = redisModule;
42-
43-
let clientConfig: any = {};
44+
45+
let clientConfig: Record<string, unknown> = {};
4446

4547
if (options.url) {
4648
clientConfig.url = options.url;

src/datastream/datastream-client.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EventEmitter } from 'events';
22
import * as Schematic from '../api/types';
33
import { DatastreamWSClient } from './websocket-client';
4-
import { DataStreamResp, DataStreamReq, EntityType, MessageType } from './types';
4+
import { DataStreamResp, DataStreamReq, DataStreamError, EntityType, MessageType } from './types';
55
import { RulesEngineClient } from '../rules-engine';
66
import { Logger } from '../logger';
77

@@ -44,7 +44,6 @@ export interface DataStreamClientOptions {
4444
type PendingRequestHandler<T> = (value: T | null) => void;
4545

4646
// Cache key constants
47-
const CACHE_KEY_PREFIX = 'schematic';
4847
const CACHE_KEY_PREFIX_COMPANY = 'company';
4948
const CACHE_KEY_PREFIX_USER = 'user';
5049
const CACHE_KEY_PREFIX_FLAGS = 'flags';
@@ -796,7 +795,7 @@ export class DataStreamClient extends EventEmitter {
796795
* handleErrorMessage processes error messages
797796
*/
798797
private async handleErrorMessage(message: DataStreamResp): Promise<void> {
799-
const errorData = message.data as any;
798+
const errorData = message.data as DataStreamError;
800799

801800
if (errorData?.keys && errorData?.entity_type) {
802801
// Notify pending requests with null/error
@@ -1146,28 +1145,28 @@ export class DataStreamClient extends EventEmitter {
11461145
}
11471146

11481147
/**
1149-
* evaluateFlag evaluates a flag using the rules engine
1148+
* sanitizeForWasm removes null/undefined values from objects for WASM compatibility
11501149
*/
1151-
private sanitizeForWasm(obj: any): any {
1150+
private sanitizeForWasm<T>(obj: T): T {
11521151
if (obj === null || obj === undefined) {
1153-
return null;
1152+
return null as T;
11541153
}
1155-
1154+
11561155
if (Array.isArray(obj)) {
1157-
return obj.map(item => this.sanitizeForWasm(item)).filter(item => item !== null);
1156+
return obj.map(item => this.sanitizeForWasm(item)).filter(item => item !== null) as T;
11581157
}
1159-
1158+
11601159
if (typeof obj === 'object') {
1161-
const sanitized: any = {};
1160+
const sanitized: Record<string, unknown> = {};
11621161
for (const [key, value] of Object.entries(obj)) {
11631162
const sanitizedValue = this.sanitizeForWasm(value);
11641163
if (sanitizedValue !== null) {
11651164
sanitized[key] = sanitizedValue;
11661165
}
11671166
}
1168-
return sanitized;
1167+
return sanitized as T;
11691168
}
1170-
1169+
11711170
return obj;
11721171
}
11731172

src/datastream/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export interface DataStreamBaseReq {
3939
* DataStreamResp represents a response message from the datastream
4040
*/
4141
export interface DataStreamResp {
42-
data: any; // JSON raw message equivalent
42+
data: unknown;
4343
entity_type: string;
4444
message_type: string;
4545
}

src/datastream/websocket-client.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ export class DatastreamWSClient extends EventEmitter {
349349
/**
350350
* connect establishes the WebSocket connection
351351
*/
352-
private connect(): Promise<any> {
352+
private connect(): Promise<WebSocket> {
353353
return new Promise((resolve, reject) => {
354354
this.logger.debug(`Connecting to WebSocket: ${this.url}`);
355355

@@ -472,7 +472,12 @@ export class DatastreamWSClient extends EventEmitter {
472472
return;
473473
}
474474

475-
475+
// Clear any existing pong timeout before setting a new one
476+
if (this.pongTimeout) {
477+
clearTimeout(this.pongTimeout);
478+
this.pongTimeout = undefined;
479+
}
480+
476481
// Set pong timeout
477482
this.pongTimeout = setTimeout(() => {
478483
this.logger.warn('Pong timeout - closing connection');

src/rules-engine.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,43 @@
11
import { RulesEngineJS } from './wasm/rulesengine.js';
2+
import type * as Schematic from './api/types';
3+
4+
/** Result returned by the WASM rules engine (snake_case keys) */
5+
export interface WasmCheckFlagResult {
6+
value: boolean;
7+
reason: string;
8+
rule_id?: string;
9+
flag_id?: string;
10+
flag_key?: string;
11+
company_id?: string;
12+
user_id?: string;
13+
rule_type?: string;
14+
err?: string;
15+
}
216

3-
/**
4-
* High-performance rules engine for flag evaluation and rule processing.
5-
*
6-
* This engine provides significant performance improvements over traditional JavaScript
7-
* implementations by leveraging compiled Rust code running in WebAssembly.
8-
*/
917
export class RulesEngineClient {
1018
private wasmInstance: RulesEngineJS | null = null;
1119
private initialized = false;
1220

1321
constructor() {}
1422

15-
/**
16-
* Initialize the WASM rules engine.
17-
* Must be called before using any other methods.
18-
*/
1923
async initialize(): Promise<void> {
2024
if (this.initialized) {
2125
return;
2226
}
2327

2428
try {
25-
// Initialize the WASM module
2629
this.wasmInstance = new RulesEngineJS();
2730
this.initialized = true;
2831
} catch (error) {
2932
throw new Error(`Failed to initialize WASM rules engine: ${error}`);
3033
}
3134
}
3235

33-
/**
34-
* Check a feature flag using the WASM engine.
35-
*
36-
* @param flag - The flag configuration object
37-
* @param company - Optional company context
38-
* @param user - Optional user context
39-
* @returns Promise resolving to the flag check result
40-
*/
4136
async checkFlag(
42-
flag: any,
43-
company?: any,
44-
user?: any
45-
): Promise<any> {
37+
flag: Schematic.RulesengineFlag,
38+
company?: Schematic.RulesengineCompany | null,
39+
user?: Schematic.RulesengineUser | null
40+
): Promise<WasmCheckFlagResult> {
4641
this.ensureInitialized();
4742

4843
try {

src/wrapper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ export class SchematicClient extends BaseClient {
180180
flagKey: key,
181181
value: flagValue ?? false,
182182
reason: resp?.reason ?? "unknown",
183+
ruleId: resp?.ruleId,
183184
companyId: resp?.companyId,
184185
userId: resp?.userId,
185186
flagId: resp?.flagId,

tests/unit/datastream/datastream-client.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,10 @@ describe('DataStreamClient', () => {
166166

167167
// Simulate WebSocket events
168168
const onCalls = mockDatastreamWSClientInstance.on.mock.calls;
169-
const connectedHandler = onCalls.find((call: any) => call[0] === 'connected')?.[1];
170-
const disconnectedHandler = onCalls.find((call: any) => call[0] === 'disconnected')?.[1];
171-
const readyHandler = onCalls.find((call: any) => call[0] === 'ready')?.[1];
172-
const errorHandler = onCalls.find((call: any) => call[0] === 'error')?.[1];
169+
const connectedHandler = onCalls.find((call: [string, Function]) => call[0] === 'connected')?.[1];
170+
const disconnectedHandler = onCalls.find((call: [string, Function]) => call[0] === 'disconnected')?.[1];
171+
const readyHandler = onCalls.find((call: [string, Function]) => call[0] === 'ready')?.[1];
172+
const errorHandler = onCalls.find((call: [string, Function]) => call[0] === 'error')?.[1];
173173

174174
if (connectedHandler) connectedHandler();
175175
if (disconnectedHandler) disconnectedHandler();
@@ -260,7 +260,7 @@ describe('DataStreamClient', () => {
260260

261261
// Get the connection handler from the WebSocket client mock
262262
const onCalls = mockDatastreamWSClientInstance.on.mock.calls;
263-
const connectedHandler = onCalls.find((call: any) => call[0] === 'connected')?.[1];
263+
const connectedHandler = onCalls.find((call: [string, Function]) => call[0] === 'connected')?.[1];
264264

265265
// Mock sendMessage to automatically trigger response
266266
mockDatastreamWSClientInstance.sendMessage.mockImplementation(async (message: any) => {
@@ -313,7 +313,7 @@ describe('DataStreamClient', () => {
313313

314314
// Get the connection handler from the WebSocket client mock
315315
const onCalls = mockDatastreamWSClientInstance.on.mock.calls;
316-
const connectedHandler = onCalls.find((call: any) => call[0] === 'connected')?.[1];
316+
const connectedHandler = onCalls.find((call: [string, Function]) => call[0] === 'connected')?.[1];
317317

318318
// Mock sendMessage to track requests and auto-respond
319319
mockDatastreamWSClientInstance.sendMessage.mockImplementation(async (message: any) => {

0 commit comments

Comments
 (0)