@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,79 @@
|
|
|
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
|
+
import createPluginSourceIgnore from './createPluginSourceIgnore.mjs';
|
|
20
|
+
import listServerSidePackages from './listServerSidePackages.mjs';
|
|
21
|
+
import readPluginDefinitions from '../utils/readPluginDefinitions.mjs';
|
|
22
|
+
import selectLocalPluginPackages from './selectLocalPluginPackages.mjs';
|
|
23
|
+
import setupWatcher from '../utils/setupWatcher.mjs';
|
|
24
|
+
|
|
25
|
+
function isInside({ filePath, dir }) {
|
|
26
|
+
const relative = path.relative(dir, filePath);
|
|
27
|
+
return !relative.startsWith('..') && !path.isAbsolute(relative);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// A local plugin's code can change while the dev server runs, and nothing
|
|
31
|
+
// else watches it when it lives outside the config directory. The config
|
|
32
|
+
// build reads each plugin's type list, so any change rebuilds - which also
|
|
33
|
+
// clears a build a broken plugin failed. The server process caches the
|
|
34
|
+
// modules of server-side types (requests, connections, operators, ...), so a
|
|
35
|
+
// change to a plugin that has them also restarts the server; client code
|
|
36
|
+
// is Vite's to hot-replace. Only code files anywhere in the package are
|
|
37
|
+
// watched: a plugin may be imported from its sources or from a build output
|
|
38
|
+
// its own watcher writes, but non-code files (public/, YAML, Markdown) are
|
|
39
|
+
// never plugin code, and a package that is also the config directory writes
|
|
40
|
+
// some of them on every build (see createPluginSourceIgnore).
|
|
41
|
+
async function pluginSourceWatcher(context) {
|
|
42
|
+
const plugins = await readPluginDefinitions({ directories: context.directories });
|
|
43
|
+
const packages = selectLocalPluginPackages({
|
|
44
|
+
directories: context.directories,
|
|
45
|
+
packageNames: plugins.map((plugin) => plugin.name),
|
|
46
|
+
});
|
|
47
|
+
if (packages.length === 0) {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const callback = async (filePaths) => {
|
|
52
|
+
const changedFiles = filePaths.flat();
|
|
53
|
+
const customTypesMap = JSON.parse(
|
|
54
|
+
fs.readFileSync(path.join(context.directories.build, 'customTypesMap.json'), 'utf8')
|
|
55
|
+
);
|
|
56
|
+
const serverSide = listServerSidePackages({ customTypesMap });
|
|
57
|
+
const restart = packages.some(
|
|
58
|
+
({ package: packageName, dir }) =>
|
|
59
|
+
serverSide.has(packageName) && changedFiles.some((filePath) => isInside({ filePath, dir }))
|
|
60
|
+
);
|
|
61
|
+
context.logger.info({ spin: 'start' }, 'Local plugin source changed, rebuilding.');
|
|
62
|
+
try {
|
|
63
|
+
await context.lowdefyBuild();
|
|
64
|
+
} finally {
|
|
65
|
+
await context.syncServer({ restart });
|
|
66
|
+
await context.reloadClients();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
return setupWatcher({
|
|
71
|
+
callback,
|
|
72
|
+
context,
|
|
73
|
+
onBusy: context.buildActivity.setBusy,
|
|
74
|
+
ignorePaths: [createPluginSourceIgnore({ directories: context.directories })],
|
|
75
|
+
watchPaths: packages.map(({ dir }) => dir),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default pluginSourceWatcher;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import os from 'os';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { jest } from '@jest/globals';
|
|
21
|
+
|
|
22
|
+
import flushFsEvents from '../../test-utils/flushFsEvents.mjs';
|
|
23
|
+
import waitFor from '../../test-utils/waitFor.mjs';
|
|
24
|
+
|
|
25
|
+
const { default: pluginSourceWatcher } = await import('./pluginSourceWatcher.mjs');
|
|
26
|
+
|
|
27
|
+
// macOS can hold file events back for tens of seconds on a busy machine.
|
|
28
|
+
jest.setTimeout(180000);
|
|
29
|
+
|
|
30
|
+
let root;
|
|
31
|
+
let context;
|
|
32
|
+
let watcher;
|
|
33
|
+
|
|
34
|
+
function writeCustomTypesMap(customTypesMap) {
|
|
35
|
+
fs.writeFileSync(
|
|
36
|
+
path.join(context.directories.build, 'customTypesMap.json'),
|
|
37
|
+
JSON.stringify(customTypesMap)
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function writeLowdefyYaml(pluginNames) {
|
|
42
|
+
fs.writeFileSync(
|
|
43
|
+
path.join(context.directories.config, 'lowdefy.yaml'),
|
|
44
|
+
`lowdefy: local\nplugins:\n${pluginNames
|
|
45
|
+
.map((name) => ` - name: '${name}'\n version: 'workspace:*'\n`)
|
|
46
|
+
.join('')}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Otherwise the fixture writes can reach the watcher as edits and rebuild
|
|
51
|
+
// before the edit under test.
|
|
52
|
+
async function startWatcher() {
|
|
53
|
+
await flushFsEvents();
|
|
54
|
+
watcher = await pluginSourceWatcher(context);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function addLinkedPackage(name) {
|
|
58
|
+
const dir = path.join(root, 'plugins', name);
|
|
59
|
+
fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
|
|
60
|
+
fs.mkdirSync(path.join(dir, 'dist'), { recursive: true });
|
|
61
|
+
const linked = path.join(context.directories.server, 'node_modules', name);
|
|
62
|
+
fs.mkdirSync(path.dirname(linked), { recursive: true });
|
|
63
|
+
fs.symlinkSync(dir, linked, 'dir');
|
|
64
|
+
return dir;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
beforeEach(() => {
|
|
68
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-plugin-watcher-test-'));
|
|
69
|
+
const config = path.join(root, 'app');
|
|
70
|
+
const server = path.join(config, '.lowdefy', 'dev');
|
|
71
|
+
fs.mkdirSync(path.join(server, 'node_modules'), { recursive: true });
|
|
72
|
+
fs.mkdirSync(path.join(server, 'build'), { recursive: true });
|
|
73
|
+
context = {
|
|
74
|
+
buildActivity: { setBusy: jest.fn() },
|
|
75
|
+
directories: { build: path.join(server, 'build'), config, server },
|
|
76
|
+
logger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() },
|
|
77
|
+
lowdefyBuild: jest.fn(async () => {}),
|
|
78
|
+
reloadClients: jest.fn(async () => {}),
|
|
79
|
+
syncServer: jest.fn(async () => {}),
|
|
80
|
+
};
|
|
81
|
+
writeCustomTypesMap({});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
afterEach(async () => {
|
|
85
|
+
if (watcher) {
|
|
86
|
+
await watcher.close();
|
|
87
|
+
watcher = undefined;
|
|
88
|
+
}
|
|
89
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('pluginSourceWatcher watches nothing when lowdefy.yaml lists no local plugin', async () => {
|
|
93
|
+
writeLowdefyYaml(['@lowdefy/blocks-basic']);
|
|
94
|
+
|
|
95
|
+
await expect(pluginSourceWatcher(context)).resolves.toBeUndefined();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test.each([
|
|
99
|
+
['a server-side plugin rebuilds and restarts the server', 'requests', 'src/find.js', true],
|
|
100
|
+
['a block-only plugin rebuilds without a restart', 'blocks', 'src/Fancy.js', false],
|
|
101
|
+
['a plugin imported from its build output rebuilds', 'blocks', 'dist/Fancy.js', false],
|
|
102
|
+
])('editing %s', async (_, kind, file, restart) => {
|
|
103
|
+
const dir = addLinkedPackage('@app/plugin');
|
|
104
|
+
fs.writeFileSync(path.join(dir, file), 'export default 1;');
|
|
105
|
+
writeLowdefyYaml(['@app/plugin']);
|
|
106
|
+
writeCustomTypesMap({ [kind]: { Type: { package: '@app/plugin', version: '1.0.0' } } });
|
|
107
|
+
|
|
108
|
+
await startWatcher();
|
|
109
|
+
fs.writeFileSync(path.join(dir, file), 'export default 2;');
|
|
110
|
+
await waitFor(() => context.reloadClients.mock.calls.length > 0, {
|
|
111
|
+
description: 'clients to reload',
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
expect(context.lowdefyBuild).toHaveBeenCalledTimes(1);
|
|
115
|
+
expect(context.syncServer).toHaveBeenCalledWith({ restart });
|
|
116
|
+
expect(context.buildActivity.setBusy.mock.calls[0]).toEqual([true]);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('a fix to a plugin whose edit failed the build rebuilds again', async () => {
|
|
120
|
+
const dir = addLinkedPackage('@app/plugin');
|
|
121
|
+
fs.writeFileSync(path.join(dir, 'src', 'types.js'), 'export default {};');
|
|
122
|
+
writeLowdefyYaml(['@app/plugin']);
|
|
123
|
+
context.lowdefyBuild.mockRejectedValueOnce(new Error('Failed to import plugin "@app/plugin".'));
|
|
124
|
+
|
|
125
|
+
await startWatcher();
|
|
126
|
+
fs.writeFileSync(path.join(dir, 'src', 'types.js'), 'export default {;');
|
|
127
|
+
await waitFor(() => context.syncServer.mock.calls.length === 1, {
|
|
128
|
+
description: 'the failed build to sync the server',
|
|
129
|
+
});
|
|
130
|
+
fs.writeFileSync(path.join(dir, 'src', 'types.js'), 'export default { blocks: [] };');
|
|
131
|
+
await waitFor(() => context.syncServer.mock.calls.length === 2, {
|
|
132
|
+
description: 'the fixed build to sync the server',
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
expect(context.lowdefyBuild).toHaveBeenCalledTimes(2);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
function linkConfigAsPackage(name) {
|
|
139
|
+
const dir = context.directories.config;
|
|
140
|
+
fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
|
|
141
|
+
fs.mkdirSync(path.join(dir, 'dist'), { recursive: true });
|
|
142
|
+
fs.mkdirSync(path.join(dir, 'public', 'md'), { recursive: true });
|
|
143
|
+
fs.mkdirSync(path.join(dir, 'pages'), { recursive: true });
|
|
144
|
+
const linked = path.join(context.directories.server, 'node_modules', name);
|
|
145
|
+
fs.mkdirSync(path.dirname(linked), { recursive: true });
|
|
146
|
+
fs.symlinkSync(dir, linked, 'dir');
|
|
147
|
+
return dir;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Gives chokidar time to report an event the watcher should ignore, then
|
|
151
|
+
// checks no build ran: a build would have started within the batch delay.
|
|
152
|
+
async function expectNoRebuild() {
|
|
153
|
+
await new Promise((resolve) => setTimeout(resolve, 1200));
|
|
154
|
+
expect(context.lowdefyBuild).not.toHaveBeenCalled();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
describe('a plugin package that is also the config directory', () => {
|
|
158
|
+
// Only non-code files are written before the watcher starts: macOS can
|
|
159
|
+
// report a write made just before the watch began, which for a code file
|
|
160
|
+
// would rebuild and spoil a test that expects none.
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
const dir = linkConfigAsPackage('@app/docs');
|
|
163
|
+
fs.writeFileSync(path.join(dir, 'public', 'llms.txt'), 'one');
|
|
164
|
+
fs.writeFileSync(path.join(dir, 'public', 'search-index.json'), '[]');
|
|
165
|
+
fs.writeFileSync(path.join(dir, 'public', 'md', 'page.md'), 'one');
|
|
166
|
+
fs.writeFileSync(path.join(dir, 'pages', 'home.yaml'), 'id: home');
|
|
167
|
+
fs.writeFileSync(path.join(dir, 'pages', 'home.md'), 'one');
|
|
168
|
+
writeLowdefyYaml(['@app/docs']);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test.each([['public/llms.txt'], ['public/search-index.json'], ['public/md/page.md']])(
|
|
172
|
+
'writing %s does not rebuild',
|
|
173
|
+
async (file) => {
|
|
174
|
+
await startWatcher();
|
|
175
|
+
fs.writeFileSync(path.join(context.directories.config, file), 'two');
|
|
176
|
+
await expectNoRebuild();
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
test.each([['pages/home.yaml'], ['pages/home.md']])(
|
|
181
|
+
'editing %s does not rebuild',
|
|
182
|
+
async (file) => {
|
|
183
|
+
await startWatcher();
|
|
184
|
+
fs.writeFileSync(path.join(context.directories.config, file), 'two');
|
|
185
|
+
await expectNoRebuild();
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
test('writing into the build directory does not rebuild', async () => {
|
|
190
|
+
await startWatcher();
|
|
191
|
+
fs.writeFileSync(path.join(context.directories.build, 'keyMap.json'), '{}');
|
|
192
|
+
await expectNoRebuild();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test.each([['src/types.js'], ['dist/types.js']])('editing %s rebuilds', async (file) => {
|
|
196
|
+
await startWatcher();
|
|
197
|
+
fs.writeFileSync(path.join(context.directories.config, file), 'export default 2;');
|
|
198
|
+
await waitFor(() => context.reloadClients.mock.calls.length > 0);
|
|
199
|
+
expect(context.lowdefyBuild).toHaveBeenCalledTimes(1);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test.each([
|
|
204
|
+
['a .js file rebuilds', 'src/other.js', true],
|
|
205
|
+
['a .json file rebuilds', 'package.json', true],
|
|
206
|
+
['a .md file does not rebuild', 'README.md', false],
|
|
207
|
+
['a .yaml file does not rebuild', 'src/config.yaml', false],
|
|
208
|
+
['a file with no extension does not rebuild', 'LICENSE', false],
|
|
209
|
+
])('in a plugin outside the config directory, %s', async (_, file, rebuilds) => {
|
|
210
|
+
const dir = addLinkedPackage('@app/plugin');
|
|
211
|
+
fs.writeFileSync(path.join(dir, file), 'one');
|
|
212
|
+
writeLowdefyYaml(['@app/plugin']);
|
|
213
|
+
|
|
214
|
+
await startWatcher();
|
|
215
|
+
fs.writeFileSync(path.join(dir, file), 'two');
|
|
216
|
+
if (rebuilds) {
|
|
217
|
+
await waitFor(() => context.reloadClients.mock.calls.length > 0);
|
|
218
|
+
expect(context.lowdefyBuild).toHaveBeenCalledTimes(1);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
await expectNoRebuild();
|
|
222
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
import { type } from '@lowdefy/helpers';
|
|
20
|
+
import setupWatcher from '../utils/setupWatcher.mjs';
|
|
21
|
+
|
|
22
|
+
function readReason(filePath) {
|
|
23
|
+
try {
|
|
24
|
+
const { reason } = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
25
|
+
return type.isString(reason) && reason !== '' ? reason : 'no reason given';
|
|
26
|
+
} catch {
|
|
27
|
+
// The app may still be writing the file, or it was already consumed.
|
|
28
|
+
return 'no reason given';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The dev tools (POST /lowdefy-docs/restart, lowdefy_restart) run inside the
|
|
33
|
+
// app process, which has no channel to the manager except the build directory.
|
|
34
|
+
// They write build/.restart; the manager consumes it here.
|
|
35
|
+
function restartRequestWatcher(context) {
|
|
36
|
+
const sentinelPath = path.join(context.directories.build, '.restart');
|
|
37
|
+
|
|
38
|
+
const callback = async () => {
|
|
39
|
+
if (!fs.existsSync(sentinelPath)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const reason = readReason(sentinelPath);
|
|
43
|
+
fs.rmSync(sentinelPath, { force: true });
|
|
44
|
+
context.logger.info({ spin: 'start' }, `Restart requested by the dev tools: ${reason}.`);
|
|
45
|
+
// The config build reads the plugins' type lists afresh, so a block,
|
|
46
|
+
// request or operator type added to a plugin since the last build is
|
|
47
|
+
// defined once the server is back. The server restarts even when the
|
|
48
|
+
// build fails.
|
|
49
|
+
try {
|
|
50
|
+
await context.lowdefyBuild();
|
|
51
|
+
} finally {
|
|
52
|
+
await context.syncServer({ restart: true });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// The sentinel does not exist until the first request, so watch the build
|
|
57
|
+
// directory (non-recursively) and react only to the sentinel itself.
|
|
58
|
+
return setupWatcher({
|
|
59
|
+
callback,
|
|
60
|
+
context,
|
|
61
|
+
onBusy: context.buildActivity.setBusy,
|
|
62
|
+
watchDotfiles: true,
|
|
63
|
+
watchPaths: [context.directories.build],
|
|
64
|
+
ignorePaths: [
|
|
65
|
+
(filePath) => filePath !== context.directories.build && filePath !== sentinelPath,
|
|
66
|
+
],
|
|
67
|
+
delay: 100,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default restartRequestWatcher;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import fs from 'fs';
|
|
18
|
+
import os from 'os';
|
|
19
|
+
import path from 'path';
|
|
20
|
+
import { jest } from '@jest/globals';
|
|
21
|
+
|
|
22
|
+
import flushFsEvents from '../../test-utils/flushFsEvents.mjs';
|
|
23
|
+
import waitFor from '../../test-utils/waitFor.mjs';
|
|
24
|
+
|
|
25
|
+
const { default: restartRequestWatcher } = await import('./restartRequestWatcher.mjs');
|
|
26
|
+
|
|
27
|
+
// macOS can hold file events back for tens of seconds on a busy machine.
|
|
28
|
+
jest.setTimeout(180000);
|
|
29
|
+
|
|
30
|
+
let fixtureDir;
|
|
31
|
+
let context;
|
|
32
|
+
let watcher;
|
|
33
|
+
|
|
34
|
+
beforeEach(async () => {
|
|
35
|
+
fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-restart-watcher-test-'));
|
|
36
|
+
fs.mkdirSync(path.join(fixtureDir, 'build'), { recursive: true });
|
|
37
|
+
await flushFsEvents();
|
|
38
|
+
context = {
|
|
39
|
+
directories: { build: path.join(fixtureDir, 'build') },
|
|
40
|
+
logger: { info: jest.fn(), warn: jest.fn(), error: jest.fn() },
|
|
41
|
+
buildActivity: { setBusy: jest.fn() },
|
|
42
|
+
lowdefyBuild: jest.fn(async () => {}),
|
|
43
|
+
syncServer: jest.fn(async () => {}),
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
afterEach(async () => {
|
|
48
|
+
if (watcher) {
|
|
49
|
+
await watcher.close();
|
|
50
|
+
watcher = undefined;
|
|
51
|
+
}
|
|
52
|
+
fs.rmSync(fixtureDir, { recursive: true, force: true });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('writing build/.restart restarts the server once and removes the sentinel', async () => {
|
|
56
|
+
watcher = await restartRequestWatcher(context);
|
|
57
|
+
const sentinelPath = path.join(fixtureDir, 'build', '.restart');
|
|
58
|
+
|
|
59
|
+
fs.writeFileSync(
|
|
60
|
+
sentinelPath,
|
|
61
|
+
JSON.stringify({ requestedAt: new Date().toISOString(), reason: 'Edited a request plugin' })
|
|
62
|
+
);
|
|
63
|
+
await waitFor(() => context.syncServer.mock.calls.length > 0, {
|
|
64
|
+
description: 'the server restart',
|
|
65
|
+
});
|
|
66
|
+
expect(fs.existsSync(sentinelPath)).toBe(false);
|
|
67
|
+
|
|
68
|
+
// Removing the sentinel is itself a file event. A second request, seen
|
|
69
|
+
// after it, fences it: any restart the removal caused lands before the
|
|
70
|
+
// second request's.
|
|
71
|
+
fs.writeFileSync(sentinelPath, JSON.stringify({ reason: 'Fence' }));
|
|
72
|
+
await waitFor(() => context.syncServer.mock.calls.length > 1, {
|
|
73
|
+
description: 'the second restart',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(context.syncServer).toHaveBeenCalledTimes(2);
|
|
77
|
+
expect(
|
|
78
|
+
context.logger.info.mock.calls.filter(([, message]) => message.startsWith('Restart requested'))
|
|
79
|
+
).toEqual([
|
|
80
|
+
[{ spin: 'start' }, 'Restart requested by the dev tools: Edited a request plugin.'],
|
|
81
|
+
[{ spin: 'start' }, 'Restart requested by the dev tools: Fence.'],
|
|
82
|
+
]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('a restart request rebuilds the config before restarting the server', async () => {
|
|
86
|
+
watcher = await restartRequestWatcher(context);
|
|
87
|
+
const sentinelPath = path.join(fixtureDir, 'build', '.restart');
|
|
88
|
+
|
|
89
|
+
fs.writeFileSync(sentinelPath, JSON.stringify({ reason: 'Added a block type' }));
|
|
90
|
+
await waitFor(() => context.syncServer.mock.calls.length > 0, {
|
|
91
|
+
description: 'the server restart',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
expect(context.lowdefyBuild).toHaveBeenCalledTimes(1);
|
|
95
|
+
expect(context.lowdefyBuild.mock.invocationCallOrder[0]).toBeLessThan(
|
|
96
|
+
context.syncServer.mock.invocationCallOrder[0]
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('a restart request restarts the server when the config build fails', async () => {
|
|
101
|
+
context.lowdefyBuild.mockRejectedValue(new Error('Build failed with 1 error(s).'));
|
|
102
|
+
watcher = await restartRequestWatcher(context);
|
|
103
|
+
const sentinelPath = path.join(fixtureDir, 'build', '.restart');
|
|
104
|
+
|
|
105
|
+
fs.writeFileSync(sentinelPath, JSON.stringify({ reason: 'Added a block type' }));
|
|
106
|
+
await waitFor(() => context.syncServer.mock.calls.length > 0, {
|
|
107
|
+
description: 'the server restart',
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(context.syncServer).toHaveBeenCalledTimes(1);
|
|
111
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
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 imports plugins from the server directory; an app in a pnpm
|
|
21
|
+
// workspace also links them into its own node_modules. A published package
|
|
22
|
+
// lives inside node_modules and cannot change under a running dev server;
|
|
23
|
+
// only a linked local plugin resolves outside it.
|
|
24
|
+
function resolveLocalPluginDir({ directories, packageName }) {
|
|
25
|
+
for (const base of [directories.server, directories.config]) {
|
|
26
|
+
const linked = path.join(base, 'node_modules', packageName);
|
|
27
|
+
if (fs.existsSync(linked)) {
|
|
28
|
+
const realPath = fs.realpathSync(linked);
|
|
29
|
+
return realPath.split(path.sep).includes('node_modules') ? null : realPath;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The plugins lowdefy.yaml lists that are local packages, whatever kinds of
|
|
36
|
+
// types they hold: a block-only plugin's type list is read by the config
|
|
37
|
+
// build too, so an edit to it - or a fix to one that broke the build - must
|
|
38
|
+
// rebuild.
|
|
39
|
+
function selectLocalPluginPackages({ directories, packageNames }) {
|
|
40
|
+
return [...new Set(packageNames)]
|
|
41
|
+
.map((packageName) => ({
|
|
42
|
+
package: packageName,
|
|
43
|
+
dir: resolveLocalPluginDir({ directories, packageName }),
|
|
44
|
+
}))
|
|
45
|
+
.filter(({ dir }) => dir !== null);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default selectLocalPluginPackages;
|
|
@@ -0,0 +1,73 @@
|
|
|
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 selectLocalPluginPackages from './selectLocalPluginPackages.mjs';
|
|
22
|
+
|
|
23
|
+
let root;
|
|
24
|
+
let directories;
|
|
25
|
+
|
|
26
|
+
function addLinkedPackage({ name, base }) {
|
|
27
|
+
const dir = path.join(root, 'plugins', name);
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
fs.mkdirSync(path.dirname(path.join(base, 'node_modules', name)), { recursive: true });
|
|
30
|
+
fs.symlinkSync(dir, path.join(base, 'node_modules', name), 'dir');
|
|
31
|
+
return fs.realpathSync(dir);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
root = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-plugin-select-test-'));
|
|
36
|
+
directories = {
|
|
37
|
+
config: path.join(root, 'app'),
|
|
38
|
+
server: path.join(root, 'app', '.lowdefy', 'dev'),
|
|
39
|
+
};
|
|
40
|
+
fs.mkdirSync(path.join(directories.server, 'node_modules'), { recursive: true });
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
afterEach(() => {
|
|
44
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('selectLocalPluginPackages selects every linked plugin, block-only plugins included', () => {
|
|
48
|
+
const ui = addLinkedPackage({ name: '@app/ui-plugin', base: directories.server });
|
|
49
|
+
const db = addLinkedPackage({ name: '@app/db-plugin', base: directories.config });
|
|
50
|
+
|
|
51
|
+
expect(
|
|
52
|
+
selectLocalPluginPackages({
|
|
53
|
+
directories,
|
|
54
|
+
packageNames: ['@app/ui-plugin', '@app/db-plugin', '@app/ui-plugin'],
|
|
55
|
+
})
|
|
56
|
+
).toEqual([
|
|
57
|
+
{ package: '@app/ui-plugin', dir: ui },
|
|
58
|
+
{ package: '@app/db-plugin', dir: db },
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('selectLocalPluginPackages skips installed and missing packages', () => {
|
|
63
|
+
fs.mkdirSync(path.join(directories.server, 'node_modules', '@lowdefy', 'connection-mongodb'), {
|
|
64
|
+
recursive: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(
|
|
68
|
+
selectLocalPluginPackages({
|
|
69
|
+
directories,
|
|
70
|
+
packageNames: ['@lowdefy/connection-mongodb', '@app/not-installed'],
|
|
71
|
+
})
|
|
72
|
+
).toEqual([]);
|
|
73
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import path from 'path';
|
|
18
|
+
import setupWatcher from '../utils/setupWatcher.mjs';
|
|
19
|
+
|
|
20
|
+
// A page build in the dev server that finds a plugin package missing adds it
|
|
21
|
+
// to the server's package.json (the page answers "installing"). Installing it,
|
|
22
|
+
// rebuilding and restarting is syncServer's work, as it is after a config
|
|
23
|
+
// build that adds one. Config builds sync the server themselves, so this
|
|
24
|
+
// watcher only has work when the change came from the dev server.
|
|
25
|
+
function serverPackageWatcher(context) {
|
|
26
|
+
const callback = async () => {
|
|
27
|
+
await context.syncServer();
|
|
28
|
+
};
|
|
29
|
+
return setupWatcher({
|
|
30
|
+
callback,
|
|
31
|
+
context,
|
|
32
|
+
onBusy: context.buildActivity.setBusy,
|
|
33
|
+
watchDotfiles: true,
|
|
34
|
+
watchPaths: [path.join(context.directories.server, 'package.json')],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default serverPackageWatcher;
|