datool 0.0.2 → 0.0.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "datool",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "description": "Local-only config-driven log viewer with SSE streaming and a generic table UI.",
6
6
  "bin": {
@@ -25,6 +25,7 @@ import type {
25
25
  DatoolClientConfig,
26
26
  DatoolClientStream,
27
27
  DatoolColumn,
28
+ DatoolSseEndEvent,
28
29
  DatoolRowEvent,
29
30
  DatoolSseErrorEvent,
30
31
  } from "../shared/types"
@@ -45,6 +46,7 @@ import {
45
46
  writeDatoolUrlState,
46
47
  } from "@/lib/datool-url-state"
47
48
  import { LOG_VIEWER_ICONS } from "@/lib/datool-icons"
49
+ import { upsertViewerRow } from "./stream-state"
48
50
 
49
51
  type ViewerRow = Record<string, unknown> & {
50
52
  __datoolRowId: string
@@ -242,6 +244,10 @@ function parseErrorEvent(event: MessageEvent<string>) {
242
244
  return JSON.parse(event.data) as DatoolSseErrorEvent
243
245
  }
244
246
 
247
+ function parseEndEvent(event: MessageEvent<string>) {
248
+ return JSON.parse(event.data) as DatoolSseEndEvent
249
+ }
250
+
245
251
  function stringifyRowActionValue(value: unknown) {
246
252
  if (value === null || value === undefined) {
247
253
  return ""
@@ -773,13 +779,12 @@ export default function App() {
773
779
  const handleRow = (event: MessageEvent<string>) => {
774
780
  const payload = parseRowEvent(event)
775
781
 
776
- setRows((currentRows) => [
777
- ...currentRows,
778
- {
782
+ setRows((currentRows) =>
783
+ upsertViewerRow(currentRows, {
779
784
  ...payload.row,
780
785
  __datoolRowId: payload.id,
781
- },
782
- ])
786
+ })
787
+ )
783
788
  }
784
789
 
785
790
  const handleRuntimeError = (event: MessageEvent<string>) => {
@@ -788,6 +793,13 @@ export default function App() {
788
793
  setErrorMessage(payload.message)
789
794
  }
790
795
 
796
+ const handleEnd = (event: MessageEvent<string>) => {
797
+ parseEndEvent(event)
798
+ eventSource.close()
799
+ eventSourceRef.current = null
800
+ setIsConnected(false)
801
+ }
802
+
791
803
  eventSource.onopen = () => {
792
804
  setIsConnected(true)
793
805
  }
@@ -797,14 +809,19 @@ export default function App() {
797
809
  }
798
810
 
799
811
  eventSource.addEventListener("row", handleRow as EventListener)
800
- eventSource.addEventListener("error", handleRuntimeError as EventListener)
812
+ eventSource.addEventListener(
813
+ "runtime-error",
814
+ handleRuntimeError as EventListener
815
+ )
816
+ eventSource.addEventListener("end", handleEnd as EventListener)
801
817
 
802
818
  return () => {
803
819
  eventSource.removeEventListener("row", handleRow as EventListener)
804
820
  eventSource.removeEventListener(
805
- "error",
821
+ "runtime-error",
806
822
  handleRuntimeError as EventListener
807
823
  )
824
+ eventSource.removeEventListener("end", handleEnd as EventListener)
808
825
  eventSource.close()
809
826
  setIsConnected(false)
810
827
  }
@@ -0,0 +1,20 @@
1
+ export type StreamViewerRow = Record<string, unknown> & {
2
+ __datoolRowId: string
3
+ }
4
+
5
+ export function upsertViewerRow<T extends StreamViewerRow>(
6
+ currentRows: T[],
7
+ nextRow: T
8
+ ) {
9
+ const existingIndex = currentRows.findIndex(
10
+ (row) => row.__datoolRowId === nextRow.__datoolRowId
11
+ )
12
+
13
+ if (existingIndex < 0) {
14
+ return [...currentRows, nextRow]
15
+ }
16
+
17
+ return currentRows.map((row, index) =>
18
+ index === existingIndex ? nextRow : row
19
+ )
20
+ }
@@ -10,6 +10,7 @@ import type {
10
10
  DatoolActionResolveResult,
11
11
  DatoolActionResponse,
12
12
  DatoolConfig,
13
+ DatoolSseEndEvent,
13
14
  DatoolSseErrorEvent,
14
15
  } from "../shared/types"
15
16
 
@@ -74,6 +75,12 @@ function toErrorPayload(error: unknown): DatoolSseErrorEvent {
74
75
  }
75
76
  }
76
77
 
78
+ function toEndPayload(reason: DatoolSseEndEvent["reason"]): DatoolSseEndEvent {
79
+ return {
80
+ reason,
81
+ }
82
+ }
83
+
77
84
  function encodeSseEvent(event: string, data: unknown) {
78
85
  return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`
79
86
  }
@@ -279,15 +286,23 @@ function createSseResponse(
279
286
  abortController.signal,
280
287
  {
281
288
  async onError(error) {
282
- send("error", toErrorPayload(error))
289
+ send("runtime-error", toErrorPayload(error))
283
290
  },
284
291
  async onRow(payload) {
285
292
  send("row", payload)
286
293
  },
287
294
  }
288
295
  )
296
+ .then(() => {
297
+ if (!abortController.signal.aborted) {
298
+ send("end", toEndPayload("completed"))
299
+ }
300
+ })
289
301
  .catch((error) => {
290
- send("error", toErrorPayload(error))
302
+ if (!abortController.signal.aborted) {
303
+ send("runtime-error", toErrorPayload(error))
304
+ send("end", toEndPayload("error"))
305
+ }
291
306
  })
292
307
  .finally(() => {
293
308
  clearInterval(heartbeat)
@@ -179,6 +179,10 @@ export type DatoolSseErrorEvent = {
179
179
  message: string
180
180
  }
181
181
 
182
+ export type DatoolSseEndEvent = {
183
+ reason: "completed" | "error"
184
+ }
185
+
182
186
  export type DatoolActionRequest = {
183
187
  rows: Record<string, unknown>[]
184
188
  }