@sanity/client 6.17.3 → 6.18.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/dist/index.browser.cjs +117 -45
- package/dist/index.browser.cjs.map +1 -1
- package/dist/index.browser.d.cts +34 -0
- package/dist/index.browser.d.ts +34 -0
- package/dist/index.browser.js +117 -45
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +118 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +118 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/SanityClient.ts +6 -0
- package/src/data/dataMethods.ts +6 -1
- package/src/data/live.ts +126 -0
- package/src/types.ts +21 -0
- package/umd/sanityClient.js +131 -59
- package/umd/sanityClient.min.js +3 -3
package/package.json
CHANGED
package/src/SanityClient.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {AssetsClient, ObservableAssetsClient} from './assets/AssetsClient'
|
|
|
4
4
|
import {defaultConfig, initConfig} from './config'
|
|
5
5
|
import * as dataMethods from './data/dataMethods'
|
|
6
6
|
import {_listen} from './data/listen'
|
|
7
|
+
import {LiveClient} from './data/live'
|
|
7
8
|
import {ObservablePatch, Patch} from './data/patch'
|
|
8
9
|
import {ObservableTransaction, Transaction} from './data/transaction'
|
|
9
10
|
import {DatasetsClient, ObservableDatasetsClient} from './datasets/DatasetsClient'
|
|
@@ -43,6 +44,7 @@ export type {
|
|
|
43
44
|
_listen,
|
|
44
45
|
AssetsClient,
|
|
45
46
|
DatasetsClient,
|
|
47
|
+
LiveClient,
|
|
46
48
|
ObservableAssetsClient,
|
|
47
49
|
ObservableDatasetsClient,
|
|
48
50
|
ObservableProjectsClient,
|
|
@@ -55,6 +57,7 @@ export type {
|
|
|
55
57
|
export class ObservableSanityClient {
|
|
56
58
|
assets: ObservableAssetsClient
|
|
57
59
|
datasets: ObservableDatasetsClient
|
|
60
|
+
live: LiveClient
|
|
58
61
|
projects: ObservableProjectsClient
|
|
59
62
|
users: ObservableUsersClient
|
|
60
63
|
|
|
@@ -76,6 +79,7 @@ export class ObservableSanityClient {
|
|
|
76
79
|
|
|
77
80
|
this.assets = new ObservableAssetsClient(this, this.#httpRequest)
|
|
78
81
|
this.datasets = new ObservableDatasetsClient(this, this.#httpRequest)
|
|
82
|
+
this.live = new LiveClient(this)
|
|
79
83
|
this.projects = new ObservableProjectsClient(this, this.#httpRequest)
|
|
80
84
|
this.users = new ObservableUsersClient(this, this.#httpRequest)
|
|
81
85
|
}
|
|
@@ -695,6 +699,7 @@ export class ObservableSanityClient {
|
|
|
695
699
|
export class SanityClient {
|
|
696
700
|
assets: AssetsClient
|
|
697
701
|
datasets: DatasetsClient
|
|
702
|
+
live: LiveClient
|
|
698
703
|
projects: ProjectsClient
|
|
699
704
|
users: UsersClient
|
|
700
705
|
|
|
@@ -721,6 +726,7 @@ export class SanityClient {
|
|
|
721
726
|
|
|
722
727
|
this.assets = new AssetsClient(this, this.#httpRequest)
|
|
723
728
|
this.datasets = new DatasetsClient(this, this.#httpRequest)
|
|
729
|
+
this.live = new LiveClient(this)
|
|
724
730
|
this.projects = new ProjectsClient(this, this.#httpRequest)
|
|
725
731
|
this.users = new UsersClient(this, this.#httpRequest)
|
|
726
732
|
|
package/src/data/dataMethods.ts
CHANGED
|
@@ -257,7 +257,7 @@ export function _dataRequest(
|
|
|
257
257
|
const useGet = !isMutation && strQuery.length < getQuerySizeLimit
|
|
258
258
|
const stringQuery = useGet ? strQuery : ''
|
|
259
259
|
const returnFirst = options.returnFirst
|
|
260
|
-
const {timeout, token, tag, headers, returnQuery} = options
|
|
260
|
+
const {timeout, token, tag, headers, returnQuery, lastLiveEventId} = options
|
|
261
261
|
|
|
262
262
|
const uri = _getDataUrl(client, endpoint, stringQuery)
|
|
263
263
|
|
|
@@ -274,6 +274,7 @@ export function _dataRequest(
|
|
|
274
274
|
returnQuery,
|
|
275
275
|
perspective: options.perspective,
|
|
276
276
|
resultSourceMap: options.resultSourceMap,
|
|
277
|
+
lastLiveEventId: Array.isArray(lastLiveEventId) ? lastLiveEventId[0] : lastLiveEventId,
|
|
277
278
|
canUseCdn: isQuery,
|
|
278
279
|
signal: options.signal,
|
|
279
280
|
fetch: options.fetch,
|
|
@@ -375,6 +376,10 @@ export function _requestObservable<R>(
|
|
|
375
376
|
}
|
|
376
377
|
}
|
|
377
378
|
|
|
379
|
+
if (options.lastLiveEventId) {
|
|
380
|
+
options.query = {...options.query, lastLiveEventId: options.lastLiveEventId}
|
|
381
|
+
}
|
|
382
|
+
|
|
378
383
|
if (options.returnQuery === false) {
|
|
379
384
|
options.query = {returnQuery: 'false', ...options.query}
|
|
380
385
|
}
|
package/src/data/live.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {Observable} from 'rxjs'
|
|
2
|
+
|
|
3
|
+
import type {ObservableSanityClient, SanityClient} from '../SanityClient'
|
|
4
|
+
import type {Any, LiveEventMessage, LiveEventRestart} from '../types'
|
|
5
|
+
import {_getDataUrl} from './dataMethods'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @alpha this API is experimental and may change or even be removed
|
|
9
|
+
*/
|
|
10
|
+
export class LiveClient {
|
|
11
|
+
#client: SanityClient | ObservableSanityClient
|
|
12
|
+
constructor(client: SanityClient | ObservableSanityClient) {
|
|
13
|
+
this.#client = client
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
events(): Observable<LiveEventMessage | LiveEventRestart> {
|
|
17
|
+
const path = _getDataUrl(this.#client, 'live/events')
|
|
18
|
+
const url = new URL(this.#client.getUrl(path, false))
|
|
19
|
+
|
|
20
|
+
const listenFor = ['restart', 'message'] as const
|
|
21
|
+
|
|
22
|
+
return new Observable((observer) => {
|
|
23
|
+
let es: InstanceType<typeof EventSource> | undefined
|
|
24
|
+
let reconnectTimer: NodeJS.Timeout
|
|
25
|
+
let stopped = false
|
|
26
|
+
// Unsubscribe differs from stopped in that we will never reopen.
|
|
27
|
+
// Once it is`true`, it will never be `false` again.
|
|
28
|
+
let unsubscribed = false
|
|
29
|
+
|
|
30
|
+
open()
|
|
31
|
+
|
|
32
|
+
// EventSource will emit a regular event if it fails to connect, however the API will emit an `error` MessageEvent if the server goes down
|
|
33
|
+
// So we need to handle both cases
|
|
34
|
+
function onError(evt: MessageEvent | Event) {
|
|
35
|
+
if (stopped) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If the event has a `data` property, then it`s a MessageEvent emitted by the API and we should forward the error and close the connection
|
|
40
|
+
if ('data' in evt) {
|
|
41
|
+
const event = parseEvent(evt)
|
|
42
|
+
observer.error(new Error(event.message, {cause: event}))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Unless we've explicitly stopped the ES (in which case `stopped` should be true),
|
|
46
|
+
// we should never be in a disconnected state. By default, EventSource will reconnect
|
|
47
|
+
// automatically, in which case it sets readyState to `CONNECTING`, but in some cases
|
|
48
|
+
// (like when a laptop lid is closed), it closes the connection. In these cases we need
|
|
49
|
+
// to explicitly reconnect.
|
|
50
|
+
if (es!.readyState === es!.CLOSED) {
|
|
51
|
+
unsubscribe()
|
|
52
|
+
clearTimeout(reconnectTimer)
|
|
53
|
+
reconnectTimer = setTimeout(open, 100)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function onMessage(evt: Any) {
|
|
58
|
+
const event = parseEvent(evt)
|
|
59
|
+
return event instanceof Error ? observer.error(event) : observer.next(event)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function unsubscribe() {
|
|
63
|
+
if (!es) return
|
|
64
|
+
es.removeEventListener('error', onError)
|
|
65
|
+
for (const type of listenFor) {
|
|
66
|
+
es.removeEventListener(type, onMessage)
|
|
67
|
+
}
|
|
68
|
+
es.close()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function getEventSource() {
|
|
72
|
+
const EventSourceImplementation: typeof EventSource =
|
|
73
|
+
typeof EventSource === 'undefined'
|
|
74
|
+
? ((await import('@sanity/eventsource')).default as typeof EventSource)
|
|
75
|
+
: EventSource
|
|
76
|
+
|
|
77
|
+
// If the listener has been unsubscribed from before we managed to load the module,
|
|
78
|
+
// do not set up the EventSource.
|
|
79
|
+
if (unsubscribed) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const evs = new EventSourceImplementation(url.toString())
|
|
84
|
+
evs.addEventListener('error', onError)
|
|
85
|
+
for (const type of listenFor) {
|
|
86
|
+
evs.addEventListener(type, onMessage)
|
|
87
|
+
}
|
|
88
|
+
return evs
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function open() {
|
|
92
|
+
getEventSource()
|
|
93
|
+
.then((eventSource) => {
|
|
94
|
+
if (eventSource) {
|
|
95
|
+
es = eventSource
|
|
96
|
+
// Handle race condition where the observer is unsubscribed before the EventSource is set up
|
|
97
|
+
if (unsubscribed) {
|
|
98
|
+
unsubscribe()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
.catch((reason) => {
|
|
103
|
+
observer.error(reason)
|
|
104
|
+
stop()
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function stop() {
|
|
109
|
+
stopped = true
|
|
110
|
+
unsubscribe()
|
|
111
|
+
unsubscribed = true
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return stop
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function parseEvent(event: MessageEvent) {
|
|
120
|
+
try {
|
|
121
|
+
const data = (event.data && JSON.parse(event.data)) || {}
|
|
122
|
+
return {type: event.type, id: event.lastEventId, ...data}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
return err
|
|
125
|
+
}
|
|
126
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -306,6 +306,8 @@ export interface RequestObservableOptions extends Omit<RequestOptions, 'url'> {
|
|
|
306
306
|
returnQuery?: boolean
|
|
307
307
|
resultSourceMap?: boolean | 'withKeyArraySelector'
|
|
308
308
|
perspective?: ClientPerspective
|
|
309
|
+
/** @alpha this API is experimental and may change or even be removed */
|
|
310
|
+
lastLiveEventId?: string
|
|
309
311
|
}
|
|
310
312
|
|
|
311
313
|
/** @public */
|
|
@@ -479,6 +481,8 @@ export interface QueryParams {
|
|
|
479
481
|
token?: never
|
|
480
482
|
/** @deprecated you're using a fetch option as a GROQ parameter, this is likely a mistake */
|
|
481
483
|
useCdn?: never
|
|
484
|
+
/** @deprecated you're using a fetch option as a GROQ parameter, this is likely a mistake */
|
|
485
|
+
lastLiveEventId?: never
|
|
482
486
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
483
487
|
}
|
|
484
488
|
|
|
@@ -743,6 +747,8 @@ export interface ResponseQueryOptions extends RequestOptions {
|
|
|
743
747
|
// The `cache` and `next` options are specific to the Next.js App Router integration
|
|
744
748
|
cache?: 'next' extends keyof RequestInit ? RequestInit['cache'] : never
|
|
745
749
|
next?: ('next' extends keyof RequestInit ? RequestInit : never)['next']
|
|
750
|
+
/** @alpha this API is experimental and may change or even be removed */
|
|
751
|
+
lastLiveEventId?: string | string[] | null
|
|
746
752
|
}
|
|
747
753
|
|
|
748
754
|
/** @public */
|
|
@@ -785,6 +791,8 @@ export interface RawQueryResponse<R> {
|
|
|
785
791
|
ms: number
|
|
786
792
|
result: R
|
|
787
793
|
resultSourceMap?: ContentSourceMap
|
|
794
|
+
/** @alpha this API is experimental and may change or even be removed */
|
|
795
|
+
syncTags?: SyncTag[]
|
|
788
796
|
}
|
|
789
797
|
|
|
790
798
|
/** @public */
|
|
@@ -999,6 +1007,19 @@ export interface ContentSourceMap {
|
|
|
999
1007
|
paths: ContentSourceMapPaths
|
|
1000
1008
|
}
|
|
1001
1009
|
|
|
1010
|
+
/** @alpha this API is experimental and may change or even be removed */
|
|
1011
|
+
export type SyncTag = `s1:${string}`
|
|
1012
|
+
/** @alpha this API is experimental and may change or even be removed */
|
|
1013
|
+
export interface LiveEventRestart {
|
|
1014
|
+
type: 'restart'
|
|
1015
|
+
}
|
|
1016
|
+
/** @alpha this API is experimental and may change or even be removed */
|
|
1017
|
+
export interface LiveEventMessage {
|
|
1018
|
+
type: 'message'
|
|
1019
|
+
id: string
|
|
1020
|
+
tags: SyncTag[]
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1002
1023
|
export type {
|
|
1003
1024
|
ContentSourceMapParsedPath,
|
|
1004
1025
|
ContentSourceMapParsedPathKeyedSegment,
|