@retrivora-ai/rag-engine 2.2.7 → 2.2.9

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.js CHANGED
@@ -4823,7 +4823,7 @@ var ConfigValidator = class {
4823
4823
  // package.json
4824
4824
  var package_default = {
4825
4825
  name: "@retrivora-ai/rag-engine",
4826
- version: "2.2.7",
4826
+ version: "2.2.9",
4827
4827
  description: "Retrivora AI is a plug-and-play AI engine for RAG chat experiences \u2014 generic vector DB + LLM provider, embeddable or standalone.",
4828
4828
  author: "Abhinav Alkuchi",
4829
4829
  license: "UNLICENSED",
@@ -11012,6 +11012,46 @@ function createHealthHandler(configOrPlugin, options) {
11012
11012
  }
11013
11013
  };
11014
11014
  }
11015
+ function createLicenseHandler(configOrPlugin, options) {
11016
+ const plugin = getOrCreatePlugin(configOrPlugin);
11017
+ const onAuthorize = options == null ? void 0 : options.onAuthorize;
11018
+ return async function POST(req) {
11019
+ var _a2;
11020
+ if (req) {
11021
+ const authResult = await checkAuth(req, onAuthorize);
11022
+ if (authResult) return authResult;
11023
+ }
11024
+ try {
11025
+ const body = await req.json().catch(() => ({}));
11026
+ const config = plugin.getConfig();
11027
+ const licenseKey = req.headers.get("x-license-key") || (body == null ? void 0 : body.licenseKey) || ((_a2 = req.headers.get("authorization")) == null ? void 0 : _a2.replace(/^Bearer\s+/i, "")) || config.licenseKey || process.env.RETRIVORA_LICENSE_KEY || process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY || "";
11028
+ const projectId = (body == null ? void 0 : body.projectId) || config.projectId || "my-rag-app";
11029
+ const payload = LicenseVerifier.verify(licenseKey, projectId);
11030
+ return import_server.NextResponse.json({
11031
+ valid: true,
11032
+ licenseStatus: "ACTIVE",
11033
+ minimumSupportedVersion: "2.1.0",
11034
+ latestVersion: SDK_VERSION,
11035
+ forceUpgrade: false,
11036
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1e3).toISOString() : null,
11037
+ message: "License key is active and valid."
11038
+ });
11039
+ } catch (err) {
11040
+ return import_server.NextResponse.json(
11041
+ {
11042
+ valid: false,
11043
+ licenseStatus: "REVOKED",
11044
+ minimumSupportedVersion: "2.1.0",
11045
+ latestVersion: SDK_VERSION,
11046
+ forceUpgrade: false,
11047
+ expiresAt: null,
11048
+ message: (err == null ? void 0 : err.message) || "License validation failed."
11049
+ },
11050
+ { status: 400 }
11051
+ );
11052
+ }
11053
+ };
11054
+ }
11015
11055
  function createUploadHandler(configOrPlugin, options) {
11016
11056
  const plugin = getOrCreatePlugin(configOrPlugin);
11017
11057
  const onAuthorize = options == null ? void 0 : options.onAuthorize;
@@ -11263,6 +11303,7 @@ function createRagHandler(configOrPlugin, options) {
11263
11303
  const historyHandler = createHistoryHandler(plugin, { onAuthorize });
11264
11304
  const feedbackHandler = createFeedbackHandler(plugin, { onAuthorize });
11265
11305
  const sessionsHandler = createSessionsHandler(plugin, { onAuthorize });
11306
+ const licenseHandler = createLicenseHandler(plugin, { onAuthorize });
11266
11307
  async function routePostRequest(req, segment) {
11267
11308
  switch (segment) {
11268
11309
  case "chat":
@@ -11280,6 +11321,9 @@ function createRagHandler(configOrPlugin, options) {
11280
11321
  return historyHandler(req);
11281
11322
  case "feedback":
11282
11323
  return feedbackHandler(req);
11324
+ case "license/validate":
11325
+ case "v1/license/validate":
11326
+ return licenseHandler(req);
11283
11327
  default:
11284
11328
  return import_server.NextResponse.json({ error: `Not Found: POST segment "${segment}" not supported.` }, { status: 404 });
11285
11329
  }
@@ -11309,6 +11353,7 @@ function createRagHandler(configOrPlugin, options) {
11309
11353
  try {
11310
11354
  const url = new URL(req.url);
11311
11355
  const pathname = url.pathname;
11356
+ if (pathname.includes("/license/validate")) return "v1/license/validate";
11312
11357
  if (pathname.endsWith("/upload")) return "upload";
11313
11358
  if (pathname.endsWith("/chat") || pathname.endsWith("/chat-sync")) return "chat";
11314
11359
  if (pathname.endsWith("/health")) return "health";
@@ -11370,6 +11415,7 @@ var _LicenseValidator = class _LicenseValidator {
11370
11415
  * Validate license status and SDK version against Retrivora License Service
11371
11416
  */
11372
11417
  async validate(request) {
11418
+ var _a2;
11373
11419
  const {
11374
11420
  licenseKey,
11375
11421
  projectId,
@@ -11379,33 +11425,42 @@ var _LicenseValidator = class _LicenseValidator {
11379
11425
  retrivoraApiBase,
11380
11426
  headers: customHeaders
11381
11427
  } = request;
11382
- if (!licenseKey) {
11383
- this.purgeCache(licenseKey);
11384
- throw new LicenseValidationError("Missing RETRIVORA_LICENSE_KEY in request.");
11428
+ const getHeader = (name) => {
11429
+ if (!customHeaders) return void 0;
11430
+ const target = name.toLowerCase();
11431
+ for (const [k, v] of Object.entries(customHeaders)) {
11432
+ if (k.toLowerCase() === target) return v;
11433
+ }
11434
+ return void 0;
11435
+ };
11436
+ const effectiveLicenseKey = licenseKey || getHeader("x-license-key") || ((_a2 = getHeader("authorization")) == null ? void 0 : _a2.replace(/^Bearer\s+/i, "")) || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY || process.env.RETRIVORA_LICENSE_KEY : "") || "";
11437
+ if (!effectiveLicenseKey) {
11438
+ this.purgeCache(effectiveLicenseKey);
11439
+ throw new LicenseValidationError("Missing RETRIVORA_LICENSE_KEY in request. Set NEXT_PUBLIC_RETRIVORA_LICENSE_KEY in your environment or pass licenseKey as a prop.");
11385
11440
  }
11386
- const cached = this.cache.get(licenseKey);
11441
+ const cached = this.cache.get(effectiveLicenseKey);
11387
11442
  if (cached) {
11388
11443
  const isFresh = Date.now() - cached.timestamp < _LicenseValidator.SUCCESS_CACHE_TTL_MS;
11389
11444
  if (isFresh && cached.response.valid && cached.response.licenseStatus === "ACTIVE" && !cached.response.forceUpgrade) {
11390
11445
  return cached.response;
11391
11446
  } else {
11392
- this.cache.delete(licenseKey);
11447
+ this.cache.delete(effectiveLicenseKey);
11393
11448
  }
11394
11449
  }
11395
11450
  const base = retrivoraApiBase || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_RETRIVORA_API_BASE || process.env.RETRIVORA_API_BASE : "") || "/api/retrivora";
11396
11451
  const validateUrl = `${base.replace(/\/$/, "")}/v1/license/validate`;
11397
- let res;
11452
+ let res = null;
11398
11453
  try {
11399
11454
  res = await fetch(validateUrl, {
11400
11455
  method: "POST",
11401
11456
  cache: "no-store",
11402
11457
  headers: __spreadValues({
11403
11458
  "Content-Type": "application/json",
11404
- "x-license-key": licenseKey,
11459
+ "x-license-key": effectiveLicenseKey,
11405
11460
  "x-sdk-version": sdkVersion
11406
11461
  }, customHeaders || {}),
11407
11462
  body: JSON.stringify({
11408
- licenseKey,
11463
+ licenseKey: effectiveLicenseKey,
11409
11464
  projectId,
11410
11465
  sdkVersion,
11411
11466
  sdk,
@@ -11413,8 +11468,40 @@ var _LicenseValidator = class _LicenseValidator {
11413
11468
  })
11414
11469
  });
11415
11470
  } catch (err) {
11416
- this.purgeCache(licenseKey);
11417
- throw new LicenseValidationError(`License validation request failed: ${(err == null ? void 0 : err.message) || "Network error"}`);
11471
+ try {
11472
+ const payload = LicenseVerifier.verify(effectiveLicenseKey, projectId || "my-rag-app");
11473
+ const fallbackResp = {
11474
+ valid: true,
11475
+ licenseStatus: "ACTIVE",
11476
+ minimumSupportedVersion: "2.1.0",
11477
+ latestVersion: SDK_VERSION,
11478
+ forceUpgrade: false,
11479
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1e3).toISOString() : null,
11480
+ message: "Local license verification active."
11481
+ };
11482
+ this.cache.set(effectiveLicenseKey, { response: fallbackResp, timestamp: Date.now() });
11483
+ return fallbackResp;
11484
+ } catch (e) {
11485
+ this.purgeCache(effectiveLicenseKey);
11486
+ throw new LicenseValidationError(`License validation request failed: ${(err == null ? void 0 : err.message) || "Network error"}`);
11487
+ }
11488
+ }
11489
+ if (res && !res.ok) {
11490
+ try {
11491
+ const payload = LicenseVerifier.verify(effectiveLicenseKey, projectId || "my-rag-app");
11492
+ const fallbackResp = {
11493
+ valid: true,
11494
+ licenseStatus: "ACTIVE",
11495
+ minimumSupportedVersion: "2.1.0",
11496
+ latestVersion: SDK_VERSION,
11497
+ forceUpgrade: false,
11498
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1e3).toISOString() : null,
11499
+ message: "Local license verification active."
11500
+ };
11501
+ this.cache.set(effectiveLicenseKey, { response: fallbackResp, timestamp: Date.now() });
11502
+ return fallbackResp;
11503
+ } catch (e) {
11504
+ }
11418
11505
  }
11419
11506
  const data = await res.json().catch(() => ({
11420
11507
  valid: false,
@@ -11426,7 +11513,7 @@ var _LicenseValidator = class _LicenseValidator {
11426
11513
  message: "Failed to parse license validation response."
11427
11514
  }));
11428
11515
  if (!res.ok || !data.valid || data.licenseStatus !== "ACTIVE" || data.forceUpgrade) {
11429
- this.purgeCache(licenseKey);
11516
+ this.purgeCache(effectiveLicenseKey);
11430
11517
  if (data.forceUpgrade || res.status === 426) {
11431
11518
  throw new SDKVersionUnsupportedError(
11432
11519
  data.message || `This SDK version (${sdkVersion}) is no longer supported. Upgrade to version ${data.latestVersion || "2.2.6"} or later.`,
@@ -11436,7 +11523,7 @@ var _LicenseValidator = class _LicenseValidator {
11436
11523
  const errMsg = data.message || `License validation failed with status: ${data.licenseStatus}. Access denied.`;
11437
11524
  throw new LicenseValidationError(errMsg, data);
11438
11525
  }
11439
- this.cache.set(licenseKey, {
11526
+ this.cache.set(effectiveLicenseKey, {
11440
11527
  response: data,
11441
11528
  timestamp: Date.now()
11442
11529
  });
package/dist/server.mjs CHANGED
@@ -4723,7 +4723,7 @@ var ConfigValidator = class {
4723
4723
  // package.json
4724
4724
  var package_default = {
4725
4725
  name: "@retrivora-ai/rag-engine",
4726
- version: "2.2.7",
4726
+ version: "2.2.9",
4727
4727
  description: "Retrivora AI is a plug-and-play AI engine for RAG chat experiences \u2014 generic vector DB + LLM provider, embeddable or standalone.",
4728
4728
  author: "Abhinav Alkuchi",
4729
4729
  license: "UNLICENSED",
@@ -10912,6 +10912,46 @@ function createHealthHandler(configOrPlugin, options) {
10912
10912
  }
10913
10913
  };
10914
10914
  }
10915
+ function createLicenseHandler(configOrPlugin, options) {
10916
+ const plugin = getOrCreatePlugin(configOrPlugin);
10917
+ const onAuthorize = options == null ? void 0 : options.onAuthorize;
10918
+ return async function POST(req) {
10919
+ var _a2;
10920
+ if (req) {
10921
+ const authResult = await checkAuth(req, onAuthorize);
10922
+ if (authResult) return authResult;
10923
+ }
10924
+ try {
10925
+ const body = await req.json().catch(() => ({}));
10926
+ const config = plugin.getConfig();
10927
+ const licenseKey = req.headers.get("x-license-key") || (body == null ? void 0 : body.licenseKey) || ((_a2 = req.headers.get("authorization")) == null ? void 0 : _a2.replace(/^Bearer\s+/i, "")) || config.licenseKey || process.env.RETRIVORA_LICENSE_KEY || process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY || "";
10928
+ const projectId = (body == null ? void 0 : body.projectId) || config.projectId || "my-rag-app";
10929
+ const payload = LicenseVerifier.verify(licenseKey, projectId);
10930
+ return NextResponse.json({
10931
+ valid: true,
10932
+ licenseStatus: "ACTIVE",
10933
+ minimumSupportedVersion: "2.1.0",
10934
+ latestVersion: SDK_VERSION,
10935
+ forceUpgrade: false,
10936
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1e3).toISOString() : null,
10937
+ message: "License key is active and valid."
10938
+ });
10939
+ } catch (err) {
10940
+ return NextResponse.json(
10941
+ {
10942
+ valid: false,
10943
+ licenseStatus: "REVOKED",
10944
+ minimumSupportedVersion: "2.1.0",
10945
+ latestVersion: SDK_VERSION,
10946
+ forceUpgrade: false,
10947
+ expiresAt: null,
10948
+ message: (err == null ? void 0 : err.message) || "License validation failed."
10949
+ },
10950
+ { status: 400 }
10951
+ );
10952
+ }
10953
+ };
10954
+ }
10915
10955
  function createUploadHandler(configOrPlugin, options) {
10916
10956
  const plugin = getOrCreatePlugin(configOrPlugin);
10917
10957
  const onAuthorize = options == null ? void 0 : options.onAuthorize;
@@ -11163,6 +11203,7 @@ function createRagHandler(configOrPlugin, options) {
11163
11203
  const historyHandler = createHistoryHandler(plugin, { onAuthorize });
11164
11204
  const feedbackHandler = createFeedbackHandler(plugin, { onAuthorize });
11165
11205
  const sessionsHandler = createSessionsHandler(plugin, { onAuthorize });
11206
+ const licenseHandler = createLicenseHandler(plugin, { onAuthorize });
11166
11207
  async function routePostRequest(req, segment) {
11167
11208
  switch (segment) {
11168
11209
  case "chat":
@@ -11180,6 +11221,9 @@ function createRagHandler(configOrPlugin, options) {
11180
11221
  return historyHandler(req);
11181
11222
  case "feedback":
11182
11223
  return feedbackHandler(req);
11224
+ case "license/validate":
11225
+ case "v1/license/validate":
11226
+ return licenseHandler(req);
11183
11227
  default:
11184
11228
  return NextResponse.json({ error: `Not Found: POST segment "${segment}" not supported.` }, { status: 404 });
11185
11229
  }
@@ -11209,6 +11253,7 @@ function createRagHandler(configOrPlugin, options) {
11209
11253
  try {
11210
11254
  const url = new URL(req.url);
11211
11255
  const pathname = url.pathname;
11256
+ if (pathname.includes("/license/validate")) return "v1/license/validate";
11212
11257
  if (pathname.endsWith("/upload")) return "upload";
11213
11258
  if (pathname.endsWith("/chat") || pathname.endsWith("/chat-sync")) return "chat";
11214
11259
  if (pathname.endsWith("/health")) return "health";
@@ -11270,6 +11315,7 @@ var _LicenseValidator = class _LicenseValidator {
11270
11315
  * Validate license status and SDK version against Retrivora License Service
11271
11316
  */
11272
11317
  async validate(request) {
11318
+ var _a2;
11273
11319
  const {
11274
11320
  licenseKey,
11275
11321
  projectId,
@@ -11279,33 +11325,42 @@ var _LicenseValidator = class _LicenseValidator {
11279
11325
  retrivoraApiBase,
11280
11326
  headers: customHeaders
11281
11327
  } = request;
11282
- if (!licenseKey) {
11283
- this.purgeCache(licenseKey);
11284
- throw new LicenseValidationError("Missing RETRIVORA_LICENSE_KEY in request.");
11328
+ const getHeader = (name) => {
11329
+ if (!customHeaders) return void 0;
11330
+ const target = name.toLowerCase();
11331
+ for (const [k, v] of Object.entries(customHeaders)) {
11332
+ if (k.toLowerCase() === target) return v;
11333
+ }
11334
+ return void 0;
11335
+ };
11336
+ const effectiveLicenseKey = licenseKey || getHeader("x-license-key") || ((_a2 = getHeader("authorization")) == null ? void 0 : _a2.replace(/^Bearer\s+/i, "")) || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY || process.env.RETRIVORA_LICENSE_KEY : "") || "";
11337
+ if (!effectiveLicenseKey) {
11338
+ this.purgeCache(effectiveLicenseKey);
11339
+ throw new LicenseValidationError("Missing RETRIVORA_LICENSE_KEY in request. Set NEXT_PUBLIC_RETRIVORA_LICENSE_KEY in your environment or pass licenseKey as a prop.");
11285
11340
  }
11286
- const cached = this.cache.get(licenseKey);
11341
+ const cached = this.cache.get(effectiveLicenseKey);
11287
11342
  if (cached) {
11288
11343
  const isFresh = Date.now() - cached.timestamp < _LicenseValidator.SUCCESS_CACHE_TTL_MS;
11289
11344
  if (isFresh && cached.response.valid && cached.response.licenseStatus === "ACTIVE" && !cached.response.forceUpgrade) {
11290
11345
  return cached.response;
11291
11346
  } else {
11292
- this.cache.delete(licenseKey);
11347
+ this.cache.delete(effectiveLicenseKey);
11293
11348
  }
11294
11349
  }
11295
11350
  const base = retrivoraApiBase || (typeof process !== "undefined" ? process.env.NEXT_PUBLIC_RETRIVORA_API_BASE || process.env.RETRIVORA_API_BASE : "") || "/api/retrivora";
11296
11351
  const validateUrl = `${base.replace(/\/$/, "")}/v1/license/validate`;
11297
- let res;
11352
+ let res = null;
11298
11353
  try {
11299
11354
  res = await fetch(validateUrl, {
11300
11355
  method: "POST",
11301
11356
  cache: "no-store",
11302
11357
  headers: __spreadValues({
11303
11358
  "Content-Type": "application/json",
11304
- "x-license-key": licenseKey,
11359
+ "x-license-key": effectiveLicenseKey,
11305
11360
  "x-sdk-version": sdkVersion
11306
11361
  }, customHeaders || {}),
11307
11362
  body: JSON.stringify({
11308
- licenseKey,
11363
+ licenseKey: effectiveLicenseKey,
11309
11364
  projectId,
11310
11365
  sdkVersion,
11311
11366
  sdk,
@@ -11313,8 +11368,40 @@ var _LicenseValidator = class _LicenseValidator {
11313
11368
  })
11314
11369
  });
11315
11370
  } catch (err) {
11316
- this.purgeCache(licenseKey);
11317
- throw new LicenseValidationError(`License validation request failed: ${(err == null ? void 0 : err.message) || "Network error"}`);
11371
+ try {
11372
+ const payload = LicenseVerifier.verify(effectiveLicenseKey, projectId || "my-rag-app");
11373
+ const fallbackResp = {
11374
+ valid: true,
11375
+ licenseStatus: "ACTIVE",
11376
+ minimumSupportedVersion: "2.1.0",
11377
+ latestVersion: SDK_VERSION,
11378
+ forceUpgrade: false,
11379
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1e3).toISOString() : null,
11380
+ message: "Local license verification active."
11381
+ };
11382
+ this.cache.set(effectiveLicenseKey, { response: fallbackResp, timestamp: Date.now() });
11383
+ return fallbackResp;
11384
+ } catch (e) {
11385
+ this.purgeCache(effectiveLicenseKey);
11386
+ throw new LicenseValidationError(`License validation request failed: ${(err == null ? void 0 : err.message) || "Network error"}`);
11387
+ }
11388
+ }
11389
+ if (res && !res.ok) {
11390
+ try {
11391
+ const payload = LicenseVerifier.verify(effectiveLicenseKey, projectId || "my-rag-app");
11392
+ const fallbackResp = {
11393
+ valid: true,
11394
+ licenseStatus: "ACTIVE",
11395
+ minimumSupportedVersion: "2.1.0",
11396
+ latestVersion: SDK_VERSION,
11397
+ forceUpgrade: false,
11398
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1e3).toISOString() : null,
11399
+ message: "Local license verification active."
11400
+ };
11401
+ this.cache.set(effectiveLicenseKey, { response: fallbackResp, timestamp: Date.now() });
11402
+ return fallbackResp;
11403
+ } catch (e) {
11404
+ }
11318
11405
  }
11319
11406
  const data = await res.json().catch(() => ({
11320
11407
  valid: false,
@@ -11326,7 +11413,7 @@ var _LicenseValidator = class _LicenseValidator {
11326
11413
  message: "Failed to parse license validation response."
11327
11414
  }));
11328
11415
  if (!res.ok || !data.valid || data.licenseStatus !== "ACTIVE" || data.forceUpgrade) {
11329
- this.purgeCache(licenseKey);
11416
+ this.purgeCache(effectiveLicenseKey);
11330
11417
  if (data.forceUpgrade || res.status === 426) {
11331
11418
  throw new SDKVersionUnsupportedError(
11332
11419
  data.message || `This SDK version (${sdkVersion}) is no longer supported. Upgrade to version ${data.latestVersion || "2.2.6"} or later.`,
@@ -11336,7 +11423,7 @@ var _LicenseValidator = class _LicenseValidator {
11336
11423
  const errMsg = data.message || `License validation failed with status: ${data.licenseStatus}. Access denied.`;
11337
11424
  throw new LicenseValidationError(errMsg, data);
11338
11425
  }
11339
- this.cache.set(licenseKey, {
11426
+ this.cache.set(effectiveLicenseKey, {
11340
11427
  response: data,
11341
11428
  timestamp: Date.now()
11342
11429
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@retrivora-ai/rag-engine",
3
- "version": "2.2.7",
3
+ "version": "2.2.9",
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",
@@ -46,7 +46,7 @@ const GEMINI_STYLES = `
46
46
  }
47
47
  `;
48
48
 
49
- export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, retrivoraApiBase, headers }: ChatWidgetProps) {
49
+ export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, retrivoraApiBase, licenseKey: licenseKeyProp, headers }: ChatWidgetProps) {
50
50
  const { ui, projectId } = useConfig();
51
51
  const [isOpen, setIsOpen] = useState(false);
52
52
  const [hasUnread, setHasUnread] = useState(false);
@@ -72,9 +72,26 @@ export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, ret
72
72
  const handleOpen = async () => {
73
73
  setIsValidating(true);
74
74
  try {
75
- const licenseKey = headers?.['x-license-key'] || headers?.['authorization']?.replace(/^Bearer\s+/i, '') || '';
75
+ const getHeader = (name: string) => {
76
+ if (!headers) return undefined;
77
+ const target = name.toLowerCase();
78
+ for (const [k, v] of Object.entries(headers)) {
79
+ if (k.toLowerCase() === target) return v;
80
+ }
81
+ return undefined;
82
+ };
83
+
84
+ const resolvedLicenseKey =
85
+ licenseKeyProp ||
86
+ getHeader('x-license-key') ||
87
+ getHeader('authorization')?.replace(/^Bearer\s+/i, '') ||
88
+ (typeof process !== 'undefined'
89
+ ? (process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY || process.env.RETRIVORA_LICENSE_KEY)
90
+ : '') ||
91
+ '';
92
+
76
93
  await LicenseValidator.getInstance().validate({
77
- licenseKey,
94
+ licenseKey: resolvedLicenseKey,
78
95
  projectId,
79
96
  retrivoraApiBase,
80
97
  headers,
@@ -167,6 +184,7 @@ export function ChatWidget({ position = 'bottom-right', onAddToCart, apiUrl, ret
167
184
  onAddToCart={onAddToCart}
168
185
  apiUrl={apiUrl}
169
186
  retrivoraApiBase={retrivoraApiBase}
187
+ licenseKey={licenseKeyProp}
170
188
  headers={headers}
171
189
  />
172
190
  {/* Pointer (Tail) */}
@@ -68,6 +68,7 @@ export function ChatWindow({
68
68
  onAddToCart,
69
69
  apiUrl,
70
70
  retrivoraApiBase,
71
+ licenseKey: licenseKeyProp,
71
72
  headers
72
73
  }: ChatWindowProps) {
73
74
  const { ui, projectId } = useConfig();
@@ -81,6 +82,7 @@ export function ChatWindow({
81
82
  namespace: projectId,
82
83
  apiUrl,
83
84
  retrivoraApiBase,
85
+ licenseKey: licenseKeyProp,
84
86
  headers,
85
87
  });
86
88
 
@@ -98,9 +100,26 @@ export function ChatWindow({
98
100
  let isMounted = true;
99
101
  async function validateSessionLicense() {
100
102
  try {
101
- const licenseKey = headers?.['x-license-key'] || headers?.['authorization']?.replace(/^Bearer\s+/i, '') || '';
103
+ const getHeader = (name: string) => {
104
+ if (!headers) return undefined;
105
+ const target = name.toLowerCase();
106
+ for (const [k, v] of Object.entries(headers)) {
107
+ if (k.toLowerCase() === target) return v;
108
+ }
109
+ return undefined;
110
+ };
111
+
112
+ const resolvedLicenseKey =
113
+ licenseKeyProp ||
114
+ getHeader('x-license-key') ||
115
+ getHeader('authorization')?.replace(/^Bearer\s+/i, '') ||
116
+ (typeof process !== 'undefined'
117
+ ? (process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY || process.env.RETRIVORA_LICENSE_KEY)
118
+ : '') ||
119
+ '';
120
+
102
121
  await LicenseValidator.getInstance().validate({
103
- licenseKey,
122
+ licenseKey: resolvedLicenseKey,
104
123
  projectId,
105
124
  retrivoraApiBase,
106
125
  headers,
@@ -1,5 +1,6 @@
1
1
  import { SDK_VERSION } from '../version';
2
2
  import { SDKVersionUnsupportedError, LicenseValidationError } from '../exceptions';
3
+ import { LicenseVerifier } from './LicenseVerifier';
3
4
 
4
5
  export interface LicenseValidationRequest {
5
6
  licenseKey: string;
@@ -65,39 +66,57 @@ export class LicenseValidator {
65
66
  headers: customHeaders,
66
67
  } = request;
67
68
 
68
- if (!licenseKey) {
69
- this.purgeCache(licenseKey);
70
- throw new LicenseValidationError('Missing RETRIVORA_LICENSE_KEY in request.');
69
+ const getHeader = (name: string): string | undefined => {
70
+ if (!customHeaders) return undefined;
71
+ const target = name.toLowerCase();
72
+ for (const [k, v] of Object.entries(customHeaders)) {
73
+ if (k.toLowerCase() === target) return v;
74
+ }
75
+ return undefined;
76
+ };
77
+
78
+ const effectiveLicenseKey =
79
+ licenseKey ||
80
+ getHeader('x-license-key') ||
81
+ getHeader('authorization')?.replace(/^Bearer\s+/i, '') ||
82
+ (typeof process !== 'undefined'
83
+ ? (process.env.NEXT_PUBLIC_RETRIVORA_LICENSE_KEY || process.env.RETRIVORA_LICENSE_KEY)
84
+ : '') ||
85
+ '';
86
+
87
+ if (!effectiveLicenseKey) {
88
+ this.purgeCache(effectiveLicenseKey);
89
+ throw new LicenseValidationError('Missing RETRIVORA_LICENSE_KEY in request. Set NEXT_PUBLIC_RETRIVORA_LICENSE_KEY in your environment or pass licenseKey as a prop.');
71
90
  }
72
91
 
73
92
  // 1. Check successful 5-minute cache
74
- const cached = this.cache.get(licenseKey);
93
+ const cached = this.cache.get(effectiveLicenseKey);
75
94
  if (cached) {
76
95
  const isFresh = Date.now() - cached.timestamp < LicenseValidator.SUCCESS_CACHE_TTL_MS;
77
96
  if (isFresh && cached.response.valid && cached.response.licenseStatus === 'ACTIVE' && !cached.response.forceUpgrade) {
78
97
  return cached.response;
79
98
  } else {
80
- this.cache.delete(licenseKey);
99
+ this.cache.delete(effectiveLicenseKey);
81
100
  }
82
101
  }
83
102
 
84
- // 2. Fetch live validation from Retrivora backend API
103
+ // 2. Fetch live validation from Retrivora backend API / local catch-all route
85
104
  const base = retrivoraApiBase || (typeof process !== 'undefined' ? process.env.NEXT_PUBLIC_RETRIVORA_API_BASE || process.env.RETRIVORA_API_BASE : '') || '/api/retrivora';
86
105
  const validateUrl = `${base.replace(/\/$/, '')}/v1/license/validate`;
87
106
 
88
- let res: Response;
107
+ let res: Response | null = null;
89
108
  try {
90
109
  res = await fetch(validateUrl, {
91
110
  method: 'POST',
92
111
  cache: 'no-store',
93
112
  headers: {
94
113
  'Content-Type': 'application/json',
95
- 'x-license-key': licenseKey,
114
+ 'x-license-key': effectiveLicenseKey,
96
115
  'x-sdk-version': sdkVersion,
97
116
  ...(customHeaders || {}),
98
117
  },
99
118
  body: JSON.stringify({
100
- licenseKey,
119
+ licenseKey: effectiveLicenseKey,
101
120
  projectId,
102
121
  sdkVersion,
103
122
  sdk,
@@ -105,12 +124,45 @@ export class LicenseValidator {
105
124
  }),
106
125
  });
107
126
  } 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'}`);
127
+ // Fallback: If network error occurs, verify locally via RSA LicenseVerifier
128
+ try {
129
+ const payload = LicenseVerifier.verify(effectiveLicenseKey, projectId || 'my-rag-app');
130
+ const fallbackResp: LicenseValidationResponse = {
131
+ valid: true,
132
+ licenseStatus: 'ACTIVE',
133
+ minimumSupportedVersion: '2.1.0',
134
+ latestVersion: SDK_VERSION,
135
+ forceUpgrade: false,
136
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1000).toISOString() : null,
137
+ message: 'Local license verification active.',
138
+ };
139
+ this.cache.set(effectiveLicenseKey, { response: fallbackResp, timestamp: Date.now() });
140
+ return fallbackResp;
141
+ } catch {
142
+ this.purgeCache(effectiveLicenseKey);
143
+ throw new LicenseValidationError(`License validation request failed: ${err?.message || 'Network error'}`);
144
+ }
145
+ }
146
+
147
+ if (res && !res.ok) {
148
+ // Attempt local verification fallback if API route returned 404 or non-OK response
149
+ try {
150
+ const payload = LicenseVerifier.verify(effectiveLicenseKey, projectId || 'my-rag-app');
151
+ const fallbackResp: LicenseValidationResponse = {
152
+ valid: true,
153
+ licenseStatus: 'ACTIVE',
154
+ minimumSupportedVersion: '2.1.0',
155
+ latestVersion: SDK_VERSION,
156
+ forceUpgrade: false,
157
+ expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1000).toISOString() : null,
158
+ message: 'Local license verification active.',
159
+ };
160
+ this.cache.set(effectiveLicenseKey, { response: fallbackResp, timestamp: Date.now() });
161
+ return fallbackResp;
162
+ } catch { /* proceed to parse error response */ }
111
163
  }
112
164
 
113
- const data: LicenseValidationResponse = await res.json().catch(() => ({
165
+ const data: LicenseValidationResponse = await res!.json().catch(() => ({
114
166
  valid: false,
115
167
  licenseStatus: 'REVOKED' as const,
116
168
  minimumSupportedVersion: '2.1.0',
@@ -122,7 +174,7 @@ export class LicenseValidator {
122
174
 
123
175
  // 3. Negative validation responses immediately purge any existing cache
124
176
  if (!res.ok || !data.valid || data.licenseStatus !== 'ACTIVE' || data.forceUpgrade) {
125
- this.purgeCache(licenseKey);
177
+ this.purgeCache(effectiveLicenseKey);
126
178
 
127
179
  if (data.forceUpgrade || res.status === 426) {
128
180
  throw new SDKVersionUnsupportedError(
@@ -136,7 +188,7 @@ export class LicenseValidator {
136
188
  }
137
189
 
138
190
  // 4. Cache SUCCESSFUL validation only (TTL = 5 minutes)
139
- this.cache.set(licenseKey, {
191
+ this.cache.set(effectiveLicenseKey, {
140
192
  response: data,
141
193
  timestamp: Date.now(),
142
194
  });