@farmslot/adapter-web 0.4.1 → 0.6.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/CHANGELOG.md +10 -0
- package/README.md +24 -1
- package/dist/launch-browser.d.cts +12 -0
- package/dist/web-dapp/cleanup.d.mts +15 -0
- package/dist/web-dapp/index.d.mts +123 -0
- package/dist/web-dapp/inject.d.mts +12 -0
- package/dist/web-dapp/launch.d.mts +56 -0
- package/dist/web-dapp/lib/blocked-hosts.d.mts +3 -0
- package/dist/web-dapp/lib/cdp-client.d.mts +8 -0
- package/dist/web-dapp/lib/display.d.mts +26 -0
- package/dist/web-dapp/lib/hud.d.mts +14 -0
- package/dist/web-dapp/lib/paths.d.mts +11 -0
- package/dist/web-dapp/lib/policy-fence.d.mts +18 -0
- package/dist/web-dapp/lib/policy.d.mts +4 -0
- package/dist/web-dapp/lib/processes.d.mts +57 -0
- package/dist/web-dapp/lib/readiness.d.mts +85 -0
- package/dist/web-dapp/lib/runtime.d.mts +29 -0
- package/dist/web-dapp/lib/signers.d.mts +13 -0
- package/dist/web-dapp/lib/viem.d.mts +1 -0
- package/dist/web-dapp/stop.d.mts +2 -0
- package/dist/web-dapp/verify.d.mts +2 -0
- package/dist/web-dapp/wallet-host.d.mts +2 -0
- package/package.json +17 -10
- package/src/launch-browser.cjs +20 -4
- package/src/web-dapp/cleanup.mjs +74 -0
- package/src/web-dapp/index.mjs +590 -0
- package/src/web-dapp/inject.mjs +100 -0
- package/src/web-dapp/launch.mjs +806 -0
- package/src/web-dapp/lib/blocked-hosts.mjs +24 -0
- package/src/web-dapp/lib/cdp-client.mjs +86 -0
- package/src/web-dapp/lib/display.mjs +169 -0
- package/src/web-dapp/lib/hud.mjs +156 -0
- package/src/web-dapp/lib/paths.mjs +55 -0
- package/src/web-dapp/lib/policy-fence.mjs +128 -0
- package/src/web-dapp/lib/policy.mjs +108 -0
- package/src/web-dapp/lib/processes.mjs +215 -0
- package/src/web-dapp/lib/readiness.mjs +438 -0
- package/src/web-dapp/lib/runtime.mjs +195 -0
- package/src/web-dapp/lib/signers.mjs +105 -0
- package/src/web-dapp/lib/viem.mjs +26 -0
- package/src/web-dapp/stop.mjs +44 -0
- package/src/web-dapp/verify.mjs +68 -0
- package/src/web-dapp/wallet-host.mjs +728 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farmslot/adapter-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Web platform adapter for Farmslot recipes: resolve and launch an isolated Chromium, own its CDP port, load unpacked extensions, and keep macOS focus.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"exports": {
|
|
@@ -64,30 +64,35 @@
|
|
|
64
64
|
"./validation-process-ownership": {
|
|
65
65
|
"types": "./dist/validation-process-ownership.d.cts",
|
|
66
66
|
"default": "./src/validation-process-ownership.cjs"
|
|
67
|
+
},
|
|
68
|
+
"./web-dapp": {
|
|
69
|
+
"types": "./dist/web-dapp/index.d.mts",
|
|
70
|
+
"default": "./src/web-dapp/index.mjs"
|
|
67
71
|
}
|
|
68
72
|
},
|
|
69
73
|
"scripts": {
|
|
70
|
-
"typecheck": "tsc --noEmit --project tsconfig.json && yarn build && tsc --project tsconfig.consumer.json",
|
|
71
|
-
"test": "node --test \"test/*.test.cjs\"",
|
|
74
|
+
"typecheck": "yarn workspace @farmslot/adapter-sdk build && tsc --noEmit --project tsconfig.json && yarn build && tsc --project tsconfig.consumer.json",
|
|
75
|
+
"test": "node --test \"test/*.test.cjs\" \"test/*.test.mjs\"",
|
|
72
76
|
"format": "prettier --write --ignore-path ../../.prettierignore .",
|
|
73
77
|
"format:check": "prettier --check --ignore-path ../../.prettierignore .",
|
|
74
|
-
"lint": "eslint \"src/**/*.cjs\" \"test/**/*.cjs\" \"test/**/*.ts\"",
|
|
75
|
-
"lint:fix": "eslint \"src/**/*.cjs\" \"test/**/*.cjs\" \"test/**/*.ts\" --fix",
|
|
76
|
-
"quality": "prettier --check --ignore-path ../../.prettierignore . && eslint \"src/**/*.cjs\" \"test/**/*.cjs\" \"test/**/*.ts\" && yarn typecheck && yarn test",
|
|
78
|
+
"lint": "eslint \"src/**/*.cjs\" \"src/**/*.mjs\" \"test/**/*.cjs\" \"test/**/*.mjs\" \"test/**/*.ts\"",
|
|
79
|
+
"lint:fix": "eslint \"src/**/*.cjs\" \"src/**/*.mjs\" \"test/**/*.cjs\" \"test/**/*.mjs\" \"test/**/*.ts\" --fix",
|
|
80
|
+
"quality": "prettier --check --ignore-path ../../.prettierignore . && eslint \"src/**/*.cjs\" \"src/**/*.mjs\" \"test/**/*.cjs\" \"test/**/*.mjs\" \"test/**/*.ts\" && yarn typecheck && yarn test",
|
|
77
81
|
"build": "yarn workspace @farmslot/adapter-sdk build && rm -rf dist && tsc --project tsconfig.build.json",
|
|
78
82
|
"prepack": "cd ../.. && node scripts/quality/check-farmslot-package-readiness.mjs --require-changelog --packages @farmslot/adapter-web && yarn workspace @farmslot/adapter-web build",
|
|
79
83
|
"prepublishOnly": "node ../../scripts/quality/check-farmslot-package-readiness.mjs --publish --packages @farmslot/adapter-web"
|
|
80
84
|
},
|
|
81
85
|
"dependencies": {
|
|
82
|
-
"@farmslot/adapter-sdk": "0.
|
|
83
|
-
"@farmslot/recipe-runner": "0.
|
|
86
|
+
"@farmslot/adapter-sdk": "0.7.1",
|
|
87
|
+
"@farmslot/recipe-runner": "0.27.0",
|
|
84
88
|
"ws": "^8.18.0"
|
|
85
89
|
},
|
|
86
90
|
"devDependencies": {
|
|
87
91
|
"@types/node": "^22.0.0",
|
|
88
92
|
"eslint": "^10.3.0",
|
|
89
93
|
"prettier": "^3.8.3",
|
|
90
|
-
"typescript": "^5.6.0"
|
|
94
|
+
"typescript": "^5.6.0",
|
|
95
|
+
"viem": "^2.54.3"
|
|
91
96
|
},
|
|
92
97
|
"repository": {
|
|
93
98
|
"type": "git",
|
|
@@ -115,7 +120,9 @@
|
|
|
115
120
|
"LICENSE",
|
|
116
121
|
"CHANGELOG.md",
|
|
117
122
|
"src/**/*.cjs",
|
|
118
|
-
"
|
|
123
|
+
"src/**/*.mjs",
|
|
124
|
+
"dist/**/*.d.cts",
|
|
125
|
+
"dist/**/*.d.mts"
|
|
119
126
|
],
|
|
120
127
|
"publishConfig": {
|
|
121
128
|
"access": "public"
|
package/src/launch-browser.cjs
CHANGED
|
@@ -78,6 +78,8 @@ const BROWSER_RESOLVER_CLI = require.resolve('./browser-resolver.cjs');
|
|
|
78
78
|
* @property {string} [rerunCommand] Command named in "Next:" hints.
|
|
79
79
|
* @property {number} [focusSettleMs] macOS: wait before the one focus check (default 1000).
|
|
80
80
|
* @property {(line: string) => void} [log] Progress lines (default stderr).
|
|
81
|
+
* @property {(progress: { message?: string, waitingFor?: string, current?: number, total?: number, unit?: string }) => void} [progress]
|
|
82
|
+
* What the launch is waiting for, for a caller's stage handle (`stage.progress`).
|
|
81
83
|
*/
|
|
82
84
|
|
|
83
85
|
/**
|
|
@@ -117,6 +119,7 @@ function normalizeOptions(options) {
|
|
|
117
119
|
if (!opts[key]) throw new Error(`Missing --${flag}`);
|
|
118
120
|
}
|
|
119
121
|
opts.log = opts.log ?? ((line) => process.stderr.write(`${line}\n`));
|
|
122
|
+
opts.progress = opts.progress ?? (() => {});
|
|
120
123
|
return opts;
|
|
121
124
|
}
|
|
122
125
|
|
|
@@ -254,12 +257,13 @@ function runLaunch(opts, acquireLock) {
|
|
|
254
257
|
try {
|
|
255
258
|
try {
|
|
256
259
|
beginDetachedLaunch(cdpPort, profile, activeValidationLease);
|
|
260
|
+
opts.progress({ message: 'starting the browser' });
|
|
257
261
|
if (application) {
|
|
258
262
|
execFileSync('open', macBackgroundOpenArgs(application, chromeArgs), {
|
|
259
263
|
env: sanitizedChildEnv(),
|
|
260
264
|
stdio: ['ignore', logFd, logFd],
|
|
261
265
|
});
|
|
262
|
-
browserPid = waitForOwnedCdpPid(cdpPort, ownedBrowser);
|
|
266
|
+
browserPid = waitForOwnedCdpPid(cdpPort, ownedBrowser, opts.progress);
|
|
263
267
|
if (browserPid === null) {
|
|
264
268
|
stopProfileProcessesSync(profile, { waitForAppearanceMs: 400 });
|
|
265
269
|
throw ownedListenerMissing();
|
|
@@ -271,7 +275,7 @@ function runLaunch(opts, acquireLock) {
|
|
|
271
275
|
stdio: ['ignore', logFd, logFd],
|
|
272
276
|
});
|
|
273
277
|
child.unref();
|
|
274
|
-
browserPid = waitForOwnedCdpPid(cdpPort, ownedBrowser);
|
|
278
|
+
browserPid = waitForOwnedCdpPid(cdpPort, ownedBrowser, opts.progress);
|
|
275
279
|
if (browserPid === null) {
|
|
276
280
|
stopProfileProcessesSync(profile, { extraPids: child.pid ? [child.pid] : [] });
|
|
277
281
|
throw ownedListenerMissing();
|
|
@@ -342,6 +346,7 @@ function runLaunch(opts, acquireLock) {
|
|
|
342
346
|
// Without it the slot has no window at all, so a failure fails the launch:
|
|
343
347
|
// the owned browser is stopped (its markers kept if it cannot be).
|
|
344
348
|
function openBackgroundWindow(opts, pid, url, clearLaunchMarkers) {
|
|
349
|
+
opts.progress({ message: 'opening the start window' });
|
|
345
350
|
const result = spawnSync(
|
|
346
351
|
process.execPath,
|
|
347
352
|
[BROWSER_RESOLVER_CLI, 'open-window', '--port', String(opts.cdpPort), '--url', url],
|
|
@@ -399,6 +404,7 @@ function writeBrowserResolution(runtime, resolution) {
|
|
|
399
404
|
}
|
|
400
405
|
|
|
401
406
|
function loadExtensionOverCdp(opts, pid, initialUrl, clearLaunchMarkers) {
|
|
407
|
+
opts.progress({ message: 'loading the extension over CDP' });
|
|
402
408
|
const result = spawnSync(
|
|
403
409
|
process.execPath,
|
|
404
410
|
[
|
|
@@ -495,10 +501,20 @@ function macApplicationForExecutable(executable) {
|
|
|
495
501
|
return null;
|
|
496
502
|
}
|
|
497
503
|
|
|
498
|
-
function waitForOwnedCdpPid(port, ownedBrowser) {
|
|
499
|
-
|
|
504
|
+
function waitForOwnedCdpPid(port, ownedBrowser, progress) {
|
|
505
|
+
const attempts = 300;
|
|
506
|
+
for (let i = 0; i < attempts; i += 1) {
|
|
500
507
|
const pid = cdpListenerPids(port).find(ownedBrowser);
|
|
501
508
|
if (pid !== undefined) return pid;
|
|
509
|
+
// Every 50 attempts (about 5 s): enough to show the wait is moving.
|
|
510
|
+
if (i > 0 && i % 50 === 0) {
|
|
511
|
+
progress({
|
|
512
|
+
waitingFor: `an owned CDP listener on 127.0.0.1:${port}`,
|
|
513
|
+
current: i,
|
|
514
|
+
total: attempts,
|
|
515
|
+
unit: 'attempts',
|
|
516
|
+
});
|
|
517
|
+
}
|
|
502
518
|
spawnSync('sleep', ['0.1']);
|
|
503
519
|
}
|
|
504
520
|
return null;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// cleanup.mjs — `mm-harness cleanup --adapter <web-dapp adapter>`: stop the slot
|
|
3
|
+
// browser and wallet host, then remove the slot's runtime state and the installed
|
|
4
|
+
// wallet fixture. Browser profiles are kept unless --reset-profile.
|
|
5
|
+
//
|
|
6
|
+
// Inputs: --target <checkout> [--cdp-port <port>] [--reset-profile]
|
|
7
|
+
// Outputs: JSON summary on stdout. Exit 0 cleaned; 1 the CDP port stayed busy.
|
|
8
|
+
// Never touches: product source files, the Next.js dev server.
|
|
9
|
+
|
|
10
|
+
import { existsSync, readdirSync, rmSync } from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
import { recipeHarnessPath, walletFixturePath } from './lib/paths.mjs';
|
|
15
|
+
import { stopConsoleCollector, stopWebDappBrowser } from './lib/processes.mjs';
|
|
16
|
+
import { webDappPolicy, webDappRuntimeDir } from './lib/runtime.mjs';
|
|
17
|
+
|
|
18
|
+
const usage = 'Usage: cleanup.mjs --target <checkout> [--cdp-port <port>] [--reset-profile]';
|
|
19
|
+
|
|
20
|
+
export async function cleanupWebDappRuntime({ target, cdpPort, resetProfile = false }) {
|
|
21
|
+
const root = path.resolve(target);
|
|
22
|
+
const stop = await stopWebDappBrowser(root, { cdpPort });
|
|
23
|
+
await stopConsoleCollector(root);
|
|
24
|
+
const runtime = webDappRuntimeDir(root);
|
|
25
|
+
const removed = [];
|
|
26
|
+
if (existsSync(runtime)) {
|
|
27
|
+
for (const entry of readdirSync(runtime)) {
|
|
28
|
+
if (entry.startsWith('profile-') && !resetProfile) continue;
|
|
29
|
+
rmSync(path.join(runtime, entry), { recursive: true, force: true });
|
|
30
|
+
removed.push(entry);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const overlay = recipeHarnessPath(root, webDappPolicy().adapterId);
|
|
34
|
+
if (existsSync(overlay)) {
|
|
35
|
+
rmSync(overlay, { recursive: true, force: true });
|
|
36
|
+
removed.push(path.relative(root, overlay));
|
|
37
|
+
}
|
|
38
|
+
const fixture = walletFixturePath(root);
|
|
39
|
+
if (existsSync(fixture)) {
|
|
40
|
+
rmSync(fixture, { force: true });
|
|
41
|
+
removed.push(path.relative(root, fixture));
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
status: 'pass',
|
|
45
|
+
target: root,
|
|
46
|
+
stopped: stop.stopped,
|
|
47
|
+
cdpPortFree: stop.cdpPortFree,
|
|
48
|
+
removed,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
53
|
+
const argv = process.argv.slice(2);
|
|
54
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
55
|
+
process.stdout.write(`${usage}\n`);
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
const value = (flag) => {
|
|
59
|
+
const index = argv.indexOf(flag);
|
|
60
|
+
return index >= 0 ? argv[index + 1] : undefined;
|
|
61
|
+
};
|
|
62
|
+
const rawPort = value('--cdp-port') ?? process.env.RECIPE_CDP_PORT ?? process.env.CDP_PORT;
|
|
63
|
+
try {
|
|
64
|
+
const result = await cleanupWebDappRuntime({
|
|
65
|
+
target: value('--target') ?? process.cwd(),
|
|
66
|
+
cdpPort: rawPort ? Number(rawPort) : undefined,
|
|
67
|
+
resetProfile: argv.includes('--reset-profile'),
|
|
68
|
+
});
|
|
69
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
process.stdout.write(`${JSON.stringify({ status: 'fail', error: error.message })}\n`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
}
|