@retrivora-ai/rag-engine 2.2.6 → 2.2.7

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/dist/server.mjs CHANGED
@@ -2231,6 +2231,16 @@ var AuthenticationException = class extends RetrivoraError {
2231
2231
  super(message, "AUTHENTICATION_ERROR", details);
2232
2232
  }
2233
2233
  };
2234
+ var SDKVersionUnsupportedError = class extends RetrivoraError {
2235
+ constructor(message = "This SDK version is no longer supported.", details) {
2236
+ super(message, "SDK_VERSION_UNSUPPORTED", details);
2237
+ }
2238
+ };
2239
+ var LicenseValidationError = class extends RetrivoraError {
2240
+ constructor(message = "License validation failed.", details) {
2241
+ super(message, "LICENSE_VALIDATION_ERROR", details);
2242
+ }
2243
+ };
2234
2244
  function wrapError(err, defaultCode, defaultMessage) {
2235
2245
  var _a2;
2236
2246
  if (err instanceof RetrivoraError) {
@@ -2243,8 +2253,10 @@ function wrapError(err, defaultCode, defaultMessage) {
2243
2253
  if (status === 429 || /rate[- ]?limit/i.test(message) || code === "RATE_LIMIT_EXCEEDED") {
2244
2254
  return new RateLimitException(message, err);
2245
2255
  }
2246
- if (status === 401 || status === 403 || /unauthorized|auth|api[- ]?key/i.test(message) || code === "INVALID_API_KEY") {
2247
- return new AuthenticationException(message, err);
2256
+ if (status === 401 || status === 403 || /unauthorized|auth|license|revoked|terminated|api[- ]?key/i.test(message) || code === "INVALID_API_KEY" || code === "LICENSE_DELETED" || code === "LICENSE_INACTIVE") {
2257
+ const isLicenseError = /403|license|revoked|terminated/i.test(message) || status === 403;
2258
+ const cleanMsg = isLicenseError ? "Your Retrivora license key has been terminated, suspended, or revoked. Access denied." : message;
2259
+ return new AuthenticationException(cleanMsg, err);
2248
2260
  }
2249
2261
  switch (defaultCode) {
2250
2262
  case "PROVIDER_NOT_FOUND":
@@ -4711,7 +4723,7 @@ var ConfigValidator = class {
4711
4723
  // package.json
4712
4724
  var package_default = {
4713
4725
  name: "@retrivora-ai/rag-engine",
4714
- version: "2.2.6",
4726
+ version: "2.2.7",
4715
4727
  description: "Retrivora AI is a plug-and-play AI engine for RAG chat experiences \u2014 generic vector DB + LLM provider, embeddable or standalone.",
4716
4728
  author: "Abhinav Alkuchi",
4717
4729
  license: "UNLICENSED",
@@ -10827,7 +10839,10 @@ function createStreamHandler(configOrPlugin, options) {
10827
10839
  }
10828
10840
  } catch (streamError) {
10829
10841
  if (isActive) {
10830
- const errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
10842
+ let errorMessage = streamError instanceof Error ? streamError.message : String(streamError);
10843
+ if (errorMessage.includes("403") || errorMessage.toLowerCase().includes("license") || errorMessage.toLowerCase().includes("revoked") || errorMessage.toLowerCase().includes("terminated")) {
10844
+ errorMessage = "Your Retrivora license key has been terminated, suspended, or revoked. Access denied.";
10845
+ }
10831
10846
  console.error("[createStreamHandler] Stream error:", streamError);
10832
10847
  reportTelemetry(req, plugin, "QUERY_GENERATION", "error", errorMessage);
10833
10848
  try {
@@ -11228,6 +11243,108 @@ function createRagHandler(configOrPlugin, options) {
11228
11243
  }
11229
11244
  };
11230
11245
  }
11246
+
11247
+ // src/core/LicenseValidator.ts
11248
+ var _LicenseValidator = class _LicenseValidator {
11249
+ // 5-minute TTL for successful validation only
11250
+ constructor() {
11251
+ this.cache = /* @__PURE__ */ new Map();
11252
+ }
11253
+ static getInstance() {
11254
+ if (!_LicenseValidator.instance) {
11255
+ _LicenseValidator.instance = new _LicenseValidator();
11256
+ }
11257
+ return _LicenseValidator.instance;
11258
+ }
11259
+ /**
11260
+ * Invalidate local validation cache for a license key
11261
+ */
11262
+ purgeCache(licenseKey) {
11263
+ if (licenseKey) {
11264
+ this.cache.delete(licenseKey);
11265
+ } else {
11266
+ this.cache.clear();
11267
+ }
11268
+ }
11269
+ /**
11270
+ * Validate license status and SDK version against Retrivora License Service
11271
+ */
11272
+ async validate(request) {
11273
+ const {
11274
+ licenseKey,
11275
+ projectId,
11276
+ sdkVersion = SDK_VERSION,
11277
+ sdk = "@retrivora-ai/rag-engine",
11278
+ platform = typeof window !== "undefined" ? "browser" : "node",
11279
+ retrivoraApiBase,
11280
+ headers: customHeaders
11281
+ } = request;
11282
+ if (!licenseKey) {
11283
+ this.purgeCache(licenseKey);
11284
+ throw new LicenseValidationError("Missing RETRIVORA_LICENSE_KEY in request.");
11285
+ }
11286
+ const cached = this.cache.get(licenseKey);
11287
+ if (cached) {
11288
+ const isFresh = Date.now() - cached.timestamp < _LicenseValidator.SUCCESS_CACHE_TTL_MS;
11289
+ if (isFresh && cached.response.valid && cached.response.licenseStatus === "ACTIVE" && !cached.response.forceUpgrade) {
11290
+ return cached.response;
11291
+ } else {
11292
+ this.cache.delete(licenseKey);
11293
+ }
11294
+ }
11295
+ const base = retrivoraApiBase || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_RETRIVORA_API_BASE || process.env.RETRIVORA_API_BASE : "") || "/api/retrivora";
11296
+ const validateUrl = `${base.replace(/\/$/, "")}/v1/license/validate`;
11297
+ let res;
11298
+ try {
11299
+ res = await fetch(validateUrl, {
11300
+ method: "POST",
11301
+ cache: "no-store",
11302
+ headers: __spreadValues({
11303
+ "Content-Type": "application/json",
11304
+ "x-license-key": licenseKey,
11305
+ "x-sdk-version": sdkVersion
11306
+ }, customHeaders || {}),
11307
+ body: JSON.stringify({
11308
+ licenseKey,
11309
+ projectId,
11310
+ sdkVersion,
11311
+ sdk,
11312
+ platform
11313
+ })
11314
+ });
11315
+ } catch (err) {
11316
+ this.purgeCache(licenseKey);
11317
+ throw new LicenseValidationError(`License validation request failed: ${(err == null ? void 0 : err.message) || "Network error"}`);
11318
+ }
11319
+ const data = await res.json().catch(() => ({
11320
+ valid: false,
11321
+ licenseStatus: "REVOKED",
11322
+ minimumSupportedVersion: "2.1.0",
11323
+ latestVersion: SDK_VERSION,
11324
+ forceUpgrade: false,
11325
+ expiresAt: null,
11326
+ message: "Failed to parse license validation response."
11327
+ }));
11328
+ if (!res.ok || !data.valid || data.licenseStatus !== "ACTIVE" || data.forceUpgrade) {
11329
+ this.purgeCache(licenseKey);
11330
+ if (data.forceUpgrade || res.status === 426) {
11331
+ throw new SDKVersionUnsupportedError(
11332
+ data.message || `This SDK version (${sdkVersion}) is no longer supported. Upgrade to version ${data.latestVersion || "2.2.6"} or later.`,
11333
+ data
11334
+ );
11335
+ }
11336
+ const errMsg = data.message || `License validation failed with status: ${data.licenseStatus}. Access denied.`;
11337
+ throw new LicenseValidationError(errMsg, data);
11338
+ }
11339
+ this.cache.set(licenseKey, {
11340
+ response: data,
11341
+ timestamp: Date.now()
11342
+ });
11343
+ return data;
11344
+ }
11345
+ };
11346
+ _LicenseValidator.SUCCESS_CACHE_TTL_MS = 5 * 60 * 1e3;
11347
+ var LicenseValidator = _LicenseValidator;
11231
11348
  export {
11232
11349
  AnthropicProvider,
11233
11350
  AuthenticationException,
@@ -11249,6 +11366,8 @@ export {
11249
11366
  IntentClassifier,
11250
11367
  LLMFactory,
11251
11368
  LLM_PROFILES,
11369
+ LicenseValidationError,
11370
+ LicenseValidator,
11252
11371
  LicenseVerifier,
11253
11372
  MilvusProvider,
11254
11373
  MixedRendererStrategy,
@@ -11279,6 +11398,7 @@ export {
11279
11398
  Rule6SmallResultSetRule,
11280
11399
  Rule7LargeTableRule,
11281
11400
  RuleEngine,
11401
+ SDKVersionUnsupportedError,
11282
11402
  SDK_VERSION,
11283
11403
  TableRendererStrategy,
11284
11404
  TextRendererStrategy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "description": "Retrivora AI is a plug-and-play AI engine for RAG chat experiences — generic vector DB + LLM provider, embeddable or standalone.",
5
5
  "author": "Abhinav Alkuchi",
6
6
  "license": "UNLICENSED",
@@ -4,6 +4,7 @@ import { ChatWindow } from './ChatWindow';
4
4
  import { useConfig } from './ConfigProvider';
5
5
  import { ChatWidgetProps } from '../types';
6
6
  import { SDK_VERSION } from '../version';
7
+ import { LicenseValidator } from '../core/LicenseValidator';
7
8
 
8
9
  const DEFAULT_DIMENSIONS = { width: 380, height: 600 };
9
10
 
@@ -46,7 +47,7 @@ const GEMINI_STYLES = `
46
47
  `;
47
48
 
48
49
  export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, retrivoraApiBase, headers }: ChatWidgetProps) {
49
- const { ui } = useConfig();
50
+ const { ui, projectId } = useConfig();
50
51
  const [isOpen, setIsOpen] = useState(false);
51
52
  const [hasUnread, setHasUnread] = useState(false);
52
53
  const [dimensions, setDimensions] = useState(DEFAULT_DIMENSIONS);
@@ -71,44 +72,20 @@ export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, ret
71
72
  const handleOpen = async () => {
72
73
  setIsValidating(true);
73
74
  try {
74
- const base = retrivoraApiBase || '/api/retrivora';
75
- const configUrl = `${base.replace(/\/$/, '')}/config?sdkVersion=${SDK_VERSION}`;
76
- const res = await fetch(configUrl, {
77
- method: 'GET',
78
- cache: 'no-store',
79
- headers: {
80
- 'Content-Type': 'application/json',
81
- 'x-sdk-version': SDK_VERSION,
82
- ...(headers || {})
83
- },
75
+ const licenseKey = headers?.['x-license-key'] || headers?.['authorization']?.replace(/^Bearer\s+/i, '') || '';
76
+ await LicenseValidator.getInstance().validate({
77
+ licenseKey,
78
+ projectId,
79
+ retrivoraApiBase,
80
+ headers,
84
81
  });
85
82
 
86
- if (!res.ok) {
87
- const data = await res.json().catch(() => ({}));
88
- if (res.status === 426 || data.code === 'SDK_VERSION_OUTDATED') {
89
- const msg = data.error || 'Your Retrivora SDK package version is outdated. Please update your package to the latest version to continue.';
90
- setWidgetError(msg);
91
- setIsOpen(false); // DO NOT OPEN CHAT WIDGET
92
- return;
93
- }
94
- if (
95
- res.status === 403 ||
96
- res.status === 401 ||
97
- (typeof data.error === 'string' && data.error.toLowerCase().includes('license'))
98
- ) {
99
- const msg = (typeof data.error === 'string' ? data.error : data.error?.message) || 'Your Retrivora license is no longer valid. Please contact your administrator or obtain a valid license key.';
100
- setWidgetError(msg);
101
- setIsOpen(false); // DO NOT OPEN CHAT WIDGET
102
- return;
103
- }
104
- }
105
-
106
83
  setWidgetError(null);
107
84
  setIsOpen(true);
108
85
  setHasUnread(false);
109
- } catch {
110
- // Fallback
111
- setIsOpen(true);
86
+ } catch (err: any) {
87
+ setWidgetError(err?.message || 'Access denied.');
88
+ setIsOpen(false); // DO NOT OPEN CHAT WIDGET
112
89
  } finally {
113
90
  setIsValidating(false);
114
91
  }
@@ -23,6 +23,7 @@ import { useRagChat } from '../hooks/useRagChat';
23
23
  import { BORDER_RADIUS_MAP, CHAT_SUGGESTIONS } from '../config/uiConstants';
24
24
  import { ChatWindowProps } from '../types';
25
25
  import { SDK_VERSION } from '../version';
26
+ import { LicenseValidator } from '../core/LicenseValidator';
26
27
 
27
28
  interface SpeechRecognitionEvent {
28
29
  resultIndex: number;
@@ -97,48 +98,32 @@ export function ChatWindow({
97
98
  let isMounted = true;
98
99
  async function validateSessionLicense() {
99
100
  try {
100
- const base = retrivoraApiBase || '/api/retrivora';
101
- const configUrl = `${base.replace(/\/$/, '')}/config?sdkVersion=${SDK_VERSION}`;
102
- const res = await fetch(configUrl, {
103
- method: 'GET',
104
- cache: 'no-store',
105
- headers: {
106
- 'Content-Type': 'application/json',
107
- 'x-sdk-version': SDK_VERSION,
108
- ...(headers || {})
109
- },
101
+ const licenseKey = headers?.['x-license-key'] || headers?.['authorization']?.replace(/^Bearer\s+/i, '') || '';
102
+ await LicenseValidator.getInstance().validate({
103
+ licenseKey,
104
+ projectId,
105
+ retrivoraApiBase,
106
+ headers,
110
107
  });
111
108
 
112
- if (!res.ok) {
113
- const data = await res.json().catch(() => ({}));
114
- if (res.status === 426 || data.code === 'SDK_VERSION_OUTDATED') {
115
- if (isMounted) {
116
- setVersionError(data.error || 'Your Retrivora SDK package is outdated. Please update your package to the latest version to continue.');
117
- }
118
- } else if (
119
- res.status === 403 ||
120
- res.status === 401 ||
121
- (typeof data.error === 'string' && data.error.toLowerCase().includes('license'))
122
- ) {
123
- if (isMounted) {
124
- const errMsg = typeof data.error === 'string' ? data.error : data.error?.message || 'Your Retrivora license is no longer valid. Please contact your administrator or obtain a valid license key.';
125
- setLicenseError(errMsg);
126
- }
127
- }
128
- } else {
129
- if (isMounted) {
130
- setLicenseError(null);
131
- setVersionError(null);
109
+ if (isMounted) {
110
+ setLicenseError(null);
111
+ setVersionError(null);
112
+ }
113
+ } catch (err: any) {
114
+ if (isMounted) {
115
+ if (err?.code === 'SDK_VERSION_UNSUPPORTED') {
116
+ setVersionError(err?.message || 'SDK version unsupported.');
117
+ } else {
118
+ setLicenseError(err?.message || 'License validation failed.');
132
119
  }
133
120
  }
134
- } catch {
135
- // Non-blocking fallback
136
121
  }
137
122
  }
138
123
 
139
124
  validateSessionLicense();
140
125
  return () => { isMounted = false; };
141
- }, [retrivoraApiBase, headers]);
126
+ }, [retrivoraApiBase, headers, projectId]);
142
127
 
143
128
  // Initialize SpeechRecognition
144
129
  useEffect(() => {
@@ -0,0 +1,146 @@
1
+ import { SDK_VERSION } from '../version';
2
+ import { SDKVersionUnsupportedError, LicenseValidationError } from '../exceptions';
3
+
4
+ export interface LicenseValidationRequest {
5
+ licenseKey: string;
6
+ projectId?: string;
7
+ sdkVersion?: string;
8
+ sdk?: string;
9
+ platform?: string;
10
+ retrivoraApiBase?: string;
11
+ headers?: Record<string, string>;
12
+ }
13
+
14
+ export interface LicenseValidationResponse {
15
+ valid: boolean;
16
+ licenseStatus: 'ACTIVE' | 'SUSPENDED' | 'TERMINATED' | 'EXPIRED' | 'REVOKED';
17
+ minimumSupportedVersion: string;
18
+ latestVersion: string;
19
+ forceUpgrade: boolean;
20
+ expiresAt: string | null;
21
+ message: string;
22
+ }
23
+
24
+ interface CachedSuccessValidation {
25
+ response: LicenseValidationResponse;
26
+ timestamp: number;
27
+ }
28
+
29
+ export class LicenseValidator {
30
+ private static instance: LicenseValidator;
31
+ private cache: Map<string, CachedSuccessValidation> = new Map();
32
+ private static readonly SUCCESS_CACHE_TTL_MS = 5 * 60 * 1000; // 5-minute TTL for successful validation only
33
+
34
+ private constructor() {}
35
+
36
+ public static getInstance(): LicenseValidator {
37
+ if (!LicenseValidator.instance) {
38
+ LicenseValidator.instance = new LicenseValidator();
39
+ }
40
+ return LicenseValidator.instance;
41
+ }
42
+
43
+ /**
44
+ * Invalidate local validation cache for a license key
45
+ */
46
+ public purgeCache(licenseKey?: string): void {
47
+ if (licenseKey) {
48
+ this.cache.delete(licenseKey);
49
+ } else {
50
+ this.cache.clear();
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Validate license status and SDK version against Retrivora License Service
56
+ */
57
+ public async validate(request: LicenseValidationRequest): Promise<LicenseValidationResponse> {
58
+ const {
59
+ licenseKey,
60
+ projectId,
61
+ sdkVersion = SDK_VERSION,
62
+ sdk = '@retrivora-ai/rag-engine',
63
+ platform = typeof window !== 'undefined' ? 'browser' : 'node',
64
+ retrivoraApiBase,
65
+ headers: customHeaders,
66
+ } = request;
67
+
68
+ if (!licenseKey) {
69
+ this.purgeCache(licenseKey);
70
+ throw new LicenseValidationError('Missing RETRIVORA_LICENSE_KEY in request.');
71
+ }
72
+
73
+ // 1. Check successful 5-minute cache
74
+ const cached = this.cache.get(licenseKey);
75
+ if (cached) {
76
+ const isFresh = Date.now() - cached.timestamp < LicenseValidator.SUCCESS_CACHE_TTL_MS;
77
+ if (isFresh && cached.response.valid && cached.response.licenseStatus === 'ACTIVE' && !cached.response.forceUpgrade) {
78
+ return cached.response;
79
+ } else {
80
+ this.cache.delete(licenseKey);
81
+ }
82
+ }
83
+
84
+ // 2. Fetch live validation from Retrivora backend API
85
+ const base = retrivoraApiBase || (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_RETRIVORA_API_BASE || process.env.RETRIVORA_API_BASE : '') || '/api/retrivora';
86
+ const validateUrl = `${base.replace(/\/$/, '')}/v1/license/validate`;
87
+
88
+ let res: Response;
89
+ try {
90
+ res = await fetch(validateUrl, {
91
+ method: 'POST',
92
+ cache: 'no-store',
93
+ headers: {
94
+ 'Content-Type': 'application/json',
95
+ 'x-license-key': licenseKey,
96
+ 'x-sdk-version': sdkVersion,
97
+ ...(customHeaders || {}),
98
+ },
99
+ body: JSON.stringify({
100
+ licenseKey,
101
+ projectId,
102
+ sdkVersion,
103
+ sdk,
104
+ platform,
105
+ }),
106
+ });
107
+ } catch (err: any) {
108
+ // Fallback: If network error occurs, do not cache and throw error
109
+ this.purgeCache(licenseKey);
110
+ throw new LicenseValidationError(`License validation request failed: ${err?.message || 'Network error'}`);
111
+ }
112
+
113
+ const data: LicenseValidationResponse = await res.json().catch(() => ({
114
+ valid: false,
115
+ licenseStatus: 'REVOKED' as const,
116
+ minimumSupportedVersion: '2.1.0',
117
+ latestVersion: SDK_VERSION,
118
+ forceUpgrade: false,
119
+ expiresAt: null,
120
+ message: 'Failed to parse license validation response.',
121
+ }));
122
+
123
+ // 3. Negative validation responses immediately purge any existing cache
124
+ if (!res.ok || !data.valid || data.licenseStatus !== 'ACTIVE' || data.forceUpgrade) {
125
+ this.purgeCache(licenseKey);
126
+
127
+ if (data.forceUpgrade || res.status === 426) {
128
+ throw new SDKVersionUnsupportedError(
129
+ data.message || `This SDK version (${sdkVersion}) is no longer supported. Upgrade to version ${data.latestVersion || '2.2.6'} or later.`,
130
+ data
131
+ );
132
+ }
133
+
134
+ const errMsg = data.message || `License validation failed with status: ${data.licenseStatus}. Access denied.`;
135
+ throw new LicenseValidationError(errMsg, data);
136
+ }
137
+
138
+ // 4. Cache SUCCESSFUL validation only (TTL = 5 minutes)
139
+ this.cache.set(licenseKey, {
140
+ response: data,
141
+ timestamp: Date.now(),
142
+ });
143
+
144
+ return data;
145
+ }
146
+ }
@@ -8,7 +8,9 @@ export type RetrivoraErrorCode =
8
8
  | 'RETRIEVAL_FAILED'
9
9
  | 'RATE_LIMITED'
10
10
  | 'CONFIGURATION_ERROR'
11
- | 'AUTHENTICATION_ERROR';
11
+ | 'AUTHENTICATION_ERROR'
12
+ | 'SDK_VERSION_UNSUPPORTED'
13
+ | 'LICENSE_VALIDATION_ERROR';
12
14
 
13
15
  export class RetrivoraError extends Error {
14
16
  readonly code: RetrivoraErrorCode;
@@ -58,6 +60,18 @@ export class AuthenticationException extends RetrivoraError {
58
60
  }
59
61
  }
60
62
 
63
+ export class SDKVersionUnsupportedError extends RetrivoraError {
64
+ constructor(message = 'This SDK version is no longer supported.', details?: unknown) {
65
+ super(message, 'SDK_VERSION_UNSUPPORTED', details);
66
+ }
67
+ }
68
+
69
+ export class LicenseValidationError extends RetrivoraError {
70
+ constructor(message = 'License validation failed.', details?: unknown) {
71
+ super(message, 'LICENSE_VALIDATION_ERROR', details);
72
+ }
73
+ }
74
+
61
75
  /**
62
76
  * Wraps any unknown error into an appropriate RetrivoraError subclass.
63
77
  */
@@ -80,14 +94,20 @@ export function wrapError(
80
94
  return new RateLimitException(message, err);
81
95
  }
82
96
 
83
- // 2. Authentication Recognition
97
+ // 2. Authentication / License Recognition
84
98
  if (
85
99
  status === 401 ||
86
100
  status === 403 ||
87
- /unauthorized|auth|api[- ]?key/i.test(message) ||
88
- code === 'INVALID_API_KEY'
101
+ /unauthorized|auth|license|revoked|terminated|api[- ]?key/i.test(message) ||
102
+ code === 'INVALID_API_KEY' ||
103
+ code === 'LICENSE_DELETED' ||
104
+ code === 'LICENSE_INACTIVE'
89
105
  ) {
90
- return new AuthenticationException(message, err);
106
+ const isLicenseError = /403|license|revoked|terminated/i.test(message) || status === 403;
107
+ const cleanMsg = isLicenseError
108
+ ? 'Your Retrivora license key has been terminated, suspended, or revoked. Access denied.'
109
+ : message;
110
+ return new AuthenticationException(cleanMsg, err);
91
111
  }
92
112
 
93
113
  // Fallback Mapping based on defaultCode
@@ -492,8 +492,18 @@ export function createStreamHandler(
492
492
  }
493
493
  } catch (streamError) {
494
494
  if (isActive) {
495
- const errorMessage =
495
+ let errorMessage =
496
496
  streamError instanceof Error ? streamError.message : String(streamError);
497
+
498
+ if (
499
+ errorMessage.includes('403') ||
500
+ errorMessage.toLowerCase().includes('license') ||
501
+ errorMessage.toLowerCase().includes('revoked') ||
502
+ errorMessage.toLowerCase().includes('terminated')
503
+ ) {
504
+ errorMessage = 'Your Retrivora license key has been terminated, suspended, or revoked. Access denied.';
505
+ }
506
+
497
507
  console.error('[createStreamHandler] Stream error:', streamError);
498
508
  reportTelemetry(req, plugin, 'QUERY_GENERATION', 'error', errorMessage);
499
509
  try {
@@ -292,7 +292,15 @@ export function useRagChat(
292
292
  console.log('[useRagChat] Streaming stopped by user.');
293
293
  return;
294
294
  }
295
- const msg = err instanceof Error ? err.message : 'Something went wrong. Please try again.';
295
+ let msg = err instanceof Error ? err.message : 'Something went wrong. Please try again.';
296
+ if (
297
+ msg.includes('403') ||
298
+ msg.toLowerCase().includes('license') ||
299
+ msg.toLowerCase().includes('revoked') ||
300
+ msg.toLowerCase().includes('terminated')
301
+ ) {
302
+ msg = 'Your Retrivora license key has been terminated, suspended, or revoked. Access denied.';
303
+ }
296
304
  setError(msg);
297
305
  onError?.(msg);
298
306
  } finally {
package/src/index.ts CHANGED
@@ -63,6 +63,8 @@ export type { ILLMProvider, EmbedOptions } from './llm/ILLMProvider';
63
63
  export type { Chunk, ChunkOptions } from './rag/DocumentChunker';
64
64
  export type { Retrivora } from './core/Retrivora';
65
65
  export type { RetrivoraErrorCode } from './exceptions';
66
+ export { LicenseValidator } from './core/LicenseValidator';
67
+ export type { LicenseValidationRequest, LicenseValidationResponse } from './core/LicenseValidator';
66
68
  export {
67
69
  RetrivoraError,
68
70
  ProviderNotFoundException,
@@ -71,6 +73,8 @@ export {
71
73
  RateLimitException,
72
74
  ConfigurationException,
73
75
  AuthenticationException,
76
+ SDKVersionUnsupportedError,
77
+ LicenseValidationError,
74
78
  } from './exceptions';
75
79
  export type {
76
80
  VectorMatch,
package/src/server.ts CHANGED
@@ -90,6 +90,10 @@ export {
90
90
  sseErrorFrame,
91
91
  } from './handlers';
92
92
 
93
+ // ── License Validator ─────────────────────────────────────────
94
+ export { LicenseValidator } from './core/LicenseValidator';
95
+ export type { LicenseValidationRequest, LicenseValidationResponse } from './core/LicenseValidator';
96
+
93
97
  // ── Exceptions ────────────────────────────────────────────────
94
98
  export type { RetrivoraErrorCode } from './exceptions';
95
99
  export {
@@ -100,6 +104,8 @@ export {
100
104
  RateLimitException,
101
105
  ConfigurationException,
102
106
  AuthenticationException,
107
+ SDKVersionUnsupportedError,
108
+ LicenseValidationError,
103
109
  wrapError,
104
110
  } from './exceptions';
105
111