Browse Source

fix(inspector): preserve event stream replay order

imccyu 1 month ago
parent
commit
c5f34e8ada

+ 4 - 25
packages/experimental/inspector/src/worker/inspection/network-store.ts

@@ -69,10 +69,7 @@ export type NetworkStoreEvent =
   }
   }
   | { readonly type: 'request-evicted'; readonly requestKey: string }
   | { readonly type: 'request-evicted'; readonly requestKey: string }
 
 
-type JournalNetworkEvent = Exclude<NetworkStoreEvent, {
-  readonly type: 'response-data' | 'event-source-message' | 'request-evicted'
-}>
-type ReplayableNetworkEvent = Exclude<NetworkStoreEvent, { readonly type: 'response-data' | 'request-evicted' }>
+type JournalNetworkEvent = Exclude<NetworkStoreEvent, { readonly type: 'response-data' | 'request-evicted' }>
 
 
 interface CapturedRequest {
 interface CapturedRequest {
   readonly key: string
   readonly key: string
@@ -140,26 +137,8 @@ export class NetworkStore implements InspectorRecordConsumer {
    * Read retained request lifecycle events.
    * Read retained request lifecycle events.
    * @returns Events in observation order.
    * @returns Events in observation order.
    */
    */
-  replay(): readonly ReplayableNetworkEvent[] {
-    const replay: ReplayableNetworkEvent[] = []
-    for (const event of this.journal) {
-      replay.push(event)
-      if (event.type !== 'response-received' || event.mimeType !== 'text/event-stream') continue
-      const request = this.requests.get(event.requestKey) as CapturedRequest
-      const messages = new InspectorEventSourceParser().push(Buffer.concat(request.responseBody))
-      let eventId = 0
-      for (const message of messages) {
-        replay.push({
-          type: 'event-source-message',
-          requestKey: request.key,
-          requestId: request.requestId,
-          timestampMs: event.timestampMs,
-          ...message,
-          eventId: String(++eventId),
-        })
-      }
-    }
-    return replay
+  replay(): readonly JournalNetworkEvent[] {
+    return this.journal
   }
   }
 
 
   /**
   /**
@@ -274,7 +253,7 @@ export class NetworkStore implements InspectorRecordConsumer {
         const bytes = this.appendBody(request, 'response', data)
         const bytes = this.appendBody(request, 'response', data)
         const byteLength = bytes.byteLength
         const byteLength = bytes.byteLength
         for (const message of request.eventSourceParser?.push(bytes) ?? []) {
         for (const message of request.eventSourceParser?.push(bytes) ?? []) {
-          this.emit({
+          this.publish({
             type: 'event-source-message',
             type: 'event-source-message',
             requestKey: key,
             requestKey: key,
             requestId: request.requestId,
             requestId: request.requestId,

+ 9 - 2
packages/experimental/inspector/tests/network.host.spec.ts

@@ -129,9 +129,16 @@ describe('Inspector Network domain', () => {
       .filter(call => call[0] === 'Network.eventSourceMessageReceived')
       .filter(call => call[0] === 'Network.eventSourceMessageReceived')
       .map(call => call[1] as unknown))
       .map(call => call[1] as unknown))
       .toEqual([
       .toEqual([
-        expect.objectContaining({ eventName: 'message', eventId: '1', data: 'first' }),
-        expect.objectContaining({ eventName: 'update', eventId: '2', data: 'second\nline' }),
+        expect.objectContaining({ timestamp: 0.003, eventName: 'message', eventId: '1', data: 'first' }),
+        expect.objectContaining({ timestamp: 0.004, eventName: 'update', eventId: '2', data: 'second\nline' }),
       ])
       ])
+    expect(replay.mock.calls.map(call => String(call[0]))).toEqual([
+      'Network.requestWillBeSent',
+      'Network.responseReceived',
+      'Network.eventSourceMessageReceived',
+      'Network.eventSourceMessageReceived',
+      'Network.loadingFinished',
+    ])
   })
   })
 
 
   it('bounds active request metadata and does not retain per-chunk events for replay', () => {
   it('bounds active request metadata and does not retain per-chunk events for replay', () => {