@webhands/core 0.5.0 → 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.
Files changed (53) hide show
  1. package/README.md +20 -4
  2. package/dist/errors.d.ts +92 -1
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +100 -0
  5. package/dist/errors.js.map +1 -1
  6. package/dist/hand-host.d.ts +198 -5
  7. package/dist/hand-host.d.ts.map +1 -1
  8. package/dist/hand-host.js +664 -21
  9. package/dist/hand-host.js.map +1 -1
  10. package/dist/index.d.ts +4 -4
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -3
  13. package/dist/index.js.map +1 -1
  14. package/dist/playwright-attach-transport.d.ts +8 -1
  15. package/dist/playwright-attach-transport.d.ts.map +1 -1
  16. package/dist/playwright-attach-transport.js +19 -4
  17. package/dist/playwright-attach-transport.js.map +1 -1
  18. package/dist/playwright-launch-transport.d.ts.map +1 -1
  19. package/dist/playwright-launch-transport.js +13 -4
  20. package/dist/playwright-launch-transport.js.map +1 -1
  21. package/dist/profile-location.d.ts +19 -0
  22. package/dist/profile-location.d.ts.map +1 -1
  23. package/dist/profile-location.js +21 -0
  24. package/dist/profile-location.js.map +1 -1
  25. package/dist/seam.d.ts +501 -7
  26. package/dist/seam.d.ts.map +1 -1
  27. package/dist/seam.js +31 -0
  28. package/dist/seam.js.map +1 -1
  29. package/dist/session-rpc.d.ts +63 -1
  30. package/dist/session-rpc.d.ts.map +1 -1
  31. package/dist/session-rpc.js +174 -11
  32. package/dist/session-rpc.js.map +1 -1
  33. package/dist/stub-transport.d.ts.map +1 -1
  34. package/dist/stub-transport.js +74 -6
  35. package/dist/stub-transport.js.map +1 -1
  36. package/dist/test-fixtures/fixture-pages.d.ts.map +1 -1
  37. package/dist/test-fixtures/fixture-pages.js +994 -0
  38. package/dist/test-fixtures/fixture-pages.js.map +1 -1
  39. package/dist/test-fixtures/fixture-server.d.ts.map +1 -1
  40. package/dist/test-fixtures/fixture-server.js +33 -3
  41. package/dist/test-fixtures/fixture-server.js.map +1 -1
  42. package/package.json +1 -1
  43. package/src/errors.ts +134 -1
  44. package/src/hand-host.ts +797 -21
  45. package/src/index.ts +20 -1
  46. package/src/playwright-attach-transport.ts +25 -3
  47. package/src/playwright-launch-transport.ts +13 -2
  48. package/src/profile-location.ts +25 -0
  49. package/src/seam.ts +535 -7
  50. package/src/session-rpc.ts +276 -14
  51. package/src/stub-transport.ts +83 -6
  52. package/src/test-fixtures/fixture-pages.ts +1010 -0
  53. package/src/test-fixtures/fixture-server.ts +32 -3
package/src/index.ts CHANGED
@@ -1,8 +1,22 @@
1
1
  export type {
2
+ ActionOptions,
3
+ BoundingBox,
2
4
  Cookie,
3
5
  Driver,
6
+ EvalOptions,
4
7
  LocatorString,
8
+ MouseAction,
9
+ MouseButton,
10
+ MouseInput,
5
11
  OpenTarget,
12
+ PwExtra,
13
+ QueryOptions,
14
+ QueryRow,
15
+ Screenshot,
16
+ ScreenshotOptions,
17
+ ScreenshotScope,
18
+ ScrollTarget,
19
+ SelectChoice,
6
20
  WebHandsPage,
7
21
  Session,
8
22
  Snapshot,
@@ -11,7 +25,7 @@ export type {
11
25
  Transport,
12
26
  WaitCondition,
13
27
  } from './seam.js';
14
- export {locator} from './seam.js';
28
+ export {locator, validateSnapshotOptions} from './seam.js';
15
29
 
16
30
  export {
17
31
  serializeCookies,
@@ -68,6 +82,9 @@ export {
68
82
  AttachNoContextError,
69
83
  NoLiveServerError,
70
84
  SessionAlreadyActiveError,
85
+ CrossOriginFrameError,
86
+ ScreenshotPathError,
87
+ StaleRefError,
71
88
  isControllerError,
72
89
  type ControllerErrorCode,
73
90
  } from './errors.js';
@@ -104,9 +121,11 @@ export {
104
121
  export {
105
122
  resolveHomeRoot,
106
123
  resolveProfileLocation,
124
+ resolveScreenshotsDir,
107
125
  CONTROLLER_HOME_ENV,
108
126
  DEFAULT_HOME_DIRNAME,
109
127
  PROFILES_DIRNAME,
128
+ SCREENSHOTS_DIRNAME,
110
129
  type ProfileLocation,
111
130
  type ProfileLocationOptions,
112
131
  } from './profile-location.js';
@@ -6,6 +6,10 @@ import {
6
6
  } from 'playwright';
7
7
  import {AttachNoContextError, AttachNotChromiumError} from './errors.js';
8
8
  import {composeWithHands, type Hand, type HandContext} from './hand-host.js';
9
+ import {
10
+ resolveScreenshotsDir,
11
+ type ProfileLocationOptions,
12
+ } from './profile-location.js';
9
13
  import type {OpenTarget, Session, Transport} from './seam.js';
10
14
 
11
15
  /**
@@ -33,15 +37,26 @@ import type {OpenTarget, Session, Transport} from './seam.js';
33
37
  */
34
38
  export class PlaywrightAttachTransport implements Transport {
35
39
  readonly #hands: readonly Hand[];
40
+ readonly #location: ProfileLocationOptions;
36
41
 
37
42
  /**
38
43
  * @param hands explicitly-loaded third-party hands to compose alongside the
39
44
  * built-ins (Phase 2, ADR-0007). These come from {@link loadHands} against
40
45
  * the operator's explicit config; the transport does NOT discover them. Omit
41
46
  * for the built-ins-only surface.
47
+ * @param location overrides for the controller home root, used ONLY to resolve
48
+ * the managed SCREENSHOTS dir (`<homeRoot>/screenshots`) the Tier-4
49
+ * `screenshot` verb mints under — attach reuses the user's own browser, so it
50
+ * owns no profile dir, but the screenshot output location still honours the
51
+ * same `root`/`WEBHANDS_HOME` override so a test can isolate it. Omit in
52
+ * production to use `~/.webhands/screenshots`.
42
53
  */
43
- constructor(hands: readonly Hand[] = []) {
54
+ constructor(
55
+ hands: readonly Hand[] = [],
56
+ location: ProfileLocationOptions = {},
57
+ ) {
44
58
  this.#hands = hands;
59
+ this.#location = location;
45
60
  }
46
61
 
47
62
  async open(target: OpenTarget): Promise<Session> {
@@ -77,7 +92,8 @@ export class PlaywrightAttachTransport implements Transport {
77
92
  // browser exposes a context with no page yet (single active session in
78
93
  // v1, PRD Out of Scope).
79
94
  const pwPage = context.pages()[0] ?? (await context.newPage());
80
- return makeAttachedSession(browser, pwPage, this.#hands);
95
+ const screenshotsDir = resolveScreenshotsDir(this.#location);
96
+ return makeAttachedSession(browser, pwPage, this.#hands, screenshotsDir);
81
97
  } catch (cause) {
82
98
  // On any open-time refusal, disconnect from the user's browser without
83
99
  // closing it (a CDP connection close detaches; it does not kill the
@@ -107,6 +123,7 @@ function makeAttachedSession(
107
123
  browser: Browser,
108
124
  pwPage: Page,
109
125
  extraHands: readonly Hand[],
126
+ screenshotsDir: string,
110
127
  ): Session {
111
128
  const context: BrowserContext = pwPage.context();
112
129
  let closed = false;
@@ -134,7 +151,12 @@ function makeAttachedSession(
134
151
  // the same shared host the launch transport uses, so the verbs behave
135
152
  // identically across both transports. The live `pwPage`/`context` stay
136
153
  // in-process and never cross the seam (ADR-0003).
137
- const handContext: HandContext = {pwPage, context, ensureOpen};
154
+ const handContext: HandContext = {
155
+ pwPage,
156
+ context,
157
+ ensureOpen,
158
+ screenshotsDir,
159
+ };
138
160
  const {page, dispose: disposeHands} = composeWithHands(
139
161
  handContext,
140
162
  extraHands,
@@ -8,6 +8,7 @@ import {
8
8
  import {composeWithHands, type Hand, type HandContext} from './hand-host.js';
9
9
  import {
10
10
  resolveProfileLocation,
11
+ resolveScreenshotsDir,
11
12
  type ProfileLocationOptions,
12
13
  } from './profile-location.js';
13
14
  import {hostResolverRulesArg, parseSocksProxy} from './socks-proxy.js';
@@ -346,7 +347,11 @@ export class PlaywrightLaunchTransport implements Transport {
346
347
  // the single active page (PRD: single active session in v1). Create one if
347
348
  // the build ever changes that invariant.
348
349
  const pwPage = context.pages()[0] ?? (await context.newPage());
349
- return makeSession(context, pwPage, this.#hands);
350
+ // The managed screenshots dir resolves from the SAME location override as
351
+ // profiles (`<homeRoot>/screenshots`), so a test's temp `root` isolates
352
+ // screenshots too and the real `~/.webhands/screenshots` stays untouched.
353
+ const screenshotsDir = resolveScreenshotsDir(this.#location);
354
+ return makeSession(context, pwPage, this.#hands, screenshotsDir);
350
355
  }
351
356
 
352
357
  /**
@@ -427,6 +432,7 @@ function makeSession(
427
432
  context: BrowserContext,
428
433
  pwPage: Page,
429
434
  extraHands: readonly Hand[],
435
+ screenshotsDir: string,
430
436
  ): Session {
431
437
  let closed = false;
432
438
  const ensureOpen = () => {
@@ -453,7 +459,12 @@ function makeSession(
453
459
  // Build the verb surface from the built-in hands over a live hand-context.
454
460
  // The host keeps the live `pwPage`/`context` in-process (they never cross the
455
461
  // seam, ADR-0003); the hand-context carries live page access only.
456
- const handContext: HandContext = {pwPage, context, ensureOpen};
462
+ const handContext: HandContext = {
463
+ pwPage,
464
+ context,
465
+ ensureOpen,
466
+ screenshotsDir,
467
+ };
457
468
  const {page, dispose: disposeHands} = composeWithHands(
458
469
  handContext,
459
470
  extraHands,
@@ -30,6 +30,16 @@ export const CONTROLLER_HOME_ENV = 'WEBHANDS_HOME';
30
30
  /** The subdirectory under the home root that holds dedicated profiles. */
31
31
  export const PROFILES_DIRNAME = 'profiles';
32
32
 
33
+ /**
34
+ * The subdirectory under the home root where the `screenshot` verb MINTS its
35
+ * PNG files (the Tier-4 managed screenshots dir, prd
36
+ * `broaden-agent-verb-surface`, R3). It lives BESIDE `profiles/` under the SAME
37
+ * overridable home root, so the same `root`/`WEBHANDS_HOME` override that
38
+ * isolates profiles in a test also isolates screenshots — nothing writes to the
39
+ * real `~/.webhands/screenshots` unless the home root points there.
40
+ */
41
+ export const SCREENSHOTS_DIRNAME = 'screenshots';
42
+
33
43
  /** Inputs that influence where a profile resolves (all optional, for tests). */
34
44
  export interface ProfileLocationOptions {
35
45
  /**
@@ -90,3 +100,18 @@ export function resolveProfileLocation(
90
100
  profile,
91
101
  };
92
102
  }
103
+
104
+ /**
105
+ * Resolve the managed SCREENSHOTS directory (`<homeRoot>/screenshots`) the
106
+ * `screenshot` verb mints PNGs under (prd `broaden-agent-verb-surface`, R3).
107
+ * Like {@link resolveProfileLocation} it is PURE (creates no directory) and
108
+ * honours the same `root`/`WEBHANDS_HOME` precedence, so a test that points the
109
+ * home root at a temp dir isolates screenshots there and the real
110
+ * `~/.webhands/screenshots` stays untouched. The verb (in the transport) is
111
+ * responsible for creating the dir lazily on first write.
112
+ */
113
+ export function resolveScreenshotsDir(
114
+ options: ProfileLocationOptions = {},
115
+ ): string {
116
+ return join(resolveHomeRoot(options), SCREENSHOTS_DIRNAME);
117
+ }