@sanity/sdk
Version:
1,431 lines (1,398 loc) • 80.9 kB
TypeScript
import * as _sanity_client12 from "@sanity/client";
import { ClientConfig, ClientPerspective, ListenEvent, ResponseQueryOptions, SanityClient, SanityDocument as SanityDocument$1, SanityProject as SanityProject$1, StackablePerspective } from "@sanity/client";
import { Observable, Subject } from "rxjs";
import { CurrentUser, CurrentUser as CurrentUser$1, Mutation, PatchOperations, Role, SanityDocument, SanityDocument as SanityDocument$2, SanityDocumentLike } from "@sanity/types";
import * as _sanity_comlink3 from "@sanity/comlink";
import { ChannelInput, ChannelInstance, Controller, Message, Node, NodeInput, Status } from "@sanity/comlink";
import { PatchMutation } from "@sanity/mutate/_unstable_store";
import { SanityDocument as SanityDocument$3, SanityProjectionResult, SanityQueryResult } from "groq";
import { CanvasResource, MediaResource, StudioResource } from "@sanity/message-protocol";
import { getIndexForKey, getPathDepth, joinPaths, jsonMatch, slicePath, stringifyPath } from "@sanity/json-match";
/**
* Represents the various states the authentication type can be in.
*
* @public
*/
declare enum AuthStateType {
LOGGED_IN = "logged-in",
LOGGING_IN = "logging-in",
ERROR = "error",
LOGGED_OUT = "logged-out",
}
/**
* Configuration for an authentication provider
* @public
*/
interface AuthProvider {
/**
* Unique identifier for the auth provider (e.g., 'google', 'github')
*/
name: string;
/**
* Display name for the auth provider in the UI
*/
title: string;
/**
* Complete authentication URL including callback and token parameters
*/
url: string;
/**
* Optional URL for direct sign-up flow
*/
signUpUrl?: string;
}
/**
* Configuration options for creating an auth store.
*
* @public
*/
interface AuthConfig {
/**
* The initial location href to use when handling auth callbacks.
* Defaults to the current window location if available.
*/
initialLocationHref?: string;
/**
* Factory function to create a SanityClient instance.
* Defaults to the standard Sanity client factory if not provided.
*/
clientFactory?: (config: ClientConfig) => SanityClient;
/**
* Custom authentication providers to use instead of or in addition to the default ones.
* Can be an array of providers or a function that takes the default providers and returns
* a modified array or a Promise resolving to one.
*/
providers?: AuthProvider[] | ((prev: AuthProvider[]) => AuthProvider[] | Promise<AuthProvider[]>);
/**
* The API hostname for requests. Usually leave this undefined, but it can be set
* if using a custom domain or CNAME for the API endpoint.
*/
apiHost?: string;
/**
* Storage implementation to persist authentication state.
* Defaults to `localStorage` if available.
*/
storageArea?: Storage;
/**
* A callback URL for your application.
* If none is provided, the auth API will redirect back to the current location (`location.href`).
* When handling callbacks, this URL's pathname is checked to ensure it matches the callback.
*/
callbackUrl?: string;
/**
* A static authentication token to use instead of handling the OAuth flow.
* When provided, the auth store will remain in a logged-in state with this token,
* ignoring any storage or callback handling.
*/
token?: string;
}
/**
* Represents the minimal configuration required to identify a Sanity project.
* @public
*/
interface ProjectHandle<TProjectId extends string = string> {
projectId?: TProjectId;
}
/**
* @public
*/
type ReleasePerspective = {
releaseName: string;
excludedPerspectives?: StackablePerspective[];
};
/**
* @public
*/
interface PerspectiveHandle {
perspective?: ClientPerspective | ReleasePerspective;
}
/**
* @public
*/
interface DatasetHandle<TDataset extends string = string, TProjectId extends string = string> extends ProjectHandle<TProjectId>, PerspectiveHandle {
dataset?: TDataset;
}
/**
* Identifies a specific document type within a Sanity dataset and project.
* Includes `projectId`, `dataset`, and `documentType`.
* Optionally includes a `documentId`, useful for referencing a specific document type context, potentially without a specific document ID.
* @public
*/
interface DocumentTypeHandle<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DatasetHandle<TDataset, TProjectId> {
documentId?: string;
documentType: TDocumentType;
}
/**
* Uniquely identifies a specific document within a Sanity dataset and project.
* Includes `projectId`, `dataset`, `documentType`, and the required `documentId`.
* Commonly used by document-related hooks and components to reference a document without fetching its full content initially.
* @public
*/
interface DocumentHandle<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentTypeHandle<TDocumentType, TDataset, TProjectId> {
documentId: string;
}
/**
* Represents the complete configuration for a Sanity SDK instance
* @public
*/
interface SanityConfig extends DatasetHandle, PerspectiveHandle {
/**
* Authentication configuration for the instance
* @remarks Merged with parent configurations when using createChild
*/
auth?: AuthConfig;
/**
* Studio mode configuration for use of the SDK in a Sanity Studio
* @remarks Controls whether studio mode features are enabled
*/
studioMode?: {
enabled: boolean;
};
}
/**
* Represents a Sanity.io resource instance with its own configuration and lifecycle
* @remarks Instances form a hierarchy through parent/child relationships
*
* @public
*/
interface SanityInstance {
/**
* Unique identifier for this instance
* @remarks Generated using crypto.randomUUID()
*/
readonly instanceId: string;
/**
* Resolved configuration for this instance
* @remarks Merges values from parent instances where appropriate
*/
readonly config: SanityConfig;
/**
* Checks if the instance has been disposed
* @returns true if dispose() has been called
*/
isDisposed(): boolean;
/**
* Disposes the instance and cleans up associated resources
* @remarks Triggers all registered onDispose callbacks
*/
dispose(): void;
/**
* Registers a callback to be invoked when the instance is disposed
* @param cb - Callback to execute on disposal
* @returns Function to unsubscribe the callback
*/
onDispose(cb: () => void): () => void;
/**
* Gets the parent instance in the hierarchy
* @returns Parent instance or undefined if this is the root
*/
getParent(): SanityInstance | undefined;
/**
* Creates a child instance with merged configuration
* @param config - Configuration to merge with parent values
* @remarks Child instances inherit parent configuration but can override values
*/
createChild(config: SanityConfig): SanityInstance;
/**
* Traverses the instance hierarchy to find the first instance whose configuration
* matches the given target config using a shallow comparison.
* @param targetConfig - A partial configuration object containing key-value pairs to match.
* @returns The first matching instance or undefined if no match is found.
*/
match(targetConfig: Partial<SanityConfig>): SanityInstance | undefined;
}
/**
* Creates a new Sanity resource instance
* @param config - Configuration for the instance (optional)
* @returns A configured SanityInstance
* @remarks When creating child instances, configurations are merged with parent values
*
* @public
*/
declare function createSanityInstance(config?: SanityConfig): SanityInstance;
/**
* Represents a store action that has been bound to a specific store instance
*/
type BoundStoreAction<_TState, TParams extends unknown[], TReturn> = (instance: SanityInstance, ...params: TParams) => TReturn;
/**
* Creates an action binder function that uses the provided key function
* to determine how store instances are shared between Sanity instances
*
* @param keyFn - Function that generates a key from a Sanity config
* @returns A function that binds store actions to Sanity instances
*
* @remarks
* Action binders determine how store instances are shared across multiple
* Sanity instances. The key function determines which instances share state.
*
* @example
* ```ts
* // Create a custom binder that uses a tenant ID for isolation
* const bindActionByTenant = createActionBinder(config => config.tenantId || 'default')
*
* // Use the custom binder with a store definition
* const getTenantUsers = bindActionByTenant(
* userStore,
* ({state}) => state.get().users
* )
* ```
*/
/**
* Represents the various states the authentication can be in.
*
* @public
*/
type AuthState = LoggedInAuthState | LoggedOutAuthState | LoggingInAuthState | ErrorAuthState;
/**
* Logged-in state from the auth state.
* @public
*/
type LoggedInAuthState = {
type: AuthStateType.LOGGED_IN;
token: string;
currentUser: CurrentUser$1 | null;
lastTokenRefresh?: number;
};
/**
* Logged-out state from the auth state.
* @public
*/
type LoggedOutAuthState = {
type: AuthStateType.LOGGED_OUT;
isDestroyingSession: boolean;
};
/**
* Logging-in state from the auth state.
* @public
*/
type LoggingInAuthState = {
type: AuthStateType.LOGGING_IN;
isExchangingToken: boolean;
};
/**
* Error state from the auth state.
* @public
*/
type ErrorAuthState = {
type: AuthStateType.ERROR;
error: unknown;
};
/**
* Represents the various states the authentication can be in.
*
* @public
*/
interface DashboardContext {
mode?: string;
env?: string;
orgId?: string;
}
type AuthMethodOptions = 'localstorage' | 'cookie' | undefined;
/**
* @public
*/
interface AuthStoreState {
authState: AuthState;
providers?: AuthProvider[];
options: {
initialLocationHref: string;
clientFactory: (config: ClientConfig) => SanityClient;
customProviders: AuthConfig['providers'];
storageKey: string;
storageArea: Storage | undefined;
apiHost: string | undefined;
loginUrl: string;
callbackUrl: string | undefined;
providedToken: string | undefined;
authMethod: AuthMethodOptions;
};
dashboardContext?: DashboardContext;
}
/**
* @public
*/
declare const getCurrentUserState: BoundStoreAction<AuthStoreState, [], StateSource<CurrentUser$1 | null>>;
/**
* @public
*/
declare const getTokenState: BoundStoreAction<AuthStoreState, [], StateSource<string | null>>;
/**
* @internal
*/
/**
* @public
*/
declare const getLoginUrlState: BoundStoreAction<AuthStoreState, [], StateSource<string>>;
/**
* @public
*/
declare const getAuthState: BoundStoreAction<AuthStoreState, [], StateSource<AuthState>>;
/**
* @public
*/
declare const getDashboardOrganizationId: BoundStoreAction<AuthStoreState, [], StateSource<string | undefined>>;
/**
* Returns a state source indicating if the SDK is running within a dashboard context.
* @public
*/
declare const getIsInDashboardState: BoundStoreAction<AuthStoreState, [], StateSource<boolean>>;
/**
* Action to explicitly set the authentication token.
* Used internally by the Comlink token refresh.
* @internal
*/
declare const setAuthToken: BoundStoreAction<AuthStoreState, [token: string | null], void>;
/**
* Error message returned by the organization verification
* @public
*/
interface OrgVerificationResult {
error: string | null;
}
/**
* Compares a project's actual organization ID with the expected organization ID.
* @public
*/
/**
* Creates an observable that emits the organization verification state for a given instance.
* It combines the dashboard organization ID (from auth context) with the
* project's actual organization ID (fetched via getProjectState) and compares them.
* @public
*/
declare function observeOrganizationVerificationState(instance: SanityInstance, projectIds: string[]): Observable<OrgVerificationResult>;
/**
* @public
*/
declare const handleAuthCallback: BoundStoreAction<AuthStoreState, [locationHref?: string | undefined], Promise<string | false>>;
/**
* @public
*/
declare const logout: BoundStoreAction<AuthStoreState, [], Promise<void>>;
type AllowedClientConfigKey = 'useCdn' | 'token' | 'perspective' | 'apiHost' | 'proxy' | 'withCredentials' | 'timeout' | 'maxRetries' | 'dataset' | 'projectId' | 'requestTagPrefix' | 'useProjectHostname';
/**
* States tracked by the client store
* @public
*/
interface ClientStoreState {
token: string | null;
clients: { [TKey in string]?: SanityClient };
authMethod?: 'localstorage' | 'cookie';
}
/**
* Options used when retrieving a client instance from the client store.
*
* This interface extends the base {@link ClientConfig} and adds:
*
* - **apiVersion:** A required string indicating the API version for the client.
* - **scope:** An optional flag to choose between the project-specific client
* ('project') and the global client ('global'). When set to `'global'`, the
* global client is used.
*
* These options are utilized by `getClient` and `getClientState` to configure and
* return appropriate client instances that automatically handle authentication
* updates and configuration changes.
*
* @public
*/
interface ClientOptions extends Pick<ClientConfig, AllowedClientConfigKey> {
/**
* An optional flag to choose between the default client (typically project-level)
* and the global client ('global'). When set to `'global'`, the global client
* is used.
*/
'scope'?: 'default' | 'global';
/**
* A required string indicating the API version for the client.
*/
'apiVersion': string;
/**
* @internal
*/
'~experimental_resource'?: ClientConfig['~experimental_resource'];
}
/**
* Retrieves a Sanity client instance configured with the provided options.
*
* This function returns a client instance configured for the project or as a
* global client based on the options provided. It ensures efficient reuse of
* client instances by returning the same instance for the same options.
* For automatic handling of authentication token updates, consider using
* `getClientState`.
*
* @public
*/
declare const getClient: BoundStoreAction<ClientStoreState, [options: ClientOptions], SanityClient>;
/**
* Returns a state source for the Sanity client instance.
*
* This function provides a subscribable state source that emits updated client
* instances whenever relevant configurations change (such as authentication tokens).
* Use this when you need to react to client configuration changes in your application.
*
* @public
*/
declare const getClientState: BoundStoreAction<ClientStoreState, [options: ClientOptions], StateSource<SanityClient>>;
/**
* Message sent from a containing app to an iframe
* @public
*/
type FrameMessage = Message;
/**
* Message sent from an iframe to a containing app
* @public
*/
type WindowMessage = Message;
/**
* Message from SDK (iframe) to Parent (dashboard) to request a new token
* @internal
*/
type RequestNewTokenMessage = {
type: 'dashboard/v1/auth/tokens/create';
payload?: undefined;
};
/**
* Message from Parent (dashboard) to SDK (iframe) with the new token
* @internal
*/
type NewTokenResponseMessage = {
type: 'dashboard/v1/auth/tokens/create';
payload: {
token: string | null;
error?: string;
};
};
/**
* Individual channel with its relevant options
* @public
*/
interface ChannelEntry {
channel: ChannelInstance<FrameMessage, WindowMessage>;
options: ChannelInput;
refCount: number;
}
/**
* Internal state tracking comlink connections
* @public
*/
interface ComlinkControllerState {
controller: Controller | null;
controllerOrigin: string | null;
channels: Map<string, ChannelEntry>;
}
/**
* Calls the destroy method on the controller and resets the controller state.
* @public
*/
declare const destroyController: BoundStoreAction<ComlinkControllerState, [], void>;
/**
* Retrieve or create a channel to be used for communication between
* an application and the controller.
* @public
*/
declare const getOrCreateChannel: BoundStoreAction<ComlinkControllerState, [options: ChannelInput], ChannelInstance<_sanity_comlink3.Message, _sanity_comlink3.Message>>;
/**
* Initializes or fetches a controller to handle communication
* between an application and iframes.
* @public
*/
declare const getOrCreateController: BoundStoreAction<ComlinkControllerState, [targetOrigin: string], Controller>;
/**
* Signals to the store that the consumer has stopped using the channel
* @public
*/
declare const releaseChannel: BoundStoreAction<ComlinkControllerState, [name: string], void>;
/**
* Individual node with its relevant options
* @public
*/
interface NodeEntry {
node: Node<WindowMessage, FrameMessage>;
options: NodeInput;
status: Status;
statusUnsub?: () => void;
}
/**
* Internal state tracking comlink connections
* @public
*/
interface ComlinkNodeState {
nodes: Map<string, NodeEntry>;
subscriptions: Map<string, Set<symbol>>;
}
/**
* Signals to the store that the consumer has stopped using the node
* @public
*/
declare const releaseNode: BoundStoreAction<ComlinkNodeState, [name: string], void>;
/**
* Retrieve or create a node to be used for communication between
* an application and the controller -- specifically, a node should
* be created within a frame / window to communicate with the controller.
* @public
*/
declare const getOrCreateNode: BoundStoreAction<ComlinkNodeState, [options: NodeInput], Node<_sanity_comlink3.Message, _sanity_comlink3.Message>>;
/**
* @public
*/
interface NodeState {
node: Node<WindowMessage, FrameMessage>;
status: Status | undefined;
}
/**
* Provides a subscribable state source for a node by name
* @param instance - The Sanity instance to get the node state for
* @param nodeInput - The configuration for the node to get the state for
* @returns A subscribable state source for the node
* @public
*/
declare const getNodeState: BoundStoreAction<ComlinkNodeState, [NodeInput], StateSource<NodeState | undefined>>;
/**
* Creates or validates a `DocumentHandle` object.
* Ensures the provided object conforms to the `DocumentHandle` interface.
* @param handle - The object containing document identification properties.
* @returns The validated `DocumentHandle` object.
* @public
*/
declare function createDocumentHandle<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(handle: DocumentHandle<TDocumentType, TDataset, TProjectId>): DocumentHandle<TDocumentType, TDataset, TProjectId>;
/**
* Creates or validates a `DocumentTypeHandle` object.
* Ensures the provided object conforms to the `DocumentTypeHandle` interface.
* @param handle - The object containing document type identification properties.
* @returns The validated `DocumentTypeHandle` object.
* @public
*/
declare function createDocumentTypeHandle<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(handle: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>): DocumentTypeHandle<TDocumentType, TDataset, TProjectId>;
/**
* Creates or validates a `ProjectHandle` object.
* Ensures the provided object conforms to the `ProjectHandle` interface.
* @param handle - The object containing project identification properties.
* @returns The validated `ProjectHandle` object.
* @public
*/
declare function createProjectHandle<TProjectId extends string = string>(handle: ProjectHandle<TProjectId>): ProjectHandle<TProjectId>;
/**
* Creates or validates a `DatasetHandle` object.
* Ensures the provided object conforms to the `DatasetHandle` interface.
* @param handle - The object containing dataset identification properties.
* @returns The validated `DatasetHandle` object.
* @public
*/
declare function createDatasetHandle<TDataset extends string = string, TProjectId extends string = string>(handle: DatasetHandle<TDataset, TProjectId>): DatasetHandle<TDataset, TProjectId>;
/** @public */
declare const getDatasetsState: BoundStoreAction<FetcherStoreState<[options?: ProjectHandle<string> | undefined], _sanity_client12.DatasetsResponse>, [options?: ProjectHandle<string> | undefined], StateSource<_sanity_client12.DatasetsResponse | undefined>>;
/** @public */
declare const resolveDatasets: BoundStoreAction<FetcherStoreState<[options?: ProjectHandle<string> | undefined], _sanity_client12.DatasetsResponse>, [options?: ProjectHandle<string> | undefined], Promise<_sanity_client12.DatasetsResponse>>;
/**
* Represents an action to create a new document.
* Specifies the document type and optionally a document ID (which will be treated as the published ID).
* @beta
*/
interface CreateDocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentTypeHandle<TDocumentType, TDataset, TProjectId> {
type: 'document.create';
}
/**
* Represents an action to delete an existing document.
* Requires the full document handle including the document ID.
* @beta
*/
interface DeleteDocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
type: 'document.delete';
}
/**
* Represents an action to edit an existing document using patches.
* Requires the full document handle and an array of patch operations.
* @beta
*/
interface EditDocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
type: 'document.edit';
patches?: PatchOperations[];
}
/**
* Represents an action to publish the draft version of a document.
* Requires the full document handle.
* @beta
*/
interface PublishDocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
type: 'document.publish';
}
/**
* Represents an action to unpublish a document, moving its published content to a draft.
* Requires the full document handle.
* @beta
*/
interface UnpublishDocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
type: 'document.unpublish';
}
/**
* Represents an action to discard the draft changes of a document.
* Requires the full document handle.
* @beta
*/
interface DiscardDocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {
type: 'document.discard';
}
/**
* Union type representing all possible document actions within the SDK.
* @beta
*/
type DocumentAction<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string> = CreateDocumentAction<TDocumentType, TDataset, TProjectId> | DeleteDocumentAction<TDocumentType, TDataset, TProjectId> | EditDocumentAction<TDocumentType, TDataset, TProjectId> | PublishDocumentAction<TDocumentType, TDataset, TProjectId> | UnpublishDocumentAction<TDocumentType, TDataset, TProjectId> | DiscardDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Creates a `CreateDocumentAction` object.
* @param doc - A handle identifying the document type, dataset, and project. An optional `documentId` can be provided.
* @returns A `CreateDocumentAction` object ready for dispatch.
* @beta
*/
declare function createDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>): CreateDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Creates a `DeleteDocumentAction` object.
* @param doc - A handle uniquely identifying the document to be deleted.
* @returns A `DeleteDocumentAction` object ready for dispatch.
* @beta
*/
declare function deleteDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentHandle<TDocumentType, TDataset, TProjectId>): DeleteDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Creates an `EditDocumentAction` object with patches for modifying a document.
* Accepts patches in either the standard `PatchOperations` format or as a `SanityMutatePatchMutation` from `@sanity/mutate`.
*
* @param doc - A handle uniquely identifying the document to be edited.
* @param sanityMutatePatch - A patch mutation object from `@sanity/mutate`.
* @returns An `EditDocumentAction` object ready for dispatch.
* @beta
*/
declare function editDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentHandle<TDocumentType, TDataset, TProjectId>, sanityMutatePatch: PatchMutation): EditDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Creates an `EditDocumentAction` object with patches for modifying a document.
*
* @param doc - A handle uniquely identifying the document to be edited.
* @param patches - A single patch operation or an array of patch operations.
* @returns An `EditDocumentAction` object ready for dispatch.
* @beta
*/
declare function editDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentHandle<TDocumentType, TDataset, TProjectId>, patches?: PatchOperations | PatchOperations[]): EditDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Creates a `PublishDocumentAction` object.
* @param doc - A handle uniquely identifying the document to be published.
* @returns A `PublishDocumentAction` object ready for dispatch.
* @beta
*/
declare function publishDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentHandle<TDocumentType, TDataset, TProjectId>): PublishDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Creates an `UnpublishDocumentAction` object.
* @param doc - A handle uniquely identifying the document to be unpublished.
* @returns An `UnpublishDocumentAction` object ready for dispatch.
* @beta
*/
declare function unpublishDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentHandle<TDocumentType, TDataset, TProjectId>): UnpublishDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Creates a `DiscardDocumentAction` object.
* @param doc - A handle uniquely identifying the document whose draft changes are to be discarded.
* @returns A `DiscardDocumentAction` object ready for dispatch.
* @beta
*/
declare function discardDocument<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(doc: DocumentHandle<TDocumentType, TDataset, TProjectId>): DiscardDocumentAction<TDocumentType, TDataset, TProjectId>;
/**
* Represents a set of document that will go into `applyMutations`. Before
* applying a mutation, it's expected that all relevant documents that the
* mutations affect are included, including those that do not exist yet.
* Documents that don't exist have a `null` value.
*/
type DocumentSet<TDocument extends SanityDocument$2 = SanityDocument$2> = { [TDocumentId in string]?: TDocument | null };
/**
* Implements ID generation:
*
* A create mutation creates a new document. It takes the literal document
* content as its argument. The rules for the new document's identifier are as
* follows:
*
* - If the `_id` attribute is missing, then a new, random, unique ID is
* generated.
* - If the `_id` attribute is present but ends with `.`, then it is used as a
* prefix for a new, random, unique ID.
* - If the _id attribute is present, it is used as-is.
*
* [- source](https://www.sanity.io/docs/http-mutations#c732f27330a4)
*/
/** @beta */
interface ActionsResult<TDocument extends SanityDocument$3 = SanityDocument$3> {
transactionId: string;
documents: DocumentSet<TDocument>;
previous: DocumentSet<TDocument>;
previousRevs: {
[documentId: string]: string | undefined;
};
appeared: string[];
updated: string[];
disappeared: string[];
submitted: () => ReturnType<SanityClient['action']>;
}
/** @beta */
interface ApplyDocumentActionsOptions {
/**
* Optionally provide an ID to be used as this transaction ID
*/
transactionId?: string;
/**
* Set this to true to prevent this action from being batched with others.
*/
disableBatching?: boolean;
}
/** @beta */
declare function applyDocumentActions<TDocumentType extends string = string, TDataset extends string = string, TProjectId extends string = string>(instance: SanityInstance, action: DocumentAction<TDocumentType, TDataset, TProjectId> | DocumentAction<TDocumentType, TDataset, TProjectId>[], options?: ApplyDocumentActionsOptions): Promise<ActionsResult<SanityDocument$3<TDocumentType, `${TProjectId}.${TDataset}`>>>;
/** @beta */
declare function applyDocumentActions(instance: SanityInstance, action: DocumentAction | DocumentAction[], options?: ApplyDocumentActionsOptions): Promise<ActionsResult>;
declare interface AccessAttributeNode extends BaseNode {
type: 'AccessAttribute';
base?: ExprNode;
name: string;
}
declare interface AccessElementNode extends BaseNode {
type: 'AccessElement';
base: ExprNode;
index: number;
}
declare interface AndNode extends BaseNode {
type: 'And';
left: ExprNode;
right: ExprNode;
}
declare type AnyStaticValue = StringValue | NumberValue | NullValue | BooleanValue | DateTimeValue | ObjectValue | ArrayValue | PathValue;
declare interface ArrayCoerceNode extends BaseNode {
type: 'ArrayCoerce';
base: ExprNode;
}
declare interface ArrayElementNode extends BaseNode {
type: 'ArrayElement';
value: ExprNode;
isSplat: boolean;
}
declare interface ArrayNode extends BaseNode {
type: 'Array';
elements: ArrayElementNode[];
}
/** Describes a type node for array values. */
declare type ArrayValue = StaticValue<unknown[], 'array'>;
declare interface AscNode extends BaseNode {
type: 'Asc';
base: ExprNode;
}
/** The base interface for SyntaxNode. */
declare interface BaseNode {
type: string;
}
/** Describes a type node for boolean values, optionally including a value. If a value is provided it will always be the given boolean value. */
declare type BooleanValue = StaticValue<boolean, 'boolean'>;
declare interface Context {
timestamp: Date;
identity: string;
before: Value | null;
after: Value | null;
sanity?: {
projectId: string;
dataset: string;
};
dereference?: DereferenceFunction;
}
declare interface ContextNode extends BaseNode {
type: 'Context';
key: string;
}
/**
* createReferenceTypeNode creates a ObjectTypeNode representing a reference type
* it adds required attributes for a reference type.
* @param name - The name of the reference type
* @param inArray - Whether the reference is in an array
* @returns A ObjectTypeNode representing a reference type
* @internal
*/
declare class DateTime {
date: Date;
constructor(date: Date);
static parseToValue(str: string): Value;
equals(other: DateTime): boolean;
add(secs: number): DateTime;
difference(other: DateTime): number;
compareTo(other: DateTime): number;
toString(): string;
toJSON(): string;
}
declare type DateTimeValue = StaticValue<DateTime, 'datetime'>;
declare type DereferenceFunction = (obj: {
_ref: string;
}) => PromiseLike<Document_2 | null | undefined>;
declare interface DerefNode extends BaseNode {
type: 'Deref';
base: ExprNode;
}
declare interface DescNode extends BaseNode {
type: 'Desc';
base: ExprNode;
}
declare type Document_2 = {
_id?: string;
_type?: string;
[T: string]: unknown;
};
declare interface EverythingNode extends BaseNode {
type: 'Everything';
}
declare type Executor<N = ExprNode> = (node: N, scope: Scope) => Value | PromiseLike<Value>;
/**
* A node which can be evaluated into a value.
* @public
*/
declare type ExprNode = AccessAttributeNode | AccessElementNode | AndNode | ArrayNode | ArrayCoerceNode | AscNode | ContextNode | DerefNode | DescNode | EverythingNode | FilterNode | FlatMapNode | FuncCallNode | GroupNode | InRangeNode | MapNode | NegNode | NotNode | ObjectNode | OpCallNode | OrNode | ParameterNode | ParentNode_2 | PipeFuncCallNode | PosNode | ProjectionNode | SelectNode | SelectorNode | SliceNode | ThisNode | TupleNode | ValueNode;
declare interface FilterNode extends BaseNode {
type: 'Filter';
base: ExprNode;
expr: ExprNode;
}
declare interface FlatMapNode extends BaseNode {
type: 'FlatMap';
base: ExprNode;
expr: ExprNode;
}
declare interface FuncCallNode extends BaseNode {
type: 'FuncCall';
func: GroqFunction;
namespace: string;
name: string;
args: ExprNode[];
}
/** @public */
declare type GroqFunction = (args: GroqFunctionArg[], scope: Scope, execute: Executor) => PromiseLike<Value>;
/** @public */
declare type GroqFunctionArg = ExprNode;
declare type GroqPipeFunction = (base: Value, args: ExprNode[], scope: Scope, execute: Executor) => PromiseLike<Value>;
/**
* A type of a value in GROQ.
*/
declare type GroqType = 'null' | 'boolean' | 'number' | 'string' | 'array' | 'object' | 'path' | 'datetime';
declare interface GroupNode extends BaseNode {
type: 'Group';
base: ExprNode;
}
/** Describes a type node for inline values, including a name that references another type. */
declare interface InRangeNode extends BaseNode {
type: 'InRange';
base: ExprNode;
left: ExprNode;
right: ExprNode;
isInclusive: boolean;
}
declare interface MapNode extends BaseNode {
type: 'Map';
base: ExprNode;
expr: ExprNode;
}
declare interface NegNode extends BaseNode {
type: 'Neg';
base: ExprNode;
}
declare interface NotNode extends BaseNode {
type: 'Not';
base: ExprNode;
}
/** Describes a type node for null values, always being the null value. */
declare type NullValue = StaticValue<null, 'null'>;
/** Describes a type node for number values, optionally including a value. If a value is provided it will always be the given numeric value.*/
declare type NumberValue = StaticValue<number, 'number'>;
/** Describes a type node for object attributes, including a type and an optional flag for being optional. */
declare type ObjectAttributeNode = ObjectAttributeValueNode | ObjectConditionalSplatNode | ObjectSplatNode;
declare interface ObjectAttributeValueNode extends BaseNode {
type: 'ObjectAttributeValue';
name: string;
value: ExprNode;
}
declare interface ObjectConditionalSplatNode extends BaseNode {
type: 'ObjectConditionalSplat';
condition: ExprNode;
value: ExprNode;
}
declare interface ObjectNode extends BaseNode {
type: 'Object';
attributes: ObjectAttributeNode[];
}
declare interface ObjectSplatNode extends BaseNode {
type: 'ObjectSplat';
value: ExprNode;
}
/**
* Describes a type node for object values, including a collection of attributes and an optional rest value.
* The rest value can be another ObjectTypeNode, an UnknownTypeNode, or an InlineTypeNode.
* If the rest value is an ObjectTypeNode, it means that the object can have additional attributes.
* If the rest value is an UnknownTypeNode, the entire object is unknown.
* If the rest value is an InlineTypeNode, it means that the object has additional attributes from the referenced type.
*/
declare type ObjectValue = StaticValue<Record<string, unknown>, 'object'>;
declare type OpCall = '==' | '!=' | '>' | '>=' | '<' | '<=' | '+' | '-' | '*' | '/' | '%' | '**' | 'in' | 'match';
declare interface OpCallNode extends BaseNode {
type: 'OpCall';
op: OpCall;
left: ExprNode;
right: ExprNode;
}
declare interface OrNode extends BaseNode {
type: 'Or';
left: ExprNode;
right: ExprNode;
}
declare interface ParameterNode extends BaseNode {
type: 'Parameter';
name: string;
}
declare interface ParentNode_2 extends BaseNode {
type: 'Parent';
n: number;
}
declare class Path {
private pattern;
private patternRe;
constructor(pattern: string);
matches(str: string): boolean;
toJSON(): string;
}
declare type PathValue = StaticValue<Path, 'path'>;
declare interface PipeFuncCallNode extends BaseNode {
type: 'PipeFuncCall';
func: GroqPipeFunction;
base: ExprNode;
name: string;
args: ExprNode[];
}
declare interface PosNode extends BaseNode {
type: 'Pos';
base: ExprNode;
}
/** Union of any primitive type nodes. */
declare interface ProjectionNode extends BaseNode {
type: 'Projection';
base: ExprNode;
expr: ExprNode;
}
/** A schema consisting of a list of Document or TypeDeclaration items, allowing for complex type definitions. */
declare class Scope {
params: Record<string, unknown>;
source: Value;
value: Value;
parent: Scope | null;
context: Context;
isHidden: boolean;
constructor(params: Record<string, unknown>, source: Value, value: Value, context: Context, parent: Scope | null);
createNested(value: Value): Scope;
createHidden(value: Value): Scope;
}
declare interface SelectAlternativeNode extends BaseNode {
type: 'SelectAlternative';
condition: ExprNode;
value: ExprNode;
}
declare interface SelectNode extends BaseNode {
type: 'Select';
alternatives: SelectAlternativeNode[];
fallback?: ExprNode;
}
declare interface SelectorNode extends BaseNode {
type: 'Selector';
}
declare interface SliceNode extends BaseNode {
type: 'Slice';
base: ExprNode;
left: number;
right: number;
isInclusive: boolean;
}
declare class StaticValue<P, T extends GroqType> {
data: P;
type: T;
constructor(data: P, type: T);
isArray(): boolean;
get(): Promise<any>;
[Symbol.asyncIterator](): Generator<Value, void, unknown>;
}
declare class StreamValue {
type: 'stream';
private generator;
private ticker;
private isDone;
private data;
constructor(generator: () => AsyncGenerator<Value, void, unknown>);
isArray(): boolean;
get(): Promise<any>;
[Symbol.asyncIterator](): AsyncGenerator<Value, void, unknown>;
_nextTick(): Promise<void>;
}
/** Describes a type node for string values, optionally including a value. If a value is provided it will always be the given string value. */
declare type StringValue = StaticValue<string, 'string'>;
/** Any sort of node which appears as syntax */
declare interface ThisNode extends BaseNode {
type: 'This';
}
declare interface TupleNode extends BaseNode {
type: 'Tuple';
members: Array<ExprNode>;
}
/** Defines a type declaration with a specific name and a value that describes the structure of the type using a TypeNode. */
/**
* The result of an expression.
*/
declare type Value = AnyStaticValue | StreamValue;
declare interface ValueNode<P = any> {
type: 'Value';
value: P;
}
/**
* Represents a reactive state source that provides synchronized access to store data
*
* @remarks
* Designed to work with React's useSyncExternalStore hook. Provides three ways to access data:
* 1. `getCurrent()` for synchronous current value access
* 2. `subscribe()` for imperative change notifications
* 3. `observable` for reactive stream access
*
* @public
*/
interface StateSource<T> {
/**
* Subscribes to state changes with optional callback
* @param onStoreChanged - Called whenever relevant state changes occur
* @returns Unsubscribe function to clean up the subscription
*/
subscribe: (onStoreChanged?: () => void) => () => void;
/**
* Gets the current derived state value
*
* @remarks
* Safe to call without subscription. Will always return the latest value
* based on the current store state and selector parameters.
*/
getCurrent: () => T;
/**
* Observable stream of state values
*
* @remarks
* Shares a single underlying subscription between all observers. Emits:
* - Immediately with current value on subscription
* - On every relevant state change
* - Errors if selector throws
*/
observable: Observable<T>;
}
/**
* Context passed to selectors when deriving state
*
* @remarks
* Provides access to both the current state value and the Sanity instance,
* allowing selectors to use configuration values when computing derived state.
* The context is memoized for each state object and instance combination
* to optimize performance and prevent unnecessary recalculations.
*
* @example
* ```ts
* // Using both state and instance in a selector (psuedo example)
* const getUserByProjectId = createStateSourceAction(
* ({ state, instance }: SelectorContext<UsersState>, options?: ProjectHandle) => {
* const allUsers = state.users
* const projectId = options?.projectId ?? instance.config.projectId
* return allUsers.filter(user => user.projectId === projectId)
* }
* )
* ```
*/
interface SelectorContext<TState> {
/**
* The current state object from the store
*/
state: TState;
/**
* The Sanity instance associated with this state
*/
instance: SanityInstance;
}
/**
* Function type for selecting derived state from store state and parameters
* @public
*/
type Selector<TState, TParams extends unknown[], TReturn> = (context: SelectorContext<TState>, ...params: TParams) => TReturn;
/**
* Configuration options for creating a state source action
*/
type ActionMap = {
create: 'sanity.action.document.version.create';
discard: 'sanity.action.document.version.discard';
unpublish: 'sanity.action.document.unpublish';
delete: 'sanity.action.document.delete';
edit: 'sanity.action.document.edit';
publish: 'sanity.action.document.publish';
};
type OptimisticLock = {
ifDraftRevisionId?: string;
ifPublishedRevisionId?: string;
};
type HttpAction = {
actionType: ActionMap['create'];
publishedId: string;
attributes: SanityDocumentLike;
} | {
actionType: ActionMap['discard'];
versionId: string;
purge?: boolean;
} | {
actionType: ActionMap['unpublish'];
draftId: string;
publishedId: string;
} | {
actionType: ActionMap['delete'];
publishedId: string;
includeDrafts?: string[];
} | {
actionType: ActionMap['edit'];
draftId: string;
publishedId: string;
patch: PatchOperations;
} | ({
actionType: ActionMap['publish'];
draftId: string;
publishedId: string;
} & OptimisticLock);
/**
* Represents a transaction that is queued to be applied but has not yet been
* applied. A transaction will remain in a queued state until all required
* documents for the transactions are available locally.
*/
interface QueuedTransaction {
/**
* the ID of this transaction. this is generated client-side.
*/
transactionId: string;
/**
* the high-level actions associated with this transaction. note that these
* actions don't mention draft IDs and is meant to abstract away the draft
* model from users.
*/
actions: DocumentAction[];
/**
* An optional flag set to disable this transaction from being batched with
* other transactions.
*/
disableBatching?: boolean;
}
/**
* Represents a transaction that has been applied locally but has not been
* committed/transitioned-to-outgoing. These transactions are visible to the
* user but may be rebased upon a new working document set. Applied transactions
* also contain the resulting `outgoingActions` that will be submitted to
* Content Lake. These `outgoingActions` depend on the state of the working
* documents so they are recomputed on rebase and are only relevant to applied
* actions (we cannot compute `outgoingActions` for queued transactions because
* we haven't resolved the set of documents the actions are dependent on yet).
*
* In order to support better conflict resolution, the original `previous` set
* is saved as the `base` set.
*/
interface AppliedTransaction extends QueuedTransaction {
/**
* the resulting set of documents after the actions have been applied
*/
working: DocumentSet;
/**
* the previous set of documents before the action was applied
*/
previous: DocumentSet;
/**
* the original `previous` document set captured when this action was
* originally applied. this is used as a reference point to do a 3-way merge
* if this applied transaction ever needs to be reapplied on a different
* set of documents.
*/
base: DocumentSet;
/**
* the `_rev`s from `previous` document set
*/
previousRevs: { [TDocumentId in string]?: string };
/**
* a timestamp for when this transaction was applied locally
*/
timestamp: string;
/**
* the resulting HTTP actions derived from the state of the `working` document
* set. these are sent to Content Lake as-is when this transaction is batched
* and transitioned into an outgoing transaction.
*/
outgoingActions: HttpAction[];
/**
* similar to `outgoingActions` but comprised of mutations instead of action.
* this left here for debugging purposes but could be used to send mutations
* to Content Lake instead of actions.
*/
outgoingMutations: Mutation[];
}
/**
* Represents a set of applied transactions batched into a single outgoing
* transaction. An outgoing transaction is the result of batching many applied
* actions. An outgoing transaction may be reverted locally if the server
* does not accept it.
*/
interface OutgoingTransaction extends AppliedTransaction {
disableBatching: boolean;
batchedTransactionIds: string[];
}
interface UnverifiedDocumentRevision {
transactionId: string;
documentId: string;
previousRev: string | undefined;
timestamp: string;
}
type Grant = 'read' | 'update' | 'create' | 'history';
/** @beta */
interface PermissionDeniedReason {
type: 'precondition' | 'access';
message: string;
documentId?: string;
}
/** @beta */
type DocumentPermissionsResult = {
allowed: false;
message: string;
reasons: PermissionDeniedReason[];
} | {
allowed: true;
message?: undefined;
reasons?: undefined;
};
/** @beta */
type DocumentEvent = ActionErrorEvent | TransactionRevertedEvent | TransactionAcceptedEvent | DocumentRebaseErrorEvent | DocumentEditedEvent | DocumentCreatedEvent | DocumentDeletedEvent | DocumentPublishedEvent | DocumentUnpublishedEvent | DocumentDiscardedEvent;
/**
* @beta
* Event emitted when a precondition to applying an action fails.
* (For example: when trying to edit a document that no longer exists.)
*/
interface ActionErrorEvent {
type: 'error';
documentId: string;
transactionId: string;
message: string;
error: unknown;
}
/**
* @beta
* Event emitted when a transaction is accepted.
*/
interface TransactionAcceptedEvent {
type: 'accepted';
outgoing: OutgoingTransaction;
result: Awaited<ReturnType<SanityClient['action']>>;
}
/**
* @beta
* Event emitted when a transaction is reverted.
*/
interface TransactionRevertedEvent {
type: 'reverted';
message: string;
error: unknown;
outgoing: OutgoingTransaction;
}
/**
* @beta
* Event emitted when an attempt to apply local changes to a modified remote document fails.
*/
interface DocumentRebaseErrorEvent {
type: 'rebase-error';
documentId: string;
transactionId: string;
message: string;
error: unknown;
}
/**
* @beta
* Event emitted when a document is edited.
*/
interface DocumentEditedEvent {
type: 'edited';
documentId: string;
outgoing: OutgoingTransaction;
}
/**
* @beta
* Event emitted when a document is created.
*/
interface DocumentCreatedEvent {
type: 'created';
documentId: string;
outgoing: OutgoingTransaction;
}
/**
* @beta
* Event emitted when a document is deleted.
*/
interface DocumentDeletedEvent {
type: 'deleted';
documentId: string;
outgoing: OutgoingTransaction;
}
/**
* @beta
* Event emitted when a document is published.
*/
interface DocumentPublishedEvent {
type: 'published';
documentId: string;
outgoing: OutgoingTransaction;
}
/**
* @beta
* Event emitted when a document is unpublished.
*/
interface DocumentUnpublishedEvent {
type: 'unpublished';
documentId: string;
outgoing: OutgoingTransaction;
}
/**
* @beta
* Event emitted when a document version is discarded.
*/
interface DocumentDiscardedEvent {
type: 'discarded';
documentId: string;
outgoing: OutgoingTransaction;
}
/**
* Split the entire path string on dots "outside" of any brackets.
*
* For example:
* ```
* "friends[0].name"
* ```
*
* becomes:
*
* ```
* [...ParseSegment<"friends[0]">, ...ParseSegment<"name">]
* ```
*
* (We use a simple recursion that splits on the first dot.)
*/
type PathParts<TPath extends string> = TPath extends `${infer Head}.${infer Tail}` ? [Head, ...PathParts<Tail>] : TPath extends '' ? [] : [TPath];
/**
* Given a type T and an array of "access keys" Parts, recursively index into T.
*
* If a part is a key, it looks up that property.
* If T is an array and the part is a number, it "indexes" into the element type.
*/
type DeepGet<TValue, TPath extends readonly (string | number)[]> = TPath extends [] ? TValue : TPath extends readonly [infer THead, ...infer TTail] ? DeepGet<TValue extends undefined | null ? undefined : THead extends keyof TValue ? TValue[THead] : THead extends number ? TValue extends readonly (infer TElement)[] ? TElement | undefined : undefined : undefined,
// Key/index doesn't exist
TTail extends readonly (string | number)[] ? TTail : []> : never;
/**
* Given a document type TDocument and a JSON Match path string TPath,
* compute the type found at that path.
* @beta
*/
type JsonMatch<TDocument, TPath extends string> = DeepGet<TDocument, PathParts<TPath>>;
/**
* Recursively traverse a value. When an array is encountered, ensure that
* each object item has a _key property. Memoized such that sub-objects that
* have not changed aren't re-computed.
*/
interface SharedListener {
events: Observable<Lis