@durable-streams/client 0.1.5 → 0.2.1
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/README.md +212 -18
- package/dist/index.cjs +1152 -805
- package/dist/index.d.cts +201 -33
- package/dist/index.d.ts +201 -33
- package/dist/index.js +1150 -806
- package/package.json +2 -2
- package/src/constants.ts +19 -2
- package/src/error.ts +20 -0
- package/src/idempotent-producer.ts +195 -43
- package/src/index.ts +7 -0
- package/src/response.ts +245 -35
- package/src/sse.ts +27 -5
- package/src/stream-api.ts +30 -10
- package/src/stream.ts +213 -71
- package/src/types.ts +97 -12
- package/src/utils.ts +10 -1
package/src/types.ts
CHANGED
|
@@ -62,11 +62,11 @@ export type ParamsRecord = {
|
|
|
62
62
|
/**
|
|
63
63
|
* Live mode for reading from a stream.
|
|
64
64
|
* - false: Catch-up only, stop at first `upToDate`
|
|
65
|
-
* -
|
|
65
|
+
* - true: Auto-select best mode (SSE for JSON streams, long-poll for binary)
|
|
66
66
|
* - "long-poll": Explicit long-poll mode for live updates
|
|
67
67
|
* - "sse": Explicit server-sent events for live updates
|
|
68
68
|
*/
|
|
69
|
-
export type LiveMode =
|
|
69
|
+
export type LiveMode = boolean | `long-poll` | `sse`
|
|
70
70
|
|
|
71
71
|
// ============================================================================
|
|
72
72
|
// Stream Options (Read API)
|
|
@@ -136,7 +136,7 @@ export interface StreamOptions {
|
|
|
136
136
|
/**
|
|
137
137
|
* Live mode behavior:
|
|
138
138
|
* - false: Catch-up only, stop at first `upToDate`
|
|
139
|
-
* -
|
|
139
|
+
* - true (default): Auto-select best mode (SSE for JSON, long-poll for binary)
|
|
140
140
|
* - "long-poll": Explicit long-poll mode for live updates
|
|
141
141
|
* - "sse": Explicit server-sent events for live updates
|
|
142
142
|
*/
|
|
@@ -228,6 +228,12 @@ export interface JsonBatchMeta {
|
|
|
228
228
|
* Last Stream-Cursor / streamCursor, if present.
|
|
229
229
|
*/
|
|
230
230
|
cursor?: string
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Whether the stream is closed and this batch contains the final data.
|
|
234
|
+
* When true, no more data will ever be appended to the stream.
|
|
235
|
+
*/
|
|
236
|
+
streamClosed: boolean
|
|
231
237
|
}
|
|
232
238
|
|
|
233
239
|
/**
|
|
@@ -357,6 +363,18 @@ export interface CreateOptions extends StreamHandleOptions {
|
|
|
357
363
|
* @default true
|
|
358
364
|
*/
|
|
359
365
|
batching?: boolean
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* If true, create the stream in the closed state.
|
|
369
|
+
* Any body provided becomes the complete and final content.
|
|
370
|
+
*
|
|
371
|
+
* Useful for:
|
|
372
|
+
* - Cached responses
|
|
373
|
+
* - Placeholder errors
|
|
374
|
+
* - Pre-computed results
|
|
375
|
+
* - Single-message streams that are immediately complete
|
|
376
|
+
*/
|
|
377
|
+
closed?: boolean
|
|
360
378
|
}
|
|
361
379
|
|
|
362
380
|
/**
|
|
@@ -403,6 +421,41 @@ export interface AppendOptions {
|
|
|
403
421
|
producerSeq?: number
|
|
404
422
|
}
|
|
405
423
|
|
|
424
|
+
/**
|
|
425
|
+
* Result of a close operation.
|
|
426
|
+
*/
|
|
427
|
+
export interface CloseResult {
|
|
428
|
+
/**
|
|
429
|
+
* The final offset of the stream.
|
|
430
|
+
* This is the offset after the last byte (including any final message).
|
|
431
|
+
* Returned via the `Stream-Next-Offset` header.
|
|
432
|
+
*/
|
|
433
|
+
finalOffset: Offset
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Options for closing a stream.
|
|
438
|
+
*/
|
|
439
|
+
export interface CloseOptions {
|
|
440
|
+
/**
|
|
441
|
+
* Optional final message to append atomically with close.
|
|
442
|
+
* For JSON streams, pass a pre-serialized JSON string.
|
|
443
|
+
* Strings are UTF-8 encoded.
|
|
444
|
+
*/
|
|
445
|
+
body?: Uint8Array | string
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Content type for the final message.
|
|
449
|
+
* Defaults to the stream's content type. Must match if provided.
|
|
450
|
+
*/
|
|
451
|
+
contentType?: string
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* AbortSignal for this operation.
|
|
455
|
+
*/
|
|
456
|
+
signal?: AbortSignal
|
|
457
|
+
}
|
|
458
|
+
|
|
406
459
|
/**
|
|
407
460
|
* Legacy live mode type (internal use only).
|
|
408
461
|
* @internal
|
|
@@ -470,6 +523,12 @@ export interface HeadResult {
|
|
|
470
523
|
* Cache-Control header value.
|
|
471
524
|
*/
|
|
472
525
|
cacheControl?: string
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Whether the stream is closed.
|
|
529
|
+
* When true, no further appends are permitted.
|
|
530
|
+
*/
|
|
531
|
+
streamClosed: boolean
|
|
473
532
|
}
|
|
474
533
|
|
|
475
534
|
/**
|
|
@@ -518,6 +577,8 @@ export type DurableStreamErrorCode =
|
|
|
518
577
|
| `RATE_LIMITED`
|
|
519
578
|
| `ALREADY_CONSUMED`
|
|
520
579
|
| `ALREADY_CLOSED`
|
|
580
|
+
| `PARSE_ERROR`
|
|
581
|
+
| `STREAM_CLOSED`
|
|
521
582
|
| `UNKNOWN`
|
|
522
583
|
|
|
523
584
|
/**
|
|
@@ -659,21 +720,34 @@ export interface StreamResponse<TJson = unknown> {
|
|
|
659
720
|
*
|
|
660
721
|
* Use this for resuming reads after a disconnect or saving checkpoints.
|
|
661
722
|
*/
|
|
662
|
-
offset: Offset
|
|
723
|
+
readonly offset: Offset
|
|
663
724
|
|
|
664
725
|
/**
|
|
665
726
|
* Stream cursor for CDN collapsing (stream-cursor header).
|
|
666
727
|
*
|
|
667
728
|
* Updated after each chunk is delivered to the consumer.
|
|
668
729
|
*/
|
|
669
|
-
cursor?: string
|
|
730
|
+
readonly cursor?: string
|
|
670
731
|
|
|
671
732
|
/**
|
|
672
733
|
* Whether we've reached the current end of the stream (stream-up-to-date header).
|
|
673
734
|
*
|
|
674
735
|
* Updated after each chunk is delivered to the consumer.
|
|
675
736
|
*/
|
|
676
|
-
upToDate: boolean
|
|
737
|
+
readonly upToDate: boolean
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Whether the stream is closed (EOF).
|
|
741
|
+
*
|
|
742
|
+
* When true, no more data will ever be appended to the stream.
|
|
743
|
+
* This is updated after each chunk is delivered to the consumer.
|
|
744
|
+
*
|
|
745
|
+
* In live mode, when streamClosed becomes true:
|
|
746
|
+
* - Long-poll requests return immediately (no waiting)
|
|
747
|
+
* - SSE connections are closed by the server
|
|
748
|
+
* - Clients stop reconnecting automatically
|
|
749
|
+
*/
|
|
750
|
+
readonly streamClosed: boolean
|
|
677
751
|
|
|
678
752
|
// =================================
|
|
679
753
|
// 1) Accumulating helpers (Promise)
|
|
@@ -682,20 +756,20 @@ export interface StreamResponse<TJson = unknown> {
|
|
|
682
756
|
|
|
683
757
|
/**
|
|
684
758
|
* Accumulate raw bytes until first `upToDate` batch, then resolve.
|
|
685
|
-
* When used with `live:
|
|
759
|
+
* When used with `live: true`, signals the session to stop after upToDate.
|
|
686
760
|
*/
|
|
687
761
|
body: () => Promise<Uint8Array>
|
|
688
762
|
|
|
689
763
|
/**
|
|
690
764
|
* Accumulate JSON *items* across batches into a single array, resolve at `upToDate`.
|
|
691
765
|
* Only valid in JSON-mode; throws otherwise.
|
|
692
|
-
* When used with `live:
|
|
766
|
+
* When used with `live: true`, signals the session to stop after upToDate.
|
|
693
767
|
*/
|
|
694
768
|
json: <T = TJson>() => Promise<Array<T>>
|
|
695
769
|
|
|
696
770
|
/**
|
|
697
771
|
* Accumulate text chunks into a single string, resolve at `upToDate`.
|
|
698
|
-
* When used with `live:
|
|
772
|
+
* When used with `live: true`, signals the session to stop after upToDate.
|
|
699
773
|
*/
|
|
700
774
|
text: () => Promise<string>
|
|
701
775
|
|
|
@@ -737,24 +811,35 @@ export interface StreamResponse<TJson = unknown> {
|
|
|
737
811
|
/**
|
|
738
812
|
* Subscribe to JSON batches as they arrive.
|
|
739
813
|
* Returns unsubscribe function.
|
|
814
|
+
*
|
|
815
|
+
* The subscriber can be sync or async. If async, backpressure is applied
|
|
816
|
+
* (the next batch waits for the previous callback to complete).
|
|
740
817
|
*/
|
|
741
818
|
subscribeJson: <T = TJson>(
|
|
742
|
-
subscriber: (batch: JsonBatch<T>) => Promise<void>
|
|
819
|
+
subscriber: (batch: JsonBatch<T>) => void | Promise<void>
|
|
743
820
|
) => () => void
|
|
744
821
|
|
|
745
822
|
/**
|
|
746
823
|
* Subscribe to raw byte chunks as they arrive.
|
|
747
824
|
* Returns unsubscribe function.
|
|
825
|
+
*
|
|
826
|
+
* The subscriber can be sync or async. If async, backpressure is applied
|
|
827
|
+
* (the next chunk waits for the previous callback to complete).
|
|
748
828
|
*/
|
|
749
829
|
subscribeBytes: (
|
|
750
|
-
subscriber: (chunk: ByteChunk) => Promise<void>
|
|
830
|
+
subscriber: (chunk: ByteChunk) => void | Promise<void>
|
|
751
831
|
) => () => void
|
|
752
832
|
|
|
753
833
|
/**
|
|
754
834
|
* Subscribe to text chunks as they arrive.
|
|
755
835
|
* Returns unsubscribe function.
|
|
836
|
+
*
|
|
837
|
+
* The subscriber can be sync or async. If async, backpressure is applied
|
|
838
|
+
* (the next chunk waits for the previous callback to complete).
|
|
756
839
|
*/
|
|
757
|
-
subscribeText: (
|
|
840
|
+
subscribeText: (
|
|
841
|
+
subscriber: (chunk: TextChunk) => void | Promise<void>
|
|
842
|
+
) => () => void
|
|
758
843
|
|
|
759
844
|
// =====================
|
|
760
845
|
// 4) Lifecycle
|
package/src/utils.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Shared utility functions for the Durable Streams client.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { STREAM_CLOSED_HEADER, STREAM_OFFSET_HEADER } from "./constants"
|
|
6
|
+
import { DurableStreamError, StreamClosedError } from "./error"
|
|
6
7
|
import type { HeadersRecord, MaybePromise } from "./types"
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -45,6 +46,14 @@ export async function handleErrorResponse(
|
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
if (status === 409) {
|
|
49
|
+
// Check if this is a stream closed error
|
|
50
|
+
const streamClosedHeader = response.headers.get(STREAM_CLOSED_HEADER)
|
|
51
|
+
if (streamClosedHeader?.toLowerCase() === `true`) {
|
|
52
|
+
const finalOffset =
|
|
53
|
+
response.headers.get(STREAM_OFFSET_HEADER) ?? undefined
|
|
54
|
+
throw new StreamClosedError(url, finalOffset)
|
|
55
|
+
}
|
|
56
|
+
|
|
48
57
|
// Context-specific 409 messages
|
|
49
58
|
const message =
|
|
50
59
|
context?.operation === `create`
|