@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,107 @@
|
|
|
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 resolveDevAuthUrl from './resolveDevAuthUrl.mjs';
|
|
18
|
+
|
|
19
|
+
test('a localhost origin on another port follows the dev server port', () => {
|
|
20
|
+
expect(resolveDevAuthUrl({ configured: 'http://localhost:3000', port: 4102 })).toEqual({
|
|
21
|
+
authUrl: 'http://localhost:4102',
|
|
22
|
+
rewritten: true,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('127.0.0.1 and [::1] origins follow the dev server port', () => {
|
|
27
|
+
expect(resolveDevAuthUrl({ configured: 'http://127.0.0.1:3000', port: 4102 })).toEqual({
|
|
28
|
+
authUrl: 'http://127.0.0.1:4102',
|
|
29
|
+
rewritten: true,
|
|
30
|
+
});
|
|
31
|
+
expect(resolveDevAuthUrl({ configured: 'http://[::1]:3000', port: 4102 })).toEqual({
|
|
32
|
+
authUrl: 'http://[::1]:4102',
|
|
33
|
+
rewritten: true,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('a path on the configured origin is kept', () => {
|
|
38
|
+
expect(resolveDevAuthUrl({ configured: 'http://localhost:3000/app', port: 4102 })).toEqual({
|
|
39
|
+
authUrl: 'http://localhost:4102/app',
|
|
40
|
+
rewritten: true,
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('a localhost origin already on the dev server port is returned as configured', () => {
|
|
45
|
+
expect(resolveDevAuthUrl({ configured: 'http://localhost:4102', port: 4102 })).toEqual({
|
|
46
|
+
authUrl: 'http://localhost:4102',
|
|
47
|
+
rewritten: false,
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('surrounding whitespace is trimmed without counting as a rewrite', () => {
|
|
52
|
+
expect(resolveDevAuthUrl({ configured: 'http://localhost:4102 ', port: 4102 })).toEqual({
|
|
53
|
+
authUrl: 'http://localhost:4102',
|
|
54
|
+
rewritten: false,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('an https localhost origin is kept, since a local TLS proxy fronts the dev server', () => {
|
|
59
|
+
expect(resolveDevAuthUrl({ configured: 'https://localhost', port: 4102 })).toEqual({
|
|
60
|
+
authUrl: 'https://localhost',
|
|
61
|
+
rewritten: false,
|
|
62
|
+
});
|
|
63
|
+
expect(resolveDevAuthUrl({ configured: 'https://localhost:8443', port: 4102 })).toEqual({
|
|
64
|
+
authUrl: 'https://localhost:8443',
|
|
65
|
+
rewritten: false,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('a localhost origin on the default port is kept, since a local proxy fronts the dev server', () => {
|
|
70
|
+
expect(resolveDevAuthUrl({ configured: 'http://localhost', port: 4102 })).toEqual({
|
|
71
|
+
authUrl: 'http://localhost',
|
|
72
|
+
rewritten: false,
|
|
73
|
+
});
|
|
74
|
+
expect(resolveDevAuthUrl({ configured: 'http://localhost:80', port: 4102 })).toEqual({
|
|
75
|
+
authUrl: 'http://localhost:80',
|
|
76
|
+
rewritten: false,
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test('a non-loopback origin is kept', () => {
|
|
81
|
+
expect(resolveDevAuthUrl({ configured: 'https://dev.example.ngrok.app', port: 4102 })).toEqual({
|
|
82
|
+
authUrl: 'https://dev.example.ngrok.app',
|
|
83
|
+
rewritten: false,
|
|
84
|
+
});
|
|
85
|
+
expect(resolveDevAuthUrl({ configured: 'http://192.168.1.20:3000', port: 4102 })).toEqual({
|
|
86
|
+
authUrl: 'http://192.168.1.20:3000',
|
|
87
|
+
rewritten: false,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('an unset origin stays unset', () => {
|
|
92
|
+
expect(resolveDevAuthUrl({ configured: undefined, port: 4102 })).toEqual({
|
|
93
|
+
authUrl: undefined,
|
|
94
|
+
rewritten: false,
|
|
95
|
+
});
|
|
96
|
+
expect(resolveDevAuthUrl({ configured: '', port: 4102 })).toEqual({
|
|
97
|
+
authUrl: '',
|
|
98
|
+
rewritten: false,
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('an unparseable value is returned as configured', () => {
|
|
103
|
+
expect(resolveDevAuthUrl({ configured: 'localhost:3000', port: 4102 })).toEqual({
|
|
104
|
+
authUrl: 'localhost:3000',
|
|
105
|
+
rewritten: false,
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
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 { findAvailablePort, isPortAvailable } from '@lowdefy/node-utils';
|
|
18
|
+
|
|
19
|
+
// An explicit port (--port, PORT, cli.port, or one the hub allocated) is a
|
|
20
|
+
// promise to whoever asked for it: moving silently to the next free port sent
|
|
21
|
+
// agents to a different app's server on the port they expected. Only the
|
|
22
|
+
// unrequested default still moves.
|
|
23
|
+
async function resolvePort({ port, strict, name }) {
|
|
24
|
+
if (strict) {
|
|
25
|
+
if (!(await isPortAvailable({ port }))) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`${name} ${port} is already in use. Stop the process using it or choose another port.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
return port;
|
|
31
|
+
}
|
|
32
|
+
return findAvailablePort({ port });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function resolvePorts(context) {
|
|
36
|
+
const { options } = context;
|
|
37
|
+
const port = await resolvePort({ port: options.port, strict: options.strictPort, name: 'Port' });
|
|
38
|
+
if (port !== options.port) {
|
|
39
|
+
context.logger.warn(`Port ${options.port} is in use, using port ${port} instead.`);
|
|
40
|
+
}
|
|
41
|
+
const internalPort = await resolvePort({
|
|
42
|
+
port: options.internalPort ?? port + 1,
|
|
43
|
+
strict: options.internalPort !== undefined,
|
|
44
|
+
name: 'Internal port',
|
|
45
|
+
});
|
|
46
|
+
return { port, internalPort };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default resolvePorts;
|
|
@@ -14,8 +14,22 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import path from 'path';
|
|
18
|
+
|
|
17
19
|
import chokidar from 'chokidar';
|
|
18
20
|
import BatchChanges from './BatchChanges.mjs';
|
|
21
|
+
import createDotPathIgnore from './createDotPathIgnore.mjs';
|
|
22
|
+
|
|
23
|
+
// chokidar hands ignore functions a path with forward slashes on every platform
|
|
24
|
+
// (anymatch normalizes it), while watch paths and the directories they are
|
|
25
|
+
// compared with use the platform separator. Ignore functions get the platform
|
|
26
|
+
// form, so a comparison with a watch path holds on Windows too.
|
|
27
|
+
function withNativePath(ignore) {
|
|
28
|
+
if (typeof ignore !== 'function') {
|
|
29
|
+
return ignore;
|
|
30
|
+
}
|
|
31
|
+
return (filePath, ...rest) => ignore(path.normalize(filePath), ...rest);
|
|
32
|
+
}
|
|
19
33
|
|
|
20
34
|
function setupWatcher({
|
|
21
35
|
callback,
|
|
@@ -24,26 +38,20 @@ function setupWatcher({
|
|
|
24
38
|
ignorePaths = [],
|
|
25
39
|
watchPaths,
|
|
26
40
|
delay = 500,
|
|
41
|
+
onBusy,
|
|
27
42
|
}) {
|
|
28
43
|
return new Promise((resolve) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const batchChanges = new BatchChanges({ context, fn: callback, delay });
|
|
33
|
-
const defaultIgnorePaths = watchDotfiles
|
|
34
|
-
? []
|
|
35
|
-
: [
|
|
36
|
-
/(^|[/\\])\../, // ignore dotfiles
|
|
37
|
-
];
|
|
44
|
+
const batchChanges = new BatchChanges({ context, fn: callback, delay, onBusy });
|
|
45
|
+
const defaultIgnorePaths = watchDotfiles ? [] : [createDotPathIgnore({ watchPaths })];
|
|
38
46
|
const configWatcher = chokidar.watch(watchPaths, {
|
|
39
|
-
ignored: [...defaultIgnorePaths, ...ignorePaths],
|
|
47
|
+
ignored: [...defaultIgnorePaths, ...ignorePaths].map(withNativePath),
|
|
40
48
|
persistent: true,
|
|
41
49
|
ignoreInitial: true,
|
|
42
50
|
});
|
|
43
51
|
configWatcher.on('add', (...args) => batchChanges.newChange(...args));
|
|
44
52
|
configWatcher.on('change', (...args) => batchChanges.newChange(...args));
|
|
45
53
|
configWatcher.on('unlink', (...args) => batchChanges.newChange(...args));
|
|
46
|
-
configWatcher.on('ready', () => resolve());
|
|
54
|
+
configWatcher.on('ready', () => resolve(configWatcher));
|
|
47
55
|
});
|
|
48
56
|
}
|
|
49
57
|
|
|
@@ -0,0 +1,45 @@
|
|
|
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 pruneRecordings from './pruneRecordings.mjs';
|
|
18
|
+
|
|
19
|
+
const HOUR_MS = 60 * 60 * 1000;
|
|
20
|
+
|
|
21
|
+
// Runs in the manager, which lives as long as the session (the server child
|
|
22
|
+
// restarts). Pruning runs whether or not recording is on, so old traces still
|
|
23
|
+
// age out after a developer turns recording off. A failure is logged at debug
|
|
24
|
+
// and never stops the dev server.
|
|
25
|
+
function startRecordingPruner({ configDirectory, logger, intervalMs = HOUR_MS }) {
|
|
26
|
+
function prune() {
|
|
27
|
+
try {
|
|
28
|
+
const deleted = pruneRecordings({ configDirectory });
|
|
29
|
+
const count = deleted.directories.length + deleted.files.length;
|
|
30
|
+
if (count > 0) {
|
|
31
|
+
logger.debug(
|
|
32
|
+
`Pruned ${deleted.directories.length} recording date directories and ${deleted.files.length} recording files.`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
} catch (error) {
|
|
36
|
+
logger.debug(`Pruning dev recordings failed: ${error.message}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
prune();
|
|
40
|
+
const interval = setInterval(prune, intervalMs);
|
|
41
|
+
interval.unref();
|
|
42
|
+
return interval;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default startRecordingPruner;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { jest } from '@jest/globals';
|
|
18
|
+
|
|
19
|
+
const mockPruneRecordings = jest.fn();
|
|
20
|
+
jest.unstable_mockModule('./pruneRecordings.mjs', () => ({ default: mockPruneRecordings }));
|
|
21
|
+
|
|
22
|
+
const { default: startRecordingPruner } = await import('./startRecordingPruner.mjs');
|
|
23
|
+
|
|
24
|
+
const logger = { debug: jest.fn() };
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.useFakeTimers({ doNotFake: ['performance'] });
|
|
28
|
+
mockPruneRecordings.mockReset();
|
|
29
|
+
mockPruneRecordings.mockReturnValue({ directories: [], files: [] });
|
|
30
|
+
logger.debug.mockReset();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
jest.useRealTimers();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('startRecordingPruner prunes at start and then on the interval, with recording off', () => {
|
|
38
|
+
process.env.LOWDEFY_DEV_RECORD = 'false';
|
|
39
|
+
const interval = startRecordingPruner({ configDirectory: '/app', logger, intervalMs: 1000 });
|
|
40
|
+
expect(mockPruneRecordings).toHaveBeenCalledTimes(1);
|
|
41
|
+
expect(mockPruneRecordings).toHaveBeenCalledWith({ configDirectory: '/app' });
|
|
42
|
+
jest.advanceTimersByTime(2500);
|
|
43
|
+
expect(mockPruneRecordings).toHaveBeenCalledTimes(3);
|
|
44
|
+
clearInterval(interval);
|
|
45
|
+
delete process.env.LOWDEFY_DEV_RECORD;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('startRecordingPruner logs a failure at debug and keeps running', () => {
|
|
49
|
+
mockPruneRecordings.mockImplementationOnce(() => {
|
|
50
|
+
throw new Error('EACCES');
|
|
51
|
+
});
|
|
52
|
+
const interval = startRecordingPruner({ configDirectory: '/app', logger, intervalMs: 1000 });
|
|
53
|
+
expect(logger.debug).toHaveBeenCalledWith('Pruning dev recordings failed: EACCES');
|
|
54
|
+
jest.advanceTimersByTime(1000);
|
|
55
|
+
expect(mockPruneRecordings).toHaveBeenCalledTimes(2);
|
|
56
|
+
clearInterval(interval);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('startRecordingPruner logs what it deleted at debug', () => {
|
|
60
|
+
mockPruneRecordings.mockReturnValue({ directories: ['/a'], files: ['/b', '/c'] });
|
|
61
|
+
const interval = startRecordingPruner({ configDirectory: '/app', logger });
|
|
62
|
+
expect(logger.debug).toHaveBeenCalledWith(
|
|
63
|
+
'Pruned 1 recording date directories and 2 recording files.'
|
|
64
|
+
);
|
|
65
|
+
clearInterval(interval);
|
|
66
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
function hasExited(child) {
|
|
18
|
+
return child.exitCode !== null || child.signalCode !== null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function waitForExit({ child, timeoutMs }) {
|
|
22
|
+
if (hasExited(child)) {
|
|
23
|
+
return Promise.resolve(true);
|
|
24
|
+
}
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
const timer = setTimeout(() => {
|
|
27
|
+
child.off('exit', onExit);
|
|
28
|
+
resolve(false);
|
|
29
|
+
}, timeoutMs);
|
|
30
|
+
function onExit() {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
resolve(true);
|
|
33
|
+
}
|
|
34
|
+
child.once('exit', onExit);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Waits for a signalled child to let go of its port before a new one binds
|
|
39
|
+
// it. The wait is bounded so a child that ignores SIGTERM, or never exits even
|
|
40
|
+
// after SIGKILL, cannot hang a restart or the build-status wait around it.
|
|
41
|
+
// Resolves true once the child has exited, false if it never did.
|
|
42
|
+
async function waitForChildExit({ child, timeoutMs = 10000, killTimeoutMs = 5000 }) {
|
|
43
|
+
if (!child) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (await waitForExit({ child, timeoutMs })) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
child.kill('SIGKILL');
|
|
50
|
+
return waitForExit({ child, timeoutMs: killTimeoutMs });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default waitForChildExit;
|
|
@@ -0,0 +1,70 @@
|
|
|
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 { EventEmitter } from 'node:events';
|
|
18
|
+
|
|
19
|
+
import { jest } from '@jest/globals';
|
|
20
|
+
|
|
21
|
+
import waitForChildExit from './waitForChildExit.mjs';
|
|
22
|
+
|
|
23
|
+
function createChild({ exitOn = [] } = {}) {
|
|
24
|
+
const child = Object.assign(new EventEmitter(), {
|
|
25
|
+
exitCode: null,
|
|
26
|
+
signalCode: null,
|
|
27
|
+
kill: jest.fn((signal) => {
|
|
28
|
+
if (exitOn.includes(signal)) {
|
|
29
|
+
setImmediate(() => {
|
|
30
|
+
child.signalCode = signal;
|
|
31
|
+
child.emit('exit', null, signal);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}),
|
|
35
|
+
});
|
|
36
|
+
return child;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
test('waitForChildExit resolves at once when there is no child', async () => {
|
|
40
|
+
await expect(waitForChildExit({ child: null })).resolves.toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('waitForChildExit resolves at once when the child has already exited', async () => {
|
|
44
|
+
const child = createChild();
|
|
45
|
+
child.exitCode = 0;
|
|
46
|
+
await expect(waitForChildExit({ child, timeoutMs: 1000 })).resolves.toBe(true);
|
|
47
|
+
expect(child.kill).not.toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('waitForChildExit resolves when the child exits within the timeout, without SIGKILL', async () => {
|
|
51
|
+
const child = createChild();
|
|
52
|
+
const waiting = waitForChildExit({ child, timeoutMs: 1000, killTimeoutMs: 1000 });
|
|
53
|
+
child.exitCode = 0;
|
|
54
|
+
child.emit('exit', 0, null);
|
|
55
|
+
await expect(waiting).resolves.toBe(true);
|
|
56
|
+
expect(child.kill).not.toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('waitForChildExit SIGKILLs a child that ignores SIGTERM and resolves once it exits', async () => {
|
|
60
|
+
const child = createChild({ exitOn: ['SIGKILL'] });
|
|
61
|
+
await expect(waitForChildExit({ child, timeoutMs: 10, killTimeoutMs: 1000 })).resolves.toBe(true);
|
|
62
|
+
expect(child.kill).toHaveBeenCalledWith('SIGKILL');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('waitForChildExit resolves false after both timeouts when the child never exits', async () => {
|
|
66
|
+
const child = createChild();
|
|
67
|
+
await expect(waitForChildExit({ child, timeoutMs: 10, killTimeoutMs: 10 })).resolves.toBe(false);
|
|
68
|
+
expect(child.kill).toHaveBeenCalledWith('SIGKILL');
|
|
69
|
+
expect(child.listenerCount('exit')).toBe(0);
|
|
70
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
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 { wait } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
function hasExited(child) {
|
|
20
|
+
return child.exitCode !== null || child.signalCode !== null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The child answers <basePath>/api/ping once Vite and the Hono app are both up, so this
|
|
24
|
+
// is the moment the server is really usable - waitForDevServer flips the
|
|
25
|
+
// instance record to "ready" then, which is what the hub and the MCP shim
|
|
26
|
+
// wait on. A child that exits (a plugin that fails to load) will never
|
|
27
|
+
// answer, so the wait ends.
|
|
28
|
+
async function waitForServer({ port, basePath, child, timeoutMs = 120000, intervalMs = 250 }) {
|
|
29
|
+
const deadline = Date.now() + timeoutMs;
|
|
30
|
+
while (Date.now() < deadline && !hasExited(child)) {
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch(`http://127.0.0.1:${port}${basePath}/api/ping`);
|
|
33
|
+
if (response.ok) {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
// Not listening yet.
|
|
38
|
+
}
|
|
39
|
+
await wait(intervalMs);
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default waitForServer;
|
|
@@ -22,7 +22,7 @@ import { writeFileIfChanged } from '@lowdefy/node-utils';
|
|
|
22
22
|
// process - a separate process from this manager, sharing only the build
|
|
23
23
|
// directory - can serve current build errors/warnings to AI coding agents.
|
|
24
24
|
// writeFileIfChanged creates the build directory if it does not exist, which
|
|
25
|
-
// covers
|
|
25
|
+
// covers a first build that fails.
|
|
26
26
|
async function writeBuildStatus({ directories, status, errors, warnings }) {
|
|
27
27
|
const buildStatus = {
|
|
28
28
|
status,
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
const CODE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.jsx', '.ts', '.tsx', '.json']);
|
|
21
|
+
|
|
22
|
+
// Watched packages are resolved to their real paths, which chokidar reports,
|
|
23
|
+
// so the directories compared with them must be too (a config directory
|
|
24
|
+
// reached through a symlink, or macOS's /var -> /private/var).
|
|
25
|
+
function realPath(dir) {
|
|
26
|
+
return fs.existsSync(dir) ? fs.realpathSync(dir) : dir;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function statIfExists(filePath) {
|
|
30
|
+
try {
|
|
31
|
+
return fs.statSync(filePath);
|
|
32
|
+
} catch {
|
|
33
|
+
// Already gone (an unlink): a removed directory's files are reported on their own.
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isInside({ filePath, dir }) {
|
|
39
|
+
const relative = path.relative(dir, filePath);
|
|
40
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Only plugin code (and package.json, or a type list kept in JSON) can change
|
|
44
|
+
// what the config build reads from a plugin. Everything else in a watched
|
|
45
|
+
// package is ignored by path, without knowing who wrote it: a plugin package
|
|
46
|
+
// that is also the config directory has its YAML and Markdown watched by the
|
|
47
|
+
// config watcher already, and its ref resolvers write public/ on every build,
|
|
48
|
+
// which would otherwise trigger the next build, forever. The build, staging
|
|
49
|
+
// and server directories hold the build's own output.
|
|
50
|
+
function createPluginSourceIgnore({ directories }) {
|
|
51
|
+
const ignoredDirs = [
|
|
52
|
+
path.join(directories.config, 'public'),
|
|
53
|
+
directories.build,
|
|
54
|
+
directories.buildStaging,
|
|
55
|
+
directories.server,
|
|
56
|
+
]
|
|
57
|
+
.filter(Boolean)
|
|
58
|
+
.map(realPath);
|
|
59
|
+
return function isIgnored(filePath, stats) {
|
|
60
|
+
if (filePath.split(path.sep).includes('node_modules')) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (ignoredDirs.some((dir) => isInside({ filePath, dir }))) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
const extension = path.extname(filePath);
|
|
67
|
+
if (CODE_EXTENSIONS.has(extension)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
// chokidar asks about directories too, often without their stats, and a
|
|
71
|
+
// directory must not be ignored, or nothing under it is watched.
|
|
72
|
+
const pathStats = stats ?? statIfExists(filePath);
|
|
73
|
+
return pathStats?.isDirectory() !== true;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export default createPluginSourceIgnore;
|
|
@@ -21,12 +21,19 @@ function envWatcher(context) {
|
|
|
21
21
|
const callback = async () => {
|
|
22
22
|
context.logger.warn('.env file changed.');
|
|
23
23
|
context.readDotEnv();
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
// The server reads the environment only when it starts, and a later
|
|
25
|
+
// successful config build does not restart it, so it restarts with the
|
|
26
|
+
// new environment even when this build fails.
|
|
27
|
+
try {
|
|
28
|
+
await context.lowdefyBuild();
|
|
29
|
+
} finally {
|
|
30
|
+
await context.syncServer({ restart: true });
|
|
31
|
+
}
|
|
26
32
|
};
|
|
27
33
|
return setupWatcher({
|
|
28
34
|
callback,
|
|
29
35
|
context,
|
|
36
|
+
onBusy: context.buildActivity.setBusy,
|
|
30
37
|
watchDotfiles: true,
|
|
31
38
|
watchPaths: [path.join(context.directories.config, '.env')],
|
|
32
39
|
});
|