@eshal-bot/chat-widget 0.1.61 → 0.1.63

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.
@@ -8011,14 +8011,18 @@
8011
8011
  * @param {object} request - Chat request object
8012
8012
  * @param {string} apiBaseUrl - Base URL for the API
8013
8013
  */
8014
- const sendStreamingRequest = async (request, apiBaseUrl, organizationId) => {
8014
+ const sendStreamingRequest = async (request, apiBaseUrl, organizationId, secretKey) => {
8015
8015
  const headers = {
8016
8016
  'Content-Type': 'application/json',
8017
8017
  Accept: 'text/event-stream'
8018
8018
  };
8019
8019
 
8020
- // Add organization ID header if provided
8021
- if (organizationId) {
8020
+ // Mode A — secret key (direct embed): key replaces orgId in the header
8021
+ // Mode B — proxy (@eshal-bot/server): no header from widget; proxy injects x-eshal-sig + x-eshal-org
8022
+ // Mode C — legacy: orgId sent directly
8023
+ if (secretKey) {
8024
+ headers['x-eshal-key'] = secretKey;
8025
+ } else if (organizationId) {
8022
8026
  headers['x-eshal-org'] = organizationId;
8023
8027
  }
8024
8028
  const response = await fetch("".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/support"), {
@@ -9129,6 +9133,7 @@
9129
9133
  apiBaseUrl,
9130
9134
  apiKey,
9131
9135
  organizationId,
9136
+ secretKey,
9132
9137
  autoOpen,
9133
9138
  openDelay,
9134
9139
  darkMode,
@@ -9765,7 +9770,7 @@
9765
9770
  if (apiType === 'userName' || apiType === 'email' || apiType === 'phone') {
9766
9771
  // For onboarding fields, just consume the stream without processing.
9767
9772
  // Single attempt — no retry, no user-visible partial state to clean up.
9768
- const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
9773
+ const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId, secretKey);
9769
9774
  const reader = stream.getReader();
9770
9775
  const decoder = new TextDecoder();
9771
9776
  while (true) {
@@ -9784,7 +9789,7 @@
9784
9789
  let lastErr;
9785
9790
  for (let attempt = 0; attempt <= STREAM_RETRY_DELAYS_MS.length; attempt += 1) {
9786
9791
  try {
9787
- const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId);
9792
+ const stream = await sendStreamingRequest(chatRequest, apiBaseUrl, organizationId, secretKey);
9788
9793
  await processStream(stream, setMessages);
9789
9794
  updateActivity(organizationId);
9790
9795
  return {
@@ -91749,6 +91754,31 @@
91749
91754
  return String(n);
91750
91755
  }
91751
91756
 
91757
+ // ── Currency-aware value formatting ──────────────────────────────────────
91758
+ // A2UI chart data is normalized to `{ label, value }` by A2uiBlock's
91759
+ // numericData(), which drops the original Y-column key. A2uiBlock now threads
91760
+ // that key through as `props.yKey` so these renderers can decide whether the
91761
+ // axis / tooltip values are money. Heuristic mirrors the frontend's
91762
+ // chartShared.tsx `isCurrencyKey` — keep the keyword list in lockstep.
91763
+ // `(^|[_\W])` (not `\b`) so "average_salary" / "total_expenses" still match —
91764
+ // JS `\b` doesn't fire next to `_`.
91765
+ const CURRENCY_KEY_RE$1 = /(^|[_\W])(price|salary|cost|amount|revenue|expense|total|value|budget|spend|income|sale|balance)/i;
91766
+ function isCurrencyKey$1(key) {
91767
+ return typeof key === 'string' && CURRENCY_KEY_RE$1.test(key);
91768
+ }
91769
+
91770
+ // Compact currency for axis ticks → "$120.0k". Full currency for tooltips →
91771
+ // "$87,600" (comma-grouped, up to 2 decimals, no forced trailing zeros so a
91772
+ // whole-dollar salary reads "$81,200" not "$81,200.00").
91773
+ function formatCurrencyCompact(v) {
91774
+ return "$".concat(formatCompactNumber(v));
91775
+ }
91776
+ function formatCurrencyFull(v) {
91777
+ return "$".concat(Number(v).toLocaleString('en-US', {
91778
+ maximumFractionDigits: 2
91779
+ }));
91780
+ }
91781
+
91752
91782
  /**
91753
91783
  * Recharts bar chart — mirrors agent-sdk's eshalRenderers.BarChart.
91754
91784
  *
@@ -91761,6 +91791,11 @@
91761
91791
  isDark = false
91762
91792
  } = _ref;
91763
91793
  const data = Array.isArray(props.data) ? props.data : [];
91794
+ // Whether the Y values are money — drives the `$` on axis ticks + tooltip.
91795
+ const currency = isCurrencyKey$1(props.yKey);
91796
+ // Human series name for the tooltip ("Average Salary"), so it no longer
91797
+ // reads the generic dataKey "value".
91798
+ const seriesName = props.valueLabel || undefined;
91764
91799
  const borderColor = isDark ? '#374151' : '#e2e8f0';
91765
91800
  const bgColor = isDark ? '#1f2937' : '#ffffff';
91766
91801
  const labelColor = isDark ? '#9ca3af' : '#64748b';
@@ -91835,6 +91870,9 @@
91835
91870
  fontSize: 11,
91836
91871
  fill: labelColor
91837
91872
  },
91873
+ angle: 0,
91874
+ textAnchor: "middle",
91875
+ interval: 0,
91838
91876
  label: props.xAxisLabel ? {
91839
91877
  value: props.xAxisLabel,
91840
91878
  position: 'insideBottom',
@@ -91855,9 +91893,9 @@
91855
91893
  fontSize: 11,
91856
91894
  fill: labelColor
91857
91895
  } : undefined,
91858
- tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
91896
+ tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
91859
91897
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
91860
- formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
91898
+ formatter: v => currency ? formatCurrencyFull(v) : props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
91861
91899
  contentStyle: {
91862
91900
  background: tooltipBg,
91863
91901
  border: "1px solid ".concat(borderColor),
@@ -91867,6 +91905,7 @@
91867
91905
  }
91868
91906
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Bar, {
91869
91907
  dataKey: "value",
91908
+ name: seriesName,
91870
91909
  fill: "#3b82f6",
91871
91910
  radius: [2, 2, 0, 0]
91872
91911
  })]
@@ -91950,6 +91989,7 @@
91950
91989
  donut = false
91951
91990
  } = _ref3;
91952
91991
  const data = Array.isArray(props.data) ? props.data : [];
91992
+ const currency = isCurrencyKey$1(props.yKey);
91953
91993
  const borderColor = isDark ? '#374151' : '#e2e8f0';
91954
91994
  const bgColor = isDark ? '#1f2937' : '#ffffff';
91955
91995
  const titleColor = isDark ? '#f3f4f6' : '#0f172a';
@@ -92000,7 +92040,7 @@
92000
92040
  fill: PIE_COLORS[i % PIE_COLORS.length]
92001
92041
  }, i))
92002
92042
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
92003
- formatter: v => Number(v).toLocaleString(),
92043
+ formatter: v => currency ? formatCurrencyFull(v) : Number(v).toLocaleString(),
92004
92044
  contentStyle: {
92005
92045
  background: tooltipBg,
92006
92046
  border: "1px solid ".concat(borderColor),
@@ -92034,6 +92074,8 @@
92034
92074
  area = false
92035
92075
  } = _ref4;
92036
92076
  const data = Array.isArray(props.data) ? props.data : [];
92077
+ const currency = isCurrencyKey$1(props.yKey);
92078
+ const seriesName = props.valueLabel || undefined;
92037
92079
  const borderColor = isDark ? '#374151' : '#e2e8f0';
92038
92080
  const bgColor = isDark ? '#1f2937' : '#ffffff';
92039
92081
  const labelColor = isDark ? '#9ca3af' : '#64748b';
@@ -92109,6 +92151,9 @@
92109
92151
  fontSize: 11,
92110
92152
  fill: labelColor
92111
92153
  },
92154
+ angle: 0,
92155
+ textAnchor: "middle",
92156
+ interval: 0,
92112
92157
  label: props.xAxisLabel ? {
92113
92158
  value: props.xAxisLabel,
92114
92159
  position: 'insideBottom',
@@ -92122,9 +92167,9 @@
92122
92167
  fontSize: 11,
92123
92168
  fill: labelColor
92124
92169
  },
92125
- tickFormatter: v => props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
92170
+ tickFormatter: v => currency ? formatCurrencyCompact(v) : props.unit ? "".concat(formatCompactNumber(v), " ").concat(props.unit) : formatCompactNumber(v)
92126
92171
  }), /*#__PURE__*/jsxRuntimeExports.jsx(Tooltip, {
92127
- formatter: v => props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
92172
+ formatter: v => currency ? formatCurrencyFull(v) : props.unit ? "".concat(Number(v).toLocaleString(), " ").concat(props.unit) : Number(v).toLocaleString(),
92128
92173
  contentStyle: {
92129
92174
  background: tooltipBg,
92130
92175
  border: "1px solid ".concat(borderColor),
@@ -92135,6 +92180,7 @@
92135
92180
  }), area ? /*#__PURE__*/jsxRuntimeExports.jsx(Area, {
92136
92181
  type: "monotone",
92137
92182
  dataKey: "value",
92183
+ name: seriesName,
92138
92184
  stroke: "#3b82f6",
92139
92185
  fill: "#3b82f6",
92140
92186
  fillOpacity: 0.15,
@@ -92146,6 +92192,7 @@
92146
92192
  }) : /*#__PURE__*/jsxRuntimeExports.jsx(Line, {
92147
92193
  type: "monotone",
92148
92194
  dataKey: "value",
92195
+ name: seriesName,
92149
92196
  stroke: "#3b82f6",
92150
92197
  strokeWidth: 2,
92151
92198
  dot: {
@@ -92343,7 +92390,14 @@
92343
92390
  data: rows,
92344
92391
  unit: spec.unit,
92345
92392
  xAxisLabel: spec.xAxisLabel,
92346
- yAxisLabel: spec.yAxisLabel
92393
+ yAxisLabel: spec.yAxisLabel,
92394
+ // numericData() collapses the rows to { label, value }, dropping the
92395
+ // original Y-column key. Pass it through so the renderer can detect a
92396
+ // currency column (→ "$" on the Y-axis + tooltip) and label the tooltip
92397
+ // series with the human column name ("Average Salary") instead of the
92398
+ // generic dataKey "value".
92399
+ yKey: y,
92400
+ valueLabel: prettifyLabel(y)
92347
92401
  };
92348
92402
  if (t === 'pie') return /*#__PURE__*/jsxRuntimeExports.jsx(PieChartRenderer, {
92349
92403
  props: chartProps,
@@ -92670,10 +92724,12 @@
92670
92724
 
92671
92725
  // ISO date / datetime prefix — matches the typical SQL output the
92672
92726
  // agent's tools produce when GROUP BY-ing by month
92673
- // (`DATE_TRUNC('month', col)` → `YYYY-MM-DD`). Used by formatAxisTick
92674
- // below so a chart of monthly expenses shows "Jan '26" / "Feb '26"
92675
- // horizontally instead of "2026 01 01" slanted (EV-1187 follow-up).
92676
- const ISO_DATE_RE = /^(\d{4})-(\d{2})-(\d{2})(?:T|$)/;
92727
+ // (`DATE_TRUNC('month', col)` → `YYYY-MM-DD`, or `to_char(..,'YYYY-MM')`
92728
+ // → `YYYY-MM`). Used by formatAxisTick below so a chart of monthly
92729
+ // expenses shows "Jan '26" / "Feb '26" horizontally instead of
92730
+ // "2026 01 01" or "2026 01" slanted (EV-1187 follow-up + 2026-06-24
92731
+ // day-less variant fix).
92732
+ const ISO_DATE_RE = /^(\d{4})-(\d{2})(?:-\d{2})?(?:[T\s]|$)/;
92677
92733
  function formatIsoDateAsMonthTick(value) {
92678
92734
  if (typeof value !== 'string') return null;
92679
92735
  const m = value.match(ISO_DATE_RE);
@@ -92716,6 +92772,14 @@
92716
92772
  if (!SNAKE_CASE_CATEGORY_RE.test(value)) return value;
92717
92773
  return prettifyLabel(value);
92718
92774
  }
92775
+
92776
+ // Numeric string under a money column, e.g. "81,200" / "105444" / "-1,250.50".
92777
+ // Markdown tables (the common concierge path) arrive as text cells, so a
92778
+ // salary value is the STRING "81,200" — not a number — and skips the
92779
+ // USD_FORMATTER branch below. We prefix "$" while keeping the source's own
92780
+ // grouping/decimals so it reads "$81,200" (not "$81,200.00"). A leading "$"
92781
+ // already present makes the regex miss, so we never double-prefix.
92782
+ const _NUMERIC_STRING_RE = /^-?\d[\d,]*(\.\d+)?$/;
92719
92783
  function formatCell(v, columnKey) {
92720
92784
  if (v == null) return '';
92721
92785
  if (typeof v === 'number') {
@@ -92725,6 +92789,11 @@
92725
92789
  return Number.isInteger(v) ? String(v) : v.toFixed(2);
92726
92790
  }
92727
92791
  if (typeof v === 'object') return JSON.stringify(v);
92792
+ // Numeric string under a money-named column → prefix "$" (Bug: concierge
92793
+ // salary table showed "81,200" with no currency symbol).
92794
+ if (isCurrencyKey(columnKey) && _NUMERIC_STRING_RE.test(String(v).trim())) {
92795
+ return "$".concat(String(v).trim());
92796
+ }
92728
92797
  // String cells: prettify snake_case category labels (e.g.
92729
92798
  // `office_supplies` → "Office Supplies") so the table reads
92730
92799
  // consistently with the chart's X-axis ticks on the same data.
@@ -96163,7 +96232,18 @@
96163
96232
  copyChat: "Copy Chat",
96164
96233
  startChat: "Start Chat",
96165
96234
  startingVoiceSession: "Starting voice session...",
96166
- allowMicAccess: "Allow microphone access"
96235
+ allowMicAccess: "Allow microphone access",
96236
+ // Onboarding-form validation messages. These are the English fallback for
96237
+ // every locale (translate() falls back to EN[key]); without them the form
96238
+ // rendered the raw key strings ("emailInvalid", etc.). `fieldRequiredSuffix`
96239
+ // is appended after a field label: `${label} ${fieldRequiredSuffix}`.
96240
+ emailRequired: "Please enter your email address",
96241
+ nameRequired: "Please enter your name",
96242
+ phoneRequired: "Please enter your phone number",
96243
+ fieldRequiredSuffix: "is required",
96244
+ emailInvalid: "Please enter a valid email address",
96245
+ nameInvalid: "Please enter a valid name",
96246
+ phoneInvalid: "Please enter a valid phone number"
96167
96247
  };
96168
96248
  const DICTIONARIES = {
96169
96249
  en: EN,
@@ -99553,6 +99633,7 @@
99553
99633
  apiBaseUrl,
99554
99634
  apiKey,
99555
99635
  organizationId,
99636
+ secretKey,
99556
99637
  conversationId,
99557
99638
  conciergeId,
99558
99639
  onboardingQuestions = [],
@@ -100349,6 +100430,7 @@
100349
100430
  /**
100350
100431
  * @param {string|undefined|null} apiBaseUrl
100351
100432
  * @param {string|undefined|null} orgId
100433
+ * @param {string|undefined|null} secretKey — Mode A/B: orgId resolved server-side
100352
100434
  * @returns {{
100353
100435
  * categories: Array<{id:string,label:string,count:number,tags:string[]}>,
100354
100436
  * loading: boolean,
@@ -100356,14 +100438,17 @@
100356
100438
  * hasFilters: boolean,
100357
100439
  * }}
100358
100440
  */
100359
- function useFilterCatalog(apiBaseUrl, orgId) {
100441
+ function useFilterCatalog(apiBaseUrl, orgId, secretKey) {
100442
+ // Mode A: secretKey present, orgId null → key-based endpoint
100443
+ // Mode C: orgId present (legacy) → orgId-in-path endpoint
100444
+ const hasAuth = !!(apiBaseUrl && (orgId || secretKey));
100360
100445
  const [state, setState] = reactExports.useState({
100361
100446
  categories: [],
100362
- loading: !!(apiBaseUrl && orgId),
100447
+ loading: hasAuth,
100363
100448
  error: null
100364
100449
  });
100365
100450
  reactExports.useEffect(() => {
100366
- if (!apiBaseUrl || !orgId) {
100451
+ if (!apiBaseUrl || !orgId && !secretKey) {
100367
100452
  setState({
100368
100453
  categories: [],
100369
100454
  loading: false,
@@ -100371,8 +100456,8 @@
100371
100456
  });
100372
100457
  return;
100373
100458
  }
100374
- const key = "".concat(apiBaseUrl, "|").concat(orgId);
100375
- const cached = _PROCESS_CACHE.get(key);
100459
+ const cacheKey = secretKey ? "".concat(apiBaseUrl, "|__key__") : "".concat(apiBaseUrl, "|").concat(orgId);
100460
+ const cached = _PROCESS_CACHE.get(cacheKey);
100376
100461
  if (cached) {
100377
100462
  setState({
100378
100463
  categories: cached,
@@ -100386,12 +100471,15 @@
100386
100471
  loading: true,
100387
100472
  error: null
100388
100473
  }));
100389
- const url = "".concat(apiBaseUrl.replace(/\/$/, ""), "/api/v1/widget/filters/").concat(encodeURIComponent(orgId));
100474
+ const base = apiBaseUrl.replace(/\/$/, "");
100475
+ const url = secretKey ? "".concat(base, "/api/v1/widget/filters") : "".concat(base, "/api/v1/widget/filters/").concat(encodeURIComponent(orgId));
100476
+ const headers = {
100477
+ Accept: "application/json"
100478
+ };
100479
+ if (secretKey) headers["x-eshal-key"] = secretKey;
100390
100480
  fetch(url, {
100391
100481
  method: "GET",
100392
- headers: {
100393
- Accept: "application/json"
100394
- }
100482
+ headers
100395
100483
  }).then(async res => {
100396
100484
  if (!res.ok) {
100397
100485
  throw new Error("filter catalog HTTP ".concat(res.status));
@@ -100400,7 +100488,7 @@
100400
100488
  return Array.isArray(body === null || body === void 0 ? void 0 : body.categories) ? body.categories : [];
100401
100489
  }).then(categories => {
100402
100490
  if (cancelled) return;
100403
- _PROCESS_CACHE.set(key, categories);
100491
+ _PROCESS_CACHE.set(cacheKey, categories);
100404
100492
  setState({
100405
100493
  categories,
100406
100494
  loading: false,
@@ -100422,7 +100510,7 @@
100422
100510
  return () => {
100423
100511
  cancelled = true;
100424
100512
  };
100425
- }, [apiBaseUrl, orgId]);
100513
+ }, [apiBaseUrl, orgId, secretKey]);
100426
100514
  const hasFilters = reactExports.useMemo(() => Array.isArray(state.categories) && state.categories.length > 0, [state.categories]);
100427
100515
  return _objectSpread2(_objectSpread2({}, state), {}, {
100428
100516
  hasFilters
@@ -100504,17 +100592,25 @@
100504
100592
  * @param {string} orgId - Organization ID
100505
100593
  * @returns {Promise<object>} Agent configuration object
100506
100594
  */
100507
- const fetchAgentConfig = async (apiBaseUrl, orgId) => {
100508
- if (!apiBaseUrl || !orgId) {
100509
- throw new Error("apiBaseUrl and orgId are required");
100595
+ const fetchAgentConfig = async (apiBaseUrl, orgId, secretKey) => {
100596
+ if (!apiBaseUrl) {
100597
+ throw new Error("apiBaseUrl is required");
100598
+ }
100599
+ if (!secretKey && !orgId) {
100600
+ throw new Error("secretKey or orgId is required");
100510
100601
  }
100511
100602
  try {
100512
- const url = "".concat(apiBaseUrl.replace(/\/$/, ''), "/api/v1/deploy-agent/").concat(orgId);
100603
+ const base = apiBaseUrl.replace(/\/$/, '');
100604
+ // Mode A/B: no orgId in URL — backend resolves org from x-eshal-key or HMAC sig
100605
+ // Mode C legacy: orgId in URL path, no header needed
100606
+ const url = secretKey ? "".concat(base, "/api/v1/deploy-agent") : "".concat(base, "/api/v1/deploy-agent/").concat(orgId);
100607
+ const headers = {
100608
+ "Content-Type": "application/json"
100609
+ };
100610
+ if (secretKey) headers['x-eshal-key'] = secretKey;
100513
100611
  const response = await fetch(url, {
100514
100612
  method: "GET",
100515
- headers: {
100516
- "Content-Type": "application/json"
100517
- },
100613
+ headers,
100518
100614
  credentials: "include"
100519
100615
  });
100520
100616
  if (!response.ok) {
@@ -100531,8 +100627,11 @@
100531
100627
  const ChatWidget = _ref => {
100532
100628
  var _agentConfig$concierg;
100533
100629
  let {
100534
- // Required props for dynamic config
100630
+ // Auth — at least one of secretKey (Mode A/B) or orgId (Mode C legacy) is needed.
100631
+ secretKey,
100632
+ // sk_live_... from ChatWidget.init({ secretKey, apiBaseUrl })
100535
100633
  orgId,
100634
+ // legacy — kept for backward compatibility
100536
100635
  apiBaseUrl,
100537
100636
  // Optional override props (will override fetched config if provided)
100538
100637
  darkMode,
@@ -100583,14 +100682,21 @@
100583
100682
  // Fetch agent configuration on mount
100584
100683
  reactExports.useEffect(() => {
100585
100684
  const loadAgentConfig = async () => {
100586
- if (!orgId || !apiBaseUrl) {
100587
- setConfigError("orgId and apiBaseUrl are required");
100685
+ if (!apiBaseUrl) {
100686
+ setConfigError("apiBaseUrl is required");
100687
+ setIsLoadingConfig(false);
100688
+ return;
100689
+ }
100690
+ if (!secretKey && !orgId) {
100691
+ setConfigError("secretKey or orgId is required");
100588
100692
  setIsLoadingConfig(false);
100589
100693
  return;
100590
100694
  }
100591
100695
  try {
100592
100696
  setIsLoadingConfig(true);
100593
- const config = await fetchAgentConfig(apiBaseUrl, orgId);
100697
+ // Pass secretKey for Mode A/B — fetchAgentConfig will send it as a
100698
+ // header and omit the orgId path segment. Pass orgId for Mode C legacy.
100699
+ const config = await fetchAgentConfig(apiBaseUrl, orgId, secretKey);
100594
100700
  setAgentConfig(config);
100595
100701
  setConfigError(null);
100596
100702
  } catch (error) {
@@ -100601,7 +100707,7 @@
100601
100707
  }
100602
100708
  };
100603
100709
  loadAgentConfig();
100604
- }, [orgId, apiBaseUrl]);
100710
+ }, [orgId, secretKey, apiBaseUrl]);
100605
100711
 
100606
100712
  // Map API response to widget props (use props as overrides if provided)
100607
100713
  const widgetConfig = reactExports.useMemo(() => {
@@ -100779,6 +100885,7 @@
100779
100885
  // knowledge-api LoadBalancer.
100780
100886
  apiKey,
100781
100887
  organizationId: widgetConfig.organizationId,
100888
+ secretKey,
100782
100889
  autoOpen,
100783
100890
  openDelay,
100784
100891
  darkMode: widgetConfig.darkMode,
@@ -100805,14 +100912,15 @@
100805
100912
  // navigation (but not a page refresh — feedback is not written to
100806
100913
  // localStorage so it resets on reload as intended).
100807
100914
  updateMessageFeedback(messageContent, feedback);
100808
- if (!orgId || !conversationId || !apiBaseUrl) return;
100915
+ if (!orgId && !secretKey || !conversationId || !apiBaseUrl) return;
100809
100916
  const base = apiBaseUrl.replace(/\/$/, "");
100917
+ const feedbackHeaders = {
100918
+ "Content-Type": "application/json"
100919
+ };
100920
+ if (secretKey) feedbackHeaders['x-eshal-key'] = secretKey;else if (orgId) feedbackHeaders['x-eshal-org'] = orgId;
100810
100921
  fetch("".concat(base, "/api/v1/support/feedback"), {
100811
100922
  method: "POST",
100812
- headers: {
100813
- "Content-Type": "application/json",
100814
- "x-eshal-org": orgId
100815
- },
100923
+ headers: feedbackHeaders,
100816
100924
  credentials: "include",
100817
100925
  body: JSON.stringify({
100818
100926
  messageContent,
@@ -100838,7 +100946,7 @@
100838
100946
  const {
100839
100947
  categories: filterCategories,
100840
100948
  hasFilters: showFilterStep
100841
- } = useFilterCatalog(apiBaseUrl, orgId);
100949
+ } = useFilterCatalog(apiBaseUrl, orgId, secretKey);
100842
100950
 
100843
100951
  // Copy chat handler — copy all messages to clipboard
100844
100952
  const handleCopyChat = reactExports.useCallback(() => {
@@ -100944,6 +101052,7 @@
100944
101052
  apiBaseUrl: apiBaseUrl,
100945
101053
  apiKey: apiKey,
100946
101054
  organizationId: widgetConfig.organizationId,
101055
+ secretKey: secretKey,
100947
101056
  conversationId: conversationId,
100948
101057
  conciergeId: (_agentConfig$concierg = agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.conciergeId) !== null && _agentConfig$concierg !== void 0 ? _agentConfig$concierg : agentConfig === null || agentConfig === void 0 ? void 0 : agentConfig.id,
100949
101058
  onboardingQuestions: widgetConfig.onboardingQuestions,
@@ -101091,11 +101200,14 @@
101091
101200
  }
101092
101201
 
101093
101202
  // Render component
101094
- // If orgId and apiBaseUrl are provided, use dynamic config
101095
- // Otherwise, use the old static config approach for backward compatibility
101096
- const widgetProps = config.orgId && config.apiBaseUrl ? _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
101097
- // Required props
101098
- orgId: config.orgId,
101203
+ // Mode A/B: secretKey present — orgId derived server-side from the key
101204
+ // Mode C legacy: orgId in config — backward compatible
101205
+ // Both: apiBaseUrl required
101206
+ const hasNewAuth = (config.secretKey || config.orgId) && config.apiBaseUrl;
101207
+ const widgetProps = hasNewAuth ? _objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
101208
+ // Auth props — secretKey replaces orgId in Mode A/B
101209
+ secretKey: config.secretKey || null,
101210
+ orgId: config.orgId || null,
101099
101211
  apiBaseUrl: config.apiBaseUrl
101100
101212
  }, options.wsBaseUrl !== undefined && {
101101
101213
  wsBaseUrl: config.wsBaseUrl