fast-replay 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 +126 -0
- package/dist/api.d.ts +65 -0
- package/dist/api.js +78 -0
- package/dist/api.js.map +1 -0
- package/dist/browser.d.ts +67 -0
- package/dist/browser.js +77 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli/format.d.ts +12 -0
- package/dist/cli/format.js +55 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +199 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/compiler/compile.d.ts +18 -0
- package/dist/compiler/compile.js +226 -0
- package/dist/compiler/compile.js.map +1 -0
- package/dist/compiler/normalize.d.ts +16 -0
- package/dist/compiler/normalize.js +75 -0
- package/dist/compiler/normalize.js.map +1 -0
- package/dist/compiler/waits.d.ts +31 -0
- package/dist/compiler/waits.js +69 -0
- package/dist/compiler/waits.js.map +1 -0
- package/dist/ir/io.d.ts +47 -0
- package/dist/ir/io.js +122 -0
- package/dist/ir/io.js.map +1 -0
- package/dist/ir/schema.d.ts +236 -0
- package/dist/ir/schema.js +153 -0
- package/dist/ir/schema.js.map +1 -0
- package/dist/mcp/index.d.ts +2 -0
- package/dist/mcp/index.js +22 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +268 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/noise.d.ts +20 -0
- package/dist/noise.js +70 -0
- package/dist/noise.js.map +1 -0
- package/dist/recorder/agent/capture.d.ts +15 -0
- package/dist/recorder/agent/capture.js +310 -0
- package/dist/recorder/agent/capture.js.map +1 -0
- package/dist/recorder/agent/config.d.ts +25 -0
- package/dist/recorder/agent/config.js +23 -0
- package/dist/recorder/agent/config.js.map +1 -0
- package/dist/recorder/agent/dom-reaction.d.ts +13 -0
- package/dist/recorder/agent/dom-reaction.js +166 -0
- package/dist/recorder/agent/dom-reaction.js.map +1 -0
- package/dist/recorder/agent/main.d.ts +1 -0
- package/dist/recorder/agent/main.js +14 -0
- package/dist/recorder/agent/main.js.map +1 -0
- package/dist/recorder/agent/page-agent.d.ts +12 -0
- package/dist/recorder/agent/page-agent.js +40 -0
- package/dist/recorder/agent/page-agent.js.map +1 -0
- package/dist/recorder/agent/reveal-tracker.d.ts +26 -0
- package/dist/recorder/agent/reveal-tracker.js +41 -0
- package/dist/recorder/agent/reveal-tracker.js.map +1 -0
- package/dist/recorder/agent/roles.d.ts +11 -0
- package/dist/recorder/agent/roles.js +148 -0
- package/dist/recorder/agent/roles.js.map +1 -0
- package/dist/recorder/agent/selectors.d.ts +26 -0
- package/dist/recorder/agent/selectors.js +256 -0
- package/dist/recorder/agent/selectors.js.map +1 -0
- package/dist/recorder/agent/text.d.ts +19 -0
- package/dist/recorder/agent/text.js +82 -0
- package/dist/recorder/agent/text.js.map +1 -0
- package/dist/recorder/agent/transport.d.ts +14 -0
- package/dist/recorder/agent/transport.js +43 -0
- package/dist/recorder/agent/transport.js.map +1 -0
- package/dist/recorder/agent/visibility.d.ts +8 -0
- package/dist/recorder/agent/visibility.js +20 -0
- package/dist/recorder/agent/visibility.js.map +1 -0
- package/dist/recorder/agent-bundle.generated.d.ts +2 -0
- package/dist/recorder/agent-bundle.generated.js +6 -0
- package/dist/recorder/agent-bundle.generated.js.map +1 -0
- package/dist/recorder/attach.d.ts +52 -0
- package/dist/recorder/attach.js +159 -0
- package/dist/recorder/attach.js.map +1 -0
- package/dist/recorder/instrument.d.ts +17 -0
- package/dist/recorder/instrument.js +24 -0
- package/dist/recorder/instrument.js.map +1 -0
- package/dist/recorder/launch.d.ts +38 -0
- package/dist/recorder/launch.js +72 -0
- package/dist/recorder/launch.js.map +1 -0
- package/dist/recorder/reaction.d.ts +13 -0
- package/dist/recorder/reaction.js +65 -0
- package/dist/recorder/reaction.js.map +1 -0
- package/dist/recorder/types.d.ts +62 -0
- package/dist/recorder/types.js +2 -0
- package/dist/recorder/types.js.map +1 -0
- package/dist/replayer/artifacts.d.ts +25 -0
- package/dist/replayer/artifacts.js +58 -0
- package/dist/replayer/artifacts.js.map +1 -0
- package/dist/replayer/invariants.d.ts +26 -0
- package/dist/replayer/invariants.js +109 -0
- package/dist/replayer/invariants.js.map +1 -0
- package/dist/replayer/resolve.d.ts +35 -0
- package/dist/replayer/resolve.js +50 -0
- package/dist/replayer/resolve.js.map +1 -0
- package/dist/replayer/run.d.ts +104 -0
- package/dist/replayer/run.js +467 -0
- package/dist/replayer/run.js.map +1 -0
- package/dist/replayer/values.d.ts +13 -0
- package/dist/replayer/values.js +76 -0
- package/dist/replayer/values.js.map +1 -0
- package/dist/replayer/waits.d.ts +25 -0
- package/dist/replayer/waits.js +90 -0
- package/dist/replayer/waits.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { matchesUrlPattern } from '../compiler/normalize.js';
|
|
2
|
+
const NETWORK_POLL_MS = 25;
|
|
3
|
+
/**
|
|
4
|
+
* Poll the network buffer instead of attaching a fresh `waitForResponse`.
|
|
5
|
+
*
|
|
6
|
+
* A request can settle before we get around to waiting on it — the classic
|
|
7
|
+
* lost-wakeup race. Reading a buffer that was already recording covers the
|
|
8
|
+
* window between the action firing and this wait starting.
|
|
9
|
+
*/
|
|
10
|
+
function waitForNetwork(ctx, method, urlPattern, timeoutMs) {
|
|
11
|
+
const deadline = Date.now() + timeoutMs;
|
|
12
|
+
const satisfied = () => ctx.network.some((n) => n.settledAt !== null &&
|
|
13
|
+
n.settledAt >= ctx.since &&
|
|
14
|
+
n.method.toUpperCase() === method.toUpperCase() &&
|
|
15
|
+
matchesUrlPattern(n.url, urlPattern, ctx.baseUrl));
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
if (satisfied())
|
|
18
|
+
return resolve();
|
|
19
|
+
const timer = setInterval(() => {
|
|
20
|
+
if (satisfied()) {
|
|
21
|
+
clearInterval(timer);
|
|
22
|
+
resolve();
|
|
23
|
+
}
|
|
24
|
+
else if (Date.now() > deadline) {
|
|
25
|
+
clearInterval(timer);
|
|
26
|
+
reject(new Error(`no settled ${method} matching ${urlPattern}`));
|
|
27
|
+
}
|
|
28
|
+
}, NETWORK_POLL_MS);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wait for every recorded signal at once, with the step's timeoutMs as a single
|
|
33
|
+
* ceiling over all of them. Never a fixed sleep — replay proceeds the moment
|
|
34
|
+
* the environment actually reacts, which is where the speed comes from.
|
|
35
|
+
*/
|
|
36
|
+
export async function waitForReaction(ctx, waitAfter) {
|
|
37
|
+
const started = Date.now();
|
|
38
|
+
const timeout = waitAfter.timeoutMs;
|
|
39
|
+
const jobs = [];
|
|
40
|
+
for (const n of waitAfter.network ?? []) {
|
|
41
|
+
jobs.push({
|
|
42
|
+
label: `network ${n.method} ${n.urlPattern}`,
|
|
43
|
+
run: waitForNetwork(ctx, n.method, n.urlPattern, timeout),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
for (const selector of waitAfter.domAppeared ?? []) {
|
|
47
|
+
jobs.push({
|
|
48
|
+
label: `appeared ${selector}`,
|
|
49
|
+
run: ctx.page
|
|
50
|
+
.locator(selector)
|
|
51
|
+
.first()
|
|
52
|
+
.waitFor({ state: 'visible', timeout })
|
|
53
|
+
.then(() => undefined),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
for (const selector of waitAfter.domGone ?? []) {
|
|
57
|
+
jobs.push({
|
|
58
|
+
label: `gone ${selector}`,
|
|
59
|
+
// `hidden` covers both ways an element goes away — unmounted, or still
|
|
60
|
+
// attached but no longer rendered. `detached` would hang forever on the
|
|
61
|
+
// second case. `first()` keeps this out of strict mode and resolves
|
|
62
|
+
// immediately when nothing matches, which is exactly "already gone".
|
|
63
|
+
run: ctx.page
|
|
64
|
+
.locator(selector)
|
|
65
|
+
.first()
|
|
66
|
+
.waitFor({ state: 'hidden', timeout })
|
|
67
|
+
.then(() => undefined),
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (waitAfter.networkIdle) {
|
|
71
|
+
jobs.push({
|
|
72
|
+
label: 'network idle',
|
|
73
|
+
run: ctx.page.waitForLoadState('networkidle', { timeout }).then(() => undefined),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (!jobs.length)
|
|
77
|
+
return { ok: true, unmet: [], met: [], durationMs: 0 };
|
|
78
|
+
const results = await Promise.allSettled(jobs.map((j) => j.run));
|
|
79
|
+
const unmet = [];
|
|
80
|
+
const met = [];
|
|
81
|
+
results.forEach((r, i) => {
|
|
82
|
+
const label = jobs[i]?.label ?? 'signal';
|
|
83
|
+
if (r.status === 'fulfilled')
|
|
84
|
+
met.push(label);
|
|
85
|
+
else
|
|
86
|
+
unmet.push(label);
|
|
87
|
+
});
|
|
88
|
+
return { ok: unmet.length === 0, unmet, met, durationMs: Date.now() - started };
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=waits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waits.js","sourceRoot":"","sources":["../../src/replayer/waits.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION: string;
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
/**
|
|
3
|
+
* Single source of truth for the version, read from the manifest.
|
|
4
|
+
*
|
|
5
|
+
* Two builds both reporting 0.1.0 made them indistinguishable in the field —
|
|
6
|
+
* the only way to tell them apart was grepping dist/ for a feature string.
|
|
7
|
+
*/
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
export const VERSION = require('../package.json').version;
|
|
10
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,CAAC,MAAM,OAAO,GAAY,OAAO,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fast-replay",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Record a browser bug once, verify the fix in seconds. A deterministic eye for coding agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"repro": "./dist/cli/index.js",
|
|
12
|
+
"repro-mcp": "./dist/mcp/index.js"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/api.js",
|
|
15
|
+
"types": "./dist/api.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/api.d.ts",
|
|
19
|
+
"import": "./dist/api.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.build.json",
|
|
29
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"stress": "tsx scripts/stress.ts",
|
|
34
|
+
"demo": "npm --prefix examples/demo-app run dev",
|
|
35
|
+
"build:agent": "tsx scripts/build-agent.ts",
|
|
36
|
+
"prebuild": "npm run build:agent",
|
|
37
|
+
"pretest": "npm run build:agent",
|
|
38
|
+
"prestress": "npm run build:agent",
|
|
39
|
+
"prepare": "npm run build"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
43
|
+
"commander": "^14.0.3",
|
|
44
|
+
"playwright": "^1.61.1",
|
|
45
|
+
"zod": "^4.4.3"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.10.0",
|
|
49
|
+
"esbuild": "^0.28.1",
|
|
50
|
+
"tsx": "^4.19.2",
|
|
51
|
+
"typescript": "^5.7.2",
|
|
52
|
+
"vitest": "^4.1.10"
|
|
53
|
+
},
|
|
54
|
+
"author": "Mukhriddin Shakhriyorov",
|
|
55
|
+
"keywords": [
|
|
56
|
+
"bug",
|
|
57
|
+
"repro",
|
|
58
|
+
"reproduction",
|
|
59
|
+
"playwright",
|
|
60
|
+
"mcp",
|
|
61
|
+
"testing",
|
|
62
|
+
"debugging",
|
|
63
|
+
"browser",
|
|
64
|
+
"automation",
|
|
65
|
+
"claude",
|
|
66
|
+
"agent",
|
|
67
|
+
"ai"
|
|
68
|
+
],
|
|
69
|
+
"repository": {
|
|
70
|
+
"type": "git",
|
|
71
|
+
"url": "git+https://github.com/mukhrr/replay.git"
|
|
72
|
+
},
|
|
73
|
+
"bugs": {
|
|
74
|
+
"url": "https://github.com/mukhrr/replay/issues"
|
|
75
|
+
},
|
|
76
|
+
"homepage": "https://github.com/mukhrr/replay#readme"
|
|
77
|
+
}
|