@lowdefy/server-dev 6.1.0 → 7.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/App.jsx +52 -24
- package/client/Inspector.jsx +73 -30
- package/client/JourneyObserver.jsx +47 -0
- package/client/Page.jsx +54 -22
- package/client/Recorder.jsx +73 -0
- package/client/Routing.jsx +37 -13
- package/client/clientBuildImports.test.mjs +2 -0
- package/client/devStreamConnections.test.mjs +40 -0
- package/client/feedback/FeedbackMount.jsx +2 -2
- package/client/feedback/FeedbackOverlay.jsx +7 -4
- package/client/feedback/feedbackStyles.js +3 -0
- package/client/journeyObserver/startJourneyObserver.js +101 -0
- package/client/journeyObserver/startJourneyObserver.test.mjs +149 -0
- package/client/openInEditor/OpenInEditorListener.jsx +2 -0
- package/client/recorder/buildEngineRecord.js +37 -0
- package/client/recorder/buildInteractionRecord.js +64 -0
- package/client/recorder/buildTraceEvent.js +57 -0
- package/client/recorder/buildTraceEvent.test.mjs +81 -0
- package/client/recorder/chunkRecords.js +69 -0
- package/client/recorder/chunkRecords.test.mjs +59 -0
- package/client/recorder/collectEventActions.js +30 -0
- package/client/recorder/collectEventEndpoints.js +40 -0
- package/client/recorder/collectEventRequests.js +39 -0
- package/client/recorder/createPairingBuffer.js +187 -0
- package/client/recorder/createPairingBuffer.test.mjs +405 -0
- package/client/recorder/createPasswordRedactor.js +84 -0
- package/client/recorder/createRecordBatcher.js +54 -0
- package/client/recorder/createRecordBatcher.test.mjs +76 -0
- package/client/recorder/createRecorder.js +216 -0
- package/client/recorder/createRecorder.test.mjs +318 -0
- package/client/recorder/createRecordingSession.js +61 -0
- package/client/recorder/createRecordingSession.test.mjs +72 -0
- package/client/recorder/diffStateWrites.js +77 -0
- package/client/recorder/diffStateWrites.test.mjs +66 -0
- package/client/recorder/isEventCall.js +30 -0
- package/client/recorder/jsonType.js +39 -0
- package/client/recorder/keyChord.js +47 -0
- package/client/recorder/keyChord.test.mjs +50 -0
- package/client/recorder/sendRecords.js +40 -0
- package/client/recorder/toJsonSafe.js +29 -0
- package/lib/client/BuildingPage.jsx +3 -1
- package/lib/client/ErrorBar.jsx +17 -13
- package/lib/client/RedirectingPage.jsx +56 -0
- package/lib/client/auth/Auth.jsx +11 -6
- package/lib/client/auth/AuthConfigured.jsx +222 -36
- package/lib/client/auth/AuthNotConfigured.js +45 -4
- package/lib/client/auth/authNotConfiguredParity.test.mjs +61 -0
- package/lib/client/auth/authTemplateSync.test.mjs +40 -0
- package/lib/client/auth/betterAuthSessionContract.test.mjs +181 -0
- package/lib/client/auth/createFetchCheckingBuild.js +45 -0
- package/lib/client/bundleBuildId.js +25 -0
- package/lib/client/collectVisibleBlockIds.js +51 -0
- package/lib/client/getCspNonce.js +25 -0
- package/lib/client/getPagePath.js +36 -0
- package/lib/client/getPagePath.test.mjs +51 -0
- package/lib/client/utils/formatErrorsForCopy.js +30 -0
- package/lib/client/utils/formatErrorsForCopy.test.mjs +39 -0
- package/lib/client/utils/getErrorBarColor.js +25 -0
- package/lib/client/utils/getErrorBarColor.test.mjs +34 -0
- package/lib/client/utils/request.js +8 -1
- package/lib/client/utils/usePageConfig.js +89 -25
- package/lib/client/utils/usePageConfig.test.mjs +198 -13
- package/lib/configTree/buildConfigTree.mjs +58 -0
- package/lib/configTree/buildConfigTree.test.mjs +77 -0
- package/lib/configTree/collectConfigText.mjs +101 -0
- package/lib/configTree/collectConfigText.test.mjs +116 -0
- package/lib/configTree/prepareScratchServer.mjs +46 -0
- package/lib/configTree/runConfigTreeBuild.mjs +88 -0
- package/lib/configTree/runConfigTreeBuild.skipPluginChecks.test.mjs +74 -0
- package/lib/configTree/runConfigTreeBuild.test.mjs +202 -0
- package/lib/docs/JourneyStepError.js +30 -0
- package/lib/docs/acquireBrowserSlot.js +30 -0
- package/lib/docs/addNestedEndpoints.js +54 -0
- package/lib/docs/addNestedEndpoints.test.mjs +185 -0
- package/lib/docs/addWriteFlags.js +77 -0
- package/lib/docs/buildEditedPages.js +50 -0
- package/lib/docs/buildWaitHeader.js +21 -0
- package/lib/docs/captureAnnotatedScreenshot.js +23 -12
- package/lib/docs/checkAgentFiles.js +148 -0
- package/lib/docs/checkAgentFiles.test.mjs +225 -0
- package/lib/docs/checkStepReferences.js +239 -0
- package/lib/docs/checkStepReferences.test.mjs +173 -0
- package/lib/docs/checkpointStore.js +3 -0
- package/lib/docs/clientErrorStore.js +18 -1
- package/lib/docs/clientErrorStore.test.mjs +46 -0
- package/lib/docs/collectExercised.js +52 -0
- package/lib/docs/createBrowserLifecycle.js +102 -0
- package/lib/docs/createBrowserLifecycle.test.mjs +212 -0
- package/lib/docs/createDocsMcpServer.js +345 -388
- package/lib/docs/createDocsMcpServer.test.mjs +644 -4
- package/lib/docs/createDocsMcpServerRunJourney.test.mjs +148 -0
- package/lib/docs/createJourneyActors.chromium.test.mjs +227 -0
- package/lib/docs/createJourneyActors.js +213 -0
- package/lib/docs/createJourneyActors.test.mjs +79 -0
- package/lib/docs/createJourneyObservations.js +80 -0
- package/lib/docs/createLeftOriginError.js +26 -0
- package/lib/docs/createNetworkCounter.js +101 -0
- package/lib/docs/dataSets/checkSharedCollectionIds.js +78 -0
- package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
- package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +727 -0
- package/lib/docs/dataSets/describeDataSetResult.js +25 -0
- package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
- package/lib/docs/dataSets/describeTargetKey.js +29 -0
- package/lib/docs/dataSets/deserializeFixture.js +34 -0
- package/lib/docs/dataSets/drainSessionWork.js +37 -0
- package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
- package/lib/docs/dataSets/duplicateIndexName.js +24 -0
- package/lib/docs/dataSets/getDataStore.js +106 -0
- package/lib/docs/dataSets/getDataStore.mongodb.test.mjs +63 -0
- package/lib/docs/dataSets/getDataStore.test.mjs +195 -0
- package/lib/docs/dataSets/groupDataSetByCollection.js +52 -0
- package/lib/docs/dataSets/insertFixture.js +57 -0
- package/lib/docs/dataSets/insertGeneratedDocuments.js +41 -0
- package/lib/docs/dataSets/isDataStoreGone.js +29 -0
- package/lib/docs/dataSets/loadDataSetCollection.js +42 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.js +52 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.test.mjs +59 -0
- package/lib/docs/dataSets/openDataSession.js +133 -0
- package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +493 -0
- package/lib/docs/dataSets/pickDataStorePort.js +58 -0
- package/lib/docs/dataSets/pickDataStorePort.test.mjs +63 -0
- package/lib/docs/dataSets/readConnectionArtifacts.js +46 -0
- package/lib/docs/dataSets/readDataSession.js +41 -0
- package/lib/docs/dataSets/readDataSession.test.mjs +57 -0
- package/lib/docs/dataSets/readDataSet.js +83 -0
- package/lib/docs/dataSets/readDataSet.test.mjs +251 -0
- package/lib/docs/dataSets/registerDataStoreShutdown.js +47 -0
- package/lib/docs/dataSets/removeDataStoreFiles.js +32 -0
- package/lib/docs/dataSets/resolveDataSetCollection.js +46 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.js +106 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.test.mjs +130 -0
- package/lib/docs/dataSets/sessionDatabaseName.js +32 -0
- package/lib/docs/dataSets/sweepOrphanDatabases.js +36 -0
- package/lib/docs/dataSets/test/app/lowdefy.yaml +225 -0
- package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
- package/lib/docs/dataSets/test/app/tests/data/shop.yaml +12 -0
- package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
- package/lib/docs/devEventBus.js +136 -0
- package/lib/docs/devEventBus.test.mjs +269 -0
- package/lib/docs/devToolDefinitions.js +529 -0
- package/lib/docs/docSources.js +21 -0
- package/lib/docs/docs.test.mjs +171 -15
- package/lib/docs/docsIndex.test.mjs +377 -0
- package/lib/docs/evalOperator.js +5 -3
- package/lib/docs/evalOperatorHeadless.js +84 -12
- package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
- package/lib/docs/evalOperatorInTab.js +2 -1
- package/lib/docs/findConfig.js +95 -49
- package/lib/docs/fitResponse.js +81 -0
- package/lib/docs/fitResponse.test.mjs +128 -0
- package/lib/docs/formatFeedback.js +26 -5
- package/lib/docs/formatFeedback.test.mjs +48 -0
- package/lib/docs/getAppMap.js +7 -1
- package/lib/docs/getBrowser.js +150 -80
- package/lib/docs/getBrowser.test.mjs +407 -16
- package/lib/docs/getBuildId.js +61 -0
- package/lib/docs/getBuildId.test.mjs +84 -0
- package/lib/docs/getBuildStatus.js +81 -7
- package/lib/docs/getBuildStatus.test.mjs +229 -0
- package/lib/docs/getBuildStatusAfterEdits.js +35 -0
- package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
- package/lib/docs/getCoreDoc.js +9 -1
- package/lib/docs/getDoc.js +100 -0
- package/lib/docs/getDocsIndex.js +152 -0
- package/lib/docs/getHazards.js +115 -0
- package/lib/docs/getHazards.test.mjs +175 -0
- package/lib/docs/getMailOutboxDirectory.js +26 -0
- package/lib/docs/getOverview.js +79 -13
- package/lib/docs/getOverviewBasePath.test.mjs +37 -0
- package/lib/docs/getPageBuildStatus.js +46 -0
- package/lib/docs/getPluginDoc.js +21 -23
- package/lib/docs/getSchema.js +33 -1
- package/lib/docs/getStaleStatus.js +36 -0
- package/lib/docs/getStaleStatus.test.mjs +74 -0
- package/lib/docs/guardJourneyOrigin.js +39 -0
- package/lib/docs/inspectState.js +5 -3
- package/lib/docs/inspectStateFromTab.js +2 -2
- package/lib/docs/inspectStateHeadless.js +89 -27
- package/lib/docs/inspectStateHeadless.test.mjs +20 -1
- package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
- package/lib/docs/installHeadlessShell.js +123 -0
- package/lib/docs/installHeadlessShell.test.mjs +70 -0
- package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
- package/lib/docs/isHeadlessShellIncomplete.js +34 -0
- package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
- package/lib/docs/isPageReady.js +77 -0
- package/lib/docs/isPageReady.test.mjs +235 -0
- package/lib/docs/isRunErrorEntry.js +25 -0
- package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
- package/lib/docs/journeyRunRecording.js +35 -0
- package/lib/docs/journeyRunRecording.test.mjs +39 -0
- package/lib/docs/keepEventStreamAlive.js +68 -0
- package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
- package/lib/docs/launchBrowser.js +83 -0
- package/lib/docs/launchBrowser.test.mjs +175 -0
- package/lib/docs/listModuleDocEntries.js +69 -0
- package/lib/docs/listPackageDocFiles.js +67 -0
- package/lib/docs/listPluginDocEntries.js +111 -0
- package/lib/docs/listPlugins.js +9 -58
- package/lib/docs/listTypes.js +14 -6
- package/lib/docs/loadFrameworkHazards.js +40 -0
- package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
- package/lib/docs/loadState.js +103 -35
- package/lib/docs/makeModuleManifestDoc.js +174 -0
- package/lib/docs/matchExercisedPages.js +35 -0
- package/lib/docs/matchJourneyRoute.js +69 -0
- package/lib/docs/matchJourneyRoute.test.mjs +178 -0
- package/lib/docs/mergeNetworkSnapshots.js +49 -0
- package/lib/docs/nextJourneySequence.js +30 -0
- package/lib/docs/noBrowserError.js +22 -0
- package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
- package/lib/docs/observe/claimExpectedErrors.js +67 -0
- package/lib/docs/observe/createJourneyAppErrors.js +236 -0
- package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
- package/lib/docs/observe/evaluateInvariants.js +183 -0
- package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
- package/lib/docs/observe/findingKey.js +29 -0
- package/lib/docs/observe/hasNoEffect.js +31 -0
- package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
- package/lib/docs/observe/installStepObserver.js +72 -0
- package/lib/docs/observe/isAppApiUrl.js +33 -0
- package/lib/docs/observe/parseAppApiUrl.js +37 -0
- package/lib/docs/observe/readStepObserver.js +42 -0
- package/lib/docs/observe/relativeSource.js +29 -0
- package/lib/docs/observe/resolveConfigKeySource.js +33 -0
- package/lib/docs/observe/takeJourneyEvents.js +36 -0
- package/lib/docs/observe/toAppErrorFailure.js +61 -0
- package/lib/docs/observe/usesSearchStage.js +45 -0
- package/lib/docs/observe/waitForClientErrorReports.js +31 -0
- package/lib/docs/observe/watchJourneyContext.js +75 -0
- package/lib/docs/observe/withAppErrors.js +31 -0
- package/lib/docs/observe/withAppErrors.test.mjs +101 -0
- package/lib/docs/openJourney.js +94 -0
- package/lib/docs/openJourneyEmail.js +41 -0
- package/lib/docs/openJourneyEmail.test.mjs +187 -0
- package/lib/docs/pageBlocks.js +47 -0
- package/lib/docs/pageBuildStatus.test.mjs +103 -0
- package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
- package/lib/docs/rails/authEngineActions.js +53 -0
- package/lib/docs/rails/authEngineActions.test.mjs +69 -0
- package/lib/docs/rails/authEngineMethods.js +52 -0
- package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
- package/lib/docs/rails/checkAuthActionsRan.js +66 -0
- package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
- package/lib/docs/rails/checkClickRails.js +56 -0
- package/lib/docs/rails/createDataSetRails.js +82 -0
- package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
- package/lib/docs/rails/findExternalBlocks.js +147 -0
- package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
- package/lib/docs/rails/installAuthActionRecorder.js +56 -0
- package/lib/docs/rails/readAuthActionRecorder.js +32 -0
- package/lib/docs/rails/readBuiltArtifact.js +30 -0
- package/lib/docs/readConnectionPlugins.js +26 -0
- package/lib/docs/readDevAuthMode.js +32 -0
- package/lib/docs/readDocEntry.js +40 -0
- package/lib/docs/readDocTitle.js +26 -0
- package/lib/docs/readJourneyEmailMatch.js +57 -0
- package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
- package/lib/docs/readJourneySession.js +56 -0
- package/lib/docs/readMailOutbox.js +37 -0
- package/lib/docs/readPagePath.js +27 -0
- package/lib/docs/readPluginPackages.js +99 -0
- package/lib/docs/readProxyBuildWait.js +38 -0
- package/lib/docs/readRoutineSteps.js +45 -0
- package/lib/docs/registerShownTextEngine.js +118 -0
- package/lib/docs/requestRestart.js +30 -0
- package/lib/docs/requestRestart.test.mjs +49 -0
- package/lib/docs/resolvePageInstance.js +46 -0
- package/lib/docs/resolvePageInstance.test.mjs +81 -0
- package/lib/docs/resolveSource.js +11 -7
- package/lib/docs/resolveSource.test.mjs +1 -1
- package/lib/docs/resolveToolCaller.js +62 -0
- package/lib/docs/reviewPage.js +51 -0
- package/lib/docs/reviewPageBuilds.js +56 -0
- package/lib/docs/reviewPageBuilds.test.mjs +207 -0
- package/lib/docs/runCheck.js +75 -0
- package/lib/docs/runCheck.test.mjs +91 -0
- package/lib/docs/runCheckChild.js +49 -0
- package/lib/docs/runEndpoint.js +185 -0
- package/lib/docs/runEndpoint.test.mjs +509 -0
- package/lib/docs/runErrorBuffers.js +99 -0
- package/lib/docs/runErrorBuffers.test.mjs +95 -0
- package/lib/docs/runJourney.js +347 -0
- package/lib/docs/runJourney.test.mjs +2383 -0
- package/lib/docs/runJourneyAsUsers.js +44 -0
- package/lib/docs/runJourneyDataSet.test.mjs +274 -0
- package/lib/docs/runJourneySteps.js +1320 -0
- package/lib/docs/runRequest.js +100 -59
- package/lib/docs/runRequest.test.mjs +190 -0
- package/lib/docs/saveAnnotatedScreenshot.js +1 -1
- package/lib/docs/scaffoldPage.js +3 -1
- package/lib/docs/screenshotPage.js +163 -11
- package/lib/docs/screenshotPage.test.mjs +263 -1
- package/lib/docs/screenshotPageClip.test.mjs +4 -0
- package/lib/docs/searchDocs.js +82 -30
- package/lib/docs/searchDocs.test.mjs +100 -0
- package/lib/docs/searchIcons.js +90 -0
- package/lib/docs/searchIcons.test.mjs +127 -0
- package/lib/docs/selectExercised.js +39 -0
- package/lib/docs/selectFinalState.js +66 -0
- package/lib/docs/selectFinalState.test.mjs +51 -0
- package/lib/docs/serverErrorStore.js +54 -0
- package/lib/docs/serverErrorStore.test.mjs +50 -0
- package/lib/docs/setupTestFixtures.mjs +133 -39
- package/lib/docs/shownTextSelector.js +28 -0
- package/lib/docs/snapshotState.js +13 -5
- package/lib/docs/splitSearchTerms.js +62 -0
- package/lib/docs/splitSearchTerms.test.mjs +50 -0
- package/lib/docs/stateCheckpoints.test.mjs +77 -0
- package/lib/docs/tabAvailable.js +6 -5
- package/lib/docs/tabChannel.js +127 -21
- package/lib/docs/tabChannel.test.mjs +187 -29
- package/lib/docs/unsettledPageNote.js +24 -0
- package/lib/docs/validateJourneyMail.js +36 -0
- package/lib/docs/validateJourneyTimeout.js +38 -0
- package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
- package/lib/docs/validateRecordingParam.js +42 -0
- package/lib/docs/validateStateSelection.js +33 -0
- package/lib/docs/validateStateSelection.test.mjs +33 -0
- package/lib/docs/validateViewport.js +51 -0
- package/lib/docs/validateViewport.test.mjs +54 -0
- package/lib/docs/waitForBuild.js +50 -0
- package/lib/docs/waitForBuild.test.mjs +75 -0
- package/lib/docs/waitForJourneyEmail.js +77 -0
- package/lib/docs/withBrowserSlot.js +45 -0
- package/lib/docs/withBrowserSlot.test.mjs +75 -0
- package/lib/docs/withDataSession.js +53 -0
- package/lib/server/applyDataSetRedirect.js +92 -0
- package/lib/server/applyDataSetRedirect.test.mjs +446 -0
- package/lib/server/applyDataSetRedirectTenantReach.test.mjs +207 -0
- package/lib/server/applyDataSetRedirectWalled.test.mjs +134 -0
- package/lib/server/auth/createSystemContext.js +73 -0
- package/lib/server/auth/{getAuthConfig.js → getAuth.js} +15 -9
- package/lib/server/auth/getHeadlessUser.js +9 -8
- package/lib/server/auth/getHeadlessUser.test.mjs +41 -0
- package/lib/server/auth/getMockUser.js +18 -14
- package/lib/server/auth/{session.js → getStrategies.js} +17 -16
- package/lib/server/auth/headlessUser.js +3 -2
- package/lib/server/auth/journeyActor.js +35 -0
- package/lib/server/auth/resolveHeadlessUser.test.mjs +0 -4
- package/lib/server/checkPageRecord.js +44 -0
- package/lib/server/contextMapBudget.js +24 -0
- package/lib/server/contextMapBudget.test.mjs +98 -0
- package/lib/server/continueJitKeys.js +28 -0
- package/lib/server/continueJitKeys.test.mjs +43 -0
- package/lib/server/createDataSessionEndedError.js +33 -0
- package/lib/server/createDataSetRefusal.js +26 -0
- package/lib/server/createLowdefyContext.js +168 -41
- package/lib/server/createLowdefyContext.test.mjs +331 -8
- package/lib/server/getClientAddress.js +51 -0
- package/lib/server/getClientAddress.test.mjs +56 -0
- package/lib/server/getPageJitEnrichment.test.mjs +29 -11
- package/lib/server/hashConfigContent.js +26 -0
- package/lib/server/importAppCode.test.mjs +57 -0
- package/lib/server/isRoutineStep.js +27 -0
- package/lib/server/jitPageBuilder.js +309 -126
- package/lib/server/jitPageBuilder.skipPluginChecks.test.mjs +78 -0
- package/lib/server/jitPageBuilder.test.mjs +218 -0
- package/lib/server/journeyCookies/JOURNEY_COOKIES.js +37 -0
- package/lib/server/journeyCookies/forwardJourneyCookies.js +46 -0
- package/lib/server/journeyCookies/readJourneyCookie.js +52 -0
- package/lib/server/journeyCookies/writeJourneyCookie.js +46 -0
- package/lib/server/journeyCookies.js +22 -0
- package/lib/server/journeyCookies.test.mjs +145 -0
- package/lib/server/loadDynamicJsMap.js +47 -0
- package/lib/server/log/createHandleError.js +38 -2
- package/lib/server/log/createHandleError.test.mjs +125 -2
- package/lib/server/log/createLogger.js +3 -0
- package/lib/server/log/createLogger.test.mjs +15 -0
- package/lib/server/log/logRequest.js +2 -3
- package/lib/server/mutantDataSetWrappers.test.mjs +115 -0
- package/lib/server/mutants/applyMutant.js +39 -0
- package/lib/server/mutants/artifactPatterns.js +25 -0
- package/lib/server/mutants/buildAnchor.js +45 -0
- package/lib/server/mutants/collectSharedIds.js +41 -0
- package/lib/server/mutants/configPathSegments.js +38 -0
- package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
- package/lib/server/mutants/describeEvent.js +22 -0
- package/lib/server/mutants/enumerateArtifact.js +56 -0
- package/lib/server/mutants/findNodeByKey.js +45 -0
- package/lib/server/mutants/findNodeByPointer.js +49 -0
- package/lib/server/mutants/getArtifactKind.js +32 -0
- package/lib/server/mutants/groupMutantCopies.js +56 -0
- package/lib/server/mutants/hashArtifact.js +25 -0
- package/lib/server/mutants/hashMutantTarget.js +34 -0
- package/lib/server/mutants/listMutants.js +122 -0
- package/lib/server/mutants/listMutants.test.mjs +502 -0
- package/lib/server/mutants/mutantId.js +30 -0
- package/lib/server/mutants/mutantRuns.js +57 -0
- package/lib/server/mutants/mutantRuns.test.mjs +136 -0
- package/lib/server/mutants/operators/dropAction.js +47 -0
- package/lib/server/mutants/operators/dropBlock.js +44 -0
- package/lib/server/mutants/operators/dropPayload.js +52 -0
- package/lib/server/mutants/operators/dropStep.js +44 -0
- package/lib/server/mutants/operators/flipVisible.js +53 -0
- package/lib/server/mutants/operators/index.js +37 -0
- package/lib/server/mutants/operators/retargetLink.js +72 -0
- package/lib/server/mutants/operators/skipValidate.js +77 -0
- package/lib/server/mutants/operators/swapIf.js +112 -0
- package/lib/server/mutants/operators.test.mjs +396 -0
- package/lib/server/mutants/positionFreePath.js +38 -0
- package/lib/server/mutants/removeFromParent.js +28 -0
- package/lib/server/mutants/test/fixtures.mjs +173 -0
- package/lib/server/mutants/test/keyArtifact.mjs +63 -0
- package/lib/server/mutants/validateMutantParam.js +69 -0
- package/lib/server/mutants/validateMutantsBody.js +88 -0
- package/lib/server/mutants/walkArtifact.js +179 -0
- package/lib/server/pageBuildRecords.js +131 -0
- package/lib/server/pageBuildRecords.test.mjs +258 -0
- package/lib/server/pageCache.mjs +22 -7
- package/lib/server/pageCache.test.mjs +22 -7
- package/lib/server/pageRebuilds.test.mjs +543 -0
- package/lib/server/pruneJitMaps.js +36 -0
- package/lib/server/pruneJitMaps.test.mjs +55 -0
- package/lib/server/readInstanceRecord.js +33 -0
- package/lib/server/readInstanceRecord.test.mjs +99 -0
- package/lib/server/readMergedMaps.js +86 -0
- package/lib/server/readMergedMaps.test.mjs +79 -0
- package/lib/server/recording/appendRecords.js +33 -0
- package/lib/server/recording/errorRecordingStamp.js +34 -0
- package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
- package/lib/server/recording/isRecordingEnabled.js +31 -0
- package/lib/server/recording/readRecordingCookie.js +71 -0
- package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
- package/lib/server/recording/recordingCookiePayload.js +33 -0
- package/lib/server/recording/servedBuilds.js +46 -0
- package/lib/server/recording/servedBuilds.test.mjs +42 -0
- package/lib/server/sinkExternalConnections.js +95 -0
- package/lib/server/sinkExternalConnections.test.mjs +135 -0
- package/lib/server/skipStaleMapWrites.js +62 -0
- package/lib/server/skipStaleMapWrites.test.mjs +113 -0
- package/lib/server/startIdleGc.js +67 -0
- package/lib/server/startIdleGc.test.mjs +138 -0
- package/lib/server/testFixtures/importAppCodeTwice.mjs +70 -0
- package/lib/server/trackSessionWork.js +30 -0
- package/lib/vite/createDevServerExclude.js +40 -0
- package/lib/vite/createDevServerExclude.test.mjs +79 -0
- package/manager/getContext.mjs +25 -3
- package/manager/processes/initialBuild.mjs +17 -1
- package/manager/processes/initialBuild.test.mjs +104 -0
- package/manager/processes/lowdefyBuild.mjs +31 -7
- package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +72 -0
- package/manager/processes/lowdefyBuild.test.mjs +136 -1
- package/manager/processes/optimizeDependencies.mjs +59 -0
- package/manager/processes/restartServer.mjs +34 -6
- package/manager/processes/restartServer.test.mjs +162 -0
- package/manager/processes/shutdownServer.mjs +11 -0
- package/manager/processes/shutdownServer.test.mjs +45 -0
- package/manager/processes/startFirstServer.mjs +46 -0
- package/manager/processes/startFirstServer.test.mjs +326 -0
- package/manager/processes/startMailSink.mjs +135 -0
- package/manager/processes/startMailSink.test.mjs +146 -0
- package/manager/processes/startProxy.mjs +129 -12
- package/manager/processes/startProxy.test.mjs +366 -0
- package/manager/processes/startServer.mjs +47 -6
- package/manager/processes/startServer.test.mjs +155 -0
- package/manager/processes/startWatchers.mjs +6 -4
- package/manager/processes/syncServer.mjs +69 -0
- package/manager/processes/syncServer.test.mjs +141 -0
- package/manager/processes/waitForDevServer.mjs +48 -0
- package/manager/processes/waitForDevServer.test.mjs +112 -0
- package/manager/run.mjs +128 -43
- package/manager/utils/BatchChanges.mjs +21 -5
- package/manager/utils/BatchChanges.test.mjs +77 -0
- package/manager/utils/acquireDevInstance.mjs +99 -0
- package/manager/utils/acquireDevInstance.test.mjs +160 -0
- package/manager/utils/createBuildActivity.mjs +67 -0
- package/manager/utils/createBuildActivity.test.mjs +58 -0
- package/manager/utils/createChangeSignal.mjs +39 -0
- package/manager/utils/createChangeSignal.test.mjs +62 -0
- package/manager/utils/createCustomPluginMessagesMap.mjs +23 -33
- package/manager/utils/createCustomPluginTypesMap.mjs +24 -30
- package/manager/utils/createCustomPluginTypesMap.test.mjs +94 -0
- package/manager/utils/createDotPathIgnore.mjs +44 -0
- package/manager/utils/createDotPathIgnore.test.mjs +65 -0
- package/manager/utils/createIdleGc.mjs +51 -0
- package/manager/utils/createIdleGc.test.mjs +77 -0
- package/manager/utils/createRequestActivity.mjs +100 -0
- package/manager/utils/createRequestActivity.test.mjs +121 -0
- package/manager/utils/createServerArtifactTracker.mjs +83 -0
- package/manager/utils/createServerArtifactTracker.test.mjs +78 -0
- package/manager/utils/createServerEnv.mjs +39 -0
- package/manager/utils/filesHaveSameBytes.mjs +41 -0
- package/manager/utils/findBuildFilesOutsideWatch.mjs +59 -0
- package/manager/utils/findBuildFilesOutsideWatch.test.mjs +111 -0
- package/manager/utils/importFresh.mjs +46 -0
- package/manager/utils/importFresh.test.mjs +72 -0
- package/manager/utils/importFreshWorker.mjs +20 -0
- package/manager/utils/killTaggedBrowser.mjs +35 -0
- package/manager/utils/pagePathChanged.mjs +56 -0
- package/manager/utils/pruneRecordings.mjs +85 -0
- package/manager/utils/pruneRecordings.test.mjs +117 -0
- package/manager/utils/publishBuildDirectory.mjs +121 -0
- package/manager/utils/publishBuildDirectory.order.test.mjs +74 -0
- package/manager/utils/publishBuildDirectory.test.mjs +262 -0
- package/manager/utils/readBasePath.mjs +27 -0
- package/manager/utils/readBasePath.test.mjs +43 -0
- package/manager/utils/readBuildStatusWait.mjs +67 -0
- package/manager/utils/readManagerVersion.mjs +30 -0
- package/manager/utils/readManagerVersion.test.mjs +35 -0
- package/manager/utils/readPluginDefinitions.mjs +36 -0
- package/manager/utils/resolveDevAuthUrl.mjs +58 -0
- package/manager/utils/resolveDevAuthUrl.test.mjs +107 -0
- package/manager/utils/resolvePorts.mjs +49 -0
- package/manager/utils/setupWatcher.mjs +19 -11
- package/manager/utils/startRecordingPruner.mjs +45 -0
- package/manager/utils/startRecordingPruner.test.mjs +66 -0
- package/manager/utils/waitForChildExit.mjs +53 -0
- package/manager/utils/waitForChildExit.test.mjs +70 -0
- package/manager/utils/waitForServer.mjs +44 -0
- package/manager/utils/writeBuildStatus.mjs +1 -1
- package/manager/watchers/createPluginSourceIgnore.mjs +77 -0
- package/manager/watchers/envWatcher.mjs +9 -2
- package/manager/watchers/envWatcher.test.mjs +87 -0
- package/manager/watchers/listServerSidePackages.mjs +47 -0
- package/manager/watchers/listServerSidePackages.test.mjs +38 -0
- package/manager/watchers/lowdefyBuildWatcher.mjs +106 -22
- package/manager/watchers/lowdefyBuildWatcher.test.mjs +309 -0
- package/manager/watchers/pluginSourceWatcher.mjs +79 -0
- package/manager/watchers/pluginSourceWatcher.test.mjs +222 -0
- package/manager/watchers/restartRequestWatcher.mjs +71 -0
- package/manager/watchers/restartRequestWatcher.test.mjs +111 -0
- package/manager/watchers/selectLocalPluginPackages.mjs +48 -0
- package/manager/watchers/selectLocalPluginPackages.test.mjs +73 -0
- package/manager/watchers/serverPackageWatcher.mjs +38 -0
- package/package.json +60 -47
- package/package.original.json +60 -47
- package/src/app.js +131 -20
- package/src/html/renderDevPage.js +63 -11
- package/src/html/renderDevPage.test.mjs +138 -0
- package/src/lib/getRequestPath.js +29 -0
- package/src/middleware/createSameOriginGuard.js +37 -0
- package/src/middleware/createSameOriginGuard.test.mjs +102 -0
- package/src/middleware/errorHandler.js +51 -1
- package/src/middleware/errorHandler.test.mjs +173 -3
- package/src/middleware/isRebindingSafeHost.js +44 -0
- package/src/middleware/isRebindingSafeHost.test.mjs +32 -0
- package/src/middleware/localDevToolsOnly.js +38 -0
- package/src/middleware/localDevToolsOnly.test.mjs +61 -0
- package/src/middleware/noStore.js +27 -0
- package/src/middleware/noStore.test.mjs +48 -0
- package/src/middleware/requestTimeout.js +38 -0
- package/src/middleware/staleFlag.js +76 -0
- package/src/middleware/staleFlag.test.mjs +127 -0
- package/src/middleware/stampCaller.js +38 -0
- package/src/middleware/stampCaller.test.mjs +64 -0
- package/src/routes/agent.js +15 -3
- package/src/routes/auth.js +29 -20
- package/src/routes/auth.test.mjs +30 -56
- package/src/routes/clientError.js +13 -13
- package/src/routes/cron.js +3 -1
- package/src/routes/cronForward.js +3 -1
- package/src/routes/detached.js +22 -9
- package/src/routes/devInspect.js +17 -8
- package/src/routes/devInspect.test.mjs +44 -7
- package/src/routes/devRecording.js +113 -0
- package/src/routes/devRecording.test.mjs +253 -0
- package/src/routes/docs/buildStatus.js +10 -2
- package/src/routes/docs/buildStatus.test.mjs +46 -0
- package/src/routes/docs/content.js +5 -3
- package/src/routes/docs/evalOperator.js +15 -1
- package/src/routes/docs/events.js +50 -0
- package/src/routes/docs/events.test.mjs +117 -0
- package/src/routes/docs/find.js +9 -1
- package/src/routes/docs/icons.js +30 -0
- package/src/routes/docs/inspectState.js +16 -1
- package/src/routes/docs/journey.js +194 -0
- package/src/routes/docs/journey.test.mjs +401 -0
- package/src/routes/docs/journeyRefusals.test.mjs +71 -0
- package/src/routes/docs/loadState.js +4 -2
- package/src/routes/docs/mcp.js +34 -3
- package/src/routes/docs/mcp.test.mjs +130 -0
- package/src/routes/docs/mutants.js +78 -0
- package/src/routes/docs/mutants.test.mjs +180 -0
- package/src/routes/docs/parsePathParamsParam.js +41 -0
- package/src/routes/docs/parsePathParamsParam.test.mjs +42 -0
- package/src/routes/docs/parseUserParam.js +27 -21
- package/src/routes/docs/parseUserParam.test.mjs +17 -5
- package/src/routes/docs/restart.js +35 -0
- package/src/routes/docs/restart.test.mjs +64 -0
- package/src/routes/docs/runEndpoint.js +54 -0
- package/src/routes/docs/runEndpoint.test.mjs +165 -0
- package/src/routes/docs/runRequest.js +25 -8
- package/src/routes/docs/runRequest.test.mjs +92 -0
- package/src/routes/docs/screenshot.js +50 -2
- package/src/routes/docs/screenshot.test.mjs +117 -0
- package/src/routes/docs/search.js +15 -2
- package/src/routes/docs/snapshotState.js +17 -3
- package/src/routes/endpoints.js +27 -26
- package/src/routes/endpointsTextAnswer.test.mjs +77 -0
- package/src/routes/feedback.js +10 -13
- package/src/routes/jitPage.js +99 -35
- package/src/routes/jitPage.test.mjs +248 -0
- package/src/routes/mcp.js +82 -5
- package/src/routes/mcp.test.mjs +215 -0
- package/src/routes/mcpMemberToken.test.mjs +125 -0
- package/src/routes/mcpProtectedResourceMetadata.js +34 -0
- package/src/routes/mcpProtectedResourceMetadata.test.mjs +65 -0
- package/src/routes/mountDevPageRoutes.js +25 -0
- package/src/routes/mountDevPageRoutes.test.mjs +30 -0
- package/src/routes/mountOauthDiscovery.js +44 -0
- package/src/routes/mountOauthDiscovery.test.mjs +79 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.js +46 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.test.mjs +62 -0
- package/src/routes/reload.js +16 -0
- package/src/routes/request.js +6 -4
- package/src/routes/request.test.mjs +9 -5
- package/src/routes/requestBody.test.mjs +159 -0
- package/src/routes/usage.js +6 -2
- package/src/routes/user.js +27 -0
- package/src/routes/websocket.js +29 -8
- package/src/routes/websocket.test.mjs +65 -0
- package/src/routes/wellKnownFallback.js +30 -0
- package/src/routes/wellKnownFallback.test.mjs +32 -0
- package/src/websocket/devWebSocket.js +41 -26
- package/src/websocket/devWebSocket.test.mjs +157 -0
- package/vite.config.js +53 -23
- package/lib/server/auth/getDevSession.js +0 -70
- package/lib/server/auth/getDevSession.test.mjs +0 -138
- package/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
- package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
- package/manager/watchers/moduleBuildWatcher.mjs +0 -79
- package/manager/watchers/serverArtifactWatcher.mjs +0 -120
- package/src/routes/apiPage.js +0 -37
|
@@ -0,0 +1,98 @@
|
|
|
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 'fs';
|
|
18
|
+
import os from 'os';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { jest } from '@jest/globals';
|
|
21
|
+
|
|
22
|
+
// A budget one page build passes, so the next request recreates the context.
|
|
23
|
+
jest.unstable_mockModule('./contextMapBudget.js', () => ({ default: 1 }));
|
|
24
|
+
|
|
25
|
+
const { default: buildPageIfNeeded, getBuildContext } = await import('./jitPageBuilder.js');
|
|
26
|
+
|
|
27
|
+
jest.setTimeout(30000);
|
|
28
|
+
|
|
29
|
+
function writeJson(filePath, data) {
|
|
30
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
31
|
+
fs.writeFileSync(filePath, JSON.stringify(data));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
test('a build context past its map budget is recreated, and every page rebuilds on the new one', async () => {
|
|
35
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ldf-context-budget-'));
|
|
36
|
+
const buildDirectory = path.join(root, 'server', 'build');
|
|
37
|
+
const configDirectory = path.join(root, 'config');
|
|
38
|
+
writeJson(path.join(buildDirectory, 'idCounter.json'), { prefix: 'cfg1_', counter: 10 });
|
|
39
|
+
writeJson(path.join(buildDirectory, 'installedPluginPackages.json'), ['@lowdefy/blocks-basic']);
|
|
40
|
+
writeJson(path.join(buildDirectory, 'theme.json'), {});
|
|
41
|
+
const registry = {};
|
|
42
|
+
for (const pageId of ['a', 'b']) {
|
|
43
|
+
fs.mkdirSync(path.join(configDirectory, 'pages'), { recursive: true });
|
|
44
|
+
fs.writeFileSync(
|
|
45
|
+
path.join(configDirectory, 'pages', `${pageId}.yaml`),
|
|
46
|
+
`id: ${pageId}\ntype: Box\n`
|
|
47
|
+
);
|
|
48
|
+
registry[pageId] = {
|
|
49
|
+
pageId,
|
|
50
|
+
auth: { public: true },
|
|
51
|
+
refId: `ref-${pageId}`,
|
|
52
|
+
refPath: `pages/${pageId}.yaml`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
writeJson(path.join(buildDirectory, 'pageRegistry.json'), registry);
|
|
56
|
+
const request = (pageId) => buildPageIfNeeded({ pageId, buildDirectory, configDirectory });
|
|
57
|
+
|
|
58
|
+
expect(await request('a')).toEqual({ built: true, warnings: undefined });
|
|
59
|
+
const firstContext = getBuildContext(buildDirectory, configDirectory);
|
|
60
|
+
expect(await request('b')).toEqual({ built: true, warnings: undefined });
|
|
61
|
+
const secondContext = getBuildContext(buildDirectory, configDirectory);
|
|
62
|
+
expect(secondContext).not.toBe(firstContext);
|
|
63
|
+
|
|
64
|
+
// b was built past the budget too, so the next request recreates again and
|
|
65
|
+
// a, built two contexts ago, rebuilds.
|
|
66
|
+
expect(await request('a')).toEqual({ built: true, warnings: undefined });
|
|
67
|
+
expect(getBuildContext(buildDirectory, configDirectory)).not.toBe(secondContext);
|
|
68
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('a failed page build counts toward the budget', async () => {
|
|
72
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ldf-context-budget-'));
|
|
73
|
+
const buildDirectory = path.join(root, 'server', 'build');
|
|
74
|
+
const configDirectory = path.join(root, 'config');
|
|
75
|
+
writeJson(path.join(buildDirectory, 'idCounter.json'), { prefix: 'cfg1_', counter: 10 });
|
|
76
|
+
writeJson(path.join(buildDirectory, 'installedPluginPackages.json'), ['@lowdefy/blocks-basic']);
|
|
77
|
+
writeJson(path.join(buildDirectory, 'theme.json'), {});
|
|
78
|
+
fs.mkdirSync(path.join(configDirectory, 'pages'), { recursive: true });
|
|
79
|
+
fs.writeFileSync(
|
|
80
|
+
path.join(configDirectory, 'pages', 'broken.yaml'),
|
|
81
|
+
'id: broken\ntype: Box\nblocks:\n - id: typo\n type: Buton\n'
|
|
82
|
+
);
|
|
83
|
+
writeJson(path.join(buildDirectory, 'pageRegistry.json'), {
|
|
84
|
+
broken: {
|
|
85
|
+
pageId: 'broken',
|
|
86
|
+
auth: { public: true },
|
|
87
|
+
refId: 'ref-broken',
|
|
88
|
+
refPath: 'pages/broken.yaml',
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
const request = (pageId) => buildPageIfNeeded({ pageId, buildDirectory, configDirectory });
|
|
92
|
+
|
|
93
|
+
await expect(request('broken')).rejects.toThrow('Buton');
|
|
94
|
+
const firstContext = getBuildContext(buildDirectory, configDirectory);
|
|
95
|
+
await expect(request('broken')).rejects.toThrow('Buton');
|
|
96
|
+
expect(getBuildContext(buildDirectory, configDirectory)).not.toBe(firstContext);
|
|
97
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
98
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { makeId } from '@lowdefy/build/dev';
|
|
18
|
+
|
|
19
|
+
// JIT keys extend the config build's prefix with the dev server process's id, so
|
|
20
|
+
// they never repeat a key of that build, of an earlier config build, or of another
|
|
21
|
+
// process serving the same config build (a restarted server continues the build
|
|
22
|
+
// the one before it served). Within a process the counter only moves forward, so
|
|
23
|
+
// a recreated build context never repeats an earlier page build's key.
|
|
24
|
+
function continueJitKeys({ idCounter, childId }) {
|
|
25
|
+
makeId.continueFrom({ prefix: `${idCounter.prefix}${childId}_`, counter: 0 });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default continueJitKeys;
|
|
@@ -0,0 +1,43 @@
|
|
|
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 { makeId } from '@lowdefy/build/dev';
|
|
18
|
+
|
|
19
|
+
import continueJitKeys from './continueJitKeys.js';
|
|
20
|
+
|
|
21
|
+
test('two dev server processes serving one config build never hand out the same key', () => {
|
|
22
|
+
const idCounter = { prefix: 'a1b2_', counter: 40 };
|
|
23
|
+
|
|
24
|
+
continueJitKeys({ idCounter, childId: 'aaaaaa' });
|
|
25
|
+
const first = [makeId.next(), makeId.next()];
|
|
26
|
+
continueJitKeys({ idCounter, childId: 'bbbbbb' });
|
|
27
|
+
const second = [makeId.next(), makeId.next()];
|
|
28
|
+
|
|
29
|
+
expect(first).toEqual(['a1b2_aaaaaa_1', 'a1b2_aaaaaa_2']);
|
|
30
|
+
expect(second).toEqual(['a1b2_bbbbbb_1', 'a1b2_bbbbbb_2']);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('a recreated build context in the same process continues its keys', () => {
|
|
34
|
+
const idCounter = { prefix: 'c3d4_', counter: 40 };
|
|
35
|
+
|
|
36
|
+
continueJitKeys({ idCounter, childId: 'aaaaaa' });
|
|
37
|
+
const first = makeId.next();
|
|
38
|
+
continueJitKeys({ idCounter, childId: 'aaaaaa' });
|
|
39
|
+
const second = makeId.next();
|
|
40
|
+
|
|
41
|
+
expect(first).toBe('c3d4_aaaaaa_1');
|
|
42
|
+
expect(second).toBe('c3d4_aaaaaa_2');
|
|
43
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
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 { HTTPException } from 'hono/http-exception';
|
|
18
|
+
|
|
19
|
+
// A request whose verified data cookie names a session that has closed outlived its journey. The
|
|
20
|
+
// verified token proves it came from a journey, so it is answered 410 and never falls back to the
|
|
21
|
+
// app's own database. Thrown as an HTTPException, which the error handler sends as it is.
|
|
22
|
+
function createDataSessionEndedError({ id }) {
|
|
23
|
+
const message = `Data session ${id} has ended; this request outlived its journey.`;
|
|
24
|
+
return new HTTPException(410, {
|
|
25
|
+
message,
|
|
26
|
+
res: new Response(JSON.stringify({ name: 'DataSessionEnded', message }), {
|
|
27
|
+
status: 410,
|
|
28
|
+
headers: { 'content-type': 'application/json' },
|
|
29
|
+
}),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default createDataSessionEndedError;
|
|
@@ -0,0 +1,26 @@
|
|
|
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 { ConfigError } from '@lowdefy/errors';
|
|
18
|
+
|
|
19
|
+
function createDataSetRefusal({ artifact, session, reason }) {
|
|
20
|
+
return new ConfigError(
|
|
21
|
+
`Connection "${artifact.connectionId}" (type ${artifact.type}) cannot run under data set "${session.name}": ${reason}.`,
|
|
22
|
+
{ configKey: artifact['~k'] }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default createDataSetRefusal;
|
|
@@ -14,65 +14,81 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import fs from 'node:fs';
|
|
18
17
|
import path from 'node:path';
|
|
19
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
createApiContext,
|
|
20
|
+
createRequestSignal,
|
|
21
|
+
normalizeInjectedCaller,
|
|
22
|
+
ensureMcpOauthResource,
|
|
23
|
+
resolveAuthentication,
|
|
24
|
+
resolvePinnedOrganization,
|
|
25
|
+
resolveTenantPreflight,
|
|
26
|
+
} from '@lowdefy/api';
|
|
27
|
+
import { type } from '@lowdefy/helpers';
|
|
20
28
|
import { getSecretsFromEnv } from '@lowdefy/node-utils';
|
|
21
29
|
import { v4 as uuid } from 'uuid';
|
|
22
30
|
|
|
23
31
|
import agents from '../../build/plugins/agents.js';
|
|
24
32
|
import appMeta from '../build/appMeta.js';
|
|
33
|
+
import applyDataSetRedirect from './applyDataSetRedirect.js';
|
|
34
|
+
import authJson from '../build/auth.js';
|
|
25
35
|
import config from '../build/config.js';
|
|
26
36
|
import connections from '../../build/plugins/connections.js';
|
|
37
|
+
import createDataSessionEndedError from './createDataSessionEndedError.js';
|
|
27
38
|
import createHandleError from './log/createHandleError.js';
|
|
28
39
|
import createLogger from './log/createLogger.js';
|
|
40
|
+
import createMutantReadConfigFile from './mutants/createMutantReadConfigFile.js';
|
|
29
41
|
import fileCache from './fileCache.js';
|
|
30
|
-
import
|
|
31
|
-
import
|
|
42
|
+
import getAuth from './auth/getAuth.js';
|
|
43
|
+
import getHeadlessUser from './auth/getHeadlessUser.js';
|
|
44
|
+
import getMockUser from './auth/getMockUser.js';
|
|
45
|
+
import getStrategies from './auth/getStrategies.js';
|
|
46
|
+
import { forwardJourneyCookies } from './journeyCookies.js';
|
|
32
47
|
import i18nConfig from '../build/i18n.js';
|
|
48
|
+
import loadDynamicJsMap from './loadDynamicJsMap.js';
|
|
33
49
|
import logRequest from './log/logRequest.js';
|
|
50
|
+
import { readMutantRun } from './mutants/mutantRuns.js';
|
|
51
|
+
import readDataSession from '../docs/dataSets/readDataSession.js';
|
|
52
|
+
import readMergedMaps from './readMergedMaps.js';
|
|
53
|
+
import readRecordingCookie from './recording/readRecordingCookie.js';
|
|
34
54
|
import scrubSecrets from './scrubSecrets.js';
|
|
55
|
+
import sinkExternalConnections from './sinkExternalConnections.js';
|
|
35
56
|
import notifications, {
|
|
36
57
|
interpolateProperties,
|
|
37
58
|
renderEmail,
|
|
38
59
|
} from '../../build/plugins/notifications.js';
|
|
39
60
|
import operators from '../../build/plugins/operators/server.js';
|
|
40
|
-
import
|
|
61
|
+
import resolveHeadlessUser from './auth/resolveHeadlessUser.js';
|
|
62
|
+
import steps from '../../build/plugins/steps.js';
|
|
63
|
+
import trackSessionWork from './trackSessionWork.js';
|
|
41
64
|
import websockets from '../../build/plugins/websockets.js';
|
|
42
65
|
|
|
43
66
|
const secrets = getSecretsFromEnv();
|
|
44
67
|
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
function loadDynamicJsMap(buildDirectory) {
|
|
51
|
-
const jsMapPath = path.join(buildDirectory, 'plugins', 'operators', 'serverJsMap.js');
|
|
52
|
-
try {
|
|
53
|
-
const stat = fs.statSync(jsMapPath);
|
|
54
|
-
if (cachedJsMapMtime && stat.mtimeMs === cachedJsMapMtime) {
|
|
55
|
-
return cachedJsMap;
|
|
56
|
-
}
|
|
57
|
-
cachedJsMapMtime = stat.mtimeMs;
|
|
58
|
-
// For server-side, we can read and eval the JS file
|
|
59
|
-
const content = fs.readFileSync(jsMapPath, 'utf8');
|
|
60
|
-
const fn = new Function('exports', content.replace('export default', 'exports.default ='));
|
|
61
|
-
const exports = {};
|
|
62
|
-
fn(exports);
|
|
63
|
-
cachedJsMap = { ...staticJsMap, ...(exports.default ?? {}) };
|
|
64
|
-
return cachedJsMap;
|
|
65
|
-
} catch {
|
|
66
|
-
return cachedJsMap;
|
|
67
|
-
}
|
|
68
|
+
// The MCP route authenticates by access token alone - the mcp option switches
|
|
69
|
+
// resolveAuthentication onto its bearer branch, so it is set only for the MCP
|
|
70
|
+
// path. Every other path leaves it false and authentication behaves as before.
|
|
71
|
+
function isMcpPath(path) {
|
|
72
|
+
return path.endsWith('/api/mcp');
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
// Builds the per-request lowdefy API context (connections, secrets,
|
|
71
|
-
// operators, logger,
|
|
72
|
-
// (src/middleware/apiContext.js) so run_request
|
|
73
|
-
// build an identical context outside the Hono
|
|
74
|
-
// callRequest directly for agent-driven request
|
|
75
|
-
|
|
76
|
+
// operators, logger, resolved user, etc.). Factored out of the /api/*
|
|
77
|
+
// middleware (src/middleware/apiContext.js) so run_request
|
|
78
|
+
// (lib/docs/runRequest.js) can build an identical context outside the Hono
|
|
79
|
+
// middleware chain, to call callRequest directly for agent-driven request
|
|
80
|
+
// execution. A `user` option injects a per-call caller (agent tools that run
|
|
81
|
+
// outside a browser, e.g. run_request), resolved the same way the headless
|
|
82
|
+
// renderer's cookie user is. A `dataSession` option runs a call the dev
|
|
83
|
+
// server makes itself (run_request, run_endpoint on a data set) on that data
|
|
84
|
+
// session's databases, as a journey's cookie does for its browser requests.
|
|
85
|
+
async function createLowdefyContext({ c, user, dataSession: callDataSession }) {
|
|
86
|
+
// A journey on a data set: this request, and only this one, reads the session's databases.
|
|
87
|
+
// Read before anything else, so a request that outlived its session never reaches any database.
|
|
88
|
+
const dataSession = callDataSession ?? readDataSession(c.req.header('cookie'));
|
|
89
|
+
if (!type.isNone(dataSession?.ended)) {
|
|
90
|
+
throw createDataSessionEndedError({ id: dataSession.ended });
|
|
91
|
+
}
|
|
76
92
|
const buildDirectory = path.join(process.cwd(), 'build');
|
|
77
93
|
const jsMap = loadDynamicJsMap(buildDirectory);
|
|
78
94
|
|
|
@@ -96,7 +112,7 @@ async function createLowdefyContext({ c }) {
|
|
|
96
112
|
i18n: i18nConfig,
|
|
97
113
|
interpolateProperties,
|
|
98
114
|
jsMap,
|
|
99
|
-
logger: createLogger(),
|
|
115
|
+
logger: createLogger({ rid }),
|
|
100
116
|
mode: 'dev',
|
|
101
117
|
notifications,
|
|
102
118
|
operators,
|
|
@@ -108,19 +124,130 @@ async function createLowdefyContext({ c }) {
|
|
|
108
124
|
},
|
|
109
125
|
scrubSecrets,
|
|
110
126
|
secrets,
|
|
127
|
+
// Error locations resolve through it (handleError, logClientError): JIT
|
|
128
|
+
// page builds write their keys to jitMaps/, not keyMap.json.
|
|
129
|
+
readMaps: () => readMergedMaps({ buildDirectory }),
|
|
130
|
+
// Aborts when the client disconnects before the response is sent, or when the
|
|
131
|
+
// request timeout (src/middleware/requestTimeout.js) answers first.
|
|
132
|
+
signal: createRequestSignal({
|
|
133
|
+
clientSignal: c.req.raw.signal,
|
|
134
|
+
timeoutSignal: c.get('requestTimeoutSignal'),
|
|
135
|
+
}),
|
|
136
|
+
steps,
|
|
111
137
|
websockets,
|
|
112
138
|
};
|
|
139
|
+
// The one writer of loopbackHeaders: a detached CallApi carries these on its
|
|
140
|
+
// loopback fetch, so the target runs with the journey's data set and mutant.
|
|
141
|
+
// With no journey cookie to forward it carries no Cookie header at all.
|
|
142
|
+
const journeyCookies = forwardJourneyCookies({
|
|
143
|
+
cookieHeader: c.req.header('cookie'),
|
|
144
|
+
dataSessionId: callDataSession?.id,
|
|
145
|
+
});
|
|
146
|
+
context.loopbackHeaders = journeyCookies === '' ? {} : { cookie: journeyCookies };
|
|
147
|
+
// Which browser context sent the request: errors it causes are stamped with
|
|
148
|
+
// it, so a journey run claims only its own (see createHandleError).
|
|
149
|
+
context.recording = readRecordingCookie(c.req.header('cookie'));
|
|
113
150
|
context.handleError = createHandleError({ context });
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
151
|
+
const mockUser = getMockUser();
|
|
152
|
+
const headlessCaller = getHeadlessUser(c);
|
|
153
|
+
if (!type.isNone(user)) {
|
|
154
|
+
// An explicit per-call user wins over the ambient auth.dev.mockUser: the
|
|
155
|
+
// caller named an identity for this call, so honour it. Merged over the
|
|
156
|
+
// roleless headless default by resolveHeadlessUser, exactly as the
|
|
157
|
+
// headless page tools' `user` param is.
|
|
158
|
+
context.auth = null;
|
|
159
|
+
context.user = normalizeInjectedCaller(resolveHeadlessUser({ user }));
|
|
160
|
+
} else if (headlessCaller?.explicit === true) {
|
|
161
|
+
// A headless context whose tool call or journey named its user (getBrowser.js)
|
|
162
|
+
// wins over the mock user for the same reason: a journey with
|
|
163
|
+
// user: { roles: [admin] } must act as that user, not as the mock user.
|
|
164
|
+
context.auth = null;
|
|
165
|
+
context.user = normalizeInjectedCaller(headlessCaller.user);
|
|
166
|
+
} else if (mockUser) {
|
|
167
|
+
// The mock user is a pre-resolved caller - it substitutes for the
|
|
168
|
+
// whole resolveAuthentication step and its roles are authoritative.
|
|
169
|
+
// No auth engine runs while dev.mockUser is active (see src/app.js),
|
|
170
|
+
// so mock mode never touches the auth database. normalizeInjectedCaller
|
|
171
|
+
// floors it to the resolved-caller shape.
|
|
172
|
+
context.auth = null;
|
|
173
|
+
context.user = normalizeInjectedCaller(mockUser);
|
|
174
|
+
} else if (headlessCaller !== null) {
|
|
175
|
+
// The headless renderer (docs/MCP screenshot and state tools) injects a
|
|
176
|
+
// user cookie on its own browser context (getBrowser.js), so its /api/*
|
|
177
|
+
// fetches carry a pre-resolved caller and auth-protected pages render.
|
|
178
|
+
// The developer's real browser has no cookie and resolves through the
|
|
179
|
+
// auth engine below, so it is unaffected. The roleless default it injects
|
|
180
|
+
// when no user was named yields to the mock user above.
|
|
181
|
+
context.auth = null;
|
|
182
|
+
context.user = normalizeInjectedCaller(headlessCaller.user);
|
|
183
|
+
} else if (!type.isNone(dataSession)) {
|
|
184
|
+
// A data-session request never builds the auth engine: it is bound to the real auth database.
|
|
185
|
+
// This is the detached hop (data cookie, no headless cookie); runDetachedEndpoint sets
|
|
186
|
+
// context.user from the principal the dispatcher carried. Auth steps fail, as for any injected
|
|
187
|
+
// caller.
|
|
188
|
+
context.auth = null;
|
|
189
|
+
context.user = null;
|
|
190
|
+
} else {
|
|
191
|
+
// Hoisted once per request - resolveAuthentication also needs it, and
|
|
192
|
+
// getBetterAuth memoizes the instance, but this keeps the auth engine
|
|
193
|
+
// construction to a single call site per request.
|
|
194
|
+
context.auth = getAuth({ logger: context.logger });
|
|
195
|
+
// The engine is constructed lazily on the first request, which would
|
|
196
|
+
// otherwise race the startup pinned-org ensure - await the memoized
|
|
197
|
+
// resolve so createApiContext reads a retained binding.
|
|
198
|
+
await resolvePinnedOrganization({ auth: context.auth, logger: context.logger });
|
|
199
|
+
// The one oauthResource row the MCP token audience validates against -
|
|
200
|
+
// ensured once per process, memoized like the pinned resolve above; a
|
|
201
|
+
// failure retries on the next request. No-op when the app is not an
|
|
202
|
+
// authorization server.
|
|
203
|
+
await ensureMcpOauthResource({ auth: context.auth, logger: context.logger });
|
|
204
|
+
if (!c.req.path.includes('/api/auth')) {
|
|
205
|
+
// resolveAuthentication is the single writer of context.user.
|
|
206
|
+
await resolveAuthentication(context, {
|
|
207
|
+
auth: context.auth,
|
|
208
|
+
headers: c.req.raw.headers,
|
|
209
|
+
strategies: getStrategies({ logger: context.logger }),
|
|
210
|
+
mcp: isMcpPath(c.req.path),
|
|
211
|
+
});
|
|
121
212
|
}
|
|
122
213
|
}
|
|
123
214
|
createApiContext(context);
|
|
215
|
+
// A journey's browser contexts carry a mutant cookie while `lowdefy journeys
|
|
216
|
+
// harden` runs a mutant through them: their requests, and only theirs, read
|
|
217
|
+
// the mutated artifact. The developer's own tabs carry none.
|
|
218
|
+
const mutantRun = readMutantRun(c.req.header('cookie'));
|
|
219
|
+
if (mutantRun !== null) {
|
|
220
|
+
context.readConfigFile = createMutantReadConfigFile({
|
|
221
|
+
readConfigFile: context.readConfigFile,
|
|
222
|
+
run: mutantRun,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
if (!type.isNone(dataSession)) {
|
|
226
|
+
applyDataSetRedirect({ context, session: dataSession, connections });
|
|
227
|
+
// Requests of connections that reach an outside service are recorded, never sent.
|
|
228
|
+
context.connections = sinkExternalConnections({
|
|
229
|
+
context,
|
|
230
|
+
connections,
|
|
231
|
+
session: dataSession,
|
|
232
|
+
});
|
|
233
|
+
context.dataSet = dataSession.name;
|
|
234
|
+
context.waitUntil = trackSessionWork({ session: dataSession });
|
|
235
|
+
}
|
|
236
|
+
if (!context.auth && authJson.organizations) {
|
|
237
|
+
// Mock and headless callers run no auth engine, so createApiContext
|
|
238
|
+
// retains no organization binding. Derive the policy from the built auth
|
|
239
|
+
// config so the tenant wall's policy gate still engages under
|
|
240
|
+
// policy: tenant (a mock caller there must carry organizationId, or the
|
|
241
|
+
// wall fails closed - loudly, as it should).
|
|
242
|
+
context.organization = { policy: authJson.organizations.policy, pinned: null };
|
|
243
|
+
}
|
|
244
|
+
// Under policy: tenant, report walled collections that hold unstamped rows
|
|
245
|
+
// (lazily-run-once, logged once per offending collection). It never blocks or
|
|
246
|
+
// fails a request. No-op under pinned. Skipped under a data session: the report runs once per
|
|
247
|
+
// process, so it would describe the data set's database and never the developer's.
|
|
248
|
+
if (type.isNone(dataSession)) {
|
|
249
|
+
void resolveTenantPreflight(context);
|
|
250
|
+
}
|
|
124
251
|
logRequest({ context });
|
|
125
252
|
return context;
|
|
126
253
|
}
|