waygraph 0.13.7 → 0.15.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/README.md +970 -232
- package/assets/stubs/stub.mp4 +0 -0
- package/assets/stubs/stub.pdf +32 -0
- package/assets/stubs/stub.png +0 -0
- package/dist/agent-dive.d.ts +9 -0
- package/dist/agent-dive.js +32 -0
- package/dist/auto-explore-run.d.ts +20 -2
- package/dist/auto-explore-run.js +9 -8
- package/dist/auto-explore.js +22 -2
- package/dist/auto-session-ipc.d.ts +123 -0
- package/dist/auto-session-ipc.js +381 -0
- package/dist/auto-session.d.ts +342 -0
- package/dist/auto-session.js +760 -0
- package/dist/cli.js +450 -51
- package/dist/engine.d.ts +201 -1
- package/dist/engine.js +312 -2
- package/dist/graph.d.ts +18 -0
- package/dist/graph.js +65 -2
- package/dist/highlights.d.ts +20 -1
- package/dist/highlights.js +60 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.js +5 -3
- package/dist/map-check.d.ts +18 -0
- package/dist/map-check.js +140 -0
- package/dist/pilot-overlay.d.ts +88 -0
- package/dist/pilot-overlay.js +363 -0
- package/dist/pilot.d.ts +49 -0
- package/dist/pilot.js +53 -0
- package/dist/trait.d.ts +22 -0
- package/dist/trait.js +60 -3
- package/dist/types.d.ts +21 -1
- package/dist/types.js +17 -1
- package/examples/routed-demo/.waygraph-auto/4d4902b7.log +0 -0
- package/examples/routed-demo/.waygraph-auto/51ef462e.log +0 -0
- package/examples/routed-demo/.waygraph-auto/71527ad9.log +0 -0
- package/examples/routed-demo/.waygraph-auto/ca16778e.log +0 -0
- package/examples/routed-demo/package.json +26 -0
- package/examples/routed-demo/playwright.config.ts +20 -0
- package/examples/routed-demo/scripts/fixture-server.mjs +37 -0
- package/examples/routed-demo/scripts/with-fixture.mjs +70 -0
- package/examples/routed-demo/src/flows/routed-demo-map.flow.ts +39 -0
- package/examples/routed-demo/src/flows/routed-demo.flow.ts +12 -0
- package/examples/routed-demo/src/map/(app_base)/dashboard/_methods/click-widget.block.ts +23 -0
- package/examples/routed-demo/src/map/(app_base)/dashboard/_nav.block.ts +18 -0
- package/examples/routed-demo/src/map/(app_base)/dashboard/_page.block.ts +22 -0
- package/examples/routed-demo/src/map/(app_base)/dashboard/_sel.ts +10 -0
- package/examples/routed-demo/src/map/(external)/docs/_nav.block.ts +21 -0
- package/examples/routed-demo/src/map/(external)/docs/_page.block.ts +15 -0
- package/examples/routed-demo/src/map/(external)/docs/_sel.ts +7 -0
- package/examples/routed-demo/src/pages/dashboard.html +10 -0
- package/examples/routed-demo/src/pages/docs.html +8 -0
- package/examples/routed-demo/src/states/routed-demo.states.ts +10 -0
- package/examples/routed-demo/test-results/.last-run.json +4 -0
- package/examples/routed-demo/tests/routed-demo-map.spec.ts +17 -0
- package/examples/routed-demo/tests/routed-demo.spec.ts +75 -0
- package/examples/routed-demo/tsconfig.json +18 -0
- package/examples/routed-demo-consumer/package.json +18 -0
- package/examples/saucedemo/.waygraph-auto/00d5336e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/010dd92b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/011ad671.log +0 -0
- package/examples/saucedemo/.waygraph-auto/01b45645.log +0 -0
- package/examples/saucedemo/.waygraph-auto/03764153.log +0 -0
- package/examples/saucedemo/.waygraph-auto/0593587e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/06d12823.log +0 -0
- package/examples/saucedemo/.waygraph-auto/083cc5b9.log +0 -0
- package/examples/saucedemo/.waygraph-auto/0b1f5b26.json +8 -0
- package/examples/saucedemo/.waygraph-auto/0c00f25f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/0e480591.log +0 -0
- package/examples/saucedemo/.waygraph-auto/104751ef.log +0 -0
- package/examples/saucedemo/.waygraph-auto/110958b8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/138107c1.log +0 -0
- package/examples/saucedemo/.waygraph-auto/159d541b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/16aa2c01.log +0 -0
- package/examples/saucedemo/.waygraph-auto/16c35dd5.log +0 -0
- package/examples/saucedemo/.waygraph-auto/1706547d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/17537e42.log +0 -0
- package/examples/saucedemo/.waygraph-auto/175655f7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/17ede493.log +0 -0
- package/examples/saucedemo/.waygraph-auto/1fe2414f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/1fe2e3b8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2219f00b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/23e62571.log +0 -0
- package/examples/saucedemo/.waygraph-auto/25f68ada.log +0 -0
- package/examples/saucedemo/.waygraph-auto/26ca9cd8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2bb6c4e8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2d425004.log +0 -0
- package/examples/saucedemo/.waygraph-auto/2f5b3ea6.log +0 -0
- package/examples/saucedemo/.waygraph-auto/31bb303d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/31f2042f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/36551ec1.log +0 -0
- package/examples/saucedemo/.waygraph-auto/36a1c274.log +0 -0
- package/examples/saucedemo/.waygraph-auto/381aa338.log +0 -0
- package/examples/saucedemo/.waygraph-auto/3882bce1.log +0 -0
- package/examples/saucedemo/.waygraph-auto/38acd1dc.log +0 -0
- package/examples/saucedemo/.waygraph-auto/3b8b77b9.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4028d2a2.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4168b208.log +0 -0
- package/examples/saucedemo/.waygraph-auto/42cc9113.log +0 -0
- package/examples/saucedemo/.waygraph-auto/439d30aa.log +0 -0
- package/examples/saucedemo/.waygraph-auto/43ab66ce.log +0 -0
- package/examples/saucedemo/.waygraph-auto/43f66838.log +0 -0
- package/examples/saucedemo/.waygraph-auto/442171d4.log +0 -0
- package/examples/saucedemo/.waygraph-auto/453b1ce1.log +0 -0
- package/examples/saucedemo/.waygraph-auto/457322bd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/46197027.log +0 -0
- package/examples/saucedemo/.waygraph-auto/46889468.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4890da1c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4893a63b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4a85f496.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4af71440.log +0 -0
- package/examples/saucedemo/.waygraph-auto/4f7496d1.log +0 -0
- package/examples/saucedemo/.waygraph-auto/50e8baaa.log +0 -0
- package/examples/saucedemo/.waygraph-auto/52b3a455.log +0 -0
- package/examples/saucedemo/.waygraph-auto/532dd9b8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5391ce40.log +0 -0
- package/examples/saucedemo/.waygraph-auto/57dbddb3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5897f501.log +0 -0
- package/examples/saucedemo/.waygraph-auto/59c0b57d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5b511794.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5c0eef7f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5c47910f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5d60da77.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5eda8273.log +0 -0
- package/examples/saucedemo/.waygraph-auto/5fc3a33b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/61b6546d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/621518ad.log +0 -0
- package/examples/saucedemo/.waygraph-auto/632b7118.log +0 -0
- package/examples/saucedemo/.waygraph-auto/64314c2b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/650ed066.log +0 -0
- package/examples/saucedemo/.waygraph-auto/66516213.log +0 -0
- package/examples/saucedemo/.waygraph-auto/66c17079.log +0 -0
- package/examples/saucedemo/.waygraph-auto/69bf0b80.log +0 -0
- package/examples/saucedemo/.waygraph-auto/69c2347a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/6d22bdb0.log +0 -0
- package/examples/saucedemo/.waygraph-auto/6d9aaad3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7103bebb.log +0 -0
- package/examples/saucedemo/.waygraph-auto/740c9faa.log +0 -0
- package/examples/saucedemo/.waygraph-auto/74872fc5.log +0 -0
- package/examples/saucedemo/.waygraph-auto/75846ca9.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7626c85b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/76308eec.log +0 -0
- package/examples/saucedemo/.waygraph-auto/76c1650e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7861f38f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/78b21219.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7af224ed.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7c3be585.log +0 -0
- package/examples/saucedemo/.waygraph-auto/7ed55a2c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/80086ba2.log +0 -0
- package/examples/saucedemo/.waygraph-auto/80206369.log +0 -0
- package/examples/saucedemo/.waygraph-auto/847f1092.log +0 -0
- package/examples/saucedemo/.waygraph-auto/84d6d293.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8581abfa.log +0 -0
- package/examples/saucedemo/.waygraph-auto/87b7e0fd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/87c79256.log +0 -0
- package/examples/saucedemo/.waygraph-auto/88af2495.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8a3eb088.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8c04a403.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8c9ab09e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8cc5db51.log +0 -0
- package/examples/saucedemo/.waygraph-auto/8ea3a69c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/9150f913.log +0 -0
- package/examples/saucedemo/.waygraph-auto/929a4313.log +0 -0
- package/examples/saucedemo/.waygraph-auto/95a599e4.log +0 -0
- package/examples/saucedemo/.waygraph-auto/96c45350.log +0 -0
- package/examples/saucedemo/.waygraph-auto/986030e6.log +0 -0
- package/examples/saucedemo/.waygraph-auto/99787abb.log +0 -0
- package/examples/saucedemo/.waygraph-auto/99ddb62c.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a2dd30dd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a63a2d20.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a78befb7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a84d9b79.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a87bb3ee.log +0 -0
- package/examples/saucedemo/.waygraph-auto/a9270890.log +0 -0
- package/examples/saucedemo/.waygraph-auto/aa440153.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ab7561e0.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ac19ae60.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ae21ad8e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/af09042f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b1684126.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b21e9d12.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b5a838be.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b603549d.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b81e074f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b83e6259.log +0 -0
- package/examples/saucedemo/.waygraph-auto/b9cb2796.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ba58abb7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bb0a0018.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bb9fd384.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bc0bdd0e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bc7d4a83.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bd8e082a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bea0c93e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bf35201a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/bffa3a7a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c02ea583.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c2caf0a8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c3bdc714.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c4afb8bc.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c916a654.log +0 -0
- package/examples/saucedemo/.waygraph-auto/c9b8d69e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cd68bbc4.log +0 -0
- package/examples/saucedemo/.waygraph-auto/cdc6db50.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d01906a7.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d02e5e7e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d09aff71.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d0e41396.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d1370d76.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d3714ae4.log +0 -0
- package/examples/saucedemo/.waygraph-auto/d9e5f8a4.log +0 -0
- package/examples/saucedemo/.waygraph-auto/da0685dc.log +0 -0
- package/examples/saucedemo/.waygraph-auto/da894ad8.log +0 -0
- package/examples/saucedemo/.waygraph-auto/daa2f922.log +0 -0
- package/examples/saucedemo/.waygraph-auto/daefb614.log +0 -0
- package/examples/saucedemo/.waygraph-auto/db97e833.log +0 -0
- package/examples/saucedemo/.waygraph-auto/dec3076e.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e101d19a.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e1973991.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e207c989.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e633e545.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e69aec68.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e70e2d06.log +0 -0
- package/examples/saucedemo/.waygraph-auto/e8306a4b.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ebad3410.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ec88b3ef.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ecbb0966.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ecdee753.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ed888783.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ed931dac.log +0 -0
- package/examples/saucedemo/.waygraph-auto/ef655a3f.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f34c9a26.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f3c29b21.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f53f7cfc.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f5525d53.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f5ad5830.log +0 -0
- package/examples/saucedemo/.waygraph-auto/f90035b3.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fa687bc2.log +0 -0
- package/examples/saucedemo/.waygraph-auto/faed36dd.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fb66fba0.log +0 -0
- package/examples/saucedemo/.waygraph-auto/fc48fd76.log +0 -0
- package/examples/saucedemo/docs/CONVENTION.md +25 -5
- package/examples/saucedemo/docs/HELPERS.md +15 -4
- package/examples/saucedemo/scripts/capture-wrong-login.mjs +8 -2
- package/examples/saucedemo/scripts/prove-wrong-login-runtime.mjs +8 -2
- package/examples/saucedemo/src/blocks/SITE-MAP.md +14 -3
- package/examples/saucedemo/src/blocks/saucedemo-web/NAV.md +15 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/NAV.md +1 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +3 -2
- package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
- package/examples/saucedemo/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +13 -51
- package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
- package/examples/saucedemo/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
- package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +15 -0
- package/examples/saucedemo/src/flows/checkout.flow.ts +20 -2
- package/examples/saucedemo/src/flows/device-showcase.flow.ts +4 -0
- package/examples/saucedemo/src/flows/login.flow.ts +3 -1
- package/examples/saucedemo/src/flows/shop.flow.ts +6 -0
- package/examples/saucedemo/src/flows/smoke.flow.ts +13 -7
- package/examples/saucedemo/src/flows/stub-on-error.flow.ts +4 -2
- package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +6 -2
- package/examples/saucedemo/tests/flow-block-verify.spec.ts +3 -3
- package/examples/saucedemo/tests/submit-login-branch.spec.ts +5 -3
- package/package.json +2 -1
- package/templates/agents/waygraph-author.agent.md +43 -5
- package/templates/agents/waygraph-planner.agent.md +33 -2
- package/templates/quickstart/src/blocks/saucedemo-web/NAV.md +15 -1
- package/templates/quickstart/src/blocks/saucedemo-web/cart/NAV.md +1 -0
- package/templates/quickstart/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
- package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +19 -15
- package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
- package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
- package/templates/quickstart/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
- package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +24 -16
- package/templates/quickstart/src/blocks/saucedemo-web/inventory/nav-item-detail.block.ts +3 -3
- package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
- package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
- package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
- package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
- package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
- package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
- package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
- package/templates/quickstart/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
- package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
- package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +35 -63
- package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
- package/templates/quickstart/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
- package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +15 -0
- package/templates/quickstart/src/flows/checkout.flow.ts +29 -2
- package/templates/quickstart/src/flows/device-showcase.flow.ts +4 -0
- package/templates/quickstart/src/flows/login.flow.ts +3 -1
- package/templates/quickstart/src/flows/shop.flow.ts +6 -0
- package/templates/quickstart/src/flows/smoke.flow.ts +13 -7
- package/templates/quickstart/src/flows/viewer-blocked.flow.ts +6 -2
- package/templates/quickstart/tests/chain-flow.spec.ts +8 -1
- package/templates/scaffold/STRUCTURE.md +23 -3
- package/templates/scaffold/scripts/fixture-server.mjs +11 -2
- package/templates/scaffold/src/flows/example.flow.ts +8 -6
- package/templates/scaffold/src/flows/mail-verify.flow.ts +85 -0
- package/templates/scaffold/src/flows/shop.flow.ts +23 -6
- package/templates/scaffold/src/map/(app_base)/docs/_nav.block.ts +20 -0
- package/templates/scaffold/src/map/(app_base)/docs/_page.block.ts +15 -0
- package/templates/scaffold/src/map/(app_base)/docs/_sel.ts +7 -0
- package/templates/scaffold/src/map/(app_base)/docs/docs.html +8 -0
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/add-item.effect.block.ts +3 -3
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +46 -0
- package/templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts +38 -0
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/clear-cart.method.block.ts +9 -3
- package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/remove-item.effect.block.ts +3 -3
- package/templates/scaffold/src/{blocks/demo-web/nav-home.block.ts → map/(app_base)/home/_nav.block.ts} +2 -2
- package/templates/scaffold/src/{blocks/demo-web/home.page.block.ts → map/(app_base)/home/_page.block.ts} +7 -7
- package/templates/scaffold/src/{blocks/demo-web/demo-sel.ts → map/(app_base)/home/_sel.ts} +1 -0
- package/templates/scaffold/src/{blocks/demo-web → map/(app_base)/home}/home.html +4 -0
- package/templates/scaffold/src/map/(app_base)/home/nav-verification-link.block.ts +34 -0
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-content.method.block.ts +48 -0
- package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-received.method.block.ts +49 -0
- package/templates/scaffold/src/map/(external)/mailpit/_methods/extract-email-link.method.block.ts +41 -0
- package/templates/scaffold/src/map/(external)/mailpit/_methods/open-message.method.block.ts +39 -0
- package/templates/scaffold/src/map/(external)/mailpit/_nav.block.ts +20 -0
- package/templates/scaffold/src/map/(external)/mailpit/_page.block.ts +30 -0
- package/templates/scaffold/src/map/(external)/mailpit/_sel.ts +5 -0
- package/templates/scaffold/src/states/demo.mem-keys.ts +25 -1
- package/templates/scaffold/src/states/demo.states.ts +11 -0
- package/templates/scaffold/test-results/.last-run.json +4 -0
- package/templates/scaffold/tests/example.spec.ts +2 -2
- package/templates/scaffold/tests/mail-verify.spec.ts +191 -0
- package/templates/skills/waygraph-blind-pilot.skill.md +61 -0
- package/templates/skills/waygraph-pilot.skill.md +108 -0
- package/templates/quickstart/README-saucedemo-commands.md +0 -72
- package/templates/scaffold/src/blocks/SITE-MAP.md +0 -12
- package/templates/scaffold/src/blocks/demo-web/NAV.md +0 -29
- package/templates/scaffold/src/blocks/demo-web/methods/assert-hello.method.block.ts +0 -50
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { definePageBlock, Trait } from "waygraph";
|
|
2
|
+
import type { MailpitInbox } from "../../../states/demo.states.js";
|
|
3
|
+
import { AssertEmailReceivedBlock } from "./_methods/assert-email-received.method.block.js";
|
|
4
|
+
import { OpenMessageBlock } from "./_methods/open-message.method.block.js";
|
|
5
|
+
import { AssertEmailContentBlock } from "./_methods/assert-email-content.method.block.js";
|
|
6
|
+
import { ExtractEmailLinkBlock } from "./_methods/extract-email-link.method.block.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Kind: Page
|
|
10
|
+
* Helper: definePageBlock
|
|
11
|
+
* Route: (external)/mailpit/ (= http://127.0.0.1:8025/ by default)
|
|
12
|
+
*
|
|
13
|
+
* Hub for the Mailpit dev-inbox screen. Arrival-only (no url/click) -
|
|
14
|
+
* nav-mailpit-inbox owns the goto. Methods: confirm the email arrived, open
|
|
15
|
+
* it, confirm its content, then read its link - all mem-driven (recipient,
|
|
16
|
+
* link pattern, expected content), so this one page's methods serve any
|
|
17
|
+
* number of different email scenarios, not just one.
|
|
18
|
+
*/
|
|
19
|
+
export const MailpitInboxPageBlock = definePageBlock<MailpitInbox>({
|
|
20
|
+
name: "page-mailpit-inbox",
|
|
21
|
+
description: "Mailpit dev inbox hub - confirm arrival/content, open the expected message, read its link.",
|
|
22
|
+
checkpoint: "MailpitInbox",
|
|
23
|
+
verify: [Trait.url({ pathname: "/" })],
|
|
24
|
+
methods: {
|
|
25
|
+
assertEmailReceived: () => AssertEmailReceivedBlock,
|
|
26
|
+
openMessage: () => OpenMessageBlock,
|
|
27
|
+
assertEmailContent: () => AssertEmailContentBlock,
|
|
28
|
+
extractEmailLink: () => ExtractEmailLinkBlock,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
@@ -1,7 +1,31 @@
|
|
|
1
|
-
import { keyGroup } from "waygraph";
|
|
1
|
+
import { key, keyGroup } from "waygraph";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Which catalog row `add-item` / `remove-item` act on.
|
|
5
5
|
* `waygraph auto` fills this via Effect `instanceOptions` (one menu row per live button).
|
|
6
6
|
*/
|
|
7
7
|
export const SelectedItem = keyGroup<{ id: string; name: string }>("demo.selectedItem");
|
|
8
|
+
|
|
9
|
+
/** The link read out of the opened message - set by extract-email-link. */
|
|
10
|
+
export const EmailLink = keyGroup<{ url: string }>("demo.emailLink");
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The recipient open-message looks for in the inbox. Mailpit lists newest
|
|
14
|
+
* first, but `.first()` alone picks the wrong row once more than one
|
|
15
|
+
* recipient's mail is sitting in the same inbox - match by mem, not position.
|
|
16
|
+
*/
|
|
17
|
+
export const ExpectedRecipient = keyGroup<{ email: string }>("demo.expectedRecipient");
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Substring the link's `href` must contain - lets the same extract-email-link
|
|
21
|
+
* Block serve any number of different email templates/scenarios in one
|
|
22
|
+
* project without a new Block per scenario. Only the mem values change per
|
|
23
|
+
* run, not the Blocks.
|
|
24
|
+
*/
|
|
25
|
+
export const ExpectedLinkPattern = key<string>("demo.expectedLinkPattern");
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The substring the open message's body must contain - same reuse reasoning
|
|
29
|
+
* as ExpectedLinkPattern: one Block, many scenarios, driven by mem.
|
|
30
|
+
*/
|
|
31
|
+
export const ExpectedEmailContent = key<string>("demo.expectedEmailContent");
|
|
@@ -4,3 +4,14 @@ export type Home = Checkpoint<"Home">;
|
|
|
4
4
|
export type HomeVerified = Checkpoint<"HomeVerified">;
|
|
5
5
|
export type ItemInCart = Checkpoint<"ItemInCart">;
|
|
6
6
|
export type CartEmpty = Checkpoint<"CartEmpty">;
|
|
7
|
+
|
|
8
|
+
// --- Mailpit (external tool surface, src/map/(external)/mailpit/) ---
|
|
9
|
+
|
|
10
|
+
/** Mailpit inbox UI reached; message list visible. */
|
|
11
|
+
export type MailpitInbox = Checkpoint<"MailpitInbox">;
|
|
12
|
+
|
|
13
|
+
/** The message addressed to the expected recipient is open; preview iframe visible. */
|
|
14
|
+
export type MailpitMessageOpen = Checkpoint<"MailpitMessageOpen">;
|
|
15
|
+
|
|
16
|
+
/** Reached via src/map/(app_base)/docs/ - see openspec/changes/waygraph-map. */
|
|
17
|
+
export type Docs = Checkpoint<"Docs">;
|
|
@@ -4,9 +4,9 @@ import { exampleFlow } from "../src/flows/example.flow.js";
|
|
|
4
4
|
import { shopFlow } from "../src/flows/shop.flow.js";
|
|
5
5
|
import { SelectedItem } from "../src/states/demo.mem-keys.js";
|
|
6
6
|
|
|
7
|
-
test("example flow reaches
|
|
7
|
+
test("example flow reaches Docs (home -> assert -> sibling docs/ page)", async ({ context }) => {
|
|
8
8
|
const result = await exampleFlow.run(context, new MemPage());
|
|
9
|
-
expect(result).toEqual(checkpoint("
|
|
9
|
+
expect(result).toEqual(checkpoint("Docs"));
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
test("shop flow adds then clears (Effect + Method)", async ({ context }) => {
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { test, expect } from "@playwright/test";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import { MemPage, checkpoint } from "waygraph";
|
|
5
|
+
import { mailVerifyFlow } from "../src/flows/mail-verify.flow.js";
|
|
6
|
+
import { ExpectedRecipient, ExpectedLinkPattern, ExpectedEmailContent } from "../src/states/demo.mem-keys.js";
|
|
7
|
+
import { HOME_URL } from "../src/map/(app_base)/home/_sel.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* This scaffold has no real backend, so these tests play the role of "the
|
|
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
|
+
*/
|
|
20
|
+
|
|
21
|
+
function runMailpit(): { containerId: string; smtpPort: number; httpPort: number } {
|
|
22
|
+
const run = spawnSync("docker", ["run", "--rm", "-d", "-p", "0:1025", "-p", "0:8025", "axllent/mailpit"], {
|
|
23
|
+
encoding: "utf8",
|
|
24
|
+
});
|
|
25
|
+
if (run.status !== 0) throw new Error(`docker run axllent/mailpit failed: ${run.stderr || run.stdout}`);
|
|
26
|
+
const containerId = run.stdout.trim();
|
|
27
|
+
const portCheck = spawnSync("docker", ["port", containerId], { encoding: "utf8" });
|
|
28
|
+
const portFor = (containerPort: number) => {
|
|
29
|
+
const line = portCheck.stdout.split("\n").find((l) => l.startsWith(`${containerPort}/tcp`) && l.includes("0.0.0.0"));
|
|
30
|
+
const m = line?.match(/:(\d+)\s*$/);
|
|
31
|
+
if (!m) throw new Error(`could not determine host port for ${containerPort}`);
|
|
32
|
+
return Number(m[1]);
|
|
33
|
+
};
|
|
34
|
+
return { containerId, smtpPort: portFor(1025), httpPort: portFor(8025) };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function dockerAvailable(): boolean {
|
|
38
|
+
return spawnSync("docker", ["ps"], { encoding: "utf8" }).status === 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function sendTestEmail(opts: { host: string; port: number; to: string; subject: string; html: string }): Promise<void> {
|
|
42
|
+
const body = [
|
|
43
|
+
"From: scaffold-app@example.com",
|
|
44
|
+
`To: ${opts.to}`,
|
|
45
|
+
`Subject: ${opts.subject}`,
|
|
46
|
+
"Content-Type: text/html; charset=utf-8",
|
|
47
|
+
"",
|
|
48
|
+
opts.html,
|
|
49
|
+
".",
|
|
50
|
+
].join("\r\n");
|
|
51
|
+
|
|
52
|
+
await new Promise<void>((resolve, reject) => {
|
|
53
|
+
const socket = net.createConnection({ host: opts.host, port: opts.port });
|
|
54
|
+
function cmd(line: string): Promise<string> {
|
|
55
|
+
return new Promise((res) => {
|
|
56
|
+
socket.once("data", (d) => res(d.toString()));
|
|
57
|
+
socket.write(line + "\r\n");
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
socket.once("error", reject);
|
|
61
|
+
socket.once("connect", () => {
|
|
62
|
+
(async () => {
|
|
63
|
+
await new Promise<void>((r) => socket.once("data", () => r()));
|
|
64
|
+
await cmd("HELO localhost");
|
|
65
|
+
await cmd("MAIL FROM:<scaffold-app@example.com>");
|
|
66
|
+
await cmd(`RCPT TO:<${opts.to}>`);
|
|
67
|
+
await cmd("DATA");
|
|
68
|
+
await cmd(body);
|
|
69
|
+
await cmd("QUIT");
|
|
70
|
+
socket.end();
|
|
71
|
+
resolve();
|
|
72
|
+
})().catch(reject);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function withMailpit<T>(fn: (opts: { httpPort: number; smtpPort: number }) => Promise<T>): Promise<T> {
|
|
78
|
+
const { containerId, smtpPort, httpPort } = runMailpit();
|
|
79
|
+
const originalMailUrl = process.env.WAYGRAPH_MAIL_URL;
|
|
80
|
+
try {
|
|
81
|
+
process.env.WAYGRAPH_MAIL_URL = `http://127.0.0.1:${httpPort}`;
|
|
82
|
+
return await fn({ httpPort, smtpPort });
|
|
83
|
+
} finally {
|
|
84
|
+
if (originalMailUrl === undefined) delete process.env.WAYGRAPH_MAIL_URL;
|
|
85
|
+
else process.env.WAYGRAPH_MAIL_URL = originalMailUrl;
|
|
86
|
+
spawnSync("docker", ["stop", containerId]);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const VERIFICATION_CONTENT = "Please confirm your email address to finish setting up your account.";
|
|
91
|
+
|
|
92
|
+
test("mailVerifyFlow: confirms arrival, confirms content, reads the real link, navigates to it", async ({
|
|
93
|
+
context,
|
|
94
|
+
}) => {
|
|
95
|
+
test.skip(!dockerAvailable(), "Docker not reachable in this environment");
|
|
96
|
+
test.setTimeout(60_000);
|
|
97
|
+
|
|
98
|
+
await withMailpit(async ({ smtpPort }) => {
|
|
99
|
+
const recipient = "demo-user@example.com";
|
|
100
|
+
await sendTestEmail({
|
|
101
|
+
host: "127.0.0.1",
|
|
102
|
+
port: smtpPort,
|
|
103
|
+
to: recipient,
|
|
104
|
+
subject: "Please verify your account",
|
|
105
|
+
html: `<p>${VERIFICATION_CONTENT}</p><a href="${HOME_URL}?verified=1">Verify your account</a>`,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const mem = new MemPage();
|
|
109
|
+
mem.set(ExpectedRecipient({ email: recipient }));
|
|
110
|
+
mem.set(ExpectedLinkPattern, "verified=1");
|
|
111
|
+
mem.set(ExpectedEmailContent, VERIFICATION_CONTENT);
|
|
112
|
+
const result = await mailVerifyFlow.run(context, mem);
|
|
113
|
+
expect(result).toEqual(checkpoint("HomeVerified"));
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("the same four mail-reading Blocks handle a second, unrelated email scenario - zero new Blocks", async ({
|
|
118
|
+
context,
|
|
119
|
+
}) => {
|
|
120
|
+
test.skip(!dockerAvailable(), "Docker not reachable in this environment");
|
|
121
|
+
test.setTimeout(60_000);
|
|
122
|
+
|
|
123
|
+
await withMailpit(async ({ smtpPort }) => {
|
|
124
|
+
const recipient = "password-reset-user@example.com";
|
|
125
|
+
const resetContent = "We received a request to reset your password.";
|
|
126
|
+
await sendTestEmail({
|
|
127
|
+
host: "127.0.0.1",
|
|
128
|
+
port: smtpPort,
|
|
129
|
+
to: recipient,
|
|
130
|
+
subject: "Reset your password",
|
|
131
|
+
// A decoy link comes first - proves ExpectedLinkPattern actually
|
|
132
|
+
// discriminates between two links in the body, not just "the first one".
|
|
133
|
+
html: `<p>${resetContent}</p>
|
|
134
|
+
<a href="https://example.com/report-abuse?ref=xyz">Not you? Report this</a>
|
|
135
|
+
<a href="${HOME_URL}?verified=1">Reset your password</a>`,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const mem = new MemPage();
|
|
139
|
+
mem.set(ExpectedRecipient({ email: recipient }));
|
|
140
|
+
mem.set(ExpectedLinkPattern, "verified=1");
|
|
141
|
+
mem.set(ExpectedEmailContent, resetContent);
|
|
142
|
+
const result = await mailVerifyFlow.run(context, mem);
|
|
143
|
+
expect(result).toEqual(checkpoint("HomeVerified"));
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("assert-email-received fails loud, naming itself, when no email ever arrives", async ({ context }) => {
|
|
148
|
+
test.skip(!dockerAvailable(), "Docker not reachable in this environment");
|
|
149
|
+
test.setTimeout(30_000);
|
|
150
|
+
|
|
151
|
+
await withMailpit(async () => {
|
|
152
|
+
const mem = new MemPage();
|
|
153
|
+
mem.set(ExpectedRecipient({ email: "nobody-sent-this-one@example.com" }));
|
|
154
|
+
mem.set(ExpectedLinkPattern, "verified=1");
|
|
155
|
+
mem.set(ExpectedEmailContent, VERIFICATION_CONTENT);
|
|
156
|
+
// A defineFlow chain's verify-failure error names the whole path taken so
|
|
157
|
+
// far (each composed Block's own name accumulates through connect()),
|
|
158
|
+
// not just the leaf Block - real, useful QA context, not a bug - so this
|
|
159
|
+
// matches the accumulated name ending at assert-email-received, not an
|
|
160
|
+
// exact "failed after \"assert-email-received\"" alone.
|
|
161
|
+
await expect(mailVerifyFlow.run(context, mem)).rejects.toThrow(
|
|
162
|
+
/trait "email-received" failed after ".*assert-email-received"/,
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("assert-email-content fails loud, naming itself, when the email's copy doesn't match", async ({ context }) => {
|
|
168
|
+
test.skip(!dockerAvailable(), "Docker not reachable in this environment");
|
|
169
|
+
test.setTimeout(60_000);
|
|
170
|
+
|
|
171
|
+
await withMailpit(async ({ smtpPort }) => {
|
|
172
|
+
const recipient = "demo-user@example.com";
|
|
173
|
+
await sendTestEmail({
|
|
174
|
+
host: "127.0.0.1",
|
|
175
|
+
port: smtpPort,
|
|
176
|
+
to: recipient,
|
|
177
|
+
subject: "Please verify your account",
|
|
178
|
+
// A real link is present (so this isolates the content check, not the
|
|
179
|
+
// link-extraction step), but the surrounding copy is wrong.
|
|
180
|
+
html: `<p>Your package has shipped!</p><a href="${HOME_URL}?verified=1">Track it</a>`,
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const mem = new MemPage();
|
|
184
|
+
mem.set(ExpectedRecipient({ email: recipient }));
|
|
185
|
+
mem.set(ExpectedLinkPattern, "verified=1");
|
|
186
|
+
mem.set(ExpectedEmailContent, VERIFICATION_CONTENT);
|
|
187
|
+
await expect(mailVerifyFlow.run(context, mem)).rejects.toThrow(
|
|
188
|
+
/trait "body-contains-expected-text" failed after ".*assert-email-content"/,
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: waygraph-blind-pilot
|
|
3
|
+
description: Cold-start a waygraph project against a real site with zero existing Blocks - navigate with raw primitives, recognize real patterns, and author real Nav/Page/Method Blocks as you go. Use when the target app has no (or very little) waygraph coverage yet, not for driving an already-Block-covered app.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Blind Pilot is the same session mechanism as `waygraph-pilot`, minus the requirement of any
|
|
7
|
+
pre-existing Blocks. `auto --cli --detach` tolerates a project with zero `.block.ts` files -
|
|
8
|
+
`auto click/type/goto/press/upload` act on the real page before any Block exists to cover it.
|
|
9
|
+
This package generates **no Block content for you** - you write real `.block.ts`/`*Sel`/
|
|
10
|
+
mem-key files by hand as you go. That's the whole point: turn one-off exploration into
|
|
11
|
+
permanent, reusable, typed coverage instead of leaving nothing behind.
|
|
12
|
+
|
|
13
|
+
## The real loop
|
|
14
|
+
|
|
15
|
+
1. `auto goto <sessionId> <url>` / `auto click` / `auto type` to explore the real page.
|
|
16
|
+
2. `auto dom <sessionId> --mode full --selector <sel>` to read real structure before writing
|
|
17
|
+
a selector - never guess a selector, confirm it against the real DOM first. `--mode aria`
|
|
18
|
+
is faster for a first pass but can show stale/hidden responsive-duplicate content in some
|
|
19
|
+
real apps (confirmed live: a hidden `lg:hidden` panel's heading text showed up in an aria
|
|
20
|
+
snapshot even when scoped to `main`) - fall back to `--mode full` and check actual CSS
|
|
21
|
+
classes/structure if aria output looks inconsistent with what a screenshot shows.
|
|
22
|
+
3. Once you recognize a real pattern (a login form, a list-item-with-action row, a modal),
|
|
23
|
+
write the real Blocks for it - `*Sel` object first (grounded in the DOM you just read),
|
|
24
|
+
then `Nav`/`Page`/`Method` Blocks, following `templates/scaffold`'s own folder convention
|
|
25
|
+
(`(group)/<page-slug>/page.block.ts` + `nav.block.ts` + `methods/*.block.ts` + one `*.sel.ts`)
|
|
26
|
+
so the result becomes part of the Waygraph Map, not a scattered one-off file.
|
|
27
|
+
4. `auto reload <sessionId>` to pick up the newly-written Block(s) without restarting.
|
|
28
|
+
5. Run the new Block for real via `auto send` - prove it works before moving to the next
|
|
29
|
+
pattern, exactly like any other real Block.
|
|
30
|
+
6. Ask the user a clarifying question only when something genuinely can't be resolved from
|
|
31
|
+
the DOM alone (is this instance local or published? does a downstream integration exist?)
|
|
32
|
+
- don't ask about things `auto dom` can just answer.
|
|
33
|
+
|
|
34
|
+
## Real gotchas specific to cold-start authoring
|
|
35
|
+
|
|
36
|
+
- **`discoverGraph`'s per-file import errors are mostly silent.** A malformed newly-written
|
|
37
|
+
Block file can simply vanish from the graph with no error printed anywhere obvious - if a
|
|
38
|
+
Block you just wrote doesn't show up after `auto reload`, check `waygraph graph`'s own
|
|
39
|
+
`skipped` list and the file's syntax directly, don't assume it just needs more time.
|
|
40
|
+
- **`defineAssertBlock` needs no `<In, Out>` generic to type-check** (`checkpoint: "X"` alone
|
|
41
|
+
is enough for TypeScript to infer it) - but older/naive static-graph discovery logic may
|
|
42
|
+
rely on parsing an explicit generic from source text. If a freshly-written
|
|
43
|
+
`defineAssertBlock` Block is missing from `waygraph graph`'s edges (silently, no error),
|
|
44
|
+
check whether this package's own `graph.ts` has the matching-marker fix for it, or add an
|
|
45
|
+
explicit generic type argument as a workaround.
|
|
46
|
+
- **A `<p>`/text-based selector often needs scoping**, not just a class name - a sibling
|
|
47
|
+
"chat list item" and "message preview" frequently share the exact same CSS classes in real
|
|
48
|
+
apps, distinguished only by DOM position (first vs second child of a specific wrapper).
|
|
49
|
+
Confirm via `auto dom --mode full` before assuming a class alone is unique enough.
|
|
50
|
+
- **Confirm a selector count, not just its first match**, when building anything the menu
|
|
51
|
+
will expand into multiple live rows (an `instanceOptions`-driven Block) - `auto dom`'s
|
|
52
|
+
default read can silently show only one match even when several exist; check the real
|
|
53
|
+
page count (e.g. via a scoped debug script) before assuming a plain `.first()` is safe
|
|
54
|
+
long-term. A `.first()`-shaped Block is a legitimate STARTING point for a page that only
|
|
55
|
+
ever has one real row to act on - promote it to a mem-driven `defineMemNavBlock` /
|
|
56
|
+
`defineEffectBlock` (see the `waygraph-pilot` skill and
|
|
57
|
+
`examples/saucedemo/.../nav-item-detail.block.ts` for the real pattern) once a second real
|
|
58
|
+
row genuinely exists to pick between, not before.
|
|
59
|
+
- **Prefer the real Pilot overlay over ad hoc status output** when driving a
|
|
60
|
+
`--non-headless` session someone might be watching - it auto-updates on every menu build,
|
|
61
|
+
point a human at the badge/panel instead of re-inventing status reporting.
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: waygraph-pilot
|
|
3
|
+
description: Drive an existing waygraph Block graph as a real agent - use when a task needs to operate a web app that already has waygraph Blocks written for it (login, checkout, matching, chat, admin flows, etc.), not when writing new Blocks from scratch.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Waygraph Pilot is not a planner you call and get an answer from - `pilot start` only
|
|
7
|
+
bootstraps a session and hands you the whole project's Block graph as context. **You** are
|
|
8
|
+
the reasoning agent. Every multi-step decision (which Block to run next, when a route needs
|
|
9
|
+
`reach` vs `send`, when to fall back to raw primitives) is yours to make, turn by turn, by
|
|
10
|
+
reading each `auto status`/`auto send` response.
|
|
11
|
+
|
|
12
|
+
## Starting a session
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
waygraph pilot start # bootstraps: {sessionId, socketPath, headless, graph, snapshot}
|
|
16
|
+
waygraph auto status <sessionId> # re-read the live menu, no side effects
|
|
17
|
+
waygraph auto send <sessionId> "<pick>" # run exactly one Block (by menu index or name)
|
|
18
|
+
waygraph auto reach <sessionId> <Checkpoint> # path-find + run a whole route in one call
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`graph` in `pilot start`'s response is the **whole project's** Block/Checkpoint graph, not
|
|
22
|
+
just what's reachable from here - use it to plan several steps ahead before you start
|
|
23
|
+
sending picks.
|
|
24
|
+
|
|
25
|
+
## Rule 1: run real Blocks, not raw primitives
|
|
26
|
+
|
|
27
|
+
Default action is always `auto send`/`auto reach` (a real Block). `auto dom/click/type/
|
|
28
|
+
goto/press/upload` are for genuine gaps only - a page no Block covers yet. Before reaching
|
|
29
|
+
for a raw primitive, check `auto status`'s own menu: if a Block already does this, use it.
|
|
30
|
+
A raw click finishes the moment's task but leaves nothing typed, reusable, or re-runnable -
|
|
31
|
+
the entire reason this package exists. If a needed action truly has no Block, write one
|
|
32
|
+
properly (mirroring a sibling Block's shape) and `auto reload` it in rather than raw-clicking
|
|
33
|
+
past the gap.
|
|
34
|
+
|
|
35
|
+
The one legitimate exception: `auto dom` to resolve a real ambiguity the menu's own labels
|
|
36
|
+
can't (e.g. two products tied at the same displayed price) - and say so explicitly when you
|
|
37
|
+
do it.
|
|
38
|
+
|
|
39
|
+
## Rule 2: `resync` fixes a stale position, but has its own real limit
|
|
40
|
+
|
|
41
|
+
`auto resync <sessionId>` forces `here` to be re-detected from the live page right now -
|
|
42
|
+
use it whenever something outside this session might have changed the page (a human
|
|
43
|
+
co-driving a visible `--non-headless` session is the real, common case). But `resync` can
|
|
44
|
+
only ever be as good as the project's own Block coverage: if two Checkpoints share a route
|
|
45
|
+
and nothing in their `verify` arrays tells them apart, `resync` will confidently report the
|
|
46
|
+
wrong one. This is a coverage gap in the project's Blocks, not a `resync` bug - if you hit
|
|
47
|
+
one, fix the ambiguity (add a second, concrete `Trait.visible` condition that's only true in
|
|
48
|
+
one of the two states - see `AppHomePageBlock`/`InPoolPageBlock` in a real veciro-waygraph
|
|
49
|
+
example of exactly this: the same `/app` route, split by presence of a "Join Pool" vs
|
|
50
|
+
"Leave pool" button).
|
|
51
|
+
|
|
52
|
+
## Rule 3: mem is intent, Checkpoint is reality - never blur them
|
|
53
|
+
|
|
54
|
+
Checkpoint is *designed* to be DOM-derivable (that's what `resync` does) - when in doubt,
|
|
55
|
+
trust the DOM for "where am I." Mem is different: it holds the agent's own intent or
|
|
56
|
+
expectation (which row to act on next, what value a later assertion should find) - it must
|
|
57
|
+
never be inferred from the DOM, because doing so breaks assertion Blocks' entire ability to
|
|
58
|
+
catch a real app bug (mem states what's expected, DOM states what's real, `verify` compares
|
|
59
|
+
them - if mem always mirrored the DOM, a genuine bug would just silently agree with itself).
|
|
60
|
+
A Block with an `instanceOptions` re-asserts its own mem input fresh every time it's picked
|
|
61
|
+
from a live menu; a Block without one has no such protection - if something outside your
|
|
62
|
+
control (a human click) might have interfered, re-supply mem explicitly before the next
|
|
63
|
+
step rather than trusting stale state.
|
|
64
|
+
|
|
65
|
+
## Rule 4: a transition Block needs its OWN `verify`
|
|
66
|
+
|
|
67
|
+
A real bug this exact lesson comes from: a login Block's `act()` (click submit) succeeded
|
|
68
|
+
instantly, but the real backend call failed a moment later (401, unverified email) - well
|
|
69
|
+
after `resolve()` had already optimistically claimed "AppHome." The destination
|
|
70
|
+
Checkpoint's own PageBlock `verify` never ran, because a Block's `verify` is its own, never
|
|
71
|
+
inherited from another Block that merely resolves to the same tag. Without an explicit
|
|
72
|
+
`verify: [Trait.visible(...)]` on the transition Block itself, a real failure silently
|
|
73
|
+
self-reports success instead of failing loud. Any Block whose `act()` can plausibly fail
|
|
74
|
+
server-side after a client-side click needs its own verify - don't assume the destination
|
|
75
|
+
Checkpoint's PageBlock covers it.
|
|
76
|
+
|
|
77
|
+
## Rule 5: `defineAssertBlock` for "already there, assert a fact" checks
|
|
78
|
+
|
|
79
|
+
The majority shape of real QA/verification: you're already logged in, already at some
|
|
80
|
+
Checkpoint, and need to assert something is true. `defineAssertBlock({ name, checkpoint,
|
|
81
|
+
verify })` is sugar for exactly this - no hand-written act/resolve. For a check whose target
|
|
82
|
+
varies per run (assert a specific matched user's name is present, not a fixed string), hand-
|
|
83
|
+
write a `Trait` object directly instead of the `Trait.visible`/`text` factories - `Trait`'s
|
|
84
|
+
own shape is `{ name, check(page, mem) }`, so a plain object literal gets real mem access a
|
|
85
|
+
factory-built Trait can't. Gate it with `requires: [SomeKey]` so it's correctly hidden from
|
|
86
|
+
the live menu until that mem is actually populated, rather than appearing early and failing
|
|
87
|
+
confusingly.
|
|
88
|
+
|
|
89
|
+
## Known real environment gotchas
|
|
90
|
+
|
|
91
|
+
- **`auto reload`** picks up an edited/new consumer Block file mid-session, no restart
|
|
92
|
+
needed. It does NOT help if the waygraph *package's own source* changed (a detached
|
|
93
|
+
session process never re-imports its own already-loaded dependencies) - that needs a
|
|
94
|
+
full session restart. It also doesn't help for a non-`.block.ts` file (a `.sel.ts`, a
|
|
95
|
+
shared selector/type file) that some *other* already-loaded Block file imported earlier
|
|
96
|
+
in the session's life - ESM caches that import too, and only the top-level `.block.ts`
|
|
97
|
+
file gets cache-busted on reload. When in doubt after editing a shared file, restart.
|
|
98
|
+
- **HeadlessUI-style popovers** (or similar open/close transitions) sometimes aren't
|
|
99
|
+
interactable the instant their trigger is clicked - a real, reproducible flake, not
|
|
100
|
+
paranoia. A short `page.waitForTimeout(300-400)` between opening a popover and acting
|
|
101
|
+
inside it is a real, load-bearing fix seen live more than once, not defensive padding.
|
|
102
|
+
- **Orphan Blocks** (not wired into any `.flow.ts`) are still fully visible and runnable in
|
|
103
|
+
the live Pilot menu - orphan status only blocks `auto --blocks <From> <To>` pathfinding,
|
|
104
|
+
never `auto`'s own interactive menu.
|
|
105
|
+
- A hard `page.goto()` straight to an SPA route can render a stale/incomplete view compared
|
|
106
|
+
to reaching the same page via a real in-app click-nav - if `auto dom` output looks wrong
|
|
107
|
+
right after a raw `goto`, try reaching the page through its real click-based NavBlock
|
|
108
|
+
instead before assuming the DOM read is broken.
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# Sauce Demo - convention showcase
|
|
2
|
-
|
|
3
|
-
Full live consumer for [saucedemo.com](https://www.saucedemo.com). This tree is
|
|
4
|
-
the **reference** for waygraph consumer conventions:
|
|
5
|
-
|
|
6
|
-
- Route folders under `src/blocks/saucedemo-web/` = real Swag Labs URLs
|
|
7
|
-
- **Nav** / **Effect** / **Action** file kinds (helpers are TypeScript salt;
|
|
8
|
-
runtime is still `Block`)
|
|
9
|
-
- `instanceOptions` menus for `waygraph auto` (Add/Remove + Open details)
|
|
10
|
-
- Detailed Kind / Helper / Route comments on every Block file
|
|
11
|
-
- Per-route `NAV.md` + root `src/blocks/SITE-MAP.md`
|
|
12
|
-
|
|
13
|
-
**GitHub Pages walkthrough:**
|
|
14
|
-
[docs/saucedemo](https://deviate-dv8.github.io/waygraph/saucedemo/)
|
|
15
|
-
(`docs/saucedemo/` in this repo - README, SITE-MAP, kinds, layout, auto, comments).
|
|
16
|
-
|
|
17
|
-
`waygraph try demo` copies the sibling template at `templates/quickstart`
|
|
18
|
-
(same Blocks) into an OS temp dir.
|
|
19
|
-
|
|
20
|
-
## Setup
|
|
21
|
-
|
|
22
|
-
From the waygraph package root (after `npm run build`):
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
cd examples/saucedemo
|
|
26
|
-
npm install
|
|
27
|
-
npx playwright install chromium
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Commands
|
|
31
|
-
|
|
32
|
-
| Want | Command |
|
|
33
|
-
|------|---------|
|
|
34
|
-
| Headless suite | `npm test` |
|
|
35
|
-
| Interactive explore (headed panel) | `npm run auto` |
|
|
36
|
-
| Interactive explore (terminal menu) | `npm run auto:cli` |
|
|
37
|
-
| Shop demo stepper | `npm run demo:step` |
|
|
38
|
-
| Shop demo autoplay | `npm run demo:autoplay` |
|
|
39
|
-
| **Cart bulk** demo (named flow) | `npm run demo:bulk` |
|
|
40
|
-
| Cart bulk stepper | `npm run demo:bulk:step` |
|
|
41
|
-
| **Cart bulk** chain (Block names only) | `npm run chain:bulk` |
|
|
42
|
-
| List flows | `npm run flows` |
|
|
43
|
-
|
|
44
|
-
**Demo vs auto vs chain for cart bulk**
|
|
45
|
-
|
|
46
|
-
| | CLI | Needs `.flow.ts`? |
|
|
47
|
-
|--|-----|-------------------|
|
|
48
|
-
| **`chain` (block names)** | `npm run chain:bulk` → `loginFlow({…}) then add-all-to-cart then remove-all-from-cart` | No — Block `.name` only |
|
|
49
|
-
| **`demo` (named flow)** | `npm run demo:bulk` → `cartBulkFlow` | Yes — `cart-bulk.flow.ts` |
|
|
50
|
-
| **`auto` (pick live)** | `npm run auto` / `auto:cli` | No — menu from graph |
|
|
51
|
-
|
|
52
|
-
Same Blocks either way. `chain` is the ad-hoc path (handout-style `"A then B then C"`).
|
|
53
|
-
|
|
54
|
-
## Layout (short)
|
|
55
|
-
|
|
56
|
-
```text
|
|
57
|
-
src/blocks/
|
|
58
|
-
SITE-MAP.md
|
|
59
|
-
saucedemo-web/ # URL /
|
|
60
|
-
NAV.md
|
|
61
|
-
nav-login.block.ts
|
|
62
|
-
methods/ # submit-login, logout, …
|
|
63
|
-
inventory/ # URL /inventory.html
|
|
64
|
-
nav-item-detail.block.ts # MemNav
|
|
65
|
-
methods/*.effect.block.ts # Add / Remove
|
|
66
|
-
cart/ # URL /cart.html
|
|
67
|
-
checkout-step-one/
|
|
68
|
-
checkout-step-two/
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Docs: [Sauce Demo showcase](../../docs/saucedemo/) ·
|
|
72
|
-
[Auto explore](../../docs/auto.html) · [Consumer layout](../../docs/consumer.html)
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# Site map (scaffold)
|
|
2
|
-
|
|
3
|
-
Offline catalog uses one synthetic namespace (`demo-web/`) and a local
|
|
4
|
-
`home.html` fixture so `npm test` / `waygraph auto` need no network.
|
|
5
|
-
|
|
6
|
-
| Folder | URL | Notes |
|
|
7
|
-
|--------|-----|-------|
|
|
8
|
-
| `demo-web/` | `/` (synthetic) | Nav + Page hub + methods/effects |
|
|
9
|
-
| `demo-web/methods/` | same URL | assert, add/remove Effects, clear |
|
|
10
|
-
|
|
11
|
-
When you wire a real app, rename `demo-web/` to your FE origin namespace and
|
|
12
|
-
add one folder per real `page.tsx` route. See **Consumer layout** docs.
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# demo-web / (synthetic home)
|
|
2
|
-
|
|
3
|
-
**URL:** `http://127.0.0.1:4177/home.html` (offline catalog via `scripts/fixture-server.mjs` - replace with your real `/`)
|
|
4
|
-
|
|
5
|
-
## Nav-to
|
|
6
|
-
|
|
7
|
-
| From | Block | How |
|
|
8
|
-
|------|-------|-----|
|
|
9
|
-
| start | `nav-home` | `defineNavBlock` `url` → home.html |
|
|
10
|
-
|
|
11
|
-
## Page hub
|
|
12
|
-
|
|
13
|
-
| Block | Checkpoint | Methods |
|
|
14
|
-
|-------|------------|---------|
|
|
15
|
-
| `page-home` | `Home` | add-item, remove-item, clear-cart, assert-hello |
|
|
16
|
-
|
|
17
|
-
## Methods / Effects
|
|
18
|
-
|
|
19
|
-
| Block | Kind | Auto |
|
|
20
|
-
|-------|------|------|
|
|
21
|
-
| `assert-hello` | Method | single row |
|
|
22
|
-
| `add-item` | Effect | one row per live Add button (`instanceOptions`) |
|
|
23
|
-
| `remove-item` | Effect | one row per live Remove button |
|
|
24
|
-
| `clear-cart` | Method | single row |
|
|
25
|
-
|
|
26
|
-
## States / mem-keys
|
|
27
|
-
|
|
28
|
-
- Checkpoints: `Home`, `HomeVerified`, `ItemInCart`, `CartEmpty`
|
|
29
|
-
- Mem: `demo.selectedItem` (`SelectedItem`) - filled by auto or `--data`
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { defineMethodBlock, checkpoint, Trait } from "waygraph";
|
|
2
|
-
import { DemoSel } from "../demo-sel.js";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Kind: Method
|
|
6
|
-
* Helper: defineMethodBlock
|
|
7
|
-
* Route: demo-web/methods/
|
|
8
|
-
*
|
|
9
|
-
* Same-URL assert after nav - stubs + fixtures + YAP slide.
|
|
10
|
-
*/
|
|
11
|
-
export const AssertHelloBlock = defineMethodBlock({
|
|
12
|
-
name: "assert-hello",
|
|
13
|
-
description: "Confirms the hello heading is still visible after arrival.",
|
|
14
|
-
instruction: {
|
|
15
|
-
async act(_page) {},
|
|
16
|
-
async observe(page) {
|
|
17
|
-
await page.locator(DemoSel.title).first().waitFor({ state: "visible", timeout: 5_000 });
|
|
18
|
-
return "ok" as const;
|
|
19
|
-
},
|
|
20
|
-
resolve: () => checkpoint("HomeVerified"),
|
|
21
|
-
verify: [Trait.text(DemoSel.title, "Hello Waygraph"), Trait.visible(DemoSel.sub)],
|
|
22
|
-
stubBefore: {
|
|
23
|
-
title: { selector: DemoSel.title, label: "Heading about to check" },
|
|
24
|
-
},
|
|
25
|
-
stubAfter: {
|
|
26
|
-
title: {
|
|
27
|
-
selector: DemoSel.title,
|
|
28
|
-
label: "Hello confirmed",
|
|
29
|
-
detail: "Offline scaffold proof point.",
|
|
30
|
-
duration: true,
|
|
31
|
-
},
|
|
32
|
-
sub: { selector: DemoSel.sub, label: "Subcopy", duration: 1500 },
|
|
33
|
-
},
|
|
34
|
-
stubOnError: {
|
|
35
|
-
title: {
|
|
36
|
-
selector: DemoSel.title,
|
|
37
|
-
label: "Heading at failure",
|
|
38
|
-
tag: "FAIL",
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
slides: [
|
|
42
|
-
{
|
|
43
|
-
caption: "Scaffold covers auto + narration",
|
|
44
|
-
detail: "Page hub, Effect instanceOptions, stubs, fixtures, YAP.",
|
|
45
|
-
tag: "YAP",
|
|
46
|
-
duration: true,
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
});
|