@reclaimprotocol/attestor-core 5.0.1-beta.2 → 5.0.1-beta.22

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 (145) hide show
  1. package/browser/resources/attestor-browser.min.mjs +4512 -0
  2. package/lib/avs/abis/avsDirectoryABI.js +338 -341
  3. package/lib/avs/abis/delegationABI.js +1 -4
  4. package/lib/avs/abis/registryABI.js +719 -722
  5. package/lib/avs/client/create-claim-on-avs.js +129 -157
  6. package/lib/avs/config.js +18 -24
  7. package/lib/avs/contracts/ReclaimServiceManager.js +1 -0
  8. package/lib/avs/contracts/common.js +1 -0
  9. package/lib/avs/contracts/factories/ReclaimServiceManager__factory.js +1139 -1156
  10. package/lib/avs/contracts/factories/index.js +4 -4
  11. package/lib/avs/contracts/index.js +2 -6
  12. package/lib/avs/types/index.js +1 -0
  13. package/lib/avs/utils/contracts.js +30 -50
  14. package/lib/avs/utils/register.js +75 -70
  15. package/lib/avs/utils/tasks.js +38 -45
  16. package/lib/client/create-claim.js +402 -431
  17. package/lib/client/tunnels/make-rpc-tcp-tunnel.js +46 -48
  18. package/lib/client/tunnels/make-rpc-tls-tunnel.js +125 -121
  19. package/lib/client/utils/attestor-pool.js +23 -22
  20. package/lib/client/utils/client-socket.js +86 -109
  21. package/lib/client/utils/message-handler.js +79 -89
  22. package/lib/config/index.js +40 -58
  23. package/lib/external-rpc/benchmark.js +61 -74
  24. package/lib/external-rpc/event-bus.js +12 -15
  25. package/lib/external-rpc/handle-incoming-msg.js +216 -225
  26. package/lib/external-rpc/jsc-polyfills/1.js +70 -68
  27. package/lib/external-rpc/jsc-polyfills/2.js +17 -12
  28. package/lib/external-rpc/jsc-polyfills/event.js +10 -15
  29. package/lib/external-rpc/jsc-polyfills/index.js +2 -2
  30. package/lib/external-rpc/jsc-polyfills/ws.js +77 -79
  31. package/lib/external-rpc/setup-browser.js +28 -28
  32. package/lib/external-rpc/setup-jsc.js +17 -17
  33. package/lib/external-rpc/types.js +1 -0
  34. package/lib/external-rpc/utils.js +89 -89
  35. package/lib/external-rpc/zk.js +55 -50
  36. package/lib/index.js +2 -6
  37. package/lib/mechain/abis/governanceABI.js +457 -460
  38. package/lib/mechain/abis/taskABI.js +502 -505
  39. package/lib/mechain/client/create-claim-on-mechain.js +24 -29
  40. package/lib/mechain/constants/index.js +3 -8
  41. package/lib/mechain/types/index.js +1 -0
  42. package/lib/proto/api.js +4200 -4087
  43. package/lib/proto/tee-bundle.js +1261 -1241
  44. package/lib/providers/http/index.js +616 -603
  45. package/lib/providers/http/patch-parse5-tree.js +27 -29
  46. package/lib/providers/http/utils.js +289 -248
  47. package/lib/providers/index.js +3 -6
  48. package/lib/server/create-server.js +89 -91
  49. package/lib/server/handlers/claimTeeBundle.js +231 -211
  50. package/lib/server/handlers/claimTunnel.js +66 -73
  51. package/lib/server/handlers/completeClaimOnChain.js +20 -25
  52. package/lib/server/handlers/createClaimOnChain.js +21 -27
  53. package/lib/server/handlers/createTaskOnMechain.js +40 -50
  54. package/lib/server/handlers/createTunnel.js +85 -90
  55. package/lib/server/handlers/disconnectTunnel.js +4 -7
  56. package/lib/server/handlers/fetchCertificateBytes.js +37 -53
  57. package/lib/server/handlers/index.js +21 -24
  58. package/lib/server/handlers/init.js +27 -28
  59. package/lib/server/handlers/toprf.js +13 -16
  60. package/lib/server/socket.js +97 -100
  61. package/lib/server/tunnels/make-tcp-tunnel.js +161 -186
  62. package/lib/server/utils/apm.js +32 -25
  63. package/lib/server/utils/assert-valid-claim-request.js +305 -334
  64. package/lib/server/utils/config-env.js +2 -2
  65. package/lib/server/utils/dns.js +12 -18
  66. package/lib/server/utils/gcp-attestation.js +233 -181
  67. package/lib/server/utils/generics.d.ts +1 -1
  68. package/lib/server/utils/generics.js +43 -37
  69. package/lib/server/utils/iso.js +253 -256
  70. package/lib/server/utils/keep-alive.js +36 -36
  71. package/lib/server/utils/nitro-attestation.js +295 -220
  72. package/lib/server/utils/oprf-raw.js +48 -55
  73. package/lib/server/utils/process-handshake.js +200 -218
  74. package/lib/server/utils/proxy-session.js +5 -5
  75. package/lib/server/utils/tee-oprf-mpc-verification.js +82 -78
  76. package/lib/server/utils/tee-oprf-verification.js +165 -142
  77. package/lib/server/utils/tee-transcript-reconstruction.js +176 -129
  78. package/lib/server/utils/tee-verification.js +397 -334
  79. package/lib/server/utils/validation.js +30 -37
  80. package/lib/types/bgp.js +1 -0
  81. package/lib/types/claims.js +1 -0
  82. package/lib/types/client.js +1 -0
  83. package/lib/types/general.js +1 -0
  84. package/lib/types/handlers.js +1 -0
  85. package/lib/types/providers.d.ts +3 -2
  86. package/lib/types/providers.gen.js +9 -15
  87. package/lib/types/providers.js +1 -0
  88. package/lib/types/rpc.js +1 -0
  89. package/lib/types/signatures.d.ts +1 -2
  90. package/lib/types/signatures.js +1 -0
  91. package/lib/types/tunnel.js +1 -0
  92. package/lib/types/zk.js +1 -0
  93. package/lib/utils/auth.js +54 -66
  94. package/lib/utils/b64-json.js +15 -15
  95. package/lib/utils/bgp-listener.js +107 -111
  96. package/lib/utils/claims.js +89 -80
  97. package/lib/utils/env.js +13 -17
  98. package/lib/utils/error.js +43 -47
  99. package/lib/utils/generics.js +284 -235
  100. package/lib/utils/http-parser.js +232 -187
  101. package/lib/utils/logger.js +80 -71
  102. package/lib/utils/prepare-packets.js +69 -67
  103. package/lib/utils/redactions.js +163 -121
  104. package/lib/utils/retries.js +22 -24
  105. package/lib/utils/signatures/eth.js +29 -28
  106. package/lib/utils/signatures/index.js +5 -10
  107. package/lib/utils/socket-base.js +84 -88
  108. package/lib/utils/tls.js +28 -28
  109. package/lib/utils/ws.js +19 -19
  110. package/lib/utils/zk.js +542 -582
  111. package/package.json +12 -5
  112. package/lib/external-rpc/global.d.js +0 -0
  113. package/lib/scripts/build-browser.d.ts +0 -1
  114. package/lib/scripts/build-jsc.d.ts +0 -1
  115. package/lib/scripts/build-lib.d.ts +0 -1
  116. package/lib/scripts/check-avs-registration.d.ts +0 -1
  117. package/lib/scripts/check-avs-registration.js +0 -28
  118. package/lib/scripts/fallbacks/crypto.d.ts +0 -1
  119. package/lib/scripts/fallbacks/crypto.js +0 -4
  120. package/lib/scripts/fallbacks/empty.d.ts +0 -3
  121. package/lib/scripts/fallbacks/empty.js +0 -4
  122. package/lib/scripts/fallbacks/re2.d.ts +0 -1
  123. package/lib/scripts/fallbacks/re2.js +0 -7
  124. package/lib/scripts/fallbacks/snarkjs.d.ts +0 -1
  125. package/lib/scripts/fallbacks/snarkjs.js +0 -10
  126. package/lib/scripts/fallbacks/stwo.d.ts +0 -6
  127. package/lib/scripts/fallbacks/stwo.js +0 -159
  128. package/lib/scripts/generate-provider-types.d.ts +0 -5
  129. package/lib/scripts/generate-provider-types.js +0 -101
  130. package/lib/scripts/generate-receipt.d.ts +0 -9
  131. package/lib/scripts/generate-receipt.js +0 -101
  132. package/lib/scripts/generate-toprf-keys.d.ts +0 -1
  133. package/lib/scripts/generate-toprf-keys.js +0 -24
  134. package/lib/scripts/jsc-cli-rpc.d.ts +0 -1
  135. package/lib/scripts/jsc-cli-rpc.js +0 -35
  136. package/lib/scripts/register-avs-operator.d.ts +0 -1
  137. package/lib/scripts/register-avs-operator.js +0 -3
  138. package/lib/scripts/start-server.d.ts +0 -1
  139. package/lib/scripts/start-server.js +0 -11
  140. package/lib/scripts/update-avs-metadata.d.ts +0 -1
  141. package/lib/scripts/update-avs-metadata.js +0 -20
  142. package/lib/scripts/utils.d.ts +0 -1
  143. package/lib/scripts/utils.js +0 -10
  144. package/lib/scripts/whitelist-operator.d.ts +0 -1
  145. package/lib/scripts/whitelist-operator.js +0 -16
@@ -1,140 +1,187 @@
1
+ /**
2
+ * TLS Transcript Reconstruction from TEE data
3
+ */
1
4
  import { AttestorError } from "../../utils/error.js";
2
5
  import { REDACTION_CHAR_CODE } from "../../utils/index.js";
3
- async function reconstructTlsTranscript(bundleData, logger, oprfResults) {
4
- try {
5
- const revealedRequest = reconstructRequest(bundleData, logger);
6
- const reconstructedResponse = await reconstructConsolidatedResponse(bundleData, logger, oprfResults);
7
- const certificateInfo = bundleData.kOutputPayload.certificateInfo;
8
- logger.info("TLS transcript reconstruction completed successfully", {
9
- requestSize: revealedRequest.length,
10
- responseSize: reconstructedResponse.length,
11
- hasCertificateInfo: !!certificateInfo
12
- });
13
- return {
14
- revealedRequest,
15
- reconstructedResponse,
16
- certificateInfo
17
- };
18
- } catch (error) {
19
- logger.error({ error }, "TLS transcript reconstruction failed");
20
- throw new AttestorError("ERROR_INVALID_CLAIM", `Transcript reconstruction failed: ${error.message}`);
21
- }
6
+ /**
7
+ * Reconstructs TLS transcript from TEE bundle data
8
+ * @param bundleData - Validated TEE bundle data
9
+ * @param logger - Logger instance
10
+ * @param oprfResults - Optional OPRF results to apply during reconstruction
11
+ * @returns Reconstructed transcript data
12
+ */
13
+ export async function reconstructTlsTranscript(bundleData, logger, oprfResults) {
14
+ try {
15
+ // 1. Reconstruct request using proof stream
16
+ const revealedRequest = reconstructRequest(bundleData, logger);
17
+ // 2. Reconstruct response using consolidated keystream and ciphertext
18
+ const reconstructedResponse = await reconstructConsolidatedResponse(bundleData, logger, oprfResults);
19
+ // 3. Extract certificate info from TEE_K payload
20
+ const certificateInfo = bundleData.kOutputPayload.certificateInfo;
21
+ logger.info('TLS transcript reconstruction completed successfully', {
22
+ requestSize: revealedRequest.length,
23
+ responseSize: reconstructedResponse.length,
24
+ hasCertificateInfo: !!certificateInfo
25
+ });
26
+ return {
27
+ revealedRequest,
28
+ reconstructedResponse,
29
+ certificateInfo
30
+ };
31
+ }
32
+ catch (error) {
33
+ logger.error({ error }, 'TLS transcript reconstruction failed');
34
+ throw new AttestorError('ERROR_INVALID_CLAIM', `Transcript reconstruction failed: ${error.message}`);
35
+ }
22
36
  }
37
+ /**
38
+ * Reconstructs the original request by applying proof stream to redacted request
39
+ */
23
40
  function reconstructRequest(bundleData, logger) {
24
- const { kOutputPayload } = bundleData;
25
- if (!kOutputPayload.requestRedactionRanges || kOutputPayload.requestRedactionRanges.length === 0) {
26
- logger.warn("No request redaction ranges - using redacted request as-is");
27
- return kOutputPayload.redactedRequest;
28
- }
29
- const revealedRequest = new Uint8Array(kOutputPayload.redactedRequest);
30
- const prettyRequest = new Uint8Array(revealedRequest);
31
- for (const range of kOutputPayload.requestRedactionRanges) {
32
- if (!range.type.includes("proof")) {
33
- const start = range.start;
34
- const length = range.length;
35
- for (let i = 0; i < length && start + i < prettyRequest.length; i++) {
36
- prettyRequest[start + i] = REDACTION_CHAR_CODE;
37
- }
38
- }
39
- }
40
- return prettyRequest;
41
+ const { kOutputPayload } = bundleData;
42
+ if (!kOutputPayload.requestRedactionRanges || kOutputPayload.requestRedactionRanges.length === 0) {
43
+ logger.warn('No request redaction ranges - using redacted request as-is');
44
+ return kOutputPayload.redactedRequest;
45
+ }
46
+ // Create a copy of the redacted request
47
+ const revealedRequest = new Uint8Array(kOutputPayload.redactedRequest);
48
+ // Create pretty display: show revealed proof data, but keep other sensitive data as '*'
49
+ const prettyRequest = new Uint8Array(revealedRequest);
50
+ for (const range of kOutputPayload.requestRedactionRanges) {
51
+ // Keep non-proof sensitive data as '*' for display
52
+ if (!range.type.includes('proof')) {
53
+ const start = range.start;
54
+ const length = range.length;
55
+ for (let i = 0; i < length && start + i < prettyRequest.length; i++) {
56
+ prettyRequest[start + i] = REDACTION_CHAR_CODE;
57
+ }
58
+ }
59
+ }
60
+ return prettyRequest;
41
61
  }
62
+ /**
63
+ * NEW: Reconstructs response using consolidated keystream and ciphertext
64
+ * This is much simpler than the old packet-by-packet approach
65
+ */
42
66
  async function reconstructConsolidatedResponse(bundleData, logger, oprfResults) {
43
- const { kOutputPayload, tOutputPayload } = bundleData;
44
- const consolidatedKeystream = kOutputPayload.consolidatedResponseKeystream;
45
- const consolidatedCiphertext = tOutputPayload.consolidatedResponseCiphertext;
46
- if (!consolidatedKeystream || consolidatedKeystream.length === 0) {
47
- throw new AttestorError("ERROR_INVALID_CLAIM", "No consolidated response keystream available");
48
- }
49
- if (!consolidatedCiphertext || consolidatedCiphertext.length === 0) {
50
- throw new AttestorError("ERROR_INVALID_CLAIM", "No consolidated response ciphertext available");
51
- }
52
- if (consolidatedKeystream.length !== consolidatedCiphertext.length) {
53
- logger.warn("Keystream and ciphertext length mismatch", {
54
- keystreamLength: consolidatedKeystream.length,
55
- ciphertextLength: consolidatedCiphertext.length
56
- });
57
- }
58
- const minLength = Math.min(consolidatedKeystream.length, consolidatedCiphertext.length);
59
- const reconstructedResponse = new Uint8Array(minLength);
60
- for (let i = 0; i < minLength; i++) {
61
- reconstructedResponse[i] = consolidatedKeystream[i] ^ consolidatedCiphertext[i];
62
- }
63
- logger.info(`Reconstructed response: ${reconstructedResponse.length} bytes, ${kOutputPayload.responseRedactionRanges?.length || 0} redaction ranges`);
64
- let processedResponse = applyResponseRedactionRanges(reconstructedResponse, kOutputPayload.responseRedactionRanges, logger);
65
- if (oprfResults && oprfResults.length > 0) {
66
- logger.info(`Applying ${oprfResults.length} OPRF replacements before trimming`);
67
- const { replaceOprfRanges } = await import("../../server/utils/tee-oprf-verification.js");
68
- processedResponse = replaceOprfRanges(processedResponse, oprfResults, logger);
69
- }
70
- let leadingAsterisks = 0;
71
- for (const element of processedResponse) {
72
- if (element === REDACTION_CHAR_CODE) {
73
- leadingAsterisks++;
74
- } else {
75
- break;
76
- }
77
- }
78
- let trailingAsterisks = 0;
79
- for (let i = processedResponse.length - 1; i >= leadingAsterisks; i--) {
80
- if (processedResponse[i] === REDACTION_CHAR_CODE) {
81
- trailingAsterisks++;
82
- } else {
83
- break;
84
- }
85
- }
86
- const finalLength = processedResponse.length - leadingAsterisks - trailingAsterisks;
87
- logger.info(`After processing: ${processedResponse.length} bytes, ${leadingAsterisks} leading and ${trailingAsterisks} trailing asterisks trimmed, final: ${finalLength} bytes`);
88
- return processedResponse.slice(leadingAsterisks, processedResponse.length - trailingAsterisks);
67
+ const { kOutputPayload, tOutputPayload } = bundleData;
68
+ // Get consolidated data from both TEEs
69
+ const consolidatedKeystream = kOutputPayload.consolidatedResponseKeystream;
70
+ const consolidatedCiphertext = tOutputPayload.consolidatedResponseCiphertext;
71
+ if (!consolidatedKeystream || consolidatedKeystream.length === 0) {
72
+ throw new AttestorError('ERROR_INVALID_CLAIM', 'No consolidated response keystream available');
73
+ }
74
+ if (!consolidatedCiphertext || consolidatedCiphertext.length === 0) {
75
+ throw new AttestorError('ERROR_INVALID_CLAIM', 'No consolidated response ciphertext available');
76
+ }
77
+ // Verify lengths match
78
+ if (consolidatedKeystream.length !== consolidatedCiphertext.length) {
79
+ logger.warn('Keystream and ciphertext length mismatch', {
80
+ keystreamLength: consolidatedKeystream.length,
81
+ ciphertextLength: consolidatedCiphertext.length
82
+ });
83
+ }
84
+ // XOR to get plaintext (keystream XOR ciphertext = plaintext)
85
+ const minLength = Math.min(consolidatedKeystream.length, consolidatedCiphertext.length);
86
+ const reconstructedResponse = new Uint8Array(minLength);
87
+ for (let i = 0; i < minLength; i++) {
88
+ reconstructedResponse[i] = consolidatedKeystream[i] ^ consolidatedCiphertext[i];
89
+ }
90
+ logger.info(`Reconstructed response: ${reconstructedResponse.length} bytes, ${kOutputPayload.responseRedactionRanges?.length || 0} redaction ranges`);
91
+ // Apply response redaction ranges to the reconstructed response
92
+ let processedResponse = applyResponseRedactionRanges(reconstructedResponse, kOutputPayload.responseRedactionRanges, logger);
93
+ // Apply OPRF replacements BEFORE trimming leading asterisks
94
+ if (oprfResults && oprfResults.length > 0) {
95
+ logger.info(`Applying ${oprfResults.length} OPRF replacements before trimming`);
96
+ const { replaceOprfRanges } = await import("./tee-oprf-verification.js");
97
+ processedResponse = replaceOprfRanges(processedResponse, oprfResults, logger);
98
+ }
99
+ // Count leading asterisks
100
+ let leadingAsterisks = 0;
101
+ for (const element of processedResponse) {
102
+ if (element === REDACTION_CHAR_CODE) {
103
+ leadingAsterisks++;
104
+ }
105
+ else {
106
+ break;
107
+ }
108
+ }
109
+ // Count trailing asterisks (may contain undesired data like alerts)
110
+ let trailingAsterisks = 0;
111
+ for (let i = processedResponse.length - 1; i >= leadingAsterisks; i--) {
112
+ if (processedResponse[i] === REDACTION_CHAR_CODE) {
113
+ trailingAsterisks++;
114
+ }
115
+ else {
116
+ break;
117
+ }
118
+ }
119
+ const finalLength = processedResponse.length - leadingAsterisks - trailingAsterisks;
120
+ logger.info(`After processing: ${processedResponse.length} bytes, ${leadingAsterisks} leading and ${trailingAsterisks} trailing asterisks trimmed, final: ${finalLength} bytes`);
121
+ return processedResponse.slice(leadingAsterisks, processedResponse.length - trailingAsterisks);
89
122
  }
123
+ // Removed legacy packet-based extraction functions since we now use consolidated streams
124
+ /**
125
+ * Applies response redaction ranges to replace random garbage with asterisks
126
+ * Response redaction ranges have NO type field - they all work the same way (binary redaction)
127
+ */
90
128
  function applyResponseRedactionRanges(response, redactionRanges, logger) {
91
- if (!redactionRanges || redactionRanges.length === 0) {
92
- return response;
93
- }
94
- const result = new Uint8Array(response);
95
- const consolidatedRanges = consolidateRedactionRanges(redactionRanges);
96
- if (logger) {
97
- logger.info(`Applying ${consolidatedRanges.length} redaction ranges to ${response.length} byte response`);
98
- }
99
- for (const [idx, range] of consolidatedRanges.entries()) {
100
- const rangeStart = range.start;
101
- const rangeEnd = range.start + range.length;
102
- if (rangeStart < 0 || rangeEnd > result.length) {
103
- if (logger) {
104
- logger.warn(`Redaction range #${idx} out of bounds: [${rangeStart}-${rangeEnd}] vs ${result.length}`);
105
- }
106
- continue;
107
- }
108
- if (logger && idx < 3) {
109
- logger.info(`Redaction range #${idx}: [${rangeStart}-${rangeEnd}]`);
110
- }
111
- for (let i = rangeStart; i < rangeEnd; i++) {
112
- result[i] = REDACTION_CHAR_CODE;
113
- }
114
- }
115
- return result;
129
+ if (!redactionRanges || redactionRanges.length === 0) {
130
+ return response;
131
+ }
132
+ const result = new Uint8Array(response);
133
+ // Consolidate overlapping ranges (same as client implementation)
134
+ const consolidatedRanges = consolidateRedactionRanges(redactionRanges);
135
+ if (logger) {
136
+ logger.info(`Applying ${consolidatedRanges.length} redaction ranges to ${response.length} byte response`);
137
+ }
138
+ // Apply each redaction range to replace random garbage with asterisks
139
+ for (const [idx, range] of consolidatedRanges.entries()) {
140
+ const rangeStart = range.start;
141
+ const rangeEnd = range.start + range.length;
142
+ // Check bounds
143
+ if (rangeStart < 0 || rangeEnd > result.length) {
144
+ if (logger) {
145
+ logger.warn(`Redaction range #${idx} out of bounds: [${rangeStart}-${rangeEnd}] vs ${result.length}`);
146
+ }
147
+ continue;
148
+ }
149
+ if (logger && idx < 3) {
150
+ logger.info(`Redaction range #${idx}: [${rangeStart}-${rangeEnd}]`);
151
+ }
152
+ // Replace random garbage with asterisks
153
+ for (let i = rangeStart; i < rangeEnd; i++) {
154
+ result[i] = REDACTION_CHAR_CODE;
155
+ }
156
+ }
157
+ return result;
116
158
  }
159
+ /**
160
+ * Consolidates overlapping redaction ranges
161
+ */
117
162
  function consolidateRedactionRanges(ranges) {
118
- if (ranges.length === 0) {
119
- return [];
120
- }
121
- const sortedRanges = [...ranges].sort((a, b) => a.start - b.start);
122
- const consolidated = [];
123
- let current = { ...sortedRanges[0] };
124
- for (let i = 1; i < sortedRanges.length; i++) {
125
- const next = sortedRanges[i];
126
- if (next.start <= current.start + current.length) {
127
- const endCurrent = current.start + current.length;
128
- const endNext = next.start + next.length;
129
- current.length = Math.max(endCurrent, endNext) - current.start;
130
- } else {
131
- consolidated.push(current);
132
- current = { ...next };
133
- }
134
- }
135
- consolidated.push(current);
136
- return consolidated;
163
+ if (ranges.length === 0) {
164
+ return [];
165
+ }
166
+ // Sort ranges by start position
167
+ const sortedRanges = [...ranges].sort((a, b) => a.start - b.start);
168
+ const consolidated = [];
169
+ let current = { ...sortedRanges[0] };
170
+ for (let i = 1; i < sortedRanges.length; i++) {
171
+ const next = sortedRanges[i];
172
+ // Check if ranges overlap or are adjacent
173
+ if (next.start <= current.start + current.length) {
174
+ // Merge ranges
175
+ const endCurrent = current.start + current.length;
176
+ const endNext = next.start + next.length;
177
+ current.length = Math.max(endCurrent, endNext) - current.start;
178
+ }
179
+ else {
180
+ // No overlap, add current and move to next
181
+ consolidated.push(current);
182
+ current = { ...next };
183
+ }
184
+ }
185
+ consolidated.push(current);
186
+ return consolidated;
137
187
  }
138
- export {
139
- reconstructTlsTranscript
140
- };