rea-agents 6.2.0 → 6.3.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 (297) hide show
  1. package/README.md +18 -20
  2. package/bridge/hopper_bridge.py +2 -0
  3. package/dist/android/JadxProvider.js +8 -2
  4. package/dist/android/JadxSession.js +51 -9
  5. package/dist/application/BinaryTargetResolver.js +63 -17
  6. package/dist/application/BrowserScenarioEvidence.js +2 -2
  7. package/dist/application/ClientConfigurationDocument.js +28 -0
  8. package/dist/application/ClientRegistrationStatus.js +19 -6
  9. package/dist/application/DirectAnalysis.js +12 -6
  10. package/dist/application/DirectAnalysisCleanup.js +0 -1
  11. package/dist/application/DirectAnalysisStatus.js +2 -5
  12. package/dist/application/Doctor.js +25 -34
  13. package/dist/application/DoctorDiagnostics.js +1 -1
  14. package/dist/application/EvidenceBundleFiles.js +1 -1
  15. package/dist/application/InvestigationProviders.js +4 -4
  16. package/dist/application/JsonFiles.js +15 -15
  17. package/dist/application/LinuxHopper.js +2 -1
  18. package/dist/application/ReconstructionObligationCandidates.js +9 -2
  19. package/dist/application/ReconstructionObligationLedgerProjection.js +3 -1
  20. package/dist/application/RegularFileRead.js +17 -0
  21. package/dist/application/Setup.js +18 -7
  22. package/dist/application/SetupClientConfiguration.js +18 -7
  23. package/dist/application/SetupHost.js +6 -4
  24. package/dist/application/SetupPlan.js +19 -15
  25. package/dist/application/SetupSkill.js +57 -8
  26. package/dist/application/SupportedClients.js +43 -0
  27. package/dist/application/Uninstall.js +18 -2
  28. package/dist/application/UpdateMaintenance.js +6 -2
  29. package/dist/application/Utf8JsonInput.js +64 -0
  30. package/dist/application/WebScriptExportService.js +13 -6
  31. package/dist/application/android/AndroidAnalysisService.js +1 -1
  32. package/dist/application/binary/AnalysisSnapshotCache.js +5 -4
  33. package/dist/application/binary/BinarySession.js +16 -0
  34. package/dist/application/binary/BinarySessionOpen.js +9 -1
  35. package/dist/application/javascript/ElectronActiveEvidence.js +2 -2
  36. package/dist/application/javascript/ElectronBoundaryAnalysis.js +2 -2
  37. package/dist/application/javascript/ElectronBoundaryGraphNative.js +5 -1
  38. package/dist/application/javascript/JavaScriptApplicationEvidence.js +1 -0
  39. package/dist/application/javascript/JavaScriptApplicationEvidenceGraph.js +107 -46
  40. package/dist/application/javascript/JavaScriptApplicationService.js +1 -0
  41. package/dist/application/javascript/JavaScriptApplicationWorkflowService.js +59 -45
  42. package/dist/application/javascript/JavaScriptArtifactAnalysis.js +0 -6
  43. package/dist/application/javascript/JavaScriptArtifactGraphBuilder.js +8 -18
  44. package/dist/application/javascript/JavaScriptArtifactGraphFindings.js +34 -26
  45. package/dist/application/javascript/JavaScriptArtifactPathResolution.js +44 -12
  46. package/dist/application/javascript/JavaScriptArtifactReconstruction.js +6 -3
  47. package/dist/application/javascript/JavaScriptArtifactReconstructionInput.js +1 -0
  48. package/dist/application/javascript/JavaScriptJsonModules.js +2 -2
  49. package/dist/application/javascript/JavaScriptModuleRelationships.js +5 -17
  50. package/dist/application/javascript/JavaScriptRuntimeReconciliationService.js +11 -27
  51. package/dist/application/javascript/JavaScriptSemanticGraphAsyncProjection.js +2 -2
  52. package/dist/application/javascript/JavaScriptSemanticGraphBuilder.js +8 -6
  53. package/dist/application/javascript/JavaScriptSemanticGraphChildProcessProjection.js +1 -1
  54. package/dist/application/javascript/JavaScriptSemanticGraphConstruction.js +7 -5
  55. package/dist/application/javascript/JavaScriptSemanticGraphDataProjection.js +1 -1
  56. package/dist/application/javascript/JavaScriptSemanticGraphEvidence.js +1 -1
  57. package/dist/application/javascript/JavaScriptSemanticGraphFingerprintProjection.js +2 -2
  58. package/dist/application/javascript/JavaScriptSemanticGraphFlowProjection.js +2 -2
  59. package/dist/application/javascript/JavaScriptSemanticGraphObjectProjection.js +1 -1
  60. package/dist/application/javascript/JavaScriptSemanticGraphProjection.js +4 -4
  61. package/dist/application/javascript/JavaScriptSemanticGraphResourceProjection.js +1 -1
  62. package/dist/application/javascript/JavaScriptSemanticGraphValueProjection.js +10 -7
  63. package/dist/application/javascript/JavaScriptSemanticTraceService.js +15 -10
  64. package/dist/application/native/NativeCallRoutes.js +2 -4
  65. package/dist/artifacts/ArtifactProvider.js +14 -16
  66. package/dist/artifacts/ArtifactProviderMetadata.js +1 -4
  67. package/dist/artifacts/AsarArtifactReader.js +81 -17
  68. package/dist/artifacts/DirectoryArtifactReader.js +2 -2
  69. package/dist/artifacts/NativeDmgArtifactReader.js +152 -67
  70. package/dist/artifacts/NativeDmgOwnership.js +109 -0
  71. package/dist/artifacts/apple/KeyedArchiveReader.js +54 -46
  72. package/dist/artifacts/apple/MachoLoadCommandReader.js +36 -6
  73. package/dist/artifacts/extraction/ArtifactExtraction.js +29 -11
  74. package/dist/artifacts/inventory/ArtifactGraphConstruction.js +7 -8
  75. package/dist/artifacts/inventory/classify.js +33 -14
  76. package/dist/artifacts/inventory/hashStableRootArtifact.js +37 -0
  77. package/dist/artifacts/inventory/scanCanonical.js +28 -22
  78. package/dist/artifacts/inventory/scanReader.js +7 -11
  79. package/dist/artifacts/javascript/JavaScriptArtifactFiles.js +65 -27
  80. package/dist/browser/CdpBrowserProvider.js +4 -3
  81. package/dist/browser/CdpCaptureStorageFingerprints.js +3 -3
  82. package/dist/browser/CdpElectronInspection.js +2 -2
  83. package/dist/browser/CdpElectronProvider.js +4 -3
  84. package/dist/browser/CdpElectronScripts.js +2 -2
  85. package/dist/browser/CdpWebMcpDiscovery.js +16 -13
  86. package/dist/browser/PlaywrightBrowserScenarioProvider.js +6 -6
  87. package/dist/browser/PlaywrightScenarioBrowser.js +145 -42
  88. package/dist/browser/PlaywrightScenarioSession.js +3 -11
  89. package/dist/browser/WebSourceMapFetcher.js +32 -6
  90. package/dist/browser/assets/ScenarioScriptCapture.js +8 -12
  91. package/dist/cli/coreAnalysisCommands.js +2 -2
  92. package/dist/cli/coreBinaryCommands.js +13 -9
  93. package/dist/cli/electronCommands.js +9 -10
  94. package/dist/cli/javascriptApplicationAnalysis.js +2 -12
  95. package/dist/cli/jsonOutput.js +3 -2
  96. package/dist/cli/streamedJsonOutput.js +83 -4
  97. package/dist/cli/utilityCommands.js +2 -4
  98. package/dist/cli.js +5 -2
  99. package/dist/cliJsonFile.js +179 -0
  100. package/dist/cliJsonInput.js +47 -34
  101. package/dist/cliObservationOptions.js +4 -0
  102. package/dist/composition/auxiliaryAnalysisProviders.js +5 -4
  103. package/dist/composition/browserScenario.js +2 -2
  104. package/dist/composition/directAnalysis.js +2 -3
  105. package/dist/contracts/artifactComparisonExample.js +5 -5
  106. package/dist/contracts/errorSchemas.js +3 -1
  107. package/dist/contracts/functionComparisonExample.js +8 -0
  108. package/dist/contracts/inputSchemaPresentation.js +94 -3
  109. package/dist/contracts/javascript/electronToolContracts.js +2 -1
  110. package/dist/contracts/javascript/javascriptApplicationWorkflowExamples.js +3 -2
  111. package/dist/contracts/javascript/javascriptExportShapeComparisonExample.js +6 -3
  112. package/dist/contracts/javascript/javascriptRuntimeReconciliationExample.js +8 -7
  113. package/dist/contracts/managed/managedToolContracts.js +3 -2
  114. package/dist/contracts/mobileApplicationGraphExamples.js +5 -5
  115. package/dist/contracts/native/nativeToolContracts.js +1 -1
  116. package/dist/contracts/sessionLifecycleInputs.js +4 -3
  117. package/dist/contracts/sessionStatusContract.js +3 -2
  118. package/dist/contracts/sessionToolContracts.js +3 -2
  119. package/dist/contracts/sessionToolSchemas.js +3 -2
  120. package/dist/contracts/toolOutputSchemaGroups.js +2 -2
  121. package/dist/contracts/toolOutputSchemaPrimitives.js +2 -5
  122. package/dist/domain/analysisErrorBase.js +5 -0
  123. package/dist/domain/analysisErrorPresentation.js +4 -2
  124. package/dist/domain/analysisSnapshot.js +3 -1
  125. package/dist/domain/analysisView/analysisView.js +1 -3
  126. package/dist/domain/android/androidPartialObservation.js +36 -0
  127. package/dist/domain/apple/dylibResolution.js +1 -1
  128. package/dist/domain/artifactComparison.js +4 -4
  129. package/dist/domain/artifactGraph.js +1 -1
  130. package/dist/domain/artifactIdentity.js +8 -8
  131. package/dist/domain/artifactInspection.js +8 -8
  132. package/dist/domain/artifactInventoryEvidence.js +5 -5
  133. package/dist/domain/browserCompleteness.js +3 -2
  134. package/dist/domain/browserScenarioCaptureValues.js +2 -2
  135. package/dist/domain/browserScenarioNormalization.js +3 -3
  136. package/dist/domain/browserScenarioValues.js +3 -2
  137. package/dist/domain/callPath.js +8 -10
  138. package/dist/domain/canonicalDigest.js +7 -1
  139. package/dist/domain/canonicalOrdering.js +1 -13
  140. package/dist/domain/changedBehavior.js +2 -3
  141. package/dist/domain/comparisonSemantics.js +0 -5
  142. package/dist/domain/digests.js +0 -3
  143. package/dist/domain/evidence.js +10 -5
  144. package/dist/domain/evidenceBundle.js +51 -33
  145. package/dist/domain/firmware/firmwareAnalysis.js +4 -3
  146. package/dist/domain/functionComparison.js +2 -1
  147. package/dist/domain/functionComparisonNormalization.js +4 -3
  148. package/dist/domain/functionComparisonResults.js +2 -2
  149. package/dist/domain/functionDossierEvidence.js +2 -1
  150. package/dist/domain/hopperValues.js +11 -15
  151. package/dist/domain/javascript/electronActiveObservation.js +4 -3
  152. package/dist/domain/javascript/electronStaticAnalysisBrowser.js +2 -2
  153. package/dist/domain/javascript/electronStaticAnalysisIpc.js +9 -1
  154. package/dist/domain/javascript/electronStaticAnalysisNative.js +5 -3
  155. package/dist/domain/javascript/electronStaticAnalysisValues.js +8 -4
  156. package/dist/domain/javascript/javascriptApplicationAnalysis.js +3 -1
  157. package/dist/domain/javascript/javascriptApplicationChangeGraph.js +12 -8
  158. package/dist/domain/javascript/javascriptApplicationEvidenceSchemas.js +4 -0
  159. package/dist/domain/javascript/javascriptApplicationGraph.js +15 -13
  160. package/dist/domain/javascript/javascriptApplicationVersionComparison.js +4 -4
  161. package/dist/domain/javascript/javascriptApplicationVersionComparisonSchemas.js +37 -22
  162. package/dist/domain/javascript/javascriptApplicationVersionItems.js +45 -34
  163. package/dist/domain/javascript/javascriptApplicationVersionKeys.js +7 -7
  164. package/dist/domain/javascript/javascriptAstFingerprint.js +2 -2
  165. package/dist/domain/javascript/javascriptAstValues.js +22 -0
  166. package/dist/domain/javascript/javascriptExportShapeComparison.js +3 -2
  167. package/dist/domain/javascript/javascriptExportShapeComparisonSchemas.js +31 -23
  168. package/dist/domain/javascript/javascriptExportShapeSelection.js +2 -2
  169. package/dist/domain/javascript/javascriptExportShapeVariants.js +3 -2
  170. package/dist/domain/javascript/javascriptFeatureSeed.js +5 -3
  171. package/dist/domain/javascript/javascriptFeatureTrace.js +7 -6
  172. package/dist/domain/javascript/javascriptFeatureTraceSchemas.js +17 -9
  173. package/dist/domain/javascript/javascriptFeatureTraversal.js +4 -4
  174. package/dist/domain/javascript/javascriptNativeHandoff.js +6 -6
  175. package/dist/domain/javascript/javascriptRuntimeObservation.js +8 -5
  176. package/dist/domain/javascript/javascriptRuntimeReconciliation.js +12 -6
  177. package/dist/domain/javascript/javascriptRuntimeReconciliationActive.js +9 -8
  178. package/dist/domain/javascript/javascriptRuntimeReconciliationCaptureParsing.js +212 -0
  179. package/dist/domain/javascript/javascriptRuntimeReconciliationGraph.js +5 -4
  180. package/dist/domain/javascript/javascriptRuntimeReconciliationInputValidation.js +28 -0
  181. package/dist/domain/javascript/javascriptRuntimeReconciliationMatching.js +5 -5
  182. package/dist/domain/javascript/javascriptRuntimeReconciliationParsing.js +12 -223
  183. package/dist/domain/javascript/javascriptRuntimeReconciliationResult.js +2 -2
  184. package/dist/domain/javascript/javascriptRuntimeReconciliationRuntime.js +15 -4
  185. package/dist/domain/javascript/javascriptRuntimeReconciliationSchemas.js +2 -2
  186. package/dist/domain/javascript/javascriptRuntimeStaticCandidates.js +7 -6
  187. package/dist/domain/javascript/javascriptSemanticAnalysis.js +200 -8
  188. package/dist/domain/javascript/javascriptSemanticCallResolution.js +9 -7
  189. package/dist/domain/javascript/javascriptSemanticChildProcesses.js +2 -2
  190. package/dist/domain/javascript/javascriptSemanticCoverage.js +20 -0
  191. package/dist/domain/javascript/javascriptSemanticDataEffectHelpers.js +7 -9
  192. package/dist/domain/javascript/javascriptSemanticDataEffects.js +4 -11
  193. package/dist/domain/javascript/javascriptSemanticFingerprints.js +2 -2
  194. package/dist/domain/javascript/javascriptSemanticGraph.js +142 -35
  195. package/dist/domain/javascript/javascriptSemanticGraphSchemas.js +19 -1
  196. package/dist/domain/javascript/javascriptSemanticIr.js +0 -4
  197. package/dist/domain/javascript/javascriptSemanticIterationSources.js +106 -0
  198. package/dist/domain/javascript/javascriptSemanticMemberMutations.js +497 -113
  199. package/dist/domain/javascript/javascriptSemanticMutationInitializers.js +190 -0
  200. package/dist/domain/javascript/javascriptSemanticMutationValues.js +41 -27
  201. package/dist/domain/javascript/javascriptSemanticObjects.js +2 -6
  202. package/dist/domain/javascript/javascriptSemanticPrimitives.js +7 -2
  203. package/dist/domain/javascript/javascriptSemanticProjection.js +20 -14
  204. package/dist/domain/javascript/javascriptSemanticPromises.js +15 -21
  205. package/dist/domain/javascript/javascriptSemanticPropertyPaths.js +23 -0
  206. package/dist/domain/javascript/javascriptSemanticProvenance.js +3 -3
  207. package/dist/domain/javascript/javascriptSemanticQuery.js +8 -2
  208. package/dist/domain/javascript/javascriptSemanticQueryIdentity.js +3 -2
  209. package/dist/domain/javascript/javascriptSemanticQuerySchemas.js +8 -9
  210. package/dist/domain/javascript/javascriptSemanticRequireOrigin.js +16 -10
  211. package/dist/domain/javascript/javascriptSemanticReturns.js +23 -27
  212. package/dist/domain/javascript/javascriptSemanticTraversal.js +6 -1
  213. package/dist/domain/javascript/javascriptSemanticValues.js +135 -24
  214. package/dist/domain/javascript/javascriptSourceMapPaths.js +11 -1
  215. package/dist/domain/javascript/javascriptSourceParser.js +34 -9
  216. package/dist/domain/javascript/javascriptStaticAnalysisBundler.js +5 -6
  217. package/dist/domain/javascript/javascriptStaticAnalysisHelpers.js +26 -15
  218. package/dist/domain/javascript/sourceToBundleComparison.js +7 -5
  219. package/dist/domain/javascript/sourceToBundleSignals.js +7 -7
  220. package/dist/domain/jsonSerialization.js +109 -55
  221. package/dist/domain/jsonShape.js +46 -19
  222. package/dist/domain/localPath.js +10 -4
  223. package/dist/domain/managed/managedApplicationGraph.js +10 -6
  224. package/dist/domain/managed/managedApplicationGraphNodes.js +82 -57
  225. package/dist/domain/managed/managedMemberComparisonMatch.js +6 -7
  226. package/dist/domain/native/nativeCallObservation.js +2 -4
  227. package/dist/domain/native/nativeValueFlow.js +5 -11
  228. package/dist/domain/native/objcSwiftMetadata.js +15 -17
  229. package/dist/domain/process/processCapture.js +11 -10
  230. package/dist/domain/process/processCaptureCoverage.js +8 -5
  231. package/dist/domain/process/processCaptureExample.js +43 -0
  232. package/dist/domain/process/processCaptureValidation.js +4 -3
  233. package/dist/domain/process/processComparison.js +0 -23
  234. package/dist/domain/process/processScenario.js +3 -3
  235. package/dist/domain/process/processTraceEvaluation.js +4 -4
  236. package/dist/domain/process/processTraceSpecification.js +1 -3
  237. package/dist/domain/providerCleanupError.js +6 -2
  238. package/dist/domain/reconstructionCoverage.js +2 -1
  239. package/dist/domain/reconstructionUnknowns.js +4 -3
  240. package/dist/domain/reconstructionVerification.js +3 -2
  241. package/dist/domain/referenceSourceGraph.js +7 -7
  242. package/dist/domain/referenceSourceImportParsing.js +34 -45
  243. package/dist/domain/residualUnknown.js +3 -2
  244. package/dist/domain/staticRuntimeCorrelation.js +2 -1
  245. package/dist/domain/swiftSymbolClassification.js +6 -1
  246. package/dist/domain/symbolAnalysis.js +17 -2
  247. package/dist/domain/unicodeCodePointOrder.js +6 -0
  248. package/dist/domain/webBundleAnalyzerInspection.js +9 -1
  249. package/dist/domain/webCaptureDiff.js +71 -29
  250. package/dist/domain/webCaptureDiffSchemas.js +2 -8
  251. package/dist/domain/webInventory.js +3 -2
  252. package/dist/domain/webMcpDiscovery.js +4 -3
  253. package/dist/domain/webScriptExport.js +1 -1
  254. package/dist/dotnet/ManagedMemberInstructionDecoder.js +1 -2
  255. package/dist/dotnet/ManagedStaticProvider.js +10 -7
  256. package/dist/dotnet/ManagedStaticProviderMetadata.js +1 -4
  257. package/dist/filesystem/RegularFile.js +41 -0
  258. package/dist/generatedPackageMetadata.js +1 -1
  259. package/dist/ghidra/GhidraClient.js +14 -4
  260. package/dist/ghidra/GhidraInstallation.js +111 -10
  261. package/dist/ghidra/GhidraLauncher.js +43 -19
  262. package/dist/hopper/HopperCleanup.js +1 -1
  263. package/dist/ida/IdaOperationRunner.js +2 -0
  264. package/dist/ida/IdaSessionClient.js +4 -2
  265. package/dist/inspector/JavaScriptRuntimeScope.js +9 -5
  266. package/dist/inspector/V8InspectorCaptureProjection.js +46 -13
  267. package/dist/inspector/V8InspectorProvider.js +60 -8
  268. package/dist/main/shutdown.js +76 -32
  269. package/dist/main/transport.js +1 -1
  270. package/dist/native/AppleObjcDispatchFacets.js +3 -3
  271. package/dist/native/AppleObjcProperties.js +17 -7
  272. package/dist/native/LldbCallTracer.js +241 -75
  273. package/dist/native/NativeMacOSProvider.js +10 -5
  274. package/dist/native/NativeMacOSProviderMetadata.js +1 -2
  275. package/dist/process/ProcessOwnershipObservation.js +9 -13
  276. package/dist/process/capture/FilesystemSnapshot.js +88 -18
  277. package/dist/process/capture/ProcessCaptureLifecycle.js +13 -27
  278. package/dist/process/capture/ProcessFilesystemEffects.js +19 -4
  279. package/dist/process/capture/ProcessHarness.js +8 -21
  280. package/dist/server/analysisAdmission.js +8 -0
  281. package/dist/server/createServer.js +22 -23
  282. package/dist/server/registerApplicationTools/compareExportShapes.js +2 -1
  283. package/dist/server/registerApplicationTools/helpers.js +3 -2
  284. package/dist/server/registerArtifactTools.js +2 -2
  285. package/dist/server/registerCloseLifecycleTool.js +6 -4
  286. package/dist/server/registerEnhancedTools.js +78 -73
  287. package/dist/server/registerEvidenceTools.js +54 -46
  288. package/dist/server/registerManagedTools.js +7 -4
  289. package/dist/server/registerOfficialTools.js +31 -25
  290. package/dist/server/registerSessionTools.js +9 -3
  291. package/native/windows/build/manifest.json +1 -1
  292. package/package.json +6 -3
  293. package/skills/reverse-engineer-anything/SKILL.md +5 -5
  294. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +9 -0
  295. package/dist/artifacts/inventory/policy.js +0 -2
  296. package/dist/domain/managed/managedApplicationGraphNodeCoverage.js +0 -6
  297. package/dist/server/registerNativeTools.js +0 -6
@@ -6,6 +6,10 @@ import { AnalysisError } from "../domain/analysisErrorBase.js";
6
6
  import { BrowserObservationError } from "../domain/browserObservationError.js";
7
7
  import { withPlaywrightExecutionBoundary } from "./PlaywrightExecutionBoundary.js";
8
8
  const OPERATION = "capture_browser_scenario";
9
+ const throwIfScenarioCancelled = (signal) => {
10
+ if (signal?.aborted === true)
11
+ throw new BrowserObservationError(OPERATION, "cancelled");
12
+ };
9
13
  /** Owns one browser connection and optional private profile through cleanup retries. */
10
14
  export class PlaywrightScenarioBrowserCleanupOwner {
11
15
  resources;
@@ -22,9 +26,10 @@ export class PlaywrightScenarioBrowserCleanupOwner {
22
26
  const cleanup = this.#closePromise ?? this.#closeResources(finishEvents);
23
27
  if (this.#closePromise === undefined) {
24
28
  this.#closePromise = cleanup;
25
- void cleanup.catch(() => {
29
+ void cleanup.then(() => this.resources.onSettled?.(this.#browserClosed), () => {
26
30
  if (this.#closePromise === cleanup)
27
31
  this.#closePromise = undefined;
32
+ this.resources.onSettled?.(this.#browserClosed);
28
33
  });
29
34
  }
30
35
  return this.#awaitCleanup(cleanup, signal);
@@ -86,6 +91,79 @@ export class PlaywrightScenarioBrowserCleanupOwner {
86
91
  throw new AggregateError(failures, "Browser session cleanup failed");
87
92
  }
88
93
  }
94
+ const attachedScenarioAdmissions = new Map();
95
+ const releaseAttachedScenario = (key, admission) => {
96
+ const waiter = admission.waiters.shift();
97
+ if (waiter === undefined) {
98
+ admission.active = false;
99
+ if (admission.cleanupOwner === undefined &&
100
+ attachedScenarioAdmissions.get(key) === admission)
101
+ attachedScenarioAdmissions.delete(key);
102
+ return;
103
+ }
104
+ if (waiter.onAbort !== undefined)
105
+ waiter.signal?.removeEventListener("abort", waiter.onAbort);
106
+ if (waiter.signal?.aborted === true) {
107
+ waiter.reject(new BrowserObservationError(OPERATION, "cancelled"));
108
+ releaseAttachedScenario(key, admission);
109
+ return;
110
+ }
111
+ waiter.resolve(createAttachedScenarioRelease(key, admission));
112
+ };
113
+ const createAttachedScenarioRelease = (key, admission) => {
114
+ let released = false;
115
+ return () => {
116
+ if (released)
117
+ return;
118
+ released = true;
119
+ releaseAttachedScenario(key, admission);
120
+ };
121
+ };
122
+ const acquireAttachedScenario = (scenario, signal) => {
123
+ if (signal?.aborted === true)
124
+ return Promise.reject(new BrowserObservationError(OPERATION, "cancelled"));
125
+ const key = JSON.stringify([scenario.cdp_endpoint, scenario.target_id]);
126
+ const admission = attachedScenarioAdmissions.get(key) ??
127
+ {
128
+ active: false,
129
+ waiters: [],
130
+ cleanupOwner: undefined,
131
+ };
132
+ attachedScenarioAdmissions.set(key, admission);
133
+ return new Promise((resolve, reject) => {
134
+ const grant = (release) => resolve({ admission, release });
135
+ if (!admission.active) {
136
+ admission.active = true;
137
+ grant(createAttachedScenarioRelease(key, admission));
138
+ return;
139
+ }
140
+ const waiter = {
141
+ signal,
142
+ resolve: (release) => grant(release),
143
+ reject,
144
+ onAbort: undefined,
145
+ };
146
+ admission.waiters.push(waiter);
147
+ if (signal !== undefined) {
148
+ const onAbort = () => {
149
+ const index = admission.waiters.indexOf(waiter);
150
+ if (index < 0)
151
+ return;
152
+ admission.waiters.splice(index, 1);
153
+ signal.removeEventListener("abort", onAbort);
154
+ reject(new BrowserObservationError(OPERATION, "cancelled"));
155
+ if (!admission.active && admission.waiters.length === 0)
156
+ releaseAttachedScenario(key, admission);
157
+ };
158
+ waiter.onAbort = onAbort;
159
+ signal.addEventListener("abort", onAbort, { once: true });
160
+ if (signal.aborted) {
161
+ onAbort();
162
+ return;
163
+ }
164
+ }
165
+ });
166
+ };
89
167
  const allowedBrowserEnvironment = (environment) => {
90
168
  const allowed = [
91
169
  "DBUS_SESSION_BUS_ADDRESS",
@@ -106,12 +184,14 @@ const allowedBrowserEnvironment = (environment) => {
106
184
  .map((name) => [name, environment[name]])
107
185
  .filter((entry) => entry[1] !== undefined));
108
186
  };
109
- const findConnectedPage = async (browser, targetId) => {
187
+ const findConnectedPage = async (browser, targetId, signal) => {
110
188
  for (const context of browser.contexts())
111
189
  for (const page of context.pages()) {
112
190
  const session = await context.newCDPSession(page);
113
191
  try {
192
+ throwIfScenarioCancelled(signal);
114
193
  const { targetInfo } = await session.send("Target.getTargetInfo");
194
+ throwIfScenarioCancelled(signal);
115
195
  if (targetInfo.targetId !== targetId)
116
196
  continue;
117
197
  return { context, page };
@@ -122,33 +202,33 @@ const findConnectedPage = async (browser, targetId) => {
122
202
  }
123
203
  throw new BrowserObservationError(OPERATION, "target_not_found");
124
204
  };
125
- const configureAttachedEnvironment = async (context, page, scenario) => {
126
- await page.setViewportSize({
127
- width: scenario.environment.viewport.width,
128
- height: scenario.environment.viewport.height,
129
- });
205
+ const configureAttachedEnvironment = async (context, page, environment, signal) => {
206
+ // setViewportSize also resizes the external browser window. The scenario's
207
+ // CDP metrics override below supplies the viewport and expires on disconnect.
130
208
  await page.emulateMedia({
131
- colorScheme: scenario.environment.color_scheme,
132
- reducedMotion: scenario.environment.reduced_motion,
209
+ colorScheme: environment.color_scheme,
210
+ reducedMotion: environment.reduced_motion,
133
211
  });
212
+ throwIfScenarioCancelled(signal);
134
213
  const session = await context.newCDPSession(page);
135
- try {
136
- await session.send("Emulation.setDeviceMetricsOverride", {
137
- width: scenario.environment.viewport.width,
138
- height: scenario.environment.viewport.height,
139
- deviceScaleFactor: scenario.environment.viewport.device_scale_factor,
140
- mobile: false,
141
- });
142
- await session.send("Emulation.setLocaleOverride", {
143
- locale: scenario.environment.locale,
144
- });
145
- await session.send("Emulation.setTimezoneOverride", {
146
- timezoneId: scenario.environment.timezone,
147
- });
148
- }
149
- finally {
150
- await session.detach();
151
- }
214
+ throwIfScenarioCancelled(signal);
215
+ // Chromium resets these overrides when the CDP session detaches. Keep it on
216
+ // the scenario transport until cleanup disconnects the attached browser.
217
+ await session.send("Emulation.setDeviceMetricsOverride", {
218
+ width: environment.viewport.width,
219
+ height: environment.viewport.height,
220
+ deviceScaleFactor: environment.viewport.device_scale_factor,
221
+ mobile: false,
222
+ dontSetVisibleSize: true,
223
+ });
224
+ throwIfScenarioCancelled(signal);
225
+ await session.send("Emulation.setLocaleOverride", {
226
+ locale: environment.locale,
227
+ });
228
+ throwIfScenarioCancelled(signal);
229
+ await session.send("Emulation.setTimezoneOverride", {
230
+ timezoneId: environment.timezone,
231
+ });
152
232
  };
153
233
  /** Preserve an operation failure while reporting any incomplete cleanup. */
154
234
  export const failBrowserScenarioOperation = async (cleanup, primaryFailure, fallbackCleanupResources = ["browser_transport"]) => {
@@ -174,28 +254,51 @@ export const failBrowserScenarioOperation = async (cleanup, primaryFailure, fall
174
254
  }
175
255
  throw primaryFailure;
176
256
  };
177
- /** Launch or attach using the caller-selected environment and browser engine. */
178
- export const openPlaywrightScenarioBrowser = async (scenario, environment, launcher = chromium) => {
179
- if (scenario.browser.mode === "connect") {
180
- const browser = await launcher.connectOverCDP(scenario.browser.cdp_endpoint, { timeout: 0 });
257
+ const openAttachedScenarioBrowser = async (browserRequest, environment, launcher, signal) => {
258
+ const lease = await acquireAttachedScenario(browserRequest, signal);
259
+ let cleanupOwner;
260
+ try {
261
+ const priorOwner = lease.admission.cleanupOwner;
262
+ if (priorOwner !== undefined)
263
+ await priorOwner.close();
264
+ throwIfScenarioCancelled(signal);
265
+ const browser = await launcher.connectOverCDP(browserRequest.cdp_endpoint);
181
266
  const cleanup = new PlaywrightScenarioBrowserCleanupOwner({
182
267
  closeBrowser: () => browser.close(),
268
+ onSettled: (browserClosed) => {
269
+ if (browserClosed && lease.admission.cleanupOwner === cleanup)
270
+ lease.admission.cleanupOwner = undefined;
271
+ lease.release();
272
+ },
183
273
  removeProfile: undefined,
184
274
  });
185
- try {
186
- const target = await findConnectedPage(browser, scenario.browser.target_id);
187
- await configureAttachedEnvironment(target.context, target.page, scenario);
188
- return {
189
- ...target,
190
- browser,
191
- profilePath: undefined,
192
- cleanup,
193
- };
194
- }
195
- catch (cause) {
196
- return failBrowserScenarioOperation(() => cleanup.close(), cause);
275
+ cleanupOwner = cleanup;
276
+ lease.admission.cleanupOwner = cleanup;
277
+ throwIfScenarioCancelled(signal);
278
+ const target = await withPlaywrightExecutionBoundary(() => findConnectedPage(browser, browserRequest.target_id, signal), undefined, signal);
279
+ throwIfScenarioCancelled(signal);
280
+ await withPlaywrightExecutionBoundary(() => configureAttachedEnvironment(target.context, target.page, environment, signal), undefined, signal);
281
+ return {
282
+ ...target,
283
+ browser,
284
+ profilePath: undefined,
285
+ cleanup,
286
+ };
287
+ }
288
+ catch (cause) {
289
+ if (cleanupOwner === undefined) {
290
+ lease.release();
291
+ throw cause;
197
292
  }
293
+ const cleanup = cleanupOwner;
294
+ return failBrowserScenarioOperation(() => cleanup.close(), cause);
198
295
  }
296
+ };
297
+ /** Launch or attach using the caller-selected environment and browser engine. */
298
+ export const openPlaywrightScenarioBrowser = async (scenario, environment, options = {}) => {
299
+ const launcher = options.launcher ?? chromium;
300
+ if (scenario.browser.mode === "connect")
301
+ return openAttachedScenarioBrowser(scenario.browser, scenario.environment, launcher, options.signal);
199
302
  const profilePath = await mkdtemp(join(tmpdir(), "rea-browser-scenario-"));
200
303
  let context;
201
304
  try {
@@ -86,7 +86,9 @@ export class PlaywrightScenarioSession {
86
86
  if (secrets === undefined)
87
87
  throw new BrowserObservationError(OPERATION, "secret_unavailable");
88
88
  const startedAt = Date.now();
89
- const opening = openPlaywrightScenarioBrowser(scenario, environment);
89
+ const opening = openPlaywrightScenarioBrowser(scenario, environment, {
90
+ signal: options.signal,
91
+ });
90
92
  let opened;
91
93
  let events;
92
94
  try {
@@ -203,13 +205,3 @@ export class PlaywrightScenarioSession {
203
205
  return this.secrets.redact(error instanceof Error ? error.message : "browser action failed");
204
206
  }
205
207
  }
206
- /** Production Playwright/CDP session factory. */
207
- export class PlaywrightScenarioSessionFactory {
208
- environment;
209
- constructor(environment) {
210
- this.environment = environment;
211
- }
212
- open(scenario, options = {}) {
213
- return PlaywrightScenarioSession.open(scenario, this.environment, options);
214
- }
215
- }
@@ -106,6 +106,12 @@ class SourceMapEncodingError extends Error {
106
106
  this.name = "SourceMapEncodingError";
107
107
  }
108
108
  }
109
+ class SourceMapRedirectLocationError extends Error {
110
+ constructor(url, location) {
111
+ super(`Source-map redirect from ${JSON.stringify(url)} supplied an invalid Location URL: ${JSON.stringify(location)}.`);
112
+ this.name = "SourceMapRedirectLocationError";
113
+ }
114
+ }
109
115
  const fetchOne = async (request, input, signal, host, budget) => {
110
116
  if (!approvedUrl(request.fetchUrl, input.allowed_origins))
111
117
  return emptySourceMapItem(request, "policy_filtered", "Declared source-map URL is outside the approved exact origins.");
@@ -131,13 +137,25 @@ const fetchOne = async (request, input, signal, host, budget) => {
131
137
  throw cause;
132
138
  if (cause instanceof SourceMapEncodingError)
133
139
  return emptySourceMapItem(request, "invalid", cause.message);
134
- return emptySourceMapItem(request, "fetch_failed", cause instanceof SourceMapSizeLimitError
140
+ return emptySourceMapItem(request, "fetch_failed", cause instanceof SourceMapRedirectLocationError ||
141
+ cause instanceof SourceMapSizeLimitError ||
142
+ cause instanceof SourceMapDeadlineError
135
143
  ? cause.message
136
- : cause instanceof SourceMapDeadlineError
137
- ? cause.message
138
- : "Source-map fetch or validation failed.");
144
+ : "Source-map fetch or validation failed.");
139
145
  }
140
146
  };
147
+ /**
148
+ * Fetch redirect statuses per WHATWG Fetch (redirect status list:
149
+ * 301, 302, 303, 307, 308). Single owner for redirect decisions.
150
+ * 300/304/305/306 carry `Location` without being redirects — following
151
+ * them would replace the observed failure with an unrelated map.
152
+ * See https://fetch.spec.whatwg.org/#redirect-status
153
+ */
154
+ export const isFetchRedirectStatus = (status) => status === 301 ||
155
+ status === 302 ||
156
+ status === 303 ||
157
+ status === 307 ||
158
+ status === 308;
141
159
  const fetchFollowingApprovedRedirects = async (initialUrl, allowedOrigins, signal, host) => {
142
160
  let current = initialUrl;
143
161
  const visited = new Set();
@@ -163,13 +181,21 @@ const fetchFollowingApprovedRedirects = async (initialUrl, allowedOrigins, signa
163
181
  await response.body?.cancel(signal.reason).catch(() => undefined);
164
182
  throw signal.reason;
165
183
  }
166
- if (response.status < 300 || response.status >= 400)
184
+ // `Location` is a redirect target only for Fetch redirect statuses.
185
+ // Other 3xx responses retain their own HTTP failure (strict identity:
186
+ // never replace the observed failure with an unrelated map).
187
+ if (!isFetchRedirectStatus(response.status))
167
188
  return { response, fetchedUrl: current };
168
189
  const location = response.headers.get("location");
169
190
  if (location === null)
170
191
  return { response, fetchedUrl: current };
171
192
  await response.body?.cancel();
172
- current = new URL(location, current).href;
193
+ try {
194
+ current = new URL(location, current).href;
195
+ }
196
+ catch {
197
+ throw new SourceMapRedirectLocationError(current, location);
198
+ }
173
199
  }
174
200
  };
175
201
  const readBoundedText = async (response, budget, maxBytes, signal) => {
@@ -14,9 +14,9 @@ const indexResponses = (events) => {
14
14
  requests: new Map(),
15
15
  };
16
16
  for (const event of events) {
17
- if (event.kind === "request" && event.transaction_id !== undefined)
17
+ if (event.kind === "request")
18
18
  index.requests.set(event.transaction_id, (index.requests.get(event.transaction_id) ?? 0) + 1);
19
- if (event.kind === "response" && event.transaction_id !== undefined)
19
+ if (event.kind === "response")
20
20
  append(index.responses, event.transaction_id, event);
21
21
  if (event.kind === "network-content" && event.phase === "response")
22
22
  append(index.contents, event.source_event_sequence, event);
@@ -25,33 +25,29 @@ const indexResponses = (events) => {
25
25
  };
26
26
  const projectRequest = (request, index) => {
27
27
  const id = request.transaction_id;
28
- const matches = id === undefined ? [] : (index.responses.get(id) ?? []);
28
+ const matches = index.responses.get(id) ?? [];
29
29
  const response = matches.length === 1 ? matches[0] : undefined;
30
30
  const bodies = response === undefined ? [] : (index.contents.get(response.sequence) ?? []);
31
31
  const body = bodies.length === 1 ? bodies[0]?.body : undefined;
32
- const ambiguous = (id !== undefined && index.requests.get(id) !== 1) ||
33
- matches.length > 1 ||
34
- bodies.length > 1;
32
+ const ambiguous = index.requests.get(id) !== 1 || matches.length > 1 || bodies.length > 1;
35
33
  return {
36
34
  source: {
37
35
  kind: "scenario-response",
38
- transaction_id: id ?? null,
36
+ transaction_id: id,
39
37
  request_sequence: request.sequence,
40
38
  response_sequence: ambiguous ? null : (response?.sequence ?? null),
41
39
  status: ambiguous ? null : (response?.status ?? null),
42
40
  },
43
41
  url: request.url.url,
44
- content: projectBody(body, id, ambiguous),
42
+ content: projectBody(body, ambiguous),
45
43
  };
46
44
  };
47
45
  const unavailable = (reason, message) => ({ state: "unavailable", reason, message });
48
- const projectBody = (body, transactionId, ambiguous) => {
46
+ const projectBody = (body, ambiguous) => {
49
47
  if (ambiguous)
50
48
  return unavailable("ambiguous-transaction", "The capture does not identify one request, response, and content record for this transaction.");
51
49
  if (body === undefined)
52
- return unavailable(transactionId === undefined
53
- ? "transaction-identity-unavailable"
54
- : "response-content-not-retained", "No selected, completed script response bytes were retained for this request.");
50
+ return unavailable("response-content-not-retained", "No selected, completed script response bytes were retained for this request.");
55
51
  if (body.state === "unavailable")
56
52
  return unavailable(body.reason, body.message);
57
53
  if (body.state !== "captured")
@@ -1,7 +1,7 @@
1
1
  import { registerCoreBinaryCommands } from "./coreBinaryCommands.js";
2
2
  import { registerCoreNativeCommands } from "./coreNativeCommands.js";
3
3
  /** Register core binary and native deep-analysis CLI commands. */
4
- export const registerCoreAnalysisCommands = (cli, logger, runDirectAnalysis, resultOutput) => {
5
- registerCoreBinaryCommands(cli, logger, runDirectAnalysis, resultOutput);
4
+ export const registerCoreAnalysisCommands = (cli, logger, runDirectAnalysis) => {
5
+ registerCoreBinaryCommands(cli, logger, runDirectAnalysis);
6
6
  registerCoreNativeCommands(cli, logger, runDirectAnalysis);
7
7
  };
@@ -7,9 +7,10 @@ import { logCliCommand } from "../cliLogging.js";
7
7
  import { directAnalysisOptions, formatSelectionOption, providerSelectionOption, } from "./options.js";
8
8
  import { runCliJavaScriptApplicationAnalysis } from "./javascriptApplicationAnalysis.js";
9
9
  import { withCommandCancellation } from "./commandCancellation.js";
10
+ import { javascriptApplicationOptions } from "../cliObservationOptions.js";
10
11
  /** Register provider-neutral binary overview and procedure CLI commands. */
11
- export const registerCoreBinaryCommands = (cli, logger, runDirectAnalysis, resultOutput) => {
12
- registerOverviewCommands(cli, logger, runDirectAnalysis, resultOutput);
12
+ export const registerCoreBinaryCommands = (cli, logger, runDirectAnalysis) => {
13
+ registerOverviewCommands(cli, logger, runDirectAnalysis);
13
14
  registerDecompileCommand(cli, logger, runDirectAnalysis);
14
15
  registerFunctionCommand(cli, logger, runDirectAnalysis);
15
16
  registerInstructionsCommand(cli, logger, runDirectAnalysis);
@@ -17,7 +18,7 @@ export const registerCoreBinaryCommands = (cli, logger, runDirectAnalysis, resul
17
18
  registerXrefsCommand(cli, logger, runDirectAnalysis);
18
19
  registerTraceCommand(cli, logger, runDirectAnalysis);
19
20
  };
20
- const registerOverviewCommands = (cli, logger, runDirectAnalysis, resultOutput) => {
21
+ const registerOverviewCommands = (cli, logger, runDirectAnalysis) => {
21
22
  const overviewOptions = z.object({
22
23
  snapshot: z
23
24
  .string()
@@ -32,16 +33,19 @@ const registerOverviewCommands = (cli, logger, runDirectAnalysis, resultOutput)
32
33
  args: z.object({
33
34
  path: z.string().describe("App, program, or analysis database path"),
34
35
  }),
35
- options: overviewOptions,
36
- run: async ({ args, options, format }) => {
37
- const output = resultOutput === undefined
38
- ? undefined
39
- : { output: resultOutput, command: CLI_COMMANDS.analyze, format };
36
+ options: overviewOptions.extend({
37
+ integrityPolicy: javascriptApplicationOptions.shape.integrityPolicy,
38
+ }),
39
+ alias: { integrityPolicy: "integrity-policy" },
40
+ run: async ({ args, options }) => {
40
41
  // Route JavaScript targets exactly like analyze-javascript-application,
41
42
  // including typed cancellation; cancellation wraps logging so its exit
42
43
  // code is not replaced by the logged failure status.
43
44
  if (await routesToJavaScriptAnalysis(args.path, options))
44
- return withCommandCancellation((signal) => logCliCommand(logger, "analyze", () => runCliJavaScriptApplicationAnalysis({ input_path: resolve(args.path) }, output, signal)));
45
+ return withCommandCancellation((signal) => logCliCommand(logger, "analyze", () => runCliJavaScriptApplicationAnalysis({
46
+ input_path: resolve(args.path),
47
+ integrity_policy: options.integrityPolicy,
48
+ }, signal)));
45
49
  return logCliCommand(logger, "analyze", () => runDirectAnalysis(args.path, "binary_overview", {}, directAnalysisOptions(logger, options.snapshot, options.provider, options["target-format"])));
46
50
  },
47
51
  });
@@ -14,10 +14,10 @@ import { electronPageInspectionOptions, javascriptApplicationOptions, } from "..
14
14
  import { runCliJavaScriptApplicationAnalysis } from "./javascriptApplicationAnalysis.js";
15
15
  import { withCommandCancellation } from "./commandCancellation.js";
16
16
  /** Register CLI equivalents of the Electron MCP tools. */
17
- export const registerElectronCommands = (cli, logger, environment, resultOutput) => {
17
+ export const registerElectronCommands = (cli, logger, environment) => {
18
18
  registerElectronObservationCommands(cli, logger);
19
19
  registerElectronActiveCommand(cli, logger, environment);
20
- registerJavaScriptApplicationCommand(cli, logger, resultOutput);
20
+ registerJavaScriptApplicationCommand(cli, logger);
21
21
  registerJavaScriptRuntimeReconciliationCommand(cli, logger);
22
22
  };
23
23
  const registerElectronActiveCommand = (cli, logger, environment) => {
@@ -107,20 +107,19 @@ const registerElectronPageInspection = (cli, logger) => {
107
107
  }),
108
108
  });
109
109
  };
110
- const registerJavaScriptApplicationCommand = (cli, logger, resultOutput) => {
110
+ const registerJavaScriptApplicationCommand = (cli, logger) => {
111
111
  cli.command(CLI_COMMANDS.analyzeJavaScriptApplication, {
112
112
  description: "Statically reconstruct a local JavaScript/Electron application",
113
113
  args: z.object({
114
114
  path: z.string().describe("ASAR or extracted application path"),
115
115
  }),
116
116
  options: javascriptApplicationOptions,
117
- run: ({ args, options, format }) => withCommandCancellation((signal) => logCliCommand(logger, CLI_COMMANDS.analyzeJavaScriptApplication, () => runCliJavaScriptApplicationAnalysis({ input_path: args.path, format: options.artifactFormat }, resultOutput === undefined
118
- ? undefined
119
- : {
120
- output: resultOutput,
121
- command: CLI_COMMANDS.analyzeJavaScriptApplication,
122
- format,
123
- }, signal))),
117
+ alias: { integrityPolicy: "integrity-policy" },
118
+ run: ({ args, options }) => withCommandCancellation((signal) => logCliCommand(logger, CLI_COMMANDS.analyzeJavaScriptApplication, () => runCliJavaScriptApplicationAnalysis({
119
+ input_path: args.path,
120
+ format: options.artifactFormat,
121
+ integrity_policy: options.integrityPolicy,
122
+ }, signal))),
124
123
  });
125
124
  };
126
125
  const electronObservationContext = async () => {
@@ -2,8 +2,7 @@ import { analyzeJavaScriptApplication } from "../application/javascript/JavaScri
2
2
  import { createProgressReporter } from "../application/ProgressReporter.js";
3
3
  import { projectAnalysisError } from "../domain/analysisErrorProjection.js";
4
4
  /** Execute the shared one-shot CLI boundary for static JavaScript analysis. */
5
- export const runCliJavaScriptApplicationAnalysis = async (input, output, signal) => {
6
- const started = performance.now();
5
+ export const runCliJavaScriptApplicationAnalysis = async (input, signal) => {
7
6
  const progress = createProgressReporter(async (update) => {
8
7
  process.stderr.write(`${JSON.stringify({ rea_progress: update })}\n`);
9
8
  }, { minimumIntervalMs: 0 });
@@ -11,16 +10,7 @@ export const runCliJavaScriptApplicationAnalysis = async (input, output, signal)
11
10
  progress,
12
11
  ...(signal === undefined ? {} : { signal }),
13
12
  });
14
- const value = result.ok ? result.value : cliError(result.error);
15
- if (output !== undefined &&
16
- (await output.output.write(value, {
17
- command: output.command,
18
- format: output.format,
19
- duration: `${Math.round(performance.now() - started)}ms`,
20
- })))
21
- // Keep failures visible to command logging after the stream has handled stdout.
22
- return result.ok ? undefined : value;
23
- return value;
13
+ return result.ok ? result.value : cliError(result.error);
24
14
  };
25
15
  const cliError = (error) => ({
26
16
  error: "JavaScript application analysis failed",
@@ -1,7 +1,7 @@
1
1
  import { bufferedJsonParts, jsonParts } from "../domain/jsonSerialization.js";
2
2
  /** Write ordinary JSON without constructing a document-sized string. */
3
3
  export const writeJsonOutput = async (value, destination, format = "json") => {
4
- for (const part of bufferedJsonParts(documentParts(value, format)))
4
+ for (const part of bufferedJsonParts(jsonOutputParts(value, format)))
5
5
  await writePart(destination, part);
6
6
  };
7
7
  const writePart = (destination, part) => new Promise((resolve, reject) => {
@@ -25,7 +25,8 @@ const writePart = (destination, part) => new Promise((resolve, reject) => {
25
25
  reject(cause);
26
26
  }
27
27
  });
28
- function* documentParts(value, format) {
28
+ /** The exact formatted document, including its final newline. */
29
+ export function* jsonOutputParts(value, format) {
29
30
  if (format === "jsonl" && Array.isArray(value)) {
30
31
  for (const item of value) {
31
32
  yield* jsonParts(item);