@sanity/client 7.26.2 → 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.
Files changed (133) hide show
  1. package/README.md +63 -341
  2. package/dist/browserUpload-2tz6Sdqp.js +87 -0
  3. package/dist/browserUpload-2tz6Sdqp.js.map +1 -0
  4. package/dist/browserUpload-CwpNx7Vl.js +87 -0
  5. package/dist/browserUpload-CwpNx7Vl.js.map +1 -0
  6. package/dist/config-3wiPP-sZ.js +126 -0
  7. package/dist/config-3wiPP-sZ.js.map +1 -0
  8. package/dist/csm.d.ts +122 -335
  9. package/dist/csm.js +79 -91
  10. package/dist/csm.js.map +1 -1
  11. package/dist/dist-C9ExSk2R.js +73 -0
  12. package/dist/dist-C9ExSk2R.js.map +1 -0
  13. package/dist/dist-Z8cIRxoB.js +69 -0
  14. package/dist/dist-Z8cIRxoB.js.map +1 -0
  15. package/dist/index.d.ts +112 -6722
  16. package/dist/index.js +2950 -2848
  17. package/dist/index.js.map +1 -1
  18. package/dist/{index.d.cts → index.node.d.ts} +4529 -5322
  19. package/dist/index.node.js +3264 -0
  20. package/dist/index.node.js.map +1 -0
  21. package/dist/isRecord-Kfmt-nk-.js +7 -0
  22. package/dist/isRecord-Kfmt-nk-.js.map +1 -0
  23. package/dist/media-library.d.ts +9 -100
  24. package/dist/media-library.js +36 -14
  25. package/dist/media-library.js.map +1 -1
  26. package/dist/request-BhMuKj0D.js +373 -0
  27. package/dist/request-BhMuKj0D.js.map +1 -0
  28. package/dist/request-SnMg7nUX.js +355 -0
  29. package/dist/request-SnMg7nUX.js.map +1 -0
  30. package/dist/resolveEditInfo-Cz-smq3a.js +339 -0
  31. package/dist/resolveEditInfo-Cz-smq3a.js.map +1 -0
  32. package/dist/rolldown-runtime-vyAXikos.js +9 -0
  33. package/dist/stega.d.ts +78 -7137
  34. package/dist/stega.js +27 -20
  35. package/dist/stega.js.map +1 -1
  36. package/dist/stegaClean-C18wLWau.js +21 -0
  37. package/dist/stegaClean-C18wLWau.js.map +1 -0
  38. package/dist/stegaEncodeSourceMap-DbM2fTN4.js +373 -0
  39. package/dist/stegaEncodeSourceMap-DbM2fTN4.js.map +1 -0
  40. package/dist/stegaEncodeSourceMap-YR3NQ3iz.js +163 -0
  41. package/dist/stegaEncodeSourceMap-YR3NQ3iz.js.map +1 -0
  42. package/dist/{index.browser.d.cts → types-BODIEY7F.d.ts} +4196 -5258
  43. package/dist/types-CfGzbXrl.d.ts +108 -0
  44. package/package.json +80 -142
  45. package/src/SanityClient.ts +101 -137
  46. package/src/agent/actions/AgentActionsClient.ts +12 -12
  47. package/src/agent/actions/commonTypes.ts +2 -1
  48. package/src/agent/actions/generate.ts +26 -6
  49. package/src/agent/actions/patch.ts +27 -7
  50. package/src/agent/actions/prompt.ts +19 -5
  51. package/src/agent/actions/transform.ts +23 -5
  52. package/src/agent/actions/translate.ts +23 -5
  53. package/src/assets/AssetsClient.ts +121 -24
  54. package/src/csm/applySourceDocuments.ts +5 -5
  55. package/src/csm/createEditUrl.ts +1 -1
  56. package/src/csm/draftUtils.ts +23 -5
  57. package/src/csm/index.ts +2 -2
  58. package/src/csm/studioPath.ts +2 -1
  59. package/src/csm/types.ts +8 -2
  60. package/src/data/dataMethods.ts +784 -349
  61. package/src/data/eventsource.ts +82 -49
  62. package/src/data/listen.ts +18 -23
  63. package/src/data/live.ts +60 -35
  64. package/src/data/resolveEventSourceFetch.ts +104 -0
  65. package/src/datasets/DatasetsClient.ts +58 -41
  66. package/src/defineCreateClient.ts +38 -22
  67. package/src/functions/FunctionsClient.ts +66 -0
  68. package/src/functions/invoke.ts +176 -0
  69. package/src/http/browserMiddleware.ts +7 -1
  70. package/src/http/browserUpload.ts +163 -0
  71. package/src/http/errors.ts +61 -6
  72. package/src/http/nodeMiddleware.ts +94 -30
  73. package/src/http/request.ts +304 -87
  74. package/src/http/requestOptions.ts +131 -15
  75. package/src/{index.browser.ts → index.node.ts} +9 -4
  76. package/src/index.ts +4 -3
  77. package/src/mediaLibrary/MediaLibraryVideoClient.ts +27 -12
  78. package/src/projects/ProjectsClient.ts +18 -16
  79. package/src/releases/ReleasesClient.ts +38 -33
  80. package/src/stega/index.ts +1 -2
  81. package/src/stega/stegaEncodeSourceMap.ts +5 -0
  82. package/src/stega/types.ts +2 -2
  83. package/src/types.ts +328 -103
  84. package/src/users/UsersClient.ts +10 -14
  85. package/src/util/shareReplayLatest.ts +1 -1
  86. package/src/validators.ts +1 -1
  87. package/src/warnings.ts +13 -2
  88. package/dist/_chunks-cjs/config.cjs +0 -206
  89. package/dist/_chunks-cjs/config.cjs.map +0 -1
  90. package/dist/_chunks-cjs/isRecord.cjs +0 -6
  91. package/dist/_chunks-cjs/isRecord.cjs.map +0 -1
  92. package/dist/_chunks-cjs/resolveEditInfo.cjs +0 -334
  93. package/dist/_chunks-cjs/resolveEditInfo.cjs.map +0 -1
  94. package/dist/_chunks-cjs/stegaClean.cjs +0 -46
  95. package/dist/_chunks-cjs/stegaClean.cjs.map +0 -1
  96. package/dist/_chunks-cjs/stegaEncodeSourceMap.cjs +0 -388
  97. package/dist/_chunks-cjs/stegaEncodeSourceMap.cjs.map +0 -1
  98. package/dist/_chunks-es/config.js +0 -207
  99. package/dist/_chunks-es/config.js.map +0 -1
  100. package/dist/_chunks-es/isRecord.js +0 -7
  101. package/dist/_chunks-es/isRecord.js.map +0 -1
  102. package/dist/_chunks-es/resolveEditInfo.js +0 -335
  103. package/dist/_chunks-es/resolveEditInfo.js.map +0 -1
  104. package/dist/_chunks-es/stegaClean.js +0 -47
  105. package/dist/_chunks-es/stegaClean.js.map +0 -1
  106. package/dist/_chunks-es/stegaEncodeSourceMap.js +0 -389
  107. package/dist/_chunks-es/stegaEncodeSourceMap.js.map +0 -1
  108. package/dist/csm.cjs +0 -99
  109. package/dist/csm.cjs.map +0 -1
  110. package/dist/csm.d.cts +0 -391
  111. package/dist/index.browser.cjs +0 -3106
  112. package/dist/index.browser.cjs.map +0 -1
  113. package/dist/index.browser.d.ts +0 -6802
  114. package/dist/index.browser.js +0 -3082
  115. package/dist/index.browser.js.map +0 -1
  116. package/dist/index.cjs +0 -2946
  117. package/dist/index.cjs.map +0 -1
  118. package/dist/media-library.cjs +0 -20
  119. package/dist/media-library.cjs.map +0 -1
  120. package/dist/media-library.d.cts +0 -117
  121. package/dist/stega.browser.cjs +0 -29
  122. package/dist/stega.browser.cjs.map +0 -1
  123. package/dist/stega.browser.d.cts +0 -7200
  124. package/dist/stega.browser.d.ts +0 -7200
  125. package/dist/stega.browser.js +0 -24
  126. package/dist/stega.browser.js.map +0 -1
  127. package/dist/stega.cjs +0 -29
  128. package/dist/stega.cjs.map +0 -1
  129. package/dist/stega.d.cts +0 -7200
  130. package/src/data/eventsourcePolyfill.ts +0 -7
  131. package/umd/.gitkeep +0 -1
  132. package/umd/sanityClient.js +0 -6543
  133. package/umd/sanityClient.min.js +0 -6
@@ -1,6 +1,6 @@
1
- import {lastValueFrom, type Observable} from 'rxjs'
1
+ import {type Observable} from 'rxjs'
2
2
 
3
- import {_request} from '../data/dataMethods'
3
+ import {_request, _requestObservable} from '../data/dataMethods'
4
4
  import type {ObservableSanityClient, SanityClient} from '../SanityClient'
5
5
  import type {
6
6
  DatasetCreateOptions,
@@ -29,7 +29,7 @@ export class ObservableDatasetsClient {
29
29
  * @param options - Options for the dataset, including optional embeddings configuration
30
30
  */
31
31
  create(name: string, options?: DatasetCreateOptions): Observable<DatasetResponse> {
32
- return _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PUT', name, options)
32
+ return _modifyObservable<DatasetResponse>(this.#client, this.#httpRequest, 'PUT', name, options)
33
33
  }
34
34
 
35
35
  /**
@@ -39,7 +39,13 @@ export class ObservableDatasetsClient {
39
39
  * @param options - New options for the dataset
40
40
  */
41
41
  edit(name: string, options?: DatasetEditOptions): Observable<DatasetResponse> {
42
- return _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PATCH', name, options)
42
+ return _modifyObservable<DatasetResponse>(
43
+ this.#client,
44
+ this.#httpRequest,
45
+ 'PATCH',
46
+ name,
47
+ options,
48
+ )
43
49
  }
44
50
 
45
51
  /**
@@ -48,7 +54,7 @@ export class ObservableDatasetsClient {
48
54
  * @param name - Name of the dataset to delete
49
55
  */
50
56
  delete(name: string): Observable<{deleted: true}> {
51
- return _modify<{deleted: true}>(this.#client, this.#httpRequest, 'DELETE', name)
57
+ return _modifyObservable<{deleted: true}>(this.#client, this.#httpRequest, 'DELETE', name)
52
58
  }
53
59
 
54
60
  /**
@@ -58,13 +64,13 @@ export class ObservableDatasetsClient {
58
64
  validate.resourceGuard('dataset', this.#client.config())
59
65
  const config = this.#client.config()
60
66
  const projectId = config.projectId
61
- let uri = '/datasets'
67
+ let url = '/datasets'
62
68
  if (config.useProjectHostname === false) {
63
- uri = `/projects/${projectId}/datasets`
69
+ url = `/projects/${projectId}/datasets`
64
70
  }
65
71
 
66
- return _request<DatasetsResponse>(this.#client, this.#httpRequest, {
67
- uri,
72
+ return _requestObservable<DatasetsResponse>(this.#client, this.#httpRequest, {
73
+ url,
68
74
  tag: null,
69
75
  })
70
76
  }
@@ -77,8 +83,8 @@ export class ObservableDatasetsClient {
77
83
  getEmbeddingsSettings(name: string): Observable<EmbeddingsSettings> {
78
84
  validate.resourceGuard('dataset', this.#client.config())
79
85
  validate.dataset(name)
80
- return _request<EmbeddingsSettings>(this.#client, this.#httpRequest, {
81
- uri: _embeddingsSettingsUri(this.#client, name),
86
+ return _requestObservable<EmbeddingsSettings>(this.#client, this.#httpRequest, {
87
+ url: _embeddingsSettingsUri(this.#client, name),
82
88
  tag: null,
83
89
  })
84
90
  }
@@ -92,9 +98,9 @@ export class ObservableDatasetsClient {
92
98
  editEmbeddingsSettings(name: string, settings: EmbeddingsSettingsBody): Observable<void> {
93
99
  validate.resourceGuard('dataset', this.#client.config())
94
100
  validate.dataset(name)
95
- return _request<void>(this.#client, this.#httpRequest, {
101
+ return _requestObservable<void>(this.#client, this.#httpRequest, {
96
102
  method: 'PUT',
97
- uri: _embeddingsSettingsUri(this.#client, name),
103
+ url: _embeddingsSettingsUri(this.#client, name),
98
104
  body: settings,
99
105
  tag: null,
100
106
  })
@@ -118,9 +124,7 @@ export class DatasetsClient {
118
124
  */
119
125
  create(name: string, options?: DatasetCreateOptions): Promise<DatasetResponse> {
120
126
  validate.resourceGuard('dataset', this.#client.config())
121
- return lastValueFrom(
122
- _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PUT', name, options),
123
- )
127
+ return _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PUT', name, options)
124
128
  }
125
129
 
126
130
  /**
@@ -131,9 +135,7 @@ export class DatasetsClient {
131
135
  */
132
136
  edit(name: string, options?: DatasetEditOptions): Promise<DatasetResponse> {
133
137
  validate.resourceGuard('dataset', this.#client.config())
134
- return lastValueFrom(
135
- _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PATCH', name, options),
136
- )
138
+ return _modify<DatasetResponse>(this.#client, this.#httpRequest, 'PATCH', name, options)
137
139
  }
138
140
 
139
141
  /**
@@ -143,7 +145,7 @@ export class DatasetsClient {
143
145
  */
144
146
  delete(name: string): Promise<{deleted: true}> {
145
147
  validate.resourceGuard('dataset', this.#client.config())
146
- return lastValueFrom(_modify<{deleted: true}>(this.#client, this.#httpRequest, 'DELETE', name))
148
+ return _modify<{deleted: true}>(this.#client, this.#httpRequest, 'DELETE', name)
147
149
  }
148
150
 
149
151
  /**
@@ -153,14 +155,15 @@ export class DatasetsClient {
153
155
  validate.resourceGuard('dataset', this.#client.config())
154
156
  const config = this.#client.config()
155
157
  const projectId = config.projectId
156
- let uri = '/datasets'
158
+ let url = '/datasets'
157
159
  if (config.useProjectHostname === false) {
158
- uri = `/projects/${projectId}/datasets`
160
+ url = `/projects/${projectId}/datasets`
159
161
  }
160
162
 
161
- return lastValueFrom(
162
- _request<DatasetsResponse>(this.#client, this.#httpRequest, {uri, tag: null}),
163
- )
163
+ return _request<DatasetsResponse>(this.#client, this.#httpRequest, {
164
+ url,
165
+ tag: null,
166
+ })
164
167
  }
165
168
 
166
169
  /**
@@ -171,12 +174,10 @@ export class DatasetsClient {
171
174
  getEmbeddingsSettings(name: string): Promise<EmbeddingsSettings> {
172
175
  validate.resourceGuard('dataset', this.#client.config())
173
176
  validate.dataset(name)
174
- return lastValueFrom(
175
- _request<EmbeddingsSettings>(this.#client, this.#httpRequest, {
176
- uri: _embeddingsSettingsUri(this.#client, name),
177
- tag: null,
178
- }),
179
- )
177
+ return _request<EmbeddingsSettings>(this.#client, this.#httpRequest, {
178
+ url: _embeddingsSettingsUri(this.#client, name),
179
+ tag: null,
180
+ })
180
181
  }
181
182
 
182
183
  /**
@@ -188,14 +189,12 @@ export class DatasetsClient {
188
189
  editEmbeddingsSettings(name: string, settings: EmbeddingsSettingsBody): Promise<void> {
189
190
  validate.resourceGuard('dataset', this.#client.config())
190
191
  validate.dataset(name)
191
- return lastValueFrom(
192
- _request<void>(this.#client, this.#httpRequest, {
193
- method: 'PUT',
194
- uri: _embeddingsSettingsUri(this.#client, name),
195
- body: settings,
196
- tag: null,
197
- }),
198
- )
192
+ return _request<void>(this.#client, this.#httpRequest, {
193
+ method: 'PUT',
194
+ url: _embeddingsSettingsUri(this.#client, name),
195
+ body: settings,
196
+ tag: null,
197
+ })
199
198
  }
200
199
  }
201
200
 
@@ -210,7 +209,7 @@ function _embeddingsSettingsUri(
210
209
  return `/datasets/${name}/settings/embeddings`
211
210
  }
212
211
 
213
- function _modify<R = unknown>(
212
+ function _modifyObservable<R = unknown>(
214
213
  client: SanityClient | ObservableSanityClient,
215
214
  httpRequest: HttpRequest,
216
215
  method: 'DELETE' | 'PATCH' | 'PUT',
@@ -220,9 +219,27 @@ function _modify<R = unknown>(
220
219
  validate.resourceGuard('dataset', client.config())
221
220
  validate.dataset(name)
222
221
 
222
+ return _requestObservable<R>(client, httpRequest, {
223
+ method,
224
+ url: `/datasets/${name}`,
225
+ body: options,
226
+ tag: null,
227
+ })
228
+ }
229
+
230
+ function _modify<R = unknown>(
231
+ client: SanityClient | ObservableSanityClient,
232
+ httpRequest: HttpRequest,
233
+ method: 'DELETE' | 'PATCH' | 'PUT',
234
+ name: string,
235
+ options?: DatasetCreateOptions | DatasetEditOptions,
236
+ ): Promise<R> {
237
+ validate.resourceGuard('dataset', client.config())
238
+ validate.dataset(name)
239
+
223
240
  return _request<R>(client, httpRequest, {
224
241
  method,
225
- uri: `/datasets/${name}`,
242
+ url: `/datasets/${name}`,
226
243
  body: options,
227
244
  tag: null,
228
245
  })
@@ -1,7 +1,7 @@
1
- import type {Middlewares} from 'get-it'
1
+ import {defineRequester, type EnvironmentOptions} from './http/request'
2
+ import type {ClientConfig, HttpRequest} from './types'
2
3
 
3
- import {defineHttpRequest} from './http/request'
4
- import type {Any, ClientConfig, HttpRequest} from './types'
4
+ export {isTimeoutError, type TimeoutErrorLike} from 'get-it'
5
5
 
6
6
  export {validateApiPerspective} from './config'
7
7
  export {
@@ -29,9 +29,6 @@ export {
29
29
  export * from './SanityClient'
30
30
  export * from './types'
31
31
 
32
- /** @alpha */
33
- export {adapter as unstable__adapter, environment as unstable__environment} from 'get-it'
34
-
35
32
  /**
36
33
  * Create the `requester` and `createClient` exports, that have environment specific middleware for node and browsers
37
34
  * @internal
@@ -40,27 +37,46 @@ export default function defineCreateClientExports<
40
37
  SanityClientType,
41
38
  ClientConfigType extends ClientConfig,
42
39
  >(
43
- envMiddleware: Middlewares,
40
+ envOptions: EnvironmentOptions,
44
41
  ClassConstructor: new (httpRequest: HttpRequest, config: ClientConfigType) => SanityClientType,
45
42
  ) {
46
- // Set the http client to use for requests, and its environment specific middleware
47
- const defaultRequester = defineHttpRequest(envMiddleware)
43
+ // Set the http client to use for requests, and its environment specific options
44
+ const defaultRequester = defineRequester(envOptions).observable
48
45
 
49
46
  const createClient = (config: ClientConfigType) => {
50
- const clientRequester = defineHttpRequest(envMiddleware, {
51
- ignoreWarnings: config.ignoreWarnings,
52
- })
53
- return new ClassConstructor(
54
- (options, requester) =>
55
- (requester || clientRequester)({
56
- maxRedirects: 0,
57
- maxRetries: config.maxRetries,
58
- retryDelay: config.retryDelay,
59
- lineage: config.lineage,
60
- ...options,
61
- } as Any),
62
- config,
47
+ const {observable: clientRequester, promise: clientRequesterPromise} = defineRequester(
48
+ envOptions,
49
+ {
50
+ ignoreWarnings: config.ignoreWarnings,
51
+ maxRetries: config.maxRetries,
52
+ retryDelay: config.retryDelay,
53
+ },
63
54
  )
55
+ // The single transport for the whole client. Resolves a request to its
56
+ // parsed response body as a Promise; the observable client surface wraps
57
+ // this in `new Observable(...)` (see `_observe` in dataMethods).
58
+ // Redirects are surfaced rather than followed unless a request opts in
59
+ // (via the public `maxRedirects` option, translated in `requestOptions`).
60
+ const performRequest = async (options: Parameters<HttpRequest>[0]): Promise<unknown> => {
61
+ const event = await clientRequesterPromise({redirect: 'manual', ...options})
62
+ return event.body
63
+ }
64
+ const httpRequest: HttpRequest = (options, requestHandler) =>
65
+ requestHandler ? requestHandler(options, performRequest) : performRequest(options)
66
+ // Populate `requester` on the initialized config so internal paths
67
+ // (e.g. the asset upload event stream) can reach the underlying transport.
68
+ // `resolveFetch` is threaded onto the config so request building and
69
+ // EventSource fetches can reach the environment's fetch without importing
70
+ // `get-it/node` from a browser-shared module (would leak `undici` into
71
+ // the browser bundle). A caller-supplied `resolveFetch` wins over the
72
+ // environment's — that is the supported way to swap the transport (the
73
+ // test suite injects its mock fetch through it).
74
+ const resolvedConfig = {
75
+ ...config,
76
+ requester: clientRequester,
77
+ resolveFetch: config.resolveFetch ?? envOptions.resolveFetch,
78
+ }
79
+ return new ClassConstructor(httpRequest, resolvedConfig)
64
80
  }
65
81
 
66
82
  return {requester: defaultRequester, createClient}
@@ -0,0 +1,66 @@
1
+ import {lastValueFrom, type Observable} from 'rxjs'
2
+
3
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
4
+ import type {HttpRequest} from '../types'
5
+ import {_invoke, type InvokeFunctionRequest} from './invoke'
6
+
7
+ /** @public */
8
+ export class ObservableFunctionsClient {
9
+ #client: ObservableSanityClient
10
+ #httpRequest: HttpRequest
11
+ constructor(client: ObservableSanityClient, httpRequest: HttpRequest) {
12
+ this.#client = client
13
+ this.#httpRequest = httpRequest
14
+ }
15
+
16
+ /**
17
+ * Invoke a deployed function by its blueprint name.
18
+ *
19
+ * The name is resolved within the stack given by `stackId` on the request or
20
+ * the client config. Passes the function's return value once it finishes.
21
+ *
22
+ * @param functionName - name of the function, as declared in the blueprint
23
+ * @param request - payload and request options
24
+ */
25
+ invoke<R = unknown>(
26
+ functionName: string,
27
+ request?: InvokeFunctionRequest,
28
+ ): Observable<R | undefined> {
29
+ return _invoke<R>(this.#client, this.#httpRequest, functionName, request)
30
+ }
31
+ }
32
+
33
+ /** @public */
34
+ export class FunctionsClient {
35
+ #client: SanityClient
36
+ #httpRequest: HttpRequest
37
+ constructor(client: SanityClient, httpRequest: HttpRequest) {
38
+ this.#client = client
39
+ this.#httpRequest = httpRequest
40
+ }
41
+
42
+ /**
43
+ * Invoke a deployed function by its blueprint name.
44
+ *
45
+ * The name is resolved within the stack given by `stackId` on the request or
46
+ * the client config, which costs one extra request per call. Rejects if the
47
+ * stack has no function by that name, or if the name resolves to anything
48
+ * other than a `sanity.function.pubsub` function.
49
+ *
50
+ * The lookup is scoped to `projectId`, or to `organizationId` when one is set
51
+ * for a stack deployed at organization scope.
52
+ *
53
+ * The request stays open until the function finishes, and resolves with its
54
+ * return value, or `undefined` if it returns nothing. Long-running functions
55
+ * may need an explicit `timeout`.
56
+ *
57
+ * @param functionName - name of the function, as declared in the blueprint
58
+ * @param request - payload and request options
59
+ */
60
+ invoke<R = unknown>(
61
+ functionName: string,
62
+ request?: InvokeFunctionRequest,
63
+ ): Promise<R | undefined> {
64
+ return lastValueFrom(_invoke<R>(this.#client, this.#httpRequest, functionName, request))
65
+ }
66
+ }
@@ -0,0 +1,176 @@
1
+ import {defer, map, mergeMap, type Observable} from 'rxjs'
2
+
3
+ import {_requestObservable} from '../data/dataMethods'
4
+ import type {ObservableSanityClient, SanityClient} from '../SanityClient'
5
+ import type {HttpRequest, InitializedClientConfig} from '../types'
6
+
7
+ /** Function resource types in a blueprint are namespaced under this prefix. */
8
+ const FUNCTION_RESOURCE_PREFIX = 'sanity.function.'
9
+ const INVOKABLE_FUNCTION_TYPE = 'sanity.function.pubsub'
10
+
11
+ /** @public */
12
+ export interface InvokeFunctionEvent {
13
+ /**
14
+ * Payload handed to the function.
15
+ * The function receives it as `event.data`.
16
+ */
17
+ data?: unknown
18
+ }
19
+
20
+ /** @public */
21
+ export interface InvokeFunctionRequest {
22
+ event?: InvokeFunctionEvent
23
+ /**
24
+ * Stack to resolve the function name against.
25
+ * Overrides `stackId` from the client config.
26
+ */
27
+ stackId?: string
28
+ /**
29
+ * Organization owning the stack.
30
+ */
31
+ organizationId?: string
32
+ /**
33
+ * Milliseconds to wait for the function to return.
34
+ */
35
+ timeout?: number
36
+ /** Abort the invocation. */
37
+ signal?: AbortSignal
38
+ }
39
+
40
+ /**
41
+ * Subset of a stack resource the client needs in order to resolve a name.
42
+ *
43
+ * @internal
44
+ */
45
+ interface StackResource {
46
+ name: string
47
+ type: string
48
+ /** Provider-side id. For a function resource, the function id. */
49
+ externalId?: string
50
+ }
51
+
52
+ type Client = SanityClient | ObservableSanityClient
53
+
54
+ const scopeHeaders = (
55
+ config: InitializedClientConfig,
56
+ request: InvokeFunctionRequest | undefined,
57
+ ): Record<string, string> => {
58
+ const organizationId = request?.organizationId || config.organizationId
59
+ if (organizationId) {
60
+ return {
61
+ 'X-Sanity-Scope-Type': 'organization',
62
+ 'X-Sanity-Scope-Id': organizationId,
63
+ }
64
+ }
65
+
66
+ const {projectId} = config
67
+ if (!projectId) {
68
+ throw new Error(
69
+ '`functions.invoke()` requires a `projectId` to be set in the client config, or an ' +
70
+ '`organizationId` for a stack deployed at organization scope',
71
+ )
72
+ }
73
+
74
+ return {
75
+ 'X-Sanity-Scope-Type': 'project',
76
+ 'X-Sanity-Scope-Id': projectId,
77
+ }
78
+ }
79
+
80
+ /**
81
+ * A per-call `stackId` overrides the one in the client config.
82
+ */
83
+ const resolveStackId = (
84
+ config: InitializedClientConfig,
85
+ request: InvokeFunctionRequest | undefined,
86
+ ): string => {
87
+ const stackId = request?.stackId || config.stackId
88
+ if (!stackId) {
89
+ throw new Error(
90
+ '`functions.invoke()` requires a `stackId`, either in the client config or on the request. ' +
91
+ 'Function names are only unique within a stack.',
92
+ )
93
+ }
94
+
95
+ return stackId
96
+ }
97
+
98
+ /**
99
+ * The invoke route is keyed by function id, but callers know functions by the
100
+ * name declared in the blueprint. Names are unique within a stack, so the stack
101
+ * both makes the name resolvable and confines the call to its own functions.
102
+ *
103
+ * @internal
104
+ */
105
+ function _resolveFunctionId(
106
+ client: Client,
107
+ httpRequest: HttpRequest,
108
+ functionName: string,
109
+ stackId: string,
110
+ headers: Record<string, string>,
111
+ request: InvokeFunctionRequest | undefined,
112
+ ): Observable<string> {
113
+ return _requestObservable<{resources?: StackResource[]}>(client, httpRequest, {
114
+ method: 'GET',
115
+ url: `/blueprints/stacks/${stackId}`,
116
+ headers,
117
+ signal: request?.signal,
118
+ }).pipe(
119
+ map((stack) => {
120
+ const match = (stack?.resources || []).find(
121
+ (resource) =>
122
+ resource.type?.startsWith(FUNCTION_RESOURCE_PREFIX) && resource.name === functionName,
123
+ )
124
+
125
+ if (!match) {
126
+ throw new Error(`Function "${functionName}" not found in stack "${stackId}"`)
127
+ }
128
+
129
+ if (!match.externalId) {
130
+ throw new Error(
131
+ `Function "${functionName}" is declared in stack "${stackId}" but is not deployed`,
132
+ )
133
+ }
134
+
135
+ if (match.type !== INVOKABLE_FUNCTION_TYPE) {
136
+ throw new Error(`Function invocation is not supported for ${match.type}`)
137
+ }
138
+
139
+ return match.externalId
140
+ }),
141
+ )
142
+ }
143
+
144
+ /** @internal */
145
+ export function _invoke<R = unknown>(
146
+ client: Client,
147
+ httpRequest: HttpRequest,
148
+ functionName: string,
149
+ request?: InvokeFunctionRequest,
150
+ ): Observable<R | undefined> {
151
+ // Deferred so a bad config surfaces as an error on the returned observable
152
+ // (and so a rejected promise) rather than throwing at the call site.
153
+ return defer(() => {
154
+ const config = client.config()
155
+ const headers = scopeHeaders(config, request)
156
+ const stackId = resolveStackId(config, request)
157
+
158
+ return _resolveFunctionId(client, httpRequest, functionName, stackId, headers, request).pipe(
159
+ // A function that returns nothing answers 204, which the transport parses
160
+ // to an `undefined` body. The status code is not observable from here —
161
+ // the `HttpRequest` boundary resolves to the body alone — so an empty
162
+ // response and a function that returned nothing both surface as
163
+ // `undefined`.
164
+ mergeMap((functionId) =>
165
+ _requestObservable<R | undefined>(client, httpRequest, {
166
+ method: 'POST',
167
+ url: `/functions/${functionId}/invoke`,
168
+ headers,
169
+ body: {event: {data: request?.event?.data ?? {}}},
170
+ timeout: request?.timeout,
171
+ signal: request?.signal,
172
+ }),
173
+ ),
174
+ )
175
+ })
176
+ }
@@ -1 +1,7 @@
1
- export default []
1
+ import type {EnvironmentOptions} from './request'
2
+
3
+ const environment: EnvironmentOptions = {
4
+ middleware: [],
5
+ }
6
+
7
+ export default environment