@lowdefy/helpers 6.0.0 → 7.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.
- package/dist/appStorageKey.js +20 -0
- package/dist/buildPagePath.js +39 -0
- package/dist/builtinMessages.js +8 -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/extractErrorProps.js +64 -58
- package/dist/filterElementsChain.js +68 -0
- package/dist/findInteractiveControls.js +25 -0
- package/dist/getSafeHref.js +49 -0
- package/dist/index.js +26 -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/mapPlainValues.js +51 -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 +88 -0
- package/dist/readChainValue.js +42 -0
- package/dist/rolesHash.js +34 -0
- package/dist/serializer.js +10 -22
- 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,88 @@
|
|
|
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 { lowdefyErrorTypes, readErrorCodes } from '@lowdefy/errors';
|
|
16
|
+
import mapPlainValues from './mapPlainValues.js';
|
|
17
|
+
import type from './type.js';
|
|
18
|
+
function identity(value) {
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
function scrubStrings(value, scrub) {
|
|
22
|
+
return mapPlainValues(value, (item)=>type.isString(item) ? scrub(item) : item);
|
|
23
|
+
}
|
|
24
|
+
// Own-key lookup: an error named after an Object.prototype member, such as
|
|
25
|
+
// 'constructor', must fall back to Error rather than pick up that member.
|
|
26
|
+
function prototypeFor(name) {
|
|
27
|
+
if (Object.hasOwn(lowdefyErrorTypes, name)) {
|
|
28
|
+
return lowdefyErrorTypes[name].prototype;
|
|
29
|
+
}
|
|
30
|
+
return Error.prototype;
|
|
31
|
+
}
|
|
32
|
+
function projectNode(error, scrub, seen) {
|
|
33
|
+
seen.add(error);
|
|
34
|
+
const projected = Object.create(prototypeFor(error.name));
|
|
35
|
+
// Non-enumerable, as on a constructed Error, so JSON.stringify skips it: an
|
|
36
|
+
// `_error` sent to a plugin as data (an HTTP body, a database insert) then
|
|
37
|
+
// carries no message. Assigning it would make it enumerable, which is also
|
|
38
|
+
// why this does not go through serializer.copy.
|
|
39
|
+
Object.defineProperty(projected, 'message', {
|
|
40
|
+
value: scrub(error.message),
|
|
41
|
+
enumerable: false,
|
|
42
|
+
writable: true,
|
|
43
|
+
configurable: true
|
|
44
|
+
});
|
|
45
|
+
const { code, statusCode } = readErrorCodes(error);
|
|
46
|
+
const fields = {
|
|
47
|
+
name: error.name,
|
|
48
|
+
code: type.isString(code) ? scrub(code) : code,
|
|
49
|
+
statusCode,
|
|
50
|
+
// The server's error sink sets `handled` once it has logged the error, and
|
|
51
|
+
// the client skips reporting a handled error back to the server. A rethrown
|
|
52
|
+
// `_error` without it would be logged a second time.
|
|
53
|
+
handled: error.handled,
|
|
54
|
+
// A ServiceError's Retry-After (seconds, or an HTTP date), so a routine can wait as long
|
|
55
|
+
// as the service asked before retrying, such as an enrichment worker's backoff.
|
|
56
|
+
retryAfter: error.name === 'ServiceError' ? error.retryAfter ?? undefined : undefined
|
|
57
|
+
};
|
|
58
|
+
for (const [key, value] of Object.entries(fields)){
|
|
59
|
+
if (!type.isUndefined(value)) projected[key] = value;
|
|
60
|
+
}
|
|
61
|
+
const { cause } = error;
|
|
62
|
+
if (type.isError(cause)) {
|
|
63
|
+
if (!seen.has(cause)) projected.cause = projectNode(cause, scrub, seen);
|
|
64
|
+
} else if (error.name === 'UserError') {
|
|
65
|
+
// The author wrote a UserError's cause and metaData in config; any other
|
|
66
|
+
// class's non-Error cause is library data, such as a response body.
|
|
67
|
+
if (!type.isUndefined(cause)) projected.cause = scrubStrings(cause, scrub);
|
|
68
|
+
}
|
|
69
|
+
if (error.name === 'UserError' && !type.isUndefined(error.metaData)) {
|
|
70
|
+
projected.metaData = scrubStrings(error.metaData, scrub);
|
|
71
|
+
}
|
|
72
|
+
return projected;
|
|
73
|
+
}
|
|
74
|
+
// The value `_error` reads: the caught error rebuilt with only the fields a
|
|
75
|
+
// routine or catch list branches on (name, message, code, statusCode, handled, a
|
|
76
|
+
// ServiceError's retryAfter, and the cause). `received`, `configKey`, `location`,
|
|
77
|
+
// `source`, `config` and `stack` are left out - `received` can hold a token
|
|
78
|
+
// fetched at runtime that the scrub does not know. The result is an Error, so
|
|
79
|
+
// wherever config sends it on, the serializer's wire policy applies to it.
|
|
80
|
+
function projectCaughtError(error, { scrub = identity } = {}) {
|
|
81
|
+
if (!type.isError(error)) {
|
|
82
|
+
return projectCaughtError(new Error(String(error)), {
|
|
83
|
+
scrub
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return projectNode(error, scrub, new Set());
|
|
87
|
+
}
|
|
88
|
+
export default projectCaughtError;
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
*/ // Reads a quoted value from `start` (just past its opening quote). posthog-js escapes a quote
|
|
16
|
+
// in a value as \". Returns the value and the index past its closing quote, or the end of the
|
|
17
|
+
// chain when the quote is never closed.
|
|
18
|
+
function readChainValue(chain, start) {
|
|
19
|
+
let value = '';
|
|
20
|
+
let index = start;
|
|
21
|
+
while(index < chain.length){
|
|
22
|
+
const char = chain[index];
|
|
23
|
+
if (char === '\\' && chain[index + 1] === '"') {
|
|
24
|
+
value += '"';
|
|
25
|
+
index += 2;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (char === '"') {
|
|
29
|
+
return {
|
|
30
|
+
value,
|
|
31
|
+
end: index + 1
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
value += char;
|
|
35
|
+
index += 1;
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
value,
|
|
39
|
+
end: index
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export default readChainValue;
|
|
@@ -0,0 +1,34 @@
|
|
|
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 stampHash from './stampHash.js';
|
|
16
|
+
// The stamp hash of a caller's roles in their active organization: the app
|
|
17
|
+
// roles (_user.roles, which page and endpoint auth gates match) and the
|
|
18
|
+
// organization role tier (_user.org_roles: owner, admin or member), since app
|
|
19
|
+
// config can gate blocks on either. Each list is sorted, so the same set always
|
|
20
|
+
// hashes the same, and the pair is JSON-encoded, so no separator inside a role
|
|
21
|
+
// can make two lists read as one. The server stamps it on every response
|
|
22
|
+
// (callerStamp) and the client compares it with the roles of the caller it
|
|
23
|
+
// renders, so both sides use this one function.
|
|
24
|
+
function rolesHash({ user }) {
|
|
25
|
+
return stampHash(JSON.stringify({
|
|
26
|
+
roles: [
|
|
27
|
+
...user.roles ?? []
|
|
28
|
+
].sort(),
|
|
29
|
+
org_roles: [
|
|
30
|
+
...user.org_roles ?? []
|
|
31
|
+
].sort()
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
export default rolesHash;
|
package/dist/serializer.js
CHANGED
|
@@ -12,23 +12,10 @@
|
|
|
12
12
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
|
-
*/ import {
|
|
15
|
+
*/ import { lowdefyErrorTypes } from '@lowdefy/errors';
|
|
16
16
|
import extractErrorProps from './extractErrorProps.js';
|
|
17
17
|
import type from './type.js';
|
|
18
18
|
import stableStringify from './stableStringify.js';
|
|
19
|
-
const lowdefyErrorTypes = {
|
|
20
|
-
ActionError,
|
|
21
|
-
BlockError,
|
|
22
|
-
BuildError,
|
|
23
|
-
ConfigError,
|
|
24
|
-
ConfigWarning,
|
|
25
|
-
LowdefyInternalError,
|
|
26
|
-
OperatorError,
|
|
27
|
-
PluginError,
|
|
28
|
-
RequestError,
|
|
29
|
-
ServiceError,
|
|
30
|
-
UserError
|
|
31
|
-
};
|
|
32
19
|
function propsToError(data) {
|
|
33
20
|
const ErrorClass = lowdefyErrorTypes[data.name] || Error;
|
|
34
21
|
const error = Object.create(ErrorClass.prototype);
|
|
@@ -41,7 +28,7 @@ function propsToError(data) {
|
|
|
41
28
|
}
|
|
42
29
|
return error;
|
|
43
30
|
}
|
|
44
|
-
const makeReplacer = ({ replacer, isoStringDates, skipMarkers,
|
|
31
|
+
const makeReplacer = ({ replacer, isoStringDates, skipMarkers, projectError } = {})=>(key, value)=>{
|
|
45
32
|
let dateReplacer = (date)=>({
|
|
46
33
|
'~d': date.valueOf()
|
|
47
34
|
});
|
|
@@ -57,7 +44,7 @@ const makeReplacer = ({ replacer, isoStringDates, skipMarkers, omitErrorProps }
|
|
|
57
44
|
if (type.isError(newValue)) {
|
|
58
45
|
return {
|
|
59
46
|
'~e': extractErrorProps(newValue, {
|
|
60
|
-
|
|
47
|
+
project: projectError
|
|
61
48
|
})
|
|
62
49
|
};
|
|
63
50
|
}
|
|
@@ -219,12 +206,13 @@ const serialize = (json, options = {})=>{
|
|
|
219
206
|
'~d': json.valueOf()
|
|
220
207
|
};
|
|
221
208
|
}
|
|
222
|
-
//
|
|
223
|
-
//
|
|
209
|
+
// Markers survive unless the caller passes skipMarkers: config sent to the
|
|
210
|
+
// client needs them to resolve error locations, data does not.
|
|
224
211
|
return JSON.parse(JSON.stringify(json, makeReplacer({
|
|
225
212
|
replacer: options.replacer,
|
|
226
213
|
isoStringDates: options.isoStringDates,
|
|
227
|
-
|
|
214
|
+
skipMarkers: options.skipMarkers,
|
|
215
|
+
projectError: options.projectError
|
|
228
216
|
})));
|
|
229
217
|
};
|
|
230
218
|
const serializeToString = (json, options = {})=>{
|
|
@@ -240,7 +228,7 @@ const serializeToString = (json, options = {})=>{
|
|
|
240
228
|
replacer: makeReplacer({
|
|
241
229
|
replacer: options.replacer,
|
|
242
230
|
skipMarkers: options.skipMarkers,
|
|
243
|
-
|
|
231
|
+
projectError: options.projectError
|
|
244
232
|
}),
|
|
245
233
|
space: options.space
|
|
246
234
|
});
|
|
@@ -249,7 +237,7 @@ const serializeToString = (json, options = {})=>{
|
|
|
249
237
|
replacer: options.replacer,
|
|
250
238
|
isoStringDates: options.isoStringDates,
|
|
251
239
|
skipMarkers: options.skipMarkers,
|
|
252
|
-
|
|
240
|
+
projectError: options.projectError
|
|
253
241
|
}), options.space);
|
|
254
242
|
};
|
|
255
243
|
const deserialize = (json, options = {})=>{
|
|
@@ -265,7 +253,7 @@ const copy = (json, options = {})=>{
|
|
|
265
253
|
if (type.isDate(json)) return new Date(json.valueOf());
|
|
266
254
|
return JSON.parse(JSON.stringify(json, makeReplacer({
|
|
267
255
|
replacer: options.replacer,
|
|
268
|
-
|
|
256
|
+
projectError: options.projectError
|
|
269
257
|
})), makeReviver(options.reviver));
|
|
270
258
|
};
|
|
271
259
|
const serializer = {
|
|
@@ -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.0.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.0.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"
|