@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,99 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
// The error buffers of the headless journey runs open now, keyed by the run id and journey their recording cookie carries.
|
|
20
|
+
// Kept on globalThis because the routes that open a run and the error stores
|
|
21
|
+
// that feed it may load as separate module instances in one process (Vite's
|
|
22
|
+
// SSR module graph and Node's), like the mutant runs.
|
|
23
|
+
const REGISTRY_KEY = Symbol.for('lowdefy.devServer.runErrorBuffers');
|
|
24
|
+
globalThis[REGISTRY_KEY] ??= new Map();
|
|
25
|
+
const buffers = globalThis[REGISTRY_KEY];
|
|
26
|
+
|
|
27
|
+
function bufferKey({ run, journey }) {
|
|
28
|
+
return JSON.stringify([run, journey ?? null]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Opens the buffer the errors a run's browser contexts cause collect in.
|
|
32
|
+
// `journey` is the cookie's run.journey: a journey's name, or
|
|
33
|
+
// null for a run that names none. Registering an open buffer again shares it:
|
|
34
|
+
// it stays open until every holder has released it.
|
|
35
|
+
function registerRunBuffer({ run, journey }) {
|
|
36
|
+
if (!type.isString(run) || !(type.isNull(journey) || type.isString(journey))) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`registerRunBuffer requires a string "run" and a string or null "journey". Received ${JSON.stringify(
|
|
39
|
+
{ run, journey }
|
|
40
|
+
)}.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
const key = bufferKey({ run, journey });
|
|
44
|
+
const open = buffers.get(key);
|
|
45
|
+
if (!type.isUndefined(open)) {
|
|
46
|
+
open.holders += 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
buffers.set(key, { errors: [], holders: 1 });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// An error entry stamped with a run's recording goes to that run's buffer,
|
|
53
|
+
// and nowhere else. Returns whether an open buffer took it: an entry for a
|
|
54
|
+
// run whose buffer was released is the caller's to drop.
|
|
55
|
+
function recordRunError(entry) {
|
|
56
|
+
const buffer = buffers.get(
|
|
57
|
+
bufferKey({ run: entry.recording?.run, journey: entry.recording?.journey })
|
|
58
|
+
);
|
|
59
|
+
if (type.isUndefined(buffer)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
buffer.errors.push(entry);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Removes and returns the run's errors whose timestamp falls in
|
|
67
|
+
// [since, until] (milliseconds): what one step's window caused.
|
|
68
|
+
function takeRunErrors({ run, journey, since, until }) {
|
|
69
|
+
const buffer = buffers.get(bufferKey({ run, journey }));
|
|
70
|
+
if (type.isUndefined(buffer)) {
|
|
71
|
+
return [];
|
|
72
|
+
}
|
|
73
|
+
const taken = [];
|
|
74
|
+
const kept = [];
|
|
75
|
+
buffer.errors.forEach((entry) => {
|
|
76
|
+
const time = Date.parse(entry.timestamp);
|
|
77
|
+
if (time >= since && time <= until) {
|
|
78
|
+
taken.push(entry);
|
|
79
|
+
} else {
|
|
80
|
+
kept.push(entry);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
buffer.errors = kept;
|
|
84
|
+
return taken;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function releaseRunBuffer({ run, journey }) {
|
|
88
|
+
const key = bufferKey({ run, journey });
|
|
89
|
+
const buffer = buffers.get(key);
|
|
90
|
+
if (type.isUndefined(buffer)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
buffer.holders -= 1;
|
|
94
|
+
if (buffer.holders === 0) {
|
|
95
|
+
buffers.delete(key);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { recordRunError, registerRunBuffer, releaseRunBuffer, takeRunErrors };
|
|
@@ -0,0 +1,95 @@
|
|
|
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 {
|
|
18
|
+
recordRunError,
|
|
19
|
+
registerRunBuffer,
|
|
20
|
+
releaseRunBuffer,
|
|
21
|
+
takeRunErrors,
|
|
22
|
+
} from './runErrorBuffers.js';
|
|
23
|
+
|
|
24
|
+
const run = '20261003T151200Z-p0d4rm';
|
|
25
|
+
const journeyRun = { run, journey: 'tests/journeys/a.yaml#A' };
|
|
26
|
+
|
|
27
|
+
function entry({ journey = journeyRun.journey, timestamp, message = 'boom' }) {
|
|
28
|
+
return { timestamp, message, recording: { source: 'journey', run, journey } };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function takeAll(buffer) {
|
|
32
|
+
return takeRunErrors({ ...buffer, since: 0, until: Number.MAX_SAFE_INTEGER });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
releaseRunBuffer(journeyRun);
|
|
37
|
+
releaseRunBuffer({ run, journey: null });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('recordRunError puts an entry in the buffer of the run and journey it carries', () => {
|
|
41
|
+
registerRunBuffer(journeyRun);
|
|
42
|
+
registerRunBuffer({ run, journey: null });
|
|
43
|
+
expect(recordRunError(entry({ timestamp: '2026-10-03T15:12:01.000Z' }))).toBe(true);
|
|
44
|
+
expect(takeAll(journeyRun)).toHaveLength(1);
|
|
45
|
+
expect(takeAll({ run, journey: null })).toEqual([]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('recordRunError matches a run that names no journey', () => {
|
|
49
|
+
registerRunBuffer({ run, journey: null });
|
|
50
|
+
expect(recordRunError(entry({ journey: null, timestamp: '2026-10-03T15:12:01.000Z' }))).toBe(
|
|
51
|
+
true
|
|
52
|
+
);
|
|
53
|
+
expect(takeAll({ run, journey: null }).map((error) => error.message)).toEqual(['boom']);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('recordRunError refuses an entry for a run whose buffer is not open', () => {
|
|
57
|
+
expect(recordRunError(entry({ timestamp: '2026-10-03T15:12:01.000Z' }))).toBe(false);
|
|
58
|
+
registerRunBuffer(journeyRun);
|
|
59
|
+
releaseRunBuffer(journeyRun);
|
|
60
|
+
expect(recordRunError(entry({ timestamp: '2026-10-03T15:12:01.000Z' }))).toBe(false);
|
|
61
|
+
expect(recordRunError({ timestamp: '2026-10-03T15:12:01.000Z', recording: null })).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('takeRunErrors removes and returns the errors inside the window', () => {
|
|
65
|
+
registerRunBuffer(journeyRun);
|
|
66
|
+
recordRunError(entry({ timestamp: '2026-10-03T15:12:01.000Z', message: 'before' }));
|
|
67
|
+
recordRunError(entry({ timestamp: '2026-10-03T15:12:03.000Z', message: 'during' }));
|
|
68
|
+
recordRunError(entry({ timestamp: '2026-10-03T15:12:09.000Z', message: 'after' }));
|
|
69
|
+
const taken = takeRunErrors({
|
|
70
|
+
...journeyRun,
|
|
71
|
+
since: Date.parse('2026-10-03T15:12:02.000Z'),
|
|
72
|
+
until: Date.parse('2026-10-03T15:12:05.000Z'),
|
|
73
|
+
});
|
|
74
|
+
expect(taken.map((error) => error.message)).toEqual(['during']);
|
|
75
|
+
expect(takeAll(journeyRun).map((error) => error.message)).toEqual(['before', 'after']);
|
|
76
|
+
expect(takeAll({ run: '20261003T151200Z-other1', journey: null })).toEqual([]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('a buffer registered twice stays open until both holders release it', () => {
|
|
80
|
+
registerRunBuffer(journeyRun);
|
|
81
|
+
registerRunBuffer(journeyRun);
|
|
82
|
+
releaseRunBuffer(journeyRun);
|
|
83
|
+
expect(recordRunError(entry({ timestamp: '2026-10-03T15:12:01.000Z' }))).toBe(true);
|
|
84
|
+
releaseRunBuffer(journeyRun);
|
|
85
|
+
expect(recordRunError(entry({ timestamp: '2026-10-03T15:12:01.000Z' }))).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('registerRunBuffer requires a string run and a string or null journey', () => {
|
|
89
|
+
expect(() => registerRunBuffer({ run, journey: undefined })).toThrow(
|
|
90
|
+
'registerRunBuffer requires a string "run" and a string or null "journey".'
|
|
91
|
+
);
|
|
92
|
+
expect(() => registerRunBuffer({ run: 7, journey: null })).toThrow(
|
|
93
|
+
'registerRunBuffer requires a string "run" and a string or null "journey". Received {"run":7,"journey":null}.'
|
|
94
|
+
);
|
|
95
|
+
});
|
|
@@ -0,0 +1,347 @@
|
|
|
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
|
+
import { type } from '@lowdefy/helpers';
|
|
19
|
+
import { getState } from '@lowdefy/e2e-utils/runtime';
|
|
20
|
+
import {
|
|
21
|
+
findPlaceholderStep,
|
|
22
|
+
normaliseJourneySteps,
|
|
23
|
+
validateJourneySteps,
|
|
24
|
+
} from '@lowdefy/node-utils';
|
|
25
|
+
|
|
26
|
+
import collectExercised from './collectExercised.js';
|
|
27
|
+
import createDataSetRails from './rails/createDataSetRails.js';
|
|
28
|
+
import createJourneyAppErrors from './observe/createJourneyAppErrors.js';
|
|
29
|
+
import describeDataSetResult from './dataSets/describeDataSetResult.js';
|
|
30
|
+
import getDataStore from './dataSets/getDataStore.js';
|
|
31
|
+
import journeyRunRecording from './journeyRunRecording.js';
|
|
32
|
+
import { getBrowser, buildPageUrl } from './getBrowser.js';
|
|
33
|
+
import noBrowserError from './noBrowserError.js';
|
|
34
|
+
import openDataSession from './dataSets/openDataSession.js';
|
|
35
|
+
import openJourney from './openJourney.js';
|
|
36
|
+
import readBuildArtifact from './readBuildArtifact.js';
|
|
37
|
+
import readDevAuthMode from './readDevAuthMode.js';
|
|
38
|
+
import readConnectionPlugins from './readConnectionPlugins.js';
|
|
39
|
+
import readPagePath from './readPagePath.js';
|
|
40
|
+
import { registerRunBuffer, releaseRunBuffer } from './runErrorBuffers.js';
|
|
41
|
+
import resolveJourneyDataSet from './dataSets/resolveJourneyDataSet.js';
|
|
42
|
+
import runJourneySteps from './runJourneySteps.js';
|
|
43
|
+
import selectFinalState from './selectFinalState.js';
|
|
44
|
+
import unsettledPageNote from './unsettledPageNote.js';
|
|
45
|
+
import validateJourneyMail from './validateJourneyMail.js';
|
|
46
|
+
import validateJourneyTimeout from './validateJourneyTimeout.js';
|
|
47
|
+
import validateStateSelection from './validateStateSelection.js';
|
|
48
|
+
import withBrowserSlot from './withBrowserSlot.js';
|
|
49
|
+
|
|
50
|
+
// The final state is read even after a failure — it is what an agent needs to
|
|
51
|
+
// write the next assertion. A page that has navigated away or crashed may not
|
|
52
|
+
// expose one; that is reported rather than thrown.
|
|
53
|
+
async function readFinalState({ page }) {
|
|
54
|
+
try {
|
|
55
|
+
return await getState(page);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return { error: `Could not read final state: ${error.message}` };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// The built artifacts a journey's exercised path is read from. The pages it
|
|
62
|
+
// visited were built by the visit, so their request artifacts are on disk.
|
|
63
|
+
function defaultReadConfigFile(name) {
|
|
64
|
+
return readBuildArtifact({ name, deserialize: true });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// runJourney drives a page of the running dev server through a declarative
|
|
68
|
+
// list of steps — click, fill, select, press, back, goto, email, as, wait,
|
|
69
|
+
// screenshot, expect — so an agent can verify behaviour (a form submits, a
|
|
70
|
+
// modal opens, state changes, a sign-up email arrives) and not only layout.
|
|
71
|
+
// `stepTimeout` (the journey's `timeout`) bounds each step's wait for
|
|
72
|
+
// something to happen, matching Playwright's per-action timeout; `timeout`
|
|
73
|
+
// bounds each page open, raised to `stepTimeout` when that is longer, so one
|
|
74
|
+
// journey setting raises every such wait. The settle after an interaction is
|
|
75
|
+
// the exception: it stays capped (see settlePage), since it never fails a
|
|
76
|
+
// step. `state` picks what the result carries of the final page state (see
|
|
77
|
+
// selectFinalState). `user: 'none'` injects no caller, so the app's own auth
|
|
78
|
+
// decides who each actor is. `data` names a data set: the journey runs on a
|
|
79
|
+
// fresh database of its own, loaded with it, and a string `user` (or an `as`
|
|
80
|
+
// name) names one of its users. Its problems come back as { error, refused }
|
|
81
|
+
// before any browser opens. `pathParams` fill the placeholders of the page's
|
|
82
|
+
// path; one left without a value is an error before any browser opens.
|
|
83
|
+
async function runJourney({
|
|
84
|
+
origin,
|
|
85
|
+
pageId,
|
|
86
|
+
pathParams,
|
|
87
|
+
steps: inputSteps,
|
|
88
|
+
user,
|
|
89
|
+
urlQuery,
|
|
90
|
+
state: stateSelection,
|
|
91
|
+
width = 1280,
|
|
92
|
+
height = 800,
|
|
93
|
+
timeout = 15000,
|
|
94
|
+
stepTimeout = 5000,
|
|
95
|
+
basePath = '',
|
|
96
|
+
readConfigFile = defaultReadConfigFile,
|
|
97
|
+
mutantCookie,
|
|
98
|
+
data,
|
|
99
|
+
recording,
|
|
100
|
+
by,
|
|
101
|
+
}) {
|
|
102
|
+
if (type.isNone(origin) || !type.isString(origin)) {
|
|
103
|
+
return {
|
|
104
|
+
error: `runJourney requires an "origin" string. Received ${JSON.stringify(origin)}.`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
if (type.isNone(pageId) || !type.isString(pageId)) {
|
|
108
|
+
return {
|
|
109
|
+
error: `runJourney requires a "pageId" string. Received ${JSON.stringify(pageId)}.`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (
|
|
113
|
+
!type.isNone(pathParams) &&
|
|
114
|
+
(!type.isObject(pathParams) || !Object.values(pathParams).every(type.isString))
|
|
115
|
+
) {
|
|
116
|
+
return {
|
|
117
|
+
error: `runJourney requires "pathParams" to be an object of strings, one per path placeholder. Received ${JSON.stringify(
|
|
118
|
+
pathParams
|
|
119
|
+
)}.`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
if (!type.isNone(urlQuery) && !type.isObject(urlQuery)) {
|
|
123
|
+
return {
|
|
124
|
+
error: `runJourney requires "urlQuery" to be an object. Received ${JSON.stringify(
|
|
125
|
+
urlQuery
|
|
126
|
+
)}.`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const { error: stepsError } = validateJourneySteps({ steps: inputSteps });
|
|
130
|
+
if (!type.isUndefined(stepsError)) {
|
|
131
|
+
return { error: stepsError };
|
|
132
|
+
}
|
|
133
|
+
const steps = normaliseJourneySteps({ steps: inputSteps });
|
|
134
|
+
// A placeholder step or a mail step with no sink is the caller's to fix, like
|
|
135
|
+
// a data set problem: refused, so the route answers 400 and lowdefy test does
|
|
136
|
+
// not repeat it.
|
|
137
|
+
const { error: placeholderError } = findPlaceholderStep({ steps });
|
|
138
|
+
if (!type.isUndefined(placeholderError)) {
|
|
139
|
+
return { error: placeholderError, refused: true };
|
|
140
|
+
}
|
|
141
|
+
const stateSelectionError = validateStateSelection({ state: stateSelection });
|
|
142
|
+
if (!type.isUndefined(stateSelectionError)) {
|
|
143
|
+
return { error: stateSelectionError };
|
|
144
|
+
}
|
|
145
|
+
const timeoutError = validateJourneyTimeout({ timeout: stepTimeout });
|
|
146
|
+
if (!type.isUndefined(timeoutError)) {
|
|
147
|
+
return { error: timeoutError };
|
|
148
|
+
}
|
|
149
|
+
const mailError = validateJourneyMail({ steps });
|
|
150
|
+
if (!type.isUndefined(mailError)) {
|
|
151
|
+
return { error: mailError, refused: true };
|
|
152
|
+
}
|
|
153
|
+
const pagePath = readPagePath({ pageId });
|
|
154
|
+
let url;
|
|
155
|
+
try {
|
|
156
|
+
url = buildPageUrl({ origin, pageId, path: pagePath, pathParams, urlQuery });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
return { error: error.message, refused: true };
|
|
159
|
+
}
|
|
160
|
+
const resolved = await resolveJourneyDataSet({
|
|
161
|
+
data,
|
|
162
|
+
user,
|
|
163
|
+
configDirectory: process.env.LOWDEFY_DIRECTORY_CONFIG ?? process.cwd(),
|
|
164
|
+
buildDirectory: path.join(process.cwd(), 'build'),
|
|
165
|
+
...readDevAuthMode(),
|
|
166
|
+
});
|
|
167
|
+
if (!type.isUndefined(resolved.error)) {
|
|
168
|
+
return { error: resolved.error, refused: true };
|
|
169
|
+
}
|
|
170
|
+
const { dataSet } = resolved;
|
|
171
|
+
|
|
172
|
+
return withBrowserSlot({
|
|
173
|
+
task: () =>
|
|
174
|
+
runJourneyInBrowser({
|
|
175
|
+
origin,
|
|
176
|
+
basePath,
|
|
177
|
+
pageId,
|
|
178
|
+
pagePath,
|
|
179
|
+
pathParams,
|
|
180
|
+
url,
|
|
181
|
+
user: resolved.user,
|
|
182
|
+
dataSet,
|
|
183
|
+
urlQuery,
|
|
184
|
+
width,
|
|
185
|
+
height,
|
|
186
|
+
timeout,
|
|
187
|
+
stepTimeout,
|
|
188
|
+
mutantCookie,
|
|
189
|
+
recording,
|
|
190
|
+
by,
|
|
191
|
+
steps,
|
|
192
|
+
stateSelection,
|
|
193
|
+
readConfigFile,
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// The part of runJourney that runs in the browser, inside a browser slot.
|
|
199
|
+
async function runJourneyInBrowser({
|
|
200
|
+
origin,
|
|
201
|
+
basePath,
|
|
202
|
+
pageId,
|
|
203
|
+
pagePath,
|
|
204
|
+
pathParams,
|
|
205
|
+
url,
|
|
206
|
+
user,
|
|
207
|
+
dataSet,
|
|
208
|
+
urlQuery,
|
|
209
|
+
width,
|
|
210
|
+
height,
|
|
211
|
+
timeout,
|
|
212
|
+
stepTimeout,
|
|
213
|
+
mutantCookie,
|
|
214
|
+
recording,
|
|
215
|
+
by,
|
|
216
|
+
steps,
|
|
217
|
+
stateSelection,
|
|
218
|
+
readConfigFile,
|
|
219
|
+
}) {
|
|
220
|
+
// The data session opens before the browser is fetched: a data set that fails
|
|
221
|
+
// to load is refused without launching a browser for nothing.
|
|
222
|
+
let session = null;
|
|
223
|
+
let loadMs;
|
|
224
|
+
if (!type.isUndefined(dataSet)) {
|
|
225
|
+
try {
|
|
226
|
+
await getDataStore();
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return { error: `Could not start the journey data store: ${error.message}` };
|
|
229
|
+
}
|
|
230
|
+
const loadStart = Date.now();
|
|
231
|
+
try {
|
|
232
|
+
session = await openDataSession({ dataSet });
|
|
233
|
+
} catch (error) {
|
|
234
|
+
return { error: error.message, refused: true };
|
|
235
|
+
}
|
|
236
|
+
loadMs = Date.now() - loadStart;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let browser;
|
|
240
|
+
try {
|
|
241
|
+
browser = await getBrowser();
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if (session !== null) {
|
|
244
|
+
await session.close();
|
|
245
|
+
}
|
|
246
|
+
return { error: noBrowserError(error) };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Every run carries an identity, recorded or not, so the errors it causes
|
|
250
|
+
// collect in its own buffer from its first request (see runErrorBuffers).
|
|
251
|
+
const runRecording = journeyRunRecording({ recording, by });
|
|
252
|
+
const runBuffer = { run: runRecording.run.id, journey: runRecording.run.journey };
|
|
253
|
+
registerRunBuffer(runBuffer);
|
|
254
|
+
const appErrors = createJourneyAppErrors({
|
|
255
|
+
origin,
|
|
256
|
+
basePath,
|
|
257
|
+
recording: runRecording,
|
|
258
|
+
configDirectory: process.env.LOWDEFY_DIRECTORY_CONFIG ?? process.cwd(),
|
|
259
|
+
});
|
|
260
|
+
let journey;
|
|
261
|
+
try {
|
|
262
|
+
const opened = await openJourney({
|
|
263
|
+
browser,
|
|
264
|
+
origin,
|
|
265
|
+
basePath,
|
|
266
|
+
pageId,
|
|
267
|
+
path: pagePath,
|
|
268
|
+
pathParams,
|
|
269
|
+
user,
|
|
270
|
+
urlQuery,
|
|
271
|
+
width,
|
|
272
|
+
height,
|
|
273
|
+
timeout,
|
|
274
|
+
stepTimeout,
|
|
275
|
+
dataCookie: session?.cookie,
|
|
276
|
+
mutantCookie,
|
|
277
|
+
users: dataSet?.users,
|
|
278
|
+
recording: runRecording,
|
|
279
|
+
onContext: appErrors.onContext,
|
|
280
|
+
});
|
|
281
|
+
journey = opened.journey;
|
|
282
|
+
journey.recording = runRecording;
|
|
283
|
+
journey.appErrors = appErrors;
|
|
284
|
+
if (!type.isUndefined(dataSet)) {
|
|
285
|
+
journey.dataSetRails = createDataSetRails({
|
|
286
|
+
buildDirectory: path.join(process.cwd(), 'build'),
|
|
287
|
+
connections: await readConnectionPlugins(),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
const { results, screenshots, failure } = await runJourneySteps({ journey, steps });
|
|
291
|
+
const state = await readFinalState({ page: journey.actors.current().page });
|
|
292
|
+
const exercised = await collectExercised({
|
|
293
|
+
snapshots: journey.actors.networkSnapshots(),
|
|
294
|
+
observed: journey.actors.observed(),
|
|
295
|
+
readConfigFile,
|
|
296
|
+
requestSchemas: await readConfigFile('plugins/requestSchemas.json'),
|
|
297
|
+
});
|
|
298
|
+
const result = {
|
|
299
|
+
pageId,
|
|
300
|
+
passed: type.isUndefined(failure),
|
|
301
|
+
steps: results,
|
|
302
|
+
screenshots,
|
|
303
|
+
...selectFinalState({ state, selection: stateSelection }),
|
|
304
|
+
exercised,
|
|
305
|
+
};
|
|
306
|
+
if (!type.isUndefined(failure)) {
|
|
307
|
+
result.failure = failure;
|
|
308
|
+
}
|
|
309
|
+
if (!type.isUndefined(dataSet)) {
|
|
310
|
+
result.data = describeDataSetResult({ dataSet, loadMs });
|
|
311
|
+
result.warnings = dataSet.warnings;
|
|
312
|
+
}
|
|
313
|
+
// Requests of external connections (mail, HTTP, ...) a data set run recorded instead of
|
|
314
|
+
// sending, in the order they ran.
|
|
315
|
+
if (session !== null && (session.sunk?.length ?? 0) > 0) {
|
|
316
|
+
result.sunk = session.sunk;
|
|
317
|
+
}
|
|
318
|
+
// openPage already waited for the page's async lifecycle; an unsettled page
|
|
319
|
+
// still runs its steps and reports `ready: false` alongside the result.
|
|
320
|
+
if (!opened.main.ready) {
|
|
321
|
+
return {
|
|
322
|
+
...result,
|
|
323
|
+
ready: false,
|
|
324
|
+
note: unsettledPageNote({ timeout: journey.openTimeout }),
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
} catch (error) {
|
|
329
|
+
return { error: `Failed to run journey at "${url}": ${error.message}` };
|
|
330
|
+
} finally {
|
|
331
|
+
if (!type.isUndefined(journey)) {
|
|
332
|
+
if (runRecording.record !== false) {
|
|
333
|
+
await journey.actors.flushRecordings();
|
|
334
|
+
}
|
|
335
|
+
await journey.actors.closeAll();
|
|
336
|
+
}
|
|
337
|
+
releaseRunBuffer(runBuffer);
|
|
338
|
+
// After the actors: no browser request still carries the data cookie. close() then waits for
|
|
339
|
+
// the session's background work before it drops the database. openJourney closed its own
|
|
340
|
+
// actors when the first page failed to open.
|
|
341
|
+
if (session !== null) {
|
|
342
|
+
await session.close();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export default runJourney;
|