@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/{index-BbJGyNmW.d.mts → LicenseValidator-B3xpJaVf.d.mts} +42 -2
- package/dist/{index-BCbeeh74.d.ts → LicenseValidator-D4I4pbSL.d.ts} +42 -2
- package/dist/handlers/index.js +9 -4
- package/dist/handlers/index.mjs +9 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +190 -96
- package/dist/index.mjs +187 -96
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +127 -4
- package/dist/server.mjs +124 -4
- package/package.json +1 -1
- package/src/components/ChatWidget.tsx +11 -34
- package/src/components/ChatWindow.tsx +18 -33
- package/src/core/LicenseValidator.ts +146 -0
- package/src/exceptions/index.ts +25 -5
- package/src/handlers/index.ts +11 -1
- package/src/hooks/useRagChat.ts +9 -1
- package/src/index.ts +4 -0
- package/src/server.ts +6 -0
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
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
111
|
-
setIsOpen(
|
|
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
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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 (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
(
|
|
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
|
+
}
|
package/src/exceptions/index.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/src/handlers/index.ts
CHANGED
|
@@ -492,8 +492,18 @@ export function createStreamHandler(
|
|
|
492
492
|
}
|
|
493
493
|
} catch (streamError) {
|
|
494
494
|
if (isActive) {
|
|
495
|
-
|
|
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 {
|
package/src/hooks/useRagChat.ts
CHANGED
|
@@ -292,7 +292,15 @@ export function useRagChat(
|
|
|
292
292
|
console.log('[useRagChat] Streaming stopped by user.');
|
|
293
293
|
return;
|
|
294
294
|
}
|
|
295
|
-
|
|
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
|
|