@lowdefy/server-dev 6.1.0 → 7.1.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 (624) 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 +2 -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 +222 -36
  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 +78 -0
  99. package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
  100. package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +727 -0
  101. package/lib/docs/dataSets/describeDataSetResult.js +25 -0
  102. package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
  103. package/lib/docs/dataSets/describeTargetKey.js +29 -0
  104. package/lib/docs/dataSets/deserializeFixture.js +34 -0
  105. package/lib/docs/dataSets/drainSessionWork.js +37 -0
  106. package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
  107. package/lib/docs/dataSets/duplicateIndexName.js +24 -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 +52 -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 +133 -0
  119. package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +493 -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 +83 -0
  126. package/lib/docs/dataSets/readDataSet.test.mjs +251 -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/sessionDatabaseName.js +32 -0
  133. package/lib/docs/dataSets/sweepOrphanDatabases.js +36 -0
  134. package/lib/docs/dataSets/test/app/lowdefy.yaml +225 -0
  135. package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
  136. package/lib/docs/dataSets/test/app/tests/data/shop.yaml +12 -0
  137. package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
  138. package/lib/docs/devEventBus.js +136 -0
  139. package/lib/docs/devEventBus.test.mjs +269 -0
  140. package/lib/docs/devToolDefinitions.js +529 -0
  141. package/lib/docs/docSources.js +21 -0
  142. package/lib/docs/docs.test.mjs +171 -15
  143. package/lib/docs/docsIndex.test.mjs +377 -0
  144. package/lib/docs/evalOperator.js +5 -3
  145. package/lib/docs/evalOperatorHeadless.js +84 -12
  146. package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
  147. package/lib/docs/evalOperatorInTab.js +2 -1
  148. package/lib/docs/findConfig.js +95 -49
  149. package/lib/docs/fitResponse.js +81 -0
  150. package/lib/docs/fitResponse.test.mjs +128 -0
  151. package/lib/docs/formatFeedback.js +26 -5
  152. package/lib/docs/formatFeedback.test.mjs +48 -0
  153. package/lib/docs/getAppMap.js +7 -1
  154. package/lib/docs/getBrowser.js +150 -80
  155. package/lib/docs/getBrowser.test.mjs +407 -16
  156. package/lib/docs/getBuildId.js +61 -0
  157. package/lib/docs/getBuildId.test.mjs +84 -0
  158. package/lib/docs/getBuildStatus.js +81 -7
  159. package/lib/docs/getBuildStatus.test.mjs +229 -0
  160. package/lib/docs/getBuildStatusAfterEdits.js +35 -0
  161. package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
  162. package/lib/docs/getCoreDoc.js +9 -1
  163. package/lib/docs/getDoc.js +100 -0
  164. package/lib/docs/getDocsIndex.js +152 -0
  165. package/lib/docs/getHazards.js +115 -0
  166. package/lib/docs/getHazards.test.mjs +175 -0
  167. package/lib/docs/getMailOutboxDirectory.js +26 -0
  168. package/lib/docs/getOverview.js +79 -13
  169. package/lib/docs/getOverviewBasePath.test.mjs +37 -0
  170. package/lib/docs/getPageBuildStatus.js +46 -0
  171. package/lib/docs/getPluginDoc.js +21 -23
  172. package/lib/docs/getSchema.js +33 -1
  173. package/lib/docs/getStaleStatus.js +36 -0
  174. package/lib/docs/getStaleStatus.test.mjs +74 -0
  175. package/lib/docs/guardJourneyOrigin.js +39 -0
  176. package/lib/docs/inspectState.js +5 -3
  177. package/lib/docs/inspectStateFromTab.js +2 -2
  178. package/lib/docs/inspectStateHeadless.js +89 -27
  179. package/lib/docs/inspectStateHeadless.test.mjs +20 -1
  180. package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
  181. package/lib/docs/installHeadlessShell.js +123 -0
  182. package/lib/docs/installHeadlessShell.test.mjs +70 -0
  183. package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
  184. package/lib/docs/isHeadlessShellIncomplete.js +34 -0
  185. package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
  186. package/lib/docs/isPageReady.js +77 -0
  187. package/lib/docs/isPageReady.test.mjs +235 -0
  188. package/lib/docs/isRunErrorEntry.js +25 -0
  189. package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
  190. package/lib/docs/journeyRunRecording.js +35 -0
  191. package/lib/docs/journeyRunRecording.test.mjs +39 -0
  192. package/lib/docs/keepEventStreamAlive.js +68 -0
  193. package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
  194. package/lib/docs/launchBrowser.js +83 -0
  195. package/lib/docs/launchBrowser.test.mjs +175 -0
  196. package/lib/docs/listModuleDocEntries.js +69 -0
  197. package/lib/docs/listPackageDocFiles.js +67 -0
  198. package/lib/docs/listPluginDocEntries.js +111 -0
  199. package/lib/docs/listPlugins.js +9 -58
  200. package/lib/docs/listTypes.js +14 -6
  201. package/lib/docs/loadFrameworkHazards.js +40 -0
  202. package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
  203. package/lib/docs/loadState.js +103 -35
  204. package/lib/docs/makeModuleManifestDoc.js +174 -0
  205. package/lib/docs/matchExercisedPages.js +35 -0
  206. package/lib/docs/matchJourneyRoute.js +69 -0
  207. package/lib/docs/matchJourneyRoute.test.mjs +178 -0
  208. package/lib/docs/mergeNetworkSnapshots.js +49 -0
  209. package/lib/docs/nextJourneySequence.js +30 -0
  210. package/lib/docs/noBrowserError.js +22 -0
  211. package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
  212. package/lib/docs/observe/claimExpectedErrors.js +67 -0
  213. package/lib/docs/observe/createJourneyAppErrors.js +236 -0
  214. package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
  215. package/lib/docs/observe/evaluateInvariants.js +183 -0
  216. package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
  217. package/lib/docs/observe/findingKey.js +29 -0
  218. package/lib/docs/observe/hasNoEffect.js +31 -0
  219. package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
  220. package/lib/docs/observe/installStepObserver.js +72 -0
  221. package/lib/docs/observe/isAppApiUrl.js +33 -0
  222. package/lib/docs/observe/parseAppApiUrl.js +37 -0
  223. package/lib/docs/observe/readStepObserver.js +42 -0
  224. package/lib/docs/observe/relativeSource.js +29 -0
  225. package/lib/docs/observe/resolveConfigKeySource.js +33 -0
  226. package/lib/docs/observe/takeJourneyEvents.js +36 -0
  227. package/lib/docs/observe/toAppErrorFailure.js +61 -0
  228. package/lib/docs/observe/usesSearchStage.js +45 -0
  229. package/lib/docs/observe/waitForClientErrorReports.js +31 -0
  230. package/lib/docs/observe/watchJourneyContext.js +75 -0
  231. package/lib/docs/observe/withAppErrors.js +31 -0
  232. package/lib/docs/observe/withAppErrors.test.mjs +101 -0
  233. package/lib/docs/openJourney.js +94 -0
  234. package/lib/docs/openJourneyEmail.js +41 -0
  235. package/lib/docs/openJourneyEmail.test.mjs +187 -0
  236. package/lib/docs/pageBlocks.js +47 -0
  237. package/lib/docs/pageBuildStatus.test.mjs +103 -0
  238. package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
  239. package/lib/docs/rails/authEngineActions.js +53 -0
  240. package/lib/docs/rails/authEngineActions.test.mjs +69 -0
  241. package/lib/docs/rails/authEngineMethods.js +52 -0
  242. package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
  243. package/lib/docs/rails/checkAuthActionsRan.js +66 -0
  244. package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
  245. package/lib/docs/rails/checkClickRails.js +56 -0
  246. package/lib/docs/rails/createDataSetRails.js +82 -0
  247. package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
  248. package/lib/docs/rails/findExternalBlocks.js +147 -0
  249. package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
  250. package/lib/docs/rails/installAuthActionRecorder.js +56 -0
  251. package/lib/docs/rails/readAuthActionRecorder.js +32 -0
  252. package/lib/docs/rails/readBuiltArtifact.js +30 -0
  253. package/lib/docs/readConnectionPlugins.js +26 -0
  254. package/lib/docs/readDevAuthMode.js +32 -0
  255. package/lib/docs/readDocEntry.js +40 -0
  256. package/lib/docs/readDocTitle.js +26 -0
  257. package/lib/docs/readJourneyEmailMatch.js +57 -0
  258. package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
  259. package/lib/docs/readJourneySession.js +56 -0
  260. package/lib/docs/readMailOutbox.js +37 -0
  261. package/lib/docs/readPagePath.js +27 -0
  262. package/lib/docs/readPluginPackages.js +99 -0
  263. package/lib/docs/readProxyBuildWait.js +38 -0
  264. package/lib/docs/readRoutineSteps.js +45 -0
  265. package/lib/docs/registerShownTextEngine.js +118 -0
  266. package/lib/docs/requestRestart.js +30 -0
  267. package/lib/docs/requestRestart.test.mjs +49 -0
  268. package/lib/docs/resolvePageInstance.js +46 -0
  269. package/lib/docs/resolvePageInstance.test.mjs +81 -0
  270. package/lib/docs/resolveSource.js +11 -7
  271. package/lib/docs/resolveSource.test.mjs +1 -1
  272. package/lib/docs/resolveToolCaller.js +62 -0
  273. package/lib/docs/reviewPage.js +51 -0
  274. package/lib/docs/reviewPageBuilds.js +56 -0
  275. package/lib/docs/reviewPageBuilds.test.mjs +207 -0
  276. package/lib/docs/runCheck.js +75 -0
  277. package/lib/docs/runCheck.test.mjs +91 -0
  278. package/lib/docs/runCheckChild.js +49 -0
  279. package/lib/docs/runEndpoint.js +185 -0
  280. package/lib/docs/runEndpoint.test.mjs +509 -0
  281. package/lib/docs/runErrorBuffers.js +99 -0
  282. package/lib/docs/runErrorBuffers.test.mjs +95 -0
  283. package/lib/docs/runJourney.js +347 -0
  284. package/lib/docs/runJourney.test.mjs +2383 -0
  285. package/lib/docs/runJourneyAsUsers.js +44 -0
  286. package/lib/docs/runJourneyDataSet.test.mjs +274 -0
  287. package/lib/docs/runJourneySteps.js +1320 -0
  288. package/lib/docs/runRequest.js +100 -59
  289. package/lib/docs/runRequest.test.mjs +190 -0
  290. package/lib/docs/saveAnnotatedScreenshot.js +1 -1
  291. package/lib/docs/scaffoldPage.js +3 -1
  292. package/lib/docs/screenshotPage.js +163 -11
  293. package/lib/docs/screenshotPage.test.mjs +263 -1
  294. package/lib/docs/screenshotPageClip.test.mjs +4 -0
  295. package/lib/docs/searchDocs.js +82 -30
  296. package/lib/docs/searchDocs.test.mjs +100 -0
  297. package/lib/docs/searchIcons.js +90 -0
  298. package/lib/docs/searchIcons.test.mjs +127 -0
  299. package/lib/docs/selectExercised.js +39 -0
  300. package/lib/docs/selectFinalState.js +66 -0
  301. package/lib/docs/selectFinalState.test.mjs +51 -0
  302. package/lib/docs/serverErrorStore.js +54 -0
  303. package/lib/docs/serverErrorStore.test.mjs +50 -0
  304. package/lib/docs/setupTestFixtures.mjs +133 -39
  305. package/lib/docs/shownTextSelector.js +28 -0
  306. package/lib/docs/snapshotState.js +13 -5
  307. package/lib/docs/splitSearchTerms.js +62 -0
  308. package/lib/docs/splitSearchTerms.test.mjs +50 -0
  309. package/lib/docs/stateCheckpoints.test.mjs +77 -0
  310. package/lib/docs/tabAvailable.js +6 -5
  311. package/lib/docs/tabChannel.js +127 -21
  312. package/lib/docs/tabChannel.test.mjs +187 -29
  313. package/lib/docs/unsettledPageNote.js +24 -0
  314. package/lib/docs/validateJourneyMail.js +36 -0
  315. package/lib/docs/validateJourneyTimeout.js +38 -0
  316. package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
  317. package/lib/docs/validateRecordingParam.js +42 -0
  318. package/lib/docs/validateStateSelection.js +33 -0
  319. package/lib/docs/validateStateSelection.test.mjs +33 -0
  320. package/lib/docs/validateViewport.js +51 -0
  321. package/lib/docs/validateViewport.test.mjs +54 -0
  322. package/lib/docs/waitForBuild.js +50 -0
  323. package/lib/docs/waitForBuild.test.mjs +75 -0
  324. package/lib/docs/waitForJourneyEmail.js +77 -0
  325. package/lib/docs/withBrowserSlot.js +45 -0
  326. package/lib/docs/withBrowserSlot.test.mjs +75 -0
  327. package/lib/docs/withDataSession.js +53 -0
  328. package/lib/server/applyDataSetRedirect.js +92 -0
  329. package/lib/server/applyDataSetRedirect.test.mjs +446 -0
  330. package/lib/server/applyDataSetRedirectTenantReach.test.mjs +207 -0
  331. package/lib/server/applyDataSetRedirectWalled.test.mjs +134 -0
  332. package/lib/server/auth/createSystemContext.js +73 -0
  333. package/lib/server/auth/{getAuthConfig.js → getAuth.js} +15 -9
  334. package/lib/server/auth/getHeadlessUser.js +9 -8
  335. package/lib/server/auth/getHeadlessUser.test.mjs +41 -0
  336. package/lib/server/auth/getMockUser.js +18 -14
  337. package/lib/server/auth/{session.js → getStrategies.js} +17 -16
  338. package/lib/server/auth/headlessUser.js +3 -2
  339. package/lib/server/auth/journeyActor.js +35 -0
  340. package/lib/server/auth/resolveHeadlessUser.test.mjs +0 -4
  341. package/lib/server/checkPageRecord.js +44 -0
  342. package/lib/server/contextMapBudget.js +24 -0
  343. package/lib/server/contextMapBudget.test.mjs +98 -0
  344. package/lib/server/continueJitKeys.js +28 -0
  345. package/lib/server/continueJitKeys.test.mjs +43 -0
  346. package/lib/server/createDataSessionEndedError.js +33 -0
  347. package/lib/server/createDataSetRefusal.js +26 -0
  348. package/lib/server/createLowdefyContext.js +168 -41
  349. package/lib/server/createLowdefyContext.test.mjs +331 -8
  350. package/lib/server/getClientAddress.js +51 -0
  351. package/lib/server/getClientAddress.test.mjs +56 -0
  352. package/lib/server/getPageJitEnrichment.test.mjs +29 -11
  353. package/lib/server/hashConfigContent.js +26 -0
  354. package/lib/server/importAppCode.test.mjs +57 -0
  355. package/lib/server/isRoutineStep.js +27 -0
  356. package/lib/server/jitPageBuilder.js +309 -126
  357. package/lib/server/jitPageBuilder.skipPluginChecks.test.mjs +78 -0
  358. package/lib/server/jitPageBuilder.test.mjs +218 -0
  359. package/lib/server/journeyCookies/JOURNEY_COOKIES.js +37 -0
  360. package/lib/server/journeyCookies/forwardJourneyCookies.js +46 -0
  361. package/lib/server/journeyCookies/readJourneyCookie.js +52 -0
  362. package/lib/server/journeyCookies/writeJourneyCookie.js +46 -0
  363. package/lib/server/journeyCookies.js +22 -0
  364. package/lib/server/journeyCookies.test.mjs +145 -0
  365. package/lib/server/loadDynamicJsMap.js +47 -0
  366. package/lib/server/log/createHandleError.js +38 -2
  367. package/lib/server/log/createHandleError.test.mjs +125 -2
  368. package/lib/server/log/createLogger.js +3 -0
  369. package/lib/server/log/createLogger.test.mjs +15 -0
  370. package/lib/server/log/logRequest.js +2 -3
  371. package/lib/server/mutantDataSetWrappers.test.mjs +115 -0
  372. package/lib/server/mutants/applyMutant.js +39 -0
  373. package/lib/server/mutants/artifactPatterns.js +25 -0
  374. package/lib/server/mutants/buildAnchor.js +45 -0
  375. package/lib/server/mutants/collectSharedIds.js +41 -0
  376. package/lib/server/mutants/configPathSegments.js +38 -0
  377. package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
  378. package/lib/server/mutants/describeEvent.js +22 -0
  379. package/lib/server/mutants/enumerateArtifact.js +56 -0
  380. package/lib/server/mutants/findNodeByKey.js +45 -0
  381. package/lib/server/mutants/findNodeByPointer.js +49 -0
  382. package/lib/server/mutants/getArtifactKind.js +32 -0
  383. package/lib/server/mutants/groupMutantCopies.js +56 -0
  384. package/lib/server/mutants/hashArtifact.js +25 -0
  385. package/lib/server/mutants/hashMutantTarget.js +34 -0
  386. package/lib/server/mutants/listMutants.js +122 -0
  387. package/lib/server/mutants/listMutants.test.mjs +502 -0
  388. package/lib/server/mutants/mutantId.js +30 -0
  389. package/lib/server/mutants/mutantRuns.js +57 -0
  390. package/lib/server/mutants/mutantRuns.test.mjs +136 -0
  391. package/lib/server/mutants/operators/dropAction.js +47 -0
  392. package/lib/server/mutants/operators/dropBlock.js +44 -0
  393. package/lib/server/mutants/operators/dropPayload.js +52 -0
  394. package/lib/server/mutants/operators/dropStep.js +44 -0
  395. package/lib/server/mutants/operators/flipVisible.js +53 -0
  396. package/lib/server/mutants/operators/index.js +37 -0
  397. package/lib/server/mutants/operators/retargetLink.js +72 -0
  398. package/lib/server/mutants/operators/skipValidate.js +77 -0
  399. package/lib/server/mutants/operators/swapIf.js +112 -0
  400. package/lib/server/mutants/operators.test.mjs +396 -0
  401. package/lib/server/mutants/positionFreePath.js +38 -0
  402. package/lib/server/mutants/removeFromParent.js +28 -0
  403. package/lib/server/mutants/test/fixtures.mjs +173 -0
  404. package/lib/server/mutants/test/keyArtifact.mjs +63 -0
  405. package/lib/server/mutants/validateMutantParam.js +69 -0
  406. package/lib/server/mutants/validateMutantsBody.js +88 -0
  407. package/lib/server/mutants/walkArtifact.js +179 -0
  408. package/lib/server/pageBuildRecords.js +131 -0
  409. package/lib/server/pageBuildRecords.test.mjs +258 -0
  410. package/lib/server/pageCache.mjs +22 -7
  411. package/lib/server/pageCache.test.mjs +22 -7
  412. package/lib/server/pageRebuilds.test.mjs +543 -0
  413. package/lib/server/pruneJitMaps.js +36 -0
  414. package/lib/server/pruneJitMaps.test.mjs +55 -0
  415. package/lib/server/readInstanceRecord.js +33 -0
  416. package/lib/server/readInstanceRecord.test.mjs +99 -0
  417. package/lib/server/readMergedMaps.js +86 -0
  418. package/lib/server/readMergedMaps.test.mjs +79 -0
  419. package/lib/server/recording/appendRecords.js +33 -0
  420. package/lib/server/recording/errorRecordingStamp.js +34 -0
  421. package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
  422. package/lib/server/recording/isRecordingEnabled.js +31 -0
  423. package/lib/server/recording/readRecordingCookie.js +71 -0
  424. package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
  425. package/lib/server/recording/recordingCookiePayload.js +33 -0
  426. package/lib/server/recording/servedBuilds.js +46 -0
  427. package/lib/server/recording/servedBuilds.test.mjs +42 -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 +25 -3
  439. package/manager/processes/initialBuild.mjs +17 -1
  440. package/manager/processes/initialBuild.test.mjs +104 -0
  441. package/manager/processes/lowdefyBuild.mjs +31 -7
  442. package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +72 -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 +60 -47
  527. package/package.original.json +60 -47
  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 +173 -3
  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 +3 -1
  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/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
  621. package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
  622. package/manager/watchers/moduleBuildWatcher.mjs +0 -79
  623. package/manager/watchers/serverArtifactWatcher.mjs +0 -120
  624. package/src/routes/apiPage.js +0 -37
@@ -0,0 +1,37 @@
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 'fs';
18
+ import path from 'path';
19
+
20
+ import setupTestFixtures from './setupTestFixtures.mjs';
21
+
22
+ // lib/build/config.js reads build/config.json from process.cwd() at import
23
+ // time, so the fixture carries the basePath before getOverview is imported.
24
+ const fixtureDir = setupTestFixtures();
25
+ fs.writeFileSync(path.join(fixtureDir, 'build/config.json'), JSON.stringify({ basePath: '/app' }));
26
+ fs.writeFileSync(path.join(fixtureDir, 'build', 'routes.json'), JSON.stringify([]));
27
+ process.chdir(fixtureDir);
28
+
29
+ const { default: getOverview } = await import('./getOverview.js');
30
+
31
+ test('getOverview tells agents the docs routes are served under the app basePath', () => {
32
+ const overview = getOverview();
33
+
34
+ expect(overview).toContain(
35
+ 'This app sets `config.basePath: /app`, so every route below is served under it: `GET /app/lowdefy-docs`, `/app/lowdefy-docs/mcp`, `/app/api/...`.'
36
+ );
37
+ });
@@ -0,0 +1,46 @@
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 reviewPageBuilds from './reviewPageBuilds.js';
18
+
19
+ // The page side of build status. The dev server builds a page when it is first
20
+ // requested, so the config build's status says nothing about page content:
21
+ // this reports the pages whose latest build failed, the pages changed since
22
+ // they were built, and how many pages nothing has built yet. checked is the
23
+ // list of pages build status just built (wait: true).
24
+ async function getPageBuildStatus({ checked } = {}) {
25
+ const { edited, unbuilt, failed } = await reviewPageBuilds();
26
+ const status = {};
27
+ if (checked) {
28
+ status.checked = checked;
29
+ }
30
+ if (failed.length > 0) {
31
+ status.failed = failed;
32
+ }
33
+ if (edited.length > 0) {
34
+ status.changedSinceBuild = edited;
35
+ status.changedSinceBuildNote = checked
36
+ ? 'These pages changed on disk after their last build, but the dev server has not rebuilt them: it has not seen the change yet. Call again; if they stay here, the file watcher missed the edit.'
37
+ : 'These pages, or the config build they build against, changed after their last build and have not been rebuilt, so their errors are not known yet. Call with wait: true to build them.';
38
+ }
39
+ status.unbuilt = unbuilt.length;
40
+ if (unbuilt.length > 0) {
41
+ status.unbuiltNote = `${unbuilt.length} page(s) have not been built since the dev server started, so this status does not cover them. lowdefy_check validates every page.`;
42
+ }
43
+ return status;
44
+ }
45
+
46
+ export default getPageBuildStatus;
@@ -15,40 +15,38 @@
15
15
  */
16
16
 
17
17
  import fs from 'node:fs';
18
- import path from 'node:path';
19
18
  import { type } from '@lowdefy/helpers';
20
19
 
20
+ import listPackageDocFiles from './listPackageDocFiles.js';
21
+ import readBuildArtifact from './readBuildArtifact.js';
22
+ import readPluginPackages from './readPluginPackages.js';
21
23
  import resolvePluginDir from './resolvePluginDir.js';
22
24
 
23
- // Local plugins have no doc convention — probe the common locations and
24
- // return whatever markdown ships with the package.
25
+ // The package name comes from the caller, and a name such as "../../x" would
26
+ // resolve outside node_modules, so only the app's own plugins are read.
27
+ function isAppPlugin({ packageName }) {
28
+ const installed = readBuildArtifact({ name: 'installedPluginPackages.json' }) ?? [];
29
+ if (installed.includes(packageName)) {
30
+ return true;
31
+ }
32
+ return readPluginPackages().some((plugin) => plugin.package === packageName);
33
+ }
34
+
35
+ // Every markdown file a plugin package ships, joined into one document.
25
36
  function getPluginDoc({ packageName }) {
37
+ if (!isAppPlugin({ packageName })) {
38
+ return null;
39
+ }
26
40
  const pluginDir = resolvePluginDir({ packageName });
27
41
  if (type.isNone(pluginDir)) {
28
42
  return null;
29
43
  }
30
- const sections = [];
31
- for (const readme of ['README.md', 'readme.md']) {
32
- const readmePath = path.join(pluginDir, readme);
33
- if (fs.existsSync(readmePath)) {
34
- sections.push(fs.readFileSync(readmePath, 'utf8'));
35
- break;
36
- }
37
- }
38
- for (const docsDir of ['docs', 'dist/docs']) {
39
- const dirPath = path.join(pluginDir, docsDir);
40
- if (!fs.existsSync(dirPath)) {
41
- continue;
42
- }
43
- for (const fileName of fs.readdirSync(dirPath).sort()) {
44
- if (fileName.endsWith('.md')) {
45
- sections.push(fs.readFileSync(path.join(dirPath, fileName), 'utf8'));
46
- }
47
- }
48
- }
49
- if (sections.length === 0) {
44
+ const { readme, docs } = listPackageDocFiles({ dir: pluginDir });
45
+ const files = type.isNone(readme) ? docs : [readme, ...docs];
46
+ if (files.length === 0) {
50
47
  return null;
51
48
  }
49
+ const sections = files.map((filePath) => fs.readFileSync(filePath, 'utf8'));
52
50
  return { package: packageName, markdown: sections.join('\n\n---\n\n') };
53
51
  }
54
52
 
@@ -15,7 +15,9 @@
15
15
  */
16
16
 
17
17
  import { type } from '@lowdefy/helpers';
18
+ import { JOURNEY_STEP_SCHEMAS, journeyStepSchema } from '@lowdefy/node-utils';
18
19
 
20
+ import getHazards from './getHazards.js';
19
21
  import normalizeTypeKind from './normalizeTypeKind.js';
20
22
  import readBuildArtifact from './readBuildArtifact.js';
21
23
 
@@ -27,11 +29,40 @@ const SCHEMA_ARTIFACTS = {
27
29
  requests: 'plugins/requestSchemas.json',
28
30
  };
29
31
 
32
+ const JOURNEY_STEP_KINDS = ['journey-step', 'journey-steps'];
33
+
34
+ // kind journey-step returns the journey step grammar: every step, or the one
35
+ // step `type` names.
36
+ function getJourneyStepSchema({ typeName }) {
37
+ if (type.isNone(typeName) || typeName === '' || typeName === 'all') {
38
+ return { kind: 'journey-step', schema: journeyStepSchema };
39
+ }
40
+ const schema = JOURNEY_STEP_SCHEMAS[typeName];
41
+ if (type.isNone(schema)) {
42
+ return null;
43
+ }
44
+ return { kind: 'journey-step', type: typeName, schema };
45
+ }
46
+
30
47
  function getSchema({ kind, type: typeName }) {
48
+ if (JOURNEY_STEP_KINDS.includes(String(kind ?? '').toLowerCase())) {
49
+ return getJourneyStepSchema({ typeName });
50
+ }
31
51
  const normalizedKind = normalizeTypeKind({ kind });
32
52
  if (type.isNone(SCHEMA_ARTIFACTS[normalizedKind])) {
33
53
  throw new Error(
34
- `No schemas available for type kind. Received ${JSON.stringify(kind)}. Use one of: blocks, operators, actions, connections, requests.`
54
+ `No schemas available for type kind. Received ${JSON.stringify(
55
+ kind
56
+ )}. Use one of: blocks, operators, actions, connections, requests, or journey-step for the journey step grammar.`
57
+ );
58
+ }
59
+ // type is optional only for journey-step, so a type kind without one is
60
+ // refused rather than looked up as "undefined".
61
+ if (type.isNone(typeName) || typeName === '') {
62
+ throw new Error(
63
+ `Getting a ${normalizedKind} schema requires a "type", the exact type name. Received ${JSON.stringify(
64
+ typeName
65
+ )}.`
35
66
  );
36
67
  }
37
68
  const schemas = readBuildArtifact({ name: SCHEMA_ARTIFACTS[normalizedKind] }) ?? {};
@@ -59,6 +90,7 @@ function getSchema({ kind, type: typeName }) {
59
90
  result.meta = blockMetas[typeName];
60
91
  }
61
92
  }
93
+ result.hazards = getHazards({ kind: normalizedKind, type: typeName });
62
94
  return result;
63
95
  }
64
96
 
@@ -0,0 +1,36 @@
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 readBuildArtifact from './readBuildArtifact.js';
18
+
19
+ // When a dev rebuild fails the manager writes status "error" to
20
+ // build/buildStatus.json but leaves the previous build's artifacts in place, so
21
+ // every docs read keeps answering from a build that predates the caller's
22
+ // edits. Returns null — not { stale: false } — when the build is ok or unknown:
23
+ // a response with no stale fields means not stale, and a false flag on every
24
+ // response costs tokens on every call.
25
+ function getStaleStatus() {
26
+ const build = readBuildArtifact({ name: 'buildStatus.json' });
27
+ if (build?.status !== 'error') return null;
28
+ return {
29
+ stale: true,
30
+ staleSince: build.timestamp ?? null,
31
+ staleReason:
32
+ 'The last build failed. The dev server is still serving the previous successful build, so this result may not reflect your latest edits. Call lowdefy_build_status (or GET /lowdefy-docs/build-status) for the errors.',
33
+ };
34
+ }
35
+
36
+ export default getStaleStatus;
@@ -0,0 +1,74 @@
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 'fs';
18
+ import os from 'os';
19
+ import path from 'path';
20
+
21
+ // getStaleStatus reads build/buildStatus.json from process.cwd() — point it at
22
+ // a throwaway server directory before the module is imported.
23
+ const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-stale-test-'));
24
+ fs.mkdirSync(path.join(fixtureDir, 'build'));
25
+ process.chdir(fixtureDir);
26
+
27
+ const statusPath = path.join(fixtureDir, 'build', 'buildStatus.json');
28
+
29
+ function writeStatus(status) {
30
+ fs.writeFileSync(statusPath, JSON.stringify(status));
31
+ }
32
+
33
+ function removeStatus() {
34
+ if (fs.existsSync(statusPath)) fs.rmSync(statusPath);
35
+ }
36
+
37
+ const { default: getStaleStatus } = await import('./getStaleStatus.js');
38
+
39
+ test('getStaleStatus returns null when buildStatus.json is missing', () => {
40
+ removeStatus();
41
+ expect(getStaleStatus()).toBe(null);
42
+ });
43
+
44
+ test('getStaleStatus returns null when the build status is ok', () => {
45
+ writeStatus({ status: 'ok', timestamp: '2026-01-01T00:00:00.000Z', errors: [], warnings: [] });
46
+ expect(getStaleStatus()).toBe(null);
47
+ });
48
+
49
+ test('getStaleStatus returns null when the build status is unknown', () => {
50
+ writeStatus({ status: 'unknown' });
51
+ expect(getStaleStatus()).toBe(null);
52
+ });
53
+
54
+ test('getStaleStatus returns the stale fields with the artifact timestamp when the build failed', () => {
55
+ writeStatus({
56
+ status: 'error',
57
+ timestamp: '2026-02-03T04:05:06.000Z',
58
+ errors: [{ message: 'Block type "Buton" not found.' }],
59
+ });
60
+ const status = getStaleStatus();
61
+ expect(status.stale).toBe(true);
62
+ expect(status.staleSince).toEqual('2026-02-03T04:05:06.000Z');
63
+ expect(status.staleReason).toContain('The last build failed.');
64
+ expect(status.staleReason).toContain('lowdefy_build_status');
65
+ });
66
+
67
+ test('getStaleStatus returns a null staleSince when the failed build has no timestamp', () => {
68
+ writeStatus({ status: 'error', errors: [] });
69
+ expect(getStaleStatus()).toEqual({
70
+ stale: true,
71
+ staleSince: null,
72
+ staleReason: expect.any(String),
73
+ });
74
+ });
@@ -0,0 +1,39 @@
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
+ const DEFAULT_PORTS = { 'http:': '80', 'https:': '443' };
18
+
19
+ function portOf(url) {
20
+ return url.port === '' ? DEFAULT_PORTS[url.protocol] : url.port;
21
+ }
22
+
23
+ // The data cookie is set for the journey's origin only, so a request to the dev server's port on
24
+ // another host (127.0.0.1 against localhost, [::1], a LAN address) would carry none and read the
25
+ // real database as a signed-out caller. Such requests are aborted before they leave the browser and
26
+ // handed to onLeave; requests to other ports and other sites are untouched.
27
+ async function guardJourneyOrigin({ context, origin, onLeave }) {
28
+ const originUrl = new URL(origin);
29
+ const originPort = portOf(originUrl);
30
+ await context.route(
31
+ (url) => portOf(url) === originPort && url.hostname !== originUrl.hostname,
32
+ async (route) => {
33
+ onLeave(route.request().url());
34
+ await route.abort('blockedbyclient');
35
+ }
36
+ );
37
+ }
38
+
39
+ export default guardJourneyOrigin;
@@ -22,25 +22,27 @@ import resolveSource from './resolveSource.js';
22
22
  // headless-only. Falls back to headless whenever the tab path isn't usable
23
23
  // (no tab connected, or `source: 'tab'` was requested but it errored) so
24
24
  // agents always get an answer.
25
- async function inspectState({ origin, pageId, source, user }) {
25
+ async function inspectState({ origin, pageId, pathParams, source, user, data }) {
26
26
  const { tryTab, error, invalidInput } = resolveSource({
27
27
  name: 'inspectState',
28
28
  pageId,
29
+ pathParams,
29
30
  source,
30
31
  user,
32
+ data,
31
33
  });
32
34
  if (error) {
33
35
  return { error, invalidInput };
34
36
  }
35
37
 
36
38
  if (tryTab) {
37
- const result = await inspectStateFromTab({ pageId });
39
+ const result = await inspectStateFromTab({ pageId, pathParams });
38
40
  if (!result?.error) {
39
41
  return { ...result, source: 'tab' };
40
42
  }
41
43
  }
42
44
 
43
- const result = await inspectStateHeadless({ origin, pageId, user });
45
+ const result = await inspectStateHeadless({ origin, pageId, pathParams, user, data });
44
46
  return { ...result, source: 'headless' };
45
47
  }
46
48
 
@@ -26,8 +26,8 @@ import { requestFromTab } from './tabChannel.js';
26
26
  // (see tabChannel.js) — a serialized snapshot string on success, or an
27
27
  // `{ error }` object if no tab is connected, the request timed out, or
28
28
  // Inspector.jsx itself failed to build the snapshot.
29
- async function inspectStateFromTab({ pageId } = {}) {
30
- const response = await requestFromTab({ pageId, event: 'inspect-request' });
29
+ async function inspectStateFromTab({ pageId, pathParams } = {}) {
30
+ const response = await requestFromTab({ pageId, pathParams, event: 'inspect-request' });
31
31
  if (response?.error) {
32
32
  return { error: response.error };
33
33
  }
@@ -17,18 +17,27 @@
17
17
  import { type } from '@lowdefy/helpers';
18
18
 
19
19
  import { getBrowser, openPage, buildPageUrl } from './getBrowser.js';
20
+ import noBrowserError from './noBrowserError.js';
21
+ import resolvePageInstance from './resolvePageInstance.js';
22
+ import resolveToolCaller from './resolveToolCaller.js';
23
+ import unsettledPageNote from './unsettledPageNote.js';
24
+ import withBrowserSlot from './withBrowserSlot.js';
25
+ import withDataSession from './withDataSession.js';
20
26
 
21
27
  // Collects a state snapshot from a headless Chromium tab navigated to the
22
- // page's own route. Mirrors Inspector.jsx's buildSnapshot (the live-tab
28
+ // page instance's own route (`pathParams` fill a patterned page's placeholders),
29
+ // read under the instance key. Mirrors Inspector.jsx's buildSnapshot (the live-tab
23
30
  // equivalent), but window.lowdefy does not expose the app's serializer, so
24
31
  // the snapshot is round-tripped through JSON.parse(JSON.stringify(...))
25
32
  // inside the page instead. That strips functions/undefined and turns Dates
26
33
  // into plain ISO strings — good enough for agent inspection, just not a
27
34
  // byte-for-byte match of the app's own `~d`-tagged serialization.
28
- async function inspectStateHeadless({ origin, pageId, user, timeout = 15000 }) {
35
+ async function inspectStateHeadless({ origin, pageId, pathParams, user, data, timeout = 15000 }) {
29
36
  if (type.isNone(origin) || !type.isString(origin)) {
30
37
  return {
31
- error: `inspectStateHeadless requires an "origin" string. Received ${JSON.stringify(origin)}.`,
38
+ error: `inspectStateHeadless requires an "origin" string. Received ${JSON.stringify(
39
+ origin
40
+ )}.`,
32
41
  };
33
42
  }
34
43
  if (type.isNone(pageId) || !type.isString(pageId)) {
@@ -37,40 +46,93 @@ async function inspectStateHeadless({ origin, pageId, user, timeout = 15000 }) {
37
46
  };
38
47
  }
39
48
 
49
+ const instance = resolvePageInstance({ pageId, pathParams });
50
+ if (!type.isUndefined(instance.error)) {
51
+ return { error: instance.error, invalidInput: true };
52
+ }
53
+
54
+ const caller = await resolveToolCaller({ user, data });
55
+ if (!type.isUndefined(caller.error)) {
56
+ return caller;
57
+ }
58
+
59
+ return withBrowserSlot({
60
+ task: () =>
61
+ withDataSession({
62
+ dataSet: caller.dataSet,
63
+ task: ({ dataCookie }) =>
64
+ inspectStateInBrowser({
65
+ origin,
66
+ pageId,
67
+ pathParams,
68
+ instance,
69
+ user: caller.user,
70
+ dataCookie,
71
+ timeout,
72
+ }),
73
+ }),
74
+ });
75
+ }
76
+
77
+ // The part of inspectStateHeadless that runs in the browser, inside a browser slot.
78
+ async function inspectStateInBrowser({
79
+ origin,
80
+ pageId,
81
+ pathParams,
82
+ instance,
83
+ user,
84
+ dataCookie,
85
+ timeout,
86
+ }) {
40
87
  let browser;
41
88
  try {
42
89
  browser = await getBrowser();
43
90
  } catch (error) {
44
- return {
45
- error: `No Chromium available. Run: npx playwright install chromium (${error.message})`,
46
- };
91
+ return { error: noBrowserError(error) };
47
92
  }
48
93
 
49
- const url = buildPageUrl({ origin, pageId });
94
+ const url = buildPageUrl({ origin, pageId, path: instance.path, pathParams });
50
95
 
51
96
  let context;
52
97
  try {
53
- const opened = await openPage({ browser, origin, pageId, user, timeout });
98
+ const opened = await openPage({
99
+ browser,
100
+ origin,
101
+ pageId,
102
+ path: instance.path,
103
+ pathParams,
104
+ user,
105
+ dataCookie,
106
+ timeout,
107
+ });
54
108
  context = opened.context;
55
- const snapshot = await opened.page.evaluate((id) => {
56
- const lowdefy = window.lowdefy;
57
- const pageContext = lowdefy?.contexts?.[`page:${id}`];
58
- if (!pageContext) {
59
- return { error: `No live context for page "${id}".` };
60
- }
61
- return JSON.parse(
62
- JSON.stringify({
63
- pageId: id,
64
- state: pageContext.state,
65
- requests: pageContext.requests,
66
- eventLog: (pageContext.eventLog ?? []).slice(-50),
67
- global: lowdefy.lowdefyGlobal,
68
- user: lowdefy.user,
69
- input: lowdefy.inputs?.[`page:${id}`],
70
- urlQuery: window.location.search,
71
- })
72
- );
73
- }, pageId);
109
+ const snapshot = await opened.page.evaluate(
110
+ ({ id, instanceKey, path }) => {
111
+ const lowdefy = window.lowdefy;
112
+ const pageContext = lowdefy?.contexts?.[instanceKey];
113
+ if (!pageContext) {
114
+ return { error: `No live context for page "${id}" at "/${path}".` };
115
+ }
116
+ return JSON.parse(
117
+ JSON.stringify({
118
+ pageId: id,
119
+ pathParams: pageContext.pathParams,
120
+ instanceKey,
121
+ state: pageContext.state,
122
+ requests: pageContext.requests,
123
+ eventLog: (pageContext.eventLog ?? []).slice(-50),
124
+ global: lowdefy.lowdefyGlobal,
125
+ user: lowdefy.user,
126
+ input: lowdefy.inputs?.[instanceKey],
127
+ urlQuery: window.location.search,
128
+ })
129
+ );
130
+ },
131
+ { id: pageId, instanceKey: instance.instanceKey, path: instance.path }
132
+ );
133
+ if (!opened.ready) {
134
+ return { ...snapshot, ready: false, note: unsettledPageNote({ timeout }) };
135
+ }
74
136
  return snapshot;
75
137
  } catch (error) {
76
138
  return { error: `Failed to inspect state at "${url}": ${error.message}` };
@@ -25,7 +25,10 @@ import { jest } from '@jest/globals';
25
25
  // screenshotPage.test.mjs, which exercises the same getBrowser.js singleton.
26
26
  jest.unstable_mockModule('playwright-core', () => ({
27
27
  chromium: { launch: jest.fn().mockRejectedValue(new Error("Executable doesn't exist")) },
28
+ selectors: { register: jest.fn(async () => {}) },
28
29
  }));
30
+ // A missing shell would otherwise start a real download.
31
+ jest.unstable_mockModule('./installHeadlessShell.js', () => ({ default: () => null }));
29
32
 
30
33
  // lib/build/config.js reads build/config.json from process.cwd() at import
31
34
  // time — chdir into a fixture that has one before getBrowser.js (which
@@ -35,6 +38,10 @@ const originalCwd = process.cwd();
35
38
  const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-inspect-state-test-'));
36
39
  fs.mkdirSync(path.join(fixtureDir, 'build'), { recursive: true });
37
40
  fs.writeFileSync(path.join(fixtureDir, 'build', 'config.json'), JSON.stringify({ basePath: '' }));
41
+ fs.writeFileSync(
42
+ path.join(fixtureDir, 'build', 'routes.json'),
43
+ JSON.stringify([{ pageId: 'ticket', path: 'tickets/{space}/{ticket_id}' }])
44
+ );
38
45
  process.chdir(fixtureDir);
39
46
 
40
47
  const { default: inspectStateHeadless } = await import('./inspectStateHeadless.js');
@@ -56,5 +63,17 @@ test('inspectStateHeadless returns an error when pageId is missing', async () =>
56
63
 
57
64
  test('inspectStateHeadless returns an actionable error when no browser is available', async () => {
58
65
  const result = await inspectStateHeadless({ origin: 'http://localhost:3001', pageId: 'home' });
59
- expect(result.error).toMatch(/No Chromium available. Run: npx playwright install chromium/);
66
+ expect(result.error).toMatch(
67
+ /No Chromium available. Run: npx playwright install chromium-headless-shell/
68
+ );
69
+ });
70
+
71
+ test('inspectStateHeadless returns the error naming a missing placeholder before opening a browser', async () => {
72
+ const result = await inspectStateHeadless({
73
+ origin: 'http://localhost:3001',
74
+ pageId: 'ticket',
75
+ pathParams: { space: 's' },
76
+ });
77
+ expect(result.invalidInput).toBe(true);
78
+ expect(result.error).toMatch(/missing a value for path placeholder "ticket_id"/);
60
79
  });
@@ -0,0 +1,45 @@
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 { EventEmitter } from 'node:events';
18
+
19
+ import { jest } from '@jest/globals';
20
+
21
+ const mockSpawn = jest.fn();
22
+ jest.unstable_mockModule('child_process', () => ({ spawn: mockSpawn, spawnSync: jest.fn() }));
23
+
24
+ // A file of its own: installHeadlessShell keeps its one install per process
25
+ // in module state.
26
+ const { default: installHeadlessShell } = await import('./installHeadlessShell.js');
27
+
28
+ test('installHeadlessShell logs a failed install once and does not retry it in this process', async () => {
29
+ const warn = jest.spyOn(console, 'warn').mockImplementation(() => {});
30
+ jest.spyOn(console, 'info').mockImplementation(() => {});
31
+ const installer = Object.assign(new EventEmitter(), { stderr: new EventEmitter() });
32
+ mockSpawn.mockReturnValue(installer);
33
+
34
+ const install = installHeadlessShell();
35
+ installer.stderr.emit('data', Buffer.from('Download failed: server returned code 503'));
36
+ installer.emit('exit', 1);
37
+ // An error event after exit (or before it) must not log twice.
38
+ installer.emit('error', new Error('spawn EPIPE'));
39
+
40
+ expect(await install).toMatchObject({ installed: false });
41
+ expect(installHeadlessShell()).toBe(install);
42
+ expect(mockSpawn).toHaveBeenCalledTimes(1);
43
+ expect(warn).toHaveBeenCalledTimes(1);
44
+ expect(warn.mock.calls[0][0]).toContain('Download failed: server returned code 503');
45
+ });