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.
Files changed (328) hide show
  1. package/LICENSE +674 -21
  2. package/README.md +260 -1626
  3. package/dist/src/agent_workspace.contract.parity.test.js +159 -0
  4. package/dist/src/agent_workspace.frontend.test.js +10388 -0
  5. package/dist/src/agent_workspace.locale.contract.test.js +95 -0
  6. package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
  7. package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
  8. package/dist/src/appimage.patchelf.test.js +113 -0
  9. package/dist/src/appimage.portability.test.js +156 -0
  10. package/dist/src/backend/FileLoader.js +18 -4
  11. package/dist/src/backend/FileLoader.test.js +125 -0
  12. package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
  13. package/dist/src/backend/GraphBuilder.js +66 -18
  14. package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
  15. package/dist/src/backend/GraphMetrics.js +0 -3
  16. package/dist/src/backend/ResourceIdentity.js +155 -0
  17. package/dist/src/backend/ResourceIdentity.test.js +113 -0
  18. package/dist/src/backend/algorithms/CycleDetection.js +0 -1
  19. package/dist/src/backend/algorithms/HybridEngine.js +0 -3
  20. package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
  21. package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
  22. package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
  23. package/dist/src/backend/build_dag.js +0 -1
  24. package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
  25. package/dist/src/capacitor.runtime.contract.test.js +11 -2
  26. package/dist/src/copy.assets.contract.test.js +130 -0
  27. package/dist/src/core/Graph.js +138 -42
  28. package/dist/src/core/Graph.test.js +74 -0
  29. package/dist/src/core/NoteConnection.js +2 -2
  30. package/dist/src/core/PathBridge.js +293 -5
  31. package/dist/src/core/ResourceReference.js +17 -0
  32. package/dist/src/desktop.godot.pack.test.js +142 -0
  33. package/dist/src/desktop.runtime.graph.test.js +66 -0
  34. package/dist/src/export/WorkspaceExportBundle.js +1174 -0
  35. package/dist/src/export/WorkspaceExportBundle.test.js +1541 -0
  36. package/dist/src/export/index.js +18 -0
  37. package/dist/src/export/types.js +2 -0
  38. package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
  39. package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
  40. package/dist/src/foundation.release.evidence.contract.test.js +524 -0
  41. package/dist/src/foundation.sqlite.runtime.contract.test.js +88 -0
  42. package/dist/src/frontend/README.md +260 -1626
  43. package/dist/src/frontend/agent_workspace.js +6011 -0
  44. package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
  45. package/dist/src/frontend/analysis.js +31 -4
  46. package/dist/src/frontend/app.js +2178 -62
  47. package/dist/src/frontend/focus_mode_interactions.js +33 -0
  48. package/dist/src/frontend/godot_future_path_renderer.js +540 -0
  49. package/dist/src/frontend/godot_tree_interactions.js +178 -0
  50. package/dist/src/frontend/graph_state.mjs +105 -0
  51. package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
  52. package/dist/src/frontend/i18n.mjs +186 -0
  53. package/dist/src/frontend/index.html +672 -268
  54. package/dist/src/frontend/knowledge_projection_contract.js +230 -0
  55. package/dist/src/frontend/knowledge_projection_store.js +214 -0
  56. package/dist/src/frontend/layout_gpu.js +12 -4
  57. package/dist/src/frontend/libs/path_core.js +158 -42
  58. package/dist/src/frontend/locales/en.json +753 -5
  59. package/dist/src/frontend/locales/zh.json +753 -5
  60. package/dist/src/frontend/main.mjs +60 -0
  61. package/dist/src/frontend/markdown_runtime.js +827 -0
  62. package/dist/src/frontend/mobile_budget_runtime.js +33 -0
  63. package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
  64. package/dist/src/frontend/mobile_identity_contract.js +221 -0
  65. package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
  66. package/dist/src/frontend/notemd.css +49 -0
  67. package/dist/src/frontend/notemd.html +7 -1
  68. package/dist/src/frontend/notemd.js +64 -0
  69. package/dist/src/frontend/path.html +113 -2
  70. package/dist/src/frontend/path_app.js +2108 -130
  71. package/dist/src/frontend/path_layout.mjs +143 -0
  72. package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
  73. package/dist/src/frontend/path_modules_bridge.js +486 -0
  74. package/dist/src/frontend/path_state.mjs +118 -0
  75. package/dist/src/frontend/path_styles.css +146 -0
  76. package/dist/src/frontend/path_worker_bridge.mjs +85 -0
  77. package/dist/src/frontend/reader.js +393 -33
  78. package/dist/src/frontend/runtime_bridge.js +67 -54
  79. package/dist/src/frontend/runtime_bridge.mjs +279 -0
  80. package/dist/src/frontend/settings.js +130 -12
  81. package/dist/src/frontend/simulationWorker.js +1 -1
  82. package/dist/src/frontend/source_manager.js +297 -22
  83. package/dist/src/frontend/storage_provider.js +661 -71
  84. package/dist/src/frontend/styles.css +2814 -72
  85. package/dist/src/frontend/workbench_state.mjs +101 -0
  86. package/dist/src/frontend/workspace_panes.js +10272 -0
  87. package/dist/src/frontend.locale.contract.test.js +62 -0
  88. package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
  89. package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
  90. package/dist/src/graph.answer.operator.contract.test.js +72 -0
  91. package/dist/src/index.js +6 -2
  92. package/dist/src/index.knowledge-root.test.js +118 -0
  93. package/dist/src/indexing/IndexLifecycle.js +217 -0
  94. package/dist/src/indexing/IndexLifecycle.test.js +73 -0
  95. package/dist/src/indexing/SegmentBuilder.js +64 -0
  96. package/dist/src/indexing/UnitBuilder.js +48 -0
  97. package/dist/src/indexing/types.js +2 -0
  98. package/dist/src/knowledge.api.contract.test.js +172 -0
  99. package/dist/src/knowledge_projection.contract.test.js +134 -0
  100. package/dist/src/knowledge_projection_store.test.js +200 -0
  101. package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
  102. package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
  103. package/dist/src/learning/KnowledgeLearningPlatform.js +11181 -0
  104. package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +745 -0
  105. package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
  106. package/dist/src/learning/KnowledgeLearningPlatform.test.js +3208 -0
  107. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2998 -0
  108. package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3936 -0
  109. package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
  110. package/dist/src/learning/PublicAnswerContract.test.js +85 -0
  111. package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
  112. package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
  113. package/dist/src/learning/agentConversationExecution.js +211 -0
  114. package/dist/src/learning/agentConversationExecution.test.js +209 -0
  115. package/dist/src/learning/agentConversationSerialization.js +266 -0
  116. package/dist/src/learning/agentConversationSerialization.test.js +165 -0
  117. package/dist/src/learning/agentResponseBudget.js +179 -0
  118. package/dist/src/learning/agentResponseBudget.test.js +97 -0
  119. package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
  120. package/dist/src/learning/answerReleaseReview.js +5260 -0
  121. package/dist/src/learning/answerReleaseReview.test.js +4174 -0
  122. package/dist/src/learning/answerTaskPlan.js +473 -0
  123. package/dist/src/learning/api.js +2 -0
  124. package/dist/src/learning/conversationComposer.js +1721 -0
  125. package/dist/src/learning/conversationComposer.test.js +3220 -0
  126. package/dist/src/learning/domains/ConversationManager.js +53 -0
  127. package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
  128. package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
  129. package/dist/src/learning/domains/MasteryEngine.js +387 -0
  130. package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
  131. package/dist/src/learning/domains/QualityEvaluator.js +307 -0
  132. package/dist/src/learning/domains/TutorRouter.js +313 -0
  133. package/dist/src/learning/domains/index.js +33 -0
  134. package/dist/src/learning/domains/types.js +7 -0
  135. package/dist/src/learning/errors.js +29 -0
  136. package/dist/src/learning/evidenceContextAssembler.js +1368 -0
  137. package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
  138. package/dist/src/learning/graphAnswerCoverage.js +37 -0
  139. package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
  140. package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
  141. package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
  142. package/dist/src/learning/graphAnswerFacts.js +94 -0
  143. package/dist/src/learning/graphAnswerFacts.test.js +39 -0
  144. package/dist/src/learning/graphAnswerPlan.js +908 -0
  145. package/dist/src/learning/graphAnswerPlan.test.js +907 -0
  146. package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
  147. package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
  148. package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
  149. package/dist/src/learning/graphClaimMatcher.js +104 -0
  150. package/dist/src/learning/graphConditionedContext.js +109 -0
  151. package/dist/src/learning/graphConditionedContext.test.js +99 -0
  152. package/dist/src/learning/graphContextAssembler.js +860 -0
  153. package/dist/src/learning/graphContextAssembler.test.js +1033 -0
  154. package/dist/src/learning/graphExpansionPolicy.js +22 -0
  155. package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
  156. package/dist/src/learning/identityMetadata.test.js +26 -0
  157. package/dist/src/learning/index.js +29 -0
  158. package/dist/src/learning/mobileAnswerProjection.js +104 -0
  159. package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
  160. package/dist/src/learning/queryBackend.js +1922 -0
  161. package/dist/src/learning/queryBackend.test.js +976 -0
  162. package/dist/src/learning/ragContextPack.js +356 -0
  163. package/dist/src/learning/ragContextPack.test.js +311 -0
  164. package/dist/src/learning/ragEvidenceQuality.js +96 -0
  165. package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
  166. package/dist/src/learning/ragPublicText.js +101 -0
  167. package/dist/src/learning/ragPublicText.test.js +57 -0
  168. package/dist/src/learning/ragSufficiencyJudge.js +171 -0
  169. package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
  170. package/dist/src/learning/ragSufficiencyProviderJudge.js +217 -0
  171. package/dist/src/learning/ragSufficiencyProviderJudge.test.js +207 -0
  172. package/dist/src/learning/requestNormalization.js +304 -0
  173. package/dist/src/learning/requestNormalization.test.js +101 -0
  174. package/dist/src/learning/runtimeCapability.js +4677 -0
  175. package/dist/src/learning/runtimeCapability.test.js +3635 -0
  176. package/dist/src/learning/store.js +1344 -0
  177. package/dist/src/learning/store.test.js +1425 -0
  178. package/dist/src/learning/tutorAdapter.js +2 -0
  179. package/dist/src/learning/types.js +2 -0
  180. package/dist/src/learning/vectorAccelerationAdapter.js +958 -0
  181. package/dist/src/learning/vectorAccelerationAdapter.test.js +412 -0
  182. package/dist/src/learning/workspaceHydration.js +168 -0
  183. package/dist/src/learning/workspaceHydration.test.js +30 -0
  184. package/dist/src/lfs.asset.policy.contract.test.js +153 -0
  185. package/dist/src/license.policy.contract.test.js +71 -0
  186. package/dist/src/markdown/MarkdownGateway.js +4 -0
  187. package/dist/src/markdown/MarkdownGateway.test.js +59 -0
  188. package/dist/src/memory/MemoryGovernance.js +74 -0
  189. package/dist/src/memory/MemoryGovernance.test.js +46 -0
  190. package/dist/src/memory/types.js +2 -0
  191. package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
  192. package/dist/src/middleware/SseResponseWriter.js +122 -0
  193. package/dist/src/middleware/SseResponseWriter.test.js +54 -0
  194. package/dist/src/middleware/auth.js +38 -0
  195. package/dist/src/middleware/auth.test.js +33 -0
  196. package/dist/src/middleware/body-parser.js +45 -0
  197. package/dist/src/middleware/cors.js +44 -0
  198. package/dist/src/middleware/index.js +22 -0
  199. package/dist/src/middleware/request-trace.js +96 -0
  200. package/dist/src/mobile.android.evidence.contract.test.js +146 -0
  201. package/dist/src/mobile.android.signing.contract.test.js +66 -0
  202. package/dist/src/mobile.artifact.contract.test.js +144 -0
  203. package/dist/src/mobile.budget.contract.test.js +90 -0
  204. package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
  205. package/dist/src/mobile.graph.parity.contract.test.js +207 -0
  206. package/dist/src/mobile.identity.contract.test.js +66 -0
  207. package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
  208. package/dist/src/mobile.identity.corpus.test.js +40 -0
  209. package/dist/src/mobile.native.recovery.contract.test.js +64 -0
  210. package/dist/src/mobile.pipeline.test.js +11 -1
  211. package/dist/src/mobile.profile.contract.test.js +225 -0
  212. package/dist/src/mobile.semantic.parity.test.js +92 -0
  213. package/dist/src/mobile_exact_analyzer.test.js +113 -0
  214. package/dist/src/notemd/AppConfigToml.js +6 -4
  215. package/dist/src/notemd/ContentGenerator.js +4 -9
  216. package/dist/src/notemd/FileProcessor.js +4 -9
  217. package/dist/src/notemd/MermaidProcessor.js +400 -50
  218. package/dist/src/notemd/NotemdService.js +506 -26
  219. package/dist/src/notemd/PromptManager.js +15 -0
  220. package/dist/src/notemd/Translator.js +4 -9
  221. package/dist/src/notemd/cli/commands.js +357 -0
  222. package/dist/src/notemd/cli/dispatcher.js +225 -0
  223. package/dist/src/notemd/cli/index.js +169 -0
  224. package/dist/src/notemd/cli/parser.js +68 -0
  225. package/dist/src/notemd/cli/types.js +2 -0
  226. package/dist/src/notemd/constants.js +43 -0
  227. package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
  228. package/dist/src/notemd/diagram/diagramSpec.js +79 -0
  229. package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
  230. package/dist/src/notemd/diagram/intent.js +95 -0
  231. package/dist/src/notemd/diagram/planner.js +71 -0
  232. package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
  233. package/dist/src/notemd/diagram/types.js +18 -0
  234. package/dist/src/notemd/index.js +26 -0
  235. package/dist/src/notemd/operations/capabilityManifest.js +23 -0
  236. package/dist/src/notemd/operations/cliContracts.js +17 -0
  237. package/dist/src/notemd/operations/configProfileCommands.js +85 -0
  238. package/dist/src/notemd/operations/registry.contract.test.js +95 -0
  239. package/dist/src/notemd/operations/registry.js +991 -0
  240. package/dist/src/notemd/operations/types.js +2 -0
  241. package/dist/src/notemd/progressReporter.js +9 -0
  242. package/dist/src/notemd/providerDiagnostics.js +220 -0
  243. package/dist/src/notemd/providerProfiles.js +42 -0
  244. package/dist/src/notemd/providerTemplates.js +231 -0
  245. package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
  246. package/dist/src/notemd/search/SearchManager.js +13 -0
  247. package/dist/src/notemd/search/SearchProvider.js +2 -0
  248. package/dist/src/notemd/search/TavilyProvider.js +44 -0
  249. package/dist/src/notemd.agent.manifest.test.js +85 -0
  250. package/dist/src/notemd.api.contract.test.js +14 -1
  251. package/dist/src/notemd.app_config_toml.test.js +2 -1
  252. package/dist/src/notemd.batch.workflow.test.js +117 -0
  253. package/dist/src/notemd.cli.e2e.test.js +136 -0
  254. package/dist/src/notemd.core.test.js +51 -0
  255. package/dist/src/notemd.diagram.pipeline.test.js +233 -0
  256. package/dist/src/notemd.providerTemplates.test.js +34 -0
  257. package/dist/src/notemd.server.integration.test.js +262 -39
  258. package/dist/src/notemd.workflow.pipeline.test.js +162 -0
  259. package/dist/src/pathbridge.handshake.contract.test.js +45 -2
  260. package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
  261. package/dist/src/pathcore.bundle.contract.test.js +8 -0
  262. package/dist/src/pathmode.background.contract.test.js +69 -0
  263. package/dist/src/pathmode.settings.api.contract.test.js +9 -0
  264. package/dist/src/pkg.sidecar.contract.test.js +9 -3
  265. package/dist/src/platform/ExportProfile.js +86 -0
  266. package/dist/src/platform/MobileBudget.js +11 -0
  267. package/dist/src/platform/PlatformCapabilities.js +59 -0
  268. package/dist/src/platform/PlatformCapabilities.test.js +36 -0
  269. package/dist/src/platform/RenderMaterializer.js +33 -0
  270. package/dist/src/platform/RenderMaterializer.test.js +32 -0
  271. package/dist/src/query_backend.external_http.integration.test.js +410 -0
  272. package/dist/src/reader_renderer.js +404 -3
  273. package/dist/src/reader_renderer.test.js +87 -0
  274. package/dist/src/release.godot.mirror.contract.test.js +97 -0
  275. package/dist/src/resources/ResourceRegistry.js +263 -0
  276. package/dist/src/resources/ResourceRegistry.test.js +97 -0
  277. package/dist/src/resources/types.js +2 -0
  278. package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
  279. package/dist/src/routes/index.js +19 -0
  280. package/dist/src/routes/knowledge.js +981 -0
  281. package/dist/src/routes/knowledgePayload.js +185 -0
  282. package/dist/src/routes/knowledgePayload.test.js +53 -0
  283. package/dist/src/routes/markdown.js +295 -0
  284. package/dist/src/routes/notemd.js +1321 -0
  285. package/dist/src/routes/registry.contract.test.js +133 -0
  286. package/dist/src/routes/registry.shadow.contract.test.js +22 -0
  287. package/dist/src/routes/render.js +248 -0
  288. package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
  289. package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
  290. package/dist/src/routes/settings.js +6 -0
  291. package/dist/src/routes/staticFiles.js +94 -0
  292. package/dist/src/routes/types.js +2 -0
  293. package/dist/src/runtime.capabilities.test.js +18 -2
  294. package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
  295. package/dist/src/server.js +10342 -1325
  296. package/dist/src/server.migration.test.js +584 -10
  297. package/dist/src/server.port.fallback.contract.test.js +63 -0
  298. package/dist/src/session/SessionStateStore.js +81 -0
  299. package/dist/src/session/SessionStateStore.test.js +58 -0
  300. package/dist/src/session/types.js +2 -0
  301. package/dist/src/settings.runtime.contract.test.js +50 -0
  302. package/dist/src/shared/types.contract.test.js +107 -0
  303. package/dist/src/shared/types.js +22 -0
  304. package/dist/src/sidecar.build.lock.contract.test.js +201 -0
  305. package/dist/src/sidecar.freshness.contract.test.js +184 -0
  306. package/dist/src/sidecar.replacement.boundary.contract.test.js +131 -0
  307. package/dist/src/sidecar.supply.readiness.contract.test.js +155 -0
  308. package/dist/src/simulation.worker.offline.test.js +104 -0
  309. package/dist/src/source_manager.loadflow.test.js +46 -0
  310. package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
  311. package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
  312. package/dist/src/storage.provider.contract.test.js +152 -1
  313. package/dist/src/tauri.frontend.build.contract.test.js +68 -0
  314. package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
  315. package/dist/src/tauri.window.evidence.execution.test.js +68 -0
  316. package/dist/src/utils/RuntimePaths.js +10 -13
  317. package/dist/src/utils/RuntimePaths.test.js +27 -0
  318. package/dist/src/utils/platform.js +153 -0
  319. package/dist/src/windows.installer.build.test.js +109 -0
  320. package/dist/src/windows.installer.payload.test.js +76 -0
  321. package/dist/src/windows.installer.runtime.test.js +69 -0
  322. package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
  323. package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
  324. package/dist/src/workflows/types.js +2 -0
  325. package/dist/src/workspace/WorkspaceRegistry.js +135 -0
  326. package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
  327. package/dist/src/workspace/types.js +2 -0
  328. package/package.json +80 -15
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const stream_1 = require("stream");
4
+ const SseResponseWriter_1 = require("./SseResponseWriter");
5
+ describe('SSE delivery lifecycle', () => {
6
+ function slowDestination() {
7
+ const frames = [];
8
+ const callbacks = [];
9
+ const destination = new stream_1.Writable({ highWaterMark: 1, write(chunk, _encoding, callback) {
10
+ frames.push(chunk.toString());
11
+ callbacks.push(callback);
12
+ } });
13
+ return { destination, frames, callbacks };
14
+ }
15
+ test('waits for drain, preserves frame order and completes exactly once', async () => {
16
+ const { destination, frames, callbacks } = slowDestination();
17
+ const writer = new SseResponseWriter_1.SseResponseWriter(destination, { maxBufferedBytes: 64, drainTimeoutMs: 1000 });
18
+ const finish = jest.fn();
19
+ destination.on('finish', finish);
20
+ writer.write('first');
21
+ writer.write('second');
22
+ const completed = writer.finish();
23
+ expect(frames).toEqual(['first']);
24
+ callbacks.shift()();
25
+ expect(frames).toEqual(['first', 'second']);
26
+ callbacks.shift()();
27
+ await completed;
28
+ await writer.finish();
29
+ expect(finish).toHaveBeenCalledTimes(1);
30
+ expect(writer.pendingBytes).toBe(0);
31
+ });
32
+ test('bounds pending bytes and destroys an overproducing stream', async () => {
33
+ const { destination } = slowDestination();
34
+ const writer = new SseResponseWriter_1.SseResponseWriter(destination, { maxBufferedBytes: 8, drainTimeoutMs: 1000 });
35
+ writer.write('123456');
36
+ expect(() => writer.write('789')).toThrow('sse_buffer_limit');
37
+ expect(destination.destroyed).toBe(true);
38
+ await expect(writer.finish()).rejects.toThrow('sse_buffer_limit');
39
+ });
40
+ test.each(['close', 'error', 'timeout'])('settles %s while waiting for a slow client', async (cause) => {
41
+ const { destination } = slowDestination();
42
+ const writer = new SseResponseWriter_1.SseResponseWriter(destination, { maxBufferedBytes: 64, drainTimeoutMs: 10 });
43
+ writer.write('first');
44
+ writer.write('second');
45
+ const completed = writer.finish();
46
+ if (cause === 'close')
47
+ destination.destroy();
48
+ if (cause === 'error')
49
+ destination.destroy(new Error('socket_failure'));
50
+ await expect(completed).rejects.toThrow(cause === 'timeout' ? 'sse_drain_timeout' : cause === 'error' ? 'socket_failure' : 'sse_closed');
51
+ expect(writer.pendingBytes).toBe(0);
52
+ expect(destination.listenerCount('drain')).toBe(0);
53
+ });
54
+ });
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setSidecarAuthToken = setSidecarAuthToken;
4
+ exports.isRequestTokenAuthorized = isRequestTokenAuthorized;
5
+ exports.isAuthorizedRequest = isAuthorizedRequest;
6
+ let sidecarAuthToken = null;
7
+ function setSidecarAuthToken(token) {
8
+ sidecarAuthToken = String(token || '').trim() || null;
9
+ }
10
+ function readHeader(req, name) {
11
+ const value = req.headers[name];
12
+ if (Array.isArray(value)) {
13
+ return String(value[0] || '').trim();
14
+ }
15
+ return String(value || '').trim();
16
+ }
17
+ /**
18
+ * Shared credential decision for the sidecar and HTTP server entry points.
19
+ * A configured token changes the default from anonymous allow to explicit deny.
20
+ */
21
+ function isRequestTokenAuthorized(req, expectedToken) {
22
+ const expected = String(expectedToken || '').trim();
23
+ if (!expected)
24
+ return true;
25
+ const legacyToken = readHeader(req, 'x-noteconnection-token');
26
+ if (legacyToken) {
27
+ return legacyToken === expected;
28
+ }
29
+ const authHeader = readHeader(req, 'authorization');
30
+ if (!/^Bearer\s+/i.test(authHeader)) {
31
+ return false;
32
+ }
33
+ const bearerToken = authHeader.slice(7).trim();
34
+ return bearerToken.length > 0 && bearerToken === expected;
35
+ }
36
+ function isAuthorizedRequest(req) {
37
+ return isRequestTokenAuthorized(req, sidecarAuthToken || '');
38
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const auth_1 = require("./auth");
4
+ function request(headers) {
5
+ return { headers };
6
+ }
7
+ describe('request authorization boundary', () => {
8
+ afterEach(() => {
9
+ (0, auth_1.setSidecarAuthToken)('');
10
+ });
11
+ test('allows requests when no token is configured', () => {
12
+ expect((0, auth_1.isRequestTokenAuthorized)(request({}), '')).toBe(true);
13
+ expect((0, auth_1.isAuthorizedRequest)(request({}))).toBe(true);
14
+ });
15
+ test('accepts a valid Bearer credential', () => {
16
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Bearer secret' }), 'secret')).toBe(true);
17
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'bearer secret' }), 'secret')).toBe(true);
18
+ });
19
+ test('rejects missing, malformed, and invalid credentials when configured', () => {
20
+ expect((0, auth_1.isRequestTokenAuthorized)(request({}), 'secret')).toBe(false);
21
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Basic secret' }), 'secret')).toBe(false);
22
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Bearer ' }), 'secret')).toBe(false);
23
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ authorization: 'Bearer wrong' }), 'secret')).toBe(false);
24
+ });
25
+ test('preserves the legacy token header', () => {
26
+ expect((0, auth_1.isRequestTokenAuthorized)(request({ 'x-noteconnection-token': 'secret' }), 'secret')).toBe(true);
27
+ });
28
+ test('sidecar middleware uses the same strict decision', () => {
29
+ (0, auth_1.setSidecarAuthToken)('secret');
30
+ expect((0, auth_1.isAuthorizedRequest)(request({}))).toBe(false);
31
+ expect((0, auth_1.isAuthorizedRequest)(request({ authorization: 'Bearer secret' }))).toBe(true);
32
+ });
33
+ });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readRequestBody = readRequestBody;
4
+ exports.parseJsonBody = parseJsonBody;
5
+ exports.sendJson = sendJson;
6
+ exports.sendError = sendError;
7
+ const MAX_BODY_SIZE = 50 * 1024 * 1024; // 50MB
8
+ async function readRequestBody(req) {
9
+ return new Promise((resolve, reject) => {
10
+ const chunks = [];
11
+ let totalSize = 0;
12
+ req.on('data', (chunk) => {
13
+ totalSize += chunk.length;
14
+ if (totalSize > MAX_BODY_SIZE) {
15
+ req.destroy();
16
+ reject(new Error('Request body exceeds maximum size'));
17
+ return;
18
+ }
19
+ chunks.push(chunk);
20
+ });
21
+ req.on('end', () => {
22
+ resolve(Buffer.concat(chunks).toString('utf8'));
23
+ });
24
+ req.on('error', reject);
25
+ });
26
+ }
27
+ async function parseJsonBody(req) {
28
+ const raw = await readRequestBody(req);
29
+ if (!raw.trim())
30
+ return {};
31
+ return JSON.parse(raw);
32
+ }
33
+ function sendJson(res, statusCode, data) {
34
+ res.writeHead(statusCode, { 'Content-Type': 'application/json' });
35
+ res.end(JSON.stringify(data));
36
+ }
37
+ function sendError(res, statusCode, error, errorCode) {
38
+ if (errorCode) {
39
+ res.setHeader('x-nc-error-code', errorCode);
40
+ }
41
+ sendJson(res, statusCode, {
42
+ success: false,
43
+ error: error instanceof Error ? error.message : String(error),
44
+ });
45
+ }
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyCorsHeaders = applyCorsHeaders;
4
+ const ALLOWED_ORIGINS = new Set([
5
+ 'http://localhost:3000',
6
+ 'http://127.0.0.1:3000',
7
+ 'http://localhost:5173',
8
+ 'http://127.0.0.1:5173',
9
+ 'tauri://localhost',
10
+ 'https://tauri.localhost',
11
+ ]);
12
+ const ALLOWED_HEADER_NAMES = [
13
+ 'Content-Type',
14
+ 'Authorization',
15
+ 'X-Request-Id',
16
+ 'X-NoteConnection-Token',
17
+ 'X-Agent-Conversation-Turn-Id',
18
+ 'X-Agent-Conversation-Resume-Turn-Id',
19
+ ];
20
+ const EXPOSED_HEADER_NAMES = [
21
+ 'X-Request-Id',
22
+ 'X-Error-Code',
23
+ 'X-Agent-Conversation-Turn-Id',
24
+ 'X-Agent-Conversation-Replay',
25
+ ];
26
+ function applyCorsHeaders(req, res) {
27
+ const origin = req.headers.origin;
28
+ if (!origin) {
29
+ res.setHeader('Access-Control-Allow-Origin', '*');
30
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
31
+ res.setHeader('Access-Control-Allow-Headers', ALLOWED_HEADER_NAMES.join(', '));
32
+ res.setHeader('Access-Control-Expose-Headers', EXPOSED_HEADER_NAMES.join(', '));
33
+ return true;
34
+ }
35
+ if (ALLOWED_ORIGINS.has(origin)) {
36
+ res.setHeader('Access-Control-Allow-Origin', origin);
37
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
38
+ res.setHeader('Access-Control-Allow-Headers', ALLOWED_HEADER_NAMES.join(', '));
39
+ res.setHeader('Access-Control-Expose-Headers', EXPOSED_HEADER_NAMES.join(', '));
40
+ res.setHeader('Vary', 'Origin');
41
+ return true;
42
+ }
43
+ return false;
44
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendError = exports.sendJson = exports.parseJsonBody = exports.readRequestBody = exports.ERROR_CODE_HEADER = exports.appendRuntimeApiRequestTrace = exports.getRuntimeRequestTrace = exports.normalizeApiErrorCodeToken = exports.finishRequestTrace = exports.startRequestTraceHandler = exports.resolveRequestId = exports.setSidecarAuthToken = exports.isRequestTokenAuthorized = exports.isAuthorizedRequest = exports.applyCorsHeaders = void 0;
4
+ var cors_1 = require("./cors");
5
+ Object.defineProperty(exports, "applyCorsHeaders", { enumerable: true, get: function () { return cors_1.applyCorsHeaders; } });
6
+ var auth_1 = require("./auth");
7
+ Object.defineProperty(exports, "isAuthorizedRequest", { enumerable: true, get: function () { return auth_1.isAuthorizedRequest; } });
8
+ Object.defineProperty(exports, "isRequestTokenAuthorized", { enumerable: true, get: function () { return auth_1.isRequestTokenAuthorized; } });
9
+ Object.defineProperty(exports, "setSidecarAuthToken", { enumerable: true, get: function () { return auth_1.setSidecarAuthToken; } });
10
+ var request_trace_1 = require("./request-trace");
11
+ Object.defineProperty(exports, "resolveRequestId", { enumerable: true, get: function () { return request_trace_1.resolveRequestId; } });
12
+ Object.defineProperty(exports, "startRequestTraceHandler", { enumerable: true, get: function () { return request_trace_1.startRequestTraceHandler; } });
13
+ Object.defineProperty(exports, "finishRequestTrace", { enumerable: true, get: function () { return request_trace_1.finishRequestTrace; } });
14
+ Object.defineProperty(exports, "normalizeApiErrorCodeToken", { enumerable: true, get: function () { return request_trace_1.normalizeApiErrorCodeToken; } });
15
+ Object.defineProperty(exports, "getRuntimeRequestTrace", { enumerable: true, get: function () { return request_trace_1.getRuntimeRequestTrace; } });
16
+ Object.defineProperty(exports, "appendRuntimeApiRequestTrace", { enumerable: true, get: function () { return request_trace_1.appendRuntimeApiRequestTrace; } });
17
+ Object.defineProperty(exports, "ERROR_CODE_HEADER", { enumerable: true, get: function () { return request_trace_1.ERROR_CODE_HEADER; } });
18
+ var body_parser_1 = require("./body-parser");
19
+ Object.defineProperty(exports, "readRequestBody", { enumerable: true, get: function () { return body_parser_1.readRequestBody; } });
20
+ Object.defineProperty(exports, "parseJsonBody", { enumerable: true, get: function () { return body_parser_1.parseJsonBody; } });
21
+ Object.defineProperty(exports, "sendJson", { enumerable: true, get: function () { return body_parser_1.sendJson; } });
22
+ Object.defineProperty(exports, "sendError", { enumerable: true, get: function () { return body_parser_1.sendError; } });
@@ -0,0 +1,96 @@
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
+ exports.ERROR_CODE_HEADER = void 0;
37
+ exports.resolveRequestId = resolveRequestId;
38
+ exports.normalizeApiErrorCodeToken = normalizeApiErrorCodeToken;
39
+ exports.appendRuntimeApiRequestTrace = appendRuntimeApiRequestTrace;
40
+ exports.getRuntimeRequestTrace = getRuntimeRequestTrace;
41
+ exports.startRequestTraceHandler = startRequestTraceHandler;
42
+ exports.finishRequestTrace = finishRequestTrace;
43
+ const crypto = __importStar(require("crypto"));
44
+ const ERROR_CODE_HEADER = 'x-nc-error-code';
45
+ exports.ERROR_CODE_HEADER = ERROR_CODE_HEADER;
46
+ const MAX_TRACE_RECORDS = 5000;
47
+ const traceRecords = [];
48
+ function resolveRequestId(req) {
49
+ return req.headers['x-request-id'] || crypto.randomUUID();
50
+ }
51
+ function normalizeApiErrorCodeToken(value, fallback) {
52
+ if (typeof value === 'string')
53
+ return value.trim();
54
+ return fallback;
55
+ }
56
+ function appendRuntimeApiRequestTrace(record) {
57
+ traceRecords.push(record);
58
+ while (traceRecords.length > MAX_TRACE_RECORDS) {
59
+ traceRecords.shift();
60
+ }
61
+ }
62
+ function getRuntimeRequestTrace(requestId) {
63
+ if (requestId) {
64
+ return traceRecords.filter((r) => r.requestId === requestId);
65
+ }
66
+ return traceRecords.slice(-100);
67
+ }
68
+ function startRequestTraceHandler(req, res) {
69
+ const requestId = resolveRequestId(req);
70
+ const requestStartedAtMs = Date.now();
71
+ const requestStartedAt = new Date(requestStartedAtMs).toISOString();
72
+ res.setHeader('X-Request-Id', requestId);
73
+ return { requestId, requestStartedAtMs, requestStartedAt };
74
+ }
75
+ function finishRequestTrace(req, res, requestId, requestStartedAtMs, requestStartedAt, requestPath, ERROR_CODE_HEADER) {
76
+ const responseContentLengthHeader = Number(res.getHeader('content-length'));
77
+ const responseContentLength = Number.isFinite(responseContentLengthHeader)
78
+ ? Math.max(0, Math.floor(responseContentLengthHeader))
79
+ : null;
80
+ const responseErrorCode = normalizeApiErrorCodeToken(res.getHeader(ERROR_CODE_HEADER), '');
81
+ appendRuntimeApiRequestTrace({
82
+ requestId,
83
+ method: String(req.method || 'GET').trim().toUpperCase(),
84
+ path: requestPath,
85
+ statusCode: Number(res.statusCode || 0),
86
+ errorCode: responseErrorCode || undefined,
87
+ durationMs: Number((Date.now() - requestStartedAtMs).toFixed(4)),
88
+ startedAt: requestStartedAt,
89
+ finishedAt: new Date().toISOString(),
90
+ responseContentType: String(res.getHeader('content-type') || '').trim(),
91
+ responseContentLength,
92
+ requestContentLength: null,
93
+ remoteAddress: String(req.socket?.remoteAddress || '').trim(),
94
+ userAgent: String(req.headers['user-agent'] || '').trim().slice(0, 180),
95
+ });
96
+ }
@@ -0,0 +1,146 @@
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('Tauri Android device evidence contract', () => {
39
+ const repoRoot = path.resolve(__dirname, '..');
40
+ const scriptPath = path.join(repoRoot, 'scripts', 'capture-tauri-android-rss-evidence.js');
41
+ const releaseWorkflowPath = path.join(repoRoot, '.github', 'workflows', 'release-desktop-multi-os.yml');
42
+ const script = require(scriptPath);
43
+ let fixtureRoot;
44
+ beforeEach(() => {
45
+ fixtureRoot = fs.mkdtempSync(path.join(repoRoot, '.tmp-tauri-android-evidence-'));
46
+ });
47
+ afterEach(() => {
48
+ fs.rmSync(fixtureRoot, { recursive: true, force: true });
49
+ });
50
+ test('requires an ordered native workload with restart continuity', () => {
51
+ const specPath = path.join(fixtureRoot, 'workload.json');
52
+ fs.writeFileSync(specPath, JSON.stringify({
53
+ schemaVersion: 1,
54
+ steps: script.requiredWorkloadSteps.map((name) => ({
55
+ name,
56
+ adbArgs: ['shell', 'echo', name],
57
+ })),
58
+ }), 'utf8');
59
+ const parsed = script.parseWorkloadSpec(specPath);
60
+ expect(parsed.schemaVersion).toBe(1);
61
+ expect(parsed.requiredSteps).toEqual(['saf-import', 'graph-build', 'exact-query', 'path', 'continuity']);
62
+ expect(parsed.steps.map((step) => step.name)).toEqual(parsed.requiredSteps);
63
+ });
64
+ test('fails closed when a required workload phase is absent or reordered', () => {
65
+ const missingPath = path.join(fixtureRoot, 'missing.json');
66
+ fs.writeFileSync(missingPath, JSON.stringify({
67
+ schemaVersion: 1,
68
+ steps: script.requiredWorkloadSteps.slice(0, 4).map((name) => ({
69
+ name,
70
+ adbArgs: ['shell', 'echo', name],
71
+ })),
72
+ }), 'utf8');
73
+ expect(() => script.parseWorkloadSpec(missingPath)).toThrow(/missing required steps.*continuity/i);
74
+ const reorderedPath = path.join(fixtureRoot, 'reordered.json');
75
+ fs.writeFileSync(reorderedPath, JSON.stringify({
76
+ schemaVersion: 1,
77
+ steps: [...script.requiredWorkloadSteps].reverse().map((name) => ({
78
+ name,
79
+ adbArgs: ['shell', 'echo', name],
80
+ })),
81
+ }), 'utf8');
82
+ expect(() => script.parseWorkloadSpec(reorderedPath)).toThrow(/must be ordered/i);
83
+ const extraPath = path.join(fixtureRoot, 'extra.json');
84
+ fs.writeFileSync(extraPath, JSON.stringify({
85
+ schemaVersion: 1,
86
+ steps: [
87
+ ...script.requiredWorkloadSteps.map((name) => ({ name, adbArgs: ['shell', 'echo', name] })),
88
+ { name: 'unrecorded-helper', adbArgs: ['shell', 'echo', 'ignored'] },
89
+ ],
90
+ }), 'utf8');
91
+ expect(() => script.parseWorkloadSpec(extraPath)).toThrow(/unsupported steps/i);
92
+ });
93
+ test('keeps device identity private and parses proc pid output deterministically', () => {
94
+ expect(script.maskSerial('R58M123456789')).toBe('R5***89');
95
+ expect(script.maskSerial('1234')).toBe('1234');
96
+ expect(script.parsePid('12345\n')).toBe(12345);
97
+ expect(script.parsePid('')).toBe(0);
98
+ });
99
+ test('requires measurable arm64 hardware budget inputs', () => {
100
+ expect(script.parseAbiList('arm64-v8a, armeabi-v7a\n')).toEqual(['arm64-v8a', 'armeabi-v7a']);
101
+ expect(script.parseAbiList('')).toEqual([]);
102
+ expect(script.parseTotalRamBytes('MemTotal: 2097152 kB\n')).toBe(2097152 * 1024);
103
+ expect(script.parseTotalRamBytes('MemTotal: 2097152 kB\r\n')).toBe(2097152 * 1024);
104
+ expect(script.parseTotalRamBytes('')).toBe(0);
105
+ });
106
+ test('exposes signed artifact and RSS release gates in the package contract', () => {
107
+ const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
108
+ expect(packageJson.scripts?.['verify:mobile:artifact:release']).toContain('--require-signed');
109
+ expect(packageJson.scripts?.['verify:mobile:artifact:release']).toContain('--require-arm64-only');
110
+ expect(packageJson.scripts?.['capture:tauri:android:evidence']).toContain('capture-tauri-android-rss-evidence.js');
111
+ });
112
+ test('release workflow refuses unsigned Android uploads and verifies arm64 artifacts', () => {
113
+ const workflow = fs.readFileSync(releaseWorkflowPath, 'utf8');
114
+ expect(workflow).toContain('Materialize Android release signing key');
115
+ expect(workflow).toContain('Build Android arm64 APK');
116
+ expect(workflow).toContain('NOTE_CONNECTION_TAURI_ANDROID_TARGET=aarch64');
117
+ expect(workflow).not.toContain('NOTE_CONNECTION_TAURI_ANDROID_TARGET=universal npm run tauri:android:build');
118
+ expect(workflow).toContain('NOTE_CONNECTION_ANDROID_KEYSTORE_BASE64');
119
+ expect(workflow).toContain('NOTE_CONNECTION_ANDROID_REQUIRE_SIGNING: "1"');
120
+ expect(workflow).toContain('--require-arm64');
121
+ expect(workflow).toContain('--require-arm64-only');
122
+ expect(workflow).toContain('--require-signed');
123
+ expect(workflow).toContain('verify-android-device-evidence');
124
+ expect(workflow).toContain('self-hosted, android-arm64');
125
+ expect(workflow).toContain('--require-rss');
126
+ expect(workflow).toContain('publish-android-release-assets');
127
+ expect(workflow).toContain('name: release-${{ needs.ensure-release.outputs.tag_name }}-android-evidence');
128
+ expect(workflow).not.toContain('name: release-${{ github.event.inputs.tag }}-android-evidence');
129
+ expect(workflow).toContain('noteconnection-arm64-release.apk');
130
+ expect(workflow).toContain('noteconnection-arm64-release.aab');
131
+ expect(workflow).toContain("! -name '*unsigned*'");
132
+ expect(workflow).toContain('build/release/mobile/*');
133
+ expect(workflow).not.toContain('src-tauri/gen/android/app/build/outputs/apk/**/*.apk');
134
+ const androidBuildJob = workflow.split(' build-and-upload-android:')[1]
135
+ .split(' verify-android-device-evidence:')[0];
136
+ expect(androidBuildJob).not.toContain('softprops/action-gh-release@v2');
137
+ });
138
+ test('RSS harness keeps the release ABI contract strict', () => {
139
+ const evidenceScript = fs.readFileSync(scriptPath, 'utf8');
140
+ const verifierCalls = evidenceScript.match(/verifyMobileArtifact\(\{[\s\S]*?\}\);/g) || [];
141
+ expect(verifierCalls.length).toBeGreaterThanOrEqual(2);
142
+ verifierCalls.forEach((call) => {
143
+ expect(call).toContain('requireArm64Only: true');
144
+ });
145
+ });
146
+ });
@@ -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 path = __importStar(require("path"));
37
+ const fs = __importStar(require("fs"));
38
+ describe('Tauri Android signing configuration contract', () => {
39
+ const repoRoot = path.resolve(__dirname, '..');
40
+ const signingScriptPath = path.join(repoRoot, 'scripts', 'configure-tauri-android-signing.js');
41
+ const runnerScriptPath = path.join(repoRoot, 'scripts', 'run-tauri-android.js');
42
+ const signing = require(signingScriptPath);
43
+ test('keeps signing opt-in and injects the config inside the Android DSL', () => {
44
+ const source = `plugins { id("com.android.application") }\n\nandroid {\n defaultConfig {\n applicationId = "com.example"\n }\n buildTypes {\n getByName("release") {\n isMinifyEnabled = true\n }\n }\n}`;
45
+ const configured = signing.addSigningConfig(source);
46
+ expect(configured.indexOf('// NOTE_CONNECTION_ANDROID_SIGNING_START')).toBeGreaterThan(configured.indexOf('android {'));
47
+ expect(configured).toContain('signingConfigs.create("noteConnectionRelease")');
48
+ expect(configured).toContain('signingConfig = noteConnectionReleaseSigningConfig');
49
+ expect(configured).toContain('NOTE_CONNECTION_ANDROID_KEYSTORE_PASSWORD');
50
+ expect(configured).toContain('applicationId = "com.example"');
51
+ });
52
+ test('removes a stale generated signing block without touching unrelated Gradle code', () => {
53
+ const source = `android {\n${signing.renderSigningBlock()}\n defaultConfig {\n applicationId = "com.example"\n }\n}`;
54
+ const cleaned = signing.removeExistingBlock(source);
55
+ expect(cleaned).not.toContain('NOTE_CONNECTION_ANDROID_SIGNING_START');
56
+ expect(cleaned).toContain('applicationId = "com.example"');
57
+ });
58
+ test('does not embed secret values and runner invokes the signer around generated builds', () => {
59
+ const runner = fs.readFileSync(runnerScriptPath, 'utf8');
60
+ const block = signing.renderSigningBlock();
61
+ expect(block).toContain('System.getenv');
62
+ expect(block).not.toContain('password123');
63
+ expect(runner).toContain('configure-tauri-android-signing.js');
64
+ expect(runner).toContain('Failed to synchronize Android release signing configuration');
65
+ });
66
+ });