@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,127 @@
|
|
|
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
|
+
|
|
21
|
+
import { Hono } from 'hono';
|
|
22
|
+
|
|
23
|
+
// The middleware reads build/buildStatus.json from process.cwd() via
|
|
24
|
+
// readBuildArtifact — point it at a throwaway server directory before import.
|
|
25
|
+
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-stale-mw-test-'));
|
|
26
|
+
fs.mkdirSync(path.join(fixtureDir, 'build'));
|
|
27
|
+
process.chdir(fixtureDir);
|
|
28
|
+
|
|
29
|
+
const statusPath = path.join(fixtureDir, 'build', 'buildStatus.json');
|
|
30
|
+
|
|
31
|
+
function setBuildFailed() {
|
|
32
|
+
fs.writeFileSync(
|
|
33
|
+
statusPath,
|
|
34
|
+
JSON.stringify({ status: 'error', timestamp: '2026-02-03T04:05:06.000Z', errors: [] })
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function setBuildOk() {
|
|
39
|
+
fs.writeFileSync(
|
|
40
|
+
statusPath,
|
|
41
|
+
JSON.stringify({ status: 'ok', timestamp: '2026-02-03T04:05:06.000Z', errors: [] })
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { default: staleFlag } = await import('./staleFlag.js');
|
|
46
|
+
|
|
47
|
+
function createApp() {
|
|
48
|
+
const app = new Hono();
|
|
49
|
+
// Mirrors src/app.js: the MCP transport is registered BEFORE the middleware
|
|
50
|
+
// so its JSON-RPC envelopes are never given extra members.
|
|
51
|
+
app.post('/lowdefy-docs/mcp', (c) => c.json({ jsonrpc: '2.0', id: 1, result: {} }));
|
|
52
|
+
app.use('/lowdefy-docs/*', staleFlag());
|
|
53
|
+
app.get('/lowdefy-docs/object', (c) => c.json({ pageId: 'home', blocks: [] }));
|
|
54
|
+
app.get('/lowdefy-docs/own-stale', (c) => c.json({ stale: false, staleSince: 'handler' }));
|
|
55
|
+
app.get('/lowdefy-docs/array', (c) => c.json([{ type: 'Button' }]));
|
|
56
|
+
app.get('/lowdefy-docs/markdown', (c) =>
|
|
57
|
+
c.text('# Overview\n', 200, { 'Content-Type': 'text/markdown; charset=UTF-8' })
|
|
58
|
+
);
|
|
59
|
+
app.get('/lowdefy-docs/png', (c) =>
|
|
60
|
+
c.body(new Uint8Array([1, 2, 3]), 200, { 'Content-Type': 'image/png' })
|
|
61
|
+
);
|
|
62
|
+
return app;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
test('staleFlag merges the stale fields into a JSON object body when the build failed', async () => {
|
|
66
|
+
setBuildFailed();
|
|
67
|
+
const res = await createApp().request('/lowdefy-docs/object');
|
|
68
|
+
const body = await res.json();
|
|
69
|
+
expect(body.pageId).toEqual('home');
|
|
70
|
+
expect(body.stale).toBe(true);
|
|
71
|
+
expect(body.staleSince).toEqual('2026-02-03T04:05:06.000Z');
|
|
72
|
+
expect(body.staleReason).toContain('The last build failed.');
|
|
73
|
+
expect(res.headers.get('X-Lowdefy-Stale')).toEqual('true');
|
|
74
|
+
expect(res.headers.get('X-Lowdefy-Stale-Since')).toEqual('2026-02-03T04:05:06.000Z');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('staleFlag never overwrites a stale key the handler already set', async () => {
|
|
78
|
+
setBuildFailed();
|
|
79
|
+
const res = await createApp().request('/lowdefy-docs/own-stale');
|
|
80
|
+
const body = await res.json();
|
|
81
|
+
expect(body.stale).toBe(false);
|
|
82
|
+
expect(body.staleSince).toEqual('handler');
|
|
83
|
+
expect(body.staleReason).toContain('The last build failed.');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('staleFlag leaves a JSON array body unchanged but still sets the headers', async () => {
|
|
87
|
+
setBuildFailed();
|
|
88
|
+
const res = await createApp().request('/lowdefy-docs/array');
|
|
89
|
+
expect(await res.json()).toEqual([{ type: 'Button' }]);
|
|
90
|
+
expect(res.headers.get('X-Lowdefy-Stale')).toEqual('true');
|
|
91
|
+
expect(res.headers.get('X-Lowdefy-Stale-Since')).toEqual('2026-02-03T04:05:06.000Z');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('staleFlag prepends a banner to a markdown body when the build failed', async () => {
|
|
95
|
+
setBuildFailed();
|
|
96
|
+
const res = await createApp().request('/lowdefy-docs/markdown');
|
|
97
|
+
const body = await res.text();
|
|
98
|
+
expect(body.startsWith('> STALE: The last build failed.')).toBe(true);
|
|
99
|
+
expect(body).toContain('# Overview');
|
|
100
|
+
expect(res.headers.get('X-Lowdefy-Stale')).toEqual('true');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test('staleFlag leaves other content types untouched but still sets the headers', async () => {
|
|
104
|
+
setBuildFailed();
|
|
105
|
+
const res = await createApp().request('/lowdefy-docs/png');
|
|
106
|
+
expect(new Uint8Array(await res.arrayBuffer())).toEqual(new Uint8Array([1, 2, 3]));
|
|
107
|
+
expect(res.headers.get('X-Lowdefy-Stale')).toEqual('true');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test('staleFlag changes nothing when the last build succeeded', async () => {
|
|
111
|
+
setBuildOk();
|
|
112
|
+
const app = createApp();
|
|
113
|
+
const jsonRes = await app.request('/lowdefy-docs/object');
|
|
114
|
+
expect(await jsonRes.json()).toEqual({ pageId: 'home', blocks: [] });
|
|
115
|
+
expect(jsonRes.headers.get('X-Lowdefy-Stale')).toBe(null);
|
|
116
|
+
const markdownRes = await app.request('/lowdefy-docs/markdown');
|
|
117
|
+
expect(await markdownRes.text()).toEqual('# Overview\n');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// MCP results carry their own STALE notice from createDocsMcpServer; a
|
|
121
|
+
// JSON-RPC envelope must not gain unknown top-level members.
|
|
122
|
+
test('staleFlag does not touch a route registered before it, such as the MCP transport', async () => {
|
|
123
|
+
setBuildFailed();
|
|
124
|
+
const res = await createApp().request('/lowdefy-docs/mcp', { method: 'POST' });
|
|
125
|
+
expect(await res.json()).toEqual({ jsonrpc: '2.0', id: 1, result: {} });
|
|
126
|
+
expect(res.headers.get('X-Lowdefy-Stale')).toBe(null);
|
|
127
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
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 { callerStamp } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
// Stamps page, request and endpoint responses with the caller the server
|
|
20
|
+
// answered as, in full (see callerStamp): user and organization, session, roles
|
|
21
|
+
// there and every organization the person belongs to. The client compares it
|
|
22
|
+
// with the caller it renders and reloads when the person, organization or
|
|
23
|
+
// roles differ, so a tab never shows one caller's data or access after the
|
|
24
|
+
// session moved on. Registered after apiContext, so the resolved caller is
|
|
25
|
+
// always on the context here; set after next() so error responses carry it
|
|
26
|
+
// too.
|
|
27
|
+
function stampCaller() {
|
|
28
|
+
return async function stampCallerMiddleware(c, next) {
|
|
29
|
+
await next();
|
|
30
|
+
const context = c.get('lowdefyContext');
|
|
31
|
+
c.header(
|
|
32
|
+
'x-lowdefy-caller',
|
|
33
|
+
callerStamp({ user: context.user, authSession: context.authSession })
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default stampCaller;
|
|
@@ -0,0 +1,64 @@
|
|
|
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 { Hono } from 'hono';
|
|
18
|
+
|
|
19
|
+
import stampCaller from './stampCaller.js';
|
|
20
|
+
|
|
21
|
+
function createApp({ user, authSession }) {
|
|
22
|
+
const app = new Hono();
|
|
23
|
+
app.use('/api/*', async (c, next) => {
|
|
24
|
+
c.set('lowdefyContext', { user, authSession });
|
|
25
|
+
return next();
|
|
26
|
+
});
|
|
27
|
+
app.use('/api/request/*', stampCaller());
|
|
28
|
+
app.post('/api/request/page/ok', (c) => c.json({ ok: true }));
|
|
29
|
+
app.post('/api/request/page/throws', () => {
|
|
30
|
+
throw new Error('Request failed.');
|
|
31
|
+
});
|
|
32
|
+
app.onError((error, c) => c.json({ message: error.message }, 500));
|
|
33
|
+
return app;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test.each([
|
|
37
|
+
['/api/request/page/ok', 200],
|
|
38
|
+
['/api/request/page/throws', 500],
|
|
39
|
+
])('stampCaller sets x-lowdefy-caller on %s (%i)', async (path, status) => {
|
|
40
|
+
const app = createApp({
|
|
41
|
+
user: { id: 'user_1', organization_id: 'org_1', roles: ['editor', 'admin'] },
|
|
42
|
+
authSession: { id: 'session_1', memberOrganizationIds: ['org_2', 'org_1'] },
|
|
43
|
+
});
|
|
44
|
+
const res = await app.request(path, { method: 'POST' });
|
|
45
|
+
expect(res.status).toBe(status);
|
|
46
|
+
// The fixed vector callerStamp's own tests pin, so server and client agree.
|
|
47
|
+
expect(res.headers.get('x-lowdefy-caller')).toBe(
|
|
48
|
+
'82e92667feb5824d; session=3a1d7b6848ba4816; roles=228d50a09b1b1e2b; memberships=476954726056d64d'
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('stampCaller names the caller and roles alone for a caller with no session', async () => {
|
|
53
|
+
const app = createApp({
|
|
54
|
+
user: { id: 'user_1', organization_id: 'org_1', roles: ['editor', 'admin'] },
|
|
55
|
+
});
|
|
56
|
+
const res = await app.request('/api/request/page/ok', { method: 'POST' });
|
|
57
|
+
expect(res.headers.get('x-lowdefy-caller')).toBe('82e92667feb5824d; roles=228d50a09b1b1e2b');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('stampCaller sends an empty x-lowdefy-caller for an anonymous caller', async () => {
|
|
61
|
+
const app = createApp({ user: null });
|
|
62
|
+
const res = await app.request('/api/request/page/ok', { method: 'POST' });
|
|
63
|
+
expect(res.headers.get('x-lowdefy-caller')).toBe('');
|
|
64
|
+
});
|
package/src/routes/agent.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { callAgent } from '@lowdefy/api';
|
|
17
|
+
import { callAgent, parseRequestBody } from '@lowdefy/api';
|
|
18
18
|
import { translate, type } from '@lowdefy/helpers';
|
|
19
19
|
|
|
20
20
|
import getPathSegments from '../lib/getPathSegments.js';
|
|
@@ -23,7 +23,9 @@ async function agentHandler(c) {
|
|
|
23
23
|
const context = c.get('lowdefyContext');
|
|
24
24
|
const t = (key, values) => translate({ key, values, i18n: context.i18n });
|
|
25
25
|
if (c.req.method !== 'POST') {
|
|
26
|
-
|
|
26
|
+
// A wrong-method request is client-caused: answer 405 rather than raising a
|
|
27
|
+
// fault that would be logged at error level and answered with a 500.
|
|
28
|
+
return c.json({ error: t('agent.runtime.methodNotAllowed') }, 405);
|
|
27
29
|
}
|
|
28
30
|
const segments = getPathSegments(c, '/api/agent/');
|
|
29
31
|
if (segments.length < 2) {
|
|
@@ -33,22 +35,32 @@ async function agentHandler(c) {
|
|
|
33
35
|
const pageId = segments.slice(0, -1).join('/');
|
|
34
36
|
context.logger.info({ event: 'call_agent', agentId, pageId });
|
|
35
37
|
const { conversationId } = c.req.query();
|
|
36
|
-
const { messages, urlQuery, sharedState } =
|
|
38
|
+
const { messages, pathParams, urlQuery, sharedState, sharedStateReadOnly } = parseRequestBody({
|
|
39
|
+
text: await c.req.text(),
|
|
40
|
+
});
|
|
37
41
|
if (!Array.isArray(messages)) {
|
|
38
42
|
return c.json({ error: t('agent.runtime.messagesMustBeArray') }, 400);
|
|
39
43
|
}
|
|
40
44
|
if (urlQuery != null && (typeof urlQuery !== 'object' || Array.isArray(urlQuery))) {
|
|
41
45
|
return c.json({ error: t('agent.runtime.urlQueryMustBeObject') }, 400);
|
|
42
46
|
}
|
|
47
|
+
if (pathParams != null && !type.isObject(pathParams)) {
|
|
48
|
+
return c.json({ error: t('agent.runtime.pathParamsMustBeObject') }, 400);
|
|
49
|
+
}
|
|
43
50
|
if (sharedState != null && !type.isObject(sharedState)) {
|
|
44
51
|
return c.json({ error: t('agent.runtime.sharedStateMustBeObject') }, 400);
|
|
45
52
|
}
|
|
53
|
+
if (sharedStateReadOnly != null && !type.isBoolean(sharedStateReadOnly)) {
|
|
54
|
+
return c.json({ error: t('agent.runtime.sharedStateReadOnlyMustBeBoolean') }, 400);
|
|
55
|
+
}
|
|
46
56
|
const { response: webResponse } = await callAgent(context, {
|
|
47
57
|
agentId,
|
|
48
58
|
pageId,
|
|
49
59
|
messages,
|
|
50
60
|
conversationId: conversationId ?? undefined,
|
|
51
61
|
sharedState: sharedState ?? undefined,
|
|
62
|
+
sharedStateReadOnly: sharedStateReadOnly ?? undefined,
|
|
63
|
+
pathParams: pathParams ?? undefined,
|
|
52
64
|
urlQuery: urlQuery ?? undefined,
|
|
53
65
|
});
|
|
54
66
|
|
package/src/routes/auth.js
CHANGED
|
@@ -14,35 +14,44 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { handleAuthRequest } from '@lowdefy/api';
|
|
18
|
+
import { type } from '@lowdefy/helpers';
|
|
18
19
|
|
|
19
20
|
import authJson from '../../lib/build/auth.js';
|
|
20
|
-
import
|
|
21
|
+
import getAuth from '../../lib/server/auth/getAuth.js';
|
|
22
|
+
import getClientAddress from '../../lib/server/getClientAddress.js';
|
|
23
|
+
import getMockUser from '../../lib/server/auth/getMockUser.js';
|
|
21
24
|
|
|
22
|
-
//
|
|
23
|
-
// GET handlers, so
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
// Mounts BetterAuth's Web Standard handler on /api/auth/*. Hono routes HEAD
|
|
26
|
+
// requests through GET handlers, so HEAD short-circuits before the handler -
|
|
27
|
+
// corporate email link-checkers pre-fetch magic-link and verification URLs
|
|
28
|
+
// with HEAD, and letting those reach the handler would consume the one-time
|
|
29
|
+
// token before the user clicks. BetterAuth rate-limits by the client address
|
|
30
|
+
// getClientAddress resolves.
|
|
31
|
+
function authMiddleware({ logger }) {
|
|
32
|
+
return async function auth(c) {
|
|
29
33
|
if (authJson.configured !== true) {
|
|
30
34
|
return c.json({ message: 'Auth not configured' }, 404);
|
|
31
35
|
}
|
|
36
|
+
if (getMockUser()) {
|
|
37
|
+
// Mock user active - no auth engine runs; the get-session stub in
|
|
38
|
+
// app.js is the only auth endpoint.
|
|
39
|
+
return c.json({ message: 'Auth engine disabled while dev.mockUser is active' }, 404);
|
|
40
|
+
}
|
|
41
|
+
if (!type.isNone(c.get('lowdefyContext')?.dataSet)) {
|
|
42
|
+
// A journey on a data set: the auth engine is bound to the app's real auth database, so its
|
|
43
|
+
// browser contexts never reach it. Data set users are injected callers; the get-session stub
|
|
44
|
+
// in app.js answers for them.
|
|
45
|
+
return c.json({ message: 'Auth engine disabled for journeys on a data set' }, 404);
|
|
46
|
+
}
|
|
32
47
|
if (c.req.method === 'HEAD') {
|
|
33
48
|
return c.body(null, 200);
|
|
34
49
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const devSession = await getDevSession(c);
|
|
41
|
-
if (devSession) {
|
|
42
|
-
return c.json(devSession);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return handler(c, next);
|
|
50
|
+
return handleAuthRequest({
|
|
51
|
+
auth: getAuth({ logger }),
|
|
52
|
+
request: c.req.raw,
|
|
53
|
+
clientAddress: getClientAddress(c),
|
|
54
|
+
});
|
|
46
55
|
};
|
|
47
56
|
}
|
|
48
57
|
|
package/src/routes/auth.test.mjs
CHANGED
|
@@ -14,71 +14,45 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { Hono } from 'hono';
|
|
18
17
|
import { jest } from '@jest/globals';
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
jest.unstable_mockModule('
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
jest.unstable_mockModule('../../lib/build/auth.js', () => ({
|
|
29
|
-
default: { configured: true },
|
|
19
|
+
const handleAuthRequest = jest.fn(async () => new Response('engine'));
|
|
20
|
+
const getAuth = jest.fn(() => ({}));
|
|
21
|
+
|
|
22
|
+
jest.unstable_mockModule('@lowdefy/api', () => ({ handleAuthRequest }));
|
|
23
|
+
jest.unstable_mockModule('../../lib/build/auth.js', () => ({ default: { configured: true } }));
|
|
24
|
+
jest.unstable_mockModule('../../lib/server/auth/getAuth.js', () => ({ default: getAuth }));
|
|
25
|
+
jest.unstable_mockModule('../../lib/server/auth/getMockUser.js', () => ({
|
|
26
|
+
default: jest.fn(() => undefined),
|
|
30
27
|
}));
|
|
31
|
-
|
|
32
|
-
jest.
|
|
33
|
-
default: mockGetDevSession,
|
|
28
|
+
jest.unstable_mockModule('../../lib/server/getClientAddress.js', () => ({
|
|
29
|
+
default: jest.fn(() => '127.0.0.1'),
|
|
34
30
|
}));
|
|
35
31
|
|
|
36
32
|
const { default: authMiddleware } = await import('./auth.js');
|
|
37
33
|
|
|
38
|
-
function
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
afterEach(() => {
|
|
45
|
-
mockGetDevSession.mockReset();
|
|
46
|
-
mockGetDevSession.mockResolvedValue(undefined);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('GET /api/auth/session returns the dev session so the client matches the server', async () => {
|
|
50
|
-
const devSession = {
|
|
51
|
-
user: {
|
|
52
|
-
id: 'dev-mock-user',
|
|
53
|
-
roles: ['admin'],
|
|
54
|
-
global_attributes: { company_ids: ['C-1'] },
|
|
55
|
-
},
|
|
56
|
-
hashed_id: 'abc',
|
|
57
|
-
expires: '2100-01-01T00:00:00.000Z',
|
|
34
|
+
function createHonoContext({ context }) {
|
|
35
|
+
return {
|
|
36
|
+
get: (key) => (key === 'lowdefyContext' ? context : undefined),
|
|
37
|
+
json: (body, status) => ({ body, status }),
|
|
38
|
+
body: (body, status) => ({ body, status }),
|
|
39
|
+
req: { method: 'POST', raw: new Request('http://localhost/api/auth/sign-out') },
|
|
58
40
|
};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const res = await createApp().request('/api/auth/session');
|
|
62
|
-
expect(res.status).toEqual(200);
|
|
63
|
-
expect(await res.json()).toEqual(devSession);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test('GET /api/auth/session delegates to Auth.js when no dev session exists', async () => {
|
|
67
|
-
const res = await createApp().request('/api/auth/session');
|
|
68
|
-
expect(res.status).toEqual(200);
|
|
69
|
-
expect(await res.json()).toEqual({ delegated: true });
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('other auth routes delegate to Auth.js even when a dev session exists', async () => {
|
|
73
|
-
mockGetDevSession.mockResolvedValue({ user: { id: 'mock' } });
|
|
41
|
+
}
|
|
74
42
|
|
|
75
|
-
|
|
76
|
-
|
|
43
|
+
test('authMiddleware refuses a data-session request without reaching the auth engine', async () => {
|
|
44
|
+
const handler = authMiddleware({ logger: {} });
|
|
45
|
+
const result = await handler(createHonoContext({ context: { dataSet: 'staging-sample' } }));
|
|
46
|
+
expect(result).toEqual({
|
|
47
|
+
body: { message: 'Auth engine disabled for journeys on a data set' },
|
|
48
|
+
status: 404,
|
|
49
|
+
});
|
|
50
|
+
expect(getAuth).not.toHaveBeenCalled();
|
|
51
|
+
expect(handleAuthRequest).not.toHaveBeenCalled();
|
|
77
52
|
});
|
|
78
53
|
|
|
79
|
-
test('
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
expect(await res.json()).toEqual({ delegated: true });
|
|
54
|
+
test('authMiddleware hands a request with no data session to the auth engine', async () => {
|
|
55
|
+
const handler = authMiddleware({ logger: {} });
|
|
56
|
+
await handler(createHonoContext({ context: {} }));
|
|
57
|
+
expect(handleAuthRequest).toHaveBeenCalledTimes(1);
|
|
84
58
|
});
|
|
@@ -14,30 +14,29 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { logClientError } from '@lowdefy/api';
|
|
17
|
+
import { logClientError, parseRequestBody } from '@lowdefy/api';
|
|
18
18
|
|
|
19
19
|
import clientErrorStore from '../../lib/docs/clientErrorStore.js';
|
|
20
|
+
import errorRecordingStamp from '../../lib/server/recording/errorRecordingStamp.js';
|
|
21
|
+
import createSameOriginGuard from '../middleware/createSameOriginGuard.js';
|
|
22
|
+
|
|
23
|
+
const guardSameOrigin = createSameOriginGuard();
|
|
20
24
|
|
|
21
25
|
async function clientErrorHandler(c) {
|
|
22
26
|
if (c.req.method !== 'POST') {
|
|
23
|
-
|
|
27
|
+
// A wrong-method request is client-caused: answer 405 rather than raising a
|
|
28
|
+
// fault that would be logged at error level and answered with a 500.
|
|
29
|
+
return c.json({ error: 'Method not allowed.' }, 405);
|
|
24
30
|
}
|
|
25
31
|
const context = c.get('lowdefyContext');
|
|
26
32
|
|
|
27
|
-
const
|
|
28
|
-
if (
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
try {
|
|
32
|
-
if (new URL(origin).host !== c.req.header('host')) {
|
|
33
|
-
return c.json({ error: 'Forbidden' }, 403);
|
|
34
|
-
}
|
|
35
|
-
} catch {
|
|
36
|
-
return c.json({ error: 'Forbidden' }, 403);
|
|
33
|
+
const forbidden = guardSameOrigin(c);
|
|
34
|
+
if (forbidden) {
|
|
35
|
+
return forbidden;
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
// Dev keeps `received` in the payload — it powers error tracing in dev tools.
|
|
40
|
-
const body = await c.req.
|
|
39
|
+
const body = parseRequestBody({ text: await c.req.text() });
|
|
41
40
|
const { error, ...response } = await logClientError(context, body);
|
|
42
41
|
|
|
43
42
|
// Feed the agent-facing feedback loop (GET /lowdefy-docs/build-status) with a
|
|
@@ -49,6 +48,7 @@ async function clientErrorHandler(c) {
|
|
|
49
48
|
message: error?.message ?? null,
|
|
50
49
|
source: response.source ?? null,
|
|
51
50
|
config: response.config ?? null,
|
|
51
|
+
recording: errorRecordingStamp(context.recording),
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
return c.json(response);
|
package/src/routes/cron.js
CHANGED
|
@@ -26,7 +26,9 @@ import getPathSegments from '../lib/getPathSegments.js';
|
|
|
26
26
|
// the environment whose schedules apply in `x-lowdefy-cron-environment`.
|
|
27
27
|
async function cronHandler(c) {
|
|
28
28
|
if (c.req.method !== 'GET') {
|
|
29
|
-
|
|
29
|
+
// A wrong-method request is client-caused: answer 405 rather than raising a
|
|
30
|
+
// fault that would be logged at error level and answered with a 500.
|
|
31
|
+
return c.json({ error: 'Method not allowed.' }, 405);
|
|
30
32
|
}
|
|
31
33
|
const context = c.get('lowdefyContext');
|
|
32
34
|
|
|
@@ -26,7 +26,9 @@ import getPathSegments from '../lib/getPathSegments.js';
|
|
|
26
26
|
// fails closed).
|
|
27
27
|
async function cronForwardHandler(c) {
|
|
28
28
|
if (c.req.method !== 'GET') {
|
|
29
|
-
|
|
29
|
+
// A wrong-method request is client-caused: answer 405 rather than raising a
|
|
30
|
+
// fault that would be logged at error level and answered with a 500.
|
|
31
|
+
return c.json({ error: 'Method not allowed.' }, 405);
|
|
30
32
|
}
|
|
31
33
|
const context = c.get('lowdefyContext');
|
|
32
34
|
|
package/src/routes/detached.js
CHANGED
|
@@ -14,23 +14,34 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { acceptDetachedEndpoint, parseRequestBody } from '@lowdefy/api';
|
|
18
18
|
|
|
19
19
|
import getPathSegments from '../lib/getPathSegments.js';
|
|
20
20
|
|
|
21
21
|
// Target of a CallApi step with `detached: true`: the calling invocation
|
|
22
22
|
// fire-and-forgets a POST here, so the endpoint runs in its OWN function
|
|
23
23
|
// invocation with a fresh duration budget (chainable bounded work without a
|
|
24
|
-
// queue).
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
24
|
+
// queue). Transport auth is CRON_SECRET (fails closed), which proves the
|
|
25
|
+
// request originated from the deployment. The run carries the DISPATCHER'S
|
|
26
|
+
// identity (Decision 4): the request body carries a server-built principal
|
|
27
|
+
// snapshot that runDetachedEndpoint rehydrates, so nested calls authorize
|
|
28
|
+
// against the dispatcher, not a forced system context. Delivery is at-most-once
|
|
29
|
+
// with no retry: targets must be idempotent.
|
|
30
|
+
//
|
|
31
|
+
// The route answers 202 as soon as the call is authorized and parsed; the
|
|
32
|
+
// endpoint runs after the response under this invocation's waitUntil
|
|
33
|
+
// (acceptDetachedEndpoint). The dispatcher is therefore released at once
|
|
34
|
+
// instead of staying alive - and billed - until the target's routine ends.
|
|
28
35
|
async function detachedHandler(c) {
|
|
29
36
|
if (c.req.method !== 'POST') {
|
|
30
|
-
|
|
37
|
+
// A wrong-method request is client-caused: answer 405 rather than raising a
|
|
38
|
+
// fault that would be logged at error level and answered with a 500.
|
|
39
|
+
return c.json({ error: 'Method not allowed.' }, 405);
|
|
31
40
|
}
|
|
32
41
|
const context = c.get('lowdefyContext');
|
|
33
42
|
|
|
43
|
+
// The secret gate stays first: an external POST without the secret is
|
|
44
|
+
// rejected before the body (and its principal assertion) is ever read.
|
|
34
45
|
const secret = process.env.CRON_SECRET;
|
|
35
46
|
if (!secret || c.req.header('authorization') !== `Bearer ${secret}`) {
|
|
36
47
|
context.logger.warn({ event: 'detached_unauthorized' });
|
|
@@ -38,10 +49,12 @@ async function detachedHandler(c) {
|
|
|
38
49
|
}
|
|
39
50
|
|
|
40
51
|
const endpointId = getPathSegments(c, '/api/detached/').join('/');
|
|
41
|
-
const { payload } = await c.req.
|
|
52
|
+
const { credentials, payload, principal } = parseRequestBody({ text: await c.req.text() });
|
|
42
53
|
context.logger.info({ event: 'call_detached_endpoint', endpointId });
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
return c.json(
|
|
55
|
+
acceptDetachedEndpoint(context, { credentials, endpointId, payload, principal }),
|
|
56
|
+
202
|
|
57
|
+
);
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
export default detachedHandler;
|
package/src/routes/devInspect.js
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { parseRequestBody } from '@lowdefy/api';
|
|
18
|
+
import { serializer, type } from '@lowdefy/helpers';
|
|
18
19
|
|
|
19
20
|
import { readCheckpoint } from '../../lib/docs/checkpointStore.js';
|
|
20
21
|
import { loadMocks } from '../../lib/docs/devMockRegistry.js';
|
|
@@ -62,15 +63,21 @@ function handleCheckpointBootstrap(c, { name }) {
|
|
|
62
63
|
);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
// Inspector.jsx posts { tabId, pageId } here whenever
|
|
66
|
-
// the
|
|
67
|
-
//
|
|
66
|
+
// Inspector.jsx posts { tabId, pageId, pathParams, instanceKey } here whenever
|
|
67
|
+
// the page instance on screen in its tab changes, so the tab registry
|
|
68
|
+
// (lib/docs/tabChannel.js) knows which instance each connected tab shows, and
|
|
69
|
+
// which it has rendered, without the tab reconnecting its event stream.
|
|
68
70
|
async function handleTabPage(c) {
|
|
69
|
-
const { tabId, pageId } =
|
|
71
|
+
const { tabId, pageId, pathParams, instanceKey } = parseRequestBody({
|
|
72
|
+
text: await c.req.text(),
|
|
73
|
+
});
|
|
70
74
|
if (!tabId) {
|
|
71
75
|
return c.json({ error: 'Missing "tabId".' }, 400);
|
|
72
76
|
}
|
|
73
|
-
|
|
77
|
+
if (!type.isString(pageId) || !type.isString(instanceKey)) {
|
|
78
|
+
return c.json({ error: 'Missing "pageId" or "instanceKey".' }, 400);
|
|
79
|
+
}
|
|
80
|
+
updateTabPage({ id: tabId, pageId, pathParams, instanceKey });
|
|
74
81
|
return c.json({ ok: true });
|
|
75
82
|
}
|
|
76
83
|
|
|
@@ -89,7 +96,9 @@ async function devInspectHandler(c) {
|
|
|
89
96
|
}
|
|
90
97
|
|
|
91
98
|
if (c.req.method !== 'POST') {
|
|
92
|
-
|
|
99
|
+
// A wrong-method request is client-caused: answer 405 rather than raising a
|
|
100
|
+
// fault that would be logged at error level and answered with a 500.
|
|
101
|
+
return c.json({ error: 'Method not allowed.' }, 405);
|
|
93
102
|
}
|
|
94
103
|
|
|
95
104
|
if (!checkOrigin(c)) {
|
|
@@ -100,7 +109,7 @@ async function devInspectHandler(c) {
|
|
|
100
109
|
return handleTabPage(c);
|
|
101
110
|
}
|
|
102
111
|
|
|
103
|
-
const { requestId, result } = await c.req.
|
|
112
|
+
const { requestId, result } = parseRequestBody({ text: await c.req.text() });
|
|
104
113
|
if (!requestId) {
|
|
105
114
|
return c.json({ error: 'Missing "requestId".' }, 400);
|
|
106
115
|
}
|