🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@sanity/client

Package Overview
Dependencies
Maintainers
2
Versions
1151
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sanity/client - npm Package Compare versions

Comparing version
7.22.0
to
7.22.1
+1
-1
package.json
{
"name": "@sanity/client",
"version": "7.22.0",
"version": "7.22.1",
"description": "Client for retrieving, creating and patching data from Sanity.io",

@@ -5,0 +5,0 @@ "keywords": [

@@ -1,2 +0,2 @@

import {catchError, mergeMap, Observable, of} from 'rxjs'
import {catchError, mergeMap, Observable, of, throwError} from 'rxjs'
import {finalize, map} from 'rxjs/operators'

@@ -125,12 +125,6 @@

const checkCors = fetchObservable(url, {
method: 'OPTIONS',
mode: 'cors',
credentials: esOptions.withCredentials ? 'include' : 'omit',
headers: esOptions.headers,
}).pipe(
catchError(() => {
// If the request fails, then we assume it was due to CORS, and we rethrow a special error that allows special handling in userland
throw new CorsOriginError({projectId: projectId!})
}),
const checkCors = checkCorsObservable(
new URL(this.#client.getUrl('/check/cors', false)),
projectId,
esOptions.withCredentials === true,
)

@@ -149,2 +143,8 @@

catchError((err) => {
// If a prior `reconnect` already ran the CORS probe and produced a
// `CorsOriginError`, just rethrow it instead of calling `/check/cors`
// a second time only to get the same answer.
if (err instanceof CorsOriginError) {
return throwError(() => err)
}
return checkCors.pipe(

@@ -177,17 +177,88 @@ mergeMap(() => {

function fetchObservable(url: URL, init: RequestInit) {
return new Observable((observer) => {
/**
* Probes the `/check/cors` endpoint to confirm whether the current origin is
* allowed by the project's CORS configuration. EventSource failures are opaque,
* so we use this side-channel purely to tell "the server actively rejected our
* origin" apart from every other class of failure.
*
* Errors with `CorsOriginError` when either:
*
* - `requireCredentials` is `true` (the EventSource was about to send
* credentials) and `/check/cors` reports `result.withCredentials === false`.
* The credentialed request would fail due to a missing
* `access-control-allow-credentials` header. The resulting error carries
* `credentials: true` so its `addOriginUrl` deep-link pre-selects the
* "Allow credentials" toggle in the Sanity management form.
* - `/check/cors` reports `result.allowed === false` (origin is not on the
* project's CORS allow-list). The error carries `credentials: requireCredentials`
* so the deep-link still pre-selects credentials when the caller needed them.
*
* Every other outcome is intentionally treated as "we don't know": the
* observable emits a single `void` value and then completes, so downstream
* `mergeMap(() => ...)` consumers can continue. No error is surfaced for any
* of these cases:
*
* - `allowed: true` (with credentials satisfied if required) or an
* unrecognised body shape: the server did not confirm a CORS rejection.
* - Non-2xx HTTP response from `/check/cors`: same - no signal either way, and
* a 5xx on the probe shouldn't poison the EventSource's original error.
* - `fetch` / network / JSON parse failures: indistinguishable from ordinary
* connectivity hiccups (offline, DNS, certs, transient outages). Reporting
* those as CORS errors is exactly the false-positive class this helper
* exists to prevent.
* - The subscription was aborted: nothing to emit and nothing to complete.
*
* In all of those cases the caller's original underlying error from the
* EventSource is allowed to propagate unchanged.
*/
function checkCorsObservable(
url: URL,
projectId: string | undefined,
requireCredentials: boolean,
): Observable<void> {
return new Observable<void>((observer) => {
const controller = new AbortController()
const signal = controller.signal
fetch(url, {...init, signal: controller.signal}).then(
(response) => {
observer.next(response)
const {signal} = controller
fetch(url, {method: 'GET', mode: 'cors', credentials: 'omit', signal})
.then((response) => {
// Aborted or non-2xx: not a confirmed CORS rejection. Fall through with
// an undefined body so the next step takes the silent-completion path.
if (signal.aborted || !response.ok) return
return response.json() as Promise<{
result?: {allowed?: boolean; withCredentials?: boolean}
}>
})
.then((body) => {
if (signal.aborted) return
// Check the credentialed case first: if the EventSource was about to
// send credentials but the project's CORS config doesn't permit them,
// the credentialed request would fail with a missing
// `access-control-allow-credentials` header. Surface this as a CORS
// rejection with `credentials: true` so the deep-link pre-selects the
// "Allow credentials" toggle.
if (requireCredentials && body?.result?.withCredentials === false) {
observer.error(new CorsOriginError({projectId, credentials: true}))
return
}
// Generic case: the server actively rejected this origin. Propagate
// `credentials: requireCredentials` so the deep-link still pre-selects
// credentials when the caller needed them.
if (body?.result?.allowed === false) {
observer.error(new CorsOriginError({projectId, credentials: requireCredentials}))
return
}
// Anything else (allowed + credentials satisfied, unrecognised body)
// is treated as "not a confirmed CORS rejection" - let the caller's
// original error surface instead.
observer.next()
observer.complete()
},
(err) => {
if (!signal.aborted) {
observer.error(err)
}
},
)
})
// Fetch/network/JSON parse errors are intentionally ignored - see the
// helper's docblock for the rationale. We still need to settle the
// observer so downstream `mergeMap(checkCors, ...)` consumers can proceed.
.catch(() => {
if (signal.aborted || observer.closed) return
observer.next()
observer.complete()
})
return () => controller.abort()

@@ -194,0 +265,0 @@ })

@@ -259,6 +259,6 @@ import type {HttpContext} from 'get-it'

export class CorsOriginError extends Error {
projectId: string
projectId?: string
addOriginUrl?: URL
constructor({projectId}: {projectId: string}) {
constructor({projectId, credentials}: {projectId?: string; credentials?: boolean} = {}) {
super('CorsOriginError')

@@ -268,13 +268,23 @@ this.name = 'CorsOriginError'

const url = new URL(`https://sanity.io/manage/project/${projectId}/api`)
if (typeof location !== 'undefined') {
// Only build a deep-link when we know which project the user needs to
// configure - without `projectId` the management URL can't actually route
// them anywhere useful.
if (projectId && typeof location !== 'undefined') {
const url = new URL(`https://sanity.io/manage/project/${projectId}/api`)
const {origin} = location
url.searchParams.set('cors', 'add')
url.searchParams.set('origin', origin)
if (credentials) {
// Pre-selects the "Allow credentials (token-based auth)" toggle in
// the Sanity management CORS form.
url.searchParams.set('credentials', '')
}
this.addOriginUrl = url
this.message = `The current origin is not allowed to connect to the Live Content API. Add it here: ${url}`
} else if (projectId) {
this.message = `The current origin is not allowed to connect to the Live Content API. Change your configuration here: https://sanity.io/manage/project/${projectId}/api`
} else {
this.message = `The current origin is not allowed to connect to the Live Content API. Change your configuration here: ${url}`
this.message = `The current origin is not allowed to connect to the Live Content API.`
}
}
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display