waygraph 0.15.51 → 0.15.52
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/dist/analysis/graph.d.ts +1 -1
- package/dist/analysis/graph.js +10 -15
- package/dist/analysis/practices-check.d.ts +0 -1
- package/dist/analysis/practices-check.js +0 -13
- package/dist/auto-session/session.d.ts +1 -1
- package/dist/auto-session/session.js +13 -8
- package/dist/blocks-select.d.ts +7 -4
- package/dist/blocks-select.js +0 -14
- package/dist/browser.d.ts +27 -0
- package/dist/browser.js +62 -0
- package/dist/cli/run-chain.js +4 -0
- package/dist/cli/usage.js +14 -1
- package/dist/cli.js +10 -1
- package/dist/commands/agent-dive-scaffold.d.ts +5 -0
- package/dist/commands/agent-dive-scaffold.js +7 -0
- package/dist/commands/extensions.d.ts +1 -0
- package/dist/commands/extensions.js +22 -0
- package/dist/commands/init.js +42 -4
- package/dist/commands/test.js +16 -2
- package/dist/commands/try.js +2 -1
- package/dist/commands/upgrade.d.ts +6 -0
- package/dist/commands/upgrade.js +107 -0
- package/dist/config.d.ts +47 -3
- package/dist/config.js +45 -12
- package/dist/core/block.d.ts +12 -0
- package/dist/core/block.js +36 -0
- package/dist/core/blocks/nav.d.ts +11 -0
- package/dist/core/blocks/nav.js +8 -0
- package/dist/core/config.d.ts +5 -0
- package/dist/core/engine.d.ts +17 -16
- package/dist/core/engine.js +3 -3
- package/dist/core/flow.d.ts +17 -0
- package/dist/core/flow.js +44 -21
- package/dist/core/map-builder.d.ts +3 -3
- package/dist/core/map-builder.js +3 -3
- package/dist/env-file.d.ts +7 -7
- package/dist/env-file.js +29 -16
- package/dist/extensions.d.ts +21 -0
- package/dist/extensions.js +178 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +3 -2
- package/dist/pilot-overlay/overlay-fixtures.d.ts +0 -7
- package/dist/pilot-overlay/overlay-fixtures.js +0 -45
- package/dist/playwright-config.d.ts +4 -0
- package/dist/playwright-config.js +15 -0
- package/dist/playwright.config.d.ts +2 -0
- package/dist/playwright.config.js +6 -0
- package/dist/runner/demo-log.js +1 -7
- package/dist/runner/inpage/core.d.ts +1 -1
- package/dist/runner/inpage/core.js +8 -7
- package/dist/runner/main.js +18 -10
- package/dist/runner/rings.js +0 -49
- package/dist/session/auto-explore-run.js +9 -5
- package/dist/session/auto-explore.js +6 -4
- package/dist/session/step-overlay.js +3 -1
- package/dist/traverse/run.js +3 -1
- package/dist/ui/components/ring.js +1 -0
- package/dist/ui/overlay.css +1 -0
- package/dist/ui/shadow.d.ts +1 -3
- package/dist/ui/shadow.js +0 -4
- package/dist/ui/tokens.d.ts +0 -11
- package/dist/ui/tokens.js +0 -5
- package/dist/video-dir.d.ts +8 -0
- package/dist/video-dir.js +60 -0
- package/dist/webserver.js +5 -1
- package/package.json +1 -1
- package/templates/scaffold/README.md +4 -2
- package/templates/scaffold/STRUCTURE.md +5 -2
- package/templates/scaffold/config/waygraph.config.ts +26 -0
- package/templates/scaffold/package.json +1 -1
- package/templates/scaffold/src/flows/README.md +5 -5
- package/templates/scaffold/src/flows/example.flow.ts +31 -17
- package/templates/scaffold/src/flows/hoppscotch-auth.flow.ts +35 -0
- package/templates/scaffold/src/flows/hoppscotch-get.flow.ts +23 -0
- package/templates/scaffold/src/flows/hoppscotch-post.flow.ts +33 -0
- package/templates/scaffold/src/flows/mail-verify.flow.ts +36 -75
- package/templates/scaffold/src/flows/shop.flow.ts +41 -65
- package/templates/scaffold/src/map/(app_base)/docs/_nav.block.ts +0 -9
- package/templates/scaffold/src/map/(app_base)/docs/_page.block.ts +0 -5
- package/templates/scaffold/src/map/(app_base)/docs/_sel.ts +1 -1
- package/templates/scaffold/src/map/(app_base)/home/_methods/add-item.effect.block.ts +3 -19
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +2 -25
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts +2 -19
- package/templates/scaffold/src/map/(app_base)/home/_methods/clear-cart.method.block.ts +1 -17
- package/templates/scaffold/src/map/(app_base)/home/_methods/remove-item.effect.block.ts +1 -10
- package/templates/scaffold/src/map/(app_base)/home/_nav.block.ts +1 -12
- package/templates/scaffold/src/map/(app_base)/home/_page.block.ts +0 -9
- package/templates/scaffold/src/map/(app_base)/home/_sel.ts +3 -1
- package/templates/scaffold/src/map/(app_base)/home/nav-verification-link.block.ts +1 -23
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-response-body.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-response-header.method.block.ts +19 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/assert-status.method.block.ts +20 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/pick-extension-interceptor.method.block.ts +18 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/read-response-body.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/read-response-headers.method.block.ts +21 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/send-request.method.block.ts +15 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-bearer-token.method.block.ts +20 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-headers.method.block.ts +23 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-json-body.method.block.ts +36 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-method.method.block.ts +17 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-query-params.method.block.ts +22 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_methods/set-url.method.block.ts +18 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_nav.block.ts +11 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_page.block.ts +36 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/_sel.ts +27 -0
- package/templates/scaffold/src/map/(external)/hoppscotch/settings/_nav.block.ts +10 -0
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-content.method.block.ts +2 -27
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-received.method.block.ts +2 -21
- package/templates/scaffold/src/map/(external)/mailpit/_methods/extract-email-link.method.block.ts +1 -19
- package/templates/scaffold/src/map/(external)/mailpit/_methods/open-message.method.block.ts +1 -11
- package/templates/scaffold/src/map/(external)/mailpit/_nav.block.ts +1 -9
- package/templates/scaffold/src/map/(external)/mailpit/_page.block.ts +0 -11
- package/templates/scaffold/src/states/demo.mem-keys.ts +20 -19
- package/templates/scaffold/src/states/demo.mem-stubs.ts +4 -0
- package/templates/scaffold/src/states/demo.states.ts +5 -1
- package/templates/scaffold/tests/example.spec.ts +2 -3
- package/templates/scaffold/tests/hoppscotch.spec.ts +97 -0
- package/templates/scaffold/tests/mail-verify.spec.ts +2 -11
- package/templates/scaffold/tsconfig.json +1 -1
- package/templates/skills/waygraph-convention.skill.md +32 -3
- package/templates/skills/waygraph-pilot.skill.md +4 -5
- package/dist/browser-path.d.ts +0 -5
- package/dist/browser-path.js +0 -18
- package/dist/runner/overlay-present.d.ts +0 -8
- package/dist/runner/overlay-present.js +0 -57
- package/dist/ui/components/banner.d.ts +0 -2
- package/dist/ui/components/banner.js +0 -10
- package/dist/ui/components/cursor.d.ts +0 -2
- package/dist/ui/components/cursor.js +0 -19
- package/templates/scaffold/playwright.config.ts +0 -12
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import { createServer, type Server } from "node:http";
|
|
3
|
+
import { MemPage, checkpoint } from "waygraph";
|
|
4
|
+
import config from "../config/waygraph.config.js";
|
|
5
|
+
import { hoppscotchGetFlow } from "../src/flows/hoppscotch-get.flow.js";
|
|
6
|
+
import { hoppscotchAuthFlow } from "../src/flows/hoppscotch-auth.flow.js";
|
|
7
|
+
import { hoppscotchPostFlow } from "../src/flows/hoppscotch-post.flow.js";
|
|
8
|
+
import {
|
|
9
|
+
ExpectedResponseHeader,
|
|
10
|
+
ExpectedResponseText,
|
|
11
|
+
ExpectedStatus,
|
|
12
|
+
RequestBearerToken,
|
|
13
|
+
RequestBody,
|
|
14
|
+
RequestHeaders,
|
|
15
|
+
RequestMethod,
|
|
16
|
+
RequestParams,
|
|
17
|
+
RequestUrl,
|
|
18
|
+
} from "../src/states/demo.mem-keys.js";
|
|
19
|
+
|
|
20
|
+
// The API under test: no CORS headers, so only the extension can reach it from hoppscotch.io.
|
|
21
|
+
let server: Server;
|
|
22
|
+
let apiUrl: string;
|
|
23
|
+
|
|
24
|
+
test.beforeAll(async () => {
|
|
25
|
+
server = createServer((req, res) => {
|
|
26
|
+
let body = "";
|
|
27
|
+
req.on("data", (chunk) => (body += chunk));
|
|
28
|
+
req.on("end", () => {
|
|
29
|
+
res.statusCode = req.method === "POST" ? 201 : 200;
|
|
30
|
+
res.setHeader("content-type", "application/json");
|
|
31
|
+
res.setHeader("x-request-id", "req-42");
|
|
32
|
+
res.end(JSON.stringify({ method: req.method, url: req.url, token: req.headers["x-token"], auth: req.headers.authorization, body }));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
36
|
+
apiUrl = `http://127.0.0.1:${(server.address() as { port: number }).port}/items`;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test.afterAll(() => server.close());
|
|
40
|
+
|
|
41
|
+
test.skip(!config.extensions?.includes("hoppscotch"), 'add extensions: ["hoppscotch"] to config/waygraph.config.ts to run these');
|
|
42
|
+
test.setTimeout(120_000);
|
|
43
|
+
|
|
44
|
+
test("GET returns 200", async () => {
|
|
45
|
+
const mem = new MemPage();
|
|
46
|
+
mem.set(RequestUrl, apiUrl);
|
|
47
|
+
mem.set(ExpectedStatus, 200);
|
|
48
|
+
expect(await hoppscotchGetFlow.run(mem, {})).toEqual(checkpoint("HoppscotchRest"));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("POST sends the header and JSON body, returns 201", async () => {
|
|
52
|
+
const mem = new MemPage();
|
|
53
|
+
mem.set(RequestMethod, "POST");
|
|
54
|
+
mem.set(RequestUrl, apiUrl);
|
|
55
|
+
mem.set(RequestHeaders, { "x-token": "abc123" });
|
|
56
|
+
mem.set(RequestBody, '{"name":"waygraph"}');
|
|
57
|
+
mem.set(ExpectedStatus, 201);
|
|
58
|
+
mem.set(ExpectedResponseText, '"token":"abc123"');
|
|
59
|
+
expect(await hoppscotchPostFlow.run(mem, {})).toEqual(checkpoint("HoppscotchRest"));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("a wrong expected status fails the flow, naming assert-status", async () => {
|
|
63
|
+
const mem = new MemPage();
|
|
64
|
+
mem.set(RequestUrl, apiUrl);
|
|
65
|
+
mem.set(ExpectedStatus, 404);
|
|
66
|
+
await expect(hoppscotchGetFlow.run(mem, {})).rejects.toThrow(/assert-status/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("a response body without the expected text fails the flow, naming assert-response-body", async () => {
|
|
70
|
+
const mem = new MemPage();
|
|
71
|
+
mem.set(RequestMethod, "POST");
|
|
72
|
+
mem.set(RequestUrl, apiUrl);
|
|
73
|
+
mem.set(RequestHeaders, { "x-token": "abc123" });
|
|
74
|
+
mem.set(RequestBody, '{"name":"waygraph"}');
|
|
75
|
+
mem.set(ExpectedStatus, 201);
|
|
76
|
+
mem.set(ExpectedResponseText, '"token":"nope"');
|
|
77
|
+
await expect(hoppscotchPostFlow.run(mem, {})).rejects.toThrow(/assert-response-body/);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const authMem = (headerValue: string): MemPage => {
|
|
81
|
+
const mem = new MemPage();
|
|
82
|
+
mem.set(RequestUrl, apiUrl);
|
|
83
|
+
mem.set(RequestParams, { q: "waygraph" });
|
|
84
|
+
mem.set(RequestBearerToken, "tok-9");
|
|
85
|
+
mem.set(ExpectedStatus, 200);
|
|
86
|
+
mem.set(ExpectedResponseText, '"url":"/items?q=waygraph","auth":"Bearer tok-9"');
|
|
87
|
+
mem.set(ExpectedResponseHeader({ name: "X-Request-Id", value: headerValue }));
|
|
88
|
+
return mem;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
test("query params and a bearer token reach the API; a response header is checked", async () => {
|
|
92
|
+
expect(await hoppscotchAuthFlow.run(authMem("req-42"), {})).toEqual(checkpoint("HoppscotchRest"));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("a wrong response header value fails the flow, naming assert-response-header", async () => {
|
|
96
|
+
await expect(hoppscotchAuthFlow.run(authMem("other"), {})).rejects.toThrow(/assert-response-header/);
|
|
97
|
+
});
|
|
@@ -6,17 +6,8 @@ import { mailVerifyFlow } from "../src/flows/mail-verify.flow.js";
|
|
|
6
6
|
import { ExpectedRecipient, ExpectedLinkPattern, ExpectedEmailContent } from "../src/states/demo.mem-keys.js";
|
|
7
7
|
import { HOME_URL } from "../src/map/(app_base)/home/_sel.js";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* app sent a verification email" themselves: spin up a real, throwaway
|
|
12
|
-
* Mailpit container, send a real SMTP message, then run mailVerifyFlow for
|
|
13
|
-
* real - a real cross-origin browser hop into Mailpit's own web UI and
|
|
14
|
-
* back, no mail-catcher REST API anywhere. Covers the happy path, both
|
|
15
|
-
* failure paths its QA-facing asserts exist for (no email arrived, wrong
|
|
16
|
-
* content), and - the actual point of making the mail-reading Blocks
|
|
17
|
-
* mem-driven - a second, entirely different email scenario running through
|
|
18
|
-
* the exact same four Blocks with zero new code.
|
|
19
|
-
*/
|
|
9
|
+
// Plays "the app sent a verification email": starts a throwaway Mailpit container, sends a real SMTP message,
|
|
10
|
+
// then runs mailVerifyFlow against it, including the no-email and wrong-content failure paths.
|
|
20
11
|
|
|
21
12
|
function runMailpit(): { containerId: string; smtpPort: number; httpPort: number } {
|
|
22
13
|
const run = spawnSync("docker", ["run", "--rm", "-d", "-p", "0:1025", "-p", "0:8025", "axllent/mailpit"], {
|
|
@@ -17,6 +17,9 @@ against a live site, use `--skill-pilot-blind`.
|
|
|
17
17
|
2. **Navigation only inside Nav.** `page.goto` / NavBlock `click` live in
|
|
18
18
|
`defineNavBlock` / `defineNavClickBlock` only. Method/Effect `act` uses ActionPage
|
|
19
19
|
(no goto) even when the app changes URL as a side effect of a button click.
|
|
20
|
+
A Nav for a page that only exists after something else (every in-app page after sign-in)
|
|
21
|
+
declares `from: [...]` with the Checkpoints it is valid from (a `SIGNED_IN` list in
|
|
22
|
+
`src/states/`); without it, routing jumps there from anywhere, skipping sign-in.
|
|
20
23
|
3. **Selectors in `*Sel`, never inline.** DOM strings live in a `*Sel` next to the page - `_sel.ts`
|
|
21
24
|
in the Map layout (one per page folder, next to `_nav`/`_page`/`_methods`), `<name>.sel.ts` in a
|
|
22
25
|
freeform layout (one per concern) - never a fresh file-naming convention per project (`_sel.ts`,
|
|
@@ -39,7 +42,7 @@ against a live site, use `--skill-pilot-blind`.
|
|
|
39
42
|
5. **`requires` = externally supplied.** A key an earlier Block in the same chain produces
|
|
40
43
|
is never listed under `requires` - preflight checks the whole chain up front. For a
|
|
41
44
|
genuinely arbitrary key (a username, an email) that a demo/dev run shouldn't need
|
|
42
|
-
hand-supplied every time, `registerMemStub(key, fake)` + `
|
|
45
|
+
hand-supplied every time, `registerMemStub(key, fake)` + `memStub: true` on the flow (or
|
|
43
46
|
`--mem-stub`) fills it from a registry instead - a key with no registration still fails
|
|
44
47
|
preflight exactly as before, so this narrows what needs real input, never removes the guard.
|
|
45
48
|
6. **Keep Checkpoints typed.** Block helpers take concrete Checkpoint types from `src/states/`
|
|
@@ -63,6 +66,33 @@ against a live site, use `--skill-pilot-blind`.
|
|
|
63
66
|
when a *different* Block right after it in the `.flow.ts` is what actually confirms the
|
|
64
67
|
transition (e.g. an `AssertXBlock`) - `// waygraph-ignore: empty-verify`.
|
|
65
68
|
|
|
69
|
+
## Flows, stubs and config (the scaffold's shape)
|
|
70
|
+
|
|
71
|
+
- **Build flows with `map()`** (`.start().gotoPage(Nav).method(B).assert(A).end({ ... })`; `.gotoExternal` for another origin),
|
|
72
|
+
not a raw `defineFlow([start, ...])` array. Flow settings go in `.end`'s options:
|
|
73
|
+
`.end({ title: "Shop", memStub: true, sessionReset: true })`. Never `withMemStub(withTitle(withSessionReset(...)))`.
|
|
74
|
+
(`defineFlow(blocks, options)` takes the same options.)
|
|
75
|
+
- **Blocks carry no stubs; the flow does.** Demo highlights are ctx stubs chained on the Block where the flow uses it:
|
|
76
|
+
`AddItemBlock.stubAfter((ctx) => { ctx.ring("cart", { selector: DemoSel.cartCount, label: "AC · item in cart" }); })`.
|
|
77
|
+
Not a static `withHighlightFixtures({ "add-item": { stubAfter: {...} } })` map, and not an inline
|
|
78
|
+
`instruction: { stubAfter() {} }`. A Block used twice gets one `const AddShown = AddItemBlock.stubAfter(...)`.
|
|
79
|
+
- **Reading a value into `mem` happens in `act`** (`mem.set(Key, value)`), like Mailpit's `extract-email-link`.
|
|
80
|
+
`observe` is only for a Block that branches (it feeds `resolve`); an empty `act(){}` plus an `observe` that
|
|
81
|
+
writes `mem` is a misuse.
|
|
82
|
+
- **Per-environment values** (a different `baseUrl` for staging) come from `.env` files read in the config function:
|
|
83
|
+
`defineConfig(({ env }) => ({ baseUrl: env.BASE_URL }))`; `--env staging` loads `.env.staging`. Never edit the config to switch.
|
|
84
|
+
- **A browser dialog (`confirm`, `beforeunload`) is answered with `withDialog(block, { accept, into })`**, not inside the
|
|
85
|
+
Block's `act`; the click Block stays a plain click.
|
|
86
|
+
- **Project settings live in `config/waygraph.config.ts`** (`defineConfig({ baseUrl, testDir, webServer,
|
|
87
|
+
extensions, playwright: {...} })`). Flows import it and build the Engine from it (`new Engine(config)`); a project needs no
|
|
88
|
+
`playwright.config.ts` - `waygraph test` builds one from it. A leftover `playwright.config.*` is deprecated.
|
|
89
|
+
- **Another origin the test needs is a Map external** under `src/map/(external)/<tool>/`. For REST API
|
|
90
|
+
tests, `(external)/hoppscotch` (list `extensions: ["hoppscotch"]` in the config) drives hoppscotch.io's client
|
|
91
|
+
with the extension, so requests skip CORS: `set-method`, `set-url`, `set-headers`, `set-json-body`,
|
|
92
|
+
`send-request`, `assert-status`, `read-response-body`, `assert-response-body`.
|
|
93
|
+
- **Updating an older project:** `npm install waygraph@latest`, then `waygraph upgrade` (dry run) to see new
|
|
94
|
+
and differing scaffold files; copy the paths you want with `--apply`.
|
|
95
|
+
|
|
66
96
|
## Block kinds (file convention)
|
|
67
97
|
|
|
68
98
|
| Kind | File | Helper | `page.goto`? |
|
|
@@ -113,8 +143,7 @@ src/map/
|
|
|
113
143
|
- `(group)` parentheses and leading-underscore segments (`_methods/`, `_page…`) are
|
|
114
144
|
**not** URL segments. `waygraph map` excludes them when checking folder-vs-URL.
|
|
115
145
|
- A bare non-underscore folder always means a real child Checkpoint/URL segment.
|
|
116
|
-
-
|
|
117
|
-
it rejects Blocks that didn't come from a real factory (no silent teleporting).
|
|
146
|
+
- `map()` rejects Blocks that didn't come from a real factory (no silent teleporting).
|
|
118
147
|
|
|
119
148
|
Mail / external tools: under `*-external/<tool>/` or `map/(external)/<tool>/`, never under
|
|
120
149
|
the site's `*-web/` tree. Browser-driven (DOM / iframe Traits), not the catcher's REST API.
|
|
@@ -104,13 +104,12 @@ thing that differs from demo is *who* triggers the paint:
|
|
|
104
104
|
|
|
105
105
|
| Mode | Who paints | How |
|
|
106
106
|
|------|------------|-----|
|
|
107
|
-
| `waygraph demo` | Engine, automatically |
|
|
107
|
+
| `waygraph demo` | Engine, automatically | ctx stubs chained in the flow run each step |
|
|
108
108
|
| Pilot / `auto --detach` | **You (the agent)** | `waygraph auto highlight <sessionId> '<json>'` before (or between) sends |
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`auto dom`). Paint, then `auto send` the real Block. Clear when done.
|
|
110
|
+
The ctx stubs a flow chains onto Blocks show up in Pilot's `auto trace` as metadata, but they do **not**
|
|
111
|
+
auto-draw. You re-express the same intent with `auto highlight` (copy selectors/labels from the flow's stubs or
|
|
112
|
+
from `auto dom`). Paint, then `auto send` the real Block. Clear when done.
|
|
114
113
|
|
|
115
114
|
```
|
|
116
115
|
# Headful watch session (rings visible to a human audience):
|
package/dist/browser-path.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CHROME_PATH / CHROMIUM_PATH, if set and the file exists. A stale path (browser uninstalled)
|
|
3
|
-
* falls back to Playwright's bundled Chromium with a one-time warning instead of failing the run.
|
|
4
|
-
*/
|
|
5
|
-
export declare function chromiumExecutablePath(): string | undefined;
|
package/dist/browser-path.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
let warned = false;
|
|
3
|
-
/**
|
|
4
|
-
* CHROME_PATH / CHROMIUM_PATH, if set and the file exists. A stale path (browser uninstalled)
|
|
5
|
-
* falls back to Playwright's bundled Chromium with a one-time warning instead of failing the run.
|
|
6
|
-
*/
|
|
7
|
-
export function chromiumExecutablePath() {
|
|
8
|
-
const path = process.env.CHROME_PATH || process.env.CHROMIUM_PATH;
|
|
9
|
-
if (!path)
|
|
10
|
-
return undefined;
|
|
11
|
-
if (existsSync(path))
|
|
12
|
-
return path;
|
|
13
|
-
if (!warned) {
|
|
14
|
-
warned = true;
|
|
15
|
-
console.error(`waygraph: CHROME_PATH/CHROMIUM_PATH "${path}" does not exist - using Playwright's bundled Chromium`);
|
|
16
|
-
}
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Page } from "@playwright/test";
|
|
2
|
-
import type { StubPhaseResult } from "../highlights/types.js";
|
|
3
|
-
|
|
4
|
-
export interface SessionOverlay {
|
|
5
|
-
present(tag: string, phase: Partial<StubPhaseResult>): Promise<void>;
|
|
6
|
-
restore(): Promise<void>;
|
|
7
|
-
}
|
|
8
|
-
export function createSessionOverlay(page: Page, surface: string): SessionOverlay;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// Presents a resolved stub phase ("waygraph overlay fixtures": ctx.todos / device / title / banner ...) on a LIVE page
|
|
2
|
-
// for the session surfaces (auto / browser / pilot). Same OverlayStage + same in-page installers + same verbose log
|
|
3
|
-
// lines as `waygraph demo`, so an overlay fixture behaves identically on every surface. Rings are painted separately
|
|
4
|
-
// (pilot holds them; demo cycles them) - that is the only intended difference.
|
|
5
|
-
import { installOverlay } from "./overlay-install.js";
|
|
6
|
-
import { createOverlayStage } from "./overlay-stage.js";
|
|
7
|
-
import { demoLog, logStubPhaseFixtures, logTodoDockFull, setDemoLogSurface, summarizeDevice } from "./demo-log.js";
|
|
8
|
-
import { logBannerDom, probeBanner } from "./banner-log.js";
|
|
9
|
-
import { probeTodoDocksOnPage } from "./todo-dock.js";
|
|
10
|
-
|
|
11
|
-
export function createSessionOverlay(page, surface) {
|
|
12
|
-
setDemoLogSurface(surface);
|
|
13
|
-
const stage = createOverlayStage(page, { todoDockRef: { current: undefined }, deviceRef: { current: undefined } });
|
|
14
|
-
let title = undefined;
|
|
15
|
-
let painted = false;
|
|
16
|
-
|
|
17
|
-
const paintChrome = async (phaseParallel) => {
|
|
18
|
-
await installOverlay(page, title, stage.state.lastBannerUi);
|
|
19
|
-
await stage.syncAllTodoDocks({ parallel: !!phaseParallel });
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
stage,
|
|
24
|
-
/** Apply one phase (tag = before | after | error | highlight) and log the resulting overlay state. */
|
|
25
|
-
async present(tag, phase) {
|
|
26
|
-
setDemoLogSurface(surface);
|
|
27
|
-
logStubPhaseFixtures(tag, phase);
|
|
28
|
-
await stage.pushTodoDockUi(phase.todoDockUi);
|
|
29
|
-
const { appliedTodo, appliedDevice } = await stage.applyPhase(phase, { reapplyDeviceOnKeep: true });
|
|
30
|
-
if (phase.title && String(phase.title).trim()) title = String(phase.title).trim();
|
|
31
|
-
const wantsChrome = !!(title || stage.state.lastTodoDock || phase.todoSync === "set");
|
|
32
|
-
if (wantsChrome) {
|
|
33
|
-
painted = true;
|
|
34
|
-
await paintChrome(phase.todoParallel);
|
|
35
|
-
}
|
|
36
|
-
logTodoDockFull(stage.state.lastTodoDock, " " + tag);
|
|
37
|
-
demoLog(" " + tag + " device=" + summarizeDevice(stage.state.lastDevice) + " deviceSync=" + appliedDevice.sync);
|
|
38
|
-
demoLog(" " + tag + " todoSync=" + appliedTodo.sync);
|
|
39
|
-
const docks = await probeTodoDocksOnPage(page);
|
|
40
|
-
if (!docks.length && stage.state.lastTodoDock && appliedTodo.sync !== "clear") {
|
|
41
|
-
demoLog(" WARN " + tag + ": todos authored but dock-dom empty (not painted?)");
|
|
42
|
-
}
|
|
43
|
-
for (const d of docks) {
|
|
44
|
-
demoLog(
|
|
45
|
-
" " + tag + " dock-dom " + d.key + "@" + d.pos + " " + d.left + "," + d.top + " " + d.w + "x" + d.h +
|
|
46
|
-
(d.onScreen ? "" : " OFFSCREEN") + " n=" + d.n + (d.current ? ' cur="' + d.current + '"' : ""),
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
if (title || painted) logBannerDom(tag, await probeBanner(page));
|
|
50
|
-
},
|
|
51
|
-
/** A navigation wipes the overlay DOM: rebuild what was authored (same job as the demo's post-navigation restore). */
|
|
52
|
-
async restore() {
|
|
53
|
-
if (!painted) return;
|
|
54
|
-
await paintChrome(false);
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { ACCENT_SOFT, SURFACE } from "../tokens.js";
|
|
2
|
-
/** Top-left demo banner (`#wg-banner`). */
|
|
3
|
-
export function bannerCss() {
|
|
4
|
-
return ("#wg-banner{position:fixed;z-index:2147483647;top:14px;left:14px;" +
|
|
5
|
-
`background:${SURFACE};color:#fff;border-radius:12px;padding:10px 16px;` +
|
|
6
|
-
"font:14px/1.4 system-ui,sans-serif;box-shadow:0 8px 20px rgba(0,0,0,.3);" +
|
|
7
|
-
"border:1px solid rgba(124,58,237,.4);}" +
|
|
8
|
-
`#wg-banner .wg-banner-tag{display:block;font-size:10px;font-weight:700;color:${ACCENT_SOFT};` +
|
|
9
|
-
"letter-spacing:.05em;text-transform:uppercase;margin-bottom:2px;}");
|
|
10
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { TONES } from "../tokens.js";
|
|
2
|
-
/** Travelling mouse cursor + click ripple (`#wg-cursor`, `#wg-click-pulse`). */
|
|
3
|
-
export function cursorCss() {
|
|
4
|
-
const p = TONES.planned;
|
|
5
|
-
const pulseTones = ["auto", "info", "warning", "danger", "success"]
|
|
6
|
-
.map((n) => `#wg-click-pulse[data-tone=${n}]{border-color:${TONES[n].ring};background:${TONES[n].pulse};}`)
|
|
7
|
-
.join("");
|
|
8
|
-
return ("#wg-cursor{position:fixed;z-index:2147483647;width:24px;height:24px;pointer-events:none;" +
|
|
9
|
-
"left:0;top:0;opacity:0;margin:0;" +
|
|
10
|
-
// Travel time is JS-driven via --wg-cursor-ms (a hardcoded duration would make speed a no-op).
|
|
11
|
-
"transition:transform var(--wg-cursor-ms,600ms) cubic-bezier(.22,1,.36,1),opacity .2s ease;" +
|
|
12
|
-
"filter:drop-shadow(0 2px 4px rgba(12,12,26,.4));}" +
|
|
13
|
-
"#wg-click-pulse{position:fixed;z-index:2147483647;width:14px;height:14px;" +
|
|
14
|
-
"margin-left:-7px;margin-top:-7px;border-radius:50%;pointer-events:none;opacity:0;" +
|
|
15
|
-
`border:2px solid ${p.ring};background:${p.pulse};}` +
|
|
16
|
-
pulseTones +
|
|
17
|
-
"#wg-click-pulse.wg-pulse{animation:wg-pulse .5s ease-out;}" +
|
|
18
|
-
"@keyframes wg-pulse{0%{opacity:.9;transform:scale(.4);}100%{opacity:0;transform:scale(2.4);}}");
|
|
19
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from "@playwright/test";
|
|
2
|
-
|
|
3
|
-
export default defineConfig({
|
|
4
|
-
testDir: "./tests",
|
|
5
|
-
fullyParallel: true,
|
|
6
|
-
reporter: [["list"], ["html", { open: "never" }]],
|
|
7
|
-
use: {
|
|
8
|
-
// Demo pages open via file:// - no server. Filled in by `waygraph init`.
|
|
9
|
-
baseURL: "__APP_BASE_URL__",
|
|
10
|
-
trace: "retain-on-failure",
|
|
11
|
-
},
|
|
12
|
-
});
|