@umituz/react-native-ai-gemini-provider 1.14.26 → 1.14.27
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/package.json +1 -1
- package/src/index.ts +39 -70
- package/src/infrastructure/cache/SimpleCache.ts +173 -0
- package/src/infrastructure/cache/index.ts +7 -0
- package/src/infrastructure/interceptors/RequestInterceptors.ts +67 -0
- package/src/infrastructure/interceptors/ResponseInterceptors.ts +72 -0
- package/src/infrastructure/interceptors/index.ts +17 -0
- package/src/infrastructure/utils/index.ts +11 -0
- package/src/infrastructure/utils/performance.util.ts +145 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @umituz/react-native-ai-gemini-provider
|
|
3
3
|
* Google Gemini AI provider for React Native applications
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* import {
|
|
7
|
-
* geminiClientService,
|
|
8
|
-
* geminiProviderService,
|
|
9
|
-
* useGemini,
|
|
10
|
-
* mapGeminiError,
|
|
11
|
-
* } from '@umituz/react-native-ai-gemini-provider';
|
|
12
4
|
*/
|
|
13
5
|
|
|
14
|
-
//
|
|
15
|
-
// DOMAIN LAYER - Types
|
|
16
|
-
// =============================================================================
|
|
17
|
-
|
|
6
|
+
// Domain Types
|
|
18
7
|
export type {
|
|
19
8
|
GeminiConfig,
|
|
20
9
|
GeminiGenerationConfig,
|
|
@@ -42,30 +31,14 @@ export type {
|
|
|
42
31
|
VideoResolution,
|
|
43
32
|
VideoOperationStatus,
|
|
44
33
|
VeoOperation,
|
|
45
|
-
} from "./domain/entities";
|
|
46
|
-
|
|
47
|
-
export { GeminiErrorType } from "./domain/entities";
|
|
48
|
-
|
|
49
|
-
export type {
|
|
50
34
|
GeminiErrorInfo,
|
|
51
35
|
GeminiApiError,
|
|
36
|
+
ResponseModality,
|
|
52
37
|
} from "./domain/entities";
|
|
53
38
|
|
|
54
|
-
export { GeminiError } from "./domain/entities";
|
|
55
|
-
|
|
56
|
-
// Model Constants
|
|
57
|
-
export {
|
|
58
|
-
GEMINI_MODELS,
|
|
59
|
-
DEFAULT_MODELS,
|
|
60
|
-
RESPONSE_MODALITIES,
|
|
61
|
-
} from "./domain/entities";
|
|
62
|
-
|
|
63
|
-
export type { ResponseModality } from "./domain/entities";
|
|
64
|
-
|
|
65
|
-
// =============================================================================
|
|
66
|
-
// DOMAIN LAYER - Feature Models
|
|
67
|
-
// =============================================================================
|
|
39
|
+
export { GeminiErrorType, GeminiError, GEMINI_MODELS, DEFAULT_MODELS, RESPONSE_MODALITIES } from "./domain/entities";
|
|
68
40
|
|
|
41
|
+
// Feature Models
|
|
69
42
|
export {
|
|
70
43
|
GEMINI_IMAGE_FEATURE_MODELS,
|
|
71
44
|
GEMINI_VIDEO_FEATURE_MODELS,
|
|
@@ -74,14 +47,9 @@ export {
|
|
|
74
47
|
getAllFeatureModels,
|
|
75
48
|
} from "./domain/constants";
|
|
76
49
|
|
|
77
|
-
export type {
|
|
78
|
-
FeatureModelConfig,
|
|
79
|
-
} from "./domain/constants";
|
|
80
|
-
|
|
81
|
-
// =============================================================================
|
|
82
|
-
// INFRASTRUCTURE LAYER - Services
|
|
83
|
-
// =============================================================================
|
|
50
|
+
export type { FeatureModelConfig } from "./domain/constants";
|
|
84
51
|
|
|
52
|
+
// Services
|
|
85
53
|
export {
|
|
86
54
|
geminiClientCoreService,
|
|
87
55
|
geminiRetryService,
|
|
@@ -109,16 +77,12 @@ export type {
|
|
|
109
77
|
RetryOptions,
|
|
110
78
|
} from "./infrastructure/services";
|
|
111
79
|
|
|
112
|
-
//
|
|
113
|
-
// INFRASTRUCTURE LAYER - Utils
|
|
114
|
-
// =============================================================================
|
|
115
|
-
|
|
80
|
+
// Utils
|
|
116
81
|
export {
|
|
117
82
|
mapGeminiError,
|
|
118
83
|
isGeminiErrorRetryable,
|
|
119
84
|
categorizeGeminiError,
|
|
120
85
|
createGeminiError,
|
|
121
|
-
// Model validation
|
|
122
86
|
isValidModel,
|
|
123
87
|
validateModel,
|
|
124
88
|
getSafeModel,
|
|
@@ -128,7 +92,13 @@ export {
|
|
|
128
92
|
isVideoGenerationModel,
|
|
129
93
|
getModelCategory,
|
|
130
94
|
getAllValidModels,
|
|
131
|
-
|
|
95
|
+
measureAsync,
|
|
96
|
+
measureSync,
|
|
97
|
+
debounce,
|
|
98
|
+
throttle,
|
|
99
|
+
PerformanceTimer,
|
|
100
|
+
PerformanceTracker,
|
|
101
|
+
performanceTracker,
|
|
132
102
|
buildSingleImageInput,
|
|
133
103
|
buildDualImageInput,
|
|
134
104
|
buildUpscaleInput,
|
|
@@ -145,42 +115,41 @@ export {
|
|
|
145
115
|
} from "./infrastructure/utils";
|
|
146
116
|
|
|
147
117
|
export type {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
118
|
+
PreparedImage,
|
|
119
|
+
UpscaleOptions,
|
|
120
|
+
PhotoRestoreOptions,
|
|
121
|
+
FaceSwapOptions,
|
|
122
|
+
AnimeSelfieOptions,
|
|
123
|
+
RemoveBackgroundOptions,
|
|
124
|
+
RemoveObjectOptions,
|
|
125
|
+
ReplaceBackgroundOptions,
|
|
126
|
+
VideoFromImageOptions,
|
|
127
|
+
PerformanceMetrics,
|
|
156
128
|
} from "./infrastructure/utils";
|
|
157
129
|
|
|
158
|
-
//
|
|
159
|
-
// PRESENTATION LAYER - Hooks
|
|
160
|
-
// =============================================================================
|
|
161
|
-
|
|
130
|
+
// Hooks
|
|
162
131
|
export { useGemini } from "./presentation/hooks";
|
|
163
132
|
|
|
164
|
-
export type {
|
|
165
|
-
UseGeminiOptions,
|
|
166
|
-
UseGeminiReturn,
|
|
167
|
-
} from "./presentation/hooks";
|
|
168
|
-
|
|
169
|
-
// =============================================================================
|
|
170
|
-
// TELEMETRY - Monitoring and Observability
|
|
171
|
-
// =============================================================================
|
|
133
|
+
export type { UseGeminiOptions, UseGeminiReturn } from "./presentation/hooks";
|
|
172
134
|
|
|
135
|
+
// Telemetry
|
|
173
136
|
export { telemetryHooks } from "./infrastructure/telemetry";
|
|
137
|
+
export type { TelemetryEvent, TelemetryListener } from "./infrastructure/telemetry";
|
|
174
138
|
|
|
139
|
+
// Interceptors
|
|
140
|
+
export { requestInterceptors, responseInterceptors } from "./infrastructure/interceptors";
|
|
175
141
|
export type {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
142
|
+
RequestContext,
|
|
143
|
+
RequestInterceptor,
|
|
144
|
+
ResponseContext,
|
|
145
|
+
ResponseInterceptor,
|
|
146
|
+
} from "./infrastructure/interceptors";
|
|
179
147
|
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
148
|
+
// Cache
|
|
149
|
+
export { SimpleCache, modelSelectionCache } from "./infrastructure/cache";
|
|
150
|
+
export type { CacheOptions } from "./infrastructure/cache";
|
|
183
151
|
|
|
152
|
+
// Provider Config
|
|
184
153
|
export {
|
|
185
154
|
providerFactory,
|
|
186
155
|
resolveProviderConfig,
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple LRU Cache
|
|
3
|
+
* Least Recently Used cache for performance optimization
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
declare const __DEV__: boolean;
|
|
7
|
+
|
|
8
|
+
interface CacheEntry<V> {
|
|
9
|
+
value: V;
|
|
10
|
+
expiresAt: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface CacheOptions {
|
|
14
|
+
maxSize?: number;
|
|
15
|
+
ttl?: number; // Time to live in milliseconds
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class SimpleCache<K, V> {
|
|
19
|
+
private cache = new Map<K, CacheEntry<V>>();
|
|
20
|
+
private maxSize: number;
|
|
21
|
+
private ttl: number;
|
|
22
|
+
|
|
23
|
+
constructor(options: CacheOptions = {}) {
|
|
24
|
+
this.maxSize = options.maxSize ?? 100;
|
|
25
|
+
this.ttl = options.ttl ?? 5 * 60 * 1000; // 5 minutes default
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Set a value in the cache
|
|
30
|
+
*/
|
|
31
|
+
set(key: K, value: V, customTtl?: number): void {
|
|
32
|
+
// Remove oldest entry if at capacity
|
|
33
|
+
if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
|
|
34
|
+
const firstKey = this.cache.keys().next().value;
|
|
35
|
+
if (firstKey !== undefined) {
|
|
36
|
+
this.cache.delete(firstKey);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ttl = customTtl ?? this.ttl;
|
|
41
|
+
this.cache.set(key, {
|
|
42
|
+
value,
|
|
43
|
+
expiresAt: Date.now() + ttl,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get a value from the cache
|
|
49
|
+
* Returns undefined if not found or expired
|
|
50
|
+
*/
|
|
51
|
+
get(key: K): V | undefined {
|
|
52
|
+
const entry = this.cache.get(key);
|
|
53
|
+
|
|
54
|
+
if (!entry) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Check if expired
|
|
59
|
+
if (Date.now() > entry.expiresAt) {
|
|
60
|
+
this.cache.delete(key);
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Move to end (most recently used)
|
|
65
|
+
this.cache.delete(key);
|
|
66
|
+
this.cache.set(key, entry);
|
|
67
|
+
|
|
68
|
+
return entry.value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if a key exists and is not expired
|
|
73
|
+
*/
|
|
74
|
+
has(key: K): boolean {
|
|
75
|
+
return this.get(key) !== undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Delete a specific key
|
|
80
|
+
*/
|
|
81
|
+
delete(key: K): boolean {
|
|
82
|
+
return this.cache.delete(key);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Clear all entries
|
|
87
|
+
*/
|
|
88
|
+
clear(): void {
|
|
89
|
+
this.cache.clear();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get current cache size
|
|
94
|
+
*/
|
|
95
|
+
size(): number {
|
|
96
|
+
// Clean expired entries first
|
|
97
|
+
this.cleanExpired();
|
|
98
|
+
return this.cache.size;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Remove all expired entries
|
|
103
|
+
*/
|
|
104
|
+
private cleanExpired(): void {
|
|
105
|
+
const now = Date.now();
|
|
106
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
107
|
+
if (now > entry.expiresAt) {
|
|
108
|
+
this.cache.delete(key);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get all valid keys (not expired)
|
|
115
|
+
*/
|
|
116
|
+
keys(): K[] {
|
|
117
|
+
this.cleanExpired();
|
|
118
|
+
return Array.from(this.cache.keys());
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get cache statistics
|
|
123
|
+
*/
|
|
124
|
+
getStats(): {
|
|
125
|
+
size: number;
|
|
126
|
+
maxSize: number;
|
|
127
|
+
keys: K[];
|
|
128
|
+
} {
|
|
129
|
+
return {
|
|
130
|
+
size: this.size(),
|
|
131
|
+
maxSize: this.maxSize,
|
|
132
|
+
keys: this.keys(),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Global model selection cache
|
|
139
|
+
* Caches model IDs for features to avoid repeated lookups
|
|
140
|
+
*/
|
|
141
|
+
class ModelSelectionCache {
|
|
142
|
+
private cache = new SimpleCache<string, string>({
|
|
143
|
+
maxSize: 50,
|
|
144
|
+
ttl: 10 * 60 * 1000, // 10 minutes
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
set(feature: string, model: string): void {
|
|
148
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
149
|
+
// eslint-disable-next-line no-console
|
|
150
|
+
console.log("[ModelSelectionCache] Caching model:", { feature, model });
|
|
151
|
+
}
|
|
152
|
+
this.cache.set(feature, model);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get(feature: string): string | undefined {
|
|
156
|
+
const model = this.cache.get(feature);
|
|
157
|
+
if (model && typeof __DEV__ !== "undefined" && __DEV__) {
|
|
158
|
+
// eslint-disable-next-line no-console
|
|
159
|
+
console.log("[ModelSelectionCache] Cache hit:", { feature, model });
|
|
160
|
+
}
|
|
161
|
+
return model;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
clear(): void {
|
|
165
|
+
this.cache.clear();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
getStats(): ReturnType<SimpleCache<string, string>["getStats"]> {
|
|
169
|
+
return this.cache.getStats();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const modelSelectionCache = new ModelSelectionCache();
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Interceptors
|
|
3
|
+
* Allows applications to modify requests before they're sent
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface RequestContext {
|
|
7
|
+
model: string;
|
|
8
|
+
feature?: string;
|
|
9
|
+
payload: Record<string, unknown>;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type RequestInterceptor = (context: RequestContext) => RequestContext | Promise<RequestContext>;
|
|
14
|
+
|
|
15
|
+
class RequestInterceptors {
|
|
16
|
+
private interceptors: RequestInterceptor[] = [];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Register a request interceptor
|
|
20
|
+
* Interceptors are called in order (first registered = first called)
|
|
21
|
+
*/
|
|
22
|
+
use(interceptor: RequestInterceptor): () => void {
|
|
23
|
+
this.interceptors.push(interceptor);
|
|
24
|
+
|
|
25
|
+
// Return unsubscribe function
|
|
26
|
+
return () => {
|
|
27
|
+
const index = this.interceptors.indexOf(interceptor);
|
|
28
|
+
if (index > -1) {
|
|
29
|
+
this.interceptors.splice(index, 1);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Apply all interceptors to a request context
|
|
36
|
+
*/
|
|
37
|
+
async apply(context: RequestContext): Promise<RequestContext> {
|
|
38
|
+
let result = context;
|
|
39
|
+
|
|
40
|
+
for (const interceptor of this.interceptors) {
|
|
41
|
+
try {
|
|
42
|
+
result = await interceptor(result);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
// Interceptor error should fail the request
|
|
45
|
+
throw new Error(`Request interceptor failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Clear all interceptors
|
|
54
|
+
*/
|
|
55
|
+
clear(): void {
|
|
56
|
+
this.interceptors = [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get interceptor count
|
|
61
|
+
*/
|
|
62
|
+
count(): number {
|
|
63
|
+
return this.interceptors.length;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const requestInterceptors = new RequestInterceptors();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response Interceptors
|
|
3
|
+
* Allows applications to modify responses after they're received
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface ResponseContext<T = unknown> {
|
|
7
|
+
model: string;
|
|
8
|
+
feature?: string;
|
|
9
|
+
data: T;
|
|
10
|
+
duration: number;
|
|
11
|
+
timestamp: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type ResponseInterceptor<T = unknown> = (
|
|
15
|
+
context: ResponseContext<T>,
|
|
16
|
+
) => ResponseContext<T> | Promise<ResponseContext<T>>;
|
|
17
|
+
|
|
18
|
+
class ResponseInterceptors {
|
|
19
|
+
private interceptors: Array<ResponseInterceptor<unknown>> = [];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Register a response interceptor
|
|
23
|
+
* Interceptors are called in reverse order (last registered = first called)
|
|
24
|
+
*/
|
|
25
|
+
use<T = unknown>(interceptor: ResponseInterceptor<T>): () => void {
|
|
26
|
+
this.interceptors.push(interceptor as ResponseInterceptor<unknown>);
|
|
27
|
+
|
|
28
|
+
// Return unsubscribe function
|
|
29
|
+
return () => {
|
|
30
|
+
const index = this.interceptors.indexOf(interceptor as ResponseInterceptor<unknown>);
|
|
31
|
+
if (index > -1) {
|
|
32
|
+
this.interceptors.splice(index, 1);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Apply all interceptors to a response context
|
|
39
|
+
*/
|
|
40
|
+
async apply<T>(context: ResponseContext<T>): Promise<ResponseContext<T>> {
|
|
41
|
+
let result: ResponseContext<unknown> = context;
|
|
42
|
+
|
|
43
|
+
// Apply in reverse order (last added = first processed)
|
|
44
|
+
for (let i = this.interceptors.length - 1; i >= 0; i--) {
|
|
45
|
+
const interceptor = this.interceptors[i];
|
|
46
|
+
try {
|
|
47
|
+
result = await interceptor(result);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
// Interceptor error should fail the response processing
|
|
50
|
+
throw new Error(`Response interceptor failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return result as ResponseContext<T>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Clear all interceptors
|
|
59
|
+
*/
|
|
60
|
+
clear(): void {
|
|
61
|
+
this.interceptors = [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get interceptor count
|
|
66
|
+
*/
|
|
67
|
+
count(): number {
|
|
68
|
+
return this.interceptors.length;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const responseInterceptors = new ResponseInterceptors();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interceptors Module
|
|
3
|
+
* Allows applications to modify requests and responses
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { requestInterceptors } from "./RequestInterceptors";
|
|
7
|
+
export { responseInterceptors } from "./ResponseInterceptors";
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
RequestContext,
|
|
11
|
+
RequestInterceptor,
|
|
12
|
+
} from "./RequestInterceptors";
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
ResponseContext,
|
|
16
|
+
ResponseInterceptor,
|
|
17
|
+
} from "./ResponseInterceptors";
|
|
@@ -33,6 +33,17 @@ export {
|
|
|
33
33
|
getAllValidModels,
|
|
34
34
|
} from "./model-validation.util";
|
|
35
35
|
|
|
36
|
+
export {
|
|
37
|
+
measureAsync,
|
|
38
|
+
measureSync,
|
|
39
|
+
debounce,
|
|
40
|
+
throttle,
|
|
41
|
+
PerformanceTimer,
|
|
42
|
+
PerformanceTracker,
|
|
43
|
+
performanceTracker,
|
|
44
|
+
} from "./performance.util";
|
|
45
|
+
export type { PerformanceMetrics } from "./performance.util";
|
|
46
|
+
|
|
36
47
|
// Input builders
|
|
37
48
|
export {
|
|
38
49
|
buildSingleImageInput,
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performance Utilities
|
|
3
|
+
* Tools for measuring and optimizing performance
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
declare const __DEV__: boolean;
|
|
7
|
+
|
|
8
|
+
export interface PerformanceMetrics {
|
|
9
|
+
duration: number;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
metadata?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class PerformanceTimer {
|
|
15
|
+
private startTime: number;
|
|
16
|
+
private endTime?: number;
|
|
17
|
+
private metadata?: Record<string, unknown>;
|
|
18
|
+
|
|
19
|
+
constructor(metadata?: Record<string, unknown>) {
|
|
20
|
+
this.startTime = Date.now();
|
|
21
|
+
this.metadata = metadata;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
stop(): number {
|
|
25
|
+
this.endTime = Date.now();
|
|
26
|
+
return this.duration;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get duration(): number {
|
|
30
|
+
const end = this.endTime ?? Date.now();
|
|
31
|
+
return end - this.startTime;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
getMetrics(): PerformanceMetrics {
|
|
35
|
+
return { duration: this.duration, timestamp: this.startTime, metadata: this.metadata };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get isRunning(): boolean {
|
|
39
|
+
return this.endTime === undefined;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function measureAsync<T>(
|
|
44
|
+
operation: () => Promise<T>,
|
|
45
|
+
metadata?: Record<string, unknown>,
|
|
46
|
+
): Promise<{ result: T; duration: number }> {
|
|
47
|
+
const timer = new PerformanceTimer(metadata);
|
|
48
|
+
try {
|
|
49
|
+
const result = await operation();
|
|
50
|
+
const duration = timer.stop();
|
|
51
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.log("[Performance] Operation completed:", { duration: `${duration}ms`, metadata });
|
|
54
|
+
}
|
|
55
|
+
return { result, duration };
|
|
56
|
+
} catch (error) {
|
|
57
|
+
timer.stop();
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function measureSync<T>(
|
|
63
|
+
operation: () => T,
|
|
64
|
+
metadata?: Record<string, unknown>,
|
|
65
|
+
): { result: T; duration: number } {
|
|
66
|
+
const timer = new PerformanceTimer(metadata);
|
|
67
|
+
try {
|
|
68
|
+
const result = operation();
|
|
69
|
+
const duration = timer.stop();
|
|
70
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
71
|
+
// eslint-disable-next-line no-console
|
|
72
|
+
console.log("[Performance] Operation completed:", { duration: `${duration}ms`, metadata });
|
|
73
|
+
}
|
|
74
|
+
return { result, duration };
|
|
75
|
+
} catch (error) {
|
|
76
|
+
timer.stop();
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function debounce<T extends (...args: never[]) => unknown>(
|
|
82
|
+
func: T,
|
|
83
|
+
wait: number,
|
|
84
|
+
): (...args: Parameters<T>) => void {
|
|
85
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
86
|
+
return (...args: Parameters<T>) => {
|
|
87
|
+
const later = () => {
|
|
88
|
+
clearTimeout(timeout);
|
|
89
|
+
func(...args);
|
|
90
|
+
};
|
|
91
|
+
clearTimeout(timeout);
|
|
92
|
+
timeout = setTimeout(later, wait);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function throttle<T extends (...args: never[]) => unknown>(
|
|
97
|
+
func: T,
|
|
98
|
+
limit: number,
|
|
99
|
+
): (...args: Parameters<T>) => void {
|
|
100
|
+
let inThrottle: boolean;
|
|
101
|
+
return (...args: Parameters<T>) => {
|
|
102
|
+
if (!inThrottle) {
|
|
103
|
+
func(...args);
|
|
104
|
+
inThrottle = true;
|
|
105
|
+
setTimeout(() => (inThrottle = false), limit);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export class PerformanceTracker {
|
|
111
|
+
private metrics = new Map<string, number[]>();
|
|
112
|
+
|
|
113
|
+
record(operation: string, duration: number): void {
|
|
114
|
+
if (!this.metrics.has(operation)) {
|
|
115
|
+
this.metrics.set(operation, []);
|
|
116
|
+
}
|
|
117
|
+
this.metrics.get(operation)!.push(duration);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
getStats(operation: string): { count: number; avg: number; min: number; max: number } | null {
|
|
121
|
+
const durations = this.metrics.get(operation);
|
|
122
|
+
if (!durations || durations.length === 0) return null;
|
|
123
|
+
return {
|
|
124
|
+
count: durations.length,
|
|
125
|
+
avg: durations.reduce((a, b) => a + b, 0) / durations.length,
|
|
126
|
+
min: Math.min(...durations),
|
|
127
|
+
max: Math.max(...durations),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getAllStats(): Record<string, ReturnType<PerformanceTracker["getStats"]>> {
|
|
132
|
+
const stats: Record<string, ReturnType<PerformanceTracker["getStats"]>> = {};
|
|
133
|
+
for (const operation of this.metrics.keys()) {
|
|
134
|
+
stats[operation] = this.getStats(operation);
|
|
135
|
+
}
|
|
136
|
+
return stats;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
clear(): void {
|
|
140
|
+
this.metrics.clear();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const performanceTracker = new PerformanceTracker();
|
|
145
|
+
|