noteconnection 1.8.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.
Files changed (195) hide show
  1. package/README.md +257 -1803
  2. package/dist/src/agent_workspace.contract.parity.test.js +105 -421
  3. package/dist/src/agent_workspace.frontend.test.js +410 -11
  4. package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
  5. package/dist/src/appimage.patchelf.test.js +113 -0
  6. package/dist/src/appimage.portability.test.js +156 -0
  7. package/dist/src/backend/FileLoader.js +18 -4
  8. package/dist/src/backend/FileLoader.test.js +125 -0
  9. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  10. package/dist/src/backend/GraphBuilder.js +66 -18
  11. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  12. package/dist/src/backend/GraphMetrics.js +0 -3
  13. package/dist/src/backend/ResourceIdentity.js +155 -0
  14. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  15. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  16. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  17. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  18. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  19. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  20. package/dist/src/backend/build_dag.js +0 -1
  21. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  22. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  23. package/dist/src/core/Graph.js +138 -42
  24. package/dist/src/core/Graph.test.js +74 -0
  25. package/dist/src/core/NoteConnection.js +2 -2
  26. package/dist/src/core/PathBridge.js +265 -2
  27. package/dist/src/core/ResourceReference.js +17 -0
  28. package/dist/src/desktop.godot.pack.test.js +142 -0
  29. package/dist/src/desktop.runtime.graph.test.js +66 -0
  30. package/dist/src/export/WorkspaceExportBundle.js +44 -0
  31. package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
  32. package/dist/src/foundation.release.evidence.contract.test.js +127 -9
  33. package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
  34. package/dist/src/frontend/README.md +257 -1803
  35. package/dist/src/frontend/agent_workspace.js +665 -7
  36. package/dist/src/frontend/index.html +31 -2
  37. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  38. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  39. package/dist/src/frontend/libs/path_core.js +158 -42
  40. package/dist/src/frontend/locales/en.json +32 -0
  41. package/dist/src/frontend/locales/zh.json +32 -0
  42. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  43. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  44. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  45. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  46. package/dist/src/frontend/path.html +6 -2
  47. package/dist/src/frontend/simulationWorker.js +1 -1
  48. package/dist/src/frontend/source_manager.js +110 -4
  49. package/dist/src/frontend/storage_provider.js +661 -71
  50. package/dist/src/frontend/styles.css +107 -13
  51. package/dist/src/frontend/workspace_panes.js +111 -7
  52. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  53. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  54. package/dist/src/index.js +6 -2
  55. package/dist/src/index.knowledge-root.test.js +118 -0
  56. package/dist/src/indexing/IndexLifecycle.js +22 -0
  57. package/dist/src/indexing/IndexLifecycle.test.js +24 -0
  58. package/dist/src/knowledge.api.contract.test.js +4 -2
  59. package/dist/src/knowledge_projection.contract.test.js +134 -0
  60. package/dist/src/knowledge_projection_store.test.js +200 -0
  61. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  62. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  63. package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
  64. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
  65. package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
  66. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
  67. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
  68. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  69. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  70. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  71. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  72. package/dist/src/learning/agentConversationExecution.js +211 -0
  73. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  74. package/dist/src/learning/agentConversationSerialization.js +266 -0
  75. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  76. package/dist/src/learning/agentResponseBudget.js +179 -0
  77. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  78. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  79. package/dist/src/learning/answerReleaseReview.js +1191 -250
  80. package/dist/src/learning/answerReleaseReview.test.js +1293 -7
  81. package/dist/src/learning/answerTaskPlan.js +473 -0
  82. package/dist/src/learning/conversationComposer.js +627 -386
  83. package/dist/src/learning/conversationComposer.test.js +1403 -0
  84. package/dist/src/learning/evidenceContextAssembler.js +312 -120
  85. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  86. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  87. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  88. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  89. package/dist/src/learning/graphAnswerFacts.js +94 -0
  90. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  91. package/dist/src/learning/graphAnswerPlan.js +908 -0
  92. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  93. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  94. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  95. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  96. package/dist/src/learning/graphClaimMatcher.js +104 -0
  97. package/dist/src/learning/graphConditionedContext.js +109 -0
  98. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  99. package/dist/src/learning/graphContextAssembler.js +3 -13
  100. package/dist/src/learning/graphContextAssembler.test.js +2 -2
  101. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  102. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  103. package/dist/src/learning/identityMetadata.test.js +26 -0
  104. package/dist/src/learning/index.js +1 -0
  105. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  106. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  107. package/dist/src/learning/queryBackend.js +34 -10
  108. package/dist/src/learning/queryBackend.test.js +21 -0
  109. package/dist/src/learning/ragContextPack.js +115 -16
  110. package/dist/src/learning/ragContextPack.test.js +151 -0
  111. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  112. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  113. package/dist/src/learning/ragPublicText.js +67 -4
  114. package/dist/src/learning/ragPublicText.test.js +57 -0
  115. package/dist/src/learning/ragSufficiencyJudge.js +11 -1
  116. package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
  117. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
  118. package/dist/src/learning/requestNormalization.js +106 -0
  119. package/dist/src/learning/requestNormalization.test.js +101 -0
  120. package/dist/src/learning/store.js +116 -12
  121. package/dist/src/learning/store.test.js +299 -0
  122. package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
  123. package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
  124. package/dist/src/learning/workspaceHydration.js +168 -0
  125. package/dist/src/learning/workspaceHydration.test.js +30 -0
  126. package/dist/src/license.policy.contract.test.js +9 -4
  127. package/dist/src/markdown/MarkdownGateway.js +4 -0
  128. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  129. package/dist/src/middleware/SseResponseWriter.js +122 -0
  130. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  131. package/dist/src/middleware/auth.js +28 -7
  132. package/dist/src/middleware/auth.test.js +33 -0
  133. package/dist/src/middleware/index.js +2 -1
  134. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  135. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  136. package/dist/src/mobile.artifact.contract.test.js +144 -0
  137. package/dist/src/mobile.budget.contract.test.js +90 -0
  138. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  139. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  140. package/dist/src/mobile.identity.contract.test.js +66 -0
  141. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  142. package/dist/src/mobile.identity.corpus.test.js +40 -0
  143. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  144. package/dist/src/mobile.pipeline.test.js +11 -1
  145. package/dist/src/mobile.profile.contract.test.js +225 -0
  146. package/dist/src/mobile.semantic.parity.test.js +92 -0
  147. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  148. package/dist/src/notemd/ContentGenerator.js +4 -9
  149. package/dist/src/notemd/FileProcessor.js +4 -9
  150. package/dist/src/notemd/NotemdService.js +12 -17
  151. package/dist/src/notemd/Translator.js +4 -9
  152. package/dist/src/notemd/progressReporter.js +9 -0
  153. package/dist/src/notemd.server.integration.test.js +119 -4
  154. package/dist/src/notemd.workflow.pipeline.test.js +1 -1
  155. package/dist/src/pathbridge.handshake.contract.test.js +29 -0
  156. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  157. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  158. package/dist/src/platform/ExportProfile.js +28 -0
  159. package/dist/src/platform/MobileBudget.js +11 -0
  160. package/dist/src/platform/PlatformCapabilities.js +14 -0
  161. package/dist/src/platform/PlatformCapabilities.test.js +6 -0
  162. package/dist/src/query_backend.external_http.integration.test.js +2 -2
  163. package/dist/src/release.godot.mirror.contract.test.js +28 -4
  164. package/dist/src/resources/ResourceRegistry.js +40 -0
  165. package/dist/src/resources/ResourceRegistry.test.js +36 -0
  166. package/dist/src/routes/index.js +0 -4
  167. package/dist/src/routes/knowledge.js +46 -33
  168. package/dist/src/routes/knowledgePayload.js +185 -0
  169. package/dist/src/routes/knowledgePayload.test.js +53 -0
  170. package/dist/src/routes/markdown.js +8 -0
  171. package/dist/src/routes/notemd.js +990 -234
  172. package/dist/src/routes/registry.contract.test.js +5 -2
  173. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  174. package/dist/src/routes/render.js +14 -51
  175. package/dist/src/runtime.capabilities.test.js +18 -2
  176. package/dist/src/server.js +566 -1090
  177. package/dist/src/server.migration.test.js +417 -15
  178. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  179. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  180. package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
  181. package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
  182. package/dist/src/simulation.worker.offline.test.js +104 -0
  183. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  184. package/dist/src/storage.provider.contract.test.js +152 -1
  185. package/dist/src/tauri.frontend.build.contract.test.js +8 -0
  186. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  187. package/dist/src/utils/RuntimePaths.js +6 -0
  188. package/dist/src/utils/RuntimePaths.test.js +27 -0
  189. package/dist/src/windows.installer.build.test.js +109 -0
  190. package/dist/src/windows.installer.payload.test.js +76 -0
  191. package/dist/src/windows.installer.runtime.test.js +69 -0
  192. package/dist/src/workspace/WorkspaceRegistry.js +13 -0
  193. package/package.json +32 -10
  194. package/dist/src/routes/data.js +0 -267
  195. package/dist/src/routes/diagnostics.js +0 -51
@@ -192,6 +192,30 @@
192
192
  data-i18n="agentWorkspace.scope.summaryAll"
193
193
  >All folders</div>
194
194
  </div>
195
+ <div class="agent-answer-language-control">
196
+ <label for="agent-workspace-answer-language-select" data-i18n="agentWorkspace.answerLanguage.label">Answer language</label>
197
+ <select id="agent-workspace-answer-language-select" aria-describedby="agent-workspace-answer-language-summary">
198
+ <option value="en" data-i18n="agentWorkspace.answerLanguage.english">English</option>
199
+ <option value="zh" data-i18n="agentWorkspace.answerLanguage.chinese">中文</option>
200
+ </select>
201
+ <div id="agent-workspace-answer-language-summary" class="agent-answer-language-summary" data-i18n="agentWorkspace.answerLanguage.summary">Matches software language</div>
202
+ </div>
203
+ <div class="agent-response-mode-control">
204
+ <label for="agent-workspace-response-mode-select" data-i18n="agentWorkspace.responseMode.label">Answer detail</label>
205
+ <select id="agent-workspace-response-mode-select" aria-describedby="agent-workspace-response-mode-summary">
206
+ <option value="slim" data-i18n="agentWorkspace.responseMode.slim">Slim</option>
207
+ <option value="full" data-i18n="agentWorkspace.responseMode.full">Full report</option>
208
+ </select>
209
+ <div id="agent-workspace-response-mode-summary" class="agent-response-mode-summary" data-i18n="agentWorkspace.responseMode.summary">Short grounded answer</div>
210
+ </div>
211
+ <div class="agent-response-budget-control" data-agent-desktop-only="true">
212
+ <label for="agent-workspace-response-budget-select" data-i18n="agentWorkspace.responseBudget.label">Full report budget</label>
213
+ <select id="agent-workspace-response-budget-select" aria-describedby="agent-workspace-response-budget-summary">
214
+ <option value="adaptive" data-i18n="agentWorkspace.responseBudget.adaptive">Adaptive</option>
215
+ <option value="unbounded" data-i18n="agentWorkspace.responseBudget.unbounded">No product cap</option>
216
+ </select>
217
+ <div id="agent-workspace-response-budget-summary" class="agent-response-budget-summary" data-i18n="agentWorkspace.responseBudget.summary">Host-adaptive report budget</div>
218
+ </div>
195
219
  <div class="agent-pane-field">
196
220
  <label for="agent-workspace-user-id" data-i18n="agentWorkspace.userId">Learner ID</label>
197
221
  <input id="agent-workspace-user-id" type="text" value="path_user_default" />
@@ -1518,8 +1542,13 @@
1518
1542
  <script src="tutorial.js"></script>
1519
1543
 
1520
1544
  <!-- Application Scripts -->
1521
- <script src="runtime_bridge.js"></script>
1522
- <script src="storage_provider.js"></script>
1545
+ <script src="runtime_bridge.js"></script>
1546
+ <script src="mobile_budget_runtime.js"></script>
1547
+ <script src="knowledge_projection_contract.js"></script>
1548
+ <script src="knowledge_projection_store.js"></script>
1549
+ <script src="mobile_exact_analyzer.js"></script>
1550
+ <script src="mobile_identity_contract.js"></script>
1551
+ <script src="storage_provider.js"></script>
1523
1552
  <script src="settings.js"></script>
1524
1553
  <script src="markdown_runtime.js"></script>
1525
1554
  <script src="reader.js"></script>
@@ -0,0 +1,230 @@
1
+ (function attachKnowledgeProjectionContract(root, factory) {
2
+ const api = factory();
3
+ if (typeof module === 'object' && module.exports) {
4
+ module.exports = api;
5
+ }
6
+ if (root) {
7
+ root.NoteConnectionKnowledgeProjection = api;
8
+ }
9
+ }(typeof globalThis !== 'undefined' ? globalThis : this, function createKnowledgeProjectionContract() {
10
+ 'use strict';
11
+
12
+ const SCHEMA_VERSION = 1;
13
+ const MAX_NODES = 50000;
14
+ const MAX_EDGES = 250000;
15
+ const MAX_NEIGHBORS = 64;
16
+ const MAX_EVIDENCE_REFS = 8;
17
+
18
+ function isRecord(value) {
19
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
20
+ }
21
+
22
+ function cleanString(value) {
23
+ return typeof value === 'string' ? value.trim() : '';
24
+ }
25
+
26
+ function cleanStringList(value, fieldName, limit) {
27
+ if (value === undefined || value === null) {
28
+ return [];
29
+ }
30
+ if (!Array.isArray(value)) {
31
+ throw new Error(`${fieldName} must be an array when provided.`);
32
+ }
33
+ const values = Array.from(new Set(value.map(cleanString).filter(Boolean)));
34
+ if (values.length > limit) {
35
+ throw new Error(`${fieldName} exceeds the bounded limit of ${limit}.`);
36
+ }
37
+ return values;
38
+ }
39
+
40
+ function classifyEdgeKind(rawType, rawKind) {
41
+ const kind = cleanString(rawKind).toLowerCase();
42
+ if (kind === 'explicit' || kind === 'inferred' || kind === 'runtime') {
43
+ return kind;
44
+ }
45
+ const type = cleanString(rawType).toLowerCase();
46
+ if (type.includes('inferred') || type.includes('keyword') || type.includes('vector') || type.includes('statistical')) {
47
+ return 'inferred';
48
+ }
49
+ if (type === 'runtime' || type.includes('runtime')) {
50
+ return 'runtime';
51
+ }
52
+ return 'explicit';
53
+ }
54
+
55
+ function assertSupportedVersion(value) {
56
+ const version = value === undefined || value === null ? SCHEMA_VERSION : Number(value);
57
+ if (!Number.isInteger(version) || version < 1 || version > SCHEMA_VERSION) {
58
+ throw new Error(`Unsupported knowledge projection schema version: ${String(value)}.`);
59
+ }
60
+ return version;
61
+ }
62
+
63
+ function normalizeNode(rawNode, maxEvidenceRefs) {
64
+ if (!isRecord(rawNode)) {
65
+ throw new Error('Knowledge projection nodes must be objects.');
66
+ }
67
+ const id = cleanString(rawNode.id);
68
+ if (!id) {
69
+ throw new Error('Knowledge projection node id must be non-empty.');
70
+ }
71
+ return {
72
+ id,
73
+ canonicalId: cleanString(rawNode.canonicalId),
74
+ label: cleanString(rawNode.label) || id,
75
+ sourceUri: cleanString(rawNode.sourceUri),
76
+ revision: cleanString(rawNode.revision),
77
+ identityAliases: cleanStringList(rawNode.identityAliases, `node ${id}.identityAliases`, 16),
78
+ evidenceRefs: cleanStringList(rawNode.evidenceRefs, `node ${id}.evidenceRefs`, maxEvidenceRefs),
79
+ tags: cleanStringList(rawNode.tags || (rawNode.metadata && rawNode.metadata.tags), `node ${id}.tags`, 32),
80
+ };
81
+ }
82
+
83
+ function normalizeEdge(rawEdge, nodeById, maxEvidenceRefs) {
84
+ if (!isRecord(rawEdge)) {
85
+ throw new Error('Knowledge projection edges must be objects.');
86
+ }
87
+ const source = cleanString(rawEdge.source || rawEdge.from);
88
+ const target = cleanString(rawEdge.target || rawEdge.to);
89
+ if (!source || !target || source === target || !nodeById.has(source) || !nodeById.has(target)) {
90
+ throw new Error(`Knowledge projection edge references an invalid node: ${source || '?'} -> ${target || '?'}.`);
91
+ }
92
+ const type = cleanString(rawEdge.type) || 'association';
93
+ const confidence = Number(rawEdge.confidence ?? rawEdge.weight);
94
+ const sourceNode = nodeById.get(source);
95
+ const targetNode = nodeById.get(target);
96
+ return {
97
+ source,
98
+ target,
99
+ sourceUri: cleanString(rawEdge.sourceUri) || sourceNode.sourceUri,
100
+ targetUri: cleanString(rawEdge.targetUri) || targetNode.sourceUri,
101
+ type,
102
+ kind: classifyEdgeKind(type, rawEdge.kind),
103
+ provenance: cleanString(rawEdge.provenance) || type,
104
+ confidence: Number.isFinite(confidence) ? Math.max(0, Math.min(1, confidence)) : 1,
105
+ evidenceRefs: cleanStringList(rawEdge.evidenceRefs, `edge ${source}->${target}.evidenceRefs`, maxEvidenceRefs),
106
+ };
107
+ }
108
+
109
+ function buildAdjacency(nodes, edges, maxNeighbors) {
110
+ const adjacency = new Map(nodes.map((node) => [node.id, { outgoing: [], incoming: [] }]));
111
+ edges.forEach((edge) => {
112
+ const source = adjacency.get(edge.source);
113
+ const target = adjacency.get(edge.target);
114
+ if (source.outgoing.length < maxNeighbors && !source.outgoing.includes(edge.target)) {
115
+ source.outgoing.push(edge.target);
116
+ }
117
+ if (target.incoming.length < maxNeighbors && !target.incoming.includes(edge.source)) {
118
+ target.incoming.push(edge.source);
119
+ }
120
+ });
121
+ return Array.from(adjacency, ([nodeId, value]) => ({ nodeId, ...value }));
122
+ }
123
+
124
+ function normalizeProjection(rawProjection, options) {
125
+ if (!isRecord(rawProjection)) {
126
+ throw new Error('Knowledge projection must be an object.');
127
+ }
128
+ const maxNodes = Math.min(MAX_NODES, Math.max(1, Number(options && options.maxNodes) || MAX_NODES));
129
+ const maxEdges = Math.min(MAX_EDGES, Math.max(1, Number(options && options.maxEdges) || MAX_EDGES));
130
+ const maxNeighbors = Math.min(MAX_NEIGHBORS, Math.max(1, Number(options && options.maxNeighbors) || MAX_NEIGHBORS));
131
+ const maxEvidenceRefs = MAX_EVIDENCE_REFS;
132
+ const declaredSchemaVersion = rawProjection.schemaVersion !== undefined
133
+ ? rawProjection.schemaVersion
134
+ : rawProjection.projectionVersion;
135
+ const schemaVersion = assertSupportedVersion(declaredSchemaVersion);
136
+ const rawNodes = Array.isArray(rawProjection.nodes) ? rawProjection.nodes : [];
137
+ const rawEdges = Array.isArray(rawProjection.edges) ? rawProjection.edges : [];
138
+ if (rawNodes.length > maxNodes || rawEdges.length > maxEdges) {
139
+ throw new Error(`Knowledge projection exceeds bounded size: ${rawNodes.length} nodes / ${rawEdges.length} edges.`);
140
+ }
141
+
142
+ const nodes = rawNodes.map((node) => normalizeNode(node, maxEvidenceRefs));
143
+ const nodeIds = new Set();
144
+ const normalizedIds = new Set();
145
+ const canonicalIds = new Set();
146
+ nodes.forEach((node) => {
147
+ const normalizedId = node.id.normalize ? node.id.normalize('NFC').toLowerCase() : node.id.toLowerCase();
148
+ if (nodeIds.has(node.id) || normalizedIds.has(normalizedId)) {
149
+ throw new Error(`Knowledge projection contains a duplicate node id: ${node.id}.`);
150
+ }
151
+ nodeIds.add(node.id);
152
+ normalizedIds.add(normalizedId);
153
+ if (node.canonicalId) {
154
+ const normalizedCanonicalId = node.canonicalId.normalize
155
+ ? node.canonicalId.normalize('NFC').toLowerCase()
156
+ : node.canonicalId.toLowerCase();
157
+ if (canonicalIds.has(normalizedCanonicalId)
158
+ || (normalizedCanonicalId !== normalizedId && normalizedIds.has(normalizedCanonicalId))) {
159
+ throw new Error(`Knowledge projection contains a duplicate canonical node id: ${node.canonicalId}.`);
160
+ }
161
+ canonicalIds.add(normalizedCanonicalId);
162
+ }
163
+ });
164
+
165
+ const edgeKeys = new Set();
166
+ const nodeById = new Map(nodes.map((node) => [node.id, node]));
167
+ const edges = rawEdges.map((edge) => normalizeEdge(edge, nodeById, maxEvidenceRefs))
168
+ .filter((edge) => {
169
+ // Preserve distinct provenance for the same directed endpoints.
170
+ // Collapsing by kind loses whether a link was explicit-next, wiki, or markdown.
171
+ const key = `${edge.source}->${edge.target}:${edge.kind}:${edge.type}:${edge.provenance}`;
172
+ if (edgeKeys.has(key)) {
173
+ return false;
174
+ }
175
+ edgeKeys.add(key);
176
+ return true;
177
+ });
178
+
179
+ return {
180
+ schemaVersion,
181
+ projectionVersion: schemaVersion,
182
+ workspaceId: cleanString(rawProjection.workspaceId) || 'workspace',
183
+ revision: cleanString(rawProjection.revision),
184
+ nodes,
185
+ edges,
186
+ adjacency: buildAdjacency(nodes, edges, maxNeighbors),
187
+ extensions: isRecord(rawProjection.extensions) ? rawProjection.extensions : {},
188
+ };
189
+ }
190
+
191
+ function createKnowledgeProjection(graph, options) {
192
+ if (!isRecord(graph)) {
193
+ throw new Error('Knowledge graph payload must be an object.');
194
+ }
195
+ const projectionOptions = isRecord(options) ? options : {};
196
+ return normalizeProjection({
197
+ ...graph,
198
+ schemaVersion: graph.schemaVersion !== undefined
199
+ ? graph.schemaVersion
200
+ : (graph.projectionVersion !== undefined ? graph.projectionVersion : SCHEMA_VERSION),
201
+ workspaceId: projectionOptions.workspaceId !== undefined
202
+ ? projectionOptions.workspaceId
203
+ : graph.workspaceId,
204
+ revision: projectionOptions.revision !== undefined
205
+ ? projectionOptions.revision
206
+ : graph.revision,
207
+ }, projectionOptions);
208
+ }
209
+
210
+ function replayKnowledgeProjection(serialized, options) {
211
+ let payload = serialized;
212
+ if (typeof serialized === 'string') {
213
+ try {
214
+ payload = JSON.parse(serialized);
215
+ } catch (error) {
216
+ throw new Error(`Knowledge projection JSON is invalid: ${String(error && error.message || error)}.`);
217
+ }
218
+ }
219
+ return normalizeProjection(payload, options);
220
+ }
221
+
222
+ return Object.freeze({
223
+ schemaVersion: SCHEMA_VERSION,
224
+ limits: Object.freeze({ maxNodes: MAX_NODES, maxEdges: MAX_EDGES, maxNeighbors: MAX_NEIGHBORS, maxEvidenceRefs: MAX_EVIDENCE_REFS }),
225
+ classifyEdgeKind,
226
+ normalizeProjection,
227
+ createKnowledgeProjection,
228
+ replayKnowledgeProjection,
229
+ });
230
+ }));
@@ -0,0 +1,214 @@
1
+ (function attachKnowledgeProjectionStore(root, factory) {
2
+ const api = factory(root && root.NoteConnectionKnowledgeProjection);
3
+ if (typeof module === 'object' && module.exports) {
4
+ module.exports = api;
5
+ }
6
+ if (root) {
7
+ root.NoteConnectionKnowledgeProjectionStore = api;
8
+ }
9
+ }(typeof globalThis !== 'undefined' ? globalThis : this, function createProjectionStoreApi(projectionContract) {
10
+ 'use strict';
11
+
12
+ const STORE_VERSION = 1;
13
+ const FALLBACK_MAX_BYTES = 48 * 1024 * 1024;
14
+
15
+ function defaultMaxBytes() {
16
+ const root = typeof globalThis !== 'undefined' ? globalThis : null;
17
+ const runtime = root && root.NoteConnectionMobileBudget && root.NoteConnectionMobileBudget.runtime;
18
+ return runtime && Number.isInteger(runtime.maxProjectionBytes) && runtime.maxProjectionBytes > 0
19
+ ? runtime.maxProjectionBytes
20
+ : FALLBACK_MAX_BYTES;
21
+ }
22
+
23
+ function requireProjectionContract() {
24
+ if (!projectionContract || typeof projectionContract.replayKnowledgeProjection !== 'function') {
25
+ throw new Error('Knowledge projection contract is unavailable.');
26
+ }
27
+ return projectionContract;
28
+ }
29
+
30
+ function normalizeProjection(value, options) {
31
+ return requireProjectionContract().replayKnowledgeProjection(value, options);
32
+ }
33
+
34
+ function measureBytes(value) {
35
+ const text = String(value || '');
36
+ if (typeof TextEncoder === 'function') {
37
+ return new TextEncoder().encode(text).length;
38
+ }
39
+ if (typeof Buffer !== 'undefined' && typeof Buffer.byteLength === 'function') {
40
+ return Buffer.byteLength(text, 'utf8');
41
+ }
42
+ return text.length;
43
+ }
44
+
45
+ function metadataFor(projection) {
46
+ return Object.freeze({
47
+ schemaVersion: projection.schemaVersion,
48
+ projectionVersion: projection.projectionVersion,
49
+ workspaceId: projection.workspaceId,
50
+ revision: projection.revision,
51
+ nodeCount: projection.nodes.length,
52
+ edgeCount: projection.edges.length,
53
+ });
54
+ }
55
+
56
+ function createMemoryProjectionStore(initialProjection, options) {
57
+ const normalizedOptions = options && typeof options === 'object' ? options : {};
58
+ let current = initialProjection === undefined || initialProjection === null
59
+ ? null
60
+ : normalizeProjection(initialProjection, normalizedOptions);
61
+
62
+ return Object.freeze({
63
+ kind: 'memory',
64
+ async load() {
65
+ return current;
66
+ },
67
+ async save(projection) {
68
+ current = normalizeProjection(projection, normalizedOptions);
69
+ return current;
70
+ },
71
+ async metadata() {
72
+ return current ? metadataFor(current) : null;
73
+ },
74
+ });
75
+ }
76
+
77
+ function createPersistentProjectionStore(options) {
78
+ const normalizedOptions = options && typeof options === 'object' ? options : {};
79
+ const read = normalizedOptions.read;
80
+ const write = normalizedOptions.write;
81
+ if (typeof read !== 'function') {
82
+ return createMemoryProjectionStore(normalizedOptions.initialProjection, normalizedOptions);
83
+ }
84
+
85
+ const maxBytes = Math.max(1024, Math.floor(Number(normalizedOptions.maxBytes) || defaultMaxBytes()));
86
+ let cached = normalizedOptions.initialProjection === undefined || normalizedOptions.initialProjection === null
87
+ ? null
88
+ : normalizeProjection(normalizedOptions.initialProjection, normalizedOptions);
89
+ let hasLoaded = false;
90
+ let loadPromise = null;
91
+
92
+ const load = async () => {
93
+ if (hasLoaded && cached) {
94
+ return cached;
95
+ }
96
+ if (loadPromise) {
97
+ return await loadPromise;
98
+ }
99
+ loadPromise = (async () => {
100
+ let payload;
101
+ try {
102
+ payload = await read();
103
+ } catch (error) {
104
+ if (cached) {
105
+ return cached;
106
+ }
107
+ throw error;
108
+ }
109
+ const serialized = typeof payload === 'string' ? payload : JSON.stringify(payload);
110
+ if (typeof serialized !== 'string') {
111
+ throw new Error('Knowledge projection payload is not serializable.');
112
+ }
113
+ if (measureBytes(serialized) > maxBytes) {
114
+ throw new Error(`Knowledge projection payload exceeds ${maxBytes} bytes.`);
115
+ }
116
+ // Validation errors must not be hidden by a stale in-memory value.
117
+ cached = normalizeProjection(payload, normalizedOptions);
118
+ hasLoaded = true;
119
+ return cached;
120
+ })();
121
+ try {
122
+ return await loadPromise;
123
+ } finally {
124
+ loadPromise = null;
125
+ }
126
+ };
127
+
128
+ return Object.freeze({
129
+ kind: typeof write === 'function' ? 'persistent' : 'read-through',
130
+ load,
131
+ async save(projection) {
132
+ const next = normalizeProjection(projection, normalizedOptions);
133
+ const serialized = JSON.stringify(next);
134
+ if (measureBytes(serialized) > maxBytes) {
135
+ throw new Error(`Knowledge projection payload exceeds ${maxBytes} bytes.`);
136
+ }
137
+ if (typeof write !== 'function') {
138
+ throw new Error('Knowledge projection store is read-only in this runtime.');
139
+ }
140
+ await write(serialized, next);
141
+ cached = next;
142
+ hasLoaded = true;
143
+ return next;
144
+ },
145
+ async metadata() {
146
+ const projection = await load();
147
+ return projection ? metadataFor(projection) : null;
148
+ },
149
+ });
150
+ }
151
+
152
+ function createFileProjectionStore(options) {
153
+ const normalizedOptions = options && typeof options === 'object' ? options : {};
154
+ const fileName = typeof normalizedOptions.fileName === 'string'
155
+ ? normalizedOptions.fileName.trim()
156
+ : '';
157
+ const readFile = normalizedOptions.readFile;
158
+ const writeAtomic = normalizedOptions.writeAtomic;
159
+
160
+ if (!fileName) {
161
+ throw new Error('App-local projection file name is required.');
162
+ }
163
+ if (typeof readFile !== 'function') {
164
+ throw new Error('App-local projection readFile adapter is required.');
165
+ }
166
+ if (writeAtomic !== undefined && typeof writeAtomic !== 'function') {
167
+ throw new Error('App-local projection writeAtomic adapter must be callable.');
168
+ }
169
+ if (normalizedOptions.write !== undefined) {
170
+ throw new Error('App-local projection writes must use the writeAtomic adapter.');
171
+ }
172
+
173
+ const persistentOptions = {
174
+ ...Object.fromEntries(Object.entries(normalizedOptions).filter(([key]) => (
175
+ key !== 'fileName'
176
+ && key !== 'readFile'
177
+ && key !== 'writeAtomic'
178
+ && key !== 'read'
179
+ && key !== 'write'
180
+ ))),
181
+ read: async () => await readFile(fileName),
182
+ };
183
+ if (typeof writeAtomic === 'function') {
184
+ persistentOptions.write = async (serialized, projection) => {
185
+ await writeAtomic(fileName, serialized, projection);
186
+ };
187
+ }
188
+
189
+ const store = createPersistentProjectionStore(persistentOptions);
190
+ return Object.freeze({
191
+ ...store,
192
+ kind: typeof writeAtomic === 'function' ? 'file-persistent' : 'file-read-through',
193
+ fileName,
194
+ });
195
+ }
196
+
197
+ function createProjectionStore(options) {
198
+ const normalizedOptions = options && typeof options === 'object' ? options : {};
199
+ if (typeof normalizedOptions.read === 'function') {
200
+ return createPersistentProjectionStore(normalizedOptions);
201
+ }
202
+ return createMemoryProjectionStore(normalizedOptions.initialProjection, normalizedOptions);
203
+ }
204
+
205
+ return Object.freeze({
206
+ storeVersion: STORE_VERSION,
207
+ defaultMaxBytes: FALLBACK_MAX_BYTES,
208
+ createProjectionStore,
209
+ createMemoryProjectionStore,
210
+ createPersistentProjectionStore,
211
+ createFileProjectionStore,
212
+ metadataFor,
213
+ });
214
+ }));