@durable-streams/client 0.1.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.
- package/README.md +799 -0
- package/dist/index.cjs +1172 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +627 -0
- package/dist/index.d.ts +1072 -0
- package/dist/index.js +1830 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
- package/src/asyncIterableReadableStream.ts +220 -0
- package/src/constants.ts +105 -0
- package/src/error.ts +189 -0
- package/src/fetch.ts +267 -0
- package/src/index.ts +103 -0
- package/src/response.ts +1053 -0
- package/src/sse.ts +130 -0
- package/src/stream-api.ts +284 -0
- package/src/stream.ts +867 -0
- package/src/types.ts +737 -0
- package/src/utils.ts +104 -0
package/src/error.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { DurableStreamErrorCode } from "./types"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown for transport/network errors.
|
|
5
|
+
* Following the @electric-sql/client FetchError pattern.
|
|
6
|
+
*/
|
|
7
|
+
export class FetchError extends Error {
|
|
8
|
+
status: number
|
|
9
|
+
text?: string
|
|
10
|
+
json?: object
|
|
11
|
+
headers: Record<string, string>
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
status: number,
|
|
15
|
+
text: string | undefined,
|
|
16
|
+
json: object | undefined,
|
|
17
|
+
headers: Record<string, string>,
|
|
18
|
+
public url: string,
|
|
19
|
+
message?: string
|
|
20
|
+
) {
|
|
21
|
+
super(
|
|
22
|
+
message ||
|
|
23
|
+
`HTTP Error ${status} at ${url}: ${text ?? JSON.stringify(json)}`
|
|
24
|
+
)
|
|
25
|
+
this.name = `FetchError`
|
|
26
|
+
this.status = status
|
|
27
|
+
this.text = text
|
|
28
|
+
this.json = json
|
|
29
|
+
this.headers = headers
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static async fromResponse(
|
|
33
|
+
response: Response,
|
|
34
|
+
url: string
|
|
35
|
+
): Promise<FetchError> {
|
|
36
|
+
const status = response.status
|
|
37
|
+
const headers = Object.fromEntries([...response.headers.entries()])
|
|
38
|
+
let text: string | undefined = undefined
|
|
39
|
+
let json: object | undefined = undefined
|
|
40
|
+
|
|
41
|
+
const contentType = response.headers.get(`content-type`)
|
|
42
|
+
if (!response.bodyUsed) {
|
|
43
|
+
if (contentType && contentType.includes(`application/json`)) {
|
|
44
|
+
try {
|
|
45
|
+
json = (await response.json()) as object
|
|
46
|
+
} catch {
|
|
47
|
+
// If JSON parsing fails, fall back to text
|
|
48
|
+
text = await response.text()
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
text = await response.text()
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return new FetchError(status, text, json, headers, url)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Error thrown when a fetch operation is aborted during backoff.
|
|
61
|
+
*/
|
|
62
|
+
export class FetchBackoffAbortError extends Error {
|
|
63
|
+
constructor() {
|
|
64
|
+
super(`Fetch with backoff aborted`)
|
|
65
|
+
this.name = `FetchBackoffAbortError`
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Protocol-level error for Durable Streams operations.
|
|
71
|
+
* Provides structured error handling with error codes.
|
|
72
|
+
*/
|
|
73
|
+
export class DurableStreamError extends Error {
|
|
74
|
+
/**
|
|
75
|
+
* HTTP status code, if applicable.
|
|
76
|
+
*/
|
|
77
|
+
status?: number
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Structured error code for programmatic handling.
|
|
81
|
+
*/
|
|
82
|
+
code: DurableStreamErrorCode
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Additional error details (e.g., raw response body).
|
|
86
|
+
*/
|
|
87
|
+
details?: unknown
|
|
88
|
+
|
|
89
|
+
constructor(
|
|
90
|
+
message: string,
|
|
91
|
+
code: DurableStreamErrorCode,
|
|
92
|
+
status?: number,
|
|
93
|
+
details?: unknown
|
|
94
|
+
) {
|
|
95
|
+
super(message)
|
|
96
|
+
this.name = `DurableStreamError`
|
|
97
|
+
this.code = code
|
|
98
|
+
this.status = status
|
|
99
|
+
this.details = details
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create a DurableStreamError from an HTTP response.
|
|
104
|
+
*/
|
|
105
|
+
static async fromResponse(
|
|
106
|
+
response: Response,
|
|
107
|
+
url: string
|
|
108
|
+
): Promise<DurableStreamError> {
|
|
109
|
+
const status = response.status
|
|
110
|
+
let details: unknown
|
|
111
|
+
|
|
112
|
+
const contentType = response.headers.get(`content-type`)
|
|
113
|
+
if (!response.bodyUsed) {
|
|
114
|
+
if (contentType && contentType.includes(`application/json`)) {
|
|
115
|
+
try {
|
|
116
|
+
details = await response.json()
|
|
117
|
+
} catch {
|
|
118
|
+
details = await response.text()
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
details = await response.text()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const code = statusToCode(status)
|
|
126
|
+
const message = `Durable stream error at ${url}: ${response.statusText || status}`
|
|
127
|
+
|
|
128
|
+
return new DurableStreamError(message, code, status, details)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Create a DurableStreamError from a FetchError.
|
|
133
|
+
*/
|
|
134
|
+
static fromFetchError(error: FetchError): DurableStreamError {
|
|
135
|
+
const code = statusToCode(error.status)
|
|
136
|
+
return new DurableStreamError(
|
|
137
|
+
error.message,
|
|
138
|
+
code,
|
|
139
|
+
error.status,
|
|
140
|
+
error.json ?? error.text
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Map HTTP status codes to DurableStreamErrorCode.
|
|
147
|
+
*/
|
|
148
|
+
function statusToCode(status: number): DurableStreamErrorCode {
|
|
149
|
+
switch (status) {
|
|
150
|
+
case 400:
|
|
151
|
+
return `BAD_REQUEST`
|
|
152
|
+
case 401:
|
|
153
|
+
return `UNAUTHORIZED`
|
|
154
|
+
case 403:
|
|
155
|
+
return `FORBIDDEN`
|
|
156
|
+
case 404:
|
|
157
|
+
return `NOT_FOUND`
|
|
158
|
+
case 409:
|
|
159
|
+
// Could be CONFLICT_SEQ or CONFLICT_EXISTS depending on context
|
|
160
|
+
// Default to CONFLICT_SEQ, caller can override
|
|
161
|
+
return `CONFLICT_SEQ`
|
|
162
|
+
case 429:
|
|
163
|
+
return `RATE_LIMITED`
|
|
164
|
+
case 503:
|
|
165
|
+
return `BUSY`
|
|
166
|
+
default:
|
|
167
|
+
return `UNKNOWN`
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Error thrown when stream URL is missing.
|
|
173
|
+
*/
|
|
174
|
+
export class MissingStreamUrlError extends Error {
|
|
175
|
+
constructor() {
|
|
176
|
+
super(`Invalid stream options: missing required url parameter`)
|
|
177
|
+
this.name = `MissingStreamUrlError`
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Error thrown when signal option is invalid.
|
|
183
|
+
*/
|
|
184
|
+
export class InvalidSignalError extends Error {
|
|
185
|
+
constructor() {
|
|
186
|
+
super(`Invalid signal option. It must be an instance of AbortSignal.`)
|
|
187
|
+
this.name = `InvalidSignalError`
|
|
188
|
+
}
|
|
189
|
+
}
|
package/src/fetch.ts
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch utilities with retry and backoff support.
|
|
3
|
+
* Based on @electric-sql/client patterns.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FetchBackoffAbortError, FetchError } from "./error"
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* HTTP status codes that should be retried.
|
|
10
|
+
*/
|
|
11
|
+
const HTTP_RETRY_STATUS_CODES = [429, 503]
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Options for configuring exponential backoff retry behavior.
|
|
15
|
+
*/
|
|
16
|
+
export interface BackoffOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Initial delay before retrying in milliseconds.
|
|
19
|
+
*/
|
|
20
|
+
initialDelay: number
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Maximum retry delay in milliseconds.
|
|
24
|
+
* After reaching this, delay stays constant.
|
|
25
|
+
*/
|
|
26
|
+
maxDelay: number
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Multiplier for exponential backoff.
|
|
30
|
+
*/
|
|
31
|
+
multiplier: number
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Callback invoked on each failed attempt.
|
|
35
|
+
*/
|
|
36
|
+
onFailedAttempt?: () => void
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Enable debug logging.
|
|
40
|
+
*/
|
|
41
|
+
debug?: boolean
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Maximum number of retry attempts before giving up.
|
|
45
|
+
* Set to Infinity for indefinite retries (useful for offline scenarios).
|
|
46
|
+
*/
|
|
47
|
+
maxRetries?: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Default backoff options.
|
|
52
|
+
*/
|
|
53
|
+
export const BackoffDefaults: BackoffOptions = {
|
|
54
|
+
initialDelay: 100,
|
|
55
|
+
maxDelay: 60_000, // Cap at 60s
|
|
56
|
+
multiplier: 1.3,
|
|
57
|
+
maxRetries: Infinity, // Retry forever by default
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Parse Retry-After header value and return delay in milliseconds.
|
|
62
|
+
* Supports both delta-seconds format and HTTP-date format.
|
|
63
|
+
* Returns 0 if header is not present or invalid.
|
|
64
|
+
*/
|
|
65
|
+
export function parseRetryAfterHeader(retryAfter: string | undefined): number {
|
|
66
|
+
if (!retryAfter) return 0
|
|
67
|
+
|
|
68
|
+
// Try parsing as seconds (delta-seconds format)
|
|
69
|
+
const retryAfterSec = Number(retryAfter)
|
|
70
|
+
if (Number.isFinite(retryAfterSec) && retryAfterSec > 0) {
|
|
71
|
+
return retryAfterSec * 1000
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Try parsing as HTTP-date
|
|
75
|
+
const retryDate = Date.parse(retryAfter)
|
|
76
|
+
if (!isNaN(retryDate)) {
|
|
77
|
+
// Handle clock skew: clamp to non-negative, cap at reasonable max
|
|
78
|
+
const deltaMs = retryDate - Date.now()
|
|
79
|
+
return Math.max(0, Math.min(deltaMs, 3600_000)) // Cap at 1 hour
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return 0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Creates a fetch client that retries failed requests with exponential backoff.
|
|
87
|
+
*
|
|
88
|
+
* @param fetchClient - The base fetch client to wrap
|
|
89
|
+
* @param backoffOptions - Options for retry behavior
|
|
90
|
+
* @returns A fetch function with automatic retry
|
|
91
|
+
*/
|
|
92
|
+
export function createFetchWithBackoff(
|
|
93
|
+
fetchClient: typeof fetch,
|
|
94
|
+
backoffOptions: BackoffOptions = BackoffDefaults
|
|
95
|
+
): typeof fetch {
|
|
96
|
+
const {
|
|
97
|
+
initialDelay,
|
|
98
|
+
maxDelay,
|
|
99
|
+
multiplier,
|
|
100
|
+
debug = false,
|
|
101
|
+
onFailedAttempt,
|
|
102
|
+
maxRetries = Infinity,
|
|
103
|
+
} = backoffOptions
|
|
104
|
+
|
|
105
|
+
return async (...args: Parameters<typeof fetch>): Promise<Response> => {
|
|
106
|
+
const url = args[0]
|
|
107
|
+
const options = args[1]
|
|
108
|
+
|
|
109
|
+
let delay = initialDelay
|
|
110
|
+
let attempt = 0
|
|
111
|
+
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
113
|
+
while (true) {
|
|
114
|
+
try {
|
|
115
|
+
const result = await fetchClient(...args)
|
|
116
|
+
if (result.ok) {
|
|
117
|
+
return result
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const err = await FetchError.fromResponse(result, url.toString())
|
|
121
|
+
throw err
|
|
122
|
+
} catch (e) {
|
|
123
|
+
onFailedAttempt?.()
|
|
124
|
+
|
|
125
|
+
if (options?.signal?.aborted) {
|
|
126
|
+
throw new FetchBackoffAbortError()
|
|
127
|
+
} else if (
|
|
128
|
+
e instanceof FetchError &&
|
|
129
|
+
!HTTP_RETRY_STATUS_CODES.includes(e.status) &&
|
|
130
|
+
e.status >= 400 &&
|
|
131
|
+
e.status < 500
|
|
132
|
+
) {
|
|
133
|
+
// Client errors (except 429) cannot be backed off on
|
|
134
|
+
throw e
|
|
135
|
+
} else {
|
|
136
|
+
// Check max retries
|
|
137
|
+
attempt++
|
|
138
|
+
if (attempt > maxRetries) {
|
|
139
|
+
if (debug) {
|
|
140
|
+
console.log(
|
|
141
|
+
`Max retries reached (${attempt}/${maxRetries}), giving up`
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
throw e
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Calculate wait time honoring server-driven backoff as a floor
|
|
148
|
+
// Parse server-provided Retry-After (if present)
|
|
149
|
+
const serverMinimumMs =
|
|
150
|
+
e instanceof FetchError
|
|
151
|
+
? parseRetryAfterHeader(e.headers[`retry-after`])
|
|
152
|
+
: 0
|
|
153
|
+
|
|
154
|
+
// Calculate client backoff with full jitter strategy
|
|
155
|
+
// Full jitter: random_between(0, min(cap, exponential_backoff))
|
|
156
|
+
const jitter = Math.random() * delay
|
|
157
|
+
const clientBackoffMs = Math.min(jitter, maxDelay)
|
|
158
|
+
|
|
159
|
+
// Server minimum is the floor, client cap is the ceiling
|
|
160
|
+
const waitMs = Math.max(serverMinimumMs, clientBackoffMs)
|
|
161
|
+
|
|
162
|
+
if (debug) {
|
|
163
|
+
const source = serverMinimumMs > 0 ? `server+client` : `client`
|
|
164
|
+
console.log(
|
|
165
|
+
`Retry attempt #${attempt} after ${waitMs}ms (${source}, serverMin=${serverMinimumMs}ms, clientBackoff=${clientBackoffMs}ms)`
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Wait for the calculated duration
|
|
170
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs))
|
|
171
|
+
|
|
172
|
+
// Increase the delay for the next attempt (capped at maxDelay)
|
|
173
|
+
delay = Math.min(delay * multiplier, maxDelay)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Status codes where we shouldn't try to read the body.
|
|
182
|
+
*/
|
|
183
|
+
const NO_BODY_STATUS_CODES = [201, 204, 205]
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Creates a fetch client that ensures the response body is fully consumed.
|
|
187
|
+
* This prevents issues with connection pooling when bodies aren't read.
|
|
188
|
+
*
|
|
189
|
+
* Uses arrayBuffer() instead of text() to preserve binary data integrity.
|
|
190
|
+
*
|
|
191
|
+
* @param fetchClient - The base fetch client to wrap
|
|
192
|
+
* @returns A fetch function that consumes response bodies
|
|
193
|
+
*/
|
|
194
|
+
export function createFetchWithConsumedBody(
|
|
195
|
+
fetchClient: typeof fetch
|
|
196
|
+
): typeof fetch {
|
|
197
|
+
return async (...args: Parameters<typeof fetch>): Promise<Response> => {
|
|
198
|
+
const url = args[0]
|
|
199
|
+
const res = await fetchClient(...args)
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
if (res.status < 200 || NO_BODY_STATUS_CODES.includes(res.status)) {
|
|
203
|
+
return res
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Read body as arrayBuffer to preserve binary data integrity
|
|
207
|
+
const buf = await res.arrayBuffer()
|
|
208
|
+
return new Response(buf, {
|
|
209
|
+
status: res.status,
|
|
210
|
+
statusText: res.statusText,
|
|
211
|
+
headers: res.headers,
|
|
212
|
+
})
|
|
213
|
+
} catch (err) {
|
|
214
|
+
if (args[1]?.signal?.aborted) {
|
|
215
|
+
throw new FetchBackoffAbortError()
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
throw new FetchError(
|
|
219
|
+
res.status,
|
|
220
|
+
undefined,
|
|
221
|
+
undefined,
|
|
222
|
+
Object.fromEntries([...res.headers.entries()]),
|
|
223
|
+
url.toString(),
|
|
224
|
+
err instanceof Error
|
|
225
|
+
? err.message
|
|
226
|
+
: typeof err === `string`
|
|
227
|
+
? err
|
|
228
|
+
: `failed to read body`
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Chains an AbortController to an optional source signal.
|
|
236
|
+
* If the source signal is aborted, the provided controller will also abort.
|
|
237
|
+
*/
|
|
238
|
+
export function chainAborter(
|
|
239
|
+
aborter: AbortController,
|
|
240
|
+
sourceSignal?: AbortSignal | null
|
|
241
|
+
): {
|
|
242
|
+
signal: AbortSignal
|
|
243
|
+
cleanup: () => void
|
|
244
|
+
} {
|
|
245
|
+
let cleanup = noop
|
|
246
|
+
if (!sourceSignal) {
|
|
247
|
+
// no-op, nothing to chain to
|
|
248
|
+
} else if (sourceSignal.aborted) {
|
|
249
|
+
// source signal is already aborted, abort immediately
|
|
250
|
+
aborter.abort(sourceSignal.reason)
|
|
251
|
+
} else {
|
|
252
|
+
// chain to source signal abort event
|
|
253
|
+
const abortParent = () => aborter.abort(sourceSignal.reason)
|
|
254
|
+
sourceSignal.addEventListener(`abort`, abortParent, {
|
|
255
|
+
once: true,
|
|
256
|
+
signal: aborter.signal,
|
|
257
|
+
})
|
|
258
|
+
cleanup = () => sourceSignal.removeEventListener(`abort`, abortParent)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
signal: aborter.signal,
|
|
263
|
+
cleanup,
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function noop() {}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Streams TypeScript Client
|
|
3
|
+
*
|
|
4
|
+
* A client library for the Electric Durable Streams protocol.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Primary Read API (new)
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
// Standalone stream() function - the fetch-like read API
|
|
14
|
+
export { stream } from "./stream-api"
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Handle API (read/write)
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
// DurableStream class for read/write operations
|
|
21
|
+
export { DurableStream, type DurableStreamOptions } from "./stream"
|
|
22
|
+
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Types
|
|
25
|
+
// ============================================================================
|
|
26
|
+
|
|
27
|
+
export type {
|
|
28
|
+
// Core types
|
|
29
|
+
Offset,
|
|
30
|
+
HeadersRecord,
|
|
31
|
+
ParamsRecord,
|
|
32
|
+
MaybePromise,
|
|
33
|
+
|
|
34
|
+
// Stream options (new API)
|
|
35
|
+
StreamOptions,
|
|
36
|
+
StreamHandleOptions,
|
|
37
|
+
LiveMode,
|
|
38
|
+
SSEResilienceOptions,
|
|
39
|
+
|
|
40
|
+
// Chunk & batch types (new API)
|
|
41
|
+
JsonBatchMeta,
|
|
42
|
+
JsonBatch,
|
|
43
|
+
ByteChunk,
|
|
44
|
+
TextChunk,
|
|
45
|
+
StreamResponse,
|
|
46
|
+
|
|
47
|
+
// Legacy types (still used internally)
|
|
48
|
+
CreateOptions,
|
|
49
|
+
AppendOptions,
|
|
50
|
+
ReadOptions,
|
|
51
|
+
HeadResult,
|
|
52
|
+
LegacyLiveMode,
|
|
53
|
+
|
|
54
|
+
// Error handling
|
|
55
|
+
DurableStreamErrorCode,
|
|
56
|
+
RetryOpts,
|
|
57
|
+
StreamErrorHandler,
|
|
58
|
+
} from "./types"
|
|
59
|
+
|
|
60
|
+
// Re-export async iterable helper type and function
|
|
61
|
+
export type { ReadableStreamAsyncIterable } from "./asyncIterableReadableStream"
|
|
62
|
+
export { asAsyncIterableReadableStream } from "./asyncIterableReadableStream"
|
|
63
|
+
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// Errors
|
|
66
|
+
// ============================================================================
|
|
67
|
+
|
|
68
|
+
export {
|
|
69
|
+
FetchError,
|
|
70
|
+
FetchBackoffAbortError,
|
|
71
|
+
DurableStreamError,
|
|
72
|
+
MissingStreamUrlError,
|
|
73
|
+
InvalidSignalError,
|
|
74
|
+
} from "./error"
|
|
75
|
+
|
|
76
|
+
// ============================================================================
|
|
77
|
+
// Fetch utilities
|
|
78
|
+
// ============================================================================
|
|
79
|
+
|
|
80
|
+
export {
|
|
81
|
+
type BackoffOptions,
|
|
82
|
+
BackoffDefaults,
|
|
83
|
+
createFetchWithBackoff,
|
|
84
|
+
createFetchWithConsumedBody,
|
|
85
|
+
} from "./fetch"
|
|
86
|
+
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Constants (for advanced users)
|
|
89
|
+
// ============================================================================
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
STREAM_OFFSET_HEADER,
|
|
93
|
+
STREAM_CURSOR_HEADER,
|
|
94
|
+
STREAM_UP_TO_DATE_HEADER,
|
|
95
|
+
STREAM_SEQ_HEADER,
|
|
96
|
+
STREAM_TTL_HEADER,
|
|
97
|
+
STREAM_EXPIRES_AT_HEADER,
|
|
98
|
+
OFFSET_QUERY_PARAM,
|
|
99
|
+
LIVE_QUERY_PARAM,
|
|
100
|
+
CURSOR_QUERY_PARAM,
|
|
101
|
+
SSE_COMPATIBLE_CONTENT_TYPES,
|
|
102
|
+
DURABLE_STREAM_PROTOCOL_QUERY_PARAMS,
|
|
103
|
+
} from "./constants"
|