@sanity/client 8.0.0 → 8.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 +94 -0
- package/dist/{browserUpload-CQgx9YYo.js → browserUpload-2tz6Sdqp.js} +4 -3
- package/dist/browserUpload-2tz6Sdqp.js.map +1 -0
- package/dist/{browserUpload-icWlVP15.js → browserUpload-CwpNx7Vl.js} +4 -3
- package/dist/browserUpload-CwpNx7Vl.js.map +1 -0
- package/dist/{config-a8VajuEY.js → config-3wiPP-sZ.js} +2 -2
- package/dist/config-3wiPP-sZ.js.map +1 -0
- package/dist/csm.js +2 -2
- package/dist/csm.js.map +1 -1
- package/dist/index.d.ts +17 -11
- package/dist/index.js +247 -120
- package/dist/index.js.map +1 -1
- package/dist/index.node.d.ts +270 -26
- package/dist/index.node.js +183 -50
- package/dist/index.node.js.map +1 -1
- package/dist/media-library.d.ts +1 -1
- package/dist/{request-CJxcN16k.js → request-BhMuKj0D.js} +10 -9
- package/dist/request-BhMuKj0D.js.map +1 -0
- package/dist/{request-k7VS_NnC.js → request-SnMg7nUX.js} +10 -9
- package/dist/request-SnMg7nUX.js.map +1 -0
- package/dist/{resolveEditInfo-sq7yF78q.js → resolveEditInfo-Cz-smq3a.js} +17 -3
- package/dist/resolveEditInfo-Cz-smq3a.js.map +1 -0
- package/dist/stega.js +1 -1
- package/dist/{stegaEncodeSourceMap-DkoIlutY.js → stegaEncodeSourceMap-DbM2fTN4.js} +8 -2
- package/dist/stegaEncodeSourceMap-DbM2fTN4.js.map +1 -0
- package/dist/{stegaEncodeSourceMap-B2fGArSf.js → stegaEncodeSourceMap-YR3NQ3iz.js} +2 -2
- package/dist/{stegaEncodeSourceMap-B2fGArSf.js.map → stegaEncodeSourceMap-YR3NQ3iz.js.map} +1 -1
- package/dist/{types-CUxZSgB2.d.ts → types-BODIEY7F.d.ts} +262 -24
- package/package.json +26 -11
- package/src/SanityClient.ts +19 -20
- package/src/assets/AssetsClient.ts +54 -5
- package/src/csm/applySourceDocuments.ts +2 -4
- package/src/csm/draftUtils.ts +23 -4
- package/src/data/dataMethods.ts +2 -19
- package/src/data/eventsource.ts +71 -41
- package/src/data/live.ts +17 -9
- package/src/data/resolveEventSourceFetch.ts +9 -1
- package/src/defineCreateClient.ts +5 -1
- package/src/functions/FunctionsClient.ts +66 -0
- package/src/functions/invoke.ts +176 -0
- package/src/http/browserUpload.ts +1 -0
- package/src/http/errors.ts +2 -1
- package/src/http/request.ts +8 -14
- package/src/mediaLibrary/MediaLibraryVideoClient.ts +1 -1
- package/src/types.ts +186 -4
- package/src/validators.ts +1 -1
- package/src/warnings.ts +7 -1
- package/dist/browserUpload-CQgx9YYo.js.map +0 -1
- package/dist/browserUpload-icWlVP15.js.map +0 -1
- package/dist/config-a8VajuEY.js.map +0 -1
- package/dist/request-CJxcN16k.js.map +0 -1
- package/dist/request-k7VS_NnC.js.map +0 -1
- package/dist/resolveEditInfo-sq7yF78q.js.map +0 -1
- package/dist/stegaEncodeSourceMap-DkoIlutY.js.map +0 -1
package/src/SanityClient.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {LiveClient} from './data/live'
|
|
|
10
10
|
import {ObservablePatch, Patch} from './data/patch'
|
|
11
11
|
import {ObservableTransaction, Transaction} from './data/transaction'
|
|
12
12
|
import {DatasetsClient, ObservableDatasetsClient} from './datasets/DatasetsClient'
|
|
13
|
+
import {FunctionsClient, ObservableFunctionsClient} from './functions/FunctionsClient'
|
|
13
14
|
import {
|
|
14
15
|
MediaLibraryVideoClient,
|
|
15
16
|
ObservableMediaLibraryVideoClient,
|
|
@@ -81,6 +82,7 @@ export class ObservableSanityClient {
|
|
|
81
82
|
agent: {
|
|
82
83
|
action: ObservableAgentsActionClient
|
|
83
84
|
}
|
|
85
|
+
functions: ObservableFunctionsClient
|
|
84
86
|
releases: ObservableReleasesClient
|
|
85
87
|
|
|
86
88
|
/**
|
|
@@ -110,6 +112,7 @@ export class ObservableSanityClient {
|
|
|
110
112
|
this.agent = {
|
|
111
113
|
action: new ObservableAgentsActionClient(this, this.#httpRequest),
|
|
112
114
|
}
|
|
115
|
+
this.functions = new ObservableFunctionsClient(this, this.#httpRequest)
|
|
113
116
|
this.releases = new ObservableReleasesClient(this, this.#httpRequest)
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -707,15 +710,12 @@ export class ObservableSanityClient {
|
|
|
707
710
|
delete(id: string, options: AllDocumentIdsMutationOptions): Observable<MultipleMutationResult>
|
|
708
711
|
/**
|
|
709
712
|
* Deletes a document with the given document ID.
|
|
710
|
-
* Returns an observable that resolves to the deleted document.
|
|
713
|
+
* Returns an observable that resolves to a mutation result object containing the deleted document ID.
|
|
711
714
|
*
|
|
712
715
|
* @param id - Document ID to delete
|
|
713
716
|
* @param options - Options for the mutation
|
|
714
717
|
*/
|
|
715
|
-
delete
|
|
716
|
-
id: string,
|
|
717
|
-
options?: BaseMutationOptions,
|
|
718
|
-
): Observable<SanityDocument<R>>
|
|
718
|
+
delete(id: string, options?: BaseMutationOptions): Observable<MultipleMutationResult>
|
|
719
719
|
/**
|
|
720
720
|
* Deletes one or more documents matching the given query or document ID.
|
|
721
721
|
* Returns an observable that resolves to first deleted document.
|
|
@@ -762,15 +762,15 @@ export class ObservableSanityClient {
|
|
|
762
762
|
): Observable<MultipleMutationResult>
|
|
763
763
|
/**
|
|
764
764
|
* Deletes one or more documents matching the given query or document ID.
|
|
765
|
-
* Returns an observable that resolves to
|
|
765
|
+
* Returns an observable that resolves to a mutation result object containing the document IDs that were deleted.
|
|
766
766
|
*
|
|
767
767
|
* @param selection - An object with either an `id` or `query` key defining what to delete
|
|
768
768
|
* @param options - Options for the mutation
|
|
769
769
|
*/
|
|
770
|
-
delete
|
|
770
|
+
delete(
|
|
771
771
|
selection: MutationSelection,
|
|
772
772
|
options?: BaseMutationOptions,
|
|
773
|
-
): Observable<
|
|
773
|
+
): Observable<MultipleMutationResult>
|
|
774
774
|
delete<R extends Record<string, Any> = Record<string, Any>>(
|
|
775
775
|
selection: string | MutationSelection,
|
|
776
776
|
options?:
|
|
@@ -1020,7 +1020,7 @@ export class ObservableSanityClient {
|
|
|
1020
1020
|
): Observable<MultipleMutationResult>
|
|
1021
1021
|
/**
|
|
1022
1022
|
* Perform mutation operations against the configured dataset
|
|
1023
|
-
* Returns an observable that resolves to the
|
|
1023
|
+
* Returns an observable that resolves to a mutation result object containing the mutated document IDs.
|
|
1024
1024
|
*
|
|
1025
1025
|
* @param operations - Mutation operations to execute
|
|
1026
1026
|
* @param options - Mutation options
|
|
@@ -1028,7 +1028,7 @@ export class ObservableSanityClient {
|
|
|
1028
1028
|
mutate<R extends Record<string, Any> = Record<string, Any>>(
|
|
1029
1029
|
operations: Mutation<R>[] | ObservablePatch | ObservableTransaction,
|
|
1030
1030
|
options?: BaseMutationOptions,
|
|
1031
|
-
): Observable<
|
|
1031
|
+
): Observable<MultipleMutationResult>
|
|
1032
1032
|
mutate<R extends Record<string, Any> = Record<string, Any>>(
|
|
1033
1033
|
operations: Mutation<R>[] | ObservablePatch | ObservableTransaction,
|
|
1034
1034
|
options?:
|
|
@@ -1148,6 +1148,7 @@ export class SanityClient {
|
|
|
1148
1148
|
agent: {
|
|
1149
1149
|
action: AgentActionsClient
|
|
1150
1150
|
}
|
|
1151
|
+
functions: FunctionsClient
|
|
1151
1152
|
releases: ReleasesClient
|
|
1152
1153
|
|
|
1153
1154
|
/**
|
|
@@ -1182,6 +1183,7 @@ export class SanityClient {
|
|
|
1182
1183
|
this.agent = {
|
|
1183
1184
|
action: new AgentActionsClient(this, this.#httpRequest),
|
|
1184
1185
|
}
|
|
1186
|
+
this.functions = new FunctionsClient(this, this.#httpRequest)
|
|
1185
1187
|
this.releases = new ReleasesClient(this, this.#httpRequest)
|
|
1186
1188
|
|
|
1187
1189
|
this.observable = new ObservableSanityClient(httpRequest, config)
|
|
@@ -1773,15 +1775,12 @@ export class SanityClient {
|
|
|
1773
1775
|
delete(id: string, options: AllDocumentIdsMutationOptions): Promise<MultipleMutationResult>
|
|
1774
1776
|
/**
|
|
1775
1777
|
* Deletes a document with the given document ID.
|
|
1776
|
-
* Returns a promise that resolves to the deleted document.
|
|
1778
|
+
* Returns a promise that resolves to a mutation result object containing the deleted document ID.
|
|
1777
1779
|
*
|
|
1778
1780
|
* @param id - Document ID to delete
|
|
1779
1781
|
* @param options - Options for the mutation
|
|
1780
1782
|
*/
|
|
1781
|
-
delete
|
|
1782
|
-
id: string,
|
|
1783
|
-
options?: BaseMutationOptions,
|
|
1784
|
-
): Promise<SanityDocument<R>>
|
|
1783
|
+
delete(id: string, options?: BaseMutationOptions): Promise<MultipleMutationResult>
|
|
1785
1784
|
/**
|
|
1786
1785
|
* Deletes one or more documents matching the given query or document ID.
|
|
1787
1786
|
* Returns a promise that resolves to first deleted document.
|
|
@@ -1828,15 +1827,15 @@ export class SanityClient {
|
|
|
1828
1827
|
): Promise<MultipleMutationResult>
|
|
1829
1828
|
/**
|
|
1830
1829
|
* Deletes one or more documents matching the given query or document ID.
|
|
1831
|
-
* Returns a promise that resolves to
|
|
1830
|
+
* Returns a promise that resolves to a mutation result object containing the document IDs that were deleted.
|
|
1832
1831
|
*
|
|
1833
1832
|
* @param selection - An object with either an `id` or `query` key defining what to delete
|
|
1834
1833
|
* @param options - Options for the mutation
|
|
1835
1834
|
*/
|
|
1836
|
-
delete
|
|
1835
|
+
delete(
|
|
1837
1836
|
selection: MutationSelection,
|
|
1838
1837
|
options?: BaseMutationOptions,
|
|
1839
|
-
): Promise<
|
|
1838
|
+
): Promise<MultipleMutationResult>
|
|
1840
1839
|
delete<R extends Record<string, Any> = Record<string, Any>>(
|
|
1841
1840
|
selection: string | MutationSelection,
|
|
1842
1841
|
options?:
|
|
@@ -2070,7 +2069,7 @@ export class SanityClient {
|
|
|
2070
2069
|
): Promise<MultipleMutationResult>
|
|
2071
2070
|
/**
|
|
2072
2071
|
* Perform mutation operations against the configured dataset
|
|
2073
|
-
* Returns a promise that resolves to the
|
|
2072
|
+
* Returns a promise that resolves to a mutation result object containing the mutated document IDs.
|
|
2074
2073
|
*
|
|
2075
2074
|
* @param operations - Mutation operations to execute
|
|
2076
2075
|
* @param options - Mutation options
|
|
@@ -2078,7 +2077,7 @@ export class SanityClient {
|
|
|
2078
2077
|
mutate<R extends Record<string, Any> = Record<string, Any>>(
|
|
2079
2078
|
operations: Mutation<R>[] | Patch | Transaction,
|
|
2080
2079
|
options?: BaseMutationOptions,
|
|
2081
|
-
): Promise<
|
|
2080
|
+
): Promise<MultipleMutationResult>
|
|
2082
2081
|
mutate<R extends Record<string, Any> = Record<string, Any>>(
|
|
2083
2082
|
operations: Mutation<R>[] | Patch | Transaction,
|
|
2084
2083
|
options?:
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
Any,
|
|
9
9
|
HttpRequest,
|
|
10
10
|
InitializedClientConfig,
|
|
11
|
+
MediaLibraryAssetDocument,
|
|
11
12
|
SanityAssetDocument,
|
|
12
13
|
SanityImageAssetDocument,
|
|
13
14
|
UploadBody,
|
|
@@ -67,7 +68,12 @@ export class ObservableAssetsClient {
|
|
|
67
68
|
assetType: 'file' | 'image',
|
|
68
69
|
body: UploadBody,
|
|
69
70
|
options?: UploadClientConfig,
|
|
70
|
-
): Observable<
|
|
71
|
+
): Observable<
|
|
72
|
+
UploadEvent<
|
|
73
|
+
| {document: SanityAssetDocument | SanityImageAssetDocument}
|
|
74
|
+
| {asset: MediaLibraryAssetDocument}
|
|
75
|
+
>
|
|
76
|
+
> {
|
|
71
77
|
return _upload(this.#client, this.#httpRequest, assetType, body, options)
|
|
72
78
|
}
|
|
73
79
|
}
|
|
@@ -84,6 +90,16 @@ export class AssetsClient {
|
|
|
84
90
|
/**
|
|
85
91
|
* Uploads a file asset to the configured dataset
|
|
86
92
|
*
|
|
93
|
+
* Note: when the client is configured against a Media Library
|
|
94
|
+
* (`resource: {type: 'media-library', id}`), this resolves to a
|
|
95
|
+
* {@link MediaLibraryAssetDocument} at runtime, not to a
|
|
96
|
+
* {@link SanityAssetDocument}. The declared type cannot express that: the
|
|
97
|
+
* shape depends on the client's configuration rather than on the arguments,
|
|
98
|
+
* so an overload cannot discriminate it, and widening the return type into a
|
|
99
|
+
* union would be a breaking change for every existing caller. Narrow the
|
|
100
|
+
* result yourself (for example, check for `currentVersion`) if you upload to
|
|
101
|
+
* a Media Library. Typing this honestly is deferred to the next major.
|
|
102
|
+
*
|
|
87
103
|
* @param assetType - Asset type (file)
|
|
88
104
|
* @param body - Asset content - can be a browser File instance, a Blob, a Node.js Buffer instance or a Node.js ReadableStream.
|
|
89
105
|
* @param options - Options to use for the upload
|
|
@@ -96,6 +112,10 @@ export class AssetsClient {
|
|
|
96
112
|
/**
|
|
97
113
|
* Uploads an image asset to the configured dataset
|
|
98
114
|
*
|
|
115
|
+
* Note: against a Media Library this resolves to a
|
|
116
|
+
* {@link MediaLibraryAssetDocument} at runtime. See the `'file'` overload
|
|
117
|
+
* above for why the declared type cannot say so.
|
|
118
|
+
*
|
|
99
119
|
* @param assetType - Asset type (image)
|
|
100
120
|
* @param body - Asset content - can be a browser File instance, a Blob, a Node.js Buffer instance or a Node.js ReadableStream.
|
|
101
121
|
* @param options - Options to use for the upload
|
|
@@ -121,19 +141,48 @@ export class AssetsClient {
|
|
|
121
141
|
assetType: 'file' | 'image',
|
|
122
142
|
body: UploadBody,
|
|
123
143
|
options?: UploadClientConfig,
|
|
124
|
-
): Promise<SanityAssetDocument | SanityImageAssetDocument> {
|
|
125
|
-
type Doc =
|
|
144
|
+
): Promise<SanityAssetDocument | SanityImageAssetDocument | MediaLibraryAssetDocument> {
|
|
145
|
+
type Doc =
|
|
146
|
+
| {document: SanityAssetDocument | SanityImageAssetDocument}
|
|
147
|
+
| {asset: MediaLibraryAssetDocument}
|
|
126
148
|
const observable = _upload<Doc>(this.#client, this.#httpRequest, assetType, body, options)
|
|
127
149
|
return lastValueFrom(
|
|
128
150
|
observable.pipe(
|
|
129
151
|
filter((event): event is UploadResponseEvent<Doc> => event.type === 'response'),
|
|
130
|
-
map((event) => event.body
|
|
152
|
+
map((event) => pluckUploadedAsset(event.body)),
|
|
131
153
|
),
|
|
132
154
|
)
|
|
133
155
|
}
|
|
134
156
|
}
|
|
135
157
|
|
|
136
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Content Lake's upload endpoint responds with `{document: ...}`; the Media
|
|
160
|
+
* Library upload endpoint responds with `{asset: ...}` instead (a
|
|
161
|
+
* `sanity.asset` document, not a Content Lake asset document). Narrowing on
|
|
162
|
+
* the response body itself - rather than on the client's `resource` config -
|
|
163
|
+
* keeps this correct regardless of how the two are ever wired together.
|
|
164
|
+
*/
|
|
165
|
+
function isMediaLibraryUploadBody(
|
|
166
|
+
body:
|
|
167
|
+
| {document: SanityAssetDocument | SanityImageAssetDocument}
|
|
168
|
+
| {asset: MediaLibraryAssetDocument},
|
|
169
|
+
): body is {asset: MediaLibraryAssetDocument} {
|
|
170
|
+
return 'asset' in body
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function pluckUploadedAsset(
|
|
174
|
+
body:
|
|
175
|
+
| {document: SanityAssetDocument | SanityImageAssetDocument}
|
|
176
|
+
| {asset: MediaLibraryAssetDocument},
|
|
177
|
+
): SanityAssetDocument | SanityImageAssetDocument | MediaLibraryAssetDocument {
|
|
178
|
+
return isMediaLibraryUploadBody(body) ? body.asset : body.document
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function _upload<
|
|
182
|
+
T =
|
|
183
|
+
| {document: SanityAssetDocument | SanityImageAssetDocument}
|
|
184
|
+
| {asset: MediaLibraryAssetDocument},
|
|
185
|
+
>(
|
|
137
186
|
client: SanityClient | ObservableSanityClient,
|
|
138
187
|
_httpRequest: HttpRequest,
|
|
139
188
|
assetType: 'image' | 'file',
|
|
@@ -3,12 +3,10 @@ import {parseJsonPath} from './jsonPath'
|
|
|
3
3
|
import {resolveMapping} from './resolveMapping'
|
|
4
4
|
import * as paths from './studioPath'
|
|
5
5
|
import type {
|
|
6
|
-
Any,
|
|
7
6
|
ApplySourceDocumentsUpdateFunction,
|
|
8
7
|
ClientPerspective,
|
|
9
8
|
ContentSourceMap,
|
|
10
9
|
ContentSourceMapDocuments,
|
|
11
|
-
Path,
|
|
12
10
|
SanityDocument,
|
|
13
11
|
} from './types'
|
|
14
12
|
import {walkMap} from './walkMap'
|
|
@@ -55,7 +53,7 @@ export function applySourceDocuments<Result = unknown>(
|
|
|
55
53
|
|
|
56
54
|
if (sourceDocument) {
|
|
57
55
|
const parsedPath = parseJsonPath(sourcePath + pathSuffix)
|
|
58
|
-
const stringifiedPath = paths.toString(parsedPath
|
|
56
|
+
const stringifiedPath = paths.toString(parsedPath)
|
|
59
57
|
const cachedDocument = cachedDocuments[mapping.source.document]
|
|
60
58
|
|
|
61
59
|
if (!cachedDocument) {
|
|
@@ -67,7 +65,7 @@ export function applySourceDocuments<Result = unknown>(
|
|
|
67
65
|
: value
|
|
68
66
|
return value === changedValue
|
|
69
67
|
? value
|
|
70
|
-
: updateFn<Result[keyof Result]>(changedValue as
|
|
68
|
+
: updateFn<Result[keyof Result]>(changedValue as Result[keyof Result], {
|
|
71
69
|
cachedDocument,
|
|
72
70
|
previousValue: value as Result[keyof Result],
|
|
73
71
|
sourceDocument,
|
package/src/csm/draftUtils.ts
CHANGED
|
@@ -32,11 +32,24 @@ export function isPublishedId(id: string): id is PublishedId {
|
|
|
32
32
|
return !isDraftId(id) && !isVersionId(id)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* A phantom brand like `DraftId` has no runtime representation, so it can never be produced
|
|
37
|
+
* by narrowing a string - there's nothing to check. These two functions are the only places
|
|
38
|
+
* allowed to assert a plain string into a branded id.
|
|
39
|
+
*/
|
|
40
|
+
function asDraftId(value: string): DraftId {
|
|
41
|
+
return value as DraftId
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function asPublishedId(value: string): PublishedId {
|
|
45
|
+
return value as PublishedId
|
|
46
|
+
}
|
|
47
|
+
|
|
35
48
|
/** @internal */
|
|
36
49
|
export function getDraftId(id: string): DraftId {
|
|
37
50
|
if (isVersionId(id)) {
|
|
38
51
|
const publishedId = getPublishedId(id)
|
|
39
|
-
return (DRAFTS_PREFIX + publishedId)
|
|
52
|
+
return asDraftId(DRAFTS_PREFIX + publishedId)
|
|
40
53
|
}
|
|
41
54
|
|
|
42
55
|
return isDraftId(id) ? id : ((DRAFTS_PREFIX + id) as DraftId)
|
|
@@ -69,12 +82,18 @@ export function getVersionFromId(id: string): string | undefined {
|
|
|
69
82
|
export function getPublishedId(id: string): PublishedId {
|
|
70
83
|
if (isVersionId(id)) {
|
|
71
84
|
// make sure to only remove the versions prefix and the bundle name
|
|
72
|
-
return id.split(PATH_SEPARATOR).slice(2).join(PATH_SEPARATOR)
|
|
85
|
+
return asPublishedId(id.split(PATH_SEPARATOR).slice(2).join(PATH_SEPARATOR))
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
if (isDraftId(id)) {
|
|
76
|
-
return id.slice(DRAFTS_PREFIX.length)
|
|
89
|
+
return asPublishedId(id.slice(DRAFTS_PREFIX.length))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (isPublishedId(id)) {
|
|
93
|
+
return id
|
|
77
94
|
}
|
|
78
95
|
|
|
79
|
-
|
|
96
|
+
// Unreachable: `isPublishedId` is defined as `!isDraftId(id) && !isVersionId(id)`, both of
|
|
97
|
+
// which were already checked (and found false) above, so this can never execute.
|
|
98
|
+
throw new Error(`Unable to resolve a published id from "${id}"`)
|
|
80
99
|
}
|
package/src/data/dataMethods.ts
CHANGED
|
@@ -667,23 +667,6 @@ function _mapDataResponse(
|
|
|
667
667
|
/**
|
|
668
668
|
* @internal
|
|
669
669
|
*/
|
|
670
|
-
export function _dataRequestObservable(
|
|
671
|
-
client: Client,
|
|
672
|
-
httpRequest: HttpRequest,
|
|
673
|
-
endpoint: string,
|
|
674
|
-
body: Any,
|
|
675
|
-
options: Any = {},
|
|
676
|
-
): Observable<Any> {
|
|
677
|
-
return _observe(options.signal, (signal) =>
|
|
678
|
-
_dataRequest(client, httpRequest, endpoint, body, {...options, signal}),
|
|
679
|
-
)
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
/**
|
|
683
|
-
* Promise-based sibling of {@link _dataRequestObservable}.
|
|
684
|
-
*
|
|
685
|
-
* @internal
|
|
686
|
-
*/
|
|
687
670
|
export function _dataRequest(
|
|
688
671
|
client: Client,
|
|
689
672
|
httpRequest: HttpRequest,
|
|
@@ -1139,7 +1122,7 @@ export function _prepareRequest(client: Client, options: RequestObservableOption
|
|
|
1139
1122
|
*
|
|
1140
1123
|
* @internal
|
|
1141
1124
|
*/
|
|
1142
|
-
|
|
1125
|
+
function _observe<R>(
|
|
1143
1126
|
userSignal: AbortSignal | undefined,
|
|
1144
1127
|
run: (signal: AbortSignal) => Promise<R>,
|
|
1145
1128
|
): Observable<R> {
|
|
@@ -1171,7 +1154,7 @@ export function _observe<R>(
|
|
|
1171
1154
|
*/
|
|
1172
1155
|
export function _request<R>(client: Client, httpRequest: HttpRequest, options: Any): Promise<R> {
|
|
1173
1156
|
const reqOptions = _prepareRequest(client, options)
|
|
1174
|
-
return httpRequest(reqOptions).then((body) => body as R)
|
|
1157
|
+
return httpRequest(reqOptions, client.config().requestHandler).then((body) => body as R)
|
|
1175
1158
|
}
|
|
1176
1159
|
|
|
1177
1160
|
/**
|
package/src/data/eventsource.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import type {ErrorEvent, EventSourceConstructor} from 'eventsource'
|
|
1
2
|
import {defer, isObservable, mergeMap, Observable, of} from 'rxjs'
|
|
2
3
|
|
|
3
4
|
import {formatQueryParseError, isQueryParseError} from '../http/errors'
|
|
4
|
-
import {
|
|
5
|
+
import {isRecord} from '../util/isRecord'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
* @public
|
|
8
8
|
* Thrown when the EventSource connection could not be established, or was rejected by the server.
|
|
9
9
|
* Transient failures (network drops, 5xx, 408, 429) are reconnected internally and emitted as
|
|
10
10
|
* `reconnect` events; a permanent rejection (any other 4xx, eg an expired token) errors the
|
|
11
11
|
* stream with this class so consumers can react — check `status` for the rejection code.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
12
14
|
*/
|
|
13
15
|
export class ConnectionFailedError extends Error {
|
|
14
16
|
readonly name = 'ConnectionFailedError'
|
|
@@ -28,8 +30,9 @@ export class ConnectionFailedError extends Error {
|
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* The listener has been told to explicitly disconnect.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
+
* This is a rare situation, but may occur if the API knows reconnect attempts will fail,
|
|
34
|
+
* eg in the case of a deleted dataset, a blocked project or similar events.
|
|
35
|
+
*
|
|
33
36
|
* @public
|
|
34
37
|
*/
|
|
35
38
|
export class DisconnectError extends Error {
|
|
@@ -42,8 +45,9 @@ export class DisconnectError extends Error {
|
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
/**
|
|
45
|
-
* @public
|
|
46
48
|
* The server sent a `channelError` message. Usually indicative of a bad or malformed request
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
47
51
|
*/
|
|
48
52
|
export class ChannelError extends Error {
|
|
49
53
|
readonly name = 'ChannelError'
|
|
@@ -55,8 +59,9 @@ export class ChannelError extends Error {
|
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
|
-
* @public
|
|
59
62
|
* The server sent an `error`-event to tell the client that an unexpected error has happened.
|
|
63
|
+
*
|
|
64
|
+
* @public
|
|
60
65
|
*/
|
|
61
66
|
export class MessageError extends Error {
|
|
62
67
|
readonly name = 'MessageError'
|
|
@@ -68,8 +73,9 @@ export class MessageError extends Error {
|
|
|
68
73
|
}
|
|
69
74
|
|
|
70
75
|
/**
|
|
71
|
-
* @public
|
|
72
76
|
* An error occurred while parsing the message sent by the server as JSON. Should normally not happen.
|
|
77
|
+
*
|
|
78
|
+
* @public
|
|
73
79
|
*/
|
|
74
80
|
export class MessageParseError extends Error {
|
|
75
81
|
readonly name = 'MessageParseError'
|
|
@@ -95,7 +101,7 @@ export type EventSourceEvent<Name extends string> = ServerSentEvent<Name>
|
|
|
95
101
|
/**
|
|
96
102
|
* @internal
|
|
97
103
|
*/
|
|
98
|
-
export type EventSourceInstance = InstanceType<
|
|
104
|
+
export type EventSourceInstance = InstanceType<EventSourceConstructor>
|
|
99
105
|
|
|
100
106
|
/**
|
|
101
107
|
* Sanity API specific EventSource handler shared between the listen and live APIs
|
|
@@ -123,9 +129,7 @@ export function connectEventSource<EventName extends string>(
|
|
|
123
129
|
return defer(() => {
|
|
124
130
|
const es = initEventSource()
|
|
125
131
|
return isObservable(es) ? es : of(es)
|
|
126
|
-
}).pipe(mergeMap((es) => connectWithESInstance(es, events)))
|
|
127
|
-
ServerSentEvent<EventName>
|
|
128
|
-
>
|
|
132
|
+
}).pipe(mergeMap((es) => connectWithESInstance(es, events)))
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
/**
|
|
@@ -140,21 +144,30 @@ function connectWithESInstance<EventTypeName extends string>(
|
|
|
140
144
|
events: EventTypeName[],
|
|
141
145
|
) {
|
|
142
146
|
return new Observable<EventSourceEvent<EventTypeName>>((observer) => {
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
// Events actually requested by the caller. Backs `isRequestedEvent`, the type
|
|
148
|
+
// guard used below to narrow plain strings (eg `message.type`) to `EventTypeName`
|
|
149
|
+
// without a cast.
|
|
150
|
+
const requestedEvents = new Set<string>(events)
|
|
151
|
+
const isRequestedEvent = (type: string): type is EventTypeName => requestedEvents.has(type)
|
|
152
|
+
const emitOpen = isRequestedEvent('open')
|
|
145
153
|
|
|
146
154
|
// EventSource will emit a regular Event if it fails to connect, however the API may also emit an `error` MessageEvent
|
|
147
155
|
// So we need to handle both cases
|
|
148
|
-
function onError(evt:
|
|
156
|
+
function onError(evt: ErrorEvent | MessageEvent) {
|
|
149
157
|
// If the event has a `data` property, then it`s a MessageEvent emitted by the API and we should forward the error
|
|
150
158
|
if ('data' in evt) {
|
|
151
|
-
const [parseError, event] = parseEvent(evt
|
|
159
|
+
const [parseError, event] = parseEvent(evt)
|
|
152
160
|
observer.error(
|
|
153
|
-
parseError
|
|
161
|
+
parseError || !event
|
|
154
162
|
? new MessageParseError('Unable to parse EventSource error message', {
|
|
155
163
|
cause: parseError,
|
|
156
164
|
})
|
|
157
|
-
: new MessageError(
|
|
165
|
+
: new MessageError(
|
|
166
|
+
isRecord(event.data) && typeof event.data.message === 'string'
|
|
167
|
+
? event.data.message
|
|
168
|
+
: '',
|
|
169
|
+
event,
|
|
170
|
+
),
|
|
158
171
|
)
|
|
159
172
|
return
|
|
160
173
|
}
|
|
@@ -169,29 +182,35 @@ function connectWithESInstance<EventTypeName extends string>(
|
|
|
169
182
|
// regardless of readyState — implementations disagree on whether the connection
|
|
170
183
|
// closes before or after the error event is dispatched — and let
|
|
171
184
|
// `reconnectOnConnectionFailure` classify it (4xx fatal, otherwise retried).
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
185
|
+
if (evt.code !== undefined) {
|
|
186
|
+
observer.error(
|
|
187
|
+
new ConnectionFailedError('EventSource connection failed', {status: evt.code}),
|
|
188
|
+
)
|
|
176
189
|
return
|
|
177
190
|
}
|
|
178
191
|
|
|
179
192
|
if (es.readyState === es.CLOSED) {
|
|
180
193
|
// In these cases we'll signal to consumers (via the error path) that a retry/reconnect is needed.
|
|
181
194
|
observer.error(new ConnectionFailedError('EventSource connection failed'))
|
|
182
|
-
} else
|
|
183
|
-
|
|
195
|
+
} else {
|
|
196
|
+
const type = 'reconnect'
|
|
197
|
+
if (isRequestedEvent(type)) {
|
|
198
|
+
observer.next({type})
|
|
199
|
+
}
|
|
184
200
|
}
|
|
185
201
|
}
|
|
186
202
|
|
|
187
203
|
function onOpen() {
|
|
188
204
|
// The open event of the EventSource API is fired when a connection with an event source is opened.
|
|
189
|
-
|
|
205
|
+
const type = 'open'
|
|
206
|
+
if (isRequestedEvent(type)) {
|
|
207
|
+
observer.next({type})
|
|
208
|
+
}
|
|
190
209
|
}
|
|
191
210
|
|
|
192
211
|
function onMessage(message: MessageEvent) {
|
|
193
212
|
const [parseError, event] = parseEvent(message)
|
|
194
|
-
if (parseError) {
|
|
213
|
+
if (parseError || !event) {
|
|
195
214
|
observer.error(
|
|
196
215
|
new MessageParseError('Unable to parse EventSource message', {cause: parseError}),
|
|
197
216
|
)
|
|
@@ -212,17 +231,25 @@ function connectWithESInstance<EventTypeName extends string>(
|
|
|
212
231
|
observer.error(
|
|
213
232
|
new DisconnectError(
|
|
214
233
|
`Server disconnected client: ${
|
|
215
|
-
(event.data
|
|
234
|
+
(isRecord(event.data) &&
|
|
235
|
+
typeof event.data.reason === 'string' &&
|
|
236
|
+
event.data.reason) ||
|
|
237
|
+
'unknown error'
|
|
216
238
|
}`,
|
|
217
239
|
),
|
|
218
240
|
)
|
|
219
241
|
return
|
|
220
242
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
243
|
+
// `onMessage` is only ever registered for `REQUIRED_EVENTS` (handled above, and always
|
|
244
|
+
// returned from before reaching here) and the caller-requested `events` (see
|
|
245
|
+
// `cleanedEvents` below), so `message.type` is guaranteed to be a requested event here.
|
|
246
|
+
if (isRequestedEvent(message.type)) {
|
|
247
|
+
observer.next({
|
|
248
|
+
type: message.type,
|
|
249
|
+
id: message.lastEventId,
|
|
250
|
+
...(event.data ? {data: event.data} : {}),
|
|
251
|
+
})
|
|
252
|
+
}
|
|
226
253
|
}
|
|
227
254
|
|
|
228
255
|
es.addEventListener('error', onError)
|
|
@@ -251,7 +278,7 @@ function connectWithESInstance<EventTypeName extends string>(
|
|
|
251
278
|
|
|
252
279
|
function parseEvent(
|
|
253
280
|
message: MessageEvent,
|
|
254
|
-
): [null, {type: string; id: string; data?: unknown}] | [
|
|
281
|
+
): [null, {type: string; id: string; data?: unknown}] | [unknown, null] {
|
|
255
282
|
try {
|
|
256
283
|
const data = typeof message.data === 'string' && JSON.parse(message.data)
|
|
257
284
|
return [
|
|
@@ -263,23 +290,26 @@ function parseEvent(
|
|
|
263
290
|
},
|
|
264
291
|
]
|
|
265
292
|
} catch (err) {
|
|
266
|
-
return [err
|
|
293
|
+
return [err, null]
|
|
267
294
|
}
|
|
268
295
|
}
|
|
269
296
|
|
|
270
|
-
function extractErrorMessage(err:
|
|
271
|
-
const error = err.error
|
|
297
|
+
function extractErrorMessage(err: unknown, tag?: string | null): string {
|
|
298
|
+
const error = isRecord(err) ? err.error : undefined
|
|
272
299
|
|
|
273
300
|
if (!error) {
|
|
274
|
-
|
|
301
|
+
const message = isRecord(err) ? err.message : undefined
|
|
302
|
+
return (typeof message === 'string' && message) || 'Unknown listener error'
|
|
275
303
|
}
|
|
276
304
|
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
305
|
+
if (isRecord(error)) {
|
|
306
|
+
if (isQueryParseError(error)) {
|
|
307
|
+
return formatQueryParseError(error, tag)
|
|
308
|
+
}
|
|
280
309
|
|
|
281
|
-
|
|
282
|
-
|
|
310
|
+
if (typeof error.description === 'string') {
|
|
311
|
+
return error.description
|
|
312
|
+
}
|
|
283
313
|
}
|
|
284
314
|
|
|
285
315
|
return typeof error === 'string' ? error : JSON.stringify(error, null, 2)
|