@browsercore/testing 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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +98 -0
  3. package/dist/bench/bench.d.ts +21 -0
  4. package/dist/bench/bench.d.ts.map +1 -0
  5. package/dist/bench/bench.js +19 -0
  6. package/dist/bench/bench.js.map +1 -0
  7. package/dist/captures/manifest.d.ts +18 -0
  8. package/dist/captures/manifest.d.ts.map +1 -0
  9. package/dist/captures/manifest.js +62 -0
  10. package/dist/captures/manifest.js.map +1 -0
  11. package/dist/categories/capture.d.ts +8 -0
  12. package/dist/categories/capture.d.ts.map +1 -0
  13. package/dist/categories/capture.js +31 -0
  14. package/dist/categories/capture.js.map +1 -0
  15. package/dist/categories/compression.d.ts +8 -0
  16. package/dist/categories/compression.d.ts.map +1 -0
  17. package/dist/categories/compression.js +15 -0
  18. package/dist/categories/compression.js.map +1 -0
  19. package/dist/categories/cookies.d.ts +8 -0
  20. package/dist/categories/cookies.d.ts.map +1 -0
  21. package/dist/categories/cookies.js +20 -0
  22. package/dist/categories/cookies.js.map +1 -0
  23. package/dist/categories/errors.d.ts +11 -0
  24. package/dist/categories/errors.d.ts.map +1 -0
  25. package/dist/categories/errors.js +41 -0
  26. package/dist/categories/errors.js.map +1 -0
  27. package/dist/categories/http.d.ts +9 -0
  28. package/dist/categories/http.d.ts.map +1 -0
  29. package/dist/categories/http.js +46 -0
  30. package/dist/categories/http.js.map +1 -0
  31. package/dist/categories/index.d.ts +17 -0
  32. package/dist/categories/index.d.ts.map +1 -0
  33. package/dist/categories/index.js +17 -0
  34. package/dist/categories/index.js.map +1 -0
  35. package/dist/categories/redirects.d.ts +8 -0
  36. package/dist/categories/redirects.d.ts.map +1 -0
  37. package/dist/categories/redirects.js +21 -0
  38. package/dist/categories/redirects.js.map +1 -0
  39. package/dist/categories/regression.d.ts +8 -0
  40. package/dist/categories/regression.d.ts.map +1 -0
  41. package/dist/categories/regression.js +28 -0
  42. package/dist/categories/regression.js.map +1 -0
  43. package/dist/categories/session.d.ts +8 -0
  44. package/dist/categories/session.d.ts.map +1 -0
  45. package/dist/categories/session.js +23 -0
  46. package/dist/categories/session.js.map +1 -0
  47. package/dist/categories/tls.d.ts +10 -0
  48. package/dist/categories/tls.d.ts.map +1 -0
  49. package/dist/categories/tls.js +48 -0
  50. package/dist/categories/tls.js.map +1 -0
  51. package/dist/categories/transport.d.ts +8 -0
  52. package/dist/categories/transport.d.ts.map +1 -0
  53. package/dist/categories/transport.js +22 -0
  54. package/dist/categories/transport.js.map +1 -0
  55. package/dist/errors.d.ts +22 -0
  56. package/dist/errors.d.ts.map +1 -0
  57. package/dist/errors.js +28 -0
  58. package/dist/errors.js.map +1 -0
  59. package/dist/fingerprint/index.d.ts +5 -0
  60. package/dist/fingerprint/index.d.ts.map +1 -0
  61. package/dist/fingerprint/index.js +4 -0
  62. package/dist/fingerprint/index.js.map +1 -0
  63. package/dist/fingerprint/ja3.d.ts +36 -0
  64. package/dist/fingerprint/ja3.d.ts.map +1 -0
  65. package/dist/fingerprint/ja3.js +141 -0
  66. package/dist/fingerprint/ja3.js.map +1 -0
  67. package/dist/fingerprint/ja4.d.ts +109 -0
  68. package/dist/fingerprint/ja4.d.ts.map +1 -0
  69. package/dist/fingerprint/ja4.js +325 -0
  70. package/dist/fingerprint/ja4.js.map +1 -0
  71. package/dist/golden/golden.d.ts +45 -0
  72. package/dist/golden/golden.d.ts.map +1 -0
  73. package/dist/golden/golden.js +216 -0
  74. package/dist/golden/golden.js.map +1 -0
  75. package/dist/index.d.ts +25 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +25 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/reference/node-reference.d.ts +83 -0
  80. package/dist/reference/node-reference.d.ts.map +1 -0
  81. package/dist/reference/node-reference.js +161 -0
  82. package/dist/reference/node-reference.js.map +1 -0
  83. package/dist/reference/reference.d.ts +143 -0
  84. package/dist/reference/reference.d.ts.map +1 -0
  85. package/dist/reference/reference.js +323 -0
  86. package/dist/reference/reference.js.map +1 -0
  87. package/dist/rfc/rfcTests.d.ts +14 -0
  88. package/dist/rfc/rfcTests.d.ts.map +1 -0
  89. package/dist/rfc/rfcTests.js +22 -0
  90. package/dist/rfc/rfcTests.js.map +1 -0
  91. package/dist/types.d.ts +140 -0
  92. package/dist/types.d.ts.map +1 -0
  93. package/dist/types.js +34 -0
  94. package/dist/types.js.map +1 -0
  95. package/dist/utils.d.ts +29 -0
  96. package/dist/utils.d.ts.map +1 -0
  97. package/dist/utils.js +102 -0
  98. package/dist/utils.js.map +1 -0
  99. package/package.json +53 -0
@@ -0,0 +1,323 @@
1
+ /**
2
+ * Pluggable layered reference provider.
3
+ *
4
+ * The reference implementation is the source of truth for what a given browser
5
+ * profile SHOULD produce on the wire. Three backends are wrapped behind one
6
+ * facade:
7
+ *
8
+ * - `node-reference` (oracle) — Node's built-in crypto/dns/zlib/http
9
+ * implementations, used as the spec oracle for primitive layers where Node IS
10
+ * the reference (see `node-reference.ts`).
11
+ * - `curl-impersonate` (PRIMARY) — fast CI path; shells out to the
12
+ * curl-impersonate binary to capture real traffic for a profile.
13
+ * - `real-browser` (SECONDARY) — loads pre-recorded captures from the
14
+ * `captures/` directory, recorded from an actual browser.
15
+ *
16
+ * The {@link ReferenceProviderFacade} ties them together: capture resolves via
17
+ * primary then secondary; fingerprint is derived from the captured bytes; the
18
+ * node-reference oracle is exposed for primitive-layer comparisons.
19
+ *
20
+ * See docs/TEST-SUITE.md (Cat 3, 4, 14) for how references feed comparison.
21
+ */
22
+ import { execFile } from "node:child_process";
23
+ import { fileURLToPath } from "node:url";
24
+ import { dirname, join } from "node:path";
25
+ import { promisify } from "node:util";
26
+ import { computeJa3 } from "../fingerprint/ja3.js";
27
+ import { computeJa4Fingerprint } from "../fingerprint/ja4.js";
28
+ import { loadCaptureMeta } from "../golden/golden.js";
29
+ import { TestingError } from "../errors.js";
30
+ const execFileAsync = promisify(execFile);
31
+ const here = dirname(fileURLToPath(import.meta.url));
32
+ const packageRoot = join(here, "..", "..");
33
+ const defaultCapturesDir = join(packageRoot, "captures");
34
+ /** Raised when a reference provider cannot capture or fingerprint a profile. */
35
+ export class ReferenceError extends TestingError {
36
+ constructor(message, options) {
37
+ super(message, options);
38
+ this.name = "ReferenceError";
39
+ }
40
+ }
41
+ /**
42
+ * PRIMARY provider — shells out to curl-impersonate.
43
+ *
44
+ * `capture()` invokes the curl-impersonate binary against `url`, impersonating
45
+ * `profile`, and returns the raw bytes it observed on the wire. The binary
46
+ * must be on PATH (or in `command`); if it is missing, the call throws
47
+ * {@link ReferenceError} so the facade can fall back to the secondary.
48
+ */
49
+ export class CurlImpersonateProvider {
50
+ kind = { kind: "curl-impersonate" };
51
+ command;
52
+ extraArgs;
53
+ constructor(options) {
54
+ this.command = options?.command ?? "curl-impersonate";
55
+ this.extraArgs = options?.extraArgs ?? [];
56
+ }
57
+ availableProfiles() {
58
+ // curl-impersonate ships these browser impersonation targets.
59
+ return [
60
+ "chrome-140",
61
+ "chrome-139",
62
+ "firefox-135",
63
+ "firefox-128",
64
+ "safari-18",
65
+ "edge-140",
66
+ ];
67
+ }
68
+ async capture(profile, url) {
69
+ const profileFlag = `--${String(profile)}`;
70
+ const args = [profileFlag, "--dump-traffic", ...this.extraArgs, url];
71
+ let stdout;
72
+ let stderr;
73
+ try {
74
+ const out = await execFileAsync(this.command, args, {
75
+ timeout: 30_000,
76
+ maxBuffer: 64 * 1024 * 1024,
77
+ });
78
+ stdout = out.stdout;
79
+ stderr = out.stderr;
80
+ }
81
+ catch (e) {
82
+ const cause = e instanceof Error ? e : new Error(String(e));
83
+ throw new ReferenceError(`curl-impersonate capture for ${String(profile)} failed: ${cause.message}`, { cause });
84
+ }
85
+ void stderr;
86
+ const bytes = parseDumpOutput(stdout);
87
+ return {
88
+ id: `${profile}/tls/client_hello`,
89
+ source: profileToSource(profile),
90
+ protocol: "tls",
91
+ bytes,
92
+ description: `curl-impersonate capture for ${String(profile)}`,
93
+ };
94
+ }
95
+ async fingerprint(profile) {
96
+ const capture = await this.capture(profile, "https://example.com");
97
+ return fingerprintFromTlsCapture(capture);
98
+ }
99
+ }
100
+ /**
101
+ * SECONDARY provider — loads pre-recorded captures from the `captures/` dir.
102
+ *
103
+ * `capture()` resolves a stored capture for the profile; `fingerprint()`
104
+ * derives a {@link Fingerprint} from the captured bytes (only TLS
105
+ * ClientHellos are fingerprinted — other protocols return a stub).
106
+ */
107
+ export class RealBrowserCaptureProvider {
108
+ kind = { kind: "real-browser" };
109
+ capturesDir;
110
+ constructor(options) {
111
+ this.capturesDir = options?.capturesDir ?? defaultCapturesDir;
112
+ }
113
+ availableProfiles() {
114
+ // Discovery is driven by the captures manifest (captures/manifest.ts).
115
+ return [
116
+ "chrome-140",
117
+ "firefox-128",
118
+ ];
119
+ }
120
+ async capture(profile, _url) {
121
+ void _url;
122
+ const manifest = await import("../captures/manifest.js");
123
+ const entry = manifest.captures.find((c) => c.meta.profile === profile);
124
+ if (entry === undefined) {
125
+ throw new ReferenceError(`No pre-recorded capture for profile ${String(profile)}`);
126
+ }
127
+ return {
128
+ id: `${profile}/tls/client_hello`,
129
+ source: profileToSource(profile),
130
+ protocol: entry.meta.protocol,
131
+ bytes: entry.bytes,
132
+ description: entry.meta.description,
133
+ };
134
+ }
135
+ async fingerprint(profile) {
136
+ const capture = await this.capture(profile, "https://example.com");
137
+ if (capture.protocol !== "tls") {
138
+ throw new ReferenceError(`fingerprint() only supports TLS captures; got ${capture.protocol}`);
139
+ }
140
+ return fingerprintFromTlsCapture(capture);
141
+ }
142
+ }
143
+ /**
144
+ * Facade wrapping the node-reference oracle + primary (curl-impersonate) and
145
+ * secondary (real-browser) providers behind a single {@link ReferenceProvider}.
146
+ *
147
+ * `capture()` tries the primary first; if it fails (binary missing, network
148
+ * error, ...) it falls back to the secondary's pre-recorded captures. The
149
+ * node-reference oracle is exposed via {@link ReferenceProviderFacade.nodeOracle}
150
+ * for primitive-layer comparisons (crypto, dns, zlib, wire format) — those
151
+ * layers are NOT fingerprinted here; they live in `node-reference.ts`.
152
+ */
153
+ export class ReferenceProviderFacade {
154
+ kind = { kind: "curl-impersonate" };
155
+ primary;
156
+ secondary;
157
+ constructor(options) {
158
+ this.primary = new CurlImpersonateProvider(options?.curl);
159
+ this.secondary = new RealBrowserCaptureProvider(options?.browser);
160
+ }
161
+ /**
162
+ * Node-reference oracle — the spec oracle for primitive layers where Node
163
+ * IS the reference (crypto, dns, zlib, wire format). NOT used for
164
+ * browser-fingerprint comparison; that is what the providers are for.
165
+ */
166
+ get nodeOracle() {
167
+ return nodeOracle;
168
+ }
169
+ availableProfiles() {
170
+ // Union of profiles from both providers, de-duplicated.
171
+ const seen = new Set();
172
+ const out = [];
173
+ for (const p of [...this.primary.availableProfiles(), ...this.secondary.availableProfiles()]) {
174
+ const key = String(p);
175
+ if (seen.has(key))
176
+ continue;
177
+ seen.add(key);
178
+ out.push(p);
179
+ }
180
+ return out;
181
+ }
182
+ async capture(profile, url) {
183
+ try {
184
+ return await this.primary.capture(profile, url);
185
+ }
186
+ catch (primaryErr) {
187
+ const cause = primaryErr instanceof Error ? primaryErr : new Error(String(primaryErr));
188
+ // Fall back to the secondary provider's pre-recorded captures.
189
+ try {
190
+ return await this.secondary.capture(profile, url);
191
+ }
192
+ catch (secondaryErr) {
193
+ const secondaryCause = secondaryErr instanceof Error ? secondaryErr : new Error(String(secondaryErr));
194
+ throw new ReferenceError(`No reference available for ${String(profile)}: primary failed (${cause.message}), secondary failed (${secondaryCause.message})`, { cause: secondaryCause });
195
+ }
196
+ }
197
+ }
198
+ async fingerprint(profile) {
199
+ try {
200
+ return await this.primary.fingerprint(profile);
201
+ }
202
+ catch (primaryErr) {
203
+ const cause = primaryErr instanceof Error ? primaryErr : new Error(String(primaryErr));
204
+ try {
205
+ return await this.secondary.fingerprint(profile);
206
+ }
207
+ catch (secondaryErr) {
208
+ const secondaryCause = secondaryErr instanceof Error ? secondaryErr : new Error(String(secondaryErr));
209
+ throw new ReferenceError(`No fingerprint available for ${String(profile)}: primary failed (${cause.message}), secondary failed (${secondaryCause.message})`, { cause: secondaryCause });
210
+ }
211
+ }
212
+ }
213
+ }
214
+ /**
215
+ * Node-reference oracle re-export — the spec oracle for primitive layers.
216
+ *
217
+ * Imported lazily so the facade type references resolve without a cycle.
218
+ */
219
+ import * as nodeOracle from "./node-reference.js";
220
+ /** Construct the {@link ReferenceProvider} matching {@link ReferenceProviderKind}. */
221
+ export function createReferenceProvider(kind, options) {
222
+ switch (kind.kind) {
223
+ case "curl-impersonate":
224
+ return new CurlImpersonateProvider(options);
225
+ case "real-browser":
226
+ return new RealBrowserCaptureProvider(options);
227
+ }
228
+ }
229
+ /**
230
+ * Construct the full {@link ReferenceProviderFacade} wrapping both providers
231
+ * plus the node-reference oracle. This is the recommended entry point for
232
+ * tests that want primary→secondary fallback.
233
+ */
234
+ export function createReferenceFacade(options) {
235
+ return new ReferenceProviderFacade(options);
236
+ }
237
+ /** Map a {@link ProfileId} to its {@link CaptureSource} tag. */
238
+ function profileToSource(profile) {
239
+ const p = String(profile);
240
+ if (p.startsWith("firefox"))
241
+ return "firefox-135";
242
+ if (p.startsWith("safari"))
243
+ return "safari-18";
244
+ if (p.startsWith("edge"))
245
+ return "edge-140";
246
+ return "chrome-140";
247
+ }
248
+ /**
249
+ * Parse curl-impersonate `--dump-traffic` output into raw bytes.
250
+ *
251
+ * The dump format is a hex dump with one line per record; we extract the hex
252
+ * payload between the markers. For now we assume the body is a contiguous hex
253
+ * block — adjust the parser if curl-impersonate's format differs.
254
+ */
255
+ function parseDumpOutput(stdout) {
256
+ // Find the hex body — everything after the ">>> traffic <<<" marker.
257
+ const marker = ">>> traffic <<<";
258
+ const idx = stdout.indexOf(marker);
259
+ const body = idx === -1 ? stdout : stdout.slice(idx + marker.length);
260
+ const hex = body.replace(/[^0-9a-fA-F]/g, "");
261
+ if (hex.length === 0) {
262
+ throw new ReferenceError("curl-impersonate dump produced no hex bytes");
263
+ }
264
+ if (hex.length % 2 !== 0) {
265
+ throw new ReferenceError(`curl-impersonate dump produced odd-length hex (${hex.length})`);
266
+ }
267
+ const bytes = new Uint8Array(hex.length / 2);
268
+ for (let i = 0; i < bytes.length; i++) {
269
+ bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
270
+ }
271
+ return bytes;
272
+ }
273
+ /**
274
+ * Derive a {@link Fingerprint} from a TLS ClientHello capture.
275
+ *
276
+ * Computes JA3 + JA4 from the raw bytes and reads the per-extension metadata
277
+ * (supported groups, signature algorithms, ALPN) from the sidecar
278
+ * `.meta.json` when available.
279
+ */
280
+ function fingerprintFromTlsCapture(capture) {
281
+ const ja3 = computeJa3(capture.bytes);
282
+ const ja4 = computeJa4Fingerprint(capture.bytes);
283
+ // Read the sidecar meta for richer fields (signature algorithms, ALPN,
284
+ // supported curves). Fall back to empty arrays if missing.
285
+ let alpn = [];
286
+ let signatureAlgorithms = [];
287
+ let ellipticCurves = [];
288
+ try {
289
+ const meta = loadCaptureMeta(capture.id);
290
+ if (meta.protocol === "tls") {
291
+ // CaptureMeta doesn't carry ALPN/sigAlgs/curves yet; this is a
292
+ // placeholder for when the sidecar schema is extended.
293
+ void meta;
294
+ void alpn;
295
+ void signatureAlgorithms;
296
+ void ellipticCurves;
297
+ }
298
+ }
299
+ catch {
300
+ // Sidecar missing or unparseable — leave richer fields empty.
301
+ }
302
+ return {
303
+ ja3,
304
+ ja4: ja4.tag,
305
+ alpn,
306
+ cipherSuite: cipherSuiteName(ja4.tag),
307
+ protocolVersion: ja4.tag.slice(5, 7) || "unknown",
308
+ signatureAlgorithms,
309
+ ellipticCurves,
310
+ };
311
+ }
312
+ /**
313
+ * Extract a human-readable cipher-suite name from a JA4 tag.
314
+ *
315
+ * JA4 doesn't directly encode the negotiated cipher; this is a placeholder
316
+ * that returns the JA4_a segment for inspection. Real cipher-suite resolution
317
+ * requires parsing the ServerHello, which is out of scope for the capture.
318
+ */
319
+ function cipherSuiteName(ja4Tag) {
320
+ const a = ja4Tag.split("_")[0] ?? "";
321
+ return a.length > 0 ? a : "unknown";
322
+ }
323
+ //# sourceMappingURL=reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reference.js","sourceRoot":"","sources":["../../src/reference/reference.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * RFC compliance test suites.
3
+ *
4
+ * Each function runs a focused compliance check and returns a {@link TestResult}.
5
+ * Stubbed until the underlying protocol stacks exist.
6
+ */
7
+ import type { TestCaseId, TestResult } from "../types.js";
8
+ /** Run the TLS 1.3 (RFC 8446) compliance checks. */
9
+ export declare function runTlsCompliance(_id?: TestCaseId): TestResult;
10
+ /** Run the HTTP/2 (RFC 9113) compliance checks. */
11
+ export declare function runHttp2Compliance(_id?: TestCaseId): TestResult;
12
+ /** Run the HTTP/1.1 (RFC 9110) compliance checks. */
13
+ export declare function runHttp1Compliance(_id?: TestCaseId): TestResult;
14
+ //# sourceMappingURL=rfcTests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rfcTests.d.ts","sourceRoot":"","sources":["../../src/rfc/rfcTests.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1D,oDAAoD;AACpD,wBAAgB,gBAAgB,CAC5B,GAAG,GAAE,UAAiC,GACvC,UAAU,CAGZ;AAED,mDAAmD;AACnD,wBAAgB,kBAAkB,CAC9B,GAAG,GAAE,UAAmC,GACzC,UAAU,CAGZ;AAED,qDAAqD;AACrD,wBAAgB,kBAAkB,CAC9B,GAAG,GAAE,UAAmC,GACzC,UAAU,CAGZ"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * RFC compliance test suites.
3
+ *
4
+ * Each function runs a focused compliance check and returns a {@link TestResult}.
5
+ * Stubbed until the underlying protocol stacks exist.
6
+ */
7
+ /** Run the TLS 1.3 (RFC 8446) compliance checks. */
8
+ export function runTlsCompliance(_id = "tls_rfc") {
9
+ void _id;
10
+ throw new Error("not implemented — see PLAN.md");
11
+ }
12
+ /** Run the HTTP/2 (RFC 9113) compliance checks. */
13
+ export function runHttp2Compliance(_id = "http2_rfc") {
14
+ void _id;
15
+ throw new Error("not implemented — see PLAN.md");
16
+ }
17
+ /** Run the HTTP/1.1 (RFC 9110) compliance checks. */
18
+ export function runHttp1Compliance(_id = "http1_rfc") {
19
+ void _id;
20
+ throw new Error("not implemented — see PLAN.md");
21
+ }
22
+ //# sourceMappingURL=rfcTests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rfcTests.js","sourceRoot":"","sources":["../../src/rfc/rfcTests.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,oDAAoD;AACpD,MAAM,UAAU,gBAAgB,CAC5B,MAAmB,SAAmB;IAEtC,KAAK,GAAG,CAAC;IACT,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACrD,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,kBAAkB,CAC9B,MAAmB,WAAqB;IAExC,KAAK,GAAG,CAAC;IACT,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACrD,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,kBAAkB,CAC9B,MAAmB,WAAqB;IAExC,KAAK,GAAG,CAAC;IACT,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;AACrD,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Domain types for @browsercore/testing.
3
+ *
4
+ * Protocol verification: RFC compliance tests, browser golden packet captures,
5
+ * integration tests, and benchmarking. Compares generated packets against
6
+ * captures from real browsers.
7
+ */
8
+ /** Branded test-case identifier. */
9
+ export type TestCaseId = string & {
10
+ __brand: "TestCaseId";
11
+ };
12
+ /** Branded golden-capture identifier. */
13
+ export type CaptureId = string & {
14
+ __brand: "CaptureId";
15
+ };
16
+ /** Known browser capture sources. */
17
+ export type CaptureSource = "chrome-140" | "firefox-135" | "safari-18" | "edge-140";
18
+ /** Which protocol layer a capture records. */
19
+ export type CaptureProtocol = "tls" | "http2" | "http1" | "tcp";
20
+ /** A single runnable test case. */
21
+ export interface TestCase {
22
+ readonly id: TestCaseId;
23
+ readonly name: string;
24
+ readonly description: string;
25
+ run(): Promise<TestResult>;
26
+ }
27
+ /** Outcome of running a single {@link TestCase}. */
28
+ export interface TestResult {
29
+ readonly id: TestCaseId;
30
+ readonly pass: boolean;
31
+ /** Actual bytes / value produced. */
32
+ readonly actual: unknown;
33
+ /** Expected bytes / value. */
34
+ readonly expected: unknown;
35
+ /** Human-readable diff when `pass === false`. */
36
+ readonly diff?: string;
37
+ }
38
+ /** A golden packet capture recorded from a real browser. */
39
+ export interface GoldenCapture {
40
+ readonly id: CaptureId;
41
+ readonly source: CaptureSource;
42
+ readonly protocol: CaptureProtocol;
43
+ /** Raw bytes as seen on the wire. */
44
+ readonly bytes: Uint8Array;
45
+ readonly description: string;
46
+ }
47
+ /** Result of comparing actual bytes against a golden capture. */
48
+ export interface ComparisonResult {
49
+ readonly matches: boolean;
50
+ /** First byte index where actual diverges from expected, if any. */
51
+ readonly divergenceByteIndex: number | undefined;
52
+ readonly message: string;
53
+ }
54
+ /** Summary statistics for a benchmark run. */
55
+ export interface BenchStats {
56
+ readonly iterations: number;
57
+ readonly avgMs: number;
58
+ readonly p50: number;
59
+ readonly p95: number;
60
+ readonly p99: number;
61
+ }
62
+ /**
63
+ * The 17 test categories from docs/TEST-SUITE.md.
64
+ *
65
+ * Defined as a `const` object so the id set is the single source of truth and
66
+ * `TestCategoryId` is derived from it — never a bare string (see
67
+ * CODING_STANDARDS.md §17 "No magic strings").
68
+ */
69
+ export declare const TestCategory: {
70
+ readonly TcpTransport: "tcp-transport";
71
+ readonly TlsSerialization: "tls-serialization";
72
+ readonly BrowserProfileComparison: "browser-profile-comparison";
73
+ readonly TlsFingerprint: "tls-fingerprint";
74
+ readonly Http1: "http1";
75
+ readonly Http2: "http2";
76
+ readonly HeaderProfiles: "header-profiles";
77
+ readonly CookieBehavior: "cookie-behavior";
78
+ readonly Compression: "compression";
79
+ readonly RedirectHandling: "redirect-handling";
80
+ readonly SessionResumption: "session-resumption";
81
+ readonly ConnectionReuse: "connection-reuse";
82
+ readonly ErrorHandling: "error-handling";
83
+ readonly PacketCaptureComparison: "packet-capture-comparison";
84
+ readonly RealWorldCompatibility: "real-world-compatibility";
85
+ readonly Regression: "regression";
86
+ readonly PerformanceBenchmarks: "performance-benchmarks";
87
+ };
88
+ /** One of the 17 known test-category ids. */
89
+ export type TestCategoryId = (typeof TestCategory)[keyof typeof TestCategory];
90
+ /** Lifecycle status of a spec test case in the plan. */
91
+ export type TestStatus = "planned" | "implemented" | "skipped";
92
+ /**
93
+ * A single test case as recorded in the spec plan.
94
+ *
95
+ * Distinct from the runnable {@link TestCase} interface (which carries a
96
+ * `run()` method); this is a static plan/record entry.
97
+ */
98
+ export interface SpecTestCase {
99
+ readonly id: TestCaseId;
100
+ readonly category: TestCategoryId;
101
+ readonly name: string;
102
+ readonly description: string;
103
+ readonly status: TestStatus;
104
+ }
105
+ /** A recorded run of spec test cases plus their outcomes. */
106
+ export interface TestRun {
107
+ readonly id: string;
108
+ readonly timestamp: string;
109
+ readonly cases: readonly SpecTestCase[];
110
+ }
111
+ /**
112
+ * A byte range within a capture that is intentionally randomized by the
113
+ * protocol spec and so MUST be masked before golden comparison (Cat 14).
114
+ */
115
+ export interface RandomizedField {
116
+ readonly byteOffset: number;
117
+ readonly length: number;
118
+ readonly reason: "ephemeral_key" | "nonce" | "grease" | "random";
119
+ }
120
+ /** Metadata describing a stored golden capture (sidecar to its `.bin`). */
121
+ export interface CaptureMeta {
122
+ readonly source: "curl-impersonate" | "real-browser";
123
+ readonly profile: import("@browsercore/profiles").ProfileId;
124
+ readonly protocol: CaptureProtocol;
125
+ readonly record: "client_hello" | "settings" | "headers" | "server_hello";
126
+ readonly description: string;
127
+ readonly randomizedFields: readonly RandomizedField[];
128
+ readonly createdAt: string;
129
+ }
130
+ /**
131
+ * Result of a byte comparison that supports an ignore-list of randomized
132
+ * ranges. Extends {@link ComparisonResult} with the ranges that were masked.
133
+ */
134
+ export interface ComparisonResultWithIgnore {
135
+ readonly matches: boolean;
136
+ readonly divergenceByteIndex: number | undefined;
137
+ readonly message: string;
138
+ readonly maskedRanges: readonly RandomizedField[];
139
+ }
140
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Domain types for @browsercore/testing.
3
+ *
4
+ * Protocol verification: RFC compliance tests, browser golden packet captures,
5
+ * integration tests, and benchmarking. Compares generated packets against
6
+ * captures from real browsers.
7
+ */
8
+ /**
9
+ * The 17 test categories from docs/TEST-SUITE.md.
10
+ *
11
+ * Defined as a `const` object so the id set is the single source of truth and
12
+ * `TestCategoryId` is derived from it — never a bare string (see
13
+ * CODING_STANDARDS.md §17 "No magic strings").
14
+ */
15
+ export const TestCategory = {
16
+ TcpTransport: "tcp-transport",
17
+ TlsSerialization: "tls-serialization",
18
+ BrowserProfileComparison: "browser-profile-comparison",
19
+ TlsFingerprint: "tls-fingerprint",
20
+ Http1: "http1",
21
+ Http2: "http2",
22
+ HeaderProfiles: "header-profiles",
23
+ CookieBehavior: "cookie-behavior",
24
+ Compression: "compression",
25
+ RedirectHandling: "redirect-handling",
26
+ SessionResumption: "session-resumption",
27
+ ConnectionReuse: "connection-reuse",
28
+ ErrorHandling: "error-handling",
29
+ PacketCaptureComparison: "packet-capture-comparison",
30
+ RealWorldCompatibility: "real-world-compatibility",
31
+ Regression: "regression",
32
+ PerformanceBenchmarks: "performance-benchmarks",
33
+ };
34
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAiEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,YAAY,EAAE,eAAe;IAC7B,gBAAgB,EAAE,mBAAmB;IACrC,wBAAwB,EAAE,4BAA4B;IACtD,cAAc,EAAE,iBAAiB;IACjC,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,cAAc,EAAE,iBAAiB;IACjC,cAAc,EAAE,iBAAiB;IACjC,WAAW,EAAE,aAAa;IAC1B,gBAAgB,EAAE,mBAAmB;IACrC,iBAAiB,EAAE,oBAAoB;IACvC,eAAe,EAAE,kBAAkB;IACnC,aAAa,EAAE,gBAAgB;IAC/B,uBAAuB,EAAE,2BAA2B;IACpD,sBAAsB,EAAE,0BAA0B;IAClD,UAAU,EAAE,YAAY;IACxB,qBAAqB,EAAE,wBAAwB;CACzC,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Small shared helpers for @browsercore/testing.
3
+ */
4
+ import type { ComparisonResult, ComparisonResultWithIgnore, RandomizedField } from "./types.js";
5
+ /**
6
+ * Exhaustiveness check for `switch`/`if-else` over discriminated unions.
7
+ * Call in the `default` branch: `default: assertNever(x)`.
8
+ * Adding a new union member forces every handler to compile-error until handled.
9
+ */
10
+ export declare function assertNever(x: never): never;
11
+ /** Generate a unique TestCaseId (not cryptographically random). */
12
+ export declare function createId(prefix?: "tc"): string;
13
+ /** Format bytes as a lowercase hex string with no separators. */
14
+ export declare function bytesToHex(buf: Uint8Array): string;
15
+ /**
16
+ * Compare two byte arrays. Returns a {@link ComparisonResult} reporting whether
17
+ * they match, the first divergence index (if any), and a message.
18
+ */
19
+ export declare function compareBytes(a: Uint8Array, b: Uint8Array): ComparisonResult;
20
+ /**
21
+ * Compare two byte arrays while masking byte ranges that are intentionally
22
+ * randomized by the protocol spec (ephemeral keys, nonces, GREASE, random).
23
+ *
24
+ * Ranges in `ignore` are skipped when scanning for divergence. The returned
25
+ * {@link ComparisonResultWithIgnore.maskedReports} lists the ranges that were
26
+ * masked so callers can audit what was excluded.
27
+ */
28
+ export declare function compareBytesWithIgnore(a: Uint8Array, b: Uint8Array, ignore: readonly RandomizedField[]): ComparisonResultWithIgnore;
29
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACR,gBAAgB,EAChB,0BAA0B,EAC1B,eAAe,EAClB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AAEH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,GAAG,KAAK,CAE3C;AAED,mEAAmE;AACnE,wBAAgB,QAAQ,CAAC,MAAM,GAAE,IAAW,GAAG,MAAM,CAEpD;AAED,iEAAiE;AACjE,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAMlD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,UAAU,GAAG,gBAAgB,CAsB3E;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAClC,CAAC,EAAE,UAAU,EACb,CAAC,EAAE,UAAU,EACb,MAAM,EAAE,SAAS,eAAe,EAAE,GACnC,0BAA0B,CAwC5B"}