@bircleai/widget-protocol 0.6.2 → 0.7.0
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 +96 -2
- package/dist/index.d.cts +135 -2
- package/dist/index.d.ts +135 -2
- package/dist/index.js +87 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,9 +21,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
ALLOWED_UPLOAD_CONTENT_TYPES: () => ALLOWED_UPLOAD_CONTENT_TYPES,
|
|
24
|
+
DEFAULT_SURVEY_TEXT_LEN: () => DEFAULT_SURVEY_TEXT_LEN,
|
|
24
25
|
MAX_AGENT_NAME: () => MAX_AGENT_NAME,
|
|
25
26
|
MAX_QUICK_REPLIES: () => MAX_QUICK_REPLIES,
|
|
26
27
|
MAX_QUICK_REPLY_LABEL: () => MAX_QUICK_REPLY_LABEL,
|
|
28
|
+
MAX_SURVEY_CHOICE_OPTIONS: () => MAX_SURVEY_CHOICE_OPTIONS,
|
|
29
|
+
MAX_SURVEY_ID: () => MAX_SURVEY_ID,
|
|
30
|
+
MAX_SURVEY_LABEL: () => MAX_SURVEY_LABEL,
|
|
31
|
+
MAX_SURVEY_OPTION_LABEL: () => MAX_SURVEY_OPTION_LABEL,
|
|
32
|
+
MAX_SURVEY_STEPS: () => MAX_SURVEY_STEPS,
|
|
33
|
+
MAX_SURVEY_TEXT_LEN: () => MAX_SURVEY_TEXT_LEN,
|
|
27
34
|
MAX_THEME_FONT_FAMILY: () => MAX_THEME_FONT_FAMILY,
|
|
28
35
|
MAX_THEME_LABEL: () => MAX_THEME_LABEL,
|
|
29
36
|
MAX_THEME_TAGLINE: () => MAX_THEME_TAGLINE,
|
|
@@ -44,7 +51,8 @@ __export(index_exports, {
|
|
|
44
51
|
safeThemeUrl: () => safeThemeUrl,
|
|
45
52
|
sanitizeAssistantIdentity: () => sanitizeAssistantIdentity,
|
|
46
53
|
sanitizeQuickReplies: () => sanitizeQuickReplies,
|
|
47
|
-
sanitizeServerTheme: () => sanitizeServerTheme
|
|
54
|
+
sanitizeServerTheme: () => sanitizeServerTheme,
|
|
55
|
+
sanitizeSurvey: () => sanitizeSurvey
|
|
48
56
|
});
|
|
49
57
|
module.exports = __toCommonJS(index_exports);
|
|
50
58
|
var MEDIA_CONTENT_TYPES = [
|
|
@@ -210,6 +218,76 @@ function sanitizeQuickReplies(raw) {
|
|
|
210
218
|
}
|
|
211
219
|
return out;
|
|
212
220
|
}
|
|
221
|
+
var MAX_SURVEY_STEPS = 5;
|
|
222
|
+
var MAX_SURVEY_CHOICE_OPTIONS = 10;
|
|
223
|
+
var MAX_SURVEY_LABEL = 100;
|
|
224
|
+
var MAX_SURVEY_OPTION_LABEL = 30;
|
|
225
|
+
var MAX_SURVEY_ID = 64;
|
|
226
|
+
var MAX_SURVEY_TEXT_LEN = 1e3;
|
|
227
|
+
var DEFAULT_SURVEY_TEXT_LEN = 500;
|
|
228
|
+
function surveyId(value, max = MAX_SURVEY_ID) {
|
|
229
|
+
if (typeof value !== "string") return null;
|
|
230
|
+
const v = value.trim();
|
|
231
|
+
return v !== "" && v.length <= max ? v : null;
|
|
232
|
+
}
|
|
233
|
+
function sanitizeSurveyStep(raw) {
|
|
234
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
235
|
+
const o = raw;
|
|
236
|
+
const key = surveyId(o.key);
|
|
237
|
+
const label = surveyId(o.label, MAX_SURVEY_LABEL);
|
|
238
|
+
if (key === null || label === null) return null;
|
|
239
|
+
switch (o.type) {
|
|
240
|
+
case "stars":
|
|
241
|
+
return { type: "stars", key, label };
|
|
242
|
+
case "choice": {
|
|
243
|
+
if (!Array.isArray(o.options)) return null;
|
|
244
|
+
if (o.options.length === 0 || o.options.length > MAX_SURVEY_CHOICE_OPTIONS) return null;
|
|
245
|
+
const options = [];
|
|
246
|
+
for (const item of o.options) {
|
|
247
|
+
if (typeof item !== "object" || item === null) return null;
|
|
248
|
+
const opt = item;
|
|
249
|
+
const id = surveyId(opt.id);
|
|
250
|
+
const optLabel = surveyId(opt.label, MAX_SURVEY_OPTION_LABEL);
|
|
251
|
+
if (id === null || optLabel === null) return null;
|
|
252
|
+
options.push({ id, label: optLabel });
|
|
253
|
+
}
|
|
254
|
+
return { type: "choice", key, label, options };
|
|
255
|
+
}
|
|
256
|
+
case "text": {
|
|
257
|
+
const step = { type: "text", key, label };
|
|
258
|
+
if (typeof o.max_len === "number" && Number.isInteger(o.max_len) && o.max_len > 0 && o.max_len <= MAX_SURVEY_TEXT_LEN) {
|
|
259
|
+
step.max_len = o.max_len;
|
|
260
|
+
}
|
|
261
|
+
return step;
|
|
262
|
+
}
|
|
263
|
+
default:
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function sanitizeSurvey(raw) {
|
|
268
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
269
|
+
const o = raw;
|
|
270
|
+
const survey_id = surveyId(o.survey_id);
|
|
271
|
+
const survey_msg_id = surveyId(o.survey_msg_id);
|
|
272
|
+
if (survey_id === null || survey_msg_id === null) return null;
|
|
273
|
+
if (typeof o.version !== "number" || !Number.isInteger(o.version) || o.version <= 0) return null;
|
|
274
|
+
if (!Array.isArray(o.steps)) return null;
|
|
275
|
+
if (o.steps.length === 0 || o.steps.length > MAX_SURVEY_STEPS) return null;
|
|
276
|
+
const steps = [];
|
|
277
|
+
for (const rawStep of o.steps) {
|
|
278
|
+
const step = sanitizeSurveyStep(rawStep);
|
|
279
|
+
if (step === null) return null;
|
|
280
|
+
steps.push(step);
|
|
281
|
+
}
|
|
282
|
+
let answered;
|
|
283
|
+
if (o.answered === void 0 || o.answered === null) answered = false;
|
|
284
|
+
else if (typeof o.answered === "boolean") answered = o.answered;
|
|
285
|
+
else return null;
|
|
286
|
+
const out = { survey_id, version: o.version, survey_msg_id, steps, answered };
|
|
287
|
+
const case_ref = surveyId(o.case_ref, MAX_SURVEY_LABEL);
|
|
288
|
+
if (case_ref !== null) out.case_ref = case_ref;
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
213
291
|
var MAX_AGENT_NAME = 40;
|
|
214
292
|
function sanitizeAssistantIdentity(raw, kind = "human") {
|
|
215
293
|
if (typeof raw !== "object" || raw === null) return null;
|
|
@@ -250,6 +328,7 @@ function isAssistantMessageEvent(v) {
|
|
|
250
328
|
if (v.caption !== void 0 && typeof v.caption !== "string") return false;
|
|
251
329
|
if (v.content_type !== void 0 && v.content_type !== "text" && !isMediaContentType(v.content_type))
|
|
252
330
|
return false;
|
|
331
|
+
if (v.survey !== void 0 && sanitizeSurvey(v.survey) === null) return false;
|
|
253
332
|
return v.turn_ids.every((t) => typeof t === "string");
|
|
254
333
|
}
|
|
255
334
|
function normalizeAssistantMessageEvent(raw, now = Date.now) {
|
|
@@ -268,6 +347,13 @@ function normalizeAssistantMessageEvent(raw, now = Date.now) {
|
|
|
268
347
|
const qr = sanitizeQuickReplies(raw.quick_replies);
|
|
269
348
|
return qr.length > 0 ? { quick_replies: qr } : {};
|
|
270
349
|
})(),
|
|
350
|
+
// La FUNCIÓN es la barrera (lección FAQs): un `survey` que no pasa el saneo
|
|
351
|
+
// no viaja — el mensaje conserva su `content` de texto fallback, así que
|
|
352
|
+
// una encuesta corrupta nunca cuesta la respuesta del agente.
|
|
353
|
+
...(() => {
|
|
354
|
+
const sv = sanitizeSurvey(raw.survey);
|
|
355
|
+
return sv !== null ? { survey: sv } : {};
|
|
356
|
+
})(),
|
|
271
357
|
// Cualquier valor que no sea exactamente "human" cuenta como bot: es el
|
|
272
358
|
// default seguro — decirle al usuario que lo atiende una persona cuando no
|
|
273
359
|
// es cierto es peor que lo contrario.
|
|
@@ -294,9 +380,16 @@ function parseWidgetEvent(raw, now = Date.now) {
|
|
|
294
380
|
// Annotate the CommonJS export names for ESM import in node:
|
|
295
381
|
0 && (module.exports = {
|
|
296
382
|
ALLOWED_UPLOAD_CONTENT_TYPES,
|
|
383
|
+
DEFAULT_SURVEY_TEXT_LEN,
|
|
297
384
|
MAX_AGENT_NAME,
|
|
298
385
|
MAX_QUICK_REPLIES,
|
|
299
386
|
MAX_QUICK_REPLY_LABEL,
|
|
387
|
+
MAX_SURVEY_CHOICE_OPTIONS,
|
|
388
|
+
MAX_SURVEY_ID,
|
|
389
|
+
MAX_SURVEY_LABEL,
|
|
390
|
+
MAX_SURVEY_OPTION_LABEL,
|
|
391
|
+
MAX_SURVEY_STEPS,
|
|
392
|
+
MAX_SURVEY_TEXT_LEN,
|
|
300
393
|
MAX_THEME_FONT_FAMILY,
|
|
301
394
|
MAX_THEME_LABEL,
|
|
302
395
|
MAX_THEME_TAGLINE,
|
|
@@ -317,5 +410,6 @@ function parseWidgetEvent(raw, now = Date.now) {
|
|
|
317
410
|
safeThemeUrl,
|
|
318
411
|
sanitizeAssistantIdentity,
|
|
319
412
|
sanitizeQuickReplies,
|
|
320
|
-
sanitizeServerTheme
|
|
413
|
+
sanitizeServerTheme,
|
|
414
|
+
sanitizeSurvey
|
|
321
415
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -194,6 +194,16 @@ interface HistoryMessageDto {
|
|
|
194
194
|
* elegir, al volver tiene que poder elegir igual.
|
|
195
195
|
*/
|
|
196
196
|
quick_replies?: QuickReply[];
|
|
197
|
+
/**
|
|
198
|
+
* Encuesta embebida del mensaje (`content_type: "survey"`), con su estado
|
|
199
|
+
* `answered` denormalizado EN el item (Red Team #4): reabrir el chat no
|
|
200
|
+
* re-ofrece una encuesta contestada.
|
|
201
|
+
*
|
|
202
|
+
* Mismo trato que en el evento WS: el valor crudo del wire pasa por
|
|
203
|
+
* `sanitizeSurvey` en el consumidor ANTES de renderizar — el tipo documenta
|
|
204
|
+
* el contrato, la función es la barrera.
|
|
205
|
+
*/
|
|
206
|
+
survey?: SurveyPayload;
|
|
197
207
|
/** Ausente ⇒ `bot`. Ver `MessageAuthorKind`. */
|
|
198
208
|
author?: MessageAuthorKind;
|
|
199
209
|
}
|
|
@@ -500,6 +510,119 @@ declare const MAX_QUICK_REPLY_LABEL = 40;
|
|
|
500
510
|
* respuesta del agente por un botón mal formado sería mucho peor.
|
|
501
511
|
*/
|
|
502
512
|
declare function sanitizeQuickReplies(raw: unknown): QuickReply[];
|
|
513
|
+
/** Tope de pasos de una encuesta (límite del contrato de `surveys`). */
|
|
514
|
+
declare const MAX_SURVEY_STEPS = 5;
|
|
515
|
+
/** Tope de opciones de un paso `choice`. */
|
|
516
|
+
declare const MAX_SURVEY_CHOICE_OPTIONS = 10;
|
|
517
|
+
/** Tope del `label` de un paso. */
|
|
518
|
+
declare const MAX_SURVEY_LABEL = 100;
|
|
519
|
+
/** Tope del `label` de una opción de `choice` (entra en un chip de teléfono). */
|
|
520
|
+
declare const MAX_SURVEY_OPTION_LABEL = 30;
|
|
521
|
+
/** Tope de los ids/keys (`survey_id`, `survey_msg_id`, `key`, `option.id`). */
|
|
522
|
+
declare const MAX_SURVEY_ID = 64;
|
|
523
|
+
/** Tope duro de `max_len` de un paso `text` (mismo límite que el builder). */
|
|
524
|
+
declare const MAX_SURVEY_TEXT_LEN = 1000;
|
|
525
|
+
/** `max_len` efectivo cuando la definición no trae uno. */
|
|
526
|
+
declare const DEFAULT_SURVEY_TEXT_LEN = 500;
|
|
527
|
+
/** Paso de estrellas 1–5 (el rating principal). */
|
|
528
|
+
interface SurveyStepStars {
|
|
529
|
+
type: "stars";
|
|
530
|
+
/** Clave del answer en el POST de respuesta (ej. `rating`). */
|
|
531
|
+
key: string;
|
|
532
|
+
/** Pregunta a mostrar. Texto plano, nunca markup. */
|
|
533
|
+
label: string;
|
|
534
|
+
}
|
|
535
|
+
/** Una opción elegible de un paso `choice`. */
|
|
536
|
+
interface SurveyChoiceOption {
|
|
537
|
+
/** Id estable de la opción — es lo que se REPORTA, el label es lo que se ve. */
|
|
538
|
+
id: string;
|
|
539
|
+
label: string;
|
|
540
|
+
}
|
|
541
|
+
/** Paso de opciones (motivo de la calificación), render tipo chips. */
|
|
542
|
+
interface SurveyStepChoice {
|
|
543
|
+
type: "choice";
|
|
544
|
+
key: string;
|
|
545
|
+
label: string;
|
|
546
|
+
options: SurveyChoiceOption[];
|
|
547
|
+
}
|
|
548
|
+
/** Paso de texto libre (comentario). */
|
|
549
|
+
interface SurveyStepText {
|
|
550
|
+
type: "text";
|
|
551
|
+
key: string;
|
|
552
|
+
label: string;
|
|
553
|
+
/** Tope de caracteres del comentario. Ausente ⇒ `DEFAULT_SURVEY_TEXT_LEN`. */
|
|
554
|
+
max_len?: number;
|
|
555
|
+
}
|
|
556
|
+
type SurveyStep = SurveyStepStars | SurveyStepChoice | SurveyStepText;
|
|
557
|
+
/**
|
|
558
|
+
* Encuesta embebida en un mensaje del asistente. Campo hermano de
|
|
559
|
+
* `quick_replies` — viaja igual en el evento WS y en el DTO del historial.
|
|
560
|
+
*/
|
|
561
|
+
interface SurveyPayload {
|
|
562
|
+
/** Id de la DEFINICIÓN (tabla `surveys`). */
|
|
563
|
+
survey_id: string;
|
|
564
|
+
/** Versión de la definición con la que se generó este mensaje. */
|
|
565
|
+
version: number;
|
|
566
|
+
/**
|
|
567
|
+
* Id del MENSAJE survey. Es la clave con la que se responde
|
|
568
|
+
* (`POST /v1/widget/survey-response`) y con la que el server deduplica: el
|
|
569
|
+
* `response_id` determinístico sale de acá.
|
|
570
|
+
*/
|
|
571
|
+
survey_msg_id: string;
|
|
572
|
+
/** Pasos a renderizar, en orden. Nunca vacío (sin pasos no hay encuesta). */
|
|
573
|
+
steps: SurveyStep[];
|
|
574
|
+
/** Referencia legible del caso que se califica ("¿Cómo resolvimos tu caso #123?"). */
|
|
575
|
+
case_ref?: string;
|
|
576
|
+
/**
|
|
577
|
+
* `true` = ya respondida. Denormalizado EN el item del mensaje (Red Team #4)
|
|
578
|
+
* para que la rehidratación no re-ofrezca una encuesta contestada — la UI la
|
|
579
|
+
* muestra en su estado "¡Gracias!" directamente.
|
|
580
|
+
*/
|
|
581
|
+
answered: boolean;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Encuesta USABLE del wire, o `null`.
|
|
585
|
+
*
|
|
586
|
+
* ES LA BARRERA (no el tipo): todo `survey` que llega del server —evento WS o
|
|
587
|
+
* DTO de historial— pasa por acá antes de tocar la UI. Valida el contrato
|
|
588
|
+
* completo: ids/keys no vacíos ≤ 64, ≤ 5 pasos, choice con 1..10 opciones,
|
|
589
|
+
* labels ≤ 100 (≤ 30 en opciones), `version` entero positivo.
|
|
590
|
+
*
|
|
591
|
+
* TODO-O-NADA A PROPÓSITO (distinto de `sanitizeQuickReplies`): una encuesta
|
|
592
|
+
* con un paso corrupto no es "una encuesta más corta", es un instrumento que ya
|
|
593
|
+
* no coincide con la definición contra la que el Gateway valida la respuesta.
|
|
594
|
+
* Ofrecerla garantiza un 400 DESPUÉS de que el usuario contestó — peor que no
|
|
595
|
+
* ofrecerla (el mensaje conserva su `content` de texto fallback). NUNCA lanza.
|
|
596
|
+
*
|
|
597
|
+
* Tolerancias deliberadas (ausencia ≠ inválido):
|
|
598
|
+
* - `answered` ausente o `null` ⇒ `false` (un productor viejo que no
|
|
599
|
+
* denormaliza el estado no debe matar la encuesta); cualquier otro no-bool
|
|
600
|
+
* (`"true"`) sí es inválido — adivinar el estado re-ofrecería encuestas
|
|
601
|
+
* contestadas.
|
|
602
|
+
* - `case_ref` es decorativo: no-string/vacío/gigante ⇒ se omite el campo,
|
|
603
|
+
* la encuesta sigue (mismo criterio que el avatar en
|
|
604
|
+
* `sanitizeAssistantIdentity`).
|
|
605
|
+
*/
|
|
606
|
+
declare function sanitizeSurvey(raw: unknown): SurveyPayload | null;
|
|
607
|
+
/**
|
|
608
|
+
* `POST /v1/widget/survey-response` — body (auth: Bearer JWT de sesión, igual
|
|
609
|
+
* que `/messages`). `reason` debe ser un `option.id` de la definición y
|
|
610
|
+
* `comment` respetar el `max_len` — el Gateway valida contra la definición y
|
|
611
|
+
* responde 400 si no. 409 = ya respondida (el `response_id` determinístico hace
|
|
612
|
+
* el retry no-op). 404 = mensaje survey inexistente.
|
|
613
|
+
*/
|
|
614
|
+
interface SurveyResponseRequest {
|
|
615
|
+
survey_msg_id: string;
|
|
616
|
+
/** 1..5 (entero). */
|
|
617
|
+
rating: number;
|
|
618
|
+
/** `option.id` del paso choice, si el usuario eligió uno. */
|
|
619
|
+
reason?: string;
|
|
620
|
+
comment?: string;
|
|
621
|
+
}
|
|
622
|
+
/** `POST /v1/widget/survey-response` — respuesta 200. */
|
|
623
|
+
interface SurveyResponseAck {
|
|
624
|
+
answered: true;
|
|
625
|
+
}
|
|
503
626
|
/**
|
|
504
627
|
* Quién escribió un mensaje del lado del asistente.
|
|
505
628
|
*
|
|
@@ -545,6 +668,13 @@ interface AssistantMessageEvent {
|
|
|
545
668
|
ts: number;
|
|
546
669
|
/** Botones para responder sin escribir. Ver `QuickReply`. */
|
|
547
670
|
quick_replies?: QuickReply[];
|
|
671
|
+
/**
|
|
672
|
+
* Encuesta embebida (`content_type: "survey"`). Campo hermano de
|
|
673
|
+
* `quick_replies`, mismo patrón: sólo aparece si el wire trajo una encuesta
|
|
674
|
+
* VÁLIDA (pasó por `sanitizeSurvey`); un cliente que no la renderice sigue
|
|
675
|
+
* mostrando el `content` de texto fallback.
|
|
676
|
+
*/
|
|
677
|
+
survey?: SurveyPayload;
|
|
548
678
|
/**
|
|
549
679
|
* Quién lo escribió. Ausente ⇒ `bot` (todo lo que existía antes de que hubiera
|
|
550
680
|
* handover es del agente de IA).
|
|
@@ -569,12 +699,15 @@ interface AssistantMessageEvent {
|
|
|
569
699
|
* - `caption` `| null`: mismo motivo que `ts` (Python `None` ⇒ `null`).
|
|
570
700
|
* - `turn_ids` como `readonly unknown[]`: el guard solo verifica que sea un
|
|
571
701
|
* array; los elementos los filtra `normalizeAssistantMessageEvent`.
|
|
702
|
+
* - `survey` como `unknown`: mismo tratamiento que `quick_replies` — el guard
|
|
703
|
+
* no lo mira y `sanitizeSurvey` decide en la normalización.
|
|
572
704
|
*/
|
|
573
|
-
type RawAssistantMessageEvent = Omit<AssistantMessageEvent, "ts" | "turn_ids" | "content_type" | "caption"> & {
|
|
705
|
+
type RawAssistantMessageEvent = Omit<AssistantMessageEvent, "ts" | "turn_ids" | "content_type" | "caption" | "survey"> & {
|
|
574
706
|
ts?: number | null;
|
|
575
707
|
content_type?: string | null;
|
|
576
708
|
caption?: string | null;
|
|
577
709
|
turn_ids: readonly unknown[];
|
|
710
|
+
survey?: unknown;
|
|
578
711
|
};
|
|
579
712
|
type WidgetEvent = AssistantMessageEvent;
|
|
580
713
|
/** Union cruda del wire (hoy un solo tipo de evento; `typing`/etc. se sumarían acá). */
|
|
@@ -665,4 +798,4 @@ declare function normalizeAssistantMessageEvent(raw: RawAssistantMessageEvent, n
|
|
|
665
798
|
*/
|
|
666
799
|
declare function parseWidgetEvent(raw: string, now?: () => number): WidgetEvent | null;
|
|
667
800
|
|
|
668
|
-
export { ALLOWED_UPLOAD_CONTENT_TYPES, type AssistantIdentity, type AssistantMessageEvent, type ChallengeUiConfig, type HistoryMessageDto, type HistoryResponse, MAX_AGENT_NAME, MAX_QUICK_REPLIES, MAX_QUICK_REPLY_LABEL, MAX_THEME_FONT_FAMILY, MAX_THEME_LABEL, MAX_THEME_TAGLINE, MAX_THEME_TEXT_KEY, MAX_THEME_TEXT_KEYS, MAX_THEME_TEXT_VALUE, MAX_UPLOAD_SIZE_BYTES, MEDIA_CONTENT_TYPES, type MediaContentType, type MessageAccepted, type MessageAuthorKind, type MessageRequest, type QuickReply, type RawAssistantMessageEvent, type RawWidgetEvent, type RefreshRequest, type RefreshResponse, SERVER_PROVIDED_THEME_FIELDS, type ServerThemeResponse, type SessionRefreshRequest, type SessionRefreshResponse, type SessionRequest, type SessionResponse, type ThemeResponse, type UploadRequest, type UploadResponse, type WidgetEvent, isAllowedUploadContentType, isAssistantMessageEvent, isMediaContentType, isRawAssistantMessageEvent, mediaKindForContentType, normalizeAssistantMessageEvent, parseWidgetEvent, safeThemeColor, safeThemeUrl, sanitizeAssistantIdentity, sanitizeQuickReplies, sanitizeServerTheme };
|
|
801
|
+
export { ALLOWED_UPLOAD_CONTENT_TYPES, type AssistantIdentity, type AssistantMessageEvent, type ChallengeUiConfig, DEFAULT_SURVEY_TEXT_LEN, type HistoryMessageDto, type HistoryResponse, MAX_AGENT_NAME, MAX_QUICK_REPLIES, MAX_QUICK_REPLY_LABEL, MAX_SURVEY_CHOICE_OPTIONS, MAX_SURVEY_ID, MAX_SURVEY_LABEL, MAX_SURVEY_OPTION_LABEL, MAX_SURVEY_STEPS, MAX_SURVEY_TEXT_LEN, MAX_THEME_FONT_FAMILY, MAX_THEME_LABEL, MAX_THEME_TAGLINE, MAX_THEME_TEXT_KEY, MAX_THEME_TEXT_KEYS, MAX_THEME_TEXT_VALUE, MAX_UPLOAD_SIZE_BYTES, MEDIA_CONTENT_TYPES, type MediaContentType, type MessageAccepted, type MessageAuthorKind, type MessageRequest, type QuickReply, type RawAssistantMessageEvent, type RawWidgetEvent, type RefreshRequest, type RefreshResponse, SERVER_PROVIDED_THEME_FIELDS, type ServerThemeResponse, type SessionRefreshRequest, type SessionRefreshResponse, type SessionRequest, type SessionResponse, type SurveyChoiceOption, type SurveyPayload, type SurveyResponseAck, type SurveyResponseRequest, type SurveyStep, type SurveyStepChoice, type SurveyStepStars, type SurveyStepText, type ThemeResponse, type UploadRequest, type UploadResponse, type WidgetEvent, isAllowedUploadContentType, isAssistantMessageEvent, isMediaContentType, isRawAssistantMessageEvent, mediaKindForContentType, normalizeAssistantMessageEvent, parseWidgetEvent, safeThemeColor, safeThemeUrl, sanitizeAssistantIdentity, sanitizeQuickReplies, sanitizeServerTheme, sanitizeSurvey };
|
package/dist/index.d.ts
CHANGED
|
@@ -194,6 +194,16 @@ interface HistoryMessageDto {
|
|
|
194
194
|
* elegir, al volver tiene que poder elegir igual.
|
|
195
195
|
*/
|
|
196
196
|
quick_replies?: QuickReply[];
|
|
197
|
+
/**
|
|
198
|
+
* Encuesta embebida del mensaje (`content_type: "survey"`), con su estado
|
|
199
|
+
* `answered` denormalizado EN el item (Red Team #4): reabrir el chat no
|
|
200
|
+
* re-ofrece una encuesta contestada.
|
|
201
|
+
*
|
|
202
|
+
* Mismo trato que en el evento WS: el valor crudo del wire pasa por
|
|
203
|
+
* `sanitizeSurvey` en el consumidor ANTES de renderizar — el tipo documenta
|
|
204
|
+
* el contrato, la función es la barrera.
|
|
205
|
+
*/
|
|
206
|
+
survey?: SurveyPayload;
|
|
197
207
|
/** Ausente ⇒ `bot`. Ver `MessageAuthorKind`. */
|
|
198
208
|
author?: MessageAuthorKind;
|
|
199
209
|
}
|
|
@@ -500,6 +510,119 @@ declare const MAX_QUICK_REPLY_LABEL = 40;
|
|
|
500
510
|
* respuesta del agente por un botón mal formado sería mucho peor.
|
|
501
511
|
*/
|
|
502
512
|
declare function sanitizeQuickReplies(raw: unknown): QuickReply[];
|
|
513
|
+
/** Tope de pasos de una encuesta (límite del contrato de `surveys`). */
|
|
514
|
+
declare const MAX_SURVEY_STEPS = 5;
|
|
515
|
+
/** Tope de opciones de un paso `choice`. */
|
|
516
|
+
declare const MAX_SURVEY_CHOICE_OPTIONS = 10;
|
|
517
|
+
/** Tope del `label` de un paso. */
|
|
518
|
+
declare const MAX_SURVEY_LABEL = 100;
|
|
519
|
+
/** Tope del `label` de una opción de `choice` (entra en un chip de teléfono). */
|
|
520
|
+
declare const MAX_SURVEY_OPTION_LABEL = 30;
|
|
521
|
+
/** Tope de los ids/keys (`survey_id`, `survey_msg_id`, `key`, `option.id`). */
|
|
522
|
+
declare const MAX_SURVEY_ID = 64;
|
|
523
|
+
/** Tope duro de `max_len` de un paso `text` (mismo límite que el builder). */
|
|
524
|
+
declare const MAX_SURVEY_TEXT_LEN = 1000;
|
|
525
|
+
/** `max_len` efectivo cuando la definición no trae uno. */
|
|
526
|
+
declare const DEFAULT_SURVEY_TEXT_LEN = 500;
|
|
527
|
+
/** Paso de estrellas 1–5 (el rating principal). */
|
|
528
|
+
interface SurveyStepStars {
|
|
529
|
+
type: "stars";
|
|
530
|
+
/** Clave del answer en el POST de respuesta (ej. `rating`). */
|
|
531
|
+
key: string;
|
|
532
|
+
/** Pregunta a mostrar. Texto plano, nunca markup. */
|
|
533
|
+
label: string;
|
|
534
|
+
}
|
|
535
|
+
/** Una opción elegible de un paso `choice`. */
|
|
536
|
+
interface SurveyChoiceOption {
|
|
537
|
+
/** Id estable de la opción — es lo que se REPORTA, el label es lo que se ve. */
|
|
538
|
+
id: string;
|
|
539
|
+
label: string;
|
|
540
|
+
}
|
|
541
|
+
/** Paso de opciones (motivo de la calificación), render tipo chips. */
|
|
542
|
+
interface SurveyStepChoice {
|
|
543
|
+
type: "choice";
|
|
544
|
+
key: string;
|
|
545
|
+
label: string;
|
|
546
|
+
options: SurveyChoiceOption[];
|
|
547
|
+
}
|
|
548
|
+
/** Paso de texto libre (comentario). */
|
|
549
|
+
interface SurveyStepText {
|
|
550
|
+
type: "text";
|
|
551
|
+
key: string;
|
|
552
|
+
label: string;
|
|
553
|
+
/** Tope de caracteres del comentario. Ausente ⇒ `DEFAULT_SURVEY_TEXT_LEN`. */
|
|
554
|
+
max_len?: number;
|
|
555
|
+
}
|
|
556
|
+
type SurveyStep = SurveyStepStars | SurveyStepChoice | SurveyStepText;
|
|
557
|
+
/**
|
|
558
|
+
* Encuesta embebida en un mensaje del asistente. Campo hermano de
|
|
559
|
+
* `quick_replies` — viaja igual en el evento WS y en el DTO del historial.
|
|
560
|
+
*/
|
|
561
|
+
interface SurveyPayload {
|
|
562
|
+
/** Id de la DEFINICIÓN (tabla `surveys`). */
|
|
563
|
+
survey_id: string;
|
|
564
|
+
/** Versión de la definición con la que se generó este mensaje. */
|
|
565
|
+
version: number;
|
|
566
|
+
/**
|
|
567
|
+
* Id del MENSAJE survey. Es la clave con la que se responde
|
|
568
|
+
* (`POST /v1/widget/survey-response`) y con la que el server deduplica: el
|
|
569
|
+
* `response_id` determinístico sale de acá.
|
|
570
|
+
*/
|
|
571
|
+
survey_msg_id: string;
|
|
572
|
+
/** Pasos a renderizar, en orden. Nunca vacío (sin pasos no hay encuesta). */
|
|
573
|
+
steps: SurveyStep[];
|
|
574
|
+
/** Referencia legible del caso que se califica ("¿Cómo resolvimos tu caso #123?"). */
|
|
575
|
+
case_ref?: string;
|
|
576
|
+
/**
|
|
577
|
+
* `true` = ya respondida. Denormalizado EN el item del mensaje (Red Team #4)
|
|
578
|
+
* para que la rehidratación no re-ofrezca una encuesta contestada — la UI la
|
|
579
|
+
* muestra en su estado "¡Gracias!" directamente.
|
|
580
|
+
*/
|
|
581
|
+
answered: boolean;
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Encuesta USABLE del wire, o `null`.
|
|
585
|
+
*
|
|
586
|
+
* ES LA BARRERA (no el tipo): todo `survey` que llega del server —evento WS o
|
|
587
|
+
* DTO de historial— pasa por acá antes de tocar la UI. Valida el contrato
|
|
588
|
+
* completo: ids/keys no vacíos ≤ 64, ≤ 5 pasos, choice con 1..10 opciones,
|
|
589
|
+
* labels ≤ 100 (≤ 30 en opciones), `version` entero positivo.
|
|
590
|
+
*
|
|
591
|
+
* TODO-O-NADA A PROPÓSITO (distinto de `sanitizeQuickReplies`): una encuesta
|
|
592
|
+
* con un paso corrupto no es "una encuesta más corta", es un instrumento que ya
|
|
593
|
+
* no coincide con la definición contra la que el Gateway valida la respuesta.
|
|
594
|
+
* Ofrecerla garantiza un 400 DESPUÉS de que el usuario contestó — peor que no
|
|
595
|
+
* ofrecerla (el mensaje conserva su `content` de texto fallback). NUNCA lanza.
|
|
596
|
+
*
|
|
597
|
+
* Tolerancias deliberadas (ausencia ≠ inválido):
|
|
598
|
+
* - `answered` ausente o `null` ⇒ `false` (un productor viejo que no
|
|
599
|
+
* denormaliza el estado no debe matar la encuesta); cualquier otro no-bool
|
|
600
|
+
* (`"true"`) sí es inválido — adivinar el estado re-ofrecería encuestas
|
|
601
|
+
* contestadas.
|
|
602
|
+
* - `case_ref` es decorativo: no-string/vacío/gigante ⇒ se omite el campo,
|
|
603
|
+
* la encuesta sigue (mismo criterio que el avatar en
|
|
604
|
+
* `sanitizeAssistantIdentity`).
|
|
605
|
+
*/
|
|
606
|
+
declare function sanitizeSurvey(raw: unknown): SurveyPayload | null;
|
|
607
|
+
/**
|
|
608
|
+
* `POST /v1/widget/survey-response` — body (auth: Bearer JWT de sesión, igual
|
|
609
|
+
* que `/messages`). `reason` debe ser un `option.id` de la definición y
|
|
610
|
+
* `comment` respetar el `max_len` — el Gateway valida contra la definición y
|
|
611
|
+
* responde 400 si no. 409 = ya respondida (el `response_id` determinístico hace
|
|
612
|
+
* el retry no-op). 404 = mensaje survey inexistente.
|
|
613
|
+
*/
|
|
614
|
+
interface SurveyResponseRequest {
|
|
615
|
+
survey_msg_id: string;
|
|
616
|
+
/** 1..5 (entero). */
|
|
617
|
+
rating: number;
|
|
618
|
+
/** `option.id` del paso choice, si el usuario eligió uno. */
|
|
619
|
+
reason?: string;
|
|
620
|
+
comment?: string;
|
|
621
|
+
}
|
|
622
|
+
/** `POST /v1/widget/survey-response` — respuesta 200. */
|
|
623
|
+
interface SurveyResponseAck {
|
|
624
|
+
answered: true;
|
|
625
|
+
}
|
|
503
626
|
/**
|
|
504
627
|
* Quién escribió un mensaje del lado del asistente.
|
|
505
628
|
*
|
|
@@ -545,6 +668,13 @@ interface AssistantMessageEvent {
|
|
|
545
668
|
ts: number;
|
|
546
669
|
/** Botones para responder sin escribir. Ver `QuickReply`. */
|
|
547
670
|
quick_replies?: QuickReply[];
|
|
671
|
+
/**
|
|
672
|
+
* Encuesta embebida (`content_type: "survey"`). Campo hermano de
|
|
673
|
+
* `quick_replies`, mismo patrón: sólo aparece si el wire trajo una encuesta
|
|
674
|
+
* VÁLIDA (pasó por `sanitizeSurvey`); un cliente que no la renderice sigue
|
|
675
|
+
* mostrando el `content` de texto fallback.
|
|
676
|
+
*/
|
|
677
|
+
survey?: SurveyPayload;
|
|
548
678
|
/**
|
|
549
679
|
* Quién lo escribió. Ausente ⇒ `bot` (todo lo que existía antes de que hubiera
|
|
550
680
|
* handover es del agente de IA).
|
|
@@ -569,12 +699,15 @@ interface AssistantMessageEvent {
|
|
|
569
699
|
* - `caption` `| null`: mismo motivo que `ts` (Python `None` ⇒ `null`).
|
|
570
700
|
* - `turn_ids` como `readonly unknown[]`: el guard solo verifica que sea un
|
|
571
701
|
* array; los elementos los filtra `normalizeAssistantMessageEvent`.
|
|
702
|
+
* - `survey` como `unknown`: mismo tratamiento que `quick_replies` — el guard
|
|
703
|
+
* no lo mira y `sanitizeSurvey` decide en la normalización.
|
|
572
704
|
*/
|
|
573
|
-
type RawAssistantMessageEvent = Omit<AssistantMessageEvent, "ts" | "turn_ids" | "content_type" | "caption"> & {
|
|
705
|
+
type RawAssistantMessageEvent = Omit<AssistantMessageEvent, "ts" | "turn_ids" | "content_type" | "caption" | "survey"> & {
|
|
574
706
|
ts?: number | null;
|
|
575
707
|
content_type?: string | null;
|
|
576
708
|
caption?: string | null;
|
|
577
709
|
turn_ids: readonly unknown[];
|
|
710
|
+
survey?: unknown;
|
|
578
711
|
};
|
|
579
712
|
type WidgetEvent = AssistantMessageEvent;
|
|
580
713
|
/** Union cruda del wire (hoy un solo tipo de evento; `typing`/etc. se sumarían acá). */
|
|
@@ -665,4 +798,4 @@ declare function normalizeAssistantMessageEvent(raw: RawAssistantMessageEvent, n
|
|
|
665
798
|
*/
|
|
666
799
|
declare function parseWidgetEvent(raw: string, now?: () => number): WidgetEvent | null;
|
|
667
800
|
|
|
668
|
-
export { ALLOWED_UPLOAD_CONTENT_TYPES, type AssistantIdentity, type AssistantMessageEvent, type ChallengeUiConfig, type HistoryMessageDto, type HistoryResponse, MAX_AGENT_NAME, MAX_QUICK_REPLIES, MAX_QUICK_REPLY_LABEL, MAX_THEME_FONT_FAMILY, MAX_THEME_LABEL, MAX_THEME_TAGLINE, MAX_THEME_TEXT_KEY, MAX_THEME_TEXT_KEYS, MAX_THEME_TEXT_VALUE, MAX_UPLOAD_SIZE_BYTES, MEDIA_CONTENT_TYPES, type MediaContentType, type MessageAccepted, type MessageAuthorKind, type MessageRequest, type QuickReply, type RawAssistantMessageEvent, type RawWidgetEvent, type RefreshRequest, type RefreshResponse, SERVER_PROVIDED_THEME_FIELDS, type ServerThemeResponse, type SessionRefreshRequest, type SessionRefreshResponse, type SessionRequest, type SessionResponse, type ThemeResponse, type UploadRequest, type UploadResponse, type WidgetEvent, isAllowedUploadContentType, isAssistantMessageEvent, isMediaContentType, isRawAssistantMessageEvent, mediaKindForContentType, normalizeAssistantMessageEvent, parseWidgetEvent, safeThemeColor, safeThemeUrl, sanitizeAssistantIdentity, sanitizeQuickReplies, sanitizeServerTheme };
|
|
801
|
+
export { ALLOWED_UPLOAD_CONTENT_TYPES, type AssistantIdentity, type AssistantMessageEvent, type ChallengeUiConfig, DEFAULT_SURVEY_TEXT_LEN, type HistoryMessageDto, type HistoryResponse, MAX_AGENT_NAME, MAX_QUICK_REPLIES, MAX_QUICK_REPLY_LABEL, MAX_SURVEY_CHOICE_OPTIONS, MAX_SURVEY_ID, MAX_SURVEY_LABEL, MAX_SURVEY_OPTION_LABEL, MAX_SURVEY_STEPS, MAX_SURVEY_TEXT_LEN, MAX_THEME_FONT_FAMILY, MAX_THEME_LABEL, MAX_THEME_TAGLINE, MAX_THEME_TEXT_KEY, MAX_THEME_TEXT_KEYS, MAX_THEME_TEXT_VALUE, MAX_UPLOAD_SIZE_BYTES, MEDIA_CONTENT_TYPES, type MediaContentType, type MessageAccepted, type MessageAuthorKind, type MessageRequest, type QuickReply, type RawAssistantMessageEvent, type RawWidgetEvent, type RefreshRequest, type RefreshResponse, SERVER_PROVIDED_THEME_FIELDS, type ServerThemeResponse, type SessionRefreshRequest, type SessionRefreshResponse, type SessionRequest, type SessionResponse, type SurveyChoiceOption, type SurveyPayload, type SurveyResponseAck, type SurveyResponseRequest, type SurveyStep, type SurveyStepChoice, type SurveyStepStars, type SurveyStepText, type ThemeResponse, type UploadRequest, type UploadResponse, type WidgetEvent, isAllowedUploadContentType, isAssistantMessageEvent, isMediaContentType, isRawAssistantMessageEvent, mediaKindForContentType, normalizeAssistantMessageEvent, parseWidgetEvent, safeThemeColor, safeThemeUrl, sanitizeAssistantIdentity, sanitizeQuickReplies, sanitizeServerTheme, sanitizeSurvey };
|
package/dist/index.js
CHANGED
|
@@ -162,6 +162,76 @@ function sanitizeQuickReplies(raw) {
|
|
|
162
162
|
}
|
|
163
163
|
return out;
|
|
164
164
|
}
|
|
165
|
+
var MAX_SURVEY_STEPS = 5;
|
|
166
|
+
var MAX_SURVEY_CHOICE_OPTIONS = 10;
|
|
167
|
+
var MAX_SURVEY_LABEL = 100;
|
|
168
|
+
var MAX_SURVEY_OPTION_LABEL = 30;
|
|
169
|
+
var MAX_SURVEY_ID = 64;
|
|
170
|
+
var MAX_SURVEY_TEXT_LEN = 1e3;
|
|
171
|
+
var DEFAULT_SURVEY_TEXT_LEN = 500;
|
|
172
|
+
function surveyId(value, max = MAX_SURVEY_ID) {
|
|
173
|
+
if (typeof value !== "string") return null;
|
|
174
|
+
const v = value.trim();
|
|
175
|
+
return v !== "" && v.length <= max ? v : null;
|
|
176
|
+
}
|
|
177
|
+
function sanitizeSurveyStep(raw) {
|
|
178
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
179
|
+
const o = raw;
|
|
180
|
+
const key = surveyId(o.key);
|
|
181
|
+
const label = surveyId(o.label, MAX_SURVEY_LABEL);
|
|
182
|
+
if (key === null || label === null) return null;
|
|
183
|
+
switch (o.type) {
|
|
184
|
+
case "stars":
|
|
185
|
+
return { type: "stars", key, label };
|
|
186
|
+
case "choice": {
|
|
187
|
+
if (!Array.isArray(o.options)) return null;
|
|
188
|
+
if (o.options.length === 0 || o.options.length > MAX_SURVEY_CHOICE_OPTIONS) return null;
|
|
189
|
+
const options = [];
|
|
190
|
+
for (const item of o.options) {
|
|
191
|
+
if (typeof item !== "object" || item === null) return null;
|
|
192
|
+
const opt = item;
|
|
193
|
+
const id = surveyId(opt.id);
|
|
194
|
+
const optLabel = surveyId(opt.label, MAX_SURVEY_OPTION_LABEL);
|
|
195
|
+
if (id === null || optLabel === null) return null;
|
|
196
|
+
options.push({ id, label: optLabel });
|
|
197
|
+
}
|
|
198
|
+
return { type: "choice", key, label, options };
|
|
199
|
+
}
|
|
200
|
+
case "text": {
|
|
201
|
+
const step = { type: "text", key, label };
|
|
202
|
+
if (typeof o.max_len === "number" && Number.isInteger(o.max_len) && o.max_len > 0 && o.max_len <= MAX_SURVEY_TEXT_LEN) {
|
|
203
|
+
step.max_len = o.max_len;
|
|
204
|
+
}
|
|
205
|
+
return step;
|
|
206
|
+
}
|
|
207
|
+
default:
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function sanitizeSurvey(raw) {
|
|
212
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
|
|
213
|
+
const o = raw;
|
|
214
|
+
const survey_id = surveyId(o.survey_id);
|
|
215
|
+
const survey_msg_id = surveyId(o.survey_msg_id);
|
|
216
|
+
if (survey_id === null || survey_msg_id === null) return null;
|
|
217
|
+
if (typeof o.version !== "number" || !Number.isInteger(o.version) || o.version <= 0) return null;
|
|
218
|
+
if (!Array.isArray(o.steps)) return null;
|
|
219
|
+
if (o.steps.length === 0 || o.steps.length > MAX_SURVEY_STEPS) return null;
|
|
220
|
+
const steps = [];
|
|
221
|
+
for (const rawStep of o.steps) {
|
|
222
|
+
const step = sanitizeSurveyStep(rawStep);
|
|
223
|
+
if (step === null) return null;
|
|
224
|
+
steps.push(step);
|
|
225
|
+
}
|
|
226
|
+
let answered;
|
|
227
|
+
if (o.answered === void 0 || o.answered === null) answered = false;
|
|
228
|
+
else if (typeof o.answered === "boolean") answered = o.answered;
|
|
229
|
+
else return null;
|
|
230
|
+
const out = { survey_id, version: o.version, survey_msg_id, steps, answered };
|
|
231
|
+
const case_ref = surveyId(o.case_ref, MAX_SURVEY_LABEL);
|
|
232
|
+
if (case_ref !== null) out.case_ref = case_ref;
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
165
235
|
var MAX_AGENT_NAME = 40;
|
|
166
236
|
function sanitizeAssistantIdentity(raw, kind = "human") {
|
|
167
237
|
if (typeof raw !== "object" || raw === null) return null;
|
|
@@ -202,6 +272,7 @@ function isAssistantMessageEvent(v) {
|
|
|
202
272
|
if (v.caption !== void 0 && typeof v.caption !== "string") return false;
|
|
203
273
|
if (v.content_type !== void 0 && v.content_type !== "text" && !isMediaContentType(v.content_type))
|
|
204
274
|
return false;
|
|
275
|
+
if (v.survey !== void 0 && sanitizeSurvey(v.survey) === null) return false;
|
|
205
276
|
return v.turn_ids.every((t) => typeof t === "string");
|
|
206
277
|
}
|
|
207
278
|
function normalizeAssistantMessageEvent(raw, now = Date.now) {
|
|
@@ -220,6 +291,13 @@ function normalizeAssistantMessageEvent(raw, now = Date.now) {
|
|
|
220
291
|
const qr = sanitizeQuickReplies(raw.quick_replies);
|
|
221
292
|
return qr.length > 0 ? { quick_replies: qr } : {};
|
|
222
293
|
})(),
|
|
294
|
+
// La FUNCIÓN es la barrera (lección FAQs): un `survey` que no pasa el saneo
|
|
295
|
+
// no viaja — el mensaje conserva su `content` de texto fallback, así que
|
|
296
|
+
// una encuesta corrupta nunca cuesta la respuesta del agente.
|
|
297
|
+
...(() => {
|
|
298
|
+
const sv = sanitizeSurvey(raw.survey);
|
|
299
|
+
return sv !== null ? { survey: sv } : {};
|
|
300
|
+
})(),
|
|
223
301
|
// Cualquier valor que no sea exactamente "human" cuenta como bot: es el
|
|
224
302
|
// default seguro — decirle al usuario que lo atiende una persona cuando no
|
|
225
303
|
// es cierto es peor que lo contrario.
|
|
@@ -245,9 +323,16 @@ function parseWidgetEvent(raw, now = Date.now) {
|
|
|
245
323
|
}
|
|
246
324
|
export {
|
|
247
325
|
ALLOWED_UPLOAD_CONTENT_TYPES,
|
|
326
|
+
DEFAULT_SURVEY_TEXT_LEN,
|
|
248
327
|
MAX_AGENT_NAME,
|
|
249
328
|
MAX_QUICK_REPLIES,
|
|
250
329
|
MAX_QUICK_REPLY_LABEL,
|
|
330
|
+
MAX_SURVEY_CHOICE_OPTIONS,
|
|
331
|
+
MAX_SURVEY_ID,
|
|
332
|
+
MAX_SURVEY_LABEL,
|
|
333
|
+
MAX_SURVEY_OPTION_LABEL,
|
|
334
|
+
MAX_SURVEY_STEPS,
|
|
335
|
+
MAX_SURVEY_TEXT_LEN,
|
|
251
336
|
MAX_THEME_FONT_FAMILY,
|
|
252
337
|
MAX_THEME_LABEL,
|
|
253
338
|
MAX_THEME_TAGLINE,
|
|
@@ -268,5 +353,6 @@ export {
|
|
|
268
353
|
safeThemeUrl,
|
|
269
354
|
sanitizeAssistantIdentity,
|
|
270
355
|
sanitizeQuickReplies,
|
|
271
|
-
sanitizeServerTheme
|
|
356
|
+
sanitizeServerTheme,
|
|
357
|
+
sanitizeSurvey
|
|
272
358
|
};
|
package/package.json
CHANGED