@financial-times/custom-code-component 2.0.4 → 2.0.6

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/README.md CHANGED
@@ -58,4 +58,37 @@ export default (shadowRoot, props, ...children) => {
58
58
  * `data-asset-type="custom-code-component"`
59
59
  * Part of spec.
60
60
  * <any other attributes>
61
- * All remaining attributes get passed as an object named `props` to render().
61
+ * All remaining attributes get passed as an object named `props` to render().
62
+
63
+ ## client-metrics-adaptor
64
+
65
+ This module is an adaptor of the [dotcom-reliability-kit client metrics package](https://github.com/Financial-Times/dotcom-reliability-kit/tree/main/packages/client-metrics-web) to help integrating RUM metrics for the Custom Code Components.
66
+
67
+ ### Requirements
68
+
69
+ To use it, you are required to have pre-installed the client-metrics-web package.
70
+
71
+ ### Options
72
+
73
+ - **enrichEventCb** (optional): function to enrich the event payload before is sent to AWS RUM
74
+ - **shouldIgnoreEventCb** (optional): function that checks if the event should be sent or not to AWS RUM
75
+
76
+ ### Usage:
77
+
78
+ Initialise the ClientMetrics Adaptor in your project to start listening to CCC Events and dispatch automatically AWS RUM events
79
+
80
+
81
+ #### example.jsx
82
+
83
+ ```js
84
+ import { MetricsClient } from '@dotcom-reliability-kit/client-metrics-web';
85
+ import * as ClientMetricsAdaptor from '@financial-times/custom-code-component';
86
+
87
+ const client = new MetricsClient({
88
+ // Options go here
89
+ });
90
+
91
+ ClientMetricsAdaptor.init(client, {
92
+ enrichEventCb: enrichCCCEventWithHomepageContext,
93
+ shouldIgnoreEventCb: shouldIgnoreCCCEvent,
94
+ });
@@ -0,0 +1,107 @@
1
+ declare class CCCEvent extends Event {
2
+ component: ComponentPath;
3
+ source?: string;
4
+ static eventType: string;
5
+ constructor(eventType: string | undefined, detail: {
6
+ component: ComponentPath;
7
+ source?: string;
8
+ }, opts?: EventInit);
9
+ }
10
+
11
+ declare class ComponentPath {
12
+ org: string;
13
+ repo: string;
14
+ name: string;
15
+ versionRange: string;
16
+ constructor(path?: ComponentPathType | string);
17
+ set path(path: ComponentPathType | string);
18
+ get path(): string;
19
+ get isValid(): boolean;
20
+ toString(): string;
21
+ static fromString(p?: string | null, v?: string | null): ComponentPath;
22
+ }
23
+
24
+ declare type ComponentPathType = {
25
+ org: string;
26
+ repo: string;
27
+ name: string;
28
+ versionRange: string;
29
+ };
30
+
31
+ export declare const DEFAULT_VALUE = "uknown";
32
+
33
+ export declare const EVENT_TYPES: Record<string, EventType>;
34
+
35
+ declare type EventType = {
36
+ namespace: string;
37
+ payloadFn: (event: GenericCCCEvent) => Record<string, unknown>;
38
+ };
39
+
40
+ export declare type GenericCCCEvent = CCCEvent | ErrorEvent;
41
+
42
+ export declare function getComponentProps(eventComponentProp: ComponentPathType): {
43
+ name: string;
44
+ org: string;
45
+ repo: string;
46
+ versionRange: string;
47
+ };
48
+
49
+ export declare function getEnrichEventProps(event: GenericCCCEvent): Record<string, unknown>;
50
+
51
+ export declare function getErrorPayload(event: GenericCCCEvent): Record<string, unknown>;
52
+
53
+ export declare function getPendingEventsQueue(): PendingEvent[];
54
+
55
+ export declare function getSuccessPayload(event: GenericCCCEvent): Record<string, unknown>;
56
+
57
+ /**
58
+ * Initialises the Adaptor with the provided configuration and starts listening to CCC events.
59
+ * If there's any pending event, it'll send them to the Metrics server
60
+ * @param _metricsClient MetricClient instance
61
+ * @param _options Options to configure adaptor
62
+ */
63
+ export declare function init(_metricsClient: MetricsClient, _options?: Options): void;
64
+
65
+ export declare const MAX_PENDING_EVENTS = 50;
66
+
67
+ export declare type MetricsClient = {
68
+ recordEvent: (namespace: string, payload?: Record<string, unknown>) => void;
69
+ };
70
+
71
+ export declare function onEventReceived(_event: Event): void;
72
+
73
+ declare type Options = {
74
+ enrichEventCb?: (event: GenericCCCEvent) => Record<string, unknown> | void;
75
+ shouldIgnoreEventCb?: (event: GenericCCCEvent) => boolean;
76
+ };
77
+
78
+ declare type PendingEvent = {
79
+ time: number;
80
+ event: GenericCCCEvent;
81
+ };
82
+
83
+ export declare function processEvent(event: GenericCCCEvent): void;
84
+
85
+ /**
86
+ * Sends all pending events waiting to be processed
87
+ *
88
+ * Notes:
89
+ * 1. All these events will be registered in RUM with the same time
90
+ * (at this moment the MetricsClient API doesn't allow to override it)
91
+ * 2. It's safe to process them in bulk since ClientMetrics will send them in bulk
92
+ * (all in 1 request)
93
+ */
94
+ export declare function sendPendingEvents(): void;
95
+
96
+ /**
97
+ * Start listening to CCC events
98
+ * In case there's no MetricsClient registered, it'll store them until they can be sent
99
+ */
100
+ export declare function startListeners(): void;
101
+
102
+ /**
103
+ * Stop listening CCC events
104
+ */
105
+ export declare function stopListeners(): void;
106
+
107
+ export { }
@@ -0,0 +1,98 @@
1
+ const f = Object.freeze({
2
+ "ccc:ready": {
3
+ namespace: "ccc.success",
4
+ payloadFn: y
5
+ },
6
+ "ccc:error": {
7
+ namespace: "ccc.failure",
8
+ payloadFn: w
9
+ }
10
+ }), i = "uknown", t = [], g = 50;
11
+ let a, n, s = !1;
12
+ function h() {
13
+ s || (window.addEventListener("ccc:error", u), window.addEventListener("ccc:ready", u)), s = !0;
14
+ }
15
+ function b() {
16
+ s && (window.removeEventListener("ccc:error", u), window.removeEventListener("ccc:ready", u)), s = !1, t.length = 0, a = null, n = {};
17
+ }
18
+ function v(e, c) {
19
+ if (!e || typeof e.recordEvent != "function") {
20
+ console.warn("CCC Can't initialise MetricsClientAdaptor");
21
+ return;
22
+ }
23
+ a = e, n = {}, typeof (c == null ? void 0 : c.shouldIgnoreEventCb) == "function" && (n.shouldIgnoreEventCb = c.shouldIgnoreEventCb), typeof (c == null ? void 0 : c.enrichEventCb) == "function" && (n.enrichEventCb = c.enrichEventCb), o(), h();
24
+ }
25
+ function d(e) {
26
+ var c;
27
+ try {
28
+ const r = ((c = n == null ? void 0 : n.enrichEventCb) == null ? void 0 : c.call(n, e)) || {};
29
+ if (typeof r != "object" || Array.isArray(r))
30
+ throw new TypeError("Enrich event callback returned invalid value");
31
+ return r;
32
+ } catch (r) {
33
+ return console.error("CCC RUM event couldn't be enriched", r), {};
34
+ }
35
+ }
36
+ function l(e) {
37
+ return {
38
+ name: (e == null ? void 0 : e.name) || i,
39
+ org: (e == null ? void 0 : e.org) || i,
40
+ repo: (e == null ? void 0 : e.repo) || i,
41
+ versionRange: (e == null ? void 0 : e.versionRange) || i
42
+ };
43
+ }
44
+ function y(e) {
45
+ return {
46
+ ...d(e),
47
+ component: l(e.component)
48
+ };
49
+ }
50
+ function w(e) {
51
+ const c = e.error, r = c == null ? void 0 : c.name;
52
+ return {
53
+ ...d(e),
54
+ component: l(c == null ? void 0 : c.component),
55
+ error: r || i
56
+ };
57
+ }
58
+ function E(e) {
59
+ if (n.shouldIgnoreEventCb && n.shouldIgnoreEventCb(e))
60
+ return;
61
+ if (!a) {
62
+ console.warn("CCC Couldn't process event: ClientMetrics not initialised");
63
+ return;
64
+ }
65
+ const c = f[e.type];
66
+ if (!c) {
67
+ console.warn("CCC event.type not registered");
68
+ return;
69
+ }
70
+ a.recordEvent(c.namespace, c.payloadFn(e));
71
+ }
72
+ function u(e) {
73
+ const c = e;
74
+ a ? E(c) : (t.push({ event: c, time: Date.now() }), t.splice(0, t.length - g));
75
+ }
76
+ function o() {
77
+ t.forEach(({ event: e }) => E(e)), t.length = 0;
78
+ }
79
+ function L() {
80
+ return t;
81
+ }
82
+ export {
83
+ i as DEFAULT_VALUE,
84
+ f as EVENT_TYPES,
85
+ g as MAX_PENDING_EVENTS,
86
+ l as getComponentProps,
87
+ d as getEnrichEventProps,
88
+ w as getErrorPayload,
89
+ L as getPendingEventsQueue,
90
+ y as getSuccessPayload,
91
+ v as init,
92
+ u as onEventReceived,
93
+ E as processEvent,
94
+ o as sendPendingEvents,
95
+ h as startListeners,
96
+ b as stopListeners
97
+ };
98
+ //# sourceMappingURL=client-metrics-adaptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-metrics-adaptor.js","sources":["../src/client-metrics-adaptor.ts"],"sourcesContent":["// MetricsClient type from https://github.com/Financial-Times/dotcom-reliability-kit/tree/main/packages/client-metrics-web\n// We intentionally copied this type here to avoid having version conflicts between this module and it's client\nimport type { ComponentPathType } from './path'\nimport type { CCCEvent } from './events'\n\nexport type MetricsClient = {\n recordEvent: (namespace: string, payload?: Record<string, unknown>) => void\n};\n\nexport type GenericCCCEvent = CCCEvent | ErrorEvent;\n\ntype Options = {\n enrichEventCb?: (event: GenericCCCEvent) => Record<string, unknown> | void\n shouldIgnoreEventCb?: (event: GenericCCCEvent) => boolean\n};\n\ntype PendingEvent = {\n time: number\n event: GenericCCCEvent\n};\n\ntype EventType = {\n namespace: string\n payloadFn: (event: GenericCCCEvent) => Record<string, unknown>\n};\n\nexport const EVENT_TYPES: Record<string, EventType> = Object.freeze({\n 'ccc:ready': {\n namespace: 'ccc.success',\n payloadFn: getSuccessPayload,\n },\n 'ccc:error': {\n namespace: 'ccc.failure',\n payloadFn: getErrorPayload,\n },\n});\n\nexport const DEFAULT_VALUE = 'uknown';\n\nconst pendingEvents: PendingEvent[] = [];\nexport const MAX_PENDING_EVENTS = 50;\nlet metricsClient: MetricsClient | null;\nlet options: Options;\nlet isRunning = false;\n\n/**\n * Start listening to CCC events\n * In case there's no MetricsClient registered, it'll store them until they can be sent\n */\nexport function startListeners() {\n if (!isRunning) {\n window.addEventListener('ccc:error', onEventReceived);\n window.addEventListener('ccc:ready', onEventReceived);\n }\n isRunning = true;\n}\n\n/**\n * Stop listening CCC events\n */\nexport function stopListeners() {\n if (isRunning) {\n window.removeEventListener('ccc:error', onEventReceived);\n window.removeEventListener('ccc:ready', onEventReceived);\n }\n\n isRunning = false;\n pendingEvents.length = 0;\n metricsClient = null;\n options = {};\n}\n\n/**\n * Initialises the Adaptor with the provided configuration and starts listening to CCC events.\n * If there's any pending event, it'll send them to the Metrics server\n * @param _metricsClient MetricClient instance\n * @param _options Options to configure adaptor\n */\nexport function init(_metricsClient: MetricsClient, _options?: Options) {\n if (!_metricsClient || typeof _metricsClient.recordEvent !== 'function') {\n console.warn(\"CCC Can't initialise MetricsClientAdaptor\");\n return;\n }\n\n metricsClient = _metricsClient;\n\n options = {};\n if (typeof _options?.shouldIgnoreEventCb === 'function') {\n options.shouldIgnoreEventCb = _options.shouldIgnoreEventCb;\n }\n if (typeof _options?.enrichEventCb === 'function') {\n options.enrichEventCb = _options.enrichEventCb;\n }\n\n sendPendingEvents();\n startListeners();\n}\n\nexport function getEnrichEventProps(event: GenericCCCEvent) {\n try {\n const props = options?.enrichEventCb?.(event) || {};\n if (typeof props !== 'object' || Array.isArray(props)) {\n throw new TypeError('Enrich event callback returned invalid value');\n }\n return props;\n } catch (error) {\n console.error(\"CCC RUM event couldn't be enriched\", error);\n return {};\n }\n}\n\nexport function getComponentProps(eventComponentProp: ComponentPathType) {\n return {\n name: eventComponentProp?.name || DEFAULT_VALUE,\n org: eventComponentProp?.org || DEFAULT_VALUE,\n repo: eventComponentProp?.repo || DEFAULT_VALUE,\n versionRange: eventComponentProp?.versionRange || DEFAULT_VALUE,\n };\n}\n\nexport function getSuccessPayload(event: GenericCCCEvent): Record<string, unknown> {\n return {\n ...getEnrichEventProps(event),\n component: getComponentProps((event as CCCEvent).component),\n };\n}\n\nexport function getErrorPayload(event: GenericCCCEvent): Record<string, unknown> {\n const error = (event as ErrorEvent).error;\n const errorName = error?.name;\n\n return {\n ...getEnrichEventProps(event),\n component: getComponentProps(error?.component),\n error: errorName || DEFAULT_VALUE,\n };\n}\n\nexport function processEvent(event: GenericCCCEvent) {\n // Stop processing the event if we should ignore it\n if (options.shouldIgnoreEventCb && options.shouldIgnoreEventCb(event)) {\n return;\n }\n\n if (!metricsClient) {\n console.warn(\"CCC Couldn't process event: ClientMetrics not initialised\");\n return;\n }\n\n const eventType = EVENT_TYPES[event.type];\n if (!eventType) {\n console.warn('CCC event.type not registered');\n return;\n }\n\n metricsClient.recordEvent(eventType.namespace, eventType.payloadFn(event));\n}\n\nexport function onEventReceived(_event: Event) {\n // The Event object dispatched from the CCC has extra custom props\n const event = _event as GenericCCCEvent\n\n if (metricsClient) {\n processEvent(event);\n } else {\n pendingEvents.push({ event: event, time: Date.now() });\n // keep only the last MAX_PENDING_EVENTS events (FIFO)\n pendingEvents.splice(0, pendingEvents.length - MAX_PENDING_EVENTS);\n }\n}\n\n/**\n * Sends all pending events waiting to be processed\n *\n * Notes:\n * 1. All these events will be registered in RUM with the same time\n * (at this moment the MetricsClient API doesn't allow to override it)\n * 2. It's safe to process them in bulk since ClientMetrics will send them in bulk\n * (all in 1 request)\n */\nexport function sendPendingEvents() {\n pendingEvents.forEach(({ event }) => processEvent(event));\n pendingEvents.length = 0;\n}\n\nexport function getPendingEventsQueue() {\n return pendingEvents;\n}\n"],"names":["EVENT_TYPES","getSuccessPayload","getErrorPayload","DEFAULT_VALUE","pendingEvents","MAX_PENDING_EVENTS","metricsClient","options","isRunning","startListeners","onEventReceived","stopListeners","init","_metricsClient","_options","sendPendingEvents","getEnrichEventProps","event","_a","props","error","getComponentProps","eventComponentProp","errorName","processEvent","eventType","_event","getPendingEventsQueue"],"mappings":"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"}
@@ -53,13 +53,12 @@ declare class FTCustomCodeComponent extends HTMLElement {
53
53
  disconnectedCallback(): void;
54
54
  channel: MessageChannel;
55
55
  onmessage(): void;
56
- onunmount(root?: any): void;
56
+ onunmount(e: Error): void;
57
57
  onready(app: Promise<void>): Promise<void>;
58
58
  postMessage(event: any): void;
59
59
  mount(prerendered?: ShadowRoot | null): Promise<void>;
60
60
  unmount(e: Error): void;
61
- load(): Promise<(shadowRoot: ShadowRoot, props: any, ssr?: boolean) => {
62
- unmount: (root: any) => void;
61
+ load(): Promise<(shadowRoot: ShadowRoot, props: any, ssr?: boolean, onErrorCallback?: Function) => {
63
62
  onmessage: {
64
63
  (...data: any[]): void;
65
64
  (message?: any, ...optionalParams: any[]): void;
@@ -28,7 +28,7 @@ l.prototype.on = function(o, t, e, n) {
28
28
  throw new TypeError("Invalid event type: " + o);
29
29
  if (typeof t == "function" && (n = e, e = t, t = null), n === void 0 && (n = this.captureForType(o)), typeof e != "function")
30
30
  throw new TypeError("Handler must be a type of Function");
31
- return r = this.rootElement, s = this.listenerMap[n ? 1 : 0], s[o] || (r && r.addEventListener(o, this.handle, n), s[o] = []), t ? /^[a-z]+$/i.test(t) ? (c = t, i = x) : /^#[a-z0-9\-_]+$/i.test(t) ? (c = t.slice(1), i = U) : (c = t, i = Element.prototype.matches) : (c = null, i = P.bind(this)), s[o].push({
31
+ return r = this.rootElement, s = this.listenerMap[n ? 1 : 0], s[o] || (r && r.addEventListener(o, this.handle, n), s[o] = []), t ? /^[a-z]+$/i.test(t) ? (c = t, i = P) : /^#[a-z0-9\-_]+$/i.test(t) ? (c = t.slice(1), i = I) : (c = t, i = Element.prototype.matches) : (c = null, i = U.bind(this)), s[o].push({
32
32
  selector: t,
33
33
  handler: e,
34
34
  matcher: i,
@@ -54,8 +54,8 @@ l.prototype.handle = function(o) {
54
54
  let t, e;
55
55
  const n = o.type;
56
56
  let r, s, i, c, a = [], h;
57
- const m = "ftLabsDelegateIgnore";
58
- if (o[m] === !0)
57
+ const p = "ftLabsDelegateIgnore";
58
+ if (o[p] === !0)
59
59
  return;
60
60
  switch (h = o.target, h.nodeType === 3 && (h = h.parentNode), h.correspondingUseElement && (h = h.correspondingUseElement), r = this.rootElement, s = o.eventPhase || (o.target !== o.currentTarget ? 3 : 2), s) {
61
61
  case 1:
@@ -68,17 +68,17 @@ l.prototype.handle = function(o) {
68
68
  a = this.listenerMap[0][n];
69
69
  break;
70
70
  }
71
- let p = [];
71
+ let u = [];
72
72
  for (e = a.length; h && e; ) {
73
73
  for (t = 0; t < e && (i = a[t], !!i); t++)
74
- h.tagName && ["button", "input", "select", "textarea"].indexOf(h.tagName.toLowerCase()) > -1 && h.hasAttribute("disabled") ? p = [] : i.matcher.call(h, i.matcherParam, h) && p.push([o, h, i]);
74
+ h.tagName && ["button", "input", "select", "textarea"].indexOf(h.tagName.toLowerCase()) > -1 && h.hasAttribute("disabled") ? u = [] : i.matcher.call(h, i.matcherParam, h) && u.push([o, h, i]);
75
75
  if (h === r || (e = a.length, h = h.parentElement || h.parentNode, h instanceof HTMLDocument))
76
76
  break;
77
77
  }
78
78
  let L;
79
- for (t = 0; t < p.length; t++)
80
- if (!(this._removedListeners.indexOf(p[t][2]) > -1) && (c = this.fire.apply(this, p[t]), c === !1)) {
81
- p[t][0][m] = !0, p[t][0].preventDefault(), L = !1;
79
+ for (t = 0; t < u.length; t++)
80
+ if (!(this._removedListeners.indexOf(u[t][2]) > -1) && (c = this.fire.apply(this, u[t]), c === !1)) {
81
+ u[t][0][p] = !0, u[t][0].preventDefault(), L = !1;
82
82
  break;
83
83
  }
84
84
  return L;
@@ -86,10 +86,10 @@ l.prototype.handle = function(o) {
86
86
  l.prototype.fire = function(o, t, e) {
87
87
  return e.handler.call(t, o, t);
88
88
  };
89
- function x(o, t) {
89
+ function P(o, t) {
90
90
  return o.toLowerCase() === t.tagName.toLowerCase();
91
91
  }
92
- function P(o, t) {
92
+ function U(o, t) {
93
93
  return this.rootElement === window ? (
94
94
  // Match the outer document (dispatched from document)
95
95
  t === document || // The <html> element (dispatched from document.body or document.documentElement)
@@ -97,20 +97,20 @@ function P(o, t) {
97
97
  t === window
98
98
  ) : this.rootElement === t;
99
99
  }
100
- function U(o, t) {
100
+ function I(o, t) {
101
101
  return o === t.id;
102
102
  }
103
103
  l.prototype.destroy = function() {
104
104
  this.off(), this.root();
105
105
  };
106
- function $(o) {
106
+ function C(o) {
107
107
  return typeof o == "string" ? o.trim() : o;
108
108
  }
109
- function C(o, t) {
109
+ function N(o, t) {
110
110
  for (const e in o)
111
111
  t[e] ? console.warn(`You can't set a custom property called ${e}`) : t[e] = o[e];
112
112
  }
113
- const u = Object.freeze({
113
+ const m = Object.freeze({
114
114
  DEBUG: 0,
115
115
  INFO: 1,
116
116
  WARN: 2,
@@ -118,51 +118,51 @@ const u = Object.freeze({
118
118
  TEST: 4,
119
119
  DEFAULT: 2
120
120
  });
121
- function I(o) {
121
+ function D(o) {
122
122
  const t = o == null ? void 0 : o.toLowerCase();
123
- return t === "debug" ? u.DEBUG : t === "info" ? u.INFO : t === "warn" ? u.WARN : t === "error" ? u.ERROR : t === "test" ? u.TEST : u.DEFAULT;
123
+ return t === "debug" ? m.DEBUG : t === "info" ? m.INFO : t === "warn" ? m.WARN : t === "error" ? m.ERROR : t === "test" ? m.TEST : m.DEFAULT;
124
124
  }
125
- class N {
126
- constructor({ level: t = u.DEFAULT } = {
127
- level: u.DEFAULT
125
+ class O {
126
+ constructor({ level: t = m.DEFAULT } = {
127
+ level: m.DEFAULT
128
128
  }) {
129
129
  this.log = this.debug, this.level = t;
130
130
  }
131
131
  debug(...t) {
132
- this.level <= u.DEBUG && console.info(...t);
132
+ this.level <= m.DEBUG && console.info(...t);
133
133
  }
134
134
  info(...t) {
135
- this.level <= u.INFO && console.info(...t);
135
+ this.level <= m.INFO && console.info(...t);
136
136
  }
137
137
  warn(...t) {
138
- this.level <= u.WARN && console.warn(...t);
138
+ this.level <= m.WARN && console.warn(...t);
139
139
  }
140
140
  error(...t) {
141
- this.level <= u.ERROR && console.error(...t);
141
+ this.level <= m.ERROR && console.error(...t);
142
142
  }
143
143
  }
144
- const D = (o, t, e) => {
144
+ const M = (o, t, e) => {
145
145
  const n = Array.from((o == null ? void 0 : o.querySelectorAll(e)) ?? []), r = n.findIndex((s) => s === t);
146
146
  if (r !== -1)
147
147
  return {
148
148
  siblings: n.length,
149
149
  position: r
150
150
  };
151
- }, O = [
151
+ }, x = [
152
152
  "nodeName",
153
153
  "className",
154
154
  "id",
155
155
  "href",
156
156
  "text",
157
157
  "role"
158
- ], M = (o) => {
158
+ ], j = (o) => {
159
159
  const t = {};
160
- for (const e of O) {
160
+ for (const e of x) {
161
161
  const n = o[e] || o.getAttribute(e) || o.hasAttribute(e);
162
- n !== void 0 && (typeof n == "boolean" ? t[e] = n : t[e] = $(n));
162
+ n !== void 0 && (typeof n == "boolean" ? t[e] = n : t[e] = C(n));
163
163
  }
164
164
  return t;
165
- }, j = (o) => {
165
+ }, F = (o) => {
166
166
  try {
167
167
  const t = JSON.parse(o), e = Object.prototype.toString.call(t);
168
168
  return [e === "[object Object]" || e === "[object Array]", t];
@@ -170,31 +170,31 @@ const D = (o, t, e) => {
170
170
  return [!1, null];
171
171
  }
172
172
  }, _ = (o) => {
173
- const [t, e] = j(o);
173
+ const [t, e] = F(o);
174
174
  return t ? e : o;
175
- }, F = (o, t) => (o.filter(
175
+ }, H = (o, t) => (o.filter(
176
176
  (e) => e.name.match(/^data-trackable|^data-o-|^aria-/i)
177
177
  ).forEach((e) => {
178
178
  t[e.name] = e.value;
179
- }), t), H = (o, t, e) => {
179
+ }), t), z = (o, t, e) => {
180
180
  const n = {};
181
- return e && O.forEach((r) => {
181
+ return e && x.forEach((r) => {
182
182
  typeof t[r] < "u" && r !== "id" && (n[r] = t[r]);
183
183
  }), o.filter((r) => r.name.match(/^data-trackable-context-/i)).forEach((r) => {
184
184
  n[r.name.replace("data-trackable-context-", "")] = _(r.value);
185
185
  }), n;
186
186
  };
187
- function z(o, t) {
187
+ function q(o, t) {
188
188
  const e = o, n = e != null && e.getAttribute("data-trackable") ? `[data-trackable="${e.getAttribute("data-trackable")}"]` : e == null ? void 0 : e.nodeName, r = [], s = {};
189
189
  for (; o && o !== t; ) {
190
- const i = M(o), c = Array.from(o.attributes);
191
- let a = F(c, i);
190
+ const i = j(o), c = Array.from(o.attributes);
191
+ let a = H(c, i);
192
192
  a["data-trackable"] && (a = Object.assign(
193
193
  a,
194
- D(o, e, n)
194
+ M(o, e, n)
195
195
  )), r.push(a);
196
- const h = H(c, i, o === e);
197
- C(h, s), o = o.parentNode;
196
+ const h = z(c, i, o === e);
197
+ N(h, s), o = o.parentNode;
198
198
  }
199
199
  return { trace: r, customContext: s };
200
200
  }
@@ -210,7 +210,7 @@ class Y {
210
210
  elements: c = 'a, button, input, [role="button"]',
211
211
  logger: a
212
212
  }) {
213
- this.cccId = t, this.cccName = e, this.subtype = n, this.teamName = r, this.shadowRoot = s, this.category = i, this.elements = c, this.isInitialised = !1, this.log = a ?? new N();
213
+ this.cccId = t, this.cccName = e, this.subtype = n, this.teamName = r, this.shadowRoot = s, this.category = i, this.elements = c, this.isInitialised = !1, this.log = a ?? new O();
214
214
  }
215
215
  // Get properties for the event (as opposed to properties of the clicked element)
216
216
  getEventProperties(t) {
@@ -218,7 +218,7 @@ class Y {
218
218
  for (const n of V)
219
219
  if (t[n])
220
220
  try {
221
- e[n] = $(t[n]);
221
+ e[n] = C(t[n]);
222
222
  } catch (r) {
223
223
  this.log.info(r);
224
224
  }
@@ -227,13 +227,13 @@ class Y {
227
227
  // Controller for handling click events
228
228
  handleClickEvent(t, e) {
229
229
  return (n, r) => {
230
- const s = this.getEventProperties(n), { trace: i, customContext: c } = z(r, e);
230
+ const s = this.getEventProperties(n), { trace: i, customContext: c } = q(r, e);
231
231
  s.custom = r.dataset && r.dataset.custom ? JSON.parse(r.dataset.custom) : null, s.domPathTokens = i, s.component = {
232
232
  id: this.cccId,
233
233
  name: this.cccName,
234
234
  type: "custom-code-component",
235
235
  subtype: this.subtype
236
- }, s.teamName = this.teamName, s.url = document.URL, C(c, s), s.method = "ftCustomAnalytics", t = { ...t, ...s }, document.body.dispatchEvent(
236
+ }, s.teamName = this.teamName, s.url = document.URL, N(c, s), s.method = "ftCustomAnalytics", t = { ...t, ...s }, document.body.dispatchEvent(
237
237
  new CustomEvent("oTracking.event", {
238
238
  detail: t,
239
239
  bubbles: !0,
@@ -330,7 +330,7 @@ class w extends d {
330
330
  super(t, { ...e, cause: "Import error" }), this.name = "CCCImportError";
331
331
  }
332
332
  }
333
- class q extends d {
333
+ class $ extends d {
334
334
  constructor(t, e) {
335
335
  super(t, { ...e, cause: "Render error" }), this.name = "CCCRenderError";
336
336
  }
@@ -352,20 +352,20 @@ const b = class b extends Event {
352
352
  };
353
353
  b.eventType = "ccc:event";
354
354
  let g = b;
355
- const y = class y extends g {
355
+ const E = class E extends g {
356
356
  constructor(t, e) {
357
- super(y.eventType, t, e);
357
+ super(E.eventType, t, e);
358
358
  }
359
359
  };
360
- y.eventType = "ccc:connected";
361
- let T = y;
362
- const E = class E extends g {
360
+ E.eventType = "ccc:connected";
361
+ let T = E;
362
+ const y = class y extends g {
363
363
  constructor(t, e) {
364
- super(E.eventType, t, e);
364
+ super(y.eventType, t, e);
365
365
  }
366
366
  };
367
- E.eventType = "ccc:ready";
368
- let k = E;
367
+ y.eventType = "ccc:ready";
368
+ let k = y;
369
369
  const v = class v extends g {
370
370
  constructor(t, e) {
371
371
  super(v.eventType, t, e), this.intersecting = !1, this.intersecting = t.intersecting, this.entry = t.entry;
@@ -471,8 +471,8 @@ class tt extends HTMLElement {
471
471
  `.trim(), document.head.appendChild(e);
472
472
  const n = document.createElement("script");
473
473
  n.type = "module", n.src = `${(s = this.testUrl) == null ? void 0 : s.origin}/@vite/client`, document.head.appendChild(n);
474
- }, this.log = new N({
475
- level: I(this.getAttribute("log"))
474
+ }, this.log = new O({
475
+ level: D(this.getAttribute("log"))
476
476
  });
477
477
  const t = HTMLElement.prototype.hasOwnProperty("attachInternals");
478
478
  try {
@@ -492,7 +492,7 @@ class tt extends HTMLElement {
492
492
  }
493
493
  }
494
494
  emitError(t) {
495
- if (t instanceof d)
495
+ if (console.debug("ccc#emitError", t), t instanceof d)
496
496
  this.dispatchEvent(
497
497
  new ErrorEvent("ccc:error", {
498
498
  bubbles: !0,
@@ -520,11 +520,20 @@ class tt extends HTMLElement {
520
520
  }
521
521
  disconnectedCallback() {
522
522
  const t = this.getAttribute("path");
523
- this.log.info(`<custom-code-component:${t}> disconnected`), typeof this.onunmount == "function" && this.onunmount(), this.observer.disconnect();
523
+ this.log.info(`<custom-code-component:${t}> disconnected`), this.observer.disconnect();
524
524
  }
525
525
  onmessage() {
526
526
  }
527
527
  onunmount(t) {
528
+ if (t instanceof Error) {
529
+ const e = new $(t.message, {
530
+ error: t,
531
+ component: this.component
532
+ });
533
+ requestAnimationFrame(() => {
534
+ this.emitError(e), this.unmount(e);
535
+ });
536
+ }
528
537
  }
529
538
  async onready(t) {
530
539
  try {
@@ -536,7 +545,7 @@ class tt extends HTMLElement {
536
545
  ), this.dataset.cccReady = "true", delete this.dataset.cccError, this.observer.observe(this);
537
546
  } catch (e) {
538
547
  if (e instanceof Error) {
539
- const n = new q(e.message, {
548
+ const n = new $(e.message, {
540
549
  error: e,
541
550
  component: this.component
542
551
  });
@@ -585,11 +594,7 @@ class tt extends HTMLElement {
585
594
  "text-decoration: underline;",
586
595
  " attribute instead."
587
596
  ));
588
- const {
589
- unmount: a,
590
- onmessage: h,
591
- ready: m = Promise.resolve()
592
- } = this.app(
597
+ const { onmessage: a, ready: h = Promise.resolve() } = this.app(
593
598
  s,
594
599
  {
595
600
  ...i ?? c,
@@ -599,9 +604,10 @@ class tt extends HTMLElement {
599
604
  prerendered: !!t,
600
605
  children: this.children
601
606
  },
602
- r
607
+ r,
608
+ this.onunmount.bind(this)
603
609
  ) || {};
604
- a && (this.onunmount = a), h && (this.onmessage = h), m && this.onready(m);
610
+ a && (this.onmessage = a), this.onready(h);
605
611
  }
606
612
  } catch (r) {
607
613
  throw this.log.info(
@@ -614,7 +620,6 @@ class tt extends HTMLElement {
614
620
  // slot on failure
615
621
  unmount(t) {
616
622
  var n;
617
- this.onunmount(t);
618
623
  const e = this.querySelector(
619
624
  "template[data-component-fallback]"
620
625
  ) ?? this.querySelector("template");
@@ -634,7 +639,7 @@ class tt extends HTMLElement {
634
639
  try {
635
640
  return await new Promise(
636
641
  (a, h) => {
637
- const m = setTimeout(() => {
642
+ const p = setTimeout(() => {
638
643
  this.log.error("CCC import timeout error"), h(
639
644
  new J({
640
645
  component: this.component,
@@ -647,9 +652,9 @@ class tt extends HTMLElement {
647
652
  /* webpackIgnore: true */
648
653
  this.source
649
654
  /* @vite-ignore */
650
- ).then(({ default: p }) => {
651
- if (p)
652
- clearTimeout(m), a(p);
655
+ ).then(({ default: u }) => {
656
+ if (u)
657
+ clearTimeout(p), a(u);
653
658
  else
654
659
  throw new w(
655
660
  "No component renderer default export found",
@@ -658,16 +663,16 @@ class tt extends HTMLElement {
658
663
  source: this.source
659
664
  }
660
665
  );
661
- }).catch((p) => {
662
- clearTimeout(m), this.log.error(p), p instanceof Error && !(p instanceof w) ? h(
663
- new w(p.message, {
666
+ }).catch((u) => {
667
+ clearTimeout(p), this.log.error(u), u instanceof Error && !(u instanceof w) ? h(
668
+ new w(u.message, {
664
669
  component: this.component,
665
670
  source: this.source
666
671
  })
667
- ) : h(p);
672
+ ) : h(u);
668
673
  });
669
674
  else
670
- throw clearTimeout(m), new w(`Unable to mount ${t}`, {
675
+ throw clearTimeout(p), new w(`Unable to mount ${t}`, {
671
676
  component: this.component,
672
677
  source: this.source
673
678
  });