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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PathBridge = exports.BRIDGE_INBOUND_SCHEMA_LIMITS = exports.BRIDGE_INBOUND_LIMITS = exports.BRIDGE_BACKPRESSURE_LIMITS = void 0;
|
|
3
|
+
exports.PathBridge = exports.BRIDGE_INBOUND_SCHEMA_LIMITS = exports.BRIDGE_INBOUND_LIMITS = exports.BRIDGE_BACKPRESSURE_LIMITS = exports.PATH_BRIDGE_CAPABILITIES = exports.PATH_BRIDGE_PROTOCOL_VERSION = void 0;
|
|
4
4
|
exports.resolveBridgeInboundSchemaPolicy = resolveBridgeInboundSchemaPolicy;
|
|
5
5
|
exports.resolveBridgeInboundLimitConfig = resolveBridgeInboundLimitConfig;
|
|
6
6
|
exports.parseBridgeInboundEnvelope = parseBridgeInboundEnvelope;
|
|
@@ -8,10 +8,21 @@ exports.computeBridgePathFingerprint = computeBridgePathFingerprint;
|
|
|
8
8
|
exports.buildBridgePathTransportSummary = buildBridgePathTransportSummary;
|
|
9
9
|
exports.validateBridgePathPayload = validateBridgePathPayload;
|
|
10
10
|
const ws_1 = require("ws");
|
|
11
|
+
/** Versioned, host-neutral contract shared by Web, Tauri, Capacitor, and Godot. */
|
|
12
|
+
exports.PATH_BRIDGE_PROTOCOL_VERSION = '2.0';
|
|
13
|
+
exports.PATH_BRIDGE_CAPABILITIES = Object.freeze([
|
|
14
|
+
'path.transport',
|
|
15
|
+
'knowledge.analyze',
|
|
16
|
+
'knowledge.query',
|
|
17
|
+
'knowledge.readEvidence',
|
|
18
|
+
'knowledge.exportBundle',
|
|
19
|
+
'request.cancel',
|
|
20
|
+
]);
|
|
11
21
|
const PATH_REQUEST_TIMEOUT_MS = 3000;
|
|
12
22
|
const PATH_PRODUCER_GRACE_MS = 30000;
|
|
13
23
|
const MERMAID_RENDER_TIMEOUT_MS = 12000;
|
|
14
24
|
const UNAUTHORIZED_CLIENT_TIMEOUT_MS = 5000;
|
|
25
|
+
const DEFAULT_HOST_OPERATION_TIMEOUT_MS = 15000;
|
|
15
26
|
const BYTES_PER_MIB = 1024 * 1024;
|
|
16
27
|
const DEFAULT_INBOUND_MESSAGE_LIMIT_MIB = 128;
|
|
17
28
|
const LARGE_GRAPH_INBOUND_MESSAGE_LIMIT_MIB = 256;
|
|
@@ -131,6 +142,12 @@ const PATH_MUTATION_TYPES = new Set([
|
|
|
131
142
|
const KNOWN_BRIDGE_MESSAGE_TYPES = new Set([
|
|
132
143
|
'authenticate',
|
|
133
144
|
'identify',
|
|
145
|
+
'capabilities',
|
|
146
|
+
'analyze',
|
|
147
|
+
'query',
|
|
148
|
+
'readEvidence',
|
|
149
|
+
'exportBundle',
|
|
150
|
+
'cancel',
|
|
134
151
|
'requestPath',
|
|
135
152
|
'pathResult',
|
|
136
153
|
'pathStatus',
|
|
@@ -185,8 +202,8 @@ const ALLOWED_BACKGROUND_FILE_EXTENSIONS = ['.exr', '.hdr'];
|
|
|
185
202
|
const CONFIG_TARGET_ID_MAX_LENGTH = 512;
|
|
186
203
|
const CONFIG_SHORTCUT_MAX_LENGTH = 64;
|
|
187
204
|
const CONFIG_BACKGROUND_MAX_LENGTH = 128;
|
|
188
|
-
const CONFIG_BRIGHTNESS_MIN = 0.
|
|
189
|
-
const CONFIG_BRIGHTNESS_MAX =
|
|
205
|
+
const CONFIG_BRIGHTNESS_MIN = 0.0;
|
|
206
|
+
const CONFIG_BRIGHTNESS_MAX = 0.1;
|
|
190
207
|
const CONFIG_READER_MEDIA_SCALE_MIN = 0.1;
|
|
191
208
|
const CONFIG_READER_MEDIA_SCALE_MAX = 3.0;
|
|
192
209
|
const CONFIG_CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
@@ -247,6 +264,74 @@ function validateRequestPathPayload(payload) {
|
|
|
247
264
|
}
|
|
248
265
|
return null;
|
|
249
266
|
}
|
|
267
|
+
const BRIDGE_CORRELATION_ID_MAX_LENGTH = 128;
|
|
268
|
+
const BRIDGE_CAPABILITY_MAX_COUNT = 32;
|
|
269
|
+
function validateCorrelationId(value, field) {
|
|
270
|
+
if (value === undefined) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
if (!isNonEmptyString(value)) {
|
|
274
|
+
return `${field} must be a non-empty string when provided.`;
|
|
275
|
+
}
|
|
276
|
+
if (value.trim().length > BRIDGE_CORRELATION_ID_MAX_LENGTH) {
|
|
277
|
+
return `${field} must be at most ${BRIDGE_CORRELATION_ID_MAX_LENGTH} characters.`;
|
|
278
|
+
}
|
|
279
|
+
if (/[\u0000-\u001f\u007f]/.test(value)) {
|
|
280
|
+
return `${field} must not contain control characters.`;
|
|
281
|
+
}
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
function validateCapabilitiesPayload(payload) {
|
|
285
|
+
if (payload.protocolVersion !== undefined && !isNonEmptyString(payload.protocolVersion)) {
|
|
286
|
+
return 'capabilities payload.protocolVersion must be a non-empty string when provided.';
|
|
287
|
+
}
|
|
288
|
+
if (payload.client !== undefined && !isNonEmptyString(payload.client)) {
|
|
289
|
+
return 'capabilities payload.client must be a non-empty string when provided.';
|
|
290
|
+
}
|
|
291
|
+
if (!Array.isArray(payload.capabilities)) {
|
|
292
|
+
return 'capabilities payload.capabilities must be an array.';
|
|
293
|
+
}
|
|
294
|
+
if (payload.capabilities.length > BRIDGE_CAPABILITY_MAX_COUNT) {
|
|
295
|
+
return `capabilities payload.capabilities must contain at most ${BRIDGE_CAPABILITY_MAX_COUNT} entries.`;
|
|
296
|
+
}
|
|
297
|
+
if (payload.capabilities.some((capability) => !isNonEmptyString(capability))) {
|
|
298
|
+
return 'capabilities payload.capabilities must contain only non-empty strings.';
|
|
299
|
+
}
|
|
300
|
+
if (payload.maxPayloadBytes !== undefined && !isFiniteNumberValue(payload.maxPayloadBytes)) {
|
|
301
|
+
return 'capabilities payload.maxPayloadBytes must be a finite number when provided.';
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
function validateAnalysisRequestPayload(type, payload) {
|
|
306
|
+
if (!isRecord(payload)) {
|
|
307
|
+
return `${type} payload must be an object.`;
|
|
308
|
+
}
|
|
309
|
+
const requestIdError = validateCorrelationId(payload.requestId, `${type} payload.requestId`);
|
|
310
|
+
if (requestIdError) {
|
|
311
|
+
return requestIdError;
|
|
312
|
+
}
|
|
313
|
+
if (type !== 'cancel' && !isNonEmptyString(payload.requestId)) {
|
|
314
|
+
return `${type} payload.requestId must be a non-empty string.`;
|
|
315
|
+
}
|
|
316
|
+
if (payload.workspaceId !== undefined && !isNonEmptyString(payload.workspaceId)) {
|
|
317
|
+
return `${type} payload.workspaceId must be a non-empty string when provided.`;
|
|
318
|
+
}
|
|
319
|
+
if (payload.sourceUri !== undefined && !isNonEmptyString(payload.sourceUri)) {
|
|
320
|
+
return `${type} payload.sourceUri must be a non-empty string when provided.`;
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
function identifyPayloadCapabilitiesInvalid(payload) {
|
|
325
|
+
if (payload.capabilities === undefined) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
return validateCapabilitiesPayload({
|
|
329
|
+
protocolVersion: payload.protocolVersion,
|
|
330
|
+
client: payload.client ?? payload.tag,
|
|
331
|
+
capabilities: payload.capabilities,
|
|
332
|
+
maxPayloadBytes: payload.maxPayloadBytes,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
250
335
|
function validatePathStatusPayload(payload) {
|
|
251
336
|
if (payload.level !== undefined && !isValidBridgeStatusLevel(payload.level)) {
|
|
252
337
|
return 'pathStatus payload.level must be one of info/success/warning/error when provided.';
|
|
@@ -485,8 +570,22 @@ function validateKnownEnvelopePayload(type, payload, policy) {
|
|
|
485
570
|
if (client !== undefined && typeof client !== 'string') {
|
|
486
571
|
return `${type} client/tag must be a string when provided.`;
|
|
487
572
|
}
|
|
573
|
+
if (identifyPayloadCapabilitiesInvalid(payload)) {
|
|
574
|
+
return identifyPayloadCapabilitiesInvalid(payload);
|
|
575
|
+
}
|
|
488
576
|
}
|
|
489
577
|
return null;
|
|
578
|
+
case 'capabilities':
|
|
579
|
+
if (!isRecord(payload)) {
|
|
580
|
+
return 'capabilities payload must be an object.';
|
|
581
|
+
}
|
|
582
|
+
return validateCapabilitiesPayload(payload);
|
|
583
|
+
case 'analyze':
|
|
584
|
+
case 'query':
|
|
585
|
+
case 'readEvidence':
|
|
586
|
+
case 'exportBundle':
|
|
587
|
+
case 'cancel':
|
|
588
|
+
return validateAnalysisRequestPayload(type, payload);
|
|
490
589
|
case 'requestPath':
|
|
491
590
|
if (payload !== undefined && !isRecord(payload)) {
|
|
492
591
|
return 'requestPath payload must be an object when provided.';
|
|
@@ -593,6 +692,9 @@ function parseBridgeInboundEnvelope(data, policy = BRIDGE_INBOUND_SCHEMA_POLICY)
|
|
|
593
692
|
payload: data.payload,
|
|
594
693
|
token: data.token,
|
|
595
694
|
client: data.client,
|
|
695
|
+
version: data.version,
|
|
696
|
+
requestId: data.requestId,
|
|
697
|
+
correlationId: data.correlationId,
|
|
596
698
|
};
|
|
597
699
|
if (envelope.token !== undefined && typeof envelope.token !== 'string') {
|
|
598
700
|
return {
|
|
@@ -606,6 +708,18 @@ function parseBridgeInboundEnvelope(data, policy = BRIDGE_INBOUND_SCHEMA_POLICY)
|
|
|
606
708
|
reason: 'Bridge message client must be a string when provided.',
|
|
607
709
|
};
|
|
608
710
|
}
|
|
711
|
+
const versionError = validateCorrelationId(envelope.version, 'Bridge message version');
|
|
712
|
+
if (versionError) {
|
|
713
|
+
return { ok: false, reason: versionError };
|
|
714
|
+
}
|
|
715
|
+
const requestIdError = validateCorrelationId(envelope.requestId, 'Bridge message requestId');
|
|
716
|
+
if (requestIdError) {
|
|
717
|
+
return { ok: false, reason: requestIdError };
|
|
718
|
+
}
|
|
719
|
+
const correlationIdError = validateCorrelationId(envelope.correlationId, 'Bridge message correlationId');
|
|
720
|
+
if (correlationIdError) {
|
|
721
|
+
return { ok: false, reason: correlationIdError };
|
|
722
|
+
}
|
|
609
723
|
const isKnownType = KNOWN_BRIDGE_MESSAGE_TYPES.has(type);
|
|
610
724
|
if (!isKnownType && policy.rejectUnknownTypes) {
|
|
611
725
|
return {
|
|
@@ -828,20 +942,45 @@ class PathBridge {
|
|
|
828
942
|
this.currentPath = null;
|
|
829
943
|
this.pendingPathRequests = new Map();
|
|
830
944
|
this.pendingMermaidRenderRequests = new Map();
|
|
945
|
+
this.pendingHostOperations = new Map();
|
|
831
946
|
this.unauthorizedClientTimers = new Map();
|
|
832
947
|
this.outboundQueueState = new Map();
|
|
833
948
|
this.nextMermaidRenderRequestId = 1;
|
|
949
|
+
this.readySettled = false;
|
|
834
950
|
const resolvedOptions = typeof options === 'number'
|
|
835
951
|
? { port: options }
|
|
836
952
|
: (options || {});
|
|
837
|
-
this.port = resolvedOptions.port
|
|
953
|
+
this.port = Number.isFinite(Number(resolvedOptions.port))
|
|
954
|
+
? Number(resolvedOptions.port)
|
|
955
|
+
: 9876;
|
|
838
956
|
this.host = resolvedOptions.host || '127.0.0.1';
|
|
839
957
|
this.authToken = typeof resolvedOptions.authToken === 'string' ? resolvedOptions.authToken.trim() : '';
|
|
958
|
+
this.hostAdapter = resolvedOptions.hostAdapter || null;
|
|
959
|
+
this.hostOperationTimeoutMs = Number.isFinite(Number(resolvedOptions.hostOperationTimeoutMs)) &&
|
|
960
|
+
Number(resolvedOptions.hostOperationTimeoutMs) > 0
|
|
961
|
+
? Math.floor(Number(resolvedOptions.hostOperationTimeoutMs))
|
|
962
|
+
: DEFAULT_HOST_OPERATION_TIMEOUT_MS;
|
|
840
963
|
this.wss = new ws_1.WebSocketServer({
|
|
841
964
|
port: this.port,
|
|
842
965
|
host: this.host,
|
|
843
966
|
maxPayload: MAX_INBOUND_MESSAGE_BYTES,
|
|
844
967
|
});
|
|
968
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
969
|
+
this.wss.once('listening', () => {
|
|
970
|
+
this.readySettled = true;
|
|
971
|
+
const address = this.wss.address();
|
|
972
|
+
if (address && typeof address === 'object') {
|
|
973
|
+
this.port = Number(address.port || this.port);
|
|
974
|
+
}
|
|
975
|
+
resolve();
|
|
976
|
+
});
|
|
977
|
+
this.wss.once('error', (error) => {
|
|
978
|
+
if (!this.readySettled) {
|
|
979
|
+
this.readySettled = true;
|
|
980
|
+
reject(error);
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
});
|
|
845
984
|
console.log(`[PathBridge] WebSocket Server started on ws://${this.host}:${this.port}`);
|
|
846
985
|
console.log(`[PathBridge] Inbound frame limit ${BRIDGE_INBOUND_LIMIT_CONFIG.selectedMessageMb} MiB ` +
|
|
847
986
|
`(recommended=${BRIDGE_INBOUND_LIMIT_CONFIG.recommendedMessageMb} MiB, ` +
|
|
@@ -899,6 +1038,7 @@ class PathBridge {
|
|
|
899
1038
|
const wasProducer = !!meta && this.isPathProducerTag(meta.tag);
|
|
900
1039
|
this.clearUnauthorizedDisconnect(ws);
|
|
901
1040
|
this.clearPendingPathRequest(ws);
|
|
1041
|
+
this.cancelHostOperationsForClient(ws, 'client_disconnected');
|
|
902
1042
|
this.clearOutboundQueueState(ws);
|
|
903
1043
|
this.clients.delete(ws);
|
|
904
1044
|
this.clientMeta.delete(ws);
|
|
@@ -1168,7 +1308,11 @@ class PathBridge {
|
|
|
1168
1308
|
}
|
|
1169
1309
|
let serialized = '';
|
|
1170
1310
|
try {
|
|
1171
|
-
serialized = JSON.stringify({
|
|
1311
|
+
serialized = JSON.stringify({
|
|
1312
|
+
type,
|
|
1313
|
+
payload,
|
|
1314
|
+
version: exports.PATH_BRIDGE_PROTOCOL_VERSION,
|
|
1315
|
+
});
|
|
1172
1316
|
}
|
|
1173
1317
|
catch (error) {
|
|
1174
1318
|
const meta = this.clientMeta.get(client);
|
|
@@ -1202,6 +1346,15 @@ class PathBridge {
|
|
|
1202
1346
|
sendStatus(client, payload) {
|
|
1203
1347
|
this.sendMessage(client, 'pathStatus', payload);
|
|
1204
1348
|
}
|
|
1349
|
+
sendCapabilities(client) {
|
|
1350
|
+
this.sendMessage(client, 'capabilities', {
|
|
1351
|
+
protocolVersion: exports.PATH_BRIDGE_PROTOCOL_VERSION,
|
|
1352
|
+
capabilities: [...exports.PATH_BRIDGE_CAPABILITIES],
|
|
1353
|
+
maxPayloadBytes: MAX_INBOUND_MESSAGE_BYTES,
|
|
1354
|
+
hostAdapter: Boolean(this.hostAdapter),
|
|
1355
|
+
hostOperationTimeoutMs: this.hostOperationTimeoutMs,
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1205
1358
|
notifyPendingPathRequests(payload) {
|
|
1206
1359
|
const pendingClients = Array.from(this.pendingPathRequests.keys());
|
|
1207
1360
|
pendingClients.forEach((client) => this.sendStatus(client, payload));
|
|
@@ -1220,6 +1373,17 @@ class PathBridge {
|
|
|
1220
1373
|
clearAllPendingPathRequests() {
|
|
1221
1374
|
Array.from(this.pendingPathRequests.keys()).forEach((client) => this.clearPendingPathRequest(client));
|
|
1222
1375
|
}
|
|
1376
|
+
cancelHostOperationsForClient(client, reason) {
|
|
1377
|
+
Array.from(this.pendingHostOperations.entries()).forEach(([requestId, pending]) => {
|
|
1378
|
+
if (pending.client !== client) {
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
clearTimeout(pending.timer);
|
|
1382
|
+
pending.controller.abort(reason);
|
|
1383
|
+
this.pendingHostOperations.delete(requestId);
|
|
1384
|
+
this.hostAdapter?.cancel?.(requestId);
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1223
1387
|
invalidateCurrentPath(reason) {
|
|
1224
1388
|
if (!this.currentPath) {
|
|
1225
1389
|
return;
|
|
@@ -1432,6 +1596,101 @@ class PathBridge {
|
|
|
1432
1596
|
}
|
|
1433
1597
|
this.broadcast('pathStatus', status);
|
|
1434
1598
|
}
|
|
1599
|
+
sendHostOperationResult(client, operationType, requestId, correlationId, result, error, cancelled = false) {
|
|
1600
|
+
this.sendMessage(client, 'operationResult', {
|
|
1601
|
+
type: operationType,
|
|
1602
|
+
requestId,
|
|
1603
|
+
correlationId,
|
|
1604
|
+
ok: !error && !cancelled,
|
|
1605
|
+
cancelled,
|
|
1606
|
+
result: error || cancelled ? undefined : result,
|
|
1607
|
+
error: error || (cancelled ? 'operation_cancelled' : undefined),
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
executeHostOperation(type, payloadLike, envelope, sender) {
|
|
1611
|
+
if (!this.hostAdapter || !isRecord(payloadLike)) {
|
|
1612
|
+
this.broadcast(type, payloadLike || {});
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
const requestId = typeof payloadLike.requestId === 'string' ? payloadLike.requestId.trim() : '';
|
|
1616
|
+
if (!requestId) {
|
|
1617
|
+
this.sendHostOperationResult(sender, type, '', '', undefined, 'request_id_required');
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
if (this.pendingHostOperations.has(requestId)) {
|
|
1621
|
+
this.sendHostOperationResult(sender, type, requestId, requestId, undefined, 'request_in_flight');
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
const controller = new AbortController();
|
|
1625
|
+
const correlationId = typeof envelope.correlationId === 'string' && envelope.correlationId.trim()
|
|
1626
|
+
? envelope.correlationId.trim()
|
|
1627
|
+
: requestId;
|
|
1628
|
+
const timer = setTimeout(() => {
|
|
1629
|
+
const pending = this.pendingHostOperations.get(requestId);
|
|
1630
|
+
if (!pending) {
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
this.pendingHostOperations.delete(requestId);
|
|
1634
|
+
pending.controller.abort();
|
|
1635
|
+
this.hostAdapter?.cancel?.(requestId);
|
|
1636
|
+
this.sendHostOperationResult(sender, type, requestId, correlationId, undefined, 'operation_timeout');
|
|
1637
|
+
}, this.hostOperationTimeoutMs);
|
|
1638
|
+
const pending = { client: sender, controller, timer };
|
|
1639
|
+
this.pendingHostOperations.set(requestId, pending);
|
|
1640
|
+
const clientTag = this.clientMeta.get(sender)?.tag || 'unknown';
|
|
1641
|
+
Promise.resolve()
|
|
1642
|
+
.then(() => this.hostAdapter.execute({
|
|
1643
|
+
type,
|
|
1644
|
+
requestId,
|
|
1645
|
+
correlationId,
|
|
1646
|
+
payload: payloadLike,
|
|
1647
|
+
clientTag,
|
|
1648
|
+
signal: controller.signal,
|
|
1649
|
+
}))
|
|
1650
|
+
.then((result) => {
|
|
1651
|
+
if (this.pendingHostOperations.get(requestId) !== pending) {
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
this.pendingHostOperations.delete(requestId);
|
|
1655
|
+
clearTimeout(timer);
|
|
1656
|
+
this.sendHostOperationResult(sender, type, requestId, correlationId, result);
|
|
1657
|
+
})
|
|
1658
|
+
.catch((error) => {
|
|
1659
|
+
if (this.pendingHostOperations.get(requestId) !== pending) {
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
this.pendingHostOperations.delete(requestId);
|
|
1663
|
+
clearTimeout(timer);
|
|
1664
|
+
const message = controller.signal.aborted ? 'operation_cancelled' : String(error?.message || error);
|
|
1665
|
+
this.sendHostOperationResult(sender, type, requestId, correlationId, undefined, message, controller.signal.aborted);
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
cancelHostOperation(payloadLike, sender) {
|
|
1669
|
+
if (!this.hostAdapter) {
|
|
1670
|
+
// Preserve the pre-adapter relay contract for legacy clients,
|
|
1671
|
+
// including cancellation frames without a request ID.
|
|
1672
|
+
this.broadcast('cancel', payloadLike || {});
|
|
1673
|
+
return;
|
|
1674
|
+
}
|
|
1675
|
+
const payload = isRecord(payloadLike) ? payloadLike : {};
|
|
1676
|
+
const requestId = typeof payload.requestId === 'string' ? payload.requestId.trim() : '';
|
|
1677
|
+
if (!requestId) {
|
|
1678
|
+
this.sendHostOperationResult(sender, 'cancel', '', '', undefined, 'request_id_required');
|
|
1679
|
+
return;
|
|
1680
|
+
}
|
|
1681
|
+
const pending = this.pendingHostOperations.get(requestId);
|
|
1682
|
+
if (pending) {
|
|
1683
|
+
clearTimeout(pending.timer);
|
|
1684
|
+
pending.controller.abort();
|
|
1685
|
+
this.pendingHostOperations.delete(requestId);
|
|
1686
|
+
this.hostAdapter?.cancel?.(requestId);
|
|
1687
|
+
this.sendHostOperationResult(sender, 'cancel', requestId, requestId, undefined, undefined, true);
|
|
1688
|
+
}
|
|
1689
|
+
else {
|
|
1690
|
+
this.sendHostOperationResult(sender, 'cancel', requestId, requestId, undefined, 'operation_not_found');
|
|
1691
|
+
}
|
|
1692
|
+
this.broadcast('cancel', payloadLike || {});
|
|
1693
|
+
}
|
|
1435
1694
|
handleMessage(envelope, sender) {
|
|
1436
1695
|
const { type, payload, client } = envelope;
|
|
1437
1696
|
console.log(`[PathBridge] Received: ${type}`);
|
|
@@ -1442,8 +1701,25 @@ class PathBridge {
|
|
|
1442
1701
|
const identifyPayload = isRecord(payload) ? payload : {};
|
|
1443
1702
|
const requestedTag = identifyPayload.client ?? identifyPayload.tag ?? client ?? 'unknown';
|
|
1444
1703
|
this.setClientTag(sender, String(requestedTag));
|
|
1704
|
+
this.sendCapabilities(sender);
|
|
1445
1705
|
break;
|
|
1446
1706
|
}
|
|
1707
|
+
case 'capabilities':
|
|
1708
|
+
// Capability advertisements are relayed so a Web/Tauri client
|
|
1709
|
+
// can negotiate the same contract with Godot or a mobile peer.
|
|
1710
|
+
this.broadcast('capabilities', payload || {});
|
|
1711
|
+
break;
|
|
1712
|
+
case 'analyze':
|
|
1713
|
+
case 'query':
|
|
1714
|
+
case 'readEvidence':
|
|
1715
|
+
case 'exportBundle':
|
|
1716
|
+
// The optional host adapter owns domain execution while the
|
|
1717
|
+
// bridge retains correlation, timeout, and cancellation policy.
|
|
1718
|
+
this.executeHostOperation(type, payload, envelope, sender);
|
|
1719
|
+
break;
|
|
1720
|
+
case 'cancel':
|
|
1721
|
+
this.cancelHostOperation(payload, sender);
|
|
1722
|
+
break;
|
|
1447
1723
|
case 'requestPath':
|
|
1448
1724
|
this.handlePathRequest(sender);
|
|
1449
1725
|
break;
|
|
@@ -1580,6 +1856,12 @@ class PathBridge {
|
|
|
1580
1856
|
}
|
|
1581
1857
|
close() {
|
|
1582
1858
|
this.clearAllPendingPathRequests();
|
|
1859
|
+
Array.from(this.pendingHostOperations.entries()).forEach(([requestId, pending]) => {
|
|
1860
|
+
clearTimeout(pending.timer);
|
|
1861
|
+
pending.controller.abort();
|
|
1862
|
+
this.hostAdapter?.cancel?.(requestId);
|
|
1863
|
+
});
|
|
1864
|
+
this.pendingHostOperations.clear();
|
|
1583
1865
|
Array.from(this.pendingMermaidRenderRequests.values()).forEach((pendingRequest) => {
|
|
1584
1866
|
clearTimeout(pendingRequest.timer);
|
|
1585
1867
|
pendingRequest.reject(new Error('PathBridge is closing before Mermaid render completed.'));
|
|
@@ -1591,5 +1873,11 @@ class PathBridge {
|
|
|
1591
1873
|
this.clients.clear();
|
|
1592
1874
|
this.clientMeta.clear();
|
|
1593
1875
|
}
|
|
1876
|
+
waitUntilReady() {
|
|
1877
|
+
return this.readyPromise;
|
|
1878
|
+
}
|
|
1879
|
+
getPort() {
|
|
1880
|
+
return this.port;
|
|
1881
|
+
}
|
|
1594
1882
|
}
|
|
1595
1883
|
exports.PathBridge = PathBridge;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeResourceReference = normalizeResourceReference;
|
|
4
|
+
/**
|
|
5
|
+
* Shared, host-neutral reference normalization used by Graph and backend
|
|
6
|
+
* identity generation. It intentionally has no Node or platform dependency so
|
|
7
|
+
* the path-core bundle can execute in a browser VM and in Godot WebView.
|
|
8
|
+
*/
|
|
9
|
+
function normalizeResourceReference(reference) {
|
|
10
|
+
if (typeof reference !== 'string') {
|
|
11
|
+
throw new Error('Resource reference must be a string');
|
|
12
|
+
}
|
|
13
|
+
if (reference.includes('\0')) {
|
|
14
|
+
throw new Error('Resource reference must not contain NUL characters');
|
|
15
|
+
}
|
|
16
|
+
return reference.normalize('NFC').replace(/\\/g, '/').toLowerCase();
|
|
17
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
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
|
+
jest.mock('child_process', () => ({ ...jest.requireActual('child_process'), spawnSync: jest.fn() }));
|
|
41
|
+
const { prepareDesktopGodotPack } = require('../scripts/prepare-desktop-godot-pack');
|
|
42
|
+
const { resolveHostGodotBinaryName } = require('../scripts/tauri-sidecar-utils');
|
|
43
|
+
const runtime = require('../config/godot-desktop-runtime.json');
|
|
44
|
+
const spawn = child_process_1.spawnSync;
|
|
45
|
+
describe('desktop Godot resource package', () => {
|
|
46
|
+
let directory;
|
|
47
|
+
const write = (relative, content) => {
|
|
48
|
+
const file = path.join(directory, relative);
|
|
49
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
50
|
+
fs.writeFileSync(file, content);
|
|
51
|
+
};
|
|
52
|
+
beforeEach(() => {
|
|
53
|
+
directory = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'desktop-godot-pack-'));
|
|
54
|
+
write('path_mode/project.godot', `config_version=5\nconfig/features=PackedStringArray("${runtime.version}", "Forward+")\n`);
|
|
55
|
+
write('path_mode/export_presets.cfg', '[preset.0]\nname="Windows Desktop"\n');
|
|
56
|
+
write('path_mode/scenes/main.tscn', '[gd_scene format=3]\n');
|
|
57
|
+
write('path_mode/scripts/main.gd', 'extends Node\n');
|
|
58
|
+
write('path_mode/shaders/main.gdshader', 'shader_type spatial;\n');
|
|
59
|
+
write('path_mode/assets/background.exr', 'fixture');
|
|
60
|
+
write(`src-tauri/bin/${resolveHostGodotBinaryName()}`, 'engine fixture');
|
|
61
|
+
spawn.mockImplementation((_executable, args) => {
|
|
62
|
+
if (args.includes('--version'))
|
|
63
|
+
return { status: 0, stdout: `${runtime.version}.stable.official.fixture\n`, stderr: '' };
|
|
64
|
+
if (args.includes('--export-pack'))
|
|
65
|
+
fs.writeFileSync(args[args.length - 1], 'GDPC resource fixture');
|
|
66
|
+
return { status: 0, stdout: 'done\n', stderr: '' };
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
afterEach(() => {
|
|
70
|
+
jest.resetAllMocks();
|
|
71
|
+
fs.rmSync(directory, { recursive: true, force: true });
|
|
72
|
+
});
|
|
73
|
+
test('exports only runtime roots and binds the pack to source and engine bytes', () => {
|
|
74
|
+
write('path_mode/.godot/editor/state', 'private editor state');
|
|
75
|
+
write('path_mode/tmp/debug.gd', 'debug fixture');
|
|
76
|
+
const report = prepareDesktopGodotPack(directory);
|
|
77
|
+
expect(report.engineVersion).toBe(`${runtime.version}.stable.official.fixture`);
|
|
78
|
+
expect(report.pack.path).toBe('output/desktop/path_mode.pck');
|
|
79
|
+
expect(report.pack.sha256).toMatch(/^[a-f0-9]{64}$/);
|
|
80
|
+
expect(report.files.map((file) => file.path)).toEqual(expect.arrayContaining(['project.godot', 'scenes/main.tscn', 'assets/background.exr']));
|
|
81
|
+
expect(report.files.some((file) => /editor|tmp/.test(file.path))).toBe(false);
|
|
82
|
+
expect(fs.readdirSync(path.join(directory, 'output/desktop')).some(name => name.startsWith('.pathmode-'))).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
test('rejects an engine version that cannot consume the declared project', () => {
|
|
85
|
+
spawn.mockReturnValue({ status: 0, stdout: '4.3.stable.official.fixture\n', stderr: '' });
|
|
86
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/requires Godot/);
|
|
87
|
+
expect(spawn).toHaveBeenCalledTimes(1);
|
|
88
|
+
});
|
|
89
|
+
test('rejects SVG before invoking Godot import', () => {
|
|
90
|
+
write('path_mode/assets/unsupported.svg', '<svg/>');
|
|
91
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/cannot import SVG/);
|
|
92
|
+
expect(spawn.mock.calls.every(([, args]) => args.includes('--version'))).toBe(true);
|
|
93
|
+
});
|
|
94
|
+
test('does not accept zero exit after a Godot script error or replace an earlier pack', () => {
|
|
95
|
+
write('output/desktop/path_mode.pck', 'GDPC previous pack');
|
|
96
|
+
spawn.mockImplementation((_executable, args) => args.includes('--version')
|
|
97
|
+
? { status: 0, stdout: `${runtime.version}.stable.official.fixture`, stderr: '' }
|
|
98
|
+
: { status: 0, stdout: '', stderr: '\u001b[31mSCRIPT ERROR: invalid exported script\u001b[0m\n' });
|
|
99
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/invalid exported script/);
|
|
100
|
+
expect(fs.readFileSync(path.join(directory, 'output/desktop/path_mode.pck'), 'utf8')).toBe('GDPC previous pack');
|
|
101
|
+
});
|
|
102
|
+
test('rejects successful export without a resource pack', () => {
|
|
103
|
+
spawn.mockImplementation((_executable, args) => ({ status: 0, stdout: args.includes('--version') ? `${runtime.version}.stable.official.fixture` : '', stderr: '' }));
|
|
104
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/ENOENT/);
|
|
105
|
+
});
|
|
106
|
+
test('rejects an invalid pack header', () => {
|
|
107
|
+
spawn.mockImplementation((_executable, args) => {
|
|
108
|
+
if (args.includes('--export-pack'))
|
|
109
|
+
fs.writeFileSync(args[args.length - 1], 'not a pack');
|
|
110
|
+
return { status: 0, stdout: args.includes('--version') ? `${runtime.version}.stable.official.fixture` : '', stderr: '' };
|
|
111
|
+
});
|
|
112
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/PCK resource pack/);
|
|
113
|
+
});
|
|
114
|
+
test('rejects source changes between staging and publication', () => {
|
|
115
|
+
spawn.mockImplementation((_executable, args) => {
|
|
116
|
+
if (args.includes('--export-pack')) {
|
|
117
|
+
fs.writeFileSync(args[args.length - 1], 'GDPC resource fixture');
|
|
118
|
+
write('path_mode/scripts/main.gd', 'extends Node\n# changed during export\n');
|
|
119
|
+
}
|
|
120
|
+
return { status: 0, stdout: args.includes('--version') ? `${runtime.version}.stable.official.fixture` : '', stderr: '' };
|
|
121
|
+
});
|
|
122
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/inputs changed/);
|
|
123
|
+
expect(fs.existsSync(path.join(directory, 'output/desktop/path_mode.pck'))).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
test('rejects files added between staging and publication', () => {
|
|
126
|
+
spawn.mockImplementation((_executable, args) => {
|
|
127
|
+
if (args.includes('--export-pack')) {
|
|
128
|
+
fs.writeFileSync(args[args.length - 1], 'GDPC resource fixture');
|
|
129
|
+
write('path_mode/scripts/added.gd', 'extends Node\n');
|
|
130
|
+
}
|
|
131
|
+
return { status: 0, stdout: args.includes('--version') ? `${runtime.version}.stable.official.fixture` : '', stderr: '' };
|
|
132
|
+
});
|
|
133
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/inputs changed/);
|
|
134
|
+
expect(fs.existsSync(path.join(directory, 'output/desktop/path_mode.pck'))).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
test('rejects a symlinked runtime root before importing external files', () => {
|
|
137
|
+
fs.renameSync(path.join(directory, 'path_mode/assets'), path.join(directory, 'external-assets'));
|
|
138
|
+
fs.symlinkSync(path.join(directory, 'external-assets'), path.join(directory, 'path_mode/assets'), process.platform === 'win32' ? 'junction' : 'dir');
|
|
139
|
+
expect(() => prepareDesktopGodotPack(directory)).toThrow(/must not be a symlink/);
|
|
140
|
+
expect(spawn.mock.calls.every(([, args]) => args.includes('--version'))).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
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 vm = __importStar(require("node:vm"));
|
|
37
|
+
const { waitForAcceptanceGraph } = require('../scripts/verify-desktop-runtime');
|
|
38
|
+
const graph = { nodes: [{ id: 'Alpha' }, { id: 'Beta' }], edges: [{ source: 'Alpha', target: 'Beta' }] };
|
|
39
|
+
test('waits past the build reload and returns one snapshot from the ready document', async () => {
|
|
40
|
+
const pages = [
|
|
41
|
+
vm.createContext({ performance: { timeOrigin: 100 }, graphData: graph }),
|
|
42
|
+
vm.createContext({ performance: { timeOrigin: 200 } }),
|
|
43
|
+
vm.createContext({ performance: { timeOrigin: 200 }, graphData: graph }),
|
|
44
|
+
];
|
|
45
|
+
const evaluate = jest.fn(async (expression) => {
|
|
46
|
+
const page = pages.shift();
|
|
47
|
+
if (!page)
|
|
48
|
+
throw new Error('Unexpected extra WebView read');
|
|
49
|
+
return vm.runInContext(expression, page);
|
|
50
|
+
});
|
|
51
|
+
await expect(waitForAcceptanceGraph({ evaluate }, 100)).resolves.toEqual({ nodes: ['Alpha', 'Beta'], edges: 1 });
|
|
52
|
+
expect(evaluate).toHaveBeenCalledTimes(3);
|
|
53
|
+
});
|
|
54
|
+
test('captures lexical graphData and its links before another CDP request can replace the document', async () => {
|
|
55
|
+
const page = vm.createContext({ performance: { timeOrigin: 200 } });
|
|
56
|
+
vm.runInContext(`const graphData = ${JSON.stringify({ nodes: graph.nodes, links: graph.edges })}`, page);
|
|
57
|
+
const evaluate = jest.fn(async (expression) => {
|
|
58
|
+
const snapshot = vm.runInContext(expression, page);
|
|
59
|
+
// Navigation destroys the document after this single serialized CDP response.
|
|
60
|
+
if (evaluate.mock.calls.length > 1)
|
|
61
|
+
throw new Error('Execution context was destroyed');
|
|
62
|
+
return snapshot;
|
|
63
|
+
});
|
|
64
|
+
await expect(waitForAcceptanceGraph({ evaluate }, 100)).resolves.toEqual({ nodes: ['Alpha', 'Beta'], edges: 1 });
|
|
65
|
+
expect(evaluate).toHaveBeenCalledTimes(1);
|
|
66
|
+
});
|