@lowdefy/server-dev 6.1.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 +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 +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/{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 +328 -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 +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/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 +60 -47
- package/package.original.json +60 -47
- package/src/app.js +131 -20
- package/src/html/renderDevPage.js +63 -11
- package/src/html/renderDevPage.test.mjs +138 -0
- package/src/lib/getRequestPath.js +29 -0
- package/src/middleware/createSameOriginGuard.js +37 -0
- package/src/middleware/createSameOriginGuard.test.mjs +102 -0
- package/src/middleware/errorHandler.js +51 -1
- package/src/middleware/errorHandler.test.mjs +173 -3
- package/src/middleware/isRebindingSafeHost.js +44 -0
- package/src/middleware/isRebindingSafeHost.test.mjs +32 -0
- package/src/middleware/localDevToolsOnly.js +38 -0
- package/src/middleware/localDevToolsOnly.test.mjs +61 -0
- package/src/middleware/noStore.js +27 -0
- package/src/middleware/noStore.test.mjs +48 -0
- package/src/middleware/requestTimeout.js +38 -0
- package/src/middleware/staleFlag.js +76 -0
- package/src/middleware/staleFlag.test.mjs +127 -0
- package/src/middleware/stampCaller.js +38 -0
- package/src/middleware/stampCaller.test.mjs +64 -0
- package/src/routes/agent.js +15 -3
- package/src/routes/auth.js +29 -20
- package/src/routes/auth.test.mjs +30 -56
- package/src/routes/clientError.js +13 -13
- package/src/routes/cron.js +3 -1
- package/src/routes/cronForward.js +3 -1
- package/src/routes/detached.js +22 -9
- package/src/routes/devInspect.js +17 -8
- package/src/routes/devInspect.test.mjs +44 -7
- package/src/routes/devRecording.js +113 -0
- package/src/routes/devRecording.test.mjs +253 -0
- package/src/routes/docs/buildStatus.js +10 -2
- package/src/routes/docs/buildStatus.test.mjs +46 -0
- package/src/routes/docs/content.js +5 -3
- package/src/routes/docs/evalOperator.js +15 -1
- package/src/routes/docs/events.js +50 -0
- package/src/routes/docs/events.test.mjs +117 -0
- package/src/routes/docs/find.js +9 -1
- package/src/routes/docs/icons.js +30 -0
- package/src/routes/docs/inspectState.js +16 -1
- package/src/routes/docs/journey.js +194 -0
- package/src/routes/docs/journey.test.mjs +401 -0
- package/src/routes/docs/journeyRefusals.test.mjs +71 -0
- package/src/routes/docs/loadState.js +4 -2
- package/src/routes/docs/mcp.js +34 -3
- package/src/routes/docs/mcp.test.mjs +130 -0
- package/src/routes/docs/mutants.js +78 -0
- package/src/routes/docs/mutants.test.mjs +180 -0
- package/src/routes/docs/parsePathParamsParam.js +41 -0
- package/src/routes/docs/parsePathParamsParam.test.mjs +42 -0
- package/src/routes/docs/parseUserParam.js +27 -21
- package/src/routes/docs/parseUserParam.test.mjs +17 -5
- package/src/routes/docs/restart.js +35 -0
- package/src/routes/docs/restart.test.mjs +64 -0
- package/src/routes/docs/runEndpoint.js +54 -0
- package/src/routes/docs/runEndpoint.test.mjs +165 -0
- package/src/routes/docs/runRequest.js +25 -8
- package/src/routes/docs/runRequest.test.mjs +92 -0
- package/src/routes/docs/screenshot.js +50 -2
- package/src/routes/docs/screenshot.test.mjs +117 -0
- package/src/routes/docs/search.js +15 -2
- package/src/routes/docs/snapshotState.js +17 -3
- package/src/routes/endpoints.js +27 -26
- package/src/routes/endpointsTextAnswer.test.mjs +77 -0
- package/src/routes/feedback.js +10 -13
- package/src/routes/jitPage.js +99 -35
- package/src/routes/jitPage.test.mjs +248 -0
- package/src/routes/mcp.js +82 -5
- package/src/routes/mcp.test.mjs +215 -0
- package/src/routes/mcpMemberToken.test.mjs +125 -0
- package/src/routes/mcpProtectedResourceMetadata.js +34 -0
- package/src/routes/mcpProtectedResourceMetadata.test.mjs +65 -0
- package/src/routes/mountDevPageRoutes.js +25 -0
- package/src/routes/mountDevPageRoutes.test.mjs +30 -0
- package/src/routes/mountOauthDiscovery.js +44 -0
- package/src/routes/mountOauthDiscovery.test.mjs +79 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.js +46 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.test.mjs +62 -0
- package/src/routes/reload.js +16 -0
- package/src/routes/request.js +6 -4
- package/src/routes/request.test.mjs +9 -5
- package/src/routes/requestBody.test.mjs +159 -0
- package/src/routes/usage.js +6 -2
- package/src/routes/user.js +27 -0
- package/src/routes/websocket.js +29 -8
- package/src/routes/websocket.test.mjs +65 -0
- package/src/routes/wellKnownFallback.js +30 -0
- package/src/routes/wellKnownFallback.test.mjs +32 -0
- package/src/websocket/devWebSocket.js +41 -26
- package/src/websocket/devWebSocket.test.mjs +157 -0
- package/vite.config.js +53 -23
- package/lib/server/auth/getDevSession.js +0 -70
- package/lib/server/auth/getDevSession.test.mjs +0 -138
- package/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
- package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
- package/manager/watchers/moduleBuildWatcher.mjs +0 -79
- package/manager/watchers/serverArtifactWatcher.mjs +0 -120
- package/src/routes/apiPage.js +0 -37
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { jest } from '@jest/globals';
|
|
18
|
+
|
|
19
|
+
import checkAuthActionsRan, { armAuthActionRecorder } from './checkAuthActionsRan.js';
|
|
20
|
+
import authEngineMethods from './authEngineMethods.js';
|
|
21
|
+
|
|
22
|
+
// A page whose evaluate runs the function here, against a window made for the test.
|
|
23
|
+
function createPage(window) {
|
|
24
|
+
return {
|
|
25
|
+
evaluate: async (fn, arg) => {
|
|
26
|
+
globalThis.window = window;
|
|
27
|
+
try {
|
|
28
|
+
return await fn(arg);
|
|
29
|
+
} finally {
|
|
30
|
+
delete globalThis.window;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// The app calling an auth method while its document is the page's window.
|
|
37
|
+
async function callAuth(window, method, ...args) {
|
|
38
|
+
globalThis.window = window;
|
|
39
|
+
try {
|
|
40
|
+
return await window.auth[method](...args);
|
|
41
|
+
} finally {
|
|
42
|
+
delete globalThis.window;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function createWindow() {
|
|
47
|
+
const store = new Map();
|
|
48
|
+
const auth = {
|
|
49
|
+
login: jest.fn(async () => 'logged in'),
|
|
50
|
+
setActiveOrganization: jest.fn(async () => 'switched'),
|
|
51
|
+
updateSession: jest.fn(async () => 'synced'),
|
|
52
|
+
notAuthEngine: jest.fn(),
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
auth,
|
|
56
|
+
lowdefy: { _internal: { auth } },
|
|
57
|
+
sessionStorage: {
|
|
58
|
+
getItem: (key) => store.get(key) ?? null,
|
|
59
|
+
setItem: (key, value) => store.set(key, value),
|
|
60
|
+
removeItem: (key) => store.delete(key),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const journey = { dataSetRails: {} };
|
|
66
|
+
|
|
67
|
+
test('a click whose auth actions were skipped passes', async () => {
|
|
68
|
+
const window = createWindow();
|
|
69
|
+
const page = createPage(window);
|
|
70
|
+
await armAuthActionRecorder({ journey, page });
|
|
71
|
+
await expect(
|
|
72
|
+
checkAuthActionsRan({ journey, page, blockId: 'create_org' })
|
|
73
|
+
).resolves.toBeUndefined();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('a click that ran an auth action fails, naming the block and the action', async () => {
|
|
77
|
+
const window = createWindow();
|
|
78
|
+
const page = createPage(window);
|
|
79
|
+
await armAuthActionRecorder({ journey, page });
|
|
80
|
+
await callAuth(window, 'setActiveOrganization', { organizationId: 'a' });
|
|
81
|
+
await callAuth(window, 'updateSession');
|
|
82
|
+
await expect(checkAuthActionsRan({ journey, page, blockId: 'create_org' })).rejects.toMatchObject(
|
|
83
|
+
{
|
|
84
|
+
message: expect.stringMatching(
|
|
85
|
+
/^Block "create_org" ran the auth action SetActiveOrganization, UpdateSession, which a data-set journey cannot run: its users are injected and have no auth session\./
|
|
86
|
+
),
|
|
87
|
+
expected: 'a click that runs no auth action',
|
|
88
|
+
actual: 'a click that ran the auth action SetActiveOrganization, UpdateSession',
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
// The recording is cleared once read.
|
|
92
|
+
await expect(
|
|
93
|
+
checkAuthActionsRan({ journey, page, blockId: 'create_org' })
|
|
94
|
+
).resolves.toBeUndefined();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('a wrapped auth method still calls through with its arguments and result', async () => {
|
|
98
|
+
const window = createWindow();
|
|
99
|
+
const original = window.auth.login;
|
|
100
|
+
await armAuthActionRecorder({ journey, page: createPage(window) });
|
|
101
|
+
await expect(callAuth(window, 'login', { email: 'a@b.c' })).resolves.toBe('logged in');
|
|
102
|
+
expect(original).toHaveBeenCalledWith({ email: 'a@b.c' });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test('methods that are not auth-engine methods are not recorded', async () => {
|
|
106
|
+
const window = createWindow();
|
|
107
|
+
const page = createPage(window);
|
|
108
|
+
await armAuthActionRecorder({ journey, page });
|
|
109
|
+
await callAuth(window, 'notAuthEngine');
|
|
110
|
+
await expect(checkAuthActionsRan({ journey, page })).resolves.toBeUndefined();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('the recording is read back after the document that made it is replaced', async () => {
|
|
114
|
+
const first = createWindow();
|
|
115
|
+
await armAuthActionRecorder({ journey, page: createPage(first) });
|
|
116
|
+
await callAuth(first, 'login', {});
|
|
117
|
+
// A navigation: a new document with the same sessionStorage.
|
|
118
|
+
const second = createWindow();
|
|
119
|
+
second.sessionStorage = first.sessionStorage;
|
|
120
|
+
await expect(
|
|
121
|
+
checkAuthActionsRan({ journey, page: createPage(second), blockId: 'sign_in' })
|
|
122
|
+
).rejects.toMatchObject({ actual: 'a click that ran the auth action Login' });
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test('a click with no known block says A click', async () => {
|
|
126
|
+
const window = createWindow();
|
|
127
|
+
const page = createPage(window);
|
|
128
|
+
await armAuthActionRecorder({ journey, page });
|
|
129
|
+
await callAuth(window, 'login', {});
|
|
130
|
+
await expect(checkAuthActionsRan({ journey, page, blockId: null })).rejects.toMatchObject({
|
|
131
|
+
message: expect.stringMatching(/^A click ran the auth action Login,/),
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test('a journey without a data set is not recorded or checked', async () => {
|
|
136
|
+
const window = createWindow();
|
|
137
|
+
const page = createPage(window);
|
|
138
|
+
await armAuthActionRecorder({ journey: {}, page });
|
|
139
|
+
await callAuth(window, 'login', {});
|
|
140
|
+
expect(window.sessionStorage.getItem('__lowdefyAuthActions')).toBeNull();
|
|
141
|
+
await expect(checkAuthActionsRan({ journey: {}, page })).resolves.toBeUndefined();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('a page with no auth methods records nothing and does not fail', async () => {
|
|
145
|
+
const page = createPage({ lowdefy: {}, sessionStorage: createWindow().sessionStorage });
|
|
146
|
+
await armAuthActionRecorder({ journey, page });
|
|
147
|
+
await expect(checkAuthActionsRan({ journey, page })).resolves.toBeUndefined();
|
|
148
|
+
expect(Object.keys(authEngineMethods).length).toBeGreaterThan(0);
|
|
149
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
import JourneyStepError from '../JourneyStepError.js';
|
|
20
|
+
import { armAuthActionRecorder } from './checkAuthActionsRan.js';
|
|
21
|
+
|
|
22
|
+
// The Lowdefy block a click lands on: the nearest #bl-<blockId> wrapper of the
|
|
23
|
+
// element the click resolved to, the element itself included. A `text` or
|
|
24
|
+
// `containing` target has no blockId of its own, and a container target is
|
|
25
|
+
// clicked on the control inside it, which may be a block of its own.
|
|
26
|
+
async function readClickedBlockId({ locator }) {
|
|
27
|
+
return locator.evaluate((element) => element.closest('[id^="bl-"]')?.id.slice(3) ?? null);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// On a data-set run (journey.dataSetRails), refuses a click that would reach a
|
|
31
|
+
// connection whose type does not declare a data-set mode the session can run
|
|
32
|
+
// (redirect or walled) before it acts, and starts recording the auth-engine
|
|
33
|
+
// actions the click runs (checkAuthActionsRan fails the step if one does).
|
|
34
|
+
// Returns the clicked block's id, or null when it is not known. Journeys
|
|
35
|
+
// without a data set click anything.
|
|
36
|
+
async function checkClickRails({ journey, page, locator }) {
|
|
37
|
+
if (type.isUndefined(journey.dataSetRails)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
await armAuthActionRecorder({ journey, page });
|
|
41
|
+
const pageId = await page.evaluate(() => window.lowdefy?.pageId);
|
|
42
|
+
const blockId = await readClickedBlockId({ locator });
|
|
43
|
+
if (type.isNone(pageId) || blockId === null) {
|
|
44
|
+
return blockId;
|
|
45
|
+
}
|
|
46
|
+
const refused = journey.dataSetRails.refusal({ pageId, blockId });
|
|
47
|
+
if (refused === null) {
|
|
48
|
+
return blockId;
|
|
49
|
+
}
|
|
50
|
+
throw new JourneyStepError(refused.message, {
|
|
51
|
+
expected: 'a click a data-set journey can make',
|
|
52
|
+
actual: refused.actual,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default checkClickRails;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
import { normaliseBlockId } from '@lowdefy/node-utils';
|
|
19
|
+
|
|
20
|
+
import findExternalBlocks from './findExternalBlocks.js';
|
|
21
|
+
|
|
22
|
+
// A list item's block (`rows.2.delete`) is listed by its template id
|
|
23
|
+
// (`rows.$.delete`).
|
|
24
|
+
function findListed({ blocks, blockId }) {
|
|
25
|
+
const normalised = normaliseBlockId(blockId);
|
|
26
|
+
const listed = Object.keys(blocks).find((id) => normaliseBlockId(id) === normalised);
|
|
27
|
+
return type.isUndefined(listed) ? undefined : blocks[listed];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// What the connection's type declares, as the author would fix it.
|
|
31
|
+
function describeDeclaration({ type: connectionType, dataSet }) {
|
|
32
|
+
if (type.isNone(connectionType)) return 'no connection artifact';
|
|
33
|
+
if (type.isNone(dataSet)) return `${connectionType}, no dataSet declaration`;
|
|
34
|
+
return `${connectionType}, dataSet: ${dataSet}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function describeReason({ dataSet }) {
|
|
38
|
+
if (dataSet === 'external') return 'which reaches a real service';
|
|
39
|
+
return 'which a data set cannot redirect';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function describeConnection(connection) {
|
|
43
|
+
return `"${connection.connectionId}" (${describeDeclaration(connection)})`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The static rail of a data-set journey run: a click on a block whose events
|
|
47
|
+
// reach a connection whose type declares meta.dataSet 'external', declares
|
|
48
|
+
// nothing, or has no artifact would reach a real service or be refused by the
|
|
49
|
+
// gate. Declarations are read from connections, the plugin connection map the
|
|
50
|
+
// gate reads. refusal({ pageId, blockId }) says why such a click is refused, or
|
|
51
|
+
// null when the block may be clicked. Each page's list is read from the build
|
|
52
|
+
// once per run. (Auth-engine actions are a runtime rail: see
|
|
53
|
+
// checkAuthActionsRan.)
|
|
54
|
+
function createDataSetRails({ buildDirectory, connections }) {
|
|
55
|
+
const pages = new Map();
|
|
56
|
+
function readPage(pageId) {
|
|
57
|
+
if (!pages.has(pageId)) {
|
|
58
|
+
pages.set(pageId, findExternalBlocks({ buildDirectory, pageId, connections }).blocks);
|
|
59
|
+
}
|
|
60
|
+
return pages.get(pageId);
|
|
61
|
+
}
|
|
62
|
+
function refusal({ pageId, blockId }) {
|
|
63
|
+
const refused = findListed({ blocks: readPage(pageId), blockId });
|
|
64
|
+
if (!type.isUndefined(refused)) {
|
|
65
|
+
const reasons = refused
|
|
66
|
+
.map(
|
|
67
|
+
(connection) =>
|
|
68
|
+
`connection ${describeConnection(connection)}, ${describeReason(connection)}`
|
|
69
|
+
)
|
|
70
|
+
.join(', and ');
|
|
71
|
+
const named = refused.map(describeConnection).join(', ');
|
|
72
|
+
return {
|
|
73
|
+
message: `Block "${blockId}" reaches ${reasons}, so a data-set journey cannot click it.`,
|
|
74
|
+
actual: `a click that reaches connection ${named}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return { refusal };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export default createDataSetRails;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'node:fs';
|
|
18
|
+
import os from 'node:os';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
|
|
21
|
+
import createDataSetRails from './createDataSetRails.js';
|
|
22
|
+
|
|
23
|
+
let buildDirectory;
|
|
24
|
+
|
|
25
|
+
const connections = {
|
|
26
|
+
MongoDBCollection: { meta: { tenant: true, dataSet: 'redirect' } },
|
|
27
|
+
AxiosHttp: { meta: { dataSet: 'external' } },
|
|
28
|
+
FolderRuntime: { meta: { dataSet: 'redirect' } },
|
|
29
|
+
WalledRuntime: { meta: { tenant: false, dataSet: 'walled' } },
|
|
30
|
+
Knex: {},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function write(name, value) {
|
|
34
|
+
const filePath = path.join(buildDirectory, name);
|
|
35
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
36
|
+
fs.writeFileSync(filePath, JSON.stringify(value));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function button(blockId, ...actions) {
|
|
40
|
+
return {
|
|
41
|
+
blockId,
|
|
42
|
+
type: 'Button',
|
|
43
|
+
events: { onClick: { try: actions, catch: [] } },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
buildDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-rails-'));
|
|
49
|
+
write('pages/orders.json', {
|
|
50
|
+
pageId: 'orders',
|
|
51
|
+
blockId: 'orders',
|
|
52
|
+
requests: [{ requestId: 'save' }, { requestId: 'notify' }],
|
|
53
|
+
slots: {
|
|
54
|
+
content: {
|
|
55
|
+
blocks: [
|
|
56
|
+
button('save_button', { id: 's', type: 'Request', params: 'save' }),
|
|
57
|
+
button('notify_button', { id: 'n', type: 'Request', params: 'notify' }),
|
|
58
|
+
button('logout_button', { id: 'l', type: 'Logout' }),
|
|
59
|
+
button('accept_button', { id: 'a', type: 'Request', params: 'accept' }),
|
|
60
|
+
button('archive_button', { id: 'a', type: 'Request', params: 'archive' }),
|
|
61
|
+
button('report_button', { id: 'r', type: 'Request', params: 'report' }),
|
|
62
|
+
button('orphan_button', { id: 'o', type: 'Request', params: 'orphan' }),
|
|
63
|
+
button(
|
|
64
|
+
'everything_button',
|
|
65
|
+
{ id: 'n', type: 'Request', params: 'notify' },
|
|
66
|
+
{ id: 'r', type: 'Request', params: 'report' },
|
|
67
|
+
{ id: 'o', type: 'Request', params: 'orphan' }
|
|
68
|
+
),
|
|
69
|
+
{
|
|
70
|
+
blockId: 'rows',
|
|
71
|
+
type: 'List',
|
|
72
|
+
slots: {
|
|
73
|
+
content: {
|
|
74
|
+
blocks: [button('rows.$.notify', { id: 'n', type: 'Request', params: 'notify' })],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
write('pages/orders/requests/save.json', { requestId: 'save', connectionId: 'orders_db' });
|
|
83
|
+
write('pages/orders/requests/notify.json', { requestId: 'notify', connectionId: 'mailer' });
|
|
84
|
+
write('connections/orders_db.json', { connectionId: 'orders_db', type: 'MongoDBCollection' });
|
|
85
|
+
write('connections/mailer.json', { connectionId: 'mailer', type: 'AxiosHttp' });
|
|
86
|
+
write('pages/orders/requests/accept.json', { requestId: 'accept', connectionId: 'runtime' });
|
|
87
|
+
write('pages/orders/requests/archive.json', { requestId: 'archive', connectionId: 'folders' });
|
|
88
|
+
write('pages/orders/requests/report.json', { requestId: 'report', connectionId: 'sql' });
|
|
89
|
+
write('pages/orders/requests/orphan.json', { requestId: 'orphan', connectionId: 'gone' });
|
|
90
|
+
write('connections/runtime.json', { connectionId: 'runtime', type: 'WalledRuntime' });
|
|
91
|
+
write('connections/folders.json', { connectionId: 'folders', type: 'FolderRuntime' });
|
|
92
|
+
write('connections/sql.json', { connectionId: 'sql', type: 'Knex' });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
afterEach(() => {
|
|
96
|
+
fs.rmSync(buildDirectory, { recursive: true, force: true });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('a block whose request uses a MongoDBCollection connection, declaring redirect, may be clicked', () => {
|
|
100
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
101
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'save_button' })).toBeNull();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('a block that reaches a plugin type declaring redirect or walled may be clicked', () => {
|
|
105
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
106
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'archive_button' })).toBeNull();
|
|
107
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'accept_button' })).toBeNull();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('a block that reaches a connection declaring external is refused, naming the declaration', () => {
|
|
111
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
112
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'notify_button' })).toEqual({
|
|
113
|
+
message:
|
|
114
|
+
'Block "notify_button" reaches connection "mailer" (AxiosHttp, dataSet: external), which reaches a real service, so a data-set journey cannot click it.',
|
|
115
|
+
actual: 'a click that reaches connection "mailer" (AxiosHttp, dataSet: external)',
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('a block that reaches a connection whose type declares nothing is refused, saying so', () => {
|
|
120
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
121
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'report_button' })).toEqual({
|
|
122
|
+
message:
|
|
123
|
+
'Block "report_button" reaches connection "sql" (Knex, no dataSet declaration), which a data set cannot redirect, so a data-set journey cannot click it.',
|
|
124
|
+
actual: 'a click that reaches connection "sql" (Knex, no dataSet declaration)',
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test('a block that reaches a connection with no artifact is refused, saying so', () => {
|
|
129
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
130
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'orphan_button' })).toEqual({
|
|
131
|
+
message:
|
|
132
|
+
'Block "orphan_button" reaches connection "gone" (no connection artifact), which a data set cannot redirect, so a data-set journey cannot click it.',
|
|
133
|
+
actual: 'a click that reaches connection "gone" (no connection artifact)',
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('a block that reaches several refused connections names each with its declaration', () => {
|
|
138
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
139
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'everything_button' })).toEqual({
|
|
140
|
+
message:
|
|
141
|
+
'Block "everything_button" reaches connection "gone" (no connection artifact), which a data set cannot redirect, and connection "mailer" (AxiosHttp, dataSet: external), which reaches a real service, and connection "sql" (Knex, no dataSet declaration), which a data set cannot redirect, so a data-set journey cannot click it.',
|
|
142
|
+
actual:
|
|
143
|
+
'a click that reaches connection "gone" (no connection artifact), "mailer" (AxiosHttp, dataSet: external), "sql" (Knex, no dataSet declaration)',
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('a MongoDBCollection connection is refused when the connection map does not declare it', () => {
|
|
148
|
+
const { MongoDBCollection, ...withoutMongo } = connections;
|
|
149
|
+
const rails = createDataSetRails({ buildDirectory, connections: withoutMongo });
|
|
150
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'save_button' })?.actual).toBe(
|
|
151
|
+
'a click that reaches connection "orders_db" (MongoDBCollection, no dataSet declaration)'
|
|
152
|
+
);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('a list item block is matched through its template id', () => {
|
|
156
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
157
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'rows.2.notify' })?.actual).toBe(
|
|
158
|
+
'a click that reaches connection "mailer" (AxiosHttp, dataSet: external)'
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('a block that runs an auth action is not refused: the auth rail acts at runtime', () => {
|
|
163
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
164
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'logout_button' })).toBeNull();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("a page's lists are read once per run", () => {
|
|
168
|
+
const rails = createDataSetRails({ buildDirectory, connections });
|
|
169
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'save_button' })).toBeNull();
|
|
170
|
+
fs.rmSync(path.join(buildDirectory, 'pages'), { recursive: true });
|
|
171
|
+
expect(rails.refusal({ pageId: 'orders', blockId: 'notify_button' })).not.toBeNull();
|
|
172
|
+
});
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
import pageBlocks from '../pageBlocks.js';
|
|
20
|
+
import readBuiltArtifact from './readBuiltArtifact.js';
|
|
21
|
+
import readRoutineSteps from '../readRoutineSteps.js';
|
|
22
|
+
|
|
23
|
+
// The meta.dataSet declarations a data session runs against the session's database (see
|
|
24
|
+
// lib/server/applyDataSetRedirect.js): 'redirect' takes the session database, 'walled' reaches it
|
|
25
|
+
// only through walled connections. Every other declaration, or none, would reach a real service or
|
|
26
|
+
// is refused by the gate.
|
|
27
|
+
const CLICKABLE_DATA_SET_MODES = new Set(['redirect', 'walled']);
|
|
28
|
+
|
|
29
|
+
// An 'external' type with request resolvers is clickable too: under a data session its requests
|
|
30
|
+
// are sunk (recorded, never sent), see lib/server/sinkExternalConnections.js. An external type
|
|
31
|
+
// with none (an Mcp config container) has nothing to sink and stays refused.
|
|
32
|
+
function isSunk({ connections, connectionType, dataSet }) {
|
|
33
|
+
if (dataSet !== 'external') return false;
|
|
34
|
+
const requests = connections[connectionType]?.requests;
|
|
35
|
+
return type.isObject(requests) && Object.keys(requests).length > 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function requestedIds({ params, pageRequestIds }) {
|
|
39
|
+
if (type.isString(params)) return [params];
|
|
40
|
+
if (type.isArray(params)) return params.filter((id) => type.isString(id));
|
|
41
|
+
if (!type.isObject(params)) return [];
|
|
42
|
+
if (params.all === true) return pageRequestIds;
|
|
43
|
+
if (type.isString(params.requestId)) return [params.requestId];
|
|
44
|
+
if (type.isArray(params.requestIds)) return params.requestIds.filter((id) => type.isString(id));
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createConnectionReader({ buildDirectory }) {
|
|
49
|
+
const types = new Map();
|
|
50
|
+
return function connectionType(connectionId) {
|
|
51
|
+
if (!types.has(connectionId)) {
|
|
52
|
+
const connection = readBuiltArtifact({
|
|
53
|
+
buildDirectory,
|
|
54
|
+
name: `connections/${connectionId}.json`,
|
|
55
|
+
});
|
|
56
|
+
types.set(connectionId, connection?.type ?? null);
|
|
57
|
+
}
|
|
58
|
+
return types.get(connectionId);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// The connections an endpoint's routine reaches, through endpoints its
|
|
63
|
+
// CallApi steps call. A dev build only warns about a call to an endpoint
|
|
64
|
+
// with no artifact (callapi-refs), so such a call reaches nothing.
|
|
65
|
+
function endpointConnections({ buildDirectory, endpointId }) {
|
|
66
|
+
const connections = new Set();
|
|
67
|
+
const seen = new Set();
|
|
68
|
+
const queue = [endpointId];
|
|
69
|
+
while (queue.length > 0) {
|
|
70
|
+
const id = queue.shift();
|
|
71
|
+
if (seen.has(id)) continue;
|
|
72
|
+
seen.add(id);
|
|
73
|
+
const endpoint = readBuiltArtifact({ buildDirectory, name: `api/${id}.json` });
|
|
74
|
+
if (endpoint === null) continue;
|
|
75
|
+
readRoutineSteps({ routine: endpoint.routine }).forEach((step) => {
|
|
76
|
+
if (type.isString(step.connectionId)) connections.add(step.connectionId);
|
|
77
|
+
if (step.type === 'CallApi' && type.isString(step.properties?.endpointId)) {
|
|
78
|
+
queue.push(step.properties.endpointId);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return connections;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The declared data-set mode of a connection type, from the plugin connection map the gate reads.
|
|
86
|
+
function dataSetMode({ connections, connectionType }) {
|
|
87
|
+
if (type.isNone(connectionType)) return null;
|
|
88
|
+
return connections[connectionType]?.meta?.dataSet ?? null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// The blocks of a page whose events reach a connection a data set cannot run
|
|
92
|
+
// against its database, through a Request action or a CallAPI into endpoint
|
|
93
|
+
// routines. A connection is refused when its type declares meta.dataSet
|
|
94
|
+
// 'external', declares nothing (or any value but 'redirect' and 'walled'), or
|
|
95
|
+
// it has no artifact. Each block lists the connections it reaches as
|
|
96
|
+
// { connectionId, type, dataSet } (type null for a connection with no
|
|
97
|
+
// artifact, dataSet the declared value or null). The rails mirror the
|
|
98
|
+
// declaration only: a redirect connection whose properties the gate refuses
|
|
99
|
+
// fails when its request runs. A data-set journey may not click these blocks
|
|
100
|
+
// (see createDataSetRails). Ids computed by operators are not followed.
|
|
101
|
+
function findExternalBlocks({ buildDirectory, pageId, connections }) {
|
|
102
|
+
const page = readBuiltArtifact({ buildDirectory, name: `pages/${pageId}.json` });
|
|
103
|
+
if (page === null) {
|
|
104
|
+
throw new Error(`Page "${pageId}" has no build artifact in ${buildDirectory}.`);
|
|
105
|
+
}
|
|
106
|
+
const pageRequestIds = (page.requests ?? []).map((request) => request.requestId);
|
|
107
|
+
const connectionType = createConnectionReader({ buildDirectory });
|
|
108
|
+
const blocks = {};
|
|
109
|
+
pageBlocks(page).forEach(({ blockId, actions }) => {
|
|
110
|
+
const reached = new Set();
|
|
111
|
+
actions.forEach((action) => {
|
|
112
|
+
if (action.type === 'Request') {
|
|
113
|
+
requestedIds({ params: action.params, pageRequestIds }).forEach((requestId) => {
|
|
114
|
+
const request = readBuiltArtifact({
|
|
115
|
+
buildDirectory,
|
|
116
|
+
name: `pages/${pageId}/requests/${requestId}.json`,
|
|
117
|
+
});
|
|
118
|
+
if (type.isString(request?.connectionId)) reached.add(request.connectionId);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (action.type === 'CallAPI' && type.isString(action.params?.endpointId)) {
|
|
122
|
+
endpointConnections({ buildDirectory, endpointId: action.params.endpointId }).forEach(
|
|
123
|
+
(connectionId) => reached.add(connectionId)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
const external = [...reached]
|
|
128
|
+
.sort()
|
|
129
|
+
.map((connectionId) => {
|
|
130
|
+
const reachedType = connectionType(connectionId);
|
|
131
|
+
return {
|
|
132
|
+
connectionId,
|
|
133
|
+
type: reachedType,
|
|
134
|
+
dataSet: dataSetMode({ connections, connectionType: reachedType }),
|
|
135
|
+
};
|
|
136
|
+
})
|
|
137
|
+
.filter(
|
|
138
|
+
(connection) =>
|
|
139
|
+
!CLICKABLE_DATA_SET_MODES.has(connection.dataSet) &&
|
|
140
|
+
!isSunk({ connections, connectionType: connection.type, dataSet: connection.dataSet })
|
|
141
|
+
);
|
|
142
|
+
if (external.length > 0) blocks[blockId] = external;
|
|
143
|
+
});
|
|
144
|
+
return { blocks };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export default findExternalBlocks;
|