@umituz/react-native-ai-gemini-provider 3.0.0 → 3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-gemini-provider",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "Google Gemini AI text generation provider for React Native applications",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -16,11 +16,3 @@ export const DEFAULT_MODELS = {
16
16
  /** Default model for text generation */
17
17
  TEXT: GEMINI_MODELS.TEXT.FLASH_LITE,
18
18
  } as const;
19
-
20
- /**
21
- * Pricing information for Gemini models
22
- * Prices are per 1M tokens (USD)
23
- */
24
- export const MODEL_PRICING = {
25
- [GEMINI_MODELS.TEXT.FLASH_LITE]: { input: 0.10, output: 0.40, freePerDay: 1000 },
26
- } as const;
@@ -3,4 +3,3 @@ export { textGeneration } from "./TextGeneration";
3
3
  export { structuredText } from "./StructuredText";
4
4
  export { streaming } from "./Streaming";
5
5
  export { geminiProvider, GeminiProvider } from "./GeminiProvider";
6
- export { BaseGeminiService, type BaseRequestOptions } from "./BaseService";
@@ -3,4 +3,3 @@
3
3
  */
4
4
 
5
5
  export { telemetryHooks } from "./TelemetryHooks";
6
- export type { TelemetryEvent, TelemetryListener } from "./TelemetryHooks";
@@ -4,7 +4,5 @@
4
4
 
5
5
  export {
6
6
  executeWithState,
7
- type AsyncStateCallbacks,
8
7
  type AsyncStateSetters,
9
- type AsyncStateConfig,
10
8
  } from "./execute-state.util";
@@ -76,7 +76,7 @@ export function createTextContent(
76
76
  /**
77
77
  * Transform SDK candidate to domain format
78
78
  */
79
- export function transformCandidate(
79
+ function transformCandidate(
80
80
  candidate: {
81
81
  content: { parts: Array<{ text?: string }>; role?: string };
82
82
  finishReason?: string;
@@ -87,7 +87,7 @@ function matchesPattern(message: string, patterns: string[]): boolean {
87
87
  });
88
88
  }
89
89
 
90
- export function mapGeminiError(error: unknown): GeminiErrorInfo {
90
+ function mapGeminiError(error: unknown): GeminiErrorInfo {
91
91
  const message = error instanceof Error ? error.message : String(error);
92
92
  const statusCode = getStatusCode(error);
93
93
 
@@ -114,14 +114,6 @@ export function mapGeminiError(error: unknown): GeminiErrorInfo {
114
114
  };
115
115
  }
116
116
 
117
- export function isGeminiErrorRetryable(error: unknown): boolean {
118
- return mapGeminiError(error).retryable;
119
- }
120
-
121
- export function categorizeGeminiError(error: unknown): GeminiErrorType {
122
- return mapGeminiError(error).type;
123
- }
124
-
125
117
  export function createGeminiError(error: unknown): GeminiError {
126
118
  const errorInfo = mapGeminiError(error);
127
119
  return GeminiError.fromError(error, errorInfo);
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * Clean JSON text by removing markdown code blocks and extra whitespace
8
8
  */
9
- export function cleanJsonText(text: string): string {
9
+ function cleanJsonText(text: string): string {
10
10
  if (!text || typeof text !== "string") {
11
11
  return "";
12
12
  }
@@ -39,52 +39,3 @@ export function parseJsonResponse<T>(text: string): T {
39
39
  }
40
40
  }
41
41
 
42
- /**
43
- * Safely parse JSON with optional fallback value
44
- */
45
- export function safeParseJson<T>(
46
- text: string,
47
- fallback: T
48
- ): T {
49
- try {
50
- return parseJsonResponse<T>(text);
51
- } catch {
52
- return fallback;
53
- }
54
- }
55
-
56
- /**
57
- * Extract and parse JSON from a larger text response
58
- * Looks for JSON objects within markdown code blocks or standalone
59
- */
60
- export function extractJsonFromText<T>(text: string): T | null {
61
- // Try to find JSON in code blocks first
62
- const codeBlockMatch = text.match(/```json\s*([\s\S]*?)\s*```/);
63
- if (codeBlockMatch) {
64
- try {
65
- return JSON.parse(codeBlockMatch[1].trim()) as T;
66
- } catch {
67
- // Continue to other methods
68
- }
69
- }
70
-
71
- // Try to find JSON object boundaries
72
- const firstBrace = text.indexOf("{");
73
- const lastBrace = text.lastIndexOf("}");
74
-
75
- if (firstBrace !== -1 && lastBrace !== -1 && lastBrace > firstBrace) {
76
- try {
77
- const jsonStr = text.substring(firstBrace, lastBrace + 1);
78
- return JSON.parse(jsonStr) as T;
79
- } catch {
80
- // Continue to fallback
81
- }
82
- }
83
-
84
- // Try parsing the whole text as JSON
85
- try {
86
- return parseJsonResponse<T>(text);
87
- } catch {
88
- return null;
89
- }
90
- }
@@ -3,12 +3,12 @@
3
3
  * Reusable stream handling logic
4
4
  */
5
5
 
6
- export interface StreamChunk {
6
+ interface StreamChunk {
7
7
  text: () => string;
8
8
  }
9
9
 
10
- export type ChunkCallback = (text: string) => void;
11
- export type ErrorLogger = (error: unknown, context?: string) => void;
10
+ type ChunkCallback = (text: string) => void;
11
+ type ErrorLogger = (error: unknown, context?: string) => void;
12
12
 
13
13
  /**
14
14
  * Process async stream with chunk callback
@@ -67,90 +67,3 @@ function logError(
67
67
  }
68
68
  }
69
69
 
70
- /**
71
- * Create a buffered stream processor
72
- * Accumulates chunks until a condition is met
73
- */
74
- export class BufferedStreamProcessor {
75
- private buffer = "";
76
- private fullText = "";
77
-
78
- constructor(
79
- private onFlush: (text: string) => void,
80
- private flushCondition: (buffer: string) => boolean = () => false
81
- ) {}
82
-
83
- /**
84
- * Process a single chunk
85
- */
86
- processChunk(chunk: StreamChunk): void {
87
- try {
88
- const chunkText = chunk.text();
89
- if (!chunkText) return;
90
-
91
- this.buffer += chunkText;
92
- this.fullText += chunkText;
93
-
94
- if (this.flushCondition(this.buffer)) {
95
- this.flush();
96
- }
97
- } catch (error) {
98
- // Ignore chunk errors
99
- }
100
- }
101
-
102
- /**
103
- * Flush buffer to callback
104
- */
105
- flush(): void {
106
- if (this.buffer) {
107
- try {
108
- this.onFlush(this.buffer);
109
- this.buffer = "";
110
- } catch {
111
- // Ignore callback errors
112
- }
113
- }
114
- }
115
-
116
- /**
117
- * Get accumulated full text
118
- */
119
- getFullText(): string {
120
- return this.fullText;
121
- }
122
-
123
- /**
124
- * Process entire stream
125
- */
126
- async processStream(stream: AsyncIterable<StreamChunk>): Promise<string> {
127
- for await (const chunk of stream) {
128
- this.processChunk(chunk);
129
- }
130
-
131
- // Flush remaining buffer
132
- this.flush();
133
-
134
- return this.fullText;
135
- }
136
- }
137
-
138
- /**
139
- * Common flush conditions
140
- */
141
- export const flushConditions = {
142
- /**
143
- * Flush on newline
144
- */
145
- onNewline: (buffer: string): boolean => buffer.includes("\n"),
146
-
147
- /**
148
- * Flush when buffer reaches size
149
- */
150
- onSize: (size: number) => (buffer: string): boolean => buffer.length >= size,
151
-
152
- /**
153
- * Flush on pattern match
154
- */
155
- onPattern: (pattern: RegExp) => (buffer: string): boolean => pattern.test(buffer),
156
- };
@@ -1,2 +1 @@
1
- export { useOperationManager, type OperationManager } from "./useOperationManager";
2
1
  export { useGemini, type UseGeminiOptions, type UseGeminiReturn } from "./useGemini";
@@ -1,78 +0,0 @@
1
- /**
2
- * Base Interceptor Class
3
- * Eliminates code duplication between Request and Response interceptors
4
- */
5
-
6
- import { telemetryHooks } from "../telemetry";
7
-
8
- export type InterceptorErrorStrategy = "fail" | "skip" | "log";
9
-
10
- export interface BaseContext {
11
- model: string;
12
- feature?: string;
13
- timestamp: number;
14
- }
15
-
16
- export abstract class BaseInterceptor<TContext extends BaseContext> {
17
- protected interceptors: Array<(context: TContext) => TContext | Promise<TContext>> = [];
18
- protected errorStrategy: InterceptorErrorStrategy = "fail";
19
-
20
- /**
21
- * Register an interceptor
22
- */
23
- use(interceptor: (context: TContext) => TContext | Promise<TContext>): () => void {
24
- this.interceptors.push(interceptor);
25
-
26
- // Return unsubscribe function
27
- return () => {
28
- const index = this.interceptors.indexOf(interceptor);
29
- if (index > -1) {
30
- this.interceptors.splice(index, 1);
31
- }
32
- };
33
- }
34
-
35
- /**
36
- * Set error handling strategy
37
- */
38
- setErrorStrategy(strategy: InterceptorErrorStrategy): void {
39
- this.errorStrategy = strategy;
40
- }
41
-
42
- /**
43
- * Clear all interceptors
44
- */
45
- clear(): void {
46
- this.interceptors = [];
47
- }
48
-
49
- /**
50
- * Get interceptor count
51
- */
52
- count(): number {
53
- return this.interceptors.length;
54
- }
55
-
56
- /**
57
- * Handle interceptor error based on strategy
58
- */
59
- protected handleError(context: TContext, error: unknown): void {
60
- telemetryHooks.logError(
61
- context.model,
62
- error instanceof Error ? error : new Error(String(error)),
63
- context.feature
64
- );
65
-
66
- if (this.errorStrategy === "fail") {
67
- throw new Error(
68
- `Interceptor failed: ${error instanceof Error ? error.message : String(error)}`
69
- );
70
- }
71
- // For "skip" and "log", we just continue (error already logged)
72
- }
73
-
74
- /**
75
- * Apply interceptors - to be implemented by subclasses
76
- */
77
- abstract apply(context: TContext): Promise<TContext>;
78
- }
@@ -1,30 +0,0 @@
1
- import { BaseInterceptor, type BaseContext } from "./BaseInterceptor";
2
-
3
- export interface RequestContext extends BaseContext {
4
- payload: Record<string, unknown>;
5
- }
6
-
7
- export type RequestInterceptor = (context: RequestContext) => RequestContext | Promise<RequestContext>;
8
-
9
- class RequestInterceptors extends BaseInterceptor<RequestContext> {
10
- /**
11
- * Apply all interceptors to a request context
12
- * Interceptors are called in order (first registered = first called)
13
- */
14
- async apply(context: RequestContext): Promise<RequestContext> {
15
- let result = context;
16
-
17
- for (const interceptor of this.interceptors) {
18
- try {
19
- result = await interceptor(result);
20
- } catch (error) {
21
- this.handleError(context, error);
22
- // If we get here, strategy was "skip" or "log" - continue with previous result
23
- }
24
- }
25
-
26
- return result;
27
- }
28
- }
29
-
30
- export const requestInterceptors = new RequestInterceptors();
@@ -1,35 +0,0 @@
1
- import { BaseInterceptor, type BaseContext } from "./BaseInterceptor";
2
-
3
- export interface ResponseContext<T = unknown> extends BaseContext {
4
- data: T;
5
- duration: number;
6
- }
7
-
8
- export type ResponseInterceptor<T = unknown> = (
9
- context: ResponseContext<T>,
10
- ) => ResponseContext<T> | Promise<ResponseContext<T>>;
11
-
12
- class ResponseInterceptors extends BaseInterceptor<ResponseContext<unknown>> {
13
- /**
14
- * Apply all interceptors to a response context
15
- * Interceptors are called in reverse order (last registered = first called)
16
- */
17
- async apply<T>(context: ResponseContext<T>): Promise<ResponseContext<T>> {
18
- let result: ResponseContext<unknown> = context;
19
-
20
- // Apply in reverse order (last added = first processed)
21
- for (let i = this.interceptors.length - 1; i >= 0; i--) {
22
- const interceptor = this.interceptors[i];
23
- try {
24
- result = await interceptor(result);
25
- } catch (error) {
26
- this.handleError(context, error);
27
- // If we get here, strategy was "skip" or "log" - continue with previous result
28
- }
29
- }
30
-
31
- return result as ResponseContext<T>;
32
- }
33
- }
34
-
35
- export const responseInterceptors = new ResponseInterceptors();
@@ -1,12 +0,0 @@
1
- /**
2
- * Interceptors Module - Internal Use Only
3
- */
4
-
5
- export { BaseInterceptor } from "./BaseInterceptor";
6
- export type { BaseContext, InterceptorErrorStrategy } from "./BaseInterceptor";
7
-
8
- export { requestInterceptors } from "./RequestInterceptors";
9
- export type { RequestContext, RequestInterceptor } from "./RequestInterceptors";
10
-
11
- export { responseInterceptors } from "./ResponseInterceptors";
12
- export type { ResponseContext, ResponseInterceptor } from "./ResponseInterceptors";
@@ -1,41 +0,0 @@
1
- export {
2
- mapGeminiError,
3
- isGeminiErrorRetryable,
4
- categorizeGeminiError,
5
- createGeminiError
6
- } from "./error-mapper.util";
7
-
8
- export { extractTextFromResponse } from "./gemini-data-transformer.util";
9
-
10
- export {
11
- cleanJsonText,
12
- parseJsonResponse,
13
- safeParseJson,
14
- extractJsonFromText
15
- } from "./json-parser.util";
16
-
17
- export {
18
- toSdkContent,
19
- createTextContent,
20
- transformCandidate,
21
- transformResponse,
22
- extractTextFromParts
23
- } from "./content-mapper.util";
24
-
25
- export {
26
- validateModelName,
27
- validateApiKey,
28
- validateSchema,
29
- validatePrompt,
30
- validateOrThrow,
31
- validators,
32
- compose,
33
- type ValidationRule,
34
- } from "./validation";
35
-
36
- export {
37
- executeWithState,
38
- type AsyncStateCallbacks,
39
- type AsyncStateSetters,
40
- type AsyncStateConfig
41
- } from "./async";
@@ -1,175 +0,0 @@
1
- /**
2
- * Validation utilities
3
- */
4
-
5
- export type ValidationRule<T = unknown> = (value: T) => string | null;
6
-
7
- /**
8
- * Compose multiple validation rules into one
9
- */
10
- export function compose<T>(...rules: ValidationRule<T>[]): ValidationRule<T> {
11
- return (value: T): string | null => {
12
- for (const rule of rules) {
13
- const error = rule(value);
14
- if (error) return error;
15
- }
16
- return null;
17
- };
18
- }
19
-
20
- /**
21
- * Validate that value is not empty
22
- */
23
- export function required(fieldName: string = "Field"): ValidationRule<string> {
24
- return (value: string): string | null => {
25
- if (!value || typeof value !== "string" || value.trim().length === 0) {
26
- return `${fieldName} is required`;
27
- }
28
- return null;
29
- };
30
- }
31
-
32
- /**
33
- * Validate minimum length
34
- */
35
- export function minLength(min: number, fieldName: string = "Field"): ValidationRule<string> {
36
- return (value: string): string | null => {
37
- if (value.trim().length < min) {
38
- return `${fieldName} must be at least ${min} characters`;
39
- }
40
- return null;
41
- };
42
- }
43
-
44
- /**
45
- * Validate maximum length
46
- */
47
- export function maxLength(max: number, fieldName: string = "Field"): ValidationRule<string> {
48
- return (value: string): string | null => {
49
- if (value.length > max) {
50
- return `${fieldName} must be at most ${max} characters`;
51
- }
52
- return null;
53
- };
54
- }
55
-
56
- /**
57
- * Validate string starts with prefix
58
- */
59
- export function startsWith(prefix: string, fieldName: string = "Field"): ValidationRule<string> {
60
- return (value: string): string | null => {
61
- if (!value.startsWith(prefix)) {
62
- return `${fieldName} must start with "${prefix}"`;
63
- }
64
- return null;
65
- };
66
- }
67
-
68
- /**
69
- * Validate number is in range
70
- */
71
- export function inRange(min: number, max: number, fieldName: string = "Value"): ValidationRule<number> {
72
- return (value: number): string | null => {
73
- if (typeof value !== "number" || value < min || value > max) {
74
- return `${fieldName} must be between ${min} and ${max}`;
75
- }
76
- return null;
77
- };
78
- }
79
-
80
- /**
81
- * Validate object has required properties
82
- */
83
- export function hasProperties(...props: string[]): ValidationRule<Record<string, unknown>> {
84
- return (value: Record<string, unknown>): string | null => {
85
- const missing = props.filter((prop) => !(prop in value) || value[prop] === undefined);
86
- if (missing.length > 0) {
87
- return `Missing required properties: ${missing.join(", ")}`;
88
- }
89
- return null;
90
- };
91
- }
92
-
93
- /**
94
- * Validate object structure (for schemas)
95
- */
96
- export function isValidSchema(): ValidationRule<Record<string, unknown>> {
97
- return compose(
98
- (schema): string | null => {
99
- if (!schema || typeof schema !== "object") {
100
- return "Schema must be a non-empty object";
101
- }
102
- return null;
103
- },
104
- (schema): string | null => {
105
- if (Object.keys(schema).length === 0) {
106
- return "Schema must contain at least one property";
107
- }
108
- return null;
109
- },
110
- hasProperties("type"),
111
- (schema): string | null => {
112
- const schemaType = schema.type;
113
- if (schemaType !== "object" && schemaType !== "array") {
114
- return `Schema type must be "object" or "array", got "${String(schemaType)}"`;
115
- }
116
- return null;
117
- },
118
- (schema): string | null => {
119
- if (schema.type === "object" && !("properties" in schema)) {
120
- return 'Object schema must have a "properties" field';
121
- }
122
- return null;
123
- }
124
- );
125
- }
126
-
127
- /**
128
- * Helper to validate and throw on error
129
- */
130
- export function validateOrThrow<T>(value: T, rule: ValidationRule<T>): void {
131
- const error = rule(value);
132
- if (error) {
133
- throw new Error(error);
134
- }
135
- }
136
-
137
- /**
138
- * Pre-built composite validators
139
- */
140
- export const validators = {
141
- apiKey: compose(
142
- required("API key"),
143
- minLength(10, "API key")
144
- ),
145
-
146
- modelName: compose(
147
- required("Model name"),
148
- startsWith("gemini-", "Model name")
149
- ),
150
-
151
- prompt: compose(
152
- required("Prompt"),
153
- minLength(3, "Prompt")
154
- ),
155
-
156
- timeout: inRange(1, 300000, "Timeout"),
157
-
158
- schema: isValidSchema(),
159
- };
160
-
161
- export function validateModelName(modelName: string): void {
162
- validateOrThrow(modelName, validators.modelName);
163
- }
164
-
165
- export function validateApiKey(apiKey: string): void {
166
- validateOrThrow(apiKey, validators.apiKey);
167
- }
168
-
169
- export function validateSchema(schema: Record<string, unknown>): void {
170
- validateOrThrow(schema, validators.schema);
171
- }
172
-
173
- export function validatePrompt(prompt: string): void {
174
- validateOrThrow(prompt, validators.prompt);
175
- }