@lowdefy/server-dev 6.1.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/App.jsx +52 -24
- package/client/Inspector.jsx +73 -30
- package/client/JourneyObserver.jsx +47 -0
- package/client/Page.jsx +54 -22
- package/client/Recorder.jsx +73 -0
- package/client/Routing.jsx +37 -13
- package/client/clientBuildImports.test.mjs +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 +67 -0
- package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
- package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +679 -0
- package/lib/docs/dataSets/describeDataSetResult.js +25 -0
- package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
- package/lib/docs/dataSets/deserializeFixture.js +34 -0
- package/lib/docs/dataSets/drainSessionWork.js +37 -0
- package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
- package/lib/docs/dataSets/duplicateIndexName.js +24 -0
- package/lib/docs/dataSets/findCollectionCollisions.js +52 -0
- package/lib/docs/dataSets/getDataStore.js +106 -0
- package/lib/docs/dataSets/getDataStore.mongodb.test.mjs +63 -0
- package/lib/docs/dataSets/getDataStore.test.mjs +195 -0
- package/lib/docs/dataSets/groupDataSetByCollection.js +44 -0
- package/lib/docs/dataSets/insertFixture.js +57 -0
- package/lib/docs/dataSets/insertGeneratedDocuments.js +41 -0
- package/lib/docs/dataSets/isDataStoreGone.js +29 -0
- package/lib/docs/dataSets/loadDataSetCollection.js +42 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.js +52 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.test.mjs +59 -0
- package/lib/docs/dataSets/openDataSession.js +119 -0
- package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +343 -0
- package/lib/docs/dataSets/pickDataStorePort.js +58 -0
- package/lib/docs/dataSets/pickDataStorePort.test.mjs +63 -0
- package/lib/docs/dataSets/readConnectionArtifacts.js +46 -0
- package/lib/docs/dataSets/readDataSession.js +41 -0
- package/lib/docs/dataSets/readDataSession.test.mjs +57 -0
- package/lib/docs/dataSets/readDataSet.js +68 -0
- package/lib/docs/dataSets/readDataSet.test.mjs +220 -0
- package/lib/docs/dataSets/registerDataStoreShutdown.js +47 -0
- package/lib/docs/dataSets/removeDataStoreFiles.js +32 -0
- package/lib/docs/dataSets/resolveDataSetCollection.js +46 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.js +106 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.test.mjs +130 -0
- package/lib/docs/dataSets/sweepOrphanDatabases.js +33 -0
- package/lib/docs/dataSets/test/app/lowdefy.yaml +203 -0
- package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
- package/lib/docs/dataSets/test/app/tests/data/shop.yaml +8 -0
- package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
- package/lib/docs/devEventBus.js +136 -0
- package/lib/docs/devEventBus.test.mjs +269 -0
- package/lib/docs/devToolDefinitions.js +529 -0
- package/lib/docs/docSources.js +21 -0
- package/lib/docs/docs.test.mjs +171 -15
- package/lib/docs/docsIndex.test.mjs +377 -0
- package/lib/docs/evalOperator.js +5 -3
- package/lib/docs/evalOperatorHeadless.js +84 -12
- package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
- package/lib/docs/evalOperatorInTab.js +2 -1
- package/lib/docs/findConfig.js +95 -49
- package/lib/docs/fitResponse.js +81 -0
- package/lib/docs/fitResponse.test.mjs +128 -0
- package/lib/docs/formatFeedback.js +26 -5
- package/lib/docs/formatFeedback.test.mjs +48 -0
- package/lib/docs/getAppMap.js +7 -1
- package/lib/docs/getBrowser.js +150 -80
- package/lib/docs/getBrowser.test.mjs +407 -16
- package/lib/docs/getBuildId.js +61 -0
- package/lib/docs/getBuildId.test.mjs +84 -0
- package/lib/docs/getBuildStatus.js +54 -6
- package/lib/docs/getBuildStatus.test.mjs +133 -0
- package/lib/docs/getBuildStatusAfterEdits.js +35 -0
- package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
- package/lib/docs/getCoreDoc.js +9 -1
- package/lib/docs/getDoc.js +100 -0
- package/lib/docs/getDocsIndex.js +152 -0
- package/lib/docs/getHazards.js +115 -0
- package/lib/docs/getHazards.test.mjs +175 -0
- package/lib/docs/getMailOutboxDirectory.js +26 -0
- package/lib/docs/getOverview.js +79 -13
- package/lib/docs/getOverviewBasePath.test.mjs +37 -0
- package/lib/docs/getPageBuildStatus.js +46 -0
- package/lib/docs/getPluginDoc.js +21 -23
- package/lib/docs/getSchema.js +33 -1
- package/lib/docs/getStaleStatus.js +36 -0
- package/lib/docs/getStaleStatus.test.mjs +74 -0
- package/lib/docs/guardJourneyOrigin.js +39 -0
- package/lib/docs/inspectState.js +5 -3
- package/lib/docs/inspectStateFromTab.js +2 -2
- package/lib/docs/inspectStateHeadless.js +89 -27
- package/lib/docs/inspectStateHeadless.test.mjs +20 -1
- package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
- package/lib/docs/installHeadlessShell.js +123 -0
- package/lib/docs/installHeadlessShell.test.mjs +70 -0
- package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
- package/lib/docs/isHeadlessShellIncomplete.js +34 -0
- package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
- package/lib/docs/isPageReady.js +77 -0
- package/lib/docs/isPageReady.test.mjs +235 -0
- package/lib/docs/isRunErrorEntry.js +25 -0
- package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
- package/lib/docs/journeyRunRecording.js +35 -0
- package/lib/docs/journeyRunRecording.test.mjs +39 -0
- package/lib/docs/keepEventStreamAlive.js +68 -0
- package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
- package/lib/docs/launchBrowser.js +83 -0
- package/lib/docs/launchBrowser.test.mjs +175 -0
- package/lib/docs/listModuleDocEntries.js +69 -0
- package/lib/docs/listPackageDocFiles.js +67 -0
- package/lib/docs/listPluginDocEntries.js +111 -0
- package/lib/docs/listPlugins.js +9 -58
- package/lib/docs/listTypes.js +14 -6
- package/lib/docs/loadFrameworkHazards.js +40 -0
- package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
- package/lib/docs/loadState.js +103 -35
- package/lib/docs/makeModuleManifestDoc.js +174 -0
- package/lib/docs/matchExercisedPages.js +35 -0
- package/lib/docs/matchJourneyRoute.js +69 -0
- package/lib/docs/matchJourneyRoute.test.mjs +178 -0
- package/lib/docs/mergeNetworkSnapshots.js +49 -0
- package/lib/docs/nextJourneySequence.js +30 -0
- package/lib/docs/noBrowserError.js +22 -0
- package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
- package/lib/docs/observe/claimExpectedErrors.js +67 -0
- package/lib/docs/observe/createJourneyAppErrors.js +236 -0
- package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
- package/lib/docs/observe/evaluateInvariants.js +183 -0
- package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
- package/lib/docs/observe/findingKey.js +29 -0
- package/lib/docs/observe/hasNoEffect.js +31 -0
- package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
- package/lib/docs/observe/installStepObserver.js +72 -0
- package/lib/docs/observe/isAppApiUrl.js +33 -0
- package/lib/docs/observe/parseAppApiUrl.js +37 -0
- package/lib/docs/observe/readStepObserver.js +42 -0
- package/lib/docs/observe/relativeSource.js +29 -0
- package/lib/docs/observe/resolveConfigKeySource.js +33 -0
- package/lib/docs/observe/takeJourneyEvents.js +36 -0
- package/lib/docs/observe/toAppErrorFailure.js +61 -0
- package/lib/docs/observe/usesSearchStage.js +45 -0
- package/lib/docs/observe/waitForClientErrorReports.js +31 -0
- package/lib/docs/observe/watchJourneyContext.js +75 -0
- package/lib/docs/observe/withAppErrors.js +31 -0
- package/lib/docs/observe/withAppErrors.test.mjs +101 -0
- package/lib/docs/openJourney.js +94 -0
- package/lib/docs/openJourneyEmail.js +41 -0
- package/lib/docs/openJourneyEmail.test.mjs +187 -0
- package/lib/docs/pageBlocks.js +47 -0
- package/lib/docs/pageBuildStatus.test.mjs +103 -0
- package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
- package/lib/docs/rails/authEngineActions.js +53 -0
- package/lib/docs/rails/authEngineActions.test.mjs +69 -0
- package/lib/docs/rails/authEngineMethods.js +52 -0
- package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
- package/lib/docs/rails/checkAuthActionsRan.js +66 -0
- package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
- package/lib/docs/rails/checkClickRails.js +56 -0
- package/lib/docs/rails/createDataSetRails.js +82 -0
- package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
- package/lib/docs/rails/findExternalBlocks.js +147 -0
- package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
- package/lib/docs/rails/installAuthActionRecorder.js +56 -0
- package/lib/docs/rails/readAuthActionRecorder.js +32 -0
- package/lib/docs/rails/readBuiltArtifact.js +30 -0
- package/lib/docs/readConnectionPlugins.js +26 -0
- package/lib/docs/readDevAuthMode.js +32 -0
- package/lib/docs/readDocEntry.js +40 -0
- package/lib/docs/readDocTitle.js +26 -0
- package/lib/docs/readJourneyEmailMatch.js +57 -0
- package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
- package/lib/docs/readJourneySession.js +56 -0
- package/lib/docs/readMailOutbox.js +37 -0
- package/lib/docs/readPagePath.js +27 -0
- package/lib/docs/readPluginPackages.js +99 -0
- package/lib/docs/readProxyBuildWait.js +38 -0
- package/lib/docs/readRoutineSteps.js +45 -0
- package/lib/docs/registerShownTextEngine.js +118 -0
- package/lib/docs/requestRestart.js +30 -0
- package/lib/docs/requestRestart.test.mjs +49 -0
- package/lib/docs/resolvePageInstance.js +46 -0
- package/lib/docs/resolvePageInstance.test.mjs +81 -0
- package/lib/docs/resolveSource.js +11 -7
- package/lib/docs/resolveSource.test.mjs +1 -1
- package/lib/docs/resolveToolCaller.js +62 -0
- package/lib/docs/reviewPage.js +51 -0
- package/lib/docs/reviewPageBuilds.js +56 -0
- package/lib/docs/reviewPageBuilds.test.mjs +207 -0
- package/lib/docs/runCheck.js +75 -0
- package/lib/docs/runCheck.test.mjs +91 -0
- package/lib/docs/runCheckChild.js +49 -0
- package/lib/docs/runEndpoint.js +185 -0
- package/lib/docs/runEndpoint.test.mjs +509 -0
- package/lib/docs/runErrorBuffers.js +99 -0
- package/lib/docs/runErrorBuffers.test.mjs +95 -0
- package/lib/docs/runJourney.js +347 -0
- package/lib/docs/runJourney.test.mjs +2383 -0
- package/lib/docs/runJourneyAsUsers.js +44 -0
- package/lib/docs/runJourneyDataSet.test.mjs +274 -0
- package/lib/docs/runJourneySteps.js +1320 -0
- package/lib/docs/runRequest.js +100 -59
- package/lib/docs/runRequest.test.mjs +190 -0
- package/lib/docs/saveAnnotatedScreenshot.js +1 -1
- package/lib/docs/scaffoldPage.js +3 -1
- package/lib/docs/screenshotPage.js +163 -11
- package/lib/docs/screenshotPage.test.mjs +263 -1
- package/lib/docs/screenshotPageClip.test.mjs +4 -0
- package/lib/docs/searchDocs.js +82 -30
- package/lib/docs/searchDocs.test.mjs +100 -0
- package/lib/docs/searchIcons.js +90 -0
- package/lib/docs/searchIcons.test.mjs +127 -0
- package/lib/docs/selectExercised.js +39 -0
- package/lib/docs/selectFinalState.js +66 -0
- package/lib/docs/selectFinalState.test.mjs +51 -0
- package/lib/docs/serverErrorStore.js +54 -0
- package/lib/docs/serverErrorStore.test.mjs +50 -0
- package/lib/docs/setupTestFixtures.mjs +133 -39
- package/lib/docs/shownTextSelector.js +28 -0
- package/lib/docs/snapshotState.js +13 -5
- package/lib/docs/splitSearchTerms.js +62 -0
- package/lib/docs/splitSearchTerms.test.mjs +50 -0
- package/lib/docs/stateCheckpoints.test.mjs +77 -0
- package/lib/docs/tabAvailable.js +6 -5
- package/lib/docs/tabChannel.js +127 -21
- package/lib/docs/tabChannel.test.mjs +187 -29
- package/lib/docs/unsettledPageNote.js +24 -0
- package/lib/docs/validateJourneyMail.js +36 -0
- package/lib/docs/validateJourneyTimeout.js +38 -0
- package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
- package/lib/docs/validateRecordingParam.js +42 -0
- package/lib/docs/validateStateSelection.js +33 -0
- package/lib/docs/validateStateSelection.test.mjs +33 -0
- package/lib/docs/validateViewport.js +51 -0
- package/lib/docs/validateViewport.test.mjs +54 -0
- package/lib/docs/waitForBuild.js +50 -0
- package/lib/docs/waitForBuild.test.mjs +75 -0
- package/lib/docs/waitForJourneyEmail.js +77 -0
- package/lib/docs/withBrowserSlot.js +45 -0
- package/lib/docs/withBrowserSlot.test.mjs +75 -0
- package/lib/docs/withDataSession.js +53 -0
- package/lib/server/applyDataSetRedirect.js +83 -0
- package/lib/server/applyDataSetRedirect.test.mjs +340 -0
- package/lib/server/applyDataSetRedirectTenantReach.test.mjs +204 -0
- package/lib/server/applyDataSetRedirectWalled.test.mjs +131 -0
- package/lib/server/auth/createSystemContext.js +73 -0
- package/lib/server/auth/{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 +328 -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 +112 -0
- package/lib/server/mutants/applyMutant.js +39 -0
- package/lib/server/mutants/artifactPatterns.js +25 -0
- package/lib/server/mutants/buildAnchor.js +45 -0
- package/lib/server/mutants/collectSharedIds.js +41 -0
- package/lib/server/mutants/configPathSegments.js +38 -0
- package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
- package/lib/server/mutants/describeEvent.js +22 -0
- package/lib/server/mutants/enumerateArtifact.js +56 -0
- package/lib/server/mutants/findNodeByKey.js +45 -0
- package/lib/server/mutants/findNodeByPointer.js +49 -0
- package/lib/server/mutants/getArtifactKind.js +32 -0
- package/lib/server/mutants/groupMutantCopies.js +56 -0
- package/lib/server/mutants/hashArtifact.js +25 -0
- package/lib/server/mutants/hashMutantTarget.js +34 -0
- package/lib/server/mutants/listMutants.js +122 -0
- package/lib/server/mutants/listMutants.test.mjs +502 -0
- package/lib/server/mutants/mutantId.js +30 -0
- package/lib/server/mutants/mutantRuns.js +57 -0
- package/lib/server/mutants/mutantRuns.test.mjs +136 -0
- package/lib/server/mutants/operators/dropAction.js +47 -0
- package/lib/server/mutants/operators/dropBlock.js +44 -0
- package/lib/server/mutants/operators/dropPayload.js +52 -0
- package/lib/server/mutants/operators/dropStep.js +44 -0
- package/lib/server/mutants/operators/flipVisible.js +53 -0
- package/lib/server/mutants/operators/index.js +37 -0
- package/lib/server/mutants/operators/retargetLink.js +72 -0
- package/lib/server/mutants/operators/skipValidate.js +77 -0
- package/lib/server/mutants/operators/swapIf.js +112 -0
- package/lib/server/mutants/operators.test.mjs +396 -0
- package/lib/server/mutants/positionFreePath.js +38 -0
- package/lib/server/mutants/removeFromParent.js +28 -0
- package/lib/server/mutants/test/fixtures.mjs +173 -0
- package/lib/server/mutants/test/keyArtifact.mjs +63 -0
- package/lib/server/mutants/validateMutantParam.js +69 -0
- package/lib/server/mutants/validateMutantsBody.js +88 -0
- package/lib/server/mutants/walkArtifact.js +179 -0
- package/lib/server/pageBuildRecords.js +131 -0
- package/lib/server/pageBuildRecords.test.mjs +258 -0
- package/lib/server/pageCache.mjs +22 -7
- package/lib/server/pageCache.test.mjs +22 -7
- package/lib/server/pageRebuilds.test.mjs +543 -0
- package/lib/server/pruneJitMaps.js +36 -0
- package/lib/server/pruneJitMaps.test.mjs +55 -0
- package/lib/server/readInstanceRecord.js +33 -0
- package/lib/server/readInstanceRecord.test.mjs +99 -0
- package/lib/server/readMergedMaps.js +86 -0
- package/lib/server/readMergedMaps.test.mjs +79 -0
- package/lib/server/recording/appendRecords.js +33 -0
- package/lib/server/recording/errorRecordingStamp.js +34 -0
- package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
- package/lib/server/recording/isRecordingEnabled.js +31 -0
- package/lib/server/recording/readRecordingCookie.js +71 -0
- package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
- package/lib/server/recording/recordingCookiePayload.js +33 -0
- package/lib/server/recording/servedBuilds.js +46 -0
- package/lib/server/recording/servedBuilds.test.mjs +42 -0
- package/lib/server/sinkExternalConnections.js +95 -0
- package/lib/server/sinkExternalConnections.test.mjs +135 -0
- package/lib/server/skipStaleMapWrites.js +62 -0
- package/lib/server/skipStaleMapWrites.test.mjs +113 -0
- package/lib/server/startIdleGc.js +67 -0
- package/lib/server/startIdleGc.test.mjs +138 -0
- package/lib/server/testFixtures/importAppCodeTwice.mjs +70 -0
- package/lib/server/trackSessionWork.js +30 -0
- package/lib/vite/createDevServerExclude.js +40 -0
- package/lib/vite/createDevServerExclude.test.mjs +79 -0
- package/manager/getContext.mjs +24 -2
- package/manager/processes/initialBuild.mjs +14 -0
- package/manager/processes/initialBuild.test.mjs +101 -0
- package/manager/processes/lowdefyBuild.mjs +30 -7
- package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +62 -0
- package/manager/processes/lowdefyBuild.test.mjs +136 -1
- package/manager/processes/optimizeDependencies.mjs +59 -0
- package/manager/processes/restartServer.mjs +34 -6
- package/manager/processes/restartServer.test.mjs +162 -0
- package/manager/processes/shutdownServer.mjs +11 -0
- package/manager/processes/shutdownServer.test.mjs +45 -0
- package/manager/processes/startFirstServer.mjs +46 -0
- package/manager/processes/startFirstServer.test.mjs +326 -0
- package/manager/processes/startMailSink.mjs +135 -0
- package/manager/processes/startMailSink.test.mjs +146 -0
- package/manager/processes/startProxy.mjs +129 -12
- package/manager/processes/startProxy.test.mjs +366 -0
- package/manager/processes/startServer.mjs +47 -6
- package/manager/processes/startServer.test.mjs +155 -0
- package/manager/processes/startWatchers.mjs +6 -4
- package/manager/processes/syncServer.mjs +69 -0
- package/manager/processes/syncServer.test.mjs +141 -0
- package/manager/processes/waitForDevServer.mjs +48 -0
- package/manager/processes/waitForDevServer.test.mjs +112 -0
- package/manager/run.mjs +128 -43
- package/manager/utils/BatchChanges.mjs +21 -5
- package/manager/utils/BatchChanges.test.mjs +77 -0
- package/manager/utils/acquireDevInstance.mjs +99 -0
- package/manager/utils/acquireDevInstance.test.mjs +160 -0
- package/manager/utils/createBuildActivity.mjs +67 -0
- package/manager/utils/createBuildActivity.test.mjs +58 -0
- package/manager/utils/createChangeSignal.mjs +39 -0
- package/manager/utils/createChangeSignal.test.mjs +62 -0
- package/manager/utils/createCustomPluginMessagesMap.mjs +23 -33
- package/manager/utils/createCustomPluginTypesMap.mjs +24 -30
- package/manager/utils/createCustomPluginTypesMap.test.mjs +94 -0
- package/manager/utils/createDotPathIgnore.mjs +44 -0
- package/manager/utils/createDotPathIgnore.test.mjs +65 -0
- package/manager/utils/createIdleGc.mjs +51 -0
- package/manager/utils/createIdleGc.test.mjs +77 -0
- package/manager/utils/createRequestActivity.mjs +100 -0
- package/manager/utils/createRequestActivity.test.mjs +121 -0
- package/manager/utils/createServerArtifactTracker.mjs +83 -0
- package/manager/utils/createServerArtifactTracker.test.mjs +78 -0
- package/manager/utils/createServerEnv.mjs +39 -0
- package/manager/utils/filesHaveSameBytes.mjs +41 -0
- package/manager/utils/findBuildFilesOutsideWatch.mjs +59 -0
- package/manager/utils/findBuildFilesOutsideWatch.test.mjs +111 -0
- package/manager/utils/importFresh.mjs +46 -0
- package/manager/utils/importFresh.test.mjs +72 -0
- package/manager/utils/importFreshWorker.mjs +20 -0
- package/manager/utils/killTaggedBrowser.mjs +35 -0
- package/manager/utils/pagePathChanged.mjs +56 -0
- package/manager/utils/pruneRecordings.mjs +85 -0
- package/manager/utils/pruneRecordings.test.mjs +117 -0
- package/manager/utils/publishBuildDirectory.mjs +121 -0
- package/manager/utils/publishBuildDirectory.order.test.mjs +74 -0
- package/manager/utils/publishBuildDirectory.test.mjs +262 -0
- package/manager/utils/readBasePath.mjs +27 -0
- package/manager/utils/readBasePath.test.mjs +43 -0
- package/manager/utils/readBuildStatusWait.mjs +67 -0
- package/manager/utils/readManagerVersion.mjs +30 -0
- package/manager/utils/readManagerVersion.test.mjs +35 -0
- package/manager/utils/readPluginDefinitions.mjs +36 -0
- package/manager/utils/resolveDevAuthUrl.mjs +58 -0
- package/manager/utils/resolveDevAuthUrl.test.mjs +107 -0
- package/manager/utils/resolvePorts.mjs +49 -0
- package/manager/utils/setupWatcher.mjs +19 -11
- package/manager/utils/startRecordingPruner.mjs +45 -0
- package/manager/utils/startRecordingPruner.test.mjs +66 -0
- package/manager/utils/waitForChildExit.mjs +53 -0
- package/manager/utils/waitForChildExit.test.mjs +70 -0
- package/manager/utils/waitForServer.mjs +44 -0
- package/manager/utils/writeBuildStatus.mjs +1 -1
- package/manager/watchers/createPluginSourceIgnore.mjs +77 -0
- package/manager/watchers/envWatcher.mjs +9 -2
- package/manager/watchers/envWatcher.test.mjs +87 -0
- package/manager/watchers/listServerSidePackages.mjs +47 -0
- package/manager/watchers/listServerSidePackages.test.mjs +38 -0
- package/manager/watchers/lowdefyBuildWatcher.mjs +106 -22
- package/manager/watchers/lowdefyBuildWatcher.test.mjs +309 -0
- package/manager/watchers/pluginSourceWatcher.mjs +79 -0
- package/manager/watchers/pluginSourceWatcher.test.mjs +222 -0
- package/manager/watchers/restartRequestWatcher.mjs +71 -0
- package/manager/watchers/restartRequestWatcher.test.mjs +111 -0
- package/manager/watchers/selectLocalPluginPackages.mjs +48 -0
- package/manager/watchers/selectLocalPluginPackages.test.mjs +73 -0
- package/manager/watchers/serverPackageWatcher.mjs +38 -0
- package/package.json +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,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { wait } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
// Whether the manager is processing a change: a watcher batch from its first
|
|
20
|
+
// change until it is handled, a config build, a plugin install, a server
|
|
21
|
+
// restart until the new server answers. lowdefy_build_status({ wait: true })
|
|
22
|
+
// waits on it, so an agent reads the build - and talks to the server - that
|
|
23
|
+
// includes its last edit. onChange mirrors it into the instance record for
|
|
24
|
+
// readers in other processes; the proxy waits on it here, in the manager,
|
|
25
|
+
// because a restart ends every wait running in the dev server.
|
|
26
|
+
function createBuildActivity({ onChange }) {
|
|
27
|
+
let active = 0;
|
|
28
|
+
|
|
29
|
+
function setBusy(busy) {
|
|
30
|
+
active += busy ? 1 : -1;
|
|
31
|
+
onChange(active > 0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function track(task) {
|
|
35
|
+
setBusy(true);
|
|
36
|
+
try {
|
|
37
|
+
return await task();
|
|
38
|
+
} finally {
|
|
39
|
+
setBusy(false);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// An edit reaches a watcher a moment after it is saved, so the wait first
|
|
44
|
+
// gives the watchers graceMs to start on it.
|
|
45
|
+
async function waitForIdle({ graceMs = 1000, timeoutMs = 120000, intervalMs = 50 } = {}) {
|
|
46
|
+
const start = Date.now();
|
|
47
|
+
let sawBuild = false;
|
|
48
|
+
while (Date.now() - start < graceMs) {
|
|
49
|
+
if (active > 0) {
|
|
50
|
+
sawBuild = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
await wait(intervalMs);
|
|
54
|
+
}
|
|
55
|
+
while (active > 0) {
|
|
56
|
+
if (Date.now() - start > timeoutMs) {
|
|
57
|
+
return { settled: false, sawBuild, waitedMs: Date.now() - start };
|
|
58
|
+
}
|
|
59
|
+
await wait(intervalMs);
|
|
60
|
+
}
|
|
61
|
+
return { settled: true, sawBuild, waitedMs: Date.now() - start };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { setBusy, track, waitForIdle };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export default createBuildActivity;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { jest } from '@jest/globals';
|
|
18
|
+
|
|
19
|
+
import createBuildActivity from './createBuildActivity.mjs';
|
|
20
|
+
|
|
21
|
+
test('waitForIdle waits for tracked work to finish and reports that it saw a build', async () => {
|
|
22
|
+
const onChange = jest.fn();
|
|
23
|
+
const activity = createBuildActivity({ onChange });
|
|
24
|
+
let finish;
|
|
25
|
+
const work = activity.track(
|
|
26
|
+
() =>
|
|
27
|
+
new Promise((resolve) => {
|
|
28
|
+
finish = resolve;
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
const waiting = activity.waitForIdle({ graceMs: 100, intervalMs: 10 });
|
|
32
|
+
await new Promise((resolve) => setTimeout(resolve, 150));
|
|
33
|
+
finish('done');
|
|
34
|
+
|
|
35
|
+
await expect(work).resolves.toBe('done');
|
|
36
|
+
await expect(waiting).resolves.toMatchObject({ settled: true, sawBuild: true });
|
|
37
|
+
expect(onChange.mock.calls).toEqual([[true], [false]]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('waitForIdle stays busy while overlapping work remains', async () => {
|
|
41
|
+
const activity = createBuildActivity({ onChange: () => {} });
|
|
42
|
+
activity.setBusy(true);
|
|
43
|
+
activity.setBusy(true);
|
|
44
|
+
activity.setBusy(false);
|
|
45
|
+
|
|
46
|
+
await expect(
|
|
47
|
+
activity.waitForIdle({ graceMs: 10, timeoutMs: 100, intervalMs: 10 })
|
|
48
|
+
).resolves.toMatchObject({ settled: false, sawBuild: true });
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('waitForIdle returns after the grace window when nothing starts', async () => {
|
|
52
|
+
const activity = createBuildActivity({ onChange: () => {} });
|
|
53
|
+
|
|
54
|
+
await expect(activity.waitForIdle({ graceMs: 30, intervalMs: 10 })).resolves.toMatchObject({
|
|
55
|
+
settled: true,
|
|
56
|
+
sawBuild: false,
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
// build/invalidatePages tells the dev server that watched files changed: the
|
|
21
|
+
// server reads it by value, so every write must differ from the one before,
|
|
22
|
+
// even two in one millisecond. The value stays a timestamp, which build status
|
|
23
|
+
// reads as the time of the latest edit.
|
|
24
|
+
function createChangeSignal({ buildDirectory }) {
|
|
25
|
+
const filePath = path.join(buildDirectory, 'invalidatePages');
|
|
26
|
+
let last = 0;
|
|
27
|
+
try {
|
|
28
|
+
last = Number(fs.readFileSync(filePath, 'utf8')) || 0;
|
|
29
|
+
} catch {
|
|
30
|
+
// No change signalled yet.
|
|
31
|
+
}
|
|
32
|
+
return function writeChangeSignal() {
|
|
33
|
+
last = Math.max(Date.now(), last + 1);
|
|
34
|
+
fs.writeFileSync(filePath, String(last));
|
|
35
|
+
return last;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default createChangeSignal;
|
|
@@ -0,0 +1,62 @@
|
|
|
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 createChangeSignal from './createChangeSignal.mjs';
|
|
23
|
+
|
|
24
|
+
function readSignal(buildDirectory) {
|
|
25
|
+
return Number(fs.readFileSync(path.join(buildDirectory, 'invalidatePages'), 'utf8'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
jest.restoreAllMocks();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('each write gives build/invalidatePages a new, larger value, even within one millisecond', () => {
|
|
33
|
+
const buildDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'ldf-change-signal-'));
|
|
34
|
+
jest.spyOn(Date, 'now').mockReturnValue(5000);
|
|
35
|
+
const writeChangeSignal = createChangeSignal({ buildDirectory });
|
|
36
|
+
|
|
37
|
+
expect(writeChangeSignal()).toBe(5000);
|
|
38
|
+
expect(readSignal(buildDirectory)).toBe(5000);
|
|
39
|
+
expect(writeChangeSignal()).toBe(5001);
|
|
40
|
+
expect(readSignal(buildDirectory)).toBe(5001);
|
|
41
|
+
fs.rmSync(buildDirectory, { recursive: true, force: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('a new manager continues above the value the last one wrote', () => {
|
|
45
|
+
const buildDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'ldf-change-signal-'));
|
|
46
|
+
fs.writeFileSync(path.join(buildDirectory, 'invalidatePages'), '9000');
|
|
47
|
+
jest.spyOn(Date, 'now').mockReturnValue(5000);
|
|
48
|
+
|
|
49
|
+
expect(createChangeSignal({ buildDirectory })()).toBe(9001);
|
|
50
|
+
fs.rmSync(buildDirectory, { recursive: true, force: true });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('the value is the current time when the clock is ahead of the last value', () => {
|
|
54
|
+
const buildDirectory = fs.mkdtempSync(path.join(os.tmpdir(), 'ldf-change-signal-'));
|
|
55
|
+
const writeChangeSignal = createChangeSignal({ buildDirectory });
|
|
56
|
+
jest.spyOn(Date, 'now').mockReturnValue(5000);
|
|
57
|
+
writeChangeSignal();
|
|
58
|
+
Date.now.mockReturnValue(7000);
|
|
59
|
+
|
|
60
|
+
expect(writeChangeSignal()).toBe(7000);
|
|
61
|
+
fs.rmSync(buildDirectory, { recursive: true, force: true });
|
|
62
|
+
});
|
|
@@ -16,10 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { createRequire } from 'node:module';
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import { readFile } from '@lowdefy/node-utils';
|
|
22
|
-
import YAML from 'yaml';
|
|
19
|
+
import importFresh from './importFresh.mjs';
|
|
20
|
+
import readPluginDefinitions from './readPluginDefinitions.mjs';
|
|
23
21
|
|
|
24
22
|
const require = createRequire(import.meta.url);
|
|
25
23
|
|
|
@@ -27,38 +25,30 @@ const require = createRequire(import.meta.url);
|
|
|
27
25
|
// process instead of repeating the same skip message on each rebuild.
|
|
28
26
|
const warnedMissingMessages = new Set();
|
|
29
27
|
|
|
30
|
-
async function getPluginDefinitions({ directories }) {
|
|
31
|
-
let lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yaml'));
|
|
32
|
-
if (!lowdefyYaml) {
|
|
33
|
-
lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yml'));
|
|
34
|
-
}
|
|
35
|
-
if (!lowdefyYaml) {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
38
|
-
const lowdefy = YAML.parse(lowdefyYaml);
|
|
39
|
-
return get(lowdefy, 'plugins', { default: [] });
|
|
40
|
-
}
|
|
41
|
-
|
|
42
28
|
async function createCustomPluginMessagesMap({ directories, logger }) {
|
|
43
29
|
const customMessagesMap = {};
|
|
44
|
-
const pluginDefinitions = await
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
30
|
+
const pluginDefinitions = await readPluginDefinitions({ directories });
|
|
31
|
+
|
|
32
|
+
// Each import starts a worker, so the plugins are imported in parallel.
|
|
33
|
+
await Promise.all(
|
|
34
|
+
pluginDefinitions.map(async (plugin) => {
|
|
35
|
+
let messagesPath;
|
|
36
|
+
try {
|
|
37
|
+
messagesPath = require.resolve(`${plugin.name}/messages`);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
// Plugin does not ship a `./messages` export — silently skip.
|
|
40
|
+
if (!warnedMissingMessages.has(plugin.name)) {
|
|
41
|
+
warnedMissingMessages.add(plugin.name);
|
|
42
|
+
logger?.debug?.(`Plugin "${plugin.name}" has no "./messages" export; skipping.`);
|
|
43
|
+
}
|
|
44
|
+
return;
|
|
55
45
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
46
|
+
const messagesModule = await importFresh(messagesPath);
|
|
47
|
+
const messages = messagesModule.default ?? messagesModule;
|
|
48
|
+
if (!messages || typeof messages !== 'object') return;
|
|
49
|
+
customMessagesMap[plugin.name] = messages;
|
|
50
|
+
})
|
|
51
|
+
);
|
|
62
52
|
|
|
63
53
|
return customMessagesMap;
|
|
64
54
|
}
|
|
@@ -16,40 +16,27 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { createRequire } from 'node:module';
|
|
19
|
-
import path from 'path';
|
|
20
|
-
import { get } from '@lowdefy/helpers';
|
|
21
|
-
import { readFile } from '@lowdefy/node-utils';
|
|
22
19
|
import { createPluginTypesMap } from '@lowdefy/build';
|
|
23
|
-
import
|
|
20
|
+
import importFresh from './importFresh.mjs';
|
|
21
|
+
import readPluginDefinitions from './readPluginDefinitions.mjs';
|
|
24
22
|
|
|
25
23
|
const require = createRequire(import.meta.url);
|
|
26
24
|
|
|
27
|
-
async function getPluginDefinitions({ directories }) {
|
|
28
|
-
let lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yaml'));
|
|
29
|
-
if (!lowdefyYaml) {
|
|
30
|
-
lowdefyYaml = await readFile(path.join(directories.config, 'lowdefy.yml'));
|
|
31
|
-
}
|
|
32
|
-
if (!lowdefyYaml) {
|
|
33
|
-
return [];
|
|
34
|
-
}
|
|
35
|
-
const lowdefy = YAML.parse(lowdefyYaml);
|
|
36
|
-
return get(lowdefy, 'plugins', { default: [] });
|
|
37
|
-
}
|
|
38
|
-
|
|
39
25
|
async function createCustomPluginTypesMap({ directories, logger }) {
|
|
40
26
|
const customTypesMap = {
|
|
41
27
|
actions: {},
|
|
42
28
|
agents: {},
|
|
43
29
|
auth: {
|
|
44
30
|
adapters: {},
|
|
45
|
-
callbacks: {},
|
|
46
|
-
events: {},
|
|
47
31
|
providers: {},
|
|
32
|
+
strategies: {},
|
|
48
33
|
},
|
|
49
34
|
blockMetas: {},
|
|
35
|
+
connectionMetas: {},
|
|
50
36
|
blocks: {},
|
|
51
37
|
connections: {},
|
|
52
38
|
icons: {},
|
|
39
|
+
iconSets: {},
|
|
53
40
|
operators: {
|
|
54
41
|
client: {},
|
|
55
42
|
server: {},
|
|
@@ -58,18 +45,25 @@ async function createCustomPluginTypesMap({ directories, logger }) {
|
|
|
58
45
|
websockets: {},
|
|
59
46
|
};
|
|
60
47
|
|
|
61
|
-
const pluginDefinitions = await
|
|
48
|
+
const pluginDefinitions = await readPluginDefinitions({ directories });
|
|
49
|
+
|
|
50
|
+
// Each import starts a worker, so the plugins are imported in parallel and
|
|
51
|
+
// added to the map in the order lowdefy.yaml lists them.
|
|
52
|
+
const pluginTypes = await Promise.all(
|
|
53
|
+
pluginDefinitions.map(async (plugin) => {
|
|
54
|
+
try {
|
|
55
|
+
return await importFresh(require.resolve(`${plugin.name}/types`));
|
|
56
|
+
} catch (e) {
|
|
57
|
+
logger.error(`Failed to import plugin "${plugin.name}".`);
|
|
58
|
+
logger.debug(e);
|
|
59
|
+
logger.info('If the plugin was added while the server was running, restart the server.');
|
|
60
|
+
throw new Error(`Failed to import plugin "${plugin.name}".`);
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
);
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
types = require(`${plugin.name}/types`);
|
|
67
|
-
} catch (e) {
|
|
68
|
-
logger.error(`Failed to import plugin "${plugin.name}".`);
|
|
69
|
-
logger.debug(e);
|
|
70
|
-
logger.info('If the plugin was added while the server was running, restart the server.');
|
|
71
|
-
throw new Error(`Failed to import plugin "${plugin.name}".`);
|
|
72
|
-
}
|
|
65
|
+
pluginDefinitions.forEach((plugin, index) => {
|
|
66
|
+
const types = pluginTypes[index];
|
|
73
67
|
createPluginTypesMap({
|
|
74
68
|
packageTypes: types.default ?? types,
|
|
75
69
|
typesMap: customTypesMap,
|
|
@@ -77,7 +71,7 @@ async function createCustomPluginTypesMap({ directories, logger }) {
|
|
|
77
71
|
version: plugin.version,
|
|
78
72
|
typePrefix: plugin.typePrefix,
|
|
79
73
|
});
|
|
80
|
-
}
|
|
74
|
+
});
|
|
81
75
|
|
|
82
76
|
return customTypesMap;
|
|
83
77
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
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 mockImportFresh = jest.fn();
|
|
20
|
+
jest.unstable_mockModule('./importFresh.mjs', () => ({ default: mockImportFresh }));
|
|
21
|
+
const mockCreatePluginTypesMap = jest.fn();
|
|
22
|
+
jest.unstable_mockModule('@lowdefy/build', () => ({
|
|
23
|
+
createPluginTypesMap: mockCreatePluginTypesMap,
|
|
24
|
+
}));
|
|
25
|
+
// The module under test imports @lowdefy/build, which links against every
|
|
26
|
+
// export it uses, so the real module passes through and only readFile is
|
|
27
|
+
// replaced.
|
|
28
|
+
const realNodeUtils = await import('@lowdefy/node-utils');
|
|
29
|
+
jest.unstable_mockModule('@lowdefy/node-utils', () => ({
|
|
30
|
+
...realNodeUtils,
|
|
31
|
+
readFile: jest.fn(async () => 'plugins:\n - name: plugin-a\n - name: plugin-b\n'),
|
|
32
|
+
}));
|
|
33
|
+
jest.unstable_mockModule('node:module', () => ({
|
|
34
|
+
createRequire: () => ({ resolve: (name) => `/plugins/${name}.js` }),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
const { default: createCustomPluginTypesMap } = await import('./createCustomPluginTypesMap.mjs');
|
|
38
|
+
const { default: createCustomPluginMessagesMap } = await import(
|
|
39
|
+
'./createCustomPluginMessagesMap.mjs'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const logger = { debug: jest.fn(), error: jest.fn(), info: jest.fn() };
|
|
43
|
+
const directories = { config: '/app' };
|
|
44
|
+
|
|
45
|
+
// Holds every import open until release(), so a test can see which imports
|
|
46
|
+
// started before any of them finished.
|
|
47
|
+
function deferImports(values) {
|
|
48
|
+
const releases = [];
|
|
49
|
+
mockImportFresh.mockImplementation(
|
|
50
|
+
(filePath) =>
|
|
51
|
+
new Promise((resolve) => {
|
|
52
|
+
releases.push(() => resolve(values[filePath]));
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
return {
|
|
56
|
+
started: () => mockImportFresh.mock.calls.map(([filePath]) => filePath),
|
|
57
|
+
release: () => releases.forEach((release) => release()),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test('createCustomPluginTypesMap imports every plugin before any import finishes', async () => {
|
|
62
|
+
const imports = deferImports({
|
|
63
|
+
'/plugins/plugin-a/types.js': { blocks: ['A'] },
|
|
64
|
+
'/plugins/plugin-b/types.js': { blocks: ['B'] },
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const result = createCustomPluginTypesMap({ directories, logger });
|
|
68
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
69
|
+
expect(imports.started()).toEqual(['/plugins/plugin-a/types.js', '/plugins/plugin-b/types.js']);
|
|
70
|
+
imports.release();
|
|
71
|
+
await result;
|
|
72
|
+
|
|
73
|
+
expect(mockCreatePluginTypesMap.mock.calls.map(([args]) => args.packageName)).toEqual([
|
|
74
|
+
'plugin-a',
|
|
75
|
+
'plugin-b',
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('createCustomPluginMessagesMap imports every plugin before any import finishes', async () => {
|
|
80
|
+
const imports = deferImports({
|
|
81
|
+
'/plugins/plugin-a/messages.js': { hello: 'A' },
|
|
82
|
+
'/plugins/plugin-b/messages.js': { hello: 'B' },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const result = createCustomPluginMessagesMap({ directories, logger });
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
87
|
+
expect(imports.started()).toEqual([
|
|
88
|
+
'/plugins/plugin-a/messages.js',
|
|
89
|
+
'/plugins/plugin-b/messages.js',
|
|
90
|
+
]);
|
|
91
|
+
imports.release();
|
|
92
|
+
|
|
93
|
+
expect(await result).toEqual({ 'plugin-a': { hello: 'A' }, 'plugin-b': { hello: 'B' } });
|
|
94
|
+
});
|
|
@@ -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 path from 'path';
|
|
18
|
+
|
|
19
|
+
function findWatchRoot({ filePath, watchPaths }) {
|
|
20
|
+
let watchRoot;
|
|
21
|
+
for (const watchPath of watchPaths) {
|
|
22
|
+
const contains = filePath === watchPath || filePath.startsWith(`${watchPath}${path.sep}`);
|
|
23
|
+
if (contains && (watchRoot === undefined || watchPath.length > watchRoot.length)) {
|
|
24
|
+
watchRoot = watchPath;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return watchRoot;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Ignores a path with a segment that starts with a dot (.git, .lowdefy, .env
|
|
31
|
+
// files). Segments are counted from the watched path the file is under, not
|
|
32
|
+
// from the file system root, so an app that lives inside a dot-folder (a git
|
|
33
|
+
// worktree under .claude/worktrees/) is still watched. A path under no watched
|
|
34
|
+
// path is a file added to the watch on its own, so only its name is tested.
|
|
35
|
+
function createDotPathIgnore({ watchPaths }) {
|
|
36
|
+
return function isDotPath(filePath) {
|
|
37
|
+
const watchRoot = findWatchRoot({ filePath, watchPaths });
|
|
38
|
+
const relativePath =
|
|
39
|
+
watchRoot === undefined ? path.basename(filePath) : path.relative(watchRoot, filePath);
|
|
40
|
+
return relativePath.split(path.sep).some((segment) => segment.startsWith('.'));
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default createDotPathIgnore;
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
|
|
19
|
+
import createDotPathIgnore from './createDotPathIgnore.mjs';
|
|
20
|
+
|
|
21
|
+
// setupWatcher hands the ignore function paths with the platform separator.
|
|
22
|
+
const appRoot = path.join('/home/dev/repo/.claude/worktrees/feature/app');
|
|
23
|
+
|
|
24
|
+
test('a file in an app that lives under a dot-folder is not ignored', () => {
|
|
25
|
+
const isDotPath = createDotPathIgnore({ watchPaths: [appRoot] });
|
|
26
|
+
expect(isDotPath(appRoot)).toBe(false);
|
|
27
|
+
expect(isDotPath(path.join(appRoot, 'pages/home.yaml'))).toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('a dot-folder or dotfile inside the watched path is ignored', () => {
|
|
31
|
+
const isDotPath = createDotPathIgnore({ watchPaths: [appRoot] });
|
|
32
|
+
expect(isDotPath(path.join(appRoot, '.lowdefy/server/build/app.json'))).toBe(true);
|
|
33
|
+
expect(isDotPath(path.join(appRoot, '.git'))).toBe(true);
|
|
34
|
+
expect(isDotPath(path.join(appRoot, 'pages/.home.yaml.swp'))).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('segments are counted from the deepest watched path that holds the file', () => {
|
|
38
|
+
const moduleRoot = path.join(appRoot, '.modules/layout');
|
|
39
|
+
const isDotPath = createDotPathIgnore({ watchPaths: [appRoot, moduleRoot] });
|
|
40
|
+
expect(isDotPath(path.join(moduleRoot, 'components/page.yaml'))).toBe(false);
|
|
41
|
+
expect(isDotPath(path.join(appRoot, '.modules/other/page.yaml'))).toBe(true);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('a file under no watched path is tested by its name only', () => {
|
|
45
|
+
const isDotPath = createDotPathIgnore({ watchPaths: [appRoot] });
|
|
46
|
+
expect(
|
|
47
|
+
isDotPath(path.join('/home/dev/repo/.claude/worktrees/feature/modules/shared/title.yaml'))
|
|
48
|
+
).toBe(false);
|
|
49
|
+
expect(isDotPath(path.join('/home/dev/repo/modules/shared/.title.yaml'))).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('a watched path that only shares a prefix with the file does not hold it', () => {
|
|
53
|
+
const isDotPath = createDotPathIgnore({ watchPaths: [path.join('/home/dev/app')] });
|
|
54
|
+
expect(isDotPath(path.join('/home/dev/app-other/.cache/file.yaml'))).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('dev recordings under .lowdefy/traces are ignored by the config watcher', () => {
|
|
58
|
+
const configDirectory = path.join('/home/dev/app');
|
|
59
|
+
const isDotPath = createDotPathIgnore({ watchPaths: [configDirectory] });
|
|
60
|
+
expect(
|
|
61
|
+
isDotPath(
|
|
62
|
+
path.join(configDirectory, '.lowdefy/traces/dev/2026-10-03/20261003T140311Z-k3x9qa.jsonl')
|
|
63
|
+
)
|
|
64
|
+
).toBe(true);
|
|
65
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
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 v8 from 'node:v8';
|
|
18
|
+
import vm from 'node:vm';
|
|
19
|
+
|
|
20
|
+
// The manager is started without --expose-gc (by every launch path), so it
|
|
21
|
+
// turns the flag on itself and takes gc from a fresh context.
|
|
22
|
+
function exposeGc() {
|
|
23
|
+
v8.setFlagsFromString('--expose-gc');
|
|
24
|
+
return vm.runInNewContext('gc');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// The manager's heap grows two to three times during a config build and V8
|
|
28
|
+
// does not shrink it while the process is idle. One full GC delayMs after a
|
|
29
|
+
// build ends hands that slack back (about 50-150 ms of GC, never during a
|
|
30
|
+
// build: a build starting within delayMs cancels it).
|
|
31
|
+
function createIdleGc({ delayMs = 5000, gc = exposeGc() } = {}) {
|
|
32
|
+
let timer = null;
|
|
33
|
+
|
|
34
|
+
function onBuildingChange(building) {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
timer = null;
|
|
37
|
+
if (building) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
timer = setTimeout(() => {
|
|
41
|
+
timer = null;
|
|
42
|
+
gc();
|
|
43
|
+
}, delayMs);
|
|
44
|
+
// Never the reason the manager stays up.
|
|
45
|
+
timer.unref();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return { onBuildingChange };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default createIdleGc;
|