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.
Files changed (358) hide show
  1. package/README.md +970 -232
  2. package/assets/stubs/stub.mp4 +0 -0
  3. package/assets/stubs/stub.pdf +32 -0
  4. package/assets/stubs/stub.png +0 -0
  5. package/dist/agent-dive.d.ts +9 -0
  6. package/dist/agent-dive.js +32 -0
  7. package/dist/auto-explore-run.d.ts +20 -2
  8. package/dist/auto-explore-run.js +9 -8
  9. package/dist/auto-explore.js +22 -2
  10. package/dist/auto-session-ipc.d.ts +123 -0
  11. package/dist/auto-session-ipc.js +381 -0
  12. package/dist/auto-session.d.ts +342 -0
  13. package/dist/auto-session.js +760 -0
  14. package/dist/cli.js +450 -51
  15. package/dist/engine.d.ts +201 -1
  16. package/dist/engine.js +312 -2
  17. package/dist/graph.d.ts +18 -0
  18. package/dist/graph.js +65 -2
  19. package/dist/highlights.d.ts +20 -1
  20. package/dist/highlights.js +60 -0
  21. package/dist/index.d.ts +8 -4
  22. package/dist/index.js +5 -3
  23. package/dist/map-check.d.ts +18 -0
  24. package/dist/map-check.js +140 -0
  25. package/dist/pilot-overlay.d.ts +88 -0
  26. package/dist/pilot-overlay.js +363 -0
  27. package/dist/pilot.d.ts +49 -0
  28. package/dist/pilot.js +53 -0
  29. package/dist/trait.d.ts +22 -0
  30. package/dist/trait.js +60 -3
  31. package/dist/types.d.ts +21 -1
  32. package/dist/types.js +17 -1
  33. package/examples/routed-demo/.waygraph-auto/4d4902b7.log +0 -0
  34. package/examples/routed-demo/.waygraph-auto/51ef462e.log +0 -0
  35. package/examples/routed-demo/.waygraph-auto/71527ad9.log +0 -0
  36. package/examples/routed-demo/.waygraph-auto/ca16778e.log +0 -0
  37. package/examples/routed-demo/package.json +26 -0
  38. package/examples/routed-demo/playwright.config.ts +20 -0
  39. package/examples/routed-demo/scripts/fixture-server.mjs +37 -0
  40. package/examples/routed-demo/scripts/with-fixture.mjs +70 -0
  41. package/examples/routed-demo/src/flows/routed-demo-map.flow.ts +39 -0
  42. package/examples/routed-demo/src/flows/routed-demo.flow.ts +12 -0
  43. package/examples/routed-demo/src/map/(app_base)/dashboard/_methods/click-widget.block.ts +23 -0
  44. package/examples/routed-demo/src/map/(app_base)/dashboard/_nav.block.ts +18 -0
  45. package/examples/routed-demo/src/map/(app_base)/dashboard/_page.block.ts +22 -0
  46. package/examples/routed-demo/src/map/(app_base)/dashboard/_sel.ts +10 -0
  47. package/examples/routed-demo/src/map/(external)/docs/_nav.block.ts +21 -0
  48. package/examples/routed-demo/src/map/(external)/docs/_page.block.ts +15 -0
  49. package/examples/routed-demo/src/map/(external)/docs/_sel.ts +7 -0
  50. package/examples/routed-demo/src/pages/dashboard.html +10 -0
  51. package/examples/routed-demo/src/pages/docs.html +8 -0
  52. package/examples/routed-demo/src/states/routed-demo.states.ts +10 -0
  53. package/examples/routed-demo/test-results/.last-run.json +4 -0
  54. package/examples/routed-demo/tests/routed-demo-map.spec.ts +17 -0
  55. package/examples/routed-demo/tests/routed-demo.spec.ts +75 -0
  56. package/examples/routed-demo/tsconfig.json +18 -0
  57. package/examples/routed-demo-consumer/package.json +18 -0
  58. package/examples/saucedemo/.waygraph-auto/00d5336e.log +0 -0
  59. package/examples/saucedemo/.waygraph-auto/010dd92b.log +0 -0
  60. package/examples/saucedemo/.waygraph-auto/011ad671.log +0 -0
  61. package/examples/saucedemo/.waygraph-auto/01b45645.log +0 -0
  62. package/examples/saucedemo/.waygraph-auto/03764153.log +0 -0
  63. package/examples/saucedemo/.waygraph-auto/0593587e.log +0 -0
  64. package/examples/saucedemo/.waygraph-auto/06d12823.log +0 -0
  65. package/examples/saucedemo/.waygraph-auto/083cc5b9.log +0 -0
  66. package/examples/saucedemo/.waygraph-auto/0b1f5b26.json +8 -0
  67. package/examples/saucedemo/.waygraph-auto/0c00f25f.log +0 -0
  68. package/examples/saucedemo/.waygraph-auto/0e480591.log +0 -0
  69. package/examples/saucedemo/.waygraph-auto/104751ef.log +0 -0
  70. package/examples/saucedemo/.waygraph-auto/110958b8.log +0 -0
  71. package/examples/saucedemo/.waygraph-auto/138107c1.log +0 -0
  72. package/examples/saucedemo/.waygraph-auto/159d541b.log +0 -0
  73. package/examples/saucedemo/.waygraph-auto/16aa2c01.log +0 -0
  74. package/examples/saucedemo/.waygraph-auto/16c35dd5.log +0 -0
  75. package/examples/saucedemo/.waygraph-auto/1706547d.log +0 -0
  76. package/examples/saucedemo/.waygraph-auto/17537e42.log +0 -0
  77. package/examples/saucedemo/.waygraph-auto/175655f7.log +0 -0
  78. package/examples/saucedemo/.waygraph-auto/17ede493.log +0 -0
  79. package/examples/saucedemo/.waygraph-auto/1fe2414f.log +0 -0
  80. package/examples/saucedemo/.waygraph-auto/1fe2e3b8.log +0 -0
  81. package/examples/saucedemo/.waygraph-auto/2219f00b.log +0 -0
  82. package/examples/saucedemo/.waygraph-auto/23e62571.log +0 -0
  83. package/examples/saucedemo/.waygraph-auto/25f68ada.log +0 -0
  84. package/examples/saucedemo/.waygraph-auto/26ca9cd8.log +0 -0
  85. package/examples/saucedemo/.waygraph-auto/2bb6c4e8.log +0 -0
  86. package/examples/saucedemo/.waygraph-auto/2d425004.log +0 -0
  87. package/examples/saucedemo/.waygraph-auto/2f5b3ea6.log +0 -0
  88. package/examples/saucedemo/.waygraph-auto/31bb303d.log +0 -0
  89. package/examples/saucedemo/.waygraph-auto/31f2042f.log +0 -0
  90. package/examples/saucedemo/.waygraph-auto/36551ec1.log +0 -0
  91. package/examples/saucedemo/.waygraph-auto/36a1c274.log +0 -0
  92. package/examples/saucedemo/.waygraph-auto/381aa338.log +0 -0
  93. package/examples/saucedemo/.waygraph-auto/3882bce1.log +0 -0
  94. package/examples/saucedemo/.waygraph-auto/38acd1dc.log +0 -0
  95. package/examples/saucedemo/.waygraph-auto/3b8b77b9.log +0 -0
  96. package/examples/saucedemo/.waygraph-auto/4028d2a2.log +0 -0
  97. package/examples/saucedemo/.waygraph-auto/4168b208.log +0 -0
  98. package/examples/saucedemo/.waygraph-auto/42cc9113.log +0 -0
  99. package/examples/saucedemo/.waygraph-auto/439d30aa.log +0 -0
  100. package/examples/saucedemo/.waygraph-auto/43ab66ce.log +0 -0
  101. package/examples/saucedemo/.waygraph-auto/43f66838.log +0 -0
  102. package/examples/saucedemo/.waygraph-auto/442171d4.log +0 -0
  103. package/examples/saucedemo/.waygraph-auto/453b1ce1.log +0 -0
  104. package/examples/saucedemo/.waygraph-auto/457322bd.log +0 -0
  105. package/examples/saucedemo/.waygraph-auto/46197027.log +0 -0
  106. package/examples/saucedemo/.waygraph-auto/46889468.log +0 -0
  107. package/examples/saucedemo/.waygraph-auto/4890da1c.log +0 -0
  108. package/examples/saucedemo/.waygraph-auto/4893a63b.log +0 -0
  109. package/examples/saucedemo/.waygraph-auto/4a85f496.log +0 -0
  110. package/examples/saucedemo/.waygraph-auto/4af71440.log +0 -0
  111. package/examples/saucedemo/.waygraph-auto/4f7496d1.log +0 -0
  112. package/examples/saucedemo/.waygraph-auto/50e8baaa.log +0 -0
  113. package/examples/saucedemo/.waygraph-auto/52b3a455.log +0 -0
  114. package/examples/saucedemo/.waygraph-auto/532dd9b8.log +0 -0
  115. package/examples/saucedemo/.waygraph-auto/5391ce40.log +0 -0
  116. package/examples/saucedemo/.waygraph-auto/57dbddb3.log +0 -0
  117. package/examples/saucedemo/.waygraph-auto/5897f501.log +0 -0
  118. package/examples/saucedemo/.waygraph-auto/59c0b57d.log +0 -0
  119. package/examples/saucedemo/.waygraph-auto/5b511794.log +0 -0
  120. package/examples/saucedemo/.waygraph-auto/5c0eef7f.log +0 -0
  121. package/examples/saucedemo/.waygraph-auto/5c47910f.log +0 -0
  122. package/examples/saucedemo/.waygraph-auto/5d60da77.log +0 -0
  123. package/examples/saucedemo/.waygraph-auto/5eda8273.log +0 -0
  124. package/examples/saucedemo/.waygraph-auto/5fc3a33b.log +0 -0
  125. package/examples/saucedemo/.waygraph-auto/61b6546d.log +0 -0
  126. package/examples/saucedemo/.waygraph-auto/621518ad.log +0 -0
  127. package/examples/saucedemo/.waygraph-auto/632b7118.log +0 -0
  128. package/examples/saucedemo/.waygraph-auto/64314c2b.log +0 -0
  129. package/examples/saucedemo/.waygraph-auto/650ed066.log +0 -0
  130. package/examples/saucedemo/.waygraph-auto/66516213.log +0 -0
  131. package/examples/saucedemo/.waygraph-auto/66c17079.log +0 -0
  132. package/examples/saucedemo/.waygraph-auto/69bf0b80.log +0 -0
  133. package/examples/saucedemo/.waygraph-auto/69c2347a.log +0 -0
  134. package/examples/saucedemo/.waygraph-auto/6d22bdb0.log +0 -0
  135. package/examples/saucedemo/.waygraph-auto/6d9aaad3.log +0 -0
  136. package/examples/saucedemo/.waygraph-auto/7103bebb.log +0 -0
  137. package/examples/saucedemo/.waygraph-auto/740c9faa.log +0 -0
  138. package/examples/saucedemo/.waygraph-auto/74872fc5.log +0 -0
  139. package/examples/saucedemo/.waygraph-auto/75846ca9.log +0 -0
  140. package/examples/saucedemo/.waygraph-auto/7626c85b.log +0 -0
  141. package/examples/saucedemo/.waygraph-auto/76308eec.log +0 -0
  142. package/examples/saucedemo/.waygraph-auto/76c1650e.log +0 -0
  143. package/examples/saucedemo/.waygraph-auto/7861f38f.log +0 -0
  144. package/examples/saucedemo/.waygraph-auto/78b21219.log +0 -0
  145. package/examples/saucedemo/.waygraph-auto/7af224ed.log +0 -0
  146. package/examples/saucedemo/.waygraph-auto/7c3be585.log +0 -0
  147. package/examples/saucedemo/.waygraph-auto/7ed55a2c.log +0 -0
  148. package/examples/saucedemo/.waygraph-auto/80086ba2.log +0 -0
  149. package/examples/saucedemo/.waygraph-auto/80206369.log +0 -0
  150. package/examples/saucedemo/.waygraph-auto/847f1092.log +0 -0
  151. package/examples/saucedemo/.waygraph-auto/84d6d293.log +0 -0
  152. package/examples/saucedemo/.waygraph-auto/8581abfa.log +0 -0
  153. package/examples/saucedemo/.waygraph-auto/87b7e0fd.log +0 -0
  154. package/examples/saucedemo/.waygraph-auto/87c79256.log +0 -0
  155. package/examples/saucedemo/.waygraph-auto/88af2495.log +0 -0
  156. package/examples/saucedemo/.waygraph-auto/8a3eb088.log +0 -0
  157. package/examples/saucedemo/.waygraph-auto/8c04a403.log +0 -0
  158. package/examples/saucedemo/.waygraph-auto/8c9ab09e.log +0 -0
  159. package/examples/saucedemo/.waygraph-auto/8cc5db51.log +0 -0
  160. package/examples/saucedemo/.waygraph-auto/8ea3a69c.log +0 -0
  161. package/examples/saucedemo/.waygraph-auto/9150f913.log +0 -0
  162. package/examples/saucedemo/.waygraph-auto/929a4313.log +0 -0
  163. package/examples/saucedemo/.waygraph-auto/95a599e4.log +0 -0
  164. package/examples/saucedemo/.waygraph-auto/96c45350.log +0 -0
  165. package/examples/saucedemo/.waygraph-auto/986030e6.log +0 -0
  166. package/examples/saucedemo/.waygraph-auto/99787abb.log +0 -0
  167. package/examples/saucedemo/.waygraph-auto/99ddb62c.log +0 -0
  168. package/examples/saucedemo/.waygraph-auto/a2dd30dd.log +0 -0
  169. package/examples/saucedemo/.waygraph-auto/a63a2d20.log +0 -0
  170. package/examples/saucedemo/.waygraph-auto/a78befb7.log +0 -0
  171. package/examples/saucedemo/.waygraph-auto/a84d9b79.log +0 -0
  172. package/examples/saucedemo/.waygraph-auto/a87bb3ee.log +0 -0
  173. package/examples/saucedemo/.waygraph-auto/a9270890.log +0 -0
  174. package/examples/saucedemo/.waygraph-auto/aa440153.log +0 -0
  175. package/examples/saucedemo/.waygraph-auto/ab7561e0.log +0 -0
  176. package/examples/saucedemo/.waygraph-auto/ac19ae60.log +0 -0
  177. package/examples/saucedemo/.waygraph-auto/ae21ad8e.log +0 -0
  178. package/examples/saucedemo/.waygraph-auto/af09042f.log +0 -0
  179. package/examples/saucedemo/.waygraph-auto/b1684126.log +0 -0
  180. package/examples/saucedemo/.waygraph-auto/b21e9d12.log +0 -0
  181. package/examples/saucedemo/.waygraph-auto/b5a838be.log +0 -0
  182. package/examples/saucedemo/.waygraph-auto/b603549d.log +0 -0
  183. package/examples/saucedemo/.waygraph-auto/b81e074f.log +0 -0
  184. package/examples/saucedemo/.waygraph-auto/b83e6259.log +0 -0
  185. package/examples/saucedemo/.waygraph-auto/b9cb2796.log +0 -0
  186. package/examples/saucedemo/.waygraph-auto/ba58abb7.log +0 -0
  187. package/examples/saucedemo/.waygraph-auto/bb0a0018.log +0 -0
  188. package/examples/saucedemo/.waygraph-auto/bb9fd384.log +0 -0
  189. package/examples/saucedemo/.waygraph-auto/bc0bdd0e.log +0 -0
  190. package/examples/saucedemo/.waygraph-auto/bc7d4a83.log +0 -0
  191. package/examples/saucedemo/.waygraph-auto/bd8e082a.log +0 -0
  192. package/examples/saucedemo/.waygraph-auto/bea0c93e.log +0 -0
  193. package/examples/saucedemo/.waygraph-auto/bf35201a.log +0 -0
  194. package/examples/saucedemo/.waygraph-auto/bffa3a7a.log +0 -0
  195. package/examples/saucedemo/.waygraph-auto/c02ea583.log +0 -0
  196. package/examples/saucedemo/.waygraph-auto/c2caf0a8.log +0 -0
  197. package/examples/saucedemo/.waygraph-auto/c3bdc714.log +0 -0
  198. package/examples/saucedemo/.waygraph-auto/c4afb8bc.log +0 -0
  199. package/examples/saucedemo/.waygraph-auto/c916a654.log +0 -0
  200. package/examples/saucedemo/.waygraph-auto/c9b8d69e.log +0 -0
  201. package/examples/saucedemo/.waygraph-auto/cd68bbc4.log +0 -0
  202. package/examples/saucedemo/.waygraph-auto/cdc6db50.log +0 -0
  203. package/examples/saucedemo/.waygraph-auto/d01906a7.log +0 -0
  204. package/examples/saucedemo/.waygraph-auto/d02e5e7e.log +0 -0
  205. package/examples/saucedemo/.waygraph-auto/d09aff71.log +0 -0
  206. package/examples/saucedemo/.waygraph-auto/d0e41396.log +0 -0
  207. package/examples/saucedemo/.waygraph-auto/d1370d76.log +0 -0
  208. package/examples/saucedemo/.waygraph-auto/d3714ae4.log +0 -0
  209. package/examples/saucedemo/.waygraph-auto/d9e5f8a4.log +0 -0
  210. package/examples/saucedemo/.waygraph-auto/da0685dc.log +0 -0
  211. package/examples/saucedemo/.waygraph-auto/da894ad8.log +0 -0
  212. package/examples/saucedemo/.waygraph-auto/daa2f922.log +0 -0
  213. package/examples/saucedemo/.waygraph-auto/daefb614.log +0 -0
  214. package/examples/saucedemo/.waygraph-auto/db97e833.log +0 -0
  215. package/examples/saucedemo/.waygraph-auto/dec3076e.log +0 -0
  216. package/examples/saucedemo/.waygraph-auto/e101d19a.log +0 -0
  217. package/examples/saucedemo/.waygraph-auto/e1973991.log +0 -0
  218. package/examples/saucedemo/.waygraph-auto/e207c989.log +0 -0
  219. package/examples/saucedemo/.waygraph-auto/e633e545.log +0 -0
  220. package/examples/saucedemo/.waygraph-auto/e69aec68.log +0 -0
  221. package/examples/saucedemo/.waygraph-auto/e70e2d06.log +0 -0
  222. package/examples/saucedemo/.waygraph-auto/e8306a4b.log +0 -0
  223. package/examples/saucedemo/.waygraph-auto/ebad3410.log +0 -0
  224. package/examples/saucedemo/.waygraph-auto/ec88b3ef.log +0 -0
  225. package/examples/saucedemo/.waygraph-auto/ecbb0966.log +0 -0
  226. package/examples/saucedemo/.waygraph-auto/ecdee753.log +0 -0
  227. package/examples/saucedemo/.waygraph-auto/ed888783.log +0 -0
  228. package/examples/saucedemo/.waygraph-auto/ed931dac.log +0 -0
  229. package/examples/saucedemo/.waygraph-auto/ef655a3f.log +0 -0
  230. package/examples/saucedemo/.waygraph-auto/f34c9a26.log +0 -0
  231. package/examples/saucedemo/.waygraph-auto/f3c29b21.log +0 -0
  232. package/examples/saucedemo/.waygraph-auto/f53f7cfc.log +0 -0
  233. package/examples/saucedemo/.waygraph-auto/f5525d53.log +0 -0
  234. package/examples/saucedemo/.waygraph-auto/f5ad5830.log +0 -0
  235. package/examples/saucedemo/.waygraph-auto/f90035b3.log +0 -0
  236. package/examples/saucedemo/.waygraph-auto/fa687bc2.log +0 -0
  237. package/examples/saucedemo/.waygraph-auto/faed36dd.log +0 -0
  238. package/examples/saucedemo/.waygraph-auto/fb66fba0.log +0 -0
  239. package/examples/saucedemo/.waygraph-auto/fc48fd76.log +0 -0
  240. package/examples/saucedemo/docs/CONVENTION.md +25 -5
  241. package/examples/saucedemo/docs/HELPERS.md +15 -4
  242. package/examples/saucedemo/scripts/capture-wrong-login.mjs +8 -2
  243. package/examples/saucedemo/scripts/prove-wrong-login-runtime.mjs +8 -2
  244. package/examples/saucedemo/src/blocks/SITE-MAP.md +14 -3
  245. package/examples/saucedemo/src/blocks/saucedemo-web/NAV.md +15 -1
  246. package/examples/saucedemo/src/blocks/saucedemo-web/cart/NAV.md +1 -0
  247. package/examples/saucedemo/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
  248. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-cart.block.ts +3 -2
  249. package/examples/saucedemo/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
  250. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
  251. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
  252. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
  253. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
  254. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
  255. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
  256. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
  257. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
  258. package/examples/saucedemo/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
  259. package/examples/saucedemo/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
  260. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
  261. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
  262. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
  263. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
  264. package/examples/saucedemo/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
  265. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
  266. package/examples/saucedemo/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
  267. package/examples/saucedemo/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
  268. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
  269. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +13 -51
  270. package/examples/saucedemo/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
  271. package/examples/saucedemo/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
  272. package/examples/saucedemo/src/flows/checkout-then-browse-again.flow.ts +15 -0
  273. package/examples/saucedemo/src/flows/checkout.flow.ts +20 -2
  274. package/examples/saucedemo/src/flows/device-showcase.flow.ts +4 -0
  275. package/examples/saucedemo/src/flows/login.flow.ts +3 -1
  276. package/examples/saucedemo/src/flows/shop.flow.ts +6 -0
  277. package/examples/saucedemo/src/flows/smoke.flow.ts +13 -7
  278. package/examples/saucedemo/src/flows/stub-on-error.flow.ts +4 -2
  279. package/examples/saucedemo/src/flows/viewer-blocked.flow.ts +6 -2
  280. package/examples/saucedemo/tests/flow-block-verify.spec.ts +3 -3
  281. package/examples/saucedemo/tests/submit-login-branch.spec.ts +5 -3
  282. package/package.json +2 -1
  283. package/templates/agents/waygraph-author.agent.md +43 -5
  284. package/templates/agents/waygraph-planner.agent.md +33 -2
  285. package/templates/quickstart/src/blocks/saucedemo-web/NAV.md +15 -1
  286. package/templates/quickstart/src/blocks/saucedemo-web/cart/NAV.md +1 -0
  287. package/templates/quickstart/src/blocks/saucedemo-web/cart/cart.sel.ts +9 -0
  288. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-cart.block.ts +19 -15
  289. package/templates/quickstart/src/blocks/saucedemo-web/cart/nav-checkout-info.block.ts +2 -1
  290. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/NAV.md +6 -1
  291. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/checkout-info.sel.ts +11 -0
  292. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-first-name.method.block.ts +22 -0
  293. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-last-name.method.block.ts +21 -0
  294. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/fill-postal-code.method.block.ts +21 -0
  295. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-one/methods/submit-checkout-info.method.block.ts +6 -7
  296. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/NAV.md +2 -0
  297. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/checkout-overview.sel.ts +9 -0
  298. package/templates/quickstart/src/blocks/saucedemo-web/checkout-step-two/methods/finish-order.method.block.ts +7 -6
  299. package/templates/quickstart/src/blocks/saucedemo-web/ff-owner-auth.block.ts +7 -2
  300. package/templates/quickstart/src/blocks/saucedemo-web/inventory/methods/add-to-cart.effect.block.ts +24 -16
  301. package/templates/quickstart/src/blocks/saucedemo-web/inventory/nav-item-detail.block.ts +3 -3
  302. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/NAV.md +1 -0
  303. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/item-detail.sel.ts +9 -0
  304. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-inventory.block.ts +2 -1
  305. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-back-to-products.block.ts +2 -1
  306. package/templates/quickstart/src/blocks/saucedemo-web/inventory-item/nav-continue-shopping.block.ts +2 -1
  307. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-password.method.block.ts +51 -0
  308. package/templates/quickstart/src/blocks/saucedemo-web/methods/fill-username.method.block.ts +69 -0
  309. package/templates/quickstart/src/blocks/saucedemo-web/methods/login.sel.ts +13 -0
  310. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login-for-flow.ts +4 -4
  311. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-login.method.block.ts +35 -63
  312. package/templates/quickstart/src/blocks/saucedemo-web/methods/submit-logout.method.block.ts +4 -3
  313. package/templates/quickstart/src/blocks/saucedemo-web/nav-login.block.ts +14 -6
  314. package/templates/quickstart/src/flows/checkout-then-browse-again.flow.ts +15 -0
  315. package/templates/quickstart/src/flows/checkout.flow.ts +29 -2
  316. package/templates/quickstart/src/flows/device-showcase.flow.ts +4 -0
  317. package/templates/quickstart/src/flows/login.flow.ts +3 -1
  318. package/templates/quickstart/src/flows/shop.flow.ts +6 -0
  319. package/templates/quickstart/src/flows/smoke.flow.ts +13 -7
  320. package/templates/quickstart/src/flows/viewer-blocked.flow.ts +6 -2
  321. package/templates/quickstart/tests/chain-flow.spec.ts +8 -1
  322. package/templates/scaffold/STRUCTURE.md +23 -3
  323. package/templates/scaffold/scripts/fixture-server.mjs +11 -2
  324. package/templates/scaffold/src/flows/example.flow.ts +8 -6
  325. package/templates/scaffold/src/flows/mail-verify.flow.ts +85 -0
  326. package/templates/scaffold/src/flows/shop.flow.ts +23 -6
  327. package/templates/scaffold/src/map/(app_base)/docs/_nav.block.ts +20 -0
  328. package/templates/scaffold/src/map/(app_base)/docs/_page.block.ts +15 -0
  329. package/templates/scaffold/src/map/(app_base)/docs/_sel.ts +7 -0
  330. package/templates/scaffold/src/map/(app_base)/docs/docs.html +8 -0
  331. package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/add-item.effect.block.ts +3 -3
  332. package/templates/scaffold/src/map/(app_base)/home/_methods/assert-hello.method.block.ts +46 -0
  333. package/templates/scaffold/src/map/(app_base)/home/_methods/assert-item-added.method.block.ts +38 -0
  334. package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/clear-cart.method.block.ts +9 -3
  335. package/templates/scaffold/src/{blocks/demo-web/methods → map/(app_base)/home/_methods}/remove-item.effect.block.ts +3 -3
  336. package/templates/scaffold/src/{blocks/demo-web/nav-home.block.ts → map/(app_base)/home/_nav.block.ts} +2 -2
  337. package/templates/scaffold/src/{blocks/demo-web/home.page.block.ts → map/(app_base)/home/_page.block.ts} +7 -7
  338. package/templates/scaffold/src/{blocks/demo-web/demo-sel.ts → map/(app_base)/home/_sel.ts} +1 -0
  339. package/templates/scaffold/src/{blocks/demo-web → map/(app_base)/home}/home.html +4 -0
  340. package/templates/scaffold/src/map/(app_base)/home/nav-verification-link.block.ts +34 -0
  341. package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-content.method.block.ts +48 -0
  342. package/templates/scaffold/src/map/(external)/mailpit/_methods/assert-email-received.method.block.ts +49 -0
  343. package/templates/scaffold/src/map/(external)/mailpit/_methods/extract-email-link.method.block.ts +41 -0
  344. package/templates/scaffold/src/map/(external)/mailpit/_methods/open-message.method.block.ts +39 -0
  345. package/templates/scaffold/src/map/(external)/mailpit/_nav.block.ts +20 -0
  346. package/templates/scaffold/src/map/(external)/mailpit/_page.block.ts +30 -0
  347. package/templates/scaffold/src/map/(external)/mailpit/_sel.ts +5 -0
  348. package/templates/scaffold/src/states/demo.mem-keys.ts +25 -1
  349. package/templates/scaffold/src/states/demo.states.ts +11 -0
  350. package/templates/scaffold/test-results/.last-run.json +4 -0
  351. package/templates/scaffold/tests/example.spec.ts +2 -2
  352. package/templates/scaffold/tests/mail-verify.spec.ts +191 -0
  353. package/templates/skills/waygraph-blind-pilot.skill.md +61 -0
  354. package/templates/skills/waygraph-pilot.skill.md +108 -0
  355. package/templates/quickstart/README-saucedemo-commands.md +0 -72
  356. package/templates/scaffold/src/blocks/SITE-MAP.md +0 -12
  357. package/templates/scaffold/src/blocks/demo-web/NAV.md +0 -29
  358. package/templates/scaffold/src/blocks/demo-web/methods/assert-hello.method.block.ts +0 -50
@@ -0,0 +1,85 @@
1
+ import { Engine, start, end, withTitle, withHighlightFixtures } from "waygraph";
2
+ import { NavMailpitInboxBlock } from "../map/(external)/mailpit/_nav.block.js";
3
+ import { AssertEmailReceivedBlock } from "../map/(external)/mailpit/_methods/assert-email-received.method.block.js";
4
+ import { OpenMessageBlock } from "../map/(external)/mailpit/_methods/open-message.method.block.js";
5
+ import { AssertEmailContentBlock } from "../map/(external)/mailpit/_methods/assert-email-content.method.block.js";
6
+ import { ExtractEmailLinkBlock } from "../map/(external)/mailpit/_methods/extract-email-link.method.block.js";
7
+ import { NavVerificationLinkBlock } from "../map/(app_base)/home/nav-verification-link.block.js";
8
+ import "../map/(external)/mailpit/_page.block.js";
9
+ import { MailpitSel } from "../map/(external)/mailpit/_sel.js";
10
+
11
+ const engine = new Engine();
12
+
13
+ /**
14
+ * Cross-origin mail demo, QA-rich: navigate to a real Mailpit inbox (a real,
15
+ * separate web UI - not this app), confirm the email actually arrived,
16
+ * open it, confirm its body says what it's supposed to, read a link
17
+ * straight out of the DOM, then navigate back into the app with it - six
18
+ * atomic Blocks, no mail-catcher REST API anywhere.
19
+ *
20
+ * The four mail-reading Blocks (`assert-email-received`, `open-message`,
21
+ * `assert-email-content`, `extract-email-link`) are reusable across any
22
+ * number of different email scenarios in a project (signup confirmation,
23
+ * password reset, magic link, ...) with zero new Blocks per scenario - only
24
+ * mem varies per run: `ExpectedRecipient` (who), `ExpectedLinkPattern`
25
+ * (which link in the body to follow), `ExpectedEmailContent` (what the body
26
+ * should say). This particular flow wires them to this scaffold's own
27
+ * "verified" landing route (`nav-verification-link`, app-specific by
28
+ * nature - a different scenario would use a different landing Nav) - see
29
+ * tests/mail-verify.spec.ts for two different scenarios driven through the
30
+ * same four mail-reading Blocks.
31
+ *
32
+ * Needs a real Mailpit instance reachable at WAYGRAPH_MAIL_URL (default
33
+ * http://127.0.0.1:8025) with a message already waiting for
34
+ * ExpectedRecipient's email - see tests/mail-verify.spec.ts for how the test
35
+ * drives this end to end (spins up a real Mailpit container, sends a real
36
+ * email, then runs this exact flow) - including the negative cases: no email
37
+ * arriving, or content that doesn't match, each fail loud, naming
38
+ * themselves, not a silent pass.
39
+ */
40
+ export const mailVerifyFlow = withHighlightFixtures(
41
+ withTitle(
42
+ engine.defineFlow([
43
+ start,
44
+ NavMailpitInboxBlock,
45
+ AssertEmailReceivedBlock,
46
+ OpenMessageBlock,
47
+ AssertEmailContentBlock,
48
+ ExtractEmailLinkBlock,
49
+ NavVerificationLinkBlock,
50
+ end,
51
+ ]),
52
+ "Scaffold: Cross-origin mail verification (QA-rich)",
53
+ ),
54
+ {
55
+ "assert-email-received": {
56
+ stubAfter: {
57
+ note: {
58
+ selector: MailpitSel.messageRow,
59
+ label: "AC · email arrived",
60
+ detail: "Confirmed before opening anything.",
61
+ tag: "AC",
62
+ },
63
+ },
64
+ },
65
+ "assert-email-content": {
66
+ stubAfter: {
67
+ note: {
68
+ selector: MailpitSel.previewIframe,
69
+ label: "AC · email body matches expected copy",
70
+ tag: "AC",
71
+ },
72
+ },
73
+ },
74
+ "extract-email-link": {
75
+ stubAfter: {
76
+ note: {
77
+ selector: MailpitSel.previewIframe,
78
+ label: "AC · link found",
79
+ detail: "Read from the message preview iframe.",
80
+ tag: "AC",
81
+ },
82
+ },
83
+ },
84
+ },
85
+ );
@@ -6,10 +6,12 @@ import {
6
6
  withHighlightFixtures,
7
7
  withSessionReset,
8
8
  } from "waygraph";
9
- import { NavHomeBlock } from "../blocks/demo-web/nav-home.block.js";
10
- import { AddItemBlock } from "../blocks/demo-web/methods/add-item.effect.block.js";
11
- import { ClearCartBlock } from "../blocks/demo-web/methods/clear-cart.method.block.js";
12
- import "../blocks/demo-web/home.page.block.js";
9
+ import { NavHomeBlock } from "../map/(app_base)/home/_nav.block.js";
10
+ import { AddItemBlock } from "../map/(app_base)/home/_methods/add-item.effect.block.js";
11
+ import { AssertItemAddedBlock } from "../map/(app_base)/home/_methods/assert-item-added.method.block.js";
12
+ import { RemoveItemBlock } from "../map/(app_base)/home/_methods/remove-item.effect.block.js";
13
+ import { ClearCartBlock } from "../map/(app_base)/home/_methods/clear-cart.method.block.js";
14
+ import "../map/(app_base)/home/_page.block.js";
13
15
 
14
16
  const engine = new Engine();
15
17
 
@@ -18,12 +20,27 @@ const engine = new Engine();
18
20
  * fills SelectedItem via instanceOptions).
19
21
  *
20
22
  * waygraph demo --blocks shopFlow --data '{"demo.selectedItem":{"id":"alpha","name":"Alpha"}}'
21
- * waygraph auto # after landing on home, pick Add Alpha / Add Beta / …
23
+ * waygraph auto # after landing on home, pick Add Alpha / Add Beta / Remove ...
24
+ *
25
+ * Add -> assert it actually landed (mem-matched Remove button + cart count,
26
+ * only provable from the method's real effect, not a static page state) ->
27
+ * Remove that same row (per-item Effect, `remove-item`) -> Add again ->
28
+ * Clear cart (bulk Method, `clear-cart`) - keeps both the per-item and bulk
29
+ * clear paths atomic and each wired into a real flow, not orphaned.
22
30
  */
23
31
  export const shopFlow = withHighlightFixtures(
24
32
  withTitle(
25
33
  withSessionReset(
26
- engine.defineFlow([start, NavHomeBlock, AddItemBlock, ClearCartBlock, end]),
34
+ engine.defineFlow([
35
+ start,
36
+ NavHomeBlock,
37
+ AddItemBlock,
38
+ AssertItemAddedBlock,
39
+ RemoveItemBlock,
40
+ AddItemBlock,
41
+ ClearCartBlock,
42
+ end,
43
+ ]),
27
44
  ),
28
45
  "Scaffold: Shop (Effect + clear)",
29
46
  ),
@@ -0,0 +1,20 @@
1
+ import { defineNavBlock, Trait } from "waygraph";
2
+ import type { Docs } from "../../../states/demo.states.js";
3
+ import { DocsSel, DOCS_URL } from "./_sel.js";
4
+
5
+ /**
6
+ * Kind: Nav
7
+ * Route: (app_base)/docs/
8
+ *
9
+ * Same origin as home/ (same fixture server, different HTML file) - lives
10
+ * under (app_base), not (external). See README.md "Waygraph Map" and
11
+ * openspec/changes/waygraph-map. `(app_base)` is purely organizational
12
+ * (Next.js route-group style) - never part of the Checkpoint tag.
13
+ */
14
+ export const NavDocsBlock = defineNavBlock<Docs>({
15
+ name: "nav-docs",
16
+ description: "Opens the docs page (Waygraph Map convention demo).",
17
+ checkpoint: "Docs",
18
+ url: DOCS_URL,
19
+ verify: [Trait.visible(DocsSel.heading)],
20
+ });
@@ -0,0 +1,15 @@
1
+ import { definePageBlock, Trait } from "waygraph";
2
+ import type { Docs } from "../../../states/demo.states.js";
3
+ import { DocsSel } from "./_sel.js";
4
+
5
+ /**
6
+ * Kind: Page
7
+ * Route: (app_base)/docs/
8
+ * Arrival-only (nav.block.ts already landed on Docs).
9
+ */
10
+ export const DocsPageBlock = definePageBlock<Docs>({
11
+ name: "page-docs",
12
+ description: "Docs hub - Waygraph Map convention demo.",
13
+ checkpoint: "Docs",
14
+ verify: [Trait.visible(DocsSel.heading)],
15
+ });
@@ -0,0 +1,7 @@
1
+ export const DOCS_URL =
2
+ process.env.WAYGRAPH_DOCS_URL ??
3
+ `http://${process.env.WAYGRAPH_FIXTURE_HOST || "127.0.0.1"}:${process.env.WAYGRAPH_FIXTURE_PORT || "4177"}/docs.html`;
4
+
5
+ export const DocsSel = {
6
+ heading: "#docs-heading",
7
+ } as const;
@@ -0,0 +1,8 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head><title>Scaffold - docs (Waygraph Map convention demo)</title></head>
4
+ <body>
5
+ <h1 id="docs-heading">Docs</h1>
6
+ <a id="go-home" href="/home.html">Back home</a>
7
+ </body>
8
+ </html>
@@ -4,9 +4,9 @@ import {
4
4
  type Checkpoint,
5
5
  type WaygraphInstanceOption,
6
6
  } from "waygraph";
7
- import type { ItemInCart } from "../../../states/demo.states.js";
8
- import { SelectedItem } from "../../../states/demo.mem-keys.js";
9
- import { DemoSel } from "../demo-sel.js";
7
+ import type { ItemInCart } from "../../../../states/demo.states.js";
8
+ import { SelectedItem } from "../../../../states/demo.mem-keys.js";
9
+ import { DemoSel } from "../_sel.js";
10
10
 
11
11
  /**
12
12
  * Kind: Effect
@@ -0,0 +1,46 @@
1
+ import { defineAssertBlock, Trait } from "waygraph";
2
+ import { DemoSel } from "../_sel.js";
3
+
4
+ /**
5
+ * Kind: Assert
6
+ * Helper: defineAssertBlock
7
+ * Route: demo-web/methods/
8
+ *
9
+ * Self-loop-only "verify what's already on the page" check - no hand-written
10
+ * act/resolve to accidentally do a second thing. Same-URL assert after nav,
11
+ * with stubs + fixtures + a YAP slide.
12
+ */
13
+ export const AssertHelloBlock = defineAssertBlock({
14
+ name: "assert-hello",
15
+ description: "Confirms the hello heading is still visible after arrival.",
16
+ checkpoint: "HomeVerified",
17
+ waitForHeading: "Hello Waygraph",
18
+ verify: [Trait.text(DemoSel.title, "Hello Waygraph"), Trait.visible(DemoSel.sub)],
19
+ stubBefore: {
20
+ title: { selector: DemoSel.title, label: "Heading about to check" },
21
+ },
22
+ stubAfter: {
23
+ title: {
24
+ selector: DemoSel.title,
25
+ label: "Hello confirmed",
26
+ detail: "Offline scaffold proof point.",
27
+ duration: true,
28
+ },
29
+ sub: { selector: DemoSel.sub, label: "Subcopy", duration: 1500 },
30
+ },
31
+ stubOnError: {
32
+ title: {
33
+ selector: DemoSel.title,
34
+ label: "Heading at failure",
35
+ tag: "FAIL",
36
+ },
37
+ },
38
+ slides: [
39
+ {
40
+ caption: "Scaffold covers auto + narration",
41
+ detail: "Page hub, Effect instanceOptions, stubs, fixtures, YAP.",
42
+ tag: "YAP",
43
+ duration: true,
44
+ },
45
+ ],
46
+ });
@@ -0,0 +1,38 @@
1
+ import { defineAssertBlock, Trait, type Trait as TraitType } from "waygraph";
2
+ import type { ItemInCart } from "../../../../states/demo.states.js";
3
+ import { SelectedItem } from "../../../../states/demo.mem-keys.js";
4
+ import { DemoSel } from "../_sel.js";
5
+
6
+ /**
7
+ * Mem-aware Trait: which item's Remove button should be visible depends on
8
+ * which row add-item just acted on (mem, not a static selector) - a Trait's
9
+ * own `check(page, mem)` already receives mem, so this reads it directly.
10
+ */
11
+ const removeButtonVisibleForSelectedItem: TraitType = {
12
+ name: "remove-button-visible-for-selected-item",
13
+ async check(page, mem) {
14
+ const { id } = mem.get(SelectedItem.key);
15
+ return page.locator(DemoSel.removeBtn(id)).isVisible();
16
+ },
17
+ };
18
+
19
+ /**
20
+ * Kind: Assert
21
+ * Helper: defineAssertBlock
22
+ * Route: demo-web/methods/
23
+ *
24
+ * "Did add-item actually work" - provable only after the fact, from the
25
+ * method's real effect on the page: the specific row's own Remove button
26
+ * appeared (mem-matched, not just "some" row), AND the aggregate cart count
27
+ * reflects it. Self-loop on ItemInCart - no state change, verify only.
28
+ *
29
+ * Explicit `<ItemInCart>` type argument - `defineAssertBlock` defaults to
30
+ * wildcard `Checkpoint<string>` when uninferred, which only "works" when the
31
+ * assert sits last in a chain; this one sits mid-chain (see shop.flow.ts).
32
+ */
33
+ export const AssertItemAddedBlock = defineAssertBlock<ItemInCart>({
34
+ name: "assert-item-added",
35
+ description: "Confirms the mem-selected item's own Remove button appeared and the cart count reflects it.",
36
+ checkpoint: "ItemInCart",
37
+ verify: [removeButtonVisibleForSelectedItem, Trait.text(DemoSel.cartCount, "1")],
38
+ });
@@ -1,12 +1,18 @@
1
1
  import { defineMethodBlock, checkpoint, Trait } from "waygraph";
2
- import { DemoSel } from "../demo-sel.js";
2
+ import type { ItemInCart, CartEmpty } from "../../../../states/demo.states.js";
3
+ import { DemoSel } from "../_sel.js";
3
4
 
4
5
  /**
5
6
  * Kind: Method
6
7
  * Helper: defineMethodBlock
7
- * Route: demo-web/methods/
8
+ * Route: (app_base)/home/_methods/
9
+ *
10
+ * Real, pre-existing bug found live while verifying the Waygraph Map
11
+ * rename: this Block had no explicit <In, Out> generic at all, so static
12
+ * discovery (`waygraph graph`) always skipped it silently ("no ...generic
13
+ * call found") - present before this move, not caused by it.
8
14
  */
9
- export const ClearCartBlock = defineMethodBlock({
15
+ export const ClearCartBlock = defineMethodBlock<ItemInCart, CartEmpty>({
10
16
  name: "clear-cart",
11
17
  description: "Clears every item via the Clear cart control.",
12
18
  instruction: {
@@ -4,9 +4,9 @@ import {
4
4
  type Checkpoint,
5
5
  type WaygraphInstanceOption,
6
6
  } from "waygraph";
7
- import type { CartEmpty } from "../../../states/demo.states.js";
8
- import { SelectedItem } from "../../../states/demo.mem-keys.js";
9
- import { DemoSel } from "../demo-sel.js";
7
+ import type { CartEmpty } from "../../../../states/demo.states.js";
8
+ import { SelectedItem } from "../../../../states/demo.mem-keys.js";
9
+ import { DemoSel } from "../_sel.js";
10
10
 
11
11
  /**
12
12
  * Kind: Effect
@@ -1,10 +1,10 @@
1
1
  import { defineNavBlock, Trait } from "waygraph";
2
- import { DemoSel, HOME_URL } from "./demo-sel.js";
2
+ import { DemoSel, HOME_URL } from "./_sel.js";
3
3
 
4
4
  /**
5
5
  * Kind: Nav
6
6
  * Helper: defineNavBlock
7
- * Route: demo-web/ (synthetic "/")
7
+ * Route: (app_base)/home/ (synthetic "/")
8
8
  */
9
9
  export const NavHomeBlock = defineNavBlock({
10
10
  name: "nav-home",
@@ -1,15 +1,15 @@
1
1
  import { definePageBlock, Trait } from "waygraph";
2
- import type { Home } from "../../states/demo.states.js";
3
- import { DemoSel } from "./demo-sel.js";
4
- import { AddItemBlock } from "./methods/add-item.effect.block.js";
5
- import { RemoveItemBlock } from "./methods/remove-item.effect.block.js";
6
- import { ClearCartBlock } from "./methods/clear-cart.method.block.js";
7
- import { AssertHelloBlock } from "./methods/assert-hello.method.block.js";
2
+ import type { Home } from "../../../states/demo.states.js";
3
+ import { DemoSel } from "./_sel.js";
4
+ import { AddItemBlock } from "./_methods/add-item.effect.block.js";
5
+ import { RemoveItemBlock } from "./_methods/remove-item.effect.block.js";
6
+ import { ClearCartBlock } from "./_methods/clear-cart.method.block.js";
7
+ import { AssertHelloBlock } from "./_methods/assert-hello.method.block.js";
8
8
 
9
9
  /**
10
10
  * Kind: Page
11
11
  * Helper: definePageBlock
12
- * Route: demo-web/
12
+ * Route: (app_base)/home/
13
13
  *
14
14
  * Hub for the offline home screen. Methods hang here for docs/auto grouping;
15
15
  * edges in flows still reference the method Blocks directly.
@@ -11,4 +11,5 @@ export const DemoSel = {
11
11
  clear: "[data-test=clear-cart]",
12
12
  addBtn: (id: string) => `[data-test=add-${id}]`,
13
13
  removeBtn: (id: string) => `[data-test=remove-${id}]`,
14
+ verifiedBanner: "#verified-banner",
14
15
  } as const;
@@ -14,6 +14,7 @@
14
14
  <body>
15
15
  <h1>Hello Waygraph</h1>
16
16
  <p id="sub">Scaffold demo - Effect / auto / Page hub</p>
17
+ <p id="verified-banner" hidden>Email verified!</p>
17
18
  <div id="cart">Cart: <span data-test="cart-count">0</span></div>
18
19
  <ul id="catalog">
19
20
  <li class="item" data-id="alpha">
@@ -34,6 +35,9 @@
34
35
  </ul>
35
36
  <button type="button" data-test="clear-cart" id="clear">Clear cart</button>
36
37
  <script>
38
+ if (new URLSearchParams(location.search).get("verified") === "1") {
39
+ document.getElementById("verified-banner").hidden = false;
40
+ }
37
41
  const countEl = document.querySelector("[data-test=cart-count]");
38
42
  const sync = () => {
39
43
  const n = document.querySelectorAll(".remove:not([hidden])").length;
@@ -0,0 +1,34 @@
1
+ import { defineNavBlock, Trait } from "waygraph";
2
+ import type { HomeVerified } from "../../../states/demo.states.js";
3
+ import { EmailLink } from "../../../states/demo.mem-keys.js";
4
+ import { DemoSel } from "./_sel.js";
5
+
6
+ /**
7
+ * Kind: Nav
8
+ * Helper: defineNavBlock
9
+ * Route: (app_base)/home/
10
+ *
11
+ * Navigates back into the app to the link extract-email-link read out of
12
+ * the real Mailpit message, then confirms the page actually reflects it
13
+ * (the verified banner only appears when the exact `verified=1` link was
14
+ * followed, not just any URL). This particular check is app-specific (this
15
+ * scaffold's own verification landing route) even though the mail-reading
16
+ * Blocks feeding it are scenario-agnostic - a real project would have one
17
+ * such Nav per distinct landing route, same as any other app screen.
18
+ *
19
+ * Deliberately no `requires: [EmailLink.key]` here: `requires` means "the
20
+ * caller must seed this externally before the flow starts" (preflight
21
+ * checks the whole chain's requires up front) - `EmailLink` is instead
22
+ * produced by an earlier Block in this very chain, so declaring it as a
23
+ * requirement would make preflight fail before the flow ever gets the
24
+ * chance to produce it. Engine support for declaring a mem key as "produced
25
+ * by an earlier Block in this chain" (vs. externally supplied) is a
26
+ * separate, not-yet-built roadmap item - see ROADMAP.md's "Split requires"
27
+ * row.
28
+ */
29
+ export const NavVerificationLinkBlock = defineNavBlock<HomeVerified>({
30
+ name: "nav-verification-link",
31
+ checkpoint: "HomeVerified",
32
+ url: (mem) => mem.get(EmailLink.key).url,
33
+ verify: [Trait.visible(DemoSel.verifiedBanner)],
34
+ });
@@ -0,0 +1,48 @@
1
+ import { defineAssertBlock, type Trait } from "waygraph";
2
+ import type { MailpitMessageOpen } from "../../../../states/demo.states.js";
3
+ import { ExpectedEmailContent } from "../../../../states/demo.mem-keys.js";
4
+ import { MailpitSel } from "../_sel.js";
5
+
6
+ /**
7
+ * Mem-aware Trait: the expected copy varies per email scenario, so it can't
8
+ * be a static `Trait.frameContains(...)` call (which bakes its expected
9
+ * string in at Block-definition time) - a Trait's own `check(page, mem)`
10
+ * already receives mem, so a bespoke Trait reads it at check time instead.
11
+ * This is what lets one Block serve any number of different email templates.
12
+ */
13
+ const bodyContainsExpectedText: Trait = {
14
+ name: "body-contains-expected-text",
15
+ async check(page, mem) {
16
+ const expected = mem.get(ExpectedEmailContent);
17
+ const text = await page.frameLocator(MailpitSel.previewIframe).locator("body").textContent();
18
+ return (text ?? "").includes(expected);
19
+ },
20
+ };
21
+
22
+ /**
23
+ * Kind: Assert
24
+ * Helper: defineAssertBlock
25
+ * Route: demo-external/mailpit/_methods/
26
+ *
27
+ * "Does the email actually say what it's supposed to" - not just "does a
28
+ * link exist somewhere in it." Reads the message body straight out of the
29
+ * preview iframe (the top-level page's own `Trait.text`/`Trait.visible`
30
+ * can't see inside an iframe at all - see README's "Mail adapters" section).
31
+ * Self-loop on MailpitMessageOpen - no state change, verify only.
32
+ *
33
+ * Deliberately named for what it does, not for one scenario: the expected
34
+ * copy comes from mem (`ExpectedEmailContent`), not a hardcoded string in
35
+ * this file, so this one Block serves any number of different email
36
+ * scenarios - a Block named "assert-verification-email-content" would
37
+ * wrongly imply it only ever checks verification-email copy.
38
+ *
39
+ * Explicit `<MailpitMessageOpen>` type argument - see the same note on
40
+ * `assert-email-received.method.block.ts` for why this can't be inferred.
41
+ */
42
+ export const AssertEmailContentBlock = defineAssertBlock<MailpitMessageOpen>({
43
+ name: "assert-email-content",
44
+ description: "Confirms the open message's body contains the mem-supplied expected copy.",
45
+ checkpoint: "MailpitMessageOpen",
46
+ requires: [ExpectedEmailContent],
47
+ verify: [bodyContainsExpectedText],
48
+ });
@@ -0,0 +1,49 @@
1
+ import { defineAssertBlock, type Trait } from "waygraph";
2
+ import type { MailpitInbox } from "../../../../states/demo.states.js";
3
+ import { ExpectedRecipient } from "../../../../states/demo.mem-keys.js";
4
+ import { MailpitSel } from "../_sel.js";
5
+
6
+ /**
7
+ * Mem-aware Trait: the selector depends on which recipient this run expects,
8
+ * so it can't be a static `Trait.visible(...)` string - a Trait's own
9
+ * `check(page, mem)` already receives mem, so a bespoke Trait object reads it
10
+ * directly instead of needing a new engine primitive.
11
+ */
12
+ const emailReceived: Trait = {
13
+ name: "email-received",
14
+ async check(page, mem) {
15
+ const { email } = mem.get(ExpectedRecipient.key);
16
+ try {
17
+ await page.locator(MailpitSel.messageRow, { hasText: email }).first().waitFor({
18
+ state: "visible",
19
+ timeout: 10_000,
20
+ });
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ },
26
+ };
27
+
28
+ /**
29
+ * Kind: Assert
30
+ * Helper: defineAssertBlock
31
+ * Route: demo-external/mailpit/_methods/
32
+ *
33
+ * "Did the email even arrive" - a lightweight existence check that does not
34
+ * open or consume the message, so it's safe to run before deciding to open
35
+ * anything. Self-loop on MailpitInbox - no state change, verify only.
36
+ *
37
+ * Explicit `<MailpitInbox>` type argument: `defineAssertBlock` defaults to
38
+ * wildcard `Checkpoint<string>` on both sides when uninferred, which only
39
+ * "works" when the assert sits last in a chain (nothing downstream narrows
40
+ * it) - this one sits between two specifically-typed Blocks, so it must be
41
+ * parameterized explicitly or `defineFlow`'s tuple typing breaks.
42
+ */
43
+ export const AssertEmailReceivedBlock = defineAssertBlock<MailpitInbox>({
44
+ name: "assert-email-received",
45
+ description: "Confirms a message addressed to the expected recipient has arrived.",
46
+ checkpoint: "MailpitInbox",
47
+ requires: [ExpectedRecipient.key],
48
+ verify: [emailReceived],
49
+ });
@@ -0,0 +1,41 @@
1
+ import { defineMethodBlock, checkpoint } from "waygraph";
2
+ import type { MailpitMessageOpen } from "../../../../states/demo.states.js";
3
+ import { EmailLink, ExpectedLinkPattern } from "../../../../states/demo.mem-keys.js";
4
+ import { MailpitSel } from "../_sel.js";
5
+
6
+ /**
7
+ * Kind: Method
8
+ * Helper: defineMethodBlock
9
+ * Route: demo-external/mailpit/_methods/
10
+ *
11
+ * Reads a link straight out of the open message's HTML preview iframe via a
12
+ * real DOM read (getAttribute) - no mail-catcher REST API - and stores it in
13
+ * mem. A separate NavBlock does the actual navigation: folding "read the
14
+ * link" and "go there" into one Block would repeat the exact compound-action
15
+ * anti-pattern `waygraph check` flags elsewhere.
16
+ *
17
+ * Deliberately named for what it does, not for one scenario: the
18
+ * link-matching substring comes from mem (`ExpectedLinkPattern`), not a
19
+ * hardcoded string, so this one Block serves any number of different email
20
+ * scenarios in a project (a signup confirmation, a password reset, a magic
21
+ * link, ...) by varying mem per run, not by writing a new Block per
22
+ * scenario - a Block named "extract-verification-link" would wrongly imply
23
+ * it only ever handles verification emails.
24
+ */
25
+ export const ExtractEmailLinkBlock = defineMethodBlock<MailpitMessageOpen, MailpitMessageOpen>({
26
+ name: "extract-email-link",
27
+ description: "Reads the link matching the mem-supplied pattern out of the open message's preview.",
28
+ requires: [ExpectedLinkPattern],
29
+ instruction: {
30
+ async act(page, _in, mem) {
31
+ const pattern = mem.get(ExpectedLinkPattern);
32
+ const frame = page.frameLocator(MailpitSel.previewIframe);
33
+ const link = frame.locator(`a[href*="${pattern}"]`).first();
34
+ await link.waitFor({ state: "visible", timeout: 15_000 });
35
+ const href = await link.getAttribute("href");
36
+ if (!href) throw new Error(`extract-email-link: no link matching "${pattern}" in message preview`);
37
+ mem.set(EmailLink({ url: href }));
38
+ },
39
+ resolve: () => checkpoint("MailpitMessageOpen"),
40
+ },
41
+ });
@@ -0,0 +1,39 @@
1
+ import { defineMethodBlock, checkpoint } from "waygraph";
2
+ import type { MailpitInbox, MailpitMessageOpen } from "../../../../states/demo.states.js";
3
+ import { ExpectedRecipient } from "../../../../states/demo.mem-keys.js";
4
+ import { MailpitSel } from "../_sel.js";
5
+
6
+ /**
7
+ * Kind: Method
8
+ * Helper: defineMethodBlock
9
+ * Route: demo-external/mailpit/_methods/
10
+ *
11
+ * Opens the newest inbox row addressed to the mem-set recipient (not bare
12
+ * inbox position - a prior run's leftover mail for a different recipient
13
+ * would otherwise pick the wrong row). UI click only, no mail-catcher REST
14
+ * API - the row itself is a real link (`page.waitForURL` sees the client-side
15
+ * route change the same as any other in-page navigation).
16
+ */
17
+ export const OpenMessageBlock = defineMethodBlock<MailpitInbox, MailpitMessageOpen>({
18
+ name: "open-message",
19
+ description: "Opens the newest message addressed to the expected recipient.",
20
+ requires: [ExpectedRecipient.key],
21
+ instruction: {
22
+ async act(page, _in, mem) {
23
+ const { email } = mem.get(ExpectedRecipient.key);
24
+ const row = page.locator(MailpitSel.messageRow, { hasText: email }).first();
25
+ try {
26
+ await row.waitFor({ state: "visible", timeout: 30_000 });
27
+ } catch {
28
+ throw new Error(`open-message: no email arrived for "${email}" within 30s`);
29
+ }
30
+ await row.click();
31
+ await page.waitForURL(/\/view\//, { timeout: 15_000 });
32
+ await page.frameLocator(MailpitSel.previewIframe).locator("body").waitFor({
33
+ state: "attached",
34
+ timeout: 15_000,
35
+ });
36
+ },
37
+ resolve: () => checkpoint("MailpitMessageOpen"),
38
+ },
39
+ });
@@ -0,0 +1,20 @@
1
+ import { defineNavBlock, Trait } from "waygraph";
2
+ import type { MailpitInbox } from "../../../states/demo.states.js";
3
+
4
+ /**
5
+ * Kind: Nav
6
+ * Helper: defineNavBlock
7
+ * Route: (external)/mailpit/ (cross-origin - a real Mailpit web UI, not this app)
8
+ *
9
+ * Cross-origin tooling lives under (external)/, never (app_base)/ - this is
10
+ * a dev-inbox screen, not part of the app under test. `WAYGRAPH_MAIL_URL`
11
+ * defaults to Mailpit's own usual local port.
12
+ */
13
+ const mailpitUrl = (): string => process.env.WAYGRAPH_MAIL_URL ?? "http://127.0.0.1:8025";
14
+
15
+ export const NavMailpitInboxBlock = defineNavBlock<MailpitInbox>({
16
+ name: "nav-mailpit-inbox",
17
+ checkpoint: "MailpitInbox",
18
+ url: mailpitUrl,
19
+ verify: [Trait.url({ pathname: "/" })],
20
+ });