@lorikeetai/react-native-sdk 1.2.0 → 2.0.0-alpha.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
@@ -1,233 +1,890 @@
1
- 'use strict';
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
12
+ key = keys[i];
13
+ if (!__hasOwnProp.call(to, key) && key !== except) {
14
+ __defProp(to, key, {
15
+ get: ((k) => from[k]).bind(null, key),
16
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
17
+ });
18
+ }
19
+ }
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
24
+ value: mod,
25
+ enumerable: true
26
+ }) : target, mod));
2
27
 
3
- var react = require('react');
4
- var reactNative = require('react-native');
5
- var reactNativeSafeAreaContext = require('react-native-safe-area-context');
6
- var reactNativeWebview = require('react-native-webview');
7
- var Svg = require('react-native-svg');
8
- var jsxRuntime = require('react/jsx-runtime');
28
+ //#endregion
29
+ let react = require("react");
30
+ let react_native = require("react-native");
31
+ let react_native_safe_area_context = require("react-native-safe-area-context");
32
+ let react_native_webview = require("react-native-webview");
33
+ let react_native_svg = require("react-native-svg");
34
+ react_native_svg = __toESM(react_native_svg, 1);
35
+ let react_jsx_runtime = require("react/jsx-runtime");
9
36
 
10
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
37
+ //#region src/bridge.ts
38
+ /**
39
+ * The public key, base URL and token are one unit, and a conversation belongs to
40
+ * the credentials that created it. Stamping an observation with them is what
41
+ * stops a remount resuming one environment's ticket against another's key.
42
+ */
43
+ const conversationIdentity = ({ authToken, baseUrl, publicKey }) => `${publicKey} ${baseUrl !== null && baseUrl !== void 0 ? baseUrl : ""} ${authToken}`;
44
+ /**
45
+ * A remount has to resume the conversation it was in, or the recovery abandons
46
+ * the ticket it was meant to rescue. Only usable when the credentials still
47
+ * match the ones that produced it.
48
+ */
49
+ const resumableConversation = (observed, identity, intendedTicketId) => {
50
+ if (observed === null || observed.identity !== identity || observed.intendedTicketId !== intendedTicketId || observed.ticketId === void 0) return null;
51
+ return {
52
+ session: observed.session,
53
+ ticketId: observed.ticketId
54
+ };
55
+ };
11
56
 
12
- var Svg__default = /*#__PURE__*/_interopDefault(Svg);
57
+ //#endregion
58
+ //#region src/encode.ts
59
+ const BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
60
+ /**
61
+ * Base64 of a string's UTF-8 bytes.
62
+ *
63
+ * The chat page decodes this as UTF-8 (`Buffer.from(value, 'base64')`), so
64
+ * UTF-8 is the contract. The global `btoa` cannot meet it: it accepts Latin-1
65
+ * only, so a typographic apostrophe or an emoji in a title throws
66
+ * "Found invalid character when converting to base64" and takes the render with
67
+ * it. Hermes also had no `btoa` at all before React Native 0.74, which is
68
+ * inside this package's supported peer range.
69
+ */
70
+ function base64EncodeUtf8(value) {
71
+ const bytes = encodeURIComponent(value).replace(/%([0-9A-F]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16)));
72
+ let encoded = "";
73
+ for (let index = 0; index < bytes.length; index += 3) {
74
+ const first = bytes.charCodeAt(index);
75
+ const second = index + 1 < bytes.length ? bytes.charCodeAt(index + 1) : void 0;
76
+ const third = index + 2 < bytes.length ? bytes.charCodeAt(index + 2) : void 0;
77
+ encoded += BASE64_ALPHABET[first >> 2];
78
+ encoded += BASE64_ALPHABET[(first & 3) << 4 | (second !== null && second !== void 0 ? second : 0) >> 4];
79
+ encoded += second === void 0 ? "=" : BASE64_ALPHABET[(second & 15) << 2 | (third !== null && third !== void 0 ? third : 0) >> 6];
80
+ encoded += third === void 0 ? "=" : BASE64_ALPHABET[third & 63];
81
+ }
82
+ return encoded;
83
+ }
13
84
 
14
- var __defProp = Object.defineProperty;
15
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
16
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
85
+ //#endregion
86
+ //#region \0@oxc-project+runtime@0.147.0/helpers/esm/typeof.js
87
+ function _typeof(o) {
88
+ "@babel/helpers - typeof";
89
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
90
+ return typeof o;
91
+ } : function(o) {
92
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
93
+ }, _typeof(o);
94
+ }
95
+
96
+ //#endregion
97
+ //#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPrimitive.js
98
+ function toPrimitive(t, r) {
99
+ if ("object" != _typeof(t) || !t) return t;
100
+ var e = t[Symbol.toPrimitive];
101
+ if (void 0 !== e) {
102
+ var i = e.call(t, r || "default");
103
+ if ("object" != _typeof(i)) return i;
104
+ throw new TypeError("@@toPrimitive must return a primitive value.");
105
+ }
106
+ return ("string" === r ? String : Number)(t);
107
+ }
108
+
109
+ //#endregion
110
+ //#region \0@oxc-project+runtime@0.147.0/helpers/esm/toPropertyKey.js
111
+ function toPropertyKey(t) {
112
+ var i = toPrimitive(t, "string");
113
+ return "symbol" == _typeof(i) ? i : i + "";
114
+ }
17
115
 
18
- // src/client.ts
116
+ //#endregion
117
+ //#region \0@oxc-project+runtime@0.147.0/helpers/esm/defineProperty.js
118
+ function _defineProperty(e, r, t) {
119
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
120
+ value: t,
121
+ enumerable: !0,
122
+ configurable: !0,
123
+ writable: !0
124
+ }) : e[r] = t, e;
125
+ }
126
+
127
+ //#endregion
128
+ //#region src/client.ts
19
129
  var LorikeetClient = class {
20
- constructor({ customisations, api_base, public_key }) {
21
- __publicField(this, "baseUrl", "https://chat.lorikeetcx.ai");
22
- // Fallback URL
23
- __publicField(this, "customisations", null);
24
- __publicField(this, "publicKey");
25
- __publicField(this, "getWebviewUrl", ({
26
- customer_email,
27
- customer_name,
28
- token,
29
- ticket_id
30
- }) => {
31
- const url = new URL(this.baseUrl);
32
- url.pathname = ticket_id ? `/tickets/${this.publicKey}/${ticket_id}` : "/widget";
33
- url.searchParams.append("public_key", this.publicKey);
34
- if (customer_email) {
35
- url.searchParams.append("customer_email", customer_email);
36
- }
37
- if (customer_name) {
38
- url.searchParams.append("customer_name", customer_name);
39
- }
40
- url.searchParams.append("token", token);
41
- if (this.customisations) {
42
- url.searchParams.append(
43
- "customisations",
44
- btoa(JSON.stringify(this.customisations))
45
- );
46
- }
47
- return url.toString();
48
- });
49
- if (api_base) {
50
- this.baseUrl = api_base;
51
- }
52
- this.publicKey = public_key;
53
- if (customisations) {
54
- this.customisations = customisations;
55
- }
56
- }
130
+ constructor({ customisations, api_base, metadata, public_key }) {
131
+ _defineProperty(this, "baseUrl", "https://chat.lorikeetcx.ai");
132
+ _defineProperty(this, "customisations", null);
133
+ _defineProperty(this, "metadata", null);
134
+ _defineProperty(this, "publicKey", void 0);
135
+ _defineProperty(this, "getWebviewUrl", ({ customer_email, customer_name, token, ticket_id, session }) => {
136
+ const url = new URL(this.baseUrl);
137
+ url.pathname = ticket_id ? `/tickets/${this.publicKey}/${ticket_id}` : "/widget";
138
+ url.searchParams.append("public_key", this.publicKey);
139
+ if (customer_email) url.searchParams.append("customer_email", customer_email);
140
+ if (customer_name) url.searchParams.append("customer_name", customer_name);
141
+ url.searchParams.append("token", token);
142
+ if (ticket_id && session) url.searchParams.append("session", session);
143
+ if (this.customisations) url.searchParams.append("customisations", base64EncodeUtf8(JSON.stringify(this.customisations)));
144
+ if (this.metadata) url.searchParams.append("metadata", JSON.stringify(this.metadata));
145
+ return url.toString();
146
+ });
147
+ _defineProperty(
148
+ this,
149
+ /**
150
+ * The page that clears this key's credentials from the WebView: its cookies,
151
+ * server-side, and the storage the frame keeps. Navigating a WebView here is
152
+ * the only way to reach either, so a host that only drops its own copy of
153
+ * `ticketId` and `session` leaves the next person on the device able to
154
+ * reopen the conversation.
155
+ *
156
+ * The page confirms with a `loggedOut` message, which the SDK reports as
157
+ * `logged_out`. The load event does not: an error page fires that too.
158
+ */
159
+ "getLogoutUrl",
160
+ () => {
161
+ const url = new URL(this.baseUrl);
162
+ url.pathname = "/logout";
163
+ url.searchParams.append("public_key", this.publicKey);
164
+ return url.toString();
165
+ }
166
+ );
167
+ if (api_base) this.baseUrl = api_base;
168
+ this.publicKey = public_key;
169
+ if (customisations) this.customisations = customisations;
170
+ if (metadata) this.metadata = metadata;
171
+ }
172
+ };
173
+
174
+ //#endregion
175
+ //#region src/errors.ts
176
+ /**
177
+ * Codes the chat page posts to the host as `{ type: 'error', error: CODE }`.
178
+ * Mirrored here because the page publishes them nowhere else, so a host would
179
+ * otherwise be matching on strings it discovered by observation.
180
+ */
181
+ const LORIKEET_ERROR_CODES = [
182
+ "MISSING_TOKEN",
183
+ "CORRUPT_TOKEN_DATA",
184
+ "DECRYPT_FAILED",
185
+ "EXPIRED_TOKEN",
186
+ "MALFORMED_TOKEN",
187
+ "MISSING_PUBLIC_KEY",
188
+ "UNKNOWN_PUBLIC_KEY",
189
+ "CUSTOMER_ID_CONFLICT",
190
+ "CUSTOMER_ID_MISSING",
191
+ "FATAL_ERROR",
192
+ "INVALID_SESSION",
193
+ "DOMAIN_NOT_ALLOWED",
194
+ "TICKET_NOT_FOUND"
195
+ ];
196
+ /**
197
+ * The codes that mean the stored conversation cannot be reopened, as opposed to
198
+ * the trusted token being bad or the integration being misconfigured. Only
199
+ * these are worth abandoning a resume for: a token error fails again with the
200
+ * same token, and a configuration error fails again regardless.
201
+ */
202
+ const UNUSABLE_CONVERSATION_CODES = [
203
+ "CUSTOMER_ID_MISSING",
204
+ "INVALID_SESSION",
205
+ "TICKET_NOT_FOUND"
206
+ ];
207
+ /** Whether an error code means the stored conversation cannot be reopened. */
208
+ function isUnusableConversationCode(code) {
209
+ return UNUSABLE_CONVERSATION_CODES.includes(code);
210
+ }
211
+ function isUnusableConversation(message) {
212
+ if (typeof message !== "object" || message === null) return false;
213
+ const { type, error } = message;
214
+ return type === "error" && typeof error === "string" ? isUnusableConversationCode(error) : false;
215
+ }
216
+
217
+ //#endregion
218
+ //#region src/events.ts
219
+ const asRecord = (message) => typeof message === "object" && message !== null && !Array.isArray(message) ? message : null;
220
+ const asNonEmptyString = (value) => typeof value === "string" && value !== "" ? value : null;
221
+ /**
222
+ * The event a bridge message carries, or `null` for one that carries none.
223
+ *
224
+ * `ticket_created` is not derived here: telling a fresh ticket from a resumed
225
+ * one needs the URL the page was loaded with, which only the component holds.
226
+ */
227
+ const eventFromMessage = (message) => {
228
+ const record = asRecord(message);
229
+ if (record === null || typeof record.type !== "string") return null;
230
+ switch (record.type) {
231
+ case "ready": return { type: "ready" };
232
+ case "customerMessage": return { type: "customer_message" };
233
+ case "agentMessage": return typeof record.markUnread === "boolean" ? {
234
+ type: "agent_message",
235
+ markUnread: record.markUnread
236
+ } : { type: "agent_message" };
237
+ case "closeChat": {
238
+ const reason = asNonEmptyString(record.reason);
239
+ return reason === "ended_by_customer" || reason === "minimised" ? {
240
+ type: "close",
241
+ reason
242
+ } : { type: "close" };
243
+ }
244
+ case "conversationEnded": return { type: "conversation_ended" };
245
+ case "startNewChat": return { type: "new_chat" };
246
+ case "persistLorikeetTicketId": {
247
+ const ticketId = asNonEmptyString(record.ticketId);
248
+ return ticketId === null ? null : {
249
+ type: "persist_ticket_id",
250
+ ticketId
251
+ };
252
+ }
253
+ case "persistLorikeetSession": {
254
+ const session = asNonEmptyString(record.session);
255
+ return session === null ? null : {
256
+ type: "persist_session",
257
+ session
258
+ };
259
+ }
260
+ case "loggedOut": return { type: "logged_out" };
261
+ case "error": {
262
+ const error = asNonEmptyString(record.error);
263
+ return error === null ? null : {
264
+ type: "error",
265
+ error
266
+ };
267
+ }
268
+ case "preloadComplete": return {
269
+ type: "preload_complete",
270
+ clientStart: record.clientStart === true,
271
+ lazyTicketCreationEnabled: record.lazyTicketCreationEnabled === true
272
+ };
273
+ default: return null;
274
+ }
275
+ };
276
+ /**
277
+ * The ticket a URL is trying to resume, or `null` for one that creates its own.
278
+ *
279
+ * Read from the URL rather than from any state, so it holds for a host that
280
+ * built the URL itself. An unreadable URL reports nothing, which at worst
281
+ * announces a resumed ticket as created once.
282
+ */
283
+ const resumedTicketIdFromUri = (uri) => {
284
+ var _segments$;
285
+ let pathname;
286
+ try {
287
+ pathname = new URL(uri).pathname;
288
+ } catch {
289
+ return null;
290
+ }
291
+ const segments = pathname.split("/").filter((part) => part !== "");
292
+ if (segments.length < 3 || segments[0] !== "tickets") return null;
293
+ return (_segments$ = segments[2]) !== null && _segments$ !== void 0 ? _segments$ : null;
294
+ };
295
+
296
+ //#endregion
297
+ //#region src/lifecycle.ts
298
+ /**
299
+ * The page gives its message stream ten attempts with exponential backoff
300
+ * capped at 30s (`message-stream-utils.tsx`), so the budget runs out roughly
301
+ * 151s after the stream drops, or ~196s once jitter is counted. It then sits at
302
+ * `failed` with nothing to reset it, so a customer who comes back to a
303
+ * backgrounded app is looking at a stream that will never reconnect.
304
+ *
305
+ * Three minutes sits inside that band. Shorter would remount while the page's
306
+ * own retries could still win, which costs the customer their draft message for
307
+ * nothing; much longer leaves a dead stream on screen.
308
+ */
309
+ const BACKGROUND_RELOAD_THRESHOLD_MS = 18e4;
310
+ /**
311
+ * How long `/logout` gets to confirm it cleared the frame before the host is
312
+ * told it did not. Matches the web SDK's own budget, and like it, a logout that
313
+ * misses the deadline leaves the widget unavailable rather than letting the next
314
+ * customer start a chat on a device the previous one's credentials still open.
315
+ */
316
+ const LOGOUT_CONFIRMATION_TIMEOUT_MS = 1e4;
317
+ /**
318
+ * Elapsed time is the only signal available: the bridge reports `ready`,
319
+ * `error` and the persistence messages, but never stream health, so the SDK
320
+ * cannot ask whether the connection actually survived.
321
+ */
322
+ const shouldReloadAfterBackground = (backgroundedAt, now, thresholdMs = BACKGROUND_RELOAD_THRESHOLD_MS) => {
323
+ if (backgroundedAt === null) return false;
324
+ const elapsed = now - backgroundedAt;
325
+ if (elapsed < 0) return false;
326
+ return elapsed >= thresholdMs;
327
+ };
328
+
329
+ //#endregion
330
+ //#region src/links.ts
331
+ /**
332
+ * Schemes the WebView resolves for itself. None of them is a page the customer
333
+ * asked to leave the app for, and the OS cannot open them anyway.
334
+ */
335
+ const WEBVIEW_OWNED_SCHEMES = /* @__PURE__ */ new Set([
336
+ "about:",
337
+ "blob:",
338
+ "data:",
339
+ "javascript:"
340
+ ]);
341
+ const parseUrl = (value) => {
342
+ try {
343
+ return new URL(value);
344
+ } catch {
345
+ return null;
346
+ }
347
+ };
348
+ /**
349
+ * Whether the OS could be asked to open this at all. `window.open()` with no
350
+ * argument gives `about:blank`, and a scheme the WebView resolves for itself is
351
+ * not something the OS can place.
352
+ */
353
+ const canOpenOutsideApp = (url) => {
354
+ const target = parseUrl(url);
355
+ return target !== null && !WEBVIEW_OWNED_SCHEMES.has(target.protocol);
57
356
  };
58
- var MessageCirclePlusIcon = ({
59
- size = 24,
60
- color = "currentColor",
61
- strokeWidth = 2
62
- }) => {
63
- return /* @__PURE__ */ jsxRuntime.jsxs(Svg__default.default, { fill: "none", height: size, viewBox: "0 0 24 24", width: size, children: [
64
- /* @__PURE__ */ jsxRuntime.jsx(
65
- Svg.Path,
66
- {
67
- d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z",
68
- stroke: color,
69
- strokeLinecap: "round",
70
- strokeLinejoin: "round",
71
- strokeWidth
72
- }
73
- ),
74
- /* @__PURE__ */ jsxRuntime.jsx(
75
- Svg.Line,
76
- {
77
- stroke: color,
78
- strokeLinecap: "round",
79
- strokeWidth,
80
- x1: "12",
81
- x2: "12",
82
- y1: "8",
83
- y2: "16"
84
- }
85
- ),
86
- /* @__PURE__ */ jsxRuntime.jsx(
87
- Svg.Line,
88
- {
89
- stroke: color,
90
- strokeLinecap: "round",
91
- strokeWidth,
92
- x1: "8",
93
- x2: "16",
94
- y1: "12",
95
- y2: "12"
96
- }
97
- )
98
- ] });
357
+ /**
358
+ * Whether a navigation belongs inside the widget.
359
+ *
360
+ * An origin comparison rather than the substring match this replaces. `baseUrl`
361
+ * appearing anywhere in a URL counted as internal, so
362
+ * `https://evil.example/?next=https://chat.lorikeetcx.ai` loaded in place of the
363
+ * conversation instead of opening in the browser. Comparing origins rather than
364
+ * matching a prefix also means a port, a trailing slash and a different path all
365
+ * still resolve to the same widget.
366
+ */
367
+ const isInternalNavigation = (url, baseUrl) => {
368
+ const base = parseUrl(baseUrl);
369
+ const target = parseUrl(url);
370
+ if (base === null || target === null) return true;
371
+ if (WEBVIEW_OWNED_SCHEMES.has(target.protocol)) return true;
372
+ return target.origin === base.origin;
373
+ };
374
+
375
+ //#endregion
376
+ //#region src/message-circle-plus-icon.tsx
377
+ const MessageCirclePlusIcon = ({ size = 24, color = "currentColor", strokeWidth = 2 }) => {
378
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native_svg.default, {
379
+ fill: "none",
380
+ height: size,
381
+ viewBox: "0 0 24 24",
382
+ width: size,
383
+ children: [
384
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_svg.Path, {
385
+ d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z",
386
+ stroke: color,
387
+ strokeLinecap: "round",
388
+ strokeLinejoin: "round",
389
+ strokeWidth
390
+ }),
391
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_svg.Line, {
392
+ stroke: color,
393
+ strokeLinecap: "round",
394
+ strokeWidth,
395
+ x1: "12",
396
+ x2: "12",
397
+ y1: "8",
398
+ y2: "16"
399
+ }),
400
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_svg.Line, {
401
+ stroke: color,
402
+ strokeLinecap: "round",
403
+ strokeWidth,
404
+ x1: "8",
405
+ x2: "16",
406
+ y1: "12",
407
+ y2: "12"
408
+ })
409
+ ]
410
+ });
99
411
  };
100
- var stylesheet = reactNative.StyleSheet.create({
101
- root: { flex: 1, backgroundColor: "#fff" },
102
- container: {
103
- flex: 1,
104
- borderRadius: 8,
105
- overflow: "hidden",
106
- shadowColor: "#000",
107
- shadowOffset: { width: 0, height: 2 },
108
- shadowOpacity: 0.1,
109
- shadowRadius: 4,
110
- elevation: 3
111
- },
112
- header: {
113
- padding: 16,
114
- display: "flex",
115
- flexDirection: "row",
116
- justifyContent: "space-between"
117
- },
118
- headerIcon: {
119
- width: 24
120
- },
121
- headerText: {
122
- textAlign: "center",
123
- fontSize: 16
124
- },
125
- webView: {
126
- flex: 1
127
- }
412
+
413
+ //#endregion
414
+ //#region src/widget.tsx
415
+ const stylesheet = react_native.StyleSheet.create({
416
+ root: {
417
+ flex: 1,
418
+ backgroundColor: "#fff"
419
+ },
420
+ container: {
421
+ flex: 1,
422
+ borderRadius: 8,
423
+ overflow: "hidden",
424
+ shadowColor: "#000",
425
+ shadowOffset: {
426
+ width: 0,
427
+ height: 2
428
+ },
429
+ shadowOpacity: .1,
430
+ shadowRadius: 4,
431
+ elevation: 3
432
+ },
433
+ header: {
434
+ padding: 16,
435
+ display: "flex",
436
+ flexDirection: "row",
437
+ justifyContent: "space-between"
438
+ },
439
+ headerIcon: { width: 24 },
440
+ headerText: {
441
+ textAlign: "center",
442
+ fontSize: 16
443
+ },
444
+ closedControl: { padding: 16 },
445
+ webView: { flex: 1 },
446
+ closed: {
447
+ flex: 1,
448
+ opacity: 0
449
+ }
128
450
  });
129
- var LorikeetWidgetWebView = react.forwardRef(({ uri, baseUrl = "https://chat.lorikeetcx.ai", webviewProps = {} }, ref) => {
130
- return /* @__PURE__ */ jsxRuntime.jsx(
131
- reactNativeWebview.WebView,
132
- {
133
- ...webviewProps,
134
- onNavigationStateChange: (navState) => {
135
- if (navState.url.includes(baseUrl)) {
136
- return false;
137
- } else {
138
- void reactNative.Linking.openURL(navState.url);
139
- return false;
140
- }
141
- },
142
- ref,
143
- source: {
144
- uri
145
- },
146
- style: stylesheet.webView
147
- }
148
- );
451
+ const LorikeetWidgetWebView = (0, react.forwardRef)(({ uri, baseUrl = "https://chat.lorikeetcx.ai", webviewProps = {}, onEvent }, ref) => {
452
+ const isInternal = (url) => isInternalNavigation(url, baseUrl);
453
+ const accounted = (0, react.useRef)(/* @__PURE__ */ new Set());
454
+ const seenUri = (0, react.useRef)(null);
455
+ if (seenUri.current !== uri) {
456
+ seenUri.current = uri;
457
+ const resumed = resumedTicketIdFromUri(uri);
458
+ if (resumed !== null) accounted.current.add(resumed);
459
+ }
460
+ const emit = (event) => {
461
+ onEvent === null || onEvent === void 0 || onEvent(event);
462
+ if (event.type !== "persist_ticket_id") return;
463
+ if (accounted.current.has(event.ticketId)) return;
464
+ accounted.current.add(event.ticketId);
465
+ onEvent === null || onEvent === void 0 || onEvent({
466
+ ticketId: event.ticketId,
467
+ type: "ticket_created"
468
+ });
469
+ };
470
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_webview.WebView, {
471
+ style: stylesheet.webView,
472
+ ...webviewProps,
473
+ onMessage: (event) => {
474
+ var _webviewProps$onMessa;
475
+ let message;
476
+ try {
477
+ message = JSON.parse(event.nativeEvent.data);
478
+ } catch {}
479
+ const widgetEvent = eventFromMessage(message);
480
+ if (widgetEvent !== null) emit(widgetEvent);
481
+ (_webviewProps$onMessa = webviewProps.onMessage) === null || _webviewProps$onMessa === void 0 || _webviewProps$onMessa.call(webviewProps, event);
482
+ },
483
+ onNavigationStateChange: (navState) => {
484
+ var _webviewProps$onNavig;
485
+ if (!isInternal(navState.url)) react_native.Linking.openURL(navState.url);
486
+ (_webviewProps$onNavig = webviewProps.onNavigationStateChange) === null || _webviewProps$onNavig === void 0 || _webviewProps$onNavig.call(webviewProps, navState);
487
+ },
488
+ onOpenWindow: (event) => {
489
+ var _webviewProps$onOpenW;
490
+ const { targetUrl } = event.nativeEvent;
491
+ if (canOpenOutsideApp(targetUrl)) react_native.Linking.openURL(targetUrl);
492
+ (_webviewProps$onOpenW = webviewProps.onOpenWindow) === null || _webviewProps$onOpenW === void 0 || _webviewProps$onOpenW.call(webviewProps, event);
493
+ },
494
+ onShouldStartLoadWithRequest: (request) => {
495
+ var _webviewProps$onShoul;
496
+ if (((_webviewProps$onShoul = webviewProps.onShouldStartLoadWithRequest) === null || _webviewProps$onShoul === void 0 ? void 0 : _webviewProps$onShoul.call(webviewProps, request)) === false) return false;
497
+ if (request.isTopFrame === false || isInternal(request.url)) return true;
498
+ react_native.Linking.openURL(request.url);
499
+ return false;
500
+ },
501
+ ref,
502
+ source: { uri }
503
+ });
149
504
  });
150
505
  LorikeetWidgetWebView.displayName = "LorikeetWidgetWebView";
151
- var LorikeetWidgetHeader = ({
152
- title,
153
- style,
154
- icon = /* @__PURE__ */ jsxRuntime.jsx(MessageCirclePlusIcon, { size: 16 }),
155
- onIconPress
156
- }) => {
157
- return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: stylesheet.header, children: [
158
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: stylesheet.headerIcon }),
159
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: [stylesheet.headerText, style], children: title }),
160
- /* @__PURE__ */ jsxRuntime.jsx(reactNative.TouchableOpacity, { onPress: onIconPress, style: stylesheet.headerIcon, children: icon })
161
- ] });
506
+ const LorikeetWidgetHeader = ({ title, style, icon = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MessageCirclePlusIcon, { size: 16 }), onIconPress }) => {
507
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
508
+ style: stylesheet.header,
509
+ children: [
510
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, { style: stylesheet.headerIcon }),
511
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
512
+ style: [stylesheet.headerText, style],
513
+ children: title
514
+ }),
515
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
516
+ onPress: onIconPress,
517
+ style: stylesheet.headerIcon,
518
+ children: icon
519
+ })
520
+ ]
521
+ });
162
522
  };
163
- function LorikeetWidgetView({
164
- publicKey,
165
- baseUrl,
166
- authToken,
167
- customisations,
168
- styles,
169
- onWebViewMessage
170
- }) {
171
- var _a;
172
- const [lorikeetClient, setLorikeetClient] = react.useState(
173
- null
174
- );
175
- const [uri, setUri] = react.useState(null);
176
- const [timestamp, setTimestamp] = react.useState(Date.now());
177
- const webViewRef = react.useRef(null);
178
- react.useEffect(() => {
179
- const lorikeetClient2 = new LorikeetClient({
180
- public_key: publicKey,
181
- api_base: baseUrl,
182
- customisations: {
183
- ...customisations,
184
- hideHeader: (customisations == null ? void 0 : customisations.hideHeader) === void 0 ? true : customisations.hideHeader
185
- }
186
- });
187
- setLorikeetClient(lorikeetClient2);
188
- setUri(
189
- lorikeetClient2.getWebviewUrl({
190
- token: authToken
191
- })
192
- );
193
- }, [publicKey, baseUrl]);
194
- if (!lorikeetClient || !uri) {
195
- return null;
196
- }
197
- return /* @__PURE__ */ jsxRuntime.jsx(reactNativeSafeAreaContext.SafeAreaView, { edges: ["top", "left", "right"], style: stylesheet.root, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: stylesheet.container, children: [
198
- /* @__PURE__ */ jsxRuntime.jsx(
199
- LorikeetWidgetHeader,
200
- {
201
- onIconPress: () => {
202
- setTimestamp(Date.now());
203
- },
204
- style: styles == null ? void 0 : styles.header,
205
- title: (_a = customisations == null ? void 0 : customisations.title) != null ? _a : "Lorikeet AI Agent"
206
- }
207
- ),
208
- /* @__PURE__ */ jsxRuntime.jsx(
209
- LorikeetWidgetWebView,
210
- {
211
- baseUrl: lorikeetClient.baseUrl,
212
- ref: webViewRef,
213
- uri,
214
- webviewProps: {
215
- onMessage: onWebViewMessage,
216
- allowsInlineMediaPlayback: true,
217
- mediaPlaybackRequiresUserAction: false,
218
- scalesPageToFit: true,
219
- startInLoadingState: true,
220
- style: stylesheet.webView
221
- }
222
- },
223
- timestamp
224
- )
225
- ] }) });
226
- }
523
+ const LorikeetWidgetView = (0, react.forwardRef)(function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps }, ref) {
524
+ var _customisations$hideH, _customisations$title;
525
+ const [lorikeetClient, setLorikeetClient] = (0, react.useState)(null);
526
+ const [loaded, setLoaded] = (0, react.useState)(null);
527
+ const [generation, setGeneration] = (0, react.useState)(0);
528
+ const generationRef = (0, react.useRef)(generation);
529
+ generationRef.current = generation;
530
+ const remount = (0, react.useCallback)(() => {
531
+ setGeneration((previous) => previous + 1);
532
+ }, []);
533
+ const webViewRef = (0, react.useRef)(null);
534
+ const [refusedTicketId, setRefusedTicketId] = (0, react.useState)(null);
535
+ const observedRef = (0, react.useRef)(null);
536
+ const backgroundedAtRef = (0, react.useRef)(null);
537
+ const uriRef = (0, react.useRef)(null);
538
+ const loadedForRef = (0, react.useRef)({
539
+ identity: "",
540
+ intendedTicketId: void 0
541
+ });
542
+ const identity = conversationIdentity({
543
+ authToken,
544
+ baseUrl,
545
+ publicKey
546
+ });
547
+ const resumingTicketId = ticketId !== void 0 && ticketId !== refusedTicketId ? ticketId : void 0;
548
+ const [attemptedTicketId, setAttemptedTicketId] = (0, react.useState)(void 0);
549
+ const [closed, setClosed] = (0, react.useState)(null);
550
+ const [clearing, setClearing] = (0, react.useState)(null);
551
+ const [loggedOutIdentity, setLoggedOutIdentity] = (0, react.useState)(null);
552
+ const onEventRef = (0, react.useRef)(onEvent);
553
+ onEventRef.current = onEvent;
554
+ const hideHeader = (_customisations$hideH = customisations === null || customisations === void 0 ? void 0 : customisations.hideHeader) !== null && _customisations$hideH !== void 0 ? _customisations$hideH : true;
555
+ const load = (0, react.useCallback)((client, forIdentity, intendedTicketId, resume, force = false) => {
556
+ const next = client.getWebviewUrl({
557
+ token: authToken,
558
+ ticket_id: resume === null || resume === void 0 ? void 0 : resume.ticketId,
559
+ session: resume === null || resume === void 0 ? void 0 : resume.session
560
+ });
561
+ const unchanged = uriRef.current === next;
562
+ if (loadedForRef.current.identity !== forIdentity || loadedForRef.current.intendedTicketId !== intendedTicketId) setClosed(null);
563
+ uriRef.current = next;
564
+ loadedForRef.current = {
565
+ identity: forIdentity,
566
+ intendedTicketId
567
+ };
568
+ if (unchanged && !force) {
569
+ setLoaded((previous) => previous === null || previous.identity !== forIdentity || previous.intendedTicketId !== intendedTicketId ? {
570
+ identity: forIdentity,
571
+ intendedTicketId,
572
+ uri: next
573
+ } : previous);
574
+ return;
575
+ }
576
+ setLoaded({
577
+ identity: forIdentity,
578
+ intendedTicketId,
579
+ uri: next
580
+ });
581
+ setAttemptedTicketId(resume === null || resume === void 0 ? void 0 : resume.ticketId);
582
+ remount();
583
+ }, [authToken, remount]);
584
+ (0, react.useEffect)(() => {
585
+ const lorikeetClient = new LorikeetClient({
586
+ public_key: publicKey,
587
+ api_base: baseUrl,
588
+ metadata,
589
+ customisations: {
590
+ ...customisations,
591
+ hideHeader
592
+ }
593
+ });
594
+ setLorikeetClient(lorikeetClient);
595
+ if (clearing !== null || loggedOutIdentity === identity) return;
596
+ if (loggedOutIdentity !== null) setLoggedOutIdentity(null);
597
+ const resume = resumingTicketId !== void 0 ? {
598
+ session,
599
+ ticketId: resumingTicketId
600
+ } : resumableConversation(observedRef.current, identity, void 0);
601
+ load(lorikeetClient, identity, resumingTicketId, resume);
602
+ }, [
603
+ publicKey,
604
+ baseUrl,
605
+ authToken,
606
+ clearing,
607
+ customisations,
608
+ hideHeader,
609
+ loggedOutIdentity,
610
+ metadata,
611
+ session,
612
+ resumingTicketId,
613
+ identity,
614
+ load
615
+ ]);
616
+ /**
617
+ * Clear this customer's chat from the device. Loads the page's own `/logout`,
618
+ * the only thing that can drop the chat's cookies and the storage the page
619
+ * keeps; a host clearing its stored `ticketId` and `session` leaves both
620
+ * behind.
621
+ *
622
+ * Nothing else loads until the page confirms with `logged_out`, and nothing
623
+ * loads for these credentials after it, so pass the next customer's in to
624
+ * start their chat. Clear your own stored `ticketId` and `session` too.
625
+ */
626
+ const logout = (0, react.useCallback)(() => {
627
+ observedRef.current = null;
628
+ setClosed(null);
629
+ setClearing((previous) => {
630
+ if (previous !== null) return {
631
+ ...previous,
632
+ attempt: previous.attempt + 1
633
+ };
634
+ setLoggedOutIdentity(identity);
635
+ return {
636
+ attempt: 1,
637
+ baseUrl,
638
+ identity,
639
+ uri: new LorikeetClient({
640
+ api_base: baseUrl,
641
+ public_key: publicKey
642
+ }).getLogoutUrl(),
643
+ webviewProps: {
644
+ ...webviewProps,
645
+ injectedJavaScript: void 0,
646
+ injectedJavaScriptBeforeContentLoaded: void 0,
647
+ onMessage: void 0,
648
+ onShouldStartLoadWithRequest: void 0
649
+ }
650
+ };
651
+ });
652
+ }, [
653
+ baseUrl,
654
+ identity,
655
+ publicKey,
656
+ webviewProps
657
+ ]);
658
+ /**
659
+ * Show a minimised conversation again, exactly as the customer left it.
660
+ *
661
+ * A no-op unless something is minimised, so a host with its own launcher can
662
+ * call it without first asking.
663
+ */
664
+ const reopen = (0, react.useCallback)(() => {
665
+ setClosed((previous) => previous === "minimised" ? null : previous);
666
+ }, []);
667
+ /**
668
+ * What the widget is actually on, for a host that has to store it or draw its
669
+ * own launcher around it.
670
+ */
671
+ const getConversation = (0, react.useCallback)(() => {
672
+ const resumable = resumableConversation(observedRef.current, identity, resumingTicketId);
673
+ if (resumable === null) return null;
674
+ return {
675
+ minimised: closed === "minimised",
676
+ session: resumable.session,
677
+ ticketId: resumable.ticketId
678
+ };
679
+ }, [
680
+ closed,
681
+ identity,
682
+ resumingTicketId
683
+ ]);
684
+ (0, react.useImperativeHandle)(ref, () => ({
685
+ getConversation,
686
+ logout,
687
+ reopen
688
+ }), [
689
+ getConversation,
690
+ logout,
691
+ reopen
692
+ ]);
693
+ const clearingAttempt = clearing === null || clearing === void 0 ? void 0 : clearing.attempt;
694
+ (0, react.useEffect)(() => {
695
+ if (clearingAttempt === void 0) return;
696
+ const timer = setTimeout(() => {
697
+ var _onEventRef$current;
698
+ (_onEventRef$current = onEventRef.current) === null || _onEventRef$current === void 0 || _onEventRef$current.call(onEventRef, { type: "logout_failed" });
699
+ }, LOGOUT_CONFIRMATION_TIMEOUT_MS);
700
+ return () => {
701
+ clearTimeout(timer);
702
+ };
703
+ }, [clearingAttempt]);
704
+ const handleClearingEvent = (0, react.useCallback)((event) => {
705
+ if (event.type === "logged_out") setClearing((previous) => (previous === null || previous === void 0 ? void 0 : previous.attempt) === clearingAttempt ? null : previous);
706
+ onEvent === null || onEvent === void 0 || onEvent(event);
707
+ }, [clearingAttempt, onEvent]);
708
+ const reload = (0, react.useCallback)(() => {
709
+ const resume = resumableConversation(observedRef.current, identity, resumingTicketId);
710
+ if (lorikeetClient !== null && resume !== null) {
711
+ load(lorikeetClient, identity, resumingTicketId, resume, true);
712
+ return;
713
+ }
714
+ remount();
715
+ }, [
716
+ identity,
717
+ load,
718
+ lorikeetClient,
719
+ remount,
720
+ resumingTicketId
721
+ ]);
722
+ (0, react.useEffect)(() => {
723
+ const subscription = react_native.AppState.addEventListener("change", (next) => {
724
+ if (next === "active") {
725
+ if (shouldReloadAfterBackground(backgroundedAtRef.current, Date.now())) reload();
726
+ backgroundedAtRef.current = null;
727
+ return;
728
+ }
729
+ if (next === "background") {
730
+ var _backgroundedAtRef$cu;
731
+ (_backgroundedAtRef$cu = backgroundedAtRef.current) !== null && _backgroundedAtRef$cu !== void 0 || (backgroundedAtRef.current = Date.now());
732
+ }
733
+ });
734
+ return () => {
735
+ subscription.remove();
736
+ };
737
+ }, [reload]);
738
+ const startNewChat = (0, react.useCallback)(() => {
739
+ setClosed(null);
740
+ if (lorikeetClient !== null && resumingTicketId === void 0) {
741
+ observedRef.current = null;
742
+ load(lorikeetClient, identity, void 0, null, true);
743
+ return;
744
+ }
745
+ remount();
746
+ }, [
747
+ identity,
748
+ load,
749
+ lorikeetClient,
750
+ remount,
751
+ resumingTicketId
752
+ ]);
753
+ const handleEvent = (0, react.useCallback)((event) => {
754
+ if (generation === generationRef.current && loadedForRef.current.identity === identity && loadedForRef.current.intendedTicketId === resumingTicketId) {
755
+ if (event.type === "persist_ticket_id" || event.type === "persist_session") {
756
+ var _observedRef$current;
757
+ const current = ((_observedRef$current = observedRef.current) === null || _observedRef$current === void 0 ? void 0 : _observedRef$current.identity) === identity && observedRef.current.intendedTicketId === resumingTicketId ? observedRef.current : null;
758
+ observedRef.current = {
759
+ ...current,
760
+ ...event.type === "persist_ticket_id" ? { ticketId: event.ticketId } : { session: event.session },
761
+ identity,
762
+ intendedTicketId: resumingTicketId
763
+ };
764
+ }
765
+ if (event.type === "close") setClosed(event.reason === "minimised" ? "minimised" : "ended");
766
+ if (event.type === "new_chat") startNewChat();
767
+ if (event.type === "error" && attemptedTicketId !== void 0 && isUnusableConversationCode(event.error)) {
768
+ observedRef.current = null;
769
+ setRefusedTicketId(attemptedTicketId);
770
+ if (lorikeetClient !== null) load(lorikeetClient, identity, void 0, null, true);
771
+ }
772
+ }
773
+ onEvent === null || onEvent === void 0 || onEvent(event);
774
+ }, [
775
+ attemptedTicketId,
776
+ generation,
777
+ identity,
778
+ load,
779
+ lorikeetClient,
780
+ onEvent,
781
+ resumingTicketId,
782
+ startNewChat
783
+ ]);
784
+ const handleMessage = (0, react.useCallback)((event) => {
785
+ var _webviewProps$onMessa2;
786
+ onWebViewMessage === null || onWebViewMessage === void 0 || onWebViewMessage(event);
787
+ webviewProps === null || webviewProps === void 0 || (_webviewProps$onMessa2 = webviewProps.onMessage) === null || _webviewProps$onMessa2 === void 0 || _webviewProps$onMessa2.call(webviewProps, event);
788
+ }, [onWebViewMessage, webviewProps]);
789
+ if (clearing !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_safe_area_context.SafeAreaView, {
790
+ edges: [
791
+ "top",
792
+ "left",
793
+ "right"
794
+ ],
795
+ style: stylesheet.root,
796
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
797
+ accessibilityElementsHidden: true,
798
+ importantForAccessibility: "no-hide-descendants",
799
+ pointerEvents: "none",
800
+ style: stylesheet.closed,
801
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetWebView, {
802
+ baseUrl: clearing.baseUrl,
803
+ onEvent: handleClearingEvent,
804
+ uri: clearing.uri,
805
+ webviewProps: {
806
+ ...clearing.webviewProps,
807
+ style: stylesheet.webView
808
+ }
809
+ }, `logout|${clearing.attempt}`)
810
+ })
811
+ });
812
+ if (lorikeetClient === null || loaded === null || loaded.identity !== identity || loaded.intendedTicketId !== resumingTicketId || loggedOutIdentity === identity) return null;
813
+ const isClosed = closed !== null;
814
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native_safe_area_context.SafeAreaView, {
815
+ edges: [
816
+ "top",
817
+ "left",
818
+ "right"
819
+ ],
820
+ style: stylesheet.root,
821
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_native.View, {
822
+ style: stylesheet.container,
823
+ children: [
824
+ hideHeader && closed !== "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetHeader, {
825
+ onIconPress: startNewChat,
826
+ style: styles === null || styles === void 0 ? void 0 : styles.header,
827
+ title: (_customisations$title = customisations === null || customisations === void 0 ? void 0 : customisations.title) !== null && _customisations$title !== void 0 ? _customisations$title : "Lorikeet AI Agent"
828
+ }) : null,
829
+ closed === "minimised" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
830
+ onPress: reopen,
831
+ style: stylesheet.closedControl,
832
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
833
+ style: stylesheet.headerText,
834
+ children: "Reopen chat"
835
+ })
836
+ }) : null,
837
+ !hideHeader && closed === "ended" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.TouchableOpacity, {
838
+ onPress: startNewChat,
839
+ style: stylesheet.closedControl,
840
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.Text, {
841
+ style: stylesheet.headerText,
842
+ children: "Start new chat"
843
+ })
844
+ }) : null,
845
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_native.View, {
846
+ accessibilityElementsHidden: isClosed,
847
+ importantForAccessibility: isClosed ? "no-hide-descendants" : "auto",
848
+ pointerEvents: isClosed ? "none" : "auto",
849
+ style: isClosed ? stylesheet.closed : stylesheet.webView,
850
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LorikeetWidgetWebView, {
851
+ baseUrl: lorikeetClient.baseUrl,
852
+ onEvent: handleEvent,
853
+ ref: webViewRef,
854
+ uri: loaded.uri,
855
+ webviewProps: {
856
+ allowsInlineMediaPlayback: true,
857
+ mediaPlaybackRequiresUserAction: false,
858
+ scalesPageToFit: true,
859
+ startInLoadingState: true,
860
+ style: stylesheet.webView,
861
+ ...webviewProps,
862
+ onMessage: handleMessage,
863
+ onRenderProcessGone: (event) => {
864
+ var _webviewProps$onRende;
865
+ if (generation === generationRef.current) reload();
866
+ webviewProps === null || webviewProps === void 0 || (_webviewProps$onRende = webviewProps.onRenderProcessGone) === null || _webviewProps$onRende === void 0 || _webviewProps$onRende.call(webviewProps, event);
867
+ },
868
+ onContentProcessDidTerminate: (event) => {
869
+ var _webviewProps$onConte;
870
+ if (generation === generationRef.current) reload();
871
+ webviewProps === null || webviewProps === void 0 || (_webviewProps$onConte = webviewProps.onContentProcessDidTerminate) === null || _webviewProps$onConte === void 0 || _webviewProps$onConte.call(webviewProps, event);
872
+ }
873
+ }
874
+ }, `${identity}|${resumingTicketId !== null && resumingTicketId !== void 0 ? resumingTicketId : ""}|${generation}`)
875
+ })
876
+ ]
877
+ })
878
+ });
879
+ });
880
+ LorikeetWidgetView.displayName = "LorikeetWidgetView";
227
881
 
882
+ //#endregion
883
+ exports.LORIKEET_ERROR_CODES = LORIKEET_ERROR_CODES;
228
884
  exports.LorikeetClient = LorikeetClient;
229
885
  exports.LorikeetWidgetHeader = LorikeetWidgetHeader;
230
886
  exports.LorikeetWidgetView = LorikeetWidgetView;
231
887
  exports.LorikeetWidgetWebView = LorikeetWidgetWebView;
232
- //# sourceMappingURL=index.cjs.map
888
+ exports.isUnusableConversation = isUnusableConversation;
889
+ exports.isUnusableConversationCode = isUnusableConversationCode;
233
890
  //# sourceMappingURL=index.cjs.map