@lowdefy/server-dev 6.0.0 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/App.jsx +52 -24
- package/client/Inspector.jsx +73 -30
- package/client/JourneyObserver.jsx +47 -0
- package/client/Page.jsx +54 -22
- package/client/Recorder.jsx +73 -0
- package/client/Routing.jsx +37 -13
- package/client/clientBuildImports.test.mjs +63 -0
- package/client/devStreamConnections.test.mjs +40 -0
- package/client/feedback/FeedbackMount.jsx +2 -2
- package/client/feedback/FeedbackOverlay.jsx +7 -4
- package/client/feedback/feedbackStyles.js +3 -0
- package/client/journeyObserver/startJourneyObserver.js +101 -0
- package/client/journeyObserver/startJourneyObserver.test.mjs +149 -0
- package/client/openInEditor/OpenInEditorListener.jsx +2 -0
- package/client/recorder/buildEngineRecord.js +37 -0
- package/client/recorder/buildInteractionRecord.js +64 -0
- package/client/recorder/buildTraceEvent.js +57 -0
- package/client/recorder/buildTraceEvent.test.mjs +81 -0
- package/client/recorder/chunkRecords.js +69 -0
- package/client/recorder/chunkRecords.test.mjs +59 -0
- package/client/recorder/collectEventActions.js +30 -0
- package/client/recorder/collectEventEndpoints.js +40 -0
- package/client/recorder/collectEventRequests.js +39 -0
- package/client/recorder/createPairingBuffer.js +187 -0
- package/client/recorder/createPairingBuffer.test.mjs +405 -0
- package/client/recorder/createPasswordRedactor.js +84 -0
- package/client/recorder/createRecordBatcher.js +54 -0
- package/client/recorder/createRecordBatcher.test.mjs +76 -0
- package/client/recorder/createRecorder.js +216 -0
- package/client/recorder/createRecorder.test.mjs +318 -0
- package/client/recorder/createRecordingSession.js +61 -0
- package/client/recorder/createRecordingSession.test.mjs +72 -0
- package/client/recorder/diffStateWrites.js +77 -0
- package/client/recorder/diffStateWrites.test.mjs +66 -0
- package/client/recorder/isEventCall.js +30 -0
- package/client/recorder/jsonType.js +39 -0
- package/client/recorder/keyChord.js +47 -0
- package/client/recorder/keyChord.test.mjs +50 -0
- package/client/recorder/sendRecords.js +40 -0
- package/client/recorder/toJsonSafe.js +29 -0
- package/lib/client/BuildingPage.jsx +3 -1
- package/lib/client/ErrorBar.jsx +17 -13
- package/lib/client/RedirectingPage.jsx +56 -0
- package/lib/client/auth/Auth.jsx +11 -6
- package/lib/client/auth/AuthConfigured.jsx +224 -39
- package/lib/client/auth/AuthNotConfigured.js +45 -4
- package/lib/client/auth/authNotConfiguredParity.test.mjs +61 -0
- package/lib/client/auth/authTemplateSync.test.mjs +40 -0
- package/lib/client/auth/betterAuthSessionContract.test.mjs +181 -0
- package/lib/client/auth/createFetchCheckingBuild.js +45 -0
- package/lib/client/bundleBuildId.js +25 -0
- package/lib/client/collectVisibleBlockIds.js +51 -0
- package/lib/client/getCspNonce.js +25 -0
- package/lib/client/getPagePath.js +36 -0
- package/lib/client/getPagePath.test.mjs +51 -0
- package/lib/client/utils/formatErrorsForCopy.js +30 -0
- package/lib/client/utils/formatErrorsForCopy.test.mjs +39 -0
- package/lib/client/utils/getErrorBarColor.js +25 -0
- package/lib/client/utils/getErrorBarColor.test.mjs +34 -0
- package/lib/client/utils/request.js +8 -1
- package/lib/client/utils/usePageConfig.js +89 -25
- package/lib/client/utils/usePageConfig.test.mjs +198 -13
- package/lib/configTree/buildConfigTree.mjs +58 -0
- package/lib/configTree/buildConfigTree.test.mjs +77 -0
- package/lib/configTree/collectConfigText.mjs +101 -0
- package/lib/configTree/collectConfigText.test.mjs +116 -0
- package/lib/configTree/prepareScratchServer.mjs +46 -0
- package/lib/configTree/runConfigTreeBuild.mjs +88 -0
- package/lib/configTree/runConfigTreeBuild.skipPluginChecks.test.mjs +74 -0
- package/lib/configTree/runConfigTreeBuild.test.mjs +202 -0
- package/lib/docs/JourneyStepError.js +30 -0
- package/lib/docs/acquireBrowserSlot.js +30 -0
- package/lib/docs/addNestedEndpoints.js +54 -0
- package/lib/docs/addNestedEndpoints.test.mjs +185 -0
- package/lib/docs/addWriteFlags.js +77 -0
- package/lib/docs/buildEditedPages.js +50 -0
- package/lib/docs/buildWaitHeader.js +21 -0
- package/lib/docs/captureAnnotatedScreenshot.js +23 -12
- package/lib/docs/checkAgentFiles.js +148 -0
- package/lib/docs/checkAgentFiles.test.mjs +225 -0
- package/lib/docs/checkStepReferences.js +239 -0
- package/lib/docs/checkStepReferences.test.mjs +173 -0
- package/lib/docs/checkpointStore.js +3 -0
- package/lib/docs/clientErrorStore.js +18 -1
- package/lib/docs/clientErrorStore.test.mjs +46 -0
- package/lib/docs/collectExercised.js +52 -0
- package/lib/docs/createBrowserLifecycle.js +102 -0
- package/lib/docs/createBrowserLifecycle.test.mjs +212 -0
- package/lib/docs/createDocsMcpServer.js +345 -388
- package/lib/docs/createDocsMcpServer.test.mjs +644 -4
- package/lib/docs/createDocsMcpServerRunJourney.test.mjs +148 -0
- package/lib/docs/createJourneyActors.chromium.test.mjs +227 -0
- package/lib/docs/createJourneyActors.js +213 -0
- package/lib/docs/createJourneyActors.test.mjs +79 -0
- package/lib/docs/createJourneyObservations.js +80 -0
- package/lib/docs/createLeftOriginError.js +26 -0
- package/lib/docs/createNetworkCounter.js +101 -0
- package/lib/docs/dataSets/checkSharedCollectionIds.js +67 -0
- package/lib/docs/dataSets/dataSessionRegistry.js +25 -0
- package/lib/docs/dataSets/dataSetJourneys.chromium.mongodb.test.mjs +679 -0
- package/lib/docs/dataSets/describeDataSetResult.js +25 -0
- package/lib/docs/dataSets/describeDatabaseTarget.js +46 -0
- package/lib/docs/dataSets/deserializeFixture.js +34 -0
- package/lib/docs/dataSets/drainSessionWork.js +37 -0
- package/lib/docs/dataSets/drainSessionWork.test.mjs +64 -0
- package/lib/docs/dataSets/duplicateIndexName.js +24 -0
- package/lib/docs/dataSets/findCollectionCollisions.js +52 -0
- package/lib/docs/dataSets/getDataStore.js +106 -0
- package/lib/docs/dataSets/getDataStore.mongodb.test.mjs +63 -0
- package/lib/docs/dataSets/getDataStore.test.mjs +195 -0
- package/lib/docs/dataSets/groupDataSetByCollection.js +44 -0
- package/lib/docs/dataSets/insertFixture.js +57 -0
- package/lib/docs/dataSets/insertGeneratedDocuments.js +41 -0
- package/lib/docs/dataSets/isDataStoreGone.js +29 -0
- package/lib/docs/dataSets/loadDataSetCollection.js +42 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.js +52 -0
- package/lib/docs/dataSets/mergeCollectionIndexes.test.mjs +59 -0
- package/lib/docs/dataSets/openDataSession.js +119 -0
- package/lib/docs/dataSets/openDataSession.mongodb.test.mjs +343 -0
- package/lib/docs/dataSets/pickDataStorePort.js +58 -0
- package/lib/docs/dataSets/pickDataStorePort.test.mjs +63 -0
- package/lib/docs/dataSets/readConnectionArtifacts.js +46 -0
- package/lib/docs/dataSets/readDataSession.js +41 -0
- package/lib/docs/dataSets/readDataSession.test.mjs +57 -0
- package/lib/docs/dataSets/readDataSet.js +68 -0
- package/lib/docs/dataSets/readDataSet.test.mjs +220 -0
- package/lib/docs/dataSets/registerDataStoreShutdown.js +47 -0
- package/lib/docs/dataSets/removeDataStoreFiles.js +32 -0
- package/lib/docs/dataSets/resolveDataSetCollection.js +46 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.js +106 -0
- package/lib/docs/dataSets/resolveJourneyDataSet.test.mjs +130 -0
- package/lib/docs/dataSets/sweepOrphanDatabases.js +33 -0
- package/lib/docs/dataSets/test/app/lowdefy.yaml +203 -0
- package/lib/docs/dataSets/test/app/modules/archive/module.lowdefy.yaml +30 -0
- package/lib/docs/dataSets/test/app/tests/data/shop.yaml +8 -0
- package/lib/docs/dataSets/test/buildTestApp.mjs +43 -0
- package/lib/docs/devEventBus.js +136 -0
- package/lib/docs/devEventBus.test.mjs +269 -0
- package/lib/docs/devToolDefinitions.js +529 -0
- package/lib/docs/docSources.js +21 -0
- package/lib/docs/docs.test.mjs +171 -15
- package/lib/docs/docsIndex.test.mjs +377 -0
- package/lib/docs/evalOperator.js +5 -3
- package/lib/docs/evalOperatorHeadless.js +84 -12
- package/lib/docs/evalOperatorHeadless.test.mjs +7 -1
- package/lib/docs/evalOperatorInTab.js +2 -1
- package/lib/docs/findConfig.js +95 -49
- package/lib/docs/fitResponse.js +81 -0
- package/lib/docs/fitResponse.test.mjs +128 -0
- package/lib/docs/formatFeedback.js +26 -5
- package/lib/docs/formatFeedback.test.mjs +48 -0
- package/lib/docs/getAppMap.js +7 -1
- package/lib/docs/getBrowser.js +150 -80
- package/lib/docs/getBrowser.test.mjs +407 -16
- package/lib/docs/getBuildId.js +61 -0
- package/lib/docs/getBuildId.test.mjs +84 -0
- package/lib/docs/getBuildStatus.js +54 -6
- package/lib/docs/getBuildStatus.test.mjs +133 -0
- package/lib/docs/getBuildStatusAfterEdits.js +35 -0
- package/lib/docs/getBuildStatusAfterEdits.test.mjs +76 -0
- package/lib/docs/getCoreDoc.js +9 -1
- package/lib/docs/getDoc.js +100 -0
- package/lib/docs/getDocsIndex.js +152 -0
- package/lib/docs/getHazards.js +115 -0
- package/lib/docs/getHazards.test.mjs +175 -0
- package/lib/docs/getMailOutboxDirectory.js +26 -0
- package/lib/docs/getOverview.js +79 -13
- package/lib/docs/getOverviewBasePath.test.mjs +37 -0
- package/lib/docs/getPageBuildStatus.js +46 -0
- package/lib/docs/getPluginDoc.js +21 -23
- package/lib/docs/getSchema.js +33 -1
- package/lib/docs/getStaleStatus.js +36 -0
- package/lib/docs/getStaleStatus.test.mjs +74 -0
- package/lib/docs/guardJourneyOrigin.js +39 -0
- package/lib/docs/inspectState.js +5 -3
- package/lib/docs/inspectStateFromTab.js +2 -2
- package/lib/docs/inspectStateHeadless.js +89 -27
- package/lib/docs/inspectStateHeadless.test.mjs +20 -1
- package/lib/docs/installHeadlessShell.failure.test.mjs +45 -0
- package/lib/docs/installHeadlessShell.js +123 -0
- package/lib/docs/installHeadlessShell.test.mjs +70 -0
- package/lib/docs/installHeadlessShell.timeout.test.mjs +72 -0
- package/lib/docs/isHeadlessShellIncomplete.js +34 -0
- package/lib/docs/isHeadlessShellIncomplete.test.mjs +63 -0
- package/lib/docs/isPageReady.js +77 -0
- package/lib/docs/isPageReady.test.mjs +235 -0
- package/lib/docs/isRunErrorEntry.js +25 -0
- package/lib/docs/journeyMutantCookie.chromium.test.mjs +150 -0
- package/lib/docs/journeyRunRecording.js +35 -0
- package/lib/docs/journeyRunRecording.test.mjs +39 -0
- package/lib/docs/keepEventStreamAlive.js +68 -0
- package/lib/docs/keepEventStreamAlive.test.mjs +77 -0
- package/lib/docs/launchBrowser.js +83 -0
- package/lib/docs/launchBrowser.test.mjs +175 -0
- package/lib/docs/listModuleDocEntries.js +69 -0
- package/lib/docs/listPackageDocFiles.js +67 -0
- package/lib/docs/listPluginDocEntries.js +111 -0
- package/lib/docs/listPlugins.js +9 -58
- package/lib/docs/listTypes.js +14 -6
- package/lib/docs/loadFrameworkHazards.js +40 -0
- package/lib/docs/loadFrameworkHazards.test.mjs +43 -0
- package/lib/docs/loadState.js +103 -35
- package/lib/docs/makeModuleManifestDoc.js +174 -0
- package/lib/docs/matchExercisedPages.js +35 -0
- package/lib/docs/matchJourneyRoute.js +69 -0
- package/lib/docs/matchJourneyRoute.test.mjs +178 -0
- package/lib/docs/mergeNetworkSnapshots.js +49 -0
- package/lib/docs/nextJourneySequence.js +30 -0
- package/lib/docs/noBrowserError.js +22 -0
- package/lib/docs/observe/TRANSIENT_SELECTOR.js +21 -0
- package/lib/docs/observe/claimExpectedErrors.js +67 -0
- package/lib/docs/observe/createJourneyAppErrors.js +236 -0
- package/lib/docs/observe/createJourneyAppErrors.test.mjs +385 -0
- package/lib/docs/observe/evaluateInvariants.js +183 -0
- package/lib/docs/observe/evaluateInvariants.test.mjs +299 -0
- package/lib/docs/observe/findingKey.js +29 -0
- package/lib/docs/observe/hasNoEffect.js +31 -0
- package/lib/docs/observe/hasNoEffect.test.mjs +37 -0
- package/lib/docs/observe/installStepObserver.js +72 -0
- package/lib/docs/observe/isAppApiUrl.js +33 -0
- package/lib/docs/observe/parseAppApiUrl.js +37 -0
- package/lib/docs/observe/readStepObserver.js +42 -0
- package/lib/docs/observe/relativeSource.js +29 -0
- package/lib/docs/observe/resolveConfigKeySource.js +33 -0
- package/lib/docs/observe/takeJourneyEvents.js +36 -0
- package/lib/docs/observe/toAppErrorFailure.js +61 -0
- package/lib/docs/observe/usesSearchStage.js +45 -0
- package/lib/docs/observe/waitForClientErrorReports.js +31 -0
- package/lib/docs/observe/watchJourneyContext.js +75 -0
- package/lib/docs/observe/withAppErrors.js +31 -0
- package/lib/docs/observe/withAppErrors.test.mjs +101 -0
- package/lib/docs/openJourney.js +94 -0
- package/lib/docs/openJourneyEmail.js +41 -0
- package/lib/docs/openJourneyEmail.test.mjs +187 -0
- package/lib/docs/pageBlocks.js +47 -0
- package/lib/docs/pageBuildStatus.test.mjs +103 -0
- package/lib/{client/setPageId.js → docs/parseModuleSource.js} +11 -12
- package/lib/docs/rails/authEngineActions.js +53 -0
- package/lib/docs/rails/authEngineActions.test.mjs +69 -0
- package/lib/docs/rails/authEngineMethods.js +52 -0
- package/lib/docs/rails/authEngineMethods.test.mjs +36 -0
- package/lib/docs/rails/checkAuthActionsRan.js +66 -0
- package/lib/docs/rails/checkAuthActionsRan.test.mjs +149 -0
- package/lib/docs/rails/checkClickRails.js +56 -0
- package/lib/docs/rails/createDataSetRails.js +82 -0
- package/lib/docs/rails/createDataSetRails.test.mjs +172 -0
- package/lib/docs/rails/findExternalBlocks.js +147 -0
- package/lib/docs/rails/findExternalBlocks.test.mjs +254 -0
- package/lib/docs/rails/installAuthActionRecorder.js +56 -0
- package/lib/docs/rails/readAuthActionRecorder.js +32 -0
- package/lib/docs/rails/readBuiltArtifact.js +30 -0
- package/lib/docs/readConnectionPlugins.js +26 -0
- package/lib/docs/readDevAuthMode.js +32 -0
- package/lib/docs/readDocEntry.js +40 -0
- package/lib/docs/readDocTitle.js +26 -0
- package/lib/docs/readJourneyEmailMatch.js +57 -0
- package/lib/docs/readJourneyEmailMatch.test.mjs +110 -0
- package/lib/docs/readJourneySession.js +56 -0
- package/lib/docs/readMailOutbox.js +37 -0
- package/lib/docs/readPagePath.js +27 -0
- package/lib/docs/readPluginPackages.js +99 -0
- package/lib/docs/readProxyBuildWait.js +38 -0
- package/lib/docs/readRoutineSteps.js +45 -0
- package/lib/docs/registerShownTextEngine.js +118 -0
- package/lib/docs/requestRestart.js +30 -0
- package/lib/docs/requestRestart.test.mjs +49 -0
- package/lib/docs/resolvePageInstance.js +46 -0
- package/lib/docs/resolvePageInstance.test.mjs +81 -0
- package/lib/docs/resolveSource.js +11 -7
- package/lib/docs/resolveSource.test.mjs +1 -1
- package/lib/docs/resolveToolCaller.js +62 -0
- package/lib/docs/reviewPage.js +51 -0
- package/lib/docs/reviewPageBuilds.js +56 -0
- package/lib/docs/reviewPageBuilds.test.mjs +207 -0
- package/lib/docs/runCheck.js +75 -0
- package/lib/docs/runCheck.test.mjs +91 -0
- package/lib/docs/runCheckChild.js +49 -0
- package/lib/docs/runEndpoint.js +185 -0
- package/lib/docs/runEndpoint.test.mjs +509 -0
- package/lib/docs/runErrorBuffers.js +99 -0
- package/lib/docs/runErrorBuffers.test.mjs +95 -0
- package/lib/docs/runJourney.js +347 -0
- package/lib/docs/runJourney.test.mjs +2383 -0
- package/lib/docs/runJourneyAsUsers.js +44 -0
- package/lib/docs/runJourneyDataSet.test.mjs +274 -0
- package/lib/docs/runJourneySteps.js +1320 -0
- package/lib/docs/runRequest.js +100 -59
- package/lib/docs/runRequest.test.mjs +190 -0
- package/lib/docs/saveAnnotatedScreenshot.js +1 -1
- package/lib/docs/scaffoldPage.js +3 -1
- package/lib/docs/screenshotPage.js +163 -11
- package/lib/docs/screenshotPage.test.mjs +263 -1
- package/lib/docs/screenshotPageClip.test.mjs +4 -0
- package/lib/docs/searchDocs.js +82 -30
- package/lib/docs/searchDocs.test.mjs +100 -0
- package/lib/docs/searchIcons.js +90 -0
- package/lib/docs/searchIcons.test.mjs +127 -0
- package/lib/docs/selectExercised.js +39 -0
- package/lib/docs/selectFinalState.js +66 -0
- package/lib/docs/selectFinalState.test.mjs +51 -0
- package/lib/docs/serverErrorStore.js +54 -0
- package/lib/docs/serverErrorStore.test.mjs +50 -0
- package/lib/docs/setupTestFixtures.mjs +133 -39
- package/lib/docs/shownTextSelector.js +28 -0
- package/lib/docs/snapshotState.js +13 -5
- package/lib/docs/splitSearchTerms.js +62 -0
- package/lib/docs/splitSearchTerms.test.mjs +50 -0
- package/lib/docs/stateCheckpoints.test.mjs +77 -0
- package/lib/docs/tabAvailable.js +6 -5
- package/lib/docs/tabChannel.js +127 -21
- package/lib/docs/tabChannel.test.mjs +187 -29
- package/lib/docs/unsettledPageNote.js +24 -0
- package/lib/docs/validateJourneyMail.js +36 -0
- package/lib/docs/validateJourneyTimeout.js +38 -0
- package/lib/docs/validateJourneyTimeout.test.mjs +29 -0
- package/lib/docs/validateRecordingParam.js +42 -0
- package/lib/docs/validateStateSelection.js +33 -0
- package/lib/docs/validateStateSelection.test.mjs +33 -0
- package/lib/docs/validateViewport.js +51 -0
- package/lib/docs/validateViewport.test.mjs +54 -0
- package/lib/docs/waitForBuild.js +50 -0
- package/lib/docs/waitForBuild.test.mjs +75 -0
- package/lib/docs/waitForJourneyEmail.js +77 -0
- package/lib/docs/withBrowserSlot.js +45 -0
- package/lib/docs/withBrowserSlot.test.mjs +75 -0
- package/lib/docs/withDataSession.js +53 -0
- package/lib/server/applyDataSetRedirect.js +83 -0
- package/lib/server/applyDataSetRedirect.test.mjs +340 -0
- package/lib/server/applyDataSetRedirectTenantReach.test.mjs +204 -0
- package/lib/server/applyDataSetRedirectWalled.test.mjs +131 -0
- package/lib/server/auth/createSystemContext.js +73 -0
- package/lib/server/auth/getAuth.js +46 -0
- package/lib/server/auth/getHeadlessUser.js +9 -8
- package/lib/server/auth/getHeadlessUser.test.mjs +41 -0
- package/lib/server/auth/getMockUser.js +18 -14
- package/lib/server/auth/{getAuthConfig.js → getStrategies.js} +12 -10
- package/lib/server/auth/headlessUser.js +3 -2
- package/lib/server/auth/journeyActor.js +35 -0
- package/lib/server/auth/resolveHeadlessUser.test.mjs +0 -4
- package/lib/server/checkPageRecord.js +44 -0
- package/lib/server/contextMapBudget.js +24 -0
- package/lib/server/contextMapBudget.test.mjs +98 -0
- package/lib/server/continueJitKeys.js +28 -0
- package/lib/server/continueJitKeys.test.mjs +43 -0
- package/lib/server/createDataSessionEndedError.js +33 -0
- package/lib/server/createDataSetRefusal.js +26 -0
- package/lib/server/createLowdefyContext.js +174 -41
- package/lib/server/createLowdefyContext.test.mjs +437 -0
- package/lib/server/getClientAddress.js +51 -0
- package/lib/server/getClientAddress.test.mjs +56 -0
- package/lib/server/getPageJitEnrichment.test.mjs +29 -11
- package/lib/server/hashConfigContent.js +26 -0
- package/lib/server/importAppCode.test.mjs +57 -0
- package/lib/server/isRoutineStep.js +27 -0
- package/lib/server/jitPageBuilder.js +309 -126
- package/lib/server/jitPageBuilder.skipPluginChecks.test.mjs +78 -0
- package/lib/server/jitPageBuilder.test.mjs +218 -0
- package/lib/server/journeyCookies/JOURNEY_COOKIES.js +37 -0
- package/lib/server/journeyCookies/forwardJourneyCookies.js +46 -0
- package/lib/server/journeyCookies/readJourneyCookie.js +52 -0
- package/lib/server/journeyCookies/writeJourneyCookie.js +46 -0
- package/lib/server/journeyCookies.js +22 -0
- package/lib/server/journeyCookies.test.mjs +145 -0
- package/lib/server/loadDynamicJsMap.js +47 -0
- package/lib/server/log/createHandleError.js +38 -2
- package/lib/server/log/createHandleError.test.mjs +125 -2
- package/lib/server/log/createLogger.js +3 -0
- package/lib/server/log/createLogger.test.mjs +77 -0
- package/lib/server/log/logRequest.js +2 -3
- package/lib/server/mutantDataSetWrappers.test.mjs +112 -0
- package/lib/server/mutants/applyMutant.js +39 -0
- package/lib/server/mutants/artifactPatterns.js +25 -0
- package/lib/server/mutants/buildAnchor.js +45 -0
- package/lib/server/mutants/collectSharedIds.js +41 -0
- package/lib/server/mutants/configPathSegments.js +38 -0
- package/lib/server/mutants/createMutantReadConfigFile.js +42 -0
- package/lib/server/mutants/describeEvent.js +22 -0
- package/lib/server/mutants/enumerateArtifact.js +56 -0
- package/lib/server/mutants/findNodeByKey.js +45 -0
- package/lib/server/mutants/findNodeByPointer.js +49 -0
- package/lib/server/mutants/getArtifactKind.js +32 -0
- package/lib/server/mutants/groupMutantCopies.js +56 -0
- package/lib/server/mutants/hashArtifact.js +25 -0
- package/lib/server/mutants/hashMutantTarget.js +34 -0
- package/lib/server/mutants/listMutants.js +122 -0
- package/lib/server/mutants/listMutants.test.mjs +502 -0
- package/lib/server/mutants/mutantId.js +30 -0
- package/lib/server/mutants/mutantRuns.js +57 -0
- package/lib/server/mutants/mutantRuns.test.mjs +136 -0
- package/lib/server/mutants/operators/dropAction.js +47 -0
- package/lib/server/mutants/operators/dropBlock.js +44 -0
- package/lib/server/mutants/operators/dropPayload.js +52 -0
- package/lib/server/mutants/operators/dropStep.js +44 -0
- package/lib/server/mutants/operators/flipVisible.js +53 -0
- package/lib/server/mutants/operators/index.js +37 -0
- package/lib/server/mutants/operators/retargetLink.js +72 -0
- package/lib/server/mutants/operators/skipValidate.js +77 -0
- package/lib/server/mutants/operators/swapIf.js +112 -0
- package/lib/server/mutants/operators.test.mjs +396 -0
- package/lib/server/mutants/positionFreePath.js +38 -0
- package/lib/server/mutants/removeFromParent.js +28 -0
- package/lib/server/mutants/test/fixtures.mjs +173 -0
- package/lib/server/mutants/test/keyArtifact.mjs +63 -0
- package/lib/server/mutants/validateMutantParam.js +69 -0
- package/lib/server/mutants/validateMutantsBody.js +88 -0
- package/lib/server/mutants/walkArtifact.js +179 -0
- package/lib/server/pageBuildRecords.js +131 -0
- package/lib/server/pageBuildRecords.test.mjs +258 -0
- package/lib/server/pageCache.mjs +22 -7
- package/lib/server/pageCache.test.mjs +22 -7
- package/lib/server/pageRebuilds.test.mjs +543 -0
- package/lib/server/pruneJitMaps.js +36 -0
- package/lib/server/pruneJitMaps.test.mjs +55 -0
- package/lib/server/readInstanceRecord.js +33 -0
- package/lib/server/readInstanceRecord.test.mjs +99 -0
- package/lib/server/readMergedMaps.js +86 -0
- package/lib/server/readMergedMaps.test.mjs +79 -0
- package/lib/server/recording/appendRecords.js +33 -0
- package/lib/server/recording/errorRecordingStamp.js +34 -0
- package/lib/server/recording/errorRecordingStamp.test.mjs +31 -0
- package/lib/server/recording/isRecordingEnabled.js +31 -0
- package/lib/server/recording/readRecordingCookie.js +71 -0
- package/lib/server/recording/readRecordingCookie.test.mjs +73 -0
- package/lib/server/recording/recordingCookiePayload.js +33 -0
- package/lib/server/recording/servedBuilds.js +46 -0
- package/lib/server/recording/servedBuilds.test.mjs +42 -0
- package/lib/server/scrubSecrets.js +23 -0
- package/lib/server/sinkExternalConnections.js +95 -0
- package/lib/server/sinkExternalConnections.test.mjs +135 -0
- package/lib/server/skipStaleMapWrites.js +62 -0
- package/lib/server/skipStaleMapWrites.test.mjs +113 -0
- package/lib/server/startIdleGc.js +67 -0
- package/lib/server/startIdleGc.test.mjs +138 -0
- package/lib/server/testFixtures/importAppCodeTwice.mjs +70 -0
- package/lib/server/trackSessionWork.js +30 -0
- package/lib/vite/createDevServerExclude.js +40 -0
- package/lib/vite/createDevServerExclude.test.mjs +79 -0
- package/manager/getContext.mjs +24 -2
- package/manager/processes/initialBuild.mjs +14 -0
- package/manager/processes/initialBuild.test.mjs +101 -0
- package/manager/processes/lowdefyBuild.mjs +30 -7
- package/manager/processes/lowdefyBuild.skipPluginChecks.test.mjs +62 -0
- package/manager/processes/lowdefyBuild.test.mjs +136 -1
- package/manager/processes/optimizeDependencies.mjs +59 -0
- package/manager/processes/restartServer.mjs +34 -6
- package/manager/processes/restartServer.test.mjs +162 -0
- package/manager/processes/shutdownServer.mjs +11 -0
- package/manager/processes/shutdownServer.test.mjs +45 -0
- package/manager/processes/startFirstServer.mjs +46 -0
- package/manager/processes/startFirstServer.test.mjs +326 -0
- package/manager/processes/startMailSink.mjs +135 -0
- package/manager/processes/startMailSink.test.mjs +146 -0
- package/manager/processes/startProxy.mjs +129 -12
- package/manager/processes/startProxy.test.mjs +366 -0
- package/manager/processes/startServer.mjs +47 -6
- package/manager/processes/startServer.test.mjs +155 -0
- package/manager/processes/startWatchers.mjs +6 -4
- package/manager/processes/syncServer.mjs +69 -0
- package/manager/processes/syncServer.test.mjs +141 -0
- package/manager/processes/waitForDevServer.mjs +48 -0
- package/manager/processes/waitForDevServer.test.mjs +112 -0
- package/manager/run.mjs +128 -43
- package/manager/utils/BatchChanges.mjs +21 -5
- package/manager/utils/BatchChanges.test.mjs +77 -0
- package/manager/utils/acquireDevInstance.mjs +99 -0
- package/manager/utils/acquireDevInstance.test.mjs +160 -0
- package/manager/utils/createBuildActivity.mjs +67 -0
- package/manager/utils/createBuildActivity.test.mjs +58 -0
- package/manager/utils/createChangeSignal.mjs +39 -0
- package/manager/utils/createChangeSignal.test.mjs +62 -0
- package/manager/utils/createCustomPluginMessagesMap.mjs +23 -33
- package/manager/utils/createCustomPluginTypesMap.mjs +24 -30
- package/manager/utils/createCustomPluginTypesMap.test.mjs +94 -0
- package/manager/utils/createDotPathIgnore.mjs +44 -0
- package/manager/utils/createDotPathIgnore.test.mjs +65 -0
- package/manager/utils/createIdleGc.mjs +51 -0
- package/manager/utils/createIdleGc.test.mjs +77 -0
- package/manager/utils/createRequestActivity.mjs +100 -0
- package/manager/utils/createRequestActivity.test.mjs +121 -0
- package/manager/utils/createServerArtifactTracker.mjs +83 -0
- package/manager/utils/createServerArtifactTracker.test.mjs +78 -0
- package/manager/utils/createServerEnv.mjs +39 -0
- package/manager/utils/filesHaveSameBytes.mjs +41 -0
- package/manager/utils/findBuildFilesOutsideWatch.mjs +59 -0
- package/manager/utils/findBuildFilesOutsideWatch.test.mjs +111 -0
- package/manager/utils/importFresh.mjs +46 -0
- package/manager/utils/importFresh.test.mjs +72 -0
- package/manager/utils/importFreshWorker.mjs +20 -0
- package/manager/utils/killTaggedBrowser.mjs +35 -0
- package/manager/utils/pagePathChanged.mjs +56 -0
- package/manager/utils/pruneRecordings.mjs +85 -0
- package/manager/utils/pruneRecordings.test.mjs +117 -0
- package/manager/utils/publishBuildDirectory.mjs +121 -0
- package/manager/utils/publishBuildDirectory.order.test.mjs +74 -0
- package/manager/utils/publishBuildDirectory.test.mjs +262 -0
- package/manager/utils/readBasePath.mjs +27 -0
- package/manager/utils/readBasePath.test.mjs +43 -0
- package/manager/utils/readBuildStatusWait.mjs +67 -0
- package/manager/utils/readManagerVersion.mjs +30 -0
- package/manager/utils/readManagerVersion.test.mjs +35 -0
- package/manager/utils/readPluginDefinitions.mjs +36 -0
- package/manager/utils/resolveDevAuthUrl.mjs +58 -0
- package/manager/utils/resolveDevAuthUrl.test.mjs +107 -0
- package/manager/utils/resolvePorts.mjs +49 -0
- package/manager/utils/setupWatcher.mjs +19 -11
- package/manager/utils/startRecordingPruner.mjs +45 -0
- package/manager/utils/startRecordingPruner.test.mjs +66 -0
- package/manager/utils/waitForChildExit.mjs +53 -0
- package/manager/utils/waitForChildExit.test.mjs +70 -0
- package/manager/utils/waitForServer.mjs +44 -0
- package/manager/utils/writeBuildStatus.mjs +1 -1
- package/manager/watchers/createPluginSourceIgnore.mjs +77 -0
- package/manager/watchers/envWatcher.mjs +9 -2
- package/manager/watchers/envWatcher.test.mjs +87 -0
- package/manager/watchers/listServerSidePackages.mjs +47 -0
- package/manager/watchers/listServerSidePackages.test.mjs +38 -0
- package/manager/watchers/lowdefyBuildWatcher.mjs +106 -22
- package/manager/watchers/lowdefyBuildWatcher.test.mjs +309 -0
- package/manager/watchers/pluginSourceWatcher.mjs +79 -0
- package/manager/watchers/pluginSourceWatcher.test.mjs +222 -0
- package/manager/watchers/restartRequestWatcher.mjs +71 -0
- package/manager/watchers/restartRequestWatcher.test.mjs +111 -0
- package/manager/watchers/selectLocalPluginPackages.mjs +48 -0
- package/manager/watchers/selectLocalPluginPackages.test.mjs +73 -0
- package/manager/watchers/serverPackageWatcher.mjs +38 -0
- package/package.json +61 -49
- package/package.original.json +61 -49
- package/src/app.js +131 -20
- package/src/html/renderDevPage.js +63 -11
- package/src/html/renderDevPage.test.mjs +138 -0
- package/src/lib/getRequestPath.js +29 -0
- package/src/middleware/createSameOriginGuard.js +37 -0
- package/src/middleware/createSameOriginGuard.test.mjs +102 -0
- package/src/middleware/errorHandler.js +51 -1
- package/src/middleware/errorHandler.test.mjs +196 -14
- package/src/middleware/isRebindingSafeHost.js +44 -0
- package/src/middleware/isRebindingSafeHost.test.mjs +32 -0
- package/src/middleware/localDevToolsOnly.js +38 -0
- package/src/middleware/localDevToolsOnly.test.mjs +61 -0
- package/src/middleware/noStore.js +27 -0
- package/src/middleware/noStore.test.mjs +48 -0
- package/src/middleware/requestTimeout.js +38 -0
- package/src/middleware/staleFlag.js +76 -0
- package/src/middleware/staleFlag.test.mjs +127 -0
- package/src/middleware/stampCaller.js +38 -0
- package/src/middleware/stampCaller.test.mjs +64 -0
- package/src/routes/agent.js +15 -3
- package/src/routes/auth.js +29 -20
- package/src/routes/auth.test.mjs +30 -56
- package/src/routes/clientError.js +13 -13
- package/src/routes/cron.js +3 -1
- package/src/routes/cronForward.js +4 -2
- package/src/routes/detached.js +22 -9
- package/src/routes/devInspect.js +17 -8
- package/src/routes/devInspect.test.mjs +44 -7
- package/src/routes/devRecording.js +113 -0
- package/src/routes/devRecording.test.mjs +253 -0
- package/src/routes/docs/buildStatus.js +10 -2
- package/src/routes/docs/buildStatus.test.mjs +46 -0
- package/src/routes/docs/content.js +5 -3
- package/src/routes/docs/evalOperator.js +15 -1
- package/src/routes/docs/events.js +50 -0
- package/src/routes/docs/events.test.mjs +117 -0
- package/src/routes/docs/find.js +9 -1
- package/src/routes/docs/icons.js +30 -0
- package/src/routes/docs/inspectState.js +16 -1
- package/src/routes/docs/journey.js +194 -0
- package/src/routes/docs/journey.test.mjs +401 -0
- package/src/routes/docs/journeyRefusals.test.mjs +71 -0
- package/src/routes/docs/loadState.js +4 -2
- package/src/routes/docs/mcp.js +34 -3
- package/src/routes/docs/mcp.test.mjs +130 -0
- package/src/routes/docs/mutants.js +78 -0
- package/src/routes/docs/mutants.test.mjs +180 -0
- package/src/routes/docs/parsePathParamsParam.js +41 -0
- package/src/routes/docs/parsePathParamsParam.test.mjs +42 -0
- package/src/routes/docs/parseUserParam.js +27 -21
- package/src/routes/docs/parseUserParam.test.mjs +17 -5
- package/src/routes/docs/restart.js +35 -0
- package/src/routes/docs/restart.test.mjs +64 -0
- package/src/routes/docs/runEndpoint.js +54 -0
- package/src/routes/docs/runEndpoint.test.mjs +165 -0
- package/src/routes/docs/runRequest.js +25 -8
- package/src/routes/docs/runRequest.test.mjs +92 -0
- package/src/routes/docs/screenshot.js +50 -2
- package/src/routes/docs/screenshot.test.mjs +117 -0
- package/src/routes/docs/search.js +15 -2
- package/src/routes/docs/snapshotState.js +17 -3
- package/src/routes/endpoints.js +27 -26
- package/src/routes/endpointsTextAnswer.test.mjs +77 -0
- package/src/routes/feedback.js +10 -13
- package/src/routes/jitPage.js +99 -35
- package/src/routes/jitPage.test.mjs +248 -0
- package/src/routes/mcp.js +82 -5
- package/src/routes/mcp.test.mjs +215 -0
- package/src/routes/mcpMemberToken.test.mjs +125 -0
- package/src/routes/mcpProtectedResourceMetadata.js +34 -0
- package/src/routes/mcpProtectedResourceMetadata.test.mjs +65 -0
- package/src/routes/mountDevPageRoutes.js +25 -0
- package/src/routes/mountDevPageRoutes.test.mjs +30 -0
- package/src/routes/mountOauthDiscovery.js +44 -0
- package/src/routes/mountOauthDiscovery.test.mjs +79 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.js +46 -0
- package/src/routes/normalizeMcpProtocolVersionHeader.test.mjs +62 -0
- package/src/routes/reload.js +16 -0
- package/src/routes/request.js +6 -4
- package/src/routes/request.test.mjs +9 -5
- package/src/routes/requestBody.test.mjs +159 -0
- package/src/routes/usage.js +6 -2
- package/src/routes/user.js +27 -0
- package/src/routes/websocket.js +29 -8
- package/src/routes/websocket.test.mjs +65 -0
- package/src/routes/wellKnownFallback.js +30 -0
- package/src/routes/wellKnownFallback.test.mjs +32 -0
- package/src/websocket/devWebSocket.js +41 -26
- package/src/websocket/devWebSocket.test.mjs +157 -0
- package/vite.config.js +53 -23
- package/lib/server/auth/getDevSession.js +0 -70
- package/lib/server/auth/getDevSession.test.mjs +0 -138
- package/lib/server/auth/session.js +0 -39
- package/manager/processes/warnAuthUrlPortMismatch.mjs +0 -47
- package/manager/processes/warnAuthUrlPortMismatch.test.mjs +0 -93
- package/manager/watchers/moduleBuildWatcher.mjs +0 -79
- package/manager/watchers/serverArtifactWatcher.mjs +0 -120
- package/src/routes/apiPage.js +0 -37
|
@@ -0,0 +1,57 @@
|
|
|
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 { execFile } from 'node:child_process';
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { promisify } from 'node:util';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
|
|
24
|
+
import { jest } from '@jest/globals';
|
|
25
|
+
|
|
26
|
+
jest.setTimeout(60000);
|
|
27
|
+
|
|
28
|
+
const fixture = path.join(
|
|
29
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
30
|
+
'testFixtures/importAppCodeTwice.mjs'
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
let configDir;
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
configDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lowdefy-import-app-code-'));
|
|
37
|
+
fs.writeFileSync(path.join(configDir, 'package.json'), '{ "type": "module" }');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
afterEach(() => {
|
|
41
|
+
fs.rmSync(configDir, { recursive: true, force: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// The dev server's JIT page builds import app code through Vite's SSR module
|
|
45
|
+
// runner when @lowdefy/build is linked (the monorepo), and natively when it is
|
|
46
|
+
// installed from npm.
|
|
47
|
+
test.each(['vite', 'node'])(
|
|
48
|
+
'importAppCode loaded by %s imports an edited transformer again and reuses an unchanged one',
|
|
49
|
+
async (loader) => {
|
|
50
|
+
const { stdout } = await promisify(execFile)(process.execPath, [fixture, loader, configDir]);
|
|
51
|
+
expect(JSON.parse(stdout)).toEqual({
|
|
52
|
+
first: 'before',
|
|
53
|
+
edited: 'after',
|
|
54
|
+
unchangedReused: true,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { type } from '@lowdefy/helpers';
|
|
18
|
+
|
|
19
|
+
// A step of a built endpoint routine. The build gives every step a stepId
|
|
20
|
+
// (the id the config wrote; its id becomes the scoped endpoint:<id>:<step>),
|
|
21
|
+
// so a node with a string type and stepId is a step, and a control object
|
|
22
|
+
// (`:if`, `:try`, ...) or a step's own properties never is.
|
|
23
|
+
function isRoutineStep(node) {
|
|
24
|
+
return type.isObject(node) && type.isString(node.type) && type.isString(node.stepId);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default isRoutineStep;
|
|
@@ -14,21 +14,31 @@
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import crypto from 'crypto';
|
|
17
18
|
import fs from 'fs';
|
|
18
19
|
import path from 'path';
|
|
19
20
|
import { serializer, type } from '@lowdefy/helpers';
|
|
20
21
|
import {
|
|
21
22
|
buildPageJit,
|
|
23
|
+
collectIconNames,
|
|
22
24
|
createContext,
|
|
25
|
+
createJitMaps,
|
|
26
|
+
createReadConfigFile,
|
|
23
27
|
generateClientJsModule,
|
|
24
28
|
hydrateDeferredRecords,
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
prepareJitContext,
|
|
30
|
+
restoreTenantTargets,
|
|
27
31
|
} from '@lowdefy/build/dev';
|
|
28
32
|
|
|
33
|
+
import checkPageRecord from './checkPageRecord.js';
|
|
34
|
+
import contextMapBudget from './contextMapBudget.js';
|
|
35
|
+
import continueJitKeys from './continueJitKeys.js';
|
|
29
36
|
import createLogger from './log/createLogger.js';
|
|
37
|
+
import pageBuildRecords from './pageBuildRecords.js';
|
|
30
38
|
import PageCache from './pageCache.mjs';
|
|
39
|
+
import pruneJitMaps from './pruneJitMaps.js';
|
|
31
40
|
import readBuildApiArtifacts from './readBuildApiArtifacts.mjs';
|
|
41
|
+
import skipStaleMapWrites from './skipStaleMapWrites.js';
|
|
32
42
|
|
|
33
43
|
const jitLogger = createLogger({ name: 'jit-build' });
|
|
34
44
|
|
|
@@ -36,17 +46,32 @@ function formatDuration(ms) {
|
|
|
36
46
|
if (ms < 1000) return `${ms}ms`;
|
|
37
47
|
return `${(ms / 1000).toFixed(2)}s`;
|
|
38
48
|
}
|
|
39
|
-
const pageCache = new PageCache();
|
|
40
|
-
let cachedRegistryMtime = null;
|
|
41
|
-
let cachedRegistry = null;
|
|
42
|
-
let cachedBuildContext = null;
|
|
43
|
-
let lastInvalidationMtime = null;
|
|
44
49
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let
|
|
50
|
+
const pageCache = new PageCache();
|
|
51
|
+
let keptContext = null;
|
|
52
|
+
let registry = null;
|
|
53
|
+
let registryIdentity = null;
|
|
54
|
+
let lastChangeSignal = null;
|
|
55
|
+
// Counts the change events (page file edits) this process has seen. A compiled
|
|
56
|
+
// page whose checkedAt is behind it has its inputs checked before it is served.
|
|
57
|
+
let eventCounter = 0;
|
|
58
|
+
let budgetExceeded = false;
|
|
59
|
+
|
|
60
|
+
// Names this process's JIT keys and jitMaps files. A restarted child continues
|
|
61
|
+
// the same config build as the one before it, so its keys need their own
|
|
62
|
+
// prefix, or both would hand out the same keys for different nodes.
|
|
63
|
+
const childId = crypto.randomBytes(3).toString('hex');
|
|
64
|
+
// The generation of the current build context, or of the next one when none is
|
|
65
|
+
// made yet. A page built on an earlier generation is rebuilt, and a context's
|
|
66
|
+
// jitMaps files carry it.
|
|
67
|
+
let contextGeneration = 1;
|
|
68
|
+
let previousContextGeneration = 0;
|
|
69
|
+
|
|
70
|
+
// Frozen snapshot of the icon names in the dev client bundle, from the initial
|
|
71
|
+
// build. Module-level so it persists across context resets: skeleton rebuilds
|
|
72
|
+
// rewrite iconImports.json with newly used names, but those are not in the
|
|
73
|
+
// bundle until the server restarts. JIT delivers the rest as data.
|
|
74
|
+
let bundledIcons = null;
|
|
50
75
|
|
|
51
76
|
function readJsonFile(filePath) {
|
|
52
77
|
try {
|
|
@@ -57,43 +82,29 @@ function readJsonFile(filePath) {
|
|
|
57
82
|
}
|
|
58
83
|
}
|
|
59
84
|
|
|
60
|
-
|
|
61
|
-
|
|
85
|
+
// The value the manager writes to build/invalidatePages after every batch of
|
|
86
|
+
// watched changes, or null before the first.
|
|
87
|
+
function readChangeSignal(buildDirectory) {
|
|
62
88
|
try {
|
|
63
|
-
|
|
64
|
-
if (lastInvalidationMtime && stat.mtimeMs === lastInvalidationMtime) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
lastInvalidationMtime = stat.mtimeMs;
|
|
68
|
-
pageCache.invalidateAll();
|
|
69
|
-
cachedBuildContext = null;
|
|
89
|
+
return fs.readFileSync(path.join(buildDirectory, 'invalidatePages'), 'utf8');
|
|
70
90
|
} catch {
|
|
71
|
-
|
|
91
|
+
return null;
|
|
72
92
|
}
|
|
73
93
|
}
|
|
74
94
|
|
|
75
|
-
|
|
76
|
-
|
|
95
|
+
// Every config build publish renames a new pageRegistry.json into place, so
|
|
96
|
+
// its inode and mtime identify the published build, even two publishes in one
|
|
97
|
+
// clock tick.
|
|
98
|
+
function readRegistryIdentity(buildDirectory) {
|
|
77
99
|
try {
|
|
78
|
-
const stat = fs.statSync(
|
|
79
|
-
|
|
80
|
-
if (cachedRegistryMtime && stat.mtimeMs === cachedRegistryMtime) {
|
|
81
|
-
return cachedRegistry;
|
|
82
|
-
}
|
|
83
|
-
cachedRegistryMtime = stat.mtimeMs;
|
|
84
|
-
cachedRegistry = readJsonFile(registryPath);
|
|
85
|
-
// Invalidate all pages when registry changes (skeleton rebuild happened)
|
|
86
|
-
pageCache.invalidateAll();
|
|
87
|
-
cachedBuildContext = null;
|
|
88
|
-
return cachedRegistry;
|
|
100
|
+
const stat = fs.statSync(path.join(buildDirectory, 'pageRegistry.json'), { bigint: true });
|
|
101
|
+
return `${stat.ino}:${stat.mtimeNs}`;
|
|
89
102
|
} catch {
|
|
90
103
|
return null;
|
|
91
104
|
}
|
|
92
105
|
}
|
|
93
106
|
|
|
94
|
-
function
|
|
95
|
-
if (cachedBuildContext) return cachedBuildContext;
|
|
96
|
-
|
|
107
|
+
function createBuildContext(buildDirectory, configDirectory) {
|
|
97
108
|
const refMap = readJsonFile(path.join(buildDirectory, 'refMap.json')) ?? {};
|
|
98
109
|
const keyMap = readJsonFile(path.join(buildDirectory, 'keyMap.json')) ?? {};
|
|
99
110
|
const jsMap = readJsonFile(path.join(buildDirectory, 'jsMap.json')) ?? { client: {}, server: {} };
|
|
@@ -103,7 +114,7 @@ function getBuildContext(buildDirectory, configDirectory) {
|
|
|
103
114
|
const customTypesMap = readJsonFile(path.join(buildDirectory, 'customTypesMap.json')) ?? {};
|
|
104
115
|
const customMessagesMap = readJsonFile(path.join(buildDirectory, 'customMessagesMap.json')) ?? {};
|
|
105
116
|
|
|
106
|
-
|
|
117
|
+
const context = createContext({
|
|
107
118
|
customMessagesMap,
|
|
108
119
|
customTypesMap,
|
|
109
120
|
directories: {
|
|
@@ -112,30 +123,46 @@ function getBuildContext(buildDirectory, configDirectory) {
|
|
|
112
123
|
server: path.resolve(buildDirectory, '..'),
|
|
113
124
|
},
|
|
114
125
|
logger: jitLogger,
|
|
126
|
+
// The manager's shallowBuild() reads the same variable; the page builds
|
|
127
|
+
// run in the server process and build their own context.
|
|
128
|
+
skipPluginChecks: process.env.LOWDEFY_BUILD_SKIP_PLUGIN_CHECKS === 'true',
|
|
115
129
|
stage: 'dev',
|
|
116
130
|
});
|
|
131
|
+
pageBuildRecords.trackFileReads({ context, configDirectory });
|
|
117
132
|
|
|
118
133
|
// Restore refMap, keyMap, jsMap, connectionIds, and websocketIds from skeleton build
|
|
119
|
-
Object.assign(
|
|
120
|
-
Object.assign(
|
|
121
|
-
|
|
122
|
-
|
|
134
|
+
Object.assign(context.refMap, refMap);
|
|
135
|
+
Object.assign(context.keyMap, keyMap);
|
|
136
|
+
context.jsMap.client = jsMap.client ?? {};
|
|
137
|
+
context.jsMap.server = jsMap.server ?? {};
|
|
123
138
|
for (const id of connectionIds) {
|
|
124
|
-
|
|
139
|
+
context.connectionIds.add(id);
|
|
125
140
|
}
|
|
126
141
|
for (const id of websocketIds) {
|
|
127
|
-
|
|
142
|
+
context.websocketIds.add(id);
|
|
128
143
|
}
|
|
144
|
+
// The scoped connections, walled collections and shared connections, so a
|
|
145
|
+
// page's requests get the same tenant pipeline checks as in a full build.
|
|
146
|
+
const tenantTargets = readJsonFile(path.join(buildDirectory, 'tenantTargets.json'));
|
|
147
|
+
if (tenantTargets) {
|
|
148
|
+
restoreTenantTargets({ context, tenantTargets });
|
|
149
|
+
}
|
|
150
|
+
// Pages that host a policy-bound Dynamic block count the policy's types
|
|
151
|
+
// and validate its pages and endpoints.
|
|
152
|
+
Object.assign(
|
|
153
|
+
context.dynamicPolicies,
|
|
154
|
+
readJsonFile(path.join(buildDirectory, 'dynamicPolicies.json')) ?? {}
|
|
155
|
+
);
|
|
129
156
|
|
|
130
157
|
// Load installed packages snapshot from skeleton build for missing-package detection
|
|
131
158
|
const installedPluginPackages =
|
|
132
159
|
readJsonFile(path.join(buildDirectory, 'installedPluginPackages.json')) ?? [];
|
|
133
|
-
|
|
160
|
+
context.installedPluginPackages = new Set(installedPluginPackages);
|
|
134
161
|
|
|
135
162
|
// Restore module entries from skeleton build for JIT module page builds
|
|
136
163
|
const modules = readJsonFile(path.join(buildDirectory, 'modules.json'));
|
|
137
164
|
if (modules) {
|
|
138
|
-
Object.assign(
|
|
165
|
+
Object.assign(context.modules, modules);
|
|
139
166
|
}
|
|
140
167
|
|
|
141
168
|
// Hydrate the deferred-record registry — module component bodies referenced
|
|
@@ -143,94 +170,238 @@ function getBuildContext(buildDirectory, configDirectory) {
|
|
|
143
170
|
// the marker-restoring reviver, so record-body ~r/~l markers survive.
|
|
144
171
|
const deferredRecords = readJsonFile(path.join(buildDirectory, 'deferredRecords.json'));
|
|
145
172
|
if (deferredRecords) {
|
|
146
|
-
hydrateDeferredRecords(
|
|
173
|
+
hydrateDeferredRecords(context, deferredRecords);
|
|
147
174
|
}
|
|
148
175
|
|
|
149
176
|
// Restore app metadata so JIT page builds resolve _app / _build.app against
|
|
150
177
|
// the same metadata the skeleton build computed.
|
|
151
|
-
|
|
178
|
+
context.appMeta = readJsonFile(path.join(buildDirectory, 'appMeta.json')) ?? null;
|
|
152
179
|
|
|
153
180
|
// Restore api endpoint configs so JIT CallAPI validation (validateCallApiRefs in
|
|
154
181
|
// buildPageJit) can resolve endpointIds. Without this the dev context has no
|
|
155
182
|
// components.api and every CallAPI action is flagged as a non-existent endpoint.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
// Use the frozen icon imports from the initial build for JIT detection.
|
|
159
|
-
// This represents what's actually in the client bundle — not what shallowBuild
|
|
160
|
-
// discovers on subsequent rebuilds (those icons aren't bundled yet).
|
|
161
|
-
// bundledIconImports is module-level and only resets on server restart.
|
|
162
|
-
if (!bundledIconImports) {
|
|
163
|
-
bundledIconImports = readJsonFile(path.join(buildDirectory, 'iconImports.json')) ?? [];
|
|
164
|
-
}
|
|
165
|
-
cachedBuildContext.iconImports = bundledIconImports;
|
|
183
|
+
context.components = { api: readBuildApiArtifacts(buildDirectory) };
|
|
166
184
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
185
|
+
if (!bundledIcons) {
|
|
186
|
+
bundledIcons = new Set(readJsonFile(path.join(buildDirectory, 'iconImports.json')) ?? []);
|
|
187
|
+
}
|
|
188
|
+
context.bundledIcons = bundledIcons;
|
|
189
|
+
// IconData delivered to pages as _dynamicIcons. Starts empty with each
|
|
190
|
+
// context, since theme.icons or icon set plugins may have changed; JIT
|
|
191
|
+
// re-resolves as pages are requested.
|
|
192
|
+
context.dynamicIconData = {};
|
|
170
193
|
|
|
171
|
-
// Advance makeId past all skeleton IDs to prevent collisions with JIT builds
|
|
172
194
|
const idCounter = readJsonFile(path.join(buildDirectory, 'idCounter.json'));
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
195
|
+
continueJitKeys({ idCounter, childId });
|
|
196
|
+
|
|
197
|
+
// The context's page builds write the entries they add to jitMaps/. Only the
|
|
198
|
+
// previous context's files are kept besides its own: they resolve errors that
|
|
199
|
+
// pages built just before the recreation still report.
|
|
200
|
+
context.jitMaps = createJitMaps({
|
|
201
|
+
keyMap: context.keyMap,
|
|
202
|
+
refMap: context.refMap,
|
|
203
|
+
name: `${childId}-${contextGeneration}`,
|
|
204
|
+
});
|
|
205
|
+
pruneJitMaps({ buildDirectory, keep: `${childId}-${previousContextGeneration}-` });
|
|
206
|
+
previousContextGeneration = contextGeneration;
|
|
207
|
+
skipStaleMapWrites({
|
|
208
|
+
buildDirectory,
|
|
209
|
+
context,
|
|
210
|
+
keyPrefix: idCounter.prefix,
|
|
211
|
+
isKeptContext: () => context === keptContext,
|
|
212
|
+
});
|
|
213
|
+
prepareJitContext(context);
|
|
176
214
|
|
|
177
|
-
return
|
|
215
|
+
return context;
|
|
178
216
|
}
|
|
179
217
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
218
|
+
// The next page build makes a new context, of a new generation. Pages built on
|
|
219
|
+
// this one are rebuilt.
|
|
220
|
+
function discardBuildContext() {
|
|
221
|
+
keptContext = null;
|
|
222
|
+
contextGeneration += 1;
|
|
223
|
+
budgetExceeded = false;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// The build context JIT page builds share. It outlives page edits and is
|
|
227
|
+
// recreated (a new generation) only when a config build is published or its
|
|
228
|
+
// maps pass contextMapBudget.
|
|
229
|
+
export function getBuildContext(buildDirectory, configDirectory) {
|
|
230
|
+
keptContext ??= createBuildContext(buildDirectory, configDirectory);
|
|
231
|
+
return keptContext;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// A change event: the files pages read may have changed. Reads from here on go
|
|
235
|
+
// through a fresh read cache, so a check or build sees what is on disk now;
|
|
236
|
+
// builds already running keep the cache they started with, and what they read
|
|
237
|
+
// is checked on the page's next request. Warnings are logged again.
|
|
238
|
+
function startChangeEvent(configDirectory) {
|
|
239
|
+
eventCounter += 1;
|
|
240
|
+
if (!keptContext) return;
|
|
241
|
+
keptContext.readConfigFile = pageBuildRecords.trackReadConfigFile({
|
|
242
|
+
readConfigFile: createReadConfigFile({ directories: keptContext.directories }),
|
|
243
|
+
configDirectory,
|
|
244
|
+
});
|
|
245
|
+
keptContext.seenSourceLines.clear();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Reads what the manager and the config build changed since the last call, and
|
|
249
|
+
// acts on it: a new build/invalidatePages value is a change event; a newly
|
|
250
|
+
// published page registry, or a context past its budget, discards the build
|
|
251
|
+
// context, so the next page build makes a new one. Both a page request and the
|
|
252
|
+
// build status review call it first, so each sees an edit the other has not.
|
|
253
|
+
export function syncBuildSignals({ buildDirectory, configDirectory }) {
|
|
254
|
+
const changeSignal = readChangeSignal(buildDirectory);
|
|
255
|
+
if (changeSignal !== lastChangeSignal) {
|
|
256
|
+
lastChangeSignal = changeSignal;
|
|
257
|
+
startChangeEvent(configDirectory);
|
|
258
|
+
}
|
|
259
|
+
const identity = readRegistryIdentity(buildDirectory);
|
|
260
|
+
if (identity === null) {
|
|
261
|
+
return { registry: null, eventCounter, generation: contextGeneration };
|
|
185
262
|
}
|
|
263
|
+
if (identity !== registryIdentity) {
|
|
264
|
+
registryIdentity = identity;
|
|
265
|
+
registry = readJsonFile(path.join(buildDirectory, 'pageRegistry.json'));
|
|
266
|
+
discardBuildContext();
|
|
267
|
+
} else if (budgetExceeded) {
|
|
268
|
+
discardBuildContext();
|
|
269
|
+
}
|
|
270
|
+
return { registry, eventCounter, generation: contextGeneration };
|
|
271
|
+
}
|
|
186
272
|
|
|
187
|
-
|
|
188
|
-
|
|
273
|
+
function countAddedMapEntries(context) {
|
|
274
|
+
return context.jitMaps.keys.added.length + context.jitMaps.refs.added.length;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Whether a page's last build still describes it: 'current' or 'edited'. A
|
|
278
|
+
// page built on an earlier context is edited: its content, _js and icons
|
|
279
|
+
// belong to a context that is gone. A page checked up to the current change
|
|
280
|
+
// event is current. Otherwise its last build's inputs are checked
|
|
281
|
+
// (checkPageRecord), and a match moves its checkedAt to the event the check
|
|
282
|
+
// started at, so the next request or review does not check it again. A page
|
|
283
|
+
// request and the build status review both decide with this, so a build
|
|
284
|
+
// status wait builds exactly the pages the next requests would.
|
|
285
|
+
export async function reviewBuiltPage({ pageId, eventCounter: counter, generation }) {
|
|
286
|
+
const record = pageBuildRecords.get(pageId);
|
|
287
|
+
if (!record || record.generation !== generation) return 'edited';
|
|
288
|
+
const compiled = pageCache.get(pageId);
|
|
289
|
+
if (record.checkedAt === counter || compiled?.checkedAt === counter) return 'current';
|
|
290
|
+
const check = await checkPageRecord({ record, readConfigFile: keptContext.readConfigFile });
|
|
291
|
+
const latest = pageBuildRecords.get(pageId);
|
|
292
|
+
if (latest !== record) {
|
|
293
|
+
// The page was built again while this check read its previous build's
|
|
294
|
+
// inputs, so the check says nothing about the new build: it is current
|
|
295
|
+
// only when it started at or after this check's event.
|
|
296
|
+
return latest?.generation === generation && latest.checkedAt >= counter ? 'current' : 'edited';
|
|
189
297
|
}
|
|
298
|
+
if (check !== 'current') return 'edited';
|
|
299
|
+
record.checkedAt = Math.max(record.checkedAt, counter);
|
|
300
|
+
pageCache.markChecked(pageId, { generation, checkedAt: counter });
|
|
301
|
+
return 'current';
|
|
302
|
+
}
|
|
190
303
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
304
|
+
// Whether a compiled page can be served as it is.
|
|
305
|
+
async function isPageCurrent({ pageId, eventCounter: counter, generation }) {
|
|
306
|
+
const compiled = pageCache.get(pageId);
|
|
307
|
+
if (!compiled || compiled.generation !== generation) return false;
|
|
308
|
+
if ((await reviewBuiltPage({ pageId, eventCounter: counter, generation })) !== 'current') {
|
|
309
|
+
return false;
|
|
195
310
|
}
|
|
311
|
+
// A rebuild that failed while the page was checked leaves nothing to serve.
|
|
312
|
+
return pageCache.isCompiled(pageId);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async function buildPage({ pageId, buildDirectory, configDirectory }) {
|
|
316
|
+
// Read together, before the build starts: a change event or recreation
|
|
317
|
+
// during the build leaves the page behind, so its next request checks or
|
|
318
|
+
// rebuilds it.
|
|
319
|
+
const context = getBuildContext(buildDirectory, configDirectory);
|
|
320
|
+
const generation = contextGeneration;
|
|
321
|
+
const checkedAt = eventCounter;
|
|
322
|
+
const pageRegistry = registry;
|
|
323
|
+
pageCache.remove(pageId);
|
|
196
324
|
|
|
197
325
|
jitLogger.info({ spin: 'start' }, `Building page "${pageId}"...`);
|
|
198
326
|
const startTime = Date.now();
|
|
327
|
+
let result;
|
|
199
328
|
try {
|
|
200
|
-
|
|
201
|
-
const result = await buildPageJit({
|
|
329
|
+
result = await pageBuildRecords.record({
|
|
202
330
|
pageId,
|
|
203
|
-
pageRegistry: registry,
|
|
204
331
|
context,
|
|
332
|
+
configDirectory,
|
|
333
|
+
generation,
|
|
334
|
+
checkedAt,
|
|
335
|
+
build: () => buildPageJit({ pageId, pageRegistry, context }),
|
|
205
336
|
});
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
return result;
|
|
212
|
-
}
|
|
213
|
-
pageCache.markCompiled(pageId);
|
|
214
|
-
// Touch the candidates file so Vite's CSS pipeline re-runs Tailwind for
|
|
215
|
-
// classes the JIT build discovered — globals.css imports it. This import
|
|
216
|
-
// is the ONLY recompile trigger: the tailwind .html scan inputs are
|
|
217
|
-
// excluded from Vite's watcher (their .html change events would force
|
|
218
|
-
// full browser reloads). Only touched when the build actually changed
|
|
219
|
-
// tailwind content, so unchanged rebuilds cause no CSS recompile.
|
|
220
|
-
if (result?._tailwindChanged) {
|
|
221
|
-
fs.writeFileSync(
|
|
222
|
-
path.join(buildDirectory, 'tailwind-candidates.css'),
|
|
223
|
-
`/* Generated by Lowdefy build — rewritten on page changes to trigger CSS recompilation */\n/* ${Date.now()} */\n`
|
|
224
|
-
);
|
|
337
|
+
} finally {
|
|
338
|
+
// A failed build adds map entries too, and a page being fixed fails
|
|
339
|
+
// build after build.
|
|
340
|
+
if (context === keptContext && countAddedMapEntries(context) > contextMapBudget) {
|
|
341
|
+
budgetExceeded = true;
|
|
225
342
|
}
|
|
343
|
+
}
|
|
344
|
+
if (result && result.installing) {
|
|
226
345
|
jitLogger.info(
|
|
227
|
-
{
|
|
228
|
-
|
|
346
|
+
`Installing plugin packages for page "${pageId}": ${result.packages.join(', ')}. ` +
|
|
347
|
+
'The page will be available after the server restarts.'
|
|
348
|
+
);
|
|
349
|
+
return { result, buildContext: context };
|
|
350
|
+
}
|
|
351
|
+
pageCache.markCompiled(pageId, { generation, checkedAt });
|
|
352
|
+
// Touch the candidates file so Vite's CSS pipeline re-runs Tailwind for
|
|
353
|
+
// classes the JIT build discovered — globals.css imports it. This import
|
|
354
|
+
// is the ONLY recompile trigger: the tailwind .html scan inputs are
|
|
355
|
+
// excluded from Vite's watcher (their .html change events would force
|
|
356
|
+
// full browser reloads). Only touched when the build actually changed
|
|
357
|
+
// tailwind content, so unchanged rebuilds cause no CSS recompile.
|
|
358
|
+
if (result?._tailwindChanged) {
|
|
359
|
+
fs.writeFileSync(
|
|
360
|
+
path.join(buildDirectory, 'tailwind-candidates.css'),
|
|
361
|
+
`/* Generated by Lowdefy build — rewritten on page changes to trigger CSS recompilation */\n/* ${Date.now()} */\n`
|
|
229
362
|
);
|
|
230
|
-
return { built: true, warnings: result?._warnings };
|
|
231
|
-
} finally {
|
|
232
|
-
pageCache.releaseBuildLock(pageId);
|
|
233
363
|
}
|
|
364
|
+
jitLogger.info(
|
|
365
|
+
{ spin: 'succeed', color: 'white' },
|
|
366
|
+
`Built page "${pageId}" in ${formatDuration(Date.now() - startTime)}.`
|
|
367
|
+
);
|
|
368
|
+
return { result: { built: true, warnings: result?._warnings }, buildContext: context };
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Serves a page from its last JIT build while that build is current, else
|
|
372
|
+
// builds it. Returns { result, buildContext }: result is false for a page not
|
|
373
|
+
// in the registry, true for a page already current, or the build's result;
|
|
374
|
+
// buildContext is the context the page was built on, which holds its _js
|
|
375
|
+
// entries and icons even after a later request discards it.
|
|
376
|
+
export async function buildPageWithContext({ pageId, buildDirectory, configDirectory }) {
|
|
377
|
+
for (;;) {
|
|
378
|
+
const signals = syncBuildSignals({ buildDirectory, configDirectory });
|
|
379
|
+
// A kept context is always of the current generation, so a page current
|
|
380
|
+
// in this generation was built on it.
|
|
381
|
+
const currentContext = keptContext;
|
|
382
|
+
if (!signals.registry || !signals.registry[pageId]) {
|
|
383
|
+
return { result: false, buildContext: null };
|
|
384
|
+
}
|
|
385
|
+
if (await isPageCurrent({ pageId, ...signals })) {
|
|
386
|
+
return { result: true, buildContext: currentContext };
|
|
387
|
+
}
|
|
388
|
+
const shouldBuild = await pageCache.acquireBuildLock(pageId);
|
|
389
|
+
if (shouldBuild) {
|
|
390
|
+
try {
|
|
391
|
+
return await buildPage({ pageId, buildDirectory, configDirectory });
|
|
392
|
+
} finally {
|
|
393
|
+
pageCache.releaseBuildLock(pageId);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// Another request built the page meanwhile. Its build may have started
|
|
397
|
+
// before an event this request saw, or failed, so the page is looked at
|
|
398
|
+
// again rather than taken as current.
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
async function buildPageIfNeeded({ pageId, buildDirectory, configDirectory }) {
|
|
403
|
+
const { result } = await buildPageWithContext({ pageId, buildDirectory, configDirectory });
|
|
404
|
+
return result;
|
|
234
405
|
}
|
|
235
406
|
|
|
236
407
|
// Collect every client _js hash the page references. jsMapParser reduces a _js
|
|
@@ -269,36 +440,45 @@ function scopeDynamicIcons({ pageConfig, scopedJsMap, dynamicIconData }) {
|
|
|
269
440
|
if (Object.keys(dynamicIconData).length === 0) return undefined;
|
|
270
441
|
const scanText = [JSON.stringify(pageConfig), ...Object.values(scopedJsMap)].join('\n');
|
|
271
442
|
const found = {};
|
|
272
|
-
for (const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (dynamicIconData[name] && !found[name]) {
|
|
276
|
-
found[name] = dynamicIconData[name];
|
|
277
|
-
}
|
|
443
|
+
for (const name of collectIconNames({ text: scanText })) {
|
|
444
|
+
if (Object.hasOwn(dynamicIconData, name)) {
|
|
445
|
+
found[name] = dynamicIconData[name];
|
|
278
446
|
}
|
|
279
447
|
}
|
|
280
448
|
return Object.keys(found).length > 0 ? found : undefined;
|
|
281
449
|
}
|
|
282
450
|
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
451
|
+
// The contexts whose jsMap and icons can hold a served page's entries: the one
|
|
452
|
+
// the page was built on, and the kept one. The page config is read from disk
|
|
453
|
+
// after the build, so a build of the page on a newer context can have written
|
|
454
|
+
// it meanwhile. _js keys are content hashes, so an entry found in either is
|
|
455
|
+
// the page's own.
|
|
456
|
+
function enrichmentContexts(buildContext) {
|
|
457
|
+
return [buildContext, keptContext].filter(
|
|
458
|
+
(context, index, contexts) => context && contexts.indexOf(context) === index
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Scope this page's JIT-discovered enrichment out of the build context it was
|
|
463
|
+
// built on, so jitPageHandler can fold it into the page-config response the
|
|
464
|
+
// client already awaits — removing the two secondary fetches that stalled
|
|
465
|
+
// first paint. A request can discard that context while the page config is
|
|
466
|
+
// read, so it is passed in (from buildPageWithContext) rather than taken from
|
|
467
|
+
// the kept context.
|
|
468
|
+
export function getPageJitEnrichment({ pageConfig, buildContext }) {
|
|
469
|
+
const contexts = enrichmentContexts(buildContext);
|
|
470
|
+
// No build context (before the first build) means nothing JIT-discovered to
|
|
471
|
+
// fold — the page serves what the static client bundle already carries.
|
|
472
|
+
if (contexts.length === 0) return {};
|
|
473
|
+
|
|
295
474
|
const hashes = new Set();
|
|
296
475
|
collectJsHashes(pageConfig, hashes);
|
|
297
476
|
|
|
298
477
|
const scopedJsMap = {};
|
|
299
478
|
for (const hash of hashes) {
|
|
300
|
-
|
|
301
|
-
|
|
479
|
+
const context = contexts.find((candidate) => Object.hasOwn(candidate.jsMap.client ?? {}, hash));
|
|
480
|
+
if (context) {
|
|
481
|
+
scopedJsMap[hash] = context.jsMap.client[hash];
|
|
302
482
|
}
|
|
303
483
|
}
|
|
304
484
|
|
|
@@ -308,7 +488,10 @@ export function getPageJitEnrichment({ pageConfig, buildContext = cachedBuildCon
|
|
|
308
488
|
const dynamicIcons = scopeDynamicIcons({
|
|
309
489
|
pageConfig,
|
|
310
490
|
scopedJsMap,
|
|
311
|
-
dynamicIconData:
|
|
491
|
+
dynamicIconData: Object.assign(
|
|
492
|
+
{},
|
|
493
|
+
...[...contexts].reverse().map((context) => context.dynamicIconData ?? {})
|
|
494
|
+
),
|
|
312
495
|
});
|
|
313
496
|
|
|
314
497
|
return { jsEntries, dynamicIcons };
|