clarity-js 0.6.37 → 0.6.40
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/build/clarity.js +132 -70
- package/build/clarity.min.js +1 -1
- package/build/clarity.module.js +132 -70
- package/package.json +1 -1
- package/src/core/scrub.ts +33 -10
- package/src/core/version.ts +1 -1
- package/src/index.ts +1 -1
- package/src/interaction/pointer.ts +1 -1
- package/src/layout/dom.ts +18 -14
- package/src/layout/encode.ts +1 -1
- package/src/layout/selector.ts +47 -27
- package/test/core.test.ts +26 -2
- package/test/helper.ts +30 -2
- package/test/html/core.html +1 -1
- package/types/data.d.ts +3 -1
- package/types/index.d.ts +6 -1
- package/types/layout.d.ts +13 -6
package/src/layout/dom.ts
CHANGED
|
@@ -5,7 +5,7 @@ import config from "@src/core/config";
|
|
|
5
5
|
import hash from "@src/core/hash";
|
|
6
6
|
import * as internal from "@src/diagnostic/internal";
|
|
7
7
|
import * as region from "@src/layout/region";
|
|
8
|
-
import selector from "@src/layout/selector";
|
|
8
|
+
import * as selector from "@src/layout/selector";
|
|
9
9
|
import * as mutation from "@src/layout/mutation";
|
|
10
10
|
import * as extract from "@src/data/extract";
|
|
11
11
|
let index: number = 1;
|
|
@@ -17,6 +17,7 @@ let override = [];
|
|
|
17
17
|
let unmask = [];
|
|
18
18
|
let updatedFragments: { [fragment: number]: string } = {};
|
|
19
19
|
let maskText = [];
|
|
20
|
+
let maskInput = [];
|
|
20
21
|
let maskDisable = [];
|
|
21
22
|
|
|
22
23
|
// The WeakMap object is a collection of key/value pairs in which the keys are weakly referenced
|
|
@@ -43,11 +44,13 @@ function reset(): void {
|
|
|
43
44
|
override = [];
|
|
44
45
|
unmask = [];
|
|
45
46
|
maskText = Mask.Text.split(Constant.Comma);
|
|
47
|
+
maskInput = Mask.Input.split(Constant.Comma);
|
|
46
48
|
maskDisable = Mask.Disable.split(Constant.Comma);
|
|
47
49
|
idMap = new WeakMap();
|
|
48
50
|
iframeMap = new WeakMap();
|
|
49
51
|
privacyMap = new WeakMap();
|
|
50
52
|
fraudMap = new WeakMap();
|
|
53
|
+
selector.reset();
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
// We parse new root nodes for any regions or masked nodes in the beginning (document) and
|
|
@@ -234,37 +237,37 @@ function privacy(node: Node, value: NodeValue, parent: NodeValue): void {
|
|
|
234
237
|
metadata.privacy = Privacy.Text;
|
|
235
238
|
break;
|
|
236
239
|
case tag === Constant.TextTag:
|
|
237
|
-
// If it's a text node belonging to a STYLE or TITLE tag or one of
|
|
240
|
+
// If it's a text node belonging to a STYLE or TITLE tag or one of scrub exceptions, then capture content
|
|
238
241
|
let pTag = parent && parent.data ? parent.data.tag : Constant.Empty;
|
|
239
|
-
let pSelector = parent && parent.selector ? parent.selector[Selector.
|
|
240
|
-
|
|
242
|
+
let pSelector = parent && parent.selector ? parent.selector[Selector.Default] : Constant.Empty;
|
|
243
|
+
let tags : string[] = [Constant.StyleTag, Constant.TitleTag, Constant.SvgStyle];
|
|
244
|
+
metadata.privacy = tags.includes(pTag) || override.some(x => pSelector.indexOf(x) >= 0) ? Privacy.None : current;
|
|
241
245
|
break;
|
|
242
246
|
case Constant.Type in attributes:
|
|
243
247
|
// If this node has an explicit type assigned to it, go through masking rules to determine right privacy setting
|
|
244
|
-
metadata.privacy = inspect(attributes[Constant.Type], metadata);
|
|
248
|
+
metadata.privacy = inspect(attributes[Constant.Type], maskInput, metadata);
|
|
245
249
|
break;
|
|
246
250
|
case tag === Constant.InputTag && current === Privacy.None:
|
|
247
251
|
// If even default privacy setting is to not mask, we still scan through input fields for any sensitive information
|
|
248
252
|
let field: string = Constant.Empty;
|
|
249
253
|
Object.keys(attributes).forEach(x => field += attributes[x].toLowerCase());
|
|
250
|
-
metadata.privacy = inspect(field, metadata);
|
|
254
|
+
metadata.privacy = inspect(field, maskInput, metadata);
|
|
251
255
|
break;
|
|
252
256
|
case current === Privacy.Sensitive && tag === Constant.InputTag:
|
|
257
|
+
// Look through class names to aggressively mask content
|
|
258
|
+
metadata.privacy = inspect(attributes[Constant.Class], maskText, metadata);
|
|
253
259
|
// If it's a button or an input option, make an exception to disable masking
|
|
254
260
|
metadata.privacy = maskDisable.indexOf(attributes[Constant.Type]) >= 0 ? Privacy.None : current;
|
|
255
261
|
break;
|
|
256
262
|
case current === Privacy.Sensitive:
|
|
257
263
|
// In a mode where we mask sensitive information by default, look through class names to aggressively mask content
|
|
258
|
-
metadata.privacy = inspect(attributes[Constant.Class], metadata);
|
|
259
|
-
break;
|
|
260
|
-
default:
|
|
261
|
-
metadata.privacy = parent ? parent.metadata.privacy : metadata.privacy;
|
|
264
|
+
metadata.privacy = inspect(attributes[Constant.Class], maskText, metadata);
|
|
262
265
|
break;
|
|
263
266
|
}
|
|
264
267
|
}
|
|
265
268
|
|
|
266
|
-
function inspect(input: string, metadata: NodeMeta): Privacy {
|
|
267
|
-
if (input &&
|
|
269
|
+
function inspect(input: string, lookup: string[], metadata: NodeMeta): Privacy {
|
|
270
|
+
if (input && lookup.some(x => input.indexOf(x) >= 0)) {
|
|
268
271
|
return Privacy.Text;
|
|
269
272
|
}
|
|
270
273
|
return metadata.privacy;
|
|
@@ -297,10 +300,11 @@ function updateSelector(value: NodeValue): void {
|
|
|
297
300
|
let prefix = parent ? parent.selector : null;
|
|
298
301
|
let d = value.data;
|
|
299
302
|
let p = position(parent, value);
|
|
300
|
-
let s: SelectorInput = { tag: d.tag, prefix, position: p, attributes: d.attributes };
|
|
301
|
-
value.selector = [selector(s), selector(s,
|
|
303
|
+
let s: SelectorInput = { id: value.id, tag: d.tag, prefix, position: p, attributes: d.attributes };
|
|
304
|
+
value.selector = [selector.get(s, Selector.Alpha), selector.get(s, Selector.Beta)];
|
|
302
305
|
value.hash = value.selector.map(x => x ? hash(x) : null) as [string, string];
|
|
303
306
|
value.hash.forEach(h => hashMap[h] = value.id);
|
|
307
|
+
// Match fragment configuration against both alpha and beta hash
|
|
304
308
|
if (value.hash.some(h => extract.fragments.indexOf(h) !== -1)) {
|
|
305
309
|
value.fragment = value.id;
|
|
306
310
|
}
|
package/src/layout/encode.ts
CHANGED
|
@@ -62,7 +62,7 @@ export default async function (type: Event, timer: Timer = null, ts: number = nu
|
|
|
62
62
|
if (value.parent && active) { tokens.push(value.parent); }
|
|
63
63
|
if (value.previous && active) { tokens.push(value.previous); }
|
|
64
64
|
tokens.push(suspend ? Constant.SuspendMutationTag : data[key]);
|
|
65
|
-
if (box && box.length === 2) { tokens.push(`${Constant.
|
|
65
|
+
if (box && box.length === 2) { tokens.push(`${Constant.Hash}${str(box[0])}.${str(box[1])}`); }
|
|
66
66
|
break;
|
|
67
67
|
case "attributes":
|
|
68
68
|
for (let attr in data[key]) {
|
package/src/layout/selector.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { Character } from "../../types/data";
|
|
2
2
|
import { Constant, Selector, SelectorInput } from "../../types/layout";
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const excludeClassNames = Constant.ExcludeClassNames.split(Constant.Comma);
|
|
5
|
+
let selectorMap: { [selector: string]: number[] } = {};
|
|
5
6
|
|
|
6
|
-
export
|
|
7
|
+
export function reset(): void {
|
|
8
|
+
selectorMap = {};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function get(input: SelectorInput, type: Selector): string {
|
|
7
12
|
let a = input.attributes;
|
|
8
|
-
let prefix = input.prefix ? input.prefix[
|
|
9
|
-
let suffix =
|
|
13
|
+
let prefix = input.prefix ? input.prefix[type] : null;
|
|
14
|
+
let suffix = type === Selector.Alpha ? `${Constant.Tilde}${input.position-1}` : `:nth-of-type(${input.position})`;
|
|
10
15
|
switch (input.tag) {
|
|
11
16
|
case "STYLE":
|
|
12
17
|
case "TITLE":
|
|
@@ -19,22 +24,28 @@ export default function(input: SelectorInput, beta: boolean = false): string {
|
|
|
19
24
|
return Constant.HTML;
|
|
20
25
|
default:
|
|
21
26
|
if (prefix === null) { return Constant.Empty; }
|
|
22
|
-
prefix = `${prefix}
|
|
27
|
+
prefix = `${prefix}${Constant.Separator}`;
|
|
23
28
|
input.tag = input.tag.indexOf(Constant.SvgPrefix) === 0 ? input.tag.substr(Constant.SvgPrefix.length) : input.tag;
|
|
24
29
|
let selector = `${prefix}${input.tag}${suffix}`;
|
|
25
|
-
let
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
let id = Constant.Id in a && a[Constant.Id].length > 0 ? a[Constant.Id] : null;
|
|
31
|
+
let classes = input.tag !== Constant.BodyTag && Constant.Class in a && a[Constant.Class].length > 0 ? a[Constant.Class].trim().split(/\s+/).filter(c => filter(c)).join(Constant.Period) : null;
|
|
32
|
+
if (classes && classes.length > 0) {
|
|
33
|
+
if (type === Selector.Alpha) {
|
|
34
|
+
// In Alpha mode, update selector to use class names, with relative positioning within the parent id container.
|
|
35
|
+
// If the node has valid class name(s) then drop relative positioning within the parent path to keep things simple.
|
|
36
|
+
let key = `${getDomPath(prefix)}${input.tag}${Constant.Dot}${classes}`;
|
|
37
|
+
if (!(key in selectorMap)) { selectorMap[key] = []; }
|
|
38
|
+
if (selectorMap[key].indexOf(input.id) < 0) { selectorMap[key].push(input.id); }
|
|
39
|
+
selector = `${key}${Constant.Tilde}${selectorMap[key].indexOf(input.id)}`;
|
|
40
|
+
} else {
|
|
41
|
+
// In Beta mode, we continue to look at query selectors in context of the full page
|
|
42
|
+
selector = `${prefix}${input.tag}.${classes}${suffix}`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Update selector to use "id" field when available. There are two exceptions:
|
|
46
|
+
// (1) if "id" appears to be an auto generated string token, e.g. guid or a random id containing digits
|
|
47
|
+
// (2) if "id" appears inside a shadow DOM, in which case we continue to prefix up to shadow DOM to prevent conflicts
|
|
48
|
+
selector = id && filter(id) ? `${getDomPrefix(prefix)}${Constant.Hash}${id}` : selector;
|
|
38
49
|
return selector;
|
|
39
50
|
}
|
|
40
51
|
}
|
|
@@ -44,19 +55,28 @@ function getDomPrefix(prefix: string): string {
|
|
|
44
55
|
const iframeDomStart = prefix.lastIndexOf(`${Constant.IFramePrefix}${Constant.HTML}`);
|
|
45
56
|
const domStart = Math.max(shadowDomStart, iframeDomStart);
|
|
46
57
|
|
|
47
|
-
if (domStart < 0) {
|
|
48
|
-
return "";
|
|
49
|
-
}
|
|
58
|
+
if (domStart < 0) { return Constant.Empty; }
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
return prefix.substring(0, prefix.indexOf(Constant.Separator, domStart) + 1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getDomPath(input: string): string {
|
|
64
|
+
let parts = input.split(Constant.Separator);
|
|
65
|
+
for (let i = 0; i < parts.length; i++) {
|
|
66
|
+
let tIndex = parts[i].indexOf(Constant.Tilde);
|
|
67
|
+
let dIndex = parts[i].indexOf(Constant.Dot);
|
|
68
|
+
parts[i] = parts[i].substring(0, dIndex > 0 ? dIndex : (tIndex > 0 ? tIndex : parts[i].length));
|
|
69
|
+
}
|
|
70
|
+
return parts.join(Constant.Separator);
|
|
53
71
|
}
|
|
54
72
|
|
|
55
|
-
// Check if the given input string has digits or
|
|
56
|
-
function
|
|
73
|
+
// Check if the given input string has digits or excluded class names
|
|
74
|
+
function filter(value: string): boolean {
|
|
75
|
+
if (!value) { return false; } // Do not process empty strings
|
|
76
|
+
if (excludeClassNames.some(x => value.toLowerCase().indexOf(x) >= 0)) { return false; }
|
|
57
77
|
for (let i = 0; i < value.length; i++) {
|
|
58
78
|
let c = value.charCodeAt(i);
|
|
59
|
-
if (c >= Character.Zero && c <= Character.Nine) { return
|
|
79
|
+
if (c >= Character.Zero && c <= Character.Nine) { return false };
|
|
60
80
|
}
|
|
61
|
-
return
|
|
81
|
+
return true;
|
|
62
82
|
}
|
package/test/core.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assert } from 'chai';
|
|
2
2
|
import { Browser, Page } from 'playwright';
|
|
3
|
-
import { launch, markup, node, text } from './helper';
|
|
3
|
+
import { clicks, inputs, launch, markup, node, text } from './helper';
|
|
4
4
|
import { Data, decode } from "clarity-decode";
|
|
5
5
|
|
|
6
6
|
let browser: Browser;
|
|
@@ -33,15 +33,21 @@ describe('Core Tests', () => {
|
|
|
33
33
|
let email = node(decoded, "attributes.id", "eml");
|
|
34
34
|
let password = node(decoded, "attributes.id", "pwd");
|
|
35
35
|
let search = node(decoded, "attributes.id", "search");
|
|
36
|
+
let click = clicks(decoded)[0];
|
|
37
|
+
let input = inputs(decoded)[0];
|
|
36
38
|
|
|
37
39
|
// Non-sensitive fields continue to pass through with sensitive bits masked off
|
|
38
|
-
assert.equal(heading, "Thanks for your order
|
|
40
|
+
assert.equal(heading, "Thanks for your order #••••••••");
|
|
39
41
|
|
|
40
42
|
// Sensitive fields, including input fields, are randomized and masked
|
|
41
43
|
assert.equal(address, "•••••• ••••• ••••• ••••• ••••• •••••");
|
|
42
44
|
assert.equal(email.attributes.value, "••••• •••• •••• ••••");
|
|
43
45
|
assert.equal(password.attributes.value, "••••• ••••");
|
|
44
46
|
assert.equal(search.attributes.value, "hello •••••");
|
|
47
|
+
|
|
48
|
+
// Clicked text and input value should be consistent with uber masking configuration
|
|
49
|
+
assert.equal(click.data.text, "Hello •••••");
|
|
50
|
+
assert.equal(input.data.value, "query with •••••••");
|
|
45
51
|
});
|
|
46
52
|
|
|
47
53
|
it('should mask all text in strict mode', async () => {
|
|
@@ -52,6 +58,8 @@ describe('Core Tests', () => {
|
|
|
52
58
|
let email = node(decoded, "attributes.id", "eml");
|
|
53
59
|
let password = node(decoded, "attributes.id", "pwd");
|
|
54
60
|
let search = node(decoded, "attributes.id", "search");
|
|
61
|
+
let click = clicks(decoded)[0];
|
|
62
|
+
let input = inputs(decoded)[0];
|
|
55
63
|
|
|
56
64
|
// All fields are randomized and masked
|
|
57
65
|
assert.equal(heading, "• ••••• ••••• ••••• ••••• •••••");
|
|
@@ -59,6 +67,10 @@ describe('Core Tests', () => {
|
|
|
59
67
|
assert.equal(email.attributes.value, "••••• •••• •••• ••••");
|
|
60
68
|
assert.equal(password.attributes.value, "••••• ••••");
|
|
61
69
|
assert.equal(search.attributes.value, "••••• •••• ••••");
|
|
70
|
+
|
|
71
|
+
// Clicked text and input value should also be masked in strict mode
|
|
72
|
+
assert.equal(click.data.text, "••••• •••• ••••");
|
|
73
|
+
assert.equal(input.data.value, "••••• •••• •••• ••••");
|
|
62
74
|
});
|
|
63
75
|
|
|
64
76
|
it('should unmask all text in relaxed mode', async () => {
|
|
@@ -69,6 +81,8 @@ describe('Core Tests', () => {
|
|
|
69
81
|
let email = node(decoded, "attributes.id", "eml");
|
|
70
82
|
let password = node(decoded, "attributes.id", "pwd");
|
|
71
83
|
let search = node(decoded, "attributes.id", "search");
|
|
84
|
+
let click = clicks(decoded)[0];
|
|
85
|
+
let input = inputs(decoded)[0];
|
|
72
86
|
|
|
73
87
|
// Text flows through unmasked for non-sensitive fields, including input fields
|
|
74
88
|
assert.equal(heading, "Thanks for your order #2AB700GH");
|
|
@@ -78,14 +92,24 @@ describe('Core Tests', () => {
|
|
|
78
92
|
// Sensitive fields are still masked
|
|
79
93
|
assert.equal(email.attributes.value, "••••• •••• •••• ••••");
|
|
80
94
|
assert.equal(password.attributes.value, "••••• ••••");
|
|
95
|
+
|
|
96
|
+
// Clicked text and input value (non-sensitive) both come through without masking in relaxed mode
|
|
97
|
+
assert.equal(click.data.text, "Hello Wor1d");
|
|
98
|
+
assert.equal(input.data.value, "query with numb3rs");
|
|
81
99
|
});
|
|
82
100
|
|
|
83
101
|
it('should respect mask config even in relaxed mode', async () => {
|
|
84
102
|
let encoded: string[] = await markup(page, "core.html", { mask: ["#mask"], unmask: ["body"] });
|
|
85
103
|
let decoded = encoded.map(x => decode(x));
|
|
86
104
|
let subtree = text(decoded, "child");
|
|
105
|
+
let click = clicks(decoded)[0];
|
|
106
|
+
let input = inputs(decoded)[0];
|
|
87
107
|
|
|
88
108
|
// Masked sub-trees continue to stay masked
|
|
89
109
|
assert.equal(subtree, "••••• •••••");
|
|
110
|
+
|
|
111
|
+
// Clicked text is masked due to masked configuration while input value is not masked in relaxed mode
|
|
112
|
+
assert.equal(click.data.text, "••••• •••• ••••");
|
|
113
|
+
assert.equal(input.data.value, "query with numb3rs");
|
|
90
114
|
});
|
|
91
115
|
});
|
package/test/helper.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Core, Data, Layout } from "clarity-decode";
|
|
1
|
+
import { Core, Data, decode, Interaction, Layout } from "clarity-decode";
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as url from 'url';
|
|
4
4
|
import * as path from 'path';
|
|
@@ -25,10 +25,38 @@ export async function markup(page: Page, file: string, override: Core.Config = n
|
|
|
25
25
|
</body>
|
|
26
26
|
`));
|
|
27
27
|
await page.hover("#two");
|
|
28
|
-
await page.
|
|
28
|
+
await page.click("#child");
|
|
29
|
+
await page.locator('#search').fill('query with numb3rs');
|
|
30
|
+
await page.waitForFunction("payloads && payloads.length > 2");
|
|
29
31
|
return await page.evaluate('payloads');
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
export function clicks(decoded: Data.DecodedPayload[]): Interaction.ClickEvent[] {
|
|
35
|
+
let output: Interaction.ClickEvent[] = [];
|
|
36
|
+
for (let i = decoded.length - 1; i >= 0; i--) {
|
|
37
|
+
if (decoded[i].click) {
|
|
38
|
+
for (let j = 0; j < decoded[i].click.length;j++)
|
|
39
|
+
{
|
|
40
|
+
output.push(decoded[i].click[j]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return output;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function inputs(decoded: Data.DecodedPayload[]): Interaction.InputEvent[] {
|
|
48
|
+
let output: Interaction.InputEvent[] = [];
|
|
49
|
+
for (let i = decoded.length - 1; i >= 0; i--) {
|
|
50
|
+
if (decoded[i].input) {
|
|
51
|
+
for (let j = 0; j < decoded[i].input.length;j++)
|
|
52
|
+
{
|
|
53
|
+
output.push(decoded[i].input[j]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return output;
|
|
58
|
+
}
|
|
59
|
+
|
|
32
60
|
export function node(decoded: Data.DecodedPayload[], key: string, value: string | number, tag: string = null): Layout.DomData {
|
|
33
61
|
let sub = null;
|
|
34
62
|
|
package/test/html/core.html
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
<p id="two" class="address-details">1 Microsoft Way, Redmond, WA - 98052</p>
|
|
10
10
|
</div>
|
|
11
11
|
<div id="mask">
|
|
12
|
-
<span id="child">Hello
|
|
12
|
+
<span id="child">Hello Wor1d</span>
|
|
13
13
|
</div>
|
|
14
14
|
<form name="login">
|
|
15
15
|
<input type="email" id="eml" title="Email" value="random@email.test">
|
package/types/data.d.ts
CHANGED
|
@@ -247,7 +247,9 @@ export const enum Constant {
|
|
|
247
247
|
UserId = "userId",
|
|
248
248
|
SessionId = "sessionId",
|
|
249
249
|
PageId = "pageId",
|
|
250
|
-
Mask = "•",
|
|
250
|
+
Mask = "•", // Placeholder character for explicitly masked content
|
|
251
|
+
Digit = "•", // Placeholder character for digits
|
|
252
|
+
Letter = "•", // Placeholder character for letters
|
|
251
253
|
SessionStorage = "sessionStorage",
|
|
252
254
|
Cookie = "cookie",
|
|
253
255
|
Navigation = "navigation",
|
package/types/index.d.ts
CHANGED
|
@@ -18,12 +18,17 @@ interface Clarity {
|
|
|
18
18
|
metadata: (callback: Data.MetadataCallback, wait?: boolean) => void;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
interface Selector {
|
|
22
|
+
get: (input: Layout.SelectorInput, type: Layout.Selector) => string;
|
|
23
|
+
reset: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
interface Helper {
|
|
22
27
|
get: (node: Node) => Layout.NodeValue;
|
|
23
28
|
getNode: (id: number) => Node;
|
|
24
29
|
hash: (input: string) => string;
|
|
25
30
|
lookup: (hash: string) => number;
|
|
26
|
-
selector:
|
|
31
|
+
selector: Selector;
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
declare const clarity: Clarity;
|
package/types/layout.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Privacy } from "@clarity-types/core";
|
|
2
|
-
import { BooleanFlag } from "@clarity-types/data";
|
|
3
2
|
|
|
4
3
|
/* Enum */
|
|
5
4
|
|
|
@@ -12,8 +11,9 @@ export const enum Source {
|
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
export const enum Selector {
|
|
15
|
-
|
|
16
|
-
Beta = 1
|
|
14
|
+
Alpha = 0,
|
|
15
|
+
Beta = 1,
|
|
16
|
+
Default = 1
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const enum InteractionState {
|
|
@@ -29,7 +29,8 @@ export const enum RegionVisibility {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export const enum Mask {
|
|
32
|
-
Text = "password,
|
|
32
|
+
Text = "address,password,contact",
|
|
33
|
+
Input = "password,secret,pass,social,ssn,name,code,dob,cell,mob,contact,hidden,account,cvv,ccv,email,tel,phone,address,addr,card,zip",
|
|
33
34
|
Disable = "radio,checkbox,range,button,reset,submit"
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -44,7 +45,10 @@ export const enum Constant {
|
|
|
44
45
|
Href = "href",
|
|
45
46
|
Src = "src",
|
|
46
47
|
Srcset = "srcset",
|
|
47
|
-
|
|
48
|
+
Hash = "#",
|
|
49
|
+
Dot = ".",
|
|
50
|
+
Separator = ">",
|
|
51
|
+
Tilde = "~",
|
|
48
52
|
Bang = "!",
|
|
49
53
|
Period = ".",
|
|
50
54
|
Comma = ",",
|
|
@@ -90,7 +94,9 @@ export const enum Constant {
|
|
|
90
94
|
Content = "content",
|
|
91
95
|
Generator = "generator",
|
|
92
96
|
ogType = "og:type",
|
|
93
|
-
ogTitle = "og:title"
|
|
97
|
+
ogTitle = "og:title",
|
|
98
|
+
SvgStyle = "svg:style",
|
|
99
|
+
ExcludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat"
|
|
94
100
|
}
|
|
95
101
|
|
|
96
102
|
export const enum JsonLD {
|
|
@@ -138,6 +144,7 @@ export interface Attributes {
|
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
export interface SelectorInput {
|
|
147
|
+
id: number;
|
|
141
148
|
tag: string;
|
|
142
149
|
prefix: [string, string];
|
|
143
150
|
position: number;
|