@lowdefy/server-dev 6.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/App.jsx +52 -24
- package/client/Inspector.jsx +73 -30
- package/client/JourneyObserver.jsx +47 -0
- package/client/Page.jsx +54 -22
- package/client/Recorder.jsx +73 -0
- package/client/Routing.jsx +37 -13
- package/client/clientBuildImports.test.mjs +63 -0
- package/client/devStreamConnections.test.mjs +40 -0
- package/client/feedback/FeedbackMount.jsx +2 -2
- package/client/feedback/FeedbackOverlay.jsx +7 -4
- package/client/feedback/feedbackStyles.js +3 -0
- package/client/journeyObserver/startJourneyObserver.js +101 -0
- package/client/journeyObserver/startJourneyObserver.test.mjs +149 -0
- package/client/openInEditor/OpenInEditorListener.jsx +2 -0
- package/client/recorder/buildEngineRecord.js +37 -0
- package/client/recorder/buildInteractionRecord.js +64 -0
- package/client/recorder/buildTraceEvent.js +57 -0
- package/client/recorder/buildTraceEvent.test.mjs +81 -0
- package/client/recorder/chunkRecords.js +69 -0
- package/client/recorder/chunkRecords.test.mjs +59 -0
- package/client/recorder/collectEventActions.js +30 -0
- package/client/recorder/collectEventEndpoints.js +40 -0
- package/client/recorder/collectEventRequests.js +39 -0
- package/client/recorder/createPairingBuffer.js +187 -0
- package/client/recorder/createPairingBuffer.test.mjs +405 -0
- package/client/recorder/createPasswordRedactor.js +84 -0
- package/client/recorder/createRecordBatcher.js +54 -0
- package/client/recorder/createRecordBatcher.test.mjs +76 -0
- package/client/recorder/createRecorder.js +216 -0
- package/client/recorder/createRecorder.test.mjs +318 -0
- package/client/recorder/createRecordingSession.js +61 -0
- package/client/recorder/createRecordingSession.test.mjs +72 -0
- package/client/recorder/diffStateWrites.js +77 -0
- package/client/recorder/diffStateWrites.test.mjs +66 -0
- package/client/recorder/isEventCall.js +30 -0
- package/client/recorder/jsonType.js +39 -0
- package/client/recorder/keyChord.js +47 -0
- package/client/recorder/keyChord.test.mjs +50 -0
- package/client/recorder/sendRecords.js +40 -0
- package/client/recorder/toJsonSafe.js +29 -0
- package/lib/client/BuildingPage.jsx +3 -1
- package/lib/client/ErrorBar.jsx +17 -13
- package/lib/client/RedirectingPage.jsx +56 -0
- package/lib/client/auth/Auth.jsx +11 -6
- package/lib/client/auth/AuthConfigured.jsx +224 -39
- package/lib/client/auth/AuthNotConfigured.js +45 -4
- package/lib/client/auth/authNotConfiguredParity.test.mjs +61 -0
- package/lib/client/auth/authTemplateSync.test.mjs +40 -0
- package/lib/client/auth/betterAuthSessionContract.test.mjs +181 -0
- package/lib/client/auth/createFetchCheckingBuild.js +45 -0
- package/lib/client/bundleBuildId.js +25 -0
- package/lib/client/collectVisibleBlockIds.js +51 -0
- package/lib/client/getCspNonce.js +25 -0
- package/lib/client/getPagePath.js +36 -0
- package/lib/client/getPagePath.test.mjs +51 -0
- package/lib/client/utils/formatErrorsForCopy.js +30 -0
- package/lib/client/utils/formatErrorsForCopy.test.mjs +39 -0
- package/lib/client/utils/getErrorBarColor.js +25 -0
- package/lib/client/utils/getErrorBarColor.test.mjs +34 -0
- package/lib/client/utils/request.js +8 -1
- package/lib/client/utils/usePageConfig.js +89 -25
- package/lib/client/utils/usePageConfig.test.mjs +198 -13
- package/lib/configTree/buildConfigTree.mjs +58 -0
- package/lib/configTree/buildConfigTree.test.mjs +77 -0
- package/lib/configTree/collectConfigText.mjs +101 -0
- package/lib/configTree/collectConfigText.test.mjs +116 -0
- package/lib/configTree/prepareScratchServer.mjs +46 -0
- package/lib/configTree/runConfigTreeBuild.mjs +88 -0
- package/lib/configTree/runConfigTreeBuild.skipPluginChecks.test.mjs +74 -0
- package/lib/configTree/runConfigTreeBuild.test.mjs +202 -0
- package/lib/docs/JourneyStepError.js +30 -0
- package/lib/docs/acquireBrowserSlot.js +30 -0
- package/lib/docs/addNestedEndpoints.js +54 -0
- package/lib/docs/addNestedEndpoints.test.mjs +185 -0
- package/lib/docs/addWriteFlags.js +77 -0
- package/lib/docs/buildEditedPages.js +50 -0
- package/lib/docs/buildWaitHeader.js +21 -0
- package/lib/docs/captureAnnotatedScreenshot.js +23 -12
- package/lib/docs/checkAgentFiles.js +148 -0
- package/lib/docs/checkAgentFiles.test.mjs +225 -0
- package/lib/docs/checkStepReferences.js +239 -0
- package/lib/docs/checkStepReferences.test.mjs +173 -0
- package/lib/docs/checkpointStore.js +3 -0
- package/lib/docs/clientErrorStore.js +18 -1
- package/lib/docs/clientErrorStore.test.mjs +46 -0
- package/lib/docs/collectExercised.js +52 -0
- package/lib/docs/createBrowserLifecycle.js +102 -0
- package/lib/docs/createBrowserLifecycle.test.mjs +212 -0
- package/lib/docs/createDocsMcpServer.js +345 -388
- package/lib/docs/createDocsMcpServer.test.mjs +644 -4
- package/lib/docs/createDocsMcpServerRunJourney.test.mjs +148 -0
- package/lib/docs/createJourneyActors.chromium.test.mjs +227 -0
- package/lib/docs/createJourneyActors.js +213 -0
- package/lib/docs/createJourneyActors.test.mjs +79 -0
- package/lib/docs/createJourneyObservations.js +80 -0
- package/lib/docs/createLeftOriginError.js +26 -0
- package/lib/docs/createNetworkCounter.js +101 -0
- package/lib/docs/dataSets/checkSharedCollectionIds.js +67 -0
- package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
- package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +679 -0
- package/lib/docs/dataSets/describeDataSetResult.js +25 -0
- package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
- package/lib/docs/dataSets/deserializeFixture.js +34 -0
- package/lib/docs/dataSets/drainSessionWork.js +37 -0
- package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
- package/lib/docs/dataSets/duplicateIndexName.js +24 -0
- package/lib/docs/dataSets/findCollectionCollisions.js +52 -0
- package/lib/docs/dataSets/getDataStore.js +106 -0
- package/lib/docs/dataSets/getDataStore.mongodb.test.mjs +63 -0
- package/lib/docs/dataSets/getDataStore.test.mjs +195 -0
- package/lib/docs/dataSets/groupDataSetByCollection.js +44 -0
- package/lib/docs/dataSets/insertFixture.js +57 -0
- package/lib/docs/dataSets/insertGeneratedDocuments.js +41 -0
- package/lib/docs/dataSets/isDataStoreGone.js +29 -0
- package/lib/docs/dataSets/loadDataSetCollection.js +42 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.js +52 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.test.mjs +59 -0
- package/lib/docs/dataSets/openDataSession.js +119 -0
- package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +343 -0
- package/lib/docs/dataSets/pickDataStorePort.js +58 -0
- package/lib/docs/dataSets/pickDataStorePort.test.mjs +63 -0
- package/lib/docs/dataSets/readConnectionArtifacts.js +46 -0
- package/lib/docs/dataSets/readDataSession.js +41 -0
- package/lib/docs/dataSets/readDataSession.test.mjs +57 -0
- package/lib/docs/dataSets/readDataSet.js +68 -0
- package/lib/docs/dataSets/readDataSet.test.mjs +220 -0
- package/lib/docs/dataSets/registerDataStoreShutdown.js +47 -0
- package/lib/docs/dataSets/removeDataStoreFiles.js +32 -0
- package/lib/docs/dataSets/resolveDataSetCollection.js +46 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.js +106 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.test.mjs +130 -0
- package/lib/docs/dataSets/sweepOrphanDatabases.js +33 -0
- package/lib/docs/dataSets/test/app/lowdefy.yaml +203 -0
- package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
- package/lib/docs/dataSets/test/app/tests/data/shop.yaml +8 -0
- package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
- package/lib/docs/devEventBus.js +136 -0
- package/lib/docs/devEventBus.test.mjs +269 -0
- package/lib/docs/devToolDefinitions.js +529 -0
- package/lib/docs/docSources.js +21 -0
- package/lib/docs/docs.test.mjs +171 -15
- package/lib/docs/docsIndex.test.mjs +377 -0
- package/lib/docs/evalOperator.js +5 -3
- package/lib/docs/evalOperatorHeadless.js +84 -12
- package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
- package/lib/docs/evalOperatorInTab.js +2 -1
- package/lib/docs/findConfig.js +95 -49
- package/lib/docs/fitResponse.js +81 -0
- package/lib/docs/fitResponse.test.mjs +128 -0
- package/lib/docs/formatFeedback.js +26 -5
- package/lib/docs/formatFeedback.test.mjs +48 -0
- package/lib/docs/getAppMap.js +7 -1
- package/lib/docs/getBrowser.js +150 -80
- package/lib/docs/getBrowser.test.mjs +407 -16
- package/lib/docs/getBuildId.js +61 -0
- package/lib/docs/getBuildId.test.mjs +84 -0
- package/lib/docs/getBuildStatus.js +54 -6
- package/lib/docs/getBuildStatus.test.mjs +133 -0
- package/lib/docs/getBuildStatusAfterEdits.js +35 -0
- package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
- package/lib/docs/getCoreDoc.js +9 -1
- package/lib/docs/getDoc.js +100 -0
- package/lib/docs/getDocsIndex.js +152 -0
- package/lib/docs/getHazards.js +115 -0
- package/lib/docs/getHazards.test.mjs +175 -0
- package/lib/docs/getMailOutboxDirectory.js +26 -0
- package/lib/docs/getOverview.js +79 -13
- package/lib/docs/getOverviewBasePath.test.mjs +37 -0
- package/lib/docs/getPageBuildStatus.js +46 -0
- package/lib/docs/getPluginDoc.js +21 -23
- package/lib/docs/getSchema.js +33 -1
- package/lib/docs/getStaleStatus.js +36 -0
- package/lib/docs/getStaleStatus.test.mjs +74 -0
- package/lib/docs/guardJourneyOrigin.js +39 -0
- package/lib/docs/inspectState.js +5 -3
- package/lib/docs/inspectStateFromTab.js +2 -2
- package/lib/docs/inspectStateHeadless.js +89 -27
- package/lib/docs/inspectStateHeadless.test.mjs +20 -1
- package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
- package/lib/docs/installHeadlessShell.js +123 -0
- package/lib/docs/installHeadlessShell.test.mjs +70 -0
- package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
- package/lib/docs/isHeadlessShellIncomplete.js +34 -0
- package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
- package/lib/docs/isPageReady.js +77 -0
- package/lib/docs/isPageReady.test.mjs +235 -0
- package/lib/docs/isRunErrorEntry.js +25 -0
- package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
- package/lib/docs/journeyRunRecording.js +35 -0
- package/lib/docs/journeyRunRecording.test.mjs +39 -0
- package/lib/docs/keepEventStreamAlive.js +68 -0
- package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
- package/lib/docs/launchBrowser.js +83 -0
- package/lib/docs/launchBrowser.test.mjs +175 -0
- package/lib/docs/listModuleDocEntries.js +69 -0
- package/lib/docs/listPackageDocFiles.js +67 -0
- package/lib/docs/listPluginDocEntries.js +111 -0
- package/lib/docs/listPlugins.js +9 -58
- package/lib/docs/listTypes.js +14 -6
- package/lib/docs/loadFrameworkHazards.js +40 -0
- package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
- package/lib/docs/loadState.js +103 -35
- package/lib/docs/makeModuleManifestDoc.js +174 -0
- package/lib/docs/matchExercisedPages.js +35 -0
- package/lib/docs/matchJourneyRoute.js +69 -0
- package/lib/docs/matchJourneyRoute.test.mjs +178 -0
- package/lib/docs/mergeNetworkSnapshots.js +49 -0
- package/lib/docs/nextJourneySequence.js +30 -0
- package/lib/docs/noBrowserError.js +22 -0
- package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
- package/lib/docs/observe/claimExpectedErrors.js +67 -0
- package/lib/docs/observe/createJourneyAppErrors.js +236 -0
- package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
- package/lib/docs/observe/evaluateInvariants.js +183 -0
- package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
- package/lib/docs/observe/findingKey.js +29 -0
- package/lib/docs/observe/hasNoEffect.js +31 -0
- package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
- package/lib/docs/observe/installStepObserver.js +72 -0
- package/lib/docs/observe/isAppApiUrl.js +33 -0
- package/lib/docs/observe/parseAppApiUrl.js +37 -0
- package/lib/docs/observe/readStepObserver.js +42 -0
- package/lib/docs/observe/relativeSource.js +29 -0
- package/lib/docs/observe/resolveConfigKeySource.js +33 -0
- package/lib/docs/observe/takeJourneyEvents.js +36 -0
- package/lib/docs/observe/toAppErrorFailure.js +61 -0
- package/lib/docs/observe/usesSearchStage.js +45 -0
- package/lib/docs/observe/waitForClientErrorReports.js +31 -0
- package/lib/docs/observe/watchJourneyContext.js +75 -0
- package/lib/docs/observe/withAppErrors.js +31 -0
- package/lib/docs/observe/withAppErrors.test.mjs +101 -0
- package/lib/docs/openJourney.js +94 -0
- package/lib/docs/openJourneyEmail.js +41 -0
- package/lib/docs/openJourneyEmail.test.mjs +187 -0
- package/lib/docs/pageBlocks.js +47 -0
- package/lib/docs/pageBuildStatus.test.mjs +103 -0
- package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
- package/lib/docs/rails/authEngineActions.js +53 -0
- package/lib/docs/rails/authEngineActions.test.mjs +69 -0
- package/lib/docs/rails/authEngineMethods.js +52 -0
- package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
- package/lib/docs/rails/checkAuthActionsRan.js +66 -0
- package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
- package/lib/docs/rails/checkClickRails.js +56 -0
- package/lib/docs/rails/createDataSetRails.js +82 -0
- package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
- package/lib/docs/rails/findExternalBlocks.js +147 -0
- package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
- package/lib/docs/rails/installAuthActionRecorder.js +56 -0
- package/lib/docs/rails/readAuthActionRecorder.js +32 -0
- package/lib/docs/rails/readBuiltArtifact.js +30 -0
- package/lib/docs/readConnectionPlugins.js +26 -0
- package/lib/docs/readDevAuthMode.js +32 -0
- package/lib/docs/readDocEntry.js +40 -0
- package/lib/docs/readDocTitle.js +26 -0
- package/lib/docs/readJourneyEmailMatch.js +57 -0
- package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
- package/lib/docs/readJourneySession.js +56 -0
- package/lib/docs/readMailOutbox.js +37 -0
- package/lib/docs/readPagePath.js +27 -0
- package/lib/docs/readPluginPackages.js +99 -0
- package/lib/docs/readProxyBuildWait.js +38 -0
- package/lib/docs/readRoutineSteps.js +45 -0
- package/lib/docs/registerShownTextEngine.js +118 -0
- package/lib/docs/requestRestart.js +30 -0
- package/lib/docs/requestRestart.test.mjs +49 -0
- package/lib/docs/resolvePageInstance.js +46 -0
- package/lib/docs/resolvePageInstance.test.mjs +81 -0
- package/lib/docs/resolveSource.js +11 -7
- package/lib/docs/resolveSource.test.mjs +1 -1
- package/lib/docs/resolveToolCaller.js +62 -0
- package/lib/docs/reviewPage.js +51 -0
- package/lib/docs/reviewPageBuilds.js +56 -0
- package/lib/docs/reviewPageBuilds.test.mjs +207 -0
- package/lib/docs/runCheck.js +75 -0
- package/lib/docs/runCheck.test.mjs +91 -0
- package/lib/docs/runCheckChild.js +49 -0
- package/lib/docs/runEndpoint.js +185 -0
- package/lib/docs/runEndpoint.test.mjs +509 -0
- package/lib/docs/runErrorBuffers.js +99 -0
- package/lib/docs/runErrorBuffers.test.mjs +95 -0
- package/lib/docs/runJourney.js +347 -0
- package/lib/docs/runJourney.test.mjs +2383 -0
- package/lib/docs/runJourneyAsUsers.js +44 -0
- package/lib/docs/runJourneyDataSet.test.mjs +274 -0
- package/lib/docs/runJourneySteps.js +1320 -0
- package/lib/docs/runRequest.js +100 -59
- package/lib/docs/runRequest.test.mjs +190 -0
- package/lib/docs/saveAnnotatedScreenshot.js +1 -1
- package/lib/docs/scaffoldPage.js +3 -1
- package/lib/docs/screenshotPage.js +163 -11
- package/lib/docs/screenshotPage.test.mjs +263 -1
- package/lib/docs/screenshotPageClip.test.mjs +4 -0
- package/lib/docs/searchDocs.js +82 -30
- package/lib/docs/searchDocs.test.mjs +100 -0
- package/lib/docs/searchIcons.js +90 -0
- package/lib/docs/searchIcons.test.mjs +127 -0
- package/lib/docs/selectExercised.js +39 -0
- package/lib/docs/selectFinalState.js +66 -0
- package/lib/docs/selectFinalState.test.mjs +51 -0
- package/lib/docs/serverErrorStore.js +54 -0
- package/lib/docs/serverErrorStore.test.mjs +50 -0
- package/lib/docs/setupTestFixtures.mjs +133 -39
- package/lib/docs/shownTextSelector.js +28 -0
- package/lib/docs/snapshotState.js +13 -5
- package/lib/docs/splitSearchTerms.js +62 -0
- package/lib/docs/splitSearchTerms.test.mjs +50 -0
- package/lib/docs/stateCheckpoints.test.mjs +77 -0
- package/lib/docs/tabAvailable.js +6 -5
- package/lib/docs/tabChannel.js +127 -21
- package/lib/docs/tabChannel.test.mjs +187 -29
- package/lib/docs/unsettledPageNote.js +24 -0
- package/lib/docs/validateJourneyMail.js +36 -0
- package/lib/docs/validateJourneyTimeout.js +38 -0
- package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
- package/lib/docs/validateRecordingParam.js +42 -0
- package/lib/docs/validateStateSelection.js +33 -0
- package/lib/docs/validateStateSelection.test.mjs +33 -0
- package/lib/docs/validateViewport.js +51 -0
- package/lib/docs/validateViewport.test.mjs +54 -0
- package/lib/docs/waitForBuild.js +50 -0
- package/lib/docs/waitForBuild.test.mjs +75 -0
- package/lib/docs/waitForJourneyEmail.js +77 -0
- package/lib/docs/withBrowserSlot.js +45 -0
- package/lib/docs/withBrowserSlot.test.mjs +75 -0
- package/lib/docs/withDataSession.js +53 -0
- package/lib/server/applyDataSetRedirect.js +83 -0
- package/lib/server/applyDataSetRedirect.test.mjs +340 -0
- package/lib/server/applyDataSetRedirectTenantReach.test.mjs +204 -0
- package/lib/server/applyDataSetRedirectWalled.test.mjs +131 -0
- package/lib/server/auth/createSystemContext.js +73 -0
- package/lib/server/auth/getAuth.js +46 -0
- package/lib/server/auth/getHeadlessUser.js +9 -8
- package/lib/server/auth/getHeadlessUser.test.mjs +41 -0
- package/lib/server/auth/getMockUser.js +18 -14
- package/lib/server/auth/{getAuthConfig.js → getStrategies.js} +12 -10
- package/lib/server/auth/headlessUser.js +3 -2
- package/lib/server/auth/journeyActor.js +35 -0
- package/lib/server/auth/resolveHeadlessUser.test.mjs +0 -4
- package/lib/server/checkPageRecord.js +44 -0
- package/lib/server/contextMapBudget.js +24 -0
- package/lib/server/contextMapBudget.test.mjs +98 -0
- package/lib/server/continueJitKeys.js +28 -0
- package/lib/server/continueJitKeys.test.mjs +43 -0
- package/lib/server/createDataSessionEndedError.js +33 -0
- package/lib/server/createDataSetRefusal.js +26 -0
- package/lib/server/createLowdefyContext.js +174 -41
- package/lib/server/createLowdefyContext.test.mjs +437 -0
- package/lib/server/getClientAddress.js +51 -0
- package/lib/server/getClientAddress.test.mjs +56 -0
- package/lib/server/getPageJitEnrichment.test.mjs +29 -11
- package/lib/server/hashConfigContent.js +26 -0
- package/lib/server/importAppCode.test.mjs +57 -0
- package/lib/server/isRoutineStep.js +27 -0
- package/lib/server/jitPageBuilder.js +309 -126
- package/lib/server/jitPageBuilder.skipPluginChecks.test.mjs +78 -0
- package/lib/server/jitPageBuilder.test.mjs +218 -0
- package/lib/server/journeyCookies/JOURNEY_COOKIES.js +37 -0
- package/lib/server/journeyCookies/forwardJourneyCookies.js +46 -0
- package/lib/server/journeyCookies/readJourneyCookie.js +52 -0
- package/lib/server/journeyCookies/writeJourneyCookie.js +46 -0
- package/lib/server/journeyCookies.js +22 -0
- package/lib/server/journeyCookies.test.mjs +145 -0
- package/lib/server/loadDynamicJsMap.js +47 -0
- package/lib/server/log/createHandleError.js +38 -2
- package/lib/server/log/createHandleError.test.mjs +125 -2
- package/lib/server/log/createLogger.js +3 -0
- package/lib/server/log/createLogger.test.mjs +77 -0
- package/lib/server/log/logRequest.js +2 -3
- package/lib/server/mutantDataSetWrappers.test.mjs +112 -0
- package/lib/server/mutants/applyMutant.js +39 -0
- package/lib/server/mutants/artifactPatterns.js +25 -0
- package/lib/server/mutants/buildAnchor.js +45 -0
- package/lib/server/mutants/collectSharedIds.js +41 -0
- package/lib/server/mutants/configPathSegments.js +38 -0
- package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
- package/lib/server/mutants/describeEvent.js +22 -0
- package/lib/server/mutants/enumerateArtifact.js +56 -0
- package/lib/server/mutants/findNodeByKey.js +45 -0
- package/lib/server/mutants/findNodeByPointer.js +49 -0
- package/lib/server/mutants/getArtifactKind.js +32 -0
- package/lib/server/mutants/groupMutantCopies.js +56 -0
- package/lib/server/mutants/hashArtifact.js +25 -0
- package/lib/server/mutants/hashMutantTarget.js +34 -0
- package/lib/server/mutants/listMutants.js +122 -0
- package/lib/server/mutants/listMutants.test.mjs +502 -0
- package/lib/server/mutants/mutantId.js +30 -0
- package/lib/server/mutants/mutantRuns.js +57 -0
- package/lib/server/mutants/mutantRuns.test.mjs +136 -0
- package/lib/server/mutants/operators/dropAction.js +47 -0
- package/lib/server/mutants/operators/dropBlock.js +44 -0
- package/lib/server/mutants/operators/dropPayload.js +52 -0
- package/lib/server/mutants/operators/dropStep.js +44 -0
- package/lib/server/mutants/operators/flipVisible.js +53 -0
- package/lib/server/mutants/operators/index.js +37 -0
- package/lib/server/mutants/operators/retargetLink.js +72 -0
- package/lib/server/mutants/operators/skipValidate.js +77 -0
- package/lib/server/mutants/operators/swapIf.js +112 -0
- package/lib/server/mutants/operators.test.mjs +396 -0
- package/lib/server/mutants/positionFreePath.js +38 -0
- package/lib/server/mutants/removeFromParent.js +28 -0
- package/lib/server/mutants/test/fixtures.mjs +173 -0
- package/lib/server/mutants/test/keyArtifact.mjs +63 -0
- package/lib/server/mutants/validateMutantParam.js +69 -0
- package/lib/server/mutants/validateMutantsBody.js +88 -0
- package/lib/server/mutants/walkArtifact.js +179 -0
- package/lib/server/pageBuildRecords.js +131 -0
- package/lib/server/pageBuildRecords.test.mjs +258 -0
- package/lib/server/pageCache.mjs +22 -7
- package/lib/server/pageCache.test.mjs +22 -7
- package/lib/server/pageRebuilds.test.mjs +543 -0
- package/lib/server/pruneJitMaps.js +36 -0
- package/lib/server/pruneJitMaps.test.mjs +55 -0
- package/lib/server/readInstanceRecord.js +33 -0
- package/lib/server/readInstanceRecord.test.mjs +99 -0
- package/lib/server/readMergedMaps.js +86 -0
- package/lib/server/readMergedMaps.test.mjs +79 -0
- package/lib/server/recording/appendRecords.js +33 -0
- package/lib/server/recording/errorRecordingStamp.js +34 -0
- package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
- package/lib/server/recording/isRecordingEnabled.js +31 -0
- package/lib/server/recording/readRecordingCookie.js +71 -0
- package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
- package/lib/server/recording/recordingCookiePayload.js +33 -0
- package/lib/server/recording/servedBuilds.js +46 -0
- package/lib/server/recording/servedBuilds.test.mjs +42 -0
- package/lib/server/scrubSecrets.js +23 -0
- package/lib/server/sinkExternalConnections.js +95 -0
- package/lib/server/sinkExternalConnections.test.mjs +135 -0
- package/lib/server/skipStaleMapWrites.js +62 -0
- package/lib/server/skipStaleMapWrites.test.mjs +113 -0
- package/lib/server/startIdleGc.js +67 -0
- package/lib/server/startIdleGc.test.mjs +138 -0
- package/lib/server/testFixtures/importAppCodeTwice.mjs +70 -0
- package/lib/server/trackSessionWork.js +30 -0
- package/lib/vite/createDevServerExclude.js +40 -0
- package/lib/vite/createDevServerExclude.test.mjs +79 -0
- package/manager/getContext.mjs +24 -2
- package/manager/processes/initialBuild.mjs +14 -0
- package/manager/processes/initialBuild.test.mjs +101 -0
- package/manager/processes/lowdefyBuild.mjs +30 -7
- package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +62 -0
- package/manager/processes/lowdefyBuild.test.mjs +136 -1
- package/manager/processes/optimizeDependencies.mjs +59 -0
- package/manager/processes/restartServer.mjs +34 -6
- package/manager/processes/restartServer.test.mjs +162 -0
- package/manager/processes/shutdownServer.mjs +11 -0
- package/manager/processes/shutdownServer.test.mjs +45 -0
- package/manager/processes/startFirstServer.mjs +46 -0
- package/manager/processes/startFirstServer.test.mjs +326 -0
- package/manager/processes/startMailSink.mjs +135 -0
- package/manager/processes/startMailSink.test.mjs +146 -0
- package/manager/processes/startProxy.mjs +129 -12
- package/manager/processes/startProxy.test.mjs +366 -0
- package/manager/processes/startServer.mjs +47 -6
- package/manager/processes/startServer.test.mjs +155 -0
- package/manager/processes/startWatchers.mjs +6 -4
- package/manager/processes/syncServer.mjs +69 -0
- package/manager/processes/syncServer.test.mjs +141 -0
- package/manager/processes/waitForDevServer.mjs +48 -0
- package/manager/processes/waitForDevServer.test.mjs +112 -0
- package/manager/run.mjs +128 -43
- package/manager/utils/BatchChanges.mjs +21 -5
- package/manager/utils/BatchChanges.test.mjs +77 -0
- package/manager/utils/acquireDevInstance.mjs +99 -0
- package/manager/utils/acquireDevInstance.test.mjs +160 -0
- package/manager/utils/createBuildActivity.mjs +67 -0
- package/manager/utils/createBuildActivity.test.mjs +58 -0
- package/manager/utils/createChangeSignal.mjs +39 -0
- package/manager/utils/createChangeSignal.test.mjs +62 -0
- package/manager/utils/createCustomPluginMessagesMap.mjs +23 -33
- package/manager/utils/createCustomPluginTypesMap.mjs +24 -30
- package/manager/utils/createCustomPluginTypesMap.test.mjs +94 -0
- package/manager/utils/createDotPathIgnore.mjs +44 -0
- package/manager/utils/createDotPathIgnore.test.mjs +65 -0
- package/manager/utils/createIdleGc.mjs +51 -0
- package/manager/utils/createIdleGc.test.mjs +77 -0
- package/manager/utils/createRequestActivity.mjs +100 -0
- package/manager/utils/createRequestActivity.test.mjs +121 -0
- package/manager/utils/createServerArtifactTracker.mjs +83 -0
- package/manager/utils/createServerArtifactTracker.test.mjs +78 -0
- package/manager/utils/createServerEnv.mjs +39 -0
- package/manager/utils/filesHaveSameBytes.mjs +41 -0
- package/manager/utils/findBuildFilesOutsideWatch.mjs +59 -0
- package/manager/utils/findBuildFilesOutsideWatch.test.mjs +111 -0
- package/manager/utils/importFresh.mjs +46 -0
- package/manager/utils/importFresh.test.mjs +72 -0
- package/manager/utils/importFreshWorker.mjs +20 -0
- package/manager/utils/killTaggedBrowser.mjs +35 -0
- package/manager/utils/pagePathChanged.mjs +56 -0
- package/manager/utils/pruneRecordings.mjs +85 -0
- package/manager/utils/pruneRecordings.test.mjs +117 -0
- package/manager/utils/publishBuildDirectory.mjs +121 -0
- package/manager/utils/publishBuildDirectory.order.test.mjs +74 -0
- package/manager/utils/publishBuildDirectory.test.mjs +262 -0
- package/manager/utils/readBasePath.mjs +27 -0
- package/manager/utils/readBasePath.test.mjs +43 -0
- package/manager/utils/readBuildStatusWait.mjs +67 -0
- package/manager/utils/readManagerVersion.mjs +30 -0
- package/manager/utils/readManagerVersion.test.mjs +35 -0
- package/manager/utils/readPluginDefinitions.mjs +36 -0
- package/manager/utils/resolveDevAuthUrl.mjs +58 -0
- package/manager/utils/resolveDevAuthUrl.test.mjs +107 -0
- package/manager/utils/resolvePorts.mjs +49 -0
- package/manager/utils/setupWatcher.mjs +19 -11
- package/manager/utils/startRecordingPruner.mjs +45 -0
- package/manager/utils/startRecordingPruner.test.mjs +66 -0
- package/manager/utils/waitForChildExit.mjs +53 -0
- package/manager/utils/waitForChildExit.test.mjs +70 -0
- package/manager/utils/waitForServer.mjs +44 -0
- package/manager/utils/writeBuildStatus.mjs +1 -1
- package/manager/watchers/createPluginSourceIgnore.mjs +77 -0
- package/manager/watchers/envWatcher.mjs +9 -2
- package/manager/watchers/envWatcher.test.mjs +87 -0
- package/manager/watchers/listServerSidePackages.mjs +47 -0
- package/manager/watchers/listServerSidePackages.test.mjs +38 -0
- package/manager/watchers/lowdefyBuildWatcher.mjs +106 -22
- package/manager/watchers/lowdefyBuildWatcher.test.mjs +309 -0
- package/manager/watchers/pluginSourceWatcher.mjs +79 -0
- package/manager/watchers/pluginSourceWatcher.test.mjs +222 -0
- package/manager/watchers/restartRequestWatcher.mjs +71 -0
- package/manager/watchers/restartRequestWatcher.test.mjs +111 -0
- package/manager/watchers/selectLocalPluginPackages.mjs +48 -0
- package/manager/watchers/selectLocalPluginPackages.test.mjs +73 -0
- package/manager/watchers/serverPackageWatcher.mjs +38 -0
- package/package.json +61 -49
- package/package.original.json +61 -49
- package/src/app.js +131 -20
- package/src/html/renderDevPage.js +63 -11
- package/src/html/renderDevPage.test.mjs +138 -0
- package/src/lib/getRequestPath.js +29 -0
- package/src/middleware/createSameOriginGuard.js +37 -0
- package/src/middleware/createSameOriginGuard.test.mjs +102 -0
- package/src/middleware/errorHandler.js +51 -1
- package/src/middleware/errorHandler.test.mjs +196 -14
- package/src/middleware/isRebindingSafeHost.js +44 -0
- package/src/middleware/isRebindingSafeHost.test.mjs +32 -0
- package/src/middleware/localDevToolsOnly.js +38 -0
- package/src/middleware/localDevToolsOnly.test.mjs +61 -0
- package/src/middleware/noStore.js +27 -0
- package/src/middleware/noStore.test.mjs +48 -0
- package/src/middleware/requestTimeout.js +38 -0
- package/src/middleware/staleFlag.js +76 -0
- package/src/middleware/staleFlag.test.mjs +127 -0
- package/src/middleware/stampCaller.js +38 -0
- package/src/middleware/stampCaller.test.mjs +64 -0
- package/src/routes/agent.js +15 -3
- package/src/routes/auth.js +29 -20
- package/src/routes/auth.test.mjs +30 -56
- package/src/routes/clientError.js +13 -13
- package/src/routes/cron.js +3 -1
- package/src/routes/cronForward.js +4 -2
- package/src/routes/detached.js +22 -9
- package/src/routes/devInspect.js +17 -8
- package/src/routes/devInspect.test.mjs +44 -7
- package/src/routes/devRecording.js +113 -0
- package/src/routes/devRecording.test.mjs +253 -0
- package/src/routes/docs/buildStatus.js +10 -2
- package/src/routes/docs/buildStatus.test.mjs +46 -0
- package/src/routes/docs/content.js +5 -3
- package/src/routes/docs/evalOperator.js +15 -1
- package/src/routes/docs/events.js +50 -0
- package/src/routes/docs/events.test.mjs +117 -0
- package/src/routes/docs/find.js +9 -1
- package/src/routes/docs/icons.js +30 -0
- package/src/routes/docs/inspectState.js +16 -1
- package/src/routes/docs/journey.js +194 -0
- package/src/routes/docs/journey.test.mjs +401 -0
- package/src/routes/docs/journeyRefusals.test.mjs +71 -0
- package/src/routes/docs/loadState.js +4 -2
- package/src/routes/docs/mcp.js +34 -3
- package/src/routes/docs/mcp.test.mjs +130 -0
- package/src/routes/docs/mutants.js +78 -0
- package/src/routes/docs/mutants.test.mjs +180 -0
- package/src/routes/docs/parsePathParamsParam.js +41 -0
- package/src/routes/docs/parsePathParamsParam.test.mjs +42 -0
- package/src/routes/docs/parseUserParam.js +27 -21
- package/src/routes/docs/parseUserParam.test.mjs +17 -5
- package/src/routes/docs/restart.js +35 -0
- package/src/routes/docs/restart.test.mjs +64 -0
- package/src/routes/docs/runEndpoint.js +54 -0
- package/src/routes/docs/runEndpoint.test.mjs +165 -0
- package/src/routes/docs/runRequest.js +25 -8
- package/src/routes/docs/runRequest.test.mjs +92 -0
- package/src/routes/docs/screenshot.js +50 -2
- package/src/routes/docs/screenshot.test.mjs +117 -0
- package/src/routes/docs/search.js +15 -2
- package/src/routes/docs/snapshotState.js +17 -3
- package/src/routes/endpoints.js +27 -26
- package/src/routes/endpointsTextAnswer.test.mjs +77 -0
- package/src/routes/feedback.js +10 -13
- package/src/routes/jitPage.js +99 -35
- package/src/routes/jitPage.test.mjs +248 -0
- package/src/routes/mcp.js +82 -5
- package/src/routes/mcp.test.mjs +215 -0
- package/src/routes/mcpMemberToken.test.mjs +125 -0
- package/src/routes/mcpProtectedResourceMetadata.js +34 -0
- package/src/routes/mcpProtectedResourceMetadata.test.mjs +65 -0
- package/src/routes/mountDevPageRoutes.js +25 -0
- package/src/routes/mountDevPageRoutes.test.mjs +30 -0
- package/src/routes/mountOauthDiscovery.js +44 -0
- package/src/routes/mountOauthDiscovery.test.mjs +79 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.js +46 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.test.mjs +62 -0
- package/src/routes/reload.js +16 -0
- package/src/routes/request.js +6 -4
- package/src/routes/request.test.mjs +9 -5
- package/src/routes/requestBody.test.mjs +159 -0
- package/src/routes/usage.js +6 -2
- package/src/routes/user.js +27 -0
- package/src/routes/websocket.js +29 -8
- package/src/routes/websocket.test.mjs +65 -0
- package/src/routes/wellKnownFallback.js +30 -0
- package/src/routes/wellKnownFallback.test.mjs +32 -0
- package/src/websocket/devWebSocket.js +41 -26
- package/src/websocket/devWebSocket.test.mjs +157 -0
- package/vite.config.js +53 -23
- package/lib/server/auth/getDevSession.js +0 -70
- package/lib/server/auth/getDevSession.test.mjs +0 -138
- package/lib/server/auth/session.js +0 -39
- package/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
- package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
- package/manager/watchers/moduleBuildWatcher.mjs +0 -79
- package/manager/watchers/serverArtifactWatcher.mjs +0 -120
- package/src/routes/apiPage.js +0 -37
|
@@ -0,0 +1,74 @@
|
|
|
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 { jest } from '@jest/globals';
|
|
22
|
+
|
|
23
|
+
const realFs = await import('fs/promises');
|
|
24
|
+
const moved = [];
|
|
25
|
+
|
|
26
|
+
// readdir order is up to the file system. Listing in reverse alphabetical order puts the
|
|
27
|
+
// maps ahead of idCounter.json, so only an explicit order passes.
|
|
28
|
+
jest.unstable_mockModule('fs/promises', () => ({
|
|
29
|
+
default: {
|
|
30
|
+
...realFs.default,
|
|
31
|
+
readdir: async (...args) => (await realFs.default.readdir(...args)).reverse(),
|
|
32
|
+
rename: async (from, to) => {
|
|
33
|
+
moved.push(path.basename(to));
|
|
34
|
+
return realFs.default.rename(from, to);
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
const { default: publishBuildDirectory } = await import('./publishBuildDirectory.mjs');
|
|
40
|
+
|
|
41
|
+
test('publishBuildDirectory moves idCounter.json before the key and ref maps', async () => {
|
|
42
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-publish-order-'));
|
|
43
|
+
const buildDirectory = path.join(root, 'build');
|
|
44
|
+
const stagingDirectory = path.join(root, 'build-staging');
|
|
45
|
+
fs.mkdirSync(stagingDirectory, { recursive: true });
|
|
46
|
+
['app.json', 'idCounter.json', 'keyMap.json', 'pageRegistry.json', 'refMap.json'].forEach(
|
|
47
|
+
(file) => fs.writeFileSync(path.join(stagingDirectory, file), '{}')
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
51
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
52
|
+
|
|
53
|
+
expect(moved[0]).toBe('idCounter.json');
|
|
54
|
+
expect(moved.at(-1)).toBe('pageRegistry.json');
|
|
55
|
+
expect(moved.slice(1, -1).sort()).toEqual(['app.json', 'keyMap.json', 'refMap.json']);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('publishBuildDirectory moves a byte-identical idCounter.json first and pageRegistry.json last', async () => {
|
|
59
|
+
moved.length = 0;
|
|
60
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-publish-order-'));
|
|
61
|
+
const buildDirectory = path.join(root, 'build');
|
|
62
|
+
const stagingDirectory = path.join(root, 'build-staging');
|
|
63
|
+
fs.mkdirSync(stagingDirectory, { recursive: true });
|
|
64
|
+
fs.mkdirSync(buildDirectory, { recursive: true });
|
|
65
|
+
['app.json', 'idCounter.json', 'keyMap.json', 'pageRegistry.json'].forEach((file) => {
|
|
66
|
+
fs.writeFileSync(path.join(stagingDirectory, file), '{}');
|
|
67
|
+
fs.writeFileSync(path.join(buildDirectory, file), '{}');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
71
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
72
|
+
|
|
73
|
+
expect(moved).toEqual(['idCounter.json', 'pageRegistry.json']);
|
|
74
|
+
});
|
|
@@ -0,0 +1,262 @@
|
|
|
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 fsPromises from 'fs/promises';
|
|
19
|
+
import os from 'os';
|
|
20
|
+
import path from 'path';
|
|
21
|
+
import { jest } from '@jest/globals';
|
|
22
|
+
|
|
23
|
+
import publishBuildDirectory from './publishBuildDirectory.mjs';
|
|
24
|
+
|
|
25
|
+
// The publish-window tests move 200 files through several thread pool fs calls
|
|
26
|
+
// each while a poller runs on every loop turn, and the locked-rename test waits
|
|
27
|
+
// out the full 3.3 s retry schedule. On a loaded machine they pass jest's 5 s
|
|
28
|
+
// default, and a timed-out poller then fails the next test.
|
|
29
|
+
jest.setTimeout(30000);
|
|
30
|
+
|
|
31
|
+
let root;
|
|
32
|
+
let buildDirectory;
|
|
33
|
+
let stagingDirectory;
|
|
34
|
+
|
|
35
|
+
function write(directory, file, content) {
|
|
36
|
+
const filePath = path.join(directory, file);
|
|
37
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
38
|
+
fs.writeFileSync(filePath, content);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function read(file) {
|
|
42
|
+
return fs.readFileSync(path.join(buildDirectory, file), 'utf8');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-publish-build-'));
|
|
47
|
+
buildDirectory = path.join(root, 'build');
|
|
48
|
+
stagingDirectory = path.join(root, 'build-staging');
|
|
49
|
+
write(stagingDirectory, 'pageRegistry.json', 'new');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
jest.restoreAllMocks();
|
|
54
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
function lockedError(code) {
|
|
58
|
+
return Object.assign(new Error(`${code}: resource busy or locked, rename`), { code });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test('publishBuildDirectory replaces live files with the staged build', async () => {
|
|
62
|
+
write(buildDirectory, 'app.json', 'old');
|
|
63
|
+
write(buildDirectory, 'connections/db.json', 'old');
|
|
64
|
+
write(stagingDirectory, 'app.json', 'new');
|
|
65
|
+
write(stagingDirectory, 'connections/db.json', 'new');
|
|
66
|
+
write(stagingDirectory, 'api/endpoint.json', 'new');
|
|
67
|
+
|
|
68
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
69
|
+
|
|
70
|
+
expect(read('app.json')).toBe('new');
|
|
71
|
+
expect(read('connections/db.json')).toBe('new');
|
|
72
|
+
expect(read('api/endpoint.json')).toBe('new');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('publishBuildDirectory removes live files the new build did not write', async () => {
|
|
76
|
+
write(buildDirectory, 'app.json', 'old');
|
|
77
|
+
write(buildDirectory, 'connections/removed.json', 'old');
|
|
78
|
+
write(buildDirectory, 'pages/jit-page.json', 'old');
|
|
79
|
+
write(stagingDirectory, 'app.json', 'new');
|
|
80
|
+
|
|
81
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
82
|
+
|
|
83
|
+
expect(fs.existsSync(path.join(buildDirectory, 'connections/removed.json'))).toBe(false);
|
|
84
|
+
expect(fs.existsSync(path.join(buildDirectory, 'pages/jit-page.json'))).toBe(false);
|
|
85
|
+
expect(read('app.json')).toBe('new');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('publishBuildDirectory removes the staging directory', async () => {
|
|
89
|
+
write(stagingDirectory, 'app.json', 'new');
|
|
90
|
+
|
|
91
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
92
|
+
|
|
93
|
+
expect(fs.existsSync(stagingDirectory)).toBe(false);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('publishBuildDirectory creates the live directory on the first build', async () => {
|
|
97
|
+
write(stagingDirectory, 'app.json', 'new');
|
|
98
|
+
|
|
99
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
100
|
+
|
|
101
|
+
expect(read('app.json')).toBe('new');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('an artifact present in both builds exists at every point of the publish', async () => {
|
|
105
|
+
write(buildDirectory, 'connections/db.json', 'old');
|
|
106
|
+
write(stagingDirectory, 'connections/db.json', 'new');
|
|
107
|
+
for (let i = 0; i < 200; i++) {
|
|
108
|
+
write(buildDirectory, `api/endpoint-${i}.json`, 'old');
|
|
109
|
+
write(stagingDirectory, `api/endpoint-${i}.json`, 'new');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let checks = 0;
|
|
113
|
+
let missing = 0;
|
|
114
|
+
let publishing = true;
|
|
115
|
+
const check = () => {
|
|
116
|
+
checks += 1;
|
|
117
|
+
if (!fs.existsSync(path.join(buildDirectory, 'connections/db.json'))) {
|
|
118
|
+
missing += 1;
|
|
119
|
+
}
|
|
120
|
+
if (publishing) {
|
|
121
|
+
setImmediate(check);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
setImmediate(check);
|
|
125
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
126
|
+
publishing = false;
|
|
127
|
+
|
|
128
|
+
expect(checks).toBeGreaterThan(1);
|
|
129
|
+
expect(missing).toBe(0);
|
|
130
|
+
expect(read('connections/db.json')).toBe('new');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('the page registry arrives after every other new file and after stale files are removed', async () => {
|
|
134
|
+
write(buildDirectory, 'pageRegistry.json', 'old');
|
|
135
|
+
write(buildDirectory, 'pages/jit-page/jit-page.json', 'old');
|
|
136
|
+
for (let i = 0; i < 200; i++) {
|
|
137
|
+
write(buildDirectory, `api/endpoint-${i}.json`, 'old');
|
|
138
|
+
write(stagingDirectory, `api/endpoint-${i}.json`, 'new');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
let checks = 0;
|
|
142
|
+
let early = 0;
|
|
143
|
+
let publishing = true;
|
|
144
|
+
const check = () => {
|
|
145
|
+
if (!publishing) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
checks += 1;
|
|
149
|
+
if (read('pageRegistry.json') === 'new') {
|
|
150
|
+
const oldEndpoint = [...Array(200).keys()].some(
|
|
151
|
+
(i) => read(`api/endpoint-${i}.json`) === 'old'
|
|
152
|
+
);
|
|
153
|
+
const stalePage = fs.existsSync(path.join(buildDirectory, 'pages/jit-page/jit-page.json'));
|
|
154
|
+
if (oldEndpoint || stalePage) {
|
|
155
|
+
early += 1;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
setImmediate(check);
|
|
159
|
+
};
|
|
160
|
+
setImmediate(check);
|
|
161
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
162
|
+
publishing = false;
|
|
163
|
+
|
|
164
|
+
expect(checks).toBeGreaterThan(1);
|
|
165
|
+
expect(early).toBe(0);
|
|
166
|
+
expect(read('pageRegistry.json')).toBe('new');
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('publishBuildDirectory keeps the control files the manager and dev tools write', async () => {
|
|
170
|
+
write(buildDirectory, '.restart', '{"reason":"Added a block type"}');
|
|
171
|
+
write(buildDirectory, 'buildStatus.json', '{"status":"ok"}');
|
|
172
|
+
write(buildDirectory, 'invalidatePages', '1');
|
|
173
|
+
write(buildDirectory, 'reload', '1');
|
|
174
|
+
write(stagingDirectory, 'app.json', 'new');
|
|
175
|
+
write(stagingDirectory, 'pageRegistry.json', '{}');
|
|
176
|
+
|
|
177
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
178
|
+
|
|
179
|
+
expect(read('.restart')).toBe('{"reason":"Added a block type"}');
|
|
180
|
+
expect(read('buildStatus.json')).toBe('{"status":"ok"}');
|
|
181
|
+
expect(read('invalidatePages')).toBe('1');
|
|
182
|
+
expect(read('reload')).toBe('1');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test('publishBuildDirectory retries a rename while another process holds the file', async () => {
|
|
186
|
+
write(buildDirectory, 'app.json', 'old');
|
|
187
|
+
write(stagingDirectory, 'app.json', 'new');
|
|
188
|
+
jest
|
|
189
|
+
.spyOn(fsPromises, 'rename')
|
|
190
|
+
.mockRejectedValueOnce(lockedError('EPERM'))
|
|
191
|
+
.mockRejectedValueOnce(lockedError('EBUSY'));
|
|
192
|
+
|
|
193
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
194
|
+
|
|
195
|
+
expect(read('app.json')).toBe('new');
|
|
196
|
+
expect(read('pageRegistry.json')).toBe('new');
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('publishBuildDirectory fails with the rename error when a file stays locked', async () => {
|
|
200
|
+
write(stagingDirectory, 'app.json', 'new');
|
|
201
|
+
const rename = jest.spyOn(fsPromises, 'rename').mockRejectedValue(lockedError('EPERM'));
|
|
202
|
+
|
|
203
|
+
await expect(publishBuildDirectory({ buildDirectory, stagingDirectory })).rejects.toThrow(
|
|
204
|
+
'EPERM: resource busy or locked, rename'
|
|
205
|
+
);
|
|
206
|
+
expect(rename).toHaveBeenCalledTimes(11);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
function setMtime(file, seconds) {
|
|
210
|
+
fs.utimesSync(path.join(buildDirectory, file), seconds, seconds);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function mtime(file) {
|
|
214
|
+
return fs.statSync(path.join(buildDirectory, file)).mtimeMs;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
test('publishBuildDirectory leaves an unchanged artifact in place and replaces a changed one', async () => {
|
|
218
|
+
write(buildDirectory, 'app.json', 'same');
|
|
219
|
+
write(buildDirectory, 'client/page.json', 'old');
|
|
220
|
+
write(buildDirectory, 'client/sized.json', 'aaa');
|
|
221
|
+
setMtime('app.json', 1000);
|
|
222
|
+
setMtime('client/page.json', 1000);
|
|
223
|
+
setMtime('client/sized.json', 1000);
|
|
224
|
+
write(stagingDirectory, 'app.json', 'same');
|
|
225
|
+
write(stagingDirectory, 'client/page.json', 'new');
|
|
226
|
+
write(stagingDirectory, 'client/sized.json', 'bbb');
|
|
227
|
+
|
|
228
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
229
|
+
|
|
230
|
+
expect(mtime('app.json')).toBe(1000 * 1000);
|
|
231
|
+
expect(read('client/page.json')).toBe('new');
|
|
232
|
+
expect(mtime('client/page.json')).not.toBe(1000 * 1000);
|
|
233
|
+
expect(read('client/sized.json')).toBe('bbb');
|
|
234
|
+
expect(fs.existsSync(stagingDirectory)).toBe(false);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('publishBuildDirectory moves a byte-identical page registry and id counter anyway', async () => {
|
|
238
|
+
write(buildDirectory, 'pageRegistry.json', 'new');
|
|
239
|
+
write(buildDirectory, 'idCounter.json', '{"prefix":"a1b2"}');
|
|
240
|
+
setMtime('pageRegistry.json', 1000);
|
|
241
|
+
setMtime('idCounter.json', 1000);
|
|
242
|
+
write(stagingDirectory, 'idCounter.json', '{"prefix":"a1b2"}');
|
|
243
|
+
|
|
244
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
245
|
+
|
|
246
|
+
expect(mtime('pageRegistry.json')).not.toBe(1000 * 1000);
|
|
247
|
+
expect(mtime('idCounter.json')).not.toBe(1000 * 1000);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test('publishBuildDirectory removes stale files and keeps control files when artifacts are unchanged', async () => {
|
|
251
|
+
write(buildDirectory, 'app.json', 'same');
|
|
252
|
+
write(buildDirectory, 'jitMaps/abc-1-1.json', '{}');
|
|
253
|
+
write(buildDirectory, 'buildStatus.json', '{"status":"ok"}');
|
|
254
|
+
write(stagingDirectory, 'app.json', 'same');
|
|
255
|
+
|
|
256
|
+
await publishBuildDirectory({ buildDirectory, stagingDirectory });
|
|
257
|
+
|
|
258
|
+
expect(read('app.json')).toBe('same');
|
|
259
|
+
expect(fs.existsSync(path.join(buildDirectory, 'jitMaps/abc-1-1.json'))).toBe(false);
|
|
260
|
+
expect(read('buildStatus.json')).toBe('{"status":"ok"}');
|
|
261
|
+
expect(fs.existsSync(stagingDirectory)).toBe(false);
|
|
262
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
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 path from 'path';
|
|
19
|
+
|
|
20
|
+
// The build always writes config.json, and Vite reads basePath from the same
|
|
21
|
+
// file when the child starts, so the manager and the child agree on it.
|
|
22
|
+
function readBasePath({ directories }) {
|
|
23
|
+
const config = JSON.parse(fs.readFileSync(path.join(directories.build, 'config.json'), 'utf8'));
|
|
24
|
+
return config.basePath ?? '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default readBasePath;
|
|
@@ -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 fs from 'fs';
|
|
18
|
+
import os from 'os';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
|
|
21
|
+
import readBasePath from './readBasePath.mjs';
|
|
22
|
+
|
|
23
|
+
let buildDirectory;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
buildDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-read-base-path-'));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(() => {
|
|
30
|
+
fs.rmSync(buildDirectory, { recursive: true, force: true });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('readBasePath returns the basePath from the build config', () => {
|
|
34
|
+
fs.writeFileSync(path.join(buildDirectory, 'config.json'), JSON.stringify({ basePath: '/app' }));
|
|
35
|
+
|
|
36
|
+
expect(readBasePath({ directories: { build: buildDirectory } })).toEqual('/app');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('readBasePath returns an empty string when the app sets no basePath', () => {
|
|
40
|
+
fs.writeFileSync(path.join(buildDirectory, 'config.json'), JSON.stringify({ theme: {} }));
|
|
41
|
+
|
|
42
|
+
expect(readBasePath({ directories: { build: buildDirectory } })).toEqual('');
|
|
43
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
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
|
+
// A lowdefy_build_status call is a small JSON-RPC message. A body of unknown length,
|
|
18
|
+
// or larger than this, is forwarded unread, so the proxy never holds a large body in
|
|
19
|
+
// memory before the dev server has checked the request.
|
|
20
|
+
const MAX_WAIT_BODY_BYTES = 64 * 1024;
|
|
21
|
+
|
|
22
|
+
function parseJson(body) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(body.toString('utf8'));
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isBuildStatusWaitCall(message) {
|
|
31
|
+
return (
|
|
32
|
+
message?.method === 'tools/call' &&
|
|
33
|
+
message.params?.name === 'lowdefy_build_status' &&
|
|
34
|
+
message.params?.arguments?.wait === true
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function readBody(req) {
|
|
39
|
+
const chunks = [];
|
|
40
|
+
for await (const chunk of req) {
|
|
41
|
+
chunks.push(chunk);
|
|
42
|
+
}
|
|
43
|
+
return Buffer.concat(chunks);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Whether a proxied request is a build-status wait: GET build-status?wait=true,
|
|
47
|
+
// or the lowdefy_build_status MCP tool called with wait: true. Telling the
|
|
48
|
+
// second apart means reading the body of every MCP POST (small JSON-RPC
|
|
49
|
+
// messages), so it is returned for the proxy to forward; body is undefined
|
|
50
|
+
// for any other request, whose body is still unread.
|
|
51
|
+
async function readBuildStatusWait({ basePath, req }) {
|
|
52
|
+
const { pathname, searchParams } = new URL(req.url, 'http://localhost');
|
|
53
|
+
if (req.method === 'GET' && pathname === `${basePath}/lowdefy-docs/build-status`) {
|
|
54
|
+
return { wait: searchParams.get('wait') === 'true', body: undefined };
|
|
55
|
+
}
|
|
56
|
+
if (req.method !== 'POST' || pathname !== `${basePath}/lowdefy-docs/mcp`) {
|
|
57
|
+
return { wait: false, body: undefined };
|
|
58
|
+
}
|
|
59
|
+
const contentLength = Number(req.headers['content-length']);
|
|
60
|
+
if (!Number.isInteger(contentLength) || contentLength > MAX_WAIT_BODY_BYTES) {
|
|
61
|
+
return { wait: false, body: undefined };
|
|
62
|
+
}
|
|
63
|
+
const body = await readBody(req);
|
|
64
|
+
return { wait: [parseJson(body)].flat().some(isBuildStatusWaitCall), body };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default readBuildStatusWait;
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
|
|
19
|
+
// The manager's own package.json - in an app, the CLI's copy in .lowdefy/dev,
|
|
20
|
+
// which carries the installed server-dev version. Read from the file, not
|
|
21
|
+
// npm_package_version, which only a package manager's `run` sets: the CLI
|
|
22
|
+
// starts the manager with node directly.
|
|
23
|
+
function readManagerVersion() {
|
|
24
|
+
const packageJson = JSON.parse(
|
|
25
|
+
fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')
|
|
26
|
+
);
|
|
27
|
+
return packageJson.version;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default readManagerVersion;
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
+
|
|
19
|
+
import readManagerVersion from './readManagerVersion.mjs';
|
|
20
|
+
|
|
21
|
+
test('readManagerVersion reads the version from the server package.json, without npm_package_version', () => {
|
|
22
|
+
const saved = process.env.npm_package_version;
|
|
23
|
+
delete process.env.npm_package_version;
|
|
24
|
+
try {
|
|
25
|
+
const packageJson = JSON.parse(
|
|
26
|
+
fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')
|
|
27
|
+
);
|
|
28
|
+
expect(readManagerVersion()).toBe(packageJson.version);
|
|
29
|
+
expect(packageJson.name).toBe('@lowdefy/server-dev');
|
|
30
|
+
} finally {
|
|
31
|
+
if (saved !== undefined) {
|
|
32
|
+
process.env.npm_package_version = saved;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
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 path from 'path';
|
|
18
|
+
import { get } from '@lowdefy/helpers';
|
|
19
|
+
import { readFile } from '@lowdefy/node-utils';
|
|
20
|
+
import YAML from 'yaml';
|
|
21
|
+
|
|
22
|
+
// The plugins lowdefy.yaml lists, read straight from the file: the plugin
|
|
23
|
+
// type maps are read before the config build, which needs them.
|
|
24
|
+
async function readPluginDefinitions({ directories }) {
|
|
25
|
+
let lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yaml'));
|
|
26
|
+
if (!lowdefyYaml) {
|
|
27
|
+
lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yml'));
|
|
28
|
+
}
|
|
29
|
+
if (!lowdefyYaml) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const lowdefy = YAML.parse(lowdefyYaml);
|
|
33
|
+
return get(lowdefy, 'plugins', { default: [] });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default readPluginDefinitions;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const LOOPBACK_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
18
|
+
|
|
19
|
+
// The auth canonical origin for this dev server. BetterAuth trusts only that
|
|
20
|
+
// origin (the CSRF Origin check, callbackURL targets) and builds magic-link and
|
|
21
|
+
// verification links from it. A developer's BETTER_AUTH_URL usually names the
|
|
22
|
+
// port they ran on before - http://localhost:3000 - but the dev server's port
|
|
23
|
+
// is chosen at start (the hub allocates one, a busy port is skipped), and a
|
|
24
|
+
// loopback origin on another port refuses every sign-in from the page this
|
|
25
|
+
// server serves with "Invalid origin". So a plain-http loopback BETTER_AUTH_URL
|
|
26
|
+
// with an explicit port follows the dev server's port.
|
|
27
|
+
//
|
|
28
|
+
// Everything else is the developer's deliberate choice and is kept: a tunnel or
|
|
29
|
+
// LAN host, and a loopback origin the dev server cannot be serving itself - an
|
|
30
|
+
// https one (it speaks plain http) or one on the default port (80 needs root),
|
|
31
|
+
// which is a local reverse proxy (Caddy, mkcert) in front of it. An unset one
|
|
32
|
+
// stays unset: the dev server then derives the origin from each request.
|
|
33
|
+
//
|
|
34
|
+
// Returns the origin to use and whether it was rewritten, so the caller reports
|
|
35
|
+
// a rewrite and not a mere difference in whitespace.
|
|
36
|
+
function resolveDevAuthUrl({ configured, port }) {
|
|
37
|
+
const value = configured?.trim();
|
|
38
|
+
if (!value) return { authUrl: value, rewritten: false };
|
|
39
|
+
let url;
|
|
40
|
+
try {
|
|
41
|
+
url = new URL(value);
|
|
42
|
+
} catch {
|
|
43
|
+
return { authUrl: value, rewritten: false };
|
|
44
|
+
}
|
|
45
|
+
if (
|
|
46
|
+
!LOOPBACK_HOSTNAMES.has(url.hostname) ||
|
|
47
|
+
url.protocol !== 'http:' ||
|
|
48
|
+
url.port === '' ||
|
|
49
|
+
url.port === String(port)
|
|
50
|
+
) {
|
|
51
|
+
return { authUrl: value, rewritten: false };
|
|
52
|
+
}
|
|
53
|
+
url.port = String(port);
|
|
54
|
+
const authUrl = url.pathname === '/' ? url.origin : `${url.origin}${url.pathname}`;
|
|
55
|
+
return { authUrl, rewritten: true };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default resolveDevAuthUrl;
|