@gemx-dev/clarity-js 0.8.67 → 0.8.69

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/src/layout/dom.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Privacy } from "@clarity-types/core";
2
2
  import { Code, Setting, Severity } from "@clarity-types/data";
3
- import { Constant, Mask, NodeInfo, NodeMeta, NodeValue, Selector, SelectorInput, Source } from "@clarity-types/layout";
3
+ import { Constant, NodeInfo, NodeMeta, NodeValue, Selector, SelectorInput, Source } from "@clarity-types/layout";
4
4
  import config from "@src/core/config";
5
5
  import { bind } from "@src/core/event";
6
6
  import hash from "@src/core/hash";
@@ -9,6 +9,7 @@ import * as internal from "@src/diagnostic/internal";
9
9
  import { removeObserver } from "@src/layout/node";
10
10
  import * as region from "@src/layout/region";
11
11
  import * as selector from "@src/layout/selector";
12
+ import { MaskTextList, MaskDisableList, MaskExcludeList, MaskTagsList } from "./constants";
12
13
  let index: number = 1;
13
14
  let nodesMap: Map<Number, Node> = null; // Maps id => node to retrieve further node details using id.
14
15
  let values: NodeValue[] = [];
@@ -16,10 +17,10 @@ let updateMap: number[] = [];
16
17
  let hashMap: { [hash: string]: number } = {};
17
18
  let override = [];
18
19
  let unmask = [];
19
- let maskText = [];
20
- let maskExclude = [];
21
- let maskDisable = [];
22
- let maskTags = [];
20
+ let maskText: string[] = [];
21
+ let maskExclude: string[] = [];
22
+ let maskDisable: string[] = [];
23
+ let maskTags: string[] = [];
23
24
 
24
25
  // The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced
25
26
  let idMap: WeakMap<Node, number> = null; // Maps node => id.
@@ -44,10 +45,10 @@ function reset(): void {
44
45
  hashMap = {};
45
46
  override = [];
46
47
  unmask = [];
47
- maskText = Mask.Text.split(Constant.Comma);
48
- maskExclude = Mask.Exclude.split(Constant.Comma);
49
- maskDisable = Mask.Disable.split(Constant.Comma);
50
- maskTags = Mask.Tags.split(Constant.Comma);
48
+ maskText = MaskTextList;
49
+ maskExclude = MaskExcludeList;
50
+ maskDisable = MaskDisableList;
51
+ maskTags = MaskTagsList;
51
52
  nodesMap = new Map();
52
53
  idMap = new WeakMap();
53
54
  iframeMap = new WeakMap();
@@ -105,7 +105,7 @@ export default async function (type: Event, timer: Timer = null, ts: number = nu
105
105
  case "attributes":
106
106
  for (let attr in data[key]) {
107
107
  if (data[key][attr] !== undefined) {
108
- tokens.push(attribute(attr, data[key][attr], privacy));
108
+ tokens.push(attribute(attr, data[key][attr], privacy, data.tag));
109
109
  }
110
110
  }
111
111
  break;
@@ -149,6 +149,9 @@ function str(input: number): string {
149
149
  return input.toString(36);
150
150
  }
151
151
 
152
- function attribute(key: string, value: string, privacy: Privacy): string {
152
+ function attribute(key: string, value: string, privacy: Privacy, tag: string): string {
153
+ if (key === Constant.Href && tag === Constant.LinkTag) {
154
+ return `${key}=${value}`;
155
+ }
153
156
  return `${key}=${scrub.text(value, key.indexOf(Constant.DataAttribute) === 0 ? Constant.DataAttribute : key, privacy)}`;
154
157
  }
@@ -20,17 +20,19 @@ export function start(): void {
20
20
  region.start();
21
21
  dom.start();
22
22
  if (config.delayDom) {
23
- // Lazy load layout module as part of page load time performance improvements experiment
23
+ // Lazy load layout module as part of page load time performance improvements experiment
24
24
  bind(window, 'load', () => {
25
25
  mutation.start();
26
26
  });
27
27
  } else {
28
28
  mutation.start();
29
29
  }
30
+ // IMPORTANT: Start custom element detection BEFORE discover
31
+ // This ensures pre-existing custom elements are registered before DOM traversal
32
+ custom.start();
30
33
  discover.start();
31
34
  style.start();
32
35
  animation.start();
33
- custom.start();
34
36
  }
35
37
 
36
38
  export function stop(): void {
@@ -352,7 +352,7 @@ function proxyStyleRules(win: any): void {
352
352
  // by injecting CSS using insertRule API vs. appending text node. A side effect of
353
353
  // using javascript API is that it doesn't trigger DOM mutation and therefore we
354
354
  // need to override the insertRule API and listen for changes manually.
355
- if (win.clarityOverrides.InsertRule === undefined) {
355
+ if ("CSSStyleSheet" in win && win.CSSStyleSheet && win.CSSStyleSheet.prototype && win.clarityOverrides.InsertRule === undefined) {
356
356
  win.clarityOverrides.InsertRule = win.CSSStyleSheet.prototype.insertRule;
357
357
  win.CSSStyleSheet.prototype.insertRule = function (): number {
358
358
  if (core.active()) {
@@ -362,7 +362,7 @@ function proxyStyleRules(win: any): void {
362
362
  };
363
363
  }
364
364
 
365
- if ("CSSMediaRule" in win && win.clarityOverrides.MediaInsertRule === undefined) {
365
+ if ("CSSMediaRule" in win && win.CSSMediaRule && win.CSSMediaRule.prototype && win.clarityOverrides.MediaInsertRule === undefined) {
366
366
  win.clarityOverrides.MediaInsertRule = win.CSSMediaRule.prototype.insertRule;
367
367
  win.CSSMediaRule.prototype.insertRule = function (): number {
368
368
  if (core.active()) {
@@ -372,7 +372,7 @@ function proxyStyleRules(win: any): void {
372
372
  };
373
373
  }
374
374
 
375
- if (win.clarityOverrides.DeleteRule === undefined) {
375
+ if ("CSSStyleSheet" in win && win.CSSStyleSheet && win.CSSStyleSheet.prototype && win.clarityOverrides.DeleteRule === undefined) {
376
376
  win.clarityOverrides.DeleteRule = win.CSSStyleSheet.prototype.deleteRule;
377
377
  win.CSSStyleSheet.prototype.deleteRule = function (): void {
378
378
  if (core.active()) {
@@ -382,7 +382,7 @@ function proxyStyleRules(win: any): void {
382
382
  };
383
383
  }
384
384
 
385
- if ("CSSMediaRule" in win && win.clarityOverrides.MediaDeleteRule === undefined) {
385
+ if ("CSSMediaRule" in win && win.CSSMediaRule && win.CSSMediaRule.prototype && win.clarityOverrides.MediaDeleteRule === undefined) {
386
386
  win.clarityOverrides.MediaDeleteRule = win.CSSMediaRule.prototype.deleteRule;
387
387
  win.CSSMediaRule.prototype.deleteRule = function (): void {
388
388
  if (core.active()) {
@@ -395,7 +395,7 @@ function proxyStyleRules(win: any): void {
395
395
  // Add a hook to attachShadow API calls
396
396
  // In case we are unable to add a hook and browser throws an exception,
397
397
  // reset attachShadow variable and resume processing like before
398
- if (win.clarityOverrides.AttachShadow === undefined) {
398
+ if ("Element" in win && win.Element && win.Element.prototype && win.clarityOverrides.AttachShadow === undefined) {
399
399
  win.clarityOverrides.AttachShadow = win.Element.prototype.attachShadow;
400
400
  try {
401
401
  win.Element.prototype.attachShadow = function (): ShadowRoot {
@@ -1,7 +1,8 @@
1
1
  import { Character } from "../../types/data";
2
2
  import { Constant, Selector, SelectorInput } from "../../types/layout";
3
+ import { ExcludeClassNamesList } from "./constants";
3
4
 
4
- const excludeClassNames = Constant.ExcludeClassNames.split(Constant.Comma);
5
+ const excludeClassNames = ExcludeClassNamesList;
5
6
 
6
7
  let extraExcludeClassNames: string[] = [];
7
8
  let selectorMap: { [selector: string]: number[] } = {};
@@ -57,9 +57,10 @@ const clarityJsPath = join(__dirname, "../build/clarity.min.js");
57
57
  /**
58
58
  * Sets up a cookie mock for data: URLs which don't support cookies natively.
59
59
  * Handles both cookie setting and deletion (via max-age or empty values).
60
+ * @param initialCookieValue - Optional initial cookie value (e.g., "marketing_id=abc123")
60
61
  */
61
- function setupCookieMock() {
62
- let cookieStore = "";
62
+ function setupCookieMock(initialCookieValue?: string): void {
63
+ let cookieStore = initialCookieValue || "";
63
64
  Object.defineProperty(document, "cookie", {
64
65
  get: () => cookieStore,
65
66
  set: (value: string) => {
@@ -101,7 +102,7 @@ test.describe("consentv2 - Production API", () => {
101
102
  // ========================
102
103
 
103
104
  test("implicit denied: track=false results in denied consent", async ({ page }) => {
104
- await page.evaluate(setupCookieMock);
105
+ await page.evaluate(setupCookieMock as () => void);
105
106
 
106
107
  // Verify initial state (before Clarity starts) - no cookies should exist
107
108
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -263,7 +264,7 @@ test.describe("consentv2 - Production API", () => {
263
264
  });
264
265
 
265
266
  test("consentv2 explicit denial: track=false → denied/denied remains without cookies", async ({ page }) => {
266
- await page.evaluate(setupCookieMock);
267
+ await page.evaluate(setupCookieMock as () => void);
267
268
 
268
269
  // Verify initial state - no cookies before Clarity starts
269
270
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -333,7 +334,7 @@ test.describe("consentv2 - Production API", () => {
333
334
  });
334
335
 
335
336
  test("consentv2 mixed consent: track=false → denied analytics, granted ads no cookies", async ({ page }) => {
336
- await page.evaluate(setupCookieMock);
337
+ await page.evaluate(setupCookieMock as () => void);
337
338
 
338
339
  // Verify initial state - no cookies before Clarity starts
339
340
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -400,7 +401,7 @@ test.describe("consentv2 - Production API", () => {
400
401
  });
401
402
 
402
403
  test("consentv2 mixed consent: track=false → granted analytics, denied ads sets cookies", async ({ page }) => {
403
- await page.evaluate(setupCookieMock);
404
+ await page.evaluate(setupCookieMock as () => void);
404
405
 
405
406
  // Verify initial state - no cookies before Clarity starts
406
407
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -468,7 +469,7 @@ test.describe("consentv2 - Production API", () => {
468
469
  });
469
470
 
470
471
  test("consentv2 grants consent: track=false → granted/granted sets cookies", async ({ page }) => {
471
- await page.evaluate(setupCookieMock);
472
+ await page.evaluate(setupCookieMock as () => void);
472
473
 
473
474
  // Verify initial state - no cookies before Clarity starts
474
475
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -573,7 +574,7 @@ test.describe("consentv2 - Production API", () => {
573
574
  // ========================
574
575
 
575
576
  test("implicit granted: track=true results in granted consent", async ({ page }) => {
576
- await page.evaluate(setupCookieMock);
577
+ await page.evaluate(setupCookieMock as () => void);
577
578
 
578
579
  // Verify initial state - no cookies before Clarity starts
579
580
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -648,7 +649,7 @@ test.describe("consentv2 - Production API", () => {
648
649
  });
649
650
 
650
651
  test("consentv2 revokes consent: track=true → denied/denied deletes cookies", async ({ page }) => {
651
- await page.evaluate(setupCookieMock);
652
+ await page.evaluate(setupCookieMock as () => void);
652
653
 
653
654
  // Verify initial state - no cookies before Clarity starts
654
655
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -750,7 +751,7 @@ test.describe("consentv2 - Production API", () => {
750
751
  });
751
752
 
752
753
  test("consentv2 mixed consent: track=true → granted analytics, denied ads keeps cookies", async ({ page }) => {
753
- await page.evaluate(setupCookieMock);
754
+ await page.evaluate(setupCookieMock as () => void);
754
755
 
755
756
  // Verify initial state - no cookies before Clarity starts
756
757
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -817,7 +818,7 @@ test.describe("consentv2 - Production API", () => {
817
818
  });
818
819
 
819
820
  test("consentv2 mixed consent: track=true → denied analytics, granted ads deletes cookies", async ({ page }) => {
820
- await page.evaluate(setupCookieMock);
821
+ await page.evaluate(setupCookieMock as () => void);
821
822
 
822
823
  // Verify initial state - no cookies before Clarity starts
823
824
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -887,7 +888,7 @@ test.describe("consentv2 - Production API", () => {
887
888
  });
888
889
 
889
890
  test("consentv2 maintains consent: track=true → granted/granted keeps cookies", async ({ page }) => {
890
- await page.evaluate(setupCookieMock);
891
+ await page.evaluate(setupCookieMock as () => void);
891
892
 
892
893
  // Verify initial state - no cookies before Clarity starts
893
894
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -998,7 +999,7 @@ test.describe("consentv2 - Production API", () => {
998
999
  // ========================
999
1000
 
1000
1001
  test("consent v1: track=false → consent(true) grants consent and sets cookies", async ({ page }) => {
1001
- await page.evaluate(setupCookieMock);
1002
+ await page.evaluate(setupCookieMock as () => void);
1002
1003
 
1003
1004
  // Verify initial state - no cookies before Clarity starts
1004
1005
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -1064,7 +1065,7 @@ test.describe("consentv2 - Production API", () => {
1064
1065
  });
1065
1066
 
1066
1067
  test("consent v1: track=true → consent(false) revokes consent and deletes cookies", async ({ page }) => {
1067
- await page.evaluate(setupCookieMock);
1068
+ await page.evaluate(setupCookieMock as () => void);
1068
1069
 
1069
1070
  // Verify initial state - no cookies before Clarity starts
1070
1071
  const initialState = await page.evaluate(({ sessionKey, cookieKey }) => {
@@ -1160,5 +1161,149 @@ test.describe("consentv2 - Production API", () => {
1160
1161
  expect(consentResult.clskCookieValue).toBe("");
1161
1162
  expect(consentResult.clckCookieValue).toBe("");
1162
1163
  });
1163
- });
1164
1164
 
1165
+ // ========================
1166
+ // Config cookies tests
1167
+ // ========================
1168
+
1169
+ interface ConfigCookieTestOptions {
1170
+ track: boolean;
1171
+ grantConsentAfterStart?: boolean;
1172
+ denyConsentAfterStart?: boolean;
1173
+ }
1174
+
1175
+ interface ConfigCookieTestResult {
1176
+ containsCookieValue: boolean;
1177
+ beforeChangeContainsCookie?: boolean;
1178
+ afterChangeContainsCookie?: boolean;
1179
+ }
1180
+
1181
+ /**
1182
+ * Helper to run config cookie tests with consistent setup.
1183
+ * Sets up cookie mock with marketing_id=abc123 and returns whether the cookie value appears in payloads.
1184
+ */
1185
+ async function runConfigCookieTest(page: any, options: ConfigCookieTestOptions): Promise<ConfigCookieTestResult> {
1186
+ // Set up cookie mock with initial marketing cookie before evaluating test logic
1187
+ await page.evaluate(setupCookieMock as (initialValue: string) => void, "marketing_id=abc123");
1188
+
1189
+ const result = await page.evaluate((opts: ConfigCookieTestOptions) => {
1190
+
1191
+ return new Promise((resolve) => {
1192
+ const payloadsBefore: string[] = [];
1193
+ const payloadsAfter: string[] = [];
1194
+ let phase = "before";
1195
+
1196
+ (window as any).clarity("start", {
1197
+ projectId: "test",
1198
+ track: opts.track,
1199
+ cookies: ["marketing_id"],
1200
+ upload: (payload: string) => {
1201
+ if (phase === "before") {
1202
+ payloadsBefore.push(payload);
1203
+ } else {
1204
+ payloadsAfter.push(payload);
1205
+ }
1206
+ }
1207
+ });
1208
+
1209
+ const hasConsentChange = opts.grantConsentAfterStart || opts.denyConsentAfterStart;
1210
+ // For denial tests, use longer delay to ensure initial upload completes before denial
1211
+ const consentChangeDelay = opts.denyConsentAfterStart
1212
+ ? (window as any).CONSENT_CALLBACK_TIMEOUT / 2
1213
+ : (window as any).COOKIE_SETUP_DELAY;
1214
+
1215
+ if (hasConsentChange) {
1216
+ setTimeout(() => {
1217
+ phase = "after";
1218
+ const adStorage = opts.grantConsentAfterStart ? "granted" : "denied";
1219
+ const analyticsStorage = opts.grantConsentAfterStart ? "granted" : "denied";
1220
+ (window as any).clarity("consentv2", { ad_Storage: adStorage, analytics_Storage: analyticsStorage });
1221
+ }, consentChangeDelay);
1222
+ }
1223
+
1224
+ // For denial tests, wait longer to allow for restart
1225
+ const totalTimeout = opts.denyConsentAfterStart
1226
+ ? consentChangeDelay + (window as any).CONSENT_CALLBACK_TIMEOUT
1227
+ : (window as any).CONSENT_CALLBACK_TIMEOUT;
1228
+
1229
+ setTimeout(() => {
1230
+ const allPayloads = [...payloadsBefore, ...payloadsAfter].join("");
1231
+ resolve({
1232
+ containsCookieValue: allPayloads.includes("abc123"),
1233
+ beforeChangeContainsCookie: payloadsBefore.join("").includes("abc123"),
1234
+ afterChangeContainsCookie: payloadsAfter.join("").includes("abc123")
1235
+ });
1236
+ }, totalTimeout);
1237
+ });
1238
+ }, options);
1239
+
1240
+ return result as ConfigCookieTestResult;
1241
+ }
1242
+
1243
+ test("config cookies: track=true logs config cookies as variables", async ({ page }) => {
1244
+ const result = await runConfigCookieTest(page, { track: true });
1245
+ expect(result.containsCookieValue).toBe(true);
1246
+ });
1247
+
1248
+ test("config cookies: track=false does not log config cookies", async ({ page }) => {
1249
+ const result = await runConfigCookieTest(page, { track: false });
1250
+ expect(result.containsCookieValue).toBe(false);
1251
+ });
1252
+
1253
+ test("config cookies: track=false then consentv2 grants consent logs config cookies", async ({ page }) => {
1254
+ const result = await runConfigCookieTest(page, { track: false, grantConsentAfterStart: true });
1255
+ expect(result.containsCookieValue).toBe(true);
1256
+ });
1257
+
1258
+ test("config cookies: track=true then consentv2 denies consent does not log cookies after restart", async ({ page }) => {
1259
+ const result = await runConfigCookieTest(page, { track: true, denyConsentAfterStart: true });
1260
+ // Before denial, cookies should be logged (track=true, implicit granted)
1261
+ expect(result.beforeChangeContainsCookie).toBe(true);
1262
+ // After restart with denied consent, cookies should NOT be logged
1263
+ expect(result.afterChangeContainsCookie).toBe(false);
1264
+ });
1265
+
1266
+ test("GCM error handling: handles misconfigured GTM gracefully", async ({ page }) => {
1267
+ await page.evaluate(setupCookieMock as () => void);
1268
+
1269
+ const result = await page.evaluate(({ clarityJs }) => {
1270
+ return new Promise<{ errorWasThrown: boolean }>((resolve) => {
1271
+ let errorWasThrown = false;
1272
+
1273
+ // Mock GTM with misconfigured consent state
1274
+ (window as any).google_tag_data = {
1275
+ ics: {
1276
+ addListener: function(_events: string[], callback: () => void) {
1277
+ setTimeout(() => callback(), 100);
1278
+ },
1279
+ // getConsentState throws error due to uninitialized internal state
1280
+ getConsentState: function() {
1281
+ errorWasThrown = true;
1282
+ throw new TypeError("Cannot read properties of undefined (reading 'usedContainerScopedDefaults')");
1283
+ }
1284
+ }
1285
+ };
1286
+
1287
+ // Load and execute Clarity
1288
+ eval(clarityJs);
1289
+
1290
+ // Start Clarity with track=true
1291
+ (window as any).clarity("start", {
1292
+ projectId: "test",
1293
+ track: true,
1294
+ upload: false
1295
+ });
1296
+
1297
+ // Wait for callback to be triggered and error to be thrown
1298
+ setTimeout(() => {
1299
+ resolve({
1300
+ errorWasThrown
1301
+ });
1302
+ }, 500);
1303
+ });
1304
+ }, { clarityJs: readFileSync(clarityJsPath, "utf-8") });
1305
+
1306
+ // Verify error was thrown and handled gracefully (test success proves it was caught)
1307
+ expect(result.errorWasThrown).toBe(true);
1308
+ });
1309
+ });
package/types/core.d.ts CHANGED
@@ -141,6 +141,7 @@ export interface Config {
141
141
  conversions?: boolean;
142
142
  includeSubdomains?: boolean;
143
143
  modules?: string[];
144
+ diagnostics?: boolean;
144
145
  excludeClassNames?: string[];
145
146
  externalSession?: boolean;
146
147
  userId?: string;
package/types/data.d.ts CHANGED
@@ -264,6 +264,7 @@ export const enum Setting {
264
264
  PlaybackBytesLimit = 10 * 1024 * 1024, // 10MB
265
265
  CollectionLimit = 128, // Number of unique entries for dimensions
266
266
  ClickPrecision = 32767, // 2^15 - 1
267
+ ClickParentTraversal = 10, // Maximum number of parent elements to traverse when computing relative click coordinates
267
268
  BoxPrecision = 100, // Up to 2 decimal points (e.g. 34.56)
268
269
  ScriptErrorLimit = 5, // Do not send the same script error more than 5 times per page
269
270
  DimensionLimit = 256, // Do not extract dimensions which are over 256 characters
@@ -11,6 +11,14 @@ export const enum BrowsingContext {
11
11
  Top = 3
12
12
  }
13
13
 
14
+ export const enum ClickSource {
15
+ Undefined = 0,
16
+ FirstParty = 1,
17
+ ThirdParty = 2,
18
+ Eval = 3,
19
+ Unknown = 4
20
+ }
21
+
14
22
  export const enum Setting {
15
23
  LookAhead = 500, // 500ms
16
24
  InputLookAhead = 1000, // 1s
@@ -131,6 +139,7 @@ export interface ClickData {
131
139
  tag: string;
132
140
  class: string;
133
141
  id: string;
142
+ source: ClickSource;
134
143
  }
135
144
 
136
145
  export interface TextInfo {
package/types/layout.d.ts CHANGED
@@ -56,13 +56,6 @@ export const enum RegionVisibility {
56
56
  ScrolledToEnd = 13
57
57
  }
58
58
 
59
- export const enum Mask {
60
- Text = "address,password,contact",
61
- Disable = "radio,checkbox,range,button,reset,submit",
62
- Exclude = "password,secret,pass,social,ssn,code,hidden",
63
- Tags = "INPUT,SELECT,TEXTAREA"
64
- }
65
-
66
59
  export const enum Constant {
67
60
  Empty = "",
68
61
  SvgPrefix = "svg:",
@@ -95,6 +88,7 @@ export const enum Constant {
95
88
  Object = "object",
96
89
  Function = "function",
97
90
  StyleTag = "STYLE",
91
+ LinkTag = "LINK",
98
92
  InputTag = "INPUT",
99
93
  IFrameTag = "IFRAME",
100
94
  ImageTag = "IMG",
@@ -128,7 +122,6 @@ export const enum Constant {
128
122
  ogType = "og:type",
129
123
  ogTitle = "og:title",
130
124
  SvgStyle = "svg:style",
131
- ExcludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat",
132
125
  StyleSheet = "stylesheet"
133
126
  }
134
127
 
@@ -1,5 +1,8 @@
1
1
  /* Helper Interface */
2
2
 
3
+ export const enum FunctionNames {
4
+ ClickSource = 1,
5
+ }
3
6
 
4
7
  // Reference: https://wicg.github.io/netinfo/#networkinformation-interface
5
8
  export interface NavigatorConnection extends EventTarget {