noteconnection 1.7.0 → 1.9.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/LICENSE +674 -21
- package/README.md +260 -1626
- package/dist/src/agent_workspace.contract.parity.test.js +159 -0
- package/dist/src/agent_workspace.frontend.test.js +10388 -0
- package/dist/src/agent_workspace.locale.contract.test.js +95 -0
- package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
- package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
- package/dist/src/appimage.patchelf.test.js +113 -0
- package/dist/src/appimage.portability.test.js +156 -0
- package/dist/src/backend/FileLoader.js +18 -4
- package/dist/src/backend/FileLoader.test.js +125 -0
- package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
- package/dist/src/backend/GraphBuilder.js +66 -18
- package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
- package/dist/src/backend/GraphMetrics.js +0 -3
- package/dist/src/backend/ResourceIdentity.js +155 -0
- package/dist/src/backend/ResourceIdentity.test.js +113 -0
- package/dist/src/backend/algorithms/CycleDetection.js +0 -1
- package/dist/src/backend/algorithms/HybridEngine.js +0 -3
- package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
- package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
- package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
- package/dist/src/backend/build_dag.js +0 -1
- package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
- package/dist/src/capacitor.runtime.contract.test.js +11 -2
- package/dist/src/copy.assets.contract.test.js +130 -0
- package/dist/src/core/Graph.js +138 -42
- package/dist/src/core/Graph.test.js +74 -0
- package/dist/src/core/NoteConnection.js +2 -2
- package/dist/src/core/PathBridge.js +293 -5
- package/dist/src/core/ResourceReference.js +17 -0
- package/dist/src/desktop.godot.pack.test.js +142 -0
- package/dist/src/desktop.runtime.graph.test.js +66 -0
- package/dist/src/export/WorkspaceExportBundle.js +1174 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +1541 -0
- package/dist/src/export/index.js +18 -0
- package/dist/src/export/types.js +2 -0
- package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
- package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
- package/dist/src/foundation.release.evidence.contract.test.js +524 -0
- package/dist/src/foundation.sqlite.runtime.contract.test.js +88 -0
- package/dist/src/frontend/README.md +260 -1626
- package/dist/src/frontend/agent_workspace.js +6011 -0
- package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
- package/dist/src/frontend/analysis.js +31 -4
- package/dist/src/frontend/app.js +2178 -62
- package/dist/src/frontend/focus_mode_interactions.js +33 -0
- package/dist/src/frontend/godot_future_path_renderer.js +540 -0
- package/dist/src/frontend/godot_tree_interactions.js +178 -0
- package/dist/src/frontend/graph_state.mjs +105 -0
- package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
- package/dist/src/frontend/i18n.mjs +186 -0
- package/dist/src/frontend/index.html +672 -268
- package/dist/src/frontend/knowledge_projection_contract.js +230 -0
- package/dist/src/frontend/knowledge_projection_store.js +214 -0
- package/dist/src/frontend/layout_gpu.js +12 -4
- package/dist/src/frontend/libs/path_core.js +158 -42
- package/dist/src/frontend/locales/en.json +753 -5
- package/dist/src/frontend/locales/zh.json +753 -5
- package/dist/src/frontend/main.mjs +60 -0
- package/dist/src/frontend/markdown_runtime.js +827 -0
- package/dist/src/frontend/mobile_budget_runtime.js +33 -0
- package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
- package/dist/src/frontend/mobile_identity_contract.js +221 -0
- package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
- package/dist/src/frontend/notemd.css +49 -0
- package/dist/src/frontend/notemd.html +7 -1
- package/dist/src/frontend/notemd.js +64 -0
- package/dist/src/frontend/path.html +113 -2
- package/dist/src/frontend/path_app.js +2108 -130
- package/dist/src/frontend/path_layout.mjs +143 -0
- package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
- package/dist/src/frontend/path_modules_bridge.js +486 -0
- package/dist/src/frontend/path_state.mjs +118 -0
- package/dist/src/frontend/path_styles.css +146 -0
- package/dist/src/frontend/path_worker_bridge.mjs +85 -0
- package/dist/src/frontend/reader.js +393 -33
- package/dist/src/frontend/runtime_bridge.js +67 -54
- package/dist/src/frontend/runtime_bridge.mjs +279 -0
- package/dist/src/frontend/settings.js +130 -12
- package/dist/src/frontend/simulationWorker.js +1 -1
- package/dist/src/frontend/source_manager.js +297 -22
- package/dist/src/frontend/storage_provider.js +661 -71
- package/dist/src/frontend/styles.css +2814 -72
- package/dist/src/frontend/workbench_state.mjs +101 -0
- package/dist/src/frontend/workspace_panes.js +10272 -0
- package/dist/src/frontend.locale.contract.test.js +62 -0
- package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
- package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
- package/dist/src/graph.answer.operator.contract.test.js +72 -0
- package/dist/src/index.js +6 -2
- package/dist/src/index.knowledge-root.test.js +118 -0
- package/dist/src/indexing/IndexLifecycle.js +217 -0
- package/dist/src/indexing/IndexLifecycle.test.js +73 -0
- package/dist/src/indexing/SegmentBuilder.js +64 -0
- package/dist/src/indexing/UnitBuilder.js +48 -0
- package/dist/src/indexing/types.js +2 -0
- package/dist/src/knowledge.api.contract.test.js +172 -0
- package/dist/src/knowledge_projection.contract.test.js +134 -0
- package/dist/src/knowledge_projection_store.test.js +200 -0
- package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
- package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +11181 -0
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +745 -0
- package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +3208 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2998 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3936 -0
- package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
- package/dist/src/learning/PublicAnswerContract.test.js +85 -0
- package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
- package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
- package/dist/src/learning/agentConversationExecution.js +211 -0
- package/dist/src/learning/agentConversationExecution.test.js +209 -0
- package/dist/src/learning/agentConversationSerialization.js +266 -0
- package/dist/src/learning/agentConversationSerialization.test.js +165 -0
- package/dist/src/learning/agentResponseBudget.js +179 -0
- package/dist/src/learning/agentResponseBudget.test.js +97 -0
- package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
- package/dist/src/learning/answerReleaseReview.js +5260 -0
- package/dist/src/learning/answerReleaseReview.test.js +4174 -0
- package/dist/src/learning/answerTaskPlan.js +473 -0
- package/dist/src/learning/api.js +2 -0
- package/dist/src/learning/conversationComposer.js +1721 -0
- package/dist/src/learning/conversationComposer.test.js +3220 -0
- package/dist/src/learning/domains/ConversationManager.js +53 -0
- package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
- package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
- package/dist/src/learning/domains/MasteryEngine.js +387 -0
- package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
- package/dist/src/learning/domains/QualityEvaluator.js +307 -0
- package/dist/src/learning/domains/TutorRouter.js +313 -0
- package/dist/src/learning/domains/index.js +33 -0
- package/dist/src/learning/domains/types.js +7 -0
- package/dist/src/learning/errors.js +29 -0
- package/dist/src/learning/evidenceContextAssembler.js +1368 -0
- package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
- package/dist/src/learning/graphAnswerCoverage.js +37 -0
- package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
- package/dist/src/learning/graphAnswerFacts.js +94 -0
- package/dist/src/learning/graphAnswerFacts.test.js +39 -0
- package/dist/src/learning/graphAnswerPlan.js +908 -0
- package/dist/src/learning/graphAnswerPlan.test.js +907 -0
- package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
- package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
- package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
- package/dist/src/learning/graphClaimMatcher.js +104 -0
- package/dist/src/learning/graphConditionedContext.js +109 -0
- package/dist/src/learning/graphConditionedContext.test.js +99 -0
- package/dist/src/learning/graphContextAssembler.js +860 -0
- package/dist/src/learning/graphContextAssembler.test.js +1033 -0
- package/dist/src/learning/graphExpansionPolicy.js +22 -0
- package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
- package/dist/src/learning/identityMetadata.test.js +26 -0
- package/dist/src/learning/index.js +29 -0
- package/dist/src/learning/mobileAnswerProjection.js +104 -0
- package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
- package/dist/src/learning/queryBackend.js +1922 -0
- package/dist/src/learning/queryBackend.test.js +976 -0
- package/dist/src/learning/ragContextPack.js +356 -0
- package/dist/src/learning/ragContextPack.test.js +311 -0
- package/dist/src/learning/ragEvidenceQuality.js +96 -0
- package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
- package/dist/src/learning/ragPublicText.js +101 -0
- package/dist/src/learning/ragPublicText.test.js +57 -0
- package/dist/src/learning/ragSufficiencyJudge.js +171 -0
- package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.js +217 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +207 -0
- package/dist/src/learning/requestNormalization.js +304 -0
- package/dist/src/learning/requestNormalization.test.js +101 -0
- package/dist/src/learning/runtimeCapability.js +4677 -0
- package/dist/src/learning/runtimeCapability.test.js +3635 -0
- package/dist/src/learning/store.js +1344 -0
- package/dist/src/learning/store.test.js +1425 -0
- package/dist/src/learning/tutorAdapter.js +2 -0
- package/dist/src/learning/types.js +2 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +958 -0
- package/dist/src/learning/vectorAccelerationAdapter.test.js +412 -0
- package/dist/src/learning/workspaceHydration.js +168 -0
- package/dist/src/learning/workspaceHydration.test.js +30 -0
- package/dist/src/lfs.asset.policy.contract.test.js +153 -0
- package/dist/src/license.policy.contract.test.js +71 -0
- package/dist/src/markdown/MarkdownGateway.js +4 -0
- package/dist/src/markdown/MarkdownGateway.test.js +59 -0
- package/dist/src/memory/MemoryGovernance.js +74 -0
- package/dist/src/memory/MemoryGovernance.test.js +46 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
- package/dist/src/middleware/SseResponseWriter.js +122 -0
- package/dist/src/middleware/SseResponseWriter.test.js +54 -0
- package/dist/src/middleware/auth.js +38 -0
- package/dist/src/middleware/auth.test.js +33 -0
- package/dist/src/middleware/body-parser.js +45 -0
- package/dist/src/middleware/cors.js +44 -0
- package/dist/src/middleware/index.js +22 -0
- package/dist/src/middleware/request-trace.js +96 -0
- package/dist/src/mobile.android.evidence.contract.test.js +146 -0
- package/dist/src/mobile.android.signing.contract.test.js +66 -0
- package/dist/src/mobile.artifact.contract.test.js +144 -0
- package/dist/src/mobile.budget.contract.test.js +90 -0
- package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
- package/dist/src/mobile.graph.parity.contract.test.js +207 -0
- package/dist/src/mobile.identity.contract.test.js +66 -0
- package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
- package/dist/src/mobile.identity.corpus.test.js +40 -0
- package/dist/src/mobile.native.recovery.contract.test.js +64 -0
- package/dist/src/mobile.pipeline.test.js +11 -1
- package/dist/src/mobile.profile.contract.test.js +225 -0
- package/dist/src/mobile.semantic.parity.test.js +92 -0
- package/dist/src/mobile_exact_analyzer.test.js +113 -0
- package/dist/src/notemd/AppConfigToml.js +6 -4
- package/dist/src/notemd/ContentGenerator.js +4 -9
- package/dist/src/notemd/FileProcessor.js +4 -9
- package/dist/src/notemd/MermaidProcessor.js +400 -50
- package/dist/src/notemd/NotemdService.js +506 -26
- package/dist/src/notemd/PromptManager.js +15 -0
- package/dist/src/notemd/Translator.js +4 -9
- package/dist/src/notemd/cli/commands.js +357 -0
- package/dist/src/notemd/cli/dispatcher.js +225 -0
- package/dist/src/notemd/cli/index.js +169 -0
- package/dist/src/notemd/cli/parser.js +68 -0
- package/dist/src/notemd/cli/types.js +2 -0
- package/dist/src/notemd/constants.js +43 -0
- package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
- package/dist/src/notemd/diagram/diagramSpec.js +79 -0
- package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
- package/dist/src/notemd/diagram/intent.js +95 -0
- package/dist/src/notemd/diagram/planner.js +71 -0
- package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
- package/dist/src/notemd/diagram/types.js +18 -0
- package/dist/src/notemd/index.js +26 -0
- package/dist/src/notemd/operations/capabilityManifest.js +23 -0
- package/dist/src/notemd/operations/cliContracts.js +17 -0
- package/dist/src/notemd/operations/configProfileCommands.js +85 -0
- package/dist/src/notemd/operations/registry.contract.test.js +95 -0
- package/dist/src/notemd/operations/registry.js +991 -0
- package/dist/src/notemd/operations/types.js +2 -0
- package/dist/src/notemd/progressReporter.js +9 -0
- package/dist/src/notemd/providerDiagnostics.js +220 -0
- package/dist/src/notemd/providerProfiles.js +42 -0
- package/dist/src/notemd/providerTemplates.js +231 -0
- package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
- package/dist/src/notemd/search/SearchManager.js +13 -0
- package/dist/src/notemd/search/SearchProvider.js +2 -0
- package/dist/src/notemd/search/TavilyProvider.js +44 -0
- package/dist/src/notemd.agent.manifest.test.js +85 -0
- package/dist/src/notemd.api.contract.test.js +14 -1
- package/dist/src/notemd.app_config_toml.test.js +2 -1
- package/dist/src/notemd.batch.workflow.test.js +117 -0
- package/dist/src/notemd.cli.e2e.test.js +136 -0
- package/dist/src/notemd.core.test.js +51 -0
- package/dist/src/notemd.diagram.pipeline.test.js +233 -0
- package/dist/src/notemd.providerTemplates.test.js +34 -0
- package/dist/src/notemd.server.integration.test.js +262 -39
- package/dist/src/notemd.workflow.pipeline.test.js +162 -0
- package/dist/src/pathbridge.handshake.contract.test.js +45 -2
- package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
- package/dist/src/pathcore.bundle.contract.test.js +8 -0
- package/dist/src/pathmode.background.contract.test.js +69 -0
- package/dist/src/pathmode.settings.api.contract.test.js +9 -0
- package/dist/src/pkg.sidecar.contract.test.js +9 -3
- package/dist/src/platform/ExportProfile.js +86 -0
- package/dist/src/platform/MobileBudget.js +11 -0
- package/dist/src/platform/PlatformCapabilities.js +59 -0
- package/dist/src/platform/PlatformCapabilities.test.js +36 -0
- package/dist/src/platform/RenderMaterializer.js +33 -0
- package/dist/src/platform/RenderMaterializer.test.js +32 -0
- package/dist/src/query_backend.external_http.integration.test.js +410 -0
- package/dist/src/reader_renderer.js +404 -3
- package/dist/src/reader_renderer.test.js +87 -0
- package/dist/src/release.godot.mirror.contract.test.js +97 -0
- package/dist/src/resources/ResourceRegistry.js +263 -0
- package/dist/src/resources/ResourceRegistry.test.js +97 -0
- package/dist/src/resources/types.js +2 -0
- package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
- package/dist/src/routes/index.js +19 -0
- package/dist/src/routes/knowledge.js +981 -0
- package/dist/src/routes/knowledgePayload.js +185 -0
- package/dist/src/routes/knowledgePayload.test.js +53 -0
- package/dist/src/routes/markdown.js +295 -0
- package/dist/src/routes/notemd.js +1321 -0
- package/dist/src/routes/registry.contract.test.js +133 -0
- package/dist/src/routes/registry.shadow.contract.test.js +22 -0
- package/dist/src/routes/render.js +248 -0
- package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
- package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
- package/dist/src/routes/settings.js +6 -0
- package/dist/src/routes/staticFiles.js +94 -0
- package/dist/src/routes/types.js +2 -0
- package/dist/src/runtime.capabilities.test.js +18 -2
- package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
- package/dist/src/server.js +10342 -1325
- package/dist/src/server.migration.test.js +584 -10
- package/dist/src/server.port.fallback.contract.test.js +63 -0
- package/dist/src/session/SessionStateStore.js +81 -0
- package/dist/src/session/SessionStateStore.test.js +58 -0
- package/dist/src/session/types.js +2 -0
- package/dist/src/settings.runtime.contract.test.js +50 -0
- package/dist/src/shared/types.contract.test.js +107 -0
- package/dist/src/shared/types.js +22 -0
- package/dist/src/sidecar.build.lock.contract.test.js +201 -0
- package/dist/src/sidecar.freshness.contract.test.js +184 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +131 -0
- package/dist/src/sidecar.supply.readiness.contract.test.js +155 -0
- package/dist/src/simulation.worker.offline.test.js +104 -0
- package/dist/src/source_manager.loadflow.test.js +46 -0
- package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
- package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
- package/dist/src/storage.provider.contract.test.js +152 -1
- package/dist/src/tauri.frontend.build.contract.test.js +68 -0
- package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
- package/dist/src/tauri.window.evidence.execution.test.js +68 -0
- package/dist/src/utils/RuntimePaths.js +10 -13
- package/dist/src/utils/RuntimePaths.test.js +27 -0
- package/dist/src/utils/platform.js +153 -0
- package/dist/src/windows.installer.build.test.js +109 -0
- package/dist/src/windows.installer.payload.test.js +76 -0
- package/dist/src/windows.installer.runtime.test.js +69 -0
- package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
- package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
- package/dist/src/workflows/types.js +2 -0
- package/dist/src/workspace/WorkspaceRegistry.js +135 -0
- package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
- package/dist/src/workspace/types.js +2 -0
- package/package.json +80 -15
|
@@ -140,6 +140,46 @@ function loadStartServer() {
|
|
|
140
140
|
const serverModule = require('./server');
|
|
141
141
|
return serverModule.startServer;
|
|
142
142
|
}
|
|
143
|
+
function loadStartServerWithPathBridgeFallbackProbe() {
|
|
144
|
+
jest.resetModules();
|
|
145
|
+
jest.doMock('./index', () => ({
|
|
146
|
+
buildGraph: jest.fn().mockResolvedValue(undefined)
|
|
147
|
+
}));
|
|
148
|
+
jest.doMock('./core/PathBridge', () => {
|
|
149
|
+
class MockPathBridge {
|
|
150
|
+
constructor(options) {
|
|
151
|
+
const requestedPort = Number(options?.port || 9876);
|
|
152
|
+
if (requestedPort === 48765) {
|
|
153
|
+
const error = new Error('permission denied');
|
|
154
|
+
error.code = 'EACCES';
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
this.port = requestedPort;
|
|
158
|
+
}
|
|
159
|
+
getPort() {
|
|
160
|
+
return this.port;
|
|
161
|
+
}
|
|
162
|
+
getStatus() {
|
|
163
|
+
return { port: this.port };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return { PathBridge: MockPathBridge };
|
|
167
|
+
});
|
|
168
|
+
jest.doMock('./reader_renderer', () => ({
|
|
169
|
+
renderMathPng: jest.fn().mockResolvedValue({
|
|
170
|
+
pngBase64: 'math',
|
|
171
|
+
width: 1,
|
|
172
|
+
height: 1
|
|
173
|
+
}),
|
|
174
|
+
renderMermaidPng: jest.fn().mockResolvedValue({
|
|
175
|
+
pngBase64: 'mermaid',
|
|
176
|
+
width: 1,
|
|
177
|
+
height: 1
|
|
178
|
+
})
|
|
179
|
+
}));
|
|
180
|
+
const serverModule = require('./server');
|
|
181
|
+
return serverModule.startServer;
|
|
182
|
+
}
|
|
143
183
|
describe('server ephemeral port fallback policy contract', () => {
|
|
144
184
|
let tempProject = null;
|
|
145
185
|
let envRestorers = [];
|
|
@@ -163,6 +203,8 @@ describe('server ephemeral port fallback policy contract', () => {
|
|
|
163
203
|
setEnv('NOTE_CONNECTION_KB_ROOT', kbRoot),
|
|
164
204
|
setEnv('NOTE_CONNECTION_AUTH_TOKEN', undefined),
|
|
165
205
|
setEnv('NOTE_CONNECTION_PORT', undefined),
|
|
206
|
+
setEnv('NOTE_CONNECTION_BRIDGE_PORT', undefined),
|
|
207
|
+
setEnv('NOTE_CONNECTION_ALLOW_EPHEMERAL_BRIDGE_PORT_FALLBACK', undefined),
|
|
166
208
|
setEnv('PORT', undefined)
|
|
167
209
|
];
|
|
168
210
|
originalArgv = [...process.argv];
|
|
@@ -219,4 +261,25 @@ describe('server ephemeral port fallback policy contract', () => {
|
|
|
219
261
|
await closeServer(blocker);
|
|
220
262
|
}
|
|
221
263
|
});
|
|
264
|
+
test('falls back from an unavailable PathBridge port and publishes the effective bridge port', async () => {
|
|
265
|
+
if (!tempProject) {
|
|
266
|
+
throw new Error('missing temp project');
|
|
267
|
+
}
|
|
268
|
+
const projectRoot = path.join(tempProject.root, 'project');
|
|
269
|
+
const manifestPath = path.join(projectRoot, 'tmp', 'active-sidecar-runtime.json');
|
|
270
|
+
envRestorers.push(setEnv('NOTE_CONNECTION_BRIDGE_PORT', '48765'));
|
|
271
|
+
envRestorers.push(setEnv('NOTE_CONNECTION_ALLOW_EPHEMERAL_BRIDGE_PORT_FALLBACK', '1'));
|
|
272
|
+
const startServer = loadStartServerWithPathBridgeFallbackProbe();
|
|
273
|
+
let appServer = null;
|
|
274
|
+
try {
|
|
275
|
+
appServer = await startServer({ port: 0 });
|
|
276
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
277
|
+
expect(manifest.bridgePort).not.toBe(48765);
|
|
278
|
+
expect(manifest.bridgePort).toBeGreaterThan(0);
|
|
279
|
+
expect(manifest.bridgeWsUrl).toBe(`ws://127.0.0.1:${manifest.bridgePort}`);
|
|
280
|
+
}
|
|
281
|
+
finally {
|
|
282
|
+
await closeServer(appServer);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
222
285
|
});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionStateStore = void 0;
|
|
4
|
+
class SessionStateStore {
|
|
5
|
+
constructor(nextId) {
|
|
6
|
+
this.nextId = nextId;
|
|
7
|
+
this.sessionStates = new Map();
|
|
8
|
+
}
|
|
9
|
+
upsert(record) {
|
|
10
|
+
const existing = this.sessionStates.get(record.sessionId);
|
|
11
|
+
const nextRecord = {
|
|
12
|
+
sessionStateId: existing?.sessionStateId || this.nextId('session_state'),
|
|
13
|
+
sessionId: record.sessionId,
|
|
14
|
+
userId: record.userId,
|
|
15
|
+
workspaceId: record.workspaceId || null,
|
|
16
|
+
corpusId: record.corpusId || null,
|
|
17
|
+
mode: record.mode,
|
|
18
|
+
activeResourceIds: Array.from(new Set(record.activeResourceIds)).filter(Boolean),
|
|
19
|
+
activeProjectionIds: Array.from(new Set(record.activeProjectionIds)).filter(Boolean),
|
|
20
|
+
retrievalSettings: { ...record.retrievalSettings },
|
|
21
|
+
memorySettings: { ...record.memorySettings },
|
|
22
|
+
exportProfileId: record.exportProfileId || null,
|
|
23
|
+
panelState: { ...(record.panelState || {}) },
|
|
24
|
+
createdAt: existing?.createdAt || record.recordedAt,
|
|
25
|
+
updatedAt: record.recordedAt,
|
|
26
|
+
};
|
|
27
|
+
this.sessionStates.set(record.sessionId, nextRecord);
|
|
28
|
+
return nextRecord;
|
|
29
|
+
}
|
|
30
|
+
get(sessionId) {
|
|
31
|
+
return this.sessionStates.get(String(sessionId || '').trim()) || null;
|
|
32
|
+
}
|
|
33
|
+
listByWorkspace(workspaceId, userId) {
|
|
34
|
+
const normalizedWorkspaceId = String(workspaceId || '').trim().toLowerCase();
|
|
35
|
+
const normalizedUserId = String(userId || '').trim();
|
|
36
|
+
if (!normalizedWorkspaceId) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return Array.from(this.sessionStates.values())
|
|
40
|
+
.filter((state) => state.workspaceId === normalizedWorkspaceId)
|
|
41
|
+
.filter((state) => !normalizedUserId || state.userId === normalizedUserId)
|
|
42
|
+
.sort((left, right) => left.updatedAt.localeCompare(right.updatedAt));
|
|
43
|
+
}
|
|
44
|
+
listBySessionIds(sessionIds) {
|
|
45
|
+
const allowedIds = new Set(sessionIds
|
|
46
|
+
.map((sessionId) => String(sessionId || '').trim())
|
|
47
|
+
.filter(Boolean));
|
|
48
|
+
if (allowedIds.size <= 0) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
return Array.from(this.sessionStates.values())
|
|
52
|
+
.filter((state) => allowedIds.has(state.sessionId))
|
|
53
|
+
.sort((left, right) => left.sessionId.localeCompare(right.sessionId));
|
|
54
|
+
}
|
|
55
|
+
buildSnapshot() {
|
|
56
|
+
return {
|
|
57
|
+
sessionStates: Array.from(this.sessionStates.values()).map((state) => ({
|
|
58
|
+
...state,
|
|
59
|
+
activeResourceIds: [...state.activeResourceIds],
|
|
60
|
+
activeProjectionIds: [...state.activeProjectionIds],
|
|
61
|
+
retrievalSettings: { ...state.retrievalSettings },
|
|
62
|
+
memorySettings: { ...state.memorySettings },
|
|
63
|
+
panelState: { ...state.panelState },
|
|
64
|
+
})),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
restoreFromSnapshot(snapshot) {
|
|
68
|
+
this.sessionStates.clear();
|
|
69
|
+
(snapshot?.sessionStates || []).forEach((state) => {
|
|
70
|
+
this.sessionStates.set(state.sessionId, {
|
|
71
|
+
...state,
|
|
72
|
+
activeResourceIds: [...state.activeResourceIds],
|
|
73
|
+
activeProjectionIds: [...state.activeProjectionIds],
|
|
74
|
+
retrievalSettings: { ...state.retrievalSettings },
|
|
75
|
+
memorySettings: { ...state.memorySettings },
|
|
76
|
+
panelState: { ...state.panelState },
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.SessionStateStore = SessionStateStore;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const SessionStateStore_1 = require("./SessionStateStore");
|
|
4
|
+
describe('SessionStateStore', () => {
|
|
5
|
+
test('upserts session state and lists by workspace', () => {
|
|
6
|
+
let idCounter = 0;
|
|
7
|
+
const store = new SessionStateStore_1.SessionStateStore((prefix = 'session_state') => `${prefix}_${++idCounter}`);
|
|
8
|
+
const first = store.upsert({
|
|
9
|
+
sessionId: 'session_a',
|
|
10
|
+
userId: 'user_a',
|
|
11
|
+
workspaceId: 'optics',
|
|
12
|
+
corpusId: 'optics',
|
|
13
|
+
mode: 'grounded_conversation',
|
|
14
|
+
activeResourceIds: ['resource_1'],
|
|
15
|
+
activeProjectionIds: ['projection_1'],
|
|
16
|
+
retrievalSettings: {
|
|
17
|
+
topK: 6,
|
|
18
|
+
queryBackend: 'local_hybrid',
|
|
19
|
+
persistMemory: true,
|
|
20
|
+
},
|
|
21
|
+
memorySettings: {
|
|
22
|
+
namespace: 'conversation',
|
|
23
|
+
enabled: true,
|
|
24
|
+
},
|
|
25
|
+
exportProfileId: 'desktop-full',
|
|
26
|
+
panelState: {
|
|
27
|
+
lastGroundedAnswerAt: '2026-05-26T00:00:00.000Z',
|
|
28
|
+
},
|
|
29
|
+
recordedAt: '2026-05-26T00:00:00.000Z',
|
|
30
|
+
});
|
|
31
|
+
const second = store.upsert({
|
|
32
|
+
sessionId: 'session_a',
|
|
33
|
+
userId: 'user_a',
|
|
34
|
+
workspaceId: 'optics',
|
|
35
|
+
corpusId: 'optics',
|
|
36
|
+
mode: 'study_session',
|
|
37
|
+
activeResourceIds: ['resource_1', 'resource_2'],
|
|
38
|
+
activeProjectionIds: ['projection_1', 'projection_2'],
|
|
39
|
+
retrievalSettings: {
|
|
40
|
+
topK: 0,
|
|
41
|
+
queryBackend: null,
|
|
42
|
+
persistMemory: true,
|
|
43
|
+
},
|
|
44
|
+
memorySettings: {
|
|
45
|
+
namespace: null,
|
|
46
|
+
enabled: true,
|
|
47
|
+
},
|
|
48
|
+
exportProfileId: 'mobile-slim',
|
|
49
|
+
panelState: {
|
|
50
|
+
lastExecutionAt: '2026-05-26T00:05:00.000Z',
|
|
51
|
+
},
|
|
52
|
+
recordedAt: '2026-05-26T00:05:00.000Z',
|
|
53
|
+
});
|
|
54
|
+
expect(second.sessionStateId).toBe(first.sessionStateId);
|
|
55
|
+
expect(store.listByWorkspace('optics')).toHaveLength(1);
|
|
56
|
+
expect(store.listBySessionIds(['session_a'])[0]?.mode).toBe('study_session');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
describe('frontend settings runtime sync guard', () => {
|
|
39
|
+
const repoRoot = path.resolve(__dirname, '..');
|
|
40
|
+
const settingsPath = path.join(repoRoot, 'src', 'frontend', 'settings.js');
|
|
41
|
+
test('disables runtime hydration and persistence when the page is only a detached frontend preview', () => {
|
|
42
|
+
const source = fs.readFileSync(settingsPath, 'utf8');
|
|
43
|
+
expect(source).toContain('function shouldEnableRuntimeSync()');
|
|
44
|
+
expect(source).toContain('this.runtimeSyncEnabled = shouldEnableRuntimeSync();');
|
|
45
|
+
expect(source).toContain('if (!this.runtimeSyncEnabled) {');
|
|
46
|
+
expect(source).toContain('this.isHydrationComplete = true;');
|
|
47
|
+
expect(source).toContain('if (!this.runtimeSyncEnabled) {');
|
|
48
|
+
expect(source).toContain('return /^https?:\\/\\/(?:127\\.0\\.0\\.1|localhost):3000$/.test(origin);');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
/**
|
|
37
|
+
* Shared types contract test — validates shared type module integrity.
|
|
38
|
+
*/
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
describe('shared types module', () => {
|
|
42
|
+
const sharedDir = path.resolve(__dirname);
|
|
43
|
+
const typesFile = path.join(sharedDir, 'types.ts');
|
|
44
|
+
test('shared types file exists on disk', () => {
|
|
45
|
+
expect(fs.existsSync(typesFile)).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
test('shared types file is under 350 lines (contract boundary discipline, v2.9 expanded for CI contracts)', () => {
|
|
48
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
49
|
+
const lines = source.split('\n').length;
|
|
50
|
+
expect(lines).toBeLessThan(350);
|
|
51
|
+
});
|
|
52
|
+
test('shared types file contains canonical contract documentation', () => {
|
|
53
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
54
|
+
expect(source).toContain('Shared contract types');
|
|
55
|
+
expect(source).toContain('single source of truth');
|
|
56
|
+
expect(source).toContain('API contract boundary');
|
|
57
|
+
});
|
|
58
|
+
test('shared types re-exports from learning/types', () => {
|
|
59
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
60
|
+
expect(source).toContain("from '../learning/types'");
|
|
61
|
+
// Verify key contract types are re-exported
|
|
62
|
+
expect(source).toContain('KnowledgeAtom');
|
|
63
|
+
expect(source).toContain('KnowledgeQueryRequest');
|
|
64
|
+
expect(source).toContain('KnowledgeQueryResponse');
|
|
65
|
+
expect(source).toContain('AgentConversationRequest');
|
|
66
|
+
expect(source).toContain('AgentConversationResponse');
|
|
67
|
+
});
|
|
68
|
+
test('shared types defines RuntimeCapabilityContract', () => {
|
|
69
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
70
|
+
expect(source).toContain('RuntimeCapabilityContract');
|
|
71
|
+
expect(source).toContain('capabilityId');
|
|
72
|
+
expect(source).toContain('status');
|
|
73
|
+
});
|
|
74
|
+
test('shared types defines AgentWorkspaceContract', () => {
|
|
75
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
76
|
+
expect(source).toContain('AgentWorkspaceContract');
|
|
77
|
+
expect(source).toContain('conversationId');
|
|
78
|
+
expect(source).toContain('activePaneId');
|
|
79
|
+
});
|
|
80
|
+
test('shared types defines Notemd CLI and workflow contracts', () => {
|
|
81
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
82
|
+
expect(source).toContain('NotemdCliOperationContract');
|
|
83
|
+
expect(source).toContain('NotemdCliInvocationContract');
|
|
84
|
+
expect(source).toContain('NotemdWorkflowStageContract');
|
|
85
|
+
expect(source).toContain('NotemdWorkflowResultContract');
|
|
86
|
+
});
|
|
87
|
+
test('shared types defines Search and Diagram contracts', () => {
|
|
88
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
89
|
+
expect(source).toContain('SearchResultItemContract');
|
|
90
|
+
expect(source).toContain('SearchResultContract');
|
|
91
|
+
expect(source).toContain('DiagramGenerationContract');
|
|
92
|
+
expect(source).toContain('NotemdSettingsContract');
|
|
93
|
+
});
|
|
94
|
+
test('shared types defines CI and Infrastructure contracts (GitNexus pattern)', () => {
|
|
95
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
96
|
+
expect(source).toContain('CiGateStatusContract');
|
|
97
|
+
expect(source).toContain('CiDashboardContract');
|
|
98
|
+
expect(source).toContain('SnapshotStalenessContract');
|
|
99
|
+
});
|
|
100
|
+
test('SnapshotStalenessContract has staleness tracking fields', () => {
|
|
101
|
+
const source = fs.readFileSync(typesFile, 'utf8');
|
|
102
|
+
expect(source).toContain('lastSaveAt');
|
|
103
|
+
expect(source).toContain('staleSince');
|
|
104
|
+
expect(source).toContain('fileMtime');
|
|
105
|
+
expect(source).toContain('isStale');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* src/shared/types.ts — Shared contract types
|
|
4
|
+
*
|
|
5
|
+
* This module defines and re-exports the canonical types shared between
|
|
6
|
+
* the frontend (.mjs) and backend (.ts) code. These types constitute the
|
|
7
|
+
* API contract boundary and should be treated as the single source of truth
|
|
8
|
+
* for cross-cutting type definitions.
|
|
9
|
+
*
|
|
10
|
+
* === Usage ===
|
|
11
|
+
* - Backend (TypeScript): import type { KnowledgeAtom, ... } from '../shared/types';
|
|
12
|
+
* - Frontend (JavaScript): @typedef {import('../shared/types').KnowledgeAtom} KnowledgeAtom
|
|
13
|
+
*
|
|
14
|
+
* === Design Principle ===
|
|
15
|
+
* Types defined here belong to the API contract, not to any single module.
|
|
16
|
+
* They are versioned with the project and must remain backward-compatible
|
|
17
|
+
* within a major version.
|
|
18
|
+
*
|
|
19
|
+
* @module shared/types
|
|
20
|
+
* @version 2.3.0 — added Notemd CLI, workflow, search, diagram contracts
|
|
21
|
+
*/
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
function runChild(script, args = []) {
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const child = (0, child_process_1.spawn)(process.execPath, ['-e', script, ...args], {
|
|
43
|
+
cwd: path.resolve(__dirname, '..'),
|
|
44
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
45
|
+
windowsHide: true,
|
|
46
|
+
});
|
|
47
|
+
let output = '';
|
|
48
|
+
child.stdout.on('data', (chunk) => { output += String(chunk); });
|
|
49
|
+
child.stderr.on('data', (chunk) => { output += String(chunk); });
|
|
50
|
+
child.once('error', reject);
|
|
51
|
+
child.once('close', (code) => resolve({ code, output }));
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function waitForOutput(childScript, args = []) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const child = (0, child_process_1.spawn)(process.execPath, ['-e', childScript, ...args], {
|
|
57
|
+
cwd: path.resolve(__dirname, '..'),
|
|
58
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
59
|
+
windowsHide: true,
|
|
60
|
+
});
|
|
61
|
+
let output = '';
|
|
62
|
+
let timer;
|
|
63
|
+
const onData = (chunk) => {
|
|
64
|
+
output += String(chunk);
|
|
65
|
+
if (output.includes('acquired')) {
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
child.stdout.off('data', onData);
|
|
68
|
+
resolve({ child, output });
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
child.stdout.on('data', onData);
|
|
72
|
+
child.stderr.on('data', onData);
|
|
73
|
+
timer = setTimeout(() => {
|
|
74
|
+
child.kill();
|
|
75
|
+
reject(new Error(`Timed out waiting for child lock acquisition. Output: ${output}`));
|
|
76
|
+
}, 2500);
|
|
77
|
+
child.once('close', (code) => {
|
|
78
|
+
if (!output.includes('acquired')) {
|
|
79
|
+
clearTimeout(timer);
|
|
80
|
+
reject(new Error(`Lock holder exited before acquisition (code=${String(code)}). Output: ${output}`));
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
child.once('error', reject);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function waitForChildClose(child) {
|
|
87
|
+
if (child.exitCode !== null) {
|
|
88
|
+
return Promise.resolve();
|
|
89
|
+
}
|
|
90
|
+
return new Promise((resolve) => child.once('close', () => resolve()));
|
|
91
|
+
}
|
|
92
|
+
describe('sidecar build lock', () => {
|
|
93
|
+
test('rejects an empty lock path at the boundary', () => {
|
|
94
|
+
expect(() => {
|
|
95
|
+
const { acquireSidecarBuildLock } = require('../scripts/sidecar-build-lock.js');
|
|
96
|
+
acquireSidecarBuildLock('');
|
|
97
|
+
}).toThrow('A concrete sidecar build lock path is required.');
|
|
98
|
+
});
|
|
99
|
+
test('serializes independent ensure processes and releases the lock', async () => {
|
|
100
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-sidecar-lock-'));
|
|
101
|
+
const lockPath = path.join(tempRoot, 'sidecar-build.lock');
|
|
102
|
+
const holderScript = `
|
|
103
|
+
const { acquireSidecarBuildLock } = require('./scripts/sidecar-build-lock.js');
|
|
104
|
+
const lock = acquireSidecarBuildLock(process.argv[1], { timeoutMs: 3000, staleMs: 30000, pollMs: 25 });
|
|
105
|
+
console.log('acquired');
|
|
106
|
+
setTimeout(() => { lock.release(); process.exit(0); }, 800);
|
|
107
|
+
`;
|
|
108
|
+
const waiterScript = `
|
|
109
|
+
const { acquireSidecarBuildLock } = require('./scripts/sidecar-build-lock.js');
|
|
110
|
+
const startedAt = Date.now();
|
|
111
|
+
const lock = acquireSidecarBuildLock(process.argv[1], { timeoutMs: 3000, staleMs: 30000, pollMs: 25 });
|
|
112
|
+
console.log(JSON.stringify({ acquired: true, waitedMs: Date.now() - startedAt }));
|
|
113
|
+
lock.release();
|
|
114
|
+
`;
|
|
115
|
+
try {
|
|
116
|
+
const holder = await waitForOutput(holderScript, [lockPath]);
|
|
117
|
+
const waiter = await runChild(waiterScript, [lockPath]);
|
|
118
|
+
expect(waiter.code).toBe(0);
|
|
119
|
+
expect(waiter.output).toContain('acquired');
|
|
120
|
+
const parsed = JSON.parse(waiter.output.match(/\{[^\n]+\}/)?.[0] || '{}');
|
|
121
|
+
expect(Number(parsed.waitedMs || 0)).toBeGreaterThanOrEqual(250);
|
|
122
|
+
await waitForChildClose(holder.child);
|
|
123
|
+
expect(fs.existsSync(lockPath)).toBe(false);
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
test('reclaims an old lock whose owner process is no longer alive', async () => {
|
|
130
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-sidecar-lock-stale-'));
|
|
131
|
+
const lockPath = path.join(tempRoot, 'sidecar-build.lock');
|
|
132
|
+
try {
|
|
133
|
+
fs.writeFileSync(lockPath, JSON.stringify({
|
|
134
|
+
pid: 2147483647,
|
|
135
|
+
hostname: 'stale-test',
|
|
136
|
+
startedAt: '2020-01-01T00:00:00.000Z',
|
|
137
|
+
token: 'stale-token',
|
|
138
|
+
}), 'utf8');
|
|
139
|
+
const result = await runChild(`
|
|
140
|
+
const { acquireSidecarBuildLock } = require('./scripts/sidecar-build-lock.js');
|
|
141
|
+
const lock = acquireSidecarBuildLock(process.argv[1], { timeoutMs: 500, staleMs: 1, pollMs: 10 });
|
|
142
|
+
console.log('acquired');
|
|
143
|
+
lock.release();
|
|
144
|
+
`, [lockPath]);
|
|
145
|
+
expect(result.code).toBe(0);
|
|
146
|
+
expect(result.output).toContain('acquired');
|
|
147
|
+
expect(fs.existsSync(lockPath)).toBe(false);
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
test('reclaims an old lock owned by a different host without trusting a reused pid', async () => {
|
|
154
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-sidecar-lock-host-'));
|
|
155
|
+
const lockPath = path.join(tempRoot, 'sidecar-build.lock');
|
|
156
|
+
try {
|
|
157
|
+
fs.writeFileSync(lockPath, JSON.stringify({
|
|
158
|
+
pid: process.pid,
|
|
159
|
+
hostname: 'different-host',
|
|
160
|
+
startedAt: '2020-01-01T00:00:00.000Z',
|
|
161
|
+
token: 'foreign-token',
|
|
162
|
+
}), 'utf8');
|
|
163
|
+
const result = await runChild(`
|
|
164
|
+
const { acquireSidecarBuildLock } = require('./scripts/sidecar-build-lock.js');
|
|
165
|
+
const lock = acquireSidecarBuildLock(process.argv[1], { timeoutMs: 500, staleMs: 1, pollMs: 10 });
|
|
166
|
+
console.log('acquired');
|
|
167
|
+
lock.release();
|
|
168
|
+
`, [lockPath]);
|
|
169
|
+
expect(result.code).toBe(0);
|
|
170
|
+
expect(result.output).toContain('acquired');
|
|
171
|
+
expect(fs.existsSync(lockPath)).toBe(false);
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
test('fails closed when a live owner holds the lock past the wait budget', async () => {
|
|
178
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-sidecar-lock-timeout-'));
|
|
179
|
+
const lockPath = path.join(tempRoot, 'sidecar-build.lock');
|
|
180
|
+
const holderScript = `
|
|
181
|
+
const { acquireSidecarBuildLock } = require('./scripts/sidecar-build-lock.js');
|
|
182
|
+
const lock = acquireSidecarBuildLock(process.argv[1], { timeoutMs: 3000, staleMs: 30000, pollMs: 25 });
|
|
183
|
+
console.log('acquired');
|
|
184
|
+
setTimeout(() => { lock.release(); process.exit(0); }, 700);
|
|
185
|
+
`;
|
|
186
|
+
try {
|
|
187
|
+
const holder = await waitForOutput(holderScript, [lockPath]);
|
|
188
|
+
const result = await runChild(`
|
|
189
|
+
const { acquireSidecarBuildLock } = require('./scripts/sidecar-build-lock.js');
|
|
190
|
+
acquireSidecarBuildLock(process.argv[1], { timeoutMs: 100, staleMs: 30000, pollMs: 25 });
|
|
191
|
+
`, [lockPath]);
|
|
192
|
+
expect(result.code).not.toBe(0);
|
|
193
|
+
expect(result.output).toContain('Timed out waiting for sidecar build lock');
|
|
194
|
+
holder.child.kill();
|
|
195
|
+
await waitForChildClose(holder.child);
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
});
|