swipium 1.0.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 (387) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/dist/appMap/automationLink.js +74 -0
  4. package/dist/appMap/automationLink.js.map +1 -0
  5. package/dist/appMap/build.js +193 -0
  6. package/dist/appMap/build.js.map +1 -0
  7. package/dist/appMap/codeIndex.js +73 -0
  8. package/dist/appMap/codeIndex.js.map +1 -0
  9. package/dist/appMap/featureModel.js +83 -0
  10. package/dist/appMap/featureModel.js.map +1 -0
  11. package/dist/appMap/firstRunApply.js +131 -0
  12. package/dist/appMap/firstRunApply.js.map +1 -0
  13. package/dist/appMap/fsWalk.js +75 -0
  14. package/dist/appMap/fsWalk.js.map +1 -0
  15. package/dist/appMap/issues.js +227 -0
  16. package/dist/appMap/issues.js.map +1 -0
  17. package/dist/appMap/migrations.js +87 -0
  18. package/dist/appMap/migrations.js.map +1 -0
  19. package/dist/appMap/prelaunch.js +66 -0
  20. package/dist/appMap/prelaunch.js.map +1 -0
  21. package/dist/appMap/projectRegistry.js +70 -0
  22. package/dist/appMap/projectRegistry.js.map +1 -0
  23. package/dist/appMap/provenance.js +52 -0
  24. package/dist/appMap/provenance.js.map +1 -0
  25. package/dist/appMap/query.js +118 -0
  26. package/dist/appMap/query.js.map +1 -0
  27. package/dist/appMap/runtimeMerge.js +207 -0
  28. package/dist/appMap/runtimeMerge.js.map +1 -0
  29. package/dist/appMap/schema.js +65 -0
  30. package/dist/appMap/schema.js.map +1 -0
  31. package/dist/appMap/screenMatch.js +75 -0
  32. package/dist/appMap/screenMatch.js.map +1 -0
  33. package/dist/appMap/staticScan.js +614 -0
  34. package/dist/appMap/staticScan.js.map +1 -0
  35. package/dist/appMap/store.js +173 -0
  36. package/dist/appMap/store.js.map +1 -0
  37. package/dist/appMap/tsAstScan.js +184 -0
  38. package/dist/appMap/tsAstScan.js.map +1 -0
  39. package/dist/artifacts/bundletool.js +199 -0
  40. package/dist/artifacts/bundletool.js.map +1 -0
  41. package/dist/artifacts/resolve.js +388 -0
  42. package/dist/artifacts/resolve.js.map +1 -0
  43. package/dist/automationGen/appiumModel.js +129 -0
  44. package/dist/automationGen/appiumModel.js.map +1 -0
  45. package/dist/automationGen/ciEmitter.js +80 -0
  46. package/dist/automationGen/ciEmitter.js.map +1 -0
  47. package/dist/automationGen/jsEmitter.js +440 -0
  48. package/dist/automationGen/jsEmitter.js.map +1 -0
  49. package/dist/automationGen/packagePatch.js +56 -0
  50. package/dist/automationGen/packagePatch.js.map +1 -0
  51. package/dist/automationGen/projectProfile.js +317 -0
  52. package/dist/automationGen/projectProfile.js.map +1 -0
  53. package/dist/automationGen/pythonEmitter.js +386 -0
  54. package/dist/automationGen/pythonEmitter.js.map +1 -0
  55. package/dist/automationGen/readmeEmitter.js +66 -0
  56. package/dist/automationGen/readmeEmitter.js.map +1 -0
  57. package/dist/automationGen/suitePlan.js +94 -0
  58. package/dist/automationGen/suitePlan.js.map +1 -0
  59. package/dist/automationGen/validation.js +150 -0
  60. package/dist/automationGen/validation.js.map +1 -0
  61. package/dist/build/parseBuildLog.js +119 -0
  62. package/dist/build/parseBuildLog.js.map +1 -0
  63. package/dist/build/plan.js +150 -0
  64. package/dist/build/plan.js.map +1 -0
  65. package/dist/ci/preflight.js +85 -0
  66. package/dist/ci/preflight.js.map +1 -0
  67. package/dist/cli/init.js +105 -0
  68. package/dist/cli/init.js.map +1 -0
  69. package/dist/cli/scan.js +85 -0
  70. package/dist/cli/scan.js.map +1 -0
  71. package/dist/cli/suite.js +85 -0
  72. package/dist/cli/suite.js.map +1 -0
  73. package/dist/cli/verify.js +62 -0
  74. package/dist/cli/verify.js.map +1 -0
  75. package/dist/consent/consent.js +43 -0
  76. package/dist/consent/consent.js.map +1 -0
  77. package/dist/context/detect.js +156 -0
  78. package/dist/context/detect.js.map +1 -0
  79. package/dist/context/findApps.js +198 -0
  80. package/dist/context/findApps.js.map +1 -0
  81. package/dist/context/projectRoot.js +41 -0
  82. package/dist/context/projectRoot.js.map +1 -0
  83. package/dist/context/scan.js +167 -0
  84. package/dist/context/scan.js.map +1 -0
  85. package/dist/core/target.js +115 -0
  86. package/dist/core/target.js.map +1 -0
  87. package/dist/core/targetPlan.js +140 -0
  88. package/dist/core/targetPlan.js.map +1 -0
  89. package/dist/drivers/DirectDriver.js +228 -0
  90. package/dist/drivers/DirectDriver.js.map +1 -0
  91. package/dist/drivers/Driver.js +4 -0
  92. package/dist/drivers/Driver.js.map +1 -0
  93. package/dist/drivers/SimctlDriver.js +127 -0
  94. package/dist/drivers/SimctlDriver.js.map +1 -0
  95. package/dist/drivers/WdaDriver.js +250 -0
  96. package/dist/drivers/WdaDriver.js.map +1 -0
  97. package/dist/explore/candidates.js +93 -0
  98. package/dist/explore/candidates.js.map +1 -0
  99. package/dist/explore/graph.js +116 -0
  100. package/dist/explore/graph.js.map +1 -0
  101. package/dist/explore/memory.js +35 -0
  102. package/dist/explore/memory.js.map +1 -0
  103. package/dist/explore/planner.js +105 -0
  104. package/dist/explore/planner.js.map +1 -0
  105. package/dist/explore/policy.js +85 -0
  106. package/dist/explore/policy.js.map +1 -0
  107. package/dist/explore/runner.js +475 -0
  108. package/dist/explore/runner.js.map +1 -0
  109. package/dist/explore/signatures.js +35 -0
  110. package/dist/explore/signatures.js.map +1 -0
  111. package/dist/explore/suite.js +55 -0
  112. package/dist/explore/suite.js.map +1 -0
  113. package/dist/featureTesting/executionBootstrap.js +140 -0
  114. package/dist/featureTesting/executionBootstrap.js.map +1 -0
  115. package/dist/firstRun/authStateMachine.js +83 -0
  116. package/dist/firstRun/authStateMachine.js.map +1 -0
  117. package/dist/firstRun/classifyScreen.js +201 -0
  118. package/dist/firstRun/classifyScreen.js.map +1 -0
  119. package/dist/firstRun/firstRunPlanner.js +207 -0
  120. package/dist/firstRun/firstRunPlanner.js.map +1 -0
  121. package/dist/firstRun/firstRunRunner.js +331 -0
  122. package/dist/firstRun/firstRunRunner.js.map +1 -0
  123. package/dist/firstRun/generatedDataPolicy.js +215 -0
  124. package/dist/firstRun/generatedDataPolicy.js.map +1 -0
  125. package/dist/firstRun/inputPlanner.js +67 -0
  126. package/dist/firstRun/inputPlanner.js.map +1 -0
  127. package/dist/firstRun/onboardingStateMachine.js +46 -0
  128. package/dist/firstRun/onboardingStateMachine.js.map +1 -0
  129. package/dist/firstRun/paywallPolicy.js +54 -0
  130. package/dist/firstRun/paywallPolicy.js.map +1 -0
  131. package/dist/firstRun/types.js +7 -0
  132. package/dist/firstRun/types.js.map +1 -0
  133. package/dist/fixtures/catalog.js +131 -0
  134. package/dist/fixtures/catalog.js.map +1 -0
  135. package/dist/flows/discover.js +18 -0
  136. package/dist/flows/discover.js.map +1 -0
  137. package/dist/flows/generate.js +211 -0
  138. package/dist/flows/generate.js.map +1 -0
  139. package/dist/flows/lint.js +97 -0
  140. package/dist/flows/lint.js.map +1 -0
  141. package/dist/flows/pack.js +68 -0
  142. package/dist/flows/pack.js.map +1 -0
  143. package/dist/flows/run.js +738 -0
  144. package/dist/flows/run.js.map +1 -0
  145. package/dist/flows/schema.js +240 -0
  146. package/dist/flows/schema.js.map +1 -0
  147. package/dist/flows/seedExec.js +108 -0
  148. package/dist/flows/seedExec.js.map +1 -0
  149. package/dist/flows/templates.js +155 -0
  150. package/dist/flows/templates.js.map +1 -0
  151. package/dist/flows/waitGuards.js +12 -0
  152. package/dist/flows/waitGuards.js.map +1 -0
  153. package/dist/index.js +33 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/ios/signing.js +182 -0
  156. package/dist/ios/signing.js.map +1 -0
  157. package/dist/issues/classify.js +186 -0
  158. package/dist/issues/classify.js.map +1 -0
  159. package/dist/issues/fingerprint.js +130 -0
  160. package/dist/issues/fingerprint.js.map +1 -0
  161. package/dist/issues/index.js +240 -0
  162. package/dist/issues/index.js.map +1 -0
  163. package/dist/issues/metrics.js +155 -0
  164. package/dist/issues/metrics.js.map +1 -0
  165. package/dist/issues/recurrence.js +193 -0
  166. package/dist/issues/recurrence.js.map +1 -0
  167. package/dist/issues/report.js +110 -0
  168. package/dist/issues/report.js.map +1 -0
  169. package/dist/issues/reportBridge.js +119 -0
  170. package/dist/issues/reportBridge.js.map +1 -0
  171. package/dist/issues/schema.js +90 -0
  172. package/dist/issues/schema.js.map +1 -0
  173. package/dist/issues/sourceRevision.js +108 -0
  174. package/dist/issues/sourceRevision.js.map +1 -0
  175. package/dist/issues/store.js +155 -0
  176. package/dist/issues/store.js.map +1 -0
  177. package/dist/lib/android.js +278 -0
  178. package/dist/lib/android.js.map +1 -0
  179. package/dist/lib/commandTemplate.js +55 -0
  180. package/dist/lib/commandTemplate.js.map +1 -0
  181. package/dist/lib/coordSpace.js +23 -0
  182. package/dist/lib/coordSpace.js.map +1 -0
  183. package/dist/lib/device.js +87 -0
  184. package/dist/lib/device.js.map +1 -0
  185. package/dist/lib/gitignore.js +22 -0
  186. package/dist/lib/gitignore.js.map +1 -0
  187. package/dist/lib/image.js +97 -0
  188. package/dist/lib/image.js.map +1 -0
  189. package/dist/lib/logger.js +8 -0
  190. package/dist/lib/logger.js.map +1 -0
  191. package/dist/lib/metroState.js +45 -0
  192. package/dist/lib/metroState.js.map +1 -0
  193. package/dist/lib/needsInput.js +201 -0
  194. package/dist/lib/needsInput.js.map +1 -0
  195. package/dist/lib/png.js +103 -0
  196. package/dist/lib/png.js.map +1 -0
  197. package/dist/lib/redact.js +21 -0
  198. package/dist/lib/redact.js.map +1 -0
  199. package/dist/lib/result.js +87 -0
  200. package/dist/lib/result.js.map +1 -0
  201. package/dist/lib/schemaHash.js +65 -0
  202. package/dist/lib/schemaHash.js.map +1 -0
  203. package/dist/lib/sensitive.js +13 -0
  204. package/dist/lib/sensitive.js.map +1 -0
  205. package/dist/lib/simctl.js +149 -0
  206. package/dist/lib/simctl.js.map +1 -0
  207. package/dist/lib/spawn.js +144 -0
  208. package/dist/lib/spawn.js.map +1 -0
  209. package/dist/lib/wda.js +347 -0
  210. package/dist/lib/wda.js.map +1 -0
  211. package/dist/lib/wdaConfig.js +69 -0
  212. package/dist/lib/wdaConfig.js.map +1 -0
  213. package/dist/lib/wdaTune.js +58 -0
  214. package/dist/lib/wdaTune.js.map +1 -0
  215. package/dist/oracle/auth.js +22 -0
  216. package/dist/oracle/auth.js.map +1 -0
  217. package/dist/oracle/failures.js +193 -0
  218. package/dist/oracle/failures.js.map +1 -0
  219. package/dist/oracle/health.js +123 -0
  220. package/dist/oracle/health.js.map +1 -0
  221. package/dist/oracle/record.js +36 -0
  222. package/dist/oracle/record.js.map +1 -0
  223. package/dist/orchestration/envelope.js +52 -0
  224. package/dist/orchestration/envelope.js.map +1 -0
  225. package/dist/orchestration/goal.js +98 -0
  226. package/dist/orchestration/goal.js.map +1 -0
  227. package/dist/plan/plan.js +124 -0
  228. package/dist/plan/plan.js.map +1 -0
  229. package/dist/prompts/index.js +63 -0
  230. package/dist/prompts/index.js.map +1 -0
  231. package/dist/report/artifactManifest.js +44 -0
  232. package/dist/report/artifactManifest.js.map +1 -0
  233. package/dist/report/coverage.js +60 -0
  234. package/dist/report/coverage.js.map +1 -0
  235. package/dist/report/evidence.js +84 -0
  236. package/dist/report/evidence.js.map +1 -0
  237. package/dist/report/export.js +447 -0
  238. package/dist/report/export.js.map +1 -0
  239. package/dist/report/flake.js +61 -0
  240. package/dist/report/flake.js.map +1 -0
  241. package/dist/report/history.js +564 -0
  242. package/dist/report/history.js.map +1 -0
  243. package/dist/report/policy.js +98 -0
  244. package/dist/report/policy.js.map +1 -0
  245. package/dist/report/qaLevel.js +96 -0
  246. package/dist/report/qaLevel.js.map +1 -0
  247. package/dist/report/readiness.js +213 -0
  248. package/dist/report/readiness.js.map +1 -0
  249. package/dist/report/summary.js +42 -0
  250. package/dist/report/summary.js.map +1 -0
  251. package/dist/report/testCatalog.js +93 -0
  252. package/dist/report/testCatalog.js.map +1 -0
  253. package/dist/report/timing.js +37 -0
  254. package/dist/report/timing.js.map +1 -0
  255. package/dist/server.js +180 -0
  256. package/dist/server.js.map +1 -0
  257. package/dist/services/automationGenerate.js +60 -0
  258. package/dist/services/automationGenerate.js.map +1 -0
  259. package/dist/services/build.js +66 -0
  260. package/dist/services/build.js.map +1 -0
  261. package/dist/services/preflight.js +51 -0
  262. package/dist/services/preflight.js.map +1 -0
  263. package/dist/services/prepareAndroid.js +183 -0
  264. package/dist/services/prepareAndroid.js.map +1 -0
  265. package/dist/services/prepareIos.js +162 -0
  266. package/dist/services/prepareIos.js.map +1 -0
  267. package/dist/services/report.js +537 -0
  268. package/dist/services/report.js.map +1 -0
  269. package/dist/services/smoke.js +120 -0
  270. package/dist/services/smoke.js.map +1 -0
  271. package/dist/services/suiteGenerate.js +123 -0
  272. package/dist/services/suiteGenerate.js.map +1 -0
  273. package/dist/services/testSuiteKnowledge.js +150 -0
  274. package/dist/services/testSuiteKnowledge.js.map +1 -0
  275. package/dist/session/attach.js +47 -0
  276. package/dist/session/attach.js.map +1 -0
  277. package/dist/session/progress.js +56 -0
  278. package/dist/session/progress.js.map +1 -0
  279. package/dist/session/store.js +329 -0
  280. package/dist/session/store.js.map +1 -0
  281. package/dist/snapshot/overlays.js +132 -0
  282. package/dist/snapshot/overlays.js.map +1 -0
  283. package/dist/snapshot/parse.js +204 -0
  284. package/dist/snapshot/parse.js.map +1 -0
  285. package/dist/snapshot/present.js +12 -0
  286. package/dist/snapshot/present.js.map +1 -0
  287. package/dist/snapshot/settle.js +36 -0
  288. package/dist/snapshot/settle.js.map +1 -0
  289. package/dist/state/profile.js +318 -0
  290. package/dist/state/profile.js.map +1 -0
  291. package/dist/suite/compile.js +176 -0
  292. package/dist/suite/compile.js.map +1 -0
  293. package/dist/suite/lint.js +45 -0
  294. package/dist/suite/lint.js.map +1 -0
  295. package/dist/suite/pom.js +285 -0
  296. package/dist/suite/pom.js.map +1 -0
  297. package/dist/suite/testcase.js +87 -0
  298. package/dist/suite/testcase.js.map +1 -0
  299. package/dist/testSuite/generator.js +222 -0
  300. package/dist/testSuite/generator.js.map +1 -0
  301. package/dist/testSuite/history.js +44 -0
  302. package/dist/testSuite/history.js.map +1 -0
  303. package/dist/testSuite/issueLinks.js +96 -0
  304. package/dist/testSuite/issueLinks.js.map +1 -0
  305. package/dist/testSuite/merge.js +190 -0
  306. package/dist/testSuite/merge.js.map +1 -0
  307. package/dist/testSuite/schema.js +86 -0
  308. package/dist/testSuite/schema.js.map +1 -0
  309. package/dist/testSuite/store.js +116 -0
  310. package/dist/testSuite/store.js.map +1 -0
  311. package/dist/testSuite/traceability.js +55 -0
  312. package/dist/testSuite/traceability.js.map +1 -0
  313. package/dist/tools/act.js +457 -0
  314. package/dist/tools/act.js.map +1 -0
  315. package/dist/tools/agent.js +225 -0
  316. package/dist/tools/agent.js.map +1 -0
  317. package/dist/tools/appMap.js +317 -0
  318. package/dist/tools/appMap.js.map +1 -0
  319. package/dist/tools/assertVisual.js +74 -0
  320. package/dist/tools/assertVisual.js.map +1 -0
  321. package/dist/tools/automationGenerate.js +394 -0
  322. package/dist/tools/automationGenerate.js.map +1 -0
  323. package/dist/tools/capabilities.js +114 -0
  324. package/dist/tools/capabilities.js.map +1 -0
  325. package/dist/tools/clearOverlay.js +150 -0
  326. package/dist/tools/clearOverlay.js.map +1 -0
  327. package/dist/tools/detectContext.js +41 -0
  328. package/dist/tools/detectContext.js.map +1 -0
  329. package/dist/tools/doctor.js +116 -0
  330. package/dist/tools/doctor.js.map +1 -0
  331. package/dist/tools/explore.js +289 -0
  332. package/dist/tools/explore.js.map +1 -0
  333. package/dist/tools/firstRun.js +136 -0
  334. package/dist/tools/firstRun.js.map +1 -0
  335. package/dist/tools/flow.js +331 -0
  336. package/dist/tools/flow.js.map +1 -0
  337. package/dist/tools/flowGenerate.js +55 -0
  338. package/dist/tools/flowGenerate.js.map +1 -0
  339. package/dist/tools/getArtifact.js +39 -0
  340. package/dist/tools/getArtifact.js.map +1 -0
  341. package/dist/tools/health.js +41 -0
  342. package/dist/tools/health.js.map +1 -0
  343. package/dist/tools/ios.js +492 -0
  344. package/dist/tools/ios.js.map +1 -0
  345. package/dist/tools/jobs.js +20 -0
  346. package/dist/tools/jobs.js.map +1 -0
  347. package/dist/tools/network.js +50 -0
  348. package/dist/tools/network.js.map +1 -0
  349. package/dist/tools/note.js +69 -0
  350. package/dist/tools/note.js.map +1 -0
  351. package/dist/tools/plan.js +49 -0
  352. package/dist/tools/plan.js.map +1 -0
  353. package/dist/tools/prepareIosTarget.js +99 -0
  354. package/dist/tools/prepareIosTarget.js.map +1 -0
  355. package/dist/tools/prepareTarget.js +257 -0
  356. package/dist/tools/prepareTarget.js.map +1 -0
  357. package/dist/tools/report.js +42 -0
  358. package/dist/tools/report.js.map +1 -0
  359. package/dist/tools/screenRecord.js +24 -0
  360. package/dist/tools/screenRecord.js.map +1 -0
  361. package/dist/tools/screenshot.js +63 -0
  362. package/dist/tools/screenshot.js.map +1 -0
  363. package/dist/tools/smoke.js +39 -0
  364. package/dist/tools/smoke.js.map +1 -0
  365. package/dist/tools/snapshot.js +129 -0
  366. package/dist/tools/snapshot.js.map +1 -0
  367. package/dist/tools/startSession.js +169 -0
  368. package/dist/tools/startSession.js.map +1 -0
  369. package/dist/tools/state.js +87 -0
  370. package/dist/tools/state.js.map +1 -0
  371. package/dist/tools/suite.js +412 -0
  372. package/dist/tools/suite.js.map +1 -0
  373. package/dist/tools/testThis.js +774 -0
  374. package/dist/tools/testThis.js.map +1 -0
  375. package/dist/tools/wda.js +477 -0
  376. package/dist/tools/wda.js.map +1 -0
  377. package/dist/version.js +62 -0
  378. package/dist/version.js.map +1 -0
  379. package/dist/visual/ocr.js +72 -0
  380. package/dist/visual/ocr.js.map +1 -0
  381. package/dist/visual/provider.js +75 -0
  382. package/dist/visual/provider.js.map +1 -0
  383. package/docs/README.md +19 -0
  384. package/docs/assets/swipium-lockup-ink-on-light.png +0 -0
  385. package/docs/mcp-server.md +135 -0
  386. package/docs/tools.md +130 -0
  387. package/package.json +70 -0
@@ -0,0 +1,176 @@
1
+ // POM → Flow V2 compiler (hardening P0.4). Generated POM suites reference page-object elements by
2
+ // name; this resolves those refs against the page objects and emits Flow V2 YAML that parseFlow()
3
+ // accepts and qa_flow_run / `swipium ci` can execute. Without this, a generated suite is only
4
+ // documentation — with it, a recorded run becomes a runnable, CI-usable flow.
5
+ //
6
+ // PURE: reads YAML files, no device. The compiler output is validated by the caller via parseFlow().
7
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { parse as parseYaml, stringify } from 'yaml';
10
+ import { waitForVisibleGuard } from '../flows/waitGuards.js';
11
+ /** Load all page objects under .swipium/pages, keyed by their declared `name`. */
12
+ export function loadPages(root) {
13
+ const dir = join(root, '.swipium', 'pages');
14
+ const out = new Map();
15
+ if (!existsSync(dir))
16
+ return out;
17
+ for (const f of readdirSync(dir)) {
18
+ if (!f.endsWith('.page.yaml'))
19
+ continue;
20
+ try {
21
+ const doc = parseYaml(readFileSync(join(dir, f), 'utf8'));
22
+ if (doc?.name)
23
+ out.set(doc.name, doc);
24
+ }
25
+ catch {
26
+ /* skip unparseable page */
27
+ }
28
+ }
29
+ return out;
30
+ }
31
+ /** Turn a page element's locator into a Flow V2 selector string (mirrors flow generate). */
32
+ export function elementSelector(el) {
33
+ if (!el)
34
+ return null;
35
+ if (typeof el['accessibility id'] === 'string')
36
+ return `accessibility id=${el['accessibility id']}`;
37
+ // Android resource id → `id=<value>`: the Flow V2 runner resolves this via resolveTarget({ id }).
38
+ // (A bare `resource-id=` prefix is NOT parsed by the runner and would fall back to a text match.)
39
+ if (typeof el['resource-id'] === 'string')
40
+ return `id=${el['resource-id']}`;
41
+ if (typeof el.name === 'string')
42
+ return `name=${el.name}`;
43
+ if (typeof el.predicate === 'string')
44
+ return `predicate string=${el.predicate}`;
45
+ if (typeof el['class chain'] === 'string')
46
+ return `class chain=${el['class chain']}`;
47
+ if (typeof el.text === 'string')
48
+ return el.text; // bare text selector
49
+ if (typeof el.fallbackText === 'string')
50
+ return el.fallbackText;
51
+ return null;
52
+ }
53
+ /** Compile one POM test doc into a Flow V2 flow object + variables + per-step errors. */
54
+ export function compileTest(testDoc, pages) {
55
+ const errors = [];
56
+ const variables = new Set();
57
+ const steps = ['prepareTarget'];
58
+ const resolveSel = (s) => {
59
+ if (!s.element)
60
+ return null;
61
+ const page = s.page ? pages.get(s.page) : undefined;
62
+ if (!page) {
63
+ errors.push(`step references unknown page "${s.page}"`);
64
+ return null;
65
+ }
66
+ const el = page.elements?.[s.element];
67
+ const sel = elementSelector(el);
68
+ if (!sel)
69
+ errors.push(`page "${s.page}" has no usable locator for element "${s.element}"`);
70
+ return sel;
71
+ };
72
+ const rawSteps = testDoc.steps ?? [];
73
+ rawSteps.forEach((s, i) => {
74
+ const collectVars = (t) => {
75
+ if (t)
76
+ for (const m of t.matchAll(/\$\{([^}]+)\}/g))
77
+ variables.add(m[1]);
78
+ };
79
+ switch (s.action) {
80
+ case 'tap':
81
+ if (s.element) {
82
+ const sel = resolveSel(s);
83
+ if (sel)
84
+ steps.push(waitForVisibleGuard(sel), { tap: sel });
85
+ }
86
+ else if (s.coords) {
87
+ steps.push({ tapAt: s.coords });
88
+ }
89
+ else {
90
+ errors.push(`step ${i}: tap has neither element nor coords`);
91
+ }
92
+ break;
93
+ case 'tapAt':
94
+ if (s.coords)
95
+ steps.push({ tapAt: s.coords });
96
+ break;
97
+ case 'inputText': {
98
+ collectVars(s.text);
99
+ const sel = s.element ? resolveSel(s) : null;
100
+ if (s.element && sel)
101
+ steps.push(waitForVisibleGuard(sel), { inputText: { into: sel, text: s.text ?? '', ...(s.secret ? { secret: true } : {}) } });
102
+ else
103
+ steps.push({ inputText: s.text ?? '' });
104
+ break;
105
+ }
106
+ case 'press':
107
+ steps.push({ press: s.key ?? 'back' });
108
+ break;
109
+ case 'swipe':
110
+ steps.push({ swipe: s.direction ?? 'up' });
111
+ break;
112
+ case 'scrollTo': {
113
+ const sel = resolveSel(s);
114
+ if (sel)
115
+ steps.push({ scrollTo: sel });
116
+ break;
117
+ }
118
+ case 'openUrl':
119
+ if (s.url)
120
+ steps.push({ openUrl: s.url });
121
+ break;
122
+ case 'assertVisible':
123
+ if (s.text)
124
+ steps.push(waitForVisibleGuard(s.text), { assertVisible: s.text });
125
+ break;
126
+ default:
127
+ errors.push(`step ${i}: unknown action "${s.action}"`);
128
+ }
129
+ });
130
+ const flow = { name: testDoc.name ?? 'compiled' };
131
+ if (testDoc.appId)
132
+ flow.appId = testDoc.appId;
133
+ if (testDoc.budgetProfile)
134
+ flow.budgetProfile = testDoc.budgetProfile;
135
+ flow.steps = steps;
136
+ return { flow, variables: [...variables], errors };
137
+ }
138
+ /** Load a POM test file (tests/*.smoke.yaml) and compile it to Flow V2 YAML. */
139
+ export function compileTestFile(root, testRel, pages) {
140
+ const abs = join(root, '.swipium', testRel.replace(/^\.swipium\//, ''));
141
+ if (!existsSync(abs))
142
+ return { name: testRel, yaml: '', variables: [], errors: [`test file not found: ${testRel}`] };
143
+ let doc;
144
+ try {
145
+ doc = parseYaml(readFileSync(abs, 'utf8'));
146
+ }
147
+ catch (e) {
148
+ return { name: testRel, yaml: '', variables: [], errors: [`could not parse ${testRel}: ${String(e.message ?? e)}`] };
149
+ }
150
+ const compiled = compileTest(doc, pages ?? loadPages(root));
151
+ const header = [
152
+ '# Compiled by Swipium qa_suite_compile from a POM test — runnable via qa_flow_run / `swipium ci`.',
153
+ ...(compiled.variables.length ? [`# variables: ${compiled.variables.join(', ')}`] : []),
154
+ ].join('\n');
155
+ return { name: doc.name ?? testRel, yaml: `${header}\n${stringify(compiled.flow)}`, variables: compiled.variables, errors: compiled.errors };
156
+ }
157
+ /** Compile every test referenced by a suite file (default .swipium/suites/smoke.yaml). */
158
+ export function compileSuite(root, suiteRel = 'suites/smoke.yaml') {
159
+ const abs = join(root, '.swipium', suiteRel.replace(/^\.swipium\//, ''));
160
+ if (!existsSync(abs))
161
+ return { suite: suiteRel, flows: [], errors: [`suite not found: ${suiteRel}`] };
162
+ let doc;
163
+ try {
164
+ doc = parseYaml(readFileSync(abs, 'utf8'));
165
+ }
166
+ catch (e) {
167
+ return { suite: suiteRel, flows: [], errors: [`could not parse suite: ${String(e.message ?? e)}`] };
168
+ }
169
+ const tests = doc.tests ?? [];
170
+ if (!tests.length)
171
+ return { suite: suiteRel, flows: [], errors: ['suite has no tests'] };
172
+ const pages = loadPages(root);
173
+ const flows = tests.map((t) => compileTestFile(root, t, pages));
174
+ return { suite: suiteRel, flows, errors: [] };
175
+ }
176
+ //# sourceMappingURL=compile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile.js","sourceRoot":"","sources":["../../src/suite/compile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ // Suite linting (roadmap §6 / §12 `swipium suite lint`) — flag page objects whose locators are
2
+ // brittle (coordinate-only), copy/locale-fragile (text), or dynamic-looking. Reads the generated
3
+ // .swipium/pages/*.page.yaml; shared by the suite CLI.
4
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+ import { parse as parseYaml } from 'yaml';
7
+ export function lintSuitePages(root) {
8
+ const pagesDir = join(root, '.swipium', 'pages');
9
+ if (!existsSync(pagesDir))
10
+ return { pagesDir, exists: false, pageCount: 0, items: [] };
11
+ const items = [];
12
+ let pageCount = 0;
13
+ for (const f of readdirSync(pagesDir)) {
14
+ if (!f.endsWith('.page.yaml'))
15
+ continue;
16
+ pageCount++;
17
+ let doc;
18
+ try {
19
+ doc = parseYaml(readFileSync(join(pagesDir, f), 'utf8'));
20
+ }
21
+ catch {
22
+ items.push({ page: f, element: '(file)', severity: 'error', message: 'could not parse page YAML' });
23
+ continue;
24
+ }
25
+ const pageName = doc?.name ?? f;
26
+ const elements = doc?.elements ?? {};
27
+ for (const [name, el] of Object.entries(elements)) {
28
+ const durability = el.durability;
29
+ const hasLocator = el['accessibility id'] || el['resource-id'] || el.text || el.name;
30
+ const readinessCode = typeof el.readinessCode === 'string' ? el.readinessCode : undefined;
31
+ if (durability === 'brittle' || !hasLocator) {
32
+ items.push({ page: pageName, element: name, severity: 'error', code: readinessCode ?? 'COORDINATE_ONLY', message: el.remediation ?? 'no durable locator (coordinate-only) — add a testID/accessibilityIdentifier' });
33
+ }
34
+ else if (durability === 'semi' || el.text) {
35
+ const code = readinessCode ?? ((el.text || el.name) && !el['accessibility id'] ? 'IOS_MISSING_ACCESSIBILITY_IDENTIFIER' : undefined);
36
+ items.push({ page: pageName, element: name, severity: 'warning', code, message: el.remediation ?? 'text/locale-fragile selector — add an accessibilityIdentifier/testID for CI-stable replay' });
37
+ }
38
+ if (typeof el.text === 'string' && /\d{3,}|[0-9a-f]{8,}/i.test(el.text)) {
39
+ items.push({ page: pageName, element: name, severity: 'warning', code: 'DYNAMIC_TEXT_LOCATOR', message: `selector "${el.text}" looks dynamic (contains digits/ids) — prefer a stable identifier` });
40
+ }
41
+ }
42
+ }
43
+ return { pagesDir, exists: true, pageCount, items };
44
+ }
45
+ //# sourceMappingURL=lint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.js","sourceRoot":"","sources":["../../src/suite/lint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,285 @@
1
+ // Screen/Page Object Model generation (roadmap §6) — turn a linear action recording into a
2
+ // MAINTAINABLE suite where selectors live in page objects and tests reference them by name,
3
+ // instead of duplicating raw selectors. PURE: no filesystem, no device — callers serialize the
4
+ // returned files. Also produces a locator audit (durable vs brittle) with app-code remediation,
5
+ // because durable automation is the point (§2.4 / §6 locator policy).
6
+ import { stringify } from 'yaml';
7
+ function durabilityOf(kind) {
8
+ switch (kind) {
9
+ case 'accessibility_id':
10
+ case 'resource_id':
11
+ return 'durable';
12
+ case 'name':
13
+ case 'predicate':
14
+ case 'class_chain':
15
+ case 'text':
16
+ return 'semi';
17
+ default:
18
+ return 'brittle'; // coords / unknown
19
+ }
20
+ }
21
+ function pascal(s) {
22
+ const parts = s
23
+ .replace(/[^A-Za-z0-9]+/g, ' ')
24
+ .trim()
25
+ .split(/\s+/)
26
+ .filter(Boolean);
27
+ return parts.map((p) => p[0].toUpperCase() + p.slice(1)).join('') || 'Screen';
28
+ }
29
+ function camel(s) {
30
+ const p = pascal(s);
31
+ return p ? p[0].toLowerCase() + p.slice(1) : 'el';
32
+ }
33
+ /** Page name from a foreground-owner string: strip android package, take a readable segment. */
34
+ function pageNameFromScreen(screen, index) {
35
+ if (!screen)
36
+ return `Screen${index + 1}Page`;
37
+ // android: com.app/.LoginActivity → LoginActivity → LoginPage
38
+ const activity = screen.includes('/') ? screen.split('/').pop() : screen.split('.').pop();
39
+ const base = activity.replace(/Activity$|ViewController$|Screen$/i, '');
40
+ return `${pascal(base || activity)}Page`;
41
+ }
42
+ /** Derive a meaningful secret variable name (P0.5) — reuses an existing ${VAR} the agent set,
43
+ * else names from the field (password/otp/token), else a numbered SWIPIUM_SECRET_N. */
44
+ function secretVarFor(a, index) {
45
+ const existing = a.text?.match(/^\$\{([^}]+)\}$/);
46
+ // Reuse an already-meaningful var; but rename a generic recorder placeholder (SECRET_N).
47
+ if (existing && !/^SECRET_\d+$/i.test(existing[1]))
48
+ return existing[1];
49
+ const key = `${a.selector ?? ''}`.toLowerCase();
50
+ if (/pass/.test(key))
51
+ return 'SWIPIUM_TEST_PASSWORD';
52
+ if (/otp|code|2fa|mfa/.test(key))
53
+ return 'SWIPIUM_TEST_OTP';
54
+ if (/token|api[_-]?key/.test(key))
55
+ return 'SWIPIUM_TEST_TOKEN';
56
+ if (/pin/.test(key))
57
+ return 'SWIPIUM_TEST_PIN';
58
+ return `SWIPIUM_SECRET_${index}`;
59
+ }
60
+ function remediationFor(el) {
61
+ if (el.selectorKind === 'coords') {
62
+ return `add a testID (RN) / accessibilityIdentifier (iOS) / android:contentDescription / Flutter Key + Semantics(identifier:) to the tapped element${el.screen ? ` on ${el.screen}` : ''} — it has no durable locator`;
63
+ }
64
+ if (el.selectorKind === 'text') {
65
+ return `text selector "${el.selector}" is locale/copy-fragile — add an accessibilityIdentifier/testID (or a Flutter Key/Semantics label) for CI-stable replay`;
66
+ }
67
+ if (el.selectorKind === 'name') {
68
+ return `iOS name selector "${el.selector}" can drift with labels/localization — add an accessibilityIdentifier for CI-stable replay`;
69
+ }
70
+ if (el.selectorKind === 'predicate' || el.selectorKind === 'class_chain') {
71
+ return `iOS ${el.selectorKind} selector "${el.selector}" is a fallback locator — prefer accessibilityIdentifier and avoid XPath-style hierarchy dependence`;
72
+ }
73
+ return undefined;
74
+ }
75
+ function auditCodeFor(el) {
76
+ if (el.selectorKind === 'coords')
77
+ return 'COORDINATE_ONLY';
78
+ if (el.selectorKind === 'text' || el.selectorKind === 'name' || el.selectorKind === 'predicate' || el.selectorKind === 'class_chain')
79
+ return 'IOS_MISSING_ACCESSIBILITY_IDENTIFIER';
80
+ return undefined;
81
+ }
82
+ /**
83
+ * Build the POM model + serialized files from a recording.
84
+ * - segments the recording into pages by screen (foreground owner);
85
+ * - hoists each distinct selector into a named page element (de-duped);
86
+ * - emits a test that references `page` + `element` (no raw selectors in the test);
87
+ * - turns secrets into ${VARS};
88
+ * - audits every locator's durability with app-code remediation.
89
+ */
90
+ export function generatePom(actions, opts) {
91
+ // 1. segment into pages by contiguous screen.
92
+ const pages = [];
93
+ const usedPageNames = new Set();
94
+ const pageByScreen = new Map();
95
+ const pageFor = (screen) => {
96
+ const key = screen ?? '__unknown__';
97
+ const existing = pageByScreen.get(key);
98
+ if (existing)
99
+ return existing;
100
+ let name = pageNameFromScreen(screen, pages.length);
101
+ let n = 2;
102
+ const baseName = name;
103
+ while (usedPageNames.has(name))
104
+ name = `${baseName.replace(/Page$/, '')}${n++}Page`;
105
+ usedPageNames.add(name);
106
+ const page = { name, screen, elements: [] };
107
+ pages.push(page);
108
+ pageByScreen.set(key, page);
109
+ return page;
110
+ };
111
+ const steps = [];
112
+ const variables = [];
113
+ let secretCount = 0;
114
+ const ensureElement = (page, a) => {
115
+ // Coordinate-only actions have no nameable element — keep as inline coords in the step.
116
+ if (!a.selector || a.selectorKind === 'coords')
117
+ return undefined;
118
+ const baseName = camel(a.selector);
119
+ let name = baseName || 'el';
120
+ // Reuse if an element with the same selector already exists on this page.
121
+ const match = page.elements.find((e) => e.selector === a.selector && e.selectorKind === a.selectorKind);
122
+ if (match)
123
+ return match.name;
124
+ let n = 2;
125
+ while (page.elements.some((e) => e.name === name))
126
+ name = `${baseName}${n++}`;
127
+ const durability = durabilityOf(a.selectorKind);
128
+ const readinessCode = auditCodeFor({ selectorKind: a.selectorKind ?? 'text' });
129
+ const el = {
130
+ name,
131
+ selector: a.selector,
132
+ selectorKind: a.selectorKind ?? 'text',
133
+ fallbackText: a.selectorKind === 'text' ? a.selector : undefined,
134
+ required: true,
135
+ secure: a.secret || undefined,
136
+ durability,
137
+ readinessCode,
138
+ remediation: remediationFor({ selectorKind: a.selectorKind ?? 'text', selector: a.selector, screen: a.screen }),
139
+ };
140
+ page.elements.push(el);
141
+ return name;
142
+ };
143
+ for (const a of actions) {
144
+ const page = pageFor(a.screen);
145
+ switch (a.action) {
146
+ case 'tap':
147
+ case 'clear': {
148
+ const element = ensureElement(page, a);
149
+ if (element)
150
+ steps.push({ page: page.name, element, action: a.action === 'clear' ? 'inputText' : 'tap', ...(a.action === 'clear' ? { text: '' } : {}) });
151
+ else
152
+ steps.push({ page: page.name, action: 'tap', coords: [a.x ?? 0, a.y ?? 0] });
153
+ break;
154
+ }
155
+ case 'type': {
156
+ const element = ensureElement(page, a);
157
+ let text = a.text ?? '';
158
+ if (a.secret) {
159
+ // Secret values become ${SWIPIUM_TEST_*} variables — never the raw value (P0.5).
160
+ const v = secretVarFor(a, ++secretCount);
161
+ text = `\${${v}}`;
162
+ variables.push(v);
163
+ }
164
+ else {
165
+ // A non-secret value the agent already templated (e.g. ${SWIPIUM_TEST_EMAIL}) is preserved.
166
+ for (const m of text.matchAll(/\$\{([^}]+)\}/g))
167
+ variables.push(m[1]);
168
+ }
169
+ steps.push({ page: page.name, element, action: 'inputText', text, secret: a.secret || undefined });
170
+ break;
171
+ }
172
+ case 'press':
173
+ steps.push({ page: page.name, action: 'press', key: a.key ?? 'back' });
174
+ break;
175
+ case 'swipe':
176
+ steps.push({ page: page.name, action: 'swipe', direction: a.direction ?? 'up' });
177
+ break;
178
+ case 'scroll': {
179
+ const element = ensureElement(page, a);
180
+ if (element)
181
+ steps.push({ page: page.name, element, action: 'scrollTo' });
182
+ else
183
+ steps.push({ page: page.name, action: 'swipe', direction: a.direction ?? 'up' });
184
+ break;
185
+ }
186
+ case 'open_url':
187
+ steps.push({ page: page.name, action: 'openUrl', url: a.url ?? '' });
188
+ break;
189
+ case 'assert_visual':
190
+ steps.push({ page: page.name, action: 'assertVisible', text: a.assertion ?? a.selector });
191
+ break;
192
+ }
193
+ }
194
+ // 2. audit.
195
+ const entries = [];
196
+ for (const p of pages) {
197
+ for (const e of p.elements) {
198
+ entries.push({ page: p.name, element: e.name, durability: e.durability, selectorKind: e.selectorKind, code: e.readinessCode, remediation: e.remediation });
199
+ }
200
+ }
201
+ // coordinate-only steps also count against durability.
202
+ for (const s of steps) {
203
+ if (!s.element && s.coords)
204
+ entries.push({ page: s.page, element: `(coords ${s.coords[0]},${s.coords[1]})`, durability: 'brittle', selectorKind: 'coords', code: 'COORDINATE_ONLY', remediation: remediationFor({ selectorKind: 'coords' }) });
205
+ }
206
+ const durable = entries.filter((e) => e.durability === 'durable').length;
207
+ const semi = entries.filter((e) => e.durability === 'semi').length;
208
+ const brittle = entries.filter((e) => e.durability === 'brittle').length;
209
+ const total = entries.length || 1;
210
+ const audit = { entries, durable, semi, brittle, brittlePct: Math.round((brittle / total) * 100) };
211
+ // 3. serialize files.
212
+ const files = [];
213
+ for (const p of pages) {
214
+ const elementsYaml = {};
215
+ for (const e of p.elements) {
216
+ const ev = {};
217
+ if (e.selectorKind === 'accessibility_id')
218
+ ev['accessibility id'] = e.selector;
219
+ else if (e.selectorKind === 'resource_id')
220
+ ev['resource-id'] = e.selector;
221
+ else if (e.selectorKind === 'text')
222
+ ev.text = e.selector;
223
+ else
224
+ ev[e.selectorKind] = e.selector;
225
+ if (e.fallbackText && e.selectorKind !== 'text')
226
+ ev.fallbackText = e.fallbackText;
227
+ if (e.required)
228
+ ev.required = true;
229
+ if (e.secure)
230
+ ev.secure = true;
231
+ ev.durability = e.durability;
232
+ if (e.readinessCode)
233
+ ev.readinessCode = e.readinessCode;
234
+ if (e.remediation)
235
+ ev.remediation = e.remediation;
236
+ elementsYaml[e.name] = ev;
237
+ }
238
+ const pageObj = { name: p.name, platforms: ['android', 'ios'], screen: p.screen ?? undefined, elements: elementsYaml };
239
+ files.push({ path: `pages/${kebab(p.name)}.page.yaml`, content: `# Swipium page object — review before committing.\n${stringify(pageObj)}` });
240
+ }
241
+ const testObj = { name: opts.name };
242
+ if (opts.appId)
243
+ testObj.appId = opts.appId;
244
+ if (opts.budgetProfile)
245
+ testObj.budgetProfile = opts.budgetProfile;
246
+ testObj.uses = pages.map((p) => `pages/${kebab(p.name)}.page.yaml`);
247
+ testObj.steps = steps.map(serializeStep);
248
+ const testHeader = [
249
+ '# Swipium test (POM) — selectors live in page objects; this test references them by name.',
250
+ ...(variables.length ? [`# variables — provide via qa_flow_run { variables }: ${variables.join(', ')}`] : []),
251
+ `# locator durability: ${durable} durable / ${semi} semi / ${brittle} brittle (${audit.brittlePct}% brittle)`,
252
+ ].join('\n');
253
+ files.push({ path: `tests/${kebab(opts.name)}.smoke.yaml`, content: `${testHeader}\n${stringify(testObj)}` });
254
+ const suiteObj = { name: 'smoke', tests: [`tests/${kebab(opts.name)}.smoke.yaml`] };
255
+ files.push({ path: 'suites/smoke.yaml', content: `# Swipium suite.\n${stringify(suiteObj)}` });
256
+ files.push({ path: 'locators/locator-audit.json', content: JSON.stringify(audit, null, 2) });
257
+ return { pages, testName: opts.name, steps, variables: [...new Set(variables)], audit, files };
258
+ }
259
+ function serializeStep(s) {
260
+ const ref = s.element ? { page: s.page, element: s.element } : { page: s.page };
261
+ switch (s.action) {
262
+ case 'tap':
263
+ return s.element ? { ...ref, action: 'tap' } : { ...ref, action: 'tapAt', coords: s.coords };
264
+ case 'inputText':
265
+ return { ...ref, action: 'inputText', text: s.text ?? '', ...(s.secret ? { secret: true } : {}) };
266
+ case 'press':
267
+ return { ...ref, action: 'press', key: s.key };
268
+ case 'swipe':
269
+ return { ...ref, action: 'swipe', direction: s.direction };
270
+ case 'scrollTo':
271
+ return { ...ref, action: 'scrollTo' };
272
+ case 'openUrl':
273
+ return { ...ref, action: 'openUrl', url: s.url };
274
+ case 'assertVisible':
275
+ return { ...ref, action: 'assertVisible', text: s.text };
276
+ }
277
+ }
278
+ function kebab(s) {
279
+ return s
280
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
281
+ .replace(/[^A-Za-z0-9]+/g, '-')
282
+ .replace(/^-+|-+$/g, '')
283
+ .toLowerCase();
284
+ }
285
+ //# sourceMappingURL=pom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pom.js","sourceRoot":"","sources":["../../src/suite/pom.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,4FAA4F;AAC5F,+FAA+F;AAC/F,gGAAgG;AAChG,sEAAsE;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AA6DjC,SAAS,YAAY,CAAC,IAAa;IACjC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,kBAAkB,CAAC;QACxB,KAAK,aAAa;YAChB,OAAO,SAAS,CAAC;QACnB,KAAK,MAAM,CAAC;QACZ,KAAK,WAAW,CAAC;QACjB,KAAK,aAAa,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB;YACE,OAAO,SAAS,CAAC,CAAC,mBAAmB;IACzC,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,CAAS;IACvB,MAAM,KAAK,GAAG,CAAC;SACZ,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC;SAC9B,IAAI,EAAE;SACN,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC;AAChF,CAAC;AAED,SAAS,KAAK,CAAC,CAAS;IACtB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC;AAED,gGAAgG;AAChG,SAAS,kBAAkB,CAAC,MAA0B,EAAE,KAAa;IACnE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,KAAK,GAAG,CAAC,MAAM,CAAC;IAC7C,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC;IAC5F,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,oCAAoC,EAAE,EAAE,CAAC,CAAC;IACxE,OAAO,GAAG,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,MAAM,CAAC;AAC3C,CAAC;AAED;wFACwF;AACxF,SAAS,YAAY,CAAC,CAAuC,EAAE,KAAa;IAC1E,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAClD,yFAAyF;IACzF,IAAI,QAAQ,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC;IACvE,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC;IAChD,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,uBAAuB,CAAC;IACrD,IAAI,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,kBAAkB,CAAC;IAC5D,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,oBAAoB,CAAC;IAC/D,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,kBAAkB,CAAC;IAC/C,OAAO,kBAAkB,KAAK,EAAE,CAAC;AACnC,CAAC;AAED,SAAS,cAAc,CAAC,EAAgE;IACtF,IAAI,EAAE,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,8IAA8I,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,8BAA8B,CAAC;IACzN,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;QAC/B,OAAO,kBAAkB,EAAE,CAAC,QAAQ,0HAA0H,CAAC;IACjK,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,KAAK,MAAM,EAAE,CAAC;QAC/B,OAAO,sBAAsB,EAAE,CAAC,QAAQ,4FAA4F,CAAC;IACvI,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,KAAK,WAAW,IAAI,EAAE,CAAC,YAAY,KAAK,aAAa,EAAE,CAAC;QACzE,OAAO,OAAO,EAAE,CAAC,YAAY,cAAc,EAAE,CAAC,QAAQ,qGAAqG,CAAC;IAC9J,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,YAAY,CAAC,EAA4B;IAChD,IAAI,EAAE,CAAC,YAAY,KAAK,QAAQ;QAAE,OAAO,iBAAiB,CAAC;IAC3D,IAAI,EAAE,CAAC,YAAY,KAAK,MAAM,IAAI,EAAE,CAAC,YAAY,KAAK,MAAM,IAAI,EAAE,CAAC,YAAY,KAAK,WAAW,IAAI,EAAE,CAAC,YAAY,KAAK,aAAa;QAAE,OAAO,sCAAsC,CAAC;IACpL,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,OAAyB,EACzB,IAA8D;IAE9D,8CAA8C;IAC9C,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;IACxC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAmB,CAAC;IAEhD,MAAM,OAAO,GAAG,CAAC,MAA0B,EAAW,EAAE;QACtD,MAAM,GAAG,GAAG,MAAM,IAAI,aAAa,CAAC;QACpC,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,IAAI,IAAI,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,QAAQ,GAAG,IAAI,CAAC;QACtB,OAAO,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,IAAI,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QACpF,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,IAAI,GAAY,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QACrD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,YAAY,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,MAAM,aAAa,GAAG,CAAC,IAAa,EAAE,CAAiB,EAAsB,EAAE;QAC7E,wFAAwF;QACxF,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,YAAY,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACjE,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;QACnC,IAAI,IAAI,GAAG,QAAQ,IAAI,IAAI,CAAC;QAC5B,0EAA0E;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC;QACxG,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC;YAAE,IAAI,GAAG,GAAG,QAAQ,GAAG,CAAC,EAAE,EAAE,CAAC;QAC9E,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAChD,MAAM,aAAa,GAAG,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,MAAM,EAAE,CAAC,CAAC;QAC/E,MAAM,EAAE,GAAe;YACrB,IAAI;YACJ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,MAAM;YACtC,YAAY,EAAE,CAAC,CAAC,YAAY,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;YAChE,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,SAAS;YAC7B,UAAU;YACV,aAAa;YACb,WAAW,EAAE,cAAc,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;SAChH,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC/B,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;YACjB,KAAK,KAAK,CAAC;YACX,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBACvC,IAAI,OAAO;oBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;;oBACpJ,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;gBAClF,MAAM;YACR,CAAC;YACD,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBACvC,IAAI,IAAI,GAAG,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;oBACb,iFAAiF;oBACjF,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;oBACzC,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC;oBAClB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACpB,CAAC;qBAAM,CAAC;oBACN,4FAA4F;oBAC5F,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC;wBAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACxE,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;gBACnG,MAAM;YACR,CAAC;YACD,KAAK,OAAO;gBACV,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,MAAM,EAAE,CAAC,CAAC;gBACvE,MAAM;YACR,KAAK,OAAO;gBACV,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC;gBACjF,MAAM;YACR,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBACvC,IAAI,OAAO;oBAAE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;;oBACrE,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC;gBACtF,MAAM;YACR,CAAC;YACD,KAAK,UAAU;gBACb,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;gBACrE,MAAM;YACR,KAAK,eAAe;gBAClB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC1F,MAAM;QACV,CAAC;IACH,CAAC;IAED,YAAY;IACZ,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,aAAa,EAAE,WAAW,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7J,CAAC;IACH,CAAC;IACD,uDAAuD;IACvD,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,iBAAiB,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC;IACjP,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACnE,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IACzE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IAClC,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC;IAEnG,sBAAsB;IACtB,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,MAAM,YAAY,GAA4B,EAAE,CAAC;QACjD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,EAAE,GAA4B,EAAE,CAAC;YACvC,IAAI,CAAC,CAAC,YAAY,KAAK,kBAAkB;gBAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;iBAC1E,IAAI,CAAC,CAAC,YAAY,KAAK,aAAa;gBAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;iBACrE,IAAI,CAAC,CAAC,YAAY,KAAK,MAAM;gBAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC;;gBACpD,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;YACrC,IAAI,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,YAAY,KAAK,MAAM;gBAAE,EAAE,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;YAClF,IAAI,CAAC,CAAC,QAAQ;gBAAE,EAAE,CAAC,QAAQ,GAAG,IAAI,CAAC;YACnC,IAAI,CAAC,CAAC,MAAM;gBAAE,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC;YAC/B,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;YAC7B,IAAI,CAAC,CAAC,aAAa;gBAAE,EAAE,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,CAAC;YACxD,IAAI,CAAC,CAAC,WAAW;gBAAE,EAAE,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC;YAClD,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,CAAC;QACD,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,SAAS,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;QACvH,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,EAAE,sDAAsD,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAChJ,CAAC;IAED,MAAM,OAAO,GAA4B,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC7D,IAAI,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IAC3C,IAAI,IAAI,CAAC,aAAa;QAAE,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,CAAC;IACnE,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACpE,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACzC,MAAM,UAAU,GAAG;QACjB,2FAA2F;QAC3F,GAAG,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,wDAAwD,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7G,yBAAyB,OAAO,cAAc,IAAI,WAAW,OAAO,aAAa,KAAK,CAAC,UAAU,YAAY;KAC9G,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,EAAE,GAAG,UAAU,KAAK,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAE9G,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;IACpF,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,qBAAqB,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;IAE/F,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,6BAA6B,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAE7F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AACjG,CAAC;AAED,SAAS,aAAa,CAAC,CAAc;IACnC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChF,QAAQ,CAAC,CAAC,MAAM,EAAE,CAAC;QACjB,KAAK,KAAK;YACR,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;QAC/F,KAAK,WAAW;YACd,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;QACpG,KAAK,OAAO;YACV,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;QACjD,KAAK,OAAO;YACV,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,CAAC;QAC7D,KAAK,UAAU;YACb,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;QACxC,KAAK,SAAS;YACZ,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;QACnD,KAAK,eAAe;YAClB,OAAO,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7D,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,CAAS;IACtB,OAAO,CAAC;SACL,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC;SACtC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC;SAC9B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,WAAW,EAAE,CAAC;AACnB,CAAC"}
@@ -0,0 +1,87 @@
1
+ // Test-case documentation (roadmap §7) — turn a generated POM suite + the session's observed
2
+ // state into an industry-style test case catalog (TC-xxx) with preconditions, steps, expected
3
+ // result, automation status, known blockers, and last-run evidence. PURE; callers serialize.
4
+ import { stringify } from 'yaml';
5
+ function humanStep(s) {
6
+ switch (s.action) {
7
+ case 'tap':
8
+ return s.element ? `Tap ${s.element} on ${s.page}` : `Tap at (${s.coords?.[0]},${s.coords?.[1]}) on ${s.page}`;
9
+ case 'inputText':
10
+ return `Enter ${s.secret ? 'secret value' : `"${s.text}"`} into ${s.element ?? 'the focused field'} on ${s.page}`;
11
+ case 'press':
12
+ return `Press ${s.key}`;
13
+ case 'swipe':
14
+ return `Swipe ${s.direction} on ${s.page}`;
15
+ case 'scrollTo':
16
+ return `Scroll to ${s.element} on ${s.page}`;
17
+ case 'openUrl':
18
+ return `Open ${s.url}`;
19
+ case 'assertVisible':
20
+ return `Verify "${s.text}" is visible on ${s.page}`;
21
+ }
22
+ }
23
+ export function generateTestCases(pom, opts) {
24
+ const platform = opts.platform ?? 'android+ios';
25
+ const fixtures = opts.fixtures ?? [];
26
+ const notes = opts.notes ?? [];
27
+ const replayStatus = opts.replayStatus ?? 'not_replayed';
28
+ // Automation status from the locator audit: any brittle locator → partial, else automated.
29
+ const status = pom.audit.brittle > 0 ? 'partial' : 'automated';
30
+ const knownBlockers = notes
31
+ .filter((n) => n.outcome === 'blocked' || n.category === 'missing_test_data')
32
+ .map((n) => `${n.workflow}: ${n.reason ?? n.missingPrecondition ?? 'blocked'}${n.recommendedSetup ? ` — ${n.recommendedSetup}` : ''}`);
33
+ const evidence = notes.flatMap((n) => n.artifactUris ?? []);
34
+ const verifiedVisualOnly = notes.some((n) => n.method === 'visual' || n.verifiedVisually);
35
+ const expected = pom.steps
36
+ .filter((s) => s.action === 'assertVisible' && s.text)
37
+ .map((s) => `${s.text} is visible`);
38
+ if (expected.length === 0)
39
+ expected.push('App reaches the expected post-flow screen without an error surface');
40
+ // Actual result = what THIS run observed (Deliverable 4). Kept separate from `expected` so a
41
+ // reviewer/QA can compare intent vs. outcome. When the flow was only recorded (not executed as a
42
+ // discrete test), say so honestly rather than implying a pass.
43
+ const actualResult = notes.length
44
+ ? notes.map((n) => `${n.workflow}: ${n.outcome}${n.reason ? ` — ${n.reason}` : ''}`)
45
+ : ['Not executed as a discrete test in this run — generated from recorded actions; replay to capture an actual result.'];
46
+ const tc = {
47
+ id: `TC-${(opts.appId ?? 'APP').split('.').pop().slice(0, 6).toUpperCase()}-001`,
48
+ title: pom.testName.replace(/[-_]/g, ' '),
49
+ purpose: `Verify the "${pom.testName}" flow end-to-end.`,
50
+ priority: 'P0',
51
+ platform,
52
+ preconditions: ['app installed', ...(opts.appId ? [`app id ${opts.appId}`] : []), ...fixtures.map((f) => f.requiredState ?? f.name)],
53
+ fixtures: fixtures.map((f) => f.name),
54
+ steps: pom.steps.map(humanStep),
55
+ expected,
56
+ actualResult,
57
+ cleanup: ['return to home/initial screen', ...(verifiedVisualOnly ? [] : [])],
58
+ risks: [
59
+ ...(pom.audit.brittle > 0 ? [`${pom.audit.brittle} brittle locator(s) — flow may break on UI changes (see locator audit)`] : []),
60
+ ...(verifiedVisualOnly ? ['some verification was visual-only — weaker than a structured assertion'] : []),
61
+ ...(pom.variables.length ? [`requires test data: ${pom.variables.join(', ')}`] : []),
62
+ ],
63
+ automation: { status, test: `tests/${kebab(pom.testName)}.smoke.yaml`, pageObjects: pom.pages.map((p) => `pages/${kebab(p.name)}.page.yaml`) },
64
+ replayStatus,
65
+ knownBlockers,
66
+ evidence,
67
+ };
68
+ const cases = [tc];
69
+ const yaml = `# Swipium test case catalog.\n${stringify({ cases })}`;
70
+ const markdown = renderMarkdown(cases);
71
+ return { cases, yaml, markdown };
72
+ }
73
+ function renderMarkdown(cases) {
74
+ const out = ['# Test Cases', ''];
75
+ for (const c of cases) {
76
+ out.push(`## ${c.id}: ${c.title}`, ``, `- **Priority:** ${c.priority} **Platform:** ${c.platform} **Automation:** ${c.automation.status}`, `- **Purpose:** ${c.purpose}`, ``, `**Preconditions:**`, ...c.preconditions.map((p) => `- ${p}`), ``, `**Steps:**`, ...c.steps.map((s, i) => `${i + 1}. ${s}`), ``, `**Expected:**`, ...c.expected.map((e) => `- ${e}`), ``, `**Actual result:**`, ...c.actualResult.map((e) => `- ${e}`), ``, `- **Replay status:** ${c.replayStatus}`, ...(c.evidence.length ? [`- **Evidence:** ${c.evidence.join(', ')}`] : []), ...(c.cleanup.length ? [`- **Cleanup:** ${c.cleanup.join('; ')}`] : []), ...(c.risks.length ? [``, `**Risks:**`, ...c.risks.map((r) => `- ${r}`)] : []), ...(c.knownBlockers.length ? [``, `**Known blockers:**`, ...c.knownBlockers.map((b) => `- ${b}`)] : []), ...(c.automation.pageObjects.length ? [``, `**Page objects:** ${c.automation.pageObjects.join(', ')}`] : []), ``);
77
+ }
78
+ return out.join('\n');
79
+ }
80
+ function kebab(s) {
81
+ return s
82
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
83
+ .replace(/[^A-Za-z0-9]+/g, '-')
84
+ .replace(/^-+|-+$/g, '')
85
+ .toLowerCase();
86
+ }
87
+ //# sourceMappingURL=testcase.js.map