@lowdefy/server-dev 6.0.0 → 7.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 (625) hide show
  1. package/client/App.jsx +52 -24
  2. package/client/Inspector.jsx +73 -30
  3. package/client/JourneyObserver.jsx +47 -0
  4. package/client/Page.jsx +54 -22
  5. package/client/Recorder.jsx +73 -0
  6. package/client/Routing.jsx +37 -13
  7. package/client/clientBuildImports.test.mjs +63 -0
  8. package/client/devStreamConnections.test.mjs +40 -0
  9. package/client/feedback/FeedbackMount.jsx +2 -2
  10. package/client/feedback/FeedbackOverlay.jsx +7 -4
  11. package/client/feedback/feedbackStyles.js +3 -0
  12. package/client/journeyObserver/startJourneyObserver.js +101 -0
  13. package/client/journeyObserver/startJourneyObserver.test.mjs +149 -0
  14. package/client/openInEditor/OpenInEditorListener.jsx +2 -0
  15. package/client/recorder/buildEngineRecord.js +37 -0
  16. package/client/recorder/buildInteractionRecord.js +64 -0
  17. package/client/recorder/buildTraceEvent.js +57 -0
  18. package/client/recorder/buildTraceEvent.test.mjs +81 -0
  19. package/client/recorder/chunkRecords.js +69 -0
  20. package/client/recorder/chunkRecords.test.mjs +59 -0
  21. package/client/recorder/collectEventActions.js +30 -0
  22. package/client/recorder/collectEventEndpoints.js +40 -0
  23. package/client/recorder/collectEventRequests.js +39 -0
  24. package/client/recorder/createPairingBuffer.js +187 -0
  25. package/client/recorder/createPairingBuffer.test.mjs +405 -0
  26. package/client/recorder/createPasswordRedactor.js +84 -0
  27. package/client/recorder/createRecordBatcher.js +54 -0
  28. package/client/recorder/createRecordBatcher.test.mjs +76 -0
  29. package/client/recorder/createRecorder.js +216 -0
  30. package/client/recorder/createRecorder.test.mjs +318 -0
  31. package/client/recorder/createRecordingSession.js +61 -0
  32. package/client/recorder/createRecordingSession.test.mjs +72 -0
  33. package/client/recorder/diffStateWrites.js +77 -0
  34. package/client/recorder/diffStateWrites.test.mjs +66 -0
  35. package/client/recorder/isEventCall.js +30 -0
  36. package/client/recorder/jsonType.js +39 -0
  37. package/client/recorder/keyChord.js +47 -0
  38. package/client/recorder/keyChord.test.mjs +50 -0
  39. package/client/recorder/sendRecords.js +40 -0
  40. package/client/recorder/toJsonSafe.js +29 -0
  41. package/lib/client/BuildingPage.jsx +3 -1
  42. package/lib/client/ErrorBar.jsx +17 -13
  43. package/lib/client/RedirectingPage.jsx +56 -0
  44. package/lib/client/auth/Auth.jsx +11 -6
  45. package/lib/client/auth/AuthConfigured.jsx +224 -39
  46. package/lib/client/auth/AuthNotConfigured.js +45 -4
  47. package/lib/client/auth/authNotConfiguredParity.test.mjs +61 -0
  48. package/lib/client/auth/authTemplateSync.test.mjs +40 -0
  49. package/lib/client/auth/betterAuthSessionContract.test.mjs +181 -0
  50. package/lib/client/auth/createFetchCheckingBuild.js +45 -0
  51. package/lib/client/bundleBuildId.js +25 -0
  52. package/lib/client/collectVisibleBlockIds.js +51 -0
  53. package/lib/client/getCspNonce.js +25 -0
  54. package/lib/client/getPagePath.js +36 -0
  55. package/lib/client/getPagePath.test.mjs +51 -0
  56. package/lib/client/utils/formatErrorsForCopy.js +30 -0
  57. package/lib/client/utils/formatErrorsForCopy.test.mjs +39 -0
  58. package/lib/client/utils/getErrorBarColor.js +25 -0
  59. package/lib/client/utils/getErrorBarColor.test.mjs +34 -0
  60. package/lib/client/utils/request.js +8 -1
  61. package/lib/client/utils/usePageConfig.js +89 -25
  62. package/lib/client/utils/usePageConfig.test.mjs +198 -13
  63. package/lib/configTree/buildConfigTree.mjs +58 -0
  64. package/lib/configTree/buildConfigTree.test.mjs +77 -0
  65. package/lib/configTree/collectConfigText.mjs +101 -0
  66. package/lib/configTree/collectConfigText.test.mjs +116 -0
  67. package/lib/configTree/prepareScratchServer.mjs +46 -0
  68. package/lib/configTree/runConfigTreeBuild.mjs +88 -0
  69. package/lib/configTree/runConfigTreeBuild.skipPluginChecks.test.mjs +74 -0
  70. package/lib/configTree/runConfigTreeBuild.test.mjs +202 -0
  71. package/lib/docs/JourneyStepError.js +30 -0
  72. package/lib/docs/acquireBrowserSlot.js +30 -0
  73. package/lib/docs/addNestedEndpoints.js +54 -0
  74. package/lib/docs/addNestedEndpoints.test.mjs +185 -0
  75. package/lib/docs/addWriteFlags.js +77 -0
  76. package/lib/docs/buildEditedPages.js +50 -0
  77. package/lib/docs/buildWaitHeader.js +21 -0
  78. package/lib/docs/captureAnnotatedScreenshot.js +23 -12
  79. package/lib/docs/checkAgentFiles.js +148 -0
  80. package/lib/docs/checkAgentFiles.test.mjs +225 -0
  81. package/lib/docs/checkStepReferences.js +239 -0
  82. package/lib/docs/checkStepReferences.test.mjs +173 -0
  83. package/lib/docs/checkpointStore.js +3 -0
  84. package/lib/docs/clientErrorStore.js +18 -1
  85. package/lib/docs/clientErrorStore.test.mjs +46 -0
  86. package/lib/docs/collectExercised.js +52 -0
  87. package/lib/docs/createBrowserLifecycle.js +102 -0
  88. package/lib/docs/createBrowserLifecycle.test.mjs +212 -0
  89. package/lib/docs/createDocsMcpServer.js +345 -388
  90. package/lib/docs/createDocsMcpServer.test.mjs +644 -4
  91. package/lib/docs/createDocsMcpServerRunJourney.test.mjs +148 -0
  92. package/lib/docs/createJourneyActors.chromium.test.mjs +227 -0
  93. package/lib/docs/createJourneyActors.js +213 -0
  94. package/lib/docs/createJourneyActors.test.mjs +79 -0
  95. package/lib/docs/createJourneyObservations.js +80 -0
  96. package/lib/docs/createLeftOriginError.js +26 -0
  97. package/lib/docs/createNetworkCounter.js +101 -0
  98. package/lib/docs/dataSets/checkSharedCollectionIds.js +67 -0
  99. package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
  100. package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +679 -0
  101. package/lib/docs/dataSets/describeDataSetResult.js +25 -0
  102. package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
  103. package/lib/docs/dataSets/deserializeFixture.js +34 -0
  104. package/lib/docs/dataSets/drainSessionWork.js +37 -0
  105. package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
  106. package/lib/docs/dataSets/duplicateIndexName.js +24 -0
  107. package/lib/docs/dataSets/findCollectionCollisions.js +52 -0
  108. package/lib/docs/dataSets/getDataStore.js +106 -0
  109. package/lib/docs/dataSets/getDataStore.mongodb.test.mjs +63 -0
  110. package/lib/docs/dataSets/getDataStore.test.mjs +195 -0
  111. package/lib/docs/dataSets/groupDataSetByCollection.js +44 -0
  112. package/lib/docs/dataSets/insertFixture.js +57 -0
  113. package/lib/docs/dataSets/insertGeneratedDocuments.js +41 -0
  114. package/lib/docs/dataSets/isDataStoreGone.js +29 -0
  115. package/lib/docs/dataSets/loadDataSetCollection.js +42 -0
  116. package/lib/docs/dataSets/mergeCollectionIndexes.js +52 -0
  117. package/lib/docs/dataSets/mergeCollectionIndexes.test.mjs +59 -0
  118. package/lib/docs/dataSets/openDataSession.js +119 -0
  119. package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +343 -0
  120. package/lib/docs/dataSets/pickDataStorePort.js +58 -0
  121. package/lib/docs/dataSets/pickDataStorePort.test.mjs +63 -0
  122. package/lib/docs/dataSets/readConnectionArtifacts.js +46 -0
  123. package/lib/docs/dataSets/readDataSession.js +41 -0
  124. package/lib/docs/dataSets/readDataSession.test.mjs +57 -0
  125. package/lib/docs/dataSets/readDataSet.js +68 -0
  126. package/lib/docs/dataSets/readDataSet.test.mjs +220 -0
  127. package/lib/docs/dataSets/registerDataStoreShutdown.js +47 -0
  128. package/lib/docs/dataSets/removeDataStoreFiles.js +32 -0
  129. package/lib/docs/dataSets/resolveDataSetCollection.js +46 -0
  130. package/lib/docs/dataSets/resolveJourneyDataSet.js +106 -0
  131. package/lib/docs/dataSets/resolveJourneyDataSet.test.mjs +130 -0
  132. package/lib/docs/dataSets/sweepOrphanDatabases.js +33 -0
  133. package/lib/docs/dataSets/test/app/lowdefy.yaml +203 -0
  134. package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
  135. package/lib/docs/dataSets/test/app/tests/data/shop.yaml +8 -0
  136. package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
  137. package/lib/docs/devEventBus.js +136 -0
  138. package/lib/docs/devEventBus.test.mjs +269 -0
  139. package/lib/docs/devToolDefinitions.js +529 -0
  140. package/lib/docs/docSources.js +21 -0
  141. package/lib/docs/docs.test.mjs +171 -15
  142. package/lib/docs/docsIndex.test.mjs +377 -0
  143. package/lib/docs/evalOperator.js +5 -3
  144. package/lib/docs/evalOperatorHeadless.js +84 -12
  145. package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
  146. package/lib/docs/evalOperatorInTab.js +2 -1
  147. package/lib/docs/findConfig.js +95 -49
  148. package/lib/docs/fitResponse.js +81 -0
  149. package/lib/docs/fitResponse.test.mjs +128 -0
  150. package/lib/docs/formatFeedback.js +26 -5
  151. package/lib/docs/formatFeedback.test.mjs +48 -0
  152. package/lib/docs/getAppMap.js +7 -1
  153. package/lib/docs/getBrowser.js +150 -80
  154. package/lib/docs/getBrowser.test.mjs +407 -16
  155. package/lib/docs/getBuildId.js +61 -0
  156. package/lib/docs/getBuildId.test.mjs +84 -0
  157. package/lib/docs/getBuildStatus.js +54 -6
  158. package/lib/docs/getBuildStatus.test.mjs +133 -0
  159. package/lib/docs/getBuildStatusAfterEdits.js +35 -0
  160. package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
  161. package/lib/docs/getCoreDoc.js +9 -1
  162. package/lib/docs/getDoc.js +100 -0
  163. package/lib/docs/getDocsIndex.js +152 -0
  164. package/lib/docs/getHazards.js +115 -0
  165. package/lib/docs/getHazards.test.mjs +175 -0
  166. package/lib/docs/getMailOutboxDirectory.js +26 -0
  167. package/lib/docs/getOverview.js +79 -13
  168. package/lib/docs/getOverviewBasePath.test.mjs +37 -0
  169. package/lib/docs/getPageBuildStatus.js +46 -0
  170. package/lib/docs/getPluginDoc.js +21 -23
  171. package/lib/docs/getSchema.js +33 -1
  172. package/lib/docs/getStaleStatus.js +36 -0
  173. package/lib/docs/getStaleStatus.test.mjs +74 -0
  174. package/lib/docs/guardJourneyOrigin.js +39 -0
  175. package/lib/docs/inspectState.js +5 -3
  176. package/lib/docs/inspectStateFromTab.js +2 -2
  177. package/lib/docs/inspectStateHeadless.js +89 -27
  178. package/lib/docs/inspectStateHeadless.test.mjs +20 -1
  179. package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
  180. package/lib/docs/installHeadlessShell.js +123 -0
  181. package/lib/docs/installHeadlessShell.test.mjs +70 -0
  182. package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
  183. package/lib/docs/isHeadlessShellIncomplete.js +34 -0
  184. package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
  185. package/lib/docs/isPageReady.js +77 -0
  186. package/lib/docs/isPageReady.test.mjs +235 -0
  187. package/lib/docs/isRunErrorEntry.js +25 -0
  188. package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
  189. package/lib/docs/journeyRunRecording.js +35 -0
  190. package/lib/docs/journeyRunRecording.test.mjs +39 -0
  191. package/lib/docs/keepEventStreamAlive.js +68 -0
  192. package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
  193. package/lib/docs/launchBrowser.js +83 -0
  194. package/lib/docs/launchBrowser.test.mjs +175 -0
  195. package/lib/docs/listModuleDocEntries.js +69 -0
  196. package/lib/docs/listPackageDocFiles.js +67 -0
  197. package/lib/docs/listPluginDocEntries.js +111 -0
  198. package/lib/docs/listPlugins.js +9 -58
  199. package/lib/docs/listTypes.js +14 -6
  200. package/lib/docs/loadFrameworkHazards.js +40 -0
  201. package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
  202. package/lib/docs/loadState.js +103 -35
  203. package/lib/docs/makeModuleManifestDoc.js +174 -0
  204. package/lib/docs/matchExercisedPages.js +35 -0
  205. package/lib/docs/matchJourneyRoute.js +69 -0
  206. package/lib/docs/matchJourneyRoute.test.mjs +178 -0
  207. package/lib/docs/mergeNetworkSnapshots.js +49 -0
  208. package/lib/docs/nextJourneySequence.js +30 -0
  209. package/lib/docs/noBrowserError.js +22 -0
  210. package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
  211. package/lib/docs/observe/claimExpectedErrors.js +67 -0
  212. package/lib/docs/observe/createJourneyAppErrors.js +236 -0
  213. package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
  214. package/lib/docs/observe/evaluateInvariants.js +183 -0
  215. package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
  216. package/lib/docs/observe/findingKey.js +29 -0
  217. package/lib/docs/observe/hasNoEffect.js +31 -0
  218. package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
  219. package/lib/docs/observe/installStepObserver.js +72 -0
  220. package/lib/docs/observe/isAppApiUrl.js +33 -0
  221. package/lib/docs/observe/parseAppApiUrl.js +37 -0
  222. package/lib/docs/observe/readStepObserver.js +42 -0
  223. package/lib/docs/observe/relativeSource.js +29 -0
  224. package/lib/docs/observe/resolveConfigKeySource.js +33 -0
  225. package/lib/docs/observe/takeJourneyEvents.js +36 -0
  226. package/lib/docs/observe/toAppErrorFailure.js +61 -0
  227. package/lib/docs/observe/usesSearchStage.js +45 -0
  228. package/lib/docs/observe/waitForClientErrorReports.js +31 -0
  229. package/lib/docs/observe/watchJourneyContext.js +75 -0
  230. package/lib/docs/observe/withAppErrors.js +31 -0
  231. package/lib/docs/observe/withAppErrors.test.mjs +101 -0
  232. package/lib/docs/openJourney.js +94 -0
  233. package/lib/docs/openJourneyEmail.js +41 -0
  234. package/lib/docs/openJourneyEmail.test.mjs +187 -0
  235. package/lib/docs/pageBlocks.js +47 -0
  236. package/lib/docs/pageBuildStatus.test.mjs +103 -0
  237. package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
  238. package/lib/docs/rails/authEngineActions.js +53 -0
  239. package/lib/docs/rails/authEngineActions.test.mjs +69 -0
  240. package/lib/docs/rails/authEngineMethods.js +52 -0
  241. package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
  242. package/lib/docs/rails/checkAuthActionsRan.js +66 -0
  243. package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
  244. package/lib/docs/rails/checkClickRails.js +56 -0
  245. package/lib/docs/rails/createDataSetRails.js +82 -0
  246. package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
  247. package/lib/docs/rails/findExternalBlocks.js +147 -0
  248. package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
  249. package/lib/docs/rails/installAuthActionRecorder.js +56 -0
  250. package/lib/docs/rails/readAuthActionRecorder.js +32 -0
  251. package/lib/docs/rails/readBuiltArtifact.js +30 -0
  252. package/lib/docs/readConnectionPlugins.js +26 -0
  253. package/lib/docs/readDevAuthMode.js +32 -0
  254. package/lib/docs/readDocEntry.js +40 -0
  255. package/lib/docs/readDocTitle.js +26 -0
  256. package/lib/docs/readJourneyEmailMatch.js +57 -0
  257. package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
  258. package/lib/docs/readJourneySession.js +56 -0
  259. package/lib/docs/readMailOutbox.js +37 -0
  260. package/lib/docs/readPagePath.js +27 -0
  261. package/lib/docs/readPluginPackages.js +99 -0
  262. package/lib/docs/readProxyBuildWait.js +38 -0
  263. package/lib/docs/readRoutineSteps.js +45 -0
  264. package/lib/docs/registerShownTextEngine.js +118 -0
  265. package/lib/docs/requestRestart.js +30 -0
  266. package/lib/docs/requestRestart.test.mjs +49 -0
  267. package/lib/docs/resolvePageInstance.js +46 -0
  268. package/lib/docs/resolvePageInstance.test.mjs +81 -0
  269. package/lib/docs/resolveSource.js +11 -7
  270. package/lib/docs/resolveSource.test.mjs +1 -1
  271. package/lib/docs/resolveToolCaller.js +62 -0
  272. package/lib/docs/reviewPage.js +51 -0
  273. package/lib/docs/reviewPageBuilds.js +56 -0
  274. package/lib/docs/reviewPageBuilds.test.mjs +207 -0
  275. package/lib/docs/runCheck.js +75 -0
  276. package/lib/docs/runCheck.test.mjs +91 -0
  277. package/lib/docs/runCheckChild.js +49 -0
  278. package/lib/docs/runEndpoint.js +185 -0
  279. package/lib/docs/runEndpoint.test.mjs +509 -0
  280. package/lib/docs/runErrorBuffers.js +99 -0
  281. package/lib/docs/runErrorBuffers.test.mjs +95 -0
  282. package/lib/docs/runJourney.js +347 -0
  283. package/lib/docs/runJourney.test.mjs +2383 -0
  284. package/lib/docs/runJourneyAsUsers.js +44 -0
  285. package/lib/docs/runJourneyDataSet.test.mjs +274 -0
  286. package/lib/docs/runJourneySteps.js +1320 -0
  287. package/lib/docs/runRequest.js +100 -59
  288. package/lib/docs/runRequest.test.mjs +190 -0
  289. package/lib/docs/saveAnnotatedScreenshot.js +1 -1
  290. package/lib/docs/scaffoldPage.js +3 -1
  291. package/lib/docs/screenshotPage.js +163 -11
  292. package/lib/docs/screenshotPage.test.mjs +263 -1
  293. package/lib/docs/screenshotPageClip.test.mjs +4 -0
  294. package/lib/docs/searchDocs.js +82 -30
  295. package/lib/docs/searchDocs.test.mjs +100 -0
  296. package/lib/docs/searchIcons.js +90 -0
  297. package/lib/docs/searchIcons.test.mjs +127 -0
  298. package/lib/docs/selectExercised.js +39 -0
  299. package/lib/docs/selectFinalState.js +66 -0
  300. package/lib/docs/selectFinalState.test.mjs +51 -0
  301. package/lib/docs/serverErrorStore.js +54 -0
  302. package/lib/docs/serverErrorStore.test.mjs +50 -0
  303. package/lib/docs/setupTestFixtures.mjs +133 -39
  304. package/lib/docs/shownTextSelector.js +28 -0
  305. package/lib/docs/snapshotState.js +13 -5
  306. package/lib/docs/splitSearchTerms.js +62 -0
  307. package/lib/docs/splitSearchTerms.test.mjs +50 -0
  308. package/lib/docs/stateCheckpoints.test.mjs +77 -0
  309. package/lib/docs/tabAvailable.js +6 -5
  310. package/lib/docs/tabChannel.js +127 -21
  311. package/lib/docs/tabChannel.test.mjs +187 -29
  312. package/lib/docs/unsettledPageNote.js +24 -0
  313. package/lib/docs/validateJourneyMail.js +36 -0
  314. package/lib/docs/validateJourneyTimeout.js +38 -0
  315. package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
  316. package/lib/docs/validateRecordingParam.js +42 -0
  317. package/lib/docs/validateStateSelection.js +33 -0
  318. package/lib/docs/validateStateSelection.test.mjs +33 -0
  319. package/lib/docs/validateViewport.js +51 -0
  320. package/lib/docs/validateViewport.test.mjs +54 -0
  321. package/lib/docs/waitForBuild.js +50 -0
  322. package/lib/docs/waitForBuild.test.mjs +75 -0
  323. package/lib/docs/waitForJourneyEmail.js +77 -0
  324. package/lib/docs/withBrowserSlot.js +45 -0
  325. package/lib/docs/withBrowserSlot.test.mjs +75 -0
  326. package/lib/docs/withDataSession.js +53 -0
  327. package/lib/server/applyDataSetRedirect.js +83 -0
  328. package/lib/server/applyDataSetRedirect.test.mjs +340 -0
  329. package/lib/server/applyDataSetRedirectTenantReach.test.mjs +204 -0
  330. package/lib/server/applyDataSetRedirectWalled.test.mjs +131 -0
  331. package/lib/server/auth/createSystemContext.js +73 -0
  332. package/lib/server/auth/getAuth.js +46 -0
  333. package/lib/server/auth/getHeadlessUser.js +9 -8
  334. package/lib/server/auth/getHeadlessUser.test.mjs +41 -0
  335. package/lib/server/auth/getMockUser.js +18 -14
  336. package/lib/server/auth/{getAuthConfig.js → getStrategies.js} +12 -10
  337. package/lib/server/auth/headlessUser.js +3 -2
  338. package/lib/server/auth/journeyActor.js +35 -0
  339. package/lib/server/auth/resolveHeadlessUser.test.mjs +0 -4
  340. package/lib/server/checkPageRecord.js +44 -0
  341. package/lib/server/contextMapBudget.js +24 -0
  342. package/lib/server/contextMapBudget.test.mjs +98 -0
  343. package/lib/server/continueJitKeys.js +28 -0
  344. package/lib/server/continueJitKeys.test.mjs +43 -0
  345. package/lib/server/createDataSessionEndedError.js +33 -0
  346. package/lib/server/createDataSetRefusal.js +26 -0
  347. package/lib/server/createLowdefyContext.js +174 -41
  348. package/lib/server/createLowdefyContext.test.mjs +437 -0
  349. package/lib/server/getClientAddress.js +51 -0
  350. package/lib/server/getClientAddress.test.mjs +56 -0
  351. package/lib/server/getPageJitEnrichment.test.mjs +29 -11
  352. package/lib/server/hashConfigContent.js +26 -0
  353. package/lib/server/importAppCode.test.mjs +57 -0
  354. package/lib/server/isRoutineStep.js +27 -0
  355. package/lib/server/jitPageBuilder.js +309 -126
  356. package/lib/server/jitPageBuilder.skipPluginChecks.test.mjs +78 -0
  357. package/lib/server/jitPageBuilder.test.mjs +218 -0
  358. package/lib/server/journeyCookies/JOURNEY_COOKIES.js +37 -0
  359. package/lib/server/journeyCookies/forwardJourneyCookies.js +46 -0
  360. package/lib/server/journeyCookies/readJourneyCookie.js +52 -0
  361. package/lib/server/journeyCookies/writeJourneyCookie.js +46 -0
  362. package/lib/server/journeyCookies.js +22 -0
  363. package/lib/server/journeyCookies.test.mjs +145 -0
  364. package/lib/server/loadDynamicJsMap.js +47 -0
  365. package/lib/server/log/createHandleError.js +38 -2
  366. package/lib/server/log/createHandleError.test.mjs +125 -2
  367. package/lib/server/log/createLogger.js +3 -0
  368. package/lib/server/log/createLogger.test.mjs +77 -0
  369. package/lib/server/log/logRequest.js +2 -3
  370. package/lib/server/mutantDataSetWrappers.test.mjs +112 -0
  371. package/lib/server/mutants/applyMutant.js +39 -0
  372. package/lib/server/mutants/artifactPatterns.js +25 -0
  373. package/lib/server/mutants/buildAnchor.js +45 -0
  374. package/lib/server/mutants/collectSharedIds.js +41 -0
  375. package/lib/server/mutants/configPathSegments.js +38 -0
  376. package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
  377. package/lib/server/mutants/describeEvent.js +22 -0
  378. package/lib/server/mutants/enumerateArtifact.js +56 -0
  379. package/lib/server/mutants/findNodeByKey.js +45 -0
  380. package/lib/server/mutants/findNodeByPointer.js +49 -0
  381. package/lib/server/mutants/getArtifactKind.js +32 -0
  382. package/lib/server/mutants/groupMutantCopies.js +56 -0
  383. package/lib/server/mutants/hashArtifact.js +25 -0
  384. package/lib/server/mutants/hashMutantTarget.js +34 -0
  385. package/lib/server/mutants/listMutants.js +122 -0
  386. package/lib/server/mutants/listMutants.test.mjs +502 -0
  387. package/lib/server/mutants/mutantId.js +30 -0
  388. package/lib/server/mutants/mutantRuns.js +57 -0
  389. package/lib/server/mutants/mutantRuns.test.mjs +136 -0
  390. package/lib/server/mutants/operators/dropAction.js +47 -0
  391. package/lib/server/mutants/operators/dropBlock.js +44 -0
  392. package/lib/server/mutants/operators/dropPayload.js +52 -0
  393. package/lib/server/mutants/operators/dropStep.js +44 -0
  394. package/lib/server/mutants/operators/flipVisible.js +53 -0
  395. package/lib/server/mutants/operators/index.js +37 -0
  396. package/lib/server/mutants/operators/retargetLink.js +72 -0
  397. package/lib/server/mutants/operators/skipValidate.js +77 -0
  398. package/lib/server/mutants/operators/swapIf.js +112 -0
  399. package/lib/server/mutants/operators.test.mjs +396 -0
  400. package/lib/server/mutants/positionFreePath.js +38 -0
  401. package/lib/server/mutants/removeFromParent.js +28 -0
  402. package/lib/server/mutants/test/fixtures.mjs +173 -0
  403. package/lib/server/mutants/test/keyArtifact.mjs +63 -0
  404. package/lib/server/mutants/validateMutantParam.js +69 -0
  405. package/lib/server/mutants/validateMutantsBody.js +88 -0
  406. package/lib/server/mutants/walkArtifact.js +179 -0
  407. package/lib/server/pageBuildRecords.js +131 -0
  408. package/lib/server/pageBuildRecords.test.mjs +258 -0
  409. package/lib/server/pageCache.mjs +22 -7
  410. package/lib/server/pageCache.test.mjs +22 -7
  411. package/lib/server/pageRebuilds.test.mjs +543 -0
  412. package/lib/server/pruneJitMaps.js +36 -0
  413. package/lib/server/pruneJitMaps.test.mjs +55 -0
  414. package/lib/server/readInstanceRecord.js +33 -0
  415. package/lib/server/readInstanceRecord.test.mjs +99 -0
  416. package/lib/server/readMergedMaps.js +86 -0
  417. package/lib/server/readMergedMaps.test.mjs +79 -0
  418. package/lib/server/recording/appendRecords.js +33 -0
  419. package/lib/server/recording/errorRecordingStamp.js +34 -0
  420. package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
  421. package/lib/server/recording/isRecordingEnabled.js +31 -0
  422. package/lib/server/recording/readRecordingCookie.js +71 -0
  423. package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
  424. package/lib/server/recording/recordingCookiePayload.js +33 -0
  425. package/lib/server/recording/servedBuilds.js +46 -0
  426. package/lib/server/recording/servedBuilds.test.mjs +42 -0
  427. package/lib/server/scrubSecrets.js +23 -0
  428. package/lib/server/sinkExternalConnections.js +95 -0
  429. package/lib/server/sinkExternalConnections.test.mjs +135 -0
  430. package/lib/server/skipStaleMapWrites.js +62 -0
  431. package/lib/server/skipStaleMapWrites.test.mjs +113 -0
  432. package/lib/server/startIdleGc.js +67 -0
  433. package/lib/server/startIdleGc.test.mjs +138 -0
  434. package/lib/server/testFixtures/importAppCodeTwice.mjs +70 -0
  435. package/lib/server/trackSessionWork.js +30 -0
  436. package/lib/vite/createDevServerExclude.js +40 -0
  437. package/lib/vite/createDevServerExclude.test.mjs +79 -0
  438. package/manager/getContext.mjs +24 -2
  439. package/manager/processes/initialBuild.mjs +14 -0
  440. package/manager/processes/initialBuild.test.mjs +101 -0
  441. package/manager/processes/lowdefyBuild.mjs +30 -7
  442. package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +62 -0
  443. package/manager/processes/lowdefyBuild.test.mjs +136 -1
  444. package/manager/processes/optimizeDependencies.mjs +59 -0
  445. package/manager/processes/restartServer.mjs +34 -6
  446. package/manager/processes/restartServer.test.mjs +162 -0
  447. package/manager/processes/shutdownServer.mjs +11 -0
  448. package/manager/processes/shutdownServer.test.mjs +45 -0
  449. package/manager/processes/startFirstServer.mjs +46 -0
  450. package/manager/processes/startFirstServer.test.mjs +326 -0
  451. package/manager/processes/startMailSink.mjs +135 -0
  452. package/manager/processes/startMailSink.test.mjs +146 -0
  453. package/manager/processes/startProxy.mjs +129 -12
  454. package/manager/processes/startProxy.test.mjs +366 -0
  455. package/manager/processes/startServer.mjs +47 -6
  456. package/manager/processes/startServer.test.mjs +155 -0
  457. package/manager/processes/startWatchers.mjs +6 -4
  458. package/manager/processes/syncServer.mjs +69 -0
  459. package/manager/processes/syncServer.test.mjs +141 -0
  460. package/manager/processes/waitForDevServer.mjs +48 -0
  461. package/manager/processes/waitForDevServer.test.mjs +112 -0
  462. package/manager/run.mjs +128 -43
  463. package/manager/utils/BatchChanges.mjs +21 -5
  464. package/manager/utils/BatchChanges.test.mjs +77 -0
  465. package/manager/utils/acquireDevInstance.mjs +99 -0
  466. package/manager/utils/acquireDevInstance.test.mjs +160 -0
  467. package/manager/utils/createBuildActivity.mjs +67 -0
  468. package/manager/utils/createBuildActivity.test.mjs +58 -0
  469. package/manager/utils/createChangeSignal.mjs +39 -0
  470. package/manager/utils/createChangeSignal.test.mjs +62 -0
  471. package/manager/utils/createCustomPluginMessagesMap.mjs +23 -33
  472. package/manager/utils/createCustomPluginTypesMap.mjs +24 -30
  473. package/manager/utils/createCustomPluginTypesMap.test.mjs +94 -0
  474. package/manager/utils/createDotPathIgnore.mjs +44 -0
  475. package/manager/utils/createDotPathIgnore.test.mjs +65 -0
  476. package/manager/utils/createIdleGc.mjs +51 -0
  477. package/manager/utils/createIdleGc.test.mjs +77 -0
  478. package/manager/utils/createRequestActivity.mjs +100 -0
  479. package/manager/utils/createRequestActivity.test.mjs +121 -0
  480. package/manager/utils/createServerArtifactTracker.mjs +83 -0
  481. package/manager/utils/createServerArtifactTracker.test.mjs +78 -0
  482. package/manager/utils/createServerEnv.mjs +39 -0
  483. package/manager/utils/filesHaveSameBytes.mjs +41 -0
  484. package/manager/utils/findBuildFilesOutsideWatch.mjs +59 -0
  485. package/manager/utils/findBuildFilesOutsideWatch.test.mjs +111 -0
  486. package/manager/utils/importFresh.mjs +46 -0
  487. package/manager/utils/importFresh.test.mjs +72 -0
  488. package/manager/utils/importFreshWorker.mjs +20 -0
  489. package/manager/utils/killTaggedBrowser.mjs +35 -0
  490. package/manager/utils/pagePathChanged.mjs +56 -0
  491. package/manager/utils/pruneRecordings.mjs +85 -0
  492. package/manager/utils/pruneRecordings.test.mjs +117 -0
  493. package/manager/utils/publishBuildDirectory.mjs +121 -0
  494. package/manager/utils/publishBuildDirectory.order.test.mjs +74 -0
  495. package/manager/utils/publishBuildDirectory.test.mjs +262 -0
  496. package/manager/utils/readBasePath.mjs +27 -0
  497. package/manager/utils/readBasePath.test.mjs +43 -0
  498. package/manager/utils/readBuildStatusWait.mjs +67 -0
  499. package/manager/utils/readManagerVersion.mjs +30 -0
  500. package/manager/utils/readManagerVersion.test.mjs +35 -0
  501. package/manager/utils/readPluginDefinitions.mjs +36 -0
  502. package/manager/utils/resolveDevAuthUrl.mjs +58 -0
  503. package/manager/utils/resolveDevAuthUrl.test.mjs +107 -0
  504. package/manager/utils/resolvePorts.mjs +49 -0
  505. package/manager/utils/setupWatcher.mjs +19 -11
  506. package/manager/utils/startRecordingPruner.mjs +45 -0
  507. package/manager/utils/startRecordingPruner.test.mjs +66 -0
  508. package/manager/utils/waitForChildExit.mjs +53 -0
  509. package/manager/utils/waitForChildExit.test.mjs +70 -0
  510. package/manager/utils/waitForServer.mjs +44 -0
  511. package/manager/utils/writeBuildStatus.mjs +1 -1
  512. package/manager/watchers/createPluginSourceIgnore.mjs +77 -0
  513. package/manager/watchers/envWatcher.mjs +9 -2
  514. package/manager/watchers/envWatcher.test.mjs +87 -0
  515. package/manager/watchers/listServerSidePackages.mjs +47 -0
  516. package/manager/watchers/listServerSidePackages.test.mjs +38 -0
  517. package/manager/watchers/lowdefyBuildWatcher.mjs +106 -22
  518. package/manager/watchers/lowdefyBuildWatcher.test.mjs +309 -0
  519. package/manager/watchers/pluginSourceWatcher.mjs +79 -0
  520. package/manager/watchers/pluginSourceWatcher.test.mjs +222 -0
  521. package/manager/watchers/restartRequestWatcher.mjs +71 -0
  522. package/manager/watchers/restartRequestWatcher.test.mjs +111 -0
  523. package/manager/watchers/selectLocalPluginPackages.mjs +48 -0
  524. package/manager/watchers/selectLocalPluginPackages.test.mjs +73 -0
  525. package/manager/watchers/serverPackageWatcher.mjs +38 -0
  526. package/package.json +61 -49
  527. package/package.original.json +61 -49
  528. package/src/app.js +131 -20
  529. package/src/html/renderDevPage.js +63 -11
  530. package/src/html/renderDevPage.test.mjs +138 -0
  531. package/src/lib/getRequestPath.js +29 -0
  532. package/src/middleware/createSameOriginGuard.js +37 -0
  533. package/src/middleware/createSameOriginGuard.test.mjs +102 -0
  534. package/src/middleware/errorHandler.js +51 -1
  535. package/src/middleware/errorHandler.test.mjs +196 -14
  536. package/src/middleware/isRebindingSafeHost.js +44 -0
  537. package/src/middleware/isRebindingSafeHost.test.mjs +32 -0
  538. package/src/middleware/localDevToolsOnly.js +38 -0
  539. package/src/middleware/localDevToolsOnly.test.mjs +61 -0
  540. package/src/middleware/noStore.js +27 -0
  541. package/src/middleware/noStore.test.mjs +48 -0
  542. package/src/middleware/requestTimeout.js +38 -0
  543. package/src/middleware/staleFlag.js +76 -0
  544. package/src/middleware/staleFlag.test.mjs +127 -0
  545. package/src/middleware/stampCaller.js +38 -0
  546. package/src/middleware/stampCaller.test.mjs +64 -0
  547. package/src/routes/agent.js +15 -3
  548. package/src/routes/auth.js +29 -20
  549. package/src/routes/auth.test.mjs +30 -56
  550. package/src/routes/clientError.js +13 -13
  551. package/src/routes/cron.js +3 -1
  552. package/src/routes/cronForward.js +4 -2
  553. package/src/routes/detached.js +22 -9
  554. package/src/routes/devInspect.js +17 -8
  555. package/src/routes/devInspect.test.mjs +44 -7
  556. package/src/routes/devRecording.js +113 -0
  557. package/src/routes/devRecording.test.mjs +253 -0
  558. package/src/routes/docs/buildStatus.js +10 -2
  559. package/src/routes/docs/buildStatus.test.mjs +46 -0
  560. package/src/routes/docs/content.js +5 -3
  561. package/src/routes/docs/evalOperator.js +15 -1
  562. package/src/routes/docs/events.js +50 -0
  563. package/src/routes/docs/events.test.mjs +117 -0
  564. package/src/routes/docs/find.js +9 -1
  565. package/src/routes/docs/icons.js +30 -0
  566. package/src/routes/docs/inspectState.js +16 -1
  567. package/src/routes/docs/journey.js +194 -0
  568. package/src/routes/docs/journey.test.mjs +401 -0
  569. package/src/routes/docs/journeyRefusals.test.mjs +71 -0
  570. package/src/routes/docs/loadState.js +4 -2
  571. package/src/routes/docs/mcp.js +34 -3
  572. package/src/routes/docs/mcp.test.mjs +130 -0
  573. package/src/routes/docs/mutants.js +78 -0
  574. package/src/routes/docs/mutants.test.mjs +180 -0
  575. package/src/routes/docs/parsePathParamsParam.js +41 -0
  576. package/src/routes/docs/parsePathParamsParam.test.mjs +42 -0
  577. package/src/routes/docs/parseUserParam.js +27 -21
  578. package/src/routes/docs/parseUserParam.test.mjs +17 -5
  579. package/src/routes/docs/restart.js +35 -0
  580. package/src/routes/docs/restart.test.mjs +64 -0
  581. package/src/routes/docs/runEndpoint.js +54 -0
  582. package/src/routes/docs/runEndpoint.test.mjs +165 -0
  583. package/src/routes/docs/runRequest.js +25 -8
  584. package/src/routes/docs/runRequest.test.mjs +92 -0
  585. package/src/routes/docs/screenshot.js +50 -2
  586. package/src/routes/docs/screenshot.test.mjs +117 -0
  587. package/src/routes/docs/search.js +15 -2
  588. package/src/routes/docs/snapshotState.js +17 -3
  589. package/src/routes/endpoints.js +27 -26
  590. package/src/routes/endpointsTextAnswer.test.mjs +77 -0
  591. package/src/routes/feedback.js +10 -13
  592. package/src/routes/jitPage.js +99 -35
  593. package/src/routes/jitPage.test.mjs +248 -0
  594. package/src/routes/mcp.js +82 -5
  595. package/src/routes/mcp.test.mjs +215 -0
  596. package/src/routes/mcpMemberToken.test.mjs +125 -0
  597. package/src/routes/mcpProtectedResourceMetadata.js +34 -0
  598. package/src/routes/mcpProtectedResourceMetadata.test.mjs +65 -0
  599. package/src/routes/mountDevPageRoutes.js +25 -0
  600. package/src/routes/mountDevPageRoutes.test.mjs +30 -0
  601. package/src/routes/mountOauthDiscovery.js +44 -0
  602. package/src/routes/mountOauthDiscovery.test.mjs +79 -0
  603. package/src/routes/normalizeMcpProtocolVersionHeader.js +46 -0
  604. package/src/routes/normalizeMcpProtocolVersionHeader.test.mjs +62 -0
  605. package/src/routes/reload.js +16 -0
  606. package/src/routes/request.js +6 -4
  607. package/src/routes/request.test.mjs +9 -5
  608. package/src/routes/requestBody.test.mjs +159 -0
  609. package/src/routes/usage.js +6 -2
  610. package/src/routes/user.js +27 -0
  611. package/src/routes/websocket.js +29 -8
  612. package/src/routes/websocket.test.mjs +65 -0
  613. package/src/routes/wellKnownFallback.js +30 -0
  614. package/src/routes/wellKnownFallback.test.mjs +32 -0
  615. package/src/websocket/devWebSocket.js +41 -26
  616. package/src/websocket/devWebSocket.test.mjs +157 -0
  617. package/vite.config.js +53 -23
  618. package/lib/server/auth/getDevSession.js +0 -70
  619. package/lib/server/auth/getDevSession.test.mjs +0 -138
  620. package/lib/server/auth/session.js +0 -39
  621. package/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
  622. package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
  623. package/manager/watchers/moduleBuildWatcher.mjs +0 -79
  624. package/manager/watchers/serverArtifactWatcher.mjs +0 -120
  625. package/src/routes/apiPage.js +0 -37
@@ -0,0 +1,69 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import path from 'node:path';
18
+ import { type } from '@lowdefy/helpers';
19
+
20
+ import listPackageDocFiles from './listPackageDocFiles.js';
21
+ import parseModuleSource from './parseModuleSource.js';
22
+ import readDocTitle from './readDocTitle.js';
23
+
24
+ // Docs of the app's modules: each README and docs/*.md, plus a generated page
25
+ // of the components, exports and vars its module.lowdefy.yaml declares.
26
+ // Returns the entries and the local module roots, whose doc files can change
27
+ // while the server runs.
28
+ function listModuleDocEntries({ modules }) {
29
+ const entries = [];
30
+ const localDirs = [];
31
+ for (const moduleEntry of Object.values(modules ?? {})) {
32
+ if (moduleEntry.isLocal) {
33
+ localDirs.push(moduleEntry.moduleRoot);
34
+ }
35
+ const base = {
36
+ section: 'Modules',
37
+ source: 'module',
38
+ ...parseModuleSource({ source: moduleEntry.source }),
39
+ };
40
+ const moduleSlug = `modules/${moduleEntry.id}`;
41
+ const { readme, docs } = listPackageDocFiles({ dir: moduleEntry.moduleRoot });
42
+ if (!type.isNone(readme)) {
43
+ entries.push({
44
+ ...base,
45
+ slug: moduleSlug,
46
+ title: readDocTitle({ filePath: readme, fallback: `${moduleEntry.id} module` }),
47
+ filePath: readme,
48
+ });
49
+ }
50
+ for (const filePath of docs) {
51
+ const stem = path.basename(filePath, '.md');
52
+ entries.push({
53
+ ...base,
54
+ slug: `${moduleSlug}/${stem}`,
55
+ title: readDocTitle({ filePath, fallback: stem }),
56
+ filePath,
57
+ });
58
+ }
59
+ entries.push({
60
+ ...base,
61
+ slug: `${moduleSlug}/manifest`,
62
+ title: `${moduleEntry.id} module: components, exports and vars`,
63
+ moduleEntry,
64
+ });
65
+ }
66
+ return { entries, localDirs };
67
+ }
68
+
69
+ export default listModuleDocEntries;
@@ -0,0 +1,67 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+
20
+ // Docs are served from inside the package only: a file, or a docs folder,
21
+ // that links outside it is left out, as is a link that points nowhere or a
22
+ // directory named like a markdown file.
23
+ function isFileInside({ realDir, filePath }) {
24
+ if (!fs.existsSync(filePath)) {
25
+ return false;
26
+ }
27
+ const realPath = fs.realpathSync(filePath);
28
+ return realPath.startsWith(`${realDir}${path.sep}`) && fs.statSync(realPath).isFile();
29
+ }
30
+
31
+ function listMarkdownFiles({ realDir, dir }) {
32
+ return fs
33
+ .readdirSync(dir)
34
+ .filter((fileName) => fileName.endsWith('.md'))
35
+ .sort()
36
+ .map((fileName) => path.join(dir, fileName))
37
+ .filter((filePath) => isFileInside({ realDir, filePath }));
38
+ }
39
+
40
+ // The markdown a plugin or module ships: its README, and the files in docs/
41
+ // (a plugin that builds its docs may only have dist/docs/).
42
+ function listPackageDocFiles({ dir }) {
43
+ // A local plugin or module can be removed while the server runs.
44
+ if (!fs.existsSync(dir)) {
45
+ return { readme: null, docs: [] };
46
+ }
47
+ const realDir = fs.realpathSync(dir);
48
+ let readme = null;
49
+ for (const fileName of ['README.md', 'readme.md']) {
50
+ const filePath = path.join(dir, fileName);
51
+ if (isFileInside({ realDir, filePath })) {
52
+ readme = filePath;
53
+ break;
54
+ }
55
+ }
56
+ let docs = [];
57
+ for (const docsDir of ['docs', 'dist/docs']) {
58
+ const dirPath = path.join(dir, docsDir);
59
+ if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
60
+ docs = listMarkdownFiles({ realDir, dir: dirPath });
61
+ break;
62
+ }
63
+ }
64
+ return { readme, docs };
65
+ }
66
+
67
+ export default listPackageDocFiles;
@@ -0,0 +1,111 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import { type } from '@lowdefy/helpers';
20
+
21
+ import listPackageDocFiles from './listPackageDocFiles.js';
22
+ import readDocTitle from './readDocTitle.js';
23
+ import readPluginPackages from './readPluginPackages.js';
24
+ import resolvePluginDir from './resolvePluginDir.js';
25
+
26
+ // Type kinds a doc can be looked up by, singular as the core docs name them.
27
+ const TYPE_KINDS = {
28
+ actions: 'action',
29
+ agents: 'agent',
30
+ blocks: 'block',
31
+ connections: 'connection',
32
+ notifications: 'notification',
33
+ operators: 'operator',
34
+ requests: 'request',
35
+ websockets: 'websocket',
36
+ };
37
+
38
+ function readPackageVersion({ dir, fallback }) {
39
+ const packageJson = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8'));
40
+ return packageJson.version ?? fallback;
41
+ }
42
+
43
+ // A published plugin is installed inside node_modules; a local one links to a
44
+ // directory outside it.
45
+ function isLocalPluginDir({ dir }) {
46
+ return !dir.split(path.sep).includes('node_modules');
47
+ }
48
+
49
+ // Docs shipped by the app's plugins, Lowdefy's own left out (the core docs
50
+ // cover them). Returns the entries, the directories whose doc files can change
51
+ // while the server runs, and each type's doc slug, keyed "kind:typeName".
52
+ function listPluginDocEntries() {
53
+ const entries = [];
54
+ const localDirs = [];
55
+ const typeDocs = new Map();
56
+ for (const plugin of readPluginPackages()) {
57
+ if (plugin.package.startsWith('@lowdefy/')) {
58
+ continue;
59
+ }
60
+ const dir = resolvePluginDir({ packageName: plugin.package });
61
+ if (type.isNone(dir)) {
62
+ continue;
63
+ }
64
+ const local = isLocalPluginDir({ dir });
65
+ if (local) {
66
+ localDirs.push(dir);
67
+ }
68
+ const base = {
69
+ section: 'Plugins',
70
+ source: local ? 'local-plugin' : 'plugin',
71
+ package: plugin.package,
72
+ version: readPackageVersion({ dir, fallback: plugin.version }),
73
+ };
74
+ const { readme, docs } = listPackageDocFiles({ dir });
75
+ const packageSlug = `plugins/${plugin.package}`;
76
+ if (!type.isNone(readme)) {
77
+ entries.push({ ...base, slug: packageSlug, title: plugin.package, filePath: readme });
78
+ }
79
+ const docsByStem = new Map();
80
+ for (const filePath of docs) {
81
+ const stem = path.basename(filePath, '.md');
82
+ const entry = {
83
+ ...base,
84
+ slug: `${packageSlug}/${stem}`,
85
+ title: readDocTitle({ filePath, fallback: stem }),
86
+ filePath,
87
+ };
88
+ docsByStem.set(stem.toLowerCase(), entry);
89
+ entries.push(entry);
90
+ }
91
+ for (const [kinds, typeNames] of Object.entries(plugin.types)) {
92
+ const kind = TYPE_KINDS[kinds];
93
+ if (type.isUndefined(kind)) {
94
+ continue;
95
+ }
96
+ for (const typeName of typeNames) {
97
+ const typeEntry = docsByStem.get(typeName.toLowerCase());
98
+ if (!type.isUndefined(typeEntry)) {
99
+ typeEntry.kind = typeEntry.kind ?? kind;
100
+ typeEntry.typeName = typeEntry.typeName ?? typeName;
101
+ typeDocs.set(`${kind}:${typeName}`, typeEntry.slug);
102
+ } else if (!type.isNone(readme)) {
103
+ typeDocs.set(`${kind}:${typeName}`, packageSlug);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ return { entries, localDirs, typeDocs };
109
+ }
110
+
111
+ export default listPluginDocEntries;
@@ -14,67 +14,18 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import readBuildArtifact from './readBuildArtifact.js';
18
-
19
- function addTypes({ plugins, store, kind }) {
20
- for (const [typeName, definition] of Object.entries(store ?? {})) {
21
- if (!plugins[definition.package]) {
22
- plugins[definition.package] = { package: definition.package, version: definition.version, types: {} };
23
- }
24
- const plugin = plugins[definition.package];
25
- if (!plugin.types[kind]) {
26
- plugin.types[kind] = [];
27
- }
28
- plugin.types[kind].push(typeName);
29
- }
30
- }
17
+ import getDocsIndex from './getDocsIndex.js';
18
+ import readPluginPackages from './readPluginPackages.js';
31
19
 
32
20
  function listPlugins() {
33
- const availableTypes = readBuildArtifact({ name: 'plugins/availableTypes.json' }) ?? {};
34
- const customTypesMap = readBuildArtifact({ name: 'customTypesMap.json' }) ?? {};
35
- const installedPackages = readBuildArtifact({ name: 'installedPluginPackages.json' }) ?? [];
36
-
37
- const plugins = {};
38
- addTypes({ plugins, store: availableTypes.actions, kind: 'actions' });
39
- addTypes({ plugins, store: availableTypes.agents, kind: 'agents' });
40
- addTypes({ plugins, store: availableTypes.blocks, kind: 'blocks' });
41
- addTypes({ plugins, store: availableTypes.connections, kind: 'connections' });
42
- addTypes({ plugins, store: availableTypes.notifications, kind: 'notifications' });
43
- addTypes({ plugins, store: availableTypes.operators?.client, kind: 'operators' });
44
- addTypes({ plugins, store: availableTypes.operators?.server, kind: 'operators' });
45
- addTypes({ plugins, store: availableTypes.requests, kind: 'requests' });
46
- addTypes({ plugins, store: availableTypes.websockets, kind: 'websockets' });
47
-
48
- const customPackages = new Set();
49
- const customStores = [
50
- customTypesMap.actions,
51
- customTypesMap.agents,
52
- customTypesMap.blocks,
53
- customTypesMap.connections,
54
- customTypesMap.operators?.client,
55
- customTypesMap.operators?.server,
56
- customTypesMap.requests,
57
- customTypesMap.websockets,
58
- ];
59
- for (const store of customStores) {
60
- for (const definition of Object.values(store ?? {})) {
61
- customPackages.add(definition.package);
21
+ const slugs = new Set(getDocsIndex().entries.map((entry) => entry.slug));
22
+ return readPluginPackages().map((plugin) => {
23
+ const docSlug = `plugins/${plugin.package}`;
24
+ if (!slugs.has(docSlug)) {
25
+ return plugin;
62
26
  }
63
- }
64
-
65
- return Object.values(plugins)
66
- .map((plugin) => ({
67
- ...plugin,
68
- custom: customPackages.has(plugin.package),
69
- installed: installedPackages.includes(plugin.package),
70
- types: Object.fromEntries(
71
- Object.entries(plugin.types).map(([kind, typeNames]) => [
72
- kind,
73
- [...new Set(typeNames)].sort(),
74
- ])
75
- ),
76
- }))
77
- .sort((a, b) => a.package.localeCompare(b.package));
27
+ return { ...plugin, docSlug };
28
+ });
78
29
  }
79
30
 
80
31
  export default listPlugins;
@@ -16,7 +16,7 @@
16
16
 
17
17
  import { type } from '@lowdefy/helpers';
18
18
 
19
- import getDocsManifest from './getDocsManifest.js';
19
+ import getDocsIndex from './getDocsIndex.js';
20
20
  import normalizeTypeKind from './normalizeTypeKind.js';
21
21
  import readBuildArtifact from './readBuildArtifact.js';
22
22
 
@@ -52,19 +52,27 @@ function listTypes({ kind }) {
52
52
  const normalizedKind = normalizeTypeKind({ kind });
53
53
  if (type.isNone(normalizedKind)) {
54
54
  throw new Error(
55
- `Unknown type kind. Received ${JSON.stringify(kind)}. Use one of: blocks, operators, actions, connections, requests, agents, notifications, websockets.`
55
+ `Unknown type kind. Received ${JSON.stringify(
56
+ kind
57
+ )}. Use one of: blocks, operators, actions, connections, requests, agents, notifications, websockets.`
56
58
  );
57
59
  }
58
60
  const availableTypes = readBuildArtifact({ name: 'plugins/availableTypes.json' }) ?? {};
59
61
  const types = readBuildArtifact({ name: 'types.json', deserialize: true }) ?? {};
60
62
  const blockMetas = readBuildArtifact({ name: 'plugins/blockMetas.json' }) ?? {};
61
- const manifest = getDocsManifest();
63
+ const { entries, typeDocs } = getDocsIndex();
62
64
 
63
65
  const docSlugsByType = new Map();
64
66
  const kindSingular = normalizedKind.slice(0, -1);
65
- for (const doc of manifest?.docs ?? []) {
66
- if (doc.kind === kindSingular && doc.typeName) {
67
- docSlugsByType.set(doc.typeName, doc.slug);
67
+ for (const entry of entries) {
68
+ if (entry.source === 'core' && entry.kind === kindSingular && entry.typeName) {
69
+ docSlugsByType.set(entry.typeName, entry.slug);
70
+ }
71
+ }
72
+ for (const [key, slug] of typeDocs) {
73
+ const separator = key.indexOf(':');
74
+ if (key.slice(0, separator) === kindSingular) {
75
+ docSlugsByType.set(key.slice(separator + 1), slug);
68
76
  }
69
77
  }
70
78
 
@@ -0,0 +1,40 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+ import fs from 'node:fs';
17
+ import { createRequire } from 'node:module';
18
+
19
+ const require = createRequire(import.meta.url);
20
+
21
+ let hazards;
22
+
23
+ // Framework-level hazards ship hand-authored in @lowdefy/docs-content (its
24
+ // content/ directory is regenerated, hazards.json at the package root is
25
+ // not). Memoized like getDocsManifest: the list changes only with a release.
26
+ function loadFrameworkHazards() {
27
+ if (hazards !== undefined) {
28
+ return hazards;
29
+ }
30
+ try {
31
+ const hazardsPath = require.resolve('@lowdefy/docs-content/hazards.json');
32
+ hazards = JSON.parse(fs.readFileSync(hazardsPath, 'utf8'));
33
+ } catch {
34
+ // docs-content not installed — type-attached hazards are still served.
35
+ hazards = null;
36
+ }
37
+ return hazards;
38
+ }
39
+
40
+ export default loadFrameworkHazards;
@@ -0,0 +1,43 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+ import loadFrameworkHazards from './loadFrameworkHazards.js';
17
+
18
+ test('loadFrameworkHazards loads the shipped hazards.json with well-formed unique entries', () => {
19
+ const hazards = loadFrameworkHazards();
20
+ expect(Array.isArray(hazards)).toBe(true);
21
+ expect(hazards.length).toBeGreaterThan(0);
22
+ const ids = hazards.map((hazard) => hazard.id);
23
+ expect(new Set(ids).size).toEqual(ids.length);
24
+ for (const hazard of hazards) {
25
+ expect(hazard.id).toMatch(/^[a-z0-9]+(-[a-z0-9]+)*$/);
26
+ expect(typeof hazard.message).toBe('string');
27
+ expect(hazard.see === null || typeof hazard.see === 'string').toBe(true);
28
+ const { kinds, types, when } = hazard.appliesTo;
29
+ expect(kinds !== undefined || types !== undefined).toBe(true);
30
+ for (const kind of kinds ?? []) {
31
+ expect(['blocks', 'operators', 'actions', 'connections', 'requests', 'pages']).toContain(
32
+ kind
33
+ );
34
+ }
35
+ if (when !== undefined) {
36
+ expect(when).toEqual('connectionTenantNotNone');
37
+ }
38
+ }
39
+ });
40
+
41
+ test('loadFrameworkHazards returns the same memoized list on repeated calls', () => {
42
+ expect(loadFrameworkHazards()).toBe(loadFrameworkHazards());
43
+ });
@@ -14,27 +14,35 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import { type } from '@lowdefy/helpers';
17
+ import { type, urlQuery as urlQueryFn } from '@lowdefy/helpers';
18
18
 
19
- import lowdefyConfig from '../build/config.js';
20
- import { getBrowser, openPage } from './getBrowser.js';
19
+ import { buildPageUrl, getBrowser, openPage } from './getBrowser.js';
20
+ import noBrowserError from './noBrowserError.js';
21
21
  import { loadMocks } from './devMockRegistry.js';
22
22
  import { readCheckpoint } from './checkpointStore.js';
23
+ import resolvePageInstance from './resolvePageInstance.js';
24
+ import resolveToolCaller from './resolveToolCaller.js';
25
+ import unsettledPageNote from './unsettledPageNote.js';
26
+ import withBrowserSlot from './withBrowserSlot.js';
27
+ import withDataSession from './withDataSession.js';
23
28
 
24
29
  const VERIFY_KEY_COUNT = 3;
30
+ const READY_TIMEOUT = 15000;
25
31
 
26
32
  // Loads a state checkpoint's recorded requests into devMockRegistry (shared
27
33
  // by both modes below — src/routes/request.js consults it regardless of how
28
34
  // the page was opened), then either:
29
- // - 'headless': drives a headless page to the checkpoint's pageId/urlQuery,
30
- // injects its recorded state directly into the live context, and verifies
35
+ // - 'headless': drives a headless page to the checkpoint's page instance
36
+ // (pageId, pathParams) and urlQuery, injects its recorded state directly
37
+ // into that instance's context, and verifies
31
38
  // a few keys round-tripped. Good for an agent verifying its own change.
32
- // Takes a `user` so a checkpoint captured on a role-gated page restores
33
- // into a page that actually renders (see resolveHeadlessUser.js).
39
+ // Takes a `user` (and `data`, see resolveToolCaller) so a checkpoint
40
+ // captured on a role-gated page restores into a page that actually
41
+ // renders (see resolveHeadlessUser.js).
34
42
  // - 'registry-only': just returns a URL a human can open in a real browser
35
43
  // tab — client/Inspector.jsx's `?_checkpoint=` bootstrap does the state
36
44
  // injection client-side once that tab loads.
37
- async function loadState({ origin, name, mode = 'headless', user }) {
45
+ async function loadState({ origin, name, mode = 'headless', user, data }) {
38
46
  if (type.isNone(name) || !type.isString(name)) {
39
47
  return { error: `loadState requires a "name" string. Received ${JSON.stringify(name)}.` };
40
48
  }
@@ -42,10 +50,10 @@ async function loadState({ origin, name, mode = 'headless', user }) {
42
50
  // `user` only reaches a page this function opens itself. In 'registry-only'
43
51
  // mode the developer opens the returned URL in their own browser, carrying
44
52
  // their own session, so an injected caller would be silently dropped.
45
- if (!type.isNone(user) && mode === 'registry-only') {
53
+ if ((!type.isNone(user) || !type.isNone(data)) && mode === 'registry-only') {
54
+ const param = type.isNone(user) ? 'data' : 'user';
46
55
  return {
47
- error:
48
- 'loadState cannot apply "user" in "registry-only" mode — the developer opens the returned URL in their own browser, carrying their own session. Use the default "headless" mode.',
56
+ error: `loadState cannot apply "${param}" in "registry-only" mode — the developer opens the returned URL in their own browser, carrying their own session and database. Use the default "headless" mode.`,
49
57
  invalidInput: true,
50
58
  };
51
59
  }
@@ -62,16 +70,25 @@ async function loadState({ origin, name, mode = 'headless', user }) {
62
70
  return { error: `Checkpoint "${name}" has no recorded pageId.` };
63
71
  }
64
72
 
73
+ const { pathParams } = checkpoint;
74
+ const instance = resolvePageInstance({ pageId, pathParams });
75
+ if (!type.isUndefined(instance.error)) {
76
+ return { error: `Checkpoint "${name}" cannot open its page: ${instance.error}` };
77
+ }
78
+
65
79
  loadMocks({ pageId, mocks: checkpoint.requests });
66
80
 
67
- const basePath = lowdefyConfig.basePath ?? '';
68
- const urlQuery = checkpoint.urlQuery ?? '';
81
+ // The checkpoint keeps the query string the page was captured at.
82
+ const urlQuery = urlQueryFn.parse(checkpoint.urlQuery);
69
83
 
70
84
  if (mode === 'registry-only') {
71
- const separator = urlQuery.includes('?') ? '&' : '?';
72
- const url = `${origin}${basePath}/${pageId}${urlQuery}${separator}_checkpoint=${encodeURIComponent(
73
- name
74
- )}`;
85
+ const url = buildPageUrl({
86
+ origin,
87
+ pageId,
88
+ path: instance.path,
89
+ pathParams,
90
+ urlQuery: { ...urlQuery, _checkpoint: name },
91
+ });
75
92
  return {
76
93
  url,
77
94
  instructions:
@@ -91,55 +108,106 @@ async function loadState({ origin, name, mode = 'headless', user }) {
91
108
  return { error: `loadState requires an "origin" string. Received ${JSON.stringify(origin)}.` };
92
109
  }
93
110
 
111
+ const caller = await resolveToolCaller({ user, data });
112
+ if (!type.isUndefined(caller.error)) {
113
+ return caller;
114
+ }
115
+
116
+ return withBrowserSlot({
117
+ task: () =>
118
+ withDataSession({
119
+ dataSet: caller.dataSet,
120
+ task: ({ dataCookie }) =>
121
+ loadStateInBrowser({
122
+ origin,
123
+ name,
124
+ mode,
125
+ user: caller.user,
126
+ dataCookie,
127
+ pageId,
128
+ instance,
129
+ pathParams,
130
+ urlQuery,
131
+ checkpoint,
132
+ }),
133
+ }),
134
+ });
135
+ }
136
+
137
+ // The part of loadState that runs in the browser, inside a browser slot.
138
+ async function loadStateInBrowser({
139
+ origin,
140
+ name,
141
+ mode,
142
+ user,
143
+ dataCookie,
144
+ pageId,
145
+ instance,
146
+ pathParams,
147
+ urlQuery,
148
+ checkpoint,
149
+ }) {
94
150
  let browser;
95
151
  try {
96
152
  browser = await getBrowser();
97
153
  } catch (error) {
98
- return {
99
- error: `No Chromium available. Run: npx playwright install chromium (${error.message})`,
100
- };
154
+ return { error: noBrowserError(error) };
101
155
  }
102
156
 
103
157
  let context;
104
158
  try {
105
- // buildPageUrl (used internally by openPage) joins origin/basePath/pageId
106
- // as a plain string, so folding the query string into the pageId segment
107
- // reproduces the checkpoint's exact URL without needing a query-aware
108
- // variant of openPage.
109
- const opened = await openPage({ browser, origin, pageId: `${pageId}${urlQuery}`, user });
159
+ const opened = await openPage({
160
+ browser,
161
+ origin,
162
+ pageId,
163
+ path: instance.path,
164
+ pathParams,
165
+ urlQuery,
166
+ user,
167
+ dataCookie,
168
+ timeout: READY_TIMEOUT,
169
+ });
110
170
  context = opened.context;
111
171
  const { page } = opened;
112
172
 
113
- await page.waitForFunction((id) => Boolean(window.lowdefy?.contexts?.[`page:${id}`]), pageId, {
114
- timeout: 15000,
115
- });
173
+ await page.waitForFunction(
174
+ (contextKey) => Boolean(window.lowdefy?.contexts?.[contextKey]),
175
+ instance.instanceKey,
176
+ {
177
+ timeout: READY_TIMEOUT,
178
+ }
179
+ );
116
180
 
117
181
  const stateEntries = Object.entries(checkpoint.state ?? {});
118
182
  await page.evaluate(
119
- ({ id, entries }) => {
120
- const pageContext = window.lowdefy.contexts[`page:${id}`];
183
+ ({ contextKey, entries }) => {
184
+ const pageContext = window.lowdefy.contexts[contextKey];
121
185
  entries.forEach(([key, value]) => {
122
186
  pageContext._internal.State.set(key, value);
123
187
  });
124
188
  pageContext._internal.update();
125
189
  },
126
- { id: pageId, entries: stateEntries }
190
+ { contextKey: instance.instanceKey, entries: stateEntries }
127
191
  );
128
192
 
129
193
  const verifyKeys = stateEntries.slice(0, VERIFY_KEY_COUNT).map(([key]) => key);
130
194
  const verifiedKeys = await page.evaluate(
131
- ({ id, keys }) => {
132
- const pageContext = window.lowdefy.contexts[`page:${id}`];
195
+ ({ contextKey, keys }) => {
196
+ const pageContext = window.lowdefy.contexts[contextKey];
133
197
  const result = {};
134
198
  keys.forEach((key) => {
135
199
  result[key] = pageContext.state?.[key];
136
200
  });
137
201
  return result;
138
202
  },
139
- { id: pageId, keys: verifyKeys }
203
+ { contextKey: instance.instanceKey, keys: verifyKeys }
140
204
  );
141
205
 
142
- return { loaded: true, mode, url: opened.url, verifiedKeys };
206
+ const result = { loaded: true, mode, url: opened.url, verifiedKeys };
207
+ if (!opened.ready) {
208
+ return { ...result, ready: false, note: unsettledPageNote({ timeout: READY_TIMEOUT }) };
209
+ }
210
+ return result;
143
211
  } catch (error) {
144
212
  return { error: `Failed to load checkpoint "${name}": ${error.message}` };
145
213
  } finally {