@bircleai/widget-protocol 0.6.0 → 0.6.2

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.cjs CHANGED
@@ -131,6 +131,13 @@ function sanitizeThemeTexts(raw) {
131
131
  }
132
132
  return n > 0 ? out : null;
133
133
  }
134
+ function safeLauncherToken(v) {
135
+ if (typeof v !== "string") return null;
136
+ const t = v.trim();
137
+ if (t === "bircle" || t === "chat") return t;
138
+ if (t.length > 0 && t.length <= 8 && !t.includes(":") && !t.includes("/")) return t;
139
+ return null;
140
+ }
134
141
  function sanitizeServerTheme(raw) {
135
142
  if (typeof raw !== "object" || raw === null) return {};
136
143
  const o = raw;
@@ -139,7 +146,7 @@ function sanitizeServerTheme(raw) {
139
146
  if (primaryColor !== null) out.primaryColor = primaryColor;
140
147
  const accentColor = safeThemeColor(o.accentColor);
141
148
  if (accentColor !== null) out.accentColor = accentColor;
142
- const launcherIcon = safeThemeUrl(o.launcherIcon);
149
+ const launcherIcon = safeThemeUrl(o.launcherIcon) ?? safeLauncherToken(o.launcherIcon);
143
150
  if (launcherIcon !== null) out.launcherIcon = launcherIcon;
144
151
  const headerLogo = safeThemeUrl(o.headerLogo);
145
152
  if (headerLogo !== null) out.headerLogo = headerLogo;
@@ -160,8 +167,28 @@ function sanitizeServerTheme(raw) {
160
167
  if (locale !== null) out.locale = locale;
161
168
  const texts = sanitizeThemeTexts(o.texts);
162
169
  if (texts !== null) out.texts = texts;
170
+ const faqs = sanitizeThemeFaqs(o.faqs);
171
+ if (faqs !== null) out.faqs = faqs;
163
172
  return out;
164
173
  }
174
+ function sanitizeThemeFaqs(raw) {
175
+ if (!Array.isArray(raw)) return null;
176
+ const out = [];
177
+ for (const item of raw) {
178
+ if (out.length >= 6) break;
179
+ if (typeof item !== "object" || item === null) continue;
180
+ const v = item;
181
+ const label = typeof v.label === "string" ? v.label.trim() : "";
182
+ if (label === "" || label.length > 60) continue;
183
+ const entry = { label };
184
+ if (typeof v.message === "string") {
185
+ const message = v.message.trim();
186
+ if (message !== "" && message.length <= 500) entry.message = message;
187
+ }
188
+ out.push(entry);
189
+ }
190
+ return out.length > 0 ? out : null;
191
+ }
165
192
  function safeThemeLocale(value) {
166
193
  if (typeof value !== "string") return null;
167
194
  const v = value.trim();
package/dist/index.d.cts CHANGED
@@ -334,7 +334,7 @@ interface ChallengeUiConfig {
334
334
  interface ThemeResponse {
335
335
  /** Color de marca. `#rgb` o `#rrggbb`. Pasa por `safeThemeColor`. */
336
336
  primaryColor?: string;
337
- /** URL https del ícono del launcher. */
337
+ /** Ícono del launcher: URL https, un built-in ("bircle" | "chat") o un emoji corto. */
338
338
  launcherIcon?: string;
339
339
  position?: "bottom-right" | "bottom-left";
340
340
  /** URL https del logo del header. */
@@ -460,19 +460,6 @@ declare function safeThemeColor(value: unknown): string | null;
460
460
  * que termina en un `<img src>` es un vector de XSS en el sitio del cliente.
461
461
  */
462
462
  declare function safeThemeUrl(value: unknown): string | null;
463
- /**
464
- * Tema del server, saneado campo por campo contra el contrato.
465
- *
466
- * ES EL ÚNICO CAMINO DE ENTRADA del branding remoto: lo que no sale de acá no
467
- * debería aplicarse. Devuelve un objeto NUEVO con sólo los campos válidos —
468
- * nunca el input— así ninguna clave extra del wire (una que el server agregue
469
- * mañana, o una que un theme viejo tenga guardada) se cuela por un spread.
470
- *
471
- * NUNCA LANZA Y NUNCA RECHAZA EL TEMA ENTERO: un campo inválido se descarta y el
472
- * consumidor cae a su default. Perder el branding completo —o peor, no pintar el
473
- * chat— porque el `accentColor` está mal cargado sería mucho peor que mostrar el
474
- * acento por default.
475
- */
476
463
  declare function sanitizeServerTheme(raw: unknown): ServerThemeResponse;
477
464
  /**
478
465
  * Evento entregado por el WebSocket del widget (respuesta del asistente o del
package/dist/index.d.ts CHANGED
@@ -334,7 +334,7 @@ interface ChallengeUiConfig {
334
334
  interface ThemeResponse {
335
335
  /** Color de marca. `#rgb` o `#rrggbb`. Pasa por `safeThemeColor`. */
336
336
  primaryColor?: string;
337
- /** URL https del ícono del launcher. */
337
+ /** Ícono del launcher: URL https, un built-in ("bircle" | "chat") o un emoji corto. */
338
338
  launcherIcon?: string;
339
339
  position?: "bottom-right" | "bottom-left";
340
340
  /** URL https del logo del header. */
@@ -460,19 +460,6 @@ declare function safeThemeColor(value: unknown): string | null;
460
460
  * que termina en un `<img src>` es un vector de XSS en el sitio del cliente.
461
461
  */
462
462
  declare function safeThemeUrl(value: unknown): string | null;
463
- /**
464
- * Tema del server, saneado campo por campo contra el contrato.
465
- *
466
- * ES EL ÚNICO CAMINO DE ENTRADA del branding remoto: lo que no sale de acá no
467
- * debería aplicarse. Devuelve un objeto NUEVO con sólo los campos válidos —
468
- * nunca el input— así ninguna clave extra del wire (una que el server agregue
469
- * mañana, o una que un theme viejo tenga guardada) se cuela por un spread.
470
- *
471
- * NUNCA LANZA Y NUNCA RECHAZA EL TEMA ENTERO: un campo inválido se descarta y el
472
- * consumidor cae a su default. Perder el branding completo —o peor, no pintar el
473
- * chat— porque el `accentColor` está mal cargado sería mucho peor que mostrar el
474
- * acento por default.
475
- */
476
463
  declare function sanitizeServerTheme(raw: unknown): ServerThemeResponse;
477
464
  /**
478
465
  * Evento entregado por el WebSocket del widget (respuesta del asistente o del
package/dist/index.js CHANGED
@@ -83,6 +83,13 @@ function sanitizeThemeTexts(raw) {
83
83
  }
84
84
  return n > 0 ? out : null;
85
85
  }
86
+ function safeLauncherToken(v) {
87
+ if (typeof v !== "string") return null;
88
+ const t = v.trim();
89
+ if (t === "bircle" || t === "chat") return t;
90
+ if (t.length > 0 && t.length <= 8 && !t.includes(":") && !t.includes("/")) return t;
91
+ return null;
92
+ }
86
93
  function sanitizeServerTheme(raw) {
87
94
  if (typeof raw !== "object" || raw === null) return {};
88
95
  const o = raw;
@@ -91,7 +98,7 @@ function sanitizeServerTheme(raw) {
91
98
  if (primaryColor !== null) out.primaryColor = primaryColor;
92
99
  const accentColor = safeThemeColor(o.accentColor);
93
100
  if (accentColor !== null) out.accentColor = accentColor;
94
- const launcherIcon = safeThemeUrl(o.launcherIcon);
101
+ const launcherIcon = safeThemeUrl(o.launcherIcon) ?? safeLauncherToken(o.launcherIcon);
95
102
  if (launcherIcon !== null) out.launcherIcon = launcherIcon;
96
103
  const headerLogo = safeThemeUrl(o.headerLogo);
97
104
  if (headerLogo !== null) out.headerLogo = headerLogo;
@@ -112,8 +119,28 @@ function sanitizeServerTheme(raw) {
112
119
  if (locale !== null) out.locale = locale;
113
120
  const texts = sanitizeThemeTexts(o.texts);
114
121
  if (texts !== null) out.texts = texts;
122
+ const faqs = sanitizeThemeFaqs(o.faqs);
123
+ if (faqs !== null) out.faqs = faqs;
115
124
  return out;
116
125
  }
126
+ function sanitizeThemeFaqs(raw) {
127
+ if (!Array.isArray(raw)) return null;
128
+ const out = [];
129
+ for (const item of raw) {
130
+ if (out.length >= 6) break;
131
+ if (typeof item !== "object" || item === null) continue;
132
+ const v = item;
133
+ const label = typeof v.label === "string" ? v.label.trim() : "";
134
+ if (label === "" || label.length > 60) continue;
135
+ const entry = { label };
136
+ if (typeof v.message === "string") {
137
+ const message = v.message.trim();
138
+ if (message !== "" && message.length <= 500) entry.message = message;
139
+ }
140
+ out.push(entry);
141
+ }
142
+ return out.length > 0 ? out : null;
143
+ }
117
144
  function safeThemeLocale(value) {
118
145
  if (typeof value !== "string") return null;
119
146
  const v = value.trim();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bircleai/widget-protocol",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Contrato compartido del Chat Gateway de BircleAI (tipos + helpers de parseo).",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "type": "module",