@sanity/client 6.24.2 → 6.24.4
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/dist/csm.d.cts +99 -34
- package/dist/csm.d.ts +99 -34
- package/dist/index.browser.cjs +188 -128
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.d.cts +112 -4
- package/dist/index.browser.d.ts +112 -4
- package/dist/index.browser.js +189 -129
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +189 -129
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -4
- package/dist/index.d.ts +112 -4
- package/dist/index.js +190 -130
- package/dist/index.js.map +1 -1
- package/dist/stega.browser.d.cts +3419 -96
- package/dist/stega.browser.d.ts +3419 -96
- package/dist/stega.d.cts +3419 -96
- package/dist/stega.d.ts +3419 -96
- package/package.json +4 -4
- package/src/data/eventsource.ts +255 -0
- package/src/data/eventsourcePolyfill.ts +7 -0
- package/src/data/listen.ts +31 -142
- package/src/data/live.ts +60 -120
- package/src/data/reconnectOnConnectionFailure.ts +30 -0
- package/src/data/transaction.ts +26 -1
- package/src/defineCreateClient.ts +11 -0
- package/src/types.ts +10 -0
- package/umd/sanityClient.js +988 -144
- package/umd/sanityClient.min.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/client",
|
|
3
|
-
"version": "6.24.
|
|
3
|
+
"version": "6.24.4",
|
|
4
4
|
"description": "Client for retrieving, creating and patching data from Sanity.io",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
},
|
|
120
120
|
"dependencies": {
|
|
121
121
|
"@sanity/eventsource": "^5.0.2",
|
|
122
|
-
"get-it": "^8.6.
|
|
122
|
+
"get-it": "^8.6.6",
|
|
123
123
|
"rxjs": "^7.0.0"
|
|
124
124
|
},
|
|
125
125
|
"devDependencies": {
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
"@rollup/plugin-commonjs": "^28.0.2",
|
|
129
129
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
130
130
|
"@sanity/client-latest": "npm:@sanity/client@latest",
|
|
131
|
-
"@sanity/pkg-utils": "^
|
|
131
|
+
"@sanity/pkg-utils": "^7.0.1",
|
|
132
132
|
"@types/json-diff": "^1.0.3",
|
|
133
133
|
"@types/node": "^22.9.0",
|
|
134
134
|
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
@@ -152,7 +152,7 @@
|
|
|
152
152
|
"rollup": "^4.30.1",
|
|
153
153
|
"sse-channel": "^4.0.0",
|
|
154
154
|
"terser": "^5.37.0",
|
|
155
|
-
"typescript": "5.7.
|
|
155
|
+
"typescript": "5.7.3",
|
|
156
156
|
"vitest": "2.1.8",
|
|
157
157
|
"vitest-github-actions-reporter": "0.11.1"
|
|
158
158
|
},
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import {defer, isObservable, mergeMap, Observable, of} from 'rxjs'
|
|
2
|
+
|
|
3
|
+
import {type Any} from '../types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @public
|
|
7
|
+
* Thrown if the EventSource connection could not be established.
|
|
8
|
+
* Note that ConnectionFailedErrors are rare, and disconnects will normally be handled by the EventSource instance itself and emitted as `reconnect` events.
|
|
9
|
+
*/
|
|
10
|
+
export class ConnectionFailedError extends Error {
|
|
11
|
+
readonly name = 'ConnectionFailedError'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The listener has been told to explicitly disconnect.
|
|
16
|
+
* This is a rare situation, but may occur if the API knows reconnect attempts will fail,
|
|
17
|
+
* eg in the case of a deleted dataset, a blocked project or similar events.
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export class DisconnectError extends Error {
|
|
21
|
+
readonly name = 'DisconnectError'
|
|
22
|
+
readonly reason?: string
|
|
23
|
+
constructor(message: string, reason?: string, options: ErrorOptions = {}) {
|
|
24
|
+
super(message, options)
|
|
25
|
+
this.reason = reason
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @public
|
|
31
|
+
* The server sent a `channelError` message. Usually indicative of a bad or malformed request
|
|
32
|
+
*/
|
|
33
|
+
export class ChannelError extends Error {
|
|
34
|
+
readonly name = 'ChannelError'
|
|
35
|
+
readonly data?: unknown
|
|
36
|
+
constructor(message: string, data: unknown) {
|
|
37
|
+
super(message)
|
|
38
|
+
this.data = data
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @public
|
|
44
|
+
* The server sent an `error`-event to tell the client that an unexpected error has happened.
|
|
45
|
+
*/
|
|
46
|
+
export class MessageError extends Error {
|
|
47
|
+
readonly name = 'MessageError'
|
|
48
|
+
readonly data?: unknown
|
|
49
|
+
constructor(message: string, data: unknown, options: ErrorOptions = {}) {
|
|
50
|
+
super(message, options)
|
|
51
|
+
this.data = data
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @public
|
|
57
|
+
* An error occurred while parsing the message sent by the server as JSON. Should normally not happen.
|
|
58
|
+
*/
|
|
59
|
+
export class MessageParseError extends Error {
|
|
60
|
+
readonly name = 'MessageParseError'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @public
|
|
65
|
+
*/
|
|
66
|
+
export interface ServerSentEvent<Name extends string> {
|
|
67
|
+
type: Name
|
|
68
|
+
id?: string
|
|
69
|
+
data?: unknown
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Always listen for these events, no matter what
|
|
73
|
+
const REQUIRED_EVENTS = ['channelError', 'disconnect']
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @internal
|
|
77
|
+
*/
|
|
78
|
+
export type EventSourceEvent<Name extends string> = ServerSentEvent<Name>
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @internal
|
|
82
|
+
*/
|
|
83
|
+
export type EventSourceInstance = InstanceType<typeof globalThis.EventSource>
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Sanity API specific EventSource handler shared between the listen and live APIs
|
|
87
|
+
*
|
|
88
|
+
* Since the `EventSource` API is not provided by all environments, this function enables custom initialization of the EventSource instance
|
|
89
|
+
* for runtimes that requires polyfilling or custom setup logic (e.g. custom HTTP headers)
|
|
90
|
+
* via the passed `initEventSource` function which must return an EventSource instance.
|
|
91
|
+
*
|
|
92
|
+
* Possible errors to be thrown on the returned observable are:
|
|
93
|
+
* - {@link MessageError}
|
|
94
|
+
* - {@link MessageParseError}
|
|
95
|
+
* - {@link ChannelError}
|
|
96
|
+
* - {@link DisconnectError}
|
|
97
|
+
* - {@link ConnectionFailedError}
|
|
98
|
+
*
|
|
99
|
+
* @param initEventSource - A function that returns an EventSource instance or an Observable that resolves to an EventSource instance
|
|
100
|
+
* @param events - an array of named events from the API to listen for.
|
|
101
|
+
*
|
|
102
|
+
* @internal
|
|
103
|
+
*/
|
|
104
|
+
export function connectEventSource<EventName extends string>(
|
|
105
|
+
initEventSource: () => EventSourceInstance | Observable<EventSourceInstance>,
|
|
106
|
+
events: EventName[],
|
|
107
|
+
) {
|
|
108
|
+
return defer(() => {
|
|
109
|
+
const es = initEventSource()
|
|
110
|
+
return isObservable(es) ? es : of(es)
|
|
111
|
+
}).pipe(mergeMap((es) => connectWithESInstance(es, events))) as Observable<
|
|
112
|
+
ServerSentEvent<EventName>
|
|
113
|
+
>
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Provides an observable from the passed EventSource instance, subscribing to the passed list of names of events types to listen for
|
|
118
|
+
* Handles connection logic, adding/removing event listeners, payload parsing, error propagation, etc.
|
|
119
|
+
*
|
|
120
|
+
* @param es - The EventSource instance
|
|
121
|
+
* @param events - List of event names to listen for
|
|
122
|
+
*/
|
|
123
|
+
function connectWithESInstance<EventTypeName extends string>(
|
|
124
|
+
es: EventSourceInstance,
|
|
125
|
+
events: EventTypeName[],
|
|
126
|
+
) {
|
|
127
|
+
return new Observable<EventSourceEvent<EventTypeName>>((observer) => {
|
|
128
|
+
const emitOpen = (events as string[]).includes('open')
|
|
129
|
+
const emitReconnect = (events as string[]).includes('reconnect')
|
|
130
|
+
|
|
131
|
+
// EventSource will emit a regular Event if it fails to connect, however the API may also emit an `error` MessageEvent
|
|
132
|
+
// So we need to handle both cases
|
|
133
|
+
function onError(evt: MessageEvent | Event) {
|
|
134
|
+
// If the event has a `data` property, then it`s a MessageEvent emitted by the API and we should forward the error
|
|
135
|
+
if ('data' in evt) {
|
|
136
|
+
const [parseError, event] = parseEvent(evt as MessageEvent)
|
|
137
|
+
observer.error(
|
|
138
|
+
parseError
|
|
139
|
+
? new MessageParseError('Unable to parse EventSource error message', {cause: event})
|
|
140
|
+
: new MessageError((event?.data as {message: string}).message, event),
|
|
141
|
+
)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// We should never be in a disconnected state. By default, EventSource will reconnect
|
|
146
|
+
// automatically, but in some cases (like when a laptop lid is closed), it will trigger onError
|
|
147
|
+
// if it can't reconnect.
|
|
148
|
+
// see https://html.spec.whatwg.org/multipage/server-sent-events.html#sse-processing-model
|
|
149
|
+
if (es.readyState === es.CLOSED) {
|
|
150
|
+
// In these cases we'll signal to consumers (via the error path) that a retry/reconnect is needed.
|
|
151
|
+
observer.error(new ConnectionFailedError('EventSource connection failed'))
|
|
152
|
+
} else if (emitReconnect) {
|
|
153
|
+
observer.next({type: 'reconnect' as EventTypeName})
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function onOpen() {
|
|
158
|
+
// The open event of the EventSource API is fired when a connection with an event source is opened.
|
|
159
|
+
observer.next({type: 'open' as EventTypeName})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function onMessage(message: MessageEvent) {
|
|
163
|
+
const [parseError, event] = parseEvent(message)
|
|
164
|
+
if (parseError) {
|
|
165
|
+
observer.error(
|
|
166
|
+
new MessageParseError('Unable to parse EventSource message', {cause: parseError}),
|
|
167
|
+
)
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
if (message.type === 'channelError') {
|
|
171
|
+
// An error occurred. This is different from a network-level error (which will be emitted as 'error').
|
|
172
|
+
// Possible causes are things such as malformed filters, non-existant datasets or similar.
|
|
173
|
+
observer.error(new ChannelError(extractErrorMessage(event?.data), event.data))
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
if (message.type === 'disconnect') {
|
|
177
|
+
// The listener has been told to explicitly disconnect and not reconnect.
|
|
178
|
+
// This is a rare situation, but may occur if the API knows reconnect attempts will fail,
|
|
179
|
+
// eg in the case of a deleted dataset, a blocked project or similar events.
|
|
180
|
+
observer.error(
|
|
181
|
+
new DisconnectError(
|
|
182
|
+
`Server disconnected client: ${
|
|
183
|
+
(event.data as {reason?: string})?.reason || 'unknown error'
|
|
184
|
+
}`,
|
|
185
|
+
),
|
|
186
|
+
)
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
observer.next({
|
|
190
|
+
type: message.type as EventTypeName,
|
|
191
|
+
id: message.lastEventId,
|
|
192
|
+
...(event.data ? {data: event.data} : {}),
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
es.addEventListener('error', onError)
|
|
197
|
+
|
|
198
|
+
if (emitOpen) {
|
|
199
|
+
es.addEventListener('open', onOpen)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Make sure we have a unique list of events types to avoid listening multiple times,
|
|
203
|
+
const cleanedEvents = [...new Set([...REQUIRED_EVENTS, ...events])]
|
|
204
|
+
// filter out events that are handled separately
|
|
205
|
+
.filter((type) => type !== 'error' && type !== 'open' && type !== 'reconnect')
|
|
206
|
+
|
|
207
|
+
cleanedEvents.forEach((type: string) => es.addEventListener(type, onMessage))
|
|
208
|
+
|
|
209
|
+
return () => {
|
|
210
|
+
es.removeEventListener('error', onError)
|
|
211
|
+
if (emitOpen) {
|
|
212
|
+
es.removeEventListener('open', onOpen)
|
|
213
|
+
}
|
|
214
|
+
cleanedEvents.forEach((type: string) => es.removeEventListener(type, onMessage))
|
|
215
|
+
es.close()
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function parseEvent(
|
|
221
|
+
message: MessageEvent,
|
|
222
|
+
): [null, {type: string; id: string; data?: unknown}] | [Error, null] {
|
|
223
|
+
try {
|
|
224
|
+
const data = typeof message.data === 'string' && JSON.parse(message.data)
|
|
225
|
+
return [
|
|
226
|
+
null,
|
|
227
|
+
{
|
|
228
|
+
type: message.type,
|
|
229
|
+
id: message.lastEventId,
|
|
230
|
+
...(isEmptyObject(data) ? {} : {data}),
|
|
231
|
+
},
|
|
232
|
+
]
|
|
233
|
+
} catch (err) {
|
|
234
|
+
return [err as Error, null]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function extractErrorMessage(err: Any) {
|
|
239
|
+
if (!err.error) {
|
|
240
|
+
return err.message || 'Unknown listener error'
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (err.error.description) {
|
|
244
|
+
return err.error.description
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return typeof err.error === 'string' ? err.error : JSON.stringify(err.error, null, 2)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function isEmptyObject(data: object) {
|
|
251
|
+
for (const _ in data) {
|
|
252
|
+
return false
|
|
253
|
+
}
|
|
254
|
+
return true
|
|
255
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import {defer, shareReplay} from 'rxjs'
|
|
2
|
+
import {map} from 'rxjs/operators'
|
|
3
|
+
|
|
4
|
+
export const eventSourcePolyfill = defer(() => import('@sanity/eventsource')).pipe(
|
|
5
|
+
map(({default: EventSource}) => EventSource as unknown as typeof globalThis.EventSource),
|
|
6
|
+
shareReplay(1),
|
|
7
|
+
)
|
package/src/data/listen.ts
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import {Observable} from 'rxjs'
|
|
1
|
+
import {Observable, of, throwError} from 'rxjs'
|
|
2
|
+
import {filter, map} from 'rxjs/operators'
|
|
2
3
|
|
|
3
4
|
import type {ObservableSanityClient, SanityClient} from '../SanityClient'
|
|
4
|
-
import
|
|
5
|
+
import {
|
|
6
|
+
type Any,
|
|
7
|
+
type ListenEvent,
|
|
8
|
+
type ListenOptions,
|
|
9
|
+
type ListenParams,
|
|
10
|
+
type MutationEvent,
|
|
11
|
+
} from '../types'
|
|
5
12
|
import defaults from '../util/defaults'
|
|
6
13
|
import {pick} from '../util/pick'
|
|
7
14
|
import {_getDataUrl} from './dataMethods'
|
|
8
15
|
import {encodeQueryString} from './encodeQueryString'
|
|
16
|
+
import {connectEventSource} from './eventsource'
|
|
17
|
+
import {eventSourcePolyfill} from './eventsourcePolyfill'
|
|
18
|
+
import {reconnectOnConnectionFailure} from './reconnectOnConnectionFailure'
|
|
9
19
|
|
|
10
20
|
// Limit is 16K for a _request_, eg including headers. Have to account for an
|
|
11
21
|
// unknown range of headers, but an average EventSource request from Chrome seems
|
|
@@ -67,11 +77,10 @@ export function _listen<R extends Record<string, Any> = Record<string, Any>>(
|
|
|
67
77
|
|
|
68
78
|
const uri = `${url}${_getDataUrl(this, 'listen', qs)}`
|
|
69
79
|
if (uri.length > MAX_URL_LENGTH) {
|
|
70
|
-
return
|
|
80
|
+
return throwError(() => new Error('Query too large for listener'))
|
|
71
81
|
}
|
|
72
82
|
|
|
73
83
|
const listenFor = options.events ? options.events : ['mutation']
|
|
74
|
-
const shouldEmitReconnect = listenFor.indexOf('reconnect') !== -1
|
|
75
84
|
|
|
76
85
|
const esOptions: EventSourceInit & {headers?: Record<string, string>} = {}
|
|
77
86
|
if (token || withCredentials) {
|
|
@@ -84,142 +93,22 @@ export function _listen<R extends Record<string, Any> = Record<string, Any>>(
|
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (stopped) {
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Unless we've explicitly stopped the ES (in which case `stopped` should be true),
|
|
110
|
-
// we should never be in a disconnected state. By default, EventSource will reconnect
|
|
111
|
-
// automatically, in which case it sets readyState to `CONNECTING`, but in some cases
|
|
112
|
-
// (like when a laptop lid is closed), it closes the connection. In these cases we need
|
|
113
|
-
// to explicitly reconnect.
|
|
114
|
-
if (es.readyState === es.CLOSED) {
|
|
115
|
-
unsubscribe()
|
|
116
|
-
clearTimeout(reconnectTimer)
|
|
117
|
-
reconnectTimer = setTimeout(open, 100)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function onChannelError(err: Any) {
|
|
122
|
-
observer.error(cooerceError(err))
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function onMessage(evt: Any) {
|
|
126
|
-
const event = parseEvent(evt)
|
|
127
|
-
return event instanceof Error ? observer.error(event) : observer.next(event)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function onDisconnect() {
|
|
131
|
-
stopped = true
|
|
132
|
-
unsubscribe()
|
|
133
|
-
observer.complete()
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function unsubscribe() {
|
|
137
|
-
if (!es) return
|
|
138
|
-
es.removeEventListener('error', onError)
|
|
139
|
-
es.removeEventListener('channelError', onChannelError)
|
|
140
|
-
es.removeEventListener('disconnect', onDisconnect)
|
|
141
|
-
listenFor.forEach((type: string) => es.removeEventListener(type, onMessage))
|
|
142
|
-
es.close()
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function emitReconnect() {
|
|
146
|
-
if (shouldEmitReconnect) {
|
|
147
|
-
observer.next({type: 'reconnect'})
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async function getEventSource(): Promise<InstanceType<
|
|
152
|
-
typeof import('@sanity/eventsource')
|
|
153
|
-
> | void> {
|
|
154
|
-
const {default: EventSource} = await import('@sanity/eventsource')
|
|
155
|
-
|
|
156
|
-
// If the listener has been unsubscribed from before we managed to load the module,
|
|
157
|
-
// do not set up the EventSource.
|
|
158
|
-
if (unsubscribed) {
|
|
159
|
-
return
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const evs = new EventSource(uri, esOptions)
|
|
163
|
-
evs.addEventListener('error', onError)
|
|
164
|
-
evs.addEventListener('channelError', onChannelError)
|
|
165
|
-
evs.addEventListener('disconnect', onDisconnect)
|
|
166
|
-
listenFor.forEach((type: string) => evs.addEventListener(type, onMessage))
|
|
167
|
-
return evs
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function open() {
|
|
171
|
-
getEventSource()
|
|
172
|
-
.then((eventSource) => {
|
|
173
|
-
if (eventSource) {
|
|
174
|
-
es = eventSource
|
|
175
|
-
// Handle race condition where the observer is unsubscribed before the EventSource is set up
|
|
176
|
-
if (unsubscribed) {
|
|
177
|
-
unsubscribe()
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
})
|
|
181
|
-
.catch((reason) => {
|
|
182
|
-
observer.error(reason)
|
|
183
|
-
stop()
|
|
184
|
-
})
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function stop() {
|
|
188
|
-
stopped = true
|
|
189
|
-
unsubscribe()
|
|
190
|
-
unsubscribed = true
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
return stop
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function parseEvent(event: Any) {
|
|
198
|
-
try {
|
|
199
|
-
const data = (event.data && JSON.parse(event.data)) || {}
|
|
200
|
-
return Object.assign({type: event.type}, data)
|
|
201
|
-
} catch (err) {
|
|
202
|
-
return err
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function cooerceError(err: Any) {
|
|
207
|
-
if (err instanceof Error) {
|
|
208
|
-
return err
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const evt = parseEvent(err)
|
|
212
|
-
return evt instanceof Error ? evt : new Error(extractErrorMessage(evt))
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
function extractErrorMessage(err: Any) {
|
|
216
|
-
if (!err.error) {
|
|
217
|
-
return err.message || 'Unknown listener error'
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (err.error.description) {
|
|
221
|
-
return err.error.description
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return typeof err.error === 'string' ? err.error : JSON.stringify(err.error, null, 2)
|
|
96
|
+
const initEventSource = () =>
|
|
97
|
+
// use polyfill if there is no global EventSource or if we need to set headers
|
|
98
|
+
(typeof EventSource === 'undefined' || esOptions.headers
|
|
99
|
+
? eventSourcePolyfill
|
|
100
|
+
: of(EventSource)
|
|
101
|
+
).pipe(map((EventSource) => new EventSource(uri, esOptions)))
|
|
102
|
+
|
|
103
|
+
return connectEventSource(initEventSource, listenFor).pipe(
|
|
104
|
+
reconnectOnConnectionFailure(),
|
|
105
|
+
filter((event) => listenFor.includes(event.type)),
|
|
106
|
+
map(
|
|
107
|
+
(event) =>
|
|
108
|
+
({
|
|
109
|
+
type: event.type,
|
|
110
|
+
...('data' in event ? (event.data as object) : {}),
|
|
111
|
+
}) as MutationEvent<R> | ListenEvent<R>,
|
|
112
|
+
),
|
|
113
|
+
)
|
|
225
114
|
}
|