@bircleai/widget-protocol 0.4.3

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/LICENSE.md ADDED
@@ -0,0 +1,50 @@
1
+ # Bircle SDK License
2
+
3
+ Copyright (c) 2026 Bircle AI. All rights reserved.
4
+
5
+ ## Grant
6
+
7
+ Subject to the terms below, Bircle AI grants you a limited, non-exclusive,
8
+ non-transferable, revocable license to install this package and to use it,
9
+ including in compiled or bundled form within your application, **solely to
10
+ develop, test and operate applications that integrate with the services
11
+ provided by Bircle AI** under an active service relationship.
12
+
13
+ ## Restrictions
14
+
15
+ Except as expressly permitted above, you may NOT:
16
+
17
+ 1. Copy, redistribute, publish, sublicense, sell, rent or otherwise make this
18
+ software (or any part or derivative of it) available as a standalone
19
+ library, template, UI kit, or as part of any product or service that does
20
+ not connect to the services of Bircle AI;
21
+ 2. Use this software to build, or assist in building, a product or service
22
+ that competes with the services of Bircle AI;
23
+ 3. Remove or alter any copyright, trademark or attribution notices;
24
+ 4. Use the Bircle name or logos except to state that your application
25
+ integrates with Bircle.
26
+
27
+ Modifications strictly necessary for the permitted integration (including
28
+ bundling, minification and tree-shaking performed by standard build tooling)
29
+ are permitted and are covered by the same restrictions.
30
+
31
+ ## Termination
32
+
33
+ This license terminates automatically if you breach any of its terms or when
34
+ your service relationship with Bircle AI ends. Upon termination you must stop
35
+ distributing builds that include this software, except that end-user
36
+ applications already published may continue to operate while an active service
37
+ relationship exists for them.
38
+
39
+ ## No warranty
40
+
41
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
44
+ BIRCLE AI BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
45
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
46
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
47
+
48
+ ---
49
+
50
+ For licensing questions: soporte@bircle.ai
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # `@bircleai/widget-protocol`
2
+
3
+ Tipos y validadores del protocolo de mensajes del chat de BircleAI (eventos,
4
+ mensajes, quick replies, adjuntos, handover). Sin dependencias.
5
+
6
+ Lo consumen el widget web, el SDK y el backend; publicado para que una integración
7
+ custom pueda tipar los mismos eventos.
8
+
9
+ ```sh
10
+ npm install @bircleai/widget-protocol
11
+ ```
12
+
13
+ Licencia: Propietaria — ver [LICENSE.md](./LICENSE.md). Uso permitido únicamente para integrar con los servicios de Bircle; prohibida la redistribución standalone y la reventa.
package/dist/index.cjs ADDED
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ALLOWED_UPLOAD_CONTENT_TYPES: () => ALLOWED_UPLOAD_CONTENT_TYPES,
24
+ MAX_AGENT_NAME: () => MAX_AGENT_NAME,
25
+ MAX_QUICK_REPLIES: () => MAX_QUICK_REPLIES,
26
+ MAX_QUICK_REPLY_LABEL: () => MAX_QUICK_REPLY_LABEL,
27
+ MAX_THEME_FONT_FAMILY: () => MAX_THEME_FONT_FAMILY,
28
+ MAX_THEME_LABEL: () => MAX_THEME_LABEL,
29
+ MAX_THEME_TAGLINE: () => MAX_THEME_TAGLINE,
30
+ MAX_THEME_TEXT_KEY: () => MAX_THEME_TEXT_KEY,
31
+ MAX_THEME_TEXT_KEYS: () => MAX_THEME_TEXT_KEYS,
32
+ MAX_THEME_TEXT_VALUE: () => MAX_THEME_TEXT_VALUE,
33
+ MAX_UPLOAD_SIZE_BYTES: () => MAX_UPLOAD_SIZE_BYTES,
34
+ MEDIA_CONTENT_TYPES: () => MEDIA_CONTENT_TYPES,
35
+ SERVER_PROVIDED_THEME_FIELDS: () => SERVER_PROVIDED_THEME_FIELDS,
36
+ isAllowedUploadContentType: () => isAllowedUploadContentType,
37
+ isAssistantMessageEvent: () => isAssistantMessageEvent,
38
+ isMediaContentType: () => isMediaContentType,
39
+ isRawAssistantMessageEvent: () => isRawAssistantMessageEvent,
40
+ mediaKindForContentType: () => mediaKindForContentType,
41
+ normalizeAssistantMessageEvent: () => normalizeAssistantMessageEvent,
42
+ parseWidgetEvent: () => parseWidgetEvent,
43
+ safeThemeColor: () => safeThemeColor,
44
+ safeThemeUrl: () => safeThemeUrl,
45
+ sanitizeAssistantIdentity: () => sanitizeAssistantIdentity,
46
+ sanitizeQuickReplies: () => sanitizeQuickReplies,
47
+ sanitizeServerTheme: () => sanitizeServerTheme
48
+ });
49
+ module.exports = __toCommonJS(index_exports);
50
+ var MEDIA_CONTENT_TYPES = [
51
+ "image",
52
+ "audio",
53
+ "video",
54
+ "document"
55
+ ];
56
+ function isMediaContentType(v) {
57
+ return typeof v === "string" && MEDIA_CONTENT_TYPES.includes(v);
58
+ }
59
+ var ALLOWED_UPLOAD_CONTENT_TYPES = {
60
+ "image/png": "image",
61
+ "image/jpeg": "image",
62
+ "image/webp": "image",
63
+ "image/gif": "image",
64
+ "audio/webm": "audio",
65
+ "audio/mp4": "audio",
66
+ "audio/mpeg": "audio",
67
+ "audio/ogg": "audio",
68
+ "video/mp4": "video",
69
+ "video/webm": "video",
70
+ "application/pdf": "document"
71
+ };
72
+ var MAX_UPLOAD_SIZE_BYTES = 10 * 1024 * 1024;
73
+ function isAllowedUploadContentType(contentType) {
74
+ return Object.prototype.hasOwnProperty.call(ALLOWED_UPLOAD_CONTENT_TYPES, contentType);
75
+ }
76
+ function mediaKindForContentType(contentType) {
77
+ return ALLOWED_UPLOAD_CONTENT_TYPES[contentType] ?? null;
78
+ }
79
+ var SERVER_PROVIDED_THEME_FIELDS = [
80
+ "primaryColor",
81
+ "accentColor",
82
+ "launcherIcon",
83
+ "launcherLabel",
84
+ "position",
85
+ "headerLogo",
86
+ "assistantName",
87
+ "assistantTagline",
88
+ "assistantAvatar",
89
+ "fontFamily",
90
+ "colorScheme",
91
+ "poweredBy",
92
+ "locale",
93
+ "texts"
94
+ ];
95
+ var MAX_THEME_LABEL = 40;
96
+ var MAX_THEME_TAGLINE = 80;
97
+ var MAX_THEME_FONT_FAMILY = 120;
98
+ var MAX_THEME_TEXT_KEYS = 40;
99
+ var MAX_THEME_TEXT_KEY = 60;
100
+ var MAX_THEME_TEXT_VALUE = 200;
101
+ function safeThemeColor(value) {
102
+ if (typeof value !== "string") return null;
103
+ const v = value.trim();
104
+ return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(v) ? v : null;
105
+ }
106
+ function safeThemeUrl(value) {
107
+ if (typeof value !== "string" || value.trim() === "") return null;
108
+ try {
109
+ const u = new URL(value.trim());
110
+ return u.protocol === "https:" ? u.toString() : null;
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+ function safeThemeText(value, max) {
116
+ if (typeof value !== "string") return null;
117
+ const v = value.trim();
118
+ return v !== "" && v.length <= max ? v : null;
119
+ }
120
+ function sanitizeThemeTexts(raw) {
121
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
122
+ const out = {};
123
+ let n = 0;
124
+ for (const [k, v] of Object.entries(raw)) {
125
+ if (n >= MAX_THEME_TEXT_KEYS) break;
126
+ if (k === "" || k.length > MAX_THEME_TEXT_KEY) continue;
127
+ if (typeof v !== "string" || v === "" || v.length > MAX_THEME_TEXT_VALUE) continue;
128
+ out[k] = v;
129
+ n += 1;
130
+ }
131
+ return n > 0 ? out : null;
132
+ }
133
+ function sanitizeServerTheme(raw) {
134
+ if (typeof raw !== "object" || raw === null) return {};
135
+ const o = raw;
136
+ const out = {};
137
+ const primaryColor = safeThemeColor(o.primaryColor);
138
+ if (primaryColor !== null) out.primaryColor = primaryColor;
139
+ const accentColor = safeThemeColor(o.accentColor);
140
+ if (accentColor !== null) out.accentColor = accentColor;
141
+ const launcherIcon = safeThemeUrl(o.launcherIcon);
142
+ if (launcherIcon !== null) out.launcherIcon = launcherIcon;
143
+ const headerLogo = safeThemeUrl(o.headerLogo);
144
+ if (headerLogo !== null) out.headerLogo = headerLogo;
145
+ const assistantAvatar = safeThemeUrl(o.assistantAvatar);
146
+ if (assistantAvatar !== null) out.assistantAvatar = assistantAvatar;
147
+ const launcherLabel = safeThemeText(o.launcherLabel, MAX_THEME_LABEL);
148
+ if (launcherLabel !== null) out.launcherLabel = launcherLabel;
149
+ const assistantName = safeThemeText(o.assistantName, MAX_THEME_LABEL);
150
+ if (assistantName !== null) out.assistantName = assistantName;
151
+ const assistantTagline = safeThemeText(o.assistantTagline, MAX_THEME_TAGLINE);
152
+ if (assistantTagline !== null) out.assistantTagline = assistantTagline;
153
+ const fontFamily = safeThemeText(o.fontFamily, MAX_THEME_FONT_FAMILY);
154
+ if (fontFamily !== null) out.fontFamily = fontFamily;
155
+ if (o.position === "bottom-right" || o.position === "bottom-left") out.position = o.position;
156
+ if (o.colorScheme === "light" || o.colorScheme === "dark") out.colorScheme = o.colorScheme;
157
+ if (typeof o.poweredBy === "boolean") out.poweredBy = o.poweredBy;
158
+ const locale = safeThemeLocale(o.locale);
159
+ if (locale !== null) out.locale = locale;
160
+ const texts = sanitizeThemeTexts(o.texts);
161
+ if (texts !== null) out.texts = texts;
162
+ return out;
163
+ }
164
+ function safeThemeLocale(value) {
165
+ if (typeof value !== "string") return null;
166
+ const v = value.trim();
167
+ return v.length >= 2 && v.length <= 5 && /^[a-zA-Z-]+$/.test(v) ? v : null;
168
+ }
169
+ var MAX_QUICK_REPLIES = 6;
170
+ var MAX_QUICK_REPLY_LABEL = 40;
171
+ function sanitizeQuickReplies(raw) {
172
+ if (!Array.isArray(raw)) return [];
173
+ const out = [];
174
+ for (const item of raw) {
175
+ if (out.length >= MAX_QUICK_REPLIES) break;
176
+ if (typeof item !== "object" || item === null) continue;
177
+ const o = item;
178
+ const label = typeof o.label === "string" ? o.label.trim() : "";
179
+ if (label === "" || label.length > MAX_QUICK_REPLY_LABEL) continue;
180
+ const value = typeof o.value === "string" && o.value.trim() !== "" ? o.value.trim() : void 0;
181
+ out.push(value !== void 0 ? { label, value } : { label });
182
+ }
183
+ return out;
184
+ }
185
+ var MAX_AGENT_NAME = 40;
186
+ function sanitizeAssistantIdentity(raw, kind = "human") {
187
+ if (typeof raw !== "object" || raw === null) return null;
188
+ const o = raw;
189
+ const rawName = typeof o.name === "string" ? o.name.trim() : "";
190
+ const name = rawName !== "" && rawName.length <= MAX_AGENT_NAME ? rawName : void 0;
191
+ let avatar;
192
+ if (typeof o.avatar === "string" && o.avatar.trim() !== "") {
193
+ try {
194
+ const u = new URL(o.avatar.trim());
195
+ if (u.protocol === "https:") avatar = u.toString();
196
+ } catch {
197
+ }
198
+ }
199
+ if (name === void 0 && avatar === void 0) return null;
200
+ return {
201
+ kind,
202
+ ...name !== void 0 ? { name } : {},
203
+ ...avatar !== void 0 ? { avatar } : {}
204
+ };
205
+ }
206
+ function isRawAssistantMessageEvent(v) {
207
+ if (typeof v !== "object" || v === null) return false;
208
+ const o = v;
209
+ if (o.type !== "assistant_message") return false;
210
+ if (typeof o.conversation_id !== "string") return false;
211
+ if (typeof o.content !== "string") return false;
212
+ if (!Array.isArray(o.turn_ids)) return false;
213
+ if (o.ts !== void 0 && o.ts !== null && typeof o.ts !== "number") return false;
214
+ if (o.content_type !== void 0 && o.content_type !== null && typeof o.content_type !== "string")
215
+ return false;
216
+ if (o.caption !== void 0 && o.caption !== null && typeof o.caption !== "string") return false;
217
+ return true;
218
+ }
219
+ function isAssistantMessageEvent(v) {
220
+ if (!isRawAssistantMessageEvent(v)) return false;
221
+ if (typeof v.ts !== "number") return false;
222
+ if (v.caption !== void 0 && typeof v.caption !== "string") return false;
223
+ if (v.content_type !== void 0 && v.content_type !== "text" && !isMediaContentType(v.content_type))
224
+ return false;
225
+ return v.turn_ids.every((t) => typeof t === "string");
226
+ }
227
+ function normalizeAssistantMessageEvent(raw, now = Date.now) {
228
+ const turnIds = raw.turn_ids.filter((t) => typeof t === "string");
229
+ return {
230
+ type: "assistant_message",
231
+ conversation_id: raw.conversation_id,
232
+ content: raw.content,
233
+ turn_ids: turnIds,
234
+ ts: typeof raw.ts === "number" && Number.isFinite(raw.ts) && raw.ts > 0 ? raw.ts : now(),
235
+ ...raw.content_type != null ? {
236
+ content_type: isMediaContentType(raw.content_type) ? raw.content_type : "text"
237
+ } : {},
238
+ ...raw.caption != null ? { caption: raw.caption } : {},
239
+ ...(() => {
240
+ const qr = sanitizeQuickReplies(raw.quick_replies);
241
+ return qr.length > 0 ? { quick_replies: qr } : {};
242
+ })(),
243
+ // Cualquier valor que no sea exactamente "human" cuenta como bot: es el
244
+ // default seguro — decirle al usuario que lo atiende una persona cuando no
245
+ // es cierto es peor que lo contrario.
246
+ ...raw.author === "human" ? { author: "human" } : {},
247
+ // La identidad SÓLO se propaga junto a `author: "human"`. Un `agent` en un
248
+ // mensaje del bot le pondría cara y nombre de persona a algo que escribió
249
+ // una máquina, que es exactamente la confusión que el handover evita.
250
+ ...(() => {
251
+ if (raw.author !== "human") return {};
252
+ const id = sanitizeAssistantIdentity(raw.agent);
253
+ return id !== null ? { agent: id } : {};
254
+ })()
255
+ };
256
+ }
257
+ function parseWidgetEvent(raw, now = Date.now) {
258
+ let v;
259
+ try {
260
+ v = JSON.parse(raw);
261
+ } catch {
262
+ return null;
263
+ }
264
+ return isRawAssistantMessageEvent(v) ? normalizeAssistantMessageEvent(v, now) : null;
265
+ }
266
+ // Annotate the CommonJS export names for ESM import in node:
267
+ 0 && (module.exports = {
268
+ ALLOWED_UPLOAD_CONTENT_TYPES,
269
+ MAX_AGENT_NAME,
270
+ MAX_QUICK_REPLIES,
271
+ MAX_QUICK_REPLY_LABEL,
272
+ MAX_THEME_FONT_FAMILY,
273
+ MAX_THEME_LABEL,
274
+ MAX_THEME_TAGLINE,
275
+ MAX_THEME_TEXT_KEY,
276
+ MAX_THEME_TEXT_KEYS,
277
+ MAX_THEME_TEXT_VALUE,
278
+ MAX_UPLOAD_SIZE_BYTES,
279
+ MEDIA_CONTENT_TYPES,
280
+ SERVER_PROVIDED_THEME_FIELDS,
281
+ isAllowedUploadContentType,
282
+ isAssistantMessageEvent,
283
+ isMediaContentType,
284
+ isRawAssistantMessageEvent,
285
+ mediaKindForContentType,
286
+ normalizeAssistantMessageEvent,
287
+ parseWidgetEvent,
288
+ safeThemeColor,
289
+ safeThemeUrl,
290
+ sanitizeAssistantIdentity,
291
+ sanitizeQuickReplies,
292
+ sanitizeServerTheme
293
+ });