@guardian/ophan-tracker-js 4.0.2 → 5.0.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 (210) hide show
  1. package/NPM-dist/adblock-detection.js +17 -11
  2. package/NPM-dist/adblock-detection.js.map +1 -0
  3. package/NPM-dist/attention.js +32 -19
  4. package/NPM-dist/attention.js.map +1 -0
  5. package/NPM-dist/campaign.js +6 -2
  6. package/NPM-dist/campaign.js.map +1 -0
  7. package/NPM-dist/click-path-capture.js +5 -2
  8. package/NPM-dist/click-path-capture.js.map +1 -0
  9. package/NPM-dist/components.js +1 -0
  10. package/NPM-dist/components.js.map +1 -0
  11. package/NPM-dist/contribution.js +1 -0
  12. package/NPM-dist/contribution.js.map +1 -0
  13. package/NPM-dist/core.js +40 -10
  14. package/NPM-dist/core.js.map +1 -0
  15. package/NPM-dist/embed.js +1 -0
  16. package/NPM-dist/embed.js.map +1 -0
  17. package/NPM-dist/fb-instant.js +1 -0
  18. package/NPM-dist/fb-instant.js.map +1 -0
  19. package/NPM-dist/heatmap.js +4 -0
  20. package/NPM-dist/heatmap.js.map +1 -0
  21. package/NPM-dist/holidays.js +1 -0
  22. package/NPM-dist/holidays.js.map +1 -0
  23. package/NPM-dist/http-status.js +1 -0
  24. package/NPM-dist/http-status.js.map +1 -0
  25. package/NPM-dist/iframe-tracking.js +16 -3
  26. package/NPM-dist/iframe-tracking.js.map +1 -0
  27. package/NPM-dist/index.js +285 -0
  28. package/NPM-dist/index.js.map +1 -0
  29. package/NPM-dist/interactive.js +1 -0
  30. package/NPM-dist/interactive.js.map +1 -0
  31. package/NPM-dist/jobs-courses.js +1 -0
  32. package/NPM-dist/jobs-courses.js.map +1 -0
  33. package/NPM-dist/jobs.js +1 -0
  34. package/NPM-dist/jobs.js.map +1 -0
  35. package/NPM-dist/membership.js +1 -0
  36. package/NPM-dist/membership.js.map +1 -0
  37. package/NPM-dist/ng.js +10 -33
  38. package/NPM-dist/ng.js.map +1 -0
  39. package/NPM-dist/perf.js +3 -2
  40. package/NPM-dist/perf.js.map +1 -0
  41. package/NPM-dist/privatebrowsing.js +1 -0
  42. package/NPM-dist/privatebrowsing.js.map +1 -0
  43. package/NPM-dist/r2.js +8 -4
  44. package/NPM-dist/r2.js.map +1 -0
  45. package/NPM-dist/smart-news.js +10 -3
  46. package/NPM-dist/smart-news.js.map +1 -0
  47. package/NPM-dist/tests/attention.test.js +1 -0
  48. package/NPM-dist/tests/attention.test.js.map +1 -0
  49. package/NPM-dist/tests/click-path-capture.test.js +1 -0
  50. package/NPM-dist/tests/click-path-capture.test.js.map +1 -0
  51. package/NPM-dist/tests/components.test.js +1 -0
  52. package/NPM-dist/tests/components.test.js.map +1 -0
  53. package/NPM-dist/tests/core.test.js +1 -0
  54. package/NPM-dist/tests/core.test.js.map +1 -0
  55. package/NPM-dist/tests/heatmap.test.js +1 -0
  56. package/NPM-dist/tests/heatmap.test.js.map +1 -0
  57. package/NPM-dist/tests/index.test.js +18 -0
  58. package/NPM-dist/tests/index.test.js.map +1 -0
  59. package/NPM-dist/tests/transmit.test.js +1 -0
  60. package/NPM-dist/tests/transmit.test.js.map +1 -0
  61. package/NPM-dist/transmit.js +23 -5
  62. package/NPM-dist/transmit.js.map +1 -0
  63. package/NPM-dist/types/abtest.js +1 -0
  64. package/NPM-dist/types/abtest.js.map +1 -0
  65. package/NPM-dist/types/adblock-detection.d.ts +1 -0
  66. package/NPM-dist/types/adblock-detection.d.ts.map +1 -0
  67. package/NPM-dist/types/attention.d.ts +1 -0
  68. package/NPM-dist/types/attention.d.ts.map +1 -0
  69. package/NPM-dist/types/campaign.d.ts +1 -0
  70. package/NPM-dist/types/campaign.d.ts.map +1 -0
  71. package/NPM-dist/types/click-path-capture.d.ts +1 -0
  72. package/NPM-dist/types/click-path-capture.d.ts.map +1 -0
  73. package/NPM-dist/types/component-event.js +1 -0
  74. package/NPM-dist/types/component-event.js.map +1 -0
  75. package/NPM-dist/types/component-type.js +1 -0
  76. package/NPM-dist/types/component-type.js.map +1 -0
  77. package/NPM-dist/types/components.d.ts +1 -0
  78. package/NPM-dist/types/components.d.ts.map +1 -0
  79. package/NPM-dist/types/consent.js +1 -0
  80. package/NPM-dist/types/consent.js.map +1 -0
  81. package/NPM-dist/types/contribution.d.ts +2 -1
  82. package/NPM-dist/types/contribution.d.ts.map +1 -0
  83. package/NPM-dist/types/core.d.ts +1 -0
  84. package/NPM-dist/types/core.d.ts.map +1 -0
  85. package/NPM-dist/types/embed.d.ts +2 -1
  86. package/NPM-dist/types/embed.d.ts.map +1 -0
  87. package/NPM-dist/types/event.js +1 -0
  88. package/NPM-dist/types/event.js.map +1 -0
  89. package/NPM-dist/types/fb-instant.d.ts +1 -0
  90. package/NPM-dist/types/fb-instant.d.ts.map +1 -0
  91. package/NPM-dist/types/heatmap.d.ts +1 -0
  92. package/NPM-dist/types/heatmap.d.ts.map +1 -0
  93. package/NPM-dist/types/holidays.d.ts +2 -1
  94. package/NPM-dist/types/holidays.d.ts.map +1 -0
  95. package/NPM-dist/types/http-status.d.ts +1 -0
  96. package/NPM-dist/types/http-status.d.ts.map +1 -0
  97. package/NPM-dist/types/iframe-tracking.d.ts +1 -0
  98. package/NPM-dist/types/iframe-tracking.d.ts.map +1 -0
  99. package/NPM-dist/types/index.d.ts +199 -0
  100. package/NPM-dist/types/index.d.ts.map +1 -0
  101. package/NPM-dist/types/inpageclick.js +1 -0
  102. package/NPM-dist/types/inpageclick.js.map +1 -0
  103. package/NPM-dist/types/interaction.js +1 -0
  104. package/NPM-dist/types/interaction.js.map +1 -0
  105. package/NPM-dist/types/interactive.d.ts +1 -0
  106. package/NPM-dist/types/interactive.d.ts.map +1 -0
  107. package/NPM-dist/types/jobs-courses.d.ts +2 -1
  108. package/NPM-dist/types/jobs-courses.d.ts.map +1 -0
  109. package/NPM-dist/types/jobs.d.ts +2 -1
  110. package/NPM-dist/types/jobs.d.ts.map +1 -0
  111. package/NPM-dist/types/link.js +1 -0
  112. package/NPM-dist/types/link.js.map +1 -0
  113. package/NPM-dist/types/media.js +1 -0
  114. package/NPM-dist/types/media.js.map +1 -0
  115. package/NPM-dist/types/membership.d.ts +2 -1
  116. package/NPM-dist/types/membership.d.ts.map +1 -0
  117. package/NPM-dist/types/ng.d.ts +2 -13
  118. package/NPM-dist/types/ng.d.ts.map +1 -0
  119. package/NPM-dist/types/perf.d.ts +2 -1
  120. package/NPM-dist/types/perf.d.ts.map +1 -0
  121. package/NPM-dist/types/platform.js +1 -0
  122. package/NPM-dist/types/platform.js.map +1 -0
  123. package/NPM-dist/types/printproduct.js +1 -0
  124. package/NPM-dist/types/printproduct.js.map +1 -0
  125. package/NPM-dist/types/privatebrowsing.d.ts +1 -0
  126. package/NPM-dist/types/privatebrowsing.d.ts.map +1 -0
  127. package/NPM-dist/types/product.js +1 -0
  128. package/NPM-dist/types/product.js.map +1 -0
  129. package/NPM-dist/types/r2.d.ts +1 -0
  130. package/NPM-dist/types/r2.d.ts.map +1 -0
  131. package/NPM-dist/types/smart-news.d.ts +2 -1
  132. package/NPM-dist/types/smart-news.d.ts.map +1 -0
  133. package/NPM-dist/types/subscription.js +1 -0
  134. package/NPM-dist/types/subscription.js.map +1 -0
  135. package/NPM-dist/types/tests/attention.test.d.ts +1 -0
  136. package/NPM-dist/types/tests/attention.test.d.ts.map +1 -0
  137. package/NPM-dist/types/tests/click-path-capture.test.d.ts +1 -0
  138. package/NPM-dist/types/tests/click-path-capture.test.d.ts.map +1 -0
  139. package/NPM-dist/types/tests/components.test.d.ts +1 -0
  140. package/NPM-dist/types/tests/components.test.d.ts.map +1 -0
  141. package/NPM-dist/types/tests/core.test.d.ts +1 -0
  142. package/NPM-dist/types/tests/core.test.d.ts.map +1 -0
  143. package/NPM-dist/types/tests/heatmap.test.d.ts +1 -0
  144. package/NPM-dist/types/tests/heatmap.test.d.ts.map +1 -0
  145. package/NPM-dist/types/tests/index.test.d.ts +2 -0
  146. package/NPM-dist/types/tests/index.test.d.ts.map +1 -0
  147. package/NPM-dist/types/tests/transmit.test.d.ts +1 -0
  148. package/NPM-dist/types/tests/transmit.test.d.ts.map +1 -0
  149. package/NPM-dist/types/transmit.d.ts +1 -0
  150. package/NPM-dist/types/transmit.d.ts.map +1 -0
  151. package/NPM-dist/types/types/abtest.d.ts +1 -0
  152. package/NPM-dist/types/types/abtest.d.ts.map +1 -0
  153. package/NPM-dist/types/types/component-event.d.ts +15 -0
  154. package/NPM-dist/types/types/component-event.d.ts.map +1 -0
  155. package/NPM-dist/types/types/component-type.d.ts +7 -0
  156. package/NPM-dist/types/types/component-type.d.ts.map +1 -0
  157. package/NPM-dist/types/types/consent.d.ts +1 -0
  158. package/NPM-dist/types/types/consent.d.ts.map +1 -0
  159. package/NPM-dist/types/types/event.d.ts +11 -0
  160. package/NPM-dist/types/types/event.d.ts.map +1 -0
  161. package/NPM-dist/types/types/inpageclick.d.ts +1 -0
  162. package/NPM-dist/types/types/inpageclick.d.ts.map +1 -0
  163. package/NPM-dist/types/types/interaction.d.ts +1 -0
  164. package/NPM-dist/types/types/interaction.d.ts.map +1 -0
  165. package/NPM-dist/types/types/link.d.ts +1 -0
  166. package/NPM-dist/types/types/link.d.ts.map +1 -0
  167. package/NPM-dist/types/types/media.d.ts +1 -0
  168. package/NPM-dist/types/types/media.d.ts.map +1 -0
  169. package/NPM-dist/types/types/platform.d.ts +1 -0
  170. package/NPM-dist/types/types/platform.d.ts.map +1 -0
  171. package/NPM-dist/types/types/printproduct.d.ts +1 -0
  172. package/NPM-dist/types/types/printproduct.d.ts.map +1 -0
  173. package/NPM-dist/types/types/product.d.ts +7 -0
  174. package/NPM-dist/types/types/product.d.ts.map +1 -0
  175. package/NPM-dist/types/types/subscription.d.ts +1 -0
  176. package/NPM-dist/types/types/subscription.d.ts.map +1 -0
  177. package/NPM-dist/types/vendor/adBlockDetectionLib.d.ts +3 -1
  178. package/NPM-dist/types/vendor/adBlockDetectionLib.d.ts.map +1 -0
  179. package/NPM-dist/types/visibility.d.ts +2 -1
  180. package/NPM-dist/types/visibility.d.ts.map +1 -0
  181. package/NPM-dist/types/witness.d.ts +2 -1
  182. package/NPM-dist/types/witness.d.ts.map +1 -0
  183. package/NPM-dist/vendor/adBlockDetectionLib.js +19 -6
  184. package/NPM-dist/vendor/adBlockDetectionLib.js.map +1 -0
  185. package/NPM-dist/visibility.js +14 -5
  186. package/NPM-dist/visibility.js.map +1 -0
  187. package/NPM-dist/witness.js +1 -0
  188. package/NPM-dist/witness.js.map +1 -0
  189. package/package.json +12 -26
  190. package/readme.md +112 -53
  191. package/src/index.ts +360 -0
  192. package/src/ng.ts +33 -0
  193. package/src/transmit.ts +176 -0
  194. package/src/types/abtest.ts +45 -0
  195. package/src/types/component-event.ts +121 -0
  196. package/src/types/component-type.ts +70 -0
  197. package/src/types/consent.ts +49 -0
  198. package/src/types/event.ts +116 -0
  199. package/src/types/inpageclick.ts +13 -0
  200. package/src/types/interaction.ts +19 -0
  201. package/src/types/link.ts +14 -0
  202. package/src/types/media.ts +184 -0
  203. package/src/types/platform.ts +32 -0
  204. package/src/types/printproduct.ts +53 -0
  205. package/src/types/product.ts +72 -0
  206. package/src/types/subscription.ts +70 -0
  207. package/NPM-dist/manage-my-account.js +0 -46
  208. package/NPM-dist/support.js +0 -45
  209. package/NPM-dist/types/manage-my-account.d.ts +0 -28
  210. package/NPM-dist/types/support.d.ts +0 -28
@@ -0,0 +1,176 @@
1
+ import { EventPayload } from './types/event.js';
2
+
3
+ const canSendFromCurrentRuntime = (): boolean =>
4
+ typeof window !== 'undefined' && typeof document !== 'undefined';
5
+
6
+ // Initialize ophanRemoteHost when needed, not at module load
7
+ let ophanRemoteHost: string;
8
+ const getOphanRemoteHost = (): string => {
9
+ if (!ophanRemoteHost) {
10
+ ophanRemoteHost =
11
+ typeof window !== 'undefined' && (window as any)?.ophanRemoteHost
12
+ ? (window as any).ophanRemoteHost
13
+ : 'https://ophan.theguardian.com';
14
+ }
15
+ return ophanRemoteHost;
16
+ };
17
+
18
+ /**
19
+ * Generates a unique page view ID.
20
+ * @returns {string}
21
+ */
22
+ const generatePageViewId = (): string => {
23
+ return `${new Date().getTime().toString(36)}${'xxxxxxxxxxxx'.replace(
24
+ /x/g,
25
+ () => Math.floor(Math.random() * 36).toString(36),
26
+ )}`;
27
+ };
28
+
29
+ declare const guardian: any;
30
+
31
+ // Guardian Frontend Project pre-generates the pageViewId
32
+ let viewId: string;
33
+ if (typeof guardian !== 'undefined') {
34
+ viewId = guardian.config?.ophan?.pageViewId ?? generatePageViewId();
35
+ } else {
36
+ viewId = generatePageViewId();
37
+ }
38
+
39
+ const bumpViewId = (): void => {
40
+ const viewIdGen = generatePageViewId();
41
+ viewId = viewIdGen;
42
+
43
+ if (typeof guardian !== 'undefined' && guardian.config?.ophan) {
44
+ guardian.config.ophan.pageViewId = viewIdGen;
45
+ }
46
+ };
47
+
48
+ /**
49
+ * Sends the initial event associated with this page view.
50
+ * @param {Record<string, any>} obj - The data to be sent.
51
+ */
52
+ const sendInitial = (obj: Record<string, any>): void => {
53
+ send(`${getOphanRemoteHost()}/img/1?${buildQueryString(obj)}`);
54
+ };
55
+
56
+ /**
57
+ * Encodes a value for URL transmission.
58
+ * @param {string | number} value - The value to encode.
59
+ * @returns {string} The encoded value.
60
+ */
61
+ const smartEncode = (value: string | number): string => {
62
+ if (typeof value === 'string' || typeof value === 'number') {
63
+ return encodeURIComponent(value);
64
+ } else {
65
+ return encodeURIComponent(JSON.stringify(value));
66
+ }
67
+ };
68
+
69
+ /**
70
+ * Used for sending additional events after the initial PageView event.
71
+ * @param {EventPayload} obj - The data object to be sent.
72
+ * @param {Function} [f] - An optional callback function.
73
+ */
74
+ const sendMore = (obj: EventPayload, f?: () => void): void => {
75
+ if (!JSON) return;
76
+
77
+ const values: string[] = [];
78
+ for (const prop in obj) {
79
+ // @ts-ignore
80
+ if (obj.hasOwnProperty(prop) && isDefined(obj[prop])) {
81
+ // @ts-ignore
82
+ values.push(`${encodeURIComponent(prop)}=${smartEncode(obj[prop])}`);
83
+ }
84
+ }
85
+
86
+ if (values.length === 0) {
87
+ return;
88
+ }
89
+
90
+ const url = `${getOphanRemoteHost()}/img/2?viewId=${viewId}&${values.join('&')}`;
91
+ send(url, f);
92
+ };
93
+
94
+ /**
95
+ * Checks if a value is defined and not null.
96
+ * @param {*} v - The value to check.
97
+ * @returns {boolean} True if the value is defined, false otherwise.
98
+ */
99
+ const isDefined = (v: any): boolean => {
100
+ if (Array.isArray(v)) {
101
+ return !!v.length;
102
+ } else {
103
+ return v != null;
104
+ }
105
+ };
106
+
107
+ /**
108
+ * Builds a query string from an object.
109
+ * @param {Record<string, any>} obj - The object to convert to a query string.
110
+ * @returns {string} The resulting query string.
111
+ */
112
+ const buildQueryString = (obj: Record<string, any>): string => {
113
+ obj.viewId = viewId;
114
+
115
+ const values: string[] = [];
116
+ for (const prop in obj) {
117
+ if (obj.hasOwnProperty(prop) && obj[prop] != null) {
118
+ values.push(
119
+ `${encodeURIComponent(prop)}=${encodeURIComponent(obj[prop])}`,
120
+ );
121
+ }
122
+ }
123
+
124
+ return values.join('&');
125
+ };
126
+
127
+ /**
128
+ * Sends data to a specified URL.
129
+ * @param {string} url - The URL to send data to.
130
+ * @param {() => void} [f] - An optional callback function.
131
+ */
132
+ const send = (url: string, f?: () => void): void => {
133
+ if (!canSendFromCurrentRuntime()) {
134
+ f?.();
135
+ return;
136
+ }
137
+
138
+ if (typeof fetch === 'function') {
139
+ fetch(url, {
140
+ method: 'GET',
141
+ mode: 'no-cors',
142
+ keepalive: true,
143
+ credentials: 'include',
144
+ }).then(() => f?.());
145
+ } else {
146
+ const image = new Image();
147
+ const checker = setInterval(() => {
148
+ if (image.complete) {
149
+ if (typeof f === 'function') {
150
+ f();
151
+ }
152
+ clearInterval(checker);
153
+ }
154
+ }, 10);
155
+ image.src = url;
156
+ }
157
+ };
158
+
159
+ export default {
160
+ sendInitial,
161
+ sendMore,
162
+ get viewId() {
163
+ return viewId;
164
+ },
165
+ bumpViewId,
166
+ };
167
+
168
+ /**
169
+ * Exports for testing purposes.
170
+ */
171
+ export const _testExports = {
172
+ generatePageViewId,
173
+ smartEncode,
174
+ isDefined,
175
+ buildQueryString,
176
+ };
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Represents the AbTest on the web and apps
3
+ */
4
+ export interface AbTest {
5
+ /**
6
+ * the test that the user is participating in
7
+ */
8
+ name: string;
9
+
10
+ /**
11
+ * the variant that they are seeing
12
+ */
13
+ variant: string;
14
+
15
+ /**
16
+ * whether the test has been completed or not
17
+ */
18
+ complete?: boolean;
19
+
20
+ /**
21
+ * the campaign codes associated with the variant
22
+ */
23
+ campaignCodes?: Array<string>;
24
+ }
25
+
26
+ export interface AbTestRegisterEntry {
27
+ /**
28
+ * Variant assigned to the user for this test
29
+ */
30
+ variantName: string;
31
+
32
+ /**
33
+ * Whether this test has completed for the user
34
+ */
35
+ complete: boolean;
36
+
37
+ /**
38
+ * Campaign codes associated with this variant
39
+ */
40
+ campaignCodes?: Array<string>;
41
+ }
42
+
43
+ export type AbTestRegisterValue = AbTestRegisterEntry | string;
44
+
45
+ export type AbTestRegister = Record<string, AbTestRegisterValue>;
@@ -0,0 +1,121 @@
1
+ import type { AbTest } from './abtest.js';
2
+ import type { Product } from './product.js';
3
+ import { TComponentType } from './component-type.js';
4
+
5
+ /** User or lifecycle action observed for a tracked component. */
6
+ export type TAction =
7
+ /**
8
+ * The component was inserted into its container (e.g. a web page or view in an app)
9
+ **/
10
+ | 'INSERT'
11
+
12
+ /**
13
+ * The component was in view on screen
14
+ **/
15
+ | 'VIEW'
16
+
17
+ /**
18
+ * The component was expanded (e.g. "see more").
19
+ * Not a navigation away to another page
20
+ **/
21
+ | 'EXPAND'
22
+
23
+ /**
24
+ * A "like", thumbs up, etc.
25
+ **/
26
+ | 'LIKE'
27
+
28
+ /**
29
+ * A "dislike", thumbs down, etc.
30
+ **/
31
+ | 'DISLIKE'
32
+
33
+ /**
34
+ * A subscription to a service, newsletter, etc.
35
+ **/
36
+ | 'SUBSCRIBE'
37
+
38
+ /**
39
+ * Selection of an answer in a quiz
40
+ **/
41
+ | 'ANSWER'
42
+
43
+ /**
44
+ * A vote in a poll
45
+ **/
46
+ | 'VOTE'
47
+
48
+ /**
49
+ * A single click on the component which is not covered by
50
+ * any of the existing Actions.
51
+ **/
52
+ | 'CLICK'
53
+
54
+ /**
55
+ * User sign in to Guardian Identity account
56
+ **/
57
+ | 'SIGN_IN'
58
+
59
+ /**
60
+ * User creates a Guardian Identity account
61
+ **/
62
+ | 'CREATE_ACCOUNT'
63
+
64
+ /**
65
+ * User Consent
66
+ **/
67
+ | 'ACCEPT_DEFAULT_CONSENT'
68
+ | 'MANAGE_CONSENT'
69
+ | 'CONSENT_ACCEPT_ALL'
70
+ | 'CONSENT_REJECT_ALL'
71
+ | 'CONSENT_GEOLOCATION_MISMATCH'
72
+
73
+ /**
74
+ * The component sticks in the screen
75
+ **/
76
+ | 'STICK'
77
+
78
+ /**
79
+ * The component is closed
80
+ **/
81
+ | 'CLOSE'
82
+
83
+ /**
84
+ * The component is returned to its original location
85
+ **/
86
+ | 'RETURN'
87
+
88
+ /**
89
+ * The component has detected something
90
+ */
91
+ | 'DETECT';
92
+
93
+ /** Component metadata attached to a `ComponentEvent`. */
94
+ interface ComponentV2 {
95
+ /** Stable type describing the product/UI area that owns the component. */
96
+ componentType: TComponentType;
97
+ /** Optional caller-defined identifier for this component instance. */
98
+ id?: string;
99
+ /** Guardian products associated with the component, usually for acquisition flows. */
100
+ products?: Array<Product>;
101
+ /** Campaign code associated with the component. */
102
+ campaignCode?: string;
103
+ /** Free-form labels used for downstream grouping and analysis. */
104
+ labels?: Array<string>;
105
+ }
106
+
107
+ /** Event generated by a component interaction or component lifecycle change. */
108
+ export interface ComponentEvent {
109
+ /** Component metadata describing what was interacted with. */
110
+ component: ComponentV2;
111
+ /** Action the user or component performed. */
112
+ action: TAction;
113
+ /** Optional action-specific value, such as a quiz answer or poll choice. */
114
+ value?: string;
115
+ /** Optional caller-defined identifier for this event. */
116
+ id?: string;
117
+ /** A/B test context for the component. */
118
+ abTest?: AbTest;
119
+ /** A/B test context used for targeting the component. */
120
+ targetingAbTest?: AbTest;
121
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Stable component category used by Ophan component events.
3
+ *
4
+ * Choose the closest existing value before adding a new one; these values are
5
+ * consumed downstream for reporting and analysis.
6
+ */
7
+ export type TComponentType =
8
+ | 'READERS_QUESTIONS_ATOM'
9
+ | 'QANDA_ATOM'
10
+ | 'PROFILE_ATOM'
11
+ | 'GUIDE_ATOM'
12
+ | 'TIMELINE_ATOM'
13
+ | 'NEWSLETTER_SUBSCRIPTION'
14
+ | 'SURVEYS_QUESTIONS'
15
+ | 'ACQUISITIONS_EPIC'
16
+ | 'ACQUISITIONS_ENGAGEMENT_BANNER'
17
+ | 'ACQUISITIONS_THANK_YOU_EPIC'
18
+ | 'ACQUISITIONS_HEADER'
19
+ | 'ACQUISITIONS_FOOTER'
20
+ | 'ACQUISITIONS_INTERACTIVE_SLICE'
21
+ | 'ACQUISITIONS_NUGGET'
22
+ | 'ACQUISITIONS_STANDFIRST'
23
+ | 'ACQUISITIONS_THRASHER'
24
+ | 'ACQUISITIONS_EDITORIAL_LINK'
25
+ | 'ACQUISITIONS_MANAGE_MY_ACCOUNT'
26
+ | 'ACQUISITIONS_BUTTON'
27
+ | 'ACQUISITIONS_OTHER'
28
+ | 'APP_ADVERT'
29
+ | 'APP_AUDIO'
30
+ | 'APP_BUTTON'
31
+ | 'APP_CARD'
32
+ | 'APP_CROSSWORDS'
33
+ | 'APP_ENGAGEMENT_BANNER'
34
+ | 'APP_EPIC'
35
+ | 'APP_GALLERY'
36
+ | 'APP_LINK'
37
+ | 'APP_NAVIGATION_ITEM'
38
+ | 'APP_SCREEN'
39
+ | 'APP_THRASHER'
40
+ | 'APP_VIDEO'
41
+ | 'AUDIO_ATOM'
42
+ | 'CHART_ATOM'
43
+ | 'ACQUISITIONS_MERCHANDISING'
44
+ | 'ACQUISITIONS_HOUSE_ADS'
45
+ | 'SIGN_IN_GATE'
46
+ | 'ACQUISITIONS_SUBSCRIPTIONS_BANNER'
47
+ | 'MOBILE_STICKY_AD'
48
+ | 'IDENTITY_AUTHENTICATION'
49
+ | 'RETENTION_ENGAGEMENT_BANNER'
50
+ | 'ACQUISITION_SUPPORT_SITE'
51
+ | 'RETENTION_EPIC'
52
+ | 'CONSENT'
53
+ | 'LIVE_BLOG_PINNED_POST'
54
+ | 'STICKY_VIDEO'
55
+ | 'KEY_EVENT_CARD'
56
+ | 'RETENTION_HEADER'
57
+ | 'SLIDESHOW'
58
+ | 'APP_FEATURE'
59
+ | 'CARD'
60
+ | 'CAROUSEL'
61
+ | 'CONTAINER'
62
+ | 'MENU'
63
+ | 'ACQUISITIONS_GUTTER'
64
+ | 'INTERACTIVE_ATOM'
65
+ | 'LOOP_VIDEO'
66
+ | 'AD_BLOCK_RECOVERY'
67
+ | 'SIGN_IN_GOOGLE_ONE_TAP'
68
+ | 'BF_CACHE'
69
+ | 'AFFILIATE_DISCLAIMER'
70
+ | 'STORYLINES';
@@ -0,0 +1,49 @@
1
+ /** A user’s recorded tracking consent. Because the consent data can come in
2
+ * different formats from different sources, the jurisdiction field determines
3
+ * the format of consentData.
4
+ **/
5
+ export interface Consent {
6
+ /**
7
+ * The jurisdiction is a tag for the consent data: TCF, CCPA, or AUS if the
8
+ * consent has been successfully parsed in one of those formats; or
9
+ * OTHER otherwise.
10
+ **/
11
+ consentJurisdiction: TJurisdiction;
12
+
13
+ /**
14
+ * If the jurisdiction is TCF, CCPA, or AUS, consentData contains a
15
+ * successfully interpreted consent value the corresponding type (for TCF
16
+ * this is a base-64-encoded consent string, and for CCPA and AUS it’s
17
+ * either the string "true" or "false" – where "true" means the user has
18
+ * given their consent). If the jurisdiction is OTHER, then consentData
19
+ * contains the unparsed consent data, in the format
20
+ * key1:value1,key2:value2,….
21
+ **/
22
+ consent: string;
23
+
24
+ /**
25
+ * consentUUID is an identifier for the consent, for debugging.
26
+ **/
27
+ consentUUID?: string;
28
+
29
+ /**
30
+ * cmp version for debugging
31
+ **/
32
+ cmpVersion?: string;
33
+ }
34
+
35
+ /**
36
+ * For a recognised/successfully parsed consent, a value of TCF, USNAT, CCPA, or AUS
37
+ * indicates which jurisdiction the consent is for. OTHER marks a consent
38
+ * that was not successfully parsed into one of these formats.
39
+ *
40
+ * - TCF is Europe’s [Transparency & Consent Framework](https://iabeurope.eu/transparency-consent-framework/).
41
+ * - CCPA is the [California Consumer Privacy Act](https://oag.ca.gov/privacy/ccpa). This has now been deprecated and replaced by USNAT: https://github.com/guardian/ophan/pull/5974
42
+ * - AUS is for Australian consents, using a version of the CCPA.
43
+ **/
44
+ export type TJurisdiction =
45
+ | "TCF"
46
+ | "USNAT"
47
+ | "CCPA"
48
+ | "AUS"
49
+ | "OTHER";
@@ -0,0 +1,116 @@
1
+ import type { AbTestRegister } from './abtest.js';
2
+ import type { ComponentEvent } from './component-event.js';
3
+ import type { Consent } from './consent.js';
4
+ import type { InPageClick } from './inpageclick.js';
5
+ import { Interaction } from './interaction.js';
6
+ import type { AudioEvent, VideoEvent } from './media.js';
7
+ import type { TPlatform } from './platform.js';
8
+ import type { TMembershipTier, TSubscriptionType } from './subscription.js';
9
+
10
+ /**
11
+ * Details about a page view - only populated for _PAGE_VIEW
12
+ * event types
13
+ **/
14
+ interface PageView {
15
+ /** Url of the page served */
16
+ url: string;
17
+ /** The platform that served this page */
18
+ platform?: TPlatform;
19
+ /** Some approximation of the content type */
20
+ contentType?: string;
21
+ /** URL of the referrer to this page view */
22
+ ref?: string;
23
+ /** The number of days in the previous week that the device has had a page view */
24
+ daysVisitedInLastWeek?: number;
25
+ /** Total number of days on which this browser has visited the guardian */
26
+ totalDaysVisited?: number;
27
+ /** Calculated average days between recent visits */
28
+ averageDaysBetweenRecentVisits?: number;
29
+ /** No longer used in Ophan. Replaced by Frequency */
30
+ regular?: boolean;
31
+ subscriptionType?: TSubscriptionType;
32
+ membershipTier?: TMembershipTier;
33
+ navigationType?: PerformanceNavigationTiming['type'];
34
+ /** The type of page load - normal or from cache */
35
+ loadType?: 'normal' | 'bfcache';
36
+ from?: string;
37
+ to?: string;
38
+ tz?: string;
39
+ visibilityState?: string;
40
+ }
41
+
42
+ interface AssetPerformanceData {
43
+ /** The name of the metric being captured */
44
+ name: string;
45
+ /** Value of window.performance.now at which metric was captured */
46
+ timing: number;
47
+ }
48
+
49
+ interface WebPerformanceData {
50
+ /** Time in ms that dns lookup took */
51
+ dns: number;
52
+ /** Time in ms that connection to the server took */
53
+ connection: number;
54
+ /** Time to first byte */
55
+ firstByte: number;
56
+ /** First byte to last byte */
57
+ lastByte: number;
58
+ /** From last byte of doc to start of domContentLoaded */
59
+ domContentLoadedEvent: number;
60
+ /** domContentLoaded to start of load event */
61
+ loadEvent: number;
62
+ /** The navigation type */
63
+ navType: number;
64
+ /** Number of redirects on current domain */
65
+ redirectCount: number;
66
+ /** List of custom Asset performance data */
67
+ assetsPerformance?: AssetPerformanceData[];
68
+ }
69
+
70
+ interface Component {
71
+ /** The name of this component */
72
+ name: string;
73
+ /** How long, in milliseconds, that the component took to load */
74
+ loadTimeMs?: number;
75
+ }
76
+
77
+ interface LazyComponents {
78
+ /** The set of components that were loaded lazily on this page */
79
+ components: Component[];
80
+ }
81
+
82
+ /** Guardian edition associated with a page view or event. */
83
+ export type TEdition = 'UK' | 'US' | 'AU' | 'International' | 'Europe';
84
+
85
+ /**
86
+ * Payload accepted by `record()`.
87
+ *
88
+ * Each property maps to an Ophan event field. Most calls set one focused event
89
+ * shape, such as `componentEvent`, `interaction`, `audio`, or `video`, plus any
90
+ * supporting metadata such as `edition`.
91
+ */
92
+ export interface EventPayload extends Partial<Consent>, Partial<InPageClick> {
93
+ /** If the user is logged in, the identity user id */
94
+ userId?: string;
95
+ /** Page-view details. Usually sent by tracker initialisation rather than application code. */
96
+ pageView?: PageView;
97
+ /** Attention time spent on this page view in milliseconds */
98
+ attentionMs?: number;
99
+ /** Map of component name to time in ms */
100
+ componentAttentionMs?: { [key: string]: number };
101
+ performance?: WebPerformanceData;
102
+ audio?: AudioEvent;
103
+ video?: VideoEvent;
104
+ abTestRegister?: AbTestRegister;
105
+ lazyComponents?: LazyComponents;
106
+ interaction?: Interaction;
107
+ inPrivateBrowsingMode?: boolean;
108
+ adUnitWasHidden?: boolean;
109
+ /** Component interaction, render, consent, or acquisition event. */
110
+ componentEvent?: ComponentEvent;
111
+ /** Experience contains information about the rendering of the page, e.g.: dotcom-rendering */
112
+ experiences?: Array<string>;
113
+ edition?: TEdition;
114
+ /** The set of component names that were rendered on this page, e.g.: ["nav3","nav2","sub-nav","footer"] */
115
+ renderedComponents?: Array<string>;
116
+ }
@@ -0,0 +1,13 @@
1
+ import { LinkName } from './link.js';
2
+
3
+ export interface InPageClick {
4
+ /**
5
+ * the component that contained the item clicked on
6
+ */
7
+ clickComponent?: string;
8
+
9
+ /**
10
+ * The link name associated with the element clicked.
11
+ */
12
+ clickLinkNames?: LinkName;
13
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Represents an interaction on the web or apps
3
+ */
4
+ export interface Interaction {
5
+ /**
6
+ * Key for the interaction.
7
+ */
8
+ component: string;
9
+
10
+ /**
11
+ * Value associated with the interaction
12
+ */
13
+ value?: string;
14
+
15
+ /**
16
+ * The id of the atom that the interaction has come from
17
+ */
18
+ atomId?: string;
19
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Represents the position and location of a link within the Guardian site.
3
+ * We hope to enhance this structure to include a better representation of what the links
4
+ * actually mean, but for now we just report exactly what the web site tells us,
5
+ * which is a hierarchical list of named items e.g.
6
+ * "more","container-2 | highlights","Front | /uk"
7
+ * "article","news | group-1+ | card-3","container-1 | headlines","Front | /uk"
8
+ */
9
+ export type LinkName = {
10
+ /**
11
+ * List of link names, most specific first
12
+ */
13
+ raw?: string[];
14
+ }