@wenathlan/devthink 2.0.15 → 2.0.19

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 (444) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/{dist/bridge.d.ts → bridge.d.ts} +1 -1
  3. package/bridge.d.ts.map +1 -0
  4. package/bridge.js +1877 -0
  5. package/bun.d.ts.map +1 -0
  6. package/bun.js +52329 -0
  7. package/caps/background.json +712 -0
  8. package/caps/cli.json +360 -0
  9. package/caps/library.json +3611 -0
  10. package/caps/mcp.json +126 -0
  11. package/caps/pagebridge.json +377 -0
  12. package/caps/popup.json +51 -0
  13. package/caps/sidepanel.json +522 -0
  14. package/checksums.txt +142 -0
  15. package/cli.d.ts.map +1 -0
  16. package/{dist/cli.js → cli.js} +2 -2
  17. package/companion.d.ts.map +1 -0
  18. package/companion.js +389 -0
  19. package/crossbrowser.d.ts.map +1 -0
  20. package/crossbrowser.js +1191 -0
  21. package/dashdone.d.ts.map +1 -0
  22. package/dashdone.js +368 -0
  23. package/deno.d.ts.map +1 -0
  24. package/deno.js +52325 -0
  25. package/devthink.umd.js +52346 -0
  26. package/fixtures/bun.ts +7 -0
  27. package/fixtures/cjs.js +6 -0
  28. package/fixtures/deno.ts +6 -0
  29. package/fixtures/example-org-pagestate.json +22 -0
  30. package/fixtures/importers/automa-workflow.json +18 -0
  31. package/fixtures/importers/selenium-side.json +20 -0
  32. package/fixtures/importers/tabular-plan.csv +7 -0
  33. package/fixtures/importers/uivision-macro.json +10 -0
  34. package/fixtures/importers/v1-plan.json +13 -0
  35. package/fixtures/mcp-client.mjs +14 -0
  36. package/fixtures/pages/checkout.html +192 -0
  37. package/fixtures/pages/dashboard.html +110 -0
  38. package/fixtures/pages/feed.html +113 -0
  39. package/fixtures/pages/product-grid.html +150 -0
  40. package/fixtures/plans/form-inventory-plan.json +11 -0
  41. package/fixtures/plans/release-notes-plan.json +11 -0
  42. package/fixtures/recipes/agentscompete.json +11 -0
  43. package/fixtures/recipes/agentsforms.json +11 -0
  44. package/fixtures/recipes/agentsmonitor.json +11 -0
  45. package/fixtures/recipes/agentsreview.json +29 -0
  46. package/fixtures/recipes/agentswarm.json +11 -0
  47. package/fixtures/recipes/fillcalendar.json +12 -0
  48. package/fixtures/recipes/fillcheckout.json +13 -0
  49. package/fixtures/recipes/filldependentdropdowns.json +12 -0
  50. package/fixtures/recipes/fillfromcsv.json +13 -0
  51. package/fixtures/recipes/fillloginform.json +12 -0
  52. package/fixtures/recipes/fillmultistepwizard.json +13 -0
  53. package/fixtures/recipes/fillregistration.json +12 -0
  54. package/fixtures/recipes/fillsearchfilters.json +12 -0
  55. package/fixtures/recipes/filltypeahead.json +11 -0
  56. package/fixtures/recipes/monitoralerts.json +12 -0
  57. package/fixtures/recipes/monitoravailability.json +11 -0
  58. package/fixtures/recipes/monitornetworkquiet.json +12 -0
  59. package/fixtures/recipes/monitorpagechanges.json +11 -0
  60. package/fixtures/recipes/monitorpricechange.json +11 -0
  61. package/fixtures/recipes/monitorschedule.json +12 -0
  62. package/fixtures/recipes/scrapedynamicprices.json +13 -0
  63. package/fixtures/recipes/scrapeimagegallery.json +12 -0
  64. package/fixtures/recipes/scrapeinfinitefeed.json +13 -0
  65. package/fixtures/recipes/scrapenestedlists.json +12 -0
  66. package/fixtures/recipes/scrapeproductgrid.json +12 -0
  67. package/fixtures/recipes/scrapereviews.json +12 -0
  68. package/fixtures/recipes/scrapesearchresults.json +12 -0
  69. package/fixtures/recipes/scrapetablepagination.json +12 -0
  70. package/fixtures/recipes/testaccessibility.json +34 -0
  71. package/fixtures/recipes/testconsoleerrors.json +12 -0
  72. package/fixtures/recipes/testdeeplinks.json +12 -0
  73. package/fixtures/recipes/testformvalidation.json +13 -0
  74. package/fixtures/recipes/testlayoutshift.json +12 -0
  75. package/fixtures/recipes/testlinkcheck.json +12 -0
  76. package/fixtures/recipes/testloadtiming.json +12 -0
  77. package/fixtures/recipes/testvisualdiff.json +12 -0
  78. package/fixtures/workflows/changelog-digest-workflow.json +13 -0
  79. package/fixtures/workflows/form-survey-workflow.json +13 -0
  80. package/gallery.json +706 -0
  81. package/hardening.d.ts.map +1 -0
  82. package/hardening.js +188 -0
  83. package/headless.d.ts.map +1 -0
  84. package/headless.js +11522 -0
  85. package/http.d.ts.map +1 -0
  86. package/http.js +2165 -0
  87. package/index.cjs +54682 -0
  88. package/index.d.ts.map +1 -0
  89. package/index.js +52310 -0
  90. package/index.neutral.js +46343 -0
  91. package/{web/manifest.json → manifest.json} +1 -1
  92. package/mcp.d.ts.map +1 -0
  93. package/mcp.js +20515 -0
  94. package/memory.d.ts.map +1 -0
  95. package/{memory.ts → memory.js} +2310 -3834
  96. package/nativehost.template.json +10 -0
  97. package/node.cjs +54686 -0
  98. package/node.d.ts.map +1 -0
  99. package/pack.d.ts.map +1 -0
  100. package/pack.js +805 -0
  101. package/package.json +83 -176
  102. package/policy.d.ts.map +1 -0
  103. package/{policy.ts → policy.js} +4679 -5073
  104. package/progress.d.ts.map +1 -0
  105. package/progress.js +645 -0
  106. package/protocol.d.ts.map +1 -0
  107. package/protocol.js +8334 -0
  108. package/schemas/audit.schema.json +226 -0
  109. package/schemas/capability.schema.json +113 -0
  110. package/schemas/envelope.schema.json +162 -0
  111. package/schemas/memory.schema.json +51 -0
  112. package/schemas/observation.schema.json +113 -0
  113. package/schemas/plan.schema.json +208 -0
  114. package/schemas/progress.schema.json +126 -0
  115. package/schemas/proposal.schema.json +68 -0
  116. package/schemas/session.schema.json +82 -0
  117. package/schemas/tool.schema.json +94 -0
  118. package/server.d.ts.map +1 -0
  119. package/server.js +28877 -0
  120. package/umd-example.html +18 -0
  121. package/umd.d.ts.map +1 -0
  122. package/accounts.ts +0 -2087
  123. package/agent.ts +0 -2331
  124. package/antigravity.ts +0 -2139
  125. package/apifreeze.ts +0 -4146
  126. package/auth.ts +0 -642
  127. package/background.ts +0 -16837
  128. package/bridge.ts +0 -2080
  129. package/build.ts +0 -54
  130. package/bun.ts +0 -26
  131. package/capture.ts +0 -1614
  132. package/checksecrets.ts +0 -72
  133. package/cjs.ts +0 -9
  134. package/cli.ts +0 -3500
  135. package/commands.ts +0 -1101
  136. package/companion.ts +0 -450
  137. package/compatibility.ts +0 -37
  138. package/config.ts +0 -3137
  139. package/constants.ts +0 -943
  140. package/core.ts +0 -1473
  141. package/crossbrowser.ts +0 -1359
  142. package/dashdone.ts +0 -605
  143. package/data.ts +0 -943
  144. package/database.ts +0 -133
  145. package/debug.ts +0 -850
  146. package/deno.ts +0 -27
  147. package/devthink.ts +0 -707
  148. package/dist/accounts.d.ts +0 -409
  149. package/dist/agent.d.ts +0 -837
  150. package/dist/antigravity.d.ts +0 -380
  151. package/dist/apifreeze.d.ts +0 -113
  152. package/dist/auth.d.ts +0 -224
  153. package/dist/capture.d.ts +0 -595
  154. package/dist/cjs.d.ts +0 -6
  155. package/dist/commands.d.ts +0 -310
  156. package/dist/compatibility.d.ts +0 -17
  157. package/dist/config.d.ts +0 -1853
  158. package/dist/constants.d.ts +0 -637
  159. package/dist/core.d.ts +0 -361
  160. package/dist/data.d.ts +0 -330
  161. package/dist/database.d.ts +0 -24
  162. package/dist/debug.d.ts +0 -270
  163. package/dist/engine.d.ts +0 -74
  164. package/dist/environments.d.ts +0 -244
  165. package/dist/evidence.d.ts +0 -277
  166. package/dist/export.d.ts +0 -311
  167. package/dist/fingerprint.d.ts +0 -594
  168. package/dist/flow.d.ts +0 -300
  169. package/dist/gates.d.ts +0 -103
  170. package/dist/identity.d.ts +0 -51
  171. package/dist/ids.d.ts +0 -7
  172. package/dist/llm.d.ts +0 -652
  173. package/dist/models.d.ts +0 -1807
  174. package/dist/net.d.ts +0 -325
  175. package/dist/neutral.d.ts +0 -41
  176. package/dist/oauth.d.ts +0 -703
  177. package/dist/page.d.ts +0 -1166
  178. package/dist/perf.d.ts +0 -481
  179. package/dist/plan.d.ts +0 -134
  180. package/dist/project.d.ts +0 -395
  181. package/dist/providers.d.ts +0 -37
  182. package/dist/quota.d.ts +0 -453
  183. package/dist/recovery.d.ts +0 -621
  184. package/dist/request.d.ts +0 -1492
  185. package/dist/run.d.ts +0 -779
  186. package/dist/runtime.d.ts +0 -212
  187. package/dist/search.d.ts +0 -375
  188. package/dist/security.d.ts +0 -534
  189. package/dist/serve.d.ts +0 -136
  190. package/dist/session.d.ts +0 -296
  191. package/dist/storage.d.ts +0 -53
  192. package/dist/streaming.d.ts +0 -1119
  193. package/dist/swarm.d.ts +0 -328
  194. package/dist/tools.d.ts +0 -170
  195. package/dist/types.d.ts +0 -8357
  196. package/dist/utils.d.ts +0 -82
  197. package/dist/version.d.ts +0 -7
  198. package/dist/versionregistry.d.ts +0 -406
  199. package/dist/views.d.ts +0 -579
  200. package/dist/workbenchsession.d.ts +0 -69
  201. package/dist/workflow.d.ts +0 -649
  202. package/docs/readme.md +0 -92
  203. package/docs/schemas/antigravity.json +0 -192
  204. package/engine.ts +0 -1524
  205. package/environments.ts +0 -1005
  206. package/evidence.ts +0 -643
  207. package/export.ts +0 -889
  208. package/fingerprint.ts +0 -1458
  209. package/flow.ts +0 -1190
  210. package/gates.ts +0 -307
  211. package/gateway.ts +0 -34
  212. package/hardening.ts +0 -191
  213. package/headless.ts +0 -369
  214. package/http.ts +0 -2110
  215. package/identity.ts +0 -246
  216. package/ids.ts +0 -26
  217. package/index.ts +0 -369
  218. package/llm.ts +0 -2682
  219. package/mcp.ts +0 -1835
  220. package/models.ts +0 -3805
  221. package/modes.ts +0 -43
  222. package/net.ts +0 -1214
  223. package/neutral.ts +0 -362
  224. package/node.ts +0 -26
  225. package/oauth.ts +0 -2216
  226. package/offscreen.ts +0 -170
  227. package/pack.ts +0 -956
  228. package/page.ts +0 -7824
  229. package/pagebridge.ts +0 -1116
  230. package/perf.ts +0 -957
  231. package/plan.ts +0 -672
  232. package/plugin.ts +0 -76
  233. package/prisma.config.ts +0 -27
  234. package/progress.ts +0 -1308
  235. package/project.ts +0 -1291
  236. package/protocol.ts +0 -4187
  237. package/providers.ts +0 -246
  238. package/quota.ts +0 -1887
  239. package/recovery.ts +0 -2433
  240. package/request.ts +0 -4482
  241. package/run.ts +0 -2135
  242. package/runtime.ts +0 -526
  243. package/search.ts +0 -1760
  244. package/security.ts +0 -1673
  245. package/serve.ts +0 -419
  246. package/server.ts +0 -5538
  247. package/session.ts +0 -1162
  248. package/storage.ts +0 -182
  249. package/streaming.ts +0 -4101
  250. package/swarm.ts +0 -884
  251. package/sync.ts +0 -56
  252. package/tests/agentcert.test.ts +0 -128
  253. package/tests/agentfleet.test.ts +0 -214
  254. package/tests/agentreview.test.ts +0 -241
  255. package/tests/agentstream.test.ts +0 -335
  256. package/tests/agentwork.test.ts +0 -356
  257. package/tests/apifreeze.test.ts +0 -290
  258. package/tests/apimap.test.ts +0 -112
  259. package/tests/approvalgate.test.ts +0 -175
  260. package/tests/artifactmanifest.test.ts +0 -204
  261. package/tests/attentionfeed.test.ts +0 -163
  262. package/tests/backgroundruns.test.ts +0 -100
  263. package/tests/batchscheduling.test.ts +0 -131
  264. package/tests/blackboard.test.ts +0 -238
  265. package/tests/bridge.test.ts +0 -1088
  266. package/tests/browsercoverage.test.ts +0 -134
  267. package/tests/browserpolyfills.test.ts +0 -124
  268. package/tests/build.mjs +0 -1260
  269. package/tests/clientauth.test.ts +0 -223
  270. package/tests/clitools.test.ts +0 -578
  271. package/tests/confirmgates.test.ts +0 -190
  272. package/tests/containerpack.test.ts +0 -143
  273. package/tests/controlflow.test.ts +0 -2120
  274. package/tests/coordination.test.ts +0 -345
  275. package/tests/copyscreen.test.ts +0 -139
  276. package/tests/core.test.ts +0 -417
  277. package/tests/costcert.test.ts +0 -95
  278. package/tests/crossbrowser.test.ts +0 -115
  279. package/tests/cspaudit.test.ts +0 -79
  280. package/tests/dashdone.test.ts +0 -615
  281. package/tests/datagrid.test.ts +0 -137
  282. package/tests/doccheck.test.ts +0 -107
  283. package/tests/dombatch.test.ts +0 -109
  284. package/tests/emulation.test.ts +0 -282
  285. package/tests/environments.test.ts +0 -259
  286. package/tests/evidenceviews.test.ts +0 -88
  287. package/tests/exporttools.test.ts +0 -185
  288. package/tests/firefoxprep.test.ts +0 -134
  289. package/tests/flowlibrary.test.ts +0 -242
  290. package/tests/flowrun.test.ts +0 -303
  291. package/tests/forensics.test.ts +0 -439
  292. package/tests/gateway.test.ts +0 -939
  293. package/tests/headlesslib.test.ts +0 -171
  294. package/tests/httpstream.test.ts +0 -277
  295. package/tests/immutablelog.test.ts +0 -157
  296. package/tests/inboundguard.test.ts +0 -177
  297. package/tests/lazyload.test.ts +0 -72
  298. package/tests/librarymodes.test.ts +0 -623
  299. package/tests/llm.test.ts +0 -696
  300. package/tests/manifest.test.ts +0 -226
  301. package/tests/maskinputs.test.ts +0 -150
  302. package/tests/matrixverify.test.ts +0 -111
  303. package/tests/mavenpack.test.ts +0 -75
  304. package/tests/mcpserver.test.ts +0 -704
  305. package/tests/memory.test.ts +0 -4155
  306. package/tests/memorycare.test.ts +0 -230
  307. package/tests/migrateplan.test.ts +0 -349
  308. package/tests/minimization.test.ts +0 -414
  309. package/tests/modelroute.test.ts +0 -112
  310. package/tests/multiagent.test.ts +0 -440
  311. package/tests/nativebridge.test.ts +0 -807
  312. package/tests/navigation.test.ts +0 -284
  313. package/tests/nugetpack.test.ts +0 -83
  314. package/tests/orchestration.test.ts +0 -449
  315. package/tests/originpolicy.test.ts +0 -405
  316. package/tests/outputcompare.test.ts +0 -97
  317. package/tests/package.test.ts +0 -91
  318. package/tests/pagebridge.test.ts +0 -5909
  319. package/tests/pentest.test.ts +0 -75
  320. package/tests/perfrecords.test.ts +0 -257
  321. package/tests/permdiff.test.ts +0 -85
  322. package/tests/phishguard.test.ts +0 -128
  323. package/tests/pickerviews.test.ts +0 -155
  324. package/tests/pipelines.test.ts +0 -381
  325. package/tests/planlint.test.ts +0 -240
  326. package/tests/planreview.test.ts +0 -383
  327. package/tests/platformtargets.test.ts +0 -56
  328. package/tests/policy.test.ts +0 -8284
  329. package/tests/portability.test.ts +0 -96
  330. package/tests/progress.test.ts +0 -1036
  331. package/tests/promptlibrary.test.ts +0 -116
  332. package/tests/protocol.test.ts +0 -4793
  333. package/tests/publishingpipeline.test.ts +0 -314
  334. package/tests/quickactions.test.ts +0 -164
  335. package/tests/readiness.test.ts +0 -78
  336. package/tests/recipes.test.ts +0 -163
  337. package/tests/redactshots.test.ts +0 -216
  338. package/tests/registernvidiakeys.ts +0 -51
  339. package/tests/relayserve.test.ts +0 -304
  340. package/tests/releasenotes.test.ts +0 -276
  341. package/tests/resilience.test.ts +0 -467
  342. package/tests/resourceaware.test.ts +0 -187
  343. package/tests/runbudget.test.ts +0 -125
  344. package/tests/runhistory.test.ts +0 -299
  345. package/tests/runreplay.test.ts +0 -113
  346. package/tests/runresume.test.ts +0 -184
  347. package/tests/runstate.test.ts +0 -288
  348. package/tests/runtimeadapters.test.ts +0 -251
  349. package/tests/safariskeleton.test.ts +0 -115
  350. package/tests/sandboxframe.test.ts +0 -196
  351. package/tests/sbom.test.ts +0 -105
  352. package/tests/secretvault.test.ts +0 -188
  353. package/tests/selcache.test.ts +0 -111
  354. package/tests/sessioninterface.test.ts +0 -791
  355. package/tests/sessions.test.ts +0 -591
  356. package/tests/siteprefs.test.ts +0 -228
  357. package/tests/snapshotdelta.test.ts +0 -165
  358. package/tests/statusviews.test.ts +0 -237
  359. package/tests/stepmeter.test.ts +0 -200
  360. package/tests/streamparse.test.ts +0 -163
  361. package/tests/stylepruning.test.ts +0 -90
  362. package/tests/surfaces.test.ts +0 -282
  363. package/tests/sweep.test.ts +0 -78
  364. package/tests/syncbridge.test.ts +0 -164
  365. package/tests/taskqueue.test.ts +0 -206
  366. package/tests/toolcalls.test.ts +0 -336
  367. package/tests/toolcatalog.test.ts +0 -201
  368. package/tests/torture-agent.test.ts +0 -706
  369. package/tests/torture-auth.test.ts +0 -461
  370. package/tests/torture-commands.test.ts +0 -895
  371. package/tests/torture-data.test.ts +0 -923
  372. package/tests/torture-environments.test.ts +0 -285
  373. package/tests/torture-export.test.ts +0 -263
  374. package/tests/torture-http.test.ts +0 -1379
  375. package/tests/torture-llm.test.ts +0 -808
  376. package/tests/torture-memory.test.ts +0 -1084
  377. package/tests/torture-plan.test.ts +0 -829
  378. package/tests/torture-run.test.ts +0 -626
  379. package/tests/torture-session.test.ts +0 -928
  380. package/tests/torture-swarm.test.ts +0 -1245
  381. package/tests/torture-tools.test.ts +0 -964
  382. package/tests/torture-workflow.test.ts +0 -2354
  383. package/tests/tourviews.test.ts +0 -52
  384. package/tests/transparency.test.ts +0 -147
  385. package/tests/trigger.test.ts +0 -941
  386. package/tests/v1sunset.test.ts +0 -428
  387. package/tests/virtlist.test.ts +0 -94
  388. package/tests/vision.test.ts +0 -540
  389. package/tests/vsixpack.test.ts +0 -145
  390. package/tests/webapi.test.ts +0 -523
  391. package/tests/workflow.test.ts +0 -1785
  392. package/tests/workfloweditor.test.ts +0 -759
  393. package/tests/xpipack.test.ts +0 -128
  394. package/tools.ts +0 -734
  395. package/types.ts +0 -9908
  396. package/umd.ts +0 -9
  397. package/utils.ts +0 -249
  398. package/version.ts +0 -8
  399. package/versionregistry.ts +0 -1310
  400. package/views.ts +0 -2153
  401. package/vitest.config.ts +0 -12
  402. package/web/App.tsx +0 -78
  403. package/web/README.md +0 -47
  404. package/web/capacitor.config.ts +0 -46
  405. package/web/const.ts +0 -19
  406. package/web/control.shell.tsx +0 -66
  407. package/web/dashboardpage.ts +0 -554
  408. package/web/db.ts +0 -493
  409. package/web/design.html +0 -541
  410. package/web/gatewayview/Gateway.tsx +0 -345
  411. package/web/gatewayview/config.ts +0 -1042
  412. package/web/gatewayview/definition.ts +0 -174
  413. package/web/icons.ts +0 -15
  414. package/web/optionspage.ts +0 -691
  415. package/web/popup.ts +0 -1651
  416. package/web/schema.prisma +0 -176
  417. package/web/sidepanel.ts +0 -7941
  418. package/web/sitemanifest.json +0 -13
  419. package/web/terminal-ui.tsx +0 -622
  420. package/web/transparencypage.ts +0 -541
  421. package/web/vite.config.ts +0 -39
  422. package/web/workspace.ts +0 -23
  423. package/workbenchmemory.ts +0 -104
  424. package/workbenchsession.ts +0 -304
  425. package/workflow.ts +0 -4495
  426. /package/{dist/bun.d.ts → bun.d.ts} +0 -0
  427. /package/{dist/cli.d.ts → cli.d.ts} +0 -0
  428. /package/{dist/companion.d.ts → companion.d.ts} +0 -0
  429. /package/{dist/crossbrowser.d.ts → crossbrowser.d.ts} +0 -0
  430. /package/{dist/dashdone.d.ts → dashdone.d.ts} +0 -0
  431. /package/{dist/deno.d.ts → deno.d.ts} +0 -0
  432. /package/{dist/hardening.d.ts → hardening.d.ts} +0 -0
  433. /package/{dist/headless.d.ts → headless.d.ts} +0 -0
  434. /package/{dist/http.d.ts → http.d.ts} +0 -0
  435. /package/{dist/index.d.ts → index.d.ts} +0 -0
  436. /package/{dist/mcp.d.ts → mcp.d.ts} +0 -0
  437. /package/{dist/memory.d.ts → memory.d.ts} +0 -0
  438. /package/{dist/node.d.ts → node.d.ts} +0 -0
  439. /package/{dist/pack.d.ts → pack.d.ts} +0 -0
  440. /package/{dist/policy.d.ts → policy.d.ts} +0 -0
  441. /package/{dist/progress.d.ts → progress.d.ts} +0 -0
  442. /package/{dist/protocol.d.ts → protocol.d.ts} +0 -0
  443. /package/{dist/server.d.ts → server.d.ts} +0 -0
  444. /package/{dist/umd.d.ts → umd.d.ts} +0 -0
package/debug.ts DELETED
@@ -1,850 +0,0 @@
1
- /** The debug module of the 1.1.90 consolidation: every correlated variation of the cdp instrumentation and the profilers interned in this one file, so the module family carries one surface without duplicate variations. */
2
-
3
- /* ── Merged from cdpbus.ts: the 1.1.90 consolidation interns the correlated cdpbus logic here, so no variation of the same file lives beside another. ── */
4
- import type {
5
- attachtarget,
6
- breakpointspec,
7
- cdpallowlist,
8
- cdpcommand,
9
- cdpeventrule,
10
- cdpsession,
11
- cpuprofile,
12
- flowmetric,
13
- flowspec,
14
- growsample,
15
- heaprecord,
16
- memorytrend,
17
- pausestate,
18
- scriptoverride,
19
- shiftentry,
20
- sourcemapref,
21
- stackframe,
22
- stepmode,
23
- teardownplan,
24
- traceannotation,
25
- tracerecord,
26
- watchexpression,
27
- } from "./types.js";
28
-
29
- /**
30
- * Devtools protocol bus for the 1.1.46 debugging family.
31
- * Every correlated rule for the reviewed instrumented devtools session lives in this file: the domain grammar, the attach and detach lifecycle with the honest derivation note, the raw command records with duration and error class, the per session command serialization in send order, the domain event rules with match filters and per domain event counts, the breakpoint input grammar, the pause state capture with call frames, the step mode grammar, the watch expression values per pause, the script override url patterns and the teardown plan that reverts every breakpoint and override and decides the resume policy when the user detaches the debugger.
32
- * The chrome devtools protocol is unavailable without the debugger permission, which the manifest gate forbids, so every command routes through the page-instrumented harness injected through the scripting api; the derivation is recorded on every session instead of hidden.
33
- */
34
-
35
- /** The debugging kinds of the devtools family, listed among the available capabilities of every proposal request. */
36
- export const cdpkinds: string[] = [
37
- "attachcdp",
38
- "detachcdp",
39
- "cdpcmd",
40
- "watchcdp",
41
- "setbreakpoint",
42
- "stepcode",
43
- "watchexpr",
44
- "overridescript",
45
- ];
46
-
47
- /** The reviewed devtools domain grammar: runtime evaluation, log capture, debugger state, dom snapshots, network facts and page lifecycle; the enabled subset stays a user choice bounded by this grammar only. */
48
- export const cdpdomains: string[] = ["Runtime", "Log", "Debugger", "DOM", "Network", "Page"];
49
-
50
- /** Resolves the domain of one devtools method name of the form Domain.method; malformed methods resolve to undefined. */
51
- export function methoddomain(method: string): string | undefined {
52
- const match = /^([A-Z][A-Za-z]*)\.([a-zA-Z][A-Za-z0-9]*)$/.exec(method.trim());
53
- return match?.[1];
54
- }
55
-
56
- /** Normalizes one reviewed devtools domain allowlist: the enabled domains bounded by the reviewed domain grammar and the optional method gates inside the enabled domains; a gate list that names no method of the enabled domains is refused. */
57
- export function cdpallowlistof(value: unknown): cdpallowlist | undefined {
58
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
59
- const entry = value as Record<string, unknown>;
60
- const domains = Array.isArray(entry.domains)
61
- ? entry.domains.filter((domain): domain is string => typeof domain === "string" && cdpdomains.includes(domain))
62
- : [];
63
- if (domains.length === 0) return undefined;
64
- if (entry.methods === undefined) return { domains };
65
- const methods = Array.isArray(entry.methods)
66
- ? entry.methods.filter(
67
- (method): method is string =>
68
- typeof method === "string" &&
69
- methoddomain(method) !== undefined &&
70
- domains.includes(methoddomain(method) as string),
71
- )
72
- : [];
73
- if (methods.length === 0) return undefined;
74
- return { domains, methods };
75
- }
76
-
77
- /** True when the allowlist covers one method: the method domain must be enabled and a present method gate list must name the method. */
78
- export function allowlistcovers(allowlist: cdpallowlist, method: string): boolean {
79
- const domain = methoddomain(method);
80
- if (domain === undefined) return false;
81
- if (!allowlist.domains.includes(domain)) return false;
82
- if (allowlist.methods !== undefined && !allowlist.methods.includes(method)) return false;
83
- return true;
84
- }
85
-
86
- /** Attaches one instrumented devtools session to the run tab with the reviewed enabled domains and the honest debugger derivation note. */
87
- export function attachcdpsession(input: {
88
- id: string;
89
- runid: string;
90
- stepid: string;
91
- tabid: number;
92
- origin: string;
93
- domains: string[];
94
- now: number;
95
- debuggerversion: string;
96
- }): cdpsession {
97
- return {
98
- id: input.id,
99
- runid: input.runid,
100
- stepid: input.stepid,
101
- tabid: input.tabid,
102
- origin: input.origin,
103
- attachedat: input.now,
104
- domains: [...new Set(input.domains)],
105
- debuggerversion: input.debuggerversion,
106
- };
107
- }
108
-
109
- /** Detaches one session cleanly: the detach time stamps the record and the domains stay auditable after the detach. */
110
- export function detachcdpsession(session: cdpsession, at: number, userdetached = false): cdpsession {
111
- return { ...session, detachedat: at, ...(userdetached ? { userdetached: true } : {}) };
112
- }
113
-
114
- /** Builds one raw protocol command record with its method, params, domain, dotted result path, duration and error class. */
115
- export function sendcdpcommand(input: {
116
- id: string;
117
- sessionid: string;
118
- runid: string;
119
- stepid: string;
120
- method: string;
121
- params?: Record<string, unknown>;
122
- resultpath?: string;
123
- duration: number;
124
- errorclass?: string;
125
- at: number;
126
- }): cdpcommand {
127
- const domain = methoddomain(input.method);
128
- if (domain === undefined)
129
- return {
130
- ...input,
131
- method: input.method.trim(),
132
- domain: "",
133
- duration: input.duration,
134
- ...(input.errorclass !== undefined ? { errorclass: input.errorclass } : { errorclass: "malformedmethod" }),
135
- at: input.at,
136
- };
137
- return {
138
- ...input,
139
- method: input.method.trim(),
140
- domain,
141
- duration: input.duration,
142
- ...(input.errorclass !== undefined ? { errorclass: input.errorclass } : {}),
143
- at: input.at,
144
- };
145
- }
146
-
147
- /** Appends one command to the per session send queue so concurrent commands serialize in send order. */
148
- export function serializecdpcommand(queue: cdpcommand[], command: cdpcommand): cdpcommand[] {
149
- return [...queue, command];
150
- }
151
-
152
- /** Normalizes one reviewed domain event rule: the domain of the reviewed grammar, the event name and the optional payload match filter. */
153
- export function cdpeventruleof(value: unknown): Pick<cdpeventrule, "domain" | "event" | "match"> | undefined {
154
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
155
- const entry = value as Record<string, unknown>;
156
- const domain = typeof entry.domain === "string" && cdpdomains.includes(entry.domain) ? entry.domain : undefined;
157
- const event = typeof entry.event === "string" && entry.event.trim() ? entry.event.trim() : undefined;
158
- if (domain === undefined || event === undefined) return undefined;
159
- const match = typeof entry.match === "string" && entry.match.trim() ? entry.match.trim() : undefined;
160
- return { domain, event, ...(match !== undefined ? { match } : {}) };
161
- }
162
-
163
- /** Matches observed domain events against the reviewed rules and counts the matched events per domain; the match filter must appear in the payload before an event forwards. */
164
- export function watchcdpevents(
165
- rules: cdpeventrule[],
166
- events: Array<{ domain: string; event: string; payload?: string }>,
167
- ): {
168
- matched: Array<{ ruleid: string; domain: string; event: string; payload?: string }>;
169
- counts: Record<string, number>;
170
- } {
171
- const matched: Array<{ ruleid: string; domain: string; event: string; payload?: string }> = [];
172
- const counts: Record<string, number> = {};
173
- for (const domain of cdpdomains) counts[domain] = 0;
174
- for (const event of events) {
175
- for (const rule of rules) {
176
- if (rule.domain !== event.domain || rule.event !== event.event) continue;
177
- if (rule.match !== undefined && !(event.payload ?? "").includes(rule.match)) continue;
178
- matched.push({
179
- ruleid: rule.id,
180
- domain: event.domain,
181
- event: event.event,
182
- ...(event.payload !== undefined ? { payload: event.payload } : {}),
183
- });
184
- counts[event.domain] = (counts[event.domain] ?? 0) + 1;
185
- }
186
- }
187
- return { matched, counts };
188
- }
189
-
190
- /** Normalizes one reviewed breakpoint input: the script url, the zero based line, the optional column and the condition of the reviewed expression grammar. */
191
- export function breakpointinputof(
192
- value: unknown,
193
- ): Pick<breakpointspec, "url" | "line" | "column" | "condition"> | undefined {
194
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
195
- const entry = value as Record<string, unknown>;
196
- const url = typeof entry.url === "string" && entry.url.trim() ? entry.url.trim() : undefined;
197
- const line =
198
- typeof entry.line === "number" && Number.isInteger(entry.line) && entry.line >= 0 ? entry.line : undefined;
199
- if (url === undefined || line === undefined) return undefined;
200
- const column =
201
- typeof entry.column === "number" && Number.isInteger(entry.column) && entry.column >= 0 ? entry.column : undefined;
202
- const condition = typeof entry.condition === "string" && entry.condition.trim() ? entry.condition.trim() : undefined;
203
- return { url, line, ...(column !== undefined ? { column } : {}), ...(condition !== undefined ? { condition } : {}) };
204
- }
205
-
206
- /** Captures one pause state from the instrumented pause: the reason, the call frames, the hit breakpoint and the dom snapshot reference of the page bridge capture. */
207
- export function capturepause(input: {
208
- id: string;
209
- runid: string;
210
- stepid: string;
211
- reason: string;
212
- callframes: stackframe[];
213
- hitbreakpoint?: string;
214
- domsnapshotid?: string;
215
- at: number;
216
- }): pausestate {
217
- return {
218
- id: input.id,
219
- runid: input.runid,
220
- stepid: input.stepid,
221
- reason: input.reason,
222
- callframes: [...input.callframes],
223
- ...(input.hitbreakpoint !== undefined ? { hitbreakpoint: input.hitbreakpoint } : {}),
224
- ...(input.domsnapshotid !== undefined ? { domsnapshotid: input.domsnapshotid } : {}),
225
- at: input.at,
226
- };
227
- }
228
-
229
- /** Normalizes one reviewed step mode of the instrumented debugger: stepover, stepinto, stepout or resume. */
230
- export function stepmodeof(value: unknown): stepmode | undefined {
231
- const modes: stepmode[] = ["stepover", "stepinto", "stepout", "resume"];
232
- return typeof value === "string" && modes.includes(value as stepmode) ? (value as stepmode) : undefined;
233
- }
234
-
235
- /** Normalizes one reviewed watch expression input: the expression text and the pause scope it evaluates in. */
236
- export function watchexpressionof(value: unknown): Pick<watchexpression, "expression" | "scope"> | undefined {
237
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
238
- const entry = value as Record<string, unknown>;
239
- const expression =
240
- typeof entry.expression === "string" && entry.expression.trim() ? entry.expression.trim() : undefined;
241
- if (expression === undefined) return undefined;
242
- const scope = typeof entry.scope === "string" && entry.scope.trim() ? entry.scope.trim() : "topframe";
243
- return { expression, scope };
244
- }
245
-
246
- /** Records one watch value captured at a pause with the pause scope correlation. */
247
- export function recordwatchvalue(
248
- expression: watchexpression,
249
- pauseid: string,
250
- value: string,
251
- at: number,
252
- ): watchexpression {
253
- return { ...expression, values: [...expression.values, { pauseid, value, at }] };
254
- }
255
-
256
- /** Normalizes one reviewed script override input: the url pattern that names its origin explicitly and the full fixture source. */
257
- export function overrideinputof(value: unknown): Pick<scriptoverride, "urlpattern" | "source"> | undefined {
258
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
259
- const entry = value as Record<string, unknown>;
260
- const urlpattern =
261
- typeof entry.urlpattern === "string" && entry.urlpattern.trim() ? entry.urlpattern.trim() : undefined;
262
- const source = typeof entry.source === "string" ? entry.source : undefined;
263
- if (urlpattern === undefined || source === undefined || source.trim().length === 0) return undefined;
264
- return { urlpattern, source };
265
- }
266
-
267
- /** Matches one script url against a reviewed override pattern of an explicit https origin with single star segments and double star subtrees. */
268
- export function overridematches(urlpattern: string, url: string): boolean {
269
- const patternmatch = /^(https:\/\/[^/]+)(\/.*)?$/.exec(urlpattern);
270
- const urlmatch = /^(https:\/\/[^/]+)(\/.*)?$/.exec(url);
271
- if (!patternmatch || !urlmatch) return false;
272
- if (patternmatch[1] !== urlmatch[1]) return false;
273
- const patternpath = (patternmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
274
- const urlpath = (urlmatch[2] ?? "/").split("/").filter((segment) => segment.length > 0);
275
- const walk = (patternindex: number, urlindex: number): boolean => {
276
- if (patternindex >= patternpath.length) return urlindex >= urlpath.length;
277
- const segment = patternpath[patternindex];
278
- if (segment === "**")
279
- return walk(patternindex + 1, urlindex) || (urlindex < urlpath.length && walk(patternindex, urlindex + 1));
280
- if (urlindex >= urlpath.length) return false;
281
- if (segment !== "*" && segment !== urlpath[urlindex]) return false;
282
- return walk(patternindex + 1, urlindex + 1);
283
- };
284
- return walk(0, 0);
285
- }
286
-
287
- /** Normalizes one reviewed teardown plan: the revert steps and the resume policy of resume, pause or ask. */
288
- export function teardownplanof(value: unknown): teardownplan | undefined {
289
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
290
- const entry = value as Record<string, unknown>;
291
- const revertsteps = Array.isArray(entry.revertsteps)
292
- ? entry.revertsteps.filter((step): step is string => typeof step === "string" && step.trim().length > 0)
293
- : [];
294
- const policy = entry.resumepolicy;
295
- if (revertsteps.length === 0) return undefined;
296
- if (policy !== undefined && policy !== "resume" && policy !== "pause" && policy !== "ask") return undefined;
297
- return { revertsteps, resumepolicy: policy ?? "ask" };
298
- }
299
-
300
- /** The teardown outcome of one session: every breakpoint and override reverts, the resume policy decides the continuation and a user detach keeps the session record alive for review while the run pauses. */
301
- export interface teardowndecision {
302
- session: cdpsession;
303
- revertedbreakpoints: string[];
304
- revertedoverrides: string[];
305
- resumepolicy: teardownplan["resumepolicy"];
306
- paused: boolean;
307
- keepsalive: boolean;
308
- }
309
-
310
- /** Tears one session down safely: the revert steps of the reviewed teardown plan revert every active breakpoint and override in order, the session detaches, and a user detach keeps the session record alive while the run pauses for review before continuing. */
311
- export function teardowncdpsession(input: {
312
- session: cdpsession;
313
- breakpoints: breakpointspec[];
314
- overrides: scriptoverride[];
315
- plan: teardownplan | undefined;
316
- userdetached: boolean;
317
- at: number;
318
- }): teardowndecision {
319
- const revertedbreakpoints = input.breakpoints.filter((spec) => spec.revertedat === undefined).map((spec) => spec.id);
320
- const revertedoverrides = input.overrides.filter((spec) => spec.revertedat === undefined).map((spec) => spec.id);
321
- const resumepolicy = input.userdetached ? "pause" : (input.plan?.resumepolicy ?? "ask");
322
- return {
323
- session: detachcdpsession(input.session, input.at, input.userdetached),
324
- revertedbreakpoints,
325
- revertedoverrides,
326
- resumepolicy,
327
- paused: input.userdetached || resumepolicy === "pause",
328
- keepsalive: input.userdetached,
329
- };
330
- }
331
-
332
- /* ── Merged from profilers.ts: the 1.1.90 consolidation interns the correlated profilers logic here, so no variation of the same file lives beside another. ── */
333
- /**
334
- * Profilers for the 1.1.47 debugging part three family.
335
- * Every correlated rule for the reviewed flow, heap, cpu, trace and source map instruments lives in this file: the flow metric grammar with per step mark windows and blocking sums, the on demand heap snapshot records with the user chosen interval, the growth trend slopes with the flagged steps, the cpu profile records with hot functions, the layout shift entries with impacted selectors, the trace records with the reviewed category list and step annotations, the offline replay of a captured trace file, and the source map references with stack rewriting.
336
- * The chrome devtools protocol needs the debugger permission, which the manifest gate forbids, so every measurement derives from the performance timeline buffers, the injected instrumentation probes of the page harness and the existing page seams; the derivation stays recorded on every record instead of hidden.
337
- */
338
-
339
- /** The profiling kinds of the debugging part three family, listed among the available capabilities of every proposal request. */
340
- export const profilerkinds: string[] = [
341
- "measureflow",
342
- "heapshot",
343
- "trackmemory",
344
- "profilecpu",
345
- "watchshifts",
346
- "traceload",
347
- "annotatetrace",
348
- "replaytrace",
349
- "capturesourcemaps",
350
- ];
351
-
352
- /** The reviewed flow metric set: navigation, paint and largest contentful paint timings, first input delay and interaction timings, and the blocking time sums per step window. */
353
- export const flowmetricnames: string[] = ["navigation", "paint", "lcp", "fid", "interaction", "blocking"];
354
-
355
- /** The reviewed trace category list applied to every recorded trace; the enabled subset stays a user choice bounded by this grammar only. */
356
- export const tracecategories: string[] = ["navigation", "scripting", "rendering", "painting", "loading", "network"];
357
-
358
- /** Normalizes one reviewed profiling attach target: the target kind of page, iframe, worker or service worker with an explicit https url. */
359
- export function attachtargetof(value: unknown): attachtarget | undefined {
360
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
361
- const entry = value as Record<string, unknown>;
362
- const kinds: attachtarget["kind"][] = ["page", "iframe", "worker", "serviceworker"];
363
- const kind =
364
- typeof entry.kind === "string" && kinds.includes(entry.kind as attachtarget["kind"])
365
- ? (entry.kind as attachtarget["kind"])
366
- : undefined;
367
- const url = typeof entry.url === "string" && entry.url.trim() ? entry.url.trim() : undefined;
368
- if (kind === undefined || url === undefined) return undefined;
369
- if (kind !== "page" && !/^https:\/\//.test(url)) return undefined;
370
- return { kind, url };
371
- }
372
-
373
- /** Normalizes one reviewed flow spec: a non-empty mark prefix, a non-empty step window and a metric list of the reviewed metric set. */
374
- export function flowspecof(value: unknown): flowspec | undefined {
375
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
376
- const entry = value as Record<string, unknown>;
377
- const prefix = typeof entry.prefix === "string" && entry.prefix.trim() ? entry.prefix.trim() : undefined;
378
- const steps = Array.isArray(entry.steps)
379
- ? entry.steps.filter((step): step is string => typeof step === "string" && step.trim().length > 0)
380
- : [];
381
- const metrics = Array.isArray(entry.metrics)
382
- ? entry.metrics.filter((metric): metric is string => typeof metric === "string" && flowmetricnames.includes(metric))
383
- : [];
384
- if (prefix === undefined || steps.length === 0 || metrics.length === 0) return undefined;
385
- return { prefix, steps, metrics };
386
- }
387
-
388
- /** One flattened performance entry of the page buffers: the name, the entry type, the start time and the duration. */
389
- export interface perfentry {
390
- name: string;
391
- type: string;
392
- start: number;
393
- duration: number;
394
- }
395
-
396
- /** Resolves the reviewed step windows of a flow spec from the performance marks of the prefix: every step id of the window gains its start and end from the marks of the measured page. */
397
- export function stepwindows(spec: flowspec, marks: perfentry[]): Array<{ stepid: string; start: number; end: number }> {
398
- const windows: Array<{ stepid: string; start: number; end: number }> = [];
399
- for (const stepid of spec.steps) {
400
- const start = marks.find((mark) => mark.type === "mark" && mark.name === `${spec.prefix}:${stepid}:start`);
401
- const end = marks.find((mark) => mark.type === "mark" && mark.name === `${spec.prefix}:${stepid}:end`);
402
- if (start === undefined || end === undefined) continue;
403
- windows.push({ stepid, start: start.start, end: Math.max(end.start, start.start) });
404
- }
405
- return windows;
406
- }
407
-
408
- /** Measures one reviewed flow from the performance buffers of the page: every metric of the reviewed set carries its window start and end, its duration and the step ids it spans, navigation, paint and largest contentful paint timings come from the navigation and paint buffers, first input delay and interaction timings from the event buffers and the blocking time is summed per step window from the long task entries over the fifty millisecond threshold. */
409
- export function measure(input: {
410
- runid: string;
411
- stepid: string;
412
- spec: flowspec;
413
- entries: perfentry[];
414
- now: number;
415
- }): flowmetric[] {
416
- const metrics: flowmetric[] = [];
417
- const windows = stepwindows(
418
- input.spec,
419
- input.entries.filter((entry) => entry.type === "mark"),
420
- );
421
- const stepsof = (start: number, end: number): string[] =>
422
- windows.filter((window) => window.end >= start && window.start <= end).map((window) => window.stepid);
423
- const push = (name: string, start: number, end: number, steps: string[]): void => {
424
- if (!input.spec.metrics.includes(name)) return;
425
- metrics.push({
426
- id: `${input.runid}-${input.stepid}-${name}-${metrics.length}`,
427
- runid: input.runid,
428
- stepid: input.stepid,
429
- name,
430
- start,
431
- end,
432
- duration: Math.max(0, end - start),
433
- steps,
434
- at: input.now,
435
- });
436
- };
437
- const navigation = input.entries.find((entry) => entry.type === "navigation");
438
- if (navigation !== undefined)
439
- push(
440
- "navigation",
441
- navigation.start,
442
- navigation.start + navigation.duration,
443
- stepsof(navigation.start, navigation.start + navigation.duration),
444
- );
445
- for (const paint of input.entries.filter((entry) => entry.type === "paint")) {
446
- if (!input.spec.metrics.includes("paint")) break;
447
- push("paint", paint.start, paint.start + paint.duration, stepsof(paint.start, paint.start + paint.duration));
448
- }
449
- const lcps = input.entries.filter((entry) => entry.type === "largest-contentful-paint");
450
- const lcp =
451
- lcps.length > 0 ? lcps.reduce((largest, entry) => (entry.start > largest.start ? entry : largest)) : undefined;
452
- if (lcp !== undefined) push("lcp", lcp.start, lcp.start + lcp.duration, stepsof(lcp.start, lcp.start + lcp.duration));
453
- const firstinput = input.entries.find((entry) => entry.type === "first-input");
454
- if (firstinput !== undefined)
455
- push(
456
- "fid",
457
- firstinput.start,
458
- firstinput.start + firstinput.duration,
459
- stepsof(firstinput.start, firstinput.start + firstinput.duration),
460
- );
461
- const interactions = input.entries.filter((entry) => entry.type === "event");
462
- if (interactions.length > 0) {
463
- const start = interactions.reduce((earliest, entry) => (entry.start < earliest.start ? entry : earliest)).start;
464
- const end = interactions.reduce(
465
- (latest, entry) => (entry.start + entry.duration > latest ? entry.start + entry.duration : latest),
466
- start,
467
- );
468
- push("interaction", start, end, stepsof(start, end));
469
- }
470
- for (const window of windows) {
471
- if (!input.spec.metrics.includes("blocking")) break;
472
- const blocking = input.entries
473
- .filter((entry) => entry.type === "longtask" && entry.start >= window.start && entry.start <= window.end)
474
- .reduce((total, entry) => total + Math.max(0, entry.duration - 50), 0);
475
- metrics.push({
476
- id: `${input.runid}-${input.stepid}-blocking-${window.stepid}`,
477
- runid: input.runid,
478
- stepid: input.stepid,
479
- name: "blocking",
480
- start: window.start,
481
- end: window.end,
482
- duration: blocking,
483
- steps: [window.stepid],
484
- at: input.now,
485
- });
486
- }
487
- return metrics;
488
- }
489
-
490
- /** True when the user chosen heap interval allows a new on demand snapshot: the frequency bound stays a user choice only and an absent interval always allows the capture. */
491
- export function heapintervalallowed(
492
- lastcapturedat: number | undefined,
493
- interval: number | undefined,
494
- now: number,
495
- ): boolean {
496
- if (interval === undefined || lastcapturedat === undefined) return true;
497
- return now - lastcapturedat >= interval;
498
- }
499
-
500
- /** Captures one on demand heap snapshot record with its byte size, node count and capture time; the counts derive from the page performance memory buffer and the dom node count because no heap profiler exists without the debugger permission. */
501
- export function heapsnap(input: {
502
- id: string;
503
- runid: string;
504
- stepid: string;
505
- origin: string;
506
- usedbytes: number;
507
- limitbytes: number;
508
- nodecount: number;
509
- now: number;
510
- }): heaprecord {
511
- return {
512
- id: input.id,
513
- runid: input.runid,
514
- stepid: input.stepid,
515
- origin: input.origin,
516
- bytesize: input.usedbytes,
517
- nodecount: input.nodecount,
518
- capturedat: input.now,
519
- };
520
- }
521
-
522
- /** Records one heap growth sample taken beside a step with the used and limit bytes of the page memory buffer. */
523
- export function growsampleof(input: {
524
- id: string;
525
- runid: string;
526
- stepid: string;
527
- usedbytes: number;
528
- limitbytes: number;
529
- now: number;
530
- }): growsample {
531
- return {
532
- id: input.id,
533
- runid: input.runid,
534
- stepid: input.stepid,
535
- usedbytes: input.usedbytes,
536
- limitbytes: input.limitbytes,
537
- at: input.now,
538
- };
539
- }
540
-
541
- /** Computes the growth trend of the heap samples: the slope in bytes per millisecond from the first to the last sample, the sample count and the flagged steps whose per step growth exceeds the reviewed slope. */
542
- export function growthtrend(input: { runid: string; samples: growsample[]; slope: number; now: number }): memorytrend {
543
- const ordered = [...input.samples].sort((left, right) => left.at - right.at);
544
- const first = ordered[0];
545
- const last = ordered[ordered.length - 1];
546
- const computed =
547
- first !== undefined && last !== undefined && last.at > first.at
548
- ? (last.usedbytes - first.usedbytes) / (last.at - first.at)
549
- : 0;
550
- const flaggedsteps: string[] = [];
551
- for (let index = 1; index < ordered.length; index += 1) {
552
- const previous = ordered[index - 1];
553
- const current = ordered[index];
554
- if (previous === undefined || current === undefined) continue;
555
- const growth = current.at > previous.at ? (current.usedbytes - previous.usedbytes) / (current.at - previous.at) : 0;
556
- if (growth > input.slope && !flaggedsteps.includes(current.stepid)) flaggedsteps.push(current.stepid);
557
- }
558
- return { runid: input.runid, slope: computed, samples: ordered.length, flaggedsteps, at: input.now };
559
- }
560
-
561
- /** One cpu sample of the profiled window: the observed function name and the sampled time in milliseconds. */
562
- export interface cpusample {
563
- name: string;
564
- time: number;
565
- }
566
-
567
- /** Captures one cpu profile record around the step window: the profiled duration, the sample count and the hot functions ranked by their accumulated sampled time; the samples derive from the long task attribution and event timing buffers because no sampling profiler exists without the debugger permission. */
568
- export function cpusnap(input: {
569
- id: string;
570
- runid: string;
571
- stepid: string;
572
- origin: string;
573
- duration: number;
574
- samples: cpusample[];
575
- now: number;
576
- }): cpuprofile {
577
- const ranked = new Map<string, number>();
578
- for (const sample of input.samples) ranked.set(sample.name, (ranked.get(sample.name) ?? 0) + sample.time);
579
- const hotfunctions = [...ranked.entries()]
580
- .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))
581
- .map((entry) => entry[0]);
582
- return {
583
- id: input.id,
584
- runid: input.runid,
585
- stepid: input.stepid,
586
- origin: input.origin,
587
- duration: input.duration,
588
- samplecount: input.samples.length,
589
- hotfunctions,
590
- at: input.now,
591
- };
592
- }
593
-
594
- /** Normalizes one observed layout shift entry: the shift score, the start time and the impacted element selectors derived from the shift sources of the page observer. */
595
- export function shiftentryof(value: unknown): shiftentry | undefined {
596
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
597
- const entry = value as Record<string, unknown>;
598
- const score =
599
- typeof entry.score === "number" && Number.isFinite(entry.score) && entry.score >= 0 ? entry.score : undefined;
600
- const starttime =
601
- typeof entry.starttime === "number" && Number.isFinite(entry.starttime) ? entry.starttime : undefined;
602
- if (score === undefined || starttime === undefined) return undefined;
603
- const selectors = Array.isArray(entry.selectors)
604
- ? entry.selectors.filter(
605
- (selector): selector is string => typeof selector === "string" && selector.trim().length > 0,
606
- )
607
- : [];
608
- return {
609
- id: typeof entry.id === "string" ? entry.id : "",
610
- runid: typeof entry.runid === "string" ? entry.runid : "",
611
- stepid: typeof entry.stepid === "string" ? entry.stepid : "",
612
- score,
613
- starttime,
614
- selectors,
615
- at: typeof entry.at === "number" ? entry.at : starttime,
616
- };
617
- }
618
-
619
- /** Starts one trace record of the flow with the reviewed category list; the record stops at the reviewed window end and the exported file derives from the performance timeline buffers. */
620
- export function tracestart(input: {
621
- id: string;
622
- runid: string;
623
- stepid: string;
624
- origin: string;
625
- categories: string[];
626
- now: number;
627
- }): tracerecord {
628
- return {
629
- id: input.id,
630
- runid: input.runid,
631
- stepid: input.stepid,
632
- origin: input.origin,
633
- categories: [...new Set(input.categories)],
634
- bytesize: 0,
635
- events: 0,
636
- annotations: [],
637
- startedat: input.now,
638
- endedat: input.now,
639
- };
640
- }
641
-
642
- /** One derived trace event of the recorded window: the name, the reviewed category and the time offset from the trace start. */
643
- export interface traceevent {
644
- name: string;
645
- category: string;
646
- offset: number;
647
- }
648
-
649
- /** Serializes the recorded trace into its exportable file content: the honest derived format of the performance timeline events with the reviewed categories and the step annotations, never the devtools trace binary format. */
650
- export function tracetofile(
651
- trace: tracerecord,
652
- events: traceevent[],
653
- ): { content: string; bytesize: number; events: number } {
654
- const payload = {
655
- devthinktrace: "1.1.47",
656
- runid: trace.runid,
657
- origin: trace.origin,
658
- categories: trace.categories,
659
- startedat: trace.startedat,
660
- endedat: trace.endedat,
661
- annotations: trace.annotations,
662
- traceevents: events.map((event) => ({ name: event.name, cat: event.category, offset: event.offset })),
663
- };
664
- const content = JSON.stringify(payload);
665
- return { content, bytesize: content.length, events: events.length };
666
- }
667
-
668
- /** Normalizes one reviewed trace annotation input: the step id, the label and the time offset from the trace start. */
669
- export function annotationof(value: unknown): traceannotation | undefined {
670
- if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
671
- const entry = value as Record<string, unknown>;
672
- const stepid = typeof entry.stepid === "string" && entry.stepid.trim() ? entry.stepid.trim() : undefined;
673
- const label = typeof entry.label === "string" && entry.label.trim() ? entry.label.trim() : undefined;
674
- if (stepid === undefined || label === undefined) return undefined;
675
- const offset =
676
- typeof entry.offset === "number" && Number.isFinite(entry.offset) && entry.offset >= 0 ? entry.offset : 0;
677
- return { stepid, label, offset };
678
- }
679
-
680
- /** Writes the reviewed step ids and labels into the trace metadata: every annotation aligns with the run timeline entry of its step so the replay view shows the trace beside the reviewed steps; exported traces without step annotations are refused upstream. */
681
- export function annotatetrace(input: {
682
- trace: tracerecord;
683
- annotations: traceannotation[];
684
- timeline: Array<{ stepid: string; time: number }>;
685
- now: number;
686
- }): tracerecord {
687
- const aligned = input.annotations.map((annotation) => {
688
- const entry = input.timeline.find((item) => item.stepid === annotation.stepid);
689
- if (entry === undefined) return annotation;
690
- return { ...annotation, offset: Math.max(0, entry.time - input.trace.startedat) };
691
- });
692
- return { ...input.trace, annotations: aligned, endedat: Math.max(input.trace.endedat, input.now) };
693
- }
694
-
695
- /** The offline replay of one captured trace file: the events, the category counts and the step annotations of the file render without a live browser. */
696
- export function replaytrace(content: string): {
697
- traceid: string;
698
- runid: string;
699
- categories: Record<string, number>;
700
- events: Array<{ name: string; category: string; offset: number; stepid?: string }>;
701
- annotations: traceannotation[];
702
- } {
703
- let parsed: unknown;
704
- try {
705
- parsed = JSON.parse(content);
706
- } catch {
707
- throw new Error("The trace file is not valid json.");
708
- }
709
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
710
- throw new Error("The trace file is not a json object.");
711
- const record = parsed as Record<string, unknown>;
712
- const runid = typeof record.runid === "string" ? record.runid : "";
713
- const annotations = Array.isArray(record.annotations)
714
- ? record.annotations.flatMap((item) => {
715
- const annotation = annotationof(item);
716
- return annotation !== undefined ? [annotation] : [];
717
- })
718
- : [];
719
- const rawevents = Array.isArray(record.traceevents) ? record.traceevents : [];
720
- const categories: Record<string, number> = {};
721
- const events = rawevents.flatMap((item) => {
722
- if (!item || typeof item !== "object") return [];
723
- const entry = item as Record<string, unknown>;
724
- if (typeof entry.name !== "string" || typeof entry.cat !== "string" || typeof entry.offset !== "number") return [];
725
- const offset = entry.offset as number;
726
- categories[entry.cat] = (categories[entry.cat] ?? 0) + 1;
727
- const annotation = annotations.find((candidate) => Math.abs(candidate.offset - offset) < 1);
728
- return [
729
- {
730
- name: entry.name,
731
- category: entry.cat,
732
- offset,
733
- ...(annotation !== undefined ? { stepid: annotation.stepid } : {}),
734
- },
735
- ];
736
- });
737
- return { traceid: typeof record.id === "string" ? record.id : "", runid, categories, events, annotations };
738
- }
739
-
740
- /** Resolves the sourceMappingURL declaration of one script source: the map url resolves against the script url and scripts without a declaration have no map to capture. */
741
- export function mapurlof(scripturl: string, source: string): string | undefined {
742
- const match = /[#@]\s*sourceMappingURL=(\S+)/.exec(source);
743
- if (match === null || match[1] === undefined) return undefined;
744
- try {
745
- return new URL(match[1], scripturl).toString();
746
- } catch {
747
- return undefined;
748
- }
749
- }
750
-
751
- /** Builds the source map reference drafts of the loaded scripts: every script with a sourceMappingURL declaration gains its map url while the parsed state is decided by the capture. */
752
- export function capturesourcemaps(input: {
753
- runid: string;
754
- stepid: string;
755
- origin: string;
756
- scripts: Array<{ url: string; source: string }>;
757
- now: number;
758
- }): sourcemapref[] {
759
- return input.scripts.flatMap((script) => {
760
- const mapurl = mapurlof(script.url, script.source);
761
- if (mapurl === undefined) return [];
762
- return [
763
- {
764
- id: `${input.runid}-${script.url}`,
765
- runid: input.runid,
766
- stepid: input.stepid,
767
- origin: input.origin,
768
- scripturl: script.url,
769
- mapurl,
770
- parsed: false,
771
- at: input.now,
772
- },
773
- ];
774
- });
775
- }
776
-
777
- /** Decodes one base64 vlq segment of a source map mappings field into its integer values. */
778
- function decodevlq(segment: string): number[] | undefined {
779
- const values: number[] = [];
780
- let shift = 0;
781
- let value = 0;
782
- for (const character of segment) {
783
- const digit = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".indexOf(character);
784
- if (digit < 0) return undefined;
785
- value += (digit & 31) << shift;
786
- shift += 5;
787
- if ((digit & 32) === 0) {
788
- const negative = (value & 1) === 1;
789
- values.push(negative ? -(value >>> 1) : value >>> 1);
790
- value = 0;
791
- shift = 0;
792
- }
793
- }
794
- return values.length > 0 ? values : undefined;
795
- }
796
-
797
- /** Rewrites one stack location through a parsed source map: the minimal mapping lookup decodes the mappings field, walks the generated lines and resolves the original source file and line of the generated position; unparsed maps leave the location untouched. */
798
- export function rewritesourcelocation(
799
- input: { url: string; line: number; column?: number },
800
- map: { sources?: string[]; mappings?: string },
801
- ): { url: string; line: number } | undefined {
802
- const sources = Array.isArray(map.sources)
803
- ? map.sources.filter((source): source is string => typeof source === "string")
804
- : [];
805
- if (sources.length === 0 || typeof map.mappings !== "string" || map.mappings.length === 0) return undefined;
806
- const lines = map.mappings.split(";");
807
- if (input.line >= lines.length) return undefined;
808
- let sourceindex = 0;
809
- let sourceline = 0;
810
- for (let line = 0; line <= input.line; line += 1) {
811
- const linemappings = lines[line];
812
- if (linemappings === undefined) continue;
813
- const first = linemappings.split(",")[0] ?? "";
814
- if (first.length === 0) continue;
815
- const values = decodevlq(first);
816
- if (values === undefined || values.length < 4) continue;
817
- sourceindex = Math.max(0, sourceindex + (values[1] ?? 0));
818
- sourceline = Math.max(0, sourceline + (values[2] ?? 0));
819
- }
820
- const targetmappings = lines[input.line];
821
- const target = targetmappings !== undefined ? (targetmappings.split(",")[0] ?? "") : "";
822
- if (target.length === 0) return undefined;
823
- const source = sources[Math.min(sources.length - 1, sourceindex)];
824
- if (source === undefined) return undefined;
825
- return { url: source, line: sourceline };
826
- }
827
-
828
- /** Expires the heavy profile bytes after the user configured retention window: the snapshot bytes, sample payloads and trace file bytes drop while the byte and node counts, the sample counts, hot functions, category lists and step annotations survive. */
829
- export function expireprofilerecords(input: {
830
- heaps: heaprecord[];
831
- profiles: cpuprofile[];
832
- traces: tracerecord[];
833
- retention: number | undefined;
834
- now: number;
835
- }): { heaps: heaprecord[]; profiles: cpuprofile[]; traces: tracerecord[] } {
836
- const retention = input.retention;
837
- if (retention === undefined) return { heaps: input.heaps, profiles: input.profiles, traces: input.traces };
838
- const expired = (at: number): boolean => input.now - at > retention;
839
- return {
840
- heaps: input.heaps.map((heap) =>
841
- expired(heap.capturedat) && heap.bytesexpired !== true ? { ...heap, bytesexpired: true } : heap,
842
- ),
843
- profiles: input.profiles.map((profile) =>
844
- expired(profile.at) && profile.samplesexpired !== true ? { ...profile, samplesexpired: true } : profile,
845
- ),
846
- traces: input.traces.map((trace) =>
847
- expired(trace.endedat) && trace.bytesexpired !== true ? { ...trace, bytesexpired: true } : trace,
848
- ),
849
- };
850
- }