@sanity/client 0.0.0-dev.2 → 0.0.0-dev.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/client",
3
- "version": "0.0.0-dev.2",
3
+ "version": "0.0.0-dev.4",
4
4
  "description": "Client for retrieving, creating and patching data from Sanity.io",
5
5
  "keywords": [
6
6
  "sanity",
@@ -33,6 +33,7 @@
33
33
  "import": "./dist/index.browser.js"
34
34
  },
35
35
  "deno": "./dist/index.browser.js",
36
+ "edge-light": "./dist/index.browser.js",
36
37
  "worker": "./dist/index.browser.js",
37
38
  "source": "./src/index.ts",
38
39
  "require": "./dist/index.cjs",
@@ -88,37 +89,37 @@
88
89
  "singleQuote": true
89
90
  },
90
91
  "dependencies": {
91
- "@sanity/eventsource": "^4",
92
- "get-it": "^8",
93
- "rxjs": "^7"
92
+ "@sanity/eventsource": "5",
93
+ "get-it": "^8.1",
94
+ "rxjs": "7"
94
95
  },
95
96
  "devDependencies": {
96
- "@edge-runtime/types": "^2.0.7",
97
- "@edge-runtime/vm": "^2.1.1",
97
+ "@edge-runtime/types": "^2.0.8",
98
+ "@edge-runtime/vm": "^2.1.2",
98
99
  "@rollup/plugin-commonjs": "^24.0.1",
99
100
  "@rollup/plugin-node-resolve": "^15.0.1",
100
- "@sanity/pkg-utils": "^2.2.6",
101
- "@sanity/semantic-release-preset": "^4.0.0",
102
- "@types/node": "^18.14.2",
103
- "@typescript-eslint/eslint-plugin": "^5.53.0",
104
- "@typescript-eslint/parser": "^5.53.0",
105
- "@vitest/coverage-c8": "^0.29.2",
106
- "eslint": "^8.35.0",
107
- "eslint-config-prettier": "^8.6.0",
101
+ "@sanity/pkg-utils": "^2.2.13",
102
+ "@sanity/semantic-release-preset": "^4.1.0",
103
+ "@types/node": "^18.15.10",
104
+ "@typescript-eslint/eslint-plugin": "^5.57.0",
105
+ "@typescript-eslint/parser": "^5.57.0",
106
+ "@vitest/coverage-c8": "^0.29.8",
107
+ "eslint": "^8.36.0",
108
+ "eslint-config-prettier": "^8.8.0",
108
109
  "eslint-plugin-prettier": "^4.2.1",
109
110
  "eslint-plugin-simple-import-sort": "^10.0.0",
110
111
  "faucet": "^0.0.4",
111
112
  "happy-dom": "^8.9.0",
112
113
  "ls-engines": "^0.9.0",
113
114
  "nock": "^13.3.0",
114
- "prettier": "^2.8.4",
115
+ "prettier": "^2.8.7",
115
116
  "prettier-plugin-packagejson": "^2.4.3",
116
- "rimraf": "^4.1.2",
117
- "rollup": "^3.17.3",
117
+ "rimraf": "^4.4.1",
118
+ "rollup": "^3.20.2",
118
119
  "sse-channel": "^4.0.0",
119
- "terser": "^5.16.5",
120
- "typescript": "^4.9.5",
121
- "vitest": "^0.29.2",
120
+ "terser": "^5.16.8",
121
+ "typescript": "^5.0.2",
122
+ "vitest": "^0.29.8",
122
123
  "vitest-github-actions-reporter": "^0.10.0"
123
124
  },
124
125
  "engines": {
@@ -10,6 +10,7 @@ import type {
10
10
  ResponseEvent,
11
11
  SanityAssetDocument,
12
12
  SanityImageAssetDocument,
13
+ UploadBody,
13
14
  UploadClientConfig,
14
15
  } from '../types'
15
16
  import * as validators from '../validators'
@@ -32,7 +33,7 @@ export class ObservableAssetsClient {
32
33
  */
33
34
  upload(
34
35
  assetType: 'file',
35
- body: File | Blob | Buffer | NodeJS.ReadableStream,
36
+ body: UploadBody,
36
37
  options?: UploadClientConfig
37
38
  ): Observable<HttpRequestEvent<{document: SanityAssetDocument}>>
38
39
 
@@ -45,7 +46,7 @@ export class ObservableAssetsClient {
45
46
  */
46
47
  upload(
47
48
  assetType: 'image',
48
- body: File | Blob | Buffer | NodeJS.ReadableStream,
49
+ body: UploadBody,
49
50
  options?: UploadClientConfig
50
51
  ): Observable<HttpRequestEvent<{document: SanityImageAssetDocument}>>
51
52
  /**
@@ -57,12 +58,12 @@ export class ObservableAssetsClient {
57
58
  */
58
59
  upload(
59
60
  assetType: 'file' | 'image',
60
- body: File | Blob | Buffer | NodeJS.ReadableStream,
61
+ body: UploadBody,
61
62
  options?: UploadClientConfig
62
63
  ): Observable<HttpRequestEvent<{document: SanityAssetDocument | SanityImageAssetDocument}>>
63
64
  upload(
64
65
  assetType: 'file' | 'image',
65
- body: File | Blob | Buffer | NodeJS.ReadableStream,
66
+ body: UploadBody,
66
67
  options?: UploadClientConfig
67
68
  ): Observable<HttpRequestEvent<{document: SanityAssetDocument | SanityImageAssetDocument}>> {
68
69
  return _upload(this.#client, this.#httpRequest, assetType, body, options)
@@ -87,7 +88,7 @@ export class AssetsClient {
87
88
  */
88
89
  upload(
89
90
  assetType: 'file',
90
- body: File | Blob | Buffer | NodeJS.ReadableStream,
91
+ body: UploadBody,
91
92
  options?: UploadClientConfig
92
93
  ): Promise<SanityAssetDocument>
93
94
  /**
@@ -99,7 +100,7 @@ export class AssetsClient {
99
100
  */
100
101
  upload(
101
102
  assetType: 'image',
102
- body: File | Blob | Buffer | NodeJS.ReadableStream,
103
+ body: UploadBody,
103
104
  options?: UploadClientConfig
104
105
  ): Promise<SanityImageAssetDocument>
105
106
  /**
@@ -111,12 +112,12 @@ export class AssetsClient {
111
112
  */
112
113
  upload(
113
114
  assetType: 'file' | 'image',
114
- body: File | Blob | Buffer | NodeJS.ReadableStream,
115
+ body: UploadBody,
115
116
  options?: UploadClientConfig
116
117
  ): Promise<SanityAssetDocument | SanityImageAssetDocument>
117
118
  upload(
118
119
  assetType: 'file' | 'image',
119
- body: File | Blob | Buffer | NodeJS.ReadableStream,
120
+ body: UploadBody,
120
121
  options?: UploadClientConfig
121
122
  ): Promise<SanityAssetDocument | SanityImageAssetDocument> {
122
123
  const observable = _upload(this.#client, this.#httpRequest, assetType, body, options)
@@ -137,7 +138,7 @@ function _upload(
137
138
  client: SanityClient | ObservableSanityClient,
138
139
  httpRequest: HttpRequest,
139
140
  assetType: 'image' | 'file',
140
- body: File | Blob | Buffer | NodeJS.ReadableStream,
141
+ body: UploadBody,
141
142
  opts: UploadClientConfig = {}
142
143
  ): Observable<HttpRequestEvent<{document: SanityAssetDocument | SanityImageAssetDocument}>> {
143
144
  validators.validateAssetType(assetType)
@@ -177,7 +178,7 @@ function _upload(
177
178
  }
178
179
 
179
180
  function optionsFromFile(opts: Record<string, Any>, file: Any) {
180
- if (typeof window === 'undefined' || !(file instanceof window.File)) {
181
+ if (typeof File === 'undefined' || !(file instanceof File)) {
181
182
  return opts
182
183
  }
183
184
 
@@ -301,12 +301,8 @@ export function _requestObservable<R>(
301
301
  options.query = {tag: validate.requestTag(tag), ...options.query}
302
302
  }
303
303
 
304
- if (config.unstable_overlayDrafts) {
305
- if (config.apiVersion !== 'X') {
306
- // eslint-disable-next-line no-console
307
- console.error('You need to set `apiVersion` to `X` to use `unstable_overlayDrafts')
308
- }
309
- options.query = {...options.query, params: {...(options.query.params || {}), draftMode: true}}
304
+ if (config.encodeStegaSourceMap) {
305
+ options.query = {encodeHackySourceMap: true, ...options.query}
310
306
  }
311
307
 
312
308
  const reqOptions = requestOptions(
@@ -1,4 +1,3 @@
1
- import polyfilledEventSource from '@sanity/eventsource'
2
1
  import {Observable} from 'rxjs'
3
2
 
4
3
  import type {ObservableSanityClient, SanityClient} from '../SanityClient'
@@ -12,7 +11,6 @@ import {encodeQueryString} from './encodeQueryString'
12
11
  // unknown range of headers, but an average EventSource request from Chrome seems
13
12
  // to have around 700 bytes of cruft, so let us account for 1.2K to be "safe"
14
13
  const MAX_URL_LENGTH = 16000 - 1200
15
- const EventSource = polyfilledEventSource
16
14
 
17
15
  const possibleOptions = [
18
16
  'includePreviousRevision',
@@ -86,7 +84,15 @@ export function _listen<R extends Record<string, Any> = Record<string, Any>>(
86
84
  }
87
85
 
88
86
  return new Observable((observer) => {
89
- let es = getEventSource()
87
+ let es: InstanceType<typeof import('@sanity/eventsource')>
88
+ getEventSource()
89
+ .then((eventSource) => {
90
+ es = eventSource
91
+ })
92
+ .catch((reason) => {
93
+ observer.error(reason)
94
+ stop()
95
+ })
90
96
  let reconnectTimer: NodeJS.Timeout
91
97
  let stopped = false
92
98
 
@@ -107,7 +113,7 @@ export function _listen<R extends Record<string, Any> = Record<string, Any>>(
107
113
  // automatically, in which case it sets readyState to `CONNECTING`, but in some cases
108
114
  // (like when a laptop lid is closed), it closes the connection. In these cases we need
109
115
  // to explicitly reconnect.
110
- if (es.readyState === EventSource.CLOSED) {
116
+ if (es.readyState === es.CLOSED) {
111
117
  unsubscribe()
112
118
  clearTimeout(reconnectTimer)
113
119
  reconnectTimer = setTimeout(open, 100)
@@ -130,10 +136,11 @@ export function _listen<R extends Record<string, Any> = Record<string, Any>>(
130
136
  }
131
137
 
132
138
  function unsubscribe() {
133
- es.removeEventListener('error', onError, false)
134
- es.removeEventListener('channelError', onChannelError, false)
135
- es.removeEventListener('disconnect', onDisconnect, false)
136
- listenFor.forEach((type: string) => es.removeEventListener(type, onMessage, false))
139
+ if (!es) return
140
+ es.removeEventListener('error', onError)
141
+ es.removeEventListener('channelError', onChannelError)
142
+ es.removeEventListener('disconnect', onDisconnect)
143
+ listenFor.forEach((type: string) => es.removeEventListener(type, onMessage))
137
144
  es.close()
138
145
  }
139
146
 
@@ -143,17 +150,25 @@ export function _listen<R extends Record<string, Any> = Record<string, Any>>(
143
150
  }
144
151
  }
145
152
 
146
- function getEventSource() {
153
+ async function getEventSource(): Promise<InstanceType<typeof import('@sanity/eventsource')>> {
154
+ const {default: EventSource} = await import('@sanity/eventsource')
147
155
  const evs = new EventSource(uri, esOptions)
148
- evs.addEventListener('error', onError, false)
149
- evs.addEventListener('channelError', onChannelError, false)
150
- evs.addEventListener('disconnect', onDisconnect, false)
151
- listenFor.forEach((type: string) => evs.addEventListener(type, onMessage, false))
156
+ evs.addEventListener('error', onError)
157
+ evs.addEventListener('channelError', onChannelError)
158
+ evs.addEventListener('disconnect', onDisconnect)
159
+ listenFor.forEach((type: string) => evs.addEventListener(type, onMessage))
152
160
  return evs
153
161
  }
154
162
 
155
163
  function open() {
156
- es = getEventSource()
164
+ getEventSource()
165
+ .then((eventSource) => {
166
+ es = eventSource
167
+ })
168
+ .catch((reason) => {
169
+ observer.error(reason)
170
+ stop()
171
+ })
157
172
  }
158
173
 
159
174
  function stop() {
@@ -1,4 +1,6 @@
1
- import type {Any, ErrorProps} from '../types'
1
+ import type {Any, ErrorProps, MutationError} from '../types'
2
+
3
+ const MAX_ITEMS_IN_ERROR_MESSAGE = 5
2
4
 
3
5
  /** @public */
4
6
  export class ClientError extends Error {
@@ -44,6 +46,22 @@ function extractErrorProps(res: Any): ErrorProps {
44
46
  return props
45
47
  }
46
48
 
49
+ // Mutation errors (specifically)
50
+ if (isMutationError(body)) {
51
+ const allItems = body.error.items || []
52
+ const items = allItems
53
+ .slice(0, MAX_ITEMS_IN_ERROR_MESSAGE)
54
+ .map((item) => item.error?.description)
55
+ .filter(Boolean)
56
+ let itemsStr = items.length ? `:\n- ${items.join('\n- ')}` : ''
57
+ if (allItems.length > MAX_ITEMS_IN_ERROR_MESSAGE) {
58
+ itemsStr += `\n...and ${allItems.length - MAX_ITEMS_IN_ERROR_MESSAGE} more`
59
+ }
60
+ props.message = `${body.error.description}${itemsStr}`
61
+ props.details = body.error
62
+ return props
63
+ }
64
+
47
65
  // Query/database errors ({error: {description, other, arb, props}})
48
66
  if (body.error && body.error.description) {
49
67
  props.message = body.error.description
@@ -56,6 +74,19 @@ function extractErrorProps(res: Any): ErrorProps {
56
74
  return props
57
75
  }
58
76
 
77
+ function isMutationError(body: Any): body is MutationError {
78
+ return (
79
+ isPlainObject(body) &&
80
+ isPlainObject(body.error) &&
81
+ body.error.type === 'mutationError' &&
82
+ typeof body.error.description === 'string'
83
+ )
84
+ }
85
+
86
+ function isPlainObject(obj: Any): obj is Record<string, unknown> {
87
+ return typeof obj === 'object' && obj !== null && !Array.isArray(obj)
88
+ }
89
+
59
90
  function httpErrorMessage(res: Any) {
60
91
  const statusMessage = res.statusMessage ? ` ${res.statusMessage}` : ''
61
92
  return `${res.method}-request to ${res.url} resulted in HTTP ${res.statusCode}${statusMessage}`
@@ -26,3 +26,6 @@ export default function deprecatedCreateClient(config: ClientConfig) {
26
26
  printNoDefaultExport()
27
27
  return new SanityClient(httpRequest, config)
28
28
  }
29
+
30
+ /** @alpha */
31
+ export {adapter as unstable__adapter, environment as unstable__environment} from 'get-it'
package/src/index.ts CHANGED
@@ -26,3 +26,6 @@ export default function deprecatedCreateClient(config: ClientConfig) {
26
26
  printNoDefaultExport()
27
27
  return new SanityClient(httpRequest, config)
28
28
  }
29
+
30
+ /** @alpha */
31
+ export {adapter as unstable__adapter, environment as unstable__environment} from 'get-it'
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ // deno-lint-ignore-file no-empty-interface
1
2
  import type {Requester} from 'get-it'
2
3
 
3
4
  /**
@@ -6,6 +7,14 @@ import type {Requester} from 'get-it'
6
7
  */
7
8
  export type Any = any // eslint-disable-line @typescript-eslint/no-explicit-any
8
9
 
10
+ declare global {
11
+ // Declare empty stub interfaces for environments where "dom" lib is not included
12
+ interface File {}
13
+ }
14
+
15
+ /** @public */
16
+ export type UploadBody = File | Blob | Buffer | NodeJS.ReadableStream
17
+
9
18
  /** @public */
10
19
  export interface RequestOptions {
11
20
  timeout?: number
@@ -43,12 +52,7 @@ export interface ClientConfig {
43
52
  */
44
53
  requester?: Requester
45
54
 
46
- /**
47
- * Experimental, requires apiVersion: 'X' and will overlay drafts on top of published documents
48
- * which lets you preview your query on what it'll look like after every document is published.
49
- * @alpha
50
- */
51
- unstable_overlayDrafts?: boolean
55
+ encodeStegaSourceMap?: boolean
52
56
  }
53
57
 
54
58
  /** @public */
@@ -573,3 +577,28 @@ export interface RawRequestOptions {
573
577
  body?: Any
574
578
  maxRedirects?: number
575
579
  }
580
+
581
+ /** @internal */
582
+ export interface ApiError {
583
+ error: string
584
+ message: string
585
+ statusCode: number
586
+ }
587
+
588
+ /** @internal */
589
+ export interface MutationError {
590
+ error: {
591
+ type: 'mutationError'
592
+ description: string
593
+ items?: MutationErrorItem[]
594
+ }
595
+ }
596
+
597
+ /** @internal */
598
+ export interface MutationErrorItem {
599
+ error: {
600
+ type: string
601
+ description: string
602
+ value?: unknown
603
+ }
604
+ }
package/src/validators.ts CHANGED
@@ -30,7 +30,7 @@ export const validateObject = (op: string, val: Any) => {
30
30
  }
31
31
 
32
32
  export const validateDocumentId = (op: string, id: string) => {
33
- if (typeof id !== 'string' || !/^[a-z0-9_.-]+$/i.test(id)) {
33
+ if (typeof id !== 'string' || !/^[a-z0-9_][a-z0-9_.-]{0,127}$/i.test(id) || id.includes('..')) {
34
34
  throw new Error(`${op}(): "${id}" is not a valid document ID`)
35
35
  }
36
36
  }