@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,70 @@
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
+ // Run by importAppCode.test.mjs in its own process: Jest can load neither Vite
18
+ // nor a module URL with a query. Loads the build's importAppCode the way the dev
19
+ // server's JIT page builds do, then imports an app code file, imports it again
20
+ // unchanged, edits it and imports it again. With `vite`, through Vite's SSR
21
+ // module runner (@lowdefy/build linked, as in the monorepo); with `node`,
22
+ // natively (@lowdefy/build installed from npm, which Vite externalises).
23
+ import fs from 'node:fs';
24
+ import path from 'node:path';
25
+ import { createRequire } from 'node:module';
26
+ import { fileURLToPath, pathToFileURL } from 'node:url';
27
+
28
+ import { createServer } from 'vite';
29
+
30
+ const [loader, configDir] = process.argv.slice(2);
31
+ const require = createRequire(import.meta.url);
32
+ const serverDevDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..');
33
+
34
+ const buildDevEntry = require.resolve('@lowdefy/build/dev');
35
+ const modulePath = path.join(path.dirname(buildDevEntry), 'utils/createImportAppCode.js');
36
+
37
+ let server = null;
38
+ if (loader === 'vite') {
39
+ server = await createServer({
40
+ configFile: false,
41
+ root: serverDevDir,
42
+ logLevel: 'error',
43
+ appType: 'custom',
44
+ server: { middlewareMode: true, hmr: false, watch: null },
45
+ });
46
+ }
47
+
48
+ try {
49
+ const { default: createImportAppCode } = server
50
+ ? await server.ssrLoadModule(modulePath)
51
+ : await import(pathToFileURL(modulePath).href);
52
+ const importAppCode = createImportAppCode({ directories: { config: configDir } });
53
+ const filePath = path.join(configDir, 'transformer.js');
54
+
55
+ fs.writeFileSync(filePath, "export default () => 'before';\n");
56
+ const first = await importAppCode('transformer.js');
57
+ const unchanged = await importAppCode('transformer.js');
58
+ fs.writeFileSync(filePath, "export default () => 'after';\n");
59
+ const edited = await importAppCode('transformer.js');
60
+
61
+ process.stdout.write(
62
+ JSON.stringify({
63
+ first: first(),
64
+ edited: edited(),
65
+ unchangedReused: unchanged === first,
66
+ })
67
+ );
68
+ } finally {
69
+ await server?.close();
70
+ }
@@ -0,0 +1,30 @@
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
+ // The context.waitUntil of a data-session request: background work (scheduleBackground, the detached
18
+ // dispatch and the detached run) is added to the session's work set, so closing the session waits
19
+ // for it before it drops the database.
20
+ function trackSessionWork({ session }) {
21
+ return function waitUntil(promise) {
22
+ session.work.add(promise);
23
+ const remove = () => {
24
+ session.work.delete(promise);
25
+ };
26
+ promise.then(remove, remove);
27
+ };
28
+ }
29
+
30
+ export default trackSessionWork;
@@ -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
+
17
+ function escapeRegExp(value) {
18
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
19
+ }
20
+
21
+ // The paths Vite serves itself; every other request routes to the Hono app.
22
+ // @hono/vite-dev-server tests these against the raw request url, before Vite
23
+ // strips its `base`, and Vite serves every module under `base` - so with
24
+ // config.basePath the patterns must carry the prefix. Without it
25
+ // `<basePath>/@vite/client` and `<basePath>/client/main.jsx` fall through to
26
+ // the Hono page route and come back as the HTML shell.
27
+ function createDevServerExclude({ basePath }) {
28
+ const prefix = escapeRegExp(basePath);
29
+ return [
30
+ new RegExp(`^${prefix}/client/.+`),
31
+ new RegExp(`^${prefix}/lib/.+`),
32
+ new RegExp(`^${prefix}/build/.+`),
33
+ new RegExp(`^${prefix}/@.+$`),
34
+ new RegExp(`^${prefix}/node_modules/.*`),
35
+ /\?t=\d+$/,
36
+ /^\/favicon\.ico$/,
37
+ ];
38
+ }
39
+
40
+ export default createDevServerExclude;
@@ -0,0 +1,79 @@
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 createDevServerExclude from './createDevServerExclude.js';
18
+
19
+ function isExcluded(exclude, url) {
20
+ return exclude.some((pattern) => pattern.test(url));
21
+ }
22
+
23
+ test('createDevServerExclude leaves Vite module paths to Vite when there is no basePath', () => {
24
+ const exclude = createDevServerExclude({ basePath: '' });
25
+
26
+ expect(isExcluded(exclude, '/@vite/client')).toBe(true);
27
+ expect(isExcluded(exclude, '/@react-refresh')).toBe(true);
28
+ expect(isExcluded(exclude, '/client/main.jsx')).toBe(true);
29
+ expect(isExcluded(exclude, '/build/plugins/blocks.js')).toBe(true);
30
+ expect(isExcluded(exclude, '/lib/client/ErrorBar.jsx')).toBe(true);
31
+ expect(isExcluded(exclude, '/node_modules/.vite/deps/react.js?v=1')).toBe(true);
32
+ expect(isExcluded(exclude, '/client/App.jsx?t=1712345678')).toBe(true);
33
+ expect(isExcluded(exclude, '/favicon.ico')).toBe(true);
34
+ });
35
+
36
+ test('createDevServerExclude routes page, api and docs paths to Hono when there is no basePath', () => {
37
+ const exclude = createDevServerExclude({ basePath: '' });
38
+
39
+ expect(isExcluded(exclude, '/')).toBe(false);
40
+ expect(isExcluded(exclude, '/home')).toBe(false);
41
+ expect(isExcluded(exclude, '/api/ping')).toBe(false);
42
+ expect(isExcluded(exclude, '/lowdefy-docs/mcp')).toBe(false);
43
+ });
44
+
45
+ test('createDevServerExclude leaves Vite module paths under the basePath to Vite', () => {
46
+ const exclude = createDevServerExclude({ basePath: '/app' });
47
+
48
+ expect(isExcluded(exclude, '/app/@vite/client')).toBe(true);
49
+ expect(isExcluded(exclude, '/app/@react-refresh')).toBe(true);
50
+ expect(isExcluded(exclude, '/app/@fs/Users/dev/app/plugin.js')).toBe(true);
51
+ expect(isExcluded(exclude, '/app/client/main.jsx')).toBe(true);
52
+ expect(isExcluded(exclude, '/app/build/plugins/blocks.js')).toBe(true);
53
+ expect(isExcluded(exclude, '/app/lib/client/ErrorBar.jsx')).toBe(true);
54
+ expect(isExcluded(exclude, '/app/node_modules/.vite/deps/react.js?v=1')).toBe(true);
55
+ });
56
+
57
+ test('createDevServerExclude routes page, api and docs paths under the basePath to Hono', () => {
58
+ const exclude = createDevServerExclude({ basePath: '/app' });
59
+
60
+ expect(isExcluded(exclude, '/app')).toBe(false);
61
+ expect(isExcluded(exclude, '/app/home')).toBe(false);
62
+ expect(isExcluded(exclude, '/app/client-list')).toBe(false);
63
+ expect(isExcluded(exclude, '/app/api/ping')).toBe(false);
64
+ expect(isExcluded(exclude, '/app/lowdefy-docs/mcp')).toBe(false);
65
+ });
66
+
67
+ test('createDevServerExclude does not hand unprefixed module paths to Vite under a basePath', () => {
68
+ const exclude = createDevServerExclude({ basePath: '/app' });
69
+
70
+ expect(isExcluded(exclude, '/@vite/client')).toBe(false);
71
+ expect(isExcluded(exclude, '/client/main.jsx')).toBe(false);
72
+ });
73
+
74
+ test('createDevServerExclude matches a basePath containing regular expression characters literally', () => {
75
+ const exclude = createDevServerExclude({ basePath: '/my.app' });
76
+
77
+ expect(isExcluded(exclude, '/my.app/@vite/client')).toBe(true);
78
+ expect(isExcluded(exclude, '/myxapp/@vite/client')).toBe(false);
79
+ });
@@ -25,13 +25,17 @@ import checkMockUserWarning from './processes/checkMockUserWarning.mjs';
25
25
  import initialBuild from './processes/initialBuild.mjs';
26
26
  import installPlugins from './processes/installPlugins.mjs';
27
27
  import lowdefyBuild from './processes/lowdefyBuild.mjs';
28
+ import optimizeDependencies from './processes/optimizeDependencies.mjs';
28
29
  import readDotEnv from './processes/readDotEnv.mjs';
29
30
  import reloadClients from './processes/reloadClients.mjs';
30
31
  import restartServer from './processes/restartServer.mjs';
31
32
  import shutdownServer from './processes/shutdownServer.mjs';
32
33
  import startWatchers from './processes/startWatchers.mjs';
34
+ import syncServer from './processes/syncServer.mjs';
33
35
 
36
+ import createServerArtifactTracker from './utils/createServerArtifactTracker.mjs';
34
37
  import getViteBin from './utils/getViteBin.mjs';
38
+ import readManagerVersion from './utils/readManagerVersion.mjs';
35
39
 
36
40
  const argv = yargs(hideBin(process.argv)).array('watch').array('watchIgnore').argv;
37
41
 
@@ -44,6 +48,7 @@ async function getContext() {
44
48
  },
45
49
  directories: {
46
50
  build: path.resolve(process.cwd(), './build'),
51
+ buildStaging: path.resolve(process.cwd(), './build-staging'),
47
52
  config: path.resolve(argv.configDirectory ?? env.LOWDEFY_DIRECTORY_CONFIG ?? process.cwd()),
48
53
  server: process.cwd(),
49
54
  },
@@ -54,8 +59,14 @@ async function getContext() {
54
59
  destination: pino.destination({ dest: 1, sync: true }),
55
60
  }),
56
61
  options: {
62
+ internalPort: env.LOWDEFY_SERVER_DEV_INTERNAL_PORT
63
+ ? Number(env.LOWDEFY_SERVER_DEV_INTERNAL_PORT)
64
+ : undefined,
65
+ owner: env.LOWDEFY_DEV_OWNER === 'hub' ? 'hub' : 'terminal',
57
66
  port: Number(argv.port ?? env.PORT ?? 3000),
58
67
  refResolver: argv.refResolver ?? env.LOWDEFY_BUILD_REF_RESOLVER,
68
+ skipPluginChecks: env.LOWDEFY_BUILD_SKIP_PLUGIN_CHECKS === 'true',
69
+ strictPort: env.LOWDEFY_SERVER_DEV_STRICT_PORT === 'true',
59
70
  watch:
60
71
  argv.watch ?? env.LOWDEFY_SERVER_DEV_WATCH ? JSON.parse(env.LOWDEFY_SERVER_DEV_WATCH) : [],
61
72
  watchIgnore:
@@ -63,11 +74,12 @@ async function getContext() {
63
74
  ? JSON.parse(env.LOWDEFY_SERVER_DEV_WATCH_IGNORE)
64
75
  : [],
65
76
  },
66
- version: env.npm_package_version,
77
+ version: readManagerVersion(),
67
78
 
68
79
  // JIT build state
69
80
  pageRegistry: null,
70
81
  buildContext: null,
82
+ lastBuildFailed: false,
71
83
  };
72
84
 
73
85
  context.packageManagerCmd = process.platform === 'win32' ? 'pnpm.cmd' : 'pnpm';
@@ -78,18 +90,28 @@ async function getContext() {
78
90
  // Wrap lowdefyBuild to capture and store the shallow build result
79
91
  const buildFn = lowdefyBuild(context);
80
92
  context.lowdefyBuild = async () => {
81
- const result = await buildFn();
93
+ let result;
94
+ try {
95
+ result = await buildFn();
96
+ } catch (error) {
97
+ context.lastBuildFailed = true;
98
+ throw error;
99
+ }
100
+ context.lastBuildFailed = false;
82
101
  if (result) {
83
102
  context.pageRegistry = result.pageRegistry;
84
103
  context.buildContext = result.context;
85
104
  }
86
105
  };
87
106
 
107
+ context.optimizeDependencies = optimizeDependencies(context);
88
108
  context.readDotEnv = readDotEnv(context);
89
109
  context.reloadClients = reloadClients(context);
90
110
  context.restartServer = restartServer(context);
111
+ context.serverArtifacts = createServerArtifactTracker(context);
91
112
  context.shutdownServer = shutdownServer(context);
92
113
  context.startWatchers = startWatchers(context);
114
+ context.syncServer = syncServer(context);
93
115
 
94
116
  return context;
95
117
  }
@@ -15,12 +15,26 @@
15
15
  limitations under the License.
16
16
  */
17
17
 
18
+ import { pluginChecksNeedRebuild } from '@lowdefy/node-utils';
19
+
18
20
  function initialBuild(context) {
19
21
  return async () => {
20
22
  context.readDotEnv();
21
23
  await context.lowdefyBuild();
22
24
  await context.checkMockUserWarning();
23
25
  await context.installPlugins();
26
+ // The build lists the plugins it used but could not check, since they were
27
+ // not installed. The install above added them, so build once more when that
28
+ // checks something new, unless no checks run this time anyway.
29
+ if (
30
+ !context.options.skipPluginChecks &&
31
+ pluginChecksNeedRebuild({
32
+ buildDirectory: context.directories.build,
33
+ serverDirectory: context.directories.server,
34
+ })
35
+ ) {
36
+ await context.lowdefyBuild();
37
+ }
24
38
  };
25
39
  }
26
40
 
@@ -0,0 +1,101 @@
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
+ import { jest } from '@jest/globals';
21
+
22
+ import initialBuild from './initialBuild.mjs';
23
+
24
+ let buildDirectory;
25
+ let calls;
26
+
27
+ function createContext({ installPlugins, skipPluginChecks } = {}) {
28
+ const record = (name, impl) =>
29
+ jest.fn(async () => {
30
+ calls.push(name);
31
+ if (impl) await impl();
32
+ });
33
+ return {
34
+ directories: { build: buildDirectory, server: buildDirectory },
35
+ options: { skipPluginChecks },
36
+ readDotEnv: jest.fn(() => calls.push('readDotEnv')),
37
+ lowdefyBuild: record('lowdefyBuild'),
38
+ checkMockUserWarning: record('checkMockUserWarning'),
39
+ installPlugins: record('installPlugins', installPlugins),
40
+ };
41
+ }
42
+
43
+ beforeEach(() => {
44
+ calls = [];
45
+ buildDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-initial-build-'));
46
+ });
47
+
48
+ afterEach(() => {
49
+ fs.rmSync(buildDirectory, { recursive: true, force: true });
50
+ });
51
+
52
+ test('initialBuild builds once when no plugin checks are pending', async () => {
53
+ const context = createContext();
54
+ await initialBuild(context)();
55
+ expect(context.lowdefyBuild).toHaveBeenCalledTimes(1);
56
+ expect(calls).toEqual(['readDotEnv', 'lowdefyBuild', 'checkMockUserWarning', 'installPlugins']);
57
+ });
58
+
59
+ test('initialBuild builds again after installing plugins when plugin checks are pending', async () => {
60
+ const context = createContext({
61
+ installPlugins: () =>
62
+ fs.writeFileSync(
63
+ path.join(buildDirectory, 'plugin-checks-pending.json'),
64
+ JSON.stringify({ packages: [{ name: '@acme/plugin', schemaChecked: true }] })
65
+ ),
66
+ });
67
+ await initialBuild(context)();
68
+ expect(context.lowdefyBuild).toHaveBeenCalledTimes(2);
69
+ expect(calls).toEqual([
70
+ 'readDotEnv',
71
+ 'lowdefyBuild',
72
+ 'checkMockUserWarning',
73
+ 'installPlugins',
74
+ 'lowdefyBuild',
75
+ ]);
76
+ });
77
+
78
+ test('initialBuild builds once with skipPluginChecks, even when plugin checks are pending', async () => {
79
+ const context = createContext({
80
+ skipPluginChecks: true,
81
+ installPlugins: () =>
82
+ fs.writeFileSync(
83
+ path.join(buildDirectory, 'plugin-checks-pending.json'),
84
+ JSON.stringify({ packages: [{ name: '@acme/plugin', schemaChecked: true }] })
85
+ ),
86
+ });
87
+ await initialBuild(context)();
88
+ expect(context.lowdefyBuild).toHaveBeenCalledTimes(1);
89
+ });
90
+
91
+ test('initialBuild builds once when the pending plugins give nothing new to check', async () => {
92
+ const context = createContext({
93
+ installPlugins: () =>
94
+ fs.writeFileSync(
95
+ path.join(buildDirectory, 'plugin-checks-pending.json'),
96
+ JSON.stringify({ packages: [{ name: '@acme/connection', schemaChecked: false }] })
97
+ ),
98
+ });
99
+ await initialBuild(context)();
100
+ expect(context.lowdefyBuild).toHaveBeenCalledTimes(1);
101
+ });
@@ -17,6 +17,7 @@
17
17
  import { serializeBuildException, shallowBuild } from '@lowdefy/build/dev';
18
18
  import createCustomPluginMessagesMap from '../utils/createCustomPluginMessagesMap.mjs';
19
19
  import createCustomPluginTypesMap from '../utils/createCustomPluginTypesMap.mjs';
20
+ import publishBuildDirectory from '../utils/publishBuildDirectory.mjs';
20
21
  import writeBuildStatus from '../utils/writeBuildStatus.mjs';
21
22
 
22
23
  function formatDuration(ms) {
@@ -25,26 +26,39 @@ function formatDuration(ms) {
25
26
  }
26
27
 
27
28
  function lowdefyBuild({ directories, logger, options }) {
28
- return async () => {
29
+ async function build() {
29
30
  logger.info({ spin: 'start' }, 'Building config...');
30
31
  const startTime = Date.now();
31
- const customTypesMap = await createCustomPluginTypesMap({ directories, logger });
32
- const customMessagesMap = await createCustomPluginMessagesMap({ directories, logger });
33
32
 
33
+ // Reading the plugin maps parses lowdefy.yaml and imports each plugin's
34
+ // types, so a YAML syntax error or a broken plugin fails the build there,
35
+ // and the build status has to report it like any other build error. So
36
+ // does a publish that fails part way: the live build is then a mix of two
37
+ // builds, and the next change must rebuild and publish it whole.
34
38
  let result;
35
39
  try {
40
+ const [customTypesMap, customMessagesMap] = await Promise.all([
41
+ createCustomPluginTypesMap({ directories, logger }),
42
+ createCustomPluginMessagesMap({ directories, logger }),
43
+ ]);
36
44
  result = await shallowBuild({
37
45
  customMessagesMap,
38
46
  customTypesMap,
39
- directories,
47
+ directories: { ...directories, build: directories.buildStaging },
40
48
  logger,
41
49
  refResolver: options.refResolver,
50
+ skipPluginChecks: options.skipPluginChecks,
42
51
  stage: 'dev',
43
52
  });
53
+ await publishBuildDirectory({
54
+ buildDirectory: directories.build,
55
+ stagingDirectory: directories.buildStaging,
56
+ }).catch((error) => {
57
+ throw new Error(`Publishing the build to ${directories.build} failed: ${error.message}`, {
58
+ cause: error,
59
+ });
60
+ });
44
61
  } catch (error) {
45
- // Write the artifact after the attempt fails - the build may have
46
- // cleaned the build directory before failing, so this must not run
47
- // before the attempt completes.
48
62
  await writeBuildStatus({
49
63
  directories,
50
64
  status: 'error',
@@ -65,6 +79,15 @@ function lowdefyBuild({ directories, logger, options }) {
65
79
  const duration = Date.now() - startTime;
66
80
  logger.info({ spin: 'succeed' }, `Built config in ${formatDuration(duration)}.`);
67
81
  return result;
82
+ }
83
+
84
+ // Watchers, restarts and plugin installs each start builds, and every build
85
+ // writes the same staging directory, so a build waits for the one before it.
86
+ let previousBuild = Promise.resolve();
87
+ return () => {
88
+ const nextBuild = previousBuild.then(build);
89
+ previousBuild = nextBuild.catch(() => {});
90
+ return nextBuild;
68
91
  };
69
92
  }
70
93
 
@@ -0,0 +1,62 @@
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 { jest } from '@jest/globals';
18
+
19
+ const mockShallowBuild = jest.fn();
20
+
21
+ jest.unstable_mockModule('@lowdefy/build/dev', () => ({
22
+ shallowBuild: mockShallowBuild,
23
+ serializeBuildException: jest.fn((exception) => ({ message: exception.message })),
24
+ }));
25
+ jest.unstable_mockModule('../utils/writeBuildStatus.mjs', () => ({
26
+ default: jest.fn(),
27
+ }));
28
+ jest.unstable_mockModule('../utils/createCustomPluginTypesMap.mjs', () => ({
29
+ default: jest.fn().mockResolvedValue({}),
30
+ }));
31
+ jest.unstable_mockModule('../utils/createCustomPluginMessagesMap.mjs', () => ({
32
+ default: jest.fn().mockResolvedValue({}),
33
+ }));
34
+ jest.unstable_mockModule('../utils/publishBuildDirectory.mjs', () => ({
35
+ default: jest.fn(async () => {}),
36
+ }));
37
+
38
+ const { default: lowdefyBuild } = await import('./lowdefyBuild.mjs');
39
+
40
+ function createContext(options) {
41
+ return {
42
+ directories: { build: '/app/build', buildStaging: '/app/build-staging' },
43
+ logger: { info: jest.fn() },
44
+ options,
45
+ };
46
+ }
47
+
48
+ beforeEach(() => {
49
+ mockShallowBuild.mockResolvedValue({ components: {}, pageRegistry: {}, context: {} });
50
+ });
51
+
52
+ test('lowdefyBuild passes skipPluginChecks true to shallowBuild', async () => {
53
+ await lowdefyBuild(createContext({ skipPluginChecks: true }))();
54
+ expect(mockShallowBuild.mock.calls[0][0].skipPluginChecks).toBe(true);
55
+ });
56
+
57
+ test('lowdefyBuild passes skipPluginChecks undefined to shallowBuild when the option is not set', async () => {
58
+ await lowdefyBuild(createContext({}))();
59
+ const [args] = mockShallowBuild.mock.calls[0];
60
+ expect(args.skipPluginChecks).toBeUndefined();
61
+ expect(args.stage).toEqual('dev');
62
+ });