jumpy-lion 0.1.6-beta.0 → 0.1.6-beta.3
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 +13 -0
- package/dist/browser-controller.d.ts.map +1 -1
- package/dist/browser-controller.js +57 -2
- package/dist/browser-controller.js.map +1 -1
- package/dist/browser-plugin.d.ts +49 -0
- package/dist/browser-plugin.d.ts.map +1 -1
- package/dist/browser-plugin.js +103 -1
- package/dist/browser-plugin.js.map +1 -1
- package/dist/browser-process/anti-detect-browser.d.ts +56 -0
- package/dist/browser-process/anti-detect-browser.d.ts.map +1 -0
- package/dist/browser-process/anti-detect-browser.js +142 -0
- package/dist/browser-process/anti-detect-browser.js.map +1 -0
- package/dist/browser-process/anti-detect-config.d.ts +102 -0
- package/dist/browser-process/anti-detect-config.d.ts.map +1 -0
- package/dist/browser-process/anti-detect-config.js +328 -0
- package/dist/browser-process/anti-detect-config.js.map +1 -0
- package/dist/browser-process/browser.d.ts +26 -0
- package/dist/browser-process/browser.d.ts.map +1 -1
- package/dist/browser-process/browser.js +115 -6
- package/dist/browser-process/browser.js.map +1 -1
- package/dist/browser-process/get-chrome-executable.d.ts +7 -0
- package/dist/browser-process/get-chrome-executable.d.ts.map +1 -1
- package/dist/browser-process/get-chrome-executable.js +42 -0
- package/dist/browser-process/get-chrome-executable.js.map +1 -1
- package/dist/browser-process/index.d.ts +4 -1
- package/dist/browser-process/index.d.ts.map +1 -1
- package/dist/browser-process/index.js +3 -1
- package/dist/browser-process/index.js.map +1 -1
- package/dist/crawler.d.ts +17 -0
- package/dist/crawler.d.ts.map +1 -1
- package/dist/crawler.js +5 -2
- package/dist/crawler.js.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.d.ts +54 -0
- package/dist/fingerprinting/fingerprint-injector.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-injector.js +207 -58
- package/dist/fingerprinting/fingerprint-injector.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.js +16 -0
- package/dist/fingerprinting/fingerprint-overrides/audio-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js +3 -2
- package/dist/fingerprinting/fingerprint-overrides/cdp-detection-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js +18 -10
- package/dist/fingerprinting/fingerprint-overrides/datadome-bypass.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts +1 -2
- package/dist/fingerprinting/fingerprint-overrides/index.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/index.js +1 -2
- package/dist/fingerprinting/fingerprint-overrides/index.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts +10 -2
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js +11 -2
- package/dist/fingerprinting/fingerprint-overrides/keyboard-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.d.ts +20 -2
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js +47 -250
- package/dist/fingerprinting/fingerprint-overrides/locale-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.d.ts +12 -1
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.js +27 -22
- package/dist/fingerprinting/fingerprint-overrides/mouse-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js +86 -28
- package/dist/fingerprinting/fingerprint-overrides/platform-consistency.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts +9 -2
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js +10 -2
- package/dist/fingerprinting/fingerprint-overrides/scroll-humanization.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js +165 -33
- package/dist/fingerprinting/fingerprint-overrides/stealth-script.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.js +10 -8
- package/dist/fingerprinting/fingerprint-overrides/storage-consistency.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.js +9 -7
- package/dist/fingerprinting/fingerprint-overrides/timing-consistency.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/ua-ch.js +6 -6
- package/dist/fingerprinting/fingerprint-overrides/utils.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/utils.js +44 -1
- package/dist/fingerprinting/fingerprint-overrides/utils.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js +43 -1
- package/dist/fingerprinting/fingerprint-overrides/webgl-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.js +9 -1
- package/dist/fingerprinting/fingerprint-overrides/webgpu-spoofing.js.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.d.ts.map +1 -1
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js +14 -3
- package/dist/fingerprinting/fingerprint-overrides/webrtc-spoofing.js.map +1 -1
- package/dist/fingerprinting/locale-resolver.d.ts +41 -0
- package/dist/fingerprinting/locale-resolver.d.ts.map +1 -0
- package/dist/fingerprinting/locale-resolver.js +155 -0
- package/dist/fingerprinting/locale-resolver.js.map +1 -0
- package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts +32 -0
- package/dist/fingerprinting/non-apify-fingerprint-generator.d.ts.map +1 -0
- package/dist/fingerprinting/non-apify-fingerprint-generator.js +480 -0
- package/dist/fingerprinting/non-apify-fingerprint-generator.js.map +1 -0
- package/dist/keyboard-layout.d.ts +45 -0
- package/dist/keyboard-layout.d.ts.map +1 -0
- package/dist/keyboard-layout.js +162 -0
- package/dist/keyboard-layout.js.map +1 -0
- package/dist/page.d.ts +18 -0
- package/dist/page.d.ts.map +1 -1
- package/dist/page.js +96 -6
- package/dist/page.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -1
- package/dist/fingerprinting/fingerprint-overrides/coalesced-events-spoofing.d.ts +0 -10
- package/dist/fingerprinting/fingerprint-overrides/coalesced-events-spoofing.d.ts.map +0 -1
- package/dist/fingerprinting/fingerprint-overrides/coalesced-events-spoofing.js +0 -210
- package/dist/fingerprinting/fingerprint-overrides/coalesced-events-spoofing.js.map +0 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "jumpy-lion",
|
|
3
3
|
"description": "",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.1.6-beta.
|
|
5
|
+
"version": "0.1.6-beta.3",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.0.0"
|
|
8
8
|
},
|
|
@@ -31,7 +31,12 @@
|
|
|
31
31
|
"compile": "tsc -p tsconfig.build.json",
|
|
32
32
|
"copy-extension": "cp -r src/fingerprinting/all-fingerprint-defender dist/fingerprinting/ && cp -r src/fingerprinting/anti-webgpu dist/fingerprinting/",
|
|
33
33
|
"build": "npm run clean && npm run compile && npm run copy-extension",
|
|
34
|
+
"build:browser": "bash browser/scripts/build.sh",
|
|
35
|
+
"build:browser:docker": "bash browser/scripts/docker-build.sh",
|
|
36
|
+
"build:browser:linux": "bash browser/scripts/docker-build.sh --linux-only",
|
|
37
|
+
"build:browser:windows": "bash browser/scripts/docker-build.sh --windows-only",
|
|
34
38
|
"package:browser": "bash browser/scripts/package-binary.sh",
|
|
39
|
+
"bundle:browser": "node scripts/bundle-browser.cjs",
|
|
35
40
|
"release:browser": "node scripts/release-browser.cjs",
|
|
36
41
|
"postinstall": "node scripts/postinstall.cjs",
|
|
37
42
|
"ci:build": "turbo run build --cache-dir=\".turbo\"",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coalesced Events Spoofing
|
|
3
|
-
* Makes CDP-dispatched pointer events include realistic coalesced events.
|
|
4
|
-
*
|
|
5
|
-
* Real mouse movements produce PointerEvents where getCoalescedEvents() returns
|
|
6
|
-
* multiple interpolated points. CDP dispatch produces 0 or 1 points, which is
|
|
7
|
-
* a detection vector used by DataDome and Cloudflare.
|
|
8
|
-
*/
|
|
9
|
-
export declare const createCoalescedEventsSpoofingScript: () => string;
|
|
10
|
-
//# sourceMappingURL=coalesced-events-spoofing.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"coalesced-events-spoofing.d.ts","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/coalesced-events-spoofing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,mCAAmC,QAAO,MAwMtD,CAAC"}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Coalesced Events Spoofing
|
|
3
|
-
* Makes CDP-dispatched pointer events include realistic coalesced events.
|
|
4
|
-
*
|
|
5
|
-
* Real mouse movements produce PointerEvents where getCoalescedEvents() returns
|
|
6
|
-
* multiple interpolated points. CDP dispatch produces 0 or 1 points, which is
|
|
7
|
-
* a detection vector used by DataDome and Cloudflare.
|
|
8
|
-
*/
|
|
9
|
-
export const createCoalescedEventsSpoofingScript = () => {
|
|
10
|
-
return `
|
|
11
|
-
(() => {
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
// Track last known pointer position for interpolation
|
|
15
|
-
let lastPointerX = 0;
|
|
16
|
-
let lastPointerY = 0;
|
|
17
|
-
let lastPointerTime = 0;
|
|
18
|
-
|
|
19
|
-
// Seeded PRNG for deterministic jitter
|
|
20
|
-
const createSeededRNG = (seed) => {
|
|
21
|
-
let s = seed | 0;
|
|
22
|
-
return () => {
|
|
23
|
-
s = s + 0x6D2B79F5 | 0;
|
|
24
|
-
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
25
|
-
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
26
|
-
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// Stable seed: must NOT use Date.now() because this script re-executes on
|
|
31
|
-
// every reload via addScriptToEvaluateOnNewDocument.
|
|
32
|
-
const _stableHash = (salt) => {
|
|
33
|
-
const _s = typeof __stealth !== 'undefined' ? __stealth : {};
|
|
34
|
-
const key = _s.seedKey || '';
|
|
35
|
-
const ua = navigator.userAgent || '';
|
|
36
|
-
const s = screen;
|
|
37
|
-
let h = salt | 0;
|
|
38
|
-
for (let i = 0; i < key.length; i++) h = (h << 5) - h + key.charCodeAt(i) | 0;
|
|
39
|
-
for (let i = 0; i < ua.length; i++) h = (h << 5) - h + ua.charCodeAt(i) | 0;
|
|
40
|
-
h = (h << 5) - h + (s.width || 0) | 0;
|
|
41
|
-
h = (h << 5) - h + (s.height || 0) | 0;
|
|
42
|
-
h = (h << 5) - h + (s.colorDepth || 0) | 0;
|
|
43
|
-
return h >>> 0;
|
|
44
|
-
};
|
|
45
|
-
const sessionSeed = _stableHash(0xCAFE1007);
|
|
46
|
-
let callCount = 0;
|
|
47
|
-
|
|
48
|
-
// Store original method
|
|
49
|
-
const originalGetCoalescedEvents = PointerEvent.prototype.getCoalescedEvents;
|
|
50
|
-
const originalGetPredictedEvents = PointerEvent.prototype.getPredictedEvents;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Generate synthetic coalesced events between two points
|
|
54
|
-
*/
|
|
55
|
-
const generateCoalescedPoints = (event, prevX, prevY, prevTime) => {
|
|
56
|
-
const dx = event.clientX - prevX;
|
|
57
|
-
const dy = event.clientY - prevY;
|
|
58
|
-
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
59
|
-
|
|
60
|
-
// Number of coalesced points based on distance (real browsers: ~1 per 2-8px)
|
|
61
|
-
const rng = createSeededRNG(sessionSeed + (++callCount));
|
|
62
|
-
const pointsPerPx = 3 + rng() * 5; // 3-8 px per point
|
|
63
|
-
const numPoints = Math.max(1, Math.min(8, Math.floor(distance / pointsPerPx)));
|
|
64
|
-
|
|
65
|
-
if (numPoints <= 1 || distance < 2) {
|
|
66
|
-
// Very small movement, return just the event itself
|
|
67
|
-
return [event];
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const points = [];
|
|
71
|
-
const timeSpan = event.timeStamp - (prevTime || event.timeStamp - 16);
|
|
72
|
-
|
|
73
|
-
for (let i = 0; i < numPoints; i++) {
|
|
74
|
-
const t = (i + 1) / (numPoints + 1);
|
|
75
|
-
|
|
76
|
-
// Interpolate with slight jitter
|
|
77
|
-
const jitterX = (rng() - 0.5) * 0.5;
|
|
78
|
-
const jitterY = (rng() - 0.5) * 0.5;
|
|
79
|
-
const pointX = prevX + dx * t + jitterX;
|
|
80
|
-
const pointY = prevY + dy * t + jitterY;
|
|
81
|
-
const pointTime = (prevTime || event.timeStamp - 16) + timeSpan * t;
|
|
82
|
-
|
|
83
|
-
// Create a PointerEvent-like object
|
|
84
|
-
const coalescedEvent = new PointerEvent(event.type, {
|
|
85
|
-
clientX: pointX,
|
|
86
|
-
clientY: pointY,
|
|
87
|
-
screenX: event.screenX - event.clientX + pointX,
|
|
88
|
-
screenY: event.screenY - event.clientY + pointY,
|
|
89
|
-
pageX: pointX + (window.scrollX || 0),
|
|
90
|
-
pageY: pointY + (window.scrollY || 0),
|
|
91
|
-
pointerId: event.pointerId,
|
|
92
|
-
pointerType: event.pointerType,
|
|
93
|
-
isPrimary: event.isPrimary,
|
|
94
|
-
width: event.width,
|
|
95
|
-
height: event.height,
|
|
96
|
-
pressure: event.pressure || 0.5,
|
|
97
|
-
tiltX: event.tiltX,
|
|
98
|
-
tiltY: event.tiltY,
|
|
99
|
-
bubbles: false,
|
|
100
|
-
cancelable: false,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
points.push(coalescedEvent);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Always include the original event as the last point
|
|
107
|
-
points.push(event);
|
|
108
|
-
|
|
109
|
-
return points;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Generate predicted events (typically 1-2 points ahead)
|
|
114
|
-
*/
|
|
115
|
-
const generatePredictedPoints = (event) => {
|
|
116
|
-
const rng = createSeededRNG(sessionSeed + (++callCount) + 1000000);
|
|
117
|
-
const numPredicted = rng() > 0.3 ? 1 : 2;
|
|
118
|
-
const points = [];
|
|
119
|
-
|
|
120
|
-
// Predict forward based on current velocity
|
|
121
|
-
const dx = event.clientX - lastPointerX;
|
|
122
|
-
const dy = event.clientY - lastPointerY;
|
|
123
|
-
|
|
124
|
-
for (let i = 1; i <= numPredicted; i++) {
|
|
125
|
-
const predictedX = event.clientX + dx * i * 0.5 + (rng() - 0.5) * 2;
|
|
126
|
-
const predictedY = event.clientY + dy * i * 0.5 + (rng() - 0.5) * 2;
|
|
127
|
-
|
|
128
|
-
const predictedEvent = new PointerEvent(event.type, {
|
|
129
|
-
clientX: predictedX,
|
|
130
|
-
clientY: predictedY,
|
|
131
|
-
screenX: event.screenX - event.clientX + predictedX,
|
|
132
|
-
screenY: event.screenY - event.clientY + predictedY,
|
|
133
|
-
pointerId: event.pointerId,
|
|
134
|
-
pointerType: event.pointerType,
|
|
135
|
-
isPrimary: event.isPrimary,
|
|
136
|
-
pressure: event.pressure || 0.5,
|
|
137
|
-
bubbles: false,
|
|
138
|
-
cancelable: false,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
points.push(predictedEvent);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return points;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
// Override getCoalescedEvents
|
|
148
|
-
PointerEvent.prototype.getCoalescedEvents = function() {
|
|
149
|
-
// Only spoof for pointermove events (that's where coalesced events matter)
|
|
150
|
-
if (this.type !== 'pointermove' && this.type !== 'pointerdown') {
|
|
151
|
-
return originalGetCoalescedEvents ? originalGetCoalescedEvents.call(this) : [];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Check if original already has coalesced events (real events)
|
|
155
|
-
if (originalGetCoalescedEvents) {
|
|
156
|
-
const original = originalGetCoalescedEvents.call(this);
|
|
157
|
-
if (original && original.length > 1) {
|
|
158
|
-
// Real events with coalesced data, don't interfere
|
|
159
|
-
lastPointerX = this.clientX;
|
|
160
|
-
lastPointerY = this.clientY;
|
|
161
|
-
lastPointerTime = this.timeStamp;
|
|
162
|
-
return original;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Generate synthetic coalesced events
|
|
167
|
-
const result = generateCoalescedPoints(this, lastPointerX, lastPointerY, lastPointerTime);
|
|
168
|
-
|
|
169
|
-
// Update last position
|
|
170
|
-
lastPointerX = this.clientX;
|
|
171
|
-
lastPointerY = this.clientY;
|
|
172
|
-
lastPointerTime = this.timeStamp;
|
|
173
|
-
|
|
174
|
-
return result;
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// Override getPredictedEvents
|
|
178
|
-
if (originalGetPredictedEvents) {
|
|
179
|
-
PointerEvent.prototype.getPredictedEvents = function() {
|
|
180
|
-
if (this.type !== 'pointermove') {
|
|
181
|
-
return originalGetPredictedEvents.call(this);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const original = originalGetPredictedEvents.call(this);
|
|
185
|
-
if (original && original.length > 0) {
|
|
186
|
-
return original;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return generatePredictedPoints(this);
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Track pointer position via listener
|
|
194
|
-
window.addEventListener('pointermove', (e) => {
|
|
195
|
-
lastPointerX = e.clientX;
|
|
196
|
-
lastPointerY = e.clientY;
|
|
197
|
-
lastPointerTime = e.timeStamp;
|
|
198
|
-
}, { capture: true, passive: true });
|
|
199
|
-
|
|
200
|
-
// Register overrides with stealth utils if available
|
|
201
|
-
const _s = typeof __stealth !== 'undefined' ? __stealth : null;
|
|
202
|
-
if (_s && _s.utils) {
|
|
203
|
-
_s.utils.registerNativeFunction(PointerEvent.prototype.getCoalescedEvents, 'getCoalescedEvents');
|
|
204
|
-
if (originalGetPredictedEvents) {
|
|
205
|
-
_s.utils.registerNativeFunction(PointerEvent.prototype.getPredictedEvents, 'getPredictedEvents');
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
})();`;
|
|
209
|
-
};
|
|
210
|
-
//# sourceMappingURL=coalesced-events-spoofing.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"coalesced-events-spoofing.js","sourceRoot":"","sources":["../../../src/fingerprinting/fingerprint-overrides/coalesced-events-spoofing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAW,EAAE;IAC5D,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAsML,CAAC;AACP,CAAC,CAAC"}
|