@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,203 @@
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
+
34
+ # A module connection is written under its module's folder (connections/archive/archive_db.json),
35
+ # which the data set redirect must reach too.
36
+ modules:
37
+ - id: archive
38
+ source: 'file:modules/archive'
39
+
40
+ api:
41
+ - id: create_ticket
42
+ type: Api
43
+ routine:
44
+ - id: insert
45
+ type: MongoDBInsertOne
46
+ connectionId: tickets
47
+ properties:
48
+ doc:
49
+ _id:
50
+ _payload: id
51
+ title:
52
+ _payload: title
53
+ - :return:
54
+ inserted:
55
+ _payload: id
56
+ - id: list_tickets
57
+ type: Api
58
+ routine:
59
+ - id: find
60
+ type: MongoDBFind
61
+ connectionId: tickets
62
+ properties:
63
+ query: {}
64
+ options:
65
+ sort:
66
+ _id: 1
67
+ - :return:
68
+ _step: find
69
+ # The developer's own cookieless tab: no caller, so the read opts out of the wall.
70
+ - id: list_all_tickets
71
+ type: Api
72
+ routine:
73
+ - id: find
74
+ type: MongoDBFind
75
+ connectionId: tickets
76
+ tenant: none
77
+ properties:
78
+ query: {}
79
+ options:
80
+ sort:
81
+ _id: 1
82
+ - :return:
83
+ _step: find
84
+ - id: create_ticket_later
85
+ type: Api
86
+ routine:
87
+ - id: dispatch
88
+ type: CallApi
89
+ properties:
90
+ endpointId: insert_detached
91
+ detached: true
92
+ payload:
93
+ id:
94
+ _payload: id
95
+ - :return:
96
+ dispatched: true
97
+ - id: insert_detached
98
+ type: Api
99
+ routine:
100
+ - id: insert
101
+ type: MongoDBInsertOne
102
+ connectionId: tickets
103
+ properties:
104
+ doc:
105
+ _id:
106
+ _payload: id
107
+ detached: true
108
+
109
+ # Run under a config mutant (`lowdefy journeys harden`) that drops their "log" step: a detached
110
+ # target and a direct endpoint, each writing a ticket and then a log entry.
111
+ - id: create_ticket_later_logged
112
+ type: Api
113
+ routine:
114
+ - id: dispatch
115
+ type: CallApi
116
+ properties:
117
+ endpointId: insert_detached_logged
118
+ detached: true
119
+ payload:
120
+ id:
121
+ _payload: id
122
+ - :return:
123
+ dispatched: true
124
+ - id: insert_detached_logged
125
+ type: Api
126
+ routine:
127
+ - id: insert
128
+ type: MongoDBInsertOne
129
+ connectionId: tickets
130
+ properties:
131
+ doc:
132
+ _id:
133
+ _payload: id
134
+ detached: true
135
+ - id: log
136
+ type: MongoDBInsertOne
137
+ connectionId: tickets
138
+ properties:
139
+ doc:
140
+ _id: log-detached
141
+ - id: create_ticket_logged
142
+ type: Api
143
+ routine:
144
+ - id: insert
145
+ type: MongoDBInsertOne
146
+ connectionId: tickets
147
+ properties:
148
+ doc:
149
+ _id:
150
+ _payload: id
151
+ - id: log
152
+ type: MongoDBInsertOne
153
+ connectionId: tickets
154
+ properties:
155
+ doc:
156
+ _id: log-direct
157
+ - :return:
158
+ inserted: true
159
+
160
+ # A detached run whose routine reaches an auth step: under a data session it has no auth engine,
161
+ # so the step fails and the insert after it never runs.
162
+ - id: create_ticket_later_auth
163
+ type: Api
164
+ routine:
165
+ - id: dispatch
166
+ type: CallApi
167
+ properties:
168
+ endpointId: insert_detached_auth
169
+ detached: true
170
+ payload: {}
171
+ - :return:
172
+ dispatched: true
173
+ - id: insert_detached_auth
174
+ type: Api
175
+ routine:
176
+ - id: before
177
+ type: MongoDBInsertOne
178
+ connectionId: tickets
179
+ properties:
180
+ doc:
181
+ _id: later-auth-before
182
+ - id: members
183
+ type: ListMembers
184
+ properties: {}
185
+ - id: after
186
+ type: MongoDBInsertOne
187
+ connectionId: tickets
188
+ properties:
189
+ doc:
190
+ _id: later-auth-after
191
+
192
+ websockets:
193
+ - id: ticket_changes
194
+ type: MongoDBChangeStream
195
+ connectionId: tickets
196
+ properties:
197
+ pipeline:
198
+ - $match:
199
+ operationType: insert
200
+
201
+ pages:
202
+ - id: tickets
203
+ 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,8 @@
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
+ users:
7
+ owner: { id: u_1, roles: [admin], organizationId: org_a }
8
+ 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 };
@@ -0,0 +1,269 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ import fs from 'fs';
18
+ import os from 'os';
19
+ import path from 'path';
20
+
21
+ import { jest } from '@jest/globals';
22
+
23
+ import flushFsEvents from '../../test-utils/flushFsEvents.mjs';
24
+ import spyOnChokidar from '../../test-utils/spyOnChokidar.mjs';
25
+
26
+ // macOS can hold file events back for tens of seconds on a busy machine.
27
+ jest.setTimeout(180000);
28
+
29
+ // The bus watches build/buildStatus.json under process.cwd() — point it at a
30
+ // throwaway server directory before the module is imported.
31
+ const originalCwd = process.cwd();
32
+ const fixtureDir = fs.realpathSync(
33
+ fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-dev-event-bus-test-'))
34
+ );
35
+ fs.mkdirSync(path.join(fixtureDir, 'build'));
36
+ process.chdir(fixtureDir);
37
+
38
+ const statusPath = path.join(fixtureDir, 'build', 'buildStatus.json');
39
+
40
+ const watchers = spyOnChokidar();
41
+ const { bootedAt, publish, subscribe } = await import('./devEventBus.js');
42
+
43
+ const unsubscribers = [];
44
+
45
+ function listen(send) {
46
+ const unsubscribe = subscribe(send);
47
+ unsubscribers.push(unsubscribe);
48
+ return unsubscribe;
49
+ }
50
+
51
+ // The bus starts its watcher with the first subscriber, so a test waits for
52
+ // that watcher to be ready before it writes the file.
53
+ function watcherReady() {
54
+ return watchers.at(-1).ready;
55
+ }
56
+
57
+ // Resolves with the next event of the given type, so file-watcher tests wait
58
+ // for the real fs event instead of sleeping a guessed interval.
59
+ function nextEvent(type, { timeout = 60000 } = {}) {
60
+ return new Promise((resolve, reject) => {
61
+ const timer = setTimeout(() => {
62
+ unsubscribe();
63
+ reject(new Error(`No ${type} event within ${timeout}ms.`));
64
+ }, timeout);
65
+ const unsubscribe = listen((event) => {
66
+ if (event.type !== type) return;
67
+ clearTimeout(timer);
68
+ unsubscribe();
69
+ resolve(event);
70
+ });
71
+ });
72
+ }
73
+
74
+ async function writeUntilSeen({ pending, content }) {
75
+ fs.writeFileSync(statusPath, content);
76
+ const retry = setInterval(() => fs.writeFileSync(statusPath, content), 1000);
77
+ try {
78
+ return await pending;
79
+ } finally {
80
+ clearInterval(retry);
81
+ }
82
+ }
83
+
84
+ function sleep(ms) {
85
+ return new Promise((resolve) => setTimeout(resolve, ms));
86
+ }
87
+
88
+ // fsevents can report a write made just before the watcher attached, so a
89
+ // file that must already exist when a test subscribes is flushed through
90
+ // before the test subscribes.
91
+ async function writeExistingStatus(status) {
92
+ fs.writeFileSync(statusPath, JSON.stringify(status));
93
+ await flushFsEvents();
94
+ }
95
+
96
+ afterEach(async () => {
97
+ unsubscribers.splice(0).forEach((unsubscribe) => unsubscribe());
98
+ await Promise.all(watchers.map(({ watcher }) => watcher.close()));
99
+ if (fs.existsSync(statusPath)) fs.rmSync(statusPath);
100
+ // The removal must not reach the next test's watcher.
101
+ await flushFsEvents();
102
+ });
103
+
104
+ afterAll(() => {
105
+ process.chdir(originalCwd);
106
+ fs.rmSync(fixtureDir, { recursive: true, force: true });
107
+ });
108
+
109
+ test('bootedAt is an ISO timestamp fixed at module load', () => {
110
+ expect(new Date(bootedAt).toISOString()).toEqual(bootedAt);
111
+ });
112
+
113
+ test('subscribe delivers published events with an ISO timestamp and unsubscribe stops delivery', () => {
114
+ const send = jest.fn();
115
+ const unsubscribe = listen(send);
116
+
117
+ publish({ type: 'client_error', message: 'boom' });
118
+ expect(send).toHaveBeenCalledTimes(1);
119
+ const [event] = send.mock.calls[0];
120
+ expect(event).toMatchObject({ type: 'client_error', message: 'boom' });
121
+ expect(new Date(event.timestamp).toISOString()).toEqual(event.timestamp);
122
+
123
+ unsubscribe();
124
+ publish({ type: 'client_error', message: 'after unsubscribe' });
125
+ expect(send).toHaveBeenCalledTimes(1);
126
+ });
127
+
128
+ test('publish keeps the entry timestamp when the entry carries one', () => {
129
+ const send = jest.fn();
130
+ listen(send);
131
+ publish({ type: 'server_error', timestamp: '2026-01-02T03:04:05.000Z', message: 'x' });
132
+ expect(send.mock.calls[0][0].timestamp).toEqual('2026-01-02T03:04:05.000Z');
133
+ });
134
+
135
+ test('publish rejects an unknown event type', () => {
136
+ expect(() => publish({ type: 'nope' })).toThrow(
137
+ 'devEventBus event type must be one of build, client_error, server_error, restart. Received "nope".'
138
+ );
139
+ });
140
+
141
+ test('subscribe rejects a non-function send', () => {
142
+ expect(() => subscribe('not a function')).toThrow(
143
+ 'devEventBus.subscribe requires a "send" function. Received "not a function".'
144
+ );
145
+ });
146
+
147
+ test('a subscriber that throws is removed and the others still receive', () => {
148
+ const throwing = jest.fn(() => {
149
+ throw new Error('stream gone');
150
+ });
151
+ const healthy = jest.fn();
152
+ listen(throwing);
153
+ listen(healthy);
154
+
155
+ publish({ type: 'client_error', message: 'first' });
156
+ publish({ type: 'client_error', message: 'second' });
157
+
158
+ expect(throwing).toHaveBeenCalledTimes(1);
159
+ expect(healthy).toHaveBeenCalledTimes(2);
160
+ });
161
+
162
+ test('a subscriber whose send rejects is removed after the rejection settles', async () => {
163
+ const rejecting = jest.fn(() => Promise.reject(new Error('stream gone')));
164
+ const healthy = jest.fn(() => Promise.resolve());
165
+ listen(rejecting);
166
+ listen(healthy);
167
+
168
+ publish({ type: 'client_error', message: 'first' });
169
+ await sleep(0);
170
+ publish({ type: 'client_error', message: 'second' });
171
+
172
+ expect(rejecting).toHaveBeenCalledTimes(1);
173
+ expect(healthy).toHaveBeenCalledTimes(2);
174
+ });
175
+
176
+ test('a buildStatus.json write publishes a build event carrying the stale flag when the build failed', async () => {
177
+ const pending = nextEvent('build');
178
+ await watcherReady();
179
+ // buildStatus.json does not exist yet, so the watcher watches its directory
180
+ // for it, and that watch can start a moment after ready. The same write is
181
+ // repeated until one is seen.
182
+ const event = await writeUntilSeen({
183
+ pending,
184
+ content: JSON.stringify({
185
+ status: 'error',
186
+ timestamp: '2026-02-03T04:05:06.000Z',
187
+ errors: [{ message: 'Block type "Buton" not found.' }],
188
+ warnings: [],
189
+ }),
190
+ });
191
+ expect(event).toMatchObject({
192
+ type: 'build',
193
+ status: 'error',
194
+ errorCount: 1,
195
+ warningCount: 0,
196
+ errors: [{ message: 'Block type "Buton" not found.' }],
197
+ warnings: [],
198
+ stale: true,
199
+ staleSince: '2026-02-03T04:05:06.000Z',
200
+ });
201
+ expect(event.staleReason).toContain('The last build failed.');
202
+ });
203
+
204
+ test('a successful buildStatus.json write publishes a build event that is not stale', async () => {
205
+ await writeExistingStatus({ status: 'error', errors: [{}], warnings: [] });
206
+ const pending = nextEvent('build');
207
+ await watcherReady();
208
+ fs.writeFileSync(
209
+ statusPath,
210
+ JSON.stringify({
211
+ status: 'ok',
212
+ timestamp: '2026-02-03T04:06:00.000Z',
213
+ errors: [],
214
+ warnings: [{ message: 'Unused block.' }],
215
+ })
216
+ );
217
+
218
+ const event = await pending;
219
+ expect(event).toMatchObject({
220
+ type: 'build',
221
+ status: 'ok',
222
+ errorCount: 0,
223
+ warningCount: 1,
224
+ stale: false,
225
+ staleSince: null,
226
+ });
227
+ expect(event).not.toHaveProperty('staleReason');
228
+ });
229
+
230
+ test('removing buildStatus.json publishes nothing', async () => {
231
+ await writeExistingStatus({ status: 'ok', errors: [], warnings: [] });
232
+ const send = jest.fn();
233
+ listen(send);
234
+ const { watcher } = watchers.at(-1);
235
+ await watcherReady();
236
+ // The bus handles a watcher event synchronously, so once the watcher has
237
+ // reported the removal, anything the bus publishes for it has been sent.
238
+ const removed = new Promise((resolve, reject) => {
239
+ const timer = setTimeout(
240
+ () => reject(new Error('The watcher did not report the removal within 60000ms.')),
241
+ 60000
242
+ );
243
+ watcher.on('unlink', (filePath) => {
244
+ if (filePath !== statusPath) return;
245
+ clearTimeout(timer);
246
+ resolve();
247
+ });
248
+ });
249
+
250
+ fs.rmSync(statusPath);
251
+ await removed;
252
+
253
+ expect(send).not.toHaveBeenCalled();
254
+ });
255
+
256
+ test('the watcher closes when the last subscriber leaves so a later write publishes nothing', async () => {
257
+ const send = jest.fn();
258
+ const unsubscribe = listen(send);
259
+ const { watcher } = watchers.at(-1);
260
+ await watcherReady();
261
+ unsubscribe();
262
+ expect(watcher.closed).toBe(true);
263
+
264
+ fs.writeFileSync(statusPath, JSON.stringify({ status: 'ok', errors: [], warnings: [] }));
265
+ // Every write so far has been handed out by the OS once this returns.
266
+ await flushFsEvents();
267
+
268
+ expect(send).not.toHaveBeenCalled();
269
+ });