clarity-js 0.6.23

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 (87) hide show
  1. package/README.md +26 -0
  2. package/build/clarity.js +4479 -0
  3. package/build/clarity.min.js +1 -0
  4. package/build/clarity.module.js +4473 -0
  5. package/package.json +66 -0
  6. package/rollup.config.ts +38 -0
  7. package/src/clarity.ts +54 -0
  8. package/src/core/config.ts +21 -0
  9. package/src/core/copy.ts +3 -0
  10. package/src/core/event.ts +25 -0
  11. package/src/core/hash.ts +19 -0
  12. package/src/core/history.ts +69 -0
  13. package/src/core/index.ts +79 -0
  14. package/src/core/measure.ts +17 -0
  15. package/src/core/report.ts +27 -0
  16. package/src/core/scrub.ts +102 -0
  17. package/src/core/task.ts +180 -0
  18. package/src/core/time.ts +14 -0
  19. package/src/core/timeout.ts +10 -0
  20. package/src/core/version.ts +2 -0
  21. package/src/data/baseline.ts +89 -0
  22. package/src/data/compress.ts +31 -0
  23. package/src/data/custom.ts +18 -0
  24. package/src/data/dimension.ts +42 -0
  25. package/src/data/encode.ts +109 -0
  26. package/src/data/envelope.ts +46 -0
  27. package/src/data/index.ts +43 -0
  28. package/src/data/limit.ts +42 -0
  29. package/src/data/metadata.ts +232 -0
  30. package/src/data/metric.ts +51 -0
  31. package/src/data/ping.ts +36 -0
  32. package/src/data/summary.ts +34 -0
  33. package/src/data/token.ts +39 -0
  34. package/src/data/upgrade.ts +36 -0
  35. package/src/data/upload.ts +250 -0
  36. package/src/data/variable.ts +46 -0
  37. package/src/diagnostic/encode.ts +40 -0
  38. package/src/diagnostic/image.ts +23 -0
  39. package/src/diagnostic/index.ts +14 -0
  40. package/src/diagnostic/internal.ts +41 -0
  41. package/src/diagnostic/script.ts +45 -0
  42. package/src/global.ts +22 -0
  43. package/src/index.ts +8 -0
  44. package/src/interaction/click.ts +140 -0
  45. package/src/interaction/encode.ts +140 -0
  46. package/src/interaction/index.ts +45 -0
  47. package/src/interaction/input.ts +64 -0
  48. package/src/interaction/pointer.ts +108 -0
  49. package/src/interaction/resize.ts +30 -0
  50. package/src/interaction/scroll.ts +73 -0
  51. package/src/interaction/selection.ts +66 -0
  52. package/src/interaction/timeline.ts +65 -0
  53. package/src/interaction/unload.ts +25 -0
  54. package/src/interaction/visibility.ts +24 -0
  55. package/src/layout/box.ts +83 -0
  56. package/src/layout/discover.ts +27 -0
  57. package/src/layout/document.ts +46 -0
  58. package/src/layout/dom.ts +442 -0
  59. package/src/layout/encode.ts +111 -0
  60. package/src/layout/extract.ts +75 -0
  61. package/src/layout/index.ts +25 -0
  62. package/src/layout/mutation.ts +232 -0
  63. package/src/layout/node.ts +211 -0
  64. package/src/layout/offset.ts +19 -0
  65. package/src/layout/region.ts +143 -0
  66. package/src/layout/schema.ts +66 -0
  67. package/src/layout/selector.ts +24 -0
  68. package/src/layout/target.ts +44 -0
  69. package/src/layout/traverse.ts +28 -0
  70. package/src/performance/connection.ts +37 -0
  71. package/src/performance/encode.ts +40 -0
  72. package/src/performance/index.ts +15 -0
  73. package/src/performance/navigation.ts +31 -0
  74. package/src/performance/observer.ts +87 -0
  75. package/test/core.test.ts +82 -0
  76. package/test/helper.ts +104 -0
  77. package/test/html/core.html +17 -0
  78. package/test/tsconfig.test.json +6 -0
  79. package/tsconfig.json +21 -0
  80. package/tslint.json +33 -0
  81. package/types/core.d.ts +127 -0
  82. package/types/data.d.ts +344 -0
  83. package/types/diagnostic.d.ts +24 -0
  84. package/types/index.d.ts +30 -0
  85. package/types/interaction.d.ts +110 -0
  86. package/types/layout.d.ts +200 -0
  87. package/types/performance.d.ts +40 -0
@@ -0,0 +1,24 @@
1
+ import { Attributes, Constant } from "../../types/layout";
2
+
3
+ export default function(tag: string, prefix: string, attributes: Attributes, position: number): string {
4
+ let suffix = position ? `:nth-of-type(${position})` : Constant.Empty;
5
+ switch (tag) {
6
+ case "STYLE":
7
+ case "TITLE":
8
+ case "LINK":
9
+ case "META":
10
+ case Constant.TextTag:
11
+ case Constant.DocumentTag:
12
+ return Constant.Empty;
13
+ case "HTML":
14
+ return Constant.HTML;
15
+ default:
16
+ if (prefix === null) { return Constant.Empty; }
17
+ tag = tag.indexOf(Constant.SvgPrefix) === 0 ? tag.substr(Constant.SvgPrefix.length) : tag;
18
+ let selector = `${prefix}${tag}${suffix}`;
19
+ if (Constant.Class in attributes && attributes[Constant.Class].length > 0) {
20
+ selector = `${prefix}${tag}.${attributes.class.trim().split(/\s+/).join(".")}${suffix}`;
21
+ }
22
+ return selector;
23
+ }
24
+ }
@@ -0,0 +1,44 @@
1
+ import { Privacy } from "@clarity-types/core";
2
+ import { Event } from "@clarity-types/data";
3
+ import { TargetMetadata } from "@clarity-types/layout";
4
+ import hash from "@src/core/hash";
5
+ import { track } from "@src/layout/region";
6
+ import * as dom from "@src/layout/dom";
7
+ import * as mutation from "@src/layout/mutation";
8
+
9
+ export function target(evt: UIEvent): Node {
10
+ let path = evt.composed && evt.composedPath ? evt.composedPath() : null;
11
+ let node = (path && path.length > 0 ? path[0] : evt.target) as Node;
12
+ mutation.active(); // Mark active periods of time so mutations can continue uninterrupted
13
+ return node.nodeType === Node.DOCUMENT_NODE ? (node as Document).documentElement : node;
14
+ }
15
+
16
+ export function link(node: Node): HTMLAnchorElement {
17
+ while (node && node !== document) {
18
+ if (node.nodeType === Node.ELEMENT_NODE) {
19
+ let element = node as HTMLElement;
20
+ if (element.tagName === "A") {
21
+ return element as HTMLAnchorElement;
22
+ }
23
+ }
24
+ node = node.parentNode;
25
+ }
26
+ return null;
27
+ }
28
+
29
+ export function metadata(node: Node, event: Event): TargetMetadata {
30
+ // If the node is null, we return a reserved value for id: 0. Valid assignment of id begins from 1+.
31
+ let output: TargetMetadata = { id: 0, hash: null, selector: null, privacy: Privacy.Text, node };
32
+ if (node) {
33
+ let value = dom.get(node);
34
+ if (value !== null) {
35
+ output.id = value.id;
36
+ output.hash = value.selector ? hash(value.selector) : null;
37
+ output.selector = value.selector;
38
+ output.privacy = value.metadata.privacy;
39
+ if (value.region) { track(value.region, event); }
40
+ }
41
+ }
42
+
43
+ return output;
44
+ }
@@ -0,0 +1,28 @@
1
+ import { Task, Timer } from "@clarity-types/core";
2
+ import { Source } from "@clarity-types/layout";
3
+ import * as task from "@src/core/task";
4
+ import processNode from "./node";
5
+
6
+ export default async function(root: Node, timer: Timer, source: Source): Promise<void> {
7
+ let queue = [root];
8
+ while (queue.length > 0) {
9
+ let node = queue.shift();
10
+ let next = node.firstChild;
11
+
12
+ while (next) {
13
+ queue.push(next);
14
+ next = next.nextSibling;
15
+ }
16
+
17
+ // Check the status of current task to see if we should yield before continuing
18
+ let state = task.state(timer);
19
+ if (state === Task.Wait) { state = await task.suspend(timer); }
20
+ if (state === Task.Stop) { break; }
21
+
22
+ // Check if processing a node gives us a pointer to one of its sub nodes for traversal
23
+ // E.g. an element node may give us a pointer to traverse shadowDom if shadowRoot property is set
24
+ // Or, an iframe from the same origin could give a pointer to it's document for traversing contents of iframe.
25
+ let subnode = processNode(node, source);
26
+ if (subnode) { queue.push(subnode); }
27
+ }
28
+ }
@@ -0,0 +1,37 @@
1
+ import { BooleanFlag, Event } from "@clarity-types/data";
2
+ import { ConnectionData, NavigatorConnection } from "@clarity-types/performance";
3
+ import encode from "./encode";
4
+
5
+ // Reference: https://wicg.github.io/netinfo/
6
+ export let data: ConnectionData;
7
+
8
+ export function start(): void {
9
+ // Check if the client supports Navigator.Connection: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/connection
10
+ // This is an experimental API so we go a bit deeper in our check and ensure that values returned are valid
11
+ if (navigator &&
12
+ "connection" in navigator &&
13
+ "downlink" in navigator["connection"] &&
14
+ typeof navigator["connection"]["downlink"] === "number") {
15
+ (navigator["connection"] as NavigatorConnection).addEventListener("change", recompute);
16
+ recompute();
17
+ }
18
+ }
19
+
20
+ function recompute(): void {
21
+ let connection = navigator["connection"] as NavigatorConnection;
22
+ data = {
23
+ downlink: connection.downlink,
24
+ rtt: connection.rtt,
25
+ saveData: connection.saveData ? BooleanFlag.True : BooleanFlag.False,
26
+ type: connection.effectiveType
27
+ };
28
+ encode(Event.Connection);
29
+ }
30
+
31
+ export function reset(): void {
32
+ data = null;
33
+ }
34
+
35
+ export function stop(): void {
36
+ reset();
37
+ }
@@ -0,0 +1,40 @@
1
+ import {Event, Token} from "@clarity-types/data";
2
+ import { time } from "@src/core/time";
3
+ import { queue } from "@src/data/upload";
4
+ import * as connection from "@src/performance/connection";
5
+ import * as navigation from "@src/performance/navigation";
6
+
7
+ export default async function(type: Event): Promise<void> {
8
+ let t = time();
9
+ let tokens: Token[] = [t, type];
10
+ switch (type) {
11
+ case Event.Connection:
12
+ tokens.push(connection.data.downlink);
13
+ tokens.push(connection.data.rtt);
14
+ tokens.push(connection.data.saveData);
15
+ tokens.push(connection.data.type);
16
+ connection.reset();
17
+ queue(tokens, false);
18
+ break;
19
+ case Event.Navigation:
20
+ tokens.push(navigation.data.fetchStart);
21
+ tokens.push(navigation.data.connectStart);
22
+ tokens.push(navigation.data.connectEnd);
23
+ tokens.push(navigation.data.requestStart);
24
+ tokens.push(navigation.data.responseStart);
25
+ tokens.push(navigation.data.responseEnd);
26
+ tokens.push(navigation.data.domInteractive);
27
+ tokens.push(navigation.data.domComplete);
28
+ tokens.push(navigation.data.loadEventStart);
29
+ tokens.push(navigation.data.loadEventEnd);
30
+ tokens.push(navigation.data.redirectCount);
31
+ tokens.push(navigation.data.size);
32
+ tokens.push(navigation.data.type);
33
+ tokens.push(navigation.data.protocol);
34
+ tokens.push(navigation.data.encodedSize);
35
+ tokens.push(navigation.data.decodedSize);
36
+ navigation.reset();
37
+ queue(tokens, false);
38
+ break;
39
+ }
40
+ }
@@ -0,0 +1,15 @@
1
+ import * as connection from "@src/performance/connection";
2
+ import * as navigation from "@src/performance/navigation";
3
+ import * as observer from "@src/performance/observer";
4
+
5
+ export function start(): void {
6
+ navigation.reset();
7
+ connection.start();
8
+ observer.start();
9
+ }
10
+
11
+ export function stop(): void {
12
+ observer.stop();
13
+ connection.stop();
14
+ navigation.reset();
15
+ }
@@ -0,0 +1,31 @@
1
+ import { Event } from "@clarity-types/data";
2
+ import { NavigationData } from "@clarity-types/performance";
3
+ import encode from "./encode";
4
+
5
+ export let data: NavigationData = null;
6
+
7
+ export function reset(): void {
8
+ data = null;
9
+ }
10
+
11
+ export function compute(entry: PerformanceNavigationTiming): void {
12
+ data = {
13
+ fetchStart: Math.round(entry.fetchStart),
14
+ connectStart: Math.round(entry.connectStart),
15
+ connectEnd: Math.round(entry.connectEnd),
16
+ requestStart: Math.round(entry.requestStart),
17
+ responseStart: Math.round(entry.responseStart),
18
+ responseEnd: Math.round(entry.responseEnd),
19
+ domInteractive: Math.round(entry.domInteractive),
20
+ domComplete: Math.round(entry.domComplete),
21
+ loadEventStart: Math.round(entry.loadEventStart),
22
+ loadEventEnd: Math.round(entry.loadEventEnd),
23
+ redirectCount: Math.round(entry.redirectCount),
24
+ size: entry.transferSize ? entry.transferSize : 0,
25
+ type: entry.type,
26
+ protocol: entry.nextHopProtocol,
27
+ encodedSize: entry.encodedBodySize ? entry.encodedBodySize : 0,
28
+ decodedSize: entry.decodedBodySize ? entry.decodedBodySize : 0
29
+ };
30
+ encode(Event.Navigation);
31
+ }
@@ -0,0 +1,87 @@
1
+ import { Code, Constant, Dimension, Metric, Severity } from "@clarity-types/data";
2
+ import { bind } from "@src/core/event";
3
+ import measure from "@src/core/measure";
4
+ import { setTimeout } from "@src/core/timeout";
5
+ import * as dimension from "@src/data/dimension";
6
+ import * as metric from "@src/data/metric";
7
+ import * as internal from "@src/diagnostic/internal";
8
+ import * as navigation from "@src/performance/navigation";
9
+
10
+ let observer: PerformanceObserver;
11
+ const types: string[] = [Constant.Navigation, Constant.Resource, Constant.LongTask, Constant.FID, Constant.CLS, Constant.LCP];
12
+
13
+ export function start(): void {
14
+ // Check the browser support performance observer as a pre-requisite for any performance measurement
15
+ if (window["PerformanceObserver"] && PerformanceObserver.supportedEntryTypes) {
16
+ // Start monitoring performance data after page has finished loading.
17
+ // If the document.readyState is not yet complete, we intentionally call observe using a setTimeout.
18
+ // This allows us to capture loadEventEnd on navigation timeline.
19
+ if (document.readyState !== "complete") {
20
+ bind(window, "load", setTimeout.bind(this, observe, 0));
21
+ } else { observe(); }
22
+ } else { internal.log(Code.PerformanceObserver, Severity.Info); }
23
+ }
24
+
25
+ function observe(): void {
26
+ // Some browsers will throw an error for unsupported entryType, e.g. "layout-shift"
27
+ // In those cases, we log it as a warning and continue with rest of the Clarity processing
28
+ try {
29
+ if (observer) { observer.disconnect(); }
30
+ observer = new PerformanceObserver(measure(handle) as PerformanceObserverCallback);
31
+ // Reference: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/observe
32
+ // "buffered" flag indicates whether buffered entries should be queued into the observer's buffer.
33
+ // It must only be used only with the "type" option, and cannot be used with entryTypes.
34
+ // This is why we need to individually "observe" each supported type
35
+ for (let x of types) {
36
+ if (PerformanceObserver.supportedEntryTypes.indexOf(x) >= 0) {
37
+ // Initialize CLS with a value of zero. It's possible (and recommended) for sites to not have any cumulative layout shift.
38
+ // In those cases, we want to still initialize the metric in Clarity
39
+ if (x === Constant.CLS) { metric.sum(Metric.CumulativeLayoutShift, 0); }
40
+ observer.observe({type: x, buffered: true});
41
+ }
42
+ }
43
+ } catch { internal.log(Code.PerformanceObserver, Severity.Warning); }
44
+ }
45
+
46
+ function handle(entries: PerformanceObserverEntryList): void {
47
+ process(entries.getEntries());
48
+ }
49
+
50
+ function process(entries: PerformanceEntryList): void {
51
+ let visible = "visibilityState" in document ? document.visibilityState === "visible" : true;
52
+ for (let i = 0; i < entries.length; i++) {
53
+ let entry = entries[i];
54
+ switch (entry.entryType) {
55
+ case Constant.Navigation:
56
+ navigation.compute(entry as PerformanceNavigationTiming);
57
+ break;
58
+ case Constant.Resource:
59
+ dimension.log(Dimension.NetworkHosts, host(entry.name));
60
+ break;
61
+ case Constant.LongTask:
62
+ metric.count(Metric.LongTaskCount);
63
+ break;
64
+ case Constant.FID:
65
+ if (visible) { metric.max(Metric.FirstInputDelay, entry["processingStart"] - entry.startTime); }
66
+ break;
67
+ case Constant.CLS:
68
+ // Scale the value to avoid sending back floating point number
69
+ if (visible && !entry["hadRecentInput"]) { metric.sum(Metric.CumulativeLayoutShift, entry["value"] * 1000); }
70
+ break;
71
+ case Constant.LCP:
72
+ if (visible) { metric.max(Metric.LargestPaint, entry.startTime); }
73
+ break;
74
+ }
75
+ }
76
+ }
77
+
78
+ export function stop(): void {
79
+ if (observer) { observer.disconnect(); }
80
+ observer = null;
81
+ }
82
+
83
+ function host(url: string): string {
84
+ let a = document.createElement("a");
85
+ a.href = url;
86
+ return a.hostname;
87
+ }
@@ -0,0 +1,82 @@
1
+ import { expect } from 'chai';
2
+ import { Browser, Page } from 'playwright';
3
+ import { launch, markup, node, text } from './helper';
4
+ import { Data, decode } from "clarity-decode";
5
+
6
+ let browser: Browser;
7
+ let page: Page;
8
+
9
+ describe('Core Tests', () => {
10
+ before(async () => {
11
+ browser = await launch();
12
+ });
13
+
14
+ beforeEach(async () => {
15
+ page = await browser.newPage();
16
+ await page.goto('about:blank');
17
+ });
18
+
19
+ afterEach(async () => {
20
+ await page.close();
21
+ });
22
+
23
+ after(async () => {
24
+ await browser.close();
25
+ browser = null;
26
+ });
27
+
28
+ it('should mask sensitive content by default', async () => {
29
+ let encoded: string[] = await markup(page, "core.html");
30
+ let decoded = encoded.map(x => decode(x));
31
+ let heading = text(decoded, "one");
32
+ let address = text(decoded, "two");
33
+ let email = node(decoded, "attributes.id", "eml");
34
+ let password = node(decoded, "attributes.id", "pwd");
35
+ let search = node(decoded, "attributes.id", "search");
36
+
37
+ // Non-sensitive fields continue to pass through with sensitive bits masked off
38
+ expect(heading, "Thanks for your order •••••••••");
39
+
40
+ // Sensitive fields, including input fields, are randomized and masked
41
+ expect(address, "•••••• ••••• ••••• ••••• ••••• •••••");
42
+ expect(email.attributes.value, "••••• •••• •••• ••••");
43
+ expect(password.attributes.value, "••••• ••••");
44
+ expect(search.attributes.value, "••••• •••• ••••");
45
+ });
46
+
47
+ it('should mask all text in strict mode', async () => {
48
+ let encoded: string[] = await markup(page, "core.html", { content: false });
49
+ let decoded = encoded.map(x => decode(x));
50
+ let heading = text(decoded, "one");
51
+ let address = text(decoded, "two");
52
+ let email = node(decoded, "attributes.id", "eml");
53
+ let password = node(decoded, "attributes.id", "pwd");
54
+ let search = node(decoded, "attributes.id", "search");
55
+
56
+ // All fields are randomized and masked
57
+ expect(heading, "• ••••• ••••• ••••• ••••• •••••");
58
+ expect(address, "•••••• ••••• ••••• ••••• ••••• •••••");
59
+ expect(email.attributes.value, "••••• •••• •••• ••••");
60
+ expect(password.attributes.value, "••••• ••••");
61
+ expect(search.attributes.value, "••••• •••• ••••");
62
+ });
63
+
64
+ it('should mask all text in relaxed mode', async () => {
65
+ let encoded: string[] = await markup(page, "core.html", { unmask: ["body"] });
66
+ let decoded = encoded.map(x => decode(x));
67
+ let heading = text(decoded, "one");
68
+ let address = text(decoded, "two");
69
+ let email = node(decoded, "attributes.id", "eml");
70
+ let password = node(decoded, "attributes.id", "pwd");
71
+ let search = node(decoded, "attributes.id", "search");
72
+
73
+ // Text flows through unmasked for non-sensitive fields, including input fields
74
+ expect(heading, "Thanks for your order #2AB700GH");
75
+ expect(address, "1 Microsoft Way, Redmond, WA - 98052");
76
+ expect(search.attributes.value, "hello world");
77
+
78
+ // Sensitive fields are still masked
79
+ expect(email.attributes.value, "••••• •••• •••• ••••");
80
+ expect(password.attributes.value, "••••• ••••");
81
+ });
82
+ });
package/test/helper.ts ADDED
@@ -0,0 +1,104 @@
1
+ import { Core, Data, Layout } from "clarity-decode";
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import { Browser, Page, chromium } from 'playwright';
5
+
6
+ export async function launch(): Promise<Browser> {
7
+ return chromium.launch({ headless: true, args: ['--no-sandbox'] });
8
+ }
9
+
10
+ export async function markup(page: Page, file: string, override: Core.Config = null): Promise<string[]> {
11
+ const html = fs.readFileSync(path.resolve(__dirname, `./html/${file}`), 'utf8');
12
+ await page.setContent(html.replace("</body>", `
13
+ <script>
14
+ window.payloads = [];
15
+ ${fs.readFileSync(path.resolve(__dirname, `../build/clarity.min.js`), 'utf8')};
16
+ clarity("start", ${config(override)});
17
+ </script>
18
+ </body>
19
+ `));
20
+ await page.waitForFunction("payloads && payloads.length > 1");
21
+ return await page.evaluate('payloads');
22
+ }
23
+
24
+ export function node(decoded: Data.DecodedPayload[], key: string, value: string | number, tag: string = null): Layout.DomData {
25
+ let sub = null;
26
+
27
+ // Exploding nested keys into key and sub key
28
+ if (key.indexOf(".") > 0) {
29
+ const parts = key.split(".");
30
+ if (parts.length === 2) {
31
+ key = parts[0];
32
+ sub = parts[1];
33
+ }
34
+ }
35
+
36
+ // Walking over the decoded payload to find the right match
37
+ for (let i = decoded.length - 1; i > 0; i--) {
38
+ if (decoded[i].dom) {
39
+ for (let j = 0; j < decoded[i].dom.length; j++) {
40
+ if (decoded[i].dom[j].data) {
41
+ for (let k = 0; k < decoded[i].dom[j].data.length; k++) {
42
+ let d = decoded[i].dom[j].data[k];
43
+ if ((sub && d[key] && d[key][sub] === value) ||
44
+ (d[key] && d[key] === value)) {
45
+ if ((tag && d.tag === tag) || tag === null) {
46
+ return d;
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+
57
+ export function text(decoded: Data.DecodedPayload[], id: string): string {
58
+ let parent = node(decoded, "attributes.id", id);
59
+ if (parent) {
60
+ let child = node(decoded, "parent", parent.id, "*T");
61
+ if (child && child.value) {
62
+ return child.value;
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+
68
+
69
+ function config(override: Core.Config): string {
70
+ const settings = {
71
+ delay: 100,
72
+ content: true,
73
+ upload: payload => { window["payloads"].push(payload); window["clarity"]("upgrade", "test"); }
74
+ }
75
+
76
+ // Process overrides
77
+ if (override){
78
+ for (let key of Object.keys(override)) {
79
+ settings[key] = override[key];
80
+ }
81
+ }
82
+
83
+ // Serialize configuration
84
+ let output = "";
85
+ for (let key of Object.keys(settings)) {
86
+ switch (key) {
87
+ case "upload":
88
+ output += `${JSON.stringify(key)}: ${settings[key].toString()},`;
89
+ break;
90
+ case "projectId":
91
+ case "mask":
92
+ case "unmask":
93
+ case "regions":
94
+ case "cookies":
95
+ output += `${JSON.stringify(key)}: ${JSON.stringify(settings[key])},`;
96
+ break;
97
+ default:
98
+ output += `${JSON.stringify(key)}: ${settings[key]},`;
99
+ break;
100
+ }
101
+ }
102
+ output += `"projectId": "test"`;
103
+ return "{" + output + "}";
104
+ }
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Core Tests</title>
5
+ </head>
6
+ <body>
7
+ <div>
8
+ <h1 id="one">Thanks for your order #2AB700GH</h1>
9
+ <p id="two" class="address-details">1 Microsoft Way, Redmond, WA - 98052</p>
10
+ </div>
11
+ <form name="login">
12
+ <input type="email" id="eml" title="Email" value="random@email.test">
13
+ <input type="password" id="pwd" title="Password" maxlength="16" value="passw0rd">
14
+ <input type="search" id="search" title="Search" value="hello world">
15
+ </form>
16
+ </body>
17
+ </html>
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "module": "commonjs"
5
+ }
6
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "esnext",
4
+ "target": "es5",
5
+ "lib": ["es6", "dom", "es2016", "es2017"],
6
+ "moduleResolution": "node",
7
+ "forceConsistentCasingInFileNames": true,
8
+ "noImplicitReturns": true,
9
+ "noUnusedLocals": true,
10
+ "noUnusedParameters": true,
11
+ "resolveJsonModule": true,
12
+ "esModuleInterop": true,
13
+ "baseUrl": ".",
14
+ "paths": {
15
+ "@src/*": ["src/*"],
16
+ "@clarity-types/*": ["types/*"]
17
+ }
18
+ },
19
+ "include":["src/**/*.ts","types/**/*.d.ts"],
20
+ "exclude": ["test", "node_modules", "build"]
21
+ }
package/tslint.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "extends": "tslint:recommended",
3
+ "rules": {
4
+ "max-line-length": [
5
+ true,
6
+ 140
7
+ ],
8
+ "no-console": [
9
+ false
10
+ ],
11
+ "no-string-literal": false,
12
+ "prefer-const": false,
13
+ "prefer-for-of": false,
14
+ "object-literal-sort-keys": false,
15
+ "one-variable-per-declaration": false,
16
+ "trailing-comma": [
17
+ false
18
+ ],
19
+ "variable-name": false,
20
+ "interface-name": false,
21
+ "interface-over-type-literal": false,
22
+ "only-arrow-functions": false,
23
+ "typedef": [
24
+ true,
25
+ "call-signature",
26
+ "parameter",
27
+ "property-declaration",
28
+ "member-variable-declaration",
29
+ "object-destructuring",
30
+ "array-destructuring"
31
+ ]
32
+ }
33
+ }