@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,130 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import fs from 'node:fs';
18
+ import os from 'node:os';
19
+ import path from 'node:path';
20
+
21
+ import resolveJourneyDataSet from './resolveJourneyDataSet.js';
22
+
23
+ let configDirectory;
24
+ let buildDirectory;
25
+
26
+ beforeEach(() => {
27
+ configDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-resolve-journey-data-set-'));
28
+ buildDirectory = path.join(configDirectory, '.lowdefy', 'dev', 'build');
29
+ fs.mkdirSync(path.join(configDirectory, 'tests', 'data'), { recursive: true });
30
+ fs.mkdirSync(path.join(buildDirectory, 'connections'), { recursive: true });
31
+ fs.writeFileSync(
32
+ path.join(buildDirectory, 'connections', 'tickets.json'),
33
+ JSON.stringify({
34
+ connectionId: 'tickets',
35
+ type: 'MongoDBCollection',
36
+ properties: { databaseUri: { _secret: 'MONGODB_URI' }, collection: 'tickets' },
37
+ })
38
+ );
39
+ fs.writeFileSync(
40
+ path.join(configDirectory, 'tests', 'data', 'empty-org.yaml'),
41
+ `fixtures:
42
+ tickets: [{ _id: t1, organizationId: org_a }]
43
+ users:
44
+ owner: { id: u_1, roles: [admin], organizationId: org_a }
45
+ member: { id: u_2, roles: [member], organizationId: org_a }
46
+ `
47
+ );
48
+ });
49
+
50
+ afterEach(() => {
51
+ fs.rmSync(configDirectory, { recursive: true, force: true });
52
+ });
53
+
54
+ function resolve(overrides) {
55
+ return resolveJourneyDataSet({
56
+ configDirectory,
57
+ buildDirectory,
58
+ authConfigured: true,
59
+ mockUserActive: false,
60
+ ...overrides,
61
+ });
62
+ }
63
+
64
+ test('resolveJourneyDataSet resolves a named user from the data set', async () => {
65
+ const { dataSet, user, error } = await resolve({ data: 'empty-org', user: 'member' });
66
+ expect(error).toBeUndefined();
67
+ expect(user).toEqual({ id: 'u_2', roles: ['member'], organizationId: 'org_a' });
68
+ expect(dataSet.name).toEqual('empty-org');
69
+ expect(dataSet.collections).toEqual({ tickets: 'tickets' });
70
+ });
71
+
72
+ test('resolveJourneyDataSet keeps an inline user on a data set journey', async () => {
73
+ const { user, dataSet } = await resolve({ data: 'empty-org', user: { id: 'x', roles: [] } });
74
+ expect(user).toEqual({ id: 'x', roles: [] });
75
+ expect(dataSet.users.owner.id).toEqual('u_1');
76
+ });
77
+
78
+ test('resolveJourneyDataSet refuses a user name the data set does not declare', async () => {
79
+ const { error } = await resolve({ data: 'empty-org', user: 'outsider' });
80
+ expect(error).toEqual(
81
+ 'Data set "empty-org" declares no user "outsider". Declared: owner, member.'
82
+ );
83
+ });
84
+
85
+ test('resolveJourneyDataSet refuses a user name on a journey with no data', async () => {
86
+ const { error } = await resolve({ user: 'member' });
87
+ expect(error).toMatch(
88
+ 'The journey\'s user "member" names a data set user, but the journey has no "data".'
89
+ );
90
+ });
91
+
92
+ test('resolveJourneyDataSet passes a journey with no data and no named user through untouched', async () => {
93
+ expect(await resolve({ user: 'none' })).toEqual({ user: 'none' });
94
+ expect(await resolve({ user: { roles: ['admin'] } })).toEqual({ user: { roles: ['admin'] } });
95
+ });
96
+
97
+ test('resolveJourneyDataSet refuses user none on a data set while auth is configured', async () => {
98
+ const { error } = await resolve({ data: 'empty-org', user: 'none' });
99
+ expect(error).toMatch('has user "none", which is refused while auth is configured');
100
+ });
101
+
102
+ test('resolveJourneyDataSet allows user none on a data set when auth is not configured', async () => {
103
+ const { error, user } = await resolve({ data: 'empty-org', user: 'none', authConfigured: false });
104
+ expect(error).toBeUndefined();
105
+ expect(user).toEqual('none');
106
+ });
107
+
108
+ test.each([['member'], [{ id: 'x', roles: ['admin'] }], [undefined]])(
109
+ 'resolveJourneyDataSet refuses a data set journey with user %j while a dev mock user is active',
110
+ async (user) => {
111
+ const { error } = await resolve({ data: 'empty-org', user, mockUserActive: true });
112
+ expect(error).toMatch('cannot run while a dev mock user is active');
113
+ }
114
+ );
115
+
116
+ test('resolveJourneyDataSet returns readDataSet errors as its own', async () => {
117
+ const { error } = await resolve({ data: 'missing', user: 'member' });
118
+ expect(error).toEqual('Data set "missing" not found in tests/data. Declared: empty-org.');
119
+ });
120
+
121
+ test('resolveJourneyDataSet refuses user none while a dev mock user is active', async () => {
122
+ const { error } = await resolve({ user: 'none', mockUserActive: true });
123
+ expect(error).toMatch(
124
+ 'The journey has user "none", which cannot run while a dev mock user is active'
125
+ );
126
+ });
127
+
128
+ test('resolveJourneyDataSet keeps user none when no dev mock user is active', async () => {
129
+ expect(await resolve({ user: 'none' })).toEqual({ user: 'none' });
130
+ });
@@ -0,0 +1,32 @@
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 { type } from '@lowdefy/helpers';
18
+
19
+ // The session database a database target (describeTargetKey) reads. Each target the build names has
20
+ // its own, numbered when the session opens; a target the build did not name (a config mutant's, say)
21
+ // gets the next number when first met, so it never merges with another. A target computed per
22
+ // request (null) reads the session's first database.
23
+ function sessionDatabaseName({ session, target }) {
24
+ if (type.isNone(target)) return session.databaseName;
25
+ if (!session.databases.has(target)) {
26
+ session.databases.set(target, `${session.prefix}_${session.databaseCount}`);
27
+ session.databaseCount += 1;
28
+ }
29
+ return session.databases.get(target);
30
+ }
31
+
32
+ export default sessionDatabaseName;
@@ -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 dataSessionRegistry from './dataSessionRegistry.js';
18
+
19
+ // Drops every session database on the store that no registered session owns: one left behind by a
20
+ // write that was still in flight when its session dropped its databases, or by a dev server that
21
+ // stopped mid-run. A session owns every database under its prefix (<prefix>_<n>), including one it
22
+ // numbers after this sweep lists them.
23
+ async function sweepOrphanDatabases({ client }) {
24
+ const prefixes = [...dataSessionRegistry.values()].map((session) => `${session.prefix}_`);
25
+ const { databases } = await client.db('admin').admin().listDatabases({ nameOnly: true });
26
+ await Promise.all(
27
+ databases
28
+ .map((database) => database.name)
29
+ .filter(
30
+ (name) => name.startsWith('ld_') && !prefixes.some((prefix) => name.startsWith(prefix))
31
+ )
32
+ .map((name) => client.db(name).dropDatabase())
33
+ );
34
+ }
35
+
36
+ export default sweepOrphanDatabases;
@@ -0,0 +1,225 @@
1
+ # The app the data set journey end-to-end test (dataSetJourneys.chromium.mongodb.test.mjs) builds and
2
+ # serves. Its connections read the MONGODB_URI secret, which the test points at a stand-in for the
3
+ # developer's real database; a journey on the "shop" data set must never touch it.
4
+ # Auth is configured, with its adapter on the same stand-in, so the suite can prove that no journey
5
+ # request reaches the auth engine or writes an auth collection there.
6
+ lowdefy: local
7
+ name: Data set journeys
8
+
9
+ auth:
10
+ database:
11
+ id: auth_db
12
+ type: MongoDBAuthAdapter
13
+ properties:
14
+ uri:
15
+ _secret: MONGODB_URI
16
+ secret:
17
+ _secret: BETTER_AUTH_SECRET
18
+ emailAndPassword:
19
+ enabled: true
20
+ organizations:
21
+ policy: tenant
22
+
23
+ # Under policy: tenant a MongoDBCollection connection is walled by default: every read is filtered
24
+ # and every write stamped with the caller's organization_id.
25
+ connections:
26
+ - id: tickets
27
+ type: MongoDBCollection
28
+ properties:
29
+ databaseUri:
30
+ _secret: MONGODB_URI
31
+ collection: tickets
32
+ write: true
33
+ # A second database that also has a tickets collection: under a data set it reads a session
34
+ # database of its own, so its fixtures never merge with the app's tickets.
35
+ - id: crm_tickets
36
+ type: MongoDBCollection
37
+ properties:
38
+ databaseUri:
39
+ _secret: CRM_MONGODB_URI
40
+ collection: tickets
41
+ write: true
42
+
43
+ # A module connection is written under its module's folder (connections/archive/archive_db.json),
44
+ # which the data set redirect must reach too.
45
+ modules:
46
+ - id: archive
47
+ source: 'file:modules/archive'
48
+
49
+ api:
50
+ - id: create_ticket
51
+ type: Api
52
+ routine:
53
+ - id: insert
54
+ type: MongoDBInsertOne
55
+ connectionId: tickets
56
+ properties:
57
+ doc:
58
+ _id:
59
+ _payload: id
60
+ title:
61
+ _payload: title
62
+ - :return:
63
+ inserted:
64
+ _payload: id
65
+ - id: list_tickets
66
+ type: Api
67
+ routine:
68
+ - id: find
69
+ type: MongoDBFind
70
+ connectionId: tickets
71
+ properties:
72
+ query: {}
73
+ options:
74
+ sort:
75
+ _id: 1
76
+ - :return:
77
+ _step: find
78
+ # The developer's own cookieless tab: no caller, so the read opts out of the wall.
79
+ - id: list_all_tickets
80
+ type: Api
81
+ routine:
82
+ - id: find
83
+ type: MongoDBFind
84
+ connectionId: tickets
85
+ tenant: none
86
+ properties:
87
+ query: {}
88
+ options:
89
+ sort:
90
+ _id: 1
91
+ - :return:
92
+ _step: find
93
+ - id: list_crm_tickets
94
+ type: Api
95
+ routine:
96
+ - id: find
97
+ type: MongoDBFind
98
+ connectionId: crm_tickets
99
+ properties:
100
+ query: {}
101
+ options:
102
+ sort:
103
+ _id: 1
104
+ - :return:
105
+ _step: find
106
+ - id: create_ticket_later
107
+ type: Api
108
+ routine:
109
+ - id: dispatch
110
+ type: CallApi
111
+ properties:
112
+ endpointId: insert_detached
113
+ detached: true
114
+ payload:
115
+ id:
116
+ _payload: id
117
+ - :return:
118
+ dispatched: true
119
+ - id: insert_detached
120
+ type: Api
121
+ routine:
122
+ - id: insert
123
+ type: MongoDBInsertOne
124
+ connectionId: tickets
125
+ properties:
126
+ doc:
127
+ _id:
128
+ _payload: id
129
+ detached: true
130
+
131
+ # Run under a config mutant (`lowdefy journeys harden`) that drops their "log" step: a detached
132
+ # target and a direct endpoint, each writing a ticket and then a log entry.
133
+ - id: create_ticket_later_logged
134
+ type: Api
135
+ routine:
136
+ - id: dispatch
137
+ type: CallApi
138
+ properties:
139
+ endpointId: insert_detached_logged
140
+ detached: true
141
+ payload:
142
+ id:
143
+ _payload: id
144
+ - :return:
145
+ dispatched: true
146
+ - id: insert_detached_logged
147
+ type: Api
148
+ routine:
149
+ - id: insert
150
+ type: MongoDBInsertOne
151
+ connectionId: tickets
152
+ properties:
153
+ doc:
154
+ _id:
155
+ _payload: id
156
+ detached: true
157
+ - id: log
158
+ type: MongoDBInsertOne
159
+ connectionId: tickets
160
+ properties:
161
+ doc:
162
+ _id: log-detached
163
+ - id: create_ticket_logged
164
+ type: Api
165
+ routine:
166
+ - id: insert
167
+ type: MongoDBInsertOne
168
+ connectionId: tickets
169
+ properties:
170
+ doc:
171
+ _id:
172
+ _payload: id
173
+ - id: log
174
+ type: MongoDBInsertOne
175
+ connectionId: tickets
176
+ properties:
177
+ doc:
178
+ _id: log-direct
179
+ - :return:
180
+ inserted: true
181
+
182
+ # A detached run whose routine reaches an auth step: under a data session it has no auth engine,
183
+ # so the step fails and the insert after it never runs.
184
+ - id: create_ticket_later_auth
185
+ type: Api
186
+ routine:
187
+ - id: dispatch
188
+ type: CallApi
189
+ properties:
190
+ endpointId: insert_detached_auth
191
+ detached: true
192
+ payload: {}
193
+ - :return:
194
+ dispatched: true
195
+ - id: insert_detached_auth
196
+ type: Api
197
+ routine:
198
+ - id: before
199
+ type: MongoDBInsertOne
200
+ connectionId: tickets
201
+ properties:
202
+ doc:
203
+ _id: later-auth-before
204
+ - id: members
205
+ type: ListMembers
206
+ properties: {}
207
+ - id: after
208
+ type: MongoDBInsertOne
209
+ connectionId: tickets
210
+ properties:
211
+ doc:
212
+ _id: later-auth-after
213
+
214
+ websockets:
215
+ - id: ticket_changes
216
+ type: MongoDBChangeStream
217
+ connectionId: tickets
218
+ properties:
219
+ pipeline:
220
+ - $match:
221
+ operationType: insert
222
+
223
+ pages:
224
+ - id: tickets
225
+ type: Box
@@ -0,0 +1,30 @@
1
+ name: Archive
2
+
3
+ secrets:
4
+ - name: MONGODB_URI
5
+ description: The app's database, the same stand-in the app's own connection reads.
6
+
7
+ connections:
8
+ - id: archive_db
9
+ type: MongoDBCollection
10
+ properties:
11
+ databaseUri:
12
+ _secret: MONGODB_URI
13
+ collection: archived
14
+ write: true
15
+
16
+ api:
17
+ - id: archive_ticket
18
+ type: Api
19
+ routine:
20
+ - id: insert
21
+ type: MongoDBInsertOne
22
+ connectionId:
23
+ _module.connectionId: archive_db
24
+ properties:
25
+ doc:
26
+ _id:
27
+ _payload: id
28
+ - :return:
29
+ archived:
30
+ _payload: id
@@ -0,0 +1,12 @@
1
+ fixtures:
2
+ tickets:
3
+ - { _id: t-a1, organization_id: org_a, title: First }
4
+ - { _id: t-a2, organization_id: org_a, title: Second }
5
+ - { _id: t-b1, organization_id: org_b, title: Other org }
6
+ # The same collection name in another database: it loads into a session database of its own.
7
+ crm_tickets:
8
+ - { _id: t-a1, organization_id: org_a, title: CRM copy }
9
+ - { _id: crm-a2, organization_id: org_a, title: CRM only }
10
+ users:
11
+ owner: { id: u_1, roles: [admin], organizationId: org_a }
12
+ outsider: { id: u_9, roles: [admin], organizationId: org_b }
@@ -0,0 +1,43 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import { shallowBuild } from '@lowdefy/build/dev';
20
+
21
+ // Builds an app into <serverDirectory>/build for a test, in a Node process of its own: the build
22
+ // imports plugin modules dynamically, which jest's module runtime cannot load.
23
+ // node buildTestApp.mjs <configDirectory> <serverDirectory>
24
+ const [configDirectory, serverDirectory] = process.argv.slice(2);
25
+ const serverDevDirectory = path.resolve(import.meta.dirname, '../../../..');
26
+ const directories = {
27
+ config: configDirectory,
28
+ build: path.join(serverDirectory, 'build'),
29
+ server: path.join(serverDirectory, 'server'),
30
+ };
31
+ fs.mkdirSync(path.join(directories.server, 'public_default'), { recursive: true });
32
+ fs.copyFileSync(
33
+ path.join(serverDevDirectory, 'package.json'),
34
+ path.join(directories.server, 'package.json')
35
+ );
36
+ const quiet = () => {};
37
+ await shallowBuild({
38
+ customMessagesMap: {},
39
+ customTypesMap: {},
40
+ directories,
41
+ logger: { debug: quiet, info: quiet, warn: quiet, error: console.error, ui: {} },
42
+ stage: 'dev',
43
+ });
@@ -0,0 +1,136 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import path from 'node:path';
18
+
19
+ import chokidar from 'chokidar';
20
+ import { type } from '@lowdefy/helpers';
21
+
22
+ import getStaleStatus from './getStaleStatus.js';
23
+ import readBuildArtifact from './readBuildArtifact.js';
24
+
25
+ // In-process publish/subscribe bus behind the dev server's push channels
26
+ // (MCP notifications/message and GET /lowdefy-docs/events). Deliberately
27
+ // unbuffered: a subscriber that missed an event polls build_status, which is
28
+ // derived from the same build/buildStatus.json this bus watches, so the two
29
+ // always agree. Event shape is { type, timestamp, ...entry } with type one of
30
+ // build | client_error | server_error | restart.
31
+ const EVENT_TYPES = ['build', 'client_error', 'server_error', 'restart'];
32
+
33
+ // The Hono process boots once per (re)start, so module load time is the
34
+ // restart time — subscribers receive it as a restart event on connect.
35
+ const bootedAt = new Date().toISOString();
36
+
37
+ const subscribers = [];
38
+
39
+ let watcher = null;
40
+
41
+ function publish(event) {
42
+ if (!EVENT_TYPES.includes(event?.type)) {
43
+ throw new Error(
44
+ `devEventBus event type must be one of ${EVENT_TYPES.join(', ')}. Received ${JSON.stringify(
45
+ event?.type
46
+ )}.`
47
+ );
48
+ }
49
+ const fullEvent = { timestamp: new Date().toISOString(), ...event };
50
+ // Iterate over a copy: a failing send unsubscribes itself mid-loop.
51
+ [...subscribers].forEach((send) => {
52
+ let result;
53
+ try {
54
+ result = send(fullEvent);
55
+ } catch {
56
+ unsubscribe(send);
57
+ return;
58
+ }
59
+ if (type.isFunction(result?.then)) {
60
+ result.catch(() => unsubscribe(send));
61
+ }
62
+ });
63
+ }
64
+
65
+ function readBuildEvent() {
66
+ // The manager writes buildStatus.json from another process; a change event
67
+ // can fire while the write is still in flight. A half-written file is not an
68
+ // error worth surfacing — the completed write fires its own change event.
69
+ try {
70
+ const build = readBuildArtifact({ name: 'buildStatus.json' });
71
+ if (type.isNone(build)) return null;
72
+ return {
73
+ type: 'build',
74
+ status: build.status,
75
+ errorCount: (build.errors ?? []).length,
76
+ warningCount: (build.warnings ?? []).length,
77
+ errors: build.errors ?? [],
78
+ warnings: build.warnings ?? [],
79
+ stale: false,
80
+ staleSince: null,
81
+ ...getStaleStatus(),
82
+ };
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+
88
+ function publishBuildStatus() {
89
+ const event = readBuildEvent();
90
+ if (event === null) return;
91
+ publish(event);
92
+ }
93
+
94
+ // The watcher is held open only while someone is listening. Starting it at
95
+ // module load would pin every process that imports an error store — including
96
+ // test runners — on a persistent fs handle for a file it may never need.
97
+ function startWatcher() {
98
+ watcher = chokidar.watch(path.join(process.cwd(), 'build', 'buildStatus.json'), {
99
+ persistent: true,
100
+ ignoreInitial: true,
101
+ });
102
+ watcher.on('add', publishBuildStatus);
103
+ watcher.on('change', publishBuildStatus);
104
+ // No unlink handler: cleanBuildDirectory removes the file mid-rebuild and the
105
+ // rebuild's own write re-adds it.
106
+ }
107
+
108
+ function stopWatcher() {
109
+ const closing = watcher.close();
110
+ watcher = null;
111
+ return closing;
112
+ }
113
+
114
+ function unsubscribe(send) {
115
+ const index = subscribers.indexOf(send);
116
+ if (index === -1) return;
117
+ subscribers.splice(index, 1);
118
+ if (subscribers.length === 0 && watcher !== null) {
119
+ stopWatcher();
120
+ }
121
+ }
122
+
123
+ function subscribe(send) {
124
+ if (!type.isFunction(send)) {
125
+ throw new Error(
126
+ `devEventBus.subscribe requires a "send" function. Received ${JSON.stringify(send)}.`
127
+ );
128
+ }
129
+ subscribers.push(send);
130
+ if (watcher === null) {
131
+ startWatcher();
132
+ }
133
+ return () => unsubscribe(send);
134
+ }
135
+
136
+ export { bootedAt, publish, subscribe };