@umituz/web-ai 0.0.0-stage → 1.0.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.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -2
  3. package/dist/ABTesting-DDqNhHtP.d.ts +184 -0
  4. package/dist/ABTesting-DWBtKTMQ.d.cts +184 -0
  5. package/dist/ContentGeneration-B6iK2B33.d.cts +93 -0
  6. package/dist/ContentGeneration-B6iK2B33.d.ts +93 -0
  7. package/dist/ProviderConfig-a55tLhWT.d.cts +99 -0
  8. package/dist/ProviderConfig-a55tLhWT.d.ts +99 -0
  9. package/dist/cache-manager-Df9LLYGA.d.cts +31 -0
  10. package/dist/cache-manager-Df9LLYGA.d.ts +31 -0
  11. package/dist/chat/index.cjs +534 -0
  12. package/dist/chat/index.cjs.map +1 -0
  13. package/dist/chat/index.d.cts +25 -0
  14. package/dist/chat/index.d.ts +25 -0
  15. package/dist/chat/index.js +527 -0
  16. package/dist/chat/index.js.map +1 -0
  17. package/dist/chat.service-DBKq9I2c.d.cts +124 -0
  18. package/dist/chat.service-RQn8UTXD.d.ts +124 -0
  19. package/dist/content/index.cjs +3769 -0
  20. package/dist/content/index.cjs.map +1 -0
  21. package/dist/content/index.d.cts +225 -0
  22. package/dist/content/index.d.ts +225 -0
  23. package/dist/content/index.js +3738 -0
  24. package/dist/content/index.js.map +1 -0
  25. package/dist/core/index.cjs +1040 -0
  26. package/dist/core/index.cjs.map +1 -0
  27. package/dist/core/index.d.cts +232 -0
  28. package/dist/core/index.d.ts +232 -0
  29. package/dist/core/index.js +969 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/create-ai-BhNaz86i.d.ts +268 -0
  32. package/dist/create-ai-DGZFBOA1.d.cts +268 -0
  33. package/dist/hooks/index.cjs +5056 -0
  34. package/dist/hooks/index.cjs.map +1 -0
  35. package/dist/hooks/index.d.cts +210 -0
  36. package/dist/hooks/index.d.ts +210 -0
  37. package/dist/hooks/index.js +5045 -0
  38. package/dist/hooks/index.js.map +1 -0
  39. package/dist/index-DjX9ZKuM.d.cts +14 -0
  40. package/dist/index-DxLvE588.d.ts +14 -0
  41. package/dist/index-fXjxTR5W.d.cts +66 -0
  42. package/dist/index-fXjxTR5W.d.ts +66 -0
  43. package/dist/index.cjs +3158 -0
  44. package/dist/index.cjs.map +1 -0
  45. package/dist/index.d.cts +11 -0
  46. package/dist/index.d.ts +11 -0
  47. package/dist/index.js +3048 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/provider.factory-COsnprLH.d.cts +110 -0
  50. package/dist/provider.factory-CpWFyjbw.d.ts +110 -0
  51. package/dist/providers/index.cjs +2410 -0
  52. package/dist/providers/index.cjs.map +1 -0
  53. package/dist/providers/index.d.cts +333 -0
  54. package/dist/providers/index.d.ts +333 -0
  55. package/dist/providers/index.js +2376 -0
  56. package/dist/providers/index.js.map +1 -0
  57. package/dist/request-deduplicator.util-NDo0_n1y.d.cts +50 -0
  58. package/dist/request-deduplicator.util-NDo0_n1y.d.ts +50 -0
  59. package/dist/wizard/index.cjs +230 -0
  60. package/dist/wizard/index.cjs.map +1 -0
  61. package/dist/wizard/index.d.cts +86 -0
  62. package/dist/wizard/index.d.ts +86 -0
  63. package/dist/wizard/index.js +223 -0
  64. package/dist/wizard/index.js.map +1 -0
  65. package/package.json +111 -4
@@ -0,0 +1,969 @@
1
+ // src/core/tiers/index.ts
2
+ var TEXT_TIERS = ["fast", "balanced", "powerful"];
3
+ function isTextTier(value) {
4
+ return typeof value === "string" && TEXT_TIERS.includes(value);
5
+ }
6
+ function resolveModel(tiers, tier, providerId) {
7
+ const model = tiers[tier] ?? tiers.balanced;
8
+ if (!model) {
9
+ throw new Error(
10
+ `${providerId} has no model configured for tier "${tier}" \u2014 pass an explicit model or configure the tier map`
11
+ );
12
+ }
13
+ return model;
14
+ }
15
+ function mergeTierOverrides(defaults, overrides) {
16
+ if (!overrides) return defaults;
17
+ return { ...defaults, ...overrides };
18
+ }
19
+
20
+ // src/core/errors/ai-error-types.ts
21
+ var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
22
+ AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
23
+ AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
24
+ AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
25
+ AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
26
+ AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
27
+ AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
28
+ AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
29
+ AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
30
+ return AIErrorType2;
31
+ })(AIErrorType || {});
32
+ var USER_FRIENDLY_ERROR_MESSAGES = {
33
+ ["INVALID_API_KEY" /* INVALID_API_KEY */]: "Please check your API key and try again.",
34
+ ["MISSING_CONFIG" /* MISSING_CONFIG */]: "The provider is not configured. Please create a provider with createAI first.",
35
+ ["NETWORK_ERROR" /* NETWORK_ERROR */]: "Network error. Please check your connection and try again.",
36
+ ["ABORT_ERROR" /* ABORT_ERROR */]: "Request was cancelled.",
37
+ ["RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */]: "You've reached the rate limit. Please wait a moment and try again.",
38
+ ["SERVER_ERROR" /* SERVER_ERROR */]: "The AI service is temporarily unavailable. Please try again later.",
39
+ ["CLIENT_ERROR" /* CLIENT_ERROR */]: "The request was invalid. Please check your input and try again.",
40
+ ["UNKNOWN_ERROR" /* UNKNOWN_ERROR */]: "An unexpected error occurred. Please try again."
41
+ };
42
+
43
+ // src/core/errors/ai-error.ts
44
+ var AIError = class extends Error {
45
+ type;
46
+ cause;
47
+ metadata;
48
+ constructor(type, message, cause, metadata = {}) {
49
+ super(message);
50
+ this.name = "AIError";
51
+ this.type = type;
52
+ this.cause = cause;
53
+ this.metadata = metadata;
54
+ }
55
+ toString() {
56
+ const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
57
+ return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
58
+ }
59
+ toJSON() {
60
+ return {
61
+ name: this.name,
62
+ type: this.type,
63
+ message: this.message,
64
+ metadata: this.metadata
65
+ };
66
+ }
67
+ };
68
+
69
+ // src/core/errors/error-classifier.ts
70
+ var DEFAULT_RETRYABLE_TYPES = /* @__PURE__ */ new Set([
71
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
72
+ "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */,
73
+ "SERVER_ERROR" /* SERVER_ERROR */
74
+ ]);
75
+ var DEFAULT_AUTH_TYPES = /* @__PURE__ */ new Set([
76
+ "INVALID_API_KEY" /* INVALID_API_KEY */
77
+ ]);
78
+ var DEFAULT_ERROR_CLASSIFICATION_RULE = {
79
+ retryable: DEFAULT_RETRYABLE_TYPES,
80
+ auth: DEFAULT_AUTH_TYPES
81
+ };
82
+ function isRetryableErrorType(type, rule = DEFAULT_ERROR_CLASSIFICATION_RULE) {
83
+ return rule.retryable.has(type);
84
+ }
85
+ function isAuthErrorType(type, rule = DEFAULT_ERROR_CLASSIFICATION_RULE) {
86
+ return rule.auth.has(type);
87
+ }
88
+
89
+ // src/core/errors/http-status-classifier.ts
90
+ var DEFAULT_HTTP_STATUS_RULE = {
91
+ unauthorizedStatuses: /* @__PURE__ */ new Set([401, 403]),
92
+ rateLimitedStatus: 429,
93
+ serverErrorThreshold: 500,
94
+ clientErrorThreshold: 400
95
+ };
96
+ function classifyHttpStatus(status, rule = DEFAULT_HTTP_STATUS_RULE) {
97
+ if (rule.unauthorizedStatuses.has(status)) return "INVALID_API_KEY" /* INVALID_API_KEY */;
98
+ if (status === rule.rateLimitedStatus) return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
99
+ if (status >= rule.serverErrorThreshold) return "SERVER_ERROR" /* SERVER_ERROR */;
100
+ if (status >= rule.clientErrorThreshold) return "CLIENT_ERROR" /* CLIENT_ERROR */;
101
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
102
+ }
103
+
104
+ // src/core/errors/user-friendly-error-resolver.ts
105
+ var KEYWORD_FALLBACK_RULES = [
106
+ { keywords: ["api key", "unauthorized"], type: "INVALID_API_KEY" /* INVALID_API_KEY */ },
107
+ { keywords: ["rate limit"], type: "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */ },
108
+ { keywords: ["network", "fetch"], type: "NETWORK_ERROR" /* NETWORK_ERROR */ },
109
+ { keywords: ["abort"], type: "ABORT_ERROR" /* ABORT_ERROR */ }
110
+ ];
111
+ function resolveUserFriendlyErrorMessage(error) {
112
+ if (error instanceof AIError) {
113
+ return USER_FRIENDLY_ERROR_MESSAGES[error.type];
114
+ }
115
+ if (error instanceof Error) {
116
+ const lower = error.message.toLowerCase();
117
+ for (const rule of KEYWORD_FALLBACK_RULES) {
118
+ if (rule.keywords.some((kw) => lower.includes(kw))) {
119
+ return USER_FRIENDLY_ERROR_MESSAGES[rule.type];
120
+ }
121
+ }
122
+ if (error.message.length > 0) return error.message;
123
+ }
124
+ return USER_FRIENDLY_ERROR_MESSAGES["UNKNOWN_ERROR" /* UNKNOWN_ERROR */];
125
+ }
126
+
127
+ // src/core/errors/error-resolver.ts
128
+ var resolveUserFriendlyMessage = resolveUserFriendlyErrorMessage;
129
+ function formatErrorForLogging(error) {
130
+ if (error instanceof AIError) {
131
+ return {
132
+ message: error.message,
133
+ type: error.type,
134
+ stack: error.stack,
135
+ metadata: error.metadata
136
+ };
137
+ }
138
+ if (error instanceof Error) {
139
+ return { message: error.message, type: error.name, stack: error.stack };
140
+ }
141
+ return { message: String(error), type: typeof error };
142
+ }
143
+
144
+ // src/core/errors/response-error-message-extractor.ts
145
+ function extractErrorMessageFromJsonBody(body) {
146
+ if (!body || typeof body !== "object") return null;
147
+ const candidate = body;
148
+ const message = candidate.error?.message ?? candidate.message;
149
+ if (typeof message === "string" && message.length > 0) return message;
150
+ return null;
151
+ }
152
+ function buildHttpStatusFallback(status, statusText) {
153
+ const trimmedStatusText = statusText.trim();
154
+ if (trimmedStatusText.length > 0) {
155
+ return `HTTP ${status}: ${trimmedStatusText}`;
156
+ }
157
+ return `HTTP ${status}`;
158
+ }
159
+ async function extractErrorMessageFromResponse(response) {
160
+ const body = await response.json().catch(() => null);
161
+ const fromBody = extractErrorMessageFromJsonBody(body);
162
+ if (fromBody) return fromBody;
163
+ return buildHttpStatusFallback(response.status, response.statusText);
164
+ }
165
+ function describeAIErrorType(type) {
166
+ return `AIError[${type}]`;
167
+ }
168
+
169
+ // src/core/executor/retry-executor.strategy.ts
170
+ var RetryExecutor = class {
171
+ constructor(retryManager2, options) {
172
+ this.retryManager = retryManager2;
173
+ this.options = options;
174
+ }
175
+ retryManager;
176
+ options;
177
+ execute(fn, ...args) {
178
+ return this.retryManager.execute(() => fn(...args), this.options);
179
+ }
180
+ };
181
+
182
+ // src/core/executor/queue-executor.strategy.ts
183
+ var QueueExecutor = class {
184
+ constructor(queue, priority) {
185
+ this.queue = queue;
186
+ this.priority = priority;
187
+ }
188
+ queue;
189
+ priority;
190
+ execute(fn, ...args) {
191
+ return this.queue.add(() => fn(...args), this.priority);
192
+ }
193
+ };
194
+
195
+ // src/core/executor/dedupe-executor.strategy.ts
196
+ var DedupeExecutor = class {
197
+ constructor(deduplicator, options) {
198
+ this.deduplicator = deduplicator;
199
+ this.options = options;
200
+ }
201
+ deduplicator;
202
+ options;
203
+ execute(fn, ...args) {
204
+ const key = this.options.generateKey(...args);
205
+ return this.deduplicator.execute(key, () => fn(...args));
206
+ }
207
+ };
208
+
209
+ // src/core/executor/composite-executor.strategy.ts
210
+ var CompositeExecutor = class {
211
+ executors;
212
+ constructor(executors) {
213
+ this.executors = executors;
214
+ }
215
+ execute(fn, ...args) {
216
+ const composed = this.compress(fn);
217
+ return composed(...args);
218
+ }
219
+ compress(fn) {
220
+ return this.executors.reduceRight(
221
+ (innerFn, executor) => (...a) => executor.execute(innerFn, ...a),
222
+ fn
223
+ );
224
+ }
225
+ };
226
+
227
+ // src/core/calculations/lru-key-selector.ts
228
+ function selectLeastRecentlyUsedKey(entries) {
229
+ let oldestKey = null;
230
+ let oldestAccess = Number.POSITIVE_INFINITY;
231
+ for (const [key, entry] of entries.entries()) {
232
+ if (entry.lastAccess < oldestAccess) {
233
+ oldestAccess = entry.lastAccess;
234
+ oldestKey = key;
235
+ }
236
+ }
237
+ return oldestKey;
238
+ }
239
+
240
+ // src/core/calculations/cache-hit-rate.ts
241
+ function calculateCacheHitRate(entries) {
242
+ if (entries.length === 0) return 0;
243
+ const totalAccess = entries.reduce((sum, e) => sum + e.accessCount, 0);
244
+ return totalAccess / entries.length;
245
+ }
246
+
247
+ // src/core/utils/cache-key-builder.ts
248
+ function buildCacheKey(params) {
249
+ const sortedKeys = Object.keys(params).sort();
250
+ const sortedParams = {};
251
+ for (const key of sortedKeys) {
252
+ sortedParams[key] = params[key];
253
+ }
254
+ return JSON.stringify(sortedParams);
255
+ }
256
+
257
+ // src/core/utils/cache-manager.ts
258
+ var DEFAULT_CACHE_MAX_SIZE = 100;
259
+ var DEFAULT_CACHE_TTL_MS = 3e5;
260
+ var CacheManager = class {
261
+ cache = /* @__PURE__ */ new Map();
262
+ maxSize;
263
+ ttl;
264
+ constructor(options = {}) {
265
+ this.maxSize = options.maxSize ?? DEFAULT_CACHE_MAX_SIZE;
266
+ this.ttl = options.ttl ?? DEFAULT_CACHE_TTL_MS;
267
+ }
268
+ get(key) {
269
+ const entry = this.cache.get(key);
270
+ if (!entry) return null;
271
+ if (this.isExpired(entry)) {
272
+ this.cache.delete(key);
273
+ return null;
274
+ }
275
+ entry.accessCount += 1;
276
+ entry.lastAccess = Date.now();
277
+ return entry.value;
278
+ }
279
+ set(key, value) {
280
+ if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
281
+ const lruKey = selectLeastRecentlyUsedKey(this.cache);
282
+ if (lruKey !== null) this.cache.delete(lruKey);
283
+ }
284
+ this.cache.set(key, {
285
+ value,
286
+ timestamp: Date.now(),
287
+ accessCount: 1,
288
+ lastAccess: Date.now()
289
+ });
290
+ }
291
+ has(key) {
292
+ const entry = this.cache.get(key);
293
+ return entry !== void 0 && !this.isExpired(entry);
294
+ }
295
+ delete(key) {
296
+ return this.cache.delete(key);
297
+ }
298
+ clear() {
299
+ this.cache.clear();
300
+ }
301
+ generateKey(params) {
302
+ return buildCacheKey(params);
303
+ }
304
+ getStats() {
305
+ const now = Date.now();
306
+ const entries = Array.from(this.cache.entries()).map(([key, entry]) => ({
307
+ key,
308
+ accessCount: entry.accessCount,
309
+ ageMs: now - entry.timestamp
310
+ }));
311
+ const hitRate = calculateCacheHitRate(
312
+ Array.from(this.cache.values()).map((e) => ({ accessCount: e.accessCount }))
313
+ );
314
+ return {
315
+ size: this.cache.size,
316
+ maxSize: this.maxSize,
317
+ hitRate,
318
+ entries
319
+ };
320
+ }
321
+ isExpired(entry) {
322
+ return Date.now() - entry.timestamp > this.ttl;
323
+ }
324
+ };
325
+ var cacheManager = new CacheManager();
326
+
327
+ // src/core/calculations/exponential-backoff.ts
328
+ function calculateExponentialBackoff(params) {
329
+ const {
330
+ attempt,
331
+ initialDelay,
332
+ maxDelay,
333
+ backoffMultiplier,
334
+ jitterFactor,
335
+ random = Math.random
336
+ } = params;
337
+ const base = initialDelay * Math.pow(backoffMultiplier, Math.max(0, attempt - 1));
338
+ const capped = Math.min(base, maxDelay);
339
+ const jitterMultiplier = random() * 2 - 1;
340
+ const jitter = capped * jitterFactor * jitterMultiplier;
341
+ return Math.max(0, capped + jitter);
342
+ }
343
+
344
+ // src/core/constants/identifier.constants.ts
345
+ var DEFAULT_RETRY_MAX_ATTEMPTS = 3;
346
+ var DEFAULT_RETRY_INITIAL_DELAY_MS = 1e3;
347
+ var DEFAULT_RETRY_MAX_DELAY_MS = 1e4;
348
+ var DEFAULT_RETRY_BACKOFF_MULTIPLIER = 2;
349
+ var DEFAULT_RETRY_JITTER_FACTOR = 0.1;
350
+
351
+ // src/core/utils/retry.ts
352
+ var RETRYABLE_MESSAGE_KEYWORDS = [
353
+ "network",
354
+ "timeout",
355
+ "econnrefused",
356
+ "enotfound",
357
+ "etimedout"
358
+ ];
359
+ var RetryManager = class {
360
+ defaults;
361
+ constructor() {
362
+ this.defaults = {
363
+ maxAttempts: DEFAULT_RETRY_MAX_ATTEMPTS,
364
+ initialDelay: DEFAULT_RETRY_INITIAL_DELAY_MS,
365
+ maxDelay: DEFAULT_RETRY_MAX_DELAY_MS,
366
+ backoffMultiplier: DEFAULT_RETRY_BACKOFF_MULTIPLIER,
367
+ jitterFactor: DEFAULT_RETRY_JITTER_FACTOR
368
+ };
369
+ }
370
+ async execute(fn, options = {}) {
371
+ const maxAttempts = options.maxAttempts ?? this.defaults.maxAttempts;
372
+ let lastError;
373
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
374
+ try {
375
+ return await fn();
376
+ } catch (error) {
377
+ lastError = error instanceof Error ? error : new Error(String(error));
378
+ if (attempt >= maxAttempts || !this.shouldRetry(lastError, options)) {
379
+ throw lastError;
380
+ }
381
+ const delay = calculateExponentialBackoff({
382
+ attempt,
383
+ initialDelay: options.initialDelay ?? this.defaults.initialDelay,
384
+ maxDelay: options.maxDelay ?? this.defaults.maxDelay,
385
+ backoffMultiplier: options.backoffMultiplier ?? this.defaults.backoffMultiplier,
386
+ jitterFactor: options.jitterFactor ?? this.defaults.jitterFactor
387
+ });
388
+ await this.delay(delay);
389
+ }
390
+ }
391
+ throw lastError ?? new Error("Retry loop exited without result");
392
+ }
393
+ shouldRetry(error, options) {
394
+ if (options.shouldRetry) return options.shouldRetry(error);
395
+ if (error instanceof AIError) {
396
+ return isRetryableErrorType(error.type, DEFAULT_ERROR_CLASSIFICATION_RULE);
397
+ }
398
+ const lower = error.message.toLowerCase();
399
+ return RETRYABLE_MESSAGE_KEYWORDS.some((kw) => lower.includes(kw));
400
+ }
401
+ delay(ms) {
402
+ return new Promise((resolve) => setTimeout(resolve, ms));
403
+ }
404
+ };
405
+ var retryManager = new RetryManager();
406
+ async function withRetry(fn, options) {
407
+ return retryManager.execute(fn, options);
408
+ }
409
+
410
+ // src/core/utils/request-queue.util.ts
411
+ var DEFAULT_MAX_CONCURRENT = 3;
412
+ var DEFAULT_PRIORITY = 0;
413
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
414
+ var RequestQueue = class {
415
+ queue = [];
416
+ activeRequests = /* @__PURE__ */ new Set();
417
+ maxConcurrent;
418
+ defaultPriority;
419
+ requestTimeout;
420
+ requestIdCounter = 0;
421
+ constructor(options = {}) {
422
+ this.maxConcurrent = options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT;
423
+ this.defaultPriority = options.defaultPriority ?? DEFAULT_PRIORITY;
424
+ this.requestTimeout = options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT_MS;
425
+ }
426
+ add(requestFn, priority) {
427
+ return new Promise((resolve, reject) => {
428
+ const id = `req-${++this.requestIdCounter}`;
429
+ const queuedRequest = {
430
+ id,
431
+ priority: priority ?? this.defaultPriority,
432
+ requestFn,
433
+ resolve,
434
+ reject,
435
+ timestamp: Date.now()
436
+ };
437
+ this.queue.push(queuedRequest);
438
+ this.sortQueue();
439
+ this.process();
440
+ });
441
+ }
442
+ cancelAll() {
443
+ for (const request of this.queue) {
444
+ request.reject(new Error("Request was cancelled"));
445
+ }
446
+ this.queue = [];
447
+ }
448
+ sortQueue() {
449
+ this.queue.sort((a, b) => {
450
+ if (a.priority !== b.priority) {
451
+ return b.priority - a.priority;
452
+ }
453
+ return a.timestamp - b.timestamp;
454
+ });
455
+ }
456
+ process() {
457
+ while (this.queue.length > 0 && this.activeRequests.size < this.maxConcurrent) {
458
+ const request = this.queue.shift();
459
+ if (!request) break;
460
+ this.executeRequest(request);
461
+ }
462
+ }
463
+ executeRequest(request) {
464
+ this.activeRequests.add(request.id);
465
+ const timeoutId = setTimeout(() => {
466
+ this.activeRequests.delete(request.id);
467
+ request.reject(new Error(`Request ${request.id} timed out`));
468
+ this.process();
469
+ }, this.requestTimeout);
470
+ request.requestFn().then((result) => {
471
+ clearTimeout(timeoutId);
472
+ request.resolve(result);
473
+ }).catch((error) => {
474
+ clearTimeout(timeoutId);
475
+ request.reject(error);
476
+ }).finally(() => {
477
+ this.activeRequests.delete(request.id);
478
+ this.process();
479
+ });
480
+ }
481
+ };
482
+ var requestQueue = new RequestQueue();
483
+
484
+ // src/core/utils/request-deduplicator.util.ts
485
+ var DEFAULT_DEDUPE_TTL_MS = 3e4;
486
+ var RequestDeduplicator = class {
487
+ pendingRequests = /* @__PURE__ */ new Map();
488
+ ttl;
489
+ constructor(ttl = DEFAULT_DEDUPE_TTL_MS) {
490
+ this.ttl = ttl;
491
+ }
492
+ execute(key, requestFn) {
493
+ this.cleanup();
494
+ const existing = this.pendingRequests.get(key);
495
+ if (existing) {
496
+ return existing.promise;
497
+ }
498
+ const promise = requestFn();
499
+ this.pendingRequests.set(key, {
500
+ promise,
501
+ timestamp: Date.now()
502
+ });
503
+ promise.catch(() => void 0).then(() => {
504
+ this.pendingRequests.delete(key);
505
+ });
506
+ return promise;
507
+ }
508
+ cancelAll() {
509
+ this.pendingRequests.clear();
510
+ }
511
+ cleanup() {
512
+ const now = Date.now();
513
+ for (const [key, request] of this.pendingRequests.entries()) {
514
+ if (now - request.timestamp > this.ttl) {
515
+ this.pendingRequests.delete(key);
516
+ }
517
+ }
518
+ }
519
+ };
520
+ var requestDeduplicator = new RequestDeduplicator();
521
+
522
+ // src/core/calculations/timing-window.ts
523
+ function shouldCallOnLeadingEdge(params) {
524
+ const { now, lastCallTime, interval, leadingEnabled, isThrottled } = params;
525
+ if (!leadingEnabled) return false;
526
+ if (isThrottled) return false;
527
+ return now - lastCallTime >= interval;
528
+ }
529
+ function calculateTrailingDelay(params) {
530
+ return Math.max(0, params.interval - params.timeSinceLastCall);
531
+ }
532
+
533
+ // src/core/utils/debounce.ts
534
+ var DEFAULT_DEBOUNCE_DELAY_MS = 300;
535
+ var DEFAULT_THROTTLE_INTERVAL_MS = 1e3;
536
+ var DebounceManager = class {
537
+ timers = /* @__PURE__ */ new Map();
538
+ lastArgs = /* @__PURE__ */ new Map();
539
+ debounce(fn, options = {}) {
540
+ const delay = options.delay ?? DEFAULT_DEBOUNCE_DELAY_MS;
541
+ const leading = options.leading ?? false;
542
+ const trailing = options.trailing ?? true;
543
+ let timer = null;
544
+ let lastCallTime = 0;
545
+ let lastResult;
546
+ return ((...args) => {
547
+ const now = Date.now();
548
+ const shouldCallLeading = shouldCallOnLeadingEdge({
549
+ now,
550
+ lastCallTime,
551
+ interval: delay,
552
+ leadingEnabled: leading,
553
+ isThrottled: timer !== null
554
+ });
555
+ if (timer) {
556
+ clearTimeout(timer);
557
+ timer = null;
558
+ }
559
+ if (shouldCallLeading) {
560
+ lastCallTime = now;
561
+ lastResult = fn(...args);
562
+ return lastResult;
563
+ }
564
+ if (trailing) {
565
+ timer = setTimeout(() => {
566
+ timer = null;
567
+ lastCallTime = Date.now();
568
+ lastResult = fn(...args);
569
+ }, delay);
570
+ }
571
+ return lastResult;
572
+ });
573
+ }
574
+ debounceByKey(key, fn, delay = DEFAULT_DEBOUNCE_DELAY_MS) {
575
+ return ((...args) => {
576
+ this.lastArgs.set(key, args);
577
+ const existingTimer = this.timers.get(key);
578
+ if (existingTimer) clearTimeout(existingTimer);
579
+ const timer = setTimeout(() => {
580
+ this.timers.delete(key);
581
+ const pendingArgs = this.lastArgs.get(key);
582
+ this.lastArgs.delete(key);
583
+ if (pendingArgs) fn(...pendingArgs);
584
+ }, delay);
585
+ this.timers.set(key, timer);
586
+ });
587
+ }
588
+ cancelDebounce(key) {
589
+ const timer = this.timers.get(key);
590
+ if (timer) {
591
+ clearTimeout(timer);
592
+ this.timers.delete(key);
593
+ }
594
+ this.lastArgs.delete(key);
595
+ }
596
+ flushDebounce(key, fn) {
597
+ this.cancelDebounce(key);
598
+ const args = this.lastArgs.get(key);
599
+ if (args) {
600
+ fn(...args);
601
+ this.lastArgs.delete(key);
602
+ }
603
+ }
604
+ };
605
+ var ThrottleManager = class {
606
+ timers = /* @__PURE__ */ new Map();
607
+ lastArgs = /* @__PURE__ */ new Map();
608
+ throttle(fn, options = {}) {
609
+ const interval = options.interval ?? DEFAULT_THROTTLE_INTERVAL_MS;
610
+ const leading = options.leading ?? true;
611
+ const trailing = options.trailing ?? false;
612
+ let timer = null;
613
+ let lastCallTime = 0;
614
+ let lastResult;
615
+ let pendingArgs = null;
616
+ return ((...args) => {
617
+ const now = Date.now();
618
+ const timeSinceLastCall = now - lastCallTime;
619
+ const shouldCall = shouldCallOnLeadingEdge({
620
+ now,
621
+ lastCallTime,
622
+ interval,
623
+ leadingEnabled: leading,
624
+ isThrottled: timer !== null
625
+ });
626
+ pendingArgs = args;
627
+ if (shouldCall) {
628
+ lastCallTime = now;
629
+ lastResult = fn(...args);
630
+ return lastResult;
631
+ }
632
+ if (trailing && !timer) {
633
+ const delay = calculateTrailingDelay({ interval, timeSinceLastCall });
634
+ timer = setTimeout(() => {
635
+ timer = null;
636
+ lastCallTime = Date.now();
637
+ if (pendingArgs) {
638
+ lastResult = fn(...pendingArgs);
639
+ pendingArgs = null;
640
+ }
641
+ }, delay);
642
+ }
643
+ return lastResult;
644
+ });
645
+ }
646
+ throttleByKey(key, fn, interval = DEFAULT_THROTTLE_INTERVAL_MS) {
647
+ return ((...args) => {
648
+ const existingTimer = this.timers.get(key);
649
+ if (existingTimer) {
650
+ this.lastArgs.set(key, args);
651
+ return;
652
+ }
653
+ fn(...args);
654
+ const timer = setTimeout(() => {
655
+ this.timers.delete(key);
656
+ const pendingArgs = this.lastArgs.get(key);
657
+ if (pendingArgs) {
658
+ this.lastArgs.delete(key);
659
+ fn(...pendingArgs);
660
+ const nextTimer = setTimeout(() => {
661
+ this.timers.delete(key);
662
+ }, interval);
663
+ this.timers.set(key, nextTimer);
664
+ }
665
+ }, interval);
666
+ this.timers.set(key, timer);
667
+ });
668
+ }
669
+ cancelThrottle(key) {
670
+ const timer = this.timers.get(key);
671
+ if (timer) {
672
+ clearTimeout(timer);
673
+ this.timers.delete(key);
674
+ }
675
+ this.lastArgs.delete(key);
676
+ }
677
+ isThrottled(key) {
678
+ return this.timers.has(key);
679
+ }
680
+ };
681
+ var debounceManager = new DebounceManager();
682
+ var throttleManager = new ThrottleManager();
683
+ function debounce(fn, delay) {
684
+ return debounceManager.debounce(fn, { delay });
685
+ }
686
+ function throttle(fn, interval) {
687
+ return throttleManager.throttle(fn, { interval });
688
+ }
689
+
690
+ // src/core/utils/api-key-validator.ts
691
+ var DEFAULT_API_KEY_RULE = {
692
+ minLength: 10
693
+ };
694
+ function isValidApiKey(apiKey, rule = DEFAULT_API_KEY_RULE) {
695
+ if (!apiKey) return false;
696
+ return apiKey.trim().length >= rule.minLength;
697
+ }
698
+ function validateApiKey(apiKey, rule = DEFAULT_API_KEY_RULE) {
699
+ if (!apiKey) {
700
+ return { valid: false, reason: "API key is required" };
701
+ }
702
+ const trimmed = apiKey.trim();
703
+ if (trimmed.length === 0) {
704
+ return { valid: false, reason: "API key cannot be empty" };
705
+ }
706
+ if (trimmed.length < rule.minLength) {
707
+ return {
708
+ valid: false,
709
+ reason: `API key must be at least ${rule.minLength} characters`
710
+ };
711
+ }
712
+ return { valid: true };
713
+ }
714
+
715
+ // src/core/utils/coercion.util.ts
716
+ function asRecord(value) {
717
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return {};
718
+ return value;
719
+ }
720
+ function asStringArray(value, max) {
721
+ if (!Array.isArray(value)) return [];
722
+ return value.filter((entry) => typeof entry === "string" && entry.trim().length > 0).slice(0, max);
723
+ }
724
+ function asObjectArray(value) {
725
+ if (!Array.isArray(value)) return [];
726
+ return value.filter(
727
+ (entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry)
728
+ );
729
+ }
730
+ function asTrimmedOptionalString(value) {
731
+ if (typeof value !== "string") return void 0;
732
+ const trimmed = value.trim();
733
+ return trimmed.length > 0 ? trimmed : void 0;
734
+ }
735
+ function clampScore(value, min, max) {
736
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
737
+ return Math.max(min, Math.min(max, Math.round(value)));
738
+ }
739
+ function oneOfCatalog(catalog, value) {
740
+ return catalog.includes(value) ? value : null;
741
+ }
742
+
743
+ // src/core/constants/protocol.constants.ts
744
+ var SSE_PREFIX = "data: ";
745
+ var SSE_DONE_MARKER = "[DONE]";
746
+ var JSON_CODE_BLOCK_PATTERN = /```json\n([\s\S]*?)\n```/;
747
+ var JSON_OBJECT_PATTERN = /\{[\s\S]*\}/;
748
+
749
+ // src/core/parsers/sse-lines.ts
750
+ function isSseDataLine(line) {
751
+ return line.startsWith(SSE_PREFIX);
752
+ }
753
+ function extractSseDataPayload(line) {
754
+ return line.slice(SSE_PREFIX.length);
755
+ }
756
+ function isSseDoneMarker(payload) {
757
+ return payload === SSE_DONE_MARKER;
758
+ }
759
+ function splitSseLines(rawChunk) {
760
+ return rawChunk.split("\n").map((l) => l.trim()).filter((l) => l.length > 0);
761
+ }
762
+ function normalizeSseChunk(rawChunk) {
763
+ const lines = splitSseLines(rawChunk);
764
+ const result = [];
765
+ for (const line of lines) {
766
+ if (!isSseDataLine(line)) continue;
767
+ const payload = extractSseDataPayload(line);
768
+ result.push({ payload, isDone: isSseDoneMarker(payload) });
769
+ }
770
+ return result;
771
+ }
772
+
773
+ // src/core/parsers/streaming-response.parser.ts
774
+ var StreamingResponseParser = class {
775
+ constructor(callbacks, extractDelta) {
776
+ this.callbacks = callbacks;
777
+ this.extractDelta = extractDelta;
778
+ }
779
+ callbacks;
780
+ extractDelta;
781
+ decoder = new TextDecoder();
782
+ async parse(stream) {
783
+ const reader = stream.getReader();
784
+ const contentChunks = [];
785
+ let buffer = "";
786
+ const consumeLine = (rawLine) => {
787
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
788
+ const trimmed = line.trim();
789
+ if (trimmed.length === 0 || !isSseDataLine(trimmed)) return;
790
+ const payload = extractSseDataPayload(trimmed);
791
+ if (isSseDoneMarker(payload)) return;
792
+ const content = this.extractDelta(payload);
793
+ if (content !== null) {
794
+ contentChunks.push(content);
795
+ this.callbacks.onChunk(content);
796
+ }
797
+ };
798
+ try {
799
+ while (true) {
800
+ const { done, value } = await reader.read();
801
+ if (done) break;
802
+ buffer += this.decoder.decode(value, { stream: true });
803
+ const lines = buffer.split("\n");
804
+ buffer = lines.pop() ?? "";
805
+ for (const line of lines) consumeLine(line);
806
+ }
807
+ buffer += this.decoder.decode();
808
+ if (buffer.length > 0) consumeLine(buffer);
809
+ } catch (error) {
810
+ if (error instanceof AIError) throw error;
811
+ throw new AIError(
812
+ "NETWORK_ERROR" /* NETWORK_ERROR */,
813
+ "Stream was interrupted",
814
+ error instanceof Error ? error : void 0
815
+ );
816
+ } finally {
817
+ reader.releaseLock();
818
+ }
819
+ return contentChunks.join("");
820
+ }
821
+ };
822
+
823
+ // src/core/parsers/json-content-extractor.ts
824
+ function extractJsonFromCodeFence(content) {
825
+ const match = content.match(JSON_CODE_BLOCK_PATTERN);
826
+ if (match && match[1]) return match[1];
827
+ return null;
828
+ }
829
+ function extractJsonObject(content) {
830
+ const match = content.match(JSON_OBJECT_PATTERN);
831
+ if (match && match[0]) return match[0];
832
+ return null;
833
+ }
834
+ function extractJsonCandidate(content) {
835
+ return extractJsonFromCodeFence(content) ?? extractJsonObject(content) ?? content;
836
+ }
837
+
838
+ // src/core/parsers/json-response.parser.ts
839
+ var JsonResponseParser = class {
840
+ parse(rawContent) {
841
+ const candidate = extractJsonCandidate(rawContent);
842
+ try {
843
+ return JSON.parse(candidate);
844
+ } catch (error) {
845
+ throw new AIError(
846
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
847
+ "Failed to parse structured response as JSON",
848
+ error instanceof Error ? error : void 0
849
+ );
850
+ }
851
+ }
852
+ };
853
+
854
+ // src/core/parsers/structured-prompt.builder.ts
855
+ var StructuredPromptBuilder = class _StructuredPromptBuilder {
856
+ schema;
857
+ request;
858
+ constructor(request) {
859
+ this.request = request;
860
+ }
861
+ static for(request) {
862
+ return new _StructuredPromptBuilder(request);
863
+ }
864
+ withSchema(schema) {
865
+ this.schema = schema;
866
+ return this;
867
+ }
868
+ build() {
869
+ const schemaSection = this.schema ? `Expected JSON schema:
870
+ ${JSON.stringify(this.schema, null, 2)}
871
+
872
+ ` : "";
873
+ return "Please generate a JSON response for the following request.\n\n" + schemaSection + `Request:
874
+ ${this.request}
875
+
876
+ Respond only with valid JSON, formatted as:
877
+ \`\`\`json
878
+ {...}
879
+ \`\`\``;
880
+ }
881
+ };
882
+
883
+ // src/core/sse/sse-response.service.ts
884
+ var SSE_BRIDGE_EVENTS = {
885
+ delta: "delta",
886
+ done: "done",
887
+ error: "error"
888
+ };
889
+ function createSseResponse(openUpstream, options) {
890
+ const encoder = new TextEncoder();
891
+ const decoder = new TextDecoder();
892
+ const stream = new ReadableStream({
893
+ async start(controller) {
894
+ let closed = false;
895
+ const emit = (event, data) => {
896
+ if (closed) return;
897
+ try {
898
+ controller.enqueue(
899
+ encoder.encode(`event: ${event}
900
+ data: ${JSON.stringify(data)}
901
+
902
+ `)
903
+ );
904
+ } catch {
905
+ closed = true;
906
+ }
907
+ };
908
+ const finish = () => {
909
+ if (closed) return;
910
+ closed = true;
911
+ try {
912
+ controller.close();
913
+ } catch {
914
+ closed = true;
915
+ }
916
+ };
917
+ let delivered = false;
918
+ const consumeLine = (line) => {
919
+ const trimmed = line.trim();
920
+ if (!isSseDataLine(trimmed)) return false;
921
+ const payload = extractSseDataPayload(trimmed);
922
+ if (isSseDoneMarker(payload)) return true;
923
+ const text = options.extractDelta(payload);
924
+ if (text) {
925
+ delivered = true;
926
+ emit(SSE_BRIDGE_EVENTS.delta, { text });
927
+ }
928
+ return false;
929
+ };
930
+ try {
931
+ const upstream = await openUpstream();
932
+ const reader = upstream.getReader();
933
+ let buffer = "";
934
+ let finished = false;
935
+ while (!finished) {
936
+ const { value, done: readerDone } = await reader.read();
937
+ if (readerDone) break;
938
+ buffer += decoder.decode(value, { stream: true });
939
+ const lines = buffer.split("\n");
940
+ buffer = lines.pop() ?? "";
941
+ for (const line of lines) {
942
+ finished = consumeLine(line);
943
+ if (finished) break;
944
+ }
945
+ }
946
+ if (!finished && buffer.length > 0) consumeLine(buffer);
947
+ emit(SSE_BRIDGE_EVENTS.done, options.donePayload);
948
+ finish();
949
+ } catch (err) {
950
+ if (!delivered && options.onStreamFailure) {
951
+ await options.onStreamFailure(err);
952
+ }
953
+ emit(SSE_BRIDGE_EVENTS.error, { code: options.errorCode(err) });
954
+ finish();
955
+ }
956
+ }
957
+ });
958
+ return new Response(stream, {
959
+ headers: {
960
+ "content-type": "text/event-stream; charset=utf-8",
961
+ "cache-control": "no-cache, no-transform",
962
+ "x-accel-buffering": "no"
963
+ }
964
+ });
965
+ }
966
+
967
+ export { AIError, AIErrorType, CacheManager, CompositeExecutor, DEFAULT_API_KEY_RULE, DEFAULT_ERROR_CLASSIFICATION_RULE, DEFAULT_HTTP_STATUS_RULE, DEFAULT_RETRY_BACKOFF_MULTIPLIER, DEFAULT_RETRY_INITIAL_DELAY_MS, DEFAULT_RETRY_JITTER_FACTOR, DEFAULT_RETRY_MAX_ATTEMPTS, DEFAULT_RETRY_MAX_DELAY_MS, DebounceManager, DedupeExecutor, JSON_CODE_BLOCK_PATTERN, JSON_OBJECT_PATTERN, JsonResponseParser, QueueExecutor, RequestDeduplicator, RequestQueue, RetryExecutor, RetryManager, SSE_BRIDGE_EVENTS, SSE_DONE_MARKER, SSE_PREFIX, StreamingResponseParser, StructuredPromptBuilder, TEXT_TIERS, ThrottleManager, USER_FRIENDLY_ERROR_MESSAGES, asObjectArray, asRecord, asStringArray, asTrimmedOptionalString, buildCacheKey, buildHttpStatusFallback, cacheManager, clampScore, classifyHttpStatus, createSseResponse, debounce, debounceManager, describeAIErrorType, extractErrorMessageFromJsonBody, extractErrorMessageFromResponse, extractJsonCandidate, extractJsonFromCodeFence, extractJsonObject, extractSseDataPayload, formatErrorForLogging, isAuthErrorType, isRetryableErrorType, isSseDataLine, isSseDoneMarker, isTextTier, isValidApiKey, mergeTierOverrides, normalizeSseChunk, oneOfCatalog, requestDeduplicator, requestQueue, resolveModel, resolveUserFriendlyErrorMessage, resolveUserFriendlyMessage, retryManager, splitSseLines, throttle, throttleManager, validateApiKey, withRetry };
968
+ //# sourceMappingURL=index.js.map
969
+ //# sourceMappingURL=index.js.map