@anyreplay/browser 0.2.0 → 0.2.2
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/README.md +16 -9
- package/dist/index.cjs +94 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +94 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -504,6 +504,14 @@ interface ShellInfo {
|
|
|
504
504
|
appVersion?: string;
|
|
505
505
|
/** The hardware model (`iPhone15,2`, `Pixel 8`), ≤ 64. */
|
|
506
506
|
deviceModel?: string;
|
|
507
|
+
/**
|
|
508
|
+
* Called the moment a chunk number is reserved, before the request that
|
|
509
|
+
* uses it leaves. A shell that copies the recorder's keys into native
|
|
510
|
+
* storage copies them then: an app killed between a send and the shell's
|
|
511
|
+
* next periodic copy came back with the old number, and sent its first
|
|
512
|
+
* chunk under a number the server already held (seen on an iOS simulator).
|
|
513
|
+
*/
|
|
514
|
+
onStateChanged?: () => void;
|
|
507
515
|
}
|
|
508
516
|
type RecorderStatus = 'idle' | 'awaiting-consent' | 'recording' | 'sampled-out' | 'stopped';
|
|
509
517
|
interface IdentifyTraits {
|
package/dist/index.d.ts
CHANGED
|
@@ -504,6 +504,14 @@ interface ShellInfo {
|
|
|
504
504
|
appVersion?: string;
|
|
505
505
|
/** The hardware model (`iPhone15,2`, `Pixel 8`), ≤ 64. */
|
|
506
506
|
deviceModel?: string;
|
|
507
|
+
/**
|
|
508
|
+
* Called the moment a chunk number is reserved, before the request that
|
|
509
|
+
* uses it leaves. A shell that copies the recorder's keys into native
|
|
510
|
+
* storage copies them then: an app killed between a send and the shell's
|
|
511
|
+
* next periodic copy came back with the old number, and sent its first
|
|
512
|
+
* chunk under a number the server already held (seen on an iOS simulator).
|
|
513
|
+
*/
|
|
514
|
+
onStateChanged?: () => void;
|
|
507
515
|
}
|
|
508
516
|
type RecorderStatus = 'idle' | 'awaiting-consent' | 'recording' | 'sampled-out' | 'stopped';
|
|
509
517
|
interface IdentifyTraits {
|
package/dist/index.js
CHANGED
|
@@ -3815,10 +3815,12 @@ var AssetCollector = class {
|
|
|
3815
3815
|
body: JSON.stringify(body)
|
|
3816
3816
|
});
|
|
3817
3817
|
const known = await post("/v1/ingest/assets/known", { projectKey, hashes: [sha256], ...app });
|
|
3818
|
-
if (known.ok) {
|
|
3819
|
-
|
|
3820
|
-
|
|
3818
|
+
if (!known.ok) {
|
|
3819
|
+
if (known.status === 401 || known.status === 403) this.stop();
|
|
3820
|
+
return false;
|
|
3821
3821
|
}
|
|
3822
|
+
const answer = await known.json();
|
|
3823
|
+
if (Array.isArray(answer.known) && answer.known.includes(sha256)) return true;
|
|
3822
3824
|
const upload = await post("/v1/ingest/assets", { projectKey, sha256, data: toBase64(bytes), ...app });
|
|
3823
3825
|
return upload.ok;
|
|
3824
3826
|
}
|
|
@@ -4123,6 +4125,13 @@ function looksLikeCardNumber(text) {
|
|
|
4123
4125
|
}
|
|
4124
4126
|
return sum % 10 === 0;
|
|
4125
4127
|
}
|
|
4128
|
+
function mayBecomeCardNumber(text) {
|
|
4129
|
+
if (!/^[2-6][0-9 -]*$/.test(text)) return false;
|
|
4130
|
+
const groups = text.split(/[ -]+/);
|
|
4131
|
+
if (groups.slice(0, -1).some((group) => group.length < 4)) return false;
|
|
4132
|
+
const digits = groups.join("").length;
|
|
4133
|
+
return digits > 6 && digits <= 19;
|
|
4134
|
+
}
|
|
4126
4135
|
function isMarkedByCustomer(node, maskClass) {
|
|
4127
4136
|
var _a;
|
|
4128
4137
|
let element = asElement(node);
|
|
@@ -4137,8 +4146,53 @@ function shouldMaskValue(text, node, policy) {
|
|
|
4137
4146
|
if (policy.maskEveryField) return true;
|
|
4138
4147
|
if (isSensitiveField(node)) return true;
|
|
4139
4148
|
if (isMarkedByCustomer(node, policy.maskClass)) return true;
|
|
4140
|
-
return
|
|
4149
|
+
return cardShaped(text);
|
|
4150
|
+
}
|
|
4151
|
+
var cardShaped = (text) => looksLikeCardNumber(text) || mayBecomeCardNumber(text);
|
|
4152
|
+
var DIGIT_RUN = /[0-9]+(?:[ -][0-9]+)*/g;
|
|
4153
|
+
function maskCardRuns(text) {
|
|
4154
|
+
if (!/[0-9]/.test(text)) return text;
|
|
4155
|
+
return text.replace(DIGIT_RUN, (run) => {
|
|
4156
|
+
const groups = run.split(/[ -]/);
|
|
4157
|
+
const starts = [];
|
|
4158
|
+
let at = 0;
|
|
4159
|
+
for (const group of groups) {
|
|
4160
|
+
starts.push(at);
|
|
4161
|
+
at += group.length + 1;
|
|
4162
|
+
}
|
|
4163
|
+
const chars2 = run.split("");
|
|
4164
|
+
let i = 0;
|
|
4165
|
+
while (i < groups.length) {
|
|
4166
|
+
let matched = -1;
|
|
4167
|
+
for (let j = groups.length - 1; j >= i; j -= 1) {
|
|
4168
|
+
const end2 = starts[j] + groups[j].length;
|
|
4169
|
+
if (cardShaped(run.slice(starts[i], end2).replace(/ /g, " "))) {
|
|
4170
|
+
matched = j;
|
|
4171
|
+
break;
|
|
4172
|
+
}
|
|
4173
|
+
}
|
|
4174
|
+
if (matched < 0) {
|
|
4175
|
+
i += 1;
|
|
4176
|
+
continue;
|
|
4177
|
+
}
|
|
4178
|
+
const end = starts[matched] + groups[matched].length;
|
|
4179
|
+
for (let k = starts[i]; k < end; k += 1) if (chars2[k] !== " " && chars2[k] !== "\xA0" && chars2[k] !== "-") chars2[k] = "*";
|
|
4180
|
+
i = matched + 1;
|
|
4181
|
+
}
|
|
4182
|
+
return chars2.join("");
|
|
4183
|
+
});
|
|
4141
4184
|
}
|
|
4185
|
+
var EDITABLE = '[contenteditable]:not([contenteditable="false"])';
|
|
4186
|
+
function insideEditable(node) {
|
|
4187
|
+
var _a;
|
|
4188
|
+
const live = node;
|
|
4189
|
+
const element = (live == null ? void 0 : live.nodeType) === 3 ? (_a = live.parentElement) != null ? _a : null : asElement(live);
|
|
4190
|
+
return typeof (element == null ? void 0 : element.closest) === "function" && element.closest(EDITABLE) !== null;
|
|
4191
|
+
}
|
|
4192
|
+
var isEditableSerialised = (attributes) => attributes.contenteditable !== void 0 && attributes.contenteditable !== "false";
|
|
4193
|
+
var typedCardShaped = (text) => cardShaped(text.replace(/\u00a0/g, " ").trim());
|
|
4194
|
+
var maskText = (text) => text.replace(/\S/g, "*");
|
|
4195
|
+
var maskTyped = (text) => typedCardShaped(text) ? maskText(text) : maskCardRuns(text);
|
|
4142
4196
|
var MUTATION = 0;
|
|
4143
4197
|
var INPUT = 5;
|
|
4144
4198
|
var ELEMENT_NODE = 2;
|
|
@@ -4153,23 +4207,36 @@ function maskTextChildren(node) {
|
|
|
4153
4207
|
maskTextChildren(child);
|
|
4154
4208
|
}
|
|
4155
4209
|
}
|
|
4156
|
-
function
|
|
4210
|
+
function maskCardRunsInChildren(node) {
|
|
4211
|
+
var _a;
|
|
4212
|
+
for (const child of (_a = node.childNodes) != null ? _a : []) {
|
|
4213
|
+
if (child.type === TEXT_NODE && typeof child.textContent === "string") child.textContent = maskCardRuns(child.textContent);
|
|
4214
|
+
}
|
|
4215
|
+
}
|
|
4216
|
+
function scrubTree(node, policy, masked, editable = false) {
|
|
4157
4217
|
var _a, _b;
|
|
4158
4218
|
if (!node) return;
|
|
4159
4219
|
const attributes = (_a = node.attributes) != null ? _a : {};
|
|
4160
4220
|
const inheritedMask = masked || hasClass(attributes, policy.maskClass);
|
|
4221
|
+
const inheritedEditable = editable || node.type === ELEMENT_NODE && isEditableSerialised(attributes);
|
|
4222
|
+
if (editable && node.type === TEXT_NODE && typeof node.textContent === "string") {
|
|
4223
|
+
node.textContent = maskTyped(node.textContent);
|
|
4224
|
+
}
|
|
4161
4225
|
if (node.type === ELEMENT_NODE && node.tagName && FIELD_TAGS.has(node.tagName.toLowerCase())) {
|
|
4162
4226
|
const value = attributes.value;
|
|
4163
|
-
const shouldMask = policy.maskEveryField || inheritedMask || isSensitiveSerialised(attributes) || typeof value === "string" &&
|
|
4227
|
+
const shouldMask = policy.maskEveryField || inheritedMask || isSensitiveSerialised(attributes) || typeof value === "string" && cardShaped(value);
|
|
4164
4228
|
if (shouldMask) {
|
|
4165
4229
|
if (typeof value === "string" && value !== "") attributes.value = maskValue(value);
|
|
4166
4230
|
if (node.tagName.toLowerCase() === "textarea") maskTextChildren(node);
|
|
4231
|
+
} else {
|
|
4232
|
+
if (typeof value === "string") attributes.value = maskCardRuns(value);
|
|
4233
|
+
if (node.tagName.toLowerCase() === "textarea") maskCardRunsInChildren(node);
|
|
4167
4234
|
}
|
|
4168
4235
|
}
|
|
4169
|
-
for (const child of (_b = node.childNodes) != null ? _b : []) scrubTree(child, policy, inheritedMask);
|
|
4236
|
+
for (const child of (_b = node.childNodes) != null ? _b : []) scrubTree(child, policy, inheritedMask, inheritedEditable);
|
|
4170
4237
|
}
|
|
4171
4238
|
function scrubEvent(event, getNode, policy) {
|
|
4172
|
-
var _a, _b, _c, _d, _e;
|
|
4239
|
+
var _a, _b, _c, _d, _e, _f;
|
|
4173
4240
|
if (!event.data) return;
|
|
4174
4241
|
if (event.type === 2) {
|
|
4175
4242
|
scrubTree(event.data.node, policy, false);
|
|
@@ -4182,7 +4249,7 @@ function scrubEvent(event, getNode, policy) {
|
|
|
4182
4249
|
const node = typeof data.id === "number" ? getNode(data.id) : null;
|
|
4183
4250
|
const type = ((_b = (_a = asElement(node)) == null ? void 0 : _a.type) != null ? _b : "").toLowerCase();
|
|
4184
4251
|
if (type === "checkbox" || type === "radio") return;
|
|
4185
|
-
|
|
4252
|
+
data.text = shouldMaskValue(data.text, node, policy) ? maskValue(data.text) : maskCardRuns(data.text);
|
|
4186
4253
|
return;
|
|
4187
4254
|
}
|
|
4188
4255
|
if (data.source !== MUTATION) return;
|
|
@@ -4190,11 +4257,15 @@ function scrubEvent(event, getNode, policy) {
|
|
|
4190
4257
|
const value = (_d = entry == null ? void 0 : entry.attributes) == null ? void 0 : _d.value;
|
|
4191
4258
|
if (typeof value !== "string" || value === "") continue;
|
|
4192
4259
|
const node = typeof entry.id === "number" ? getNode(entry.id) : null;
|
|
4193
|
-
|
|
4260
|
+
entry.attributes.value = shouldMaskValue(value, node, policy) ? maskValue(value) : maskCardRuns(value);
|
|
4194
4261
|
}
|
|
4195
4262
|
for (const add of (_e = data.adds) != null ? _e : []) {
|
|
4196
4263
|
const parent = typeof (add == null ? void 0 : add.parentId) === "number" ? getNode(add.parentId) : null;
|
|
4197
|
-
scrubTree(add == null ? void 0 : add.node, policy, isMarkedByCustomer(parent, policy.maskClass));
|
|
4264
|
+
scrubTree(add == null ? void 0 : add.node, policy, isMarkedByCustomer(parent, policy.maskClass), insideEditable(parent));
|
|
4265
|
+
}
|
|
4266
|
+
for (const change of (_f = data.texts) != null ? _f : []) {
|
|
4267
|
+
if (typeof (change == null ? void 0 : change.value) !== "string" || !/[0-9]/.test(change.value)) continue;
|
|
4268
|
+
if (typeof change.id === "number" && insideEditable(getNode(change.id))) change.value = maskTyped(change.value);
|
|
4198
4269
|
}
|
|
4199
4270
|
}
|
|
4200
4271
|
|
|
@@ -5000,7 +5071,7 @@ var Transport = class {
|
|
|
5000
5071
|
|
|
5001
5072
|
// src/version.ts
|
|
5002
5073
|
var SDK_NAME = "@anyreplay/browser";
|
|
5003
|
-
var SDK_VERSION = true ? "0.2.
|
|
5074
|
+
var SDK_VERSION = true ? "0.2.2" : "0.0.0-dev";
|
|
5004
5075
|
var PLATFORM = "web";
|
|
5005
5076
|
|
|
5006
5077
|
// src/visitor.ts
|
|
@@ -5107,6 +5178,8 @@ function forgetVisitor(local, session) {
|
|
|
5107
5178
|
}
|
|
5108
5179
|
|
|
5109
5180
|
// src/recorder.ts
|
|
5181
|
+
var RRWEB_DOM_CONTENT_LOADED = 0;
|
|
5182
|
+
var RRWEB_LOAD = 1;
|
|
5110
5183
|
var RAGE_CLICK_COUNT = 3;
|
|
5111
5184
|
var RAGE_CLICK_WINDOW_MS = 1e3;
|
|
5112
5185
|
var RAGE_CLICK_RADIUS_PX = 30;
|
|
@@ -5246,7 +5319,14 @@ function createRecorder(rawOptions, shell) {
|
|
|
5246
5319
|
initialSeq: who.nextSeq
|
|
5247
5320
|
},
|
|
5248
5321
|
{
|
|
5249
|
-
onSeqReserved: (next) =>
|
|
5322
|
+
onSeqReserved: (next) => {
|
|
5323
|
+
var _a;
|
|
5324
|
+
reserveSeq(session, next);
|
|
5325
|
+
try {
|
|
5326
|
+
(_a = shell == null ? void 0 : shell.onStateChanged) == null ? void 0 : _a.call(shell);
|
|
5327
|
+
} catch (e) {
|
|
5328
|
+
}
|
|
5329
|
+
},
|
|
5250
5330
|
onStopped: (reason) => {
|
|
5251
5331
|
status = "stopped";
|
|
5252
5332
|
stopRecording == null ? void 0 : stopRecording();
|
|
@@ -5304,6 +5384,7 @@ function createRecorder(rawOptions, shell) {
|
|
|
5304
5384
|
stopRecording = record({
|
|
5305
5385
|
emit: (event) => {
|
|
5306
5386
|
const typed = event;
|
|
5387
|
+
if (typed.type === RRWEB_DOM_CONTENT_LOADED || typed.type === RRWEB_LOAD) return;
|
|
5307
5388
|
scrubEvent(typed, (id) => record.mirror.getNode(id), policy);
|
|
5308
5389
|
assets == null ? void 0 : assets.scan(typed);
|
|
5309
5390
|
noteInteraction(typed, channel);
|