device-passport 1.0.0 → 1.2.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +110 -2
  3. package/assets/ego-profile-manager.png +0 -0
  4. package/dist/adapters/index.cjs +19 -0
  5. package/dist/adapters/index.d.cts +66 -0
  6. package/dist/adapters/index.d.mts +66 -0
  7. package/dist/adapters/index.d.ts +66 -0
  8. package/dist/adapters/index.mjs +3 -0
  9. package/dist/ego/index.cjs +22 -0
  10. package/dist/ego/index.d.cts +5 -0
  11. package/dist/ego/index.d.mts +5 -0
  12. package/dist/ego/index.d.ts +5 -0
  13. package/dist/ego/index.mjs +8 -0
  14. package/dist/evaluator/index.cjs +140 -0
  15. package/dist/evaluator/index.d.cts +204 -0
  16. package/dist/evaluator/index.d.mts +204 -0
  17. package/dist/evaluator/index.d.ts +204 -0
  18. package/dist/evaluator/index.mjs +119 -0
  19. package/dist/geo/index.cjs +50 -0
  20. package/dist/geo/index.d.cts +213 -0
  21. package/dist/geo/index.d.mts +213 -0
  22. package/dist/geo/index.d.ts +213 -0
  23. package/dist/geo/index.mjs +18 -0
  24. package/dist/index.cjs +110 -3940
  25. package/dist/index.d.cts +45 -1091
  26. package/dist/index.d.mts +45 -1091
  27. package/dist/index.d.ts +45 -1091
  28. package/dist/index.mjs +11 -3849
  29. package/dist/shared/device-passport.BGfRcQNh.mjs +565 -0
  30. package/dist/shared/device-passport.BKtmLzPt.cjs +1266 -0
  31. package/dist/shared/device-passport.BPQj0qdT.mjs +1238 -0
  32. package/dist/shared/device-passport.CDS7xrj8.d.ts +469 -0
  33. package/dist/shared/device-passport.C_F1vBE8.d.cts +79 -0
  34. package/dist/shared/device-passport.C_F1vBE8.d.mts +79 -0
  35. package/dist/shared/device-passport.C_F1vBE8.d.ts +79 -0
  36. package/dist/shared/device-passport.CfLes-lq.d.cts +469 -0
  37. package/dist/shared/device-passport.Cjxlv8xI.cjs +245 -0
  38. package/dist/shared/device-passport.CsOWqPIr.d.cts +77 -0
  39. package/dist/shared/device-passport.CyQtcZeD.mjs +237 -0
  40. package/dist/shared/device-passport.D7pEnyp2.d.ts +77 -0
  41. package/dist/shared/device-passport.D9xW6R0B.cjs +1571 -0
  42. package/dist/shared/device-passport.DirQmSd4.d.cts +47 -0
  43. package/dist/shared/device-passport.DirQmSd4.d.mts +47 -0
  44. package/dist/shared/device-passport.DirQmSd4.d.ts +47 -0
  45. package/dist/shared/device-passport.EK4RX7Xd.cjs +584 -0
  46. package/dist/shared/device-passport.ZBspe4ZW.d.mts +77 -0
  47. package/dist/shared/device-passport.dVEqzSEb.d.mts +469 -0
  48. package/dist/shared/device-passport.gNo82fQ5.mjs +1537 -0
  49. package/dist/shared/device-passport.kekU28UM.mjs +705 -0
  50. package/dist/shared/device-passport.qL4tjwc0.cjs +718 -0
  51. package/dist/storage/index.cjs +10 -0
  52. package/dist/storage/index.d.cts +97 -0
  53. package/dist/storage/index.d.mts +97 -0
  54. package/dist/storage/index.d.ts +97 -0
  55. package/dist/storage/index.mjs +3 -0
  56. package/dist/types/index.cjs +2 -0
  57. package/dist/types/index.d.cts +397 -0
  58. package/dist/types/index.d.mts +397 -0
  59. package/dist/types/index.d.ts +397 -0
  60. package/dist/types/index.mjs +1 -0
  61. package/package.json +32 -2
@@ -0,0 +1,397 @@
1
+ import { L as LocaleProfile } from '../shared/device-passport.C_F1vBE8.js';
2
+ export { C as CountryGeoDefinition, G as GeoContext, a as GeoCoordinates, b as GeoLocation, c as GeoOptions, d as LanguageWeight } from '../shared/device-passport.C_F1vBE8.js';
3
+
4
+ /**
5
+ * Metrics, Consistency Evaluation, and Benchmark types for Device Passport.
6
+ */
7
+ type ConsistencyCategory = 'os_ua_alignment' | 'client_hints_entropy' | 'hardware_plausibility' | 'geo_locale_coherence' | 'webgl_hardware_coupling' | 'screen_viewport_consistency';
8
+ interface ConsistencyCheck {
9
+ id: string;
10
+ name: string;
11
+ category: ConsistencyCategory;
12
+ passed: boolean;
13
+ score: number;
14
+ weight: number;
15
+ message: string;
16
+ details?: Record<string, unknown>;
17
+ }
18
+ interface EntropyAnalysis {
19
+ estimatedBits: number;
20
+ collisionRisk: 'very_low' | 'low' | 'moderate' | 'high';
21
+ fingerprintUniqueness?: number;
22
+ }
23
+ type EvaluationStatus = 'PASS' | 'WARN' | 'FAIL';
24
+ type EvaluationGrade = 'A+' | 'A' | 'B' | 'C' | 'F';
25
+ interface PassportEvaluation {
26
+ /** Overall consistency score (0 - 100) */
27
+ overallScore: number;
28
+ /** Pass / Warn / Fail status based on strict thresholds */
29
+ status: EvaluationStatus;
30
+ /** Academic grade assessment */
31
+ grade: EvaluationGrade;
32
+ /** Individual check breakdowns */
33
+ checks: ConsistencyCheck[];
34
+ /** Entropy and collision analysis */
35
+ entropyAnalysis?: EntropyAnalysis;
36
+ /** ISO timestamp when evaluated */
37
+ evaluatedAt: string;
38
+ }
39
+ interface BenchmarkMetrics {
40
+ passportId: string;
41
+ determinismScore: number;
42
+ contradictionRate: number;
43
+ overallConsistencyScore: number;
44
+ generationLatencyMs: number;
45
+ entropyBits: number;
46
+ timestamp: string;
47
+ }
48
+
49
+ /**
50
+ * Chrome DevTools Protocol command types.
51
+ *
52
+ * 🔴 SSOP: these live in `src/types/` so that both the CDP adapter and the
53
+ * ego-lite account profile layer speak about "a CDP command" with one shape.
54
+ * Do not redeclare these interfaces inside a submodule.
55
+ */
56
+ /** A single CDP command, ready to be sent over any CDP transport. */
57
+ interface CdpCommand {
58
+ method: string;
59
+ params?: Record<string, unknown>;
60
+ /** `sessionId` is required for page-scoped domains when using flattened sessions. */
61
+ sessionId?: string;
62
+ }
63
+ /** An init script installed on every new document of a target. */
64
+ interface CdpInitScript {
65
+ source: string;
66
+ /** `sessionId` is required for page-scoped targets when using flattened sessions. */
67
+ sessionId?: string;
68
+ }
69
+ /** The ordered, declarative plan that applies a passport to a CDP target. */
70
+ interface CdpPassportPlan {
71
+ /** Ordered commands to apply the passport to a target. */
72
+ commands: CdpCommand[];
73
+ /** Init scripts to install on every new document. */
74
+ initScripts: CdpInitScript[];
75
+ /** Extra HTTP headers to set on the session. */
76
+ headers: Record<string, string>;
77
+ /** The raw stealth + pixel-protection script, for callers that prefer their own path. */
78
+ stealthScript: string;
79
+ }
80
+
81
+ /**
82
+ * ego-lite account profile types.
83
+ *
84
+ * 🔴 SSOP: shared domain types live here, not inside `src/ego/`.
85
+ *
86
+ * ## Why the proxy is a declared *gap* rather than a feature
87
+ *
88
+ * ego-lite exposes no browser-level proxy configuration:
89
+ * · there is no `Browser.setProxySettings` in CDP (Chromium has never shipped it),
90
+ * · ego-lite's native bindings have no proxy setter either — the binding list is
91
+ * `listProfiles / createTaskSpace / useTaskSpace / closeTaskSpace / listTaskSpaces /
92
+ * sendCDPMessage / snapshot / createTab / setAgentTaskState / handOffTaskSpace /
93
+ * takeOverTaskSpace / claimTaskSpace / completeTaskSpace / animations / learnings / helpers`,
94
+ * · `--proxy-server` is a *launch* flag on the browser process, not a CDP command,
95
+ * so it cannot be changed after the profile is already running.
96
+ *
97
+ * A proxy therefore has to be bound **outside** the browser: at the OS level
98
+ * (system proxy / TUN / transparent redirect), or by routing the ego-lite process
99
+ * through a local forwarder that owns the upstream proxy.
100
+ *
101
+ * This layer refuses to pretend otherwise. An account profile either carries a
102
+ * declared proxy binding (`ready`) or it does not (`missing-proxy`), and the
103
+ * session plan repeats that state so a caller cannot mistake "no proxy" for
104
+ * "proxy applied".
105
+ */
106
+
107
+ /**
108
+ * One browser profile exposed by ego-lite.
109
+ *
110
+ * Mirrors `ego.listProfiles()`: ids are stable locators, display names are not
111
+ * guaranteed to be unique.
112
+ */
113
+ interface EgoProfile {
114
+ /** Stable profile id — the value to pass as `profileId` when creating a task space. */
115
+ id: string;
116
+ /** Human-readable display name. Not unique. */
117
+ name: string;
118
+ /** Whether this is the browser's default profile. */
119
+ isDefault: boolean;
120
+ }
121
+ /**
122
+ * The slice of the ego-lite global bridge this layer talks to.
123
+ *
124
+ * Deliberately narrow and structural: the layer never imports ego-lite (it is an
125
+ * optional peer dependency, and this package ships zero dependencies). Tests and
126
+ * callers inject their own implementation.
127
+ */
128
+ interface EgoLiteBridge {
129
+ /**
130
+ * List the browser profiles available for new task spaces.
131
+ * Mirrors `ego.listProfiles()`.
132
+ */
133
+ listProfiles(): Promise<EgoProfile[]>;
134
+ /**
135
+ * Create (or reuse) a task space bound to a profile.
136
+ * Mirrors `taskSpace(name, { profileId })`.
137
+ *
138
+ * @returns The task space identifier used to select the space later.
139
+ */
140
+ createTaskSpace(name: string, profileId: string): Promise<string>;
141
+ }
142
+ /**
143
+ * A proxy binding for an account profile.
144
+ *
145
+ * ⚠️ This is a *declaration*, not an applied configuration. ego-lite cannot
146
+ * configure a proxy at the browser layer, so the caller must make this true at
147
+ * the system layer (OS proxy / TUN / local forwarder) before starting a session.
148
+ */
149
+ interface EgoProxyBinding {
150
+ /** Proxy URL, e.g. `http://user:pass@gate.example.com:7000`. */
151
+ url: string;
152
+ /** Human-readable description of which egress this binding represents. */
153
+ description: string;
154
+ /**
155
+ * ISO 3166-1 alpha-2 country the proxy egresses from, when known.
156
+ * Used to keep the passport's geo context aligned with the exit IP.
157
+ */
158
+ country?: string;
159
+ }
160
+ /**
161
+ * Readiness of an account profile for launching a session.
162
+ *
163
+ * - `ready` — a profile is bound AND a proxy is declared.
164
+ * - `missing-proxy` — a profile is bound, but no proxy is declared.
165
+ * - `missing-profile`— a proxy is declared, but no ego-lite profile is bound.
166
+ *
167
+ * `missing-proxy` exists so that "the browser has no proxy" can never be
168
+ * silently read as "the proxy is in effect".
169
+ */
170
+ type EgoProfileReadiness = 'ready' | 'missing-proxy' | 'missing-profile';
171
+ /**
172
+ * The persisted account profile: one account (seed) mapped to a sticky
173
+ * passport, an ego-lite profile binding, and a proxy declaration.
174
+ */
175
+ interface EgoAccountProfile {
176
+ /** The account seed. Same seed ⇒ same sticky passport, forever. */
177
+ seed: string;
178
+ /** Id of the persisted passport record (see `DevicePassport.id`). */
179
+ passportId: string;
180
+ /** The ego-lite profile this account is bound to. */
181
+ profileId: string;
182
+ /** Declared proxy binding, or `null` when the account has no proxy. */
183
+ proxy: EgoProxyBinding | null;
184
+ /** ISO 8601 timestamp of the last write. */
185
+ updatedAt: string;
186
+ }
187
+ /**
188
+ * The proxy declaration attached to a session plan.
189
+ *
190
+ * `bound: false` means the session will egress directly — the caller must
191
+ * arrange the proxy outside the browser before launching.
192
+ */
193
+ interface EgoProxyDeclaration {
194
+ /** Whether a proxy is declared for this account. */
195
+ bound: boolean;
196
+ /** The declared binding, present only when `bound` is true. */
197
+ binding: EgoProxyBinding | null;
198
+ /**
199
+ * How the proxy is actually applied, given ego-lite's capability boundary.
200
+ * Always `'system-level'` — the browser layer cannot set it.
201
+ */
202
+ resolution: 'system-level';
203
+ /**
204
+ * Human-readable note telling the operator what to do when `bound` is true,
205
+ * or why it cannot be applied when `bound` is false.
206
+ */
207
+ note: string;
208
+ }
209
+ /**
210
+ * Everything required to start one account session in ego-lite.
211
+ *
212
+ * Extends {@link CdpPassportPlan} so the CDP payload has exactly one definition.
213
+ * Re-declaring `commands` / `initScripts` / `headers` here would let the two
214
+ * shapes drift apart, and would silently drop `stealthScript` from the plan.
215
+ */
216
+ interface EgoSessionPlan extends CdpPassportPlan {
217
+ /** The account seed this plan belongs to. */
218
+ seed: string;
219
+ /** The ego-lite profile the session must run under. */
220
+ profileId: string;
221
+ /** Suggested task space name (unique per launch attempt). */
222
+ taskSpaceName: string;
223
+ /** The proxy declaration, with its `bound` state spelled out. */
224
+ proxy: EgoProxyDeclaration;
225
+ /** Readiness of the underlying account profile. */
226
+ readiness: EgoProfileReadiness;
227
+ /** Whether the session may be launched as-is. */
228
+ launchable: boolean;
229
+ }
230
+
231
+ /**
232
+ * Core type definitions for Device Passport.
233
+ * Compliant with RFC 8942, W3C User-Agent Client Hints, and WebGL specs.
234
+ */
235
+
236
+ type SupportedBrowserName = 'Chrome' | 'Edge' | 'Safari' | 'Firefox';
237
+ type SupportedPlatform = 'macOS' | 'Windows';
238
+ type SupportedArchitecture = 'arm' | 'x86';
239
+ interface BrowserProfile {
240
+ name: SupportedBrowserName;
241
+ version: string;
242
+ majorVersion: number;
243
+ userAgent: string;
244
+ }
245
+ interface OperatingSystemProfile {
246
+ name: SupportedPlatform;
247
+ platform: SupportedPlatform;
248
+ version: string;
249
+ release: string;
250
+ architecture: SupportedArchitecture;
251
+ }
252
+ interface ScreenProfile {
253
+ width: number;
254
+ height: number;
255
+ availWidth: number;
256
+ availHeight: number;
257
+ dpr: number;
258
+ colorDepth: number;
259
+ pixelDepth: number;
260
+ }
261
+ interface CpuProfile {
262
+ architecture: SupportedArchitecture;
263
+ concurrency: number;
264
+ }
265
+ interface MemoryProfile {
266
+ deviceMemory: number;
267
+ physicalMemoryGB?: number;
268
+ }
269
+ interface GpuProfile {
270
+ vendor: string;
271
+ renderer: string;
272
+ webglVendor?: string;
273
+ webglRenderer?: string;
274
+ }
275
+ interface HardwareProfile {
276
+ screen: ScreenProfile;
277
+ cpu: CpuProfile;
278
+ memory: MemoryProfile;
279
+ gpu: GpuProfile;
280
+ }
281
+ interface BrandVersion {
282
+ brand: string;
283
+ version: string;
284
+ }
285
+ /**
286
+ * Raw RFC 8942 / RFC 8941 structured client hints headers.
287
+ */
288
+ interface ClientHintsHeaders {
289
+ /** Low-entropy brand and major version, e.g. `"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"` */
290
+ 'sec-ch-ua': string;
291
+ /** Low-entropy mobile indicator, `"?0"` or `"?1"` */
292
+ 'sec-ch-ua-mobile': string;
293
+ /** Low-entropy platform, e.g. `"macOS"` or `"Windows"` */
294
+ 'sec-ch-ua-platform': string;
295
+ /** High-entropy OS version, e.g. `"14.6.1"` or `"15.0.0"` */
296
+ 'sec-ch-ua-platform-version': string;
297
+ /** High-entropy architecture, e.g. `"arm"` or `"x86"` */
298
+ 'sec-ch-ua-arch': string;
299
+ /** High-entropy bitness, e.g. `"64"` */
300
+ 'sec-ch-ua-bitness': string;
301
+ /** High-entropy device model, `""` for desktop */
302
+ 'sec-ch-ua-model': string;
303
+ /** High-entropy full version list, e.g. `"Chromium";v="124.0.6367.208", "Google Chrome";v="124.0.6367.208"` */
304
+ 'sec-ch-ua-full-version-list': string;
305
+ /** High-entropy 32-bit on 64-bit Windows flag, `"?0"` */
306
+ 'sec-ch-ua-wow64'?: string;
307
+ }
308
+ /**
309
+ * Structured User-Agent Client Hints object representing navigator.userAgentData.
310
+ */
311
+ interface UserAgentClientHints {
312
+ brands: BrandVersion[];
313
+ mobile: boolean;
314
+ platform: string;
315
+ platformVersion: string;
316
+ architecture: string;
317
+ bitness: string;
318
+ model: string;
319
+ fullVersionList: BrandVersion[];
320
+ wow64?: boolean;
321
+ /** Serialized RFC 8942 compliant headers */
322
+ headers: ClientHintsHeaders;
323
+ }
324
+ interface DevicePassportContext {
325
+ viewport: {
326
+ width: number;
327
+ height: number;
328
+ };
329
+ userAgent: string;
330
+ locale: string;
331
+ timezoneId: string;
332
+ geolocation?: {
333
+ latitude: number;
334
+ longitude: number;
335
+ accuracy?: number;
336
+ };
337
+ permissions?: string[];
338
+ extraHTTPHeaders: Record<string, string>;
339
+ deviceScaleFactor: number;
340
+ hasTouch: boolean;
341
+ isMobile: boolean;
342
+ colorScheme?: 'light' | 'dark' | 'no-preference';
343
+ }
344
+ interface DevicePassport {
345
+ /** Unique deterministic or UUID identifier */
346
+ id: string;
347
+ /** Seed used to generate this passport */
348
+ seed: string;
349
+ /** ISO 8601 creation timestamp */
350
+ createdAt: string;
351
+ /** Browser profile */
352
+ browser: BrowserProfile;
353
+ /** Operating system profile */
354
+ os: OperatingSystemProfile;
355
+ /** Hardware profile (screen, cpu, memory, gpu) */
356
+ hardware: HardwareProfile;
357
+ /** Locale, language, timezone */
358
+ locale: LocaleProfile;
359
+ /** All HTTP headers (User-Agent, Sec-CH-UA*, Accept*, etc.) */
360
+ headers: Record<string, string>;
361
+ /** High-entropy and low-entropy Client Hints */
362
+ clientHints: UserAgentClientHints;
363
+ /** Ready-to-use context options for Playwright / Puppeteer / Browser */
364
+ context: DevicePassportContext;
365
+ /** Consistency and anti-detection score evaluation */
366
+ evaluation?: PassportEvaluation;
367
+ /** Optional helper method to get merged headers */
368
+ getHeaders?: (overrides?: Record<string, string>) => Record<string, string>;
369
+ }
370
+ /** Alias for backward compatibility */
371
+ type DeviceProfile = DevicePassport;
372
+ interface PassportGeneratorOptions {
373
+ /** Deterministic seed (username, account ID, proxy IP, etc.) */
374
+ seed?: string | number;
375
+ /** Geographic country code ('US', 'JP', etc.) or residential proxy URL */
376
+ geo?: string;
377
+ /** Residential proxy URL to detect geographical location from */
378
+ proxy?: string;
379
+ proxyUrl?: string;
380
+ /** Preferred Operating System */
381
+ os?: SupportedPlatform;
382
+ /** Preferred Browser */
383
+ browser?: SupportedBrowserName;
384
+ /** Preferred primary locale (e.g. 'en-US', 'zh-CN') */
385
+ locale?: string;
386
+ /** Preferred country code (e.g. 'US', 'GB', 'DE', 'JP') */
387
+ country?: string;
388
+ /** Preferred timezone (e.g. 'America/New_York') */
389
+ timezone?: string;
390
+ /** Whether to integrate with header-generator when available */
391
+ useHeaderGenerator?: boolean;
392
+ /** Hardware tier preference */
393
+ hardwareTier?: 'standard' | 'high-performance';
394
+ }
395
+
396
+ export { LocaleProfile };
397
+ export type { BenchmarkMetrics, BrandVersion, BrowserProfile, CdpCommand, CdpInitScript, CdpPassportPlan, ClientHintsHeaders, ConsistencyCategory, ConsistencyCheck, CpuProfile, DevicePassport, DevicePassportContext, DeviceProfile, EgoAccountProfile, EgoLiteBridge, EgoProfile, EgoProfileReadiness, EgoProxyBinding, EgoProxyDeclaration, EgoSessionPlan, EntropyAnalysis, EvaluationGrade, EvaluationStatus, GpuProfile, HardwareProfile, MemoryProfile, OperatingSystemProfile, PassportEvaluation, PassportGeneratorOptions, ScreenProfile, SupportedArchitecture, SupportedBrowserName, SupportedPlatform, UserAgentClientHints };
@@ -0,0 +1 @@
1
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "device-passport",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Digital device passports for scrapers, bots and automation. 100% consistent, stateful browser personas with sticky hardware, correlated Client-Hints, and geo-aligned locales.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -11,6 +11,36 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.mjs",
13
13
  "require": "./dist/index.cjs"
14
+ },
15
+ "./geo": {
16
+ "types": "./dist/geo/index.d.ts",
17
+ "import": "./dist/geo/index.mjs",
18
+ "require": "./dist/geo/index.cjs"
19
+ },
20
+ "./storage": {
21
+ "types": "./dist/storage/index.d.ts",
22
+ "import": "./dist/storage/index.mjs",
23
+ "require": "./dist/storage/index.cjs"
24
+ },
25
+ "./adapters": {
26
+ "types": "./dist/adapters/index.d.ts",
27
+ "import": "./dist/adapters/index.mjs",
28
+ "require": "./dist/adapters/index.cjs"
29
+ },
30
+ "./ego": {
31
+ "types": "./dist/ego/index.d.ts",
32
+ "import": "./dist/ego/index.mjs",
33
+ "require": "./dist/ego/index.cjs"
34
+ },
35
+ "./evaluator": {
36
+ "types": "./dist/evaluator/index.d.ts",
37
+ "import": "./dist/evaluator/index.mjs",
38
+ "require": "./dist/evaluator/index.cjs"
39
+ },
40
+ "./types": {
41
+ "types": "./dist/types/index.d.ts",
42
+ "import": "./dist/types/index.mjs",
43
+ "require": "./dist/types/index.cjs"
14
44
  }
15
45
  },
16
46
  "files": [
@@ -24,7 +54,7 @@
24
54
  "build": "unbuild",
25
55
  "test": "bun test tests/",
26
56
  "lint": "tsc --noEmit",
27
- "prepublishOnly": "bun run test && bun run build"
57
+ "prepublishOnly": "npm run build && node scripts/prepublish-gate.mjs && node scripts/readme-gate.mjs && node scripts/version-sync-gate.mjs"
28
58
  },
29
59
  "keywords": [
30
60
  "user-agent",