-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathlisten.ts
114 lines (103 loc) · 3.75 KB
/
listen.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import {Observable, of, throwError} from 'rxjs'
import {filter, map} from 'rxjs/operators'
import type {ObservableSanityClient, SanityClient} from '../SanityClient'
import {
type Any,
type ListenEvent,
type ListenOptions,
type ListenParams,
type MutationEvent,
} from '../types'
import defaults from '../util/defaults'
import {pick} from '../util/pick'
import {_getDataUrl} from './dataMethods'
import {encodeQueryString} from './encodeQueryString'
import {connectEventSource} from './eventsource'
import {eventSourcePolyfill} from './eventsourcePolyfill'
import {reconnectOnConnectionFailure} from './reconnectOnConnectionFailure'
// Limit is 16K for a _request_, eg including headers. Have to account for an
// unknown range of headers, but an average EventSource request from Chrome seems
// to have around 700 bytes of cruft, so let us account for 1.2K to be "safe"
const MAX_URL_LENGTH = 16000 - 1200
const possibleOptions = [
'includePreviousRevision',
'includeResult',
'includeMutations',
'visibility',
'effectFormat',
'tag',
]
const defaultOptions = {
includeResult: true,
}
/**
* Set up a listener that will be notified when mutations occur on documents matching the provided query/filter.
*
* @param query - GROQ-filter to listen to changes for
* @param params - Optional query parameters
* @param options - Optional listener options
* @public
*/
export function _listen<R extends Record<string, Any> = Record<string, Any>>(
this: SanityClient | ObservableSanityClient,
query: string,
params?: ListenParams,
): Observable<MutationEvent<R>>
/**
* Set up a listener that will be notified when mutations occur on documents matching the provided query/filter.
*
* @param query - GROQ-filter to listen to changes for
* @param params - Optional query parameters
* @param options - Optional listener options
* @public
*/
export function _listen<R extends Record<string, Any> = Record<string, Any>>(
this: SanityClient | ObservableSanityClient,
query: string,
params?: ListenParams,
options?: ListenOptions,
): Observable<ListenEvent<R>>
/** @public */
export function _listen<R extends Record<string, Any> = Record<string, Any>>(
this: SanityClient | ObservableSanityClient,
query: string,
params?: ListenParams,
opts: ListenOptions = {},
): Observable<MutationEvent<R> | ListenEvent<R>> {
const {url, token, withCredentials, requestTagPrefix} = this.config()
const tag = opts.tag && requestTagPrefix ? [requestTagPrefix, opts.tag].join('.') : opts.tag
const options = {...defaults(opts, defaultOptions), tag}
const listenOpts = pick(options, possibleOptions)
const qs = encodeQueryString({query, params, options: {tag, ...listenOpts}})
const uri = `${url}${_getDataUrl(this, 'listen', qs)}`
if (uri.length > MAX_URL_LENGTH) {
return throwError(() => new Error('Query too large for listener'))
}
const listenFor = options.events ? options.events : ['mutation']
const esOptions: EventSourceInit & {headers?: Record<string, string>} = {}
if (token || withCredentials) {
esOptions.withCredentials = true
}
if (token) {
esOptions.headers = {
Authorization: `Bearer ${token}`,
}
}
const initEventSource = () =>
// use polyfill if there is no global EventSource or if we need to set headers
(typeof EventSource === 'undefined' || esOptions.headers
? eventSourcePolyfill
: of(EventSource)
).pipe(map((EventSource) => new EventSource(uri, esOptions)))
return connectEventSource(initEventSource, listenFor).pipe(
reconnectOnConnectionFailure(),
filter((event) => listenFor.includes(event.type)),
map(
(event) =>
({
type: event.type,
...('data' in event ? (event.data as object) : {}),
}) as MutationEvent<R> | ListenEvent<R>,
),
)
}