@lowdefy/server-dev 6.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +63 -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 +224 -39
- 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 +67 -0
- package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
- package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +679 -0
- package/lib/docs/dataSets/describeDataSetResult.js +25 -0
- package/lib/docs/dataSets/describeDatabaseTarget.js +46 -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/findCollectionCollisions.js +52 -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 +44 -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 +119 -0
- package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +343 -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 +68 -0
- package/lib/docs/dataSets/readDataSet.test.mjs +220 -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/sweepOrphanDatabases.js +33 -0
- package/lib/docs/dataSets/test/app/lowdefy.yaml +203 -0
- package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
- package/lib/docs/dataSets/test/app/tests/data/shop.yaml +8 -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 +54 -6
- package/lib/docs/getBuildStatus.test.mjs +133 -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 +83 -0
- package/lib/server/applyDataSetRedirect.test.mjs +340 -0
- package/lib/server/applyDataSetRedirectTenantReach.test.mjs +204 -0
- package/lib/server/applyDataSetRedirectWalled.test.mjs +131 -0
- package/lib/server/auth/createSystemContext.js +73 -0
- package/lib/server/auth/getAuth.js +46 -0
- 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/{getAuthConfig.js → getStrategies.js} +12 -10
- 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 +174 -41
- package/lib/server/createLowdefyContext.test.mjs +437 -0
- 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 +77 -0
- package/lib/server/log/logRequest.js +2 -3
- package/lib/server/mutantDataSetWrappers.test.mjs +112 -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/scrubSecrets.js +23 -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 +24 -2
- package/manager/processes/initialBuild.mjs +14 -0
- package/manager/processes/initialBuild.test.mjs +101 -0
- package/manager/processes/lowdefyBuild.mjs +30 -7
- package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +62 -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 +61 -49
- package/package.original.json +61 -49
- 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 +196 -14
- 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 +4 -2
- 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/lib/server/auth/session.js +0 -39
- 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
|
@@ -18,6 +18,7 @@ import { type } from '@lowdefy/helpers';
|
|
|
18
18
|
|
|
19
19
|
import { buildPageUrl } from './getBrowser.js';
|
|
20
20
|
import inspectState from './inspectState.js';
|
|
21
|
+
import readPagePath from './readPagePath.js';
|
|
21
22
|
import { writeCheckpoint } from './checkpointStore.js';
|
|
22
23
|
|
|
23
24
|
// Snapshots the running app (page state + recorded request/api responses)
|
|
@@ -25,8 +26,10 @@ import { writeCheckpoint } from './checkpointStore.js';
|
|
|
25
26
|
// on-disk layout. Reuses inspectState.js's tab-first/headless-fallback
|
|
26
27
|
// snapshot rather than driving a browser directly, so a snapshot always
|
|
27
28
|
// reflects whatever a developer is actually looking at when one is
|
|
28
|
-
// available.
|
|
29
|
-
|
|
29
|
+
// available. With only a `pageId`, a tab's snapshot is of the instance it
|
|
30
|
+
// names (see findPageInstance in tabChannel.js), and the checkpoint keeps that
|
|
31
|
+
// instance's pathParams.
|
|
32
|
+
async function snapshotState({ origin, pageId, pathParams, name, notes, source, overwrite }) {
|
|
30
33
|
if (type.isNone(pageId) || !type.isString(pageId)) {
|
|
31
34
|
return {
|
|
32
35
|
error: `snapshotState requires a "pageId" string. Received ${JSON.stringify(pageId)}.`,
|
|
@@ -36,12 +39,17 @@ async function snapshotState({ origin, pageId, name, notes, source, overwrite })
|
|
|
36
39
|
return { error: `snapshotState requires a "name" string. Received ${JSON.stringify(name)}.` };
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
const snapshot = await inspectState({ origin, pageId, source });
|
|
42
|
+
const snapshot = await inspectState({ origin, pageId, pathParams, source });
|
|
40
43
|
if (snapshot?.error) {
|
|
41
|
-
return { error: snapshot.error };
|
|
44
|
+
return { error: snapshot.error, invalidInput: snapshot.invalidInput };
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
const url = `${buildPageUrl({
|
|
47
|
+
const url = `${buildPageUrl({
|
|
48
|
+
origin,
|
|
49
|
+
pageId,
|
|
50
|
+
path: readPagePath({ pageId }),
|
|
51
|
+
pathParams: snapshot.pathParams,
|
|
52
|
+
})}${snapshot.urlQuery ?? ''}`;
|
|
45
53
|
|
|
46
54
|
try {
|
|
47
55
|
const { dir, parts } = writeCheckpoint({
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
const MIN_TERM_LENGTH = 3;
|
|
18
|
+
|
|
19
|
+
const COMMON_WORDS = new Set([
|
|
20
|
+
'about',
|
|
21
|
+
'all',
|
|
22
|
+
'and',
|
|
23
|
+
'any',
|
|
24
|
+
'are',
|
|
25
|
+
'can',
|
|
26
|
+
'does',
|
|
27
|
+
'for',
|
|
28
|
+
'from',
|
|
29
|
+
'has',
|
|
30
|
+
'have',
|
|
31
|
+
'how',
|
|
32
|
+
'into',
|
|
33
|
+
'not',
|
|
34
|
+
'the',
|
|
35
|
+
'this',
|
|
36
|
+
'that',
|
|
37
|
+
'use',
|
|
38
|
+
'using',
|
|
39
|
+
'what',
|
|
40
|
+
'when',
|
|
41
|
+
'where',
|
|
42
|
+
'which',
|
|
43
|
+
'who',
|
|
44
|
+
'why',
|
|
45
|
+
'will',
|
|
46
|
+
'with',
|
|
47
|
+
'you',
|
|
48
|
+
'your',
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
// Underscores, dots and dollar signs stay inside a term, so operator names
|
|
52
|
+
// like _string.concat and $match are searched as written.
|
|
53
|
+
function splitSearchTerms({ query }) {
|
|
54
|
+
const terms = query
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.split(/[^\p{L}\p{N}_.$]+/u)
|
|
57
|
+
.map((term) => term.replace(/^\.+|\.+$/g, ''))
|
|
58
|
+
.filter((term) => term.length >= MIN_TERM_LENGTH && !COMMON_WORDS.has(term));
|
|
59
|
+
return [...new Set(terms)];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default splitSearchTerms;
|
|
@@ -0,0 +1,50 @@
|
|
|
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 splitSearchTerms from './splitSearchTerms.js';
|
|
18
|
+
|
|
19
|
+
test('splitSearchTerms lowercases and splits on spaces and punctuation', () => {
|
|
20
|
+
expect(splitSearchTerms({ query: 'Link action, pathParams? (path parameters)' })).toEqual([
|
|
21
|
+
'link',
|
|
22
|
+
'action',
|
|
23
|
+
'pathparams',
|
|
24
|
+
'path',
|
|
25
|
+
'parameters',
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('splitSearchTerms drops one- and two-letter terms and common words', () => {
|
|
30
|
+
expect(splitSearchTerms({ query: 'How to use a Modal with the blocks' })).toEqual([
|
|
31
|
+
'modal',
|
|
32
|
+
'blocks',
|
|
33
|
+
]);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('splitSearchTerms counts a repeated term once', () => {
|
|
37
|
+
expect(splitSearchTerms({ query: 'state State STATE page' })).toEqual(['state', 'page']);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('splitSearchTerms keeps operator names whole', () => {
|
|
41
|
+
expect(splitSearchTerms({ query: '_string.concat $match _if.' })).toEqual([
|
|
42
|
+
'_string.concat',
|
|
43
|
+
'$match',
|
|
44
|
+
'_if',
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('splitSearchTerms returns no terms when every word is dropped', () => {
|
|
49
|
+
expect(splitSearchTerms({ query: 'to a of' })).toEqual([]);
|
|
50
|
+
});
|
|
@@ -29,6 +29,13 @@ const previousConfigDirectory = process.env.LOWDEFY_DIRECTORY_CONFIG;
|
|
|
29
29
|
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-state-checkpoints-test-'));
|
|
30
30
|
fs.mkdirSync(path.join(fixtureDir, 'build'), { recursive: true });
|
|
31
31
|
fs.writeFileSync(path.join(fixtureDir, 'build', 'config.json'), JSON.stringify({ basePath: '' }));
|
|
32
|
+
fs.writeFileSync(
|
|
33
|
+
path.join(fixtureDir, 'build', 'routes.json'),
|
|
34
|
+
JSON.stringify([
|
|
35
|
+
{ pageId: 'home', path: 'home' },
|
|
36
|
+
{ pageId: 'ticket', path: 'tickets/{space}/{ticket_id}' },
|
|
37
|
+
])
|
|
38
|
+
);
|
|
32
39
|
process.chdir(fixtureDir);
|
|
33
40
|
process.env.LOWDEFY_DIRECTORY_CONFIG = fixtureDir;
|
|
34
41
|
|
|
@@ -39,6 +46,8 @@ process.env.LOWDEFY_DIRECTORY_CONFIG = fixtureDir;
|
|
|
39
46
|
jest.unstable_mockModule('playwright-core', () => ({
|
|
40
47
|
chromium: { launch: jest.fn().mockRejectedValue(new Error("Executable doesn't exist")) },
|
|
41
48
|
}));
|
|
49
|
+
// A missing shell would otherwise start a real download.
|
|
50
|
+
jest.unstable_mockModule('./installHeadlessShell.js', () => ({ default: () => null }));
|
|
42
51
|
|
|
43
52
|
const mockInspectState = jest.fn();
|
|
44
53
|
jest.unstable_mockModule('./inspectState.js', () => ({
|
|
@@ -101,6 +110,7 @@ describe('checkpointStore', () => {
|
|
|
101
110
|
'checkpoint.json',
|
|
102
111
|
'state.json',
|
|
103
112
|
'urlQuery.json',
|
|
113
|
+
'pathParams.json',
|
|
104
114
|
'inputs.json',
|
|
105
115
|
'user.json',
|
|
106
116
|
'global.json',
|
|
@@ -118,6 +128,7 @@ describe('checkpointStore', () => {
|
|
|
118
128
|
expect(typeof result.checkpoint.capturedAt).toBe('string');
|
|
119
129
|
expect(result.state).toEqual({ count: 1 });
|
|
120
130
|
expect(result.urlQuery).toBe('?a=1');
|
|
131
|
+
expect(result.pathParams).toEqual({});
|
|
121
132
|
expect(result.input).toEqual({ fromInput: true });
|
|
122
133
|
expect(result.user).toEqual({ id: 'u1' });
|
|
123
134
|
expect(result.global).toEqual({ g: 1 });
|
|
@@ -168,6 +179,7 @@ describe('checkpointStore', () => {
|
|
|
168
179
|
const result = readCheckpoint({ name: 'cp-partial' });
|
|
169
180
|
expect(result.state).toEqual({});
|
|
170
181
|
expect(result.urlQuery).toBe('');
|
|
182
|
+
expect(result.pathParams).toEqual({});
|
|
171
183
|
expect(result.input).toEqual({});
|
|
172
184
|
expect(result.user).toBe(null);
|
|
173
185
|
expect(result.global).toEqual({});
|
|
@@ -293,6 +305,32 @@ describe('snapshotState', () => {
|
|
|
293
305
|
expect(stored.checkpoint.url).toBe('http://localhost:3111/home');
|
|
294
306
|
});
|
|
295
307
|
|
|
308
|
+
test('keeps the pathParams of the instance it snapshots', async () => {
|
|
309
|
+
mockInspectState.mockResolvedValue({
|
|
310
|
+
pageId: 'ticket',
|
|
311
|
+
pathParams: { space: 's', ticket_id: '1' },
|
|
312
|
+
state: { subject: 'One' },
|
|
313
|
+
requests: {},
|
|
314
|
+
urlQuery: '?tab=notes',
|
|
315
|
+
source: 'tab',
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
const result = await snapshotState({
|
|
319
|
+
origin: 'http://localhost:3111',
|
|
320
|
+
pageId: 'ticket',
|
|
321
|
+
name: 'cp-snapshot-instance',
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
expect(result.error).toBeUndefined();
|
|
325
|
+
expect(result.parts).toContain('pathParams.json');
|
|
326
|
+
expect(mockInspectState).toHaveBeenCalledWith(
|
|
327
|
+
expect.objectContaining({ pageId: 'ticket', pathParams: undefined })
|
|
328
|
+
);
|
|
329
|
+
const stored = readCheckpoint({ name: 'cp-snapshot-instance' });
|
|
330
|
+
expect(stored.pathParams).toEqual({ space: 's', ticket_id: '1' });
|
|
331
|
+
expect(stored.checkpoint.url).toBe('http://localhost:3111/tickets/s/1?tab=notes');
|
|
332
|
+
});
|
|
333
|
+
|
|
296
334
|
test('returns the inspectState error instead of writing a checkpoint', async () => {
|
|
297
335
|
mockInspectState.mockResolvedValue({ error: 'No live context for page "home".' });
|
|
298
336
|
|
|
@@ -337,6 +375,45 @@ describe('loadState', () => {
|
|
|
337
375
|
});
|
|
338
376
|
});
|
|
339
377
|
|
|
378
|
+
test('registry-only mode opens the page instance the checkpoint names', async () => {
|
|
379
|
+
writeCheckpoint({
|
|
380
|
+
name: 'cp-load-instance',
|
|
381
|
+
snapshot: buildSnapshot({
|
|
382
|
+
pageId: 'ticket',
|
|
383
|
+
pathParams: { space: 's', ticket_id: '1' },
|
|
384
|
+
urlQuery: '?tab=notes',
|
|
385
|
+
}),
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
const result = await loadState({
|
|
389
|
+
origin: 'http://localhost:3111',
|
|
390
|
+
name: 'cp-load-instance',
|
|
391
|
+
mode: 'registry-only',
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
expect(result.error).toBeUndefined();
|
|
395
|
+
expect(result.url).toBe(
|
|
396
|
+
'http://localhost:3111/tickets/s/1?tab=notes&_checkpoint=cp-load-instance'
|
|
397
|
+
);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test('refuses a checkpoint whose pathParams miss a placeholder of its page', async () => {
|
|
401
|
+
writeCheckpoint({
|
|
402
|
+
name: 'cp-load-missing',
|
|
403
|
+
snapshot: buildSnapshot({ pageId: 'ticket', pathParams: { space: 's' }, urlQuery: '' }),
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const result = await loadState({
|
|
407
|
+
origin: 'http://localhost:3111',
|
|
408
|
+
name: 'cp-load-missing',
|
|
409
|
+
mode: 'registry-only',
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
expect(result.error).toMatch(
|
|
413
|
+
/Checkpoint "cp-load-missing" cannot open its page: .*placeholder "ticket_id"/
|
|
414
|
+
);
|
|
415
|
+
});
|
|
416
|
+
|
|
340
417
|
test('returns an error when the checkpoint does not exist', async () => {
|
|
341
418
|
const result = await loadState({
|
|
342
419
|
origin: 'http://localhost:3111',
|
package/lib/docs/tabAvailable.js
CHANGED
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
|
|
17
17
|
import { type } from '@lowdefy/helpers';
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { findPageInstance } from './tabChannel.js';
|
|
20
20
|
|
|
21
21
|
// Shared by inspectState.js and evalOperator.js to decide, when no explicit
|
|
22
|
-
// `source` is requested, whether a live browser tab
|
|
23
|
-
// fresh headless one.
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
// `source` is requested, whether a live browser tab holds the requested page
|
|
23
|
+
// instance, to prefer over a fresh headless one. A tab the dev server's own
|
|
24
|
+
// headless browser opened is not one to prefer (findPageInstance never picks it).
|
|
25
|
+
function tabAvailable({ pageId, pathParams }) {
|
|
26
|
+
return !type.isNone(findPageInstance({ pageId, pathParams }));
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export default tabAvailable;
|
package/lib/docs/tabChannel.js
CHANGED
|
@@ -29,24 +29,52 @@ import { type } from '@lowdefy/helpers';
|
|
|
29
29
|
const tabs = new Map();
|
|
30
30
|
const pendingRequests = new Map();
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
// Orders instance renders across tabs, so "most recently rendered" does not hang on clock ticks.
|
|
33
|
+
let renderSequence = 0;
|
|
34
|
+
|
|
35
|
+
// A tab the dev server's own headless browser opened (a journey run, a
|
|
36
|
+
// headless tool) is automated: it is listed, so the hub keeps
|
|
37
|
+
// the server alive while it runs, but findPageInstance never picks it for an
|
|
38
|
+
// agent's or developer's live-tab request.
|
|
39
|
+
function registerTab({ id, send, source = 'dev', automated = false }) {
|
|
33
40
|
if (type.isNone(id) || !type.isString(id)) {
|
|
34
41
|
throw new Error(`registerTab requires an "id" string. Received ${JSON.stringify(id)}.`);
|
|
35
42
|
}
|
|
36
43
|
if (!type.isFunction(send)) {
|
|
37
44
|
throw new Error('registerTab requires a "send" function.');
|
|
38
45
|
}
|
|
39
|
-
tabs.set(id, {
|
|
46
|
+
tabs.set(id, {
|
|
47
|
+
id,
|
|
48
|
+
page: null,
|
|
49
|
+
rendered: new Map(),
|
|
50
|
+
send,
|
|
51
|
+
source,
|
|
52
|
+
automated,
|
|
53
|
+
connectedAt: new Date(),
|
|
54
|
+
});
|
|
40
55
|
}
|
|
41
56
|
|
|
42
|
-
|
|
57
|
+
// Records the page instance a tab shows, { pageId, pathParams, instanceKey }, and keeps it among
|
|
58
|
+
// the instances the tab has rendered, whose contexts the tab still holds.
|
|
59
|
+
function updateTabPage({ id, pageId, pathParams, instanceKey }) {
|
|
43
60
|
const tab = tabs.get(id);
|
|
44
61
|
if (type.isNone(tab)) {
|
|
45
62
|
// The tab may have disconnected between the client sending the ping and
|
|
46
63
|
// it arriving — nothing to update, and not worth failing the request.
|
|
47
64
|
return;
|
|
48
65
|
}
|
|
49
|
-
|
|
66
|
+
if (!type.isString(pageId) || !type.isString(instanceKey)) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`updateTabPage requires "pageId" and "instanceKey" strings. Received ${JSON.stringify({
|
|
69
|
+
pageId,
|
|
70
|
+
instanceKey,
|
|
71
|
+
})}.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
renderSequence += 1;
|
|
75
|
+
const page = { pageId, pathParams: pathParams ?? {}, instanceKey };
|
|
76
|
+
tab.page = page;
|
|
77
|
+
tab.rendered.set(instanceKey, { ...page, sequence: renderSequence });
|
|
50
78
|
}
|
|
51
79
|
|
|
52
80
|
function unregisterTab({ id }) {
|
|
@@ -54,37 +82,101 @@ function unregisterTab({ id }) {
|
|
|
54
82
|
}
|
|
55
83
|
|
|
56
84
|
function listTabs() {
|
|
57
|
-
return Array.from(tabs.values()).map(({ id,
|
|
85
|
+
return Array.from(tabs.values()).map(({ id, page, source, automated, connectedAt }) => ({
|
|
58
86
|
id,
|
|
59
|
-
pageId,
|
|
87
|
+
pageId: page?.pageId ?? null,
|
|
88
|
+
pathParams: page?.pathParams ?? null,
|
|
89
|
+
instanceKey: page?.instanceKey ?? null,
|
|
90
|
+
source,
|
|
91
|
+
automated,
|
|
60
92
|
connectedAt,
|
|
61
93
|
}));
|
|
62
94
|
}
|
|
63
95
|
|
|
64
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
96
|
+
// Path values compare as strings: a tab reports them decoded from its URL, an agent may pass
|
|
97
|
+
// a number.
|
|
98
|
+
function pathParamsMatch({ requested, rendered }) {
|
|
99
|
+
if (type.isNone(requested)) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
const requestedKeys = Object.keys(requested);
|
|
103
|
+
if (requestedKeys.length !== Object.keys(rendered).length) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
return requestedKeys.every((key) => String(requested[key]) === rendered[key]);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function instanceMatches({ instance, pageId, pathParams }) {
|
|
110
|
+
if (type.isNone(pageId)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return (
|
|
114
|
+
instance.pageId === pageId &&
|
|
115
|
+
pathParamsMatch({ requested: pathParams, rendered: instance.pathParams })
|
|
70
116
|
);
|
|
71
|
-
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Which tab and page instance a dev tool request reads, as { tab, pageId, pathParams,
|
|
120
|
+
// instanceKey }, or undefined when no tab has one:
|
|
121
|
+
// - the instance on screen in a tab, when one shows the requested page (and values), the most
|
|
122
|
+
// recently connected such tab first (Map order: a reconnect deletes and re-sets the tab id);
|
|
123
|
+
// - else the requested page's most recently rendered instance (with those values) in any tab,
|
|
124
|
+
// whose context that tab still holds.
|
|
125
|
+
// A request naming no page reads the instance on screen in the most recently connected tab.
|
|
126
|
+
// Automated tabs are skipped.
|
|
127
|
+
function findPageInstance({ pageId, pathParams } = {}) {
|
|
128
|
+
const candidates = Array.from(tabs.values()).filter((tab) => !tab.automated);
|
|
129
|
+
const onScreen = candidates.filter(
|
|
130
|
+
(tab) => !type.isNone(tab.page) && instanceMatches({ instance: tab.page, pageId, pathParams })
|
|
131
|
+
);
|
|
132
|
+
if (onScreen.length > 0) {
|
|
133
|
+
const tab = onScreen[onScreen.length - 1];
|
|
134
|
+
return { tab, ...tab.page };
|
|
135
|
+
}
|
|
136
|
+
if (type.isNone(pageId)) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
let found;
|
|
140
|
+
candidates.forEach((tab) => {
|
|
141
|
+
tab.rendered.forEach((instance) => {
|
|
142
|
+
if (!instanceMatches({ instance, pageId, pathParams })) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (type.isNone(found) || instance.sequence > found.sequence) {
|
|
146
|
+
found = { tab, ...instance };
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
if (type.isNone(found)) {
|
|
72
151
|
return undefined;
|
|
73
152
|
}
|
|
74
|
-
|
|
153
|
+
const { sequence, ...instance } = found;
|
|
154
|
+
return instance;
|
|
75
155
|
}
|
|
76
156
|
|
|
77
|
-
function
|
|
157
|
+
function describeRequestedPage({ pageId, pathParams }) {
|
|
158
|
+
if (type.isNone(pageId)) {
|
|
159
|
+
return 'any page';
|
|
160
|
+
}
|
|
161
|
+
if (type.isNone(pathParams)) {
|
|
162
|
+
return `page "${pageId}"`;
|
|
163
|
+
}
|
|
164
|
+
return `page "${pageId}" with pathParams ${JSON.stringify(pathParams)}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function requestFromTab({ pageId, pathParams, event, payload = {}, timeout = 5000 }) {
|
|
78
168
|
if (type.isNone(event) || !type.isString(event)) {
|
|
79
169
|
throw new Error(
|
|
80
170
|
`requestFromTab requires an "event" string. Received ${JSON.stringify(event)}.`
|
|
81
171
|
);
|
|
82
172
|
}
|
|
83
|
-
const
|
|
84
|
-
if (type.isNone(
|
|
85
|
-
const location = type.isNone(pageId) ? 'any page' : `page "${pageId}"`;
|
|
173
|
+
const instance = findPageInstance({ pageId, pathParams });
|
|
174
|
+
if (type.isNone(instance)) {
|
|
86
175
|
return Promise.resolve({
|
|
87
|
-
error: `No browser tab connected on ${
|
|
176
|
+
error: `No browser tab connected on ${describeRequestedPage({
|
|
177
|
+
pageId,
|
|
178
|
+
pathParams,
|
|
179
|
+
})}. Ask the developer to open the page, or use source: "headless".`,
|
|
88
180
|
});
|
|
89
181
|
}
|
|
90
182
|
|
|
@@ -101,7 +193,13 @@ function requestFromTab({ pageId, event, payload = {}, timeout = 5000 }) {
|
|
|
101
193
|
resolve(result);
|
|
102
194
|
});
|
|
103
195
|
|
|
104
|
-
tab.send(event, {
|
|
196
|
+
instance.tab.send(event, {
|
|
197
|
+
requestId,
|
|
198
|
+
pageId: instance.pageId,
|
|
199
|
+
pathParams: instance.pathParams,
|
|
200
|
+
instanceKey: instance.instanceKey,
|
|
201
|
+
...payload,
|
|
202
|
+
});
|
|
105
203
|
});
|
|
106
204
|
}
|
|
107
205
|
|
|
@@ -116,4 +214,12 @@ function resolveTabRequest({ requestId, result }) {
|
|
|
116
214
|
return true;
|
|
117
215
|
}
|
|
118
216
|
|
|
119
|
-
export {
|
|
217
|
+
export {
|
|
218
|
+
findPageInstance,
|
|
219
|
+
listTabs,
|
|
220
|
+
registerTab,
|
|
221
|
+
requestFromTab,
|
|
222
|
+
resolveTabRequest,
|
|
223
|
+
unregisterTab,
|
|
224
|
+
updateTabPage,
|
|
225
|
+
};
|