@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.
- package/client/App.jsx +52 -24
- package/client/Inspector.jsx +73 -30
- package/client/JourneyObserver.jsx +47 -0
- package/client/Page.jsx +54 -22
- package/client/Recorder.jsx +73 -0
- package/client/Routing.jsx +37 -13
- package/client/clientBuildImports.test.mjs +2 -0
- package/client/devStreamConnections.test.mjs +40 -0
- package/client/feedback/FeedbackMount.jsx +2 -2
- package/client/feedback/FeedbackOverlay.jsx +7 -4
- package/client/feedback/feedbackStyles.js +3 -0
- package/client/journeyObserver/startJourneyObserver.js +101 -0
- package/client/journeyObserver/startJourneyObserver.test.mjs +149 -0
- package/client/openInEditor/OpenInEditorListener.jsx +2 -0
- package/client/recorder/buildEngineRecord.js +37 -0
- package/client/recorder/buildInteractionRecord.js +64 -0
- package/client/recorder/buildTraceEvent.js +57 -0
- package/client/recorder/buildTraceEvent.test.mjs +81 -0
- package/client/recorder/chunkRecords.js +69 -0
- package/client/recorder/chunkRecords.test.mjs +59 -0
- package/client/recorder/collectEventActions.js +30 -0
- package/client/recorder/collectEventEndpoints.js +40 -0
- package/client/recorder/collectEventRequests.js +39 -0
- package/client/recorder/createPairingBuffer.js +187 -0
- package/client/recorder/createPairingBuffer.test.mjs +405 -0
- package/client/recorder/createPasswordRedactor.js +84 -0
- package/client/recorder/createRecordBatcher.js +54 -0
- package/client/recorder/createRecordBatcher.test.mjs +76 -0
- package/client/recorder/createRecorder.js +216 -0
- package/client/recorder/createRecorder.test.mjs +318 -0
- package/client/recorder/createRecordingSession.js +61 -0
- package/client/recorder/createRecordingSession.test.mjs +72 -0
- package/client/recorder/diffStateWrites.js +77 -0
- package/client/recorder/diffStateWrites.test.mjs +66 -0
- package/client/recorder/isEventCall.js +30 -0
- package/client/recorder/jsonType.js +39 -0
- package/client/recorder/keyChord.js +47 -0
- package/client/recorder/keyChord.test.mjs +50 -0
- package/client/recorder/sendRecords.js +40 -0
- package/client/recorder/toJsonSafe.js +29 -0
- package/lib/client/BuildingPage.jsx +3 -1
- package/lib/client/ErrorBar.jsx +17 -13
- package/lib/client/RedirectingPage.jsx +56 -0
- package/lib/client/auth/Auth.jsx +11 -6
- package/lib/client/auth/AuthConfigured.jsx +222 -36
- package/lib/client/auth/AuthNotConfigured.js +45 -4
- package/lib/client/auth/authNotConfiguredParity.test.mjs +61 -0
- package/lib/client/auth/authTemplateSync.test.mjs +40 -0
- package/lib/client/auth/betterAuthSessionContract.test.mjs +181 -0
- package/lib/client/auth/createFetchCheckingBuild.js +45 -0
- package/lib/client/bundleBuildId.js +25 -0
- package/lib/client/collectVisibleBlockIds.js +51 -0
- package/lib/client/getCspNonce.js +25 -0
- package/lib/client/getPagePath.js +36 -0
- package/lib/client/getPagePath.test.mjs +51 -0
- package/lib/client/utils/formatErrorsForCopy.js +30 -0
- package/lib/client/utils/formatErrorsForCopy.test.mjs +39 -0
- package/lib/client/utils/getErrorBarColor.js +25 -0
- package/lib/client/utils/getErrorBarColor.test.mjs +34 -0
- package/lib/client/utils/request.js +8 -1
- package/lib/client/utils/usePageConfig.js +89 -25
- package/lib/client/utils/usePageConfig.test.mjs +198 -13
- package/lib/configTree/buildConfigTree.mjs +58 -0
- package/lib/configTree/buildConfigTree.test.mjs +77 -0
- package/lib/configTree/collectConfigText.mjs +101 -0
- package/lib/configTree/collectConfigText.test.mjs +116 -0
- package/lib/configTree/prepareScratchServer.mjs +46 -0
- package/lib/configTree/runConfigTreeBuild.mjs +88 -0
- package/lib/configTree/runConfigTreeBuild.skipPluginChecks.test.mjs +74 -0
- package/lib/configTree/runConfigTreeBuild.test.mjs +202 -0
- package/lib/docs/JourneyStepError.js +30 -0
- package/lib/docs/acquireBrowserSlot.js +30 -0
- package/lib/docs/addNestedEndpoints.js +54 -0
- package/lib/docs/addNestedEndpoints.test.mjs +185 -0
- package/lib/docs/addWriteFlags.js +77 -0
- package/lib/docs/buildEditedPages.js +50 -0
- package/lib/docs/buildWaitHeader.js +21 -0
- package/lib/docs/captureAnnotatedScreenshot.js +23 -12
- package/lib/docs/checkAgentFiles.js +148 -0
- package/lib/docs/checkAgentFiles.test.mjs +225 -0
- package/lib/docs/checkStepReferences.js +239 -0
- package/lib/docs/checkStepReferences.test.mjs +173 -0
- package/lib/docs/checkpointStore.js +3 -0
- package/lib/docs/clientErrorStore.js +18 -1
- package/lib/docs/clientErrorStore.test.mjs +46 -0
- package/lib/docs/collectExercised.js +52 -0
- package/lib/docs/createBrowserLifecycle.js +102 -0
- package/lib/docs/createBrowserLifecycle.test.mjs +212 -0
- package/lib/docs/createDocsMcpServer.js +345 -388
- package/lib/docs/createDocsMcpServer.test.mjs +644 -4
- package/lib/docs/createDocsMcpServerRunJourney.test.mjs +148 -0
- package/lib/docs/createJourneyActors.chromium.test.mjs +227 -0
- package/lib/docs/createJourneyActors.js +213 -0
- package/lib/docs/createJourneyActors.test.mjs +79 -0
- package/lib/docs/createJourneyObservations.js +80 -0
- package/lib/docs/createLeftOriginError.js +26 -0
- package/lib/docs/createNetworkCounter.js +101 -0
- package/lib/docs/dataSets/checkSharedCollectionIds.js +78 -0
- package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
- package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +727 -0
- package/lib/docs/dataSets/describeDataSetResult.js +25 -0
- package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
- package/lib/docs/dataSets/describeTargetKey.js +29 -0
- package/lib/docs/dataSets/deserializeFixture.js +34 -0
- package/lib/docs/dataSets/drainSessionWork.js +37 -0
- package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
- package/lib/docs/dataSets/duplicateIndexName.js +24 -0
- package/lib/docs/dataSets/getDataStore.js +106 -0
- package/lib/docs/dataSets/getDataStore.mongodb.test.mjs +63 -0
- package/lib/docs/dataSets/getDataStore.test.mjs +195 -0
- package/lib/docs/dataSets/groupDataSetByCollection.js +52 -0
- package/lib/docs/dataSets/insertFixture.js +57 -0
- package/lib/docs/dataSets/insertGeneratedDocuments.js +41 -0
- package/lib/docs/dataSets/isDataStoreGone.js +29 -0
- package/lib/docs/dataSets/loadDataSetCollection.js +42 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.js +52 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.test.mjs +59 -0
- package/lib/docs/dataSets/openDataSession.js +133 -0
- package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +493 -0
- package/lib/docs/dataSets/pickDataStorePort.js +58 -0
- package/lib/docs/dataSets/pickDataStorePort.test.mjs +63 -0
- package/lib/docs/dataSets/readConnectionArtifacts.js +46 -0
- package/lib/docs/dataSets/readDataSession.js +41 -0
- package/lib/docs/dataSets/readDataSession.test.mjs +57 -0
- package/lib/docs/dataSets/readDataSet.js +83 -0
- package/lib/docs/dataSets/readDataSet.test.mjs +251 -0
- package/lib/docs/dataSets/registerDataStoreShutdown.js +47 -0
- package/lib/docs/dataSets/removeDataStoreFiles.js +32 -0
- package/lib/docs/dataSets/resolveDataSetCollection.js +46 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.js +106 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.test.mjs +130 -0
- package/lib/docs/dataSets/sessionDatabaseName.js +32 -0
- package/lib/docs/dataSets/sweepOrphanDatabases.js +36 -0
- package/lib/docs/dataSets/test/app/lowdefy.yaml +225 -0
- package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
- package/lib/docs/dataSets/test/app/tests/data/shop.yaml +12 -0
- package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
- package/lib/docs/devEventBus.js +136 -0
- package/lib/docs/devEventBus.test.mjs +269 -0
- package/lib/docs/devToolDefinitions.js +529 -0
- package/lib/docs/docSources.js +21 -0
- package/lib/docs/docs.test.mjs +171 -15
- package/lib/docs/docsIndex.test.mjs +377 -0
- package/lib/docs/evalOperator.js +5 -3
- package/lib/docs/evalOperatorHeadless.js +84 -12
- package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
- package/lib/docs/evalOperatorInTab.js +2 -1
- package/lib/docs/findConfig.js +95 -49
- package/lib/docs/fitResponse.js +81 -0
- package/lib/docs/fitResponse.test.mjs +128 -0
- package/lib/docs/formatFeedback.js +26 -5
- package/lib/docs/formatFeedback.test.mjs +48 -0
- package/lib/docs/getAppMap.js +7 -1
- package/lib/docs/getBrowser.js +150 -80
- package/lib/docs/getBrowser.test.mjs +407 -16
- package/lib/docs/getBuildId.js +61 -0
- package/lib/docs/getBuildId.test.mjs +84 -0
- package/lib/docs/getBuildStatus.js +81 -7
- package/lib/docs/getBuildStatus.test.mjs +229 -0
- package/lib/docs/getBuildStatusAfterEdits.js +35 -0
- package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
- package/lib/docs/getCoreDoc.js +9 -1
- package/lib/docs/getDoc.js +100 -0
- package/lib/docs/getDocsIndex.js +152 -0
- package/lib/docs/getHazards.js +115 -0
- package/lib/docs/getHazards.test.mjs +175 -0
- package/lib/docs/getMailOutboxDirectory.js +26 -0
- package/lib/docs/getOverview.js +79 -13
- package/lib/docs/getOverviewBasePath.test.mjs +37 -0
- package/lib/docs/getPageBuildStatus.js +46 -0
- package/lib/docs/getPluginDoc.js +21 -23
- package/lib/docs/getSchema.js +33 -1
- package/lib/docs/getStaleStatus.js +36 -0
- package/lib/docs/getStaleStatus.test.mjs +74 -0
- package/lib/docs/guardJourneyOrigin.js +39 -0
- package/lib/docs/inspectState.js +5 -3
- package/lib/docs/inspectStateFromTab.js +2 -2
- package/lib/docs/inspectStateHeadless.js +89 -27
- package/lib/docs/inspectStateHeadless.test.mjs +20 -1
- package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
- package/lib/docs/installHeadlessShell.js +123 -0
- package/lib/docs/installHeadlessShell.test.mjs +70 -0
- package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
- package/lib/docs/isHeadlessShellIncomplete.js +34 -0
- package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
- package/lib/docs/isPageReady.js +77 -0
- package/lib/docs/isPageReady.test.mjs +235 -0
- package/lib/docs/isRunErrorEntry.js +25 -0
- package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
- package/lib/docs/journeyRunRecording.js +35 -0
- package/lib/docs/journeyRunRecording.test.mjs +39 -0
- package/lib/docs/keepEventStreamAlive.js +68 -0
- package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
- package/lib/docs/launchBrowser.js +83 -0
- package/lib/docs/launchBrowser.test.mjs +175 -0
- package/lib/docs/listModuleDocEntries.js +69 -0
- package/lib/docs/listPackageDocFiles.js +67 -0
- package/lib/docs/listPluginDocEntries.js +111 -0
- package/lib/docs/listPlugins.js +9 -58
- package/lib/docs/listTypes.js +14 -6
- package/lib/docs/loadFrameworkHazards.js +40 -0
- package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
- package/lib/docs/loadState.js +103 -35
- package/lib/docs/makeModuleManifestDoc.js +174 -0
- package/lib/docs/matchExercisedPages.js +35 -0
- package/lib/docs/matchJourneyRoute.js +69 -0
- package/lib/docs/matchJourneyRoute.test.mjs +178 -0
- package/lib/docs/mergeNetworkSnapshots.js +49 -0
- package/lib/docs/nextJourneySequence.js +30 -0
- package/lib/docs/noBrowserError.js +22 -0
- package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
- package/lib/docs/observe/claimExpectedErrors.js +67 -0
- package/lib/docs/observe/createJourneyAppErrors.js +236 -0
- package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
- package/lib/docs/observe/evaluateInvariants.js +183 -0
- package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
- package/lib/docs/observe/findingKey.js +29 -0
- package/lib/docs/observe/hasNoEffect.js +31 -0
- package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
- package/lib/docs/observe/installStepObserver.js +72 -0
- package/lib/docs/observe/isAppApiUrl.js +33 -0
- package/lib/docs/observe/parseAppApiUrl.js +37 -0
- package/lib/docs/observe/readStepObserver.js +42 -0
- package/lib/docs/observe/relativeSource.js +29 -0
- package/lib/docs/observe/resolveConfigKeySource.js +33 -0
- package/lib/docs/observe/takeJourneyEvents.js +36 -0
- package/lib/docs/observe/toAppErrorFailure.js +61 -0
- package/lib/docs/observe/usesSearchStage.js +45 -0
- package/lib/docs/observe/waitForClientErrorReports.js +31 -0
- package/lib/docs/observe/watchJourneyContext.js +75 -0
- package/lib/docs/observe/withAppErrors.js +31 -0
- package/lib/docs/observe/withAppErrors.test.mjs +101 -0
- package/lib/docs/openJourney.js +94 -0
- package/lib/docs/openJourneyEmail.js +41 -0
- package/lib/docs/openJourneyEmail.test.mjs +187 -0
- package/lib/docs/pageBlocks.js +47 -0
- package/lib/docs/pageBuildStatus.test.mjs +103 -0
- package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
- package/lib/docs/rails/authEngineActions.js +53 -0
- package/lib/docs/rails/authEngineActions.test.mjs +69 -0
- package/lib/docs/rails/authEngineMethods.js +52 -0
- package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
- package/lib/docs/rails/checkAuthActionsRan.js +66 -0
- package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
- package/lib/docs/rails/checkClickRails.js +56 -0
- package/lib/docs/rails/createDataSetRails.js +82 -0
- package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
- package/lib/docs/rails/findExternalBlocks.js +147 -0
- package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
- package/lib/docs/rails/installAuthActionRecorder.js +56 -0
- package/lib/docs/rails/readAuthActionRecorder.js +32 -0
- package/lib/docs/rails/readBuiltArtifact.js +30 -0
- package/lib/docs/readConnectionPlugins.js +26 -0
- package/lib/docs/readDevAuthMode.js +32 -0
- package/lib/docs/readDocEntry.js +40 -0
- package/lib/docs/readDocTitle.js +26 -0
- package/lib/docs/readJourneyEmailMatch.js +57 -0
- package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
- package/lib/docs/readJourneySession.js +56 -0
- package/lib/docs/readMailOutbox.js +37 -0
- package/lib/docs/readPagePath.js +27 -0
- package/lib/docs/readPluginPackages.js +99 -0
- package/lib/docs/readProxyBuildWait.js +38 -0
- package/lib/docs/readRoutineSteps.js +45 -0
- package/lib/docs/registerShownTextEngine.js +118 -0
- package/lib/docs/requestRestart.js +30 -0
- package/lib/docs/requestRestart.test.mjs +49 -0
- package/lib/docs/resolvePageInstance.js +46 -0
- package/lib/docs/resolvePageInstance.test.mjs +81 -0
- package/lib/docs/resolveSource.js +11 -7
- package/lib/docs/resolveSource.test.mjs +1 -1
- package/lib/docs/resolveToolCaller.js +62 -0
- package/lib/docs/reviewPage.js +51 -0
- package/lib/docs/reviewPageBuilds.js +56 -0
- package/lib/docs/reviewPageBuilds.test.mjs +207 -0
- package/lib/docs/runCheck.js +75 -0
- package/lib/docs/runCheck.test.mjs +91 -0
- package/lib/docs/runCheckChild.js +49 -0
- package/lib/docs/runEndpoint.js +185 -0
- package/lib/docs/runEndpoint.test.mjs +509 -0
- package/lib/docs/runErrorBuffers.js +99 -0
- package/lib/docs/runErrorBuffers.test.mjs +95 -0
- package/lib/docs/runJourney.js +347 -0
- package/lib/docs/runJourney.test.mjs +2383 -0
- package/lib/docs/runJourneyAsUsers.js +44 -0
- package/lib/docs/runJourneyDataSet.test.mjs +274 -0
- package/lib/docs/runJourneySteps.js +1320 -0
- package/lib/docs/runRequest.js +100 -59
- package/lib/docs/runRequest.test.mjs +190 -0
- package/lib/docs/saveAnnotatedScreenshot.js +1 -1
- package/lib/docs/scaffoldPage.js +3 -1
- package/lib/docs/screenshotPage.js +163 -11
- package/lib/docs/screenshotPage.test.mjs +263 -1
- package/lib/docs/screenshotPageClip.test.mjs +4 -0
- package/lib/docs/searchDocs.js +82 -30
- package/lib/docs/searchDocs.test.mjs +100 -0
- package/lib/docs/searchIcons.js +90 -0
- package/lib/docs/searchIcons.test.mjs +127 -0
- package/lib/docs/selectExercised.js +39 -0
- package/lib/docs/selectFinalState.js +66 -0
- package/lib/docs/selectFinalState.test.mjs +51 -0
- package/lib/docs/serverErrorStore.js +54 -0
- package/lib/docs/serverErrorStore.test.mjs +50 -0
- package/lib/docs/setupTestFixtures.mjs +133 -39
- package/lib/docs/shownTextSelector.js +28 -0
- package/lib/docs/snapshotState.js +13 -5
- package/lib/docs/splitSearchTerms.js +62 -0
- package/lib/docs/splitSearchTerms.test.mjs +50 -0
- package/lib/docs/stateCheckpoints.test.mjs +77 -0
- package/lib/docs/tabAvailable.js +6 -5
- package/lib/docs/tabChannel.js +127 -21
- package/lib/docs/tabChannel.test.mjs +187 -29
- package/lib/docs/unsettledPageNote.js +24 -0
- package/lib/docs/validateJourneyMail.js +36 -0
- package/lib/docs/validateJourneyTimeout.js +38 -0
- package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
- package/lib/docs/validateRecordingParam.js +42 -0
- package/lib/docs/validateStateSelection.js +33 -0
- package/lib/docs/validateStateSelection.test.mjs +33 -0
- package/lib/docs/validateViewport.js +51 -0
- package/lib/docs/validateViewport.test.mjs +54 -0
- package/lib/docs/waitForBuild.js +50 -0
- package/lib/docs/waitForBuild.test.mjs +75 -0
- package/lib/docs/waitForJourneyEmail.js +77 -0
- package/lib/docs/withBrowserSlot.js +45 -0
- package/lib/docs/withBrowserSlot.test.mjs +75 -0
- package/lib/docs/withDataSession.js +53 -0
- package/lib/server/applyDataSetRedirect.js +92 -0
- package/lib/server/applyDataSetRedirect.test.mjs +446 -0
- package/lib/server/applyDataSetRedirectTenantReach.test.mjs +207 -0
- package/lib/server/applyDataSetRedirectWalled.test.mjs +134 -0
- package/lib/server/auth/createSystemContext.js +73 -0
- package/lib/server/auth/{getAuthConfig.js → getAuth.js} +15 -9
- package/lib/server/auth/getHeadlessUser.js +9 -8
- package/lib/server/auth/getHeadlessUser.test.mjs +41 -0
- package/lib/server/auth/getMockUser.js +18 -14
- package/lib/server/auth/{session.js → getStrategies.js} +17 -16
- package/lib/server/auth/headlessUser.js +3 -2
- package/lib/server/auth/journeyActor.js +35 -0
- package/lib/server/auth/resolveHeadlessUser.test.mjs +0 -4
- package/lib/server/checkPageRecord.js +44 -0
- package/lib/server/contextMapBudget.js +24 -0
- package/lib/server/contextMapBudget.test.mjs +98 -0
- package/lib/server/continueJitKeys.js +28 -0
- package/lib/server/continueJitKeys.test.mjs +43 -0
- package/lib/server/createDataSessionEndedError.js +33 -0
- package/lib/server/createDataSetRefusal.js +26 -0
- package/lib/server/createLowdefyContext.js +168 -41
- package/lib/server/createLowdefyContext.test.mjs +331 -8
- package/lib/server/getClientAddress.js +51 -0
- package/lib/server/getClientAddress.test.mjs +56 -0
- package/lib/server/getPageJitEnrichment.test.mjs +29 -11
- package/lib/server/hashConfigContent.js +26 -0
- package/lib/server/importAppCode.test.mjs +57 -0
- package/lib/server/isRoutineStep.js +27 -0
- package/lib/server/jitPageBuilder.js +309 -126
- package/lib/server/jitPageBuilder.skipPluginChecks.test.mjs +78 -0
- package/lib/server/jitPageBuilder.test.mjs +218 -0
- package/lib/server/journeyCookies/JOURNEY_COOKIES.js +37 -0
- package/lib/server/journeyCookies/forwardJourneyCookies.js +46 -0
- package/lib/server/journeyCookies/readJourneyCookie.js +52 -0
- package/lib/server/journeyCookies/writeJourneyCookie.js +46 -0
- package/lib/server/journeyCookies.js +22 -0
- package/lib/server/journeyCookies.test.mjs +145 -0
- package/lib/server/loadDynamicJsMap.js +47 -0
- package/lib/server/log/createHandleError.js +38 -2
- package/lib/server/log/createHandleError.test.mjs +125 -2
- package/lib/server/log/createLogger.js +3 -0
- package/lib/server/log/createLogger.test.mjs +15 -0
- package/lib/server/log/logRequest.js +2 -3
- package/lib/server/mutantDataSetWrappers.test.mjs +115 -0
- package/lib/server/mutants/applyMutant.js +39 -0
- package/lib/server/mutants/artifactPatterns.js +25 -0
- package/lib/server/mutants/buildAnchor.js +45 -0
- package/lib/server/mutants/collectSharedIds.js +41 -0
- package/lib/server/mutants/configPathSegments.js +38 -0
- package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
- package/lib/server/mutants/describeEvent.js +22 -0
- package/lib/server/mutants/enumerateArtifact.js +56 -0
- package/lib/server/mutants/findNodeByKey.js +45 -0
- package/lib/server/mutants/findNodeByPointer.js +49 -0
- package/lib/server/mutants/getArtifactKind.js +32 -0
- package/lib/server/mutants/groupMutantCopies.js +56 -0
- package/lib/server/mutants/hashArtifact.js +25 -0
- package/lib/server/mutants/hashMutantTarget.js +34 -0
- package/lib/server/mutants/listMutants.js +122 -0
- package/lib/server/mutants/listMutants.test.mjs +502 -0
- package/lib/server/mutants/mutantId.js +30 -0
- package/lib/server/mutants/mutantRuns.js +57 -0
- package/lib/server/mutants/mutantRuns.test.mjs +136 -0
- package/lib/server/mutants/operators/dropAction.js +47 -0
- package/lib/server/mutants/operators/dropBlock.js +44 -0
- package/lib/server/mutants/operators/dropPayload.js +52 -0
- package/lib/server/mutants/operators/dropStep.js +44 -0
- package/lib/server/mutants/operators/flipVisible.js +53 -0
- package/lib/server/mutants/operators/index.js +37 -0
- package/lib/server/mutants/operators/retargetLink.js +72 -0
- package/lib/server/mutants/operators/skipValidate.js +77 -0
- package/lib/server/mutants/operators/swapIf.js +112 -0
- package/lib/server/mutants/operators.test.mjs +396 -0
- package/lib/server/mutants/positionFreePath.js +38 -0
- package/lib/server/mutants/removeFromParent.js +28 -0
- package/lib/server/mutants/test/fixtures.mjs +173 -0
- package/lib/server/mutants/test/keyArtifact.mjs +63 -0
- package/lib/server/mutants/validateMutantParam.js +69 -0
- package/lib/server/mutants/validateMutantsBody.js +88 -0
- package/lib/server/mutants/walkArtifact.js +179 -0
- package/lib/server/pageBuildRecords.js +131 -0
- package/lib/server/pageBuildRecords.test.mjs +258 -0
- package/lib/server/pageCache.mjs +22 -7
- package/lib/server/pageCache.test.mjs +22 -7
- package/lib/server/pageRebuilds.test.mjs +543 -0
- package/lib/server/pruneJitMaps.js +36 -0
- package/lib/server/pruneJitMaps.test.mjs +55 -0
- package/lib/server/readInstanceRecord.js +33 -0
- package/lib/server/readInstanceRecord.test.mjs +99 -0
- package/lib/server/readMergedMaps.js +86 -0
- package/lib/server/readMergedMaps.test.mjs +79 -0
- package/lib/server/recording/appendRecords.js +33 -0
- package/lib/server/recording/errorRecordingStamp.js +34 -0
- package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
- package/lib/server/recording/isRecordingEnabled.js +31 -0
- package/lib/server/recording/readRecordingCookie.js +71 -0
- package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
- package/lib/server/recording/recordingCookiePayload.js +33 -0
- package/lib/server/recording/servedBuilds.js +46 -0
- package/lib/server/recording/servedBuilds.test.mjs +42 -0
- package/lib/server/sinkExternalConnections.js +95 -0
- package/lib/server/sinkExternalConnections.test.mjs +135 -0
- package/lib/server/skipStaleMapWrites.js +62 -0
- package/lib/server/skipStaleMapWrites.test.mjs +113 -0
- package/lib/server/startIdleGc.js +67 -0
- package/lib/server/startIdleGc.test.mjs +138 -0
- package/lib/server/testFixtures/importAppCodeTwice.mjs +70 -0
- package/lib/server/trackSessionWork.js +30 -0
- package/lib/vite/createDevServerExclude.js +40 -0
- package/lib/vite/createDevServerExclude.test.mjs +79 -0
- package/manager/getContext.mjs +25 -3
- package/manager/processes/initialBuild.mjs +17 -1
- package/manager/processes/initialBuild.test.mjs +104 -0
- package/manager/processes/lowdefyBuild.mjs +31 -7
- package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +72 -0
- package/manager/processes/lowdefyBuild.test.mjs +136 -1
- package/manager/processes/optimizeDependencies.mjs +59 -0
- package/manager/processes/restartServer.mjs +34 -6
- package/manager/processes/restartServer.test.mjs +162 -0
- package/manager/processes/shutdownServer.mjs +11 -0
- package/manager/processes/shutdownServer.test.mjs +45 -0
- package/manager/processes/startFirstServer.mjs +46 -0
- package/manager/processes/startFirstServer.test.mjs +326 -0
- package/manager/processes/startMailSink.mjs +135 -0
- package/manager/processes/startMailSink.test.mjs +146 -0
- package/manager/processes/startProxy.mjs +129 -12
- package/manager/processes/startProxy.test.mjs +366 -0
- package/manager/processes/startServer.mjs +47 -6
- package/manager/processes/startServer.test.mjs +155 -0
- package/manager/processes/startWatchers.mjs +6 -4
- package/manager/processes/syncServer.mjs +69 -0
- package/manager/processes/syncServer.test.mjs +141 -0
- package/manager/processes/waitForDevServer.mjs +48 -0
- package/manager/processes/waitForDevServer.test.mjs +112 -0
- package/manager/run.mjs +128 -43
- package/manager/utils/BatchChanges.mjs +21 -5
- package/manager/utils/BatchChanges.test.mjs +77 -0
- package/manager/utils/acquireDevInstance.mjs +99 -0
- package/manager/utils/acquireDevInstance.test.mjs +160 -0
- package/manager/utils/createBuildActivity.mjs +67 -0
- package/manager/utils/createBuildActivity.test.mjs +58 -0
- package/manager/utils/createChangeSignal.mjs +39 -0
- package/manager/utils/createChangeSignal.test.mjs +62 -0
- package/manager/utils/createCustomPluginMessagesMap.mjs +23 -33
- package/manager/utils/createCustomPluginTypesMap.mjs +24 -30
- package/manager/utils/createCustomPluginTypesMap.test.mjs +94 -0
- package/manager/utils/createDotPathIgnore.mjs +44 -0
- package/manager/utils/createDotPathIgnore.test.mjs +65 -0
- package/manager/utils/createIdleGc.mjs +51 -0
- package/manager/utils/createIdleGc.test.mjs +77 -0
- package/manager/utils/createRequestActivity.mjs +100 -0
- package/manager/utils/createRequestActivity.test.mjs +121 -0
- package/manager/utils/createServerArtifactTracker.mjs +83 -0
- package/manager/utils/createServerArtifactTracker.test.mjs +78 -0
- package/manager/utils/createServerEnv.mjs +39 -0
- package/manager/utils/filesHaveSameBytes.mjs +41 -0
- package/manager/utils/findBuildFilesOutsideWatch.mjs +59 -0
- package/manager/utils/findBuildFilesOutsideWatch.test.mjs +111 -0
- package/manager/utils/importFresh.mjs +46 -0
- package/manager/utils/importFresh.test.mjs +72 -0
- package/manager/utils/importFreshWorker.mjs +20 -0
- package/manager/utils/killTaggedBrowser.mjs +35 -0
- package/manager/utils/pagePathChanged.mjs +56 -0
- package/manager/utils/pruneRecordings.mjs +85 -0
- package/manager/utils/pruneRecordings.test.mjs +117 -0
- package/manager/utils/publishBuildDirectory.mjs +121 -0
- package/manager/utils/publishBuildDirectory.order.test.mjs +74 -0
- package/manager/utils/publishBuildDirectory.test.mjs +262 -0
- package/manager/utils/readBasePath.mjs +27 -0
- package/manager/utils/readBasePath.test.mjs +43 -0
- package/manager/utils/readBuildStatusWait.mjs +67 -0
- package/manager/utils/readManagerVersion.mjs +30 -0
- package/manager/utils/readManagerVersion.test.mjs +35 -0
- package/manager/utils/readPluginDefinitions.mjs +36 -0
- package/manager/utils/resolveDevAuthUrl.mjs +58 -0
- package/manager/utils/resolveDevAuthUrl.test.mjs +107 -0
- package/manager/utils/resolvePorts.mjs +49 -0
- package/manager/utils/setupWatcher.mjs +19 -11
- package/manager/utils/startRecordingPruner.mjs +45 -0
- package/manager/utils/startRecordingPruner.test.mjs +66 -0
- package/manager/utils/waitForChildExit.mjs +53 -0
- package/manager/utils/waitForChildExit.test.mjs +70 -0
- package/manager/utils/waitForServer.mjs +44 -0
- package/manager/utils/writeBuildStatus.mjs +1 -1
- package/manager/watchers/createPluginSourceIgnore.mjs +77 -0
- package/manager/watchers/envWatcher.mjs +9 -2
- package/manager/watchers/envWatcher.test.mjs +87 -0
- package/manager/watchers/listServerSidePackages.mjs +47 -0
- package/manager/watchers/listServerSidePackages.test.mjs +38 -0
- package/manager/watchers/lowdefyBuildWatcher.mjs +106 -22
- package/manager/watchers/lowdefyBuildWatcher.test.mjs +309 -0
- package/manager/watchers/pluginSourceWatcher.mjs +79 -0
- package/manager/watchers/pluginSourceWatcher.test.mjs +222 -0
- package/manager/watchers/restartRequestWatcher.mjs +71 -0
- package/manager/watchers/restartRequestWatcher.test.mjs +111 -0
- package/manager/watchers/selectLocalPluginPackages.mjs +48 -0
- package/manager/watchers/selectLocalPluginPackages.test.mjs +73 -0
- package/manager/watchers/serverPackageWatcher.mjs +38 -0
- package/package.json +60 -47
- package/package.original.json +60 -47
- package/src/app.js +131 -20
- package/src/html/renderDevPage.js +63 -11
- package/src/html/renderDevPage.test.mjs +138 -0
- package/src/lib/getRequestPath.js +29 -0
- package/src/middleware/createSameOriginGuard.js +37 -0
- package/src/middleware/createSameOriginGuard.test.mjs +102 -0
- package/src/middleware/errorHandler.js +51 -1
- package/src/middleware/errorHandler.test.mjs +173 -3
- package/src/middleware/isRebindingSafeHost.js +44 -0
- package/src/middleware/isRebindingSafeHost.test.mjs +32 -0
- package/src/middleware/localDevToolsOnly.js +38 -0
- package/src/middleware/localDevToolsOnly.test.mjs +61 -0
- package/src/middleware/noStore.js +27 -0
- package/src/middleware/noStore.test.mjs +48 -0
- package/src/middleware/requestTimeout.js +38 -0
- package/src/middleware/staleFlag.js +76 -0
- package/src/middleware/staleFlag.test.mjs +127 -0
- package/src/middleware/stampCaller.js +38 -0
- package/src/middleware/stampCaller.test.mjs +64 -0
- package/src/routes/agent.js +15 -3
- package/src/routes/auth.js +29 -20
- package/src/routes/auth.test.mjs +30 -56
- package/src/routes/clientError.js +13 -13
- package/src/routes/cron.js +3 -1
- package/src/routes/cronForward.js +3 -1
- package/src/routes/detached.js +22 -9
- package/src/routes/devInspect.js +17 -8
- package/src/routes/devInspect.test.mjs +44 -7
- package/src/routes/devRecording.js +113 -0
- package/src/routes/devRecording.test.mjs +253 -0
- package/src/routes/docs/buildStatus.js +10 -2
- package/src/routes/docs/buildStatus.test.mjs +46 -0
- package/src/routes/docs/content.js +5 -3
- package/src/routes/docs/evalOperator.js +15 -1
- package/src/routes/docs/events.js +50 -0
- package/src/routes/docs/events.test.mjs +117 -0
- package/src/routes/docs/find.js +9 -1
- package/src/routes/docs/icons.js +30 -0
- package/src/routes/docs/inspectState.js +16 -1
- package/src/routes/docs/journey.js +194 -0
- package/src/routes/docs/journey.test.mjs +401 -0
- package/src/routes/docs/journeyRefusals.test.mjs +71 -0
- package/src/routes/docs/loadState.js +4 -2
- package/src/routes/docs/mcp.js +34 -3
- package/src/routes/docs/mcp.test.mjs +130 -0
- package/src/routes/docs/mutants.js +78 -0
- package/src/routes/docs/mutants.test.mjs +180 -0
- package/src/routes/docs/parsePathParamsParam.js +41 -0
- package/src/routes/docs/parsePathParamsParam.test.mjs +42 -0
- package/src/routes/docs/parseUserParam.js +27 -21
- package/src/routes/docs/parseUserParam.test.mjs +17 -5
- package/src/routes/docs/restart.js +35 -0
- package/src/routes/docs/restart.test.mjs +64 -0
- package/src/routes/docs/runEndpoint.js +54 -0
- package/src/routes/docs/runEndpoint.test.mjs +165 -0
- package/src/routes/docs/runRequest.js +25 -8
- package/src/routes/docs/runRequest.test.mjs +92 -0
- package/src/routes/docs/screenshot.js +50 -2
- package/src/routes/docs/screenshot.test.mjs +117 -0
- package/src/routes/docs/search.js +15 -2
- package/src/routes/docs/snapshotState.js +17 -3
- package/src/routes/endpoints.js +27 -26
- package/src/routes/endpointsTextAnswer.test.mjs +77 -0
- package/src/routes/feedback.js +10 -13
- package/src/routes/jitPage.js +99 -35
- package/src/routes/jitPage.test.mjs +248 -0
- package/src/routes/mcp.js +82 -5
- package/src/routes/mcp.test.mjs +215 -0
- package/src/routes/mcpMemberToken.test.mjs +125 -0
- package/src/routes/mcpProtectedResourceMetadata.js +34 -0
- package/src/routes/mcpProtectedResourceMetadata.test.mjs +65 -0
- package/src/routes/mountDevPageRoutes.js +25 -0
- package/src/routes/mountDevPageRoutes.test.mjs +30 -0
- package/src/routes/mountOauthDiscovery.js +44 -0
- package/src/routes/mountOauthDiscovery.test.mjs +79 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.js +46 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.test.mjs +62 -0
- package/src/routes/reload.js +16 -0
- package/src/routes/request.js +6 -4
- package/src/routes/request.test.mjs +9 -5
- package/src/routes/requestBody.test.mjs +159 -0
- package/src/routes/usage.js +6 -2
- package/src/routes/user.js +27 -0
- package/src/routes/websocket.js +29 -8
- package/src/routes/websocket.test.mjs +65 -0
- package/src/routes/wellKnownFallback.js +30 -0
- package/src/routes/wellKnownFallback.test.mjs +32 -0
- package/src/websocket/devWebSocket.js +41 -26
- package/src/websocket/devWebSocket.test.mjs +157 -0
- package/vite.config.js +53 -23
- package/lib/server/auth/getDevSession.js +0 -70
- package/lib/server/auth/getDevSession.test.mjs +0 -138
- package/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
- package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
- package/manager/watchers/moduleBuildWatcher.mjs +0 -79
- package/manager/watchers/serverArtifactWatcher.mjs +0 -120
- package/src/routes/apiPage.js +0 -37
|
@@ -0,0 +1,25 @@
|
|
|
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 { countDataSetDocuments } from '@lowdefy/node-utils';
|
|
18
|
+
|
|
19
|
+
// The `data` a journey result carries: which data set it ran on, how many documents it loaded and
|
|
20
|
+
// how long the load took, which lowdefy test prints.
|
|
21
|
+
function describeDataSetResult({ dataSet, loadMs }) {
|
|
22
|
+
return { name: dataSet.name, loadMs, documents: countDataSetDocuments({ dataSet }) };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default describeDataSetResult;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
function withoutMarkers(value) {
|
|
20
|
+
if (!type.isObject(value)) return value;
|
|
21
|
+
return Object.fromEntries(Object.entries(value).filter(([key]) => !key.startsWith('~')));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Which database a connection reads, as far as the built artifact can tell before any request:
|
|
25
|
+
// a literal databaseName (or none) and the databaseUri's _secret name (or literal). Returns null
|
|
26
|
+
// when either is computed per request, since such a connection cannot be compared.
|
|
27
|
+
function describeDatabaseTarget({ properties }) {
|
|
28
|
+
const databaseName = properties?.databaseName;
|
|
29
|
+
if (!type.isUndefined(databaseName) && !type.isString(databaseName)) return null;
|
|
30
|
+
const databaseUri = withoutMarkers(properties?.databaseUri);
|
|
31
|
+
let uri;
|
|
32
|
+
if (type.isString(databaseUri)) {
|
|
33
|
+
uri = `literal:${databaseUri}`;
|
|
34
|
+
} else if (
|
|
35
|
+
type.isObject(databaseUri) &&
|
|
36
|
+
Object.keys(databaseUri).length === 1 &&
|
|
37
|
+
type.isString(databaseUri._secret)
|
|
38
|
+
) {
|
|
39
|
+
uri = `secret:${databaseUri._secret}`;
|
|
40
|
+
} else {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return { databaseName: databaseName ?? null, uri };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default describeDatabaseTarget;
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
import describeDatabaseTarget from './describeDatabaseTarget.js';
|
|
20
|
+
|
|
21
|
+
// The key a data session gives each database target its own database by, or null when the target
|
|
22
|
+
// is computed per request.
|
|
23
|
+
function describeTargetKey({ properties }) {
|
|
24
|
+
const target = describeDatabaseTarget({ properties });
|
|
25
|
+
if (type.isNone(target)) return null;
|
|
26
|
+
return JSON.stringify([target.uri, target.databaseName]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default describeTargetKey;
|
|
@@ -0,0 +1,34 @@
|
|
|
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 { ObjectId } from 'mongodb';
|
|
18
|
+
import { serializer, type } from '@lowdefy/helpers';
|
|
19
|
+
|
|
20
|
+
function reviver(_, value) {
|
|
21
|
+
if (type.isObject(value) && type.isString(value._oid)) {
|
|
22
|
+
return ObjectId.createFromHexString(value._oid);
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// A fixture document as the database stores it: `~d` markers become Dates (the serializer) and
|
|
28
|
+
// { _oid: '<hex>' } becomes an ObjectId, the convention connection-mongodb's serialize.js uses for
|
|
29
|
+
// request payloads.
|
|
30
|
+
function deserializeFixture(document) {
|
|
31
|
+
return serializer.copy(document, { reviver });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default deserializeFixture;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Waits until every promise in `work` has settled, including any added while it waits (a background
|
|
18
|
+
// task can schedule another), or until `timeoutMs` passes. Returns true when the work drained.
|
|
19
|
+
async function drainSessionWork({ work, timeoutMs }) {
|
|
20
|
+
let timer;
|
|
21
|
+
const timedOut = new Promise((resolve) => {
|
|
22
|
+
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
23
|
+
});
|
|
24
|
+
async function settleAll() {
|
|
25
|
+
while (work.size > 0) {
|
|
26
|
+
await Promise.allSettled([...work]);
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
return await Promise.race([settleAll(), timedOut]);
|
|
32
|
+
} finally {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default drainSessionWork;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { jest } from '@jest/globals';
|
|
18
|
+
|
|
19
|
+
import drainSessionWork from './drainSessionWork.js';
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
jest.useRealTimers();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('drainSessionWork resolves true once every promise, including one added while waiting, settles', async () => {
|
|
26
|
+
const work = new Set();
|
|
27
|
+
let releaseFirst;
|
|
28
|
+
let releaseSecond;
|
|
29
|
+
const second = new Promise((resolve) => {
|
|
30
|
+
releaseSecond = resolve;
|
|
31
|
+
});
|
|
32
|
+
const first = new Promise((resolve) => {
|
|
33
|
+
releaseFirst = resolve;
|
|
34
|
+
}).then(() => {
|
|
35
|
+
work.delete(first);
|
|
36
|
+
work.add(second);
|
|
37
|
+
});
|
|
38
|
+
second.then(() => work.delete(second));
|
|
39
|
+
work.add(first);
|
|
40
|
+
const draining = drainSessionWork({ work, timeoutMs: 30000 });
|
|
41
|
+
releaseFirst();
|
|
42
|
+
await Promise.resolve();
|
|
43
|
+
releaseSecond();
|
|
44
|
+
await expect(draining).resolves.toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('drainSessionWork resolves true at once with no work', async () => {
|
|
48
|
+
await expect(drainSessionWork({ work: new Set(), timeoutMs: 30000 })).resolves.toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('drainSessionWork resolves false after the timeout when work never settles', async () => {
|
|
52
|
+
jest.useFakeTimers({ doNotFake: ['performance'] });
|
|
53
|
+
const work = new Set([new Promise(() => {})]);
|
|
54
|
+
const draining = drainSessionWork({ work, timeoutMs: 30000 });
|
|
55
|
+
let done = false;
|
|
56
|
+
draining.then(() => {
|
|
57
|
+
done = true;
|
|
58
|
+
});
|
|
59
|
+
jest.advanceTimersByTime(29999);
|
|
60
|
+
await Promise.resolve();
|
|
61
|
+
expect(done).toBe(false);
|
|
62
|
+
jest.advanceTimersByTime(1);
|
|
63
|
+
await expect(draining).resolves.toBe(false);
|
|
64
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// The index a duplicate key error names, from the server's message ("... index: number_1 dup
|
|
18
|
+
// key: ..."), or "unknown".
|
|
19
|
+
function duplicateIndexName(message) {
|
|
20
|
+
const match = /index: (\S+)/.exec(message ?? '');
|
|
21
|
+
return match === null ? 'unknown' : match[1];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default duplicateIndexName;
|
|
@@ -0,0 +1,106 @@
|
|
|
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 { MongoClient } from 'mongodb';
|
|
18
|
+
import { MongoMemoryReplSet } from 'mongodb-memory-server-core';
|
|
19
|
+
import { DryMongoBinary } from 'mongodb-memory-server-core/lib/util/DryMongoBinary.js';
|
|
20
|
+
|
|
21
|
+
import pickDataStorePort from './pickDataStorePort.js';
|
|
22
|
+
import registerDataStoreShutdown from './registerDataStoreShutdown.js';
|
|
23
|
+
import removeDataStoreFiles from './removeDataStoreFiles.js';
|
|
24
|
+
|
|
25
|
+
// One store per dev server process. Kept on globalThis because the journey route that opens a data
|
|
26
|
+
// session and the API context that reads it may load as separate module instances (Vite's SSR
|
|
27
|
+
// module graph and Node's), like the journey actor token.
|
|
28
|
+
const STORE_KEY = Symbol.for('lowdefy.devServer.dataStore');
|
|
29
|
+
|
|
30
|
+
// The store is a local mongod: a client that cannot reach it within this long is looking at a dead
|
|
31
|
+
// store, and the journey should say so rather than wait the driver's default 30 s.
|
|
32
|
+
const SERVER_SELECTION_TIMEOUT_MS = 5000;
|
|
33
|
+
|
|
34
|
+
async function announceDownload() {
|
|
35
|
+
const options = await DryMongoBinary.generateOptions();
|
|
36
|
+
const binary = await DryMongoBinary.locateBinary(options);
|
|
37
|
+
if (binary === undefined) {
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.info(`Downloading MongoDB ${options.version} for journey data sets (once).`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// A replica set, not a standalone server: transactions and change streams need one, and the app's
|
|
44
|
+
// own cluster is one. MONGOMS_VERSION, read by mongodb-memory-server, pins the MongoDB version.
|
|
45
|
+
async function startDataStore() {
|
|
46
|
+
await announceDownload();
|
|
47
|
+
const replSet = await MongoMemoryReplSet.create({
|
|
48
|
+
instanceOpts: [{ port: await pickDataStorePort() }],
|
|
49
|
+
replSet: { count: 1, storageEngine: 'wiredTiger' },
|
|
50
|
+
});
|
|
51
|
+
const uri = replSet.getUri();
|
|
52
|
+
const client = new MongoClient(uri, { serverSelectionTimeoutMS: SERVER_SELECTION_TIMEOUT_MS });
|
|
53
|
+
try {
|
|
54
|
+
await client.connect();
|
|
55
|
+
} catch (error) {
|
|
56
|
+
await replSet.stop({ doCleanup: true });
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
// Past the awaits above, getDataStore has cached the promise this call returned.
|
|
60
|
+
const storePromise = globalThis[STORE_KEY];
|
|
61
|
+
let unregisterShutdown;
|
|
62
|
+
let stopping;
|
|
63
|
+
// Stops mongod and removes its temporary database directory. A later getDataStore() starts a new
|
|
64
|
+
// store; the cache is cleared only while it still holds this one.
|
|
65
|
+
function stop() {
|
|
66
|
+
stopping =
|
|
67
|
+
stopping ??
|
|
68
|
+
(async () => {
|
|
69
|
+
if (globalThis[STORE_KEY] === storePromise) {
|
|
70
|
+
delete globalThis[STORE_KEY];
|
|
71
|
+
}
|
|
72
|
+
await client.close().catch(() => {});
|
|
73
|
+
// stop() skips or fails its cleanup when mongod has already died, which is when this store
|
|
74
|
+
// most needs it: its directory may still hold a session's rows.
|
|
75
|
+
try {
|
|
76
|
+
await replSet.stop({ doCleanup: true });
|
|
77
|
+
} finally {
|
|
78
|
+
removeDataStoreFiles({ replSet });
|
|
79
|
+
// Kept until the files are gone: Vite's SIGTERM handler can exit the process mid-stop,
|
|
80
|
+
// and the exit hook then finishes the removal.
|
|
81
|
+
unregisterShutdown();
|
|
82
|
+
}
|
|
83
|
+
})();
|
|
84
|
+
return stopping;
|
|
85
|
+
}
|
|
86
|
+
unregisterShutdown = registerDataStoreShutdown({ replSet, stop });
|
|
87
|
+
return { client, uri, stop };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The in-memory MongoDB replica set journey data sessions load into, started on first use. There
|
|
91
|
+
// is no idle stop: an idle store holds no session databases, and a restart would land on a new port
|
|
92
|
+
// while the connection plugin's cached clients keep polling the old one. It stops, with its
|
|
93
|
+
// temporary directory removed, when the dev server process shuts down. A failed start is not
|
|
94
|
+
// cached, so the next call tries again, and a store found dead (openDataSession) is stopped, so the
|
|
95
|
+
// next call starts a fresh one.
|
|
96
|
+
function getDataStore() {
|
|
97
|
+
if (globalThis[STORE_KEY] === undefined) {
|
|
98
|
+
globalThis[STORE_KEY] = startDataStore().catch((error) => {
|
|
99
|
+
delete globalThis[STORE_KEY];
|
|
100
|
+
throw error;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return globalThis[STORE_KEY];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export default getDataStore;
|
|
@@ -0,0 +1,63 @@
|
|
|
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 { jest } from '@jest/globals';
|
|
19
|
+
|
|
20
|
+
import getDataStore from './getDataStore.js';
|
|
21
|
+
import openDataSession from './openDataSession.js';
|
|
22
|
+
import { MIN_PORT } from './pickDataStorePort.js';
|
|
23
|
+
|
|
24
|
+
jest.setTimeout(120000);
|
|
25
|
+
|
|
26
|
+
// The real memory replica set, in a process of its own (jest runs each file in its own worker), so
|
|
27
|
+
// killing its mongod here touches no other suite's store.
|
|
28
|
+
|
|
29
|
+
async function describeMongod(store) {
|
|
30
|
+
const admin = store.client.db('admin');
|
|
31
|
+
const { pid } = await admin.command({ serverStatus: 1 });
|
|
32
|
+
const { parsed } = await admin.command({ getCmdLineOpts: 1 });
|
|
33
|
+
return { pid, dbPath: parsed.storage.dbPath };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test('the data store binds a port in the dynamic range and removes its directory when it stops', async () => {
|
|
37
|
+
const store = await getDataStore();
|
|
38
|
+
const port = Number(new URL(store.uri).port);
|
|
39
|
+
expect(port).toBeGreaterThanOrEqual(MIN_PORT);
|
|
40
|
+
expect(port).toBeLessThanOrEqual(65535);
|
|
41
|
+
const { dbPath } = await describeMongod(store);
|
|
42
|
+
expect(fs.existsSync(dbPath)).toBe(true);
|
|
43
|
+
await store.stop();
|
|
44
|
+
expect(fs.existsSync(dbPath)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('a store whose mongod died is replaced: the next session reports it, the one after runs on a new store', async () => {
|
|
48
|
+
const dead = await getDataStore();
|
|
49
|
+
const { pid, dbPath } = await describeMongod(dead);
|
|
50
|
+
process.kill(pid, 'SIGKILL');
|
|
51
|
+
await expect(openDataSession({ dataSet: { name: 'shop' } })).rejects.toThrow(
|
|
52
|
+
'The journey data store stopped responding'
|
|
53
|
+
);
|
|
54
|
+
expect(fs.existsSync(dbPath)).toBe(false);
|
|
55
|
+
const fresh = await getDataStore();
|
|
56
|
+
expect(fresh).not.toBe(dead);
|
|
57
|
+
expect(fresh.uri).not.toEqual(dead.uri);
|
|
58
|
+
const opened = await openDataSession({
|
|
59
|
+
dataSet: { name: 'shop', fixtures: {}, generated: {}, indexes: {}, collections: {} },
|
|
60
|
+
});
|
|
61
|
+
await opened.close();
|
|
62
|
+
await fresh.stop();
|
|
63
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
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
|
+
import { jest } from '@jest/globals';
|
|
21
|
+
|
|
22
|
+
const create = jest.fn();
|
|
23
|
+
const connect = jest.fn();
|
|
24
|
+
const listDatabases = jest.fn();
|
|
25
|
+
|
|
26
|
+
jest.unstable_mockModule('mongodb-memory-server-core', () => ({
|
|
27
|
+
MongoMemoryReplSet: { create },
|
|
28
|
+
}));
|
|
29
|
+
jest.unstable_mockModule('mongodb-memory-server-core/lib/util/DryMongoBinary.js', () => ({
|
|
30
|
+
DryMongoBinary: {
|
|
31
|
+
generateOptions: jest.fn(async () => ({ version: '6.0.14' })),
|
|
32
|
+
locateBinary: jest.fn(async () => '/cache/mongod'),
|
|
33
|
+
},
|
|
34
|
+
}));
|
|
35
|
+
const actualMongodb = await import('mongodb');
|
|
36
|
+
jest.unstable_mockModule('mongodb', () => ({
|
|
37
|
+
BSON: actualMongodb.BSON,
|
|
38
|
+
ObjectId: actualMongodb.ObjectId,
|
|
39
|
+
MongoClient: jest.fn(() => ({
|
|
40
|
+
connect,
|
|
41
|
+
close: jest.fn(async () => {}),
|
|
42
|
+
db: () => ({ admin: () => ({ listDatabases }), dropDatabase: async () => {} }),
|
|
43
|
+
})),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const { default: getDataStore } = await import('./getDataStore.js');
|
|
47
|
+
const { default: openDataSession } = await import('./openDataSession.js');
|
|
48
|
+
const { MIN_PORT } = await import('./pickDataStorePort.js');
|
|
49
|
+
|
|
50
|
+
function createReplSet({ uri = 'mongodb://127.0.0.1:50001/?replicaSet=x', instanceInfo } = {}) {
|
|
51
|
+
return {
|
|
52
|
+
getUri: () => uri,
|
|
53
|
+
stop: jest.fn(async () => true),
|
|
54
|
+
servers: [{ instanceInfo }],
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// The listeners a started store added for a process event, so a test runs the store's own handler
|
|
59
|
+
// without signalling the test process.
|
|
60
|
+
function addedListeners(event, before) {
|
|
61
|
+
return process.listeners(event).filter((listener) => !before.includes(listener));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function snapshotListeners() {
|
|
65
|
+
return {
|
|
66
|
+
SIGTERM: process.listeners('SIGTERM'),
|
|
67
|
+
SIGINT: process.listeners('SIGINT'),
|
|
68
|
+
SIGHUP: process.listeners('SIGHUP'),
|
|
69
|
+
exit: process.listeners('exit'),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
afterEach(async () => {
|
|
74
|
+
const cached = globalThis[Symbol.for('lowdefy.devServer.dataStore')];
|
|
75
|
+
if (cached !== undefined) {
|
|
76
|
+
const store = await cached.catch(() => null);
|
|
77
|
+
await store?.stop();
|
|
78
|
+
}
|
|
79
|
+
delete globalThis[Symbol.for('lowdefy.devServer.dataStore')];
|
|
80
|
+
jest.restoreAllMocks();
|
|
81
|
+
create.mockReset();
|
|
82
|
+
listDatabases.mockReset();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('getDataStore starts one replica set and returns the same store on every call', async () => {
|
|
86
|
+
create.mockResolvedValue(createReplSet());
|
|
87
|
+
const first = await getDataStore();
|
|
88
|
+
const second = await getDataStore();
|
|
89
|
+
expect(first).toBe(second);
|
|
90
|
+
expect(first.uri).toEqual('mongodb://127.0.0.1:50001/?replicaSet=x');
|
|
91
|
+
expect(create).toHaveBeenCalledTimes(1);
|
|
92
|
+
expect(create).toHaveBeenCalledWith({
|
|
93
|
+
instanceOpts: [{ port: expect.any(Number) }],
|
|
94
|
+
replSet: { count: 1, storageEngine: 'wiredTiger' },
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('getDataStore binds the store to a port in the dynamic range, above the usual app ports', async () => {
|
|
99
|
+
create.mockResolvedValue(createReplSet());
|
|
100
|
+
await getDataStore();
|
|
101
|
+
const [[{ instanceOpts }]] = create.mock.calls;
|
|
102
|
+
expect(instanceOpts[0].port).toBeGreaterThanOrEqual(MIN_PORT);
|
|
103
|
+
expect(instanceOpts[0].port).toBeLessThanOrEqual(65535);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('getDataStore clears a failed start so the next call retries', async () => {
|
|
107
|
+
create.mockRejectedValueOnce(new Error('no mongod'));
|
|
108
|
+
await expect(getDataStore()).rejects.toThrow('no mongod');
|
|
109
|
+
create.mockResolvedValueOnce(createReplSet({ uri: 'mongodb://127.0.0.1:50002/' }));
|
|
110
|
+
const store = await getDataStore();
|
|
111
|
+
expect(store.uri).toEqual('mongodb://127.0.0.1:50002/');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test('a SIGTERM stops the store with cleanup, then raises the signal again', async () => {
|
|
115
|
+
const replSet = createReplSet();
|
|
116
|
+
create.mockResolvedValue(replSet);
|
|
117
|
+
const kill = jest.spyOn(process, 'kill').mockImplementation(() => true);
|
|
118
|
+
const before = snapshotListeners();
|
|
119
|
+
await getDataStore();
|
|
120
|
+
const [onSigterm] = addedListeners('SIGTERM', before.SIGTERM);
|
|
121
|
+
expect(addedListeners('SIGINT', before.SIGINT)).toHaveLength(1);
|
|
122
|
+
expect(addedListeners('SIGHUP', before.SIGHUP)).toHaveLength(1);
|
|
123
|
+
await onSigterm('SIGTERM');
|
|
124
|
+
expect(replSet.stop).toHaveBeenCalledWith({ doCleanup: true });
|
|
125
|
+
expect(kill).toHaveBeenCalledWith(process.pid, 'SIGTERM');
|
|
126
|
+
expect(globalThis[Symbol.for('lowdefy.devServer.dataStore')]).toBeUndefined();
|
|
127
|
+
expect(addedListeners('SIGTERM', before.SIGTERM)).toEqual([]);
|
|
128
|
+
expect(addedListeners('SIGINT', before.SIGINT)).toEqual([]);
|
|
129
|
+
expect(addedListeners('SIGHUP', before.SIGHUP)).toEqual([]);
|
|
130
|
+
expect(addedListeners('exit', before.exit)).toEqual([]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('a SIGHUP from a closed terminal stops the store with cleanup, then raises the signal again', async () => {
|
|
134
|
+
const replSet = createReplSet();
|
|
135
|
+
create.mockResolvedValue(replSet);
|
|
136
|
+
const kill = jest.spyOn(process, 'kill').mockImplementation(() => true);
|
|
137
|
+
const before = snapshotListeners();
|
|
138
|
+
await getDataStore();
|
|
139
|
+
const [onSighup] = addedListeners('SIGHUP', before.SIGHUP);
|
|
140
|
+
await onSighup('SIGHUP');
|
|
141
|
+
expect(replSet.stop).toHaveBeenCalledWith({ doCleanup: true });
|
|
142
|
+
expect(kill).toHaveBeenCalledWith(process.pid, 'SIGHUP');
|
|
143
|
+
expect(addedListeners('SIGHUP', before.SIGHUP)).toEqual([]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('the exit hook kills mongod and removes its temporary directory when no stop finished', async () => {
|
|
147
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'mongo-mem-test-'));
|
|
148
|
+
fs.writeFileSync(path.join(tmpDir, 'WiredTiger'), '');
|
|
149
|
+
const mongodProcess = { kill: jest.fn() };
|
|
150
|
+
create.mockResolvedValue(
|
|
151
|
+
createReplSet({ instanceInfo: { tmpDir, dbPath: tmpDir, instance: { mongodProcess } } })
|
|
152
|
+
);
|
|
153
|
+
const before = snapshotListeners();
|
|
154
|
+
await getDataStore();
|
|
155
|
+
const [onExit] = addedListeners('exit', before.exit);
|
|
156
|
+
onExit();
|
|
157
|
+
expect(mongodProcess.kill).toHaveBeenCalledWith('SIGKILL');
|
|
158
|
+
expect(fs.existsSync(tmpDir)).toBe(false);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('a stopped store is replaced by a new one on the next getDataStore', async () => {
|
|
162
|
+
const first = createReplSet({ uri: 'mongodb://127.0.0.1:50003/' });
|
|
163
|
+
const second = createReplSet({ uri: 'mongodb://127.0.0.1:50004/' });
|
|
164
|
+
create.mockResolvedValueOnce(first).mockResolvedValueOnce(second);
|
|
165
|
+
const store = await getDataStore();
|
|
166
|
+
await store.stop();
|
|
167
|
+
await store.stop();
|
|
168
|
+
expect(first.stop).toHaveBeenCalledTimes(1);
|
|
169
|
+
expect((await getDataStore()).uri).toEqual('mongodb://127.0.0.1:50004/');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('a store whose mongod died is stopped by the next data session, and the one after starts a new store', async () => {
|
|
173
|
+
const dead = createReplSet({ uri: 'mongodb://127.0.0.1:50005/' });
|
|
174
|
+
const fresh = createReplSet({ uri: 'mongodb://127.0.0.1:50006/' });
|
|
175
|
+
create.mockResolvedValueOnce(dead).mockResolvedValueOnce(fresh);
|
|
176
|
+
await getDataStore();
|
|
177
|
+
const gone = new Error('Server selection timed out after 5000 ms');
|
|
178
|
+
gone.name = 'MongoServerSelectionError';
|
|
179
|
+
listDatabases.mockRejectedValueOnce(gone);
|
|
180
|
+
const error = await openDataSession({ dataSet: { name: 'shop' } }).catch((caught) => caught);
|
|
181
|
+
expect(error.message).toEqual(
|
|
182
|
+
'The journey data store stopped responding (Server selection timed out after 5000 ms). The next journey starts a new one.'
|
|
183
|
+
);
|
|
184
|
+
expect(error.cause).toBe(gone);
|
|
185
|
+
expect(dead.stop).toHaveBeenCalledWith({ doCleanup: true });
|
|
186
|
+
expect((await getDataStore()).uri).toEqual('mongodb://127.0.0.1:50006/');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('a data set that fails to load for another reason keeps the store', async () => {
|
|
190
|
+
create.mockResolvedValueOnce(createReplSet());
|
|
191
|
+
const store = await getDataStore();
|
|
192
|
+
listDatabases.mockRejectedValueOnce(new Error('not authorized'));
|
|
193
|
+
await expect(openDataSession({ dataSet: { name: 'shop' } })).rejects.toThrow('not authorized');
|
|
194
|
+
expect(await getDataStore()).toBe(store);
|
|
195
|
+
});
|