@humanjs/core 0.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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.cjs +285 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +278 -0
- package/dist/index.d.ts +278 -0
- package/dist/index.js +276 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gonzalo Muñoz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# @humanjs/core
|
|
2
|
+
|
|
3
|
+
The core of [HumanJS](https://humanjs.dev): personality system, timing math, types, and the plugin contract.
|
|
4
|
+
|
|
5
|
+
> Most users don't install this package directly. Install [`@humanjs/playwright`](https://www.npmjs.com/package/@humanjs/playwright) instead, which depends on this.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @humanjs/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## License
|
|
14
|
+
|
|
15
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/presets/careful.ts
|
|
4
|
+
var careful = {
|
|
5
|
+
name: "careful",
|
|
6
|
+
speed: 1,
|
|
7
|
+
mouse: {
|
|
8
|
+
curvature: 0.4,
|
|
9
|
+
travelTimeMs: 450,
|
|
10
|
+
travelTimeJitter: 0.15,
|
|
11
|
+
overshootProbability: 0.05,
|
|
12
|
+
misclickProbability: 0.01
|
|
13
|
+
},
|
|
14
|
+
typing: {
|
|
15
|
+
baseDelayMs: 140,
|
|
16
|
+
delayJitter: 0.3,
|
|
17
|
+
typoProbability: 0.02,
|
|
18
|
+
typoCorrectionProbability: 0.95,
|
|
19
|
+
thinkPauseProbability: 0.08,
|
|
20
|
+
thinkPauseMeanMs: 400
|
|
21
|
+
},
|
|
22
|
+
reading: {
|
|
23
|
+
wpm: 220,
|
|
24
|
+
jitter: 0.2
|
|
25
|
+
},
|
|
26
|
+
dwell: {
|
|
27
|
+
preClickMs: 120,
|
|
28
|
+
preClickJitter: 0.3,
|
|
29
|
+
postActionMs: 90,
|
|
30
|
+
postActionJitter: 0.3
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/presets/distracted.ts
|
|
35
|
+
var distracted = {
|
|
36
|
+
name: "distracted",
|
|
37
|
+
speed: 1.3,
|
|
38
|
+
mouse: {
|
|
39
|
+
curvature: 0.6,
|
|
40
|
+
travelTimeMs: 600,
|
|
41
|
+
travelTimeJitter: 0.35,
|
|
42
|
+
overshootProbability: 0.15,
|
|
43
|
+
misclickProbability: 0.05
|
|
44
|
+
},
|
|
45
|
+
typing: {
|
|
46
|
+
baseDelayMs: 180,
|
|
47
|
+
delayJitter: 0.5,
|
|
48
|
+
typoProbability: 0.06,
|
|
49
|
+
typoCorrectionProbability: 0.7,
|
|
50
|
+
thinkPauseProbability: 0.18,
|
|
51
|
+
thinkPauseMeanMs: 700
|
|
52
|
+
},
|
|
53
|
+
reading: {
|
|
54
|
+
wpm: 180,
|
|
55
|
+
jitter: 0.4
|
|
56
|
+
},
|
|
57
|
+
dwell: {
|
|
58
|
+
preClickMs: 200,
|
|
59
|
+
preClickJitter: 0.5,
|
|
60
|
+
postActionMs: 250,
|
|
61
|
+
postActionJitter: 0.5
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/presets/fast.ts
|
|
66
|
+
var fast = {
|
|
67
|
+
name: "fast",
|
|
68
|
+
speed: 0.5,
|
|
69
|
+
mouse: {
|
|
70
|
+
curvature: 0.25,
|
|
71
|
+
travelTimeMs: 250,
|
|
72
|
+
travelTimeJitter: 0.1,
|
|
73
|
+
overshootProbability: 0.03,
|
|
74
|
+
misclickProbability: 5e-3
|
|
75
|
+
},
|
|
76
|
+
typing: {
|
|
77
|
+
baseDelayMs: 60,
|
|
78
|
+
delayJitter: 0.25,
|
|
79
|
+
typoProbability: 0.015,
|
|
80
|
+
typoCorrectionProbability: 0.9,
|
|
81
|
+
thinkPauseProbability: 0.02,
|
|
82
|
+
thinkPauseMeanMs: 150
|
|
83
|
+
},
|
|
84
|
+
reading: {
|
|
85
|
+
wpm: 350,
|
|
86
|
+
jitter: 0.15
|
|
87
|
+
},
|
|
88
|
+
dwell: {
|
|
89
|
+
preClickMs: 50,
|
|
90
|
+
preClickJitter: 0.25,
|
|
91
|
+
postActionMs: 40,
|
|
92
|
+
postActionJitter: 0.25
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// src/presets/precise.ts
|
|
97
|
+
var precise = {
|
|
98
|
+
name: "precise",
|
|
99
|
+
speed: 1,
|
|
100
|
+
mouse: {
|
|
101
|
+
curvature: 0.2,
|
|
102
|
+
travelTimeMs: 380,
|
|
103
|
+
travelTimeJitter: 0.05,
|
|
104
|
+
overshootProbability: 5e-3,
|
|
105
|
+
misclickProbability: 1e-3
|
|
106
|
+
},
|
|
107
|
+
typing: {
|
|
108
|
+
baseDelayMs: 110,
|
|
109
|
+
delayJitter: 0.1,
|
|
110
|
+
typoProbability: 3e-3,
|
|
111
|
+
typoCorrectionProbability: 0.99,
|
|
112
|
+
thinkPauseProbability: 0.03,
|
|
113
|
+
thinkPauseMeanMs: 200
|
|
114
|
+
},
|
|
115
|
+
reading: {
|
|
116
|
+
wpm: 250,
|
|
117
|
+
jitter: 0.08
|
|
118
|
+
},
|
|
119
|
+
dwell: {
|
|
120
|
+
preClickMs: 80,
|
|
121
|
+
preClickJitter: 0.1,
|
|
122
|
+
postActionMs: 60,
|
|
123
|
+
postActionJitter: 0.1
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/resolve.ts
|
|
128
|
+
var presets = {
|
|
129
|
+
careful,
|
|
130
|
+
distracted,
|
|
131
|
+
fast,
|
|
132
|
+
precise
|
|
133
|
+
};
|
|
134
|
+
function resolvePersonality(config) {
|
|
135
|
+
if (typeof config === "string") {
|
|
136
|
+
return presets[config];
|
|
137
|
+
}
|
|
138
|
+
if ("extends" in config) {
|
|
139
|
+
const base = presets[config.extends];
|
|
140
|
+
return {
|
|
141
|
+
name: config.name ?? base.name,
|
|
142
|
+
speed: config.speed ?? base.speed,
|
|
143
|
+
mouse: { ...base.mouse, ...config.mouse },
|
|
144
|
+
typing: { ...base.typing, ...config.typing },
|
|
145
|
+
reading: { ...base.reading, ...config.reading },
|
|
146
|
+
dwell: { ...base.dwell, ...config.dwell }
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return config;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/blend.ts
|
|
153
|
+
function blend(a, b, ratio) {
|
|
154
|
+
const personalityA = resolvePersonality(a);
|
|
155
|
+
const personalityB = resolvePersonality(b);
|
|
156
|
+
const t = clamp(ratio, 0, 1);
|
|
157
|
+
return {
|
|
158
|
+
name: `${personalityA.name}+${personalityB.name}@${t.toFixed(2)}`,
|
|
159
|
+
speed: lerp(personalityA.speed, personalityB.speed, t),
|
|
160
|
+
mouse: {
|
|
161
|
+
curvature: lerp(personalityA.mouse.curvature, personalityB.mouse.curvature, t),
|
|
162
|
+
travelTimeMs: lerp(personalityA.mouse.travelTimeMs, personalityB.mouse.travelTimeMs, t),
|
|
163
|
+
travelTimeJitter: lerp(
|
|
164
|
+
personalityA.mouse.travelTimeJitter,
|
|
165
|
+
personalityB.mouse.travelTimeJitter,
|
|
166
|
+
t
|
|
167
|
+
),
|
|
168
|
+
overshootProbability: lerp(
|
|
169
|
+
personalityA.mouse.overshootProbability,
|
|
170
|
+
personalityB.mouse.overshootProbability,
|
|
171
|
+
t
|
|
172
|
+
),
|
|
173
|
+
misclickProbability: lerp(
|
|
174
|
+
personalityA.mouse.misclickProbability,
|
|
175
|
+
personalityB.mouse.misclickProbability,
|
|
176
|
+
t
|
|
177
|
+
)
|
|
178
|
+
},
|
|
179
|
+
typing: {
|
|
180
|
+
baseDelayMs: lerp(personalityA.typing.baseDelayMs, personalityB.typing.baseDelayMs, t),
|
|
181
|
+
delayJitter: lerp(personalityA.typing.delayJitter, personalityB.typing.delayJitter, t),
|
|
182
|
+
typoProbability: lerp(
|
|
183
|
+
personalityA.typing.typoProbability,
|
|
184
|
+
personalityB.typing.typoProbability,
|
|
185
|
+
t
|
|
186
|
+
),
|
|
187
|
+
typoCorrectionProbability: lerp(
|
|
188
|
+
personalityA.typing.typoCorrectionProbability,
|
|
189
|
+
personalityB.typing.typoCorrectionProbability,
|
|
190
|
+
t
|
|
191
|
+
),
|
|
192
|
+
thinkPauseProbability: lerp(
|
|
193
|
+
personalityA.typing.thinkPauseProbability,
|
|
194
|
+
personalityB.typing.thinkPauseProbability,
|
|
195
|
+
t
|
|
196
|
+
),
|
|
197
|
+
thinkPauseMeanMs: lerp(
|
|
198
|
+
personalityA.typing.thinkPauseMeanMs,
|
|
199
|
+
personalityB.typing.thinkPauseMeanMs,
|
|
200
|
+
t
|
|
201
|
+
)
|
|
202
|
+
},
|
|
203
|
+
reading: {
|
|
204
|
+
wpm: lerp(personalityA.reading.wpm, personalityB.reading.wpm, t),
|
|
205
|
+
jitter: lerp(personalityA.reading.jitter, personalityB.reading.jitter, t)
|
|
206
|
+
},
|
|
207
|
+
dwell: {
|
|
208
|
+
preClickMs: lerp(personalityA.dwell.preClickMs, personalityB.dwell.preClickMs, t),
|
|
209
|
+
preClickJitter: lerp(personalityA.dwell.preClickJitter, personalityB.dwell.preClickJitter, t),
|
|
210
|
+
postActionMs: lerp(personalityA.dwell.postActionMs, personalityB.dwell.postActionMs, t),
|
|
211
|
+
postActionJitter: lerp(
|
|
212
|
+
personalityA.dwell.postActionJitter,
|
|
213
|
+
personalityB.dwell.postActionJitter,
|
|
214
|
+
t
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function lerp(a, b, t) {
|
|
220
|
+
return a + (b - a) * t;
|
|
221
|
+
}
|
|
222
|
+
function clamp(value, min, max) {
|
|
223
|
+
return value < min ? min : value > max ? max : value;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/rng.ts
|
|
227
|
+
function createRng(seed) {
|
|
228
|
+
let state = resolveSeed(seed);
|
|
229
|
+
const next = () => {
|
|
230
|
+
state = state + 1831565813 | 0;
|
|
231
|
+
let t = Math.imul(state ^ state >>> 15, 1 | state);
|
|
232
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
233
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
234
|
+
};
|
|
235
|
+
return {
|
|
236
|
+
next,
|
|
237
|
+
nextInt(min, max) {
|
|
238
|
+
return Math.floor(next() * (max - min)) + min;
|
|
239
|
+
},
|
|
240
|
+
nextFloat(min, max) {
|
|
241
|
+
return next() * (max - min) + min;
|
|
242
|
+
},
|
|
243
|
+
chance(p) {
|
|
244
|
+
const clamped = p < 0 ? 0 : p > 1 ? 1 : p;
|
|
245
|
+
return next() < clamped;
|
|
246
|
+
},
|
|
247
|
+
nextGaussian(mean = 0, stdDev = 1) {
|
|
248
|
+
const u1 = next() || Number.MIN_VALUE;
|
|
249
|
+
const u2 = next();
|
|
250
|
+
const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
251
|
+
return mean + z * stdDev;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function resolveSeed(seed) {
|
|
256
|
+
if (seed === void 0) {
|
|
257
|
+
return (Date.now() ^ Math.random() * 4294967296) >>> 0;
|
|
258
|
+
}
|
|
259
|
+
if (typeof seed === "number") {
|
|
260
|
+
return seed >>> 0;
|
|
261
|
+
}
|
|
262
|
+
return hashString(seed);
|
|
263
|
+
}
|
|
264
|
+
function hashString(s) {
|
|
265
|
+
let h = 2166136261;
|
|
266
|
+
for (let i = 0; i < s.length; i++) {
|
|
267
|
+
h ^= s.charCodeAt(i);
|
|
268
|
+
h = Math.imul(h, 16777619);
|
|
269
|
+
}
|
|
270
|
+
return h >>> 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/index.ts
|
|
274
|
+
var VERSION = "0.0.0";
|
|
275
|
+
|
|
276
|
+
exports.VERSION = VERSION;
|
|
277
|
+
exports.blend = blend;
|
|
278
|
+
exports.careful = careful;
|
|
279
|
+
exports.createRng = createRng;
|
|
280
|
+
exports.distracted = distracted;
|
|
281
|
+
exports.fast = fast;
|
|
282
|
+
exports.precise = precise;
|
|
283
|
+
exports.resolvePersonality = resolvePersonality;
|
|
284
|
+
//# sourceMappingURL=index.cjs.map
|
|
285
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presets/careful.ts","../src/presets/distracted.ts","../src/presets/fast.ts","../src/presets/precise.ts","../src/resolve.ts","../src/blend.ts","../src/rng.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Personality } from '../personality.js';\n\n/**\n * Slow, precise, few mistakes. Longer dwell on important fields.\n * The default personality when none is specified.\n */\nexport const careful: Personality = {\n name: 'careful',\n speed: 1.0,\n mouse: {\n curvature: 0.4,\n travelTimeMs: 450,\n travelTimeJitter: 0.15,\n overshootProbability: 0.05,\n misclickProbability: 0.01,\n },\n typing: {\n baseDelayMs: 140,\n delayJitter: 0.3,\n typoProbability: 0.02,\n typoCorrectionProbability: 0.95,\n thinkPauseProbability: 0.08,\n thinkPauseMeanMs: 400,\n },\n reading: {\n wpm: 220,\n jitter: 0.2,\n },\n dwell: {\n preClickMs: 120,\n preClickJitter: 0.3,\n postActionMs: 90,\n postActionJitter: 0.3,\n },\n};\n","import type { Personality } from '../personality.js';\n\n/**\n * Scrolls back, retypes, hovers off, occasional misclicks.\n * Useful for stress-testing flows that assume perfect input.\n */\nexport const distracted: Personality = {\n name: 'distracted',\n speed: 1.3,\n mouse: {\n curvature: 0.6,\n travelTimeMs: 600,\n travelTimeJitter: 0.35,\n overshootProbability: 0.15,\n misclickProbability: 0.05,\n },\n typing: {\n baseDelayMs: 180,\n delayJitter: 0.5,\n typoProbability: 0.06,\n typoCorrectionProbability: 0.7,\n thinkPauseProbability: 0.18,\n thinkPauseMeanMs: 700,\n },\n reading: {\n wpm: 180,\n jitter: 0.4,\n },\n dwell: {\n preClickMs: 200,\n preClickJitter: 0.5,\n postActionMs: 250,\n postActionJitter: 0.5,\n },\n};\n","import type { Personality } from '../personality.js';\n\n/**\n * Quick but still natural. Fewer pauses, lower typo rate, shorter dwell.\n * Good for sessions where the user \"knows the app\".\n */\nexport const fast: Personality = {\n name: 'fast',\n speed: 0.5,\n mouse: {\n curvature: 0.25,\n travelTimeMs: 250,\n travelTimeJitter: 0.1,\n overshootProbability: 0.03,\n misclickProbability: 0.005,\n },\n typing: {\n baseDelayMs: 60,\n delayJitter: 0.25,\n typoProbability: 0.015,\n typoCorrectionProbability: 0.9,\n thinkPauseProbability: 0.02,\n thinkPauseMeanMs: 150,\n },\n reading: {\n wpm: 350,\n jitter: 0.15,\n },\n dwell: {\n preClickMs: 50,\n preClickJitter: 0.25,\n postActionMs: 40,\n postActionJitter: 0.25,\n },\n};\n","import type { Personality } from '../personality.js';\n\n/**\n * Minimal noise, smooth motion, near-zero mistakes.\n * Closer to \"expert user\" than \"average user\".\n */\nexport const precise: Personality = {\n name: 'precise',\n speed: 1.0,\n mouse: {\n curvature: 0.2,\n travelTimeMs: 380,\n travelTimeJitter: 0.05,\n overshootProbability: 0.005,\n misclickProbability: 0.001,\n },\n typing: {\n baseDelayMs: 110,\n delayJitter: 0.1,\n typoProbability: 0.003,\n typoCorrectionProbability: 0.99,\n thinkPauseProbability: 0.03,\n thinkPauseMeanMs: 200,\n },\n reading: {\n wpm: 250,\n jitter: 0.08,\n },\n dwell: {\n preClickMs: 80,\n preClickJitter: 0.1,\n postActionMs: 60,\n postActionJitter: 0.1,\n },\n};\n","import type {\n DwellProfile,\n MouseProfile,\n Personality,\n ReadingProfile,\n TypingProfile,\n} from './personality.js';\nimport { careful, distracted, fast, precise } from './presets/index.js';\n\n/** Names of the four built-in personality presets. */\nexport type PresetName = 'careful' | 'distracted' | 'fast' | 'precise';\n\nconst presets: Record<PresetName, Personality> = {\n careful,\n distracted,\n fast,\n precise,\n};\n\n/**\n * A preset extended with partial overrides at the top level and on any\n * facet. Unspecified fields fall through to the base preset.\n */\nexport interface PersonalityExtension {\n readonly extends: PresetName;\n readonly name?: string;\n readonly speed?: number;\n readonly mouse?: Partial<MouseProfile>;\n readonly typing?: Partial<TypingProfile>;\n readonly reading?: Partial<ReadingProfile>;\n readonly dwell?: Partial<DwellProfile>;\n}\n\n/**\n * Anything that can be passed where a personality is expected.\n * - A preset name (`'careful'`, `'fast'`, …)\n * - A preset + partial overrides via `{ extends: 'careful', … }`\n * - A fully built `Personality` (community packages, results of `blend()`)\n */\nexport type PersonalityConfig = PresetName | PersonalityExtension | Personality;\n\n/**\n * Resolves any `PersonalityConfig` into a flat `Personality`.\n *\n * Never mutates the base preset — overrides produce a fresh object. The\n * returned `Personality` is safe to share, snapshot, and pass to\n * `createHuman()` or `blend()`.\n */\nexport function resolvePersonality(config: PersonalityConfig): Personality {\n if (typeof config === 'string') {\n return presets[config];\n }\n if ('extends' in config) {\n const base = presets[config.extends];\n return {\n name: config.name ?? base.name,\n speed: config.speed ?? base.speed,\n mouse: { ...base.mouse, ...config.mouse },\n typing: { ...base.typing, ...config.typing },\n reading: { ...base.reading, ...config.reading },\n dwell: { ...base.dwell, ...config.dwell },\n };\n }\n return config;\n}\n","import type { Personality } from './personality.js';\nimport { type PersonalityConfig, resolvePersonality } from './resolve.js';\n\n/**\n * Composes two personalities into a new one by linearly interpolating\n * every numeric field at the given ratio.\n *\n * Both inputs are first resolved (so preset names, extensions, and full\n * personalities are all accepted). Then every numeric field — top-level\n * `speed` plus every field on every facet — is interpolated as\n * `a + (b - a) * ratio`. The base presets are never mutated.\n *\n * @param a - First personality. The result equals `a` at `ratio = 0`.\n * @param b - Second personality. The result equals `b` at `ratio = 1`.\n * @param ratio - How much of `b` to mix in, from 0 to 1.\n * `0` returns pure `a`, `1` returns pure `b`, `0.5` is the midpoint.\n * Clamped if out of range.\n *\n * @example Two-way blend\n * ```ts\n * // 70% careful + 30% distracted\n * const mostlyCareful = blend('careful', 'distracted', 0.3);\n * ```\n *\n * @example Three-way blend via composition\n * ```ts\n * // Equal mix of three: (2/3) × (0.5·careful + 0.5·fast) + (1/3) × distracted\n * const even = blend(blend('careful', 'fast', 0.5), 'distracted', 1 / 3);\n * ```\n */\nexport function blend(a: PersonalityConfig, b: PersonalityConfig, ratio: number): Personality {\n const personalityA = resolvePersonality(a);\n const personalityB = resolvePersonality(b);\n const t = clamp(ratio, 0, 1);\n\n return {\n name: `${personalityA.name}+${personalityB.name}@${t.toFixed(2)}`,\n speed: lerp(personalityA.speed, personalityB.speed, t),\n mouse: {\n curvature: lerp(personalityA.mouse.curvature, personalityB.mouse.curvature, t),\n travelTimeMs: lerp(personalityA.mouse.travelTimeMs, personalityB.mouse.travelTimeMs, t),\n travelTimeJitter: lerp(\n personalityA.mouse.travelTimeJitter,\n personalityB.mouse.travelTimeJitter,\n t,\n ),\n overshootProbability: lerp(\n personalityA.mouse.overshootProbability,\n personalityB.mouse.overshootProbability,\n t,\n ),\n misclickProbability: lerp(\n personalityA.mouse.misclickProbability,\n personalityB.mouse.misclickProbability,\n t,\n ),\n },\n typing: {\n baseDelayMs: lerp(personalityA.typing.baseDelayMs, personalityB.typing.baseDelayMs, t),\n delayJitter: lerp(personalityA.typing.delayJitter, personalityB.typing.delayJitter, t),\n typoProbability: lerp(\n personalityA.typing.typoProbability,\n personalityB.typing.typoProbability,\n t,\n ),\n typoCorrectionProbability: lerp(\n personalityA.typing.typoCorrectionProbability,\n personalityB.typing.typoCorrectionProbability,\n t,\n ),\n thinkPauseProbability: lerp(\n personalityA.typing.thinkPauseProbability,\n personalityB.typing.thinkPauseProbability,\n t,\n ),\n thinkPauseMeanMs: lerp(\n personalityA.typing.thinkPauseMeanMs,\n personalityB.typing.thinkPauseMeanMs,\n t,\n ),\n },\n reading: {\n wpm: lerp(personalityA.reading.wpm, personalityB.reading.wpm, t),\n jitter: lerp(personalityA.reading.jitter, personalityB.reading.jitter, t),\n },\n dwell: {\n preClickMs: lerp(personalityA.dwell.preClickMs, personalityB.dwell.preClickMs, t),\n preClickJitter: lerp(personalityA.dwell.preClickJitter, personalityB.dwell.preClickJitter, t),\n postActionMs: lerp(personalityA.dwell.postActionMs, personalityB.dwell.postActionMs, t),\n postActionJitter: lerp(\n personalityA.dwell.postActionJitter,\n personalityB.dwell.postActionJitter,\n t,\n ),\n },\n };\n}\n\n/**\n * Linear interpolation between two numbers. Returns `a` at `t = 0`,\n * `b` at `t = 1`, and a proportional mix in between.\n */\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\n/** Clamps `value` to the inclusive range `[min, max]`. */\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n","/**\n * Seedable pseudo-random number generator used throughout HumanJS.\n *\n * Implementations must be deterministic given a seed: `createRng('foo')`\n * followed by N calls to `next()` always produces the same sequence on\n * any platform. This is what makes humanization snapshot-testable.\n */\nexport interface Rng {\n /** Returns a uniform float in [0, 1). */\n next(): number;\n /** Returns a uniform integer in [min, max). */\n nextInt(min: number, max: number): number;\n /** Returns a uniform float in [min, max). */\n nextFloat(min: number, max: number): number;\n /** Returns true with probability `p`. Clamped to [0, 1]. */\n chance(p: number): boolean;\n /**\n * Returns a Gaussian-distributed sample via the Box-Muller transform.\n * Defaults: mean = 0, standard deviation = 1.\n */\n nextGaussian(mean?: number, stdDev?: number): number;\n}\n\n/**\n * Creates a seedable PRNG. Identical seeds produce identical sequences\n * on every run and every platform.\n *\n * Algorithm: mulberry32 — a 32-bit, 2^32-period generator with strong\n * statistical properties for the timing distributions HumanJS produces.\n * Not suitable for cryptographic use.\n *\n * @param seed - String (hashed via FNV-1a), number (used directly), or\n * undefined (system-time-based; sessions become non-reproducible).\n */\nexport function createRng(seed?: string | number): Rng {\n let state = resolveSeed(seed);\n\n const next = (): number => {\n state = (state + 0x6d2b79f5) | 0;\n let t = Math.imul(state ^ (state >>> 15), 1 | state);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 0x100000000;\n };\n\n return {\n next,\n nextInt(min, max) {\n return Math.floor(next() * (max - min)) + min;\n },\n nextFloat(min, max) {\n return next() * (max - min) + min;\n },\n chance(p) {\n const clamped = p < 0 ? 0 : p > 1 ? 1 : p;\n return next() < clamped;\n },\n nextGaussian(mean = 0, stdDev = 1) {\n const u1 = next() || Number.MIN_VALUE;\n const u2 = next();\n const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n return mean + z * stdDev;\n },\n };\n}\n\n/**\n * Normalizes any seed input to a 32-bit unsigned integer — the state shape\n * mulberry32 requires. Numbers (including negative or fractional ones) are\n * coerced via `>>> 0`; strings hash to a uint32 via FNV-1a; undefined\n * derives a non-reproducible seed from system time.\n */\nfunction resolveSeed(seed: string | number | undefined): number {\n if (seed === undefined) {\n return (Date.now() ^ (Math.random() * 0x100000000)) >>> 0;\n }\n if (typeof seed === 'number') {\n return seed >>> 0;\n }\n return hashString(seed);\n}\n\nfunction hashString(s: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n","export const VERSION = '0.0.0';\n\nexport { blend } from './blend.js';\nexport type {\n DwellProfile,\n MouseProfile,\n Personality,\n ReadingProfile,\n TypingProfile,\n} from './personality.js';\nexport type {\n ActionResult,\n ActionType,\n HumanAction,\n HumanPlugin,\n KnownActionType,\n PluginContext,\n} from './plugin.js';\nexport { careful, distracted, fast, precise } from './presets/index.js';\nexport type {\n PersonalityConfig,\n PersonalityExtension,\n PresetName,\n} from './resolve.js';\nexport { resolvePersonality } from './resolve.js';\nexport type { Rng } from './rng.js';\nexport { createRng } from './rng.js';\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A complete personality profile describing how a humanized session behaves.
|
|
3
|
+
*
|
|
4
|
+
* Personalities are pure data — they describe the rhythm and shape of
|
|
5
|
+
* humanization without owning any random state. Randomness is supplied
|
|
6
|
+
* per-session by the host, seeded deterministically when needed.
|
|
7
|
+
*
|
|
8
|
+
* Built-in presets and community packages should both satisfy this shape.
|
|
9
|
+
* Community packages are encouraged to publish as `@anything/personality-*`.
|
|
10
|
+
*/
|
|
11
|
+
interface Personality {
|
|
12
|
+
/** Identifier used in logs and observability events. */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/**
|
|
15
|
+
* Overall tempo multiplier applied on top of per-facet timings.
|
|
16
|
+
* 1.0 = base, < 1.0 = faster, > 1.0 = slower.
|
|
17
|
+
*/
|
|
18
|
+
readonly speed: number;
|
|
19
|
+
readonly mouse: MouseProfile;
|
|
20
|
+
readonly typing: TypingProfile;
|
|
21
|
+
readonly reading: ReadingProfile;
|
|
22
|
+
readonly dwell: DwellProfile;
|
|
23
|
+
}
|
|
24
|
+
/** Parameters that shape mouse movement and click behavior. */
|
|
25
|
+
interface MouseProfile {
|
|
26
|
+
/** Bezier curvature factor: 0 = straight line, 1 = exaggerated curves. */
|
|
27
|
+
readonly curvature: number;
|
|
28
|
+
/** Mean travel time in ms per 1000px of distance. */
|
|
29
|
+
readonly travelTimeMs: number;
|
|
30
|
+
/** Random variation in travel time as a fraction of the mean (0..1). */
|
|
31
|
+
readonly travelTimeJitter: number;
|
|
32
|
+
/** Probability of overshooting a target and correcting (0..1). */
|
|
33
|
+
readonly overshootProbability: number;
|
|
34
|
+
/** Probability of clicking slightly off-target and correcting (0..1). */
|
|
35
|
+
readonly misclickProbability: number;
|
|
36
|
+
}
|
|
37
|
+
/** Parameters that shape keystroke timing and typing errors. */
|
|
38
|
+
interface TypingProfile {
|
|
39
|
+
/** Mean ms between keystrokes. */
|
|
40
|
+
readonly baseDelayMs: number;
|
|
41
|
+
/** Random variation as a fraction of the mean (0..1). */
|
|
42
|
+
readonly delayJitter: number;
|
|
43
|
+
/** Probability of a typo per character (0..1). */
|
|
44
|
+
readonly typoProbability: number;
|
|
45
|
+
/**
|
|
46
|
+
* Probability of correcting a typo with backspace once one occurs (0..1).
|
|
47
|
+
* Lower values leave more uncorrected typos in the output.
|
|
48
|
+
*/
|
|
49
|
+
readonly typoCorrectionProbability: number;
|
|
50
|
+
/** Probability of pausing mid-word as if thinking (0..1). */
|
|
51
|
+
readonly thinkPauseProbability: number;
|
|
52
|
+
/** Mean duration of a think-pause in ms. */
|
|
53
|
+
readonly thinkPauseMeanMs: number;
|
|
54
|
+
}
|
|
55
|
+
/** Parameters that shape reading dwell time. */
|
|
56
|
+
interface ReadingProfile {
|
|
57
|
+
/** Reading speed in words per minute. */
|
|
58
|
+
readonly wpm: number;
|
|
59
|
+
/** Random variation in reading time as a fraction of base (0..1). */
|
|
60
|
+
readonly jitter: number;
|
|
61
|
+
}
|
|
62
|
+
/** Parameters that shape micro-pauses around actions. */
|
|
63
|
+
interface DwellProfile {
|
|
64
|
+
/** Pause in ms after hovering, before clicking. */
|
|
65
|
+
readonly preClickMs: number;
|
|
66
|
+
/** Random jitter on pre-click pause as a fraction (0..1). */
|
|
67
|
+
readonly preClickJitter: number;
|
|
68
|
+
/** Pause in ms after an action completes. */
|
|
69
|
+
readonly postActionMs: number;
|
|
70
|
+
/** Random jitter on post-action pause as a fraction (0..1). */
|
|
71
|
+
readonly postActionJitter: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Names of the four built-in personality presets. */
|
|
75
|
+
type PresetName = 'careful' | 'distracted' | 'fast' | 'precise';
|
|
76
|
+
/**
|
|
77
|
+
* A preset extended with partial overrides at the top level and on any
|
|
78
|
+
* facet. Unspecified fields fall through to the base preset.
|
|
79
|
+
*/
|
|
80
|
+
interface PersonalityExtension {
|
|
81
|
+
readonly extends: PresetName;
|
|
82
|
+
readonly name?: string;
|
|
83
|
+
readonly speed?: number;
|
|
84
|
+
readonly mouse?: Partial<MouseProfile>;
|
|
85
|
+
readonly typing?: Partial<TypingProfile>;
|
|
86
|
+
readonly reading?: Partial<ReadingProfile>;
|
|
87
|
+
readonly dwell?: Partial<DwellProfile>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Anything that can be passed where a personality is expected.
|
|
91
|
+
* - A preset name (`'careful'`, `'fast'`, …)
|
|
92
|
+
* - A preset + partial overrides via `{ extends: 'careful', … }`
|
|
93
|
+
* - A fully built `Personality` (community packages, results of `blend()`)
|
|
94
|
+
*/
|
|
95
|
+
type PersonalityConfig = PresetName | PersonalityExtension | Personality;
|
|
96
|
+
/**
|
|
97
|
+
* Resolves any `PersonalityConfig` into a flat `Personality`.
|
|
98
|
+
*
|
|
99
|
+
* Never mutates the base preset — overrides produce a fresh object. The
|
|
100
|
+
* returned `Personality` is safe to share, snapshot, and pass to
|
|
101
|
+
* `createHuman()` or `blend()`.
|
|
102
|
+
*/
|
|
103
|
+
declare function resolvePersonality(config: PersonalityConfig): Personality;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Composes two personalities into a new one by linearly interpolating
|
|
107
|
+
* every numeric field at the given ratio.
|
|
108
|
+
*
|
|
109
|
+
* Both inputs are first resolved (so preset names, extensions, and full
|
|
110
|
+
* personalities are all accepted). Then every numeric field — top-level
|
|
111
|
+
* `speed` plus every field on every facet — is interpolated as
|
|
112
|
+
* `a + (b - a) * ratio`. The base presets are never mutated.
|
|
113
|
+
*
|
|
114
|
+
* @param a - First personality. The result equals `a` at `ratio = 0`.
|
|
115
|
+
* @param b - Second personality. The result equals `b` at `ratio = 1`.
|
|
116
|
+
* @param ratio - How much of `b` to mix in, from 0 to 1.
|
|
117
|
+
* `0` returns pure `a`, `1` returns pure `b`, `0.5` is the midpoint.
|
|
118
|
+
* Clamped if out of range.
|
|
119
|
+
*
|
|
120
|
+
* @example Two-way blend
|
|
121
|
+
* ```ts
|
|
122
|
+
* // 70% careful + 30% distracted
|
|
123
|
+
* const mostlyCareful = blend('careful', 'distracted', 0.3);
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @example Three-way blend via composition
|
|
127
|
+
* ```ts
|
|
128
|
+
* // Equal mix of three: (2/3) × (0.5·careful + 0.5·fast) + (1/3) × distracted
|
|
129
|
+
* const even = blend(blend('careful', 'fast', 0.5), 'distracted', 1 / 3);
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare function blend(a: PersonalityConfig, b: PersonalityConfig, ratio: number): Personality;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Seedable pseudo-random number generator used throughout HumanJS.
|
|
136
|
+
*
|
|
137
|
+
* Implementations must be deterministic given a seed: `createRng('foo')`
|
|
138
|
+
* followed by N calls to `next()` always produces the same sequence on
|
|
139
|
+
* any platform. This is what makes humanization snapshot-testable.
|
|
140
|
+
*/
|
|
141
|
+
interface Rng {
|
|
142
|
+
/** Returns a uniform float in [0, 1). */
|
|
143
|
+
next(): number;
|
|
144
|
+
/** Returns a uniform integer in [min, max). */
|
|
145
|
+
nextInt(min: number, max: number): number;
|
|
146
|
+
/** Returns a uniform float in [min, max). */
|
|
147
|
+
nextFloat(min: number, max: number): number;
|
|
148
|
+
/** Returns true with probability `p`. Clamped to [0, 1]. */
|
|
149
|
+
chance(p: number): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Returns a Gaussian-distributed sample via the Box-Muller transform.
|
|
152
|
+
* Defaults: mean = 0, standard deviation = 1.
|
|
153
|
+
*/
|
|
154
|
+
nextGaussian(mean?: number, stdDev?: number): number;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Creates a seedable PRNG. Identical seeds produce identical sequences
|
|
158
|
+
* on every run and every platform.
|
|
159
|
+
*
|
|
160
|
+
* Algorithm: mulberry32 — a 32-bit, 2^32-period generator with strong
|
|
161
|
+
* statistical properties for the timing distributions HumanJS produces.
|
|
162
|
+
* Not suitable for cryptographic use.
|
|
163
|
+
*
|
|
164
|
+
* @param seed - String (hashed via FNV-1a), number (used directly), or
|
|
165
|
+
* undefined (system-time-based; sessions become non-reproducible).
|
|
166
|
+
*/
|
|
167
|
+
declare function createRng(seed?: string | number): Rng;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Contract for plugins that extend a humanized session.
|
|
171
|
+
*
|
|
172
|
+
* Plugins are plain objects with optional lifecycle hooks. The host
|
|
173
|
+
* (an adapter such as `@humanjs/playwright`) invokes hooks at the
|
|
174
|
+
* appropriate moments — before/after each action and on error.
|
|
175
|
+
*
|
|
176
|
+
* Hooks can be sync or async. Hosts await each hook in registration order.
|
|
177
|
+
* Hooks are observation-only in v1; they cannot transform actions.
|
|
178
|
+
*
|
|
179
|
+
* @example A logger plugin
|
|
180
|
+
* ```ts
|
|
181
|
+
* const logger: HumanPlugin = {
|
|
182
|
+
* name: 'logger',
|
|
183
|
+
* beforeAction: (action) => console.log(`[${action.type}] starting`),
|
|
184
|
+
* afterAction: (action, result) =>
|
|
185
|
+
* console.log(`[${action.type}] took ${result.durationMs}ms`),
|
|
186
|
+
* };
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
interface HumanPlugin {
|
|
190
|
+
/** Unique identifier used in logs and observability events. */
|
|
191
|
+
readonly name: string;
|
|
192
|
+
/** Called once when the plugin is installed on a session. */
|
|
193
|
+
install?(context: PluginContext): void | Promise<void>;
|
|
194
|
+
/** Called immediately before every action. */
|
|
195
|
+
beforeAction?(action: HumanAction): void | Promise<void>;
|
|
196
|
+
/** Called immediately after an action completes successfully. */
|
|
197
|
+
afterAction?(action: HumanAction, result: ActionResult): void | Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Called when an action throws. The plugin should not re-throw —
|
|
200
|
+
* the host re-throws after notifying every plugin.
|
|
201
|
+
*/
|
|
202
|
+
onError?(action: HumanAction, error: unknown): void | Promise<void>;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Runtime context provided to a plugin's `install` hook.
|
|
206
|
+
*
|
|
207
|
+
* Contains the resolved personality and the session's seeded RNG so
|
|
208
|
+
* plugins can produce deterministic output (e.g. logging fixtures).
|
|
209
|
+
*/
|
|
210
|
+
interface PluginContext {
|
|
211
|
+
/** The resolved personality this session is using. */
|
|
212
|
+
readonly personality: Personality;
|
|
213
|
+
/** The session's seeded RNG. Calling it consumes the shared sequence. */
|
|
214
|
+
readonly rng: Rng;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Action types HumanJS adapters are known to emit.
|
|
218
|
+
*
|
|
219
|
+
* Plugins can switch on these names for type-safe handling and get IDE
|
|
220
|
+
* autocomplete on them — but `HumanAction.type` is intentionally widened
|
|
221
|
+
* to accept any string so adapters can introduce custom or experimental
|
|
222
|
+
* action types without a core release.
|
|
223
|
+
*/
|
|
224
|
+
type KnownActionType = 'click' | 'drag' | 'goto' | 'hover' | 'paste' | 'read' | 'rightClick' | 'scroll' | 'shortcut' | 'type';
|
|
225
|
+
/**
|
|
226
|
+
* Loose action-type string: known names autocomplete in IDEs while any
|
|
227
|
+
* other string still typechecks. Implemented with the `(string & {})`
|
|
228
|
+
* idiom — TypeScript surfaces the literal members for completion but
|
|
229
|
+
* widens the type to `string` for assignability.
|
|
230
|
+
*/
|
|
231
|
+
type ActionType = KnownActionType | (string & {});
|
|
232
|
+
/**
|
|
233
|
+
* A humanized action a plugin can observe.
|
|
234
|
+
*
|
|
235
|
+
* Plugins should treat unknown action types defensively — different
|
|
236
|
+
* adapters emit different sets, and new types can be added at any time.
|
|
237
|
+
*/
|
|
238
|
+
interface HumanAction {
|
|
239
|
+
/** Action discriminator. Known values autocomplete; any string is accepted. */
|
|
240
|
+
readonly type: ActionType;
|
|
241
|
+
/** Action-specific parameters. Shape varies by `type`. */
|
|
242
|
+
readonly params?: Readonly<Record<string, unknown>>;
|
|
243
|
+
}
|
|
244
|
+
/** Outcome of a completed action, passed to `afterAction`. */
|
|
245
|
+
interface ActionResult {
|
|
246
|
+
/** Echo of the originating action's `type`. */
|
|
247
|
+
readonly type: ActionType;
|
|
248
|
+
/** Wall-clock duration in milliseconds. */
|
|
249
|
+
readonly durationMs: number;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Slow, precise, few mistakes. Longer dwell on important fields.
|
|
254
|
+
* The default personality when none is specified.
|
|
255
|
+
*/
|
|
256
|
+
declare const careful: Personality;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Scrolls back, retypes, hovers off, occasional misclicks.
|
|
260
|
+
* Useful for stress-testing flows that assume perfect input.
|
|
261
|
+
*/
|
|
262
|
+
declare const distracted: Personality;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Quick but still natural. Fewer pauses, lower typo rate, shorter dwell.
|
|
266
|
+
* Good for sessions where the user "knows the app".
|
|
267
|
+
*/
|
|
268
|
+
declare const fast: Personality;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Minimal noise, smooth motion, near-zero mistakes.
|
|
272
|
+
* Closer to "expert user" than "average user".
|
|
273
|
+
*/
|
|
274
|
+
declare const precise: Personality;
|
|
275
|
+
|
|
276
|
+
declare const VERSION = "0.0.0";
|
|
277
|
+
|
|
278
|
+
export { type ActionResult, type ActionType, type DwellProfile, type HumanAction, type HumanPlugin, type KnownActionType, type MouseProfile, type Personality, type PersonalityConfig, type PersonalityExtension, type PluginContext, type PresetName, type ReadingProfile, type Rng, type TypingProfile, VERSION, blend, careful, createRng, distracted, fast, precise, resolvePersonality };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A complete personality profile describing how a humanized session behaves.
|
|
3
|
+
*
|
|
4
|
+
* Personalities are pure data — they describe the rhythm and shape of
|
|
5
|
+
* humanization without owning any random state. Randomness is supplied
|
|
6
|
+
* per-session by the host, seeded deterministically when needed.
|
|
7
|
+
*
|
|
8
|
+
* Built-in presets and community packages should both satisfy this shape.
|
|
9
|
+
* Community packages are encouraged to publish as `@anything/personality-*`.
|
|
10
|
+
*/
|
|
11
|
+
interface Personality {
|
|
12
|
+
/** Identifier used in logs and observability events. */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/**
|
|
15
|
+
* Overall tempo multiplier applied on top of per-facet timings.
|
|
16
|
+
* 1.0 = base, < 1.0 = faster, > 1.0 = slower.
|
|
17
|
+
*/
|
|
18
|
+
readonly speed: number;
|
|
19
|
+
readonly mouse: MouseProfile;
|
|
20
|
+
readonly typing: TypingProfile;
|
|
21
|
+
readonly reading: ReadingProfile;
|
|
22
|
+
readonly dwell: DwellProfile;
|
|
23
|
+
}
|
|
24
|
+
/** Parameters that shape mouse movement and click behavior. */
|
|
25
|
+
interface MouseProfile {
|
|
26
|
+
/** Bezier curvature factor: 0 = straight line, 1 = exaggerated curves. */
|
|
27
|
+
readonly curvature: number;
|
|
28
|
+
/** Mean travel time in ms per 1000px of distance. */
|
|
29
|
+
readonly travelTimeMs: number;
|
|
30
|
+
/** Random variation in travel time as a fraction of the mean (0..1). */
|
|
31
|
+
readonly travelTimeJitter: number;
|
|
32
|
+
/** Probability of overshooting a target and correcting (0..1). */
|
|
33
|
+
readonly overshootProbability: number;
|
|
34
|
+
/** Probability of clicking slightly off-target and correcting (0..1). */
|
|
35
|
+
readonly misclickProbability: number;
|
|
36
|
+
}
|
|
37
|
+
/** Parameters that shape keystroke timing and typing errors. */
|
|
38
|
+
interface TypingProfile {
|
|
39
|
+
/** Mean ms between keystrokes. */
|
|
40
|
+
readonly baseDelayMs: number;
|
|
41
|
+
/** Random variation as a fraction of the mean (0..1). */
|
|
42
|
+
readonly delayJitter: number;
|
|
43
|
+
/** Probability of a typo per character (0..1). */
|
|
44
|
+
readonly typoProbability: number;
|
|
45
|
+
/**
|
|
46
|
+
* Probability of correcting a typo with backspace once one occurs (0..1).
|
|
47
|
+
* Lower values leave more uncorrected typos in the output.
|
|
48
|
+
*/
|
|
49
|
+
readonly typoCorrectionProbability: number;
|
|
50
|
+
/** Probability of pausing mid-word as if thinking (0..1). */
|
|
51
|
+
readonly thinkPauseProbability: number;
|
|
52
|
+
/** Mean duration of a think-pause in ms. */
|
|
53
|
+
readonly thinkPauseMeanMs: number;
|
|
54
|
+
}
|
|
55
|
+
/** Parameters that shape reading dwell time. */
|
|
56
|
+
interface ReadingProfile {
|
|
57
|
+
/** Reading speed in words per minute. */
|
|
58
|
+
readonly wpm: number;
|
|
59
|
+
/** Random variation in reading time as a fraction of base (0..1). */
|
|
60
|
+
readonly jitter: number;
|
|
61
|
+
}
|
|
62
|
+
/** Parameters that shape micro-pauses around actions. */
|
|
63
|
+
interface DwellProfile {
|
|
64
|
+
/** Pause in ms after hovering, before clicking. */
|
|
65
|
+
readonly preClickMs: number;
|
|
66
|
+
/** Random jitter on pre-click pause as a fraction (0..1). */
|
|
67
|
+
readonly preClickJitter: number;
|
|
68
|
+
/** Pause in ms after an action completes. */
|
|
69
|
+
readonly postActionMs: number;
|
|
70
|
+
/** Random jitter on post-action pause as a fraction (0..1). */
|
|
71
|
+
readonly postActionJitter: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Names of the four built-in personality presets. */
|
|
75
|
+
type PresetName = 'careful' | 'distracted' | 'fast' | 'precise';
|
|
76
|
+
/**
|
|
77
|
+
* A preset extended with partial overrides at the top level and on any
|
|
78
|
+
* facet. Unspecified fields fall through to the base preset.
|
|
79
|
+
*/
|
|
80
|
+
interface PersonalityExtension {
|
|
81
|
+
readonly extends: PresetName;
|
|
82
|
+
readonly name?: string;
|
|
83
|
+
readonly speed?: number;
|
|
84
|
+
readonly mouse?: Partial<MouseProfile>;
|
|
85
|
+
readonly typing?: Partial<TypingProfile>;
|
|
86
|
+
readonly reading?: Partial<ReadingProfile>;
|
|
87
|
+
readonly dwell?: Partial<DwellProfile>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Anything that can be passed where a personality is expected.
|
|
91
|
+
* - A preset name (`'careful'`, `'fast'`, …)
|
|
92
|
+
* - A preset + partial overrides via `{ extends: 'careful', … }`
|
|
93
|
+
* - A fully built `Personality` (community packages, results of `blend()`)
|
|
94
|
+
*/
|
|
95
|
+
type PersonalityConfig = PresetName | PersonalityExtension | Personality;
|
|
96
|
+
/**
|
|
97
|
+
* Resolves any `PersonalityConfig` into a flat `Personality`.
|
|
98
|
+
*
|
|
99
|
+
* Never mutates the base preset — overrides produce a fresh object. The
|
|
100
|
+
* returned `Personality` is safe to share, snapshot, and pass to
|
|
101
|
+
* `createHuman()` or `blend()`.
|
|
102
|
+
*/
|
|
103
|
+
declare function resolvePersonality(config: PersonalityConfig): Personality;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Composes two personalities into a new one by linearly interpolating
|
|
107
|
+
* every numeric field at the given ratio.
|
|
108
|
+
*
|
|
109
|
+
* Both inputs are first resolved (so preset names, extensions, and full
|
|
110
|
+
* personalities are all accepted). Then every numeric field — top-level
|
|
111
|
+
* `speed` plus every field on every facet — is interpolated as
|
|
112
|
+
* `a + (b - a) * ratio`. The base presets are never mutated.
|
|
113
|
+
*
|
|
114
|
+
* @param a - First personality. The result equals `a` at `ratio = 0`.
|
|
115
|
+
* @param b - Second personality. The result equals `b` at `ratio = 1`.
|
|
116
|
+
* @param ratio - How much of `b` to mix in, from 0 to 1.
|
|
117
|
+
* `0` returns pure `a`, `1` returns pure `b`, `0.5` is the midpoint.
|
|
118
|
+
* Clamped if out of range.
|
|
119
|
+
*
|
|
120
|
+
* @example Two-way blend
|
|
121
|
+
* ```ts
|
|
122
|
+
* // 70% careful + 30% distracted
|
|
123
|
+
* const mostlyCareful = blend('careful', 'distracted', 0.3);
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @example Three-way blend via composition
|
|
127
|
+
* ```ts
|
|
128
|
+
* // Equal mix of three: (2/3) × (0.5·careful + 0.5·fast) + (1/3) × distracted
|
|
129
|
+
* const even = blend(blend('careful', 'fast', 0.5), 'distracted', 1 / 3);
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
declare function blend(a: PersonalityConfig, b: PersonalityConfig, ratio: number): Personality;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Seedable pseudo-random number generator used throughout HumanJS.
|
|
136
|
+
*
|
|
137
|
+
* Implementations must be deterministic given a seed: `createRng('foo')`
|
|
138
|
+
* followed by N calls to `next()` always produces the same sequence on
|
|
139
|
+
* any platform. This is what makes humanization snapshot-testable.
|
|
140
|
+
*/
|
|
141
|
+
interface Rng {
|
|
142
|
+
/** Returns a uniform float in [0, 1). */
|
|
143
|
+
next(): number;
|
|
144
|
+
/** Returns a uniform integer in [min, max). */
|
|
145
|
+
nextInt(min: number, max: number): number;
|
|
146
|
+
/** Returns a uniform float in [min, max). */
|
|
147
|
+
nextFloat(min: number, max: number): number;
|
|
148
|
+
/** Returns true with probability `p`. Clamped to [0, 1]. */
|
|
149
|
+
chance(p: number): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Returns a Gaussian-distributed sample via the Box-Muller transform.
|
|
152
|
+
* Defaults: mean = 0, standard deviation = 1.
|
|
153
|
+
*/
|
|
154
|
+
nextGaussian(mean?: number, stdDev?: number): number;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Creates a seedable PRNG. Identical seeds produce identical sequences
|
|
158
|
+
* on every run and every platform.
|
|
159
|
+
*
|
|
160
|
+
* Algorithm: mulberry32 — a 32-bit, 2^32-period generator with strong
|
|
161
|
+
* statistical properties for the timing distributions HumanJS produces.
|
|
162
|
+
* Not suitable for cryptographic use.
|
|
163
|
+
*
|
|
164
|
+
* @param seed - String (hashed via FNV-1a), number (used directly), or
|
|
165
|
+
* undefined (system-time-based; sessions become non-reproducible).
|
|
166
|
+
*/
|
|
167
|
+
declare function createRng(seed?: string | number): Rng;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Contract for plugins that extend a humanized session.
|
|
171
|
+
*
|
|
172
|
+
* Plugins are plain objects with optional lifecycle hooks. The host
|
|
173
|
+
* (an adapter such as `@humanjs/playwright`) invokes hooks at the
|
|
174
|
+
* appropriate moments — before/after each action and on error.
|
|
175
|
+
*
|
|
176
|
+
* Hooks can be sync or async. Hosts await each hook in registration order.
|
|
177
|
+
* Hooks are observation-only in v1; they cannot transform actions.
|
|
178
|
+
*
|
|
179
|
+
* @example A logger plugin
|
|
180
|
+
* ```ts
|
|
181
|
+
* const logger: HumanPlugin = {
|
|
182
|
+
* name: 'logger',
|
|
183
|
+
* beforeAction: (action) => console.log(`[${action.type}] starting`),
|
|
184
|
+
* afterAction: (action, result) =>
|
|
185
|
+
* console.log(`[${action.type}] took ${result.durationMs}ms`),
|
|
186
|
+
* };
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
interface HumanPlugin {
|
|
190
|
+
/** Unique identifier used in logs and observability events. */
|
|
191
|
+
readonly name: string;
|
|
192
|
+
/** Called once when the plugin is installed on a session. */
|
|
193
|
+
install?(context: PluginContext): void | Promise<void>;
|
|
194
|
+
/** Called immediately before every action. */
|
|
195
|
+
beforeAction?(action: HumanAction): void | Promise<void>;
|
|
196
|
+
/** Called immediately after an action completes successfully. */
|
|
197
|
+
afterAction?(action: HumanAction, result: ActionResult): void | Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Called when an action throws. The plugin should not re-throw —
|
|
200
|
+
* the host re-throws after notifying every plugin.
|
|
201
|
+
*/
|
|
202
|
+
onError?(action: HumanAction, error: unknown): void | Promise<void>;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Runtime context provided to a plugin's `install` hook.
|
|
206
|
+
*
|
|
207
|
+
* Contains the resolved personality and the session's seeded RNG so
|
|
208
|
+
* plugins can produce deterministic output (e.g. logging fixtures).
|
|
209
|
+
*/
|
|
210
|
+
interface PluginContext {
|
|
211
|
+
/** The resolved personality this session is using. */
|
|
212
|
+
readonly personality: Personality;
|
|
213
|
+
/** The session's seeded RNG. Calling it consumes the shared sequence. */
|
|
214
|
+
readonly rng: Rng;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Action types HumanJS adapters are known to emit.
|
|
218
|
+
*
|
|
219
|
+
* Plugins can switch on these names for type-safe handling and get IDE
|
|
220
|
+
* autocomplete on them — but `HumanAction.type` is intentionally widened
|
|
221
|
+
* to accept any string so adapters can introduce custom or experimental
|
|
222
|
+
* action types without a core release.
|
|
223
|
+
*/
|
|
224
|
+
type KnownActionType = 'click' | 'drag' | 'goto' | 'hover' | 'paste' | 'read' | 'rightClick' | 'scroll' | 'shortcut' | 'type';
|
|
225
|
+
/**
|
|
226
|
+
* Loose action-type string: known names autocomplete in IDEs while any
|
|
227
|
+
* other string still typechecks. Implemented with the `(string & {})`
|
|
228
|
+
* idiom — TypeScript surfaces the literal members for completion but
|
|
229
|
+
* widens the type to `string` for assignability.
|
|
230
|
+
*/
|
|
231
|
+
type ActionType = KnownActionType | (string & {});
|
|
232
|
+
/**
|
|
233
|
+
* A humanized action a plugin can observe.
|
|
234
|
+
*
|
|
235
|
+
* Plugins should treat unknown action types defensively — different
|
|
236
|
+
* adapters emit different sets, and new types can be added at any time.
|
|
237
|
+
*/
|
|
238
|
+
interface HumanAction {
|
|
239
|
+
/** Action discriminator. Known values autocomplete; any string is accepted. */
|
|
240
|
+
readonly type: ActionType;
|
|
241
|
+
/** Action-specific parameters. Shape varies by `type`. */
|
|
242
|
+
readonly params?: Readonly<Record<string, unknown>>;
|
|
243
|
+
}
|
|
244
|
+
/** Outcome of a completed action, passed to `afterAction`. */
|
|
245
|
+
interface ActionResult {
|
|
246
|
+
/** Echo of the originating action's `type`. */
|
|
247
|
+
readonly type: ActionType;
|
|
248
|
+
/** Wall-clock duration in milliseconds. */
|
|
249
|
+
readonly durationMs: number;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Slow, precise, few mistakes. Longer dwell on important fields.
|
|
254
|
+
* The default personality when none is specified.
|
|
255
|
+
*/
|
|
256
|
+
declare const careful: Personality;
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Scrolls back, retypes, hovers off, occasional misclicks.
|
|
260
|
+
* Useful for stress-testing flows that assume perfect input.
|
|
261
|
+
*/
|
|
262
|
+
declare const distracted: Personality;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Quick but still natural. Fewer pauses, lower typo rate, shorter dwell.
|
|
266
|
+
* Good for sessions where the user "knows the app".
|
|
267
|
+
*/
|
|
268
|
+
declare const fast: Personality;
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Minimal noise, smooth motion, near-zero mistakes.
|
|
272
|
+
* Closer to "expert user" than "average user".
|
|
273
|
+
*/
|
|
274
|
+
declare const precise: Personality;
|
|
275
|
+
|
|
276
|
+
declare const VERSION = "0.0.0";
|
|
277
|
+
|
|
278
|
+
export { type ActionResult, type ActionType, type DwellProfile, type HumanAction, type HumanPlugin, type KnownActionType, type MouseProfile, type Personality, type PersonalityConfig, type PersonalityExtension, type PluginContext, type PresetName, type ReadingProfile, type Rng, type TypingProfile, VERSION, blend, careful, createRng, distracted, fast, precise, resolvePersonality };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
// src/presets/careful.ts
|
|
2
|
+
var careful = {
|
|
3
|
+
name: "careful",
|
|
4
|
+
speed: 1,
|
|
5
|
+
mouse: {
|
|
6
|
+
curvature: 0.4,
|
|
7
|
+
travelTimeMs: 450,
|
|
8
|
+
travelTimeJitter: 0.15,
|
|
9
|
+
overshootProbability: 0.05,
|
|
10
|
+
misclickProbability: 0.01
|
|
11
|
+
},
|
|
12
|
+
typing: {
|
|
13
|
+
baseDelayMs: 140,
|
|
14
|
+
delayJitter: 0.3,
|
|
15
|
+
typoProbability: 0.02,
|
|
16
|
+
typoCorrectionProbability: 0.95,
|
|
17
|
+
thinkPauseProbability: 0.08,
|
|
18
|
+
thinkPauseMeanMs: 400
|
|
19
|
+
},
|
|
20
|
+
reading: {
|
|
21
|
+
wpm: 220,
|
|
22
|
+
jitter: 0.2
|
|
23
|
+
},
|
|
24
|
+
dwell: {
|
|
25
|
+
preClickMs: 120,
|
|
26
|
+
preClickJitter: 0.3,
|
|
27
|
+
postActionMs: 90,
|
|
28
|
+
postActionJitter: 0.3
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/presets/distracted.ts
|
|
33
|
+
var distracted = {
|
|
34
|
+
name: "distracted",
|
|
35
|
+
speed: 1.3,
|
|
36
|
+
mouse: {
|
|
37
|
+
curvature: 0.6,
|
|
38
|
+
travelTimeMs: 600,
|
|
39
|
+
travelTimeJitter: 0.35,
|
|
40
|
+
overshootProbability: 0.15,
|
|
41
|
+
misclickProbability: 0.05
|
|
42
|
+
},
|
|
43
|
+
typing: {
|
|
44
|
+
baseDelayMs: 180,
|
|
45
|
+
delayJitter: 0.5,
|
|
46
|
+
typoProbability: 0.06,
|
|
47
|
+
typoCorrectionProbability: 0.7,
|
|
48
|
+
thinkPauseProbability: 0.18,
|
|
49
|
+
thinkPauseMeanMs: 700
|
|
50
|
+
},
|
|
51
|
+
reading: {
|
|
52
|
+
wpm: 180,
|
|
53
|
+
jitter: 0.4
|
|
54
|
+
},
|
|
55
|
+
dwell: {
|
|
56
|
+
preClickMs: 200,
|
|
57
|
+
preClickJitter: 0.5,
|
|
58
|
+
postActionMs: 250,
|
|
59
|
+
postActionJitter: 0.5
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/presets/fast.ts
|
|
64
|
+
var fast = {
|
|
65
|
+
name: "fast",
|
|
66
|
+
speed: 0.5,
|
|
67
|
+
mouse: {
|
|
68
|
+
curvature: 0.25,
|
|
69
|
+
travelTimeMs: 250,
|
|
70
|
+
travelTimeJitter: 0.1,
|
|
71
|
+
overshootProbability: 0.03,
|
|
72
|
+
misclickProbability: 5e-3
|
|
73
|
+
},
|
|
74
|
+
typing: {
|
|
75
|
+
baseDelayMs: 60,
|
|
76
|
+
delayJitter: 0.25,
|
|
77
|
+
typoProbability: 0.015,
|
|
78
|
+
typoCorrectionProbability: 0.9,
|
|
79
|
+
thinkPauseProbability: 0.02,
|
|
80
|
+
thinkPauseMeanMs: 150
|
|
81
|
+
},
|
|
82
|
+
reading: {
|
|
83
|
+
wpm: 350,
|
|
84
|
+
jitter: 0.15
|
|
85
|
+
},
|
|
86
|
+
dwell: {
|
|
87
|
+
preClickMs: 50,
|
|
88
|
+
preClickJitter: 0.25,
|
|
89
|
+
postActionMs: 40,
|
|
90
|
+
postActionJitter: 0.25
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/presets/precise.ts
|
|
95
|
+
var precise = {
|
|
96
|
+
name: "precise",
|
|
97
|
+
speed: 1,
|
|
98
|
+
mouse: {
|
|
99
|
+
curvature: 0.2,
|
|
100
|
+
travelTimeMs: 380,
|
|
101
|
+
travelTimeJitter: 0.05,
|
|
102
|
+
overshootProbability: 5e-3,
|
|
103
|
+
misclickProbability: 1e-3
|
|
104
|
+
},
|
|
105
|
+
typing: {
|
|
106
|
+
baseDelayMs: 110,
|
|
107
|
+
delayJitter: 0.1,
|
|
108
|
+
typoProbability: 3e-3,
|
|
109
|
+
typoCorrectionProbability: 0.99,
|
|
110
|
+
thinkPauseProbability: 0.03,
|
|
111
|
+
thinkPauseMeanMs: 200
|
|
112
|
+
},
|
|
113
|
+
reading: {
|
|
114
|
+
wpm: 250,
|
|
115
|
+
jitter: 0.08
|
|
116
|
+
},
|
|
117
|
+
dwell: {
|
|
118
|
+
preClickMs: 80,
|
|
119
|
+
preClickJitter: 0.1,
|
|
120
|
+
postActionMs: 60,
|
|
121
|
+
postActionJitter: 0.1
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// src/resolve.ts
|
|
126
|
+
var presets = {
|
|
127
|
+
careful,
|
|
128
|
+
distracted,
|
|
129
|
+
fast,
|
|
130
|
+
precise
|
|
131
|
+
};
|
|
132
|
+
function resolvePersonality(config) {
|
|
133
|
+
if (typeof config === "string") {
|
|
134
|
+
return presets[config];
|
|
135
|
+
}
|
|
136
|
+
if ("extends" in config) {
|
|
137
|
+
const base = presets[config.extends];
|
|
138
|
+
return {
|
|
139
|
+
name: config.name ?? base.name,
|
|
140
|
+
speed: config.speed ?? base.speed,
|
|
141
|
+
mouse: { ...base.mouse, ...config.mouse },
|
|
142
|
+
typing: { ...base.typing, ...config.typing },
|
|
143
|
+
reading: { ...base.reading, ...config.reading },
|
|
144
|
+
dwell: { ...base.dwell, ...config.dwell }
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return config;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/blend.ts
|
|
151
|
+
function blend(a, b, ratio) {
|
|
152
|
+
const personalityA = resolvePersonality(a);
|
|
153
|
+
const personalityB = resolvePersonality(b);
|
|
154
|
+
const t = clamp(ratio, 0, 1);
|
|
155
|
+
return {
|
|
156
|
+
name: `${personalityA.name}+${personalityB.name}@${t.toFixed(2)}`,
|
|
157
|
+
speed: lerp(personalityA.speed, personalityB.speed, t),
|
|
158
|
+
mouse: {
|
|
159
|
+
curvature: lerp(personalityA.mouse.curvature, personalityB.mouse.curvature, t),
|
|
160
|
+
travelTimeMs: lerp(personalityA.mouse.travelTimeMs, personalityB.mouse.travelTimeMs, t),
|
|
161
|
+
travelTimeJitter: lerp(
|
|
162
|
+
personalityA.mouse.travelTimeJitter,
|
|
163
|
+
personalityB.mouse.travelTimeJitter,
|
|
164
|
+
t
|
|
165
|
+
),
|
|
166
|
+
overshootProbability: lerp(
|
|
167
|
+
personalityA.mouse.overshootProbability,
|
|
168
|
+
personalityB.mouse.overshootProbability,
|
|
169
|
+
t
|
|
170
|
+
),
|
|
171
|
+
misclickProbability: lerp(
|
|
172
|
+
personalityA.mouse.misclickProbability,
|
|
173
|
+
personalityB.mouse.misclickProbability,
|
|
174
|
+
t
|
|
175
|
+
)
|
|
176
|
+
},
|
|
177
|
+
typing: {
|
|
178
|
+
baseDelayMs: lerp(personalityA.typing.baseDelayMs, personalityB.typing.baseDelayMs, t),
|
|
179
|
+
delayJitter: lerp(personalityA.typing.delayJitter, personalityB.typing.delayJitter, t),
|
|
180
|
+
typoProbability: lerp(
|
|
181
|
+
personalityA.typing.typoProbability,
|
|
182
|
+
personalityB.typing.typoProbability,
|
|
183
|
+
t
|
|
184
|
+
),
|
|
185
|
+
typoCorrectionProbability: lerp(
|
|
186
|
+
personalityA.typing.typoCorrectionProbability,
|
|
187
|
+
personalityB.typing.typoCorrectionProbability,
|
|
188
|
+
t
|
|
189
|
+
),
|
|
190
|
+
thinkPauseProbability: lerp(
|
|
191
|
+
personalityA.typing.thinkPauseProbability,
|
|
192
|
+
personalityB.typing.thinkPauseProbability,
|
|
193
|
+
t
|
|
194
|
+
),
|
|
195
|
+
thinkPauseMeanMs: lerp(
|
|
196
|
+
personalityA.typing.thinkPauseMeanMs,
|
|
197
|
+
personalityB.typing.thinkPauseMeanMs,
|
|
198
|
+
t
|
|
199
|
+
)
|
|
200
|
+
},
|
|
201
|
+
reading: {
|
|
202
|
+
wpm: lerp(personalityA.reading.wpm, personalityB.reading.wpm, t),
|
|
203
|
+
jitter: lerp(personalityA.reading.jitter, personalityB.reading.jitter, t)
|
|
204
|
+
},
|
|
205
|
+
dwell: {
|
|
206
|
+
preClickMs: lerp(personalityA.dwell.preClickMs, personalityB.dwell.preClickMs, t),
|
|
207
|
+
preClickJitter: lerp(personalityA.dwell.preClickJitter, personalityB.dwell.preClickJitter, t),
|
|
208
|
+
postActionMs: lerp(personalityA.dwell.postActionMs, personalityB.dwell.postActionMs, t),
|
|
209
|
+
postActionJitter: lerp(
|
|
210
|
+
personalityA.dwell.postActionJitter,
|
|
211
|
+
personalityB.dwell.postActionJitter,
|
|
212
|
+
t
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function lerp(a, b, t) {
|
|
218
|
+
return a + (b - a) * t;
|
|
219
|
+
}
|
|
220
|
+
function clamp(value, min, max) {
|
|
221
|
+
return value < min ? min : value > max ? max : value;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// src/rng.ts
|
|
225
|
+
function createRng(seed) {
|
|
226
|
+
let state = resolveSeed(seed);
|
|
227
|
+
const next = () => {
|
|
228
|
+
state = state + 1831565813 | 0;
|
|
229
|
+
let t = Math.imul(state ^ state >>> 15, 1 | state);
|
|
230
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
231
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
232
|
+
};
|
|
233
|
+
return {
|
|
234
|
+
next,
|
|
235
|
+
nextInt(min, max) {
|
|
236
|
+
return Math.floor(next() * (max - min)) + min;
|
|
237
|
+
},
|
|
238
|
+
nextFloat(min, max) {
|
|
239
|
+
return next() * (max - min) + min;
|
|
240
|
+
},
|
|
241
|
+
chance(p) {
|
|
242
|
+
const clamped = p < 0 ? 0 : p > 1 ? 1 : p;
|
|
243
|
+
return next() < clamped;
|
|
244
|
+
},
|
|
245
|
+
nextGaussian(mean = 0, stdDev = 1) {
|
|
246
|
+
const u1 = next() || Number.MIN_VALUE;
|
|
247
|
+
const u2 = next();
|
|
248
|
+
const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
249
|
+
return mean + z * stdDev;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
function resolveSeed(seed) {
|
|
254
|
+
if (seed === void 0) {
|
|
255
|
+
return (Date.now() ^ Math.random() * 4294967296) >>> 0;
|
|
256
|
+
}
|
|
257
|
+
if (typeof seed === "number") {
|
|
258
|
+
return seed >>> 0;
|
|
259
|
+
}
|
|
260
|
+
return hashString(seed);
|
|
261
|
+
}
|
|
262
|
+
function hashString(s) {
|
|
263
|
+
let h = 2166136261;
|
|
264
|
+
for (let i = 0; i < s.length; i++) {
|
|
265
|
+
h ^= s.charCodeAt(i);
|
|
266
|
+
h = Math.imul(h, 16777619);
|
|
267
|
+
}
|
|
268
|
+
return h >>> 0;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/index.ts
|
|
272
|
+
var VERSION = "0.0.0";
|
|
273
|
+
|
|
274
|
+
export { VERSION, blend, careful, createRng, distracted, fast, precise, resolvePersonality };
|
|
275
|
+
//# sourceMappingURL=index.js.map
|
|
276
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/presets/careful.ts","../src/presets/distracted.ts","../src/presets/fast.ts","../src/presets/precise.ts","../src/resolve.ts","../src/blend.ts","../src/rng.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Personality } from '../personality.js';\n\n/**\n * Slow, precise, few mistakes. Longer dwell on important fields.\n * The default personality when none is specified.\n */\nexport const careful: Personality = {\n name: 'careful',\n speed: 1.0,\n mouse: {\n curvature: 0.4,\n travelTimeMs: 450,\n travelTimeJitter: 0.15,\n overshootProbability: 0.05,\n misclickProbability: 0.01,\n },\n typing: {\n baseDelayMs: 140,\n delayJitter: 0.3,\n typoProbability: 0.02,\n typoCorrectionProbability: 0.95,\n thinkPauseProbability: 0.08,\n thinkPauseMeanMs: 400,\n },\n reading: {\n wpm: 220,\n jitter: 0.2,\n },\n dwell: {\n preClickMs: 120,\n preClickJitter: 0.3,\n postActionMs: 90,\n postActionJitter: 0.3,\n },\n};\n","import type { Personality } from '../personality.js';\n\n/**\n * Scrolls back, retypes, hovers off, occasional misclicks.\n * Useful for stress-testing flows that assume perfect input.\n */\nexport const distracted: Personality = {\n name: 'distracted',\n speed: 1.3,\n mouse: {\n curvature: 0.6,\n travelTimeMs: 600,\n travelTimeJitter: 0.35,\n overshootProbability: 0.15,\n misclickProbability: 0.05,\n },\n typing: {\n baseDelayMs: 180,\n delayJitter: 0.5,\n typoProbability: 0.06,\n typoCorrectionProbability: 0.7,\n thinkPauseProbability: 0.18,\n thinkPauseMeanMs: 700,\n },\n reading: {\n wpm: 180,\n jitter: 0.4,\n },\n dwell: {\n preClickMs: 200,\n preClickJitter: 0.5,\n postActionMs: 250,\n postActionJitter: 0.5,\n },\n};\n","import type { Personality } from '../personality.js';\n\n/**\n * Quick but still natural. Fewer pauses, lower typo rate, shorter dwell.\n * Good for sessions where the user \"knows the app\".\n */\nexport const fast: Personality = {\n name: 'fast',\n speed: 0.5,\n mouse: {\n curvature: 0.25,\n travelTimeMs: 250,\n travelTimeJitter: 0.1,\n overshootProbability: 0.03,\n misclickProbability: 0.005,\n },\n typing: {\n baseDelayMs: 60,\n delayJitter: 0.25,\n typoProbability: 0.015,\n typoCorrectionProbability: 0.9,\n thinkPauseProbability: 0.02,\n thinkPauseMeanMs: 150,\n },\n reading: {\n wpm: 350,\n jitter: 0.15,\n },\n dwell: {\n preClickMs: 50,\n preClickJitter: 0.25,\n postActionMs: 40,\n postActionJitter: 0.25,\n },\n};\n","import type { Personality } from '../personality.js';\n\n/**\n * Minimal noise, smooth motion, near-zero mistakes.\n * Closer to \"expert user\" than \"average user\".\n */\nexport const precise: Personality = {\n name: 'precise',\n speed: 1.0,\n mouse: {\n curvature: 0.2,\n travelTimeMs: 380,\n travelTimeJitter: 0.05,\n overshootProbability: 0.005,\n misclickProbability: 0.001,\n },\n typing: {\n baseDelayMs: 110,\n delayJitter: 0.1,\n typoProbability: 0.003,\n typoCorrectionProbability: 0.99,\n thinkPauseProbability: 0.03,\n thinkPauseMeanMs: 200,\n },\n reading: {\n wpm: 250,\n jitter: 0.08,\n },\n dwell: {\n preClickMs: 80,\n preClickJitter: 0.1,\n postActionMs: 60,\n postActionJitter: 0.1,\n },\n};\n","import type {\n DwellProfile,\n MouseProfile,\n Personality,\n ReadingProfile,\n TypingProfile,\n} from './personality.js';\nimport { careful, distracted, fast, precise } from './presets/index.js';\n\n/** Names of the four built-in personality presets. */\nexport type PresetName = 'careful' | 'distracted' | 'fast' | 'precise';\n\nconst presets: Record<PresetName, Personality> = {\n careful,\n distracted,\n fast,\n precise,\n};\n\n/**\n * A preset extended with partial overrides at the top level and on any\n * facet. Unspecified fields fall through to the base preset.\n */\nexport interface PersonalityExtension {\n readonly extends: PresetName;\n readonly name?: string;\n readonly speed?: number;\n readonly mouse?: Partial<MouseProfile>;\n readonly typing?: Partial<TypingProfile>;\n readonly reading?: Partial<ReadingProfile>;\n readonly dwell?: Partial<DwellProfile>;\n}\n\n/**\n * Anything that can be passed where a personality is expected.\n * - A preset name (`'careful'`, `'fast'`, …)\n * - A preset + partial overrides via `{ extends: 'careful', … }`\n * - A fully built `Personality` (community packages, results of `blend()`)\n */\nexport type PersonalityConfig = PresetName | PersonalityExtension | Personality;\n\n/**\n * Resolves any `PersonalityConfig` into a flat `Personality`.\n *\n * Never mutates the base preset — overrides produce a fresh object. The\n * returned `Personality` is safe to share, snapshot, and pass to\n * `createHuman()` or `blend()`.\n */\nexport function resolvePersonality(config: PersonalityConfig): Personality {\n if (typeof config === 'string') {\n return presets[config];\n }\n if ('extends' in config) {\n const base = presets[config.extends];\n return {\n name: config.name ?? base.name,\n speed: config.speed ?? base.speed,\n mouse: { ...base.mouse, ...config.mouse },\n typing: { ...base.typing, ...config.typing },\n reading: { ...base.reading, ...config.reading },\n dwell: { ...base.dwell, ...config.dwell },\n };\n }\n return config;\n}\n","import type { Personality } from './personality.js';\nimport { type PersonalityConfig, resolvePersonality } from './resolve.js';\n\n/**\n * Composes two personalities into a new one by linearly interpolating\n * every numeric field at the given ratio.\n *\n * Both inputs are first resolved (so preset names, extensions, and full\n * personalities are all accepted). Then every numeric field — top-level\n * `speed` plus every field on every facet — is interpolated as\n * `a + (b - a) * ratio`. The base presets are never mutated.\n *\n * @param a - First personality. The result equals `a` at `ratio = 0`.\n * @param b - Second personality. The result equals `b` at `ratio = 1`.\n * @param ratio - How much of `b` to mix in, from 0 to 1.\n * `0` returns pure `a`, `1` returns pure `b`, `0.5` is the midpoint.\n * Clamped if out of range.\n *\n * @example Two-way blend\n * ```ts\n * // 70% careful + 30% distracted\n * const mostlyCareful = blend('careful', 'distracted', 0.3);\n * ```\n *\n * @example Three-way blend via composition\n * ```ts\n * // Equal mix of three: (2/3) × (0.5·careful + 0.5·fast) + (1/3) × distracted\n * const even = blend(blend('careful', 'fast', 0.5), 'distracted', 1 / 3);\n * ```\n */\nexport function blend(a: PersonalityConfig, b: PersonalityConfig, ratio: number): Personality {\n const personalityA = resolvePersonality(a);\n const personalityB = resolvePersonality(b);\n const t = clamp(ratio, 0, 1);\n\n return {\n name: `${personalityA.name}+${personalityB.name}@${t.toFixed(2)}`,\n speed: lerp(personalityA.speed, personalityB.speed, t),\n mouse: {\n curvature: lerp(personalityA.mouse.curvature, personalityB.mouse.curvature, t),\n travelTimeMs: lerp(personalityA.mouse.travelTimeMs, personalityB.mouse.travelTimeMs, t),\n travelTimeJitter: lerp(\n personalityA.mouse.travelTimeJitter,\n personalityB.mouse.travelTimeJitter,\n t,\n ),\n overshootProbability: lerp(\n personalityA.mouse.overshootProbability,\n personalityB.mouse.overshootProbability,\n t,\n ),\n misclickProbability: lerp(\n personalityA.mouse.misclickProbability,\n personalityB.mouse.misclickProbability,\n t,\n ),\n },\n typing: {\n baseDelayMs: lerp(personalityA.typing.baseDelayMs, personalityB.typing.baseDelayMs, t),\n delayJitter: lerp(personalityA.typing.delayJitter, personalityB.typing.delayJitter, t),\n typoProbability: lerp(\n personalityA.typing.typoProbability,\n personalityB.typing.typoProbability,\n t,\n ),\n typoCorrectionProbability: lerp(\n personalityA.typing.typoCorrectionProbability,\n personalityB.typing.typoCorrectionProbability,\n t,\n ),\n thinkPauseProbability: lerp(\n personalityA.typing.thinkPauseProbability,\n personalityB.typing.thinkPauseProbability,\n t,\n ),\n thinkPauseMeanMs: lerp(\n personalityA.typing.thinkPauseMeanMs,\n personalityB.typing.thinkPauseMeanMs,\n t,\n ),\n },\n reading: {\n wpm: lerp(personalityA.reading.wpm, personalityB.reading.wpm, t),\n jitter: lerp(personalityA.reading.jitter, personalityB.reading.jitter, t),\n },\n dwell: {\n preClickMs: lerp(personalityA.dwell.preClickMs, personalityB.dwell.preClickMs, t),\n preClickJitter: lerp(personalityA.dwell.preClickJitter, personalityB.dwell.preClickJitter, t),\n postActionMs: lerp(personalityA.dwell.postActionMs, personalityB.dwell.postActionMs, t),\n postActionJitter: lerp(\n personalityA.dwell.postActionJitter,\n personalityB.dwell.postActionJitter,\n t,\n ),\n },\n };\n}\n\n/**\n * Linear interpolation between two numbers. Returns `a` at `t = 0`,\n * `b` at `t = 1`, and a proportional mix in between.\n */\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\n/** Clamps `value` to the inclusive range `[min, max]`. */\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n","/**\n * Seedable pseudo-random number generator used throughout HumanJS.\n *\n * Implementations must be deterministic given a seed: `createRng('foo')`\n * followed by N calls to `next()` always produces the same sequence on\n * any platform. This is what makes humanization snapshot-testable.\n */\nexport interface Rng {\n /** Returns a uniform float in [0, 1). */\n next(): number;\n /** Returns a uniform integer in [min, max). */\n nextInt(min: number, max: number): number;\n /** Returns a uniform float in [min, max). */\n nextFloat(min: number, max: number): number;\n /** Returns true with probability `p`. Clamped to [0, 1]. */\n chance(p: number): boolean;\n /**\n * Returns a Gaussian-distributed sample via the Box-Muller transform.\n * Defaults: mean = 0, standard deviation = 1.\n */\n nextGaussian(mean?: number, stdDev?: number): number;\n}\n\n/**\n * Creates a seedable PRNG. Identical seeds produce identical sequences\n * on every run and every platform.\n *\n * Algorithm: mulberry32 — a 32-bit, 2^32-period generator with strong\n * statistical properties for the timing distributions HumanJS produces.\n * Not suitable for cryptographic use.\n *\n * @param seed - String (hashed via FNV-1a), number (used directly), or\n * undefined (system-time-based; sessions become non-reproducible).\n */\nexport function createRng(seed?: string | number): Rng {\n let state = resolveSeed(seed);\n\n const next = (): number => {\n state = (state + 0x6d2b79f5) | 0;\n let t = Math.imul(state ^ (state >>> 15), 1 | state);\n t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n return ((t ^ (t >>> 14)) >>> 0) / 0x100000000;\n };\n\n return {\n next,\n nextInt(min, max) {\n return Math.floor(next() * (max - min)) + min;\n },\n nextFloat(min, max) {\n return next() * (max - min) + min;\n },\n chance(p) {\n const clamped = p < 0 ? 0 : p > 1 ? 1 : p;\n return next() < clamped;\n },\n nextGaussian(mean = 0, stdDev = 1) {\n const u1 = next() || Number.MIN_VALUE;\n const u2 = next();\n const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);\n return mean + z * stdDev;\n },\n };\n}\n\n/**\n * Normalizes any seed input to a 32-bit unsigned integer — the state shape\n * mulberry32 requires. Numbers (including negative or fractional ones) are\n * coerced via `>>> 0`; strings hash to a uint32 via FNV-1a; undefined\n * derives a non-reproducible seed from system time.\n */\nfunction resolveSeed(seed: string | number | undefined): number {\n if (seed === undefined) {\n return (Date.now() ^ (Math.random() * 0x100000000)) >>> 0;\n }\n if (typeof seed === 'number') {\n return seed >>> 0;\n }\n return hashString(seed);\n}\n\nfunction hashString(s: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return h >>> 0;\n}\n","export const VERSION = '0.0.0';\n\nexport { blend } from './blend.js';\nexport type {\n DwellProfile,\n MouseProfile,\n Personality,\n ReadingProfile,\n TypingProfile,\n} from './personality.js';\nexport type {\n ActionResult,\n ActionType,\n HumanAction,\n HumanPlugin,\n KnownActionType,\n PluginContext,\n} from './plugin.js';\nexport { careful, distracted, fast, precise } from './presets/index.js';\nexport type {\n PersonalityConfig,\n PersonalityExtension,\n PresetName,\n} from './resolve.js';\nexport { resolvePersonality } from './resolve.js';\nexport type { Rng } from './rng.js';\nexport { createRng } from './rng.js';\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@humanjs/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Personality system, timing math, types, and plugin contract for HumanJS.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"humanjs",
|
|
7
|
+
"humanize",
|
|
8
|
+
"playwright",
|
|
9
|
+
"browser-automation"
|
|
10
|
+
],
|
|
11
|
+
"author": "Gonzalo Muñoz <toti@eventurex.com.ar>",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://humanjs.dev",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/totigm/humanjs.git",
|
|
17
|
+
"directory": "packages/core"
|
|
18
|
+
},
|
|
19
|
+
"bugs": "https://github.com/totigm/humanjs/issues",
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/index.cjs",
|
|
22
|
+
"module": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"import": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"default": "./dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"require": {
|
|
31
|
+
"types": "./dist/index.d.cts",
|
|
32
|
+
"default": "./dist/index.cjs"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"sideEffects": false,
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=20"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsup",
|
|
51
|
+
"dev": "tsup --watch",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"typecheck": "tsc --noEmit",
|
|
55
|
+
"lint": "biome check .",
|
|
56
|
+
"clean": "rm -rf dist .turbo"
|
|
57
|
+
}
|
|
58
|
+
}
|