@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
package/lib/docs/runRequest.js
CHANGED
|
@@ -14,15 +14,20 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
|
|
18
19
|
import { callRequest } from '@lowdefy/api';
|
|
20
|
+
import { ConfigError } from '@lowdefy/errors';
|
|
21
|
+
import { type } from '@lowdefy/helpers';
|
|
19
22
|
|
|
23
|
+
import buildPageIfNeeded, { syncBuildSignals } from '../server/jitPageBuilder.js';
|
|
24
|
+
import fitResponse from './fitResponse.js';
|
|
20
25
|
import isWriteRequestsAllowed from './isWriteRequestsAllowed.js';
|
|
26
|
+
import mapPageBuildErrors from './mapPageBuildErrors.js';
|
|
21
27
|
import readBuildArtifact from './readBuildArtifact.js';
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const MAX_RESPONSE_CHARS = 100_000;
|
|
28
|
+
import resolveToolCaller from './resolveToolCaller.js';
|
|
29
|
+
import reviewPage from './reviewPage.js';
|
|
30
|
+
import withDataSession from './withDataSession.js';
|
|
26
31
|
|
|
27
32
|
function getRequestType({ pageId, requestId }) {
|
|
28
33
|
// The request's `type` is stripped from build/pages/<pageId>.json by the
|
|
@@ -37,54 +42,63 @@ function getRequestType({ pageId, requestId }) {
|
|
|
37
42
|
return requestConfig?.type ?? null;
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
function truncateResponse(result) {
|
|
41
|
-
const json = JSON.stringify(result.response);
|
|
42
|
-
if (json.length <= MAX_RESPONSE_CHARS) {
|
|
43
|
-
return result;
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
...result,
|
|
47
|
-
response: json.slice(0, MAX_RESPONSE_CHARS),
|
|
48
|
-
truncated: true,
|
|
49
|
-
note: `Response truncated to ${MAX_RESPONSE_CHARS} characters (original serialized size: ${json.length} characters).`,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
45
|
// Executes a page request the same way POST /api/request/<pageId>/<requestId>
|
|
54
46
|
// does (src/routes/request.js), but gated for agent use: requests whose type
|
|
55
47
|
// is not declared read-only (checkWrite: false in requestSchemas.json meta)
|
|
56
48
|
// are refused unless the app opts in via lowdefy.yaml's
|
|
57
49
|
// cli.agentTools.allowWriteRequests. Never throws — errors and refusals are
|
|
58
|
-
// returned as data so an agent can reason about them.
|
|
59
|
-
|
|
50
|
+
// returned as data so an agent can reason about them. `user` and `data` name
|
|
51
|
+
// the caller as a journey's do (see resolveToolCaller); a data set runs the
|
|
52
|
+
// request on that data set's own database.
|
|
53
|
+
async function runRequest({
|
|
54
|
+
pageId,
|
|
55
|
+
requestId,
|
|
56
|
+
payload = {},
|
|
57
|
+
user,
|
|
58
|
+
data,
|
|
59
|
+
saveResponse = false,
|
|
60
|
+
honoContext,
|
|
61
|
+
}) {
|
|
60
62
|
if (type.isUndefined(pageId) || !type.isString(pageId)) {
|
|
61
|
-
throw new
|
|
63
|
+
throw new ConfigError(
|
|
64
|
+
`run_request requires a "pageId" string. Received ${JSON.stringify(pageId)}.`
|
|
65
|
+
);
|
|
62
66
|
}
|
|
63
67
|
if (type.isUndefined(requestId) || !type.isString(requestId)) {
|
|
64
|
-
throw new
|
|
68
|
+
throw new ConfigError(
|
|
65
69
|
`run_request requires a "requestId" string. Received ${JSON.stringify(requestId)}.`
|
|
66
70
|
);
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
|
|
70
|
-
if (type.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
pageId,
|
|
79
|
-
buildDirectory: path.join(process.cwd(), 'build'),
|
|
80
|
-
configDirectory: process.env.LOWDEFY_DIRECTORY_CONFIG || process.cwd(),
|
|
81
|
-
});
|
|
82
|
-
requestType = getRequestType({ pageId, requestId });
|
|
83
|
-
} catch {
|
|
84
|
-
// JIT build failure surfaces through lowdefy_build_status — fall
|
|
85
|
-
// through to the not-found refusal below.
|
|
86
|
-
}
|
|
73
|
+
const caller = await resolveToolCaller({ user, data });
|
|
74
|
+
if (!type.isUndefined(caller.error)) {
|
|
75
|
+
throw new ConfigError(`run_request: ${caller.error}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!type.isBoolean(saveResponse)) {
|
|
79
|
+
throw new ConfigError(
|
|
80
|
+
`run_request "saveResponse" must be a boolean. Received ${JSON.stringify(saveResponse)}.`
|
|
81
|
+
);
|
|
87
82
|
}
|
|
83
|
+
|
|
84
|
+
// Page content, including the per-request artifacts, is built JIT, and a
|
|
85
|
+
// page edit only marks pages for rebuilding: the previous build's artifacts
|
|
86
|
+
// stay on disk until something requests the page. Run the build the page
|
|
87
|
+
// route runs (a no-op while the page is current) so the request that runs is
|
|
88
|
+
// the one in the config now.
|
|
89
|
+
const configDirectory = process.env.LOWDEFY_DIRECTORY_CONFIG || process.cwd();
|
|
90
|
+
const buildDirectory = path.join(process.cwd(), 'build');
|
|
91
|
+
try {
|
|
92
|
+
await buildPageIfNeeded({ pageId, buildDirectory, configDirectory });
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return {
|
|
95
|
+
refused: true,
|
|
96
|
+
reason: `Page "${pageId}" fails to build, so its requests cannot run until the errors are fixed.`,
|
|
97
|
+
buildErrors: mapPageBuildErrors(error),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const requestType = getRequestType({ pageId, requestId });
|
|
88
102
|
if (type.isNone(requestType)) {
|
|
89
103
|
return {
|
|
90
104
|
refused: true,
|
|
@@ -116,26 +130,53 @@ async function runRequest({ pageId, requestId, payload = {}, honoContext }) {
|
|
|
116
130
|
// at module load would break every consumer of this module (e.g. the MCP
|
|
117
131
|
// server) in environments without a full build.
|
|
118
132
|
const { default: createLowdefyContext } = await import('../server/createLowdefyContext.js');
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
133
|
+
const ran = await withDataSession({
|
|
134
|
+
dataSet: caller.dataSet,
|
|
135
|
+
task: async ({ dataSession }) => {
|
|
136
|
+
// "none" injects no caller: the app's own auth resolves this call, which
|
|
137
|
+
// carries no session, so it runs signed out.
|
|
138
|
+
const context = await createLowdefyContext({
|
|
139
|
+
c: honoContext,
|
|
140
|
+
user: caller.user === 'none' ? undefined : caller.user,
|
|
141
|
+
dataSession,
|
|
142
|
+
});
|
|
143
|
+
context.logger.info({ event: 'agent_run_request', pageId, requestId, user, data });
|
|
144
|
+
try {
|
|
145
|
+
const result = await callRequest(context, {
|
|
146
|
+
blockId: undefined,
|
|
147
|
+
pageId,
|
|
148
|
+
payload,
|
|
149
|
+
requestId,
|
|
150
|
+
});
|
|
151
|
+
return {
|
|
152
|
+
refused: false,
|
|
153
|
+
...fitResponse({ result, name: `${pageId}.${requestId}`, saveResponse }),
|
|
154
|
+
};
|
|
155
|
+
} catch (error) {
|
|
156
|
+
return {
|
|
157
|
+
refused: false,
|
|
158
|
+
error: {
|
|
159
|
+
name: error.name,
|
|
160
|
+
message: error.message,
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
if (!type.isUndefined(ran.error) && type.isString(ran.error)) {
|
|
167
|
+
return { refused: true, reason: ran.error };
|
|
168
|
+
}
|
|
169
|
+
const signals = syncBuildSignals({ buildDirectory, configDirectory });
|
|
170
|
+
const review = await reviewPage({
|
|
171
|
+
pageId,
|
|
172
|
+
entry: signals.registry?.[pageId],
|
|
173
|
+
configDirectory,
|
|
174
|
+
signals,
|
|
175
|
+
});
|
|
176
|
+
if (review === 'edited') {
|
|
177
|
+
ran.staleConfig = `Page "${pageId}" changed on disk after its last build, but the dev server has not rebuilt it yet, so this ran the previous config. Call lowdefy_build_status with wait: true, then run the request again.`;
|
|
138
178
|
}
|
|
179
|
+
return ran;
|
|
139
180
|
}
|
|
140
181
|
|
|
141
182
|
export default runRequest;
|
|
@@ -0,0 +1,190 @@
|
|
|
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
|
+
// runRequest reads build artifacts, builds a Lowdefy context and calls
|
|
20
|
+
// callRequest - all three only exist in a running server directory, so mock
|
|
21
|
+
// them and leave only runRequest's own validation and pass-through under test.
|
|
22
|
+
const mockCallRequest = jest.fn();
|
|
23
|
+
const mockReadBuildArtifact = jest.fn();
|
|
24
|
+
const mockIsWriteRequestsAllowed = jest.fn();
|
|
25
|
+
const mockCreateLowdefyContext = jest.fn();
|
|
26
|
+
const mockLoggerInfo = jest.fn();
|
|
27
|
+
|
|
28
|
+
jest.unstable_mockModule('@lowdefy/api', () => ({
|
|
29
|
+
callRequest: mockCallRequest,
|
|
30
|
+
}));
|
|
31
|
+
jest.unstable_mockModule('./readBuildArtifact.js', () => ({
|
|
32
|
+
default: mockReadBuildArtifact,
|
|
33
|
+
}));
|
|
34
|
+
jest.unstable_mockModule('./isWriteRequestsAllowed.js', () => ({
|
|
35
|
+
default: mockIsWriteRequestsAllowed,
|
|
36
|
+
}));
|
|
37
|
+
jest.unstable_mockModule('../server/createLowdefyContext.js', () => ({
|
|
38
|
+
default: mockCreateLowdefyContext,
|
|
39
|
+
}));
|
|
40
|
+
const mockBuildPageIfNeeded = jest.fn();
|
|
41
|
+
const mockSyncBuildSignals = jest.fn(() => ({
|
|
42
|
+
registry: { home: { pageId: 'home', refPath: 'pages/home.yaml' } },
|
|
43
|
+
eventCounter: 0,
|
|
44
|
+
generation: 1,
|
|
45
|
+
}));
|
|
46
|
+
jest.unstable_mockModule('../server/jitPageBuilder.js', () => ({
|
|
47
|
+
default: mockBuildPageIfNeeded,
|
|
48
|
+
syncBuildSignals: mockSyncBuildSignals,
|
|
49
|
+
}));
|
|
50
|
+
const mockReviewPage = jest.fn();
|
|
51
|
+
jest.unstable_mockModule('./reviewPage.js', () => ({
|
|
52
|
+
default: mockReviewPage,
|
|
53
|
+
}));
|
|
54
|
+
|
|
55
|
+
const { ConfigError } = await import('@lowdefy/errors');
|
|
56
|
+
const { default: runRequest } = await import('./runRequest.js');
|
|
57
|
+
|
|
58
|
+
const honoContext = { req: { path: '/lowdefy-docs/run-request' } };
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
jest.clearAllMocks();
|
|
62
|
+
mockReadBuildArtifact.mockImplementation(({ name }) => {
|
|
63
|
+
if (name === 'plugins/requestSchemas.json') {
|
|
64
|
+
return { MongoDBFind: { meta: { checkRead: true, checkWrite: false } } };
|
|
65
|
+
}
|
|
66
|
+
if (name === 'pageRegistry.json') {
|
|
67
|
+
return { home: { pageId: 'home', refPath: 'pages/home.yaml' }, other: { pageId: 'other' } };
|
|
68
|
+
}
|
|
69
|
+
return { type: 'MongoDBFind' };
|
|
70
|
+
});
|
|
71
|
+
mockIsWriteRequestsAllowed.mockResolvedValue(false);
|
|
72
|
+
mockCreateLowdefyContext.mockResolvedValue({ logger: { info: mockLoggerInfo } });
|
|
73
|
+
mockCallRequest.mockResolvedValue({ id: 'requests', response: [{ _id: 1 }] });
|
|
74
|
+
mockBuildPageIfNeeded.mockResolvedValue(true);
|
|
75
|
+
mockReviewPage.mockResolvedValue('current');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('runRequest passes a user object to createLowdefyContext', async () => {
|
|
79
|
+
const user = { roles: ['admin'], organization_id: 'org_1' };
|
|
80
|
+
|
|
81
|
+
const result = await runRequest({
|
|
82
|
+
pageId: 'home',
|
|
83
|
+
requestId: 'get_rows',
|
|
84
|
+
user,
|
|
85
|
+
honoContext,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(mockCreateLowdefyContext).toHaveBeenCalledWith({ c: honoContext, user });
|
|
89
|
+
expect(result.refused).toBe(false);
|
|
90
|
+
expect(result.response).toEqual([{ _id: 1 }]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('runRequest logs the user it ran the request as', async () => {
|
|
94
|
+
const user = { roles: ['admin'] };
|
|
95
|
+
|
|
96
|
+
await runRequest({ pageId: 'home', requestId: 'get_rows', user, honoContext });
|
|
97
|
+
|
|
98
|
+
expect(mockLoggerInfo).toHaveBeenCalledWith({
|
|
99
|
+
event: 'agent_run_request',
|
|
100
|
+
pageId: 'home',
|
|
101
|
+
requestId: 'get_rows',
|
|
102
|
+
user,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('runRequest throws a ConfigError when user is neither an object nor a data set user', async () => {
|
|
107
|
+
await expect(
|
|
108
|
+
runRequest({ pageId: 'home', requestId: 'get_rows', user: 'admin', honoContext })
|
|
109
|
+
).rejects.toThrow(ConfigError);
|
|
110
|
+
await expect(
|
|
111
|
+
runRequest({ pageId: 'home', requestId: 'get_rows', user: ['admin'], honoContext })
|
|
112
|
+
).rejects.toThrow(/run_request: "user" should be a user object/);
|
|
113
|
+
expect(mockCreateLowdefyContext).not.toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('runRequest builds a context with an undefined user when user is omitted', async () => {
|
|
117
|
+
const result = await runRequest({ pageId: 'home', requestId: 'get_rows', honoContext });
|
|
118
|
+
|
|
119
|
+
expect(mockCreateLowdefyContext).toHaveBeenCalledWith({ c: honoContext, user: undefined });
|
|
120
|
+
expect(mockLoggerInfo).toHaveBeenCalledWith({
|
|
121
|
+
event: 'agent_run_request',
|
|
122
|
+
pageId: 'home',
|
|
123
|
+
requestId: 'get_rows',
|
|
124
|
+
user: undefined,
|
|
125
|
+
});
|
|
126
|
+
expect(result).toEqual({ refused: false, id: 'requests', response: [{ _id: 1 }] });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test('runRequest refuses a write request for an impersonated caller too', async () => {
|
|
130
|
+
mockReadBuildArtifact.mockImplementation(({ name }) => {
|
|
131
|
+
if (name === 'plugins/requestSchemas.json') {
|
|
132
|
+
return { MongoDBInsertOne: { meta: { checkWrite: true } } };
|
|
133
|
+
}
|
|
134
|
+
return { type: 'MongoDBInsertOne' };
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const result = await runRequest({
|
|
138
|
+
pageId: 'home',
|
|
139
|
+
requestId: 'insert_row',
|
|
140
|
+
user: { roles: ['admin'] },
|
|
141
|
+
honoContext,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(result.refused).toBe(true);
|
|
145
|
+
expect(mockCreateLowdefyContext).not.toHaveBeenCalled();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('runRequest notes stale config when the page changed but the dev server has not rebuilt it', async () => {
|
|
149
|
+
mockReviewPage.mockResolvedValue('edited');
|
|
150
|
+
|
|
151
|
+
const result = await runRequest({ pageId: 'home', requestId: 'get_rows', honoContext });
|
|
152
|
+
|
|
153
|
+
expect(result.response).toEqual([{ _id: 1 }]);
|
|
154
|
+
expect(result.staleConfig).toContain('has not rebuilt it yet');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test('runRequest does not note stale config for a page that is current', async () => {
|
|
158
|
+
const result = await runRequest({ pageId: 'home', requestId: 'get_rows', honoContext });
|
|
159
|
+
|
|
160
|
+
expect(result.staleConfig).toBeUndefined();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('runRequest throws a ConfigError when saveResponse is not a boolean', async () => {
|
|
164
|
+
await expect(
|
|
165
|
+
runRequest({ pageId: 'home', requestId: 'get_rows', saveResponse: 1, honoContext })
|
|
166
|
+
).rejects.toThrow(ConfigError);
|
|
167
|
+
expect(mockBuildPageIfNeeded).not.toHaveBeenCalled();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('runRequest notes stale config on a request that fails', async () => {
|
|
171
|
+
mockReviewPage.mockResolvedValue('edited');
|
|
172
|
+
mockCallRequest.mockRejectedValue(new Error('connect ECONNREFUSED'));
|
|
173
|
+
|
|
174
|
+
const result = await runRequest({ pageId: 'home', requestId: 'get_rows', honoContext });
|
|
175
|
+
|
|
176
|
+
expect(result.error.message).toBe('connect ECONNREFUSED');
|
|
177
|
+
expect(result.staleConfig).toContain('ran the previous config');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test('runRequest reviews only the page it ran', async () => {
|
|
181
|
+
await runRequest({ pageId: 'home', requestId: 'get_rows', honoContext });
|
|
182
|
+
|
|
183
|
+
expect(mockReviewPage).toHaveBeenCalledTimes(1);
|
|
184
|
+
expect(mockReviewPage).toHaveBeenCalledWith(
|
|
185
|
+
expect.objectContaining({
|
|
186
|
+
pageId: 'home',
|
|
187
|
+
entry: { pageId: 'home', refPath: 'pages/home.yaml' },
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
});
|
|
@@ -34,7 +34,7 @@ function saveAnnotatedScreenshot({ dataUrl, fileName }) {
|
|
|
34
34
|
const dir = path.join(configDirectory, '.lowdefy', 'annotations');
|
|
35
35
|
fs.mkdirSync(dir, { recursive: true });
|
|
36
36
|
fs.writeFileSync(path.join(dir, fileName), buffer);
|
|
37
|
-
return { path: path.join('.lowdefy', 'annotations', fileName) };
|
|
37
|
+
return { path: path.posix.join('.lowdefy', 'annotations', fileName) };
|
|
38
38
|
} catch (error) {
|
|
39
39
|
return { error: `Failed to save annotated screenshot: ${error.message}` };
|
|
40
40
|
}
|
package/lib/docs/scaffoldPage.js
CHANGED
|
@@ -49,6 +49,8 @@ function scaffoldPage({ pageId, title }) {
|
|
|
49
49
|
|
|
50
50
|
const configDirectory = process.env.LOWDEFY_DIRECTORY_CONFIG || process.cwd();
|
|
51
51
|
const pageRelativePath = path.join('pages', `${pageId}.yaml`);
|
|
52
|
+
// _ref paths in config use forward slashes on every platform.
|
|
53
|
+
const pageRefPath = path.posix.join('pages', `${pageId}.yaml`);
|
|
52
54
|
const pageFilePath = path.join(configDirectory, pageRelativePath);
|
|
53
55
|
|
|
54
56
|
if (fs.existsSync(pageFilePath)) {
|
|
@@ -68,7 +70,7 @@ function scaffoldPage({ pageId, title }) {
|
|
|
68
70
|
|
|
69
71
|
return {
|
|
70
72
|
created: pageRelativePath,
|
|
71
|
-
next: `Add \`- _ref: ${
|
|
73
|
+
next: `Add \`- _ref: ${pageRefPath}\` under \`pages:\` in lowdefy.yaml (or the relevant pages file), then check lowdefy_build_status.`,
|
|
72
74
|
};
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -15,8 +15,19 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { type } from '@lowdefy/helpers';
|
|
18
|
+
import { normaliseJourneySteps, validateJourneySteps } from '@lowdefy/node-utils';
|
|
18
19
|
|
|
19
20
|
import { getBrowser, openPage, buildPageUrl } from './getBrowser.js';
|
|
21
|
+
import noBrowserError from './noBrowserError.js';
|
|
22
|
+
import openJourney from './openJourney.js';
|
|
23
|
+
import resolvePageInstance from './resolvePageInstance.js';
|
|
24
|
+
import resolveToolCaller from './resolveToolCaller.js';
|
|
25
|
+
import runJourneySteps from './runJourneySteps.js';
|
|
26
|
+
import unsettledPageNote from './unsettledPageNote.js';
|
|
27
|
+
import validateJourneyMail from './validateJourneyMail.js';
|
|
28
|
+
import validateViewport from './validateViewport.js';
|
|
29
|
+
import withBrowserSlot from './withBrowserSlot.js';
|
|
30
|
+
import withDataSession from './withDataSession.js';
|
|
20
31
|
|
|
21
32
|
// A feedback annotation's elementRect/shapes are captured in the developer's
|
|
22
33
|
// live tab, viewport-relative at whatever scroll position they were at
|
|
@@ -51,18 +62,59 @@ async function resolveClip({ page, clip, scrollX, scrollY }) {
|
|
|
51
62
|
};
|
|
52
63
|
}
|
|
53
64
|
|
|
65
|
+
// Opens the page and, when `steps` are given, runs them before the capture —
|
|
66
|
+
// the same step engine lowdefy_run_journey uses, so an agent can capture an
|
|
67
|
+
// open dropdown, a picker's calendar or a modal. Returns the page, a cleanup
|
|
68
|
+
// function, the page-open timeout, any captures `screenshot` steps took and any
|
|
69
|
+
// step failure; the capture happens on whatever state the steps left (also
|
|
70
|
+
// after a failed step, which is what the agent needs to see).
|
|
71
|
+
async function openAndRunSteps({ browser, steps, stepTimeout, users, ...pageOptions }) {
|
|
72
|
+
if (steps.length === 0) {
|
|
73
|
+
const opened = await openPage({ browser, ...pageOptions });
|
|
74
|
+
return {
|
|
75
|
+
opened,
|
|
76
|
+
close: () => opened.context.close(),
|
|
77
|
+
openTimeout: pageOptions.timeout,
|
|
78
|
+
screenshots: [],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const { journey, main } = await openJourney({ browser, ...pageOptions, stepTimeout, users });
|
|
82
|
+
const { screenshots, failure } = await runJourneySteps({ journey, steps });
|
|
83
|
+
return {
|
|
84
|
+
opened: { ...journey.actors.current(), ready: main.ready },
|
|
85
|
+
close: journey.actors.closeAll,
|
|
86
|
+
openTimeout: journey.openTimeout,
|
|
87
|
+
screenshots,
|
|
88
|
+
failure,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
54
92
|
// screenshotPage lets an agent visually verify a page rendered by the
|
|
55
|
-
// running dev server.
|
|
93
|
+
// running dev server, at a given viewport size and colour scheme. `pathParams`
|
|
94
|
+
// fill a patterned page's placeholders, `urlQuery` opens the page at a query
|
|
95
|
+
// string, and `steps` (see validateJourneySteps)
|
|
96
|
+
// drive it first — open a dropdown, click a button — so the capture shows
|
|
97
|
+
// that state; captures `screenshot` steps take come back as `screenshots`.
|
|
98
|
+
// Popups antd renders in a portal at the end of <body> are part of the
|
|
99
|
+
// document, so a fullPage or clip capture includes them. `user` and `data`
|
|
100
|
+
// name the caller as a journey's do (see resolveToolCaller): a data set user
|
|
101
|
+
// renders the page on that data set's own database.
|
|
56
102
|
async function screenshotPage({
|
|
57
103
|
origin,
|
|
58
104
|
pageId,
|
|
105
|
+
pathParams,
|
|
106
|
+
urlQuery,
|
|
107
|
+
steps: inputSteps = [],
|
|
108
|
+
stepTimeout = 5000,
|
|
59
109
|
fullPage = false,
|
|
60
110
|
clip,
|
|
61
111
|
scrollX = 0,
|
|
62
112
|
scrollY = 0,
|
|
63
113
|
user,
|
|
114
|
+
data,
|
|
64
115
|
width = 1280,
|
|
65
116
|
height = 800,
|
|
117
|
+
colorScheme = 'light',
|
|
66
118
|
timeout = 15000,
|
|
67
119
|
}) {
|
|
68
120
|
if (type.isNone(origin) || !type.isString(origin)) {
|
|
@@ -75,34 +127,134 @@ async function screenshotPage({
|
|
|
75
127
|
error: `screenshotPage requires a "pageId" string. Received ${JSON.stringify(pageId)}.`,
|
|
76
128
|
};
|
|
77
129
|
}
|
|
130
|
+
if (!type.isNone(urlQuery) && !type.isObject(urlQuery)) {
|
|
131
|
+
return {
|
|
132
|
+
error: `screenshotPage requires "urlQuery" to be an object. Received ${JSON.stringify(
|
|
133
|
+
urlQuery
|
|
134
|
+
)}.`,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const instance = resolvePageInstance({ pageId, pathParams });
|
|
138
|
+
if (!type.isUndefined(instance.error)) {
|
|
139
|
+
return { error: instance.error, invalidInput: true };
|
|
140
|
+
}
|
|
141
|
+
const { error: stepsError } = validateJourneySteps({ steps: inputSteps });
|
|
142
|
+
if (!type.isUndefined(stepsError)) {
|
|
143
|
+
return { error: stepsError };
|
|
144
|
+
}
|
|
145
|
+
const steps = normaliseJourneySteps({ steps: inputSteps });
|
|
146
|
+
const viewportError = validateViewport({ width, height, colorScheme });
|
|
147
|
+
if (!type.isUndefined(viewportError)) {
|
|
148
|
+
return { error: viewportError };
|
|
149
|
+
}
|
|
150
|
+
const mailError = validateJourneyMail({ steps });
|
|
151
|
+
if (!type.isUndefined(mailError)) {
|
|
152
|
+
return { error: mailError };
|
|
153
|
+
}
|
|
154
|
+
const caller = await resolveToolCaller({ user, data });
|
|
155
|
+
if (!type.isUndefined(caller.error)) {
|
|
156
|
+
return caller;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return withBrowserSlot({
|
|
160
|
+
task: () =>
|
|
161
|
+
withDataSession({
|
|
162
|
+
dataSet: caller.dataSet,
|
|
163
|
+
task: ({ dataCookie }) =>
|
|
164
|
+
screenshotInBrowser({
|
|
165
|
+
origin,
|
|
166
|
+
pageId,
|
|
167
|
+
path: instance.path,
|
|
168
|
+
pathParams,
|
|
169
|
+
urlQuery,
|
|
170
|
+
user: caller.user,
|
|
171
|
+
dataCookie,
|
|
172
|
+
users: caller.dataSet?.users,
|
|
173
|
+
width,
|
|
174
|
+
height,
|
|
175
|
+
colorScheme,
|
|
176
|
+
timeout,
|
|
177
|
+
steps,
|
|
178
|
+
stepTimeout,
|
|
179
|
+
clip,
|
|
180
|
+
scrollX,
|
|
181
|
+
scrollY,
|
|
182
|
+
fullPage,
|
|
183
|
+
}),
|
|
184
|
+
}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
78
187
|
|
|
188
|
+
// The part of screenshotPage that runs in the browser, inside a browser slot.
|
|
189
|
+
async function screenshotInBrowser({
|
|
190
|
+
origin,
|
|
191
|
+
pageId,
|
|
192
|
+
path,
|
|
193
|
+
pathParams,
|
|
194
|
+
urlQuery,
|
|
195
|
+
user,
|
|
196
|
+
dataCookie,
|
|
197
|
+
users,
|
|
198
|
+
width,
|
|
199
|
+
height,
|
|
200
|
+
colorScheme,
|
|
201
|
+
timeout,
|
|
202
|
+
steps,
|
|
203
|
+
stepTimeout,
|
|
204
|
+
clip,
|
|
205
|
+
scrollX,
|
|
206
|
+
scrollY,
|
|
207
|
+
fullPage,
|
|
208
|
+
}) {
|
|
79
209
|
let browser;
|
|
80
210
|
try {
|
|
81
211
|
browser = await getBrowser();
|
|
82
212
|
} catch (error) {
|
|
83
|
-
return {
|
|
84
|
-
error: `No Chromium available. Run: npx playwright install chromium (${error.message})`,
|
|
85
|
-
};
|
|
213
|
+
return { error: noBrowserError(error) };
|
|
86
214
|
}
|
|
87
215
|
|
|
88
|
-
const url = buildPageUrl({ origin, pageId });
|
|
216
|
+
const url = buildPageUrl({ origin, pageId, path, pathParams, urlQuery });
|
|
89
217
|
|
|
90
|
-
let
|
|
218
|
+
let close;
|
|
91
219
|
try {
|
|
92
|
-
const
|
|
93
|
-
|
|
220
|
+
const run = await openAndRunSteps({
|
|
221
|
+
browser,
|
|
222
|
+
origin,
|
|
223
|
+
pageId,
|
|
224
|
+
path,
|
|
225
|
+
pathParams,
|
|
226
|
+
urlQuery,
|
|
227
|
+
user,
|
|
228
|
+
dataCookie,
|
|
229
|
+
users,
|
|
230
|
+
width,
|
|
231
|
+
height,
|
|
232
|
+
colorScheme,
|
|
233
|
+
timeout,
|
|
234
|
+
steps,
|
|
235
|
+
stepTimeout,
|
|
236
|
+
});
|
|
237
|
+
const { opened, openTimeout, screenshots, failure } = run;
|
|
238
|
+
close = run.close;
|
|
94
239
|
// Let post-load rendering (fonts, transitions, client-side state) settle.
|
|
95
240
|
await opened.page.waitForTimeout(300);
|
|
96
241
|
|
|
97
242
|
const docClip = await resolveClip({ page: opened.page, clip, scrollX, scrollY });
|
|
98
243
|
const screenshotOptions = docClip ? { type: 'png', clip: docClip } : { type: 'png', fullPage };
|
|
99
244
|
const buffer = await opened.page.screenshot(screenshotOptions);
|
|
100
|
-
|
|
245
|
+
const result = { data: buffer.toString('base64'), mimeType: 'image/png', screenshots };
|
|
246
|
+
if (!type.isUndefined(failure)) {
|
|
247
|
+
result.failure = failure;
|
|
248
|
+
}
|
|
249
|
+
if (!opened.ready) {
|
|
250
|
+
return { ...result, ready: false, note: unsettledPageNote({ timeout: openTimeout }) };
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
101
253
|
} catch (error) {
|
|
102
254
|
return { error: `Failed to screenshot "${url}": ${error.message}` };
|
|
103
255
|
} finally {
|
|
104
|
-
if (
|
|
105
|
-
await
|
|
256
|
+
if (close) {
|
|
257
|
+
await close();
|
|
106
258
|
}
|
|
107
259
|
}
|
|
108
260
|
}
|