@lowdefy/helpers 6.2.0 → 7.1.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.
- package/dist/appStorageKey.js +20 -0
- package/dist/buildPagePath.js +39 -0
- package/dist/builtinMessages.js +7 -0
- package/dist/callerHash.js +28 -0
- package/dist/callerStamp.js +59 -0
- package/dist/cleanBuildArtifact.js +24 -0
- package/dist/createTraceId.js +35 -0
- package/dist/filterElementsChain.js +68 -0
- package/dist/findInteractiveControls.js +25 -0
- package/dist/getSafeHref.js +49 -0
- package/dist/index.js +24 -1
- package/dist/isChainHeaderEnd.js +33 -0
- package/dist/isInteractiveControl.js +22 -0
- package/dist/isMountEventName.js +27 -0
- package/dist/isTraceId.js +21 -0
- package/dist/journeyTargetSelectors.js +103 -0
- package/dist/listHash.js +25 -0
- package/dist/normalizeCaller.js +30 -0
- package/dist/pageInstanceKey.js +36 -0
- package/dist/pairTraceEvents.js +102 -0
- package/dist/parseCallerStamp.js +46 -0
- package/dist/parseElementsChain.js +58 -0
- package/dist/parsePageId.js +23 -0
- package/dist/parsePathPattern.js +88 -0
- package/dist/parseRowIndex.js +24 -0
- package/dist/projectCaughtError.js +6 -2
- package/dist/readChainValue.js +42 -0
- package/dist/rolesHash.js +34 -0
- package/dist/serializer.js +3 -2
- package/dist/sha256Hex.js +170 -0
- package/dist/stampHash.js +24 -0
- package/dist/targetFromElementsChain.js +103 -0
- package/dist/traceIdDate.js +23 -0
- package/package.json +5 -3
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ // SHA-256 round constants: the first 32 bits of the fractional parts of the
|
|
16
|
+
// cube roots of the first 64 primes.
|
|
17
|
+
const K = [
|
|
18
|
+
0x428a2f98,
|
|
19
|
+
0x71374491,
|
|
20
|
+
0xb5c0fbcf,
|
|
21
|
+
0xe9b5dba5,
|
|
22
|
+
0x3956c25b,
|
|
23
|
+
0x59f111f1,
|
|
24
|
+
0x923f82a4,
|
|
25
|
+
0xab1c5ed5,
|
|
26
|
+
0xd807aa98,
|
|
27
|
+
0x12835b01,
|
|
28
|
+
0x243185be,
|
|
29
|
+
0x550c7dc3,
|
|
30
|
+
0x72be5d74,
|
|
31
|
+
0x80deb1fe,
|
|
32
|
+
0x9bdc06a7,
|
|
33
|
+
0xc19bf174,
|
|
34
|
+
0xe49b69c1,
|
|
35
|
+
0xefbe4786,
|
|
36
|
+
0x0fc19dc6,
|
|
37
|
+
0x240ca1cc,
|
|
38
|
+
0x2de92c6f,
|
|
39
|
+
0x4a7484aa,
|
|
40
|
+
0x5cb0a9dc,
|
|
41
|
+
0x76f988da,
|
|
42
|
+
0x983e5152,
|
|
43
|
+
0xa831c66d,
|
|
44
|
+
0xb00327c8,
|
|
45
|
+
0xbf597fc7,
|
|
46
|
+
0xc6e00bf3,
|
|
47
|
+
0xd5a79147,
|
|
48
|
+
0x06ca6351,
|
|
49
|
+
0x14292967,
|
|
50
|
+
0x27b70a85,
|
|
51
|
+
0x2e1b2138,
|
|
52
|
+
0x4d2c6dfc,
|
|
53
|
+
0x53380d13,
|
|
54
|
+
0x650a7354,
|
|
55
|
+
0x766a0abb,
|
|
56
|
+
0x81c2c92e,
|
|
57
|
+
0x92722c85,
|
|
58
|
+
0xa2bfe8a1,
|
|
59
|
+
0xa81a664b,
|
|
60
|
+
0xc24b8b70,
|
|
61
|
+
0xc76c51a3,
|
|
62
|
+
0xd192e819,
|
|
63
|
+
0xd6990624,
|
|
64
|
+
0xf40e3585,
|
|
65
|
+
0x106aa070,
|
|
66
|
+
0x19a4c116,
|
|
67
|
+
0x1e376c08,
|
|
68
|
+
0x2748774c,
|
|
69
|
+
0x34b0bcb5,
|
|
70
|
+
0x391c0cb3,
|
|
71
|
+
0x4ed8aa4a,
|
|
72
|
+
0x5b9cca4f,
|
|
73
|
+
0x682e6ff3,
|
|
74
|
+
0x748f82ee,
|
|
75
|
+
0x78a5636f,
|
|
76
|
+
0x84c87814,
|
|
77
|
+
0x8cc70208,
|
|
78
|
+
0x90befffa,
|
|
79
|
+
0xa4506ceb,
|
|
80
|
+
0xbef9a3f7,
|
|
81
|
+
0xc67178f2
|
|
82
|
+
];
|
|
83
|
+
// SHA-256 initial hash values: the first 32 bits of the fractional parts of the
|
|
84
|
+
// square roots of the first 8 primes.
|
|
85
|
+
const H0 = [
|
|
86
|
+
0x6a09e667,
|
|
87
|
+
0xbb67ae85,
|
|
88
|
+
0x3c6ef372,
|
|
89
|
+
0xa54ff53a,
|
|
90
|
+
0x510e527f,
|
|
91
|
+
0x9b05688c,
|
|
92
|
+
0x1f83d9ab,
|
|
93
|
+
0x5be0cd19
|
|
94
|
+
];
|
|
95
|
+
function rotateRight(value, bits) {
|
|
96
|
+
return value >>> bits | value << 32 - bits;
|
|
97
|
+
}
|
|
98
|
+
function utf8Bytes(text) {
|
|
99
|
+
const bytes = [];
|
|
100
|
+
for (const character of text){
|
|
101
|
+
const code = character.codePointAt(0);
|
|
102
|
+
if (code < 0x80) {
|
|
103
|
+
bytes.push(code);
|
|
104
|
+
} else if (code < 0x800) {
|
|
105
|
+
bytes.push(0xc0 | code >> 6, 0x80 | code & 0x3f);
|
|
106
|
+
} else if (code < 0x10000) {
|
|
107
|
+
bytes.push(0xe0 | code >> 12, 0x80 | code >> 6 & 0x3f, 0x80 | code & 0x3f);
|
|
108
|
+
} else {
|
|
109
|
+
bytes.push(0xf0 | code >> 18, 0x80 | code >> 12 & 0x3f, 0x80 | code >> 6 & 0x3f, 0x80 | code & 0x3f);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return bytes;
|
|
113
|
+
}
|
|
114
|
+
// SHA-256 (FIPS 180-4) of a string's UTF-8 bytes, as lowercase hex.
|
|
115
|
+
function sha256Hex(text) {
|
|
116
|
+
const bytes = utf8Bytes(text);
|
|
117
|
+
const bitLength = bytes.length * 8;
|
|
118
|
+
bytes.push(0x80);
|
|
119
|
+
while(bytes.length % 64 !== 56){
|
|
120
|
+
bytes.push(0);
|
|
121
|
+
}
|
|
122
|
+
// The message length in bits as a 64-bit big-endian integer. Stamp inputs
|
|
123
|
+
// (ids, role and organization lists) are far below 2^32 bits, so the high
|
|
124
|
+
// word is zero.
|
|
125
|
+
bytes.push(0, 0, 0, 0);
|
|
126
|
+
bytes.push(bitLength >>> 24 & 0xff, bitLength >>> 16 & 0xff, bitLength >>> 8 & 0xff);
|
|
127
|
+
bytes.push(bitLength & 0xff);
|
|
128
|
+
const hash = [
|
|
129
|
+
...H0
|
|
130
|
+
];
|
|
131
|
+
const w = new Array(64);
|
|
132
|
+
for(let offset = 0; offset < bytes.length; offset += 64){
|
|
133
|
+
for(let i = 0; i < 16; i += 1){
|
|
134
|
+
const j = offset + i * 4;
|
|
135
|
+
w[i] = (bytes[j] << 24 | bytes[j + 1] << 16 | bytes[j + 2] << 8 | bytes[j + 3]) >>> 0;
|
|
136
|
+
}
|
|
137
|
+
for(let i = 16; i < 64; i += 1){
|
|
138
|
+
const s0 = rotateRight(w[i - 15], 7) ^ rotateRight(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
139
|
+
const s1 = rotateRight(w[i - 2], 17) ^ rotateRight(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
140
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
141
|
+
}
|
|
142
|
+
let [a, b, c, d, e, f, g, h] = hash;
|
|
143
|
+
for(let i = 0; i < 64; i += 1){
|
|
144
|
+
const S1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25);
|
|
145
|
+
const choice = e & f ^ ~e & g;
|
|
146
|
+
const temp1 = h + S1 + choice + K[i] + w[i] >>> 0;
|
|
147
|
+
const S0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22);
|
|
148
|
+
const majority = a & b ^ a & c ^ b & c;
|
|
149
|
+
const temp2 = S0 + majority >>> 0;
|
|
150
|
+
h = g;
|
|
151
|
+
g = f;
|
|
152
|
+
f = e;
|
|
153
|
+
e = d + temp1 >>> 0;
|
|
154
|
+
d = c;
|
|
155
|
+
c = b;
|
|
156
|
+
b = a;
|
|
157
|
+
a = temp1 + temp2 >>> 0;
|
|
158
|
+
}
|
|
159
|
+
hash[0] = hash[0] + a >>> 0;
|
|
160
|
+
hash[1] = hash[1] + b >>> 0;
|
|
161
|
+
hash[2] = hash[2] + c >>> 0;
|
|
162
|
+
hash[3] = hash[3] + d >>> 0;
|
|
163
|
+
hash[4] = hash[4] + e >>> 0;
|
|
164
|
+
hash[5] = hash[5] + f >>> 0;
|
|
165
|
+
hash[6] = hash[6] + g >>> 0;
|
|
166
|
+
hash[7] = hash[7] + h >>> 0;
|
|
167
|
+
}
|
|
168
|
+
return hash.map((word)=>word.toString(16).padStart(8, '0')).join('');
|
|
169
|
+
}
|
|
170
|
+
export default sha256Hex;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import sha256Hex from './sha256Hex.js';
|
|
16
|
+
// A short fingerprint for one part of a response's caller stamp (see
|
|
17
|
+
// callerStamp): the first 16 hex characters of SHA-256 of the text. It is an
|
|
18
|
+
// identity comparison, not a secret: SHA-256 is implemented in sha256Hex rather
|
|
19
|
+
// than taken from WebCrypto, which browsers expose only on https and localhost
|
|
20
|
+
// pages, so the server and every page compute the same value synchronously.
|
|
21
|
+
function stampHash(text) {
|
|
22
|
+
return sha256Hex(text).slice(0, 16);
|
|
23
|
+
}
|
|
24
|
+
export default stampHash;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import journeyTargetSelectors from './journeyTargetSelectors.js';
|
|
16
|
+
import parseElementsChain from './parseElementsChain.js';
|
|
17
|
+
import parseRowIndex from './parseRowIndex.js';
|
|
18
|
+
import type from './type.js';
|
|
19
|
+
const { blockWrapperPrefix, cellAttribute, interactiveRoles, rowAttribute } = journeyTargetSelectors;
|
|
20
|
+
function blockIdOf(entry) {
|
|
21
|
+
const id = entry.attributes.attr__id;
|
|
22
|
+
if (!type.isString(id) || !id.startsWith(blockWrapperPrefix)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
return id.slice(blockWrapperPrefix.length);
|
|
26
|
+
}
|
|
27
|
+
function normaliseText(value) {
|
|
28
|
+
if (!type.isString(value)) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const text = value.replace(/\s+/g, ' ').trim();
|
|
32
|
+
return text === '' ? null : text;
|
|
33
|
+
}
|
|
34
|
+
function isOption(entry) {
|
|
35
|
+
return entry.classes.includes('ant-select-item-option') || entry.attributes.attr__role === 'option';
|
|
36
|
+
}
|
|
37
|
+
// The chain form of the interactive-control selector. A radio or checkbox input inside a label
|
|
38
|
+
// is reached through the label, as the selector does.
|
|
39
|
+
function isInteractive(entry, position, scope) {
|
|
40
|
+
const { attributes, tag } = entry;
|
|
41
|
+
if (interactiveRoles.includes(attributes.attr__role)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (tag === 'input') {
|
|
45
|
+
if (attributes.attr__type === 'hidden') {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const isToggle = attributes.attr__type === 'radio' || attributes.attr__type === 'checkbox';
|
|
49
|
+
return !(isToggle && scope.slice(position + 1).some((outer)=>outer.tag === 'label'));
|
|
50
|
+
}
|
|
51
|
+
if (tag === 'a') {
|
|
52
|
+
return type.isString(attributes.href) || type.isString(attributes.attr__href);
|
|
53
|
+
}
|
|
54
|
+
return [
|
|
55
|
+
'button',
|
|
56
|
+
'textarea',
|
|
57
|
+
'select',
|
|
58
|
+
'label'
|
|
59
|
+
].includes(tag);
|
|
60
|
+
}
|
|
61
|
+
// The text of the control the click reached (an interactive control or a dropdown option): its
|
|
62
|
+
// own text, else (a label or option whose text sits in a child) the first text between the
|
|
63
|
+
// clicked element and it. posthog-js records the clicked element's direct text and the text of
|
|
64
|
+
// buttons, links and other form elements. A click that reached no control has no text, as in
|
|
65
|
+
// describeElement: its block alone targets it.
|
|
66
|
+
function textOf(scope) {
|
|
67
|
+
const controlIndex = scope.findIndex((entry, position)=>isInteractive(entry, position, scope) || isOption(entry));
|
|
68
|
+
if (controlIndex === -1) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
for (const entry of [
|
|
72
|
+
scope[controlIndex],
|
|
73
|
+
...scope.slice(0, controlIndex)
|
|
74
|
+
]){
|
|
75
|
+
const text = normaliseText(entry.attributes.text);
|
|
76
|
+
if (text !== null) {
|
|
77
|
+
return text;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
// The journey target a posthog-js `$elements_chain` describes: the block wrappers the click
|
|
83
|
+
// passed through (innermost first), the grid row and column, the control's text, and whether it
|
|
84
|
+
// was a dropdown option. Pure, so the production pull runs it in Node on captured events. Total:
|
|
85
|
+
// any string gives a target and never throws.
|
|
86
|
+
function targetFromElementsChain(chain) {
|
|
87
|
+
const entries = type.isString(chain) ? parseElementsChain(chain) : [];
|
|
88
|
+
const blockIds = entries.map(blockIdOf).filter((blockId)=>blockId !== null);
|
|
89
|
+
const blockIndex = entries.findIndex((entry)=>blockIdOf(entry) !== null);
|
|
90
|
+
const inner = blockIndex === -1 ? entries : entries.slice(0, blockIndex);
|
|
91
|
+
const scope = blockIndex === -1 ? entries : entries.slice(0, blockIndex + 1);
|
|
92
|
+
const rowEntry = inner.find((entry)=>`attr__${rowAttribute}` in entry.attributes);
|
|
93
|
+
const cellEntry = inner.find((entry)=>`attr__${cellAttribute}` in entry.attributes);
|
|
94
|
+
return {
|
|
95
|
+
block_id: blockIds[0] ?? null,
|
|
96
|
+
row: rowEntry ? parseRowIndex(rowEntry.attributes[`attr__${rowAttribute}`]) : null,
|
|
97
|
+
column: cellEntry ? cellEntry.attributes[`attr__${cellAttribute}`] : null,
|
|
98
|
+
text: textOf(scope),
|
|
99
|
+
option: inner.some(isOption),
|
|
100
|
+
block_ids: blockIds
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export default targetFromElementsChain;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/ import isTraceId from './isTraceId.js';
|
|
16
|
+
// The date directory a trace id's file lives in: `yyyy-mm-dd`.
|
|
17
|
+
function traceIdDate(id) {
|
|
18
|
+
if (!isTraceId(id)) {
|
|
19
|
+
throw new Error(`Invalid trace id. Received ${JSON.stringify(id)}.`);
|
|
20
|
+
}
|
|
21
|
+
return `${id.slice(0, 4)}-${id.slice(4, 6)}-${id.slice(6, 8)}`;
|
|
22
|
+
}
|
|
23
|
+
export default traceIdDate;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/helpers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"dist/*"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@lowdefy/errors": "
|
|
34
|
+
"@lowdefy/errors": "7.1.0",
|
|
35
|
+
"change-case": "5.4.0",
|
|
35
36
|
"intl-messageformat": "11.2.7"
|
|
36
37
|
},
|
|
37
38
|
"devDependencies": {
|
|
@@ -40,7 +41,8 @@
|
|
|
40
41
|
"@swc/core": "1.15.32",
|
|
41
42
|
"@swc/jest": "0.2.39",
|
|
42
43
|
"jest": "28.1.3",
|
|
43
|
-
"jest-diff": "28.1.3"
|
|
44
|
+
"jest-diff": "28.1.3",
|
|
45
|
+
"jest-environment-jsdom": "28.1.3"
|
|
44
46
|
},
|
|
45
47
|
"publishConfig": {
|
|
46
48
|
"access": "public"
|