@corti/embedded-web 0.1.0 → 0.2.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.
Files changed (44) hide show
  1. package/README.md +70 -47
  2. package/dist/CortiEmbedded.d.ts +5 -4
  3. package/dist/CortiEmbedded.js +186 -103
  4. package/dist/CortiEmbedded.js.map +1 -1
  5. package/dist/bundle.js +1615 -20
  6. package/dist/corti-embedded.d.ts +17 -1
  7. package/dist/corti-embedded.js +4 -3
  8. package/dist/corti-embedded.js.map +1 -1
  9. package/dist/index.d.ts +5 -5
  10. package/dist/index.js +4 -4
  11. package/dist/index.js.map +1 -1
  12. package/dist/react/CortiEmbeddedReact.d.ts +30 -23
  13. package/dist/react/CortiEmbeddedReact.js +67 -21
  14. package/dist/react/CortiEmbeddedReact.js.map +1 -1
  15. package/dist/react/index.d.ts +2 -2
  16. package/dist/react/index.js +1 -1
  17. package/dist/react/index.js.map +1 -1
  18. package/dist/styles/base.js +1 -1
  19. package/dist/styles/base.js.map +1 -1
  20. package/dist/styles/container-styles.js +2 -2
  21. package/dist/styles/container-styles.js.map +1 -1
  22. package/dist/styles/theme.js +2 -2
  23. package/dist/styles/theme.js.map +1 -1
  24. package/dist/types/api.d.ts +3 -14
  25. package/dist/types/api.js.map +1 -1
  26. package/dist/types/payloads.d.ts +1 -4
  27. package/dist/types/payloads.js.map +1 -1
  28. package/dist/types/protocol.d.ts +0 -1
  29. package/dist/types/protocol.js.map +1 -1
  30. package/dist/utils/PostMessageHandler.d.ts +19 -70
  31. package/dist/utils/PostMessageHandler.js +98 -202
  32. package/dist/utils/PostMessageHandler.js.map +1 -1
  33. package/dist/utils/baseUrl.js +8 -8
  34. package/dist/utils/baseUrl.js.map +1 -1
  35. package/dist/utils/embedUrl.js +3 -3
  36. package/dist/utils/embedUrl.js.map +1 -1
  37. package/dist/utils/errorFormatter.js +44 -20
  38. package/dist/utils/errorFormatter.js.map +1 -1
  39. package/dist/web-bundle.js +1473 -13
  40. package/dist/web-index.d.ts +3 -3
  41. package/dist/web-index.js +3 -3
  42. package/dist/web-index.js.map +1 -1
  43. package/package.json +18 -10
  44. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -4,27 +4,41 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { html, LitElement } from 'lit';
8
- import { property } from 'lit/decorators.js';
9
- import { baseStyles } from './styles/base.js';
10
- import { containerStyles } from './styles/container-styles.js';
11
- import { validateAndNormalizeBaseURL } from './utils/baseUrl.js';
12
- import { buildEmbeddedUrl, isRealEmbeddedLoad } from './utils/embedUrl.js';
13
- import { formatError } from './utils/errorFormatter.js';
14
- import { PostMessageHandler, } from './utils/PostMessageHandler.js';
7
+ import { html, LitElement } from "lit";
8
+ import { property } from "lit/decorators.js";
9
+ import { baseStyles } from "./styles/base.js";
10
+ import { containerStyles } from "./styles/container-styles.js";
11
+ import { validateAndNormalizeBaseURL } from "./utils/baseUrl.js";
12
+ import { buildEmbeddedUrl, isRealEmbeddedLoad } from "./utils/embedUrl.js";
13
+ import { formatError } from "./utils/errorFormatter.js";
14
+ import { PostMessageHandler, } from "./utils/PostMessageHandler.js";
15
+ const IFRAME_SANDBOX_POLICY = "allow-forms allow-modals allow-scripts allow-same-origin";
15
16
  export class CortiEmbedded extends LitElement {
16
17
  constructor() {
17
18
  super(...arguments);
18
- this.visibility = 'hidden';
19
+ this.visibility = "hidden";
19
20
  this.postMessageHandler = null;
20
21
  this.normalizedBaseURL = null;
21
22
  }
23
+ // eslint-disable-next-line class-methods-use-this
24
+ getIframeAllowPolicy(normalizedBaseURL) {
25
+ const permissionTarget = normalizedBaseURL
26
+ ? new URL(normalizedBaseURL).origin
27
+ : "*";
28
+ return [
29
+ `microphone ${permissionTarget}`,
30
+ `camera ${permissionTarget}`,
31
+ `device-capture ${permissionTarget}`,
32
+ `display-capture ${permissionTarget}`,
33
+ `clipboard-write ${permissionTarget}`,
34
+ ].join("; ");
35
+ }
22
36
  connectedCallback() {
23
37
  super.connectedCallback();
24
38
  // Ensure baseURL is provided
25
39
  if (!this.baseURL) {
26
40
  this.dispatchErrorEvent({
27
- message: 'baseURL is required',
41
+ message: "baseURL is required",
28
42
  });
29
43
  return;
30
44
  }
@@ -34,9 +48,10 @@ export class CortiEmbedded extends LitElement {
34
48
  }
35
49
  catch (error) {
36
50
  this.dispatchErrorEvent({
37
- message: error.message || 'Invalid baseURL',
51
+ message: error.message || "Invalid baseURL",
38
52
  });
39
- throw error;
53
+ // Dispatch the error event rather than throwing so consumers can handle it
54
+ // via the 'error' event listener without wrapping connectedCallback in try/catch.
40
55
  }
41
56
  }
42
57
  disconnectedCallback() {
@@ -65,7 +80,7 @@ export class CortiEmbedded extends LitElement {
65
80
  }
66
81
  else {
67
82
  this.dispatchErrorEvent({
68
- message: 'No iframe or contentWindow available',
83
+ message: "No iframe or contentWindow available",
69
84
  });
70
85
  }
71
86
  }
@@ -73,18 +88,29 @@ export class CortiEmbedded extends LitElement {
73
88
  this.dispatchEvent(new CustomEvent(event, { detail: data }));
74
89
  }
75
90
  dispatchEmbeddedEvent(rawEventName, payload) {
76
- // Preserve raw DOM event passthrough for consumers listening directly.
91
+ if (rawEventName === "ready" || rawEventName === "loaded")
92
+ return;
93
+ // Pass all other events through as raw DOM events for direct listeners.
77
94
  this.dispatchPublicEvent(rawEventName, payload);
78
- this.dispatchPublicEvent('embedded-event', {
95
+ // Forward supported embedded events through the generic 'event' stream so
96
+ // consumers can observe them without subscribing to each raw event name.
97
+ this.dispatchPublicEvent("event", {
98
+ name: rawEventName,
99
+ payload,
100
+ });
101
+ // Also emit the legacy 'embedded-event' stream for backward compatibility.
102
+ // This allows existing integrations listening for 'embedded-event' to continue
103
+ // working while newer consumers can use the 'event' stream.
104
+ this.dispatchPublicEvent("embedded-event", {
79
105
  name: rawEventName,
80
106
  payload,
81
107
  });
82
108
  }
83
109
  dispatchErrorEvent(error) {
84
- this.dispatchPublicEvent('error', error);
110
+ this.dispatchPublicEvent("error", error);
85
111
  }
86
112
  isRealIframeLoad(iframe) {
87
- const src = iframe.getAttribute('src') || '';
113
+ const src = iframe.getAttribute("src") || "";
88
114
  if (!this.normalizedBaseURL)
89
115
  return false;
90
116
  return isRealEmbeddedLoad(src, this.normalizedBaseURL);
@@ -108,43 +134,38 @@ export class CortiEmbedded extends LitElement {
108
134
  }
109
135
  }
110
136
  getIframe() {
111
- return this.shadowRoot?.querySelector('iframe') || null;
137
+ return this.shadowRoot?.querySelector("iframe") || null;
112
138
  }
113
139
  updated(changedProps) {
114
140
  super.updated(changedProps);
115
- if (changedProps.has('baseURL')) {
116
- // Validate baseURL and normalize; fail fast on invalid input
141
+ if (changedProps.has("baseURL")) {
142
+ // Tear down the existing handler; the new one is created in handleIframeLoad
143
+ if (this.postMessageHandler) {
144
+ this.postMessageHandler.destroy();
145
+ this.postMessageHandler = null;
146
+ }
147
+ // Validate the new URL
117
148
  try {
118
149
  this.normalizedBaseURL = validateAndNormalizeBaseURL(this.baseURL);
119
150
  }
120
151
  catch (error) {
121
- // Tear down and clear iframe to avoid keeping an old origin active
122
- if (this.postMessageHandler) {
123
- this.postMessageHandler.destroy();
124
- this.postMessageHandler = null;
125
- }
152
+ this.normalizedBaseURL = null;
126
153
  const iframe = this.getIframe();
127
154
  if (iframe) {
128
- iframe.setAttribute('src', 'about:blank');
155
+ iframe.setAttribute("src", "about:blank");
129
156
  }
130
157
  this.dispatchErrorEvent({
131
- message: error.message || 'Invalid baseURL',
158
+ message: error.message || "Invalid baseURL",
132
159
  });
133
160
  return;
134
161
  }
135
- // Tear down existing handler and re-point iframe to new URL
136
- if (this.postMessageHandler) {
137
- this.postMessageHandler.destroy();
138
- this.postMessageHandler = null;
139
- }
162
+ // Update the iframe to the new URL
140
163
  const iframe = this.getIframe();
141
164
  if (iframe) {
142
- const expected = this.normalizedBaseURL
143
- ? buildEmbeddedUrl(this.normalizedBaseURL)
144
- : '';
145
- if (iframe.getAttribute('src') !== expected) {
146
- iframe.setAttribute('src', expected);
147
- iframe.setAttribute('allow', `microphone ${expected}; camera ${expected}; device-capture ${expected}; display-capture ${expected}`);
165
+ const expected = buildEmbeddedUrl(this.normalizedBaseURL);
166
+ iframe.setAttribute("allow", this.getIframeAllowPolicy(expected));
167
+ if (iframe.getAttribute("src") !== expected) {
168
+ iframe.setAttribute("src", expected);
148
169
  }
149
170
  }
150
171
  }
@@ -157,7 +178,7 @@ export class CortiEmbedded extends LitElement {
157
178
  */
158
179
  async auth(credentials) {
159
180
  if (!this.postMessageHandler) {
160
- throw new Error('Component not ready');
181
+ throw new Error("Component not ready");
161
182
  }
162
183
  try {
163
184
  const payload = {
@@ -168,18 +189,24 @@ export class CortiEmbedded extends LitElement {
168
189
  refresh_expires_in: credentials.refresh_expires_in,
169
190
  refresh_token: credentials.refresh_token,
170
191
  id_token: credentials.id_token,
171
- 'not-before-policy': credentials['not-before-policy'],
192
+ "not-before-policy": credentials["not-before-policy"],
172
193
  session_state: credentials.session_state,
173
194
  scope: credentials.scope,
174
195
  profile: credentials.profile,
175
- mode: credentials.mode,
176
196
  };
177
- const user = await this.postMessageHandler.auth(payload);
178
- return user;
197
+ const response = await this.postMessageHandler.postMessage({
198
+ type: "CORTI_EMBEDDED",
199
+ version: "v1",
200
+ action: "auth",
201
+ payload,
202
+ });
203
+ if (response.success && response.payload) {
204
+ return response.payload.user;
205
+ }
206
+ throw new Error(response.error);
179
207
  }
180
208
  catch (error) {
181
- const formattedError = formatError(error, 'Authentication failed');
182
- this.dispatchErrorEvent(formattedError);
209
+ const formattedError = formatError(error, "Authentication failed");
183
210
  throw new Error(JSON.stringify(formattedError));
184
211
  }
185
212
  }
@@ -190,18 +217,26 @@ export class CortiEmbedded extends LitElement {
190
217
  */
191
218
  async createInteraction(encounter) {
192
219
  if (!this.postMessageHandler) {
193
- throw new Error('Component not ready');
220
+ throw new Error("Component not ready");
194
221
  }
195
222
  try {
196
- const response = await this.postMessageHandler.createInteraction(encounter);
197
- return {
198
- id: response.id,
199
- createdAt: response.createdAt,
200
- };
223
+ const response = await this.postMessageHandler.postMessage({
224
+ type: "CORTI_EMBEDDED",
225
+ version: "v1",
226
+ action: "createInteraction",
227
+ payload: encounter,
228
+ });
229
+ if (response.success && response.payload) {
230
+ const result = response.payload;
231
+ return {
232
+ id: result.id,
233
+ createdAt: result.createdAt,
234
+ };
235
+ }
236
+ throw new Error(response.error);
201
237
  }
202
238
  catch (error) {
203
- const formattedError = formatError(error, 'Failed to create interaction');
204
- this.dispatchErrorEvent(formattedError);
239
+ const formattedError = formatError(error, "Failed to create interaction");
205
240
  throw new Error(JSON.stringify(formattedError));
206
241
  }
207
242
  }
@@ -212,7 +247,7 @@ export class CortiEmbedded extends LitElement {
212
247
  */
213
248
  async configureSession(config) {
214
249
  if (!this.postMessageHandler) {
215
- throw new Error('Component not ready');
250
+ throw new Error("Component not ready");
216
251
  }
217
252
  try {
218
253
  const payload = {
@@ -221,11 +256,15 @@ export class CortiEmbedded extends LitElement {
221
256
  defaultTemplateKey: config.defaultTemplateKey,
222
257
  defaultMode: config.defaultMode,
223
258
  };
224
- await this.postMessageHandler.configureSession(payload);
259
+ await this.postMessageHandler.postMessage({
260
+ type: "CORTI_EMBEDDED",
261
+ version: "v1",
262
+ action: "configureSession",
263
+ payload,
264
+ });
225
265
  }
226
266
  catch (error) {
227
- const formattedError = formatError(error, 'Failed to configure session');
228
- this.dispatchErrorEvent(formattedError);
267
+ const formattedError = formatError(error, "Failed to configure session");
229
268
  throw new Error(JSON.stringify(formattedError));
230
269
  }
231
270
  }
@@ -236,15 +275,19 @@ export class CortiEmbedded extends LitElement {
236
275
  */
237
276
  async addFacts(facts) {
238
277
  if (!this.postMessageHandler) {
239
- throw new Error('Component not ready');
278
+ throw new Error("Component not ready");
240
279
  }
241
280
  try {
242
281
  const payload = { facts };
243
- await this.postMessageHandler.addFacts(payload);
282
+ await this.postMessageHandler.postMessage({
283
+ type: "CORTI_EMBEDDED",
284
+ version: "v1",
285
+ action: "addFacts",
286
+ payload,
287
+ });
244
288
  }
245
289
  catch (error) {
246
- const formattedError = formatError(error, 'Failed to add facts');
247
- this.dispatchErrorEvent(formattedError);
290
+ const formattedError = formatError(error, "Failed to add facts");
248
291
  throw new Error(JSON.stringify(formattedError));
249
292
  }
250
293
  }
@@ -255,15 +298,19 @@ export class CortiEmbedded extends LitElement {
255
298
  */
256
299
  async navigate(path) {
257
300
  if (!this.postMessageHandler) {
258
- throw new Error('Component not ready');
301
+ throw new Error("Component not ready");
259
302
  }
260
303
  try {
261
304
  const payload = { path };
262
- await this.postMessageHandler.navigate(payload);
305
+ await this.postMessageHandler.postMessage({
306
+ type: "CORTI_EMBEDDED",
307
+ version: "v1",
308
+ action: "navigate",
309
+ payload,
310
+ });
263
311
  }
264
312
  catch (error) {
265
- const formattedError = formatError(error, 'Failed to navigate');
266
- this.dispatchErrorEvent(formattedError);
313
+ const formattedError = formatError(error, "Failed to navigate");
267
314
  throw new Error(JSON.stringify(formattedError));
268
315
  }
269
316
  }
@@ -273,14 +320,18 @@ export class CortiEmbedded extends LitElement {
273
320
  */
274
321
  async startRecording() {
275
322
  if (!this.postMessageHandler) {
276
- throw new Error('Component not ready');
323
+ throw new Error("Component not ready");
277
324
  }
278
325
  try {
279
- await this.postMessageHandler.startRecording();
326
+ await this.postMessageHandler.postMessage({
327
+ type: "CORTI_EMBEDDED",
328
+ version: "v1",
329
+ action: "startRecording",
330
+ payload: {},
331
+ });
280
332
  }
281
333
  catch (error) {
282
- const formattedError = formatError(error, 'Failed to start recording');
283
- this.dispatchErrorEvent(formattedError);
334
+ const formattedError = formatError(error, "Failed to start recording");
284
335
  throw new Error(JSON.stringify(formattedError));
285
336
  }
286
337
  }
@@ -290,14 +341,18 @@ export class CortiEmbedded extends LitElement {
290
341
  */
291
342
  async stopRecording() {
292
343
  if (!this.postMessageHandler) {
293
- throw new Error('Component not ready');
344
+ throw new Error("Component not ready");
294
345
  }
295
346
  try {
296
- await this.postMessageHandler.stopRecording();
347
+ await this.postMessageHandler.postMessage({
348
+ type: "CORTI_EMBEDDED",
349
+ version: "v1",
350
+ action: "stopRecording",
351
+ payload: {},
352
+ });
297
353
  }
298
354
  catch (error) {
299
- const formattedError = formatError(error, 'Failed to stop recording');
300
- this.dispatchErrorEvent(formattedError);
355
+ const formattedError = formatError(error, "Failed to stop recording");
301
356
  throw new Error(JSON.stringify(formattedError));
302
357
  }
303
358
  }
@@ -312,16 +367,24 @@ export class CortiEmbedded extends LitElement {
312
367
  isAuthenticated: false,
313
368
  user: undefined,
314
369
  },
315
- currentUrl: '',
370
+ currentUrl: "",
316
371
  interaction: null,
317
372
  };
318
373
  }
319
374
  try {
320
- return await this.postMessageHandler.getStatus();
375
+ const response = await this.postMessageHandler.postMessage({
376
+ type: "CORTI_EMBEDDED",
377
+ version: "v1",
378
+ action: "getStatus",
379
+ payload: {},
380
+ });
381
+ if (response.success && response.payload) {
382
+ return response.payload;
383
+ }
384
+ throw new Error(response.error);
321
385
  }
322
386
  catch (error) {
323
- const formattedError = formatError(error, 'Failed to get status');
324
- this.dispatchErrorEvent(formattedError);
387
+ const formattedError = formatError(error, "Failed to get status");
325
388
  throw new Error(JSON.stringify(formattedError));
326
389
  }
327
390
  }
@@ -332,14 +395,22 @@ export class CortiEmbedded extends LitElement {
332
395
  */
333
396
  async configure(config) {
334
397
  if (!this.postMessageHandler) {
335
- throw new Error('Component not ready');
398
+ throw new Error("Component not ready");
336
399
  }
337
400
  try {
338
- return await this.postMessageHandler.configure(config);
401
+ const response = await this.postMessageHandler.postMessage({
402
+ type: "CORTI_EMBEDDED",
403
+ version: "v1",
404
+ action: "configure",
405
+ payload: config,
406
+ });
407
+ if (response.success && response.payload) {
408
+ return response.payload;
409
+ }
410
+ throw new Error(response.error);
339
411
  }
340
412
  catch (error) {
341
- const formattedError = formatError(error, 'Failed to configure component');
342
- this.dispatchErrorEvent(formattedError);
413
+ const formattedError = formatError(error, "Failed to configure component");
343
414
  throw new Error(JSON.stringify(formattedError));
344
415
  }
345
416
  }
@@ -350,17 +421,21 @@ export class CortiEmbedded extends LitElement {
350
421
  */
351
422
  async setCredentials(credentials) {
352
423
  if (!this.postMessageHandler) {
353
- throw new Error('Component not ready');
424
+ throw new Error("Component not ready");
354
425
  }
355
426
  try {
356
427
  if (!credentials.password) {
357
- throw new Error('Password is required');
428
+ throw new Error("Password is required");
358
429
  }
359
- await this.postMessageHandler.setCredentials(credentials);
430
+ await this.postMessageHandler.postMessage({
431
+ type: "CORTI_EMBEDDED",
432
+ version: "v1",
433
+ action: "setCredentials",
434
+ payload: credentials,
435
+ });
360
436
  }
361
437
  catch (error) {
362
- const formattedError = formatError(error, 'Failed to set credentials');
363
- this.dispatchErrorEvent(formattedError);
438
+ const formattedError = formatError(error, "Failed to set credentials");
364
439
  throw new Error(JSON.stringify(formattedError));
365
440
  }
366
441
  }
@@ -368,28 +443,34 @@ export class CortiEmbedded extends LitElement {
368
443
  * Show the embedded UI
369
444
  */
370
445
  show() {
371
- this.visibility = 'visible';
446
+ this.visibility = "visible";
372
447
  }
373
448
  /**
374
449
  * Hide the embedded UI
375
450
  */
376
451
  hide() {
377
- this.visibility = 'hidden';
452
+ this.visibility = "hidden";
378
453
  }
379
454
  /**
380
455
  * Get templates
381
456
  */
382
457
  async getTemplates() {
383
458
  if (!this.postMessageHandler) {
384
- throw new Error('Component not ready');
459
+ throw new Error("Component not ready");
385
460
  }
386
461
  try {
387
- const response = await this.postMessageHandler.getTemplates();
388
- return response;
462
+ const response = await this.postMessageHandler.postMessage({
463
+ type: "CORTI_EMBEDDED",
464
+ version: "v1",
465
+ action: "getTemplates",
466
+ });
467
+ if (response.success && response.payload) {
468
+ return response.payload;
469
+ }
470
+ throw new Error(response.error);
389
471
  }
390
472
  catch (error) {
391
- const formattedError = formatError(error, 'Failed to get templates');
392
- this.dispatchErrorEvent(formattedError);
473
+ const formattedError = formatError(error, "Failed to get templates");
393
474
  throw new Error(JSON.stringify(formattedError));
394
475
  }
395
476
  }
@@ -401,7 +482,7 @@ export class CortiEmbedded extends LitElement {
401
482
  getDebugStatus() {
402
483
  const iframe = this.getIframe();
403
484
  return {
404
- ready: iframe?.contentDocument?.readyState === 'complete',
485
+ ready: iframe?.contentDocument?.readyState === "complete",
405
486
  iframeExists: !!iframe,
406
487
  iframeSrc: iframe?.src,
407
488
  iframeContentWindow: !!iframe?.contentWindow,
@@ -413,21 +494,23 @@ export class CortiEmbedded extends LitElement {
413
494
  };
414
495
  }
415
496
  render() {
416
- // Don't render if baseURL is not provided
417
- if (!this.baseURL) {
418
- return html `<div>baseURL is required</div>`;
497
+ // Use the pre-validated normalizedBaseURL so render() never throws.
498
+ // normalizedBaseURL is set in connectedCallback (before first render) and
499
+ // kept up to date by updated() on each baseURL change.
500
+ if (!this.normalizedBaseURL) {
501
+ return html ``;
419
502
  }
420
503
  return html `
421
504
  <iframe
422
- src=${buildEmbeddedUrl(validateAndNormalizeBaseURL(this.baseURL))}
505
+ src=${buildEmbeddedUrl(this.normalizedBaseURL)}
423
506
  title="Corti Embedded UI"
424
- sandbox=${'allow-forms allow-modals allow-scripts allow-same-origin'}
425
- allow="microphone *; camera *; device-capture *; display-capture *"
507
+ sandbox=${IFRAME_SANDBOX_POLICY}
508
+ allow=${this.getIframeAllowPolicy(this.normalizedBaseURL)}
426
509
  @load=${(event) => this.handleIframeLoad(event)}
427
510
  @unload=${() => this.postMessageHandler?.destroy()}
428
- style=${this.visibility === 'hidden'
429
- ? 'display: none;'
430
- : 'display: block;'}
511
+ style=${this.visibility === "hidden"
512
+ ? "display: none;"
513
+ : "display: block;"}
431
514
  ></iframe>
432
515
  `;
433
516
  }