@sanity/sdk 3.9.1 → 3.9.2-next.20261014121948

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 (27) hide show
  1. package/dist/_exports/_internal.d.ts +2 -2
  2. package/dist/_exports/_internal.js +9 -17
  3. package/dist/_exports/_internal.js.map +1 -1
  4. package/dist/_exports/agent.js +1 -1
  5. package/dist/_exports/comlink.d.ts +1 -0
  6. package/dist/_exports/comlink.d.ts.map +1 -1
  7. package/dist/_exports/comlink.js +1 -1
  8. package/dist/_exports/dashboard.d.ts +2 -2
  9. package/dist/_exports/dashboard.js +2 -2
  10. package/dist/{applications-DowvM7CN.d.ts → applications-CkDu_W5X.d.ts} +3 -3
  11. package/dist/{applications-DowvM7CN.d.ts.map → applications-CkDu_W5X.d.ts.map} +1 -1
  12. package/dist/{authStore-KH1GNOOB.js → authStore-DwKgZOzo.js} +8 -26
  13. package/dist/{authStore-KH1GNOOB.js.map → authStore-DwKgZOzo.js.map} +1 -1
  14. package/dist/{bus-ClLO0kQU.d.ts → bus-bvKfVggT.d.ts} +2 -2
  15. package/dist/{bus-ClLO0kQU.d.ts.map → bus-bvKfVggT.d.ts.map} +1 -1
  16. package/dist/{clientStore-DW3iIcbg.js → clientStore-DnMH7LPG.js} +2 -2
  17. package/dist/{clientStore-DW3iIcbg.js.map → clientStore-DnMH7LPG.js.map} +1 -1
  18. package/dist/{getNodeState-CkxvEZD5.js → getNodeState-Bp9sxNE1.js} +2 -5
  19. package/dist/{getNodeState-CkxvEZD5.js.map → getNodeState-Bp9sxNE1.js.map} +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +50 -74
  22. package/dist/index.js.map +1 -1
  23. package/dist/{topicStore-CU-oF3fc.js → topicStore-CNzfuPGY.js} +2 -2
  24. package/dist/{topicStore-CU-oF3fc.js.map → topicStore-CNzfuPGY.js.map} +1 -1
  25. package/dist/{version-7EzMhxQE.js → version-Bx_0tNcN.js} +10 -14
  26. package/dist/{version-7EzMhxQE.js.map → version-Bx_0tNcN.js.map} +1 -1
  27. package/package.json +4 -4
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["API_VERSION","serializeInclude","API_VERSION","API_VERSION","API_VERSION","API_VERSION","API_VERSION","resolveProjectId","API_VERSION","API_VERSION","getIndexForKey","jsonMatch","slicePath","stringifyPath","mergeMap","jsonMatch","stringifyPath","API_VERSION","jsonMatch","API_VERSION","map","retry","distinctUntilChanged","share","switchMap","catchError","\"_createdAt\"","\"_updatedAt\"","\"state\""],"sources":["../src/store/createSanityInstance.ts","../src/utils/buildQuery.ts","../src/applications/applications.ts","../src/installations/installations.ts","../src/organization/organization.ts","../src/access/checkPermissions.ts","../src/project/organizationVerification.ts","../src/projects/projects.ts","../src/project/project.ts","../src/auth/getOrganizationVerificationState.ts","../src/auth/oauth/pkce.ts","../src/auth/oauth/oauthActions.ts","../src/auth/handleAuthCallback.ts","../src/auth/logout.ts","../src/comments/weakenReferences.ts","../src/comments/commentActions.ts","../src/config/handles.ts","../src/config/loggingConfig.ts","../src/datasets/datasets.ts","../src/document/util.ts","../src/document/actions.ts","../src/document/documentConstants.ts","../src/document/processActions/releaseUtil.ts","../src/document/patchOperations.ts","../src/document/processMutations.ts","../src/document/listen.ts","../src/utils/MultiKeyWeakMap.ts","../src/document/processActions/shared.ts","../src/document/permissions.ts","../src/document/processActions/create.ts","../src/document/processActions/delete.ts","../src/document/processActions/discard.ts","../src/document/processActions/edit.ts","../src/document/processActions/publish.ts","../src/document/processActions/releaseArchive.ts","../src/document/processActions/releaseCreate.ts","../src/document/processActions/releaseDelete.ts","../src/document/processActions/releaseEdit.ts","../src/document/processActions/releasePublish.ts","../src/document/processActions/releaseSchedule.ts","../src/document/processActions/unpublish.ts","../src/document/processActions/processActions.ts","../src/document/reducers.ts","../src/document/events.ts","../src/document/listenerEventOperators.ts","../src/document/sharedListener.ts","../src/document/submissionErrors.ts","../src/document/documentStore.ts","../src/document/resourceRules.ts","../src/document/applyDocumentActions.ts","../src/organizations/organizations.ts","../src/users/usersStore.ts","../src/presence/bifurTransport.ts","../src/presence/paths.ts","../src/presence/presenceStore.ts","../src/utils/hashString.ts","../src/projection/util.ts","../src/projection/projectionQuery.ts","../src/projection/statusQuery.ts","../src/projection/subscribeToStateAndFetchBatches.ts","../src/projection/projectionStore.ts","../src/projection/getProjectionState.ts","../src/projection/resolveProjection.ts","../src/typegen/defineProjection.ts","../src/utils/getCorsErrorProjectId.ts","../src/utils/isImportError.ts"],"sourcesContent":["import {type SanityConfig} from '../config/sanityConfig'\nimport {randomId} from '../utils/ids'\nimport {createLogger, type InstanceContext} from '../utils/logger'\n\n/**\n * Represents a Sanity.io resource instance with its own configuration and lifecycle\n *\n * @public\n */\nexport interface SanityInstance {\n /**\n * Unique identifier for this instance\n * @remarks Generated as a random 16-character alphanumeric ID\n */\n readonly instanceId: string\n\n /**\n * Resolved configuration for this instance\n */\n readonly config: SanityConfig\n\n /**\n * Checks if the instance has been disposed\n * @returns true if dispose() has been called\n */\n isDisposed(): boolean\n\n /**\n * Disposes the instance and cleans up associated resources\n * @remarks Triggers all registered onDispose callbacks\n */\n dispose(): void\n\n /**\n * Registers a callback to be invoked when the instance is disposed\n * @param cb - Callback to execute on disposal\n * @returns Function to unsubscribe the callback\n */\n onDispose(cb: () => void): () => void\n}\n\n/**\n * Creates a new Sanity resource instance\n * @param config - Configuration for the instance (optional)\n * @returns A configured SanityInstance\n *\n * @public\n */\nexport function createSanityInstance(config: SanityConfig = {}): SanityInstance {\n const instanceId = randomId(16)\n const disposeListeners = new Map<string, () => void>()\n const disposed = {current: false}\n\n // Create instance context for logging\n const instanceContext: InstanceContext = {\n instanceId,\n projectId: config.projectId,\n dataset: config.dataset,\n }\n\n // Create logger with instance context\n const logger = createLogger('sdk', {instanceContext})\n\n // Log instance creation\n logger.info('Sanity instance created', {\n hasProjectId: !!config.projectId,\n hasDataset: !!config.dataset,\n hasAuth: !!config.auth,\n hasPerspective: !!config.perspective,\n })\n\n // Log configuration details at debug level\n logger.debug('Instance configuration', {\n projectId: config.projectId,\n dataset: config.dataset,\n perspective: config.perspective,\n hasStudioConfig: !!config.studio,\n hasStudioTokenSource: !!config.studio?.auth?.token,\n hasAuthProviders: !!config.auth?.providers,\n hasAuthToken: !!config.auth?.token,\n })\n\n const instance: SanityInstance = {\n instanceId,\n config,\n isDisposed: () => disposed.current,\n dispose: () => {\n if (disposed.current) {\n logger.trace('Dispose called on already disposed instance', {internal: true})\n return\n }\n logger.trace('Disposing instance', {\n internal: true,\n listenerCount: disposeListeners.size,\n })\n disposed.current = true\n disposeListeners.forEach((listener) => listener())\n disposeListeners.clear()\n logger.info('Instance disposed')\n },\n onDispose: (cb) => {\n const listenerId = randomId(16)\n disposeListeners.set(listenerId, cb)\n return () => {\n disposeListeners.delete(listenerId)\n }\n },\n }\n\n return instance\n}\n","/**\n * Serialises store options into a `@sanity/client` request query object.\n * Scalars are stringified and `undefined` values dropped; string arrays pass\n * through untouched so the client serialises them as repeated keys\n * (`permissions=a&permissions=b`).\n *\n * @internal\n */\nexport function buildQuery(\n params: Record<string, string | number | boolean | string[] | undefined>,\n): Record<string, string | string[]> {\n return Object.fromEntries(\n Object.entries(params)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => [key, Array.isArray(value) ? value : String(value)]),\n )\n}\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {defineFetcher, defineMutation} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\nimport {type Included, type IncludesAny} from '../utils/includeShape'\n\n// The Applications API is currently only available on vX.\nconst API_VERSION = 'vX'\n\n/**\n * Related data the Applications API can embed in a response. `interfaces`,\n * `workspaces` and `access` are children of the active deployment and are\n * embedded under {@link Application.activeDeployment}; `config.studio` and\n * `config.mfManifest` are embedded under {@link Application.config}.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationInclude =\n | 'interfaces'\n | 'workspaces'\n | 'access'\n | 'config.studio'\n | 'config.mfManifest'\n | 'activeDeployment'\n\ntype ApplicationInterfaceBase = {\n id: string\n name: string\n title: string\n version: string\n /** Module federation module ID; resolved from the host mf-manifest at runtime */\n moduleId: string\n}\n\n/**\n * Placement metadata for application and panel interfaces.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationInterfacePlacementMetadata = {\n dock?: {group?: string; order?: number}\n}\n\n/**\n * An interface exposed by a deployment, embedded when `interfaces` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationInterface = ApplicationInterfaceBase &\n (\n | {type: 'app'; metadata: ApplicationInterfacePlacementMetadata | null}\n | {type: 'panel'; metadata: ApplicationInterfacePlacementMetadata | null}\n | {type: 'asset_source'; metadata: null}\n | {type: 'worker'; metadata: null}\n | {type: 'tile'; metadata: {order?: number; size: 'small' | 'large' | 'banner'}}\n )\n\n/**\n * A studio workspace of a deployment, embedded when `workspaces` is included.\n * Studio applications only.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationWorkspace {\n id: string\n name: string\n title: string | null\n subtitle: string | null\n projectId: string\n dataset: string\n schemaDescriptorId: string | null\n basePath: string | null\n /** Sanitized SVG icon markup */\n icon: string | null\n}\n\n/**\n * A resource an application accesses, embedded when `access` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationAccess {\n id: string\n resourceType: 'datasets' | 'canvases' | 'dashboards' | 'media-libraries'\n resourceId: string\n}\n\n/**\n * Studio configuration, embedded under `config` when `config.studio` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationStudioConfig {\n projectId: string\n /** Release channel (`next`, `stable`, `latest`) or a pinned semver; null if unset */\n autoUpdatingVersion: string | null\n}\n\n/**\n * The active deployment as embedded under {@link Application.activeDeployment}.\n * Its `interfaces`, `workspaces` and `access` children are each present only\n * when the matching {@link ApplicationInclude} token was requested. `workspaces`\n * is omitted (not `null`) for non-studio applications even when requested.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationDeployment<Include extends ApplicationInclude = never> = {\n id: string\n applicationId: string\n size: number | null\n version: string | null\n isAutoUpdating: boolean | null\n isActiveDeployment: boolean\n deployedBy: string | null\n createdAt: string\n updatedAt: string\n} & Included<ApplicationInclude, 'interfaces', Include, {interfaces: ApplicationInterface[]}> &\n Included<ApplicationInclude, 'workspaces', Include, {workspaces?: ApplicationWorkspace[]}> &\n Included<ApplicationInclude, 'access', Include, {access: ApplicationAccess[]}>\n\n/**\n * The always-present fields of an org-owned application returned by the\n * Applications API (`/applications`), before any {@link ApplicationInclude}.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationBase {\n id: string\n type: 'studio' | 'coreApp'\n title: string\n /** Stable, immutable identity, distinct from the mutable `slug` address. */\n name: string\n /** Qualified, globally-unique handle: `sanity/<name>` for singletons, `<organizationId>/<name>` otherwise. */\n reference: string\n icon: string | null\n isSingleton: boolean\n visibility: 'default' | 'unlisted' | 'disabled'\n slug: string | null\n externalUrl: string | null\n organizationId: string\n createdAt: string\n updatedAt: string\n}\n\n// Requesting any of these tokens forces the deployment summary into the response.\ntype DeploymentToken = 'activeDeployment' | 'interfaces' | 'workspaces' | 'access'\ntype ConfigToken = 'config.studio' | 'config.mfManifest'\n\ntype ApplicationConfig<Include extends ApplicationInclude> = Included<\n ApplicationInclude,\n 'config.studio',\n Include,\n {studio?: ApplicationStudioConfig}\n> &\n Included<ApplicationInclude, 'config.mfManifest', Include, {mfManifest?: unknown}>\n\n// Brett returns `config: {}` when a requested config value is not stored.\ntype ApplicationConfigShape<Include extends ApplicationInclude> = [ApplicationInclude] extends [\n Include,\n]\n ? {config?: ApplicationConfig<Include>}\n : IncludesAny<Include, ConfigToken> extends true\n ? {config: ApplicationConfig<Include>}\n : unknown\n\n// `activeDeployment` is present when any deployment-related token was requested;\n// a wide include array makes it optional.\ntype ApplicationDeploymentShape<Include extends ApplicationInclude> = [ApplicationInclude] extends [\n Include,\n]\n ? {activeDeployment?: ApplicationDeployment<Include> | null}\n : IncludesAny<Include, DeploymentToken> extends true\n ? {activeDeployment: ApplicationDeployment<Include> | null}\n : unknown\n\n/**\n * An org-owned application as returned by the Applications API\n * (`/applications`). `config` and `activeDeployment` (with its\n * `interfaces`/`workspaces`/`access` children) are present only when the\n * corresponding {@link ApplicationInclude} tokens were requested.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type Application<Include extends ApplicationInclude = never> = ApplicationBase &\n ApplicationConfigShape<Include> &\n ApplicationDeploymentShape<Include>\n\n/**\n * Options for listing an organization's applications.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationsOptions<Include extends ApplicationInclude = ApplicationInclude> {\n organizationId: string\n /** Filter by application type */\n type?: string\n include?: Include[]\n /** Page size (1-100, default 50), or `'none'` to disable pagination */\n limit?: number | 'none'\n /** Cursor from a previous response's `nextCursor` */\n cursor?: string\n}\n\n/**\n * The cursor-paginated envelope returned by `GET /applications`.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationsResponse<Include extends ApplicationInclude = never> {\n nextCursor: string | null\n data: Application<Include>[]\n}\n\n/** The API takes `include` as a comma-separated list; sorted so key and query agree */\nfunction serializeInclude(include: ApplicationInclude[] | undefined): string | undefined {\n return include?.length ? [...include].sort().join(',') : undefined\n}\n\n/**\n * Fetcher for an organization's applications (`GET /applications`), on the\n * shared fetcher cache.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const applications = defineFetcher<\n [options: ApplicationsOptions],\n ApplicationsResponse<ApplicationInclude>\n>({\n name: 'applications',\n getKey: (_instance, options) =>\n [\n options.organizationId,\n options.type ?? '',\n serializeInclude(options.include) ?? '',\n options.limit ?? '',\n options.cursor ?? '',\n ].join(':'),\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<ApplicationsResponse<ApplicationInclude>>({\n url: '/applications',\n query: buildQuery({\n organizationId: options.organizationId,\n type: options.type,\n include: serializeInclude(options.include),\n limit: options.limit,\n cursor: options.cursor,\n }),\n tag: 'applications.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'application', id: 'LIST'},\n ...data.data.map((app) => ({type: 'application', id: app.id})),\n ],\n})\n\n/**\n * Fetcher for a single application (`GET /applications/:id`), on the shared\n * fetcher cache. No pre-seeding from {@link applications}: its list keys\n * include the organization, which this fetcher's params can't reconstruct.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const application = defineFetcher<\n [applicationId: string, options?: {include?: ApplicationInclude[]}],\n Application<ApplicationInclude>\n>({\n name: 'application',\n getKey: (_instance, applicationId, options) =>\n `${applicationId}:${serializeInclude(options?.include) ?? ''}`,\n fetch: (instance) => (applicationId, options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Application<ApplicationInclude>>({\n url: `/applications/${applicationId}`,\n query: buildQuery({include: serializeInclude(options?.include)}),\n tag: 'applications.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'application', id: data.id}],\n})\n\n/**\n * Confirmation returned by the API's soft-delete endpoints.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport interface DeletedResult {\n deleted: boolean\n}\n\n/**\n * Dashboard visibility for an application.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport type ApplicationVisibility = ApplicationBase['visibility']\n\n/**\n * Mutable properties of an application (`PATCH /applications/:id`) — the\n * writable subset of {@link ApplicationBase}, each optional, keyed by id.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport type UpdateApplicationInput = {applicationId: string} & Partial<\n Pick<ApplicationBase, 'title' | 'icon' | 'visibility'>\n>\n\n/**\n * Mutation to update an application (`PATCH /applications/:id`). Invalidates the\n * updated application and the list so active entries reconverge on server truth.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const updateApplication = defineMutation<UpdateApplicationInput, Application>({\n name: 'updateApplication',\n mutationFn:\n (instance) =>\n ({applicationId, ...body}) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Application>({\n url: `/applications/${applicationId}`,\n method: 'PATCH',\n body,\n tag: 'applications.update',\n }),\n ),\n ),\n invalidates: (_result, {applicationId}) => [\n {type: 'application', id: 'LIST'},\n {type: 'application', id: applicationId},\n ],\n})\n\n/**\n * Input for {@link deleteApplication}.\n *\n * @internal\n */\nexport interface DeleteApplicationInput {\n applicationId: string\n}\n\n/**\n * Mutation to soft-delete an application (`DELETE /applications/:id`).\n * Invalidates the deleted application and the list.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const deleteApplication = defineMutation<DeleteApplicationInput, DeletedResult>({\n name: 'deleteApplication',\n mutationFn:\n (instance) =>\n ({applicationId}) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<DeletedResult>({\n url: `/applications/${applicationId}`,\n method: 'DELETE',\n tag: 'applications.delete',\n }),\n ),\n ),\n invalidates: (_result, {applicationId}) => [\n {type: 'application', id: 'LIST'},\n {type: 'application', id: applicationId},\n ],\n})\n","import {switchMap} from 'rxjs'\n\nimport {type Application, type ApplicationInterface} from '../applications/applications'\nimport {getClientState} from '../client/clientStore'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\nimport {type Included} from '../utils/includeShape'\n\n// The Applications API is currently only available on vX.\nconst API_VERSION = 'vX'\n\n/**\n * Related data the Installations API can embed in a response. Each token adds\n * a top-level field to the {@link Installation} item.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type InstallationInclude = 'activeConfig' | 'access' | 'interfaces' | 'config.mfManifest'\n\n/**\n * A resource an installation accesses, embedded when `access` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationAccess {\n id: string\n resourceType: 'datasets' | 'canvases' | 'dashboards' | 'media-libraries'\n resourceId: string\n}\n\n/**\n * The active config of an installation, embedded when `activeConfig` is\n * included. `null` when nothing is deployed.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationActiveConfig {\n id: string\n installationId: string\n deployedBy: string | null\n version: string\n isActive: boolean\n createdAt: string\n}\n\n/**\n * The always-present fields of an active singleton installation returned by the\n * Installations API (`/installations`), before any {@link InstallationInclude}.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationBase {\n id: string\n applicationId: string\n organizationId: string\n installedBy: string | null\n createdAt: string\n updatedAt: string\n application: {\n title: string\n /** Stable, immutable identity of the installed singleton, distinct from `slug`. */\n name: string\n /** Qualified, globally-unique handle of the installed singleton (e.g. `sanity/<name>`). */\n reference: string\n slug: string | null\n /** Sanitized SVG icon markup, or `null` when the singleton has no icon. */\n icon: string | null\n /** The singleton's publishing organization; the top-level `organizationId` is the installing one. */\n organizationId: string\n }\n}\n\n/**\n * An active singleton installation as returned by the Installations API\n * (`/installations`). `activeConfig`, `access` and `interfaces` are top-level\n * fields, each present only when the matching {@link InstallationInclude} token\n * was requested.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type Installation<Include extends InstallationInclude = never> = InstallationBase &\n Included<\n InstallationInclude,\n 'config.mfManifest',\n Include,\n {\n application: InstallationBase['application'] &\n ([InstallationInclude] extends [Include]\n ? Partial<Pick<Application<'config.mfManifest'>, 'config'>>\n : Pick<Application<'config.mfManifest'>, 'config'>)\n }\n > &\n Included<\n InstallationInclude,\n 'activeConfig',\n Include,\n {activeConfig: InstallationActiveConfig | null}\n > &\n Included<InstallationInclude, 'access', Include, {access: InstallationAccess[]}> &\n Included<InstallationInclude, 'interfaces', Include, {interfaces: ApplicationInterface[]}>\n\n/**\n * Options for listing an organization's installations.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationsOptions<Include extends InstallationInclude = InstallationInclude> {\n organizationId: string\n include?: Include[]\n /** Page size (1-100, default 50), or `'none'` to disable pagination */\n limit?: number | 'none'\n /** Cursor from a previous response's `nextCursor` */\n cursor?: string\n}\n\n/**\n * The cursor-paginated envelope returned by `GET /installations`.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationsResponse<Include extends InstallationInclude = never> {\n nextCursor: string | null\n data: Installation<Include>[]\n}\n\n/** The API takes `include` as a comma-separated list; sorted so key and query agree */\nfunction serializeInclude(include: InstallationInclude[] | undefined): string | undefined {\n return include?.length ? [...include].sort().join(',') : undefined\n}\n\n/**\n * Fetcher for an organization's installations (`GET /installations`), on the\n * shared fetcher cache.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const installations = defineFetcher<\n [options: InstallationsOptions],\n InstallationsResponse<InstallationInclude>\n>({\n name: 'installations',\n getKey: (_instance, options) =>\n [\n options.organizationId,\n serializeInclude(options.include) ?? '',\n options.limit ?? '',\n options.cursor ?? '',\n ].join(':'),\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<InstallationsResponse<InstallationInclude>>({\n url: '/installations',\n query: buildQuery({\n organizationId: options.organizationId,\n include: serializeInclude(options.include),\n limit: options.limit,\n cursor: options.cursor,\n }),\n tag: 'installations.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'installation', id: 'LIST'},\n ...data.data.map((installation) => ({type: 'installation', id: installation.id})),\n ],\n})\n\n/**\n * Fetcher for a single installation (`GET /installations/:id`), on the shared\n * fetcher cache. No pre-seeding from {@link installations}: its list keys\n * include the organization, which this fetcher's params can't reconstruct.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const installation = defineFetcher<\n [installationId: string, options?: {include?: InstallationInclude[]}],\n Installation<InstallationInclude>\n>({\n name: 'installation',\n getKey: (_instance, installationId, options) =>\n `${installationId}:${serializeInclude(options?.include) ?? ''}`,\n fetch: (instance) => (installationId, options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Installation<InstallationInclude>>({\n url: `/installations/${installationId}`,\n query: buildQuery({include: serializeInclude(options?.include)}),\n tag: 'installations.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'installation', id: data.id}],\n})\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/** @public */\nexport interface OrganizationMember {\n sanityUserId: string\n isCurrentUser: boolean\n user: {\n id: string\n displayName: string\n familyName: string\n givenName: string\n middleName: string | null\n imageUrl: string | null\n email: string\n loginProvider: string\n }\n roles: Array<{\n name: string\n title: string\n description?: string\n }>\n}\n\n/**\n * The base fields returned from `/organizations/<id>` for every organization.\n * @public\n */\nexport interface OrganizationBase {\n id: string\n name: string\n slug: string | null\n createdAt: string\n createdByUserId: string\n updatedAt: string\n deletedAt: string | null\n dashboardStatus: 'enabled' | 'disabled'\n aiFeaturesStatus: 'enabled' | 'disabled'\n mediaLibraryStatus: 'enabled' | 'disabled'\n requestAccessStatus: 'allowed' | 'disabled'\n telemetryConsentStatus: 'allowed' | 'msa_denied' | 'customer_denied'\n oauthAppsStatus: 'allowed' | 'blocked'\n defaultRoleName: string\n domains: string[] | null\n}\n\n/** @public */\nexport interface OrganizationOptions<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> {\n includeMembers?: IncludeMembers\n includeFeatures?: IncludeFeatures\n organizationId: string\n}\n\n/**\n * An `Organization` with `members` and/or `features` conditionally included\n * based on the query options used to fetch it.\n * @public\n */\nexport type Organization<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> = OrganizationBase &\n // `boolean extends T` is non-distributive — true only when T is the wide\n // `boolean`, in which case the field is optional. Literal `true`/`false`\n // fall through to the strict branch.\n (boolean extends IncludeMembers\n ? {members?: OrganizationMember[]}\n : IncludeMembers extends true\n ? {members: OrganizationMember[]}\n : unknown) &\n (boolean extends IncludeFeatures\n ? {features?: string[]}\n : IncludeFeatures extends true\n ? {features: string[]}\n : unknown)\n\nfunction resolveOrganizationId(options?: OrganizationOptions<boolean, boolean>) {\n const organizationId = options?.organizationId\n if (!organizationId) {\n throw new Error('An organizationId is required to use the organization API.')\n }\n return organizationId\n}\n\nfunction normalizeOrganizationOptions(options?: OrganizationOptions<boolean, boolean>) {\n return {\n includeMembers: options?.includeMembers ?? false,\n includeFeatures: options?.includeFeatures ?? false,\n }\n}\n\n/** @internal */\nexport function getOrganizationCacheKey(\n _instance: SanityInstance,\n options?: OrganizationOptions<boolean, boolean>,\n): string {\n const organizationId = resolveOrganizationId(options)\n const {includeMembers, includeFeatures} = normalizeOrganizationOptions(options)\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n return `organization:${organizationId}${membersKey}${featuresKey}`\n}\n\n/**\n * Fetcher for a single organization (`GET /organizations/:id`), on the shared\n * fetcher cache. No pre-seeding from the organizations list: it returns a\n * subset of the detail fields, and a partial seed would serve as fresh for\n * the whole stale window.\n *\n * @internal\n */\nexport const organization = defineFetcher<\n [options?: OrganizationOptions<boolean, boolean>],\n Organization<boolean, boolean>\n>({\n name: 'organization',\n getKey: getOrganizationCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Organization<boolean, boolean>>({\n url: `/organizations/${resolveOrganizationId(options)}`,\n query: buildQuery(normalizeOrganizationOptions(options)),\n tag: 'organizations.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'organization', id: data.id}],\n})\n","import {map, switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {defineFetcher} from '../store/fetcherStore'\n\nconst API_VERSION = 'v2025-07-11'\n\n/**\n * The resource types the Access API can scope a permission check to.\n *\n * @see https://www.sanity.io/docs/http-reference/access-api\n * @public\n */\nexport type AccessResourceType =\n | 'organization'\n | 'project'\n | 'media-library'\n | 'canvas'\n | 'dashboard'\n | 'view'\n\n/**\n * Fetcher checking whether the current user holds the given permissions on a\n * resource (`GET /access/:resourceType/:resourceId/user-permissions/me/check`),\n * on the shared fetcher cache. Resolves to a map of each requested permission\n * to whether the user has it. The key sorts the permission list, so permission\n * order doesn't create distinct cache entries.\n *\n * @see https://www.sanity.io/docs/http-reference/access-api\n * @internal\n */\nexport const checkPermissions = defineFetcher<\n [resourceType: AccessResourceType, resourceId: string, permissions: string[]],\n Record<string, boolean>\n>({\n name: 'checkPermissions',\n getKey: (_instance, resourceType, resourceId, permissions) =>\n `${resourceType}:${resourceId}:${[...permissions].sort().join(',')}`,\n fetch: (instance) => (resourceType, resourceId, permissions) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable\n .request<{data: Record<string, boolean>}>({\n url: `/access/${resourceType}/${resourceId}/user-permissions/me/check`,\n query: {permissions},\n tag: 'access.check',\n })\n .pipe(map((response) => response.data)),\n ),\n ),\n tags: (_data, resourceType, resourceId) => [\n {type: 'access', id: `${resourceType}:${resourceId}`},\n ],\n})\n","/**\n * Error message returned by the organization verification\n * @public\n */\nexport interface OrgVerificationResult {\n error: string | null\n}\n\n/**\n * Compares a project's actual organization ID with the expected organization ID.\n * @public\n */\nexport function compareProjectOrganization(\n projectId: string,\n projectOrganizationId: string | null | undefined,\n currentDashboardOrgId: string,\n): OrgVerificationResult {\n if (projectOrganizationId !== currentDashboardOrgId) {\n return {\n error:\n `Project ${projectId} belongs to Organization ${projectOrganizationId ?? 'unknown'}, ` +\n `but the Dashboard has Organization ${currentDashboardOrgId} selected`,\n }\n }\n return {error: null}\n}\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type Project} from '../project/project'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/** @public */\nexport interface ProjectsOptions<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = true,\n> {\n organizationId?: string\n includeMembers?: IncludeMembers\n includeFeatures?: IncludeFeatures\n onlyExplicitMembership?: boolean\n}\n\nfunction normalizeProjectsOptions(options?: ProjectsOptions<boolean, boolean>) {\n return {\n organizationId: options?.organizationId,\n includeMembers: options?.includeMembers ?? false,\n includeFeatures: options?.includeFeatures ?? true,\n onlyExplicitMembership: options?.onlyExplicitMembership ?? false,\n }\n}\n\n/** @internal */\nexport function getProjectsCacheKey(\n _instance: SanityInstance,\n options?: ProjectsOptions<boolean, boolean>,\n): string {\n const {organizationId, includeMembers, includeFeatures, onlyExplicitMembership} =\n normalizeProjectsOptions(options)\n const orgKey = organizationId ? `:org:${organizationId}` : ''\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n const explicitKey = onlyExplicitMembership ? ':explicit' : ''\n return `projects${orgKey}${membersKey}${featuresKey}${explicitKey}`\n}\n\n/**\n * Fetcher for the current user's projects (`GET /projects`), on the shared\n * fetcher cache.\n *\n * @internal\n */\nexport const projects = defineFetcher<\n [options?: ProjectsOptions<boolean, boolean>],\n Project<boolean, boolean>[]\n>({\n name: 'projects',\n getKey: getProjectsCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Project<boolean, boolean>[]>({\n url: '/projects',\n query: buildQuery(normalizeProjectsOptions(options)),\n tag: 'projects.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'project', id: 'LIST'},\n ...data.map((project) => ({type: 'project', id: project.id})),\n ],\n})\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type ProjectHandle} from '../config/sanityConfig'\nimport {projects} from '../projects/projects'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/** @public */\nexport interface ProjectMemberRole {\n name: string\n title: string\n description: string\n}\n\n/** @public */\nexport interface ProjectMember {\n id: string\n createdAt: string\n updatedAt: string\n isCurrentUser: boolean\n isRobot: boolean\n roles: ProjectMemberRole[]\n}\n\n/** @public */\nexport interface ProjectMetadata {\n color?: string\n externalStudioHost?: string\n initialTemplate?: string\n cliInitializedAt?: string\n integration: 'manage' | 'cli'\n}\n\n/**\n * The base fields returned from `/projects` for every project.\n * @public\n */\nexport interface ProjectBase {\n id: string\n displayName: string\n studioHost: string | null\n organizationId: string\n metadata: ProjectMetadata\n isBlocked: boolean\n isDisabled: boolean\n isDisabledByUser: boolean\n activityFeedEnabled: boolean\n createdAt: string\n updatedAt: string\n}\n\n/**\n * A `Project` with `members` and/or `features` conditionally included\n * based on the query options used to fetch it.\n * @public\n */\nexport type Project<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n> = ProjectBase &\n // `boolean extends T` is non-distributive — true only when T is the wide\n // `boolean`, in which case the field is optional. Literal `true`/`false`\n // fall through to the strict branch.\n (boolean extends IncludeMembers\n ? {members?: ProjectMember[]}\n : IncludeMembers extends true\n ? {members: ProjectMember[]}\n : unknown) &\n (boolean extends IncludeFeatures\n ? {features?: string[]}\n : IncludeFeatures extends true\n ? {features: string[]}\n : unknown)\n\n/** @public */\nexport interface ProjectOptions<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n> extends ProjectHandle {\n includeMembers?: IncludeMembers\n includeFeatures?: IncludeFeatures\n}\n\nfunction normalizeProjectOptions(options?: ProjectOptions<boolean, boolean>) {\n return {\n includeMembers: options?.includeMembers ?? true,\n includeFeatures: options?.includeFeatures ?? true,\n }\n}\n\nfunction resolveProjectId(instance: SanityInstance, options?: ProjectOptions<boolean, boolean>) {\n const projectId = options?.projectId ?? instance.config.projectId\n if (!projectId) {\n throw new Error('A projectId is required to use the project API.')\n }\n return projectId\n}\n\n/** @internal */\nexport function getProjectCacheKey(\n instance: SanityInstance,\n options?: ProjectOptions<boolean, boolean>,\n): string {\n const projectId = resolveProjectId(instance, options)\n const {includeMembers, includeFeatures} = normalizeProjectOptions(options)\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n return `project:${projectId}${membersKey}${featuresKey}`\n}\n\n/**\n * Fetcher for a single project (`GET /projects/:id`), on the shared fetcher\n * cache. Pre-seeds from the default {@link projects} list entry when it can\n * satisfy the read — the default list carries features but not members, so\n * only `includeMembers: false` reads seed.\n *\n * @internal\n */\nexport const project = defineFetcher<\n [options?: ProjectOptions<boolean, boolean>],\n Project<boolean, boolean>\n>({\n name: 'project',\n getKey: getProjectCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Project<boolean, boolean>>({\n url: `/projects/${resolveProjectId(instance, options)}`,\n query: buildQuery(normalizeProjectOptions(options)),\n tag: 'projects.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'project', id: data.id}],\n initialData: (instance, options) => {\n if (normalizeProjectOptions(options).includeMembers) return undefined\n const snapshot = projects.getState(instance).getCurrent()\n if (snapshot.status !== 'success') return undefined\n const projectId = resolveProjectId(instance, options)\n const match = snapshot.data.find((item) => item.id === projectId)\n return match && {data: match, dataUpdatedAt: snapshot.dataUpdatedAt}\n },\n})\n","import {combineLatest, distinctUntilChanged, map, type Observable, of, switchMap} from 'rxjs'\n\nimport {getDashboardOrganizationId} from '../dashboard/messageBus/topicStore'\nimport {\n compareProjectOrganization,\n type OrgVerificationResult,\n} from '../project/organizationVerification'\nimport {project} from '../project/project'\nimport {type SanityInstance} from '../store/createSanityInstance'\n\n/**\n * Creates an observable that emits the organization verification state for a given instance.\n * It combines the dashboard organization ID (from the message bus or `_context`) with the\n * project's actual organization ID (fetched via the project fetcher) and compares them.\n * @public\n */\nexport function observeOrganizationVerificationState(\n instance: SanityInstance,\n projectIds: string[],\n): Observable<OrgVerificationResult> {\n // Observable for the dashboard org ID (potentially null)\n const dashboardOrgId$ =\n getDashboardOrganizationId(instance).observable.pipe(distinctUntilChanged())\n\n // Create observables for each project's org ID\n const projectOrgIdObservables = projectIds.map((id) =>\n project.getState(instance, {projectId: id}).observable.pipe(\n map((snapshot) => ({projectId: id, orgId: snapshot.data?.organizationId ?? null})),\n // Ensure we only proceed if the orgId is loaded, distinct prevents unnecessary checks\n distinctUntilChanged((prev, curr) => prev.orgId === curr.orgId),\n ),\n )\n\n // Combine observables to get all project org IDs\n const allProjectOrgIds$ =\n projectOrgIdObservables.length > 0 ? combineLatest(projectOrgIdObservables) : of([])\n\n // Combine the sources\n return combineLatest([dashboardOrgId$, allProjectOrgIds$]).pipe(\n switchMap(([dashboardOrgId, projectOrgDataArray]) => {\n // If no dashboard org ID is set, or no project IDs provided, verification isn't applicable/possible\n if (!dashboardOrgId || projectOrgDataArray.length === 0) {\n return of<OrgVerificationResult>({error: null}) // Return success (no error)\n }\n\n // Iterate through all projects and check organization IDs\n for (const projectData of projectOrgDataArray) {\n // If a project doesn't have an orgId, we can't verify, treat as non-blocking for now\n // (Matches original logic where null projectOrgId resulted in {error: null})\n if (!projectData.orgId) {\n continue\n }\n\n // Perform the comparison for the current project\n const result = compareProjectOrganization(\n projectData.projectId,\n projectData.orgId,\n dashboardOrgId,\n )\n\n // If any project fails verification, immediately return the error\n if (result.error) {\n return of(result)\n }\n }\n\n // If all projects passed verification (or had no orgId to check)\n return of<OrgVerificationResult>({error: null})\n }),\n // Only emit when the overall error status actually changes\n distinctUntilChanged((prev, curr) => prev.error === curr.error),\n )\n}\n","/**\n * PKCE (Proof Key for Code Exchange, RFC 7636) helpers for the OAuth\n * authorization-code flow.\n *\n * @internal\n */\n\n/** Number of random bytes used for the `code_verifier` and `state`. */\nconst RANDOM_BYTE_LENGTH = 32\n\n/**\n * Encodes a byte array as a base64url string (RFC 4648 §5) with no padding,\n * as required for PKCE `code_verifier`/`code_challenge` values.\n *\n * @internal\n */\nexport function base64UrlEncode(bytes: Uint8Array): string {\n let binary = ''\n for (const byte of bytes) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction randomString(): string {\n const bytes = new Uint8Array(RANDOM_BYTE_LENGTH)\n crypto.getRandomValues(bytes)\n return base64UrlEncode(bytes)\n}\n\n/**\n * Generates a PKCE `code_verifier`\n *\n * @internal\n */\nexport function generateCodeVerifier(): string {\n return randomString()\n}\n\n/**\n * Generates the `state` parameter used to defend against CSRF on the OAuth\n * callback.\n *\n * @internal\n */\nexport function generateState(): string {\n return randomString()\n}\n\n/**\n * Derives the PKCE `code_challenge` from a `code_verifier` using\n * `base64url(sha256(verifier))` (the `S256` method).\n *\n * @internal\n */\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n const data = new TextEncoder().encode(codeVerifier)\n const digest = await crypto.subtle.digest('SHA-256', data)\n return base64UrlEncode(new Uint8Array(digest))\n}\n","import {createSelector} from 'reselect'\n\nimport {bindActionGlobally} from '../../store/createActionBinder'\nimport {createStateSourceAction} from '../../store/createStateSourceAction'\nimport {DEFAULT_BASE} from '../authConstants'\nimport {getAuthLogger} from '../authLogger'\nimport {AuthStateType} from '../authStateType'\nimport {authStore, type AuthStoreState} from '../authStore'\nimport {createLoggedInAuthState, getDefaultLocation} from '../utils'\nimport {\n appendResourceIndicator,\n createOAuthClient,\n getOAuthOptions,\n postTokenRequest,\n serializeTokens,\n toOAuthTokens,\n} from './oauthClient'\nimport {runOAuthTokenRefresh} from './oauthRefresh'\nimport {generateCodeChallenge, generateCodeVerifier, generateState} from './pkce'\nimport {type OAuthTokens} from './types'\n\n/** sessionStorage key for the PKCE `code_verifier`. */\nexport const OAUTH_VERIFIER_KEY = '__sanity_oauth_verifier'\n\n/** sessionStorage key for the CSRF `state` value. */\nexport const OAUTH_STATE_KEY = '__sanity_oauth_state'\n\n/** sessionStorage key for the location to return to after the callback. */\nexport const OAUTH_RETURN_TO_KEY = '__sanity_oauth_return_to'\n\nconst OAUTH_CALLBACK_PARAMS = ['code', 'state', 'error', 'error_description']\n\n/** Strips the OAuth callback params from a URL. */\nfunction stripOAuthParams(href: string): string {\n const url = new URL(href, DEFAULT_BASE)\n for (const param of OAUTH_CALLBACK_PARAMS) url.searchParams.delete(param)\n return url.toString()\n}\n\n/** Whether `href` is the registered redirect URI (origin + pathname; query and hash ignored). */\nfunction isCallbackRoute(href: string, redirectUri: string): boolean {\n const current = new URL(href, DEFAULT_BASE)\n const redirect = new URL(redirectUri, DEFAULT_BASE)\n return current.origin === redirect.origin && current.pathname === redirect.pathname\n}\n\n/**\n * Starts the OAuth authorization-code + PKCE flow: generates a `code_verifier`,\n * `code_challenge` and `state`, persists the verifier and state to\n * `sessionStorage`, then navigates the browser to the authorize endpoint.\n *\n * @public\n */\nexport const startOAuthAuthorization = bindActionGlobally(authStore, async ({state, instance}) => {\n const logger = getAuthLogger(instance)\n const options = getOAuthOptions(state.get())\n\n const codeVerifier = generateCodeVerifier()\n const oauthState = generateState()\n const codeChallenge = await generateCodeChallenge(codeVerifier)\n\n const session = typeof sessionStorage !== 'undefined' ? sessionStorage : undefined\n session?.setItem(OAUTH_VERIFIER_KEY, codeVerifier)\n session?.setItem(OAUTH_STATE_KEY, oauthState)\n // The redirect URI must match the registered one exactly, so the current\n // location (deep link) is stashed here and restored by the callback. OAuth\n // params are only stripped when we are already on the callback route, so\n // legitimate app params like `?state=draft` survive the round trip.\n const currentHref = getDefaultLocation()\n if (currentHref !== DEFAULT_BASE) {\n const returnTo = isCallbackRoute(currentHref, options.oauth.redirectUri)\n ? stripOAuthParams(currentHref)\n : currentHref\n session?.setItem(OAUTH_RETURN_TO_KEY, returnTo)\n }\n\n const authorizeUrl = new URL(\n '/v1/auth/oauth/authorize',\n options.apiHost ?? 'https://api.sanity.io',\n )\n authorizeUrl.searchParams.set('response_type', 'code')\n authorizeUrl.searchParams.set('client_id', options.oauth.clientId)\n authorizeUrl.searchParams.set('redirect_uri', options.oauth.redirectUri)\n authorizeUrl.searchParams.set('state', oauthState)\n authorizeUrl.searchParams.set('code_challenge', codeChallenge)\n authorizeUrl.searchParams.set('code_challenge_method', 'S256')\n appendResourceIndicator(authorizeUrl.searchParams, options.oauth)\n\n logger.info('Starting OAuth authorization')\n if (typeof window !== 'undefined' && typeof window.location?.assign === 'function') {\n window.location.assign(authorizeUrl.toString())\n }\n})\n\n/**\n * Handles the OAuth redirect callback: validates `state`, surfaces `error`\n * responses, exchanges the authorization `code` for tokens, persists them, and\n * transitions to `LOGGED_IN`.\n *\n * Returns the URL for the caller to `history.replaceState` to when a callback\n * was processed: on success, the same-origin location the user was on when\n * the flow started (if any), otherwise the callback URL cleaned of OAuth\n * params. Returns `false` when there was nothing to handle (no code, or an\n * exchange already in progress). A callback that fails validation while a\n * session is already `LOGGED_IN` (e.g. a remount before the params were\n * stripped) is ignored with a warning and still returns the cleaned URL, so a\n * string result does not imply an exchange ran.\n *\n * @public\n */\nexport const handleOAuthCallback = bindActionGlobally(\n authStore,\n async (\n {state, instance},\n locationHref: string = getDefaultLocation(),\n ): Promise<string | false> => {\n const logger = getAuthLogger(instance)\n const options = getOAuthOptions(state.get())\n\n const {authState} = state.get()\n if (authState.type === AuthStateType.LOGGING_IN && authState.isExchangingToken) {\n logger.debug('Skipping OAuth callback - token exchange already in progress')\n return false\n }\n\n const callbackUrl = new URL(locationHref, DEFAULT_BASE)\n const code = callbackUrl.searchParams.get('code')\n const returnedState = callbackUrl.searchParams.get('state')\n const error = callbackUrl.searchParams.get('error')\n const errorDescription = callbackUrl.searchParams.get('error_description')\n\n const session = typeof sessionStorage !== 'undefined' ? sessionStorage : undefined\n\n const cleanedUrl = stripOAuthParams(locationHref)\n\n // Callback failures move to ERROR, unless a session is already\n // established: a stale callback URL (e.g. remount before the params were\n // stripped, after the artifacts were cleared) or a denied re-authorization\n // must not clobber LOGGED_IN.\n const rejectCallback = (name: string, message: string): string => {\n if (authState.type === AuthStateType.LOGGED_IN) {\n // Leave PKCE artifacts alone: a re-authorization may be in flight.\n logger.warn(`${message} — ignoring callback, session already established`)\n return cleanedUrl\n }\n logger.error(`${message} — rejecting callback`)\n clearOAuthArtifacts(session)\n state.set(name, {authState: {type: AuthStateType.ERROR, error: new Error(message)}})\n return cleanedUrl\n }\n\n if (error) {\n return rejectCallback(\n 'oauthCallbackError',\n errorDescription ? `${error}: ${errorDescription}` : error,\n )\n }\n\n if (!code) {\n logger.debug('No OAuth code found in callback URL')\n return false\n }\n\n const storedState = session?.getItem(OAUTH_STATE_KEY) ?? null\n if (!returnedState || !storedState || returnedState !== storedState) {\n return rejectCallback('oauthStateMismatch', 'OAuth state mismatch')\n }\n\n const codeVerifier = session?.getItem(OAUTH_VERIFIER_KEY) ?? null\n if (!codeVerifier) {\n return rejectCallback('oauthVerifierMissing', 'OAuth code verifier missing')\n }\n\n logger.info('Exchanging OAuth code for tokens')\n state.set('oauthExchange', {\n authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: true},\n })\n\n try {\n const client = createOAuthClient(options)\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n code_verifier: codeVerifier,\n redirect_uri: options.oauth.redirectUri,\n client_id: options.oauth.clientId,\n })\n appendResourceIndicator(params, options.oauth)\n const response = await postTokenRequest(client, params, 'oauth.token')\n\n const tokens = toOAuthTokens(response)\n options.storageArea?.setItem(options.storageKey, serializeTokens(tokens))\n const returnTo = getSameOriginReturnTo(session, options.oauth.redirectUri)\n clearOAuthArtifacts(session)\n\n logger.info('OAuth tokens obtained, user logged in')\n state.set('oauthLoggedIn', {\n authState: createLoggedInAuthState(tokens.accessToken, null),\n oauthTokens: tokens,\n })\n return returnTo ?? cleanedUrl\n } catch (exchangeError) {\n logger.error('Failed to exchange OAuth code for tokens', {error: exchangeError})\n clearOAuthArtifacts(session)\n state.set('oauthExchangeError', {\n authState: {type: AuthStateType.ERROR, error: exchangeError},\n })\n return cleanedUrl\n }\n },\n)\n\n/**\n * Refreshes the OAuth tokens using the `refresh_token` grant. Concurrent\n * callers share a single in-flight request. An unrecoverable failure (a 4xx\n * rejecting the refresh token) clears the tokens and transitions to\n * `LOGGED_OUT`; transient failures (network, 5xx, rate limits) leave the\n * session intact and rethrow so the caller can retry. The resolved tokens omit\n * the refresh token, which core retains internally for subsequent refreshes.\n *\n * @public\n */\nexport const refreshOAuthTokens = bindActionGlobally(authStore, runOAuthTokenRefresh)\n\n/**\n * Revokes the OAuth tokens then clears local storage and transitions to `LOGGED_OUT`.\n * Local state is cleared even if the revoke request fails.\n *\n * @public\n */\nexport const revokeOAuthTokens = bindActionGlobally(authStore, async ({state, instance}) => {\n const logger = getAuthLogger(instance)\n const options = getOAuthOptions(state.get())\n\n const current = state.get().oauthTokens\n const token = current?.refreshToken ?? current?.accessToken\n\n try {\n if (token) {\n const client = createOAuthClient(options)\n const params = new URLSearchParams({token, client_id: options.oauth.clientId})\n await client.request<void>({\n method: 'POST',\n url: '/auth/oauth/revoke',\n headers: {'content-type': 'application/x-www-form-urlencoded'},\n body: params.toString(),\n tag: 'oauth.revoke',\n })\n logger.info('OAuth tokens revoked')\n }\n } catch (error) {\n // Revocation is best-effort — local state is cleared regardless.\n logger.warn('OAuth revoke request failed — clearing local state anyway', {error})\n } finally {\n options.storageArea?.removeItem(options.storageKey)\n state.set('oauthRevoked', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n oauthTokens: undefined,\n })\n }\n})\n\n// Memoised so `getCurrent()` keeps returning the same object while\n// `oauthTokens` is unchanged; `useSyncExternalStore` loops on a snapshot\n// that changes identity every read. One slot suffices: no params, global store.\nconst selectPublicOAuthTokens = createSelector(\n (state: AuthStoreState) => state.oauthTokens,\n (oauthTokens): Omit<OAuthTokens, 'refreshToken'> | null => {\n if (!oauthTokens) return null\n const {refreshToken: _refreshToken, ...tokens} = oauthTokens\n return tokens\n },\n)\n\n/**\n * A state source exposing the current OAuth tokens (including expiry), or\n * `null` when not logged in via OAuth. The refresh token is omitted — it is a\n * long-lived credential held internally by core for `refreshOAuthTokens`, not\n * part of the app-facing token view.\n *\n * @public\n */\nexport const getOAuthTokensState = bindActionGlobally(\n authStore,\n createStateSourceAction(({state}) => selectPublicOAuthTokens(state)),\n)\n\n/** Removes the transient PKCE artifacts from session storage. */\nfunction clearOAuthArtifacts(session: Storage | undefined): void {\n session?.removeItem(OAUTH_VERIFIER_KEY)\n session?.removeItem(OAUTH_STATE_KEY)\n session?.removeItem(OAUTH_RETURN_TO_KEY)\n}\n\n/**\n * Reads the stashed pre-authorize location, only honouring it when it shares\n * the registered redirect URI's origin so a tampered value cannot redirect\n * off-site. (`origin` includes the scheme, so non-http(s) URLs are rejected.)\n */\nfunction getSameOriginReturnTo(session: Storage | undefined, redirectUri: string): string | null {\n const returnTo = session?.getItem(OAUTH_RETURN_TO_KEY) ?? null\n if (!returnTo) return null\n try {\n return new URL(returnTo).origin === new URL(redirectUri).origin ? returnTo : null\n } catch {\n return null\n }\n}\n","import {bindActionGlobally} from '../store/createActionBinder'\nimport {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'\nimport {getAuthLogger} from './authLogger'\nimport {AuthStateType} from './authStateType'\nimport {authStore, type AuthStoreState, type DashboardContext} from './authStore'\nimport {handleOAuthCallback} from './oauth/oauthActions'\nimport {\n createLoggedInAuthState,\n getAuthCode,\n getCleanedUrl,\n getDefaultLocation,\n getTokenFromLocation,\n} from './utils'\n\n/**\n * Handles the auth redirect callback. In OAuth mode this delegates to\n * {@link handleOAuthCallback}; otherwise it exchanges the legacy `sid` for a\n * long-lived token.\n *\n * @public\n */\nexport const handleAuthCallback = bindActionGlobally(\n authStore,\n async ({state, instance}, locationHref: string = getDefaultLocation()) => {\n const logger = getAuthLogger(instance)\n\n const {providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey, oauth} =\n state.get().options\n\n // If a token is provided, no need to handle callback\n if (providedToken) {\n logger.debug('Skipping auth callback - token already provided')\n return false\n }\n\n if (oauth) {\n logger.debug('OAuth mode - delegating to handleOAuthCallback')\n return handleOAuthCallback(instance, locationHref)\n }\n\n // Don't handle the callback if already in flight.\n const {authState} = state.get()\n if (authState.type === AuthStateType.LOGGING_IN && authState.isExchangingToken) {\n logger.debug('Skipping auth callback - token exchange already in progress')\n return false\n }\n\n // Prepare the cleaned-up URL early. It will be returned on both success and error if an authCode/token was processed.\n const cleanedUrl = getCleanedUrl(locationHref)\n\n // Check if there is a token in the is in the Dashboard iframe url hash\n const tokenFromUrl = getTokenFromLocation(locationHref)\n if (tokenFromUrl) {\n logger.info('Auth token found in URL, logging in')\n state.set('setTokenFromUrl', {\n authState: createLoggedInAuthState(tokenFromUrl, null),\n })\n return cleanedUrl\n }\n\n // If there is no matching `authCode` then we can't handle the callback\n const authCode = getAuthCode(callbackUrl, locationHref)\n if (!authCode) {\n logger.debug('No auth code found in callback URL')\n return false\n }\n\n // Get the SanityOS dashboard context from the url\n const parsedUrl = new URL(locationHref)\n let dashboardContext: DashboardContext = {}\n try {\n const contextParam = parsedUrl.searchParams.get('_context')\n if (contextParam) {\n const parsedContext = JSON.parse(contextParam)\n if (parsedContext && typeof parsedContext === 'object') {\n delete parsedContext.sid\n dashboardContext = parsedContext\n logger.debug('Dashboard context parsed from callback URL', {\n hasDashboardContext: true,\n })\n }\n }\n } catch (err) {\n // If JSON parsing fails, use empty context\n logger.warn('Failed to parse dashboard context from callback URL', {error: err})\n }\n\n // Otherwise, start the exchange\n logger.info('Exchanging auth code for token')\n state.set('exchangeSessionForToken', {\n authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: true},\n dashboardContext,\n } as Partial<AuthStoreState>)\n\n try {\n const client = clientFactory({\n apiVersion: DEFAULT_API_VERSION,\n requestTagPrefix: REQUEST_TAG_PREFIX,\n useProjectHostname: false,\n useCdn: false,\n ...(apiHost && {apiHost}),\n })\n\n logger.debug('Fetching token from auth endpoint')\n const {token} = await client.request<{token: string; label: string}>({\n method: 'GET',\n url: '/auth/fetch',\n query: {sid: authCode},\n tag: 'fetch-token',\n })\n\n logger.info('Auth token obtained successfully, user logged in')\n storageArea?.setItem(storageKey, JSON.stringify({token}))\n state.set('setToken', {authState: createLoggedInAuthState(token, null)})\n\n return cleanedUrl\n } catch (error) {\n logger.error('Failed to exchange auth code for token', {error})\n state.set('exchangeSessionForTokenError', {authState: {type: AuthStateType.ERROR, error}})\n return cleanedUrl\n }\n },\n)\n","import {bindActionGlobally} from '../store/createActionBinder'\nimport {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'\nimport {getAuthLogger} from './authLogger'\nimport {AuthStateType} from './authStateType'\nimport {authStore} from './authStore'\nimport {revokeOAuthTokens} from './oauth/oauthActions'\n\n/**\n * Logs the current user out. In OAuth mode this revokes the OAuth tokens\n * instead of calling the legacy `/auth/logout` endpoint; revocation is\n * best-effort, so the OAuth path resolves even if the revoke request fails,\n * whereas the legacy path rejects when `/auth/logout` fails. Local state and\n * storage are cleared in both cases.\n *\n * @public\n */\nexport const logout = bindActionGlobally(authStore, async ({state, instance}) => {\n const logger = getAuthLogger(instance)\n\n const {clientFactory, apiHost, providedToken, storageArea, storageKey, oauth} =\n state.get().options\n\n // If a token is statically provided, logout does nothing\n if (providedToken) {\n logger.debug('Skipping logout - token is statically provided')\n return\n }\n\n const {authState} = state.get()\n\n // If we already have an inflight request, no-op\n if (authState.type === AuthStateType.LOGGED_OUT && authState.isDestroyingSession) {\n logger.debug('Skipping logout - already in progress')\n return\n }\n\n if (oauth) {\n logger.debug('OAuth mode - revoking tokens instead of calling /auth/logout')\n state.set('loggingOut', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: true},\n })\n await revokeOAuthTokens(instance)\n return\n }\n const token = authState.type === AuthStateType.LOGGED_IN && authState.token\n\n try {\n if (token) {\n logger.info('Logging out user')\n state.set('loggingOut', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: true},\n })\n\n const client = clientFactory({\n token,\n requestTagPrefix: REQUEST_TAG_PREFIX,\n apiVersion: DEFAULT_API_VERSION,\n ...(apiHost && {apiHost}),\n useProjectHostname: false,\n useCdn: false,\n })\n\n logger.debug('Calling logout endpoint')\n await client.request<void>({url: '/auth/logout', method: 'POST', tag: 'logout'})\n } else {\n logger.debug('No token to logout - already logged out')\n }\n } catch (error) {\n // Re-throw to preserve the existing contract: logout rejects when the\n // request fails. Local state is still cleared in the finally block.\n logger.error('Logout request failed', {error})\n throw error\n } finally {\n logger.info('User logged out, clearing stored tokens')\n state.set('logoutSuccess', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n oauthTokens: undefined,\n })\n storageArea?.removeItem(storageKey)\n storageArea?.removeItem(`${storageKey}_last_refresh`)\n }\n})\n","function weaken(node: unknown): unknown {\n if (Array.isArray(node)) {\n return node.map(weaken)\n }\n\n if (node && typeof node === 'object') {\n if ('_ref' in node) {\n return {...node, _weak: true}\n }\n\n return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, weaken(value)]))\n }\n\n return node\n}\n\n/**\n * Marks every reference in a content snapshot weak.\n *\n * A comment's snapshot is written into the addon dataset, but its references\n * point at documents in the main one. Left strong, Content Lake would refuse to\n * delete any document a snapshot happens to mention, so an old comment could\n * pin a document forever.\n *\n * @internal\n */\nexport function weakenReferencesInContentSnapshot(snapshot: unknown): unknown {\n return weaken(snapshot)\n}\n","import {type SanityClient} from '@sanity/client'\nimport {DocumentId, getPublishedId} from '@sanity/id-utils'\nimport {type Path} from '@sanity/types'\nimport {filter, firstValueFrom, take} from 'rxjs'\n\nimport {getCurrentUserState} from '../auth/authStore'\nimport {getClient} from '../client/clientStore'\nimport {\n type DatasetHandle,\n type DatasetResource,\n type DocumentHandle,\n type DocumentResource,\n} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type StoreState} from '../store/createStoreState'\nimport {type StoreContext} from '../store/defineStore'\nimport {randomUuid} from '../utils/ids'\nimport {\n assertDatasetResource,\n getAddonDatasetState,\n provisionAddonDataset,\n} from './addonDatasetStore'\nimport {toCommentFieldPath} from './commentFieldPath'\nimport {COMMENTS_API_VERSION} from './commentsConstants'\nimport {type CommentsOptions, commentsStore, toCommentsKeyParts} from './commentsStore'\nimport {normalizeComment} from './normalizeComment'\nimport {\n addComment,\n applyCommentUpdate,\n clearPendingTransaction,\n type CommentsStoreState,\n getCommentsKey,\n receiveComment,\n removeCommentById,\n restoreComments,\n rollbackCommentUpdate,\n setCommentCreateError,\n setPendingTransaction,\n} from './reducers'\nimport {\n type Comment,\n type CommentMessage,\n type CommentPostPayload,\n type CommentStatus,\n type CommentTextSelection,\n type StoredComment,\n} from './types'\nimport {weakenReferencesInContentSnapshot} from './weakenReferences'\n\n/** @beta */\nexport interface CreateCommentOptions extends DocumentHandle {\n message: CommentMessage\n /**\n * Which field the thread hangs off, for example `title` or\n * `body[_key==\"intro\"].content`.\n *\n * Required, and it has to resolve to a real path. There is no such thing as a\n * comment on a document as a whole: the Studio only ever creates field\n * comments, and its comment inspector throws on an empty path rather than\n * ignoring it, so a pathless comment takes the inspector down for everyone\n * looking at that document.\n */\n fieldPath: string | Path\n /**\n * Anchors the comment to a run of text inside a Portable Text field. Building\n * one needs a live editor, so this comes from\n * `@portabletext/plugin-sdk-value` rather than being constructed by hand.\n */\n selection?: CommentTextSelection\n /** Reuse the id of a failed comment to retry it. Defaults to a new id. */\n commentId?: string\n /** Defaults to a new id, which starts a new thread. */\n threadId?: string\n /** @defaultValue `'open'` */\n status?: CommentStatus\n /**\n * The `_rev` of the document when the comment was written.\n *\n * Not filled in automatically: the SDK's local document revision is replaced\n * by a transaction id while an edit is in flight, so it can name a revision\n * that never reached the server. Nothing in the Studio reads this today.\n */\n documentRevisionId?: string\n /**\n * Ambient information stored alongside the comment, merged over the defaults.\n *\n * The Studio writes `tool`, `payload`, `intent`, and `notification` here for\n * its notification backend. The SDK has no source for any of them, so\n * mentions in comments created this way do not send email unless you supply\n * `notification` yourself. Deliberately loose: the organization-level store\n * treats this as free-form, so no shape is worth promising.\n */\n context?: Record<string, unknown>\n /** A copy of the content being discussed. References in it are weakened. */\n contentSnapshot?: unknown\n}\n\n/** @beta */\nexport interface ReplyToCommentOptions extends DocumentHandle {\n /** The comment being replied to. Replies to a reply join the same thread. */\n parentCommentId: string\n message: CommentMessage\n commentId?: string\n /** Only needed when the parent is not loaded, such as following a deep link. */\n threadId?: string\n /** Only needed when the parent is not loaded. */\n fieldPath?: string | Path\n /** Required when the parent is not loaded. */\n status?: CommentStatus\n}\n\n/** @beta */\nexport interface UpdateCommentOptions extends DatasetHandle {\n commentId: string\n message: CommentMessage\n}\n\n/** @beta */\nexport interface SetCommentStatusOptions extends DatasetHandle {\n /** The thread's first comment. Replies follow their parent. */\n commentId: string\n status: CommentStatus\n}\n\n/** @beta */\nexport interface RemoveCommentOptions extends DatasetHandle {\n commentId: string\n}\n\n/**\n * Refuses to write a comment that points at nothing.\n *\n * The type already requires `fieldPath`, but an empty string or an empty path\n * array both survive that and normalise to `''`. Such a comment is not merely\n * useless: the Studio's comment inspector calls `fromString` on the stored\n * path, which throws on `''`, so the inspector crashes for everyone viewing\n * that document until the comment is deleted. Cheaper to refuse the write.\n */\nfunction requireFieldPath(fieldPath: string | Path): string {\n const normalized = toCommentFieldPath(fieldPath)\n if (!normalized) {\n throw new Error(\n 'A comment needs a field path. Comments attach to a field, not to a document as a whole.',\n )\n }\n return normalized\n}\n\nfunction requireCurrentUserId(instance: SanityInstance): string {\n const userId = getCurrentUserState(instance).getCurrent()?.id\n if (!userId) {\n throw new Error('Writing a comment requires a logged in user.')\n }\n return userId\n}\n\n/** Waits out discovery, then insists the addon dataset exists. */\nasync function requireAddonDataset(\n instance: SanityInstance,\n resource: DocumentResource,\n): Promise<string> {\n const datasetName = await firstValueFrom(\n getAddonDatasetState(instance, {resource}).observable.pipe(\n filter((value) => value !== undefined),\n take(1),\n ),\n )\n\n if (!datasetName) {\n throw new Error('This project has no comments dataset, so there is no comment to change.')\n }\n\n return datasetName\n}\n\nasync function getWritableClient(\n instance: SanityInstance,\n resource: DocumentResource,\n {createIfMissing}: {createIfMissing: boolean},\n): Promise<SanityClient> {\n const {projectId} = assertDatasetResource(resource)\n\n // Provisioning is a project-wide side effect, so only the create path may\n // trigger it. Editing or deleting a comment implies the dataset is there.\n const dataset = createIfMissing\n ? await provisionAddonDataset(instance, {resource})\n : await requireAddonDataset(instance, resource)\n\n return getClient(instance, {apiVersion: COMMENTS_API_VERSION, projectId, dataset})\n}\n\nfunction buildCommentPayload(options: {\n authorId: string\n commentId: string\n contentSnapshot?: unknown\n context?: Record<string, unknown>\n documentRevisionId?: string\n handle: DocumentHandle\n message: CommentMessage\n parentCommentId?: string\n resource: DatasetResource\n fieldPath: string\n selection?: CommentTextSelection\n status: CommentStatus\n threadId: string\n documentVersionId?: string\n}): CommentPostPayload {\n const {handle, resource} = options\n\n return {\n _id: options.commentId,\n _type: 'comment',\n authorId: options.authorId,\n message: options.message,\n threadId: options.threadId,\n ...(options.parentCommentId ? {parentCommentId: options.parentCommentId} : {}),\n status: options.status,\n reactions: null,\n // The Studio writes an empty tool name when no tool is active, and reads\n // this nowhere in its UI.\n context: {tool: '', ...options.context},\n ...(options.contentSnapshot === undefined\n ? {}\n : {contentSnapshot: weakenReferencesInContentSnapshot(options.contentSnapshot)}),\n target: {\n documentRevisionId: options.documentRevisionId ?? '',\n path: {\n field: options.fieldPath,\n ...(options.selection ? {selection: options.selection} : {}),\n },\n // The comment lives in the addon dataset, so this points across to the\n // dataset holding the document. Weak, or Content Lake would refuse to\n // delete a commented document.\n document: {\n _dataset: resource.dataset,\n _projectId: resource.projectId,\n _ref: getPublishedId(DocumentId(handle.documentId)),\n _type: 'crossDatasetReference',\n _weak: true,\n },\n documentType: handle.documentType,\n ...(options.documentVersionId ? {documentVersionId: options.documentVersionId} : {}),\n },\n }\n}\n\nfunction findComment(\n state: StoreState<CommentsStoreState>,\n commentsKey: string,\n commentId: string,\n): StoredComment | undefined {\n const comments = state.get().entries[commentsKey]?.comments\n return comments && Object.hasOwn(comments, commentId) ? comments[commentId] : undefined\n}\n\nfunction findCommentById(\n state: StoreState<CommentsStoreState>,\n commentId: string,\n): StoredComment | undefined {\n for (const entry of Object.values(state.get().entries)) {\n const comments = entry?.comments\n const comment = comments && Object.hasOwn(comments, commentId) ? comments[commentId] : undefined\n if (comment) return comment\n }\n return undefined\n}\n\nasync function postComment(\n context: StoreContext<CommentsStoreState, BoundResourceKey>,\n handle: DocumentHandle & CommentsOptions,\n payload: CommentPostPayload,\n): Promise<Comment> {\n const {state, instance, key} = context\n const commentsKey = getCommentsKey(toCommentsKeyParts(instance, handle))\n\n // Show it before the round trip, so a thread feels immediate.\n state.set('addComment', addComment(commentsKey, payload))\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: true})\n // `createIfNotExists` makes duplicate writes of the same id harmless, which\n // covers both a retry after a lost response and two concurrent calls with\n // the same comment id.\n const created = await client.createIfNotExists(payload, {tag: 'comments.create'})\n state.set('receiveComment', receiveComment(commentsKey, created))\n return normalizeComment(created)\n } catch (error) {\n // Keep it on screen carrying the failure, so an app can offer a retry with\n // the same id rather than silently losing what someone typed.\n state.set(\n 'setCommentCreateError',\n setCommentCreateError(payload._id, error instanceof Error ? error : new Error(String(error))),\n )\n throw error\n }\n}\n\n/**\n * Starts a thread on a document, or on one of its fields.\n *\n * Creates the project's comments dataset if this is the first comment anywhere\n * in it. The comment appears locally before the server confirms it; if the\n * write fails it stays, carrying `_state.createError`, and retrying with the\n * same `commentId` replaces it.\n *\n * @beta\n */\nexport const createComment: (\n instance: SanityInstance,\n options: CreateCommentOptions,\n) => Promise<Comment> = bindActionByResource(\n commentsStore,\n // `async` so a validation failure rejects rather than throwing at the call\n // site, which would slip past a `.catch()`.\n async (\n context: StoreContext<CommentsStoreState, BoundResourceKey>,\n options: CreateCommentOptions,\n ) => {\n const {instance, key} = context\n const perspective = options.perspective ?? instance.config.perspective\n const fieldPath = requireFieldPath(options.fieldPath)\n\n return postComment(\n context,\n options,\n buildCommentPayload({\n authorId: requireCurrentUserId(instance),\n commentId: options.commentId ?? randomUuid(),\n contentSnapshot: options.contentSnapshot,\n context: options.context,\n documentRevisionId: options.documentRevisionId,\n handle: options,\n message: options.message,\n resource: assertDatasetResource(key.resource),\n fieldPath,\n selection: options.selection,\n status: options.status ?? 'open',\n threadId: options.threadId ?? randomUuid(),\n ...(isReleasePerspective(perspective) ? {documentVersionId: perspective.releaseName} : {}),\n }),\n )\n },\n)\n\n/**\n * Adds a reply to an existing thread.\n *\n * The thread and field are taken from the parent comment, so a reply always\n * matches the same list as the comment it answers. Supply them only when the\n * parent is not loaded.\n *\n * @beta\n */\nexport const replyToComment: (\n instance: SanityInstance,\n options: ReplyToCommentOptions,\n) => Promise<Comment> = bindActionByResource(\n commentsStore,\n async (\n context: StoreContext<CommentsStoreState, BoundResourceKey>,\n options: ReplyToCommentOptions,\n ) => {\n const {instance, key, state} = context\n const commentsKey = getCommentsKey(toCommentsKeyParts(instance, options))\n const parent = findComment(state, commentsKey, options.parentCommentId)\n\n const threadId = options.threadId ?? parent?.threadId\n if (!threadId) {\n throw new Error(\n `Cannot reply to \"${options.parentCommentId}\": it is not loaded, so pass its threadId.`,\n )\n }\n\n const status = parent?.status ?? options.status\n if (!status) {\n throw new Error(\n `Cannot reply to \"${options.parentCommentId}\": it is not loaded, so pass its status.`,\n )\n }\n\n const perspective = options.perspective ?? instance.config.perspective\n\n return postComment(\n context,\n options,\n buildCommentPayload({\n authorId: requireCurrentUserId(instance),\n commentId: options.commentId ?? randomUuid(),\n handle: options,\n message: options.message,\n // Replies to a reply belong to the thread's first comment, matching how\n // the Studio flattens threads.\n parentCommentId: parent?.parentCommentId ?? options.parentCommentId,\n resource: assertDatasetResource(key.resource),\n // Inherited from the parent unless the caller names one, and checked\n // either way: a reply with no path crashes the Studio inspector exactly\n // as a pathless thread would.\n fieldPath: requireFieldPath(options.fieldPath ?? parent?.target.path?.field ?? ''),\n // A reply into a resolved thread stays consistent with its parent.\n status,\n threadId,\n ...(isReleasePerspective(perspective) ? {documentVersionId: perspective.releaseName} : {}),\n }),\n )\n },\n)\n\n/**\n * Rewrites a comment's message and stamps `lastEditedAt`.\n * @beta\n */\nexport const updateComment: (\n instance: SanityInstance,\n options: UpdateCommentOptions,\n) => Promise<void> = bindActionByResource(\n commentsStore,\n async (\n {state, instance, key}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {commentId, message}: UpdateCommentOptions,\n ) => {\n const lastEditedAt = new Date().toISOString()\n const transactionId = randomUuid()\n const previous = findCommentById(state, commentId)\n\n if (previous) {\n state.set('setPendingTransaction', setPendingTransaction(commentId, transactionId))\n state.set('updateComment', applyCommentUpdate(commentId, {message, lastEditedAt}))\n }\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: false})\n await client\n .transaction()\n .transactionId(transactionId)\n .patch(client.patch(commentId).set({message, lastEditedAt}))\n .commit({tag: 'comments.update'})\n if (previous) {\n state.set('clearPendingTransaction', clearPendingTransaction(commentId))\n }\n } catch (error) {\n if (previous) {\n state.set(\n 'rollbackCommentUpdate',\n rollbackCommentUpdate(commentId, transactionId, previous),\n )\n }\n throw error\n }\n },\n)\n\n/**\n * Resolves or reopens a thread.\n *\n * Pass the thread's first comment: replies follow it, patched server-side in\n * one query so a reply created by someone else at the same moment is caught\n * too.\n *\n * @beta\n */\nexport const setCommentStatus: (\n instance: SanityInstance,\n options: SetCommentStatusOptions,\n) => Promise<void> = bindActionByResource(\n commentsStore,\n async (\n {state, instance, key}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {commentId, status}: SetCommentStatusOptions,\n ) => {\n const transactionId = randomUuid()\n const previousComments: StoredComment[] = []\n\n for (const entry of Object.values(state.get().entries)) {\n for (const candidate of Object.values(entry?.comments ?? {})) {\n if (candidate._id !== commentId && candidate.parentCommentId !== commentId) continue\n previousComments.push(candidate)\n state.set('setPendingTransaction', setPendingTransaction(candidate._id, transactionId))\n state.set('updateCommentStatus', applyCommentUpdate(candidate._id, {status}))\n }\n }\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: false})\n\n await client.mutate(\n [\n {patch: {id: commentId, set: {status}}},\n {\n patch: {\n query: '*[_type == \"comment\" && parentCommentId == $commentId]',\n params: {commentId},\n set: {status},\n },\n },\n ],\n {transactionId, tag: 'comments.set-status'},\n )\n for (const previous of previousComments) {\n state.set('clearPendingTransaction', clearPendingTransaction(previous._id))\n }\n } catch (error) {\n for (const previous of previousComments) {\n state.set(\n 'rollbackCommentUpdate',\n rollbackCommentUpdate(previous._id, transactionId, previous),\n )\n }\n throw error\n }\n },\n)\n\n/**\n * Deletes a comment, and its replies when it starts a thread.\n * @beta\n */\nexport const removeComment: (\n instance: SanityInstance,\n options: RemoveCommentOptions,\n) => Promise<void> = bindActionByResource(\n commentsStore,\n async (\n {state, instance, key}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {commentId}: RemoveCommentOptions,\n ) => {\n const removed = Object.entries(state.get().entries).flatMap(([entryKey, entry]) => {\n const comments = Object.values(entry?.comments ?? {}).filter(\n (comment) => comment._id === commentId || comment.parentCommentId === commentId,\n )\n return comments.length ? [{key: entryKey, comments}] : []\n })\n\n state.set('removeComment', removeCommentById(commentId))\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: false})\n await client.mutate(\n [\n {\n delete: {\n query: '*[_type == \"comment\" && parentCommentId == $commentId]',\n params: {commentId},\n },\n },\n {delete: {id: commentId}},\n ],\n {tag: 'comments.remove'},\n )\n } catch (error) {\n state.set('restoreComments', restoreComments(removed))\n throw error\n }\n },\n)\n","import {\n type DatasetHandle,\n type DocumentHandle,\n type DocumentTypeHandle,\n type ProjectHandle,\n} from './sanityConfig'\n\n/**\n * Creates or validates a `DocumentHandle` object.\n * Ensures the provided object conforms to the `DocumentHandle` interface.\n *\n * This signature applies when the handle passes both `projectId` and `dataset` as strings. The\n * returned handle keeps both fields required and typed as the values passed, so\n * `createDocumentHandle({projectId: 'abc123', dataset: 'production', ...}).projectId` is typed\n * `'abc123'` instead of `'abc123' | undefined`. A field typed `string | undefined`, such as one\n * spread from a `SanityConfig`, uses the next signature and stays optional. `TDataset` and\n * `TProjectId` are `const` so a call written where a typed handle is expected, such as an\n * annotated variable or a prop typed `DocumentHandle<'book', 'production', 'abc123'>`, keeps the\n * literals instead of inferring `string`.\n * @param handle - The object containing document identification properties.\n * @returns The validated `DocumentHandle` object, with `projectId` and `dataset` required.\n * @public\n */\nexport function createDocumentHandle<\n TDocumentType extends string = string,\n const TDataset extends string = string,\n const TProjectId extends string = string,\n>(\n handle: DocumentHandle<TDocumentType, TDataset, TProjectId> & {\n dataset: TDataset\n projectId: TProjectId\n },\n): DocumentHandle<TDocumentType, TDataset, TProjectId> & {dataset: TDataset; projectId: TProjectId}\n\n/**\n * Creates or validates a `DocumentHandle` object.\n * Ensures the provided object conforms to the `DocumentHandle` interface.\n * @param handle - The object containing document identification properties.\n * @returns The validated `DocumentHandle` object.\n * @public\n */\nexport function createDocumentHandle<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n handle: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): DocumentHandle<TDocumentType, TDataset, TProjectId>\n\n/** @public */\nexport function createDocumentHandle(handle: DocumentHandle): DocumentHandle {\n return handle\n}\n\n/**\n * Creates or validates a `DocumentTypeHandle` object.\n * Ensures the provided object conforms to the `DocumentTypeHandle` interface.\n *\n * This signature applies when the handle passes both `projectId` and `dataset` as strings. The\n * returned handle keeps both fields required and typed as the values passed.\n * @param handle - The object containing document type identification properties.\n * @returns The validated `DocumentTypeHandle` object, with `projectId` and `dataset` required.\n * @public\n */\nexport function createDocumentTypeHandle<\n TDocumentType extends string = string,\n const TDataset extends string = string,\n const TProjectId extends string = string,\n>(\n handle: DocumentTypeHandle<TDocumentType, TDataset, TProjectId> & {\n dataset: TDataset\n projectId: TProjectId\n },\n): DocumentTypeHandle<TDocumentType, TDataset, TProjectId> & {\n dataset: TDataset\n projectId: TProjectId\n}\n\n/**\n * Creates or validates a `DocumentTypeHandle` object.\n * Ensures the provided object conforms to the `DocumentTypeHandle` interface.\n * @param handle - The object containing document type identification properties.\n * @returns The validated `DocumentTypeHandle` object.\n * @public\n */\nexport function createDocumentTypeHandle<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n handle: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>,\n): DocumentTypeHandle<TDocumentType, TDataset, TProjectId>\n\n/** @public */\nexport function createDocumentTypeHandle(handle: DocumentTypeHandle): DocumentTypeHandle {\n return handle\n}\n\n/**\n * Creates or validates a `ProjectHandle` object.\n * Ensures the provided object conforms to the `ProjectHandle` interface.\n * @param handle - The object containing project identification properties.\n * @returns The validated `ProjectHandle` object.\n * @public\n */\nexport function createProjectHandle<TProjectId extends string = string>(\n handle: ProjectHandle<TProjectId>,\n): ProjectHandle<TProjectId> {\n return handle\n}\n\n/**\n * Creates or validates a `DatasetHandle` object.\n * Ensures the provided object conforms to the `DatasetHandle` interface.\n *\n * This signature applies when the handle passes both `projectId` and `dataset` as strings. The\n * returned handle keeps both fields required and typed as the values passed.\n * @param handle - The object containing dataset identification properties.\n * @returns The validated `DatasetHandle` object, with `projectId` and `dataset` required.\n * @public\n */\nexport function createDatasetHandle<\n const TDataset extends string = string,\n const TProjectId extends string = string,\n>(\n handle: DatasetHandle<TDataset, TProjectId> & {dataset: TDataset; projectId: TProjectId},\n): DatasetHandle<TDataset, TProjectId> & {dataset: TDataset; projectId: TProjectId}\n\n/**\n * Creates or validates a `DatasetHandle` object.\n * Ensures the provided object conforms to the `DatasetHandle` interface.\n * @param handle - The object containing dataset identification properties.\n * @returns The validated `DatasetHandle` object.\n * @public\n */\nexport function createDatasetHandle<\n TDataset extends string = string,\n TProjectId extends string = string,\n>(handle: DatasetHandle<TDataset, TProjectId>): DatasetHandle<TDataset, TProjectId>\n\n/** @public */\nexport function createDatasetHandle(handle: DatasetHandle): DatasetHandle {\n return handle\n}\n","/**\n * Public API for configuring SDK logging\n *\n * @module loggingConfig\n */\n\nimport {configureLogging as _configureLogging, type LoggerConfig} from '../utils/logger'\n\n/**\n * Configure logging for the Sanity SDK\n *\n * This function allows you to control what logs are output by the SDK,\n * making it easier to debug issues in development or production.\n *\n * @remarks\n * **Zero-Config via Environment Variable (Recommended):**\n *\n * The SDK automatically reads the `DEBUG` environment variable, making it\n * easy to enable logging without code changes:\n *\n * ```bash\n * # Enable all SDK logging at debug level\n * DEBUG=sanity:* npm start\n *\n * # Enable specific namespaces\n * DEBUG=sanity:auth,sanity:document npm start\n *\n * # Enable trace level for all namespaces\n * DEBUG=sanity:trace:* npm start\n *\n * # Enable internal/maintainer logs\n * DEBUG=sanity:*:internal npm start\n * ```\n *\n * This matches the pattern used by Sanity CLI and Studio, making it familiar\n * and easy for support teams to help troubleshoot issues.\n *\n * **Programmatic Configuration (Advanced):**\n *\n * For more control (custom handlers, dynamic configuration), call this function\n * explicitly. Programmatic configuration overrides environment variables.\n *\n * **For Application Developers:**\n * Use `info`, `warn`, or `error` levels to see high-level SDK activity\n * without being overwhelmed by internal details.\n *\n * **For SDK Maintainers:**\n * Use `debug` or `trace` levels with `internal: true` to see detailed\n * information about store operations, RxJS streams, and state transitions.\n *\n * **Instance Context:**\n * Logs automatically include instance information (projectId, dataset, instanceId)\n * when available, making it easier to debug multi-instance scenarios:\n * ```\n * [INFO] [auth] [project:abc] [dataset:production] User logged in\n * ```\n *\n * **Available Namespaces:**\n * - `sdk` - SDK initialization, configuration, and lifecycle\n * - `auth` - Authentication and authorization (when instrumented in the future)\n * - And more as logging is added to modules\n *\n * @example Zero-config via environment variable (recommended for debugging)\n * ```bash\n * # Just set DEBUG and run your app - no code changes needed!\n * DEBUG=sanity:* npm start\n * ```\n *\n * @example Programmatic configuration (application developer)\n * ```ts\n * import {configureLogging} from '@sanity/sdk'\n *\n * // Log warnings and errors for auth and document operations\n * configureLogging({\n * level: 'warn',\n * namespaces: ['auth', 'document']\n * })\n * ```\n *\n * @example Programmatic configuration (SDK maintainer)\n * ```ts\n * import {configureLogging} from '@sanity/sdk'\n *\n * // Enable all logs including internal traces\n * configureLogging({\n * level: 'trace',\n * namespaces: ['*'],\n * internal: true\n * })\n * ```\n *\n * @example Custom handler (for testing)\n * ```ts\n * import {configureLogging} from '@sanity/sdk'\n *\n * const logs: string[] = []\n * configureLogging({\n * level: 'info',\n * namespaces: ['*'],\n * handler: {\n * error: (msg) => logs.push(msg),\n * warn: (msg) => logs.push(msg),\n * info: (msg) => logs.push(msg),\n * debug: (msg) => logs.push(msg),\n * trace: (msg) => logs.push(msg),\n * }\n * })\n * ```\n *\n * @public\n */\nexport function configureLogging(config: LoggerConfig): void {\n _configureLogging(config)\n\n // Always log configuration (bypasses namespace filtering)\n // This ensures users see the message regardless of which namespaces they enable\n const configLevel = config.level || 'warn'\n const shouldLog = ['info', 'debug', 'trace'].includes(configLevel) || configLevel === 'warn'\n\n if (shouldLog && config.handler?.info) {\n config.handler.info(`[${new Date().toISOString()}] [INFO] [sdk] Logging configured`, {\n level: configLevel,\n namespaces: config.namespaces || [],\n internal: config.internal || false,\n source: 'programmatic',\n })\n } else if (shouldLog) {\n // eslint-disable-next-line no-console\n console.info(`[${new Date().toISOString()}] [INFO] [sdk] Logging configured`, {\n level: configLevel,\n namespaces: config.namespaces || [],\n internal: config.internal || false,\n source: 'programmatic',\n })\n }\n}\n\n/**\n * Re-export types for public API\n * @public\n */\nexport type {\n InstanceContext,\n LogContext,\n Logger,\n LoggerConfig,\n LogLevel,\n LogNamespace,\n} from '../utils/logger'\n","import {type DatasetsResponse} from '@sanity/client'\nimport {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type ProjectHandle} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\n\nconst API_VERSION = 'v2025-02-19'\n\nfunction resolveProjectId(instance: SanityInstance, options?: ProjectHandle): string {\n const projectId = options?.projectId ?? instance.config.projectId\n if (!projectId) {\n throw new Error('A projectId is required to use the datasets API.')\n }\n return projectId\n}\n\n/**\n * Fetcher for a project's datasets (`GET /datasets`), on the shared fetcher\n * cache.\n *\n * @internal\n */\nexport const datasets = defineFetcher<[options?: ProjectHandle], DatasetsResponse>({\n name: 'datasets',\n getKey: resolveProjectId,\n fetch: (instance) => (options) =>\n getClientState(instance, {\n apiVersion: API_VERSION,\n projectId: resolveProjectId(instance, options),\n useProjectHostname: true,\n }).observable.pipe(switchMap((client) => client.observable.datasets.list())),\n})\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\n\n/**\n * The id of the specific document a user is editing under a given perspective:\n * the draft, the published document, or a release version.\n *\n * Distinct from {@link getEffectiveDocumentId}, which resolves the canonical key a\n * document's state is stored under and so collapses a draft onto its published id.\n * This one keeps them apart, because some things compare the exact id. Presence is\n * the motivating case: the Studio's field indicators match the id its form is on,\n * exactly, so an app reporting the published id shows up in the Studio at document\n * level while never lighting up a single field.\n *\n * @remarks\n * Resolved from the handle alone, which is right whenever a draft or version\n * exists, so for any document actually being edited. The Studio additionally falls\n * back to whichever document exists, so for a published document nobody has edited\n * yet its form sits on the published id where this returns the draft id. Matching\n * that would mean reading document state, which presence deliberately does not\n * depend on.\n *\n * @internal\n */\nexport function getEditingDocumentId(\n doc: Pick<DocumentHandle, 'documentId' | 'liveEdit' | 'perspective'>,\n): string {\n if (doc.liveEdit) {\n return getPublishedId(DocumentId(doc.documentId))\n }\n if (isReleasePerspective(doc.perspective)) {\n return getVersionId(DocumentId(doc.documentId), doc.perspective.releaseName)\n }\n if (doc.perspective === 'published') {\n return getPublishedId(DocumentId(doc.documentId))\n }\n return getDraftId(DocumentId(doc.documentId))\n}\n\nexport function getEffectiveDocumentId(doc: DocumentHandle): string {\n if (doc.liveEdit) {\n return doc.documentId\n } else if (isReleasePerspective(doc.perspective)) {\n return getVersionId(DocumentId(doc.documentId), doc.perspective.releaseName)\n } else {\n return getPublishedId(DocumentId(doc.documentId))\n }\n}\n","import {type ReleaseDocument} from '@sanity/client'\nimport {SanityEncoder} from '@sanity/mutate'\nimport {type PatchMutation as SanityMutatePatchMutation} from '@sanity/mutate/_unstable_store'\nimport {type PatchMutation, type PatchOperations} from '@sanity/types'\n\nimport {\n type DocumentHandle,\n type DocumentTypeHandle,\n type ReleaseHandle,\n} from '../config/sanityConfig'\nimport {type ResolveDocument} from '../typegen/resolve'\nimport {getEffectiveDocumentId} from './util'\n\nconst isSanityMutatePatch = (value: unknown): value is SanityMutatePatchMutation => {\n if (typeof value !== 'object' || !value) return false\n if (!('type' in value) || typeof value.type !== 'string' || value.type !== 'patch') return false\n if (!('id' in value) || typeof value.id !== 'string') return false\n if (!('patches' in value) || !Array.isArray(value.patches)) return false\n return true\n}\n\n/**\n * Represents an action to create a new document.\n * Specifies the document type and optionally a document ID (which will be treated as the published ID).\n * @beta\n */\nexport interface CreateDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentTypeHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.create'\n /**\n * Optional initial field values for the document.\n * These values will be set when the document is created.\n * System fields (_id, _type, _rev, _createdAt, _updatedAt) are omitted as they are set automatically.\n */\n initialValue?: Partial<\n Omit<\n ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>,\n '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'\n >\n >\n}\n\n/**\n * Represents an action to delete an existing document.\n * Requires the full document handle including the document ID.\n * @beta\n */\nexport interface DeleteDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.delete'\n}\n\n/**\n * Represents an action to edit an existing document using patches.\n * Requires the full document handle and an array of patch operations.\n * @beta\n */\nexport interface EditDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.edit'\n patches?: PatchOperations[]\n /**\n * When `true`, the given `patches` are forwarded verbatim to the server and\n * applied as-is locally, instead of being re-derived by diffing document\n * snapshots. This preserves the operational intent of the patches (e.g.\n * keyed array inserts/unsets), which lets concurrent edits from other\n * clients interleave coherently instead of being overwritten by\n * position-based patches computed from a stale snapshot.\n *\n * Use this when the patches were produced by an editor that tracks its own\n * operations (e.g. a collaborative text editor). During a rebase onto a\n * changed remote document, preserved patches are re-applied operationally;\n * if a patch can no longer apply, the transaction is skipped and reported\n * as a `rebase-error` document event.\n */\n preserveOperations?: boolean\n}\n\n/**\n * Represents an action to publish the draft version of a document.\n * Requires the full document handle.\n * @beta\n */\nexport interface PublishDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.publish'\n}\n\n/**\n * Represents an action to unpublish a document, moving its published content to a draft.\n * Requires the full document handle.\n * @beta\n */\nexport interface UnpublishDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.unpublish'\n}\n\n/**\n * Represents an action to discard the draft changes of a document.\n * Requires the full document handle.\n * @beta\n */\nexport interface DiscardDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.discard'\n}\n\n/**\n * Union type representing all possible document actions within the SDK.\n * @beta\n */\nexport type DocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> =\n | CreateDocumentAction<TDocumentType, TDataset, TProjectId>\n | DeleteDocumentAction<TDocumentType, TDataset, TProjectId>\n | EditDocumentAction<TDocumentType, TDataset, TProjectId>\n | PublishDocumentAction<TDocumentType, TDataset, TProjectId>\n | UnpublishDocumentAction<TDocumentType, TDataset, TProjectId>\n | DiscardDocumentAction<TDocumentType, TDataset, TProjectId>\n\n/**\n * Union of every action accepted by `applyDocumentActions` — both document-\n * level actions and release-lifecycle actions.\n * @beta\n */\nexport type Action<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> = DocumentAction<TDocumentType, TDataset, TProjectId> | ReleaseAction\n\n/**\n * Creates a `CreateDocumentAction` object.\n * @param doc - A handle identifying the document type, dataset, and project. An optional `documentId` can be provided.\n * @param initialValue - Optional initial field values for the document. (System fields are omitted as they are set automatically.)\n * @returns A `CreateDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function createDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>,\n initialValue?: Partial<\n Omit<\n ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>,\n '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'\n >\n >,\n): CreateDocumentAction<TDocumentType, TDataset, TProjectId> {\n // users may pass in an explicit documentId -- make sure we format it correctly for the action\n let effectiveDocumentId\n if (typeof doc.documentId === 'string') {\n effectiveDocumentId = getEffectiveDocumentId({...doc, documentId: doc.documentId})\n }\n return {\n type: 'document.create',\n ...doc,\n ...(effectiveDocumentId && {documentId: effectiveDocumentId}),\n ...(initialValue && {initialValue}),\n }\n}\n\n/**\n * Creates a `DeleteDocumentAction` object.\n * @param doc - A handle uniquely identifying the document to be deleted.\n * @returns A `DeleteDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function deleteDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): DeleteDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.delete',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\nfunction convertSanityMutatePatch(\n sanityPatchMutation: SanityMutatePatchMutation,\n): EditDocumentAction['patches'] {\n const encoded = SanityEncoder.encode(sanityPatchMutation) as PatchMutation[]\n return encoded.map((i) => {\n const copy: PatchOperations = {...i.patch}\n if ('id' in copy) delete copy.id\n return copy\n })\n}\n\n/**\n * Options for creating an `EditDocumentAction`.\n * @beta\n */\nexport interface EditDocumentOptions {\n /**\n * Forward the given patches verbatim instead of re-deriving them by\n * diffing document snapshots. See {@link EditDocumentAction.preserveOperations}.\n */\n preserveOperations?: boolean\n}\n\n/**\n * Creates an `EditDocumentAction` object with patches for modifying a document.\n * Accepts patches in either the standard `PatchOperations` format or as a `SanityMutatePatchMutation` from `@sanity/mutate`.\n *\n * @param doc - A handle uniquely identifying the document to be edited.\n * @param sanityMutatePatch - A patch mutation object from `@sanity/mutate`.\n * @param options - Options controlling how the patches are processed.\n * @returns An `EditDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function editDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n sanityMutatePatch: SanityMutatePatchMutation,\n options?: EditDocumentOptions,\n): EditDocumentAction<TDocumentType, TDataset, TProjectId>\n/**\n * Creates an `EditDocumentAction` object with patches for modifying a document.\n *\n * @param doc - A handle uniquely identifying the document to be edited.\n * @param patches - A single patch operation or an array of patch operations.\n * @param options - Options controlling how the patches are processed.\n * @returns An `EditDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function editDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n patches?: PatchOperations | PatchOperations[],\n options?: EditDocumentOptions,\n): EditDocumentAction<TDocumentType, TDataset, TProjectId>\n/**\n * Creates an `EditDocumentAction` object with patches for modifying a document.\n * This is the implementation signature and handles the different patch input types.\n *\n * @param doc - A handle uniquely identifying the document to be edited.\n * @param patches - Patches in various formats (`PatchOperations`, `PatchOperations[]`, or `SanityMutatePatchMutation`).\n * @param options - Options controlling how the patches are processed.\n * @returns An `EditDocumentAction` object ready for dispatch.\n */\nexport function editDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n patches?: PatchOperations | PatchOperations[] | SanityMutatePatchMutation,\n options?: EditDocumentOptions,\n): EditDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n const preserveOperations = options?.preserveOperations && {preserveOperations: true as const}\n\n if (isSanityMutatePatch(patches)) {\n const converted = convertSanityMutatePatch(patches) ?? []\n return {\n ...doc,\n type: 'document.edit',\n documentId: effectiveDocumentId,\n patches: converted,\n ...preserveOperations,\n }\n }\n\n return {\n ...doc,\n type: 'document.edit',\n documentId: effectiveDocumentId,\n ...(patches && {patches: Array.isArray(patches) ? patches : [patches]}),\n ...preserveOperations,\n }\n}\n\n/**\n * Creates a `PublishDocumentAction` object.\n * @param doc - A handle uniquely identifying the document to be published.\n * @returns A `PublishDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function publishDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): PublishDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.publish',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\n/**\n * Creates an `UnpublishDocumentAction` object.\n * @param doc - A handle uniquely identifying the document to be unpublished.\n * @returns An `UnpublishDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function unpublishDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): UnpublishDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.unpublish',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\n/**\n * Creates a `DiscardDocumentAction` object.\n * @param doc - A handle uniquely identifying the document whose draft changes are to be discarded.\n * @returns A `DiscardDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function discardDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): DiscardDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.discard',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\n/**\n * Creates a new release. The `releaseId` must be unique within the current\n * retention period.\n * @beta\n */\nexport interface CreateReleaseAction extends ReleaseHandle {\n type: 'release.create'\n metadata: ReleaseDocument['metadata']\n}\n\n/**\n * Patches the metadata of an existing release.\n * @beta\n */\nexport interface EditReleaseAction extends ReleaseHandle {\n type: 'release.edit'\n patch: PatchOperations\n}\n\n/**\n * Publishes all version documents in a release.\n * @beta\n */\nexport interface PublishReleaseAction extends ReleaseHandle {\n type: 'release.publish'\n}\n\n/**\n * Schedules a release to be published at the given UTC time. Locks the\n * version documents server-side until the release is unscheduled or published.\n * @beta\n */\nexport interface ScheduleReleaseAction extends ReleaseHandle {\n type: 'release.schedule'\n publishAt: string\n}\n\n/**\n * Unschedules a release that was previously scheduled, returning it to the\n * active editable state.\n * @beta\n */\nexport interface UnscheduleReleaseAction extends ReleaseHandle {\n type: 'release.unschedule'\n}\n\n/**\n * Archives an active release. Version documents within the release are\n * removed and no longer queryable, though still recoverable through history\n * during the retention period.\n * @beta\n */\nexport interface ArchiveReleaseAction extends ReleaseHandle {\n type: 'release.archive'\n}\n\n/**\n * Restores an archived release. Only possible during the retention period.\n * @beta\n */\nexport interface UnarchiveReleaseAction extends ReleaseHandle {\n type: 'release.unarchive'\n}\n\n/**\n * Permanently deletes an archived or published release. To remove an active\n * release, use the archive action first.\n * @beta\n */\nexport interface DeleteReleaseAction extends ReleaseHandle {\n type: 'release.delete'\n}\n\n/**\n * Union of all release actions that can be dispatched alongside document\n * actions through `applyDocumentActions`.\n * @beta\n */\nexport type ReleaseAction =\n | CreateReleaseAction\n | EditReleaseAction\n | PublishReleaseAction\n | ScheduleReleaseAction\n | UnscheduleReleaseAction\n | ArchiveReleaseAction\n | UnarchiveReleaseAction\n | DeleteReleaseAction\n\n/** @beta */\nexport function createRelease(\n handle: ReleaseHandle,\n metadata: ReleaseDocument['metadata'] = {releaseType: 'undecided'},\n): CreateReleaseAction {\n return {type: 'release.create', ...handle, metadata}\n}\n\n/** @beta */\nexport function editRelease(handle: ReleaseHandle, patch: PatchOperations): EditReleaseAction {\n return {type: 'release.edit', ...handle, patch}\n}\n\n/** @beta */\nexport function publishRelease(handle: ReleaseHandle): PublishReleaseAction {\n return {type: 'release.publish', ...handle}\n}\n\n/** @beta */\nexport function scheduleRelease(handle: ReleaseHandle, publishAt: string): ScheduleReleaseAction {\n return {type: 'release.schedule', ...handle, publishAt}\n}\n\n/** @beta */\nexport function unscheduleRelease(handle: ReleaseHandle): UnscheduleReleaseAction {\n return {type: 'release.unschedule', ...handle}\n}\n\n/** @beta */\nexport function archiveRelease(handle: ReleaseHandle): ArchiveReleaseAction {\n return {type: 'release.archive', ...handle}\n}\n\n/** @beta */\nexport function unarchiveRelease(handle: ReleaseHandle): UnarchiveReleaseAction {\n return {type: 'release.unarchive', ...handle}\n}\n\n/** @beta */\nexport function deleteRelease(handle: ReleaseHandle): DeleteReleaseAction {\n return {type: 'release.delete', ...handle}\n}\n","/**\n * When a document has no more subscribers, its state is cleaned up and removed\n * from the store. A delay used to prevent re-creating resources when the last\n * subscriber is removed quickly before another one is added. This is helpful\n * when used in a frontend where components may suspend or transition to\n * different views quickly.\n */\nexport const DOCUMENT_STATE_CLEAR_DELAY = 1000\nexport const INITIAL_OUTGOING_THROTTLE_TIME = 1000\nexport const API_VERSION = 'v2025-05-06'\n\n/**\n * How long a submitted transaction's revision is kept around waiting to be\n * verified by its listener echo. After this, the transaction is no longer\n * treated as in flight and a document held onto only for that verification can\n * be evicted. Sized to match the listener's own chaining deadline, after which\n * an echo that has not arrived is not going to.\n */\nexport const UNVERIFIED_REVISION_RETENTION_TIME = 30_000\n\n/**\n * Base delay (ms) before retrying a document listener after a transient failure\n * (an `OutOfSyncError`, a 5xx response, 408 or 429). Backoff doubles on each\n * successive retry, capped at {@link OUT_OF_SYNC_RETRY_MAX_DELAY}, and gives up\n * after {@link OUT_OF_SYNC_RETRY_COUNT} retries so a lasting failure surfaces.\n */\nexport const OUT_OF_SYNC_RETRY_BASE_DELAY = 500\nexport const OUT_OF_SYNC_RETRY_MAX_DELAY = 10_000\nexport const OUT_OF_SYNC_RETRY_COUNT = 5\n\n/**\n * Base delay (ms) before retrying the dataset ACL fetch after a transient\n * failure (a network error or 5xx response). Backoff doubles on each\n * successive retry, capped at {@link ACL_RETRY_MAX_DELAY}.\n */\nexport const ACL_RETRY_BASE_DELAY = 500\nexport const ACL_RETRY_MAX_DELAY = 10_000\n\n/**\n * Base delay (ms) before resubmitting an outgoing transaction whose submission\n * failed with a retryable error (a network error, a 5xx, 408 or 429 response,\n * a 401 from an expired session, or a 409 `transactionConflictError` from\n * contention while committing). Backoff doubles on each successive\n * attempt, capped at {@link SUBMISSION_RETRY_MAX_DELAY}. A change of client\n * (new credentials) triggers the next attempt immediately.\n */\nexport const SUBMISSION_RETRY_BASE_DELAY = 1000\nexport const SUBMISSION_RETRY_MAX_DELAY = 10_000\n","import {type Action, type ReleaseAction} from '../actions'\n\n/**\n * Path prefix shared by every release document. Matches studio's\n * `RELEASE_DOCUMENTS_PATH` constant.\n */\nconst RELEASE_DOCUMENTS_PATH = '_.releases'\n\n/**\n * Returns the full release document ID for the given release name.\n * e.g. `getReleaseDocumentId('my-release') === '_.releases.my-release'`\n * @beta\n */\nexport function getReleaseDocumentId(releaseId: string): string {\n return `${RELEASE_DOCUMENTS_PATH}.${releaseId}`\n}\n\nconst RELEASE_ACTION_TYPES = new Set([\n 'release.create',\n 'release.edit',\n 'release.publish',\n 'release.schedule',\n 'release.unschedule',\n 'release.archive',\n 'release.unarchive',\n 'release.delete',\n])\n\nexport function isReleaseAction(action: Action): action is ReleaseAction {\n return RELEASE_ACTION_TYPES.has(action.type)\n}\n","import {applyPatches, parsePatch} from '@sanity/diff-match-patch'\nimport {getIndexForKey, jsonMatch, slicePath, stringifyPath} from '@sanity/json-match'\nimport {\n type IndexTuple,\n type InsertPatch,\n isKeyedObject,\n isKeySegment,\n type PathSegment,\n} from '@sanity/types'\n\ntype SingleValuePath = Exclude<PathSegment, IndexTuple>[]\n\ntype ToNumber<TInput extends string> = TInput extends `${infer TNumber extends number}`\n ? TNumber\n : TInput\n\n/**\n * Parse a single \"segment\" that may include bracket parts.\n *\n * For example, the literal\n *\n * ```\n * \"friends[0][1]\"\n * ```\n *\n * is parsed as:\n *\n * ```\n * [\"friends\", 0, 1]\n * ```\n */\ntype ParseSegment<TInput extends string> = TInput extends `${infer TProp}[${infer TRest}`\n ? TProp extends ''\n ? [...ParseBracket<`[${TRest}`>] // no property name before '['\n : [TProp, ...ParseBracket<`[${TRest}`>]\n : TInput extends ''\n ? []\n : [TInput]\n\n/**\n * Parse one or more bracketed parts from a segment.\n *\n * It recursively \"peels off\" a bracketed part and then continues.\n *\n * For example, given the string:\n *\n * ```\n * \"[0][foo]\"\n * ```\n *\n * it produces:\n *\n * ```\n * [ToNumber<\"0\">, \"foo\"]\n * ```\n */\ntype ParseBracket<TInput extends string> = TInput extends `[${infer TPart}]${infer TRest}`\n ? [ToNumber<TPart>, ...ParseSegment<TRest>]\n : [] // no leading bracket → end of this segment\n\n/**\n * Split the entire path string on dots \"outside\" of any brackets.\n *\n * For example:\n * ```\n * \"friends[0].name\"\n * ```\n *\n * becomes:\n *\n * ```\n * [...ParseSegment<\"friends[0]\">, ...ParseSegment<\"name\">]\n * ```\n *\n * (We use a simple recursion that splits on the first dot.)\n */\ntype PathParts<TPath extends string> = TPath extends `${infer Head}.${infer Tail}`\n ? [Head, ...PathParts<Tail>]\n : TPath extends ''\n ? []\n : [TPath]\n\n/**\n * Given a type T and an array of \"access keys\" Parts, recursively index into T.\n *\n * If a part is a key, it looks up that property.\n * If T is an array and the part is a number, it \"indexes\" into the element type.\n */\ntype DeepGet<TValue, TPath extends readonly (string | number)[]> = TPath extends []\n ? TValue\n : TPath extends readonly [infer THead, ...infer TTail]\n ? // Handle traversing into optional properties\n DeepGet<\n TValue extends undefined | null\n ? undefined // Stop traversal if current value is null/undefined\n : THead extends keyof TValue // Access property if key exists\n ? TValue[THead]\n : // Handle array indexing\n THead extends number\n ? TValue extends readonly (infer TElement)[]\n ? TElement | undefined // Array element or undefined if out of bounds\n : undefined // Cannot index non-array with number\n : undefined, // Key/index doesn't exist\n TTail extends readonly (string | number)[] ? TTail : [] // Continue with the rest of the path\n >\n : never // Should be unreachable\n\n/**\n * Given a document type TDocument and a JSON Match path string TPath,\n * compute the type found at that path.\n * @beta\n */\nexport type JsonMatch<TDocument, TPath extends string> = DeepGet<TDocument, PathParts<TPath>>\n\n// this is a similar array key to the studio:\n// https://github.com/sanity-io/sanity/blob/v3.74.1/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/createProtoArrayValue.ts\nfunction generateArrayKey(length: number = 12): string {\n // Each byte gives two hex characters, so generate enough bytes.\n const numBytes = Math.ceil(length / 2)\n const bytes = crypto.getRandomValues(new Uint8Array(numBytes))\n // Convert each byte to a 2-digit hex string and join them.\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n .slice(0, length)\n}\n\nfunction memoize<TFunction extends (input: unknown) => unknown>(fn: TFunction): TFunction {\n const cache = new WeakMap<object, unknown>()\n return ((input) => {\n if (!input || typeof input !== 'object') return fn(input)\n\n const cached = cache.get(input)\n if (cached) return cached\n\n const result = fn(input)\n cache.set(input, result)\n return result\n }) as TFunction\n}\n\n/**\n * Recursively traverse a value. When an array is encountered, ensure that\n * each object item has a _key property. Memoized such that sub-objects that\n * have not changed aren't re-computed.\n */\nexport const ensureArrayKeysDeep = memoize(<R>(input: R): R => {\n if (!input || typeof input !== 'object') return input\n\n if (Array.isArray(input)) {\n // if the array is empty then just return the input\n if (!input.length) return input\n const first = input[0]\n // if the first input in the array isn't an object (null is allowed) then\n // assume that this is an array of primitives, just return the input\n if (typeof first !== 'object') return input\n\n // if all the items already have a key, then return the input\n if (input.every(isKeyedObject)) return input\n\n // otherwise return a new object item with a new key\n return input.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n if (isKeyedObject(item)) return ensureArrayKeysDeep(item)\n const next = ensureArrayKeysDeep(item)\n return {...next, _key: generateArrayKey()}\n }) as R\n }\n\n const entries = Object.entries(input).map(([key, value]) => [key, ensureArrayKeysDeep(value)])\n\n if (entries.every(([key, value]) => input[key as keyof typeof input] === value)) {\n return input\n }\n\n return Object.fromEntries(entries) as R\n})\n\n/**\n * Whether every array-addressing segment (keyed `[_key==\"…\"]` or numeric\n * index) in the given match path resolves to an existing array item.\n *\n * This mirrors Content Lake, which auto-creates missing *object* properties\n * when setting but never creates *array items*:\n *\n * ```js\n * // object properties are created, even nested ones:\n * set({}, {'meta.author.name': 'Ada'})\n * // -> {meta: {author: {name: 'Ada'}}} (applies)\n *\n * // array items are not:\n * set({items: [{_key: 'a'}]}, {'items[_key==\"b\"].title': 'x'})\n * // -> {items: [{_key: 'a'}]} (no-op)\n * ```\n *\n * Without this filter, the second example fabricated a degenerate item\n * (`{title: 'x'}`, no `_key`) that would never exist on the server, which\n * is how concurrent Portable Text edits grew phantom span-less children.\n */\nfunction resolvesArraySegments(input: unknown, path: SingleValuePath): boolean {\n let current: unknown = input\n for (const segment of path) {\n if (typeof segment === 'string') {\n // missing object properties (nested ones included) are created\n // server-side, so a string segment may descend into undefined\n current =\n typeof current === 'object' && current !== null && !Array.isArray(current)\n ? (current as Record<string, unknown>)[segment]\n : undefined\n continue\n }\n // keyed and numeric segments address array items, which are never created\n if (!Array.isArray(current)) return false\n if (isKeySegment(segment)) {\n const index = getIndexForKey(current, segment._key)\n if (index === undefined) return false\n current = current[index]\n } else {\n const index = segment < 0 ? current.length + segment : segment\n if (!(index in current)) return false\n current = current[index]\n }\n }\n return true\n}\n\n/**\n * Given an input object and a record of path expressions to values, this\n * function will set each match with the given value.\n *\n * ```js\n * const output = set(\n * {name: {first: 'initial', last: 'initial'}},\n * {'name.first': 'changed'}\n * );\n *\n * // { name: { first: 'changed', last: 'initial' } }\n * console.log(output);\n * ```\n */\nexport function set<R>(input: unknown, pathExpressionValues: Record<string, unknown>): R\nexport function set(input: unknown, pathExpressionValues: Record<string, unknown>): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, replacementValue]) =>\n Array.from(jsonMatch(input, pathExpression)).map((matchEntry) => ({\n ...matchEntry,\n replacementValue,\n })),\n )\n .filter(({path}) => resolvesArraySegments(input, path))\n .reduce((acc, {path, replacementValue}) => setDeep(acc, path, replacementValue), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of path expressions to values, this\n * function will set each match with the given value **if the value at the current\n * path is missing** (i.e. `null` or `undefined`).\n *\n * ```js\n * const output = setIfMissing(\n * {items: [null, 'initial']},\n * {'items[:]': 'changed'}\n * );\n *\n * // { items: ['changed', 'initial'] }\n * console.log(output);\n * ```\n */\nexport function setIfMissing<R>(input: unknown, pathExpressionValues: Record<string, unknown>): R\nexport function setIfMissing(\n input: unknown,\n pathExpressionValues: Record<string, unknown>,\n): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, replacementValue]) => {\n return Array.from(jsonMatch(input, pathExpression)).map((matchEntry) => ({\n ...matchEntry,\n replacementValue,\n }))\n })\n .filter((matchEntry) => matchEntry.value === null || matchEntry.value === undefined)\n .filter(({path}) => resolvesArraySegments(input, path))\n .reduce((acc, {path, replacementValue}) => setDeep(acc, path, replacementValue), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and an array of path expressions, this function will\n * remove each match from the input object.\n *\n * ```js\n * const output = unset(\n * {name: {first: 'one', last: 'two'}},\n * ['name.last']\n * );\n *\n * // { name: { first: 'one' } }\n * console.log(output);\n * ```\n */\nexport function unset<R>(input: unknown, pathExpressions: string[]): R\nexport function unset(input: unknown, pathExpressions: string[]): unknown {\n const result = pathExpressions\n .flatMap((pathExpression) => Array.from(jsonMatch(input, pathExpression)))\n // ensure that we remove in the reverse order the paths were found in\n // this is necessary for array unsets so the indexes don't change as we unset\n .reverse()\n .reduce((acc, {path}) => unsetDeep(acc, path), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object, a path expression (inside the insert patch object), and an array of items,\n * this function will insert or replace the matched items.\n *\n * **Insert before:**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c']}}\n * const output = insert(\n * input,\n * {\n * before: 'some.array[1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', '!', 'b', 'c'] } }\n * console.log(output);\n * ```\n *\n * **Insert before with negative index (append):**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c']}}\n * const output = insert(\n * input,\n * {\n * before: 'some.array[-1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', 'b', 'c', '!'] } }\n * console.log(output);\n * ```\n *\n * **Insert after:**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c']}}\n * const output = insert(\n * input,\n * {\n * after: 'some.array[1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', 'b', '!', 'c'] } }\n * console.log(output);\n * ```\n *\n * **Replace:**\n *\n * ```js\n * const output = insert(\n * { some: { array: ['a', 'b', 'c'] } },\n * {\n * replace: 'some.array[1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', '!', 'c'] } }\n * console.log(output);\n * ```\n *\n * **Replace many:**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c', 'd']}}\n * const output = insert(\n * input,\n * {\n * replace: 'some.array[1:3]',\n * items: ['!', '?']\n * }\n * );\n * // { some: { array: ['a', '!', '?', 'd'] } }\n * console.log(output);\n * ```\n */\nexport function insert<R>(input: unknown, insertPatch: InsertPatch): R\nexport function insert(input: unknown, {items, ...insertPatch}: InsertPatch): unknown {\n let operation\n let pathExpression\n\n // behavior observed from content-lake when inserting:\n // 1. if the operation is before, out of all the matches, it will use the\n // insert the items before the first match that appears in the array\n // 2. if the operation is after, it will insert the items after the first\n // match that appears in the array\n // 3. if the operation is replace, then insert the items before the first\n // match and then delete the rest\n if ('before' in insertPatch) {\n operation = 'before' as const\n pathExpression = insertPatch.before\n } else if ('after' in insertPatch) {\n operation = 'after' as const\n pathExpression = insertPatch.after\n } else if ('replace' in insertPatch) {\n operation = 'replace' as const\n pathExpression = insertPatch.replace\n }\n if (!operation) return input\n if (typeof pathExpression !== 'string') return input\n\n // in order to do an insert patch, you need to provide at least one path segment\n if (!pathExpression.length) return input\n\n const arrayPath = slicePath(pathExpression, 0, -1)\n const positionPath = slicePath(pathExpression, -1)\n\n let result = input\n\n for (const {path, value} of jsonMatch(input, arrayPath)) {\n if (!Array.isArray(value)) continue\n let arr = value\n\n switch (operation) {\n case 'replace': {\n const indexesToRemove = new Set<number>()\n let position = Infinity\n\n for (const itemMatch of jsonMatch(arr, positionPath)) {\n // there should only be one path segment for an insert patch, invalid otherwise\n if (itemMatch.path.length !== 1) continue\n const [segment] = itemMatch.path\n if (typeof segment === 'string') continue\n\n let index\n\n if (typeof segment === 'number') index = segment\n if (typeof index === 'number' && index < 0) index = arr.length + index\n if (isKeySegment(segment)) index = getIndexForKey(arr, segment._key)\n if (typeof index !== 'number') continue\n if (index < 0) index = arr.length + index\n\n indexesToRemove.add(index)\n if (index < position) position = index\n }\n\n if (position === Infinity) continue\n\n // remove all other indexes\n arr = arr\n .map((item, index) => ({item, index}))\n .filter(({index}) => !indexesToRemove.has(index))\n .map(({item}) => item)\n\n // insert at the min index\n arr = [...arr.slice(0, position), ...items, ...arr.slice(position, arr.length)]\n\n break\n }\n case 'before': {\n let position = Infinity\n\n for (const itemMatch of jsonMatch(arr, positionPath)) {\n if (itemMatch.path.length !== 1) continue\n const [segment] = itemMatch.path\n\n if (typeof segment === 'string') continue\n\n let index\n\n if (typeof segment === 'number') index = segment\n if (typeof index === 'number' && index < 0) index = arr.length + index\n if (isKeySegment(segment)) index = getIndexForKey(arr, segment._key)\n if (typeof index !== 'number') continue\n if (index < 0) index = arr.length - index\n if (index < position) position = index\n }\n\n if (position === Infinity) continue\n\n arr = [...arr.slice(0, position), ...items, ...arr.slice(position, arr.length)]\n\n break\n }\n case 'after': {\n let position = -Infinity\n\n for (const itemMatch of jsonMatch(arr, positionPath)) {\n if (itemMatch.path.length !== 1) continue\n const [segment] = itemMatch.path\n\n if (typeof segment === 'string') continue\n\n let index\n\n if (typeof segment === 'number') index = segment\n if (typeof index === 'number' && index < 0) index = arr.length + index\n if (isKeySegment(segment)) index = getIndexForKey(arr, segment._key)\n if (typeof index !== 'number') continue\n if (index > position) position = index\n }\n\n if (position === -Infinity) continue\n\n arr = [...arr.slice(0, position + 1), ...items, ...arr.slice(position + 1, arr.length)]\n\n break\n }\n\n default: {\n continue\n }\n }\n\n result = setDeep(result, path, arr)\n }\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of path expressions to numeric values,\n * this function will increment each match with the given value.\n *\n * ```js\n * const output = inc(\n * {foo: {first: 3, second: 4.5}},\n * {'foo.first': 3, 'foo.second': 4}\n * );\n *\n * // { foo: { first: 6, second: 8.5 } }\n * console.log(output);\n * ```\n */\nexport function inc<R>(input: unknown, pathExpressionValues: Record<string, number>): R\nexport function inc(input: unknown, pathExpressionValues: Record<string, number>): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, valueToAdd]) =>\n Array.from(jsonMatch(input, pathExpression)).map((matchEntry) => ({\n ...matchEntry,\n valueToAdd,\n })),\n )\n .filter(\n <T extends {value: unknown}>(matchEntry: T): matchEntry is T & {value: number} =>\n typeof matchEntry.value === 'number',\n )\n .reduce((acc, {path, value, valueToAdd}) => setDeep(acc, path, value + valueToAdd), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of path expressions to numeric values,\n * this function will decrement each match with the given value.\n *\n * ```js\n * const output = dec(\n * {foo: {first: 3, second: 4.5}},\n * {'foo.first': 3, 'foo.second': 4}\n * );\n *\n * // { foo: { first: 0, second: 0.5 } }\n * console.log(output);\n * ```\n */\nexport function dec<R>(input: unknown, pathExpressionValues: Record<string, number>): R\nexport function dec(input: unknown, pathExpressionValues: Record<string, number>): unknown {\n const result = inc(\n input,\n Object.fromEntries(\n Object.entries(pathExpressionValues)\n .filter(([, value]) => typeof value === 'number')\n .map(([key, value]) => [key, -value]),\n ),\n )\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of paths to [diff match patches][0], this\n * function will apply the diff match patch for the string at each match.\n *\n * [0]: https://www.sanity.io/docs/http-patches#aTbJhlAJ\n *\n * ```js\n * const output = diffMatchPatch(\n * {foo: 'the quick brown fox'},\n * {'foo': '@@ -13,7 +13,7 @@\\n own \\n-fox\\n+cat\\n'}\n * );\n *\n * // { foo: 'the quick brown cat' }\n * console.log(output);\n * ```\n */\nexport function diffMatchPatch<R>(input: unknown, pathExpressionValues: Record<string, string>): R\nexport function diffMatchPatch(\n input: unknown,\n pathExpressionValues: Record<string, string>,\n): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, dmp]) =>\n Array.from(jsonMatch(input, pathExpression)).map((m) => ({...m, dmp})),\n )\n .filter((i) => i.value !== undefined)\n .map(({path, value, dmp}) => {\n if (typeof value !== 'string') {\n throw new Error(\n `Can't diff-match-patch \\`${JSON.stringify(value)}\\` at path \\`${stringifyPath(path)}\\`, because it is not a string`,\n )\n }\n\n // Offsets arrive as UTF-8 byte positions and can overshoot when they\n // land inside a multi-byte character, which happens once the string a\n // patch was computed against has diverged. The default is to throw, and\n // that throw escapes the rebase loop in `reducers.ts`, kills the\n // outgoing-actions pipeline, and leaves the instance silently dropping\n // every write. Clamping to the nearest index applies the patch instead,\n // which is what the Studio and the Portable Text editor already do.\n const [nextValue] = applyPatches(parsePatch(dmp), value, {\n allowExceedingIndices: true,\n })\n return {path, value: nextValue}\n })\n .reduce((acc, {path, value}) => setDeep(acc, path, value), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Simply checks if the given document input has a `_rev` that matches the given\n * `revisionId` and throws otherwise.\n *\n * (No code example provided.)\n */\nexport function ifRevisionID<R>(input: unknown, revisionId: string): R\nexport function ifRevisionID(input: unknown, revisionId: string): unknown {\n const inputRev =\n typeof input === 'object' && !!input && '_rev' in input && typeof input._rev === 'string'\n ? input._rev\n : undefined\n\n if (typeof inputRev !== 'string') {\n throw new Error(`Patch specified \\`ifRevisionID\\` but could not find document's revision ID.`)\n }\n\n if (revisionId !== inputRev) {\n throw new Error(\n `Patch's \\`ifRevisionID\\` \\`${revisionId}\\` does not match document's revision ID \\`${inputRev}\\``,\n )\n }\n\n return input\n}\n\n/**\n * Gets a value deep inside of an object given a path. If the path does not\n * exist in the object, `undefined` will be returned.\n */\nexport function getDeep<R>(input: unknown, path: SingleValuePath): R\nexport function getDeep(input: unknown, path: SingleValuePath): unknown {\n const [currentSegment, ...restOfPath] = path\n if (currentSegment === undefined) return input\n if (typeof input !== 'object' || input === null) return undefined\n\n let key\n if (isKeySegment(currentSegment)) {\n key = getIndexForKey(input, currentSegment._key)\n } else if (typeof currentSegment === 'string') {\n key = currentSegment\n } else if (typeof currentSegment === 'number') {\n key = currentSegment\n }\n\n if (key === undefined) return undefined\n\n // Use .at() to support negative indexes on arrays.\n const nestedInput =\n typeof key === 'number' && Array.isArray(input)\n ? input.at(key)\n : (input as Record<string, unknown>)[key]\n\n return getDeep(nestedInput, restOfPath)\n}\n\n/**\n * Sets a value deep inside of an object given a path. If the path does not\n * exist in the object, it will be created.\n */\nexport function setDeep<R>(input: unknown, path: SingleValuePath, value: unknown): R\nexport function setDeep(input: unknown, path: SingleValuePath, value: unknown): unknown {\n const [currentSegment, ...restOfPath] = path\n if (currentSegment === undefined) return value\n\n // If the current input is not an object, create a new container.\n if (typeof input !== 'object' || input === null) {\n if (typeof currentSegment === 'string') {\n return {[currentSegment]: setDeep(null, restOfPath, value)}\n }\n\n let index: number | undefined\n if (isKeySegment(currentSegment)) {\n // When creating a new array via a keyed segment, use index 0.\n index = 0\n } else if (typeof currentSegment === 'number' && currentSegment >= 0) {\n index = currentSegment\n } else {\n // For negative numbers in a non‐object we simply return input.\n return input\n }\n\n return [\n // fill until index\n ...Array.from({length: index}).fill(null),\n // then set deep here\n setDeep(null, restOfPath, value),\n ]\n }\n\n // When input is an array…\n if (Array.isArray(input)) {\n let index: number | undefined\n if (isKeySegment(currentSegment)) {\n index = getIndexForKey(input, currentSegment._key) ?? input.length\n } else if (typeof currentSegment === 'number') {\n // Support negative indexes by computing the proper positive index.\n index = currentSegment < 0 ? input.length + currentSegment : currentSegment\n }\n if (index === undefined) return input\n\n if (index in input) {\n // Update the element at the resolved index.\n return input.map((nestedInput, i) =>\n i === index ? setDeep(nestedInput, restOfPath, value) : nestedInput,\n )\n }\n\n // Expand the array if needed.\n return [\n ...input,\n ...Array.from({length: index - input.length}).fill(null),\n setDeep(null, restOfPath, value),\n ]\n }\n\n // For keyed segments that aren't arrays, do nothing.\n if (typeof currentSegment === 'object') return input\n\n // For plain objects, update an existing property if it exists…\n if (currentSegment in input) {\n return Object.fromEntries(\n Object.entries(input).map(([key, nestedInput]) =>\n key === currentSegment\n ? [key, setDeep(nestedInput, restOfPath, value)]\n : [key, nestedInput],\n ),\n )\n }\n\n // ...otherwise create the new nested path.\n return {...input, [currentSegment]: setDeep(null, restOfPath, value)}\n}\n\n/**\n * Given an object and an exact path as an array, this unsets the value at the\n * given path.\n */\nexport function unsetDeep<R>(input: unknown, path: SingleValuePath): R\nexport function unsetDeep(input: unknown, path: SingleValuePath): unknown {\n const [currentSegment, ...restOfPath] = path\n if (currentSegment === undefined) return input\n if (typeof input !== 'object' || input === null) return input\n\n let _segment: string | number | undefined\n if (isKeySegment(currentSegment)) {\n _segment = getIndexForKey(input, currentSegment._key)\n } else if (typeof currentSegment === 'string' || typeof currentSegment === 'number') {\n _segment = currentSegment\n }\n if (_segment === undefined) return input\n\n // For numeric segments in arrays, compute the positive index.\n let segment: string | number = _segment\n if (typeof segment === 'number' && Array.isArray(input)) {\n segment = segment < 0 ? input.length + segment : segment\n }\n if (!(segment in input)) return input\n\n // If we're at the final segment, remove the property/element.\n if (!restOfPath.length) {\n if (Array.isArray(input)) {\n return input.filter((_nestedInput, index) => index !== segment)\n }\n return Object.fromEntries(Object.entries(input).filter(([key]) => key !== segment.toString()))\n }\n\n // Otherwise, recurse into the nested value.\n if (Array.isArray(input)) {\n return input.map((nestedInput, index) =>\n index === segment ? unsetDeep(nestedInput, restOfPath) : nestedInput,\n )\n }\n\n return Object.fromEntries(\n Object.entries(input).map(([key, value]) =>\n key === segment ? [key, unsetDeep(value, restOfPath)] : [key, value],\n ),\n )\n}\n","import {\n type Mutation,\n type MutationSelection,\n type PatchOperations,\n type SanityDocument,\n} from '@sanity/types'\n\nimport {randomUuid} from '../utils/ids'\nimport {\n dec,\n diffMatchPatch,\n ifRevisionID,\n inc,\n insert,\n set,\n setIfMissing,\n unset,\n} from './patchOperations'\n\n/**\n * Maps document IDs to documents, using `null` for documents that do not exist.\n *\n * @beta\n */\nexport type DocumentSet<TDocument extends SanityDocument = SanityDocument> = {\n [TDocumentId in string]?: TDocument | null\n}\n\ntype SupportedPatchOperation = Exclude<keyof PatchOperations, 'merge'>\n\n/**\n * Exposes the patch operations used by {@link processMutations}.\n *\n * @remarks\n * Property order determines execution order in {@link processMutations}:\n * `ifRevisionID`, `set`, `setIfMissing`, `unset`, `inc`, `dec`, `insert`,\n * then `diffMatchPatch`. Reordering the properties changes mutation results.\n * Freezes the collection to prevent callers from replacing the functions\n * used by the SDK's mutation evaluator.\n *\n * @see https://www.sanity.io/docs/http-mutations#5b4db1396e56\n *\n * @example\n * ```ts\n * const document = {items: [{_key: 'a', title: 'Before'}]}\n * const updated = patchOperations.set<typeof document>(document, {\n * 'items[_key==\"a\"].title': 'After',\n * })\n * ```\n *\n * @internal\n */\nexport const patchOperations = Object.freeze({\n ifRevisionID,\n set,\n setIfMissing,\n unset,\n inc,\n dec,\n insert,\n diffMatchPatch,\n} satisfies {\n [K in SupportedPatchOperation]: (\n input: unknown,\n pathExpressions: NonNullable<PatchOperations[K]>,\n ) => unknown\n})\n\n/**\n * Implements ID generation:\n *\n * A create mutation creates a new document. It takes the literal document\n * content as its argument. The rules for the new document's identifier are as\n * follows:\n *\n * - If the `_id` attribute is missing, then a new, random, unique ID is\n * generated.\n * - If the `_id` attribute is present but ends with `.`, then it is used as a\n * prefix for a new, random, unique ID.\n * - If the _id attribute is present, it is used as-is.\n *\n * [- source](https://www.sanity.io/docs/http-mutations#c732f27330a4)\n */\nexport function getId(id?: string): string {\n if (!id || typeof id !== 'string') return randomUuid()\n if (id.endsWith('.')) return `${id}${randomUuid()}`\n return id\n}\n\n/**\n * Configures {@link processMutations}.\n *\n * @internal\n */\nexport interface ProcessMutationsOptions {\n /**\n * The transaction ID that will become the next `_rev` for documents mutated\n * by the given mutations.\n */\n transactionId: string\n /**\n * The input document set that the mutations will be applied to.\n */\n documents: DocumentSet\n /**\n * A list of mutations to apply to the given document set.\n */\n mutations: Mutation[]\n /**\n * Supplies the default `_createdAt` and `_updatedAt` timestamps.\n * Uses the current time when omitted or empty.\n */\n timestamp?: string\n}\n\nexport function getDocumentIds(selection: MutationSelection): string[] {\n if ('id' in selection) {\n // NOTE: the `MutationSelection` type within `@sanity/client` (instead of\n // `@sanity/types`) allows for the ID field to be an array of strings so we\n // support that as well\n const array = Array.isArray(selection.id) ? selection.id : [selection.id]\n const ids = array.filter((id): id is string => typeof id === 'string')\n return Array.from(new Set(ids))\n }\n\n if ('query' in selection) {\n throw new Error(`'query' in mutations is not supported.`)\n }\n\n return []\n}\n\n/**\n * Applies mutations to an in-memory set of Sanity documents.\n * Deleted documents remain in the returned set with a value of `null`.\n *\n * Include every document affected by `mutations` in `documents`, using `null`\n * for documents that do not exist.\n *\n * Returns `documents` itself when `mutations` is empty. Otherwise, returns a\n * shallow copy of that object. The result can reuse document objects and\n * nested objects from `documents`. Mutating a reused object through the\n * result also changes the input.\n *\n * Sets `_rev` to `transactionId` on created, replaced, and patched documents.\n * Uses `timestamp` for timestamps unless the mutation preserves them.\n * Ignores `merge` patches; `inc` and `dec` skip values that are not numbers.\n * Generates document IDs when omitted or ending in `.`.\n * Patch operations can generate missing array keys.\n *\n * Does not persist documents or run Content Lake's server validation.\n *\n * @throws If a selection uses a query, a `create` targets an existing document,\n * or a `patch` targets a document that does not exist.\n * @throws If a patch operation fails, including a revision check failure or\n * `diffMatchPatch` applied to a value other than a string or `undefined`.\n *\n * @internal\n */\nexport function processMutations({\n documents,\n mutations,\n transactionId,\n timestamp,\n}: ProcessMutationsOptions): DocumentSet {\n // early return if there are no mutations given\n if (!mutations.length) return documents\n\n const dataset = {...documents}\n const now = timestamp || new Date().toISOString()\n\n for (const mutation of mutations) {\n if ('create' in mutation) {\n const id = getId(mutation.create._id)\n\n if (dataset[id]) {\n throw new Error(\n `Cannot create document with \\`_id\\` \\`${id}\\` because another document with the same ID already exists.`,\n )\n }\n\n const document: SanityDocument = {\n // > `_createdAt` and `_updatedAt` may be submitted and will override\n // > the default which is of course the current time. This can be used\n // > to reconstruct a data-set with its timestamp structure intact.\n // >\n // > [- source](https://www.sanity.io/docs/http-mutations#c732f27330a4)\n _createdAt: now,\n _updatedAt: now,\n ...mutation.create, // prefer the user's `_createdAt` and `_updatedAt`\n _rev: transactionId,\n _id: id,\n }\n\n dataset[id] = document\n\n continue\n }\n\n if ('createOrReplace' in mutation) {\n const id = getId(mutation.createOrReplace._id)\n const prev = dataset[id]\n\n const document: SanityDocument = {\n ...mutation.createOrReplace,\n // otherwise, if the mutation provided, a `_createdAt` time, use it,\n // otherwise default to now\n _createdAt:\n // if there was an existing document, use the previous `_createdAt`\n // since we're replacing the current document\n prev?._createdAt ||\n // if there was no previous document, then we're creating this\n // document for the first time so we should use the `_createdAt` from\n // the mutation if the user included it\n (typeof mutation.createOrReplace['_createdAt'] === 'string' &&\n mutation.createOrReplace['_createdAt']) ||\n // otherwise, default to now\n now,\n\n _updatedAt:\n // if there was an existing document, then set the `_updatedAt` to now\n // since we're replacing the current document\n prev\n ? now\n : // otherwise, we're creating this document for the first time,\n // in that case, use the user's `_updatedAt` if included in the\n // mutation\n (typeof mutation.createOrReplace['_updatedAt'] === 'string' &&\n mutation.createOrReplace['_updatedAt']) ||\n // otherwise default to now\n now,\n _rev: transactionId,\n _id: id,\n }\n\n dataset[id] = document\n\n continue\n }\n\n if ('createIfNotExists' in mutation) {\n const id = getId(mutation.createIfNotExists._id)\n const prev = dataset[id]\n if (prev) continue\n\n const document: SanityDocument = {\n // same logic as `create`:\n // prefer the user's `_createdAt` and `_updatedAt`\n _createdAt: now,\n _updatedAt: now,\n ...mutation.createIfNotExists,\n _rev: transactionId,\n _id: id,\n }\n\n dataset[id] = document\n\n continue\n }\n\n if ('delete' in mutation) {\n for (const id of getDocumentIds(mutation.delete)) {\n dataset[id] = null\n }\n\n continue\n }\n\n if ('patch' in mutation) {\n const {patch} = mutation\n const ids = getDocumentIds(patch)\n\n const patched = ids.map((id) => {\n if (!dataset[id]) {\n throw new Error(`Cannot patch document with ID \\`${id}\\` because it was not found.`)\n }\n\n type Entries<T> = {[K in keyof T]: [K, T[K]]}[keyof T][]\n const entries = Object.entries(patchOperations) as Entries<typeof patchOperations>\n\n return entries.reduce((acc, [type, operation]) => {\n if (patch[type]) {\n return operation(\n acc,\n // @ts-expect-error TS doesn't handle this union very well\n patch[type],\n )\n }\n return acc\n }, dataset[id])\n })\n\n for (const result of patched) {\n dataset[result._id] = {\n ...result,\n _rev: transactionId,\n _updatedAt: now,\n }\n }\n\n continue\n }\n }\n\n return dataset\n}\n","import {type MutationEvent} from '@sanity/client'\nimport {type Mutation, type SanityDocument} from '@sanity/types'\nimport {\n concat,\n concatMap,\n distinctUntilChanged,\n EMPTY,\n filter,\n map,\n type Observable,\n of,\n switchMap,\n throwError,\n timer,\n withLatestFrom,\n} from 'rxjs'\nimport {mergeMap, scan} from 'rxjs/operators'\n\nimport {type StoreContext} from '../store/defineStore'\nimport {type DocumentStoreState} from './documentStore'\nimport {processMutations} from './processMutations'\n\nexport const DEFAULT_MAX_BUFFER_SIZE = 20\nconst DEFAULT_DEADLINE_MS = 30000\n\nexport interface RemoteDocument {\n type: 'sync' | 'mutation'\n documentId: string\n document: SanityDocument | null\n revision?: string\n previousRev?: string\n timestamp: string\n /**\n * The raw mutations from the listener event that produced this document.\n * Only present for `'mutation'` events. These carry the operational intent\n * of the remote change (e.g. keyed patches) before it is collapsed into a\n * whole document snapshot.\n */\n mutations?: Mutation[]\n}\n\nexport interface SyncEvent {\n type: 'sync'\n document: SanityDocument | null\n}\n\nexport type ListenerEvent = SyncEvent | MutationEvent\n\ninterface ListenerSequenceState {\n /**\n * Tracks the latest revision from the server that can be applied locally\n * Once we receive a mutation event that has a `previousRev` that equals `base.revision`\n * we will move `base.revision` to the event's `resultRev`\n * `base.revision` will be undefined if document doesn't exist.\n * `base` is `undefined` until the snapshot event is received\n */\n base: {revision: string | undefined} | undefined\n /**\n * Array of events to pass on to the stream, e.g. when mutation applies to current head revision, or a chain is complete\n */\n emitEvents: ListenerEvent[]\n /**\n * Buffer to keep track of events that doesn't line up in a [previousRev, resultRev] -- [previousRev, resultRev] sequence\n * This can happen if events arrive out of order, or if an event in the middle for some reason gets lost\n */\n buffer: MutationEvent[]\n}\n\nexport class OutOfSyncError extends Error {\n /**\n * Attach state to the error for debugging/reporting\n */\n state: ListenerSequenceState\n constructor(message: string, state: ListenerSequenceState) {\n super(message)\n this.name = 'OutOfSyncError'\n this.state = state\n }\n}\n\nexport class DeadlineExceededError extends OutOfSyncError {\n constructor(message: string, state: ListenerSequenceState) {\n super(message, state)\n this.name = 'DeadlineExceededError'\n }\n}\n\nexport class MaxBufferExceededError extends OutOfSyncError {\n constructor(message: string, state: ListenerSequenceState) {\n super(message, state)\n this.name = 'MaxBufferExceededError'\n }\n}\n\ninterface SortListenerEventsOptions {\n maxBufferSize?: number\n resolveChainDeadline?: number\n}\n\n/**\n * Orders the given mutation events into chains where each event's\n * `previousRev` points at another event's `resultRev`. Events whose\n * `previousRev` matches no other event start a new chain. The buffer may have\n * multiple holes in it, so multiple chains can exist at once.\n */\nfunction toOrderedChains(events: MutationEvent[]): MutationEvent[][] {\n const chainStarts = events.filter(\n (event) => !events.some((other) => other.resultRev === event.previousRev),\n )\n\n return chainStarts.map((start) => {\n const chain: MutationEvent[] = []\n let current: MutationEvent | undefined = start\n while (current) {\n chain.push(current)\n const currentResultRev: string | undefined = current.resultRev\n current = events.find((event) => event.previousRev === currentResultRev)\n }\n return chain\n })\n}\n\n/**\n * Discards the leading events of a chain up to and including the event whose\n * `resultRev` equals the given revision. These events describe changes that\n * are already reflected in the current base (e.g. events replayed by a\n * resumed listener after the snapshot was refetched).\n */\nfunction discardChainTo(chain: MutationEvent[], revision: string | undefined): MutationEvent[] {\n const revisionIndex = chain.findIndex((event) => event.resultRev === revision)\n if (revisionIndex === -1) return chain\n return chain.slice(revisionIndex + 1)\n}\n\n/**\n * Takes an input observable of listener events that might arrive out of order, and emits them in sequence\n * If we receive mutation events that doesn't line up in [previousRev, resultRev] pairs we'll put them in a buffer and\n * check if we have an unbroken chain every time we receive a new event\n *\n * Buffered chains that lead up to the current base revision are discarded:\n * they describe changes already reflected in the base (e.g. events replayed\n * by a resumed listener after the snapshot was refetched)\n *\n * If the buffer grows beyond `maxBufferSize`, or if `resolveChainDeadline` milliseconds passes before the chain resolves\n * an OutOfSyncError will be thrown on the stream\n *\n * @internal\n */\nexport function sortListenerEvents(options?: SortListenerEventsOptions) {\n const {resolveChainDeadline = DEFAULT_DEADLINE_MS, maxBufferSize = DEFAULT_MAX_BUFFER_SIZE} =\n options || {}\n\n return (input$: Observable<ListenerEvent>): Observable<ListenerEvent> => {\n return input$.pipe(\n // Maintain state: current base revision, a buffer of pending mutation events,\n // and a list of events to emit.\n scan(\n (state: ListenerSequenceState, event: ListenerEvent): ListenerSequenceState => {\n // When a sync event is received, reset the base and clear any pending mutations.\n if (event.type === 'sync') {\n return {\n base: {revision: event.document?._rev},\n buffer: [],\n emitEvents: [event],\n }\n }\n // For mutation events we must have a base revision (from a prior sync event)\n if (event.type === 'mutation') {\n if (!state.base) {\n throw new Error(\n 'Invalid state. Cannot process mutation event without a base sync event',\n )\n }\n const baseRevision = state.base.revision\n\n // Order the buffered events (plus the new one) into chains, then\n // drop the parts of each chain the base already reflects.\n const chains = toOrderedChains(state.buffer.concat(event))\n .map((chain) => discardChainTo(chain, baseRevision))\n .filter((chain) => chain.length > 0)\n\n // A chain whose head continues the current base revision can be\n // applied. There can be at most one; anything else stays buffered.\n const applicable = chains.find((chain) => chain[0].previousRev === baseRevision)\n const buffer = chains.filter((chain) => chain !== applicable).flat()\n\n if (applicable) {\n const last = applicable[applicable.length - 1]\n return {\n // If the chain ends in a deletion, the new base revision is undefined.\n base: {revision: last.transition === 'disappear' ? undefined : last.resultRev},\n buffer,\n emitEvents: applicable,\n }\n }\n\n if (buffer.length >= maxBufferSize) {\n throw new MaxBufferExceededError(\n `Too many unchainable mutation events (${buffer.length}) waiting to resolve.`,\n {base: {revision: baseRevision}, buffer, emitEvents: []},\n )\n }\n\n return {\n base: {revision: baseRevision},\n buffer,\n emitEvents: [],\n }\n }\n // Any other event is simply forwarded.\n return {...state, emitEvents: [event]}\n },\n {\n base: undefined,\n buffer: [] as MutationEvent[],\n emitEvents: [] as ListenerEvent[],\n },\n ),\n switchMap((state) => {\n if (state.buffer.length > 0) {\n return concat(\n of(state),\n timer(resolveChainDeadline).pipe(\n mergeMap(() =>\n throwError(\n () =>\n new DeadlineExceededError(\n `Did not resolve chain within a deadline of ${resolveChainDeadline}ms`,\n state,\n ),\n ),\n ),\n ),\n )\n }\n return of(state)\n }),\n // Emit all events that are ready to be applied.\n mergeMap((state) => of(...state.emitEvents)),\n )\n }\n}\n\nexport const listen = (\n {state}: StoreContext<DocumentStoreState>,\n documentId: string,\n): Observable<RemoteDocument> => {\n const {sharedListener, fetchDocument} = state.get()\n\n return sharedListener.events.pipe(\n concatMap((e) => {\n // `welcome` means a fresh listener connection and `reset` means a\n // resumed listener could not replay what was missed; both require\n // refetching the snapshot. a `welcomeback` means the resume succeeded\n // and the missed events were replayed, so no refetch is needed\n if (e.type === 'welcome' || e.type === 'reset') {\n return fetchDocument(documentId).pipe(\n map((document): SyncEvent => ({type: 'sync', document})),\n )\n }\n if (e.type === 'mutation' && e.documentId === documentId) return of(e)\n return EMPTY\n }),\n sortListenerEvents(),\n withLatestFrom(\n state.observable.pipe(\n map((s) => s.documentStates[documentId]),\n filter(Boolean),\n distinctUntilChanged(),\n ),\n ),\n map(([next, documentState]): RemoteDocument => {\n if (next.type === 'sync') {\n return {\n type: 'sync',\n documentId,\n document: next.document,\n revision: next.document?._rev,\n timestamp: next.document?._updatedAt ?? new Date().toISOString(),\n }\n }\n\n // TODO: from manual testing, mendoza patches seem to be applying\n // let document\n // if (next.effects?.apply) {\n // document = applyPatch(omit(documentState.remote, '_rev'), next.effects?.apply)\n // }\n\n const [document] = Object.values(\n processMutations({\n documents: {[documentId]: documentState.remote},\n mutations: next.mutations as Mutation[],\n transactionId: next.transactionId,\n timestamp: next.timestamp,\n }),\n )\n\n const {previousRev, transactionId, timestamp} = next\n\n return {\n type: 'mutation',\n documentId,\n document: document ?? null,\n revision: transactionId,\n timestamp,\n mutations: next.mutations as Mutation[],\n ...(previousRev && {previousRev}),\n }\n }),\n )\n}\n","interface MultiKeyWeakMapNode {\n value: unknown\n next: WeakMap<object, MultiKeyWeakMapNode>\n}\n\nexport class MultiKeyWeakMap<T = unknown> {\n // The root of our nested WeakMap structure.\n #rootMap = new WeakMap<object, MultiKeyWeakMapNode>()\n\n // Instead of random IDs, we use a counter for deterministic IDs.\n static #globalIdCounter = 0\n // Each instance keeps a cache mapping a key to its assigned ID.\n #idCache = new WeakMap<object, number>()\n\n /**\n * Assigns a numeric ID to the key.\n */\n #assignId(key: object): number {\n const cachedId = this.#idCache.get(key)\n if (cachedId !== undefined) return cachedId\n\n const id = MultiKeyWeakMap.#globalIdCounter\n this.#idCache.set(key, id)\n MultiKeyWeakMap.#globalIdCounter++\n return id\n }\n\n /**\n * Remove duplicate keys and arrange them in a consistent order\n * by sorting according to their assigned IDs.\n */\n #arrangeKeys(keys: object[]): object[] {\n const uniqueKeys = Array.from(new Set(keys))\n const keyed = uniqueKeys.map((key) => [this.#assignId(key), key] as const)\n keyed.sort((a, b) => a[0] - b[0])\n return keyed.map(([, key]) => key)\n }\n\n /**\n * Recursively search the nested WeakMap structure for the value.\n */\n #getDeep(keys: object[], map: WeakMap<object, MultiKeyWeakMapNode>): unknown {\n if (keys.length === 0) return undefined\n\n const [firstKey, ...restKeys] = keys\n const node = map.get(firstKey)\n if (!node) return undefined\n if (restKeys.length === 0) return node.value\n return this.#getDeep(restKeys, node.next)\n }\n\n /**\n * Recursively create nodes along the key chain until the final key\n * is reached, then assign the value.\n */\n #setDeep(keys: object[], map: WeakMap<object, MultiKeyWeakMapNode>, value: unknown): void {\n if (keys.length === 0) return\n\n const [firstKey, ...restKeys] = keys\n let node = map.get(firstKey)\n if (!node) {\n node = {\n value: undefined,\n next: new WeakMap(),\n }\n map.set(firstKey, node)\n }\n\n if (restKeys.length === 0) {\n node.value = value\n } else {\n this.#setDeep(restKeys, node.next, value)\n }\n }\n\n /**\n * Retrieves the value associated with the array of keys.\n * The keys are de-duplicated and sorted so that the order does not matter.\n */\n get(keys: object[]): T | undefined {\n const arrangedKeys = this.#arrangeKeys(keys)\n return this.#getDeep(arrangedKeys, this.#rootMap) as T | undefined\n }\n\n /**\n * Associates the value with the given array of keys.\n */\n set(keys: object[], value: T): void {\n const arrangedKeys = this.#arrangeKeys(keys)\n this.#setDeep(arrangedKeys, this.#rootMap, value)\n }\n}\n","import {diffValue} from '@sanity/diff-patch'\nimport {jsonMatch, stringifyPath} from '@sanity/json-match'\nimport {type Mutation, type PatchOperations, type SanityDocument} from '@sanity/types'\nimport {evaluateSync, type ExprNode} from 'groq-js'\n\nimport {type Grant} from '../permissions'\nimport {type DocumentSet, processMutations} from '../processMutations'\nimport {type HttpAction} from '../reducers'\n\nexport interface ActionHandlerContext {\n base: DocumentSet\n working: DocumentSet\n transactionId: string\n timestamp: string\n grants: Record<Grant, ExprNode>\n /**\n * The current user's ID, used by GROQ's `identity()` when evaluating ACL\n * filters. May be `undefined` before the user has loaded; in that case\n * `identity()` evaluates to null.\n */\n identity: string | undefined\n outgoingActions: HttpAction[]\n outgoingMutations: Mutation[]\n}\n\nexport interface ActionHandlerResult {\n base: DocumentSet\n working: DocumentSet\n}\n\nexport function checkGrant(\n grantExpr: ExprNode,\n document: SanityDocument,\n identity: string | undefined,\n): boolean {\n const value = evaluateSync(grantExpr, {params: {document}, identity})\n return value.type === 'boolean' && value.data\n}\n\ninterface ActionErrorOptions {\n message: string\n documentId: string\n transactionId: string\n}\n\n/**\n * Thrown when a precondition for an action failed.\n */\nexport class ActionError extends Error implements ActionErrorOptions {\n documentId!: string\n transactionId!: string\n\n constructor(options: ActionErrorOptions) {\n super(options.message)\n Object.assign(this, options)\n }\n}\n\nexport class PermissionActionError extends ActionError {}\n\n/**\n * Creates a `processMutations` wrapper that surfaces application failures as\n * `ActionError`s when the caller asked to preserve their patch operations.\n * With preserved operations, patches can legitimately fail to apply (e.g.\n * re-applied onto a diverged document during a rebase), so wrapping lets a\n * rebase skip the transaction instead of failing the store. Without\n * `preserveOperations`, errors are rethrown untouched.\n */\nexport function createMutationApplier(options: {\n documentId: string\n transactionId: string\n timestamp: string\n preserveOperations: boolean | undefined\n}): (\n documents: DocumentSet,\n mutations: Mutation[],\n documentSetName: 'base' | 'working',\n) => DocumentSet {\n const {documentId, transactionId, timestamp, preserveOperations} = options\n return (documents, mutations, documentSetName) => {\n try {\n return processMutations({documents, transactionId, mutations, timestamp})\n } catch (error) {\n if (!preserveOperations) throw error\n throw new ActionError({\n documentId,\n transactionId,\n message: `Failed to apply patches to the ${documentSetName} document: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n })\n }\n }\n}\n\n/**\n * Re-applies the operational intent of user-supplied `inc`/`dec` patches to a\n * set of snapshot-diffed patches.\n *\n * The outgoing patches sent to Content Lake are re-derived by diffing the\n * base document before and after the user's patches were applied. That diff\n * collapses an `inc` into a `set` of the resulting number, which turns\n * concurrent increments from different clients into last-write-wins. This\n * helper finds diffed `set` operations that are exactly explained by the\n * user's `inc`/`dec` operations and swaps them back, so the server applies\n * the increment against whatever value is current at commit time.\n *\n * A `set` is only swapped when its value equals the base value plus the\n * accumulated user deltas for that exact path; anything else (e.g. an `inc`\n * combined with a `set` of the same field) keeps the diffed `set`.\n *\n * Content Lake fails the whole transaction when `inc`/`dec` targets a missing\n * path (unlike `set`, which always succeeds). To avoid turning a concurrent\n * field removal into a transaction revert, the emitted patch seeds the target\n * with a `setIfMissing` of the base value; the server executes `setIfMissing`\n * before `inc`/`dec` within a single patch, so a concurrently removed field\n * degrades to the same result the diffed `set` would have produced. A path\n * into a concurrently removed keyed array item can still fail the\n * transaction (`setIfMissing` cannot create array items); the transaction is\n * then reverted and reported, which is preferable to silently resurrecting\n * the item.\n */\nexport function preserveNumericOperations(\n baseBefore: SanityDocument | null | undefined,\n userPatches: PatchOperations[] | undefined,\n diffedPatches: PatchOperations[],\n): PatchOperations[] {\n if (!baseBefore || !userPatches?.length) return diffedPatches\n\n // accumulate the user's inc/dec deltas per concrete (stringified) path,\n // tracking the expected final value by replaying the additions in order\n const targets = new Map<string, {baseValue: number; expectedValue: number; delta: number}>()\n for (const patch of userPatches) {\n for (const [op, sign] of [\n ['inc', 1],\n ['dec', -1],\n ] as const) {\n const record = patch[op]\n if (!record) continue\n for (const [pathExpression, amount] of Object.entries(record)) {\n if (typeof amount !== 'number') continue\n for (const match of jsonMatch(baseBefore, pathExpression)) {\n const path = stringifyPath(match.path)\n const existing = targets.get(path)\n if (existing) {\n existing.expectedValue += sign * amount\n existing.delta += sign * amount\n } else if (typeof match.value === 'number') {\n targets.set(path, {\n baseValue: match.value,\n expectedValue: match.value + sign * amount,\n delta: sign * amount,\n })\n }\n }\n }\n }\n }\n if (!targets.size) return diffedPatches\n\n const preserved = new Map<string, {delta: number; baseValue: number}>()\n const next = diffedPatches.flatMap((patch) => {\n if (!patch.set) return [patch]\n\n const keptSet: Record<string, unknown> = {}\n for (const [path, value] of Object.entries(patch.set)) {\n const target = targets.get(path)\n if (target && typeof value === 'number' && value === target.expectedValue) {\n preserved.set(path, {delta: target.delta, baseValue: target.baseValue})\n } else {\n keptSet[path] = value\n }\n }\n\n if (Object.keys(keptSet).length === Object.keys(patch.set).length) return [patch]\n const {set: _set, ...rest} = patch\n const withKept = Object.keys(keptSet).length ? {...rest, set: keptSet} : rest\n return Object.keys(withKept).length ? [withKept] : []\n })\n\n if (!preserved.size) return next\n\n const setIfMissing: Record<string, unknown> = {}\n const inc: Record<string, number> = {}\n const dec: Record<string, number> = {}\n for (const [path, {delta, baseValue}] of preserved) {\n setIfMissing[path] = baseValue\n if (delta >= 0) inc[path] = delta\n else dec[path] = -delta\n }\n return [\n ...next,\n {\n setIfMissing,\n ...(Object.keys(inc).length && {inc}),\n ...(Object.keys(dec).length && {dec}),\n },\n ]\n}\n\ninterface ApplySingleDocPatchOptions {\n base: DocumentSet\n working: DocumentSet\n documentId: string\n patches: PatchOperations[] | undefined\n transactionId: string\n timestamp: string\n grants: Record<Grant, ExprNode>\n identity: string | undefined\n /**\n * Error message thrown when the target document does not exist in either\n * the base or working set.\n */\n notFoundMessage?: string\n /**\n * Error message thrown when the working document fails the `update` grant.\n */\n permissionMessage?: string\n /**\n * When `true`, the given patches are used verbatim instead of being\n * re-derived by diffing the base document before and after application.\n * Patch application failures are surfaced as `ActionError`s so a rebase\n * can skip the transaction instead of failing the store.\n */\n preserveOperations?: boolean\n}\n\ninterface ApplySingleDocPatchResult {\n base: DocumentSet\n working: DocumentSet\n /**\n * Patch operations representing the minimal diff between base before and\n * after the user's patches were applied. These are the patches that should\n * be sent to the server (or applied to the working set as mutations).\n */\n diffedPatches: PatchOperations[]\n /**\n * Mutation envelopes for `diffedPatches` already keyed to `documentId`.\n * Useful for callers that want to push them to `outgoingMutations`.\n */\n workingMutations: Mutation[]\n}\n\n/**\n * Shared logic for applying user-provided patches to a single document that\n * is identified by an exact ID (no draft/published wrapping). Used by the\n * liveEdit branch of `document.edit` and by `release.edit`.\n *\n * Returns the updated base + working sets, plus the diffed patches in both\n * raw and mutation form so the caller can decide what to send to the server.\n */\nexport function applySingleDocPatch({\n base: initialBase,\n working: initialWorking,\n documentId,\n patches,\n transactionId,\n timestamp,\n grants,\n identity,\n notFoundMessage = 'Cannot edit document because it does not exist.',\n permissionMessage = `You do not have permission to edit document \"${documentId}\".`,\n preserveOperations,\n}: ApplySingleDocPatchOptions): ApplySingleDocPatchResult {\n let base = initialBase\n let working = initialWorking\n\n const userPatches = patches?.map((patch) => ({patch: {id: documentId, ...patch}}))\n\n if (!userPatches?.length) {\n return {base, working, diffedPatches: [], workingMutations: []}\n }\n\n if (!working[documentId] || !base[documentId]) {\n throw new ActionError({documentId, transactionId, message: notFoundMessage})\n }\n\n const applyMutations = createMutationApplier({\n documentId,\n transactionId,\n timestamp,\n preserveOperations,\n })\n\n const baseBefore = base[documentId]\n base = applyMutations(base, userPatches, 'base')\n const baseAfter = base[documentId]\n const diffedPatches = preserveOperations\n ? (patches as PatchOperations[])\n : preserveNumericOperations(\n baseBefore,\n patches,\n diffValue(baseBefore, baseAfter) as PatchOperations[],\n )\n\n const workingBefore = working[documentId] as SanityDocument\n if (!checkGrant(grants.update, workingBefore, identity)) {\n throw new PermissionActionError({documentId, transactionId, message: permissionMessage})\n }\n\n const workingMutations: Mutation[] = diffedPatches.map((patch) => ({\n patch: {id: documentId, ...patch},\n }))\n\n working = applyMutations(working, workingMutations, 'working')\n\n return {base, working, diffedPatches, workingMutations}\n}\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type SanityDocument} from '@sanity/types'\nimport {type ExprNode, parse} from 'groq-js'\nimport {createSelector} from 'reselect'\n\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type SelectorContext} from '../store/createStateSourceAction'\nimport {randomId} from '../utils/ids'\nimport {MultiKeyWeakMap} from '../utils/MultiKeyWeakMap'\nimport {type DocumentAction} from './actions'\nimport {ActionError, PermissionActionError, processActions} from './processActions/processActions'\nimport {checkGrant} from './processActions/shared'\nimport {type DocumentSet} from './processMutations'\nimport {type SyncTransactionState} from './reducers'\n\nexport type Grant = 'read' | 'update' | 'create' | 'history'\n\nexport type DatasetAcl = {\n filter: string\n permissions: Grant[]\n}[]\n\nexport function createGrantsLookup(datasetAcl: DatasetAcl): Record<Grant, ExprNode> {\n const filtersByGrant: Record<Grant, Set<string>> = {\n create: new Set(),\n history: new Set(),\n read: new Set(),\n update: new Set(),\n }\n\n for (const entry of datasetAcl) {\n for (const grant of entry.permissions) {\n const set = filtersByGrant[grant]\n // Ignore permissions we don't model (e.g. \"manage\")\n if (!set) continue\n set.add(entry.filter)\n filtersByGrant[grant] = set\n }\n }\n\n return Object.fromEntries(\n Object.entries(filtersByGrant).map(([grant, filters]) => {\n const combinedFilter = Array.from(filters)\n .map((i) => `(${i})`)\n .join('||')\n\n if (!combinedFilter) return [grant, parse('false')]\n return [grant, parse(`$document {\"_\": ${combinedFilter}}._`)]\n }),\n ) as Record<Grant, ExprNode>\n}\n\n// Cache for documents based on an array of document objects.\nconst documentsCache = new MultiKeyWeakMap<DocumentSet>()\n// Use a WeakMap so that when a computed DocumentSet is no longer in use,\n// its nested cache for actions can be garbage-collected.\nconst actionsCache = new WeakMap<DocumentSet, Map<string, DocumentAction[]>>()\n\nconst nullReplacer: object = {}\n\n// Compute documents from state and actions.\n// (If the same documents are computed, the MultiKeyWeakMap will return the same instance.)\nconst documentsSelector = createSelector(\n [\n ({state: {documentStates}}: SelectorContext<SyncTransactionState>) => documentStates,\n (_context: SelectorContext<SyncTransactionState>, {actions}: {actions: DocumentAction[]}) =>\n actions,\n ],\n (documentStates, actions) => {\n // Collect all document IDs needed for permission checks.\n // Important: liveEdit documents don't have drafts, so we only fetch the single document to avoid waiting for non-existent draft documents.\n const documentIds = new Set(\n actions\n .map((action) => {\n if (typeof action.documentId !== 'string') return []\n // For liveEdit documents, only fetch the single document\n if (action.liveEdit) return [action.documentId]\n // For standard documents, fetch both draft and published\n const ids: string[] = [\n getPublishedId(DocumentId(action.documentId)),\n getDraftId(DocumentId(action.documentId)),\n ]\n if (isReleasePerspective(action.perspective)) {\n ids.push(getVersionId(DocumentId(action.documentId), action.perspective.releaseName))\n }\n return ids\n })\n .flat(),\n )\n\n const documents: DocumentSet = {}\n\n for (const documentId of documentIds) {\n const local = documentStates[documentId]?.local\n\n // early exit if we don't have all the documents yet\n if (local === undefined) return undefined\n documents[documentId] = local\n }\n\n // Create a key from the documents values (using a nullReplacer when needed).\n const keys = Object.values(\n // value in this record will be `undefined` because\n // of the early return if undefined is found above\n documents as Record<string, SanityDocument | null>,\n ).map((doc) => (doc === null ? nullReplacer : doc))\n const cached = documentsCache.get(keys)\n if (cached) return cached\n\n documentsCache.set(keys, documents)\n return documents\n },\n)\n\n// Cache the actions array based on a serialized version, but “attach” the cache\n// to the computed documents. That way if the computed documents object is no longer in use,\n// the cache is eligible for GC.\nconst memoizedActionsSelector = createSelector(\n [\n documentsSelector,\n (_state: SelectorContext<SyncTransactionState>, {actions}: {actions: DocumentAction[]}) =>\n actions,\n ],\n (documents, actions) => {\n if (!documents) return undefined\n\n // Get (or create) the nested Map for this computed documents.\n let nestedCache = actionsCache.get(documents)\n if (!nestedCache) {\n nestedCache = new Map<string, DocumentAction[]>()\n actionsCache.set(documents, nestedCache)\n }\n\n const normalizedActions = Array.isArray(actions) ? actions : [actions]\n\n // Use JSON.stringify to get a serialized key for the actions.\n // TODO: considering swapping thisfor a more efficient or stable hash\n const actionsKey = JSON.stringify(normalizedActions)\n const cached = nestedCache.get(actionsKey)\n if (cached) return cached\n\n nestedCache.set(actionsKey, normalizedActions)\n return normalizedActions\n },\n)\n\n/** @beta */\nexport interface PermissionDeniedReason {\n type: 'precondition' | 'access'\n message: string\n documentId?: string\n}\n\n/** @beta */\nexport type DocumentPermissionsResult =\n | {\n allowed: false\n message: string\n reasons: PermissionDeniedReason[]\n }\n | {allowed: true; message?: undefined; reasons?: undefined}\n\nconst enNarrowConjunction = new Intl.ListFormat('en', {style: 'narrow', type: 'conjunction'})\n\nexport function calculatePermissions(\n ...args: Parameters<typeof _calculatePermissions>\n): ReturnType<typeof _calculatePermissions> {\n return _calculatePermissions(...args)\n}\n\nconst _calculatePermissions = createSelector(\n [\n ({state: {grants}}: SelectorContext<SyncTransactionState>) => grants,\n ({state: {identity}}: SelectorContext<SyncTransactionState>) => identity,\n documentsSelector,\n memoizedActionsSelector,\n ],\n (\n grants: Record<Grant, ExprNode> | undefined,\n identity: string | undefined,\n documents: DocumentSet | undefined,\n actions: DocumentAction[] | undefined,\n ): DocumentPermissionsResult | undefined => {\n if (!documents) return undefined\n if (!grants) return undefined\n if (!actions) return undefined\n\n const timestamp = new Date().toISOString()\n const reasons: PermissionDeniedReason[] = []\n\n try {\n processActions({\n actions,\n transactionId: randomId(16),\n working: documents,\n base: documents,\n timestamp,\n grants,\n identity,\n })\n } catch (error) {\n if (error instanceof PermissionActionError) {\n reasons.push({\n message: error.message,\n documentId: error.documentId,\n type: 'access',\n })\n } else if (error instanceof ActionError) {\n reasons.push({\n message: error.message,\n documentId: error.documentId,\n type: 'precondition',\n })\n } else {\n throw error\n }\n }\n\n for (const action of actions) {\n // Check edit actions with no patches\n if (action.type === 'document.edit' && !action.patches?.length) {\n const docId = action.documentId\n let doc: SanityDocument | null | undefined\n if (action.liveEdit) {\n doc = documents[docId]\n }\n // don't allow users to edit version documents that don't exist\n // they should be explicitly created first, as in studio\n else if (isReleasePerspective(action.perspective)) {\n doc = documents[getVersionId(DocumentId(docId), action.perspective.releaseName)]\n } else {\n doc =\n documents[getDraftId(DocumentId(docId))] ?? documents[getPublishedId(DocumentId(docId))]\n }\n if (!doc) {\n if (isReleasePerspective(action.perspective)) {\n reasons.push({\n type: 'precondition',\n message: `The version document with ID \"${docId}\" could not be found. Please create it or add it to the release first.`,\n documentId: docId,\n })\n } else {\n reasons.push({\n type: 'precondition',\n message: `The document with ID \"${docId}\" could not be found. Please check that it exists before editing.`,\n documentId: docId,\n })\n }\n } else if (!checkGrant(grants.update, doc, identity)) {\n reasons.push({\n type: 'access',\n message: `You are not allowed to edit the document with ID \"${docId}\".`,\n documentId: docId,\n })\n }\n }\n }\n\n const allowed = reasons.length === 0\n if (allowed) return {allowed}\n\n const sortedReasons = reasons\n .map((reason, index) => ({...reason, index}))\n .sort((a, b) => {\n if (a.type !== b.type) return a.type === 'access' ? -1 : 1\n return a.message.localeCompare(b.message, 'en-US')\n })\n .map(({index: _index, ...reason}) => reason)\n\n return {\n allowed,\n reasons: sortedReasons,\n message: enNarrowConjunction.format(sortedReasons.map((i) => i.message)),\n }\n },\n)\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type CreateDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleCreate(\n action: CreateDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit) {\n if (working[documentId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `This document already exists.`,\n })\n }\n\n const newDocBase = {_type: action.documentType, _id: documentId, ...action.initialValue}\n const newDocWorking = {\n _type: action.documentType,\n _id: documentId,\n ...action.initialValue,\n }\n const mutations: Mutation[] = [{create: newDocWorking}]\n\n base = processMutations({\n documents: base,\n transactionId,\n mutations: [{create: newDocBase}],\n timestamp,\n })\n working = processMutations({\n documents: working,\n transactionId,\n mutations,\n timestamp,\n })\n\n if (!checkGrant(grants.create, working[documentId] as SanityDocument, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create document \"${documentId}\".`,\n })\n }\n\n // liveEdit documents use the mutation endpoint directly -- we don't send actions\n outgoingMutations.push(...mutations)\n return {base, working}\n }\n\n // Standard draft/published/version logic\n const versionId = isReleasePerspective(action.perspective)\n ? getVersionId(DocumentId(documentId), action.perspective.releaseName)\n : undefined\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n const alreadyHasVersion = versionId ? working[versionId] : working[draftId]\n\n if (alreadyHasVersion) {\n const errorDocType = versionId ? 'release version' : 'draft'\n throw new ActionError({\n documentId,\n transactionId,\n message: `A ${errorDocType} of this document already exists. Please use or discard the existing ${errorDocType} before creating a new one.`,\n })\n }\n\n // Spread the (possibly undefined) draft or published version directly.\n // (studio uses the draft version as a base if you are in a release perspective)\n const newDocBase = {\n ...(base[draftId] ?? base[publishedId]),\n _type: action.documentType,\n _id: versionId ?? draftId,\n ...action.initialValue,\n }\n const newDocWorking = {\n ...(working[draftId] ?? working[publishedId]),\n _type: action.documentType,\n _id: versionId ?? draftId,\n ...action.initialValue,\n }\n const mutations: Mutation[] = [{create: newDocWorking}]\n\n base = processMutations({\n documents: base,\n transactionId,\n mutations: [{create: newDocBase}],\n timestamp,\n })\n working = processMutations({\n documents: working,\n transactionId,\n mutations,\n timestamp,\n })\n\n if (versionId && !checkGrant(grants.create, working[versionId] as SanityDocument, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a release version for document \"${documentId}\".`,\n })\n } else if (\n !versionId &&\n !checkGrant(grants.create, working[draftId] as SanityDocument, identity)\n ) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a draft for document \"${documentId}\".`,\n })\n }\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.version.create',\n publishedId,\n attributes: newDocWorking,\n })\n return {base, working}\n}\n","import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'\nimport {type Mutation} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type DeleteDocumentAction} from '../actions'\nimport {processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleDelete(\n action: DeleteDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = action.documentId\n\n if (isReleasePerspective(action.perspective)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot delete a version document. You may want to use the \"unpublish\" or \"discard\" actions instead.`,\n })\n }\n\n if (action.liveEdit) {\n if (!working[documentId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: 'The document you are trying to delete does not exist.',\n })\n }\n\n if (!checkGrant(grants.update, working[documentId], identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to delete this document.`,\n })\n }\n\n const mutations: Mutation[] = [{delete: {id: documentId}}]\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n // although liveEdit documents can use the actions API for deletion,\n // having this be an action while other operations are mutations creates an inconsistency\n // (and a possible race condition in document store where mutations might get skipped)\n outgoingMutations.push(...mutations)\n return {base, working}\n }\n\n // Standard draft/published logic\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n if (!working[publishedId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: working[draftId]\n ? 'Cannot delete a document without a published version.'\n : 'The document you are trying to delete does not exist.',\n })\n }\n\n const cantDeleteDraft = working[draftId] && !checkGrant(grants.update, working[draftId], identity)\n const cantDeletePublished =\n working[publishedId] && !checkGrant(grants.update, working[publishedId], identity)\n\n if (cantDeleteDraft || cantDeletePublished) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to delete this document.`,\n })\n }\n\n const mutations: Mutation[] = [{delete: {id: publishedId}}, {delete: {id: draftId}}]\n const includeDrafts = working[draftId] ? [draftId] : undefined\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.delete',\n publishedId,\n ...(includeDrafts ? {includeDrafts} : {}),\n })\n return {base, working}\n}\n","import {DocumentId, getDraftId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type DiscardDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleDiscard(\n action: DiscardDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot discard changes for liveEdit document \"${documentId}\". LiveEdit documents do not support drafts.`,\n })\n }\n\n // draft/published or version logic\n const versionId = isReleasePerspective(action.perspective)\n ? getVersionId(DocumentId(documentId), action.perspective.releaseName)\n : getDraftId(DocumentId(documentId))\n const mutations: Mutation[] = [{delete: {id: versionId}}]\n\n if (!working[versionId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `There is no draft or version available to discard for document \"${documentId}\".`,\n })\n }\n\n if (!checkGrant(grants.update, working[versionId], identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to discard changes for document \"${documentId}\".`,\n })\n }\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.version.discard',\n versionId,\n })\n return {base, working}\n}\n","import {diffValue} from '@sanity/diff-patch'\nimport {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation, type PatchOperations, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type EditDocumentAction} from '../actions'\nimport {getId} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n applySingleDocPatch,\n checkGrant,\n createMutationApplier,\n PermissionActionError,\n preserveNumericOperations,\n} from './shared'\n\nexport function handleEdit(\n action: EditDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit) {\n const result = applySingleDocPatch({\n base,\n working,\n documentId,\n patches: action.patches,\n transactionId,\n timestamp,\n grants,\n identity,\n preserveOperations: action.preserveOperations,\n })\n // liveEdit documents use the mutation endpoint directly -- we don't send actions\n outgoingMutations.push(...result.workingMutations)\n return {base: result.base, working: result.working}\n }\n\n const versionId = isReleasePerspective(action.perspective)\n ? getVersionId(DocumentId(documentId), action.perspective.releaseName)\n : undefined\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n const patchDocumentId = isReleasePerspective(action.perspective) ? versionId! : draftId\n const userPatches = action.patches?.map((patch) => ({\n patch: {id: patchDocumentId, ...patch},\n }))\n\n // skip this action if there are no associated patches\n if (!userPatches?.length) return {base, working}\n\n if (isReleasePerspective(action.perspective)) {\n if (!working[versionId!] && !base[versionId!]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `This document does not exist in the release. Please create it or add it to the release first.`,\n })\n }\n } else if (\n (!working[draftId] && !working[publishedId]) ||\n (!base[draftId] && !base[publishedId])\n ) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot edit document because it does not exist in draft or published form.`,\n })\n }\n\n const applyMutations = createMutationApplier({\n documentId,\n transactionId,\n timestamp,\n preserveOperations: action.preserveOperations,\n })\n\n const baseMutations: Mutation[] = []\n // don't create a draft from the published version in a release perspective\n if (!isReleasePerspective(action.perspective) && !base[draftId] && base[publishedId]) {\n // otherwise make a draft from the published version\n baseMutations.push({create: {...base[publishedId], _id: draftId}})\n }\n\n // the above statement and guards should make this never be null or undefined\n const baseBefore = base[patchDocumentId] ?? base[publishedId]\n if (userPatches) {\n baseMutations.push(...userPatches)\n }\n\n base = applyMutations(base, baseMutations, 'base')\n // this one will always be defined because a patch mutation will never\n // delete an input document\n const baseAfter = base[patchDocumentId] as SanityDocument\n // when the caller asked us to preserve their operations, forward their\n // patches verbatim instead of re-deriving them from a snapshot diff. this\n // keeps keyed array operations addressable so concurrent edits from other\n // clients interleave instead of being overwritten. otherwise, diff the\n // snapshots but swap back any `inc`/`dec` the diff collapsed into a `set`,\n // so concurrent increments accumulate instead of last-write-winning\n const patches = action.preserveOperations\n ? (action.patches ?? [])\n : preserveNumericOperations(\n baseBefore,\n action.patches,\n diffValue(baseBefore, baseAfter) as PatchOperations[],\n )\n\n const workingMutations: Mutation[] = []\n if (!isReleasePerspective(action.perspective) && !working[draftId] && working[publishedId]) {\n const newDraftFromPublished = {...working[publishedId], _id: draftId}\n\n if (!checkGrant(grants.create, newDraftFromPublished, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a draft for editing this document.`,\n })\n }\n\n workingMutations.push({create: newDraftFromPublished})\n }\n\n // the first if statement should make this never be null or undefined\n const workingBefore = working[patchDocumentId] ?? working[publishedId]\n if (!checkGrant(grants.update, workingBefore!, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to edit document \"${documentId}\".`,\n })\n }\n workingMutations.push(...patches.map((patch) => ({patch: {id: patchDocumentId, ...patch}})))\n\n working = applyMutations(working, workingMutations, 'working')\n\n outgoingMutations.push(...workingMutations)\n outgoingActions.push(\n ...patches.map((patch) => ({\n actionType: 'sanity.action.document.edit' as const,\n draftId: patchDocumentId,\n publishedId,\n patch: patch as PatchOperations,\n })),\n )\n\n return {base, working}\n}\n","import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'\nimport {type Mutation, type Reference, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {isDeepEqual} from '../../utils/object'\nimport {type PublishDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handlePublish(\n action: PublishDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit || isReleasePerspective(action.perspective)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot publish this document. Publishing is not supported for liveEdit or version (release) documents.`,\n })\n }\n\n // Standard draft/published logic\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n const workingDraft = working[draftId]\n const baseDraft = base[draftId]\n if (!workingDraft || !baseDraft) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot publish because no draft version was found for document \"${documentId}\".`,\n })\n }\n\n // Before proceeding, verify that the working draft is identical to the base draft.\n // TODO: is it enough just to check for the _rev or nah?\n if (!isDeepEqual(workingDraft, baseDraft)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Publish aborted: The document has changed elsewhere. Please try again.`,\n })\n }\n\n const newPublishedFromDraft = {...strengthenOnPublish(workingDraft), _id: publishedId}\n\n const mutations: Mutation[] = [{delete: {id: draftId}}, {createOrReplace: newPublishedFromDraft}]\n\n if (working[draftId] && !checkGrant(grants.update, working[draftId], identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `Publish failed: You do not have permission to update the draft for \"${documentId}\".`,\n })\n }\n\n if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `Publish failed: You do not have permission to update the published version of \"${documentId}\".`,\n })\n } else if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `Publish failed: You do not have permission to publish a new version of \"${documentId}\".`,\n })\n }\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.publish',\n draftId,\n publishedId,\n })\n return {base, working}\n}\n\nfunction strengthenOnPublish(draft: SanityDocument): SanityDocument {\n const isStrengthenReference = (\n value: object,\n ): value is Reference & Required<Pick<Reference, '_strengthenOnPublish'>> =>\n '_strengthenOnPublish' in value\n\n function strengthen(value: unknown): unknown {\n if (typeof value !== 'object' || !value) return value\n\n if (isStrengthenReference(value)) {\n const {_strengthenOnPublish, _weak, ...rest} = value\n return {\n ...rest,\n ...(_strengthenOnPublish.weak && {_weak: true}),\n }\n }\n\n if (Array.isArray(value)) {\n return value.map(strengthen)\n }\n\n return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, strengthen(v)]))\n }\n\n return strengthen(draft) as SanityDocument\n}\n","import {type ArchiveReleaseAction, type UnarchiveReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleaseArchive(\n action: ArchiveReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot archive release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to archive release \"${action.releaseId}\".`,\n })\n }\n\n // Archiving deletes every version document in the release server-side.\n // Although technically we could search through the document store for all related\n // version documents, for now it's simpler to just let the server handle it.\n // (Those local documents will be eventually updated by the listener.)\n outgoingActions.push({\n actionType: 'sanity.action.release.archive',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n\nexport function handleReleaseUnarchive(\n action: UnarchiveReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot unarchive release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to unarchive release \"${action.releaseId}\".`,\n })\n }\n\n outgoingActions.push({\n actionType: 'sanity.action.release.unarchive',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {type Mutation, type SanityDocument} from '@sanity/types'\n\nimport {type CreateReleaseAction} from '../actions'\nimport {processMutations} from '../processMutations'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleaseCreate(\n action: CreateReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity} = ctx\n let {base, working} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n\n if (working[releaseDocumentId] || base[releaseDocumentId]) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `A release with id \"${action.releaseId}\" already exists.`,\n })\n }\n // Optimistic local release doc\n const releaseDoc = {\n _id: releaseDocumentId,\n _type: 'system.release',\n name: action.releaseId,\n state: 'active',\n metadata: action.metadata,\n }\n const mutations: Mutation[] = [{create: releaseDoc}]\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n if (!checkGrant(grants.create, working[releaseDocumentId] as SanityDocument, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to create release \"${action.releaseId}\".`,\n })\n }\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.release.create',\n releaseId: action.releaseId,\n metadata: action.metadata,\n })\n\n return {base, working}\n}\n","import {type Mutation} from '@sanity/types'\n\nimport {type DeleteReleaseAction} from '../actions'\nimport {processMutations} from '../processMutations'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\n// you can only delete archived or published releases\n// https://www.sanity.io/docs/content-lake/dispatch-actions#k22ab37420f3c\nconst DELETABLE_STATES = new Set(['archived', 'published'])\n\nexport function handleReleaseDelete(\n action: DeleteReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity} = ctx\n let {base, working} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot delete release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n const state = existing['state']\n if (state && typeof state === 'string' && !DELETABLE_STATES.has(state)) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot delete release \"${action.releaseId}\" while it is \"${state}\". Archive it first.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to delete release \"${action.releaseId}\".`,\n })\n }\n\n const mutations: Mutation[] = [{delete: {id: releaseDocumentId}}]\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.release.delete',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {type EditReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {type ActionHandlerContext, type ActionHandlerResult, applySingleDocPatch} from './shared'\n\nexport function handleReleaseEdit(\n action: EditReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity} = ctx\n const {base, working} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n\n const result = applySingleDocPatch({\n base,\n working,\n documentId: releaseDocumentId,\n patches: [action.patch],\n transactionId,\n timestamp,\n grants,\n identity,\n notFoundMessage: `Cannot edit release \"${action.releaseId}\" because it does not exist.`,\n permissionMessage: `You do not have permission to edit release \"${action.releaseId}\".`,\n })\n\n outgoingMutations.push(...result.workingMutations)\n outgoingActions.push(\n ...result.diffedPatches.map((patch) => ({\n actionType: 'sanity.action.release.edit' as const,\n releaseId: action.releaseId,\n patch,\n })),\n )\n\n return {base: result.base, working: result.working}\n}\n","import {type PublishReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleasePublish(\n action: PublishReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot publish release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to publish release \"${action.releaseId}\".`,\n })\n }\n\n // a release publish cascades to every version document in the release\n // although technically we could search through the document store for all related\n // version documents, for now it's simpler to just let the server handle it.\n // (Those local documents will be eventually updated by the listener.)\n outgoingActions.push({\n actionType: 'sanity.action.release.publish',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {type ScheduleReleaseAction, type UnscheduleReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleaseSchedule(\n action: ScheduleReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n\n if (Number.isNaN(Date.parse(action.publishAt))) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot schedule release \"${action.releaseId}\": \"publishAt\" must be a valid ISO 8601 timestamp (received \"${action.publishAt}\").`,\n })\n }\n\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot schedule release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to schedule release \"${action.releaseId}\".`,\n })\n }\n\n // Scheduling flips `state` to 'scheduled' and locks version documents\n // server-side. We don't model the lock locally yet — local edits to those\n // version docs will be rejected at submit time. The listener will sync the\n // updated release state.\n outgoingActions.push({\n actionType: 'sanity.action.release.schedule',\n releaseId: action.releaseId,\n publishAt: action.publishAt,\n })\n\n return {base, working}\n}\n\nexport function handleReleaseUnschedule(\n action: UnscheduleReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot unschedule release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to unschedule release \"${action.releaseId}\".`,\n })\n }\n\n outgoingActions.push({\n actionType: 'sanity.action.release.unschedule',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'\nimport {type Mutation, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type UnpublishDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleUnpublish(\n action: UnpublishDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit || isReleasePerspective(action.perspective)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot unpublish this document. Unpublishing is not supported for liveEdit or version (release) documents.`,\n })\n }\n\n // Standard draft/published or version logic\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n if (!working[publishedId] && !base[publishedId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot unpublish because the document \"${documentId}\" is not currently published.`,\n })\n }\n\n const sourceDoc = working[publishedId] ?? (base[publishedId] as SanityDocument)\n const newDraftFromPublished = {...sourceDoc, _id: draftId}\n const mutations: Mutation[] = [\n {delete: {id: publishedId}},\n {createIfNotExists: newDraftFromPublished},\n ]\n\n if (!checkGrant(grants.update, sourceDoc, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to unpublish the document \"${documentId}\".`,\n })\n }\n\n if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a draft from the published version of \"${documentId}\".`,\n })\n }\n\n base = processMutations({\n documents: base,\n transactionId,\n mutations: [\n {delete: {id: publishedId}},\n {createIfNotExists: {...(base[publishedId] ?? sourceDoc), _id: draftId}},\n ],\n timestamp,\n })\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.unpublish',\n draftId,\n publishedId,\n })\n return {base, working}\n}\n","import {type Mutation} from '@sanity/types'\nimport {type ExprNode} from 'groq-js'\n\nimport {type Action, type DocumentAction} from '../actions'\nimport {type Grant} from '../permissions'\nimport {type DocumentSet} from '../processMutations'\nimport {type HttpAction} from '../reducers'\nimport {handleCreate} from './create'\nimport {handleDelete} from './delete'\nimport {handleDiscard} from './discard'\nimport {handleEdit} from './edit'\nimport {handlePublish} from './publish'\nimport {handleReleaseArchive, handleReleaseUnarchive} from './releaseArchive'\nimport {handleReleaseCreate} from './releaseCreate'\nimport {handleReleaseDelete} from './releaseDelete'\nimport {handleReleaseEdit} from './releaseEdit'\nimport {handleReleasePublish} from './releasePublish'\nimport {handleReleaseSchedule, handleReleaseUnschedule} from './releaseSchedule'\nimport {isReleaseAction} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n PermissionActionError,\n} from './shared'\nimport {handleUnpublish} from './unpublish'\n\nexport {ActionError, PermissionActionError}\n\ninterface ProcessActionsOptions {\n /**\n * The ID of this transaction. This will become the resulting `_rev` for all\n * documents affected by changes derived from the current set of actions.\n */\n transactionId: string\n\n /**\n * The actions to apply to the given documents\n */\n actions: Action[]\n\n /**\n * The set of documents these actions were intended to be applied to. These\n * set of documents should be captured right before a queued action is\n * applied.\n */\n base: DocumentSet\n\n /**\n * The current \"working\" set of documents. A patch will be created by applying\n * the actions to the base. This patch will then be applied to the working\n * set for conflict resolution. Initially, this value should match the base\n * set.\n */\n working: DocumentSet\n\n /**\n * The timestamp to use for `_updateAt` and other similar timestamps for this\n * transaction\n */\n timestamp: string\n\n /**\n * the lookup with pre-parsed GROQ expressions\n */\n grants: Record<Grant, ExprNode>\n\n /**\n * The current user's ID, passed to GROQ as the value of `identity()` when\n * evaluating ACL filters. Optional because the user may not have loaded yet;\n * filters that reference `identity()` will evaluate to null in that case.\n */\n identity?: string\n\n // // TODO: implement initial values from the schema?\n // initialValues?: {[TDocumentType in string]?: {_type: string}}\n}\n\ninterface ProcessActionsResult {\n /**\n * The resulting document set after the actions have been applied. This is\n * derived from the working documents.\n */\n working: DocumentSet\n /**\n * The document set before the actions have been applied. This is simply the\n * input of the `working` document set.\n */\n previous: DocumentSet\n /**\n * The outgoing action that were collected when applying the actions. These\n * are sent to the Actions HTTP API\n */\n outgoingActions: HttpAction[]\n /**\n * The outgoing mutations that were collected when applying the actions. These\n * are here for debugging purposes.\n */\n outgoingMutations: Mutation[]\n /**\n * The previous revisions of the given documents before the actions were applied.\n */\n previousRevs: {[TDocumentId in string]?: string}\n}\n\n/**\n * Applies the given set of actions to the working set of documents and converts\n * high-level actions into lower-level outgoing mutations/actions that respect\n * the current state of the working documents.\n *\n * Supports a \"base\" and \"working\" set of documents to allow actions to be\n * applied on top of a different working set of documents in a 3-way merge\n *\n * Actions are applied to the base set of documents first. The difference\n * between the base before and after is used to create a patch. This patch is\n * then applied to the working set of documents and is set as the outgoing patch\n * sent to the server.\n */\nexport function processActions({\n actions,\n transactionId,\n working: initialWorking,\n base: initialBase,\n timestamp,\n grants,\n identity,\n}: ProcessActionsOptions): ProcessActionsResult {\n let base: DocumentSet = {...initialBase}\n let working: DocumentSet = {...initialWorking}\n\n const outgoingActions: HttpAction[] = []\n const outgoingMutations: Mutation[] = []\n\n // liveEdit document actions go to the mutations API, since the actions API\n // requires a draft+published pair. Mixing them with anything else in the same\n // transaction would silently lose atomicity for the non-liveEdit operations,\n // so require users to split the transaction.\n // (Note that the reducers already does this for us -- you'd have to try hard to mix them.)\n const liveEditAction = actions.find((action) => !isReleaseAction(action) && action.liveEdit) as\n | DocumentAction\n | undefined\n const otherAction = actions.find((action) => isReleaseAction(action) || !action.liveEdit)\n if (liveEditAction && otherAction) {\n throw new ActionError({\n documentId: liveEditAction.documentId!,\n transactionId,\n message:\n 'Cannot combine liveEdit document actions with other actions in the same transaction. Submit them as separate transactions.',\n })\n }\n\n for (const action of actions) {\n const result = dispatch(action, {\n base,\n working,\n transactionId,\n timestamp,\n grants,\n identity,\n outgoingActions,\n outgoingMutations,\n })\n base = result.base\n working = result.working\n }\n\n const previousRevs = Object.fromEntries(\n Object.entries(initialWorking).map(([id, doc]) => [id, doc?._rev]),\n )\n\n return {\n working,\n outgoingActions,\n outgoingMutations,\n previous: initialWorking,\n previousRevs,\n }\n}\n\nfunction dispatch(action: Action, ctx: ActionHandlerContext): ActionHandlerResult {\n switch (action.type) {\n case 'document.create':\n return handleCreate(action, ctx)\n case 'document.delete':\n return handleDelete(action, ctx)\n case 'document.discard':\n return handleDiscard(action, ctx)\n case 'document.edit':\n return handleEdit(action, ctx)\n case 'document.publish':\n return handlePublish(action, ctx)\n case 'document.unpublish':\n return handleUnpublish(action, ctx)\n case 'release.create':\n return handleReleaseCreate(action, ctx)\n case 'release.edit':\n return handleReleaseEdit(action, ctx)\n case 'release.publish':\n return handleReleasePublish(action, ctx)\n case 'release.schedule':\n return handleReleaseSchedule(action, ctx)\n case 'release.unschedule':\n return handleReleaseUnschedule(action, ctx)\n case 'release.archive':\n return handleReleaseArchive(action, ctx)\n case 'release.unarchive':\n return handleReleaseUnarchive(action, ctx)\n case 'release.delete':\n return handleReleaseDelete(action, ctx)\n default:\n throw new Error(\n `Unknown action type: \"${\n // @ts-expect-error invalid input\n action.type\n }\". Please contact support if this issue persists.`,\n )\n }\n}\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation, type PatchOperations, type SanityDocumentLike} from '@sanity/types'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type StoreContext} from '../store/defineStore'\nimport {randomId} from '../utils/ids'\nimport {isDeepEqual, omitProperty} from '../utils/object'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {type Action} from './actions'\nimport {DOCUMENT_STATE_CLEAR_DELAY, UNVERIFIED_REVISION_RETENTION_TIME} from './documentConstants'\nimport {type DocumentState, type DocumentStoreState} from './documentStore'\nimport {type RemoteDocument} from './listen'\nimport {ActionError, processActions} from './processActions/processActions'\nimport {getReleaseDocumentId, isReleaseAction} from './processActions/releaseUtil'\nimport {type DocumentSet} from './processMutations'\n\nconst EMPTY_REVISIONS: NonNullable<Required<DocumentState['unverifiedRevisions']>> = {}\n\n/**\n * How many of this client's own transaction IDs to remember per document for\n * `remote-patches` origin labeling. Sized to comfortably outlast the window\n * between submitting a transaction and observing its listener echo.\n */\nconst MAX_RECENT_OWN_TRANSACTION_IDS = 50\n\nexport type SyncTransactionState = Pick<\n DocumentStoreState,\n 'queued' | 'applied' | 'documentStates' | 'outgoing' | 'grants' | 'identity'\n>\n\ntype DocumentHandleLike = Pick<DocumentHandle, 'perspective'> & {\n documentId?: string\n liveEdit?: boolean\n}\n\ntype ActionMap = {\n create: 'sanity.action.document.version.create'\n discard: 'sanity.action.document.version.discard'\n unpublish: 'sanity.action.document.unpublish'\n delete: 'sanity.action.document.delete'\n edit: 'sanity.action.document.edit'\n publish: 'sanity.action.document.publish'\n releaseCreate: 'sanity.action.release.create'\n releaseEdit: 'sanity.action.release.edit'\n releasePublish: 'sanity.action.release.publish'\n releaseSchedule: 'sanity.action.release.schedule'\n releaseUnschedule: 'sanity.action.release.unschedule'\n releaseArchive: 'sanity.action.release.archive'\n releaseUnarchive: 'sanity.action.release.unarchive'\n releaseDelete: 'sanity.action.release.delete'\n}\n\ntype OptimisticLock = {\n ifDraftRevisionId?: string\n ifPublishedRevisionId?: string\n}\n\ninterface ReleaseMetadataPayload {\n title?: string\n description?: string\n intendedPublishAt?: string\n releaseType?: 'asap' | 'scheduled' | 'undecided'\n cardinality?: 'one' | 'many'\n}\n\nexport type HttpAction =\n | {actionType: ActionMap['create']; publishedId: string; attributes: SanityDocumentLike}\n | {actionType: ActionMap['discard']; versionId: string; purge?: boolean}\n | {actionType: ActionMap['unpublish']; draftId: string; publishedId: string}\n | {actionType: ActionMap['delete']; publishedId: string; includeDrafts?: string[]}\n | {actionType: ActionMap['edit']; draftId: string; publishedId: string; patch: PatchOperations}\n | ({actionType: ActionMap['publish']; draftId: string; publishedId: string} & OptimisticLock)\n | {\n actionType: ActionMap['releaseCreate']\n releaseId: string\n metadata?: ReleaseMetadataPayload\n }\n | {actionType: ActionMap['releaseEdit']; releaseId: string; patch: PatchOperations}\n | {actionType: ActionMap['releasePublish']; releaseId: string}\n | {actionType: ActionMap['releaseSchedule']; releaseId: string; publishAt: string}\n | {actionType: ActionMap['releaseUnschedule']; releaseId: string}\n | {actionType: ActionMap['releaseArchive']; releaseId: string}\n | {actionType: ActionMap['releaseUnarchive']; releaseId: string}\n | {actionType: ActionMap['releaseDelete']; releaseId: string}\n\n/**\n * Represents a transaction that is queued to be applied but has not yet been\n * applied. A transaction will remain in a queued state until all required\n * documents for the transactions are available locally.\n */\nexport interface QueuedTransaction {\n /**\n * the ID of this transaction. this is generated client-side.\n */\n transactionId: string\n /**\n * the high-level actions associated with this transaction. note that these\n * actions don't mention draft IDs and is meant to abstract away the draft\n * model from users.\n */\n actions: Action[]\n /**\n * An optional flag set to disable this transaction from being batched with\n * other transactions.\n */\n disableBatching?: boolean\n}\n\n/**\n * Represents a transaction that has been applied locally but has not been\n * committed/transitioned-to-outgoing. These transactions are visible to the\n * user but may be rebased upon a new working document set. Applied transactions\n * also contain the resulting `outgoingActions` that will be submitted to\n * Content Lake. These `outgoingActions` depend on the state of the working\n * documents so they are recomputed on rebase and are only relevant to applied\n * actions (we cannot compute `outgoingActions` for queued transactions because\n * we haven't resolved the set of documents the actions are dependent on yet).\n *\n * In order to support better conflict resolution, the original `previous` set\n * is saved as the `base` set.\n */\nexport interface AppliedTransaction extends QueuedTransaction {\n /**\n * the resulting set of documents after the actions have been applied\n */\n working: DocumentSet\n\n /**\n * the previous set of documents before the action was applied\n */\n previous: DocumentSet\n\n /**\n * the original `previous` document set captured when this action was\n * originally applied. this is used as a reference point to do a 3-way merge\n * if this applied transaction ever needs to be reapplied on a different\n * set of documents.\n */\n base: DocumentSet\n\n /**\n * the `_rev`s from `previous` document set\n */\n previousRevs: {[TDocumentId in string]?: string}\n\n /**\n * a timestamp for when this transaction was applied locally\n */\n timestamp: string\n\n /**\n * the resulting HTTP actions derived from the state of the `working` document\n * set. these are sent to Content Lake as-is when this transaction is batched\n * and transitioned into an outgoing transaction.\n */\n outgoingActions: HttpAction[]\n\n /**\n * similar to `outgoingActions` but comprised of mutations instead of actions.\n * Useful for debugging, and is also used by liveEdit documents to send mutations,\n * since they can't use the Actions API which is pretty dependent on the draft model.\n */\n outgoingMutations: Mutation[]\n}\n\n/**\n * Represents a set of applied transactions batched into a single outgoing\n * transaction. An outgoing transaction is the result of batching many applied\n * actions. An outgoing transaction may be reverted locally if the server\n * does not accept it.\n */\nexport interface OutgoingTransaction extends AppliedTransaction {\n disableBatching: boolean\n batchedTransactionIds: string[]\n}\n\nexport interface UnverifiedDocumentRevision {\n transactionId: string\n documentId: string\n previousRev: string | undefined\n timestamp: string\n}\n\nexport function queueTransaction(\n prev: SyncTransactionState,\n transaction: QueuedTransaction,\n): SyncTransactionState {\n const {transactionId, actions} = transaction\n const prevWithSubscriptionIds = getDocumentIdsFromHandleLikes(actions).reduce(\n (acc, id) => addSubscriptionIdToDocument(acc, id, transactionId),\n prev,\n )\n\n return {\n ...prevWithSubscriptionIds,\n queued: [...prev.queued, transaction],\n }\n}\n\nexport function removeQueuedTransaction(\n prev: SyncTransactionState,\n transactionId: string,\n): SyncTransactionState {\n const transaction = prev.queued.find((t) => t.transactionId === transactionId)\n if (!transaction) return prev\n\n const prevWithSubscriptionIds = getDocumentIdsFromHandleLikes(transaction.actions).reduce(\n (acc, id) => removeSubscriptionIdFromDocument(acc, id, transactionId),\n prev,\n )\n\n return {\n ...prevWithSubscriptionIds,\n queued: prev.queued.filter((t) => transactionId !== t.transactionId),\n }\n}\n\nexport function applyFirstQueuedTransaction(prev: SyncTransactionState): SyncTransactionState {\n const queued = prev.queued.at(0)\n if (!queued) return prev\n if (!prev.grants) return prev\n\n const ids = getDocumentIdsFromHandleLikes(queued.actions)\n // the local value is only ever `undefined` if it has not been loaded yet\n // we can't get the next applied state unless all relevant documents are ready\n if (ids.some((id) => prev.documentStates[id]?.local === undefined)) return prev\n\n const working = ids.reduce<DocumentSet>((acc, id) => {\n acc[id] = prev.documentStates[id]?.local\n return acc\n }, {})\n\n const timestamp = new Date().toISOString()\n\n const result = processActions({\n ...queued,\n working,\n base: working,\n timestamp,\n grants: prev.grants,\n identity: prev.identity,\n })\n const applied: AppliedTransaction = {\n ...queued,\n ...result,\n base: result.previous,\n timestamp,\n }\n\n return {\n ...prev,\n applied: [...prev.applied, applied],\n queued: prev.queued.filter((t) => t.transactionId !== queued.transactionId),\n documentStates: Object.entries(result.working).reduce(\n (acc, [id, next]) => {\n const prevDoc = acc[id]\n if (!prevDoc) return acc\n acc[id] = {...prevDoc, local: next}\n return acc\n },\n {...prev.documentStates},\n ),\n }\n}\n\nexport function batchAppliedTransactions([curr, ...rest]: AppliedTransaction[]):\n | OutgoingTransaction\n | undefined {\n // No transactions? Nothing to batch.\n if (!curr) return undefined\n\n // Skip transactions with no actions.\n if (!curr.actions.length) return batchAppliedTransactions(rest)\n\n // If there are multiple actions, we cannot batch further.\n if (curr.actions.length > 1) {\n return {\n ...curr,\n disableBatching: true,\n batchedTransactionIds: [curr.transactionId],\n }\n }\n\n const [action] = curr.actions\n\n // If the single action isn't a document.edit or batching is disabled,\n // mark this transaction as non-batchable.\n if (action.type !== 'document.edit' || curr.disableBatching) {\n return {\n ...curr,\n disableBatching: true,\n batchedTransactionIds: [curr.transactionId],\n }\n }\n\n // Create an outgoing transaction for the single edit action.\n // At this point, batching is allowed.\n const editAction: OutgoingTransaction = {\n ...curr,\n actions: [action],\n disableBatching: false,\n batchedTransactionIds: [curr.transactionId],\n }\n if (!rest.length) return editAction\n\n const next = batchAppliedTransactions(rest)\n if (!next) return undefined\n if (next.disableBatching) return editAction\n\n // Don't batch a liveEdit edit with a non-liveEdit edit — they route to different APIs\n const nextFirst = next.actions[0]\n const nextLiveEdit = nextFirst && 'liveEdit' in nextFirst ? nextFirst.liveEdit : false\n if (!!action.liveEdit !== !!nextLiveEdit) return editAction\n\n return {\n disableBatching: false,\n // Use the transactionId from the later (next) transaction.\n transactionId: next.transactionId,\n // Accumulate actions: current action first, then later ones.\n actions: [action, ...next.actions],\n // Merge outgoingActions in order.\n outgoingActions: [...curr.outgoingActions, ...next.outgoingActions],\n // Batched transaction IDs: preserve order by placing curr first.\n batchedTransactionIds: [curr.transactionId, ...next.batchedTransactionIds],\n // Merge outgoingMutations in order.\n outgoingMutations: [...curr.outgoingMutations, ...next.outgoingMutations],\n // Working state reflects the latest optimistic changes: later transactions override earlier.\n working: {...curr.working, ...next.working},\n // Base state (base, previous, previousRevs) must reflect the original state.\n // Use curr values (the earliest transaction) to override later ones.\n previousRevs: {...next.previousRevs, ...curr.previousRevs},\n previous: {...next.previous, ...curr.previous},\n base: {...next.base, ...curr.base},\n // Use the earliest timestamp from curr.\n timestamp: curr.timestamp ?? next.timestamp,\n }\n}\n\nexport function transitionAppliedTransactionsToOutgoing(\n prev: SyncTransactionState,\n): SyncTransactionState {\n if (prev.outgoing) return prev\n\n const transaction = batchAppliedTransactions(prev.applied)\n if (!transaction) return prev\n\n const {\n transactionId,\n previousRevs,\n working,\n batchedTransactionIds: consumedTransactions,\n } = transaction\n const timestamp = new Date().toISOString()\n\n return {\n ...prev,\n outgoing: transaction,\n applied: prev.applied.filter((i) => !consumedTransactions.includes(i.transactionId)),\n documentStates: Object.entries(previousRevs).reduce(\n (acc, [documentId, previousRev]) => {\n if (working[documentId]?._rev === previousRev) return acc\n\n const documentState = prev.documentStates[documentId]\n if (!documentState) return acc\n\n acc[documentId] = {\n ...documentState,\n unverifiedRevisions: {\n ...documentState.unverifiedRevisions,\n // add unverified revision\n [transactionId]: {documentId, previousRev, transactionId, timestamp},\n },\n // also track the transaction ID durably (unverified revisions can be\n // pruned by sync events before the listener echo arrives) so\n // `remote-patches` events label our own transactions correctly\n recentOwnTransactionIds: [\n ...(documentState.recentOwnTransactionIds ?? []).slice(\n -(MAX_RECENT_OWN_TRANSACTION_IDS - 1),\n ),\n transactionId,\n ],\n }\n\n return acc\n },\n {...prev.documentStates},\n ),\n }\n}\n\nexport function cleanupOutgoingTransaction(prev: SyncTransactionState): SyncTransactionState {\n const {outgoing} = prev\n if (!outgoing) return prev\n\n let next = prev\n const ids = getDocumentIdsFromHandleLikes(outgoing.actions)\n for (const transactionId of outgoing.batchedTransactionIds) {\n for (const documentId of ids) {\n next = removeSubscriptionIdFromDocument(next, documentId, transactionId)\n }\n }\n\n // every ack is also when a previously retained document's echo may have\n // stopped being expected, so re-make that eviction decision here\n return {\n ...next,\n outgoing: undefined,\n documentStates: evictOrphanedDocumentStates(next.documentStates),\n }\n}\n\nexport function revertOutgoingTransaction(prev: SyncTransactionState): SyncTransactionState {\n if (!prev.grants) return prev\n\n // unregister the reverted batch's transaction-held subscriptions, symmetric\n // with `cleanupOutgoingTransaction` on ack and `removeQueuedTransaction` on\n // apply failure. without this, a reverted document's subscription IDs are\n // never released and it can never be evicted\n let base = prev\n if (prev.outgoing) {\n const ids = getDocumentIdsFromHandleLikes(prev.outgoing.actions)\n for (const transactionId of prev.outgoing.batchedTransactionIds) {\n for (const documentId of ids) {\n base = removeSubscriptionIdFromDocument(base, documentId, transactionId)\n }\n }\n }\n\n let working = Object.fromEntries(\n Object.entries(base.documentStates).map(([documentId, documentState]) => [\n documentId,\n documentState?.remote,\n ]),\n )\n const nextApplied: AppliedTransaction[] = []\n\n for (const t of prev.applied) {\n try {\n const next = processActions({...t, working, grants: prev.grants, identity: prev.identity})\n working = next.working\n nextApplied.push({...t, ...next})\n } catch (error) {\n // if we're already reverting a transaction, skip any applied actions if\n // they throw while we rebuild the state\n if (error instanceof ActionError) continue\n throw error\n }\n }\n\n return {\n ...base,\n applied: nextApplied,\n outgoing: undefined,\n // discarding the outgoing transaction drops its echo-recognition memory,\n // which can leave a document that was only being kept alive for that echo\n // with nothing left to wait for, so sweep afterwards\n documentStates: evictOrphanedDocumentStates(\n Object.fromEntries(\n Object.entries(base.documentStates)\n .filter((e): e is [string, DocumentState] => !!e[1])\n .map(([documentId, documentState]) => {\n const {unverifiedRevisions = {}} = documentState\n const next: DocumentState = {\n ...documentState,\n unverifiedRevisions:\n prev.outgoing && prev.outgoing.transactionId in unverifiedRevisions\n ? omitProperty(unverifiedRevisions, prev.outgoing.transactionId)\n : unverifiedRevisions,\n }\n\n // a document with no subscribers that is still waiting on the echo\n // of a different, already acked transaction of its own is not\n // represented in `working` (only `applied` transactions are\n // re-applied above), so rebuilding its `local` from `remote` would\n // drop that transaction's effect. leave it for the echo to resolve\n if (!next.subscriptions.length && isAwaitingOwnEcho(next)) {\n return [documentId, next] as const\n }\n\n return [\n documentId,\n {...next, local: documentId in working ? working[documentId] : next.local},\n ] as const\n }),\n ),\n ),\n }\n}\n\n/**\n * Extracts the patch operations addressed to the given document from a set of\n * raw listener mutations, stripping the mutation-level `id` addressing so the\n * patches are rooted at the document. A transaction can carry mutations for\n * other documents, so anything not explicitly id-addressed to this document\n * (including query-addressed patches) is dropped.\n */\nfunction extractPatchOperations(\n mutations: Mutation[] | undefined,\n documentId: string,\n): PatchOperations[] {\n if (!mutations) return []\n return mutations.flatMap((mutation) => {\n if (!('patch' in mutation) || !mutation.patch) return []\n const {id, ...operations} = mutation.patch as PatchOperations & {id?: string}\n if (id !== documentId) return []\n return [operations]\n })\n}\n\nexport function applyRemoteDocument(\n prev: SyncTransactionState,\n {document, documentId, previousRev, revision, timestamp, type, mutations}: RemoteDocument,\n events: DocumentStoreState['events'],\n): SyncTransactionState {\n if (!prev.grants) return prev\n const prevDocState = prev.documentStates[documentId]\n\n // document state is deleted when there are no more subscribers so we can\n // simply skip if there is no state\n if (!prevDocState) return prev\n\n // we send out transactions with IDs generated client-side to identify them\n // when they are observed through the listener. here we can check if this\n // incoming remote document is the result of one of our transactions\n const prevUnverifiedRevisions = prevDocState.unverifiedRevisions\n const revisionToVerify = revision ? prevUnverifiedRevisions?.[revision] : undefined\n let unverifiedRevisions = prevUnverifiedRevisions ?? EMPTY_REVISIONS\n if (revision && revisionToVerify) {\n unverifiedRevisions = omitProperty(prevUnverifiedRevisions, revision)\n }\n\n // surface the operational patches from this transaction before they are\n // collapsed into the whole-document snapshot below. consumers that keep\n // their own state (e.g. collaborative text editors) rely on these to apply\n // remote changes without re-diffing document snapshots\n if (type === 'mutation' && revision) {\n const patches = extractPatchOperations(mutations, documentId)\n if (patches.length) {\n // `unverifiedRevisions` alone isn't a reliable origin marker: a sync\n // event can prune an in-flight transaction's entry before its listener\n // echo arrives. `recentOwnTransactionIds` survives that race\n const isOwnTransaction =\n Boolean(revisionToVerify) ||\n (prevDocState.recentOwnTransactionIds?.includes(revision) ?? false)\n events.next({\n type: 'remote-patches',\n documentId,\n transactionId: revision,\n previousRev,\n timestamp,\n patches,\n origin: isOwnTransaction ? 'local' : 'remote',\n })\n }\n }\n\n // if this remote document is from a `'sync'` event (meaning that the whole\n // thing was just fetched and not re-created from mutations)\n if (type === 'sync') {\n // then remove unverified revisions that are older than our sync time. we\n // don't need to verify them for a rebase any more because we synced and\n // grabbed the latest document\n unverifiedRevisions = Object.fromEntries(\n Object.entries(unverifiedRevisions).filter(([, unverifiedRevision]) => {\n if (!unverifiedRevision) return false\n return new Date(timestamp).getTime() <= new Date(unverifiedRevision.timestamp).getTime()\n }),\n )\n }\n\n // if there is a revision to verify and the previous revision from remote\n // matches the previous revision we expected, we can \"fast-forward\" and skip\n // rebasing local changes on top of this new base\n if (revisionToVerify && revisionToVerify.previousRev === previousRev) {\n // even on a clean fast-forward, the server's result can differ from our\n // optimistic prediction: a publish derives the published content from the\n // server's copy of the draft, which may carry concurrent edits our\n // prediction never saw. once nothing else is pending locally for this\n // document (no applied transaction and no in-flight transaction, other\n // than the one this event just confirmed, modifies it), converge `local`\n // to the server's document. while local work that modifies this document\n // is pending, keep the optimistic `local` so those changes stay visible;\n // that work's own echo (or rebase) recomputes `local` later.\n //\n // \"modifies\" matters: pending work on other documents must not block\n // convergence here (nothing would ever revisit this document's `local`),\n // and a pending draft edit carries the published document in its working\n // set without changing it, so a presence check would block published-doc\n // convergence whenever a draft edit is pending. this is the same\n // modified-test `transitionAppliedTransactionsToOutgoing` uses\n const modifiesDocument = (transaction: AppliedTransaction) =>\n transaction.working[documentId]?._rev !== transaction.previousRevs[documentId]\n const nothingElsePending =\n !prev.applied.some(modifiesDocument) &&\n (!prev.outgoing ||\n prev.outgoing.transactionId === revision ||\n !modifiesDocument(prev.outgoing))\n const local =\n nothingElsePending && !isDeepEqual(prevDocState.local, document)\n ? document\n : prevDocState.local\n\n return {\n ...prev,\n documentStates: evictOrphanedDocumentStates({\n ...prev.documentStates,\n [documentId]: {\n ...prevDocState,\n remote: document,\n remoteRev: revision,\n local,\n unverifiedRevisions,\n },\n }),\n }\n }\n\n // if we got this far, this means that we could not fast-forward this revision\n // for this document. now we can rebase our local changes (if any) on top of\n // this new base from remote. in order to do that we grab the set of documents\n // captured before the earliest local transaction\n const previous = prev.applied.at(0)?.previous\n // our initial working set now is the state of the documents before any of our\n // local transactions plus the newly updated document from remote\n let working = {...previous, [documentId]: document}\n const nextApplied: AppliedTransaction[] = []\n\n // now we can iterate through our applied (but not yet committed) transactions\n // starting with the updated working set and re-apply each transaction in\n // order creating a new set of applied transactions as we go.\n //\n // NOTE: we don't want to rebase over the outgoing transaction because that\n // transaction is already on its way to the server. if an outgoing transaction\n // needs to be rebased, then it eventually will be when we see that\n // transaction again through the listener and this same flow will run then\n for (const curr of prev.applied) {\n try {\n const next = processActions({...curr, working, grants: prev.grants, identity: prev.identity})\n working = next.working\n // next includes an updated `previous` set and `working` set and updates\n // the `outgoingAction` and `outgoingMutations`. the `base` set from the\n // original applied transaction gets put back into the updated transaction\n // as-is to preserve the intended base for a 3-way merge\n nextApplied.push({...curr, ...next})\n } catch (error) {\n // if processing the action ever throws a related error, we can skip this\n // local transaction and report the error to the user\n if (error instanceof ActionError) {\n events.next({\n type: 'rebase-error',\n transactionId: error.transactionId,\n documentId: error.documentId,\n message: error.message,\n error,\n })\n continue\n }\n throw error\n }\n }\n\n // when the recompute lands on a value deep-equal to the current local\n // (the common case for echoes of our own transactions), keep the previous\n // reference so subscribers don't re-render for an identical value\n const nextLocal = working[documentId]\n const local = isDeepEqual(prevDocState.local, nextLocal) ? prevDocState.local : nextLocal\n\n return {\n ...prev,\n applied: nextApplied,\n documentStates: evictOrphanedDocumentStates({\n ...prev.documentStates,\n [documentId]: {\n ...prevDocState,\n remote: document,\n remoteRev: revision,\n local,\n unverifiedRevisions,\n },\n }),\n }\n}\n\nexport function addSubscriptionIdToDocument(\n prev: SyncTransactionState,\n documentId: string,\n subscriptionId: string,\n): SyncTransactionState {\n const prevDocState = prev.documentStates?.[documentId]\n const prevSubscriptions = prevDocState?.subscriptions ?? []\n\n return {\n ...prev,\n documentStates: {\n ...prev.documentStates,\n [documentId]: {\n ...prevDocState,\n id: documentId,\n subscriptions: [...prevSubscriptions, subscriptionId],\n },\n },\n }\n}\n\nexport function removeSubscriptionIdFromDocument(\n prev: SyncTransactionState,\n documentId: string,\n subscriptionId: string,\n): SyncTransactionState {\n const prevDocState = prev.documentStates?.[documentId]\n if (!prevDocState) return prev\n\n const subscriptions = prevDocState.subscriptions.filter((id) => id !== subscriptionId)\n\n // a document with no subscribers left is normally evicted below, but if a\n // transaction was just submitted for it, its echo-recognition memory\n // (`unverifiedRevisions`) is still waiting on the listener echo.\n // `cleanupOutgoingTransaction` removes the outgoing transaction's own\n // subscription right at HTTP ack, before the echo arrives, so deleting the\n // state here would lose that memory and make the echo mislabel as\n // `origin: 'remote'` (or, worse, get skipped outright). keep the state\n // alive, unsubscribed, until the echo verifies or the wait expires\n if (!subscriptions.length && !isAwaitingOwnEcho(prevDocState)) {\n return {...prev, documentStates: omitProperty(prev.documentStates, documentId)}\n }\n\n return {\n ...prev,\n documentStates: {\n ...prev.documentStates,\n [documentId]: {...prevDocState, subscriptions},\n },\n }\n}\n\n/**\n * Whether this document still expects the listener echo of one of its own\n * submitted transactions. That expectation is the only reason a document state\n * with no subscribers is kept around.\n *\n * Revisions stop counting after `UNVERIFIED_REVISION_RETENTION_TIME` because an\n * echo is not guaranteed to arrive at all: Content Lake records no transaction\n * when every operation resolves to a no-op (e.g. a keyed path that no longer\n * exists), while the client bumps `_rev` regardless and so tracks a revision\n * for it. Without the expiry, such a document (and the listener keyed off its\n * state) would be pinned for the lifetime of the store.\n *\n * `recentOwnTransactionIds` deliberately does not extend the wait: it is capped\n * rather than drained, so honoring it would keep every edited document alive.\n */\nfunction isAwaitingOwnEcho(documentState: DocumentState): boolean {\n const expiresAfter = Date.now() - UNVERIFIED_REVISION_RETENTION_TIME\n return Object.values(documentState.unverifiedRevisions ?? {}).some(\n (revision) => !!revision && new Date(revision.timestamp).getTime() > expiresAfter,\n )\n}\n\n/**\n * Removes every document state that has no subscribers left and no own\n * transaction still awaiting its echo.\n *\n * This is a sweep rather than a decision made where the last subscriber leaves\n * because `removeSubscriptionIdFromDocument` defers that eviction while an echo\n * is in flight, and both halves of the condition can change without a\n * subscriber going anywhere: the echo can verify, be dropped by a sync, be\n * discarded by a revert, or simply never arrive. Every reducer that can settle\n * an in-flight transaction sweeps, so the deferred decision is always re-made.\n *\n * Note this can evict the very document whose remote event is being processed,\n * which synchronously unsubscribes that document's listener from inside its own\n * emission (see `subscribeToSubscriptionsAndListenToDocuments`).\n */\nfunction evictOrphanedDocumentStates(\n documentStates: SyncTransactionState['documentStates'],\n): SyncTransactionState['documentStates'] {\n let next = documentStates\n for (const [documentId, documentState] of Object.entries(documentStates)) {\n if (!documentState) continue\n if (documentState.subscriptions.length) continue\n if (isAwaitingOwnEcho(documentState)) continue\n next = omitProperty(next, documentId)\n }\n return next\n}\n\nexport function manageSubscriberIds(\n {state}: StoreContext<SyncTransactionState>,\n handles: DocumentHandleLike[],\n): () => void {\n const documentIds = getDocumentIdsFromHandleLikes(handles)\n\n const subscriptionId = randomId(16)\n state.set('addSubscribers', (prev) =>\n documentIds.reduce(\n (acc, id) => addSubscriptionIdToDocument(acc, id, subscriptionId),\n prev as SyncTransactionState,\n ),\n )\n\n return () => {\n setCleanupTimeout(() => {\n state.set('removeSubscribers', (prev) =>\n documentIds.reduce(\n (acc, id) => removeSubscriptionIdFromDocument(acc, id, subscriptionId),\n prev as SyncTransactionState,\n ),\n )\n }, DOCUMENT_STATE_CLEAR_DELAY)\n }\n}\n\n// document handles are passed in via the public facing API, but we also need to\n// pull the correct document ids from action bodies, which have similar but not\n// identical shapes to the document handles. release actions also flow through\n// here, and resolve to the underlying release document id.\nexport function getDocumentIdsFromHandleLikes(handles: (DocumentHandleLike | Action)[]): string[] {\n return handles.flatMap((handle) => {\n if ('type' in handle && isReleaseAction(handle)) {\n return [getReleaseDocumentId(handle.releaseId)]\n }\n const idsForDocument = []\n if (!handle.documentId) return []\n if (handle.liveEdit) {\n return [handle.documentId]\n }\n if (isReleasePerspective(handle.perspective)) {\n idsForDocument.push(\n getVersionId(DocumentId(handle.documentId), handle.perspective.releaseName),\n )\n }\n idsForDocument.push(getDraftId(DocumentId(handle.documentId)))\n idsForDocument.push(getPublishedId(DocumentId(handle.documentId)))\n return idsForDocument\n })\n}\n","import {type MultipleMutationResult, type SanityClient} from '@sanity/client'\nimport {type PatchOperations} from '@sanity/types'\n\nimport {type DocumentAction, type ReleaseAction} from './actions'\nimport {getReleaseDocumentId, isReleaseAction} from './processActions/releaseUtil'\nimport {type OutgoingTransaction} from './reducers'\n\n/** @beta Response body from submitting an outgoing transaction (actions or mutations API). */\nexport type DocumentTransactionSubmissionResult =\n | Awaited<ReturnType<SanityClient['action']>>\n | MultipleMutationResult\n\n/** @beta */\nexport type DocumentEvent =\n | ActionErrorEvent\n | TransactionRevertedEvent\n | TransactionSubmissionFailedEvent\n | TransactionAcceptedEvent\n | DocumentRebaseErrorEvent\n | DocumentEditedEvent\n | DocumentCreatedEvent\n | DocumentDeletedEvent\n | DocumentPublishedEvent\n | DocumentUnpublishedEvent\n | DocumentDiscardedEvent\n | DocumentRemotePatchesEvent\n\n/**\n * @beta\n * Event emitted when a precondition to applying an action fails.\n * (For example: when trying to edit a document that no longer exists.)\n */\nexport interface ActionErrorEvent {\n type: 'error'\n documentId: string\n transactionId: string\n message: string\n error: unknown\n}\n/**\n * @beta\n * Event emitted when a transaction is accepted.\n */\nexport interface TransactionAcceptedEvent {\n type: 'accepted'\n outgoing: OutgoingTransaction\n result: DocumentTransactionSubmissionResult\n}\n/**\n * @beta\n * Event emitted when a transaction is reverted.\n */\nexport interface TransactionRevertedEvent {\n type: 'reverted'\n message: string\n error: unknown\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when submitting a transaction fails with an error the store\n * retries: a network failure, a 5xx, 408 or 429 response, a 401 (for example\n * an expired session), or a 409 `transactionConflictError` (contention while\n * committing). The transaction and its local changes are kept\n * and submitted again, so the document stays out of sync until an attempt\n * succeeds. Emitted once per failed attempt; `attempt` starts at 1.\n */\nexport interface TransactionSubmissionFailedEvent {\n type: 'submission-failed'\n message: string\n error: unknown\n outgoing: OutgoingTransaction\n attempt: number\n}\n/**\n * @beta\n * Event emitted when an attempt to apply local changes to a modified remote document fails.\n */\nexport interface DocumentRebaseErrorEvent {\n type: 'rebase-error'\n documentId: string\n transactionId: string\n message: string\n error: unknown\n}\n/**\n * @beta\n * Event emitted when a document is edited.\n */\nexport interface DocumentEditedEvent {\n type: 'edited'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is created.\n */\nexport interface DocumentCreatedEvent {\n type: 'created'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is deleted.\n */\nexport interface DocumentDeletedEvent {\n type: 'deleted'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is published.\n */\nexport interface DocumentPublishedEvent {\n type: 'published'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is unpublished.\n */\nexport interface DocumentUnpublishedEvent {\n type: 'unpublished'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document version is discarded.\n */\nexport interface DocumentDiscardedEvent {\n type: 'discarded'\n documentId: string\n outgoing: OutgoingTransaction\n}\n\n/**\n * @beta\n * Event emitted when patches for a document are observed through the listener.\n *\n * Unlike the whole-document snapshots the store keeps, these are the raw patch\n * operations from the transaction that produced the change, preserving the\n * operational intent of the edit (e.g. keyed array inserts/unsets). Consumers\n * that maintain their own local state, like collaborative text editors, can\n * apply these directly instead of diffing document snapshots.\n *\n * `origin` is `'local'` when the transaction was submitted by this document\n * store instance (an own edit observed back through the listener) and\n * `'remote'` when it came from another client.\n */\nexport interface DocumentRemotePatchesEvent {\n type: 'remote-patches'\n /** The ID of the document version the patches apply to (e.g. a draft ID). */\n documentId: string\n /** The transaction ID that carried these patches. */\n transactionId: string\n /** The revision the document was at before this transaction. */\n previousRev?: string\n /** The timestamp of the transaction. */\n timestamp: string\n /** The patch operations, rooted at the document. */\n patches: PatchOperations[]\n /** Whether the transaction originated from this client or another one. */\n origin: 'local' | 'remote'\n}\n\n// Release actions that write a mutation to the local release doc map onto\n// the regular per-document events with `documentId = '_.releases.<releaseId>'`.\n// The other release actions (publish/schedule/unschedule/archive/unarchive)\n// don't mutate local state, so they aren't in the map and get skipped — they\n// surface through the transaction-level `accepted`/`reverted` events instead.\nconst actionMap = {\n 'document.create': 'created',\n 'document.delete': 'deleted',\n 'document.discard': 'discarded',\n 'document.edit': 'edited',\n 'document.publish': 'published',\n 'document.unpublish': 'unpublished',\n 'release.create': 'created',\n 'release.edit': 'edited',\n 'release.delete': 'deleted',\n} satisfies Partial<Record<DocumentAction['type'] | ReleaseAction['type'], DocumentEvent['type']>>\n\ntype MappedActionType = keyof typeof actionMap\n\nexport function getDocumentEvents(outgoing: OutgoingTransaction): DocumentEvent[] {\n const documentIdsByAction = outgoing.actions.reduce(\n (acc, action) => {\n if (!(action.type in actionMap)) return acc\n const documentId = isReleaseAction(action)\n ? getReleaseDocumentId(action.releaseId)\n : action.documentId\n if (!documentId) return acc\n const type = action.type as MappedActionType\n const ids = acc[type] ?? new Set<string>()\n ids.add(documentId)\n acc[type] = ids\n return acc\n },\n {} as Partial<Record<MappedActionType, Set<string>>>,\n )\n\n return Object.entries(documentIdsByAction).flatMap(([actionType, documentIds]) =>\n Array.from(documentIds ?? []).map((documentId): DocumentEvent => ({\n type: actionMap[actionType as MappedActionType],\n documentId,\n outgoing,\n })),\n )\n}\n","import {type ListenEvent, type MutationEvent} from '@sanity/client'\nimport {type SanityDocument} from '@sanity/types'\nimport {defer, filter, type MonoTypeOperatorFunction, Observable} from 'rxjs'\n\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\n\nconst DEDUPE_TTL = 120_000\nconst DEDUPE_MAX_ENTRIES = 1_000\n\n/**\n * How long to hold an incomplete multi-document transaction (and the events\n * held behind it) before releasing everything anyway. Only reached when part\n * of a transaction is lost.\n */\nconst GROUP_FLUSH_DEADLINE = 30_000\n\n/**\n * How many intervening events to hold behind an incomplete multi-document\n * transaction before giving up on grouping and releasing everything in\n * arrival order.\n */\nconst GROUP_MAX_HELD_EVENTS = 100\n\ninterface DedupeOptions {\n ttl?: number\n maxEntries?: number\n}\n\n/**\n * Drops mutation events that have already been observed, keyed by\n * `transactionId#documentId`. With `enableResume`, the listener replays\n * missed events after a reconnect on an at-least-once basis, so the same\n * event can be delivered twice. A duplicate would never chain onto the\n * current base revision and would clog the sequencing buffer until it\n * overflows into an `OutOfSyncError`.\n *\n * Seen keys expire after `ttl` milliseconds and the cache is capped at\n * `maxEntries`, evicting the oldest entries first.\n *\n * @internal\n */\nexport function dedupeListenerEvents(\n options?: DedupeOptions,\n): MonoTypeOperatorFunction<ListenEvent<SanityDocument>> {\n const {ttl = DEDUPE_TTL, maxEntries = DEDUPE_MAX_ENTRIES} = options ?? {}\n\n return (source) =>\n defer(() => {\n // insertion-ordered map of dedupe key -> expiry timestamp\n const seen = new Map<string, number>()\n\n return source.pipe(\n filter((event) => {\n if (event.type !== 'mutation') return true\n\n const key = `${event.transactionId}#${event.documentId}`\n const now = Date.now()\n\n const expiry = seen.get(key)\n if (expiry !== undefined && expiry > now) return false\n\n seen.delete(key)\n seen.set(key, now + ttl)\n\n for (const [existingKey, existingExpiry] of seen) {\n if (seen.size <= maxEntries && existingExpiry > now) break\n seen.delete(existingKey)\n }\n\n return true\n }),\n )\n })\n}\n\ninterface GroupTransactionEventsOptions {\n flushDeadline?: number\n maxHeldEvents?: number\n}\n\n/**\n * Buffers the mutation events of multi-document transactions (e.g. a publish\n * touching both the draft and the published document) until every event of\n * the transaction has arrived, then emits them contiguously. Without this,\n * a rebase can run between the two halves of a transaction and observe the\n * dataset in a state that never existed on the server (draft deleted, but\n * published not yet updated).\n *\n * The backend's streaming listener pipeline delivers a transaction's events\n * contiguously and in order, but the legacy pipeline can interleave\n * concurrent transactions. So while a multi-document transaction is\n * incomplete, mutation events from other transactions are held (in arrival\n * order) rather than emitted, and released right after the transaction\n * completes; released events are re-processed so a held multi-document\n * transaction is grouped too. Per-document `previousRev` sequencing\n * downstream tolerates the reordering this introduces.\n *\n * Escape hatches so a lost event degrades to ungrouped delivery instead of\n * stalling the stream: everything is released in arrival order when a\n * non-mutation event (e.g. reconnect/welcome) arrives, when more than\n * `maxHeldEvents` are held, or after `flushDeadline` milliseconds.\n * Single-event transactions pass through untouched when nothing is buffered.\n *\n * @internal\n */\nexport function groupTransactionEvents(\n options?: GroupTransactionEventsOptions,\n): MonoTypeOperatorFunction<ListenEvent<SanityDocument>> {\n const {flushDeadline = GROUP_FLUSH_DEADLINE, maxHeldEvents = GROUP_MAX_HELD_EVENTS} =\n options ?? {}\n\n return (source) =>\n new Observable<ListenEvent<SanityDocument>>((subscriber) => {\n interface OpenTransaction {\n transactionId: string\n events: MutationEvent[]\n total: number\n timer: ReturnType<typeof setTimeout>\n }\n\n let open: OpenTransaction | null = null\n let held: MutationEvent[] = []\n\n // emits the open transaction's events (complete or not) without\n // releasing the held queue; callers decide what happens to `held`\n const closeOpen = () => {\n if (!open) return\n clearTimeout(open.timer)\n const {events} = open\n open = null\n for (const event of events) subscriber.next(event)\n }\n\n // re-processes the held queue so a held multi-document transaction\n // gets grouped in turn. processing may open a new transaction and top\n // up a new held queue; each drain only touches its own snapshot, so\n // this terminates\n const releaseHeld = () => {\n const queue = held\n held = []\n for (const event of queue) processEvent(event)\n }\n\n const releaseEverything = () => {\n closeOpen()\n const queue = held\n held = []\n for (const event of queue) subscriber.next(event)\n }\n\n const processEvent = (event: ListenEvent<SanityDocument>) => {\n if (event.type !== 'mutation') {\n // connection-level events (welcome/welcomeback/reset/reconnect)\n // reset the world downstream; release everything before them\n releaseEverything()\n subscriber.next(event)\n return\n }\n\n const isMultiDocument = (event.transactionTotalEvents ?? 0) > 1\n\n if (!open) {\n if (!isMultiDocument) {\n subscriber.next(event)\n return\n }\n // isMultiDocument guarantees total >= 2, so a freshly opened\n // transaction is always incomplete; completion is checked when\n // subsequent events of the same transaction arrive below\n open = {\n transactionId: event.transactionId,\n events: [event],\n total: event.transactionTotalEvents,\n timer: setCleanupTimeout(() => {\n closeOpen()\n releaseHeld()\n }, flushDeadline),\n }\n return\n }\n\n if (event.transactionId === open.transactionId) {\n open.events.push(event)\n if (open.events.length >= open.total) {\n closeOpen()\n releaseHeld()\n }\n return\n }\n\n // an event from another transaction while one is incomplete: hold it\n // (legacy listener pipeline interleaving) instead of giving up on the\n // open transaction\n held.push(event)\n if (held.length > maxHeldEvents) releaseEverything()\n }\n\n const subscription = source.subscribe({\n next: processEvent,\n error: (error) => {\n releaseEverything()\n subscriber.error(error)\n },\n complete: () => {\n releaseEverything()\n subscriber.complete()\n },\n })\n\n return () => {\n if (open) clearTimeout(open.timer)\n open = null\n held = []\n subscription.unsubscribe()\n }\n })\n}\n","import {type ListenEvent, type SanityDocument} from '@sanity/client'\nimport {createDocumentLoaderFromClient} from '@sanity/mutate/_unstable_store'\nimport {\n first,\n map,\n merge,\n Observable,\n partition,\n ReplaySubject,\n share,\n Subject,\n switchMap,\n takeUntil,\n} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {UPSTREAM_CLOSE_DELAY_MS} from '../store/createStoreInstance'\nimport {cleanupTimer} from '../utils/setCleanupTimeout'\nimport {dedupeListenerEvents, groupTransactionEvents} from './listenerEventOperators'\n\nconst API_VERSION = 'v2025-05-06'\n\nexport interface SharedListener {\n events: Observable<ListenEvent<SanityDocument>>\n dispose: () => void\n}\n\nexport function createSharedListener(\n instance: SanityInstance,\n resource?: DocumentResource,\n): SharedListener {\n const dispose$ = new Subject<void>()\n const events$ = getClientState(instance, {\n apiVersion: API_VERSION,\n resource,\n }).observable.pipe(\n switchMap((client) =>\n // TODO: it seems like the client.listen method is not emitting disconnected\n // events. this is important to ensure we have an up to date version of the\n // doc. probably should introduce our own events for when the user goes offline\n client.listen(\n '*',\n {},\n {\n // with `enableResume`, a reconnect replays the events missed while\n // offline (emitting `welcomeback`) instead of forcing a refetch of\n // every subscribed document. when the backend cannot resume, it\n // emits `welcome` or `reset`, both of which trigger a refetch\n events: ['mutation', 'welcome', 'welcomeback', 'reconnect', 'reset'],\n enableResume: true,\n includeResult: false,\n // the document store handles version documents, so we need to include all versions\n includeAllVersions: true,\n tag: 'document-listener',\n // // from manual testing, it seems like mendoza patches may be\n // // causing some ambiguity/wonkiness\n // includeMutations: false,\n // effectFormat: 'mendoza',\n },\n ),\n ),\n // resumed events are delivered at-least-once, so drop replayed duplicates\n dedupeListenerEvents(),\n // hold multi-document transactions (e.g. publish) until complete so\n // per-document processing never observes half a transaction\n groupTransactionEvents(),\n takeUntil(dispose$),\n share(),\n )\n\n const [welcome$, mutation$] = partition(events$, (e) => e.type === 'welcome')\n\n return {\n events: merge(\n // we replay the welcome event because that event kicks off fetching the document.\n // Resets once no document listens, so the connection can close instead of staying open\n // for the page. The delay stops a quick resubscribe from reconnecting.\n welcome$.pipe(\n share({\n connector: () => new ReplaySubject(1),\n resetOnComplete: false,\n resetOnRefCountZero: () => cleanupTimer(UPSTREAM_CLOSE_DELAY_MS),\n }),\n ),\n mutation$,\n ),\n dispose: () => dispose$.next(),\n }\n}\n\nexport function createFetchDocument(instance: SanityInstance, resource?: DocumentResource) {\n return function (documentId: string): Observable<SanityDocument | null> {\n return getClientState(instance, {\n apiVersion: API_VERSION,\n resource,\n }).observable.pipe(\n switchMap((client) => {\n // creates a observable request to the /doc/{documentId} endpoint for a given document id\n // should work across all kinds of document IDs (drafts.**, version.**., etc.)\n const loadDocument = createDocumentLoaderFromClient(client)\n return loadDocument(documentId)\n }),\n map((result) => {\n if (!result.accessible) {\n if (result.reason === 'existence') return null\n throw new Error(`Document with ID \\`${documentId}\\` is inaccessible due to permissions.`)\n }\n return result.document as SanityDocument\n }),\n first(),\n )\n }\n}\n","import {ClientError, CorsOriginError} from '@sanity/client'\n\n/**\n * What the document store does with an outgoing transaction whose submission\n * failed.\n *\n * - `retry`: the failure is about the connection, the credentials or\n * contention on the server, not the transaction. Keep the transaction and\n * its optimistic local state, and submit it again.\n * - `accepted`: the server already recorded this transaction ID, so an earlier\n * attempt was committed even though its response never arrived.\n * - `revert`: the server rejected the transaction itself. Submitting it again\n * cannot succeed, so drop it and rebuild local state from the server copy.\n *\n * @internal\n */\nexport type SubmissionErrorClass = 'retry' | 'accepted' | 'revert'\n\n// Mirrors Studio's `TERMINAL_COMMIT_STATUSES` (checkoutPair.ts) without 401. A\n// 401 rejects the credentials (usually an expired session), not the\n// transaction, so the same transaction succeeds once the user re-authenticates.\nconst REJECTED_TRANSACTION_STATUS_CODES = new Set([400, 402, 403, 404, 409, 410, 412, 413, 422])\n\nfunction getErrorType(error: ClientError): unknown {\n const details: unknown = error.details\n if (details && typeof details === 'object' && 'type' in details) return details.type\n return undefined\n}\n\n/**\n * Classifies a failed transaction submission. Unknown failures (network\n * errors, timeouts, 5xx and 429 responses, and anything that is not a\n * `ClientError`) default to `retry`: wrongly retrying a rejected transaction\n * leaves the document visibly unsynced with the edits still on screen, while\n * wrongly reverting a transient failure silently discards the user's work.\n *\n * @internal\n */\nexport function classifySubmissionError(error: unknown): SubmissionErrorClass {\n if (error instanceof CorsOriginError) return 'revert'\n if (!(error instanceof ClientError)) return 'retry'\n if (error.statusCode === 409) {\n const errorType = getErrorType(error)\n if (errorType === 'transactionAlreadyExistsError') return 'accepted'\n // contention while committing, not a verdict on the transaction; the\n // server rolled it back, so the same transaction ID is safe to resend\n if (errorType === 'transactionConflictError') return 'retry'\n }\n if (REJECTED_TRANSACTION_STATUS_CODES.has(error.statusCode)) return 'revert'\n return 'retry'\n}\n","import {\n type Action,\n ClientError,\n CorsOriginError,\n type Mutation,\n type SanityClient,\n ServerError,\n} from '@sanity/client'\nimport {DocumentId, getDraftId} from '@sanity/id-utils'\nimport {jsonMatch} from '@sanity/json-match'\nimport {type ExprNode} from 'groq-js'\nimport {\n catchError,\n concatMap,\n defer,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n Observable,\n of,\n pairwise,\n race,\n retry,\n startWith,\n Subject,\n switchMap,\n tap,\n throttle,\n throwError,\n timer,\n withLatestFrom,\n} from 'rxjs'\n\nimport {getCurrentUserState} from '../auth/authStore'\nimport {type ClientOptions, getClientState} from '../client/clientStore'\nimport {\n type DocumentHandle,\n type DocumentResource,\n isCanvasResource,\n isDatasetResource,\n isMediaLibraryResource,\n} from '../config/sanityConfig'\nimport {\n bindActionByResource,\n type BoundResourceKey,\n type StoreAction,\n} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {type ResolveDocument} from '../typegen/resolve'\nimport {type DocumentAction} from './actions'\nimport {\n ACL_RETRY_BASE_DELAY,\n ACL_RETRY_MAX_DELAY,\n API_VERSION,\n INITIAL_OUTGOING_THROTTLE_TIME,\n OUT_OF_SYNC_RETRY_BASE_DELAY,\n OUT_OF_SYNC_RETRY_COUNT,\n OUT_OF_SYNC_RETRY_MAX_DELAY,\n SUBMISSION_RETRY_BASE_DELAY,\n SUBMISSION_RETRY_MAX_DELAY,\n} from './documentConstants'\nimport {\n type DocumentEvent,\n type DocumentTransactionSubmissionResult,\n getDocumentEvents,\n} from './events'\nimport {listen, OutOfSyncError} from './listen'\nimport {type JsonMatch} from './patchOperations'\nimport {\n calculatePermissions,\n createGrantsLookup,\n type DatasetAcl,\n type DocumentPermissionsResult,\n type Grant,\n} from './permissions'\nimport {ActionError} from './processActions/processActions'\nimport {isReleaseAction} from './processActions/releaseUtil'\nimport {\n type AppliedTransaction,\n applyFirstQueuedTransaction,\n applyRemoteDocument,\n cleanupOutgoingTransaction,\n getDocumentIdsFromHandleLikes,\n manageSubscriberIds,\n type OutgoingTransaction,\n type QueuedTransaction,\n removeQueuedTransaction,\n revertOutgoingTransaction,\n transitionAppliedTransactionsToOutgoing,\n type UnverifiedDocumentRevision,\n} from './reducers'\nimport {createFetchDocument, createSharedListener, type SharedListener} from './sharedListener'\nimport {classifySubmissionError} from './submissionErrors'\n\nexport interface DocumentStoreState {\n documentStates: {[TDocumentId in string]?: DocumentState}\n queued: QueuedTransaction[]\n applied: AppliedTransaction[]\n outgoing?: OutgoingTransaction\n grants?: Record<Grant, ExprNode>\n /**\n * The current user's identity (their user ID).\n */\n identity?: string\n error?: unknown\n sharedListener: SharedListener\n fetchDocument: (documentId: string) => Observable<ResolveDocument | null>\n events: Subject<DocumentEvent>\n}\n\nexport interface DocumentState {\n id: string\n error?: unknown\n /**\n * the \"remote\" local copy that matches the server. represents the last known\n * server state. this gets updated every time we confirm remote patches\n */\n remote?: ResolveDocument | null\n /**\n * the current ephemeral working copy that includes local optimistic changes\n * that have not yet been confirmed by the server\n */\n local?: ResolveDocument | null\n /**\n * the revision that our remote document is at\n */\n remoteRev?: string | null\n /**\n * Array of subscription IDs. This document state will be deleted if there are\n * no subscribers.\n */\n subscriptions: string[]\n /**\n * An object keyed by transaction ID of revisions sent out but that have not\n * yet been verified yet. When an applied transaction is transitioned to an\n * outgoing transaction, it also adds unverified revisions for each document\n * that is part of that outgoing transaction. Transactions are submitted to\n * the server with a locally generated transaction ID. This way we can observe\n * when our transaction comes back through the shared listener. Each listener\n * event that comes back contains a `previousRev`. If we see our own\n * transaction with a different `previousRev` than expected, we can rebase our\n * local transactions on top of this new remote.\n */\n unverifiedRevisions?: {[TTransactionId in string]?: UnverifiedDocumentRevision}\n /**\n * transaction IDs recently submitted by this client for this document,\n * newest last. used to label `remote-patches` events with the correct\n * `origin` even after the corresponding `unverifiedRevisions` entry has\n * been pruned (e.g. by a sync event that raced the listener echo). capped\n * to the most recent entries.\n */\n recentOwnTransactionIds?: string[]\n}\n\nexport const documentStore = defineStore<DocumentStoreState, BoundResourceKey>({\n name: 'Document',\n getInitialState: (instance, {resource}) => ({\n documentStates: {},\n // these can be emptied on refetch\n queued: [],\n applied: [],\n sharedListener: createSharedListener(instance, resource),\n fetchDocument: createFetchDocument(instance, resource),\n events: new Subject(),\n }),\n initialize(context) {\n const {sharedListener} = context.state.get()\n const subscriptions = [\n subscribeToQueuedAndApplyNextTransaction(context),\n subscribeToSubscriptionsAndListenToDocuments(context),\n subscribeToAppliedAndSubmitNextTransaction(context),\n subscribeToClientAndFetchDatasetAcl(context),\n subscribeToCurrentUserAndSetIdentity(context),\n ]\n\n return () => {\n sharedListener.dispose()\n subscriptions.forEach((subscription) => subscription.unsubscribe())\n }\n },\n})\n\n/**\n * @beta\n * Options for specifying a document and optionally a path within it.\n */\nexport interface DocumentOptions<\n TPath extends string | undefined = undefined,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n path?: TPath\n}\n\n/** @beta */\nexport function getDocumentState<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: DocumentOptions<undefined, TDocumentType, TDataset, TProjectId>,\n): StateSource<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`> | undefined | null>\n\n/** @beta */\nexport function getDocumentState<\n TPath extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: DocumentOptions<TPath, TDocumentType, TDataset, TProjectId>,\n): StateSource<\n JsonMatch<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>, TPath> | undefined\n>\n\n/** @beta */\nexport function getDocumentState<TData>(\n instance: SanityInstance,\n options: DocumentOptions<string | undefined>,\n): StateSource<TData | undefined | null>\n\n/** @beta */\nexport function getDocumentState(\n ...args: Parameters<typeof _getDocumentState>\n): StateSource<unknown> {\n return _getDocumentState(...args)\n}\n\nfunction throwDocumentError(\n documentStates: DocumentStoreState['documentStates'],\n documentIds: string[],\n): void {\n for (const documentId of documentIds) {\n const documentError = documentStates[documentId]?.error\n if (documentError) throw documentError\n }\n}\n\n/** Version wins over draft, and draft over published. Undefined until every read has arrived. */\nfunction selectLocalDocument(\n documentStates: DocumentStoreState['documentStates'],\n documentIds: string[],\n): ResolveDocument | null | undefined {\n throwDocumentError(documentStates, documentIds)\n let selected: ResolveDocument | null = null\n for (const documentId of documentIds) {\n const local = documentStates[documentId]?.local\n if (local === undefined) return undefined\n if (selected === null) selected = local\n }\n return selected\n}\n\nfunction hasEveryDocumentArrived(\n documentStates: DocumentStoreState['documentStates'],\n documentIds: string[],\n): boolean {\n throwDocumentError(documentStates, documentIds)\n return documentIds.every((documentId) => documentStates[documentId] !== undefined)\n}\n\nconst _getDocumentState = bindActionByResource(\n documentStore,\n createStateSourceAction({\n selector: ({state: {error, documentStates}}, options: DocumentOptions<string | undefined>) => {\n const {path} = options\n if (error) throw error\n const document = selectLocalDocument(documentStates, getDocumentIdsFromHandleLikes([options]))\n if (document === undefined) return undefined\n\n if (!path) return document\n const result = jsonMatch(document, path).next()\n if (result.done) return undefined\n const {value} = result.value\n return value\n },\n onSubscribe: (context, options: DocumentOptions<string | undefined>) =>\n manageSubscriberIds(context, [options]),\n }),\n)\n\n/** @beta */\nexport function resolveDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n docHandle: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): Promise<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`> | null>\n/** @beta */\nexport function resolveDocument<TData extends ResolveDocument>(\n instance: SanityInstance,\n docHandle: DocumentHandle<string, string, string>,\n): Promise<TData | null>\n/** @beta */\nexport function resolveDocument(\n ...args: Parameters<typeof _resolveDocument>\n): Promise<ResolveDocument | null> {\n return _resolveDocument(...args)\n}\nconst _resolveDocument = bindActionByResource(\n documentStore,\n ({instance}, docHandle: DocumentHandle<string, string, string>) => {\n return firstValueFrom(\n getDocumentState(instance, {\n ...docHandle,\n path: undefined,\n }).observable.pipe(filter((i) => i !== undefined)),\n ) as Promise<ResolveDocument | null>\n },\n)\n\n/** @beta */\nexport const getDocumentSyncStatus = bindActionByResource(\n documentStore,\n createStateSourceAction({\n selector: (\n {state: {error, documentStates: documents, outgoing, applied, queued}},\n doc: DocumentHandle,\n ) => {\n if (error) throw error\n if (!hasEveryDocumentArrived(documents, getDocumentIdsFromHandleLikes([doc])))\n return undefined\n return !queued.length && !applied.length && !outgoing\n },\n onSubscribe: (context, doc: DocumentHandle) => {\n return manageSubscriberIds(context, [doc])\n },\n }),\n)\n\ntype PermissionsStateOptions = {\n resource?: DocumentResource\n actions: DocumentAction[]\n}\n\n/** @beta */\nexport const getPermissionsState = bindActionByResource(\n documentStore,\n createStateSourceAction({\n selector: calculatePermissions,\n onSubscribe: (context, {actions}: PermissionsStateOptions) => {\n manageSubscriberIds(context, actions)\n },\n }) as StoreAction<\n DocumentStoreState,\n [PermissionsStateOptions],\n StateSource<DocumentPermissionsResult>\n >,\n)\n\n/** @beta */\nexport const resolvePermissions = bindActionByResource(\n documentStore,\n ({instance}, options: PermissionsStateOptions) => {\n return firstValueFrom(\n getPermissionsState(instance, options).observable.pipe(filter((i) => i !== undefined)),\n )\n },\n)\n\n/** @beta */\nexport const subscribeDocumentEvents = bindActionByResource(\n documentStore,\n ({state}, options: {resource?: DocumentResource; eventHandler: (e: DocumentEvent) => void}) => {\n const {events} = state.get()\n const subscription = events.subscribe(options.eventHandler)\n return () => subscription.unsubscribe()\n },\n)\n\nfunction failTransactionOnUnreadableDocument({queued, documentStates}: DocumentStoreState): void {\n const transaction = queued.at(0)\n if (!transaction) return\n const ids = getDocumentIdsFromHandleLikes(transaction.actions)\n const unreadableId = ids.find((id) => documentStates[id]?.error)\n if (unreadableId === undefined) return\n const error = documentStates[unreadableId]?.error\n const actionError = new ActionError({\n message: error instanceof Error ? error.message : String(error),\n documentId: unreadableId,\n transactionId: transaction.transactionId,\n })\n actionError.cause = error\n throw actionError\n}\n\nconst subscribeToQueuedAndApplyNextTransaction = ({\n state,\n}: StoreContext<DocumentStoreState, BoundResourceKey>) => {\n const {events} = state.get()\n return state.observable\n .pipe(\n tap(failTransactionOnUnreadableDocument),\n map(applyFirstQueuedTransaction),\n distinctUntilChanged(),\n tap((next) => state.set('applyFirstQueuedTransaction', next)),\n catchError((error, caught) => {\n if (error instanceof ActionError) {\n state.set('removeQueuedTransaction', (prev) =>\n removeQueuedTransaction(prev, error.transactionId),\n )\n events.next({\n type: 'error',\n message: error.message,\n documentId: error.documentId,\n transactionId: error.transactionId,\n error,\n })\n return caught\n }\n\n throw error\n }),\n )\n .subscribe({error: (error) => state.set('setError', {error})})\n}\n\nconst getErrorMessage = (error: unknown) =>\n error instanceof Error ? error.message : 'Request failed'\n\nconst submitOutgoingTransaction = (client: SanityClient, outgoing: OutgoingTransaction) => {\n // liveEdit transactions route to the mutations API; everything else routes\n // to the actions API. processActions rejects transactions that mix the two,\n // and reducers won't batch across that boundary, so a batch is always\n // entirely liveEdit or entirely not.\n if (outgoing.actions.some((action) => !isReleaseAction(action) && action.liveEdit)) {\n return client.observable.mutate(outgoing.outgoingMutations as Mutation[], {\n transactionId: outgoing.transactionId,\n visibility: 'async',\n returnDocuments: false,\n returnFirst: false,\n tag: 'document.mutate',\n skipCrossDatasetReferenceValidation: true,\n })\n }\n\n return client.observable.action(outgoing.outgoingActions as Action[], {\n transactionId: outgoing.transactionId,\n skipCrossDatasetReferenceValidation: true,\n tag: 'document.action',\n })\n}\n\nconst subscribeToAppliedAndSubmitNextTransaction = ({\n state,\n instance,\n key: {resource},\n}: StoreContext<DocumentStoreState, BoundResourceKey>) => {\n const {events} = state.get()\n const clients$ = getClientState(instance, {apiVersion: API_VERSION, resource}).observable\n\n return state.observable\n .pipe(\n throttle(\n (s) =>\n // if there is no outgoing transaction, we can throttle by the\n // initial outgoing throttle time…\n !s.outgoing\n ? timer(INITIAL_OUTGOING_THROTTLE_TIME)\n : // …otherwise, wait until the outgoing has been cleared\n state.observable.pipe(first(({outgoing}) => !outgoing)),\n {leading: false, trailing: true},\n ),\n map(transitionAppliedTransactionsToOutgoing),\n distinctUntilChanged((a, b) => a.outgoing?.transactionId === b.outgoing?.transactionId),\n tap((next) => state.set('transitionAppliedTransactionsToOutgoing', next)),\n map((s) => s.outgoing),\n distinctUntilChanged(),\n withLatestFrom(clients$),\n concatMap(([outgoing, initialClient]) => {\n if (!outgoing) return EMPTY\n\n // the client that made the latest attempt. the client store rebuilds\n // its clients when the token changes, so a different client means\n // different credentials and a retry after a 401 can go right away\n let attemptClient: SanityClient | undefined\n\n return defer(() => (attemptClient ? clients$.pipe(first()) : of(initialClient))).pipe(\n concatMap((client) => {\n attemptClient = client\n return submitOutgoingTransaction(client, outgoing)\n }),\n retry({\n delay: (error: unknown, retryCount) => {\n if (classifySubmissionError(error) !== 'retry') return throwError(() => error)\n\n // keep the transaction and its optimistic local state. the\n // document stays out of sync (see `getDocumentSyncStatus`) and\n // later edits queue behind this transaction until it lands\n events.next({\n type: 'submission-failed',\n message: getErrorMessage(error),\n error,\n outgoing,\n attempt: retryCount,\n })\n const backoff = Math.min(\n SUBMISSION_RETRY_BASE_DELAY * 2 ** (retryCount - 1),\n SUBMISSION_RETRY_MAX_DELAY,\n )\n return race(\n timer(backoff),\n clients$.pipe(first((client) => client !== attemptClient)),\n )\n },\n }),\n catchError((error: unknown) => {\n if (classifySubmissionError(error) === 'accepted') {\n // an earlier attempt was committed and only its response was\n // lost, so this is an ack\n return of({transactionId: outgoing.transactionId, documentIds: [], results: []})\n }\n state.set('revertOutgoingTransaction', revertOutgoingTransaction)\n events.next({type: 'reverted', message: getErrorMessage(error), outgoing, error})\n return EMPTY\n }),\n map((result: unknown) => ({\n result: result as DocumentTransactionSubmissionResult,\n outgoing,\n })),\n )\n }),\n tap(({outgoing, result}) => {\n state.set('cleanupOutgoingTransaction', cleanupOutgoingTransaction)\n for (const e of getDocumentEvents(outgoing)) events.next(e)\n events.next({type: 'accepted', outgoing, result})\n }),\n )\n .subscribe({error: (error) => state.set('setError', {error})})\n}\n\nconst subscribeToSubscriptionsAndListenToDocuments = (\n context: StoreContext<DocumentStoreState, BoundResourceKey>,\n) => {\n const {state} = context\n const {events} = state.get()\n\n return state.observable\n .pipe(\n filter((s) => !!s.grants),\n map((s) => Object.keys(s.documentStates)),\n distinctUntilChanged((curr, next) => {\n if (curr.length !== next.length) return false\n const currSet = new Set(curr)\n return next.every((i) => currSet.has(i))\n }),\n startWith(new Set<string>()),\n pairwise(),\n switchMap((pair) => {\n const [curr, next] = pair.map((ids) => new Set(ids))\n const added = Array.from(next).filter((i) => !curr.has(i))\n const removed = Array.from(curr).filter((i) => !next.has(i))\n\n // NOTE: the order of which these go out is somewhat important\n // because that determines the order `applyRemoteDocument` is called\n // which in turn determines which document version get populated\n // first. because we prefer drafts, it's better to have those go out\n // first so that the published document doesn't flash for a frame\n const changes = [\n ...added.map((id) => ({id, add: true})),\n ...removed.map((id) => ({id, add: false})),\n ].sort((a, b) => {\n const aIsDraft = a.id === getDraftId(DocumentId(a.id))\n const bIsDraft = b.id === getDraftId(DocumentId(b.id))\n\n if (aIsDraft && bIsDraft) return a.id.localeCompare(b.id, 'en-US')\n if (aIsDraft) return -1\n if (bIsDraft) return 1\n return a.id.localeCompare(b.id, 'en-US')\n })\n\n return of<{id: string; add: boolean}[]>(...changes)\n }),\n groupBy((i) => i.id),\n mergeMap((group) =>\n group.pipe(\n switchMap((e) => {\n if (!e.add) return EMPTY\n return listen(context, e.id).pipe(\n retry({\n count: OUT_OF_SYNC_RETRY_COUNT,\n resetOnSuccess: true,\n delay: (error, retryCount) => {\n const isTransient =\n error instanceof OutOfSyncError ||\n error instanceof ServerError ||\n (error instanceof ClientError &&\n (error.statusCode === 408 || error.statusCode === 429))\n if (!isTransient) return throwError(() => error)\n const backoff = Math.min(\n OUT_OF_SYNC_RETRY_BASE_DELAY * 2 ** (retryCount - 1),\n OUT_OF_SYNC_RETRY_MAX_DELAY,\n )\n return timer(backoff)\n },\n }),\n catchError((error) => {\n state.set('setDocumentError', (prev) => {\n const documentState = prev.documentStates[e.id]\n if (!documentState) return prev\n return {\n ...prev,\n documentStates: {...prev.documentStates, [e.id]: {...documentState, error}},\n }\n })\n return EMPTY\n }),\n tap((remote) =>\n state.set('applyRemoteDocument', (prev) =>\n applyRemoteDocument(prev, remote, events),\n ),\n ),\n )\n }),\n ),\n ),\n )\n .subscribe({error: (error) => state.set('setError', {error})})\n}\n\nconst subscribeToClientAndFetchDatasetAcl = ({\n instance,\n state,\n key: {resource},\n}: StoreContext<DocumentStoreState, BoundResourceKey>) => {\n const clientOptions: ClientOptions = {apiVersion: API_VERSION, resource}\n\n let url: string\n if (resource && isDatasetResource(resource)) {\n url = `/projects/${resource.projectId}/datasets/${resource.dataset}/acl`\n } else if (resource && isMediaLibraryResource(resource)) {\n url = `/media-libraries/${resource.mediaLibraryId}/acl`\n } else if (resource && isCanvasResource(resource)) {\n url = `/canvases/${resource.canvasId}/acl`\n } else {\n throw new Error(`Received invalid resource: ${JSON.stringify(resource)}`)\n }\n\n return getClientState(instance, clientOptions)\n .observable.pipe(\n switchMap((client) =>\n client.observable\n .request<DatasetAcl>({\n url,\n tag: 'acl.get',\n })\n .pipe(\n retry({\n delay: (error, retryCount) => {\n // 4xx responses and CORS misconfigurations are not transient —\n // the server will keep rejecting the request, so rethrow to\n // surface them as a fatal store error. 408 (request timeout)\n // and 429 (rate limit) are the exceptions: they resolve on\n // their own, so they are retried like network errors\n const isTransientClientError =\n error instanceof ClientError &&\n (error.statusCode === 408 || error.statusCode === 429)\n if (\n (error instanceof ClientError && !isTransientClientError) ||\n error instanceof CorsOriginError\n ) {\n return throwError(() => error)\n }\n // network errors (no status code), 408/429 responses, and 5xx\n // responses are retried with exponential backoff so a transient\n // failure during startup doesn't permanently brick the store\n const backoff = Math.min(\n ACL_RETRY_BASE_DELAY * 2 ** (retryCount - 1),\n ACL_RETRY_MAX_DELAY,\n )\n return timer(backoff)\n },\n }),\n ),\n ),\n tap((datasetAcl) => state.set('setGrants', {grants: createGrantsLookup(datasetAcl)})),\n )\n .subscribe({\n error: (error) => state.set('setError', {error}),\n })\n}\n\nconst subscribeToCurrentUserAndSetIdentity = ({\n instance,\n state,\n}: StoreContext<DocumentStoreState, BoundResourceKey>) =>\n getCurrentUserState(instance).observable.subscribe({\n next: (currentUser) => state.set('setIdentity', {identity: currentUser?.id}),\n // A transient identity-fetch failure (network blip, expired token, or a\n // normal logout/re-login transition) should not brick all document\n // operations. Reset the identity to `undefined` and keep going — GROQ's\n // `identity()` then evaluates to null, matching the unauthenticated state.\n error: () => state.set('setIdentity', {identity: undefined}),\n })\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\n\nimport {\n type DocumentResource,\n isCanvasResource,\n isMediaLibraryResource,\n} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type Action, type DocumentAction} from './actions'\nimport {getEffectiveDocumentId} from './util'\n\nexport interface EffectiveDocModel {\n /**\n * If this is `undefined`, the resource has no opinion and the handle's `liveEdit`\n * flag should be respected.\n */\n liveEdit: boolean | undefined\n /**\n * When `false`, the resource does not support release perspectives. Callers\n * should drop any release perspective and fall back to the standard path.\n */\n supportsReleases: boolean\n}\n\nconst MEDIA_LIBRARY_DRAFTED_TYPES = new Set(['sanity.asset'])\n\n/**\n * Different resources have different \"default\" editing models.\n *\n * Canvas uses a liveEdit model.\n * Medial Library is mostly liveEdit except for `sanity.asset`, which retains the\n * draft/published model. Neither resource supports release perspectives.\n */\nexport function getEffectiveDocumentModel(\n resource: DocumentResource | undefined,\n documentType: string | undefined,\n): EffectiveDocModel {\n if (!resource) {\n return {liveEdit: undefined, supportsReleases: true}\n }\n if (isCanvasResource(resource)) {\n return {liveEdit: true, supportsReleases: false}\n }\n if (isMediaLibraryResource(resource)) {\n const isDrafted = documentType ? MEDIA_LIBRARY_DRAFTED_TYPES.has(documentType) : false\n return {liveEdit: !isDrafted, supportsReleases: false}\n }\n return {liveEdit: undefined, supportsReleases: true}\n}\n\nfunction describeResource(resource: DocumentResource | undefined): string {\n if (resource && isCanvasResource(resource)) return 'Canvas'\n if (resource && isMediaLibraryResource(resource)) return 'Media Library'\n return 'this resource'\n}\n\n/**\n * Rewrites edit actions to match the editing model of the bound resource.\n *\n * Canvas and Media Library resources do not support release perspectives, and\n * Canvas (plus most Media Library types) does not have a draft/published\n * model. When an edit action arrives with a release perspective for a resource\n * that doesn't support it, the perspective is stripped and a single warning\n * is logged. When the resource forces liveEdit, the action's `liveEdit` flag\n * is set so the dispatcher takes the liveEdit branch.\n *\n * Only `document.edit` actions are normalized today — other action types\n * (publish, unpublish, release actions, etc.) pass through unchanged.\n *\n * @internal\n */\nexport function normalizeActionsForResource(\n actions: Action[],\n resource: DocumentResource | undefined,\n): Action[] {\n // collect actions that may have changed in unexpected ways\n const stripped: Array<{documentType: string; documentId: string}> = []\n\n const normalized = actions.map((action) => {\n if (action.type !== 'document.edit') return action\n\n const {liveEdit: forcedLiveEdit, supportsReleases} = getEffectiveDocumentModel(\n resource,\n action.documentType,\n )\n const shouldRemovePerspective = isReleasePerspective(action.perspective) && !supportsReleases\n const shouldForceLiveEdit = forcedLiveEdit === true && !action.liveEdit\n\n if (!shouldRemovePerspective && !shouldForceLiveEdit) return action\n\n const corrected: DocumentAction = {...action}\n if (shouldForceLiveEdit) corrected.liveEdit = true\n if (shouldRemovePerspective) corrected.perspective = undefined\n\n // ensure we're using the right document ID for the corrected model\n corrected.documentId = getEffectiveDocumentId({\n ...corrected,\n documentId: getPublishedId(DocumentId(corrected.documentId)),\n })\n\n if (shouldRemovePerspective) {\n stripped.push({documentType: action.documentType, documentId: corrected.documentId})\n }\n\n return corrected\n })\n\n if (stripped.length > 0) {\n const docs = stripped.map((e) => `${e.documentType} (${e.documentId})`).join(', ')\n // eslint-disable-next-line no-console\n console.warn(\n `[sanity-sdk] ${describeResource(resource)} does not support release perspectives — falling back to the standard editing path for: ${docs}`,\n )\n }\n\n return normalized\n}\n","import {distinctUntilChanged, filter, first, firstValueFrom, map, race} from 'rxjs'\n\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {bindActionByResource} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type StoreContext} from '../store/defineStore'\nimport {type ResolveDocument} from '../typegen/resolve'\nimport {randomUuid} from '../utils/ids'\nimport {type Action} from './actions'\nimport {documentStore, type DocumentStoreState} from './documentStore'\nimport {type DocumentTransactionSubmissionResult} from './events'\nimport {type DocumentSet} from './processMutations'\nimport {type AppliedTransaction, type QueuedTransaction, queueTransaction} from './reducers'\nimport {normalizeActionsForResource} from './resourceRules'\n\n/** @beta */\nexport interface ActionsResult<TDocument extends ResolveDocument = ResolveDocument> {\n transactionId: string\n documents: DocumentSet<TDocument>\n previous: DocumentSet<TDocument>\n previousRevs: {[documentId: string]: string | undefined}\n appeared: string[]\n updated: string[]\n disappeared: string[]\n submitted: () => Promise<DocumentTransactionSubmissionResult>\n}\n\n/** @beta */\nexport interface ApplyDocumentActionsOptions {\n /**\n * List of actions to apply. Accepts both document actions and release\n * lifecycle actions because they share the same transaction pipeline.\n */\n actions: Action[]\n\n /**\n * The resource to which the documents being acted on belong.\n */\n resource?: DocumentResource\n\n /**\n * Optionally provide an ID to be used as this transaction ID\n */\n transactionId?: string\n /**\n * Set this to true to prevent this action from being batched with others.\n */\n disableBatching?: boolean\n}\n\n/** @beta */\nexport function applyDocumentActions<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: ApplyDocumentActionsOptions,\n): Promise<ActionsResult<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>>>\n/** @beta */\nexport function applyDocumentActions(\n instance: SanityInstance,\n options: ApplyDocumentActionsOptions,\n): Promise<ActionsResult>\n\n/** @beta */\nexport function applyDocumentActions(\n ...args: Parameters<typeof boundApplyDocumentActions>\n): ReturnType<typeof boundApplyDocumentActions> {\n return boundApplyDocumentActions(...args)\n}\n\nconst boundApplyDocumentActions = bindActionByResource(documentStore, _applyDocumentActions)\n\n/**\n * Returns the result of a transaction with no actions. `batchAppliedTransactions`\n * skips empty transactions, so one that reached the queue would never be consumed\n * into an outgoing transaction: it would sit in `applied` and its `submitted()`\n * would never resolve.\n */\nfunction emptyActionsResult(transactionId: string): ActionsResult {\n return {\n transactionId,\n documents: {},\n previous: {},\n previousRevs: {},\n appeared: [],\n updated: [],\n disappeared: [],\n submitted: async () => ({transactionId}),\n }\n}\n\n/** @internal */\nasync function _applyDocumentActions(\n {state}: StoreContext<DocumentStoreState>,\n {actions, resource, transactionId = randomUuid(), disableBatching}: ApplyDocumentActionsOptions,\n): Promise<ActionsResult> {\n if (!actions.length) return emptyActionsResult(transactionId)\n\n const {events} = state.get()\n\n // Rewrite edit actions to match the bound resource's editing model (e.g.\n // forcing liveEdit for Canvas, stripping unsupported release perspectives).\n // Non-edit and release actions pass through unchanged.\n const normalizedActions = normalizeActionsForResource(actions, resource)\n\n const transaction: QueuedTransaction = {\n transactionId,\n actions: normalizedActions,\n ...(disableBatching && {disableBatching}),\n }\n\n const fatalError$ = state.observable.pipe(\n map((s) => s.error),\n first(Boolean),\n map((error) => ({type: 'error', error}) as const),\n )\n\n const transactionError$ = events.pipe(\n filter((e) => e.type === 'error'),\n first((e) => e.transactionId === transactionId),\n )\n\n const appliedTransaction$ = state.observable.pipe(\n map((s) => s.applied),\n distinctUntilChanged(),\n map((applied) => applied.find((t) => t.transactionId === transactionId)),\n first(Boolean),\n )\n\n const successfulTransaction$ = events.pipe(\n filter((e) => e.type === 'accepted'),\n first((e) => e.outgoing.batchedTransactionIds.includes(transactionId)),\n )\n\n const rejectedTransaction$ = events.pipe(\n filter((e) => e.type === 'reverted'),\n first((e) => e.outgoing.batchedTransactionIds.includes(transactionId)),\n )\n\n const appliedTransactionOrError = firstValueFrom(\n race([fatalError$, transactionError$, appliedTransaction$]),\n )\n const acceptedOrRejectedTransaction = firstValueFrom(\n race([successfulTransaction$, rejectedTransaction$, transactionError$]),\n )\n\n state.set('queueTransaction', (prev) => queueTransaction(prev, transaction))\n\n const result = await appliedTransactionOrError\n if ('type' in result && result.type === 'error') throw result.error\n\n const {working: documents, previous, previousRevs} = result as AppliedTransaction\n const existingIds = new Set(\n Object.entries(previous)\n .filter(([, value]) => !!value)\n .map(([key]) => key),\n )\n const resultingIds = new Set(\n Object.entries(documents)\n .filter(([, value]) => !!value)\n .map(([key]) => key),\n )\n const allIds = new Set([...existingIds, ...resultingIds])\n\n const updated: string[] = []\n const appeared: string[] = []\n const disappeared: string[] = []\n\n for (const id of allIds) {\n if (existingIds.has(id) && resultingIds.has(id)) {\n updated.push(id)\n } else if (!existingIds.has(id) && resultingIds.has(id)) {\n appeared.push(id)\n } else if (!resultingIds.has(id) && existingIds.has(id)) {\n disappeared.push(id)\n }\n }\n\n async function submitted() {\n const raceResult = await acceptedOrRejectedTransaction\n if (raceResult.type !== 'accepted') throw raceResult.error\n return raceResult.result\n }\n\n return {\n transactionId,\n documents,\n previous,\n previousRevs,\n appeared,\n updated,\n disappeared,\n submitted,\n }\n}\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {\n type OrganizationBase,\n type OrganizationMember,\n type OrganizationOptions,\n} from '../organization/organization'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/**\n * The list shape returned from `/organizations`, with `members` and/or\n * `features` conditionally included based on the query options used.\n * @public\n */\nexport type Organizations<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> = (Pick<\n OrganizationBase,\n | 'id'\n | 'name'\n | 'slug'\n | 'createdAt'\n | 'updatedAt'\n | 'defaultRoleName'\n | 'dashboardStatus'\n | 'aiFeaturesStatus'\n> &\n // `boolean extends T` is non-distributive — true only when T is the wide\n // `boolean`, in which case the field is optional. Literal `true`/`false`\n // fall through to the strict branch.\n (boolean extends IncludeMembers\n ? {members?: OrganizationMember[]}\n : IncludeMembers extends true\n ? {members: OrganizationMember[]}\n : unknown) &\n (boolean extends IncludeFeatures\n ? {features?: string[]}\n : IncludeFeatures extends true\n ? {features: string[]}\n : unknown))[]\n\n/** @public */\nexport interface OrganizationsOptions<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> extends Omit<OrganizationOptions<IncludeMembers, IncludeFeatures>, 'organizationId'> {\n /**\n * When `true`, includes organisations the user has access to via\n * project-level grants, not just direct organisation memberships.\n */\n includeImplicitMemberships?: boolean\n}\n\nfunction normalizeOrganizationsOptions(options?: OrganizationsOptions<boolean, boolean>) {\n return {\n includeImplicitMemberships: options?.includeImplicitMemberships ?? false,\n includeMembers: options?.includeMembers ?? false,\n includeFeatures: options?.includeFeatures ?? false,\n }\n}\n\n/** @internal */\nexport function getOrganizationsCacheKey(\n _instance: SanityInstance,\n options?: OrganizationsOptions<boolean, boolean>,\n): string {\n const {includeMembers, includeFeatures, includeImplicitMemberships} =\n normalizeOrganizationsOptions(options)\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n const implicitKey = includeImplicitMemberships ? ':implicit' : ''\n return `organizations${membersKey}${featuresKey}${implicitKey}`\n}\n\n/**\n * Fetcher for the current user's organizations (`GET /organizations`), on the\n * shared fetcher cache.\n *\n * @internal\n */\nexport const organizations = defineFetcher<\n [options?: OrganizationsOptions<boolean, boolean>],\n Organizations<boolean, boolean>\n>({\n name: 'organizations',\n getKey: getOrganizationsCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Organizations<boolean, boolean>>({\n url: '/organizations',\n query: buildQuery(normalizeOrganizationsOptions(options)),\n tag: 'organizations.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'organization', id: 'LIST'},\n ...data.map((org) => ({type: 'organization', id: org.id})),\n ],\n})\n","import {type SanityUser as SanityUserFromClient} from '@sanity/client'\nimport {createSelector} from 'reselect'\nimport {\n catchError,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n NEVER,\n Observable,\n of,\n pairwise,\n race,\n skip,\n startWith,\n switchMap,\n tap,\n throwError,\n withLatestFrom,\n} from 'rxjs'\n\nimport {getClient, getClientState} from '../client/clientStore'\nimport {getDashboardOrganizationId} from '../dashboard/messageBus/topicStore'\nimport {bindActionGlobally} from '../store/createActionBinder'\nimport {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'\nimport {type StoreState} from '../store/createStoreState'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {randomId} from '../utils/ids'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {\n addSubscription,\n cancelRequest,\n getUsersKey,\n initializeRequest,\n parseUsersKey,\n removeSubscription,\n setUsersData,\n setUsersError,\n updateLastLoadMoreRequest,\n} from './reducers'\nimport {\n type GetUserOptions,\n type GetUsersOptions,\n type ResolveUserOptions,\n type ResolveUsersOptions,\n type SanityUser,\n type SanityUserResponse,\n type UsersStoreState,\n} from './types'\nimport {API_VERSION, PROJECT_API_VERSION, USERS_STATE_CLEAR_DELAY} from './usersConstants'\n\n/** @internal */\nexport type PatchedSanityUserFromClient = Omit<SanityUserFromClient, 'id'> & {\n id: string\n sanityUserId: string\n email: string\n provider: string\n}\n\n/**\n * The users store resource that manages user data fetching and state.\n *\n * This store handles fetching, caching, and managing user data. It provides functionality for\n * retrieving users associated with specific resources and supports pagination through the\n * `loadMoreUsers` action.\n *\n * @internal\n */\nconst usersStore = defineStore<UsersStoreState>({\n name: 'UsersStore',\n getInitialState: () => ({users: {}}),\n initialize: (context) => {\n const subscription = listenForLoadMoreAndFetch(context)\n return () => subscription.unsubscribe()\n },\n})\n\nconst errorHandler =\n (state: StoreState<{error?: unknown}>) =>\n (error: unknown): void =>\n state.set('setError', {error})\n\n/**\n * Internal action that listens for new user subscriptions and load more requests.\n * Fetches user data when new subscriptions are added or when loadMoreUsers is called.\n */\nconst listenForLoadMoreAndFetch = ({state, instance}: StoreContext<UsersStoreState>) => {\n return state.observable\n .pipe(\n map((s) => new Set(Object.keys(s.users))),\n distinctUntilChanged((curr, next) => {\n if (curr.size !== next.size) return false\n return Array.from(next).every((i) => curr.has(i))\n }),\n startWith(new Set<string>()),\n pairwise(),\n mergeMap(([curr, next]) => {\n const added = Array.from(next).filter((i) => !curr.has(i))\n const removed = Array.from(curr).filter((i) => !next.has(i))\n\n return [\n ...added.map((key) => ({key, added: true})),\n ...removed.map((key) => ({key, added: false})),\n ]\n }),\n groupBy((i) => i.key),\n mergeMap((group$) =>\n group$.pipe(\n switchMap((e) => {\n if (!e.added) return EMPTY\n const {userId, batchSize, ...options} = parseUsersKey(group$.key)\n\n if (userId) {\n // In the future we will be able to fetch a user from the global API using the resourceUserId,\n // but for now we need to use the project subdomain to fetch a user if the userId is a project user (starts with \"p\")\n if (userId.startsWith('p')) {\n const client = getClient(instance, {\n apiVersion: PROJECT_API_VERSION,\n // this is a global store, so we need to use the projectId from the options when we're fetching\n // users from a project subdomain\n projectId: options.projectId,\n useProjectHostname: true,\n })\n\n return client.observable\n .request<PatchedSanityUserFromClient>({\n method: 'GET',\n url: `/users/${userId}`,\n tag: 'users.get',\n })\n .pipe(\n map((user) => {\n // We need to convert the user to the format we expect\n const convertedUser: SanityUser = {\n sanityUserId: user.sanityUserId,\n profile: {\n id: user.id,\n displayName: user.displayName,\n familyName: user.familyName ?? undefined,\n givenName: user.givenName ?? undefined,\n middleName: user.middleName ?? undefined,\n imageUrl: user.imageUrl ?? undefined,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt,\n isCurrentUser: user.isCurrentUser,\n email: user.email,\n provider: user.provider,\n },\n memberships: [],\n }\n return {\n data: [convertedUser],\n totalCount: 1,\n nextCursor: null,\n }\n }),\n catchError((error) => {\n state.set('setUsersError', setUsersError(group$.key, error))\n return EMPTY\n }),\n tap((response) =>\n state.set('setUsersData', setUsersData(group$.key, response)),\n ),\n )\n }\n\n // Fetch the user from the global API\n const scope = userId.startsWith('g') ? 'global' : undefined\n const client = getClient(instance, {\n scope,\n apiVersion: API_VERSION,\n })\n const resourceType = options.resourceType || 'project'\n const resourceId =\n resourceType === 'organization' ? options.organizationId : options.projectId\n if (!resourceId) {\n return throwError(() => new Error('An organizationId or a projectId is required'))\n }\n return client.observable\n .request<SanityUser | SanityUserResponse>({\n method: 'GET',\n url: `access/${resourceType}/${resourceId}/users/${userId}`,\n tag: 'users.get',\n })\n .pipe(\n map((response) => {\n // If the response is a single user object (has sanityUserId), wrap it in the expected format\n if ('sanityUserId' in response) {\n return {\n data: [response],\n totalCount: 1,\n nextCursor: null,\n } as SanityUserResponse\n }\n return response as SanityUserResponse\n }),\n catchError((error) => {\n state.set('setUsersError', setUsersError(group$.key, error))\n return EMPTY\n }),\n tap((response) => state.set('setUsersData', setUsersData(group$.key, response))),\n )\n }\n const projectId = options.projectId\n\n // the resource type this request will use\n // If resourceType is explicitly provided, use it\n // Otherwise, infer from context: organization if organizationId exists,\n // project if projectId exists, or default to organization\n const resourceType =\n options.resourceType ??\n (options.organizationId ? 'organization' : projectId ? 'project' : 'organization')\n\n const organizationId$ = options.organizationId\n ? of(options.organizationId)\n : getDashboardOrganizationId(instance).observable.pipe(\n filter((i) => typeof i === 'string'),\n )\n\n const resource$: Observable<{\n type: 'project' | 'organization'\n id: string\n }> =\n resourceType === 'project'\n ? projectId\n ? of({type: 'project', id: projectId})\n : throwError(() => new Error('Project ID required for this API.'))\n : organizationId$.pipe(map((id) => ({type: 'organization', id})))\n\n const client$ = getClientState(instance, {\n scope: 'global',\n apiVersion: API_VERSION,\n }).observable\n\n const loadMore$ = state.observable.pipe(\n map((s) => s.users[group$.key]?.lastLoadMoreRequest),\n distinctUntilChanged(),\n )\n\n const cursor$ = state.observable.pipe(\n map((s) => s.users[group$.key]?.nextCursor),\n distinctUntilChanged(),\n filter((cursor) => cursor !== null),\n )\n\n return combineLatest([resource$, client$, loadMore$]).pipe(\n withLatestFrom(cursor$),\n switchMap(([[resource, client], cursor]) =>\n client.observable.request<SanityUserResponse>({\n method: 'GET',\n url: `access/${resource.type}/${resource.id}/users`,\n tag: 'users.list',\n query: cursor\n ? {nextCursor: cursor, limit: batchSize.toString()}\n : {limit: batchSize.toString()},\n }),\n ),\n catchError((error) => {\n state.set('setUsersError', setUsersError(group$.key, error))\n return EMPTY\n }),\n tap((response) => state.set('setUsersData', setUsersData(group$.key, response))),\n )\n }),\n ),\n ),\n )\n .subscribe({error: errorHandler(state)})\n}\n\n/**\n * Returns the state source for users associated with a specific resource.\n *\n * This function returns a state source that represents the current list of users for a given\n * resource. Subscribing to the state source will instruct the SDK to fetch the users (if not\n * already fetched) and will load more from this state source as well. When the last subscriber is\n * removed, the users state is automatically cleaned up from the store after a delay.\n *\n * Note: This functionality is for advanced users who want to build their own framework\n * integrations. Our SDK also provides a React integration for convenient usage.\n *\n * @beta\n */\nexport const getUsersState = bindActionGlobally(\n usersStore,\n createStateSourceAction({\n selector: createSelector(\n [\n ({instance, state}: SelectorContext<UsersStoreState>, options?: GetUsersOptions) =>\n state.error ?? state.users[getUsersKey(instance, options)]?.error,\n ({instance, state}: SelectorContext<UsersStoreState>, options: GetUsersOptions) =>\n state.users[getUsersKey(instance, options)]?.users,\n ({instance, state}: SelectorContext<UsersStoreState>, options: GetUsersOptions) =>\n state.users[getUsersKey(instance, options)]?.totalCount,\n ({instance, state}: SelectorContext<UsersStoreState>, options: GetUsersOptions) =>\n state.users[getUsersKey(instance, options)]?.nextCursor,\n ],\n (error, data, totalCount, nextCursor) => {\n if (error) throw error\n if (data === undefined || totalCount === undefined || nextCursor === undefined) {\n return undefined\n }\n\n return {data, totalCount, hasMore: nextCursor !== null}\n },\n ),\n onSubscribe: ({instance, state}, options?: GetUsersOptions) => {\n const subscriptionId = randomId(16)\n const key = getUsersKey(instance, options)\n state.set('addSubscription', addSubscription(subscriptionId, key))\n return () => {\n setCleanupTimeout(\n () => state.set('removeSubscription', removeSubscription(subscriptionId, key)),\n USERS_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Resolves the users for a specific resource without registering a lasting subscriber.\n *\n * This function fetches the users for a given resource and returns a promise that resolves with\n * the users result. Unlike `getUsersState`, which registers subscribers to keep the data live and\n * performs automatic cleanup, `resolveUsers` does not track subscribers. This makes it ideal for\n * use with React Suspense, where the returned promise is thrown to delay rendering until the users\n * result becomes available. Once the promise resolves, it is expected that a real subscriber will\n * be added via `getUsersState` to manage ongoing updates.\n *\n * Additionally, an optional AbortSignal can be provided to cancel the request and immediately\n * clear the associated state if there are no active subscribers.\n *\n * @beta\n */\nexport const resolveUsers = bindActionGlobally(\n usersStore,\n async ({state, instance}, {signal, ...options}: ResolveUsersOptions) => {\n const key = getUsersKey(instance, options)\n const {getCurrent} = getUsersState(instance, options)\n\n const aborted$ = signal\n ? new Observable<never>((observer) => {\n const cleanup = () => {\n signal.removeEventListener('abort', listener)\n }\n\n const listener = () => {\n observer.error(new DOMException('The operation was aborted.', 'AbortError'))\n observer.complete()\n cleanup()\n }\n signal.addEventListener('abort', listener)\n\n return cleanup\n }).pipe(\n catchError((error) => {\n if (error instanceof Error && error.name === 'AbortError') {\n state.set('cancelRequest', cancelRequest(key))\n }\n throw error\n }),\n )\n : NEVER\n\n state.set('initializeRequest', initializeRequest(key))\n\n const resolved$ = state.observable.pipe(\n map(getCurrent),\n first((i) => i !== undefined),\n )\n\n return firstValueFrom(race([resolved$, aborted$]))\n },\n)\n\n/**\n * Loads more users for a specific resource.\n *\n * This function triggers a request to fetch the next page of users for a given resource. It\n * requires that users have already been loaded for the resource (via `resolveUsers` or\n * `getUsersState`), and that there are more users available to load (as indicated by the `hasMore`\n * property).\n *\n * The function returns a promise that resolves when the next page of users has been loaded.\n *\n * @beta\n */\nexport const loadMoreUsers = bindActionGlobally(\n usersStore,\n async ({state, instance}, options?: GetUsersOptions) => {\n const key = getUsersKey(instance, options)\n const users = getUsersState(instance, options)\n const usersState = users.getCurrent()\n if (!usersState) {\n throw new Error('Users not loaded for specified resource. Please call resolveUsers first.')\n }\n\n if (!usersState.hasMore) {\n throw new Error('No more users available to load for this resource.')\n }\n\n const promise = firstValueFrom(\n users.observable.pipe(\n filter((i) => i !== undefined),\n skip(1),\n ),\n )\n\n const timestamp = new Date().toISOString()\n state.set('updateLastLoadMoreRequest', updateLastLoadMoreRequest(timestamp, key))\n\n return await promise\n },\n)\n\n/**\n * @beta\n */\nexport const getUserState = bindActionGlobally(\n usersStore,\n ({instance}, {userId, ...options}: GetUserOptions) => {\n return getUsersState(instance, {userId, ...options}).observable.pipe(\n map((res) => res?.data[0]),\n distinctUntilChanged((a, b) => a?.profile.updatedAt === b?.profile.updatedAt),\n )\n },\n)\n\n/**\n * @beta\n */\nexport const resolveUser = bindActionGlobally(\n usersStore,\n async ({instance}, {signal, ...options}: ResolveUserOptions) => {\n const result = await resolveUsers(instance, {\n signal,\n ...options,\n })\n return result?.data[0]\n },\n)\n","import {type BifurClient, fromUrl} from '@sanity/bifur-client'\nimport {type SanityClient} from '@sanity/client'\nimport {defer, EMPTY, fromEvent, merge, type Observable, timer} from 'rxjs'\nimport {catchError, distinctUntilChanged, map, retry, share, switchMap} from 'rxjs/operators'\n\nimport {\n type BifurTransportOptions,\n type PresenceLocation,\n type PresenceTransport,\n type TransportEvent,\n type TransportMessage,\n} from './types'\n\n/**\n * Longest gap between reconnection attempts, matching the Studio's presence\n * transport so both behave the same way on a flaky connection.\n */\nconst CONNECT_RETRY_MAX_DELAY = 1000 * 240\n\ntype BifurStateMessage = {\n type: 'state'\n i: string\n m: {\n sessionId: string\n locations: PresenceLocation[]\n }\n}\n\ntype BifurDisconnectMessage = {\n type: 'disconnect'\n i: string\n m: {session: string}\n}\n\ntype RollCallEvent = {\n type: 'rollCall'\n i: string\n session: string\n}\n\ntype IncomingBifurEvent = RollCallEvent | BifurStateMessage | BifurDisconnectMessage\n\nfunction getBifurClient(client: SanityClient, token$: Observable<string | null>): BifurClient {\n const bifurVersionedClient = client.withConfig({apiVersion: '2022-06-30'})\n const {\n resource,\n dataset,\n url: baseUrl,\n requestTagPrefix = 'sanity.sdk.presence',\n } = bifurVersionedClient.config()\n\n let resourcePath: string\n if (resource?.type === 'canvas') {\n resourcePath = `canvases/${resource.id}`\n } else if (dataset) {\n // Dataset clients use project hostname — dataset name alone is the socket path\n resourcePath = dataset\n } else {\n throw new Error(`Unable to determine presence URL: no canvas resource or dataset configured`)\n }\n\n const url = `${baseUrl}/socket/${resourcePath}`.replace(/^http/, 'ws')\n\n const urlWithTag = `${url}?tag=${requestTagPrefix}`\n\n return fromUrl(urlWithTag, {token$})\n}\n\nconst handleIncomingMessage = (event: IncomingBifurEvent): TransportEvent => {\n switch (event.type) {\n case 'rollCall':\n return {\n type: 'rollCall',\n userId: event.i,\n sessionId: event.session,\n }\n case 'state': {\n const {sessionId, locations} = event.m\n return {\n type: 'state',\n userId: event.i,\n sessionId,\n timestamp: new Date().toISOString(),\n locations,\n }\n }\n case 'disconnect':\n return {\n type: 'disconnect',\n userId: event.i,\n sessionId: event.m.session,\n timestamp: new Date().toISOString(),\n }\n default: {\n throw new Error(`Got unknown presence event: ${JSON.stringify(event)}`)\n }\n }\n}\n\n/**\n * Emits an incrementing generation number each time the socket becomes live.\n *\n * `@sanity/bifur-client` does not reconnect on its own — it errors the stream\n * when the socket closes. Without the `retry` below, presence stops for the\n * life of the page after the first dropped connection.\n */\nconst createConnections = (bifur: BifurClient): Observable<number> => {\n let generation = 0\n\n return defer(() => {\n const current = ++generation\n // `heartbeats` emits as soon as the socket is open and authorized, then on\n // every server heartbeat and every response. Subscribing to it is also what\n // keeps the socket alive between requests.\n return bifur.heartbeats.pipe(map(() => current))\n }).pipe(\n retry({\n delay: (_error, retryCount) =>\n timer(Math.min(CONNECT_RETRY_MAX_DELAY, 2 ** retryCount * 100)),\n resetOnSuccess: true,\n }),\n // After `retry` so that one operator instance spans reconnects, making each\n // live socket yield exactly one emission.\n distinctUntilChanged(),\n share(),\n )\n}\n\n/**\n * Presence events from everyone else in the room, re-established on every live\n * connection.\n *\n * A socket close errors this stream just as it errors the heartbeats, so without\n * re-subscribing the client would keep announcing itself after a reconnect while\n * never hearing from anyone again. Driving it from `connections$` rather than\n * giving it its own `retry` keeps a single reconnect loop, instead of two\n * independent backoffs racing over one refcounted socket.\n */\nconst createIncomingEvents = (\n bifur: BifurClient,\n connections$: Observable<number>,\n): Observable<TransportEvent> =>\n connections$.pipe(\n switchMap(() =>\n bifur.listen<IncomingBifurEvent>('presence').pipe(\n // Let a dead listener go quietly. The next connection replaces it, and\n // surfacing the error here would tear down the reconnect loop with it.\n catchError(() => EMPTY),\n ),\n ),\n map(handleIncomingMessage),\n share(),\n )\n\n/**\n * Emits when the page is going away. `pagehide` is needed alongside\n * `beforeunload` because iOS Safari and pages entering the back/forward cache\n * never fire `beforeunload`.\n */\nconst createUnload = (): Observable<void> => {\n if (typeof window === 'undefined') return EMPTY\n\n return merge(fromEvent(window, 'beforeunload'), fromEvent(window, 'pagehide')).pipe(\n map(() => undefined),\n )\n}\n\nexport const createBifurTransport = (options: BifurTransportOptions): PresenceTransport => {\n const {client, token$, sessionId} = options\n const bifur = getBifurClient(client, token$)\n\n const connections$ = createConnections(bifur)\n const incomingEvents$ = createIncomingEvents(bifur, connections$)\n\n const dispatchMessage = (message: TransportMessage): Observable<void> => {\n switch (message.type) {\n case 'rollCall':\n return bifur.request('presence_rollcall', {session: sessionId})\n case 'state':\n return bifur.request('presence_announce', {\n data: {locations: message.locations, sessionId},\n })\n case 'disconnect':\n return bifur.request('presence_disconnect', {session: sessionId})\n default: {\n return EMPTY\n }\n }\n }\n\n return [incomingEvents$, dispatchMessage, connections$, createUnload()]\n}\n","import {type IndexTuple, type KeyedSegment, type Path, type PathSegment} from '@sanity/types'\n\n/**\n * Segments are not always strings, despite what `PresenceLocation.path` declares.\n * Array items arrive as `{_key}` and Portable Text spans as a mix of keys and\n * property names, because that is what the Studio sends.\n */\nfunction isKeyedSegment(segment: PathSegment): segment is KeyedSegment {\n return (\n typeof segment === 'object' && segment !== null && !Array.isArray(segment) && '_key' in segment\n )\n}\n\n/**\n * Index tuples (`[from, to]`) only occur in slice selections, which presence never\n * reports. Compared structurally rather than assumed absent.\n */\nfunction isEqualTuple(a: IndexTuple, b: IndexTuple): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index])\n}\n\n/** Compares two path segments, whatever shape they arrive in. */\nfunction isEqualSegment(a: PathSegment, b: PathSegment): boolean {\n if (a === b) return true\n if (isKeyedSegment(a) && isKeyedSegment(b)) return a._key === b._key\n if (Array.isArray(a) && Array.isArray(b)) return isEqualTuple(a, b)\n return false\n}\n\n/**\n * True when `candidate` is at or below `prefix` in the document tree.\n *\n * Equivalent to `startsWith` from the Studio's `@sanity/util/paths`, which is not\n * an SDK dependency.\n *\n * @internal\n */\nexport function startsWithPath(prefix: Path, candidate: Path): boolean {\n if (prefix.length > candidate.length) return false\n return prefix.every((segment, index) => isEqualSegment(segment, candidate[index]))\n}\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\nimport {type Path} from '@sanity/types'\nimport {createSelector} from 'reselect'\nimport {\n auditTime,\n catchError,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n map,\n merge,\n type Observable,\n of,\n Subscription,\n switchMap,\n timer,\n withLatestFrom,\n} from 'rxjs'\n\nimport {getTokenState} from '../auth/authStore'\nimport {getClient} from '../client/clientStore'\nimport {\n type DocumentResource,\n isCanvasResource,\n isDatasetResource,\n isMediaLibraryResource,\n} from '../config/sanityConfig'\nimport {getEditingDocumentId} from '../document/util'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {type SanityUser} from '../users/types'\nimport {getUserState} from '../users/usersStore'\nimport {randomId} from '../utils/ids'\nimport {createLogger} from '../utils/logger'\nimport {createBifurTransport} from './bifurTransport'\nimport {startsWithPath} from './paths'\nimport {\n type DocumentPresence,\n type DocumentPresenceOptions,\n type PresenceLocation,\n type ReportPresenceOptions,\n type TransportEvent,\n type TransportMessage,\n type UserPresence,\n type WirePresenceLocation,\n} from './types'\n\nconst logger = createLogger('presence')\n\n/**\n * Used for the canvas organization lookup only. The socket itself is pinned to\n * `2022-06-30` in `bifurTransport`, because that is the version Bifur compares\n * against when deciding whether a connection may authenticate over RPC rather\n * than requiring a JWT at upgrade. Do not align these two.\n */\nconst PRESENCE_API_VERSION = '2026-03-30'\n\n/**\n * How often the current user re-announces while idle. Matches the Studio so the\n * two agree on how long a quiet peer should be trusted.\n */\nconst REPORT_MIN_INTERVAL = 30_000\n\n/** Collapses a burst of focus changes into a single announcement. */\nconst REPORT_AUDIT_TIME = 200\n\n/**\n * How long a session may go without announcing before it is dropped. Bifur\n * publishes nothing when a peer's socket dies, so a peer that crashes, is\n * force-quit, or loses power would otherwise stay visible forever.\n *\n * Peers re-announce every 30 seconds, so this allows three missed announcements.\n */\nconst SESSION_TTL = 90_000\n\n/** How often to look for sessions that have gone quiet. */\nconst SWEEP_INTERVAL = 15_000\n\ntype PresenceSession = {\n userId: string\n locations: PresenceLocation[]\n /**\n * When we last heard from this session, on our own clock. Used for expiry in\n * preference to the sender's `lastActiveAt`, which is subject to clock skew:\n * a peer whose clock runs slow would otherwise be expired immediately, and one\n * whose clock runs fast would never be expired at all.\n */\n lastSeenAt: number\n}\n\ntype PresenceStoreState = {\n locations: Map<string, PresenceSession>\n users: Record<string, SanityUser | undefined>\n organizationId?: string\n /**\n * Set when the canvas organization lookup fails. Without it, consumers waiting\n * on `organizationId` would wait forever.\n */\n organizationIdError?: unknown\n /**\n * Where this client says it is. `undefined` means the app has never reported,\n * which keeps it silent: reading presence must not make an app start\n * broadcasting. An empty array is different, and means \"here, but not in any\n * particular document\".\n */\n localLocations?: WirePresenceLocation[]\n}\n\nconst getInitialState = (): PresenceStoreState => ({\n locations: new Map<string, PresenceSession>(),\n users: {},\n})\n\n/**\n * Sends a message without letting a failure tear down the stream it was sent on.\n *\n * This matters most for the announce pipeline, which carries the idle heartbeat,\n * roll-call responses, and the re-announce on reconnect. Those share one chain, so\n * an unhandled error from a single rejected RPC would silence this client\n * permanently while it carried on reading presence normally. A dropped socket\n * errors the in-flight request, which is exactly the case where staying alive\n * matters, since the reconnect that follows is what makes us visible again.\n *\n * No retry is needed here. The 30 second heartbeat sends again on its own, and a\n * recovered connection triggers an immediate re-announce.\n */\nconst sendSafely = (\n dispatch: (message: TransportMessage) => Observable<void>,\n message: TransportMessage,\n): Observable<void> =>\n dispatch(message).pipe(\n catchError((error: unknown) => {\n logger.warn('Failed to send presence message', {messageType: message.type, error})\n return EMPTY\n }),\n )\n\n/**\n * Reduces a stream to a bare trigger that cannot fail.\n *\n * The announce pipeline combines three inputs with `merge`, which propagates an\n * error from any one of them. Since that one pipeline carries the idle heartbeat,\n * roll-call responses, and the re-announce on reconnect, a single failing input\n * would silence this client for good. Losing one trigger is survivable; losing\n * all three is not.\n */\nconst asTrigger = (source$: Observable<unknown>, name: string): Observable<void> =>\n source$.pipe(\n map(() => undefined),\n catchError((error: unknown) => {\n logger.error('Presence announce trigger failed', {trigger: name, error})\n return EMPTY\n }),\n )\n\n/** Ignores `lastActiveAt`, which changes on every report even when nothing moved. */\nconst locationKey = ({documentId, path, selection}: WirePresenceLocation) =>\n JSON.stringify([documentId, path, selection ?? null])\n\n/**\n * Compares reported locations by value. Key order within a caller-supplied\n * `selection` could in principle differ and read as a change, which costs one\n * extra announcement and nothing else.\n */\nfunction isEqualLocations(\n a: WirePresenceLocation[] | undefined,\n b: WirePresenceLocation[] | undefined,\n): boolean {\n if (a === b) return true\n if (!a || !b || a.length !== b.length) return false\n return a.every((location, index) => locationKey(location) === locationKey(b[index]))\n}\n\n/** @public */\nexport const presenceStore = defineStore<PresenceStoreState, BoundResourceKey>({\n name: 'presence',\n getInitialState,\n initialize: (context: StoreContext<PresenceStoreState, BoundResourceKey>) => {\n if (isMediaLibraryResource(context.key.resource)) {\n throw new Error('Presence is not supported for media library resources.')\n }\n const subscription = resolvePresenceUsers(context)\n return () => subscription.unsubscribe()\n },\n // Upstream, so a hidden app's session ends rather than staying visible to peers. The\n // disconnect on close stops peers keeping a ghost session for up to 90 seconds.\n upstream: (context: StoreContext<PresenceStoreState, BoundResourceKey>) => {\n const {\n instance,\n state,\n key: {resource},\n } = context\n\n // A fresh id per connection, since peers were told the previous one disconnected,\n // and deliberately not persisted. Reusing an id across\n // page loads would collide whenever a tab inherits another's session\n // storage (`window.open`, or a same-origin iframe), making two live clients\n // filter each other out as self. Stale sessions are handled by the\n // disconnect on unload, with the expiry sweep above as the backstop.\n const sessionId = randomId(16)\n\n // Dataset resources must use the project hostname so the socket URL is project-specific.\n // Canvas resources use the global API endpoint via the resource config.\n const client = isDatasetResource(resource)\n ? getClient(instance, {\n apiVersion: PRESENCE_API_VERSION,\n projectId: resource.projectId,\n dataset: resource.dataset,\n useProjectHostname: true,\n })\n : getClient(instance, {\n apiVersion: PRESENCE_API_VERSION,\n resource,\n })\n\n const token$ = getTokenState(instance).observable.pipe(distinctUntilChanged())\n\n const [incomingEvents$, dispatch, connections$, unload$] = createBifurTransport({\n client,\n token$,\n sessionId,\n })\n\n const subscription = new Subscription()\n\n // Subscribed before the roll call below, deliberately. The transport\n // establishes its listener when this stream is subscribed, and a roll call\n // sent before we are listening would have its answers arrive to no one.\n subscription.add(\n incomingEvents$.subscribe({\n next: (event: TransportEvent) => {\n if ('sessionId' in event && event.sessionId === sessionId) {\n return\n }\n\n if (event.type === 'state') {\n state.set('presence/state', (prevState: PresenceStoreState) => {\n const newLocations = new Map(prevState.locations)\n newLocations.set(event.sessionId, {\n userId: event.userId,\n locations: event.locations,\n lastSeenAt: Date.now(),\n })\n\n return {\n ...prevState,\n locations: newLocations,\n }\n })\n } else if (event.type === 'disconnect') {\n state.set('presence/disconnect', (prevState: PresenceStoreState) => {\n const newLocations = new Map(prevState.locations)\n newLocations.delete(event.sessionId)\n return {...prevState, locations: newLocations}\n })\n }\n },\n // The transport re-establishes its listener on every live connection, so\n // this should not fire. Handled rather than left to become an unhandled\n // error, which is how presence used to die silently and for good.\n error: (error: unknown) => {\n logger.error('Presence event stream failed', {error})\n },\n }),\n )\n\n // Subscribing keeps the socket alive and reconnects it with backoff. Each\n // emission is a freshly live connection: anything we accumulated is now\n // suspect, so start over and ask everyone to announce themselves again.\n subscription.add(\n connections$.subscribe(() => {\n state.set('presence/reset', (prevState) => ({\n ...prevState,\n locations: new Map<string, PresenceSession>(),\n }))\n sendSafely(dispatch, {type: 'rollCall'}).subscribe()\n }),\n )\n\n // Restarted per connection so a long reconnect backoff does not expire\n // everyone while we are offline — reconnecting clears the map anyway.\n //\n // Uses an rxjs timer rather than `setCleanupTimeout`, despite the guidance in\n // core-package-conventions, because this is a recurring sweep and not a\n // cleanup timer. Unref'ing it would not help anyway: a live presence\n // subscription holds an open WebSocket, and that is what keeps a Node process\n // alive. Anything running server-side should not subscribe to presence at all.\n subscription.add(\n connections$.pipe(switchMap(() => timer(SWEEP_INTERVAL, SWEEP_INTERVAL))).subscribe(() => {\n state.set('presence/expire', (prevState) => {\n const cutoff = Date.now() - SESSION_TTL\n const stale = [...prevState.locations].filter(([, s]) => s.lastSeenAt < cutoff)\n if (stale.length === 0) return prevState\n\n const newLocations = new Map(prevState.locations)\n for (const [staleSessionId] of stale) newLocations.delete(staleSessionId)\n return {...prevState, locations: newLocations}\n })\n }),\n )\n\n subscription.add(\n unload$.pipe(switchMap(() => sendSafely(dispatch, {type: 'disconnect'}))).subscribe(),\n )\n\n // Announce where we are. A location change, a peer asking for a roll call, or\n // a reconnect all restart `timer(0, ...)`, so we announce immediately and then\n // every 30s while idle. That idle tick is what tells peers we are still here,\n // and is what their expiry sweep measures against.\n const localLocations$ = state.observable.pipe(\n map((s) => s.localLocations),\n // Compared by value, not by reference. The Studio compares by reference and\n // so never dedupes, because its form rebuilds the array on every call.\n distinctUntilChanged(isEqualLocations),\n )\n\n const rollCallRequests$ = incomingEvents$.pipe(\n // Bifur echoes our own roll call back to us, since we are subscribed to the\n // same topic we published to. Answering it would be a pointless round trip.\n filter((event) => event.type === 'rollCall' && event.sessionId !== sessionId),\n )\n\n subscription.add(\n merge(\n asTrigger(localLocations$, 'locationChange'),\n asTrigger(rollCallRequests$, 'rollCall'),\n asTrigger(connections$, 'connection'),\n )\n .pipe(\n switchMap(() => timer(0, REPORT_MIN_INTERVAL)),\n withLatestFrom(localLocations$),\n map(([, locations]) => locations),\n // Stay silent until the app has reported at least once.\n filter((locations): locations is WirePresenceLocation[] => locations !== undefined),\n auditTime(REPORT_AUDIT_TIME),\n switchMap((locations) => sendSafely(dispatch, {type: 'state', locations})),\n )\n .subscribe(),\n )\n\n return new Subscription(() => {\n sendSafely(dispatch, {type: 'disconnect'}).subscribe()\n subscription.unsubscribe()\n // Nothing expires these while the socket is closed, and the next connection starts over\n state.set('presence/reset', (prevState) => ({\n ...prevState,\n locations: new Map<string, PresenceSession>(),\n }))\n })\n },\n})\n\nfunction resolvePresenceUsers({\n instance,\n state,\n key: {resource},\n}: StoreContext<PresenceStoreState, BoundResourceKey>): Subscription {\n const subscription = new Subscription()\n\n // Resolve display names for everyone we can see.\n //\n // This belongs to the store rather than to one state source: it is driven by\n // which user ids are in state, and every selector needs it. It used to hang off\n // `getPresence`'s `onSubscribe`, which meant anything reading presence another\n // way saw only \"Unknown user\".\n const userIds$ = state.observable.pipe(\n map((s) =>\n Array.from(s.locations.values())\n .map((l) => l.userId)\n .filter((id): id is string => !!id),\n ),\n distinctUntilChanged((a, b) => a.length === b.length && a.every((v, i) => v === b[i])),\n )\n\n // For canvas resources, wait for the organizationId to arrive. A failed lookup\n // resolves to `undefined` rather than never emitting, so one failed request\n // does not leave every user permanently unresolved. Dataset resources emit\n // immediately so the stream is never blocked.\n const organizationId$: Observable<string | undefined> = isCanvasResource(resource)\n ? state.observable.pipe(\n filter((s) => s.organizationId !== undefined || s.organizationIdError !== undefined),\n first(),\n map((s) => s.organizationId),\n )\n : of(undefined)\n\n subscription.add(\n combineLatest([userIds$, organizationId$])\n .pipe(\n switchMap(([userIds, organizationId]) => {\n if (userIds.length === 0) {\n return of([])\n }\n // Without an organization there is nothing to scope the lookup to, so\n // skip the request rather than making one that cannot succeed.\n if (!isDatasetResource(resource) && !organizationId) {\n return of([])\n }\n const userObservables = userIds.map((userId) =>\n getUserState(instance, {\n userId,\n ...(isDatasetResource(resource)\n ? {resourceType: 'project', projectId: resource.projectId}\n : {resourceType: 'organization', organizationId}),\n }).pipe(filter((v): v is NonNullable<typeof v> => !!v)),\n )\n return combineLatest(userObservables)\n }),\n )\n .subscribe((users) => {\n state.set('presence/users', (prevState) => ({\n ...prevState,\n users: {\n ...prevState.users,\n ...users.reduce<Record<string, SanityUser>>((acc, user) => {\n if (user) {\n acc[user.profile.id] = user\n }\n return acc\n }, {}),\n },\n }))\n }),\n )\n\n // Canvas resources need the organizationId to resolve users — fetch it once from the canvas endpoint\n if (isCanvasResource(resource)) {\n const globalClient = getClient(instance, {apiVersion: PRESENCE_API_VERSION})\n subscription.add(\n globalClient.observable\n .request<{organizationId: string}>({\n url: `/canvases/${resource.canvasId}`,\n tag: 'canvases.get',\n })\n .subscribe({\n next: ({organizationId}) => {\n state.set('presence/organizationId', (prev) => ({...prev, organizationId}))\n },\n error: (organizationIdError: unknown) => {\n state.set('presence/organizationIdError', (prev) => ({...prev, organizationIdError}))\n },\n }),\n )\n }\n\n return subscription\n}\n\nconst selectLocations = (state: PresenceStoreState) => state.locations\nconst selectUsers = (state: PresenceStoreState) => state.users\n\n/**\n * Stands in for a participant whose profile has not resolved yet, or cannot be\n * resolved. This must be a structurally valid `SanityUser`: consumers read\n * `user.profile`, and a partial object would throw there on first render.\n */\nconst createUnresolvedUser = (userId: string): SanityUser => ({\n sanityUserId: userId,\n profile: {\n id: userId,\n displayName: 'Unknown user',\n email: '',\n provider: '',\n createdAt: '',\n },\n memberships: [],\n})\n\nconst selectPresence = createSelector(\n selectLocations,\n selectUsers,\n (locations, users): UserPresence[] => {\n return Array.from(locations.entries()).map(([sessionId, {userId, locations: locs}]) => ({\n user: users[userId] || createUnresolvedUser(userId),\n sessionId,\n locations: locs,\n }))\n },\n)\n\n/**\n * Results per state object, then per query.\n *\n * `createStateSourceAction` runs its selector afresh on every `getCurrent()`, and\n * `getCurrent` is what React's `useSyncExternalStore` calls as its snapshot. A\n * selector that builds a new array each call therefore sends React into an\n * infinite render loop, which is why the store docs point at memoized selectors.\n *\n * A single `reselect` memo slot is not enough here, because the selector is\n * parameterised per document and callers watching different documents would\n * evict each other. Keyed on the state object, which is replaced on every change,\n * so entries are collected once that state is unreachable.\n */\nconst documentPresenceCache = new WeakMap<PresenceStoreState, Map<string, DocumentPresence[]>>()\n\n/** Flattens the session map down to one document. */\nfunction selectDocumentPresence(\n state: PresenceStoreState,\n options: DocumentPresenceOptions,\n): DocumentPresence[] {\n const cacheKey = JSON.stringify([\n getEditingDocumentId(options),\n options.path ?? null,\n options.excludeVersions ?? false,\n ])\n\n let perState = documentPresenceCache.get(state)\n if (!perState) {\n perState = new Map<string, DocumentPresence[]>()\n documentPresenceCache.set(state, perState)\n }\n\n const cached = perState.get(cacheKey)\n if (cached) return cached\n\n const computed = computeDocumentPresence(state, options)\n perState.set(cacheKey, computed)\n return computed\n}\n\n/**\n * The id a query and a location are compared on. Exact when `excludeVersions` is\n * set, otherwise normalized so a draft, its published version, and any release\n * version count as one document.\n */\nconst scopeId = (documentId: string, excludeVersions: boolean | undefined): string =>\n excludeVersions ? documentId : getPublishedId(DocumentId(documentId))\n\n/**\n * Declared `string[]`, actually a `Path`. See the note on\n * {@link PresenceLocation.path}; `DocumentPresence.path` reports it honestly.\n */\nconst pathOf = (location: PresenceLocation): Path => location.path as unknown as Path\n\nfunction matchesQuery(\n location: PresenceLocation,\n target: string,\n {path, excludeVersions}: DocumentPresenceOptions,\n): boolean {\n if (scopeId(location.documentId, excludeVersions) !== target) return false\n return !path || startsWithPath(path, pathOf(location))\n}\n\nfunction toDocumentPresence(\n users: PresenceStoreState['users'],\n sessionId: string,\n userId: string,\n location: PresenceLocation,\n): DocumentPresence {\n return {\n user: users[userId] || createUnresolvedUser(userId),\n sessionId,\n documentId: location.documentId,\n path: pathOf(location),\n lastActiveAt: location.lastActiveAt,\n ...(location.selection === undefined ? {} : {selection: location.selection}),\n }\n}\n\nfunction computeDocumentPresence(\n state: PresenceStoreState,\n options: DocumentPresenceOptions,\n): DocumentPresence[] {\n // Resolved before scoping, and identically to the write side, or a query for a\n // document would not match the client reporting it.\n const target = scopeId(getEditingDocumentId(options), options.excludeVersions)\n\n return Array.from(state.locations).flatMap(([sessionId, session]) =>\n session.locations\n .filter((location) => matchesQuery(location, target, options))\n .map((location) => toDocumentPresence(state.users, sessionId, session.userId, location)),\n )\n}\n\nconst _getPresence = bindActionByResource(\n presenceStore,\n createStateSourceAction({\n selector: (context: SelectorContext<PresenceStoreState>): UserPresence[] =>\n selectPresence(context.state),\n }),\n)\n\n/** @beta */\nexport function getPresence(\n instance: SanityInstance,\n params?: {resource?: DocumentResource},\n): StateSource<UserPresence[]> {\n // bit of a hack to support the old bound action by dataset\n // in reality, this will always be passed a resource\n return _getPresence(instance, params ?? {})\n}\n\nconst _getDocumentPresence = bindActionByResource(\n presenceStore,\n createStateSourceAction({\n selector: (\n {state}: SelectorContext<PresenceStoreState>,\n options: {resource?: DocumentResource} & DocumentPresenceOptions,\n ): DocumentPresence[] => selectDocumentPresence(state, options),\n }),\n)\n\n/**\n * Presence within a single document, flattened to one entry per participant per\n * location so it can be rendered directly against a field.\n *\n * Reading presence never announces anything. Use `reportPresence` to make the\n * current user visible to others.\n *\n * @param instance - the Sanity instance\n * @param params - the document to look at, its perspective, an optional `path` to\n * narrow to a field subtree, and `excludeVersions` to compare document ids exactly\n *\n * @beta\n */\nexport function getDocumentPresence(\n instance: SanityInstance,\n params: {resource?: DocumentResource} & DocumentPresenceOptions,\n): StateSource<DocumentPresence[]> {\n return _getDocumentPresence(instance, params)\n}\n\nconst _reportPresence = bindActionByResource(\n presenceStore,\n (\n {state}: StoreContext<PresenceStoreState, BoundResourceKey>,\n {locations}: {resource?: DocumentResource; locations: ReportPresenceOptions[]},\n ) => {\n const lastActiveAt = new Date().toISOString()\n const wireLocations: WirePresenceLocation[] = locations.map((location) => ({\n type: 'document',\n // The specific document being edited, not the canonical id. Other clients,\n // the Studio included, compare exact ids for field-level presence.\n documentId: getEditingDocumentId(location),\n path: location.path ?? [],\n lastActiveAt,\n ...(location.selection === undefined ? {} : {selection: location.selection}),\n }))\n\n state.set('presence/report', (prevState) => ({...prevState, localLocations: wireLocations}))\n },\n)\n\n/**\n * Announces where the current user is, so other clients in the same project and\n * dataset can show them. Reading presence never announces anything, so this is\n * the only way an app becomes visible to others.\n *\n * Call it again whenever the user moves. Announcements are collapsed over a short\n * window and then repeated every 30 seconds while idle, which is what tells peers\n * the session is still alive.\n *\n * Announcements only go out while the presence connection is open, which is\n * while something subscribes to `getPresence` or `getDocumentPresence`. Keep a\n * subscription for as long as the user should appear present.\n *\n * Which specific document each location resolves to depends on its perspective, and\n * that matters for interoperability: other clients, the Studio included, compare\n * exact document ids for field-level presence. Pass the perspective you are editing\n * under rather than building a draft or version id yourself.\n *\n * @param instance - the Sanity instance\n * @param params - the resource to announce on, plus the locations to report.\n * Pass one location with a `documentId` for document-level presence, add a\n * `path` for field-level presence, and pass an empty array to appear present\n * without being in any document.\n *\n * @beta\n */\nexport function reportPresence(\n instance: SanityInstance,\n params: {resource?: DocumentResource; locations: ReportPresenceOptions[]},\n): void {\n return _reportPresence(instance, params)\n}\n","export function hashString(str: string): string {\n // Using a large prime number for the hash\n const PRIME = 31\n // Using a max 32-bit integer to prevent overflow\n const MOD = 2147483647\n\n let hash = 0\n\n // Process chunks of the string to reduce complexity\n for (let i = 0; i < str.length; i++) {\n // Rolling hash computation\n hash = (hash * PRIME + str.charCodeAt(i)) % MOD\n }\n\n // Ensure we return a positive hash\n return Math.abs(hash).toString(16).padStart(8, '0')\n}\n","export const PROJECTION_TAG = 'projection'\nexport const PROJECTION_STATE_CLEAR_DELAY = 1000\n\nexport const STABLE_EMPTY_PROJECTION = {\n data: null,\n isPending: false,\n}\n\nexport function validateProjection(projection: string): string {\n if (!projection.startsWith('{') || !projection.endsWith('}')) {\n throw new Error(\n `Invalid projection format: \"${projection}\". Projections must be enclosed in curly braces, e.g. \"{title, 'author': author.name}\"`,\n )\n }\n return projection\n}\n","import {type ClientPerspective} from '@sanity/client'\nimport {DocumentId, getPublishedId} from '@sanity/id-utils'\n\nimport {type ReleasePerspective} from '../config/sanityConfig'\nimport {\n type DocumentProjections,\n type DocumentProjectionValues,\n type ProjectionStoreState,\n} from './types'\nimport {validateProjection} from './util'\n\nexport type ProjectionQueryResult = {\n _id: string\n _type: string\n _updatedAt: string\n result: Record<string, unknown>\n __projectionHash: string\n}\n\ninterface CreateProjectionQueryResult {\n query: string\n params: Record<string, unknown>\n}\n\ntype ProjectionMap = Record<string, {projection: string; documentIds: Set<string>}>\n\nexport function createProjectionQuery(\n documentIds: Set<DocumentId>,\n documentProjections: {[TDocumentId in DocumentId]?: DocumentProjections},\n): CreateProjectionQueryResult {\n const projections = Array.from(documentIds)\n .flatMap((id) => {\n const projectionsForDoc = documentProjections[id]\n if (!projectionsForDoc) return []\n\n return Object.entries(projectionsForDoc).map(([projectionHash, projection]) => ({\n documentId: id,\n projection: validateProjection(projection),\n projectionHash,\n }))\n })\n .reduce<ProjectionMap>((acc, {documentId, projection, projectionHash}) => {\n const obj = acc[projectionHash] ?? {documentIds: new Set(), projection}\n obj.documentIds.add(documentId)\n\n acc[projectionHash] = obj\n return acc\n }, {})\n\n const query = `[${Object.entries(projections)\n .map(([projectionHash, {projection}]) => {\n return `...*[_id in $__ids_${projectionHash}]{_id,_type,_updatedAt,\"__projectionHash\":\"${projectionHash}\",\"result\":{...${projection}}}`\n })\n .join(',')}]`\n\n const params = Object.fromEntries(\n Object.entries(projections).map(([projectionHash, value]) => {\n const idsInProjection = Array.from(value.documentIds).flatMap((id) => DocumentId(id))\n return [`__ids_${projectionHash}`, Array.from(idsInProjection)]\n }),\n )\n\n return {query, params}\n}\n\ninterface ProcessProjectionQueryOptions {\n ids: Set<string>\n results: ProjectionQueryResult[]\n documentStatuses?: ProjectionStoreState['documentStatuses']\n perspective: ClientPerspective | ReleasePerspective\n}\n\nexport function processProjectionQuery({\n ids,\n results,\n documentStatuses,\n}: ProcessProjectionQueryOptions): {\n [TDocumentId in string]?: DocumentProjectionValues<Record<string, unknown>>\n} {\n const groupedResults: {\n [docId: string]: {\n [hash: string]: ProjectionQueryResult | undefined\n }\n } = {}\n\n for (const result of results) {\n const originalId = getPublishedId(DocumentId(result._id))\n const hash = result.__projectionHash\n\n if (!ids.has(originalId)) continue\n\n if (!groupedResults[originalId]) {\n groupedResults[originalId] = {}\n }\n if (!groupedResults[originalId][hash]) {\n groupedResults[originalId][hash] = undefined\n }\n\n groupedResults[originalId][hash] = result\n }\n\n const finalValues: {\n [docId: string]: DocumentProjectionValues<Record<string, unknown>>\n } = {}\n\n for (const originalId of ids) {\n finalValues[originalId] = {}\n\n const projectionsForDoc = groupedResults[originalId]\n if (!projectionsForDoc) continue\n\n for (const hash in projectionsForDoc) {\n const projectionResult = projectionsForDoc[hash]\n const projectionResultData = projectionResult?.result\n\n if (!projectionResultData) {\n finalValues[originalId][hash] = {data: null, isPending: false}\n continue\n }\n\n const statusFromStore = documentStatuses?.[originalId]\n\n finalValues[originalId][hash] = {\n data: {...projectionResultData, _status: statusFromStore},\n isPending: false,\n }\n }\n }\n\n return finalValues\n}\n","import {\n DocumentId,\n getDraftId,\n getPublishedId,\n getVersionId,\n isDraftId,\n isPublishedId,\n isVersionId,\n} from '@sanity/id-utils'\n\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type BoundPerspectiveKey} from '../store/createActionBinder'\nimport {type ProjectionStoreState} from './types'\n\ninterface StatusQueryResult {\n _id: string\n _updatedAt: string\n}\n\n/**\n * Builds an array of document IDs to query for status information using the \"raw\" perspective.\n * Includes draft, published, and version IDs as needed.\n */\nexport function buildStatusQueryIds(\n documentIds: Set<string>,\n perspective: BoundPerspectiveKey['perspective'],\n): string[] {\n const ids: string[] = []\n const releaseName = isReleasePerspective(perspective) ? perspective.releaseName : null\n\n for (const id of documentIds) {\n const publishedId = getPublishedId(DocumentId(id))\n const draftId = getDraftId(publishedId)\n\n // Always query for draft and published versions\n ids.push(draftId, publishedId)\n\n // If it's a release perspective, also query for the version\n if (releaseName) {\n ids.push(getVersionId(publishedId, releaseName))\n }\n }\n\n return ids\n}\n\n/**\n * Processes status query results into documentStatuses (same shape as _status returned to users).\n */\nexport function processStatusQueryResults(\n results: StatusQueryResult[],\n): ProjectionStoreState['documentStatuses'] {\n const documentStatuses: ProjectionStoreState['documentStatuses'] = {}\n\n for (const result of results) {\n const id = DocumentId(result._id)\n const updatedAt = result._updatedAt\n const publishedId = getPublishedId(id)\n const statusData = documentStatuses[publishedId] ?? {}\n if (isDraftId(id)) {\n statusData.lastEditedDraftAt = updatedAt\n } else if (isVersionId(id)) {\n statusData.lastEditedVersionAt = updatedAt\n } else if (isPublishedId(id)) {\n statusData.lastEditedPublishedAt = updatedAt\n }\n documentStatuses[publishedId] = statusData\n }\n\n return documentStatuses\n}\n","import {DocumentId} from '@sanity/id-utils'\nimport {\n combineLatest,\n debounceTime,\n defer,\n distinctUntilChanged,\n EMPTY,\n filter,\n from,\n map,\n Observable,\n pairwise,\n startWith,\n Subscription,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {getQueryState, resolveQuery} from '../query/queryStore'\nimport {type BoundPerspectiveKey} from '../store/createActionBinder'\nimport {type StoreContext} from '../store/defineStore'\nimport {isDeepEqual} from '../utils/object'\nimport {\n createProjectionQuery,\n processProjectionQuery,\n type ProjectionQueryResult,\n} from './projectionQuery'\nimport {buildStatusQueryIds, processStatusQueryResults} from './statusQuery'\nimport {type ProjectionStoreState} from './types'\nimport {PROJECTION_TAG} from './util'\n\nconst BATCH_DEBOUNCE_TIME = 50\n\nconst isSetEqual = <T>(a: Set<T>, b: Set<T>) =>\n a.size === b.size && Array.from(a).every((i) => b.has(i))\n\ninterface StatusQueryResult {\n _id: string\n _updatedAt: string\n}\n\nexport const subscribeToStateAndFetchBatches = ({\n state,\n instance,\n key: {resource, perspective},\n}: StoreContext<ProjectionStoreState, BoundPerspectiveKey>): Subscription => {\n const documentProjections$ = state.observable.pipe(\n map((s) => s.documentProjections),\n distinctUntilChanged(isDeepEqual),\n )\n\n const activeDocumentIds$ = state.observable.pipe(\n map(({subscriptions}) => new Set(Object.keys(subscriptions).map((id) => DocumentId(id)))),\n distinctUntilChanged(isSetEqual),\n )\n\n const pendingUpdateSubscription = activeDocumentIds$\n .pipe(\n debounceTime(BATCH_DEBOUNCE_TIME),\n startWith(new Set<DocumentId>()),\n pairwise(),\n tap(([prevIds, currIds]) => {\n const newIds = [...currIds].filter((id) => !prevIds.has(id))\n if (newIds.length === 0) return\n\n state.set('updatingPending', (prev) => {\n const nextValues = {...prev.values}\n for (const id of newIds) {\n const projectionsForDoc = prev.documentProjections[id]\n if (!projectionsForDoc) continue\n\n const currentValuesForDoc = prev.values[id] ?? {}\n const updatedValuesForDoc = {...currentValuesForDoc}\n\n for (const hash in projectionsForDoc) {\n const currentValue = updatedValuesForDoc[hash]\n updatedValuesForDoc[hash] = {\n data: currentValue?.data ?? null,\n isPending: true,\n }\n }\n nextValues[id] = updatedValuesForDoc\n }\n return {values: nextValues}\n })\n }),\n )\n .subscribe()\n\n const queryTrigger$ = combineLatest([activeDocumentIds$, documentProjections$]).pipe(\n debounceTime(BATCH_DEBOUNCE_TIME),\n distinctUntilChanged(isDeepEqual),\n )\n\n const queryExecutionSubscription = queryTrigger$\n .pipe(\n switchMap(([ids, documentProjections]) => {\n if (!ids.size) return EMPTY\n const {query, params} = createProjectionQuery(ids, documentProjections)\n const controller = new AbortController()\n\n // Build status query IDs and query\n const statusQueryIds = buildStatusQueryIds(ids, perspective)\n const statusQuery = `*[_id in $statusIds]{_id, _updatedAt}`\n const statusParams = {statusIds: statusQueryIds}\n\n const projectionQuery$ = new Observable<ProjectionQueryResult[]>((observer) => {\n const {getCurrent, observable} = getQueryState<ProjectionQueryResult[]>(instance, {\n ...{\n query,\n params,\n tag: PROJECTION_TAG,\n perspective,\n },\n resource,\n })\n\n const querySource$ = defer(() => {\n if (getCurrent() === undefined) {\n return from(\n resolveQuery<ProjectionQueryResult[]>(instance, {\n ...{\n query,\n params,\n tag: PROJECTION_TAG,\n signal: controller.signal,\n perspective,\n },\n resource,\n }),\n ).pipe(switchMap(() => observable))\n }\n return observable\n }).pipe(filter((result): result is ProjectionQueryResult[] => result !== undefined))\n\n const subscription = querySource$.subscribe(observer)\n\n return () => {\n if (!controller.signal.aborted) {\n controller.abort()\n }\n subscription.unsubscribe()\n }\n })\n\n const statusQuery$ = new Observable<StatusQueryResult[]>((observer) => {\n const {getCurrent, observable} = getQueryState<StatusQueryResult[]>(instance, {\n ...{\n query: statusQuery,\n params: statusParams,\n tag: PROJECTION_TAG,\n perspective: 'raw',\n },\n resource,\n })\n\n const statusQuerySource$ = defer(() => {\n if (getCurrent() === undefined) {\n return from(\n resolveQuery<StatusQueryResult[]>(instance, {\n ...{\n query: statusQuery,\n params: statusParams,\n tag: PROJECTION_TAG,\n signal: controller.signal,\n perspective: 'raw',\n },\n resource,\n }),\n ).pipe(switchMap(() => observable))\n }\n return observable\n }).pipe(filter((result): result is StatusQueryResult[] => result !== undefined))\n\n const subscription = statusQuerySource$.subscribe(observer)\n\n return () => {\n subscription.unsubscribe()\n }\n })\n\n // Combine both streams: emit whenever either has a new value (after both have\n // emitted at least one defined value). This keeps reacting to query store updates.\n return combineLatest([projectionQuery$, statusQuery$]).pipe(\n filter(\n (pair): pair is [ProjectionQueryResult[], StatusQueryResult[]] =>\n pair[0] !== undefined && pair[1] !== undefined,\n ),\n map(([projection, status]) => ({\n data: projection,\n ids,\n statusResults: status,\n })),\n )\n }),\n map(({ids, data, statusResults}) => {\n // Process status results into documentStatuses (same shape as _status returned to users)\n const documentStatuses = processStatusQueryResults(statusResults)\n state.set('updateStatuses', (prev) => ({\n documentStatuses: {\n ...prev.documentStatuses,\n ...documentStatuses,\n },\n }))\n\n // Assemble projection values with _status (buildStatusForDocument in statusQuery)\n const currentState = state.get()\n return processProjectionQuery({\n ids,\n results: data,\n documentStatuses: currentState.documentStatuses,\n perspective: perspective,\n })\n }),\n )\n .subscribe({\n next: (processedValues) => {\n state.set('updateResult', (prev) => {\n const nextValues = {...prev.values}\n for (const docId in processedValues) {\n if (processedValues[docId]) {\n nextValues[docId] = {\n ...(prev.values[docId] ?? {}),\n ...processedValues[docId],\n }\n }\n }\n return {values: nextValues}\n })\n },\n error: (err) => {\n // eslint-disable-next-line no-console\n console.error('Error fetching projection batches:', err)\n // TODO: Potentially update state to reflect error state for affected projections?\n },\n })\n\n return new Subscription(() => {\n pendingUpdateSubscription.unsubscribe()\n queryExecutionSubscription.unsubscribe()\n })\n}\n","import {type BoundPerspectiveKey} from '../store/createActionBinder'\nimport {defineStore} from '../store/defineStore'\nimport {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'\nimport {type ProjectionStoreState} from './types'\n\nexport const projectionStore = defineStore<ProjectionStoreState, BoundPerspectiveKey>({\n name: 'Projection',\n getInitialState() {\n return {\n values: {},\n documentProjections: {},\n subscriptions: {},\n documentStatuses: {},\n }\n },\n initialize(context) {\n const batchSubscription = subscribeToStateAndFetchBatches(context)\n return () => batchSubscription.unsubscribe()\n },\n})\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {bindActionByResourceAndPerspective} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {type ResolveProjectionResult} from '../typegen/resolve'\nimport {hashString} from '../utils/hashString'\nimport {randomId} from '../utils/ids'\nimport {omitProperty} from '../utils/object'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {projectionStore} from './projectionStore'\nimport {type ProjectionStoreState, type ProjectionValuePending} from './types'\nimport {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION, validateProjection} from './util'\n\nexport interface ProjectionOptions<\n TProjection extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n projection: TProjection\n}\n\n/**\n * @beta\n */\nexport function getProjectionState<\n TProjection extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,\n): StateSource<\n | ProjectionValuePending<\n ResolveProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>\n >\n | undefined\n>\n\n/**\n * @beta\n */\nexport function getProjectionState<TData extends object>(\n instance: SanityInstance,\n options: ProjectionOptions,\n): StateSource<ProjectionValuePending<TData> | undefined>\n\n/**\n * @beta\n */\nexport function getProjectionState(\n instance: SanityInstance,\n options: ProjectionOptions,\n): StateSource<ProjectionValuePending<Record<string, unknown>> | undefined>\n\n/**\n * @beta\n */\nexport function getProjectionState(\n ...args: Parameters<typeof _getProjectionState>\n): ReturnType<typeof _getProjectionState> {\n return _getProjectionState(...args)\n}\n\n/**\n * @beta\n */\nexport const _getProjectionState = bindActionByResourceAndPerspective(\n projectionStore,\n createStateSourceAction({\n selector: (\n {state}: SelectorContext<ProjectionStoreState>,\n options: ProjectionOptions<string, string, string, string>,\n ): ProjectionValuePending<object> | undefined => {\n const documentId = getPublishedId(DocumentId(options.documentId))\n const projectionHash = hashString(options.projection)\n return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION\n },\n onSubscribe: ({state}, options: ProjectionOptions<string, string, string, string>) => {\n const {projection, ...docHandle} = options\n const subscriptionId = randomId(16)\n const documentId = getPublishedId(DocumentId(docHandle.documentId))\n const validProjection = validateProjection(projection)\n const projectionHash = hashString(validProjection)\n\n state.set('addSubscription', (prev) => ({\n documentProjections: {\n ...prev.documentProjections,\n [documentId]: {\n ...prev.documentProjections[documentId],\n [projectionHash]: validProjection,\n },\n },\n subscriptions: {\n ...prev.subscriptions,\n [documentId]: {\n ...prev.subscriptions[documentId],\n [projectionHash]: {\n ...prev.subscriptions[documentId]?.[projectionHash],\n [subscriptionId]: true,\n },\n },\n },\n }))\n\n return () => {\n setCleanupTimeout(() => {\n state.set('removeSubscription', (prev): Partial<ProjectionStoreState> => {\n const documentSubscriptionsForHash = omitProperty(\n prev.subscriptions[documentId]?.[projectionHash],\n subscriptionId,\n )\n const hasSubscribersForProjection = !!Object.keys(documentSubscriptionsForHash).length\n\n const nextSubscriptions = {...prev.subscriptions}\n const nextDocumentProjections = {...prev.documentProjections}\n const nextValues = {...prev.values}\n\n // clean up the subscription and documentProjection if there are no subscribers\n if (!hasSubscribersForProjection) {\n delete nextSubscriptions[documentId]![projectionHash]\n delete nextDocumentProjections[documentId]![projectionHash]\n\n const currentProjectionValue = prev.values[documentId]?.[projectionHash]\n if (currentProjectionValue && nextValues[documentId]) {\n nextValues[documentId]![projectionHash] = {\n data: currentProjectionValue.data,\n isPending: false,\n }\n }\n } else {\n if (nextSubscriptions[documentId]) {\n nextSubscriptions[documentId]![projectionHash] = documentSubscriptionsForHash\n }\n }\n\n const hasAnySubscribersForDocument = Object.values(\n nextSubscriptions[documentId] ?? {},\n ).some((subs) => Object.keys(subs).length > 0)\n\n if (!hasAnySubscribersForDocument) {\n delete nextSubscriptions[documentId]\n delete nextDocumentProjections[documentId]\n // Keep nextValues[documentId] as cache\n }\n\n return {\n subscriptions: nextSubscriptions,\n documentProjections: nextDocumentProjections,\n values: nextValues,\n }\n })\n }, PROJECTION_STATE_CLEAR_DELAY)\n }\n },\n }),\n)\n","import {filter, firstValueFrom} from 'rxjs'\n\nimport {bindActionByResourceAndPerspective} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type ResolveProjectionResult} from '../typegen/resolve'\nimport {getProjectionState, type ProjectionOptions} from './getProjectionState'\nimport {projectionStore} from './projectionStore'\nimport {type ProjectionValuePending} from './types'\n\n/** @beta */\nexport function resolveProjection<\n TProjection extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,\n): Promise<\n ProjectionValuePending<\n ResolveProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>\n >\n>\n\n/** @beta */\nexport function resolveProjection<TData extends object>(\n instance: SanityInstance,\n options: ProjectionOptions,\n): Promise<ProjectionValuePending<TData>>\n\n/** @beta */\nexport function resolveProjection(\n ...args: Parameters<typeof _resolveProjection>\n): ReturnType<typeof _resolveProjection> {\n return _resolveProjection(...args)\n}\n\n/**\n * @beta\n */\nconst _resolveProjection = bindActionByResourceAndPerspective(\n projectionStore,\n (\n {instance}: {instance: SanityInstance},\n options: ProjectionOptions,\n ): Promise<ProjectionValuePending<Record<string, unknown>>> =>\n firstValueFrom(\n getProjectionState<Record<string, unknown>>(instance, options).observable.pipe(\n filter((state): state is ProjectionValuePending<Record<string, unknown>> => !!state?.data),\n ),\n ),\n)\n","/**\n * Marks a projection for Typegen and returns the projection string unchanged, with its literal\n * type. `useDocumentProjection` matches that literal against the generated projection types.\n *\n * Pass the document type first so Typegen evaluates the projection for that type only. A\n * projection passed alone is evaluated for every document type in the schema, which generates\n * a result type for each.\n *\n * @example\n * ```ts\n * const preview = defineProjection('book', '{title, \"author\": author->name}')\n * ```\n *\n * @beta\n */\nexport function defineProjection<const TProjection extends string>(\n documentType: string,\n projection: TProjection,\n): TProjection\n/**\n * Marks a projection for Typegen and returns it unchanged. Typegen evaluates a projection\n * passed alone for every document type in the schema; pass the document type first to limit it\n * to one.\n *\n * @beta\n */\nexport function defineProjection<const TProjection extends string>(\n projection: TProjection,\n): TProjection\nexport function defineProjection(first: string, second?: string): string {\n return second ?? first\n}\n","import {CorsOriginError} from '@sanity/client'\n\n/**\n * @public\n * Extracts the project ID from a CorsOriginError message.\n * @param error - The error to extract the project ID from.\n * @returns The project ID or null if the error is not a CorsOriginError.\n */\nexport function getCorsErrorProjectId(error: Error): string | null {\n if (!(error instanceof CorsOriginError)) return null\n\n const message = (error as unknown as {message?: string}).message || ''\n const projMatch = message.match(/manage\\/project\\/([^/?#]+)/)\n return projMatch ? projMatch[1] : null\n}\n","/**\n * Returns true when the given error looks like a dynamic-import or\n * code-split chunk-loading failure.\n *\n * These errors typically surface when a user has a tab open against a\n * previously-deployed version of an app and the JavaScript or CSS chunk\n * filenames have since changed: a fresh deployment removes the hashed assets\n * the open tab still references. Detecting them lets the SDK trigger an\n * automatic reload so the user gets the new build without manual intervention.\n *\n * Recognized shapes (webpack ChunkLoadError, Vite \"Failed to fetch\n * dynamically imported module\", Firefox \"error loading dynamically imported\n * module\", Safari \"Importing a module script failed\", and Vite \"Unable to\n * preload CSS\").\n *\n * @param error - The value to inspect. Anything that is not an Error\n * instance returns false.\n * @returns True if the error matches a known import/chunk-load failure.\n *\n * @public\n */\nexport function isImportError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n if (error.name === 'ChunkLoadError') return true\n\n const message = error.message || ''\n return (\n /Loading chunk [\\w-]+ failed/i.test(message) ||\n /Failed to fetch dynamically imported module/i.test(message) ||\n /error loading dynamically imported module/i.test(message) ||\n /Import(?:ing)? a module script failed/i.test(message) ||\n /Unable to preload CSS/i.test(message)\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["API_VERSION","serializeInclude","API_VERSION","API_VERSION","API_VERSION","API_VERSION","API_VERSION","resolveProjectId","API_VERSION","API_VERSION","getIndexForKey","jsonMatch","slicePath","stringifyPath","mergeMap","jsonMatch","stringifyPath","API_VERSION","jsonMatch","API_VERSION","map","retry","distinctUntilChanged","share","switchMap","catchError","\"_createdAt\"","\"_updatedAt\"","\"state\""],"sources":["../src/store/createSanityInstance.ts","../src/utils/buildQuery.ts","../src/applications/applications.ts","../src/installations/installations.ts","../src/organization/organization.ts","../src/access/checkPermissions.ts","../src/project/organizationVerification.ts","../src/projects/projects.ts","../src/project/project.ts","../src/auth/getOrganizationVerificationState.ts","../src/auth/oauth/pkce.ts","../src/auth/oauth/oauthActions.ts","../src/auth/handleAuthCallback.ts","../src/auth/logout.ts","../src/comments/weakenReferences.ts","../src/comments/commentActions.ts","../src/config/handles.ts","../src/config/loggingConfig.ts","../src/datasets/datasets.ts","../src/document/util.ts","../src/document/actions.ts","../src/document/documentConstants.ts","../src/document/processActions/releaseUtil.ts","../src/document/patchOperations.ts","../src/document/processMutations.ts","../src/document/listen.ts","../src/utils/MultiKeyWeakMap.ts","../src/document/processActions/shared.ts","../src/document/permissions.ts","../src/document/processActions/create.ts","../src/document/processActions/delete.ts","../src/document/processActions/discard.ts","../src/document/processActions/edit.ts","../src/document/processActions/publish.ts","../src/document/processActions/releaseArchive.ts","../src/document/processActions/releaseCreate.ts","../src/document/processActions/releaseDelete.ts","../src/document/processActions/releaseEdit.ts","../src/document/processActions/releasePublish.ts","../src/document/processActions/releaseSchedule.ts","../src/document/processActions/unpublish.ts","../src/document/processActions/processActions.ts","../src/document/reducers.ts","../src/document/events.ts","../src/document/listenerEventOperators.ts","../src/document/sharedListener.ts","../src/document/submissionErrors.ts","../src/document/documentStore.ts","../src/document/resourceRules.ts","../src/document/applyDocumentActions.ts","../src/organizations/organizations.ts","../src/users/usersStore.ts","../src/presence/bifurTransport.ts","../src/presence/paths.ts","../src/presence/presenceStore.ts","../src/utils/hashString.ts","../src/projection/util.ts","../src/projection/projectionQuery.ts","../src/projection/statusQuery.ts","../src/projection/subscribeToStateAndFetchBatches.ts","../src/projection/projectionStore.ts","../src/projection/getProjectionState.ts","../src/projection/resolveProjection.ts","../src/typegen/defineProjection.ts","../src/utils/getCorsErrorProjectId.ts","../src/utils/isImportError.ts"],"sourcesContent":["import {type SanityConfig} from '../config/sanityConfig'\nimport {randomId} from '../utils/ids'\nimport {createLogger, type InstanceContext} from '../utils/logger'\n\n/**\n * Represents a Sanity.io resource instance with its own configuration and lifecycle\n *\n * @public\n */\nexport interface SanityInstance {\n /**\n * Unique identifier for this instance\n * @remarks Generated as a random 16-character alphanumeric ID\n */\n readonly instanceId: string\n\n /**\n * Resolved configuration for this instance\n */\n readonly config: SanityConfig\n\n /**\n * Checks if the instance has been disposed\n * @returns true if dispose() has been called\n */\n isDisposed(): boolean\n\n /**\n * Disposes the instance and cleans up associated resources\n * @remarks Triggers all registered onDispose callbacks\n */\n dispose(): void\n\n /**\n * Registers a callback to be invoked when the instance is disposed\n * @param cb - Callback to execute on disposal\n * @returns Function to unsubscribe the callback\n */\n onDispose(cb: () => void): () => void\n}\n\n/**\n * Creates a new Sanity resource instance\n * @param config - Configuration for the instance (optional)\n * @returns A configured SanityInstance\n *\n * @public\n */\nexport function createSanityInstance(config: SanityConfig = {}): SanityInstance {\n const instanceId = randomId(16)\n const disposeListeners = new Map<string, () => void>()\n const disposed = {current: false}\n\n // Create instance context for logging\n const instanceContext: InstanceContext = {\n instanceId,\n projectId: config.projectId,\n dataset: config.dataset,\n }\n\n // Create logger with instance context\n const logger = createLogger('sdk', {instanceContext})\n\n // Log instance creation\n logger.info('Sanity instance created', {\n hasProjectId: !!config.projectId,\n hasDataset: !!config.dataset,\n hasAuth: !!config.auth,\n hasPerspective: !!config.perspective,\n })\n\n // Log configuration details at debug level\n logger.debug('Instance configuration', {\n projectId: config.projectId,\n dataset: config.dataset,\n perspective: config.perspective,\n hasStudioConfig: !!config.studio,\n hasStudioTokenSource: !!config.studio?.auth?.token,\n hasAuthProviders: !!config.auth?.providers,\n hasAuthToken: !!config.auth?.token,\n })\n\n const instance: SanityInstance = {\n instanceId,\n config,\n isDisposed: () => disposed.current,\n dispose: () => {\n if (disposed.current) {\n logger.trace('Dispose called on already disposed instance', {internal: true})\n return\n }\n logger.trace('Disposing instance', {\n internal: true,\n listenerCount: disposeListeners.size,\n })\n disposed.current = true\n disposeListeners.forEach((listener) => listener())\n disposeListeners.clear()\n logger.info('Instance disposed')\n },\n onDispose: (cb) => {\n const listenerId = randomId(16)\n disposeListeners.set(listenerId, cb)\n return () => {\n disposeListeners.delete(listenerId)\n }\n },\n }\n\n return instance\n}\n","/**\n * Serialises store options into a `@sanity/client` request query object.\n * Scalars are stringified and `undefined` values dropped; string arrays pass\n * through untouched so the client serialises them as repeated keys\n * (`permissions=a&permissions=b`).\n *\n * @internal\n */\nexport function buildQuery(\n params: Record<string, string | number | boolean | string[] | undefined>,\n): Record<string, string | string[]> {\n return Object.fromEntries(\n Object.entries(params)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => [key, Array.isArray(value) ? value : String(value)]),\n )\n}\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {defineFetcher, defineMutation} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\nimport {type Included, type IncludesAny} from '../utils/includeShape'\n\n// The Applications API is currently only available on vX.\nconst API_VERSION = 'vX'\n\n/**\n * Related data the Applications API can embed in a response. `interfaces`,\n * `workspaces` and `access` are children of the active deployment and are\n * embedded under {@link Application.activeDeployment}; `config.studio` and\n * `config.mfManifest` are embedded under {@link Application.config}.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationInclude =\n | 'interfaces'\n | 'workspaces'\n | 'access'\n | 'config.studio'\n | 'config.mfManifest'\n | 'activeDeployment'\n\ntype ApplicationInterfaceBase = {\n id: string\n name: string\n title: string\n version: string\n /** Module federation module ID; resolved from the host mf-manifest at runtime */\n moduleId: string\n}\n\n/**\n * Placement metadata for application and panel interfaces.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationInterfacePlacementMetadata = {\n dock?: {group?: string; order?: number}\n}\n\n/**\n * An interface exposed by a deployment, embedded when `interfaces` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationInterface = ApplicationInterfaceBase &\n (\n | {type: 'app'; metadata: ApplicationInterfacePlacementMetadata | null}\n | {type: 'panel'; metadata: ApplicationInterfacePlacementMetadata | null}\n | {type: 'asset_source'; metadata: null}\n | {type: 'worker'; metadata: null}\n | {type: 'tile'; metadata: {order?: number; size: 'small' | 'large' | 'banner'}}\n )\n\n/**\n * A studio workspace of a deployment, embedded when `workspaces` is included.\n * Studio applications only.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationWorkspace {\n id: string\n name: string\n title: string | null\n subtitle: string | null\n projectId: string\n dataset: string\n schemaDescriptorId: string | null\n basePath: string | null\n /** Sanitized SVG icon markup */\n icon: string | null\n}\n\n/**\n * A resource an application accesses, embedded when `access` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationAccess {\n id: string\n resourceType: 'datasets' | 'canvases' | 'dashboards' | 'media-libraries'\n resourceId: string\n}\n\n/**\n * Studio configuration, embedded under `config` when `config.studio` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationStudioConfig {\n projectId: string\n /** Release channel (`next`, `stable`, `latest`) or a pinned semver; null if unset */\n autoUpdatingVersion: string | null\n}\n\n/**\n * The active deployment as embedded under {@link Application.activeDeployment}.\n * Its `interfaces`, `workspaces` and `access` children are each present only\n * when the matching {@link ApplicationInclude} token was requested. `workspaces`\n * is omitted (not `null`) for non-studio applications even when requested.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type ApplicationDeployment<Include extends ApplicationInclude = never> = {\n id: string\n applicationId: string\n size: number | null\n version: string | null\n isAutoUpdating: boolean | null\n isActiveDeployment: boolean\n deployedBy: string | null\n createdAt: string\n updatedAt: string\n} & Included<ApplicationInclude, 'interfaces', Include, {interfaces: ApplicationInterface[]}> &\n Included<ApplicationInclude, 'workspaces', Include, {workspaces?: ApplicationWorkspace[]}> &\n Included<ApplicationInclude, 'access', Include, {access: ApplicationAccess[]}>\n\n/**\n * The always-present fields of an org-owned application returned by the\n * Applications API (`/applications`), before any {@link ApplicationInclude}.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationBase {\n id: string\n type: 'studio' | 'coreApp'\n title: string\n /** Stable, immutable identity, distinct from the mutable `slug` address. */\n name: string\n /** Qualified, globally-unique handle: `sanity/<name>` for singletons, `<organizationId>/<name>` otherwise. */\n reference: string\n icon: string | null\n isSingleton: boolean\n visibility: 'default' | 'unlisted' | 'disabled'\n slug: string | null\n externalUrl: string | null\n organizationId: string\n createdAt: string\n updatedAt: string\n}\n\n// Requesting any of these tokens forces the deployment summary into the response.\ntype DeploymentToken = 'activeDeployment' | 'interfaces' | 'workspaces' | 'access'\ntype ConfigToken = 'config.studio' | 'config.mfManifest'\n\ntype ApplicationConfig<Include extends ApplicationInclude> = Included<\n ApplicationInclude,\n 'config.studio',\n Include,\n {studio?: ApplicationStudioConfig}\n> &\n Included<ApplicationInclude, 'config.mfManifest', Include, {mfManifest?: unknown}>\n\n// Brett returns `config: {}` when a requested config value is not stored.\ntype ApplicationConfigShape<Include extends ApplicationInclude> = [ApplicationInclude] extends [\n Include,\n]\n ? {config?: ApplicationConfig<Include>}\n : IncludesAny<Include, ConfigToken> extends true\n ? {config: ApplicationConfig<Include>}\n : unknown\n\n// `activeDeployment` is present when any deployment-related token was requested;\n// a wide include array makes it optional.\ntype ApplicationDeploymentShape<Include extends ApplicationInclude> = [ApplicationInclude] extends [\n Include,\n]\n ? {activeDeployment?: ApplicationDeployment<Include> | null}\n : IncludesAny<Include, DeploymentToken> extends true\n ? {activeDeployment: ApplicationDeployment<Include> | null}\n : unknown\n\n/**\n * An org-owned application as returned by the Applications API\n * (`/applications`). `config` and `activeDeployment` (with its\n * `interfaces`/`workspaces`/`access` children) are present only when the\n * corresponding {@link ApplicationInclude} tokens were requested.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type Application<Include extends ApplicationInclude = never> = ApplicationBase &\n ApplicationConfigShape<Include> &\n ApplicationDeploymentShape<Include>\n\n/**\n * Options for listing an organization's applications.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationsOptions<Include extends ApplicationInclude = ApplicationInclude> {\n organizationId: string\n /** Filter by application type */\n type?: string\n include?: Include[]\n /** Page size (1-100, default 50), or `'none'` to disable pagination */\n limit?: number | 'none'\n /** Cursor from a previous response's `nextCursor` */\n cursor?: string\n}\n\n/**\n * The cursor-paginated envelope returned by `GET /applications`.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface ApplicationsResponse<Include extends ApplicationInclude = never> {\n nextCursor: string | null\n data: Application<Include>[]\n}\n\n/** The API takes `include` as a comma-separated list; sorted so key and query agree */\nfunction serializeInclude(include: ApplicationInclude[] | undefined): string | undefined {\n return include?.length ? [...include].sort().join(',') : undefined\n}\n\n/**\n * Fetcher for an organization's applications (`GET /applications`), on the\n * shared fetcher cache.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const applications = defineFetcher<\n [options: ApplicationsOptions],\n ApplicationsResponse<ApplicationInclude>\n>({\n name: 'applications',\n getKey: (_instance, options) =>\n [\n options.organizationId,\n options.type ?? '',\n serializeInclude(options.include) ?? '',\n options.limit ?? '',\n options.cursor ?? '',\n ].join(':'),\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<ApplicationsResponse<ApplicationInclude>>({\n url: '/applications',\n query: buildQuery({\n organizationId: options.organizationId,\n type: options.type,\n include: serializeInclude(options.include),\n limit: options.limit,\n cursor: options.cursor,\n }),\n tag: 'applications.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'application', id: 'LIST'},\n ...data.data.map((app) => ({type: 'application', id: app.id})),\n ],\n})\n\n/**\n * Fetcher for a single application (`GET /applications/:id`), on the shared\n * fetcher cache. No pre-seeding from {@link applications}: its list keys\n * include the organization, which this fetcher's params can't reconstruct.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const application = defineFetcher<\n [applicationId: string, options?: {include?: ApplicationInclude[]}],\n Application<ApplicationInclude>\n>({\n name: 'application',\n getKey: (_instance, applicationId, options) =>\n `${applicationId}:${serializeInclude(options?.include) ?? ''}`,\n fetch: (instance) => (applicationId, options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Application<ApplicationInclude>>({\n url: `/applications/${applicationId}`,\n query: buildQuery({include: serializeInclude(options?.include)}),\n tag: 'applications.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'application', id: data.id}],\n})\n\n/**\n * Confirmation returned by the API's soft-delete endpoints.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport interface DeletedResult {\n deleted: boolean\n}\n\n/**\n * Dashboard visibility for an application.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport type ApplicationVisibility = ApplicationBase['visibility']\n\n/**\n * Mutable properties of an application (`PATCH /applications/:id`) — the\n * writable subset of {@link ApplicationBase}, each optional, keyed by id.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport type UpdateApplicationInput = {applicationId: string} & Partial<\n Pick<ApplicationBase, 'title' | 'icon' | 'visibility'>\n>\n\n/**\n * Mutation to update an application (`PATCH /applications/:id`). Invalidates the\n * updated application and the list so active entries reconverge on server truth.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const updateApplication = defineMutation<UpdateApplicationInput, Application>({\n name: 'updateApplication',\n mutationFn:\n (instance) =>\n ({applicationId, ...body}) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Application>({\n url: `/applications/${applicationId}`,\n method: 'PATCH',\n body,\n tag: 'applications.update',\n }),\n ),\n ),\n invalidates: (_result, {applicationId}) => [\n {type: 'application', id: 'LIST'},\n {type: 'application', id: applicationId},\n ],\n})\n\n/**\n * Input for {@link deleteApplication}.\n *\n * @internal\n */\nexport interface DeleteApplicationInput {\n applicationId: string\n}\n\n/**\n * Mutation to soft-delete an application (`DELETE /applications/:id`).\n * Invalidates the deleted application and the list.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const deleteApplication = defineMutation<DeleteApplicationInput, DeletedResult>({\n name: 'deleteApplication',\n mutationFn:\n (instance) =>\n ({applicationId}) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<DeletedResult>({\n url: `/applications/${applicationId}`,\n method: 'DELETE',\n tag: 'applications.delete',\n }),\n ),\n ),\n invalidates: (_result, {applicationId}) => [\n {type: 'application', id: 'LIST'},\n {type: 'application', id: applicationId},\n ],\n})\n","import {switchMap} from 'rxjs'\n\nimport {type Application, type ApplicationInterface} from '../applications/applications'\nimport {getClientState} from '../client/clientStore'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\nimport {type Included} from '../utils/includeShape'\n\n// The Applications API is currently only available on vX.\nconst API_VERSION = 'vX'\n\n/**\n * Related data the Installations API can embed in a response. Each token adds\n * a top-level field to the {@link Installation} item.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type InstallationInclude = 'activeConfig' | 'access' | 'interfaces' | 'config.mfManifest'\n\n/**\n * A resource an installation accesses, embedded when `access` is included.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationAccess {\n id: string\n resourceType: 'datasets' | 'canvases' | 'dashboards' | 'media-libraries'\n resourceId: string\n}\n\n/**\n * The active config of an installation, embedded when `activeConfig` is\n * included. `null` when nothing is deployed.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationActiveConfig {\n id: string\n installationId: string\n deployedBy: string | null\n version: string\n isActive: boolean\n createdAt: string\n}\n\n/**\n * The always-present fields of an active singleton installation returned by the\n * Installations API (`/installations`), before any {@link InstallationInclude}.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationBase {\n id: string\n applicationId: string\n organizationId: string\n installedBy: string | null\n createdAt: string\n updatedAt: string\n application: {\n title: string\n /** Stable, immutable identity of the installed singleton, distinct from `slug`. */\n name: string\n /** Qualified, globally-unique handle of the installed singleton (e.g. `sanity/<name>`). */\n reference: string\n slug: string | null\n /** Sanitized SVG icon markup, or `null` when the singleton has no icon. */\n icon: string | null\n /** The singleton's publishing organization; the top-level `organizationId` is the installing one. */\n organizationId: string\n }\n}\n\n/**\n * An active singleton installation as returned by the Installations API\n * (`/installations`). `activeConfig`, `access` and `interfaces` are top-level\n * fields, each present only when the matching {@link InstallationInclude} token\n * was requested.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport type Installation<Include extends InstallationInclude = never> = InstallationBase &\n Included<\n InstallationInclude,\n 'config.mfManifest',\n Include,\n {\n application: InstallationBase['application'] &\n ([InstallationInclude] extends [Include]\n ? Partial<Pick<Application<'config.mfManifest'>, 'config'>>\n : Pick<Application<'config.mfManifest'>, 'config'>)\n }\n > &\n Included<\n InstallationInclude,\n 'activeConfig',\n Include,\n {activeConfig: InstallationActiveConfig | null}\n > &\n Included<InstallationInclude, 'access', Include, {access: InstallationAccess[]}> &\n Included<InstallationInclude, 'interfaces', Include, {interfaces: ApplicationInterface[]}>\n\n/**\n * Options for listing an organization's installations.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationsOptions<Include extends InstallationInclude = InstallationInclude> {\n organizationId: string\n include?: Include[]\n /** Page size (1-100, default 50), or `'none'` to disable pagination */\n limit?: number | 'none'\n /** Cursor from a previous response's `nextCursor` */\n cursor?: string\n}\n\n/**\n * The cursor-paginated envelope returned by `GET /installations`.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @public\n */\nexport interface InstallationsResponse<Include extends InstallationInclude = never> {\n nextCursor: string | null\n data: Installation<Include>[]\n}\n\n/** The API takes `include` as a comma-separated list; sorted so key and query agree */\nfunction serializeInclude(include: InstallationInclude[] | undefined): string | undefined {\n return include?.length ? [...include].sort().join(',') : undefined\n}\n\n/**\n * Fetcher for an organization's installations (`GET /installations`), on the\n * shared fetcher cache.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const installations = defineFetcher<\n [options: InstallationsOptions],\n InstallationsResponse<InstallationInclude>\n>({\n name: 'installations',\n getKey: (_instance, options) =>\n [\n options.organizationId,\n serializeInclude(options.include) ?? '',\n options.limit ?? '',\n options.cursor ?? '',\n ].join(':'),\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<InstallationsResponse<InstallationInclude>>({\n url: '/installations',\n query: buildQuery({\n organizationId: options.organizationId,\n include: serializeInclude(options.include),\n limit: options.limit,\n cursor: options.cursor,\n }),\n tag: 'installations.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'installation', id: 'LIST'},\n ...data.data.map((installation) => ({type: 'installation', id: installation.id})),\n ],\n})\n\n/**\n * Fetcher for a single installation (`GET /installations/:id`), on the shared\n * fetcher cache. No pre-seeding from {@link installations}: its list keys\n * include the organization, which this fetcher's params can't reconstruct.\n *\n * @see https://www.sanity.io/docs/http-reference/applications-api\n * @internal\n */\nexport const installation = defineFetcher<\n [installationId: string, options?: {include?: InstallationInclude[]}],\n Installation<InstallationInclude>\n>({\n name: 'installation',\n getKey: (_instance, installationId, options) =>\n `${installationId}:${serializeInclude(options?.include) ?? ''}`,\n fetch: (instance) => (installationId, options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Installation<InstallationInclude>>({\n url: `/installations/${installationId}`,\n query: buildQuery({include: serializeInclude(options?.include)}),\n tag: 'installations.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'installation', id: data.id}],\n})\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/** @public */\nexport interface OrganizationMember {\n sanityUserId: string\n isCurrentUser: boolean\n user: {\n id: string\n displayName: string\n familyName: string\n givenName: string\n middleName: string | null\n imageUrl: string | null\n email: string\n loginProvider: string\n }\n roles: Array<{\n name: string\n title: string\n description?: string\n }>\n}\n\n/**\n * The base fields returned from `/organizations/<id>` for every organization.\n * @public\n */\nexport interface OrganizationBase {\n id: string\n name: string\n slug: string | null\n createdAt: string\n createdByUserId: string\n updatedAt: string\n deletedAt: string | null\n dashboardStatus: 'enabled' | 'disabled'\n aiFeaturesStatus: 'enabled' | 'disabled'\n mediaLibraryStatus: 'enabled' | 'disabled'\n requestAccessStatus: 'allowed' | 'disabled'\n telemetryConsentStatus: 'allowed' | 'msa_denied' | 'customer_denied'\n oauthAppsStatus: 'allowed' | 'blocked'\n defaultRoleName: string\n domains: string[] | null\n}\n\n/** @public */\nexport interface OrganizationOptions<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> {\n includeMembers?: IncludeMembers\n includeFeatures?: IncludeFeatures\n organizationId: string\n}\n\n/**\n * An `Organization` with `members` and/or `features` conditionally included\n * based on the query options used to fetch it.\n * @public\n */\nexport type Organization<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> = OrganizationBase &\n // `boolean extends T` is non-distributive — true only when T is the wide\n // `boolean`, in which case the field is optional. Literal `true`/`false`\n // fall through to the strict branch.\n (boolean extends IncludeMembers\n ? {members?: OrganizationMember[]}\n : IncludeMembers extends true\n ? {members: OrganizationMember[]}\n : unknown) &\n (boolean extends IncludeFeatures\n ? {features?: string[]}\n : IncludeFeatures extends true\n ? {features: string[]}\n : unknown)\n\nfunction resolveOrganizationId(options?: OrganizationOptions<boolean, boolean>) {\n const organizationId = options?.organizationId\n if (!organizationId) {\n throw new Error('An organizationId is required to use the organization API.')\n }\n return organizationId\n}\n\nfunction normalizeOrganizationOptions(options?: OrganizationOptions<boolean, boolean>) {\n return {\n includeMembers: options?.includeMembers ?? false,\n includeFeatures: options?.includeFeatures ?? false,\n }\n}\n\n/** @internal */\nexport function getOrganizationCacheKey(\n _instance: SanityInstance,\n options?: OrganizationOptions<boolean, boolean>,\n): string {\n const organizationId = resolveOrganizationId(options)\n const {includeMembers, includeFeatures} = normalizeOrganizationOptions(options)\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n return `organization:${organizationId}${membersKey}${featuresKey}`\n}\n\n/**\n * Fetcher for a single organization (`GET /organizations/:id`), on the shared\n * fetcher cache. No pre-seeding from the organizations list: it returns a\n * subset of the detail fields, and a partial seed would serve as fresh for\n * the whole stale window.\n *\n * @internal\n */\nexport const organization = defineFetcher<\n [options?: OrganizationOptions<boolean, boolean>],\n Organization<boolean, boolean>\n>({\n name: 'organization',\n getKey: getOrganizationCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Organization<boolean, boolean>>({\n url: `/organizations/${resolveOrganizationId(options)}`,\n query: buildQuery(normalizeOrganizationOptions(options)),\n tag: 'organizations.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'organization', id: data.id}],\n})\n","import {map, switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {defineFetcher} from '../store/fetcherStore'\n\nconst API_VERSION = 'v2025-07-11'\n\n/**\n * The resource types the Access API can scope a permission check to.\n *\n * @see https://www.sanity.io/docs/http-reference/access-api\n * @public\n */\nexport type AccessResourceType =\n | 'organization'\n | 'project'\n | 'media-library'\n | 'canvas'\n | 'dashboard'\n | 'view'\n\n/**\n * Fetcher checking whether the current user holds the given permissions on a\n * resource (`GET /access/:resourceType/:resourceId/user-permissions/me/check`),\n * on the shared fetcher cache. Resolves to a map of each requested permission\n * to whether the user has it. The key sorts the permission list, so permission\n * order doesn't create distinct cache entries.\n *\n * @see https://www.sanity.io/docs/http-reference/access-api\n * @internal\n */\nexport const checkPermissions = defineFetcher<\n [resourceType: AccessResourceType, resourceId: string, permissions: string[]],\n Record<string, boolean>\n>({\n name: 'checkPermissions',\n getKey: (_instance, resourceType, resourceId, permissions) =>\n `${resourceType}:${resourceId}:${[...permissions].sort().join(',')}`,\n fetch: (instance) => (resourceType, resourceId, permissions) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable\n .request<{data: Record<string, boolean>}>({\n url: `/access/${resourceType}/${resourceId}/user-permissions/me/check`,\n query: {permissions},\n tag: 'access.check',\n })\n .pipe(map((response) => response.data)),\n ),\n ),\n tags: (_data, resourceType, resourceId) => [\n {type: 'access', id: `${resourceType}:${resourceId}`},\n ],\n})\n","/**\n * Error message returned by the organization verification\n * @public\n */\nexport interface OrgVerificationResult {\n error: string | null\n}\n\n/**\n * Compares a project's actual organization ID with the expected organization ID.\n * @public\n */\nexport function compareProjectOrganization(\n projectId: string,\n projectOrganizationId: string | null | undefined,\n currentDashboardOrgId: string,\n): OrgVerificationResult {\n if (projectOrganizationId !== currentDashboardOrgId) {\n return {\n error:\n `Project ${projectId} belongs to Organization ${projectOrganizationId ?? 'unknown'}, ` +\n `but the Dashboard has Organization ${currentDashboardOrgId} selected`,\n }\n }\n return {error: null}\n}\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type Project} from '../project/project'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/** @public */\nexport interface ProjectsOptions<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = true,\n> {\n organizationId?: string\n includeMembers?: IncludeMembers\n includeFeatures?: IncludeFeatures\n onlyExplicitMembership?: boolean\n}\n\nfunction normalizeProjectsOptions(options?: ProjectsOptions<boolean, boolean>) {\n return {\n organizationId: options?.organizationId,\n includeMembers: options?.includeMembers ?? false,\n includeFeatures: options?.includeFeatures ?? true,\n onlyExplicitMembership: options?.onlyExplicitMembership ?? false,\n }\n}\n\n/** @internal */\nexport function getProjectsCacheKey(\n _instance: SanityInstance,\n options?: ProjectsOptions<boolean, boolean>,\n): string {\n const {organizationId, includeMembers, includeFeatures, onlyExplicitMembership} =\n normalizeProjectsOptions(options)\n const orgKey = organizationId ? `:org:${organizationId}` : ''\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n const explicitKey = onlyExplicitMembership ? ':explicit' : ''\n return `projects${orgKey}${membersKey}${featuresKey}${explicitKey}`\n}\n\n/**\n * Fetcher for the current user's projects (`GET /projects`), on the shared\n * fetcher cache.\n *\n * @internal\n */\nexport const projects = defineFetcher<\n [options?: ProjectsOptions<boolean, boolean>],\n Project<boolean, boolean>[]\n>({\n name: 'projects',\n getKey: getProjectsCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Project<boolean, boolean>[]>({\n url: '/projects',\n query: buildQuery(normalizeProjectsOptions(options)),\n tag: 'projects.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'project', id: 'LIST'},\n ...data.map((project) => ({type: 'project', id: project.id})),\n ],\n})\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type ProjectHandle} from '../config/sanityConfig'\nimport {projects} from '../projects/projects'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/** @public */\nexport interface ProjectMemberRole {\n name: string\n title: string\n description: string\n}\n\n/** @public */\nexport interface ProjectMember {\n id: string\n createdAt: string\n updatedAt: string\n isCurrentUser: boolean\n isRobot: boolean\n roles: ProjectMemberRole[]\n}\n\n/** @public */\nexport interface ProjectMetadata {\n color?: string\n externalStudioHost?: string\n initialTemplate?: string\n cliInitializedAt?: string\n integration: 'manage' | 'cli'\n}\n\n/**\n * The base fields returned from `/projects` for every project.\n * @public\n */\nexport interface ProjectBase {\n id: string\n displayName: string\n studioHost: string | null\n organizationId: string\n metadata: ProjectMetadata\n isBlocked: boolean\n isDisabled: boolean\n isDisabledByUser: boolean\n activityFeedEnabled: boolean\n createdAt: string\n updatedAt: string\n}\n\n/**\n * A `Project` with `members` and/or `features` conditionally included\n * based on the query options used to fetch it.\n * @public\n */\nexport type Project<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n> = ProjectBase &\n // `boolean extends T` is non-distributive — true only when T is the wide\n // `boolean`, in which case the field is optional. Literal `true`/`false`\n // fall through to the strict branch.\n (boolean extends IncludeMembers\n ? {members?: ProjectMember[]}\n : IncludeMembers extends true\n ? {members: ProjectMember[]}\n : unknown) &\n (boolean extends IncludeFeatures\n ? {features?: string[]}\n : IncludeFeatures extends true\n ? {features: string[]}\n : unknown)\n\n/** @public */\nexport interface ProjectOptions<\n IncludeMembers extends boolean = true,\n IncludeFeatures extends boolean = true,\n> extends ProjectHandle {\n includeMembers?: IncludeMembers\n includeFeatures?: IncludeFeatures\n}\n\nfunction normalizeProjectOptions(options?: ProjectOptions<boolean, boolean>) {\n return {\n includeMembers: options?.includeMembers ?? true,\n includeFeatures: options?.includeFeatures ?? true,\n }\n}\n\nfunction resolveProjectId(instance: SanityInstance, options?: ProjectOptions<boolean, boolean>) {\n const projectId = options?.projectId ?? instance.config.projectId\n if (!projectId) {\n throw new Error('A projectId is required to use the project API.')\n }\n return projectId\n}\n\n/** @internal */\nexport function getProjectCacheKey(\n instance: SanityInstance,\n options?: ProjectOptions<boolean, boolean>,\n): string {\n const projectId = resolveProjectId(instance, options)\n const {includeMembers, includeFeatures} = normalizeProjectOptions(options)\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n return `project:${projectId}${membersKey}${featuresKey}`\n}\n\n/**\n * Fetcher for a single project (`GET /projects/:id`), on the shared fetcher\n * cache. Pre-seeds from the default {@link projects} list entry when it can\n * satisfy the read — the default list carries features but not members, so\n * only `includeMembers: false` reads seed.\n *\n * @internal\n */\nexport const project = defineFetcher<\n [options?: ProjectOptions<boolean, boolean>],\n Project<boolean, boolean>\n>({\n name: 'project',\n getKey: getProjectCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Project<boolean, boolean>>({\n url: `/projects/${resolveProjectId(instance, options)}`,\n query: buildQuery(normalizeProjectOptions(options)),\n tag: 'projects.get',\n }),\n ),\n ),\n tags: (data) => [{type: 'project', id: data.id}],\n initialData: (instance, options) => {\n if (normalizeProjectOptions(options).includeMembers) return undefined\n const snapshot = projects.getState(instance).getCurrent()\n if (snapshot.status !== 'success') return undefined\n const projectId = resolveProjectId(instance, options)\n const match = snapshot.data.find((item) => item.id === projectId)\n return match && {data: match, dataUpdatedAt: snapshot.dataUpdatedAt}\n },\n})\n","import {combineLatest, distinctUntilChanged, map, type Observable, of, switchMap} from 'rxjs'\n\nimport {getDashboardOrganizationId} from '../dashboard/messageBus/topicStore'\nimport {\n compareProjectOrganization,\n type OrgVerificationResult,\n} from '../project/organizationVerification'\nimport {project} from '../project/project'\nimport {type SanityInstance} from '../store/createSanityInstance'\n\n/**\n * Creates an observable that emits the organization verification state for a given instance.\n * It combines the dashboard organization ID (from the message bus or `_context`) with the\n * project's actual organization ID (fetched via the project fetcher) and compares them.\n * @public\n */\nexport function observeOrganizationVerificationState(\n instance: SanityInstance,\n projectIds: string[],\n): Observable<OrgVerificationResult> {\n // Observable for the dashboard org ID (potentially null)\n const dashboardOrgId$ =\n getDashboardOrganizationId(instance).observable.pipe(distinctUntilChanged())\n\n // Create observables for each project's org ID\n const projectOrgIdObservables = projectIds.map((id) =>\n project.getState(instance, {projectId: id}).observable.pipe(\n map((snapshot) => ({projectId: id, orgId: snapshot.data?.organizationId ?? null})),\n // Ensure we only proceed if the orgId is loaded, distinct prevents unnecessary checks\n distinctUntilChanged((prev, curr) => prev.orgId === curr.orgId),\n ),\n )\n\n // Combine observables to get all project org IDs\n const allProjectOrgIds$ =\n projectOrgIdObservables.length > 0 ? combineLatest(projectOrgIdObservables) : of([])\n\n // Combine the sources\n return combineLatest([dashboardOrgId$, allProjectOrgIds$]).pipe(\n switchMap(([dashboardOrgId, projectOrgDataArray]) => {\n // If no dashboard org ID is set, or no project IDs provided, verification isn't applicable/possible\n if (!dashboardOrgId || projectOrgDataArray.length === 0) {\n return of<OrgVerificationResult>({error: null}) // Return success (no error)\n }\n\n // Iterate through all projects and check organization IDs\n for (const projectData of projectOrgDataArray) {\n // If a project doesn't have an orgId, we can't verify, treat as non-blocking for now\n // (Matches original logic where null projectOrgId resulted in {error: null})\n if (!projectData.orgId) {\n continue\n }\n\n // Perform the comparison for the current project\n const result = compareProjectOrganization(\n projectData.projectId,\n projectData.orgId,\n dashboardOrgId,\n )\n\n // If any project fails verification, immediately return the error\n if (result.error) {\n return of(result)\n }\n }\n\n // If all projects passed verification (or had no orgId to check)\n return of<OrgVerificationResult>({error: null})\n }),\n // Only emit when the overall error status actually changes\n distinctUntilChanged((prev, curr) => prev.error === curr.error),\n )\n}\n","/**\n * PKCE (Proof Key for Code Exchange, RFC 7636) helpers for the OAuth\n * authorization-code flow.\n *\n * @internal\n */\n\n/** Number of random bytes used for the `code_verifier` and `state`. */\nconst RANDOM_BYTE_LENGTH = 32\n\n/**\n * Encodes a byte array as a base64url string (RFC 4648 §5) with no padding,\n * as required for PKCE `code_verifier`/`code_challenge` values.\n *\n * @internal\n */\nexport function base64UrlEncode(bytes: Uint8Array): string {\n let binary = ''\n for (const byte of bytes) {\n binary += String.fromCharCode(byte)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction randomString(): string {\n const bytes = new Uint8Array(RANDOM_BYTE_LENGTH)\n crypto.getRandomValues(bytes)\n return base64UrlEncode(bytes)\n}\n\n/**\n * Generates a PKCE `code_verifier`\n *\n * @internal\n */\nexport function generateCodeVerifier(): string {\n return randomString()\n}\n\n/**\n * Generates the `state` parameter used to defend against CSRF on the OAuth\n * callback.\n *\n * @internal\n */\nexport function generateState(): string {\n return randomString()\n}\n\n/**\n * Derives the PKCE `code_challenge` from a `code_verifier` using\n * `base64url(sha256(verifier))` (the `S256` method).\n *\n * @internal\n */\nexport async function generateCodeChallenge(codeVerifier: string): Promise<string> {\n const data = new TextEncoder().encode(codeVerifier)\n const digest = await crypto.subtle.digest('SHA-256', data)\n return base64UrlEncode(new Uint8Array(digest))\n}\n","import {createSelector} from 'reselect'\n\nimport {bindActionGlobally} from '../../store/createActionBinder'\nimport {createStateSourceAction} from '../../store/createStateSourceAction'\nimport {DEFAULT_BASE} from '../authConstants'\nimport {getAuthLogger} from '../authLogger'\nimport {AuthStateType} from '../authStateType'\nimport {authStore, type AuthStoreState} from '../authStore'\nimport {createLoggedInAuthState, getDefaultLocation} from '../utils'\nimport {\n appendResourceIndicator,\n createOAuthClient,\n getOAuthOptions,\n postTokenRequest,\n serializeTokens,\n toOAuthTokens,\n} from './oauthClient'\nimport {runOAuthTokenRefresh} from './oauthRefresh'\nimport {generateCodeChallenge, generateCodeVerifier, generateState} from './pkce'\nimport {type OAuthTokens} from './types'\n\n/** sessionStorage key for the PKCE `code_verifier`. */\nexport const OAUTH_VERIFIER_KEY = '__sanity_oauth_verifier'\n\n/** sessionStorage key for the CSRF `state` value. */\nexport const OAUTH_STATE_KEY = '__sanity_oauth_state'\n\n/** sessionStorage key for the location to return to after the callback. */\nexport const OAUTH_RETURN_TO_KEY = '__sanity_oauth_return_to'\n\nconst OAUTH_CALLBACK_PARAMS = ['code', 'state', 'error', 'error_description']\n\n/** Strips the OAuth callback params from a URL. */\nfunction stripOAuthParams(href: string): string {\n const url = new URL(href, DEFAULT_BASE)\n for (const param of OAUTH_CALLBACK_PARAMS) url.searchParams.delete(param)\n return url.toString()\n}\n\n/** Whether `href` is the registered redirect URI (origin + pathname; query and hash ignored). */\nfunction isCallbackRoute(href: string, redirectUri: string): boolean {\n const current = new URL(href, DEFAULT_BASE)\n const redirect = new URL(redirectUri, DEFAULT_BASE)\n return current.origin === redirect.origin && current.pathname === redirect.pathname\n}\n\n/**\n * Starts the OAuth authorization-code + PKCE flow: generates a `code_verifier`,\n * `code_challenge` and `state`, persists the verifier and state to\n * `sessionStorage`, then navigates the browser to the authorize endpoint.\n *\n * @public\n */\nexport const startOAuthAuthorization = bindActionGlobally(authStore, async ({state, instance}) => {\n const logger = getAuthLogger(instance)\n const options = getOAuthOptions(state.get())\n\n const codeVerifier = generateCodeVerifier()\n const oauthState = generateState()\n const codeChallenge = await generateCodeChallenge(codeVerifier)\n\n const session = typeof sessionStorage !== 'undefined' ? sessionStorage : undefined\n session?.setItem(OAUTH_VERIFIER_KEY, codeVerifier)\n session?.setItem(OAUTH_STATE_KEY, oauthState)\n // The redirect URI must match the registered one exactly, so the current\n // location (deep link) is stashed here and restored by the callback. OAuth\n // params are only stripped when we are already on the callback route, so\n // legitimate app params like `?state=draft` survive the round trip.\n const currentHref = getDefaultLocation()\n if (currentHref !== DEFAULT_BASE) {\n const returnTo = isCallbackRoute(currentHref, options.oauth.redirectUri)\n ? stripOAuthParams(currentHref)\n : currentHref\n session?.setItem(OAUTH_RETURN_TO_KEY, returnTo)\n }\n\n const authorizeUrl = new URL(\n '/v1/auth/oauth/authorize',\n options.apiHost ?? 'https://api.sanity.io',\n )\n authorizeUrl.searchParams.set('response_type', 'code')\n authorizeUrl.searchParams.set('client_id', options.oauth.clientId)\n authorizeUrl.searchParams.set('redirect_uri', options.oauth.redirectUri)\n authorizeUrl.searchParams.set('state', oauthState)\n authorizeUrl.searchParams.set('code_challenge', codeChallenge)\n authorizeUrl.searchParams.set('code_challenge_method', 'S256')\n appendResourceIndicator(authorizeUrl.searchParams, options.oauth)\n\n logger.info('Starting OAuth authorization')\n if (typeof window !== 'undefined' && typeof window.location?.assign === 'function') {\n window.location.assign(authorizeUrl.toString())\n }\n})\n\n/**\n * Handles the OAuth redirect callback: validates `state`, surfaces `error`\n * responses, exchanges the authorization `code` for tokens, persists them, and\n * transitions to `LOGGED_IN`.\n *\n * Returns the URL for the caller to `history.replaceState` to when a callback\n * was processed: on success, the same-origin location the user was on when\n * the flow started (if any), otherwise the callback URL cleaned of OAuth\n * params. Returns `false` when there was nothing to handle (no code, or an\n * exchange already in progress). A callback that fails validation while a\n * session is already `LOGGED_IN` (e.g. a remount before the params were\n * stripped) is ignored with a warning and still returns the cleaned URL, so a\n * string result does not imply an exchange ran.\n *\n * @public\n */\nexport const handleOAuthCallback = bindActionGlobally(\n authStore,\n async (\n {state, instance},\n locationHref: string = getDefaultLocation(),\n ): Promise<string | false> => {\n const logger = getAuthLogger(instance)\n const options = getOAuthOptions(state.get())\n\n const {authState} = state.get()\n if (authState.type === AuthStateType.LOGGING_IN && authState.isExchangingToken) {\n logger.debug('Skipping OAuth callback - token exchange already in progress')\n return false\n }\n\n const callbackUrl = new URL(locationHref, DEFAULT_BASE)\n const code = callbackUrl.searchParams.get('code')\n const returnedState = callbackUrl.searchParams.get('state')\n const error = callbackUrl.searchParams.get('error')\n const errorDescription = callbackUrl.searchParams.get('error_description')\n\n const session = typeof sessionStorage !== 'undefined' ? sessionStorage : undefined\n\n const cleanedUrl = stripOAuthParams(locationHref)\n\n // Callback failures move to ERROR, unless a session is already\n // established: a stale callback URL (e.g. remount before the params were\n // stripped, after the artifacts were cleared) or a denied re-authorization\n // must not clobber LOGGED_IN.\n const rejectCallback = (name: string, message: string): string => {\n if (authState.type === AuthStateType.LOGGED_IN) {\n // Leave PKCE artifacts alone: a re-authorization may be in flight.\n logger.warn(`${message} — ignoring callback, session already established`)\n return cleanedUrl\n }\n logger.error(`${message} — rejecting callback`)\n clearOAuthArtifacts(session)\n state.set(name, {authState: {type: AuthStateType.ERROR, error: new Error(message)}})\n return cleanedUrl\n }\n\n if (error) {\n return rejectCallback(\n 'oauthCallbackError',\n errorDescription ? `${error}: ${errorDescription}` : error,\n )\n }\n\n if (!code) {\n logger.debug('No OAuth code found in callback URL')\n return false\n }\n\n const storedState = session?.getItem(OAUTH_STATE_KEY) ?? null\n if (!returnedState || !storedState || returnedState !== storedState) {\n return rejectCallback('oauthStateMismatch', 'OAuth state mismatch')\n }\n\n const codeVerifier = session?.getItem(OAUTH_VERIFIER_KEY) ?? null\n if (!codeVerifier) {\n return rejectCallback('oauthVerifierMissing', 'OAuth code verifier missing')\n }\n\n logger.info('Exchanging OAuth code for tokens')\n state.set('oauthExchange', {\n authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: true},\n })\n\n try {\n const client = createOAuthClient(options)\n const params = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n code_verifier: codeVerifier,\n redirect_uri: options.oauth.redirectUri,\n client_id: options.oauth.clientId,\n })\n appendResourceIndicator(params, options.oauth)\n const response = await postTokenRequest(client, params, 'oauth.token')\n\n const tokens = toOAuthTokens(response)\n options.storageArea?.setItem(options.storageKey, serializeTokens(tokens))\n const returnTo = getSameOriginReturnTo(session, options.oauth.redirectUri)\n clearOAuthArtifacts(session)\n\n logger.info('OAuth tokens obtained, user logged in')\n state.set('oauthLoggedIn', {\n authState: createLoggedInAuthState(tokens.accessToken, null),\n oauthTokens: tokens,\n })\n return returnTo ?? cleanedUrl\n } catch (exchangeError) {\n logger.error('Failed to exchange OAuth code for tokens', {error: exchangeError})\n clearOAuthArtifacts(session)\n state.set('oauthExchangeError', {\n authState: {type: AuthStateType.ERROR, error: exchangeError},\n })\n return cleanedUrl\n }\n },\n)\n\n/**\n * Refreshes the OAuth tokens using the `refresh_token` grant. Concurrent\n * callers share a single in-flight request. An unrecoverable failure (a 4xx\n * rejecting the refresh token) clears the tokens and transitions to\n * `LOGGED_OUT`; transient failures (network, 5xx, rate limits) leave the\n * session intact and rethrow so the caller can retry. The resolved tokens omit\n * the refresh token, which core retains internally for subsequent refreshes.\n *\n * @public\n */\nexport const refreshOAuthTokens = bindActionGlobally(authStore, runOAuthTokenRefresh)\n\n/**\n * Revokes the OAuth tokens then clears local storage and transitions to `LOGGED_OUT`.\n * Local state is cleared even if the revoke request fails.\n *\n * @public\n */\nexport const revokeOAuthTokens = bindActionGlobally(authStore, async ({state, instance}) => {\n const logger = getAuthLogger(instance)\n const options = getOAuthOptions(state.get())\n\n const current = state.get().oauthTokens\n const token = current?.refreshToken ?? current?.accessToken\n\n try {\n if (token) {\n const client = createOAuthClient(options)\n const params = new URLSearchParams({token, client_id: options.oauth.clientId})\n await client.request<void>({\n method: 'POST',\n url: '/auth/oauth/revoke',\n headers: {'content-type': 'application/x-www-form-urlencoded'},\n body: params.toString(),\n tag: 'oauth.revoke',\n })\n logger.info('OAuth tokens revoked')\n }\n } catch (error) {\n // Revocation is best-effort — local state is cleared regardless.\n logger.warn('OAuth revoke request failed — clearing local state anyway', {error})\n } finally {\n options.storageArea?.removeItem(options.storageKey)\n state.set('oauthRevoked', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n oauthTokens: undefined,\n })\n }\n})\n\n// Memoised so `getCurrent()` keeps returning the same object while\n// `oauthTokens` is unchanged; `useSyncExternalStore` loops on a snapshot\n// that changes identity every read. One slot suffices: no params, global store.\nconst selectPublicOAuthTokens = createSelector(\n (state: AuthStoreState) => state.oauthTokens,\n (oauthTokens): Omit<OAuthTokens, 'refreshToken'> | null => {\n if (!oauthTokens) return null\n const {refreshToken: _refreshToken, ...tokens} = oauthTokens\n return tokens\n },\n)\n\n/**\n * A state source exposing the current OAuth tokens (including expiry), or\n * `null` when not logged in via OAuth. The refresh token is omitted — it is a\n * long-lived credential held internally by core for `refreshOAuthTokens`, not\n * part of the app-facing token view.\n *\n * @public\n */\nexport const getOAuthTokensState = bindActionGlobally(\n authStore,\n createStateSourceAction(({state}) => selectPublicOAuthTokens(state)),\n)\n\n/** Removes the transient PKCE artifacts from session storage. */\nfunction clearOAuthArtifacts(session: Storage | undefined): void {\n session?.removeItem(OAUTH_VERIFIER_KEY)\n session?.removeItem(OAUTH_STATE_KEY)\n session?.removeItem(OAUTH_RETURN_TO_KEY)\n}\n\n/**\n * Reads the stashed pre-authorize location, only honouring it when it shares\n * the registered redirect URI's origin so a tampered value cannot redirect\n * off-site. (`origin` includes the scheme, so non-http(s) URLs are rejected.)\n */\nfunction getSameOriginReturnTo(session: Storage | undefined, redirectUri: string): string | null {\n const returnTo = session?.getItem(OAUTH_RETURN_TO_KEY) ?? null\n if (!returnTo) return null\n try {\n return new URL(returnTo).origin === new URL(redirectUri).origin ? returnTo : null\n } catch {\n return null\n }\n}\n","import {bindActionGlobally} from '../store/createActionBinder'\nimport {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'\nimport {getAuthLogger} from './authLogger'\nimport {AuthStateType} from './authStateType'\nimport {authStore, type AuthStoreState, type DashboardContext} from './authStore'\nimport {handleOAuthCallback} from './oauth/oauthActions'\nimport {\n createLoggedInAuthState,\n getAuthCode,\n getCleanedUrl,\n getDefaultLocation,\n getTokenFromLocation,\n} from './utils'\n\n/**\n * Handles the auth redirect callback. In OAuth mode this delegates to\n * {@link handleOAuthCallback}; otherwise it exchanges the legacy `sid` for a\n * long-lived token.\n *\n * @public\n */\nexport const handleAuthCallback = bindActionGlobally(\n authStore,\n async ({state, instance}, locationHref: string = getDefaultLocation()) => {\n const logger = getAuthLogger(instance)\n\n const {providedToken, callbackUrl, clientFactory, apiHost, storageArea, storageKey, oauth} =\n state.get().options\n\n // If a token is provided, no need to handle callback\n if (providedToken) {\n logger.debug('Skipping auth callback - token already provided')\n return false\n }\n\n if (oauth) {\n logger.debug('OAuth mode - delegating to handleOAuthCallback')\n return handleOAuthCallback(instance, locationHref)\n }\n\n // Don't handle the callback if already in flight.\n const {authState} = state.get()\n if (authState.type === AuthStateType.LOGGING_IN && authState.isExchangingToken) {\n logger.debug('Skipping auth callback - token exchange already in progress')\n return false\n }\n\n // Prepare the cleaned-up URL early. It will be returned on both success and error if an authCode/token was processed.\n const cleanedUrl = getCleanedUrl(locationHref)\n\n // Check if there is a token in the is in the Dashboard iframe url hash\n const tokenFromUrl = getTokenFromLocation(locationHref)\n if (tokenFromUrl) {\n logger.info('Auth token found in URL, logging in')\n state.set('setTokenFromUrl', {\n authState: createLoggedInAuthState(tokenFromUrl, null),\n })\n return cleanedUrl\n }\n\n // If there is no matching `authCode` then we can't handle the callback\n const authCode = getAuthCode(callbackUrl, locationHref)\n if (!authCode) {\n logger.debug('No auth code found in callback URL')\n return false\n }\n\n // Get the SanityOS dashboard context from the url\n const parsedUrl = new URL(locationHref)\n let dashboardContext: DashboardContext = {}\n try {\n const contextParam = parsedUrl.searchParams.get('_context')\n if (contextParam) {\n const parsedContext = JSON.parse(contextParam)\n if (parsedContext && typeof parsedContext === 'object') {\n delete parsedContext.sid\n dashboardContext = parsedContext\n logger.debug('Dashboard context parsed from callback URL', {\n hasDashboardContext: true,\n })\n }\n }\n } catch (err) {\n // If JSON parsing fails, use empty context\n logger.warn('Failed to parse dashboard context from callback URL', {error: err})\n }\n\n // Otherwise, start the exchange\n logger.info('Exchanging auth code for token')\n state.set('exchangeSessionForToken', {\n authState: {type: AuthStateType.LOGGING_IN, isExchangingToken: true},\n dashboardContext,\n } as Partial<AuthStoreState>)\n\n try {\n const client = clientFactory({\n apiVersion: DEFAULT_API_VERSION,\n requestTagPrefix: REQUEST_TAG_PREFIX,\n useProjectHostname: false,\n useCdn: false,\n ...(apiHost && {apiHost}),\n })\n\n logger.debug('Fetching token from auth endpoint')\n const {token} = await client.request<{token: string; label: string}>({\n method: 'GET',\n url: '/auth/fetch',\n query: {sid: authCode},\n tag: 'fetch-token',\n })\n\n logger.info('Auth token obtained successfully, user logged in')\n storageArea?.setItem(storageKey, JSON.stringify({token}))\n state.set('setToken', {authState: createLoggedInAuthState(token, null)})\n\n return cleanedUrl\n } catch (error) {\n logger.error('Failed to exchange auth code for token', {error})\n state.set('exchangeSessionForTokenError', {authState: {type: AuthStateType.ERROR, error}})\n return cleanedUrl\n }\n },\n)\n","import {bindActionGlobally} from '../store/createActionBinder'\nimport {DEFAULT_API_VERSION, REQUEST_TAG_PREFIX} from './authConstants'\nimport {getAuthLogger} from './authLogger'\nimport {AuthStateType} from './authStateType'\nimport {authStore} from './authStore'\nimport {revokeOAuthTokens} from './oauth/oauthActions'\n\n/**\n * Logs the current user out. In OAuth mode this revokes the OAuth tokens\n * instead of calling the legacy `/auth/logout` endpoint; revocation is\n * best-effort, so the OAuth path resolves even if the revoke request fails,\n * whereas the legacy path rejects when `/auth/logout` fails. Local state and\n * storage are cleared in both cases.\n *\n * @public\n */\nexport const logout = bindActionGlobally(authStore, async ({state, instance}) => {\n const logger = getAuthLogger(instance)\n\n const {clientFactory, apiHost, providedToken, storageArea, storageKey, oauth} =\n state.get().options\n\n // If a token is statically provided, logout does nothing\n if (providedToken) {\n logger.debug('Skipping logout - token is statically provided')\n return\n }\n\n const {authState} = state.get()\n\n // If we already have an inflight request, no-op\n if (authState.type === AuthStateType.LOGGED_OUT && authState.isDestroyingSession) {\n logger.debug('Skipping logout - already in progress')\n return\n }\n\n if (oauth) {\n logger.debug('OAuth mode - revoking tokens instead of calling /auth/logout')\n state.set('loggingOut', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: true},\n })\n await revokeOAuthTokens(instance)\n return\n }\n const token = authState.type === AuthStateType.LOGGED_IN && authState.token\n\n try {\n if (token) {\n logger.info('Logging out user')\n state.set('loggingOut', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: true},\n })\n\n const client = clientFactory({\n token,\n requestTagPrefix: REQUEST_TAG_PREFIX,\n apiVersion: DEFAULT_API_VERSION,\n ...(apiHost && {apiHost}),\n useProjectHostname: false,\n useCdn: false,\n })\n\n logger.debug('Calling logout endpoint')\n await client.request<void>({url: '/auth/logout', method: 'POST', tag: 'logout'})\n } else {\n logger.debug('No token to logout - already logged out')\n }\n } catch (error) {\n // Re-throw to preserve the existing contract: logout rejects when the\n // request fails. Local state is still cleared in the finally block.\n logger.error('Logout request failed', {error})\n throw error\n } finally {\n logger.info('User logged out, clearing stored tokens')\n state.set('logoutSuccess', {\n authState: {type: AuthStateType.LOGGED_OUT, isDestroyingSession: false},\n oauthTokens: undefined,\n })\n storageArea?.removeItem(storageKey)\n storageArea?.removeItem(`${storageKey}_last_refresh`)\n }\n})\n","function weaken(node: unknown): unknown {\n if (Array.isArray(node)) {\n return node.map(weaken)\n }\n\n if (node && typeof node === 'object') {\n if ('_ref' in node) {\n return {...node, _weak: true}\n }\n\n return Object.fromEntries(Object.entries(node).map(([key, value]) => [key, weaken(value)]))\n }\n\n return node\n}\n\n/**\n * Marks every reference in a content snapshot weak.\n *\n * A comment's snapshot is written into the addon dataset, but its references\n * point at documents in the main one. Left strong, Content Lake would refuse to\n * delete any document a snapshot happens to mention, so an old comment could\n * pin a document forever.\n *\n * @internal\n */\nexport function weakenReferencesInContentSnapshot(snapshot: unknown): unknown {\n return weaken(snapshot)\n}\n","import {type SanityClient} from '@sanity/client'\nimport {DocumentId, getPublishedId} from '@sanity/id-utils'\nimport {type Path} from '@sanity/types'\nimport {filter, firstValueFrom, take} from 'rxjs'\n\nimport {getCurrentUserState} from '../auth/authStore'\nimport {getClient} from '../client/clientStore'\nimport {\n type DatasetHandle,\n type DatasetResource,\n type DocumentHandle,\n type DocumentResource,\n} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type StoreState} from '../store/createStoreState'\nimport {type StoreContext} from '../store/defineStore'\nimport {randomUuid} from '../utils/ids'\nimport {\n assertDatasetResource,\n getAddonDatasetState,\n provisionAddonDataset,\n} from './addonDatasetStore'\nimport {toCommentFieldPath} from './commentFieldPath'\nimport {COMMENTS_API_VERSION} from './commentsConstants'\nimport {type CommentsOptions, commentsStore, toCommentsKeyParts} from './commentsStore'\nimport {normalizeComment} from './normalizeComment'\nimport {\n addComment,\n applyCommentUpdate,\n clearPendingTransaction,\n type CommentsStoreState,\n getCommentsKey,\n receiveComment,\n removeCommentById,\n restoreComments,\n rollbackCommentUpdate,\n setCommentCreateError,\n setPendingTransaction,\n} from './reducers'\nimport {\n type Comment,\n type CommentMessage,\n type CommentPostPayload,\n type CommentStatus,\n type CommentTextSelection,\n type StoredComment,\n} from './types'\nimport {weakenReferencesInContentSnapshot} from './weakenReferences'\n\n/** @beta */\nexport interface CreateCommentOptions extends DocumentHandle {\n message: CommentMessage\n /**\n * Which field the thread hangs off, for example `title` or\n * `body[_key==\"intro\"].content`.\n *\n * Required, and it has to resolve to a real path. There is no such thing as a\n * comment on a document as a whole: the Studio only ever creates field\n * comments, and its comment inspector throws on an empty path rather than\n * ignoring it, so a pathless comment takes the inspector down for everyone\n * looking at that document.\n */\n fieldPath: string | Path\n /**\n * Anchors the comment to a run of text inside a Portable Text field. Building\n * one needs a live editor, so this comes from\n * `@portabletext/plugin-sdk-value` rather than being constructed by hand.\n */\n selection?: CommentTextSelection\n /** Reuse the id of a failed comment to retry it. Defaults to a new id. */\n commentId?: string\n /** Defaults to a new id, which starts a new thread. */\n threadId?: string\n /** @defaultValue `'open'` */\n status?: CommentStatus\n /**\n * The `_rev` of the document when the comment was written.\n *\n * Not filled in automatically: the SDK's local document revision is replaced\n * by a transaction id while an edit is in flight, so it can name a revision\n * that never reached the server. Nothing in the Studio reads this today.\n */\n documentRevisionId?: string\n /**\n * Ambient information stored alongside the comment, merged over the defaults.\n *\n * The Studio writes `tool`, `payload`, `intent`, and `notification` here for\n * its notification backend. The SDK has no source for any of them, so\n * mentions in comments created this way do not send email unless you supply\n * `notification` yourself. Deliberately loose: the organization-level store\n * treats this as free-form, so no shape is worth promising.\n */\n context?: Record<string, unknown>\n /** A copy of the content being discussed. References in it are weakened. */\n contentSnapshot?: unknown\n}\n\n/** @beta */\nexport interface ReplyToCommentOptions extends DocumentHandle {\n /** The comment being replied to. Replies to a reply join the same thread. */\n parentCommentId: string\n message: CommentMessage\n commentId?: string\n /** Only needed when the parent is not loaded, such as following a deep link. */\n threadId?: string\n /** Only needed when the parent is not loaded. */\n fieldPath?: string | Path\n /** Required when the parent is not loaded. */\n status?: CommentStatus\n}\n\n/** @beta */\nexport interface UpdateCommentOptions extends DatasetHandle {\n commentId: string\n message: CommentMessage\n}\n\n/** @beta */\nexport interface SetCommentStatusOptions extends DatasetHandle {\n /** The thread's first comment. Replies follow their parent. */\n commentId: string\n status: CommentStatus\n}\n\n/** @beta */\nexport interface RemoveCommentOptions extends DatasetHandle {\n commentId: string\n}\n\n/**\n * Refuses to write a comment that points at nothing.\n *\n * The type already requires `fieldPath`, but an empty string or an empty path\n * array both survive that and normalise to `''`. Such a comment is not merely\n * useless: the Studio's comment inspector calls `fromString` on the stored\n * path, which throws on `''`, so the inspector crashes for everyone viewing\n * that document until the comment is deleted. Cheaper to refuse the write.\n */\nfunction requireFieldPath(fieldPath: string | Path): string {\n const normalized = toCommentFieldPath(fieldPath)\n if (!normalized) {\n throw new Error(\n 'A comment needs a field path. Comments attach to a field, not to a document as a whole.',\n )\n }\n return normalized\n}\n\nfunction requireCurrentUserId(instance: SanityInstance): string {\n const userId = getCurrentUserState(instance).getCurrent()?.id\n if (!userId) {\n throw new Error('Writing a comment requires a logged in user.')\n }\n return userId\n}\n\n/** Waits out discovery, then insists the addon dataset exists. */\nasync function requireAddonDataset(\n instance: SanityInstance,\n resource: DocumentResource,\n): Promise<string> {\n const datasetName = await firstValueFrom(\n getAddonDatasetState(instance, {resource}).observable.pipe(\n filter((value) => value !== undefined),\n take(1),\n ),\n )\n\n if (!datasetName) {\n throw new Error('This project has no comments dataset, so there is no comment to change.')\n }\n\n return datasetName\n}\n\nasync function getWritableClient(\n instance: SanityInstance,\n resource: DocumentResource,\n {createIfMissing}: {createIfMissing: boolean},\n): Promise<SanityClient> {\n const {projectId} = assertDatasetResource(resource)\n\n // Provisioning is a project-wide side effect, so only the create path may\n // trigger it. Editing or deleting a comment implies the dataset is there.\n const dataset = createIfMissing\n ? await provisionAddonDataset(instance, {resource})\n : await requireAddonDataset(instance, resource)\n\n return getClient(instance, {apiVersion: COMMENTS_API_VERSION, projectId, dataset})\n}\n\nfunction buildCommentPayload(options: {\n authorId: string\n commentId: string\n contentSnapshot?: unknown\n context?: Record<string, unknown>\n documentRevisionId?: string\n handle: DocumentHandle\n message: CommentMessage\n parentCommentId?: string\n resource: DatasetResource\n fieldPath: string\n selection?: CommentTextSelection\n status: CommentStatus\n threadId: string\n documentVersionId?: string\n}): CommentPostPayload {\n const {handle, resource} = options\n\n return {\n _id: options.commentId,\n _type: 'comment',\n authorId: options.authorId,\n message: options.message,\n threadId: options.threadId,\n ...(options.parentCommentId ? {parentCommentId: options.parentCommentId} : {}),\n status: options.status,\n reactions: null,\n // The Studio writes an empty tool name when no tool is active, and reads\n // this nowhere in its UI.\n context: {tool: '', ...options.context},\n ...(options.contentSnapshot === undefined\n ? {}\n : {contentSnapshot: weakenReferencesInContentSnapshot(options.contentSnapshot)}),\n target: {\n documentRevisionId: options.documentRevisionId ?? '',\n path: {\n field: options.fieldPath,\n ...(options.selection ? {selection: options.selection} : {}),\n },\n // The comment lives in the addon dataset, so this points across to the\n // dataset holding the document. Weak, or Content Lake would refuse to\n // delete a commented document.\n document: {\n _dataset: resource.dataset,\n _projectId: resource.projectId,\n _ref: getPublishedId(DocumentId(handle.documentId)),\n _type: 'crossDatasetReference',\n _weak: true,\n },\n documentType: handle.documentType,\n ...(options.documentVersionId ? {documentVersionId: options.documentVersionId} : {}),\n },\n }\n}\n\nfunction findComment(\n state: StoreState<CommentsStoreState>,\n commentsKey: string,\n commentId: string,\n): StoredComment | undefined {\n const comments = state.get().entries[commentsKey]?.comments\n return comments && Object.hasOwn(comments, commentId) ? comments[commentId] : undefined\n}\n\nfunction findCommentById(\n state: StoreState<CommentsStoreState>,\n commentId: string,\n): StoredComment | undefined {\n for (const entry of Object.values(state.get().entries)) {\n const comments = entry?.comments\n const comment = comments && Object.hasOwn(comments, commentId) ? comments[commentId] : undefined\n if (comment) return comment\n }\n return undefined\n}\n\nasync function postComment(\n context: StoreContext<CommentsStoreState, BoundResourceKey>,\n handle: DocumentHandle & CommentsOptions,\n payload: CommentPostPayload,\n): Promise<Comment> {\n const {state, instance, key} = context\n const commentsKey = getCommentsKey(toCommentsKeyParts(instance, handle))\n\n // Show it before the round trip, so a thread feels immediate.\n state.set('addComment', addComment(commentsKey, payload))\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: true})\n // `createIfNotExists` makes duplicate writes of the same id harmless, which\n // covers both a retry after a lost response and two concurrent calls with\n // the same comment id.\n const created = await client.createIfNotExists(payload, {tag: 'comments.create'})\n state.set('receiveComment', receiveComment(commentsKey, created))\n return normalizeComment(created)\n } catch (error) {\n // Keep it on screen carrying the failure, so an app can offer a retry with\n // the same id rather than silently losing what someone typed.\n state.set(\n 'setCommentCreateError',\n setCommentCreateError(payload._id, error instanceof Error ? error : new Error(String(error))),\n )\n throw error\n }\n}\n\n/**\n * Starts a thread on a document, or on one of its fields.\n *\n * Creates the project's comments dataset if this is the first comment anywhere\n * in it. The comment appears locally before the server confirms it; if the\n * write fails it stays, carrying `_state.createError`, and retrying with the\n * same `commentId` replaces it.\n *\n * @beta\n */\nexport const createComment: (\n instance: SanityInstance,\n options: CreateCommentOptions,\n) => Promise<Comment> = bindActionByResource(\n commentsStore,\n // `async` so a validation failure rejects rather than throwing at the call\n // site, which would slip past a `.catch()`.\n async (\n context: StoreContext<CommentsStoreState, BoundResourceKey>,\n options: CreateCommentOptions,\n ) => {\n const {instance, key} = context\n const perspective = options.perspective ?? instance.config.perspective\n const fieldPath = requireFieldPath(options.fieldPath)\n\n return postComment(\n context,\n options,\n buildCommentPayload({\n authorId: requireCurrentUserId(instance),\n commentId: options.commentId ?? randomUuid(),\n contentSnapshot: options.contentSnapshot,\n context: options.context,\n documentRevisionId: options.documentRevisionId,\n handle: options,\n message: options.message,\n resource: assertDatasetResource(key.resource),\n fieldPath,\n selection: options.selection,\n status: options.status ?? 'open',\n threadId: options.threadId ?? randomUuid(),\n ...(isReleasePerspective(perspective) ? {documentVersionId: perspective.releaseName} : {}),\n }),\n )\n },\n)\n\n/**\n * Adds a reply to an existing thread.\n *\n * The thread and field are taken from the parent comment, so a reply always\n * matches the same list as the comment it answers. Supply them only when the\n * parent is not loaded.\n *\n * @beta\n */\nexport const replyToComment: (\n instance: SanityInstance,\n options: ReplyToCommentOptions,\n) => Promise<Comment> = bindActionByResource(\n commentsStore,\n async (\n context: StoreContext<CommentsStoreState, BoundResourceKey>,\n options: ReplyToCommentOptions,\n ) => {\n const {instance, key, state} = context\n const commentsKey = getCommentsKey(toCommentsKeyParts(instance, options))\n const parent = findComment(state, commentsKey, options.parentCommentId)\n\n const threadId = options.threadId ?? parent?.threadId\n if (!threadId) {\n throw new Error(\n `Cannot reply to \"${options.parentCommentId}\": it is not loaded, so pass its threadId.`,\n )\n }\n\n const status = parent?.status ?? options.status\n if (!status) {\n throw new Error(\n `Cannot reply to \"${options.parentCommentId}\": it is not loaded, so pass its status.`,\n )\n }\n\n const perspective = options.perspective ?? instance.config.perspective\n\n return postComment(\n context,\n options,\n buildCommentPayload({\n authorId: requireCurrentUserId(instance),\n commentId: options.commentId ?? randomUuid(),\n handle: options,\n message: options.message,\n // Replies to a reply belong to the thread's first comment, matching how\n // the Studio flattens threads.\n parentCommentId: parent?.parentCommentId ?? options.parentCommentId,\n resource: assertDatasetResource(key.resource),\n // Inherited from the parent unless the caller names one, and checked\n // either way: a reply with no path crashes the Studio inspector exactly\n // as a pathless thread would.\n fieldPath: requireFieldPath(options.fieldPath ?? parent?.target.path?.field ?? ''),\n // A reply into a resolved thread stays consistent with its parent.\n status,\n threadId,\n ...(isReleasePerspective(perspective) ? {documentVersionId: perspective.releaseName} : {}),\n }),\n )\n },\n)\n\n/**\n * Rewrites a comment's message and stamps `lastEditedAt`.\n * @beta\n */\nexport const updateComment: (\n instance: SanityInstance,\n options: UpdateCommentOptions,\n) => Promise<void> = bindActionByResource(\n commentsStore,\n async (\n {state, instance, key}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {commentId, message}: UpdateCommentOptions,\n ) => {\n const lastEditedAt = new Date().toISOString()\n const transactionId = randomUuid()\n const previous = findCommentById(state, commentId)\n\n if (previous) {\n state.set('setPendingTransaction', setPendingTransaction(commentId, transactionId))\n state.set('updateComment', applyCommentUpdate(commentId, {message, lastEditedAt}))\n }\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: false})\n await client\n .transaction()\n .transactionId(transactionId)\n .patch(client.patch(commentId).set({message, lastEditedAt}))\n .commit({tag: 'comments.update'})\n if (previous) {\n state.set('clearPendingTransaction', clearPendingTransaction(commentId))\n }\n } catch (error) {\n if (previous) {\n state.set(\n 'rollbackCommentUpdate',\n rollbackCommentUpdate(commentId, transactionId, previous),\n )\n }\n throw error\n }\n },\n)\n\n/**\n * Resolves or reopens a thread.\n *\n * Pass the thread's first comment: replies follow it, patched server-side in\n * one query so a reply created by someone else at the same moment is caught\n * too.\n *\n * @beta\n */\nexport const setCommentStatus: (\n instance: SanityInstance,\n options: SetCommentStatusOptions,\n) => Promise<void> = bindActionByResource(\n commentsStore,\n async (\n {state, instance, key}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {commentId, status}: SetCommentStatusOptions,\n ) => {\n const transactionId = randomUuid()\n const previousComments: StoredComment[] = []\n\n for (const entry of Object.values(state.get().entries)) {\n for (const candidate of Object.values(entry?.comments ?? {})) {\n if (candidate._id !== commentId && candidate.parentCommentId !== commentId) continue\n previousComments.push(candidate)\n state.set('setPendingTransaction', setPendingTransaction(candidate._id, transactionId))\n state.set('updateCommentStatus', applyCommentUpdate(candidate._id, {status}))\n }\n }\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: false})\n\n await client.mutate(\n [\n {patch: {id: commentId, set: {status}}},\n {\n patch: {\n query: '*[_type == \"comment\" && parentCommentId == $commentId]',\n params: {commentId},\n set: {status},\n },\n },\n ],\n {transactionId, tag: 'comments.set-status'},\n )\n for (const previous of previousComments) {\n state.set('clearPendingTransaction', clearPendingTransaction(previous._id))\n }\n } catch (error) {\n for (const previous of previousComments) {\n state.set(\n 'rollbackCommentUpdate',\n rollbackCommentUpdate(previous._id, transactionId, previous),\n )\n }\n throw error\n }\n },\n)\n\n/**\n * Deletes a comment, and its replies when it starts a thread.\n * @beta\n */\nexport const removeComment: (\n instance: SanityInstance,\n options: RemoveCommentOptions,\n) => Promise<void> = bindActionByResource(\n commentsStore,\n async (\n {state, instance, key}: StoreContext<CommentsStoreState, BoundResourceKey>,\n {commentId}: RemoveCommentOptions,\n ) => {\n const removed = Object.entries(state.get().entries).flatMap(([entryKey, entry]) => {\n const comments = Object.values(entry?.comments ?? {}).filter(\n (comment) => comment._id === commentId || comment.parentCommentId === commentId,\n )\n return comments.length ? [{key: entryKey, comments}] : []\n })\n\n state.set('removeComment', removeCommentById(commentId))\n\n try {\n const client = await getWritableClient(instance, key.resource, {createIfMissing: false})\n await client.mutate(\n [\n {\n delete: {\n query: '*[_type == \"comment\" && parentCommentId == $commentId]',\n params: {commentId},\n },\n },\n {delete: {id: commentId}},\n ],\n {tag: 'comments.remove'},\n )\n } catch (error) {\n state.set('restoreComments', restoreComments(removed))\n throw error\n }\n },\n)\n","import {\n type DatasetHandle,\n type DocumentHandle,\n type DocumentTypeHandle,\n type ProjectHandle,\n} from './sanityConfig'\n\n/**\n * Creates or validates a `DocumentHandle` object.\n * Ensures the provided object conforms to the `DocumentHandle` interface.\n *\n * This signature applies when the handle passes both `projectId` and `dataset` as strings. The\n * returned handle keeps both fields required and typed as the values passed, so\n * `createDocumentHandle({projectId: 'abc123', dataset: 'production', ...}).projectId` is typed\n * `'abc123'` instead of `'abc123' | undefined`. A field typed `string | undefined`, such as one\n * spread from a `SanityConfig`, uses the next signature and stays optional. `TDataset` and\n * `TProjectId` are `const` so a call written where a typed handle is expected, such as an\n * annotated variable or a prop typed `DocumentHandle<'book', 'production', 'abc123'>`, keeps the\n * literals instead of inferring `string`.\n * @param handle - The object containing document identification properties.\n * @returns The validated `DocumentHandle` object, with `projectId` and `dataset` required.\n * @public\n */\nexport function createDocumentHandle<\n TDocumentType extends string = string,\n const TDataset extends string = string,\n const TProjectId extends string = string,\n>(\n handle: DocumentHandle<TDocumentType, TDataset, TProjectId> & {\n dataset: TDataset\n projectId: TProjectId\n },\n): DocumentHandle<TDocumentType, TDataset, TProjectId> & {dataset: TDataset; projectId: TProjectId}\n\n/**\n * Creates or validates a `DocumentHandle` object.\n * Ensures the provided object conforms to the `DocumentHandle` interface.\n * @param handle - The object containing document identification properties.\n * @returns The validated `DocumentHandle` object.\n * @public\n */\nexport function createDocumentHandle<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n handle: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): DocumentHandle<TDocumentType, TDataset, TProjectId>\n\n/** @public */\nexport function createDocumentHandle(handle: DocumentHandle): DocumentHandle {\n return handle\n}\n\n/**\n * Creates or validates a `DocumentTypeHandle` object.\n * Ensures the provided object conforms to the `DocumentTypeHandle` interface.\n *\n * This signature applies when the handle passes both `projectId` and `dataset` as strings. The\n * returned handle keeps both fields required and typed as the values passed.\n * @param handle - The object containing document type identification properties.\n * @returns The validated `DocumentTypeHandle` object, with `projectId` and `dataset` required.\n * @public\n */\nexport function createDocumentTypeHandle<\n TDocumentType extends string = string,\n const TDataset extends string = string,\n const TProjectId extends string = string,\n>(\n handle: DocumentTypeHandle<TDocumentType, TDataset, TProjectId> & {\n dataset: TDataset\n projectId: TProjectId\n },\n): DocumentTypeHandle<TDocumentType, TDataset, TProjectId> & {\n dataset: TDataset\n projectId: TProjectId\n}\n\n/**\n * Creates or validates a `DocumentTypeHandle` object.\n * Ensures the provided object conforms to the `DocumentTypeHandle` interface.\n * @param handle - The object containing document type identification properties.\n * @returns The validated `DocumentTypeHandle` object.\n * @public\n */\nexport function createDocumentTypeHandle<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n handle: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>,\n): DocumentTypeHandle<TDocumentType, TDataset, TProjectId>\n\n/** @public */\nexport function createDocumentTypeHandle(handle: DocumentTypeHandle): DocumentTypeHandle {\n return handle\n}\n\n/**\n * Creates or validates a `ProjectHandle` object.\n * Ensures the provided object conforms to the `ProjectHandle` interface.\n * @param handle - The object containing project identification properties.\n * @returns The validated `ProjectHandle` object.\n * @public\n */\nexport function createProjectHandle<TProjectId extends string = string>(\n handle: ProjectHandle<TProjectId>,\n): ProjectHandle<TProjectId> {\n return handle\n}\n\n/**\n * Creates or validates a `DatasetHandle` object.\n * Ensures the provided object conforms to the `DatasetHandle` interface.\n *\n * This signature applies when the handle passes both `projectId` and `dataset` as strings. The\n * returned handle keeps both fields required and typed as the values passed.\n * @param handle - The object containing dataset identification properties.\n * @returns The validated `DatasetHandle` object, with `projectId` and `dataset` required.\n * @public\n */\nexport function createDatasetHandle<\n const TDataset extends string = string,\n const TProjectId extends string = string,\n>(\n handle: DatasetHandle<TDataset, TProjectId> & {dataset: TDataset; projectId: TProjectId},\n): DatasetHandle<TDataset, TProjectId> & {dataset: TDataset; projectId: TProjectId}\n\n/**\n * Creates or validates a `DatasetHandle` object.\n * Ensures the provided object conforms to the `DatasetHandle` interface.\n * @param handle - The object containing dataset identification properties.\n * @returns The validated `DatasetHandle` object.\n * @public\n */\nexport function createDatasetHandle<\n TDataset extends string = string,\n TProjectId extends string = string,\n>(handle: DatasetHandle<TDataset, TProjectId>): DatasetHandle<TDataset, TProjectId>\n\n/** @public */\nexport function createDatasetHandle(handle: DatasetHandle): DatasetHandle {\n return handle\n}\n","/**\n * Public API for configuring SDK logging\n *\n * @module loggingConfig\n */\n\nimport {configureLogging as _configureLogging, type LoggerConfig} from '../utils/logger'\n\n/**\n * Configure logging for the Sanity SDK\n *\n * This function allows you to control what logs are output by the SDK,\n * making it easier to debug issues in development or production.\n *\n * @remarks\n * **Zero-Config via Environment Variable (Recommended):**\n *\n * The SDK automatically reads the `DEBUG` environment variable, making it\n * easy to enable logging without code changes:\n *\n * ```bash\n * # Enable all SDK logging at debug level\n * DEBUG=sanity:* npm start\n *\n * # Enable specific namespaces\n * DEBUG=sanity:auth,sanity:document npm start\n *\n * # Enable trace level for all namespaces\n * DEBUG=sanity:trace:* npm start\n *\n * # Enable internal/maintainer logs\n * DEBUG=sanity:*:internal npm start\n * ```\n *\n * This matches the pattern used by Sanity CLI and Studio, making it familiar\n * and easy for support teams to help troubleshoot issues.\n *\n * **Programmatic Configuration (Advanced):**\n *\n * For more control (custom handlers, dynamic configuration), call this function\n * explicitly. Programmatic configuration overrides environment variables.\n *\n * **For Application Developers:**\n * Use `info`, `warn`, or `error` levels to see high-level SDK activity\n * without being overwhelmed by internal details.\n *\n * **For SDK Maintainers:**\n * Use `debug` or `trace` levels with `internal: true` to see detailed\n * information about store operations, RxJS streams, and state transitions.\n *\n * **Instance Context:**\n * Logs automatically include instance information (projectId, dataset, instanceId)\n * when available, making it easier to debug multi-instance scenarios:\n * ```\n * [INFO] [auth] [project:abc] [dataset:production] User logged in\n * ```\n *\n * **Available Namespaces:**\n * - `sdk` - SDK initialization, configuration, and lifecycle\n * - `auth` - Authentication and authorization (when instrumented in the future)\n * - And more as logging is added to modules\n *\n * @example Zero-config via environment variable (recommended for debugging)\n * ```bash\n * # Just set DEBUG and run your app - no code changes needed!\n * DEBUG=sanity:* npm start\n * ```\n *\n * @example Programmatic configuration (application developer)\n * ```ts\n * import {configureLogging} from '@sanity/sdk'\n *\n * // Log warnings and errors for auth and document operations\n * configureLogging({\n * level: 'warn',\n * namespaces: ['auth', 'document']\n * })\n * ```\n *\n * @example Programmatic configuration (SDK maintainer)\n * ```ts\n * import {configureLogging} from '@sanity/sdk'\n *\n * // Enable all logs including internal traces\n * configureLogging({\n * level: 'trace',\n * namespaces: ['*'],\n * internal: true\n * })\n * ```\n *\n * @example Custom handler (for testing)\n * ```ts\n * import {configureLogging} from '@sanity/sdk'\n *\n * const logs: string[] = []\n * configureLogging({\n * level: 'info',\n * namespaces: ['*'],\n * handler: {\n * error: (msg) => logs.push(msg),\n * warn: (msg) => logs.push(msg),\n * info: (msg) => logs.push(msg),\n * debug: (msg) => logs.push(msg),\n * trace: (msg) => logs.push(msg),\n * }\n * })\n * ```\n *\n * @public\n */\nexport function configureLogging(config: LoggerConfig): void {\n _configureLogging(config)\n\n // Always log configuration (bypasses namespace filtering)\n // This ensures users see the message regardless of which namespaces they enable\n const configLevel = config.level || 'warn'\n const shouldLog = ['info', 'debug', 'trace'].includes(configLevel) || configLevel === 'warn'\n\n if (shouldLog && config.handler?.info) {\n config.handler.info(`[${new Date().toISOString()}] [INFO] [sdk] Logging configured`, {\n level: configLevel,\n namespaces: config.namespaces || [],\n internal: config.internal || false,\n source: 'programmatic',\n })\n } else if (shouldLog) {\n // eslint-disable-next-line no-console\n console.info(`[${new Date().toISOString()}] [INFO] [sdk] Logging configured`, {\n level: configLevel,\n namespaces: config.namespaces || [],\n internal: config.internal || false,\n source: 'programmatic',\n })\n }\n}\n\n/**\n * Re-export types for public API\n * @public\n */\nexport type {\n InstanceContext,\n LogContext,\n Logger,\n LoggerConfig,\n LogLevel,\n LogNamespace,\n} from '../utils/logger'\n","import {type DatasetsResponse} from '@sanity/client'\nimport {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type ProjectHandle} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\n\nconst API_VERSION = 'v2025-02-19'\n\nfunction resolveProjectId(instance: SanityInstance, options?: ProjectHandle): string {\n const projectId = options?.projectId ?? instance.config.projectId\n if (!projectId) {\n throw new Error('A projectId is required to use the datasets API.')\n }\n return projectId\n}\n\n/**\n * Fetcher for a project's datasets (`GET /datasets`), on the shared fetcher\n * cache.\n *\n * @internal\n */\nexport const datasets = defineFetcher<[options?: ProjectHandle], DatasetsResponse>({\n name: 'datasets',\n getKey: resolveProjectId,\n fetch: (instance) => (options) =>\n getClientState(instance, {\n apiVersion: API_VERSION,\n projectId: resolveProjectId(instance, options),\n useProjectHostname: true,\n }).observable.pipe(switchMap((client) => client.observable.datasets.list())),\n})\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\n\n/**\n * The id of the specific document a user is editing under a given perspective:\n * the draft, the published document, or a release version.\n *\n * Distinct from {@link getEffectiveDocumentId}, which resolves the canonical key a\n * document's state is stored under and so collapses a draft onto its published id.\n * This one keeps them apart, because some things compare the exact id. Presence is\n * the motivating case: the Studio's field indicators match the id its form is on,\n * exactly, so an app reporting the published id shows up in the Studio at document\n * level while never lighting up a single field.\n *\n * @remarks\n * Resolved from the handle alone, which is right whenever a draft or version\n * exists, so for any document actually being edited. The Studio additionally falls\n * back to whichever document exists, so for a published document nobody has edited\n * yet its form sits on the published id where this returns the draft id. Matching\n * that would mean reading document state, which presence deliberately does not\n * depend on.\n *\n * @internal\n */\nexport function getEditingDocumentId(\n doc: Pick<DocumentHandle, 'documentId' | 'liveEdit' | 'perspective'>,\n): string {\n if (doc.liveEdit) {\n return getPublishedId(DocumentId(doc.documentId))\n }\n if (isReleasePerspective(doc.perspective)) {\n return getVersionId(DocumentId(doc.documentId), doc.perspective.releaseName)\n }\n if (doc.perspective === 'published') {\n return getPublishedId(DocumentId(doc.documentId))\n }\n return getDraftId(DocumentId(doc.documentId))\n}\n\nexport function getEffectiveDocumentId(doc: DocumentHandle): string {\n if (doc.liveEdit) {\n return doc.documentId\n } else if (isReleasePerspective(doc.perspective)) {\n return getVersionId(DocumentId(doc.documentId), doc.perspective.releaseName)\n } else {\n return getPublishedId(DocumentId(doc.documentId))\n }\n}\n","import {type ReleaseDocument} from '@sanity/client'\nimport {SanityEncoder} from '@sanity/mutate'\nimport {type PatchMutation as SanityMutatePatchMutation} from '@sanity/mutate/_unstable_store'\nimport {type PatchMutation, type PatchOperations} from '@sanity/types'\n\nimport {\n type DocumentHandle,\n type DocumentTypeHandle,\n type ReleaseHandle,\n} from '../config/sanityConfig'\nimport {type ResolveDocument} from '../typegen/resolve'\nimport {getEffectiveDocumentId} from './util'\n\nconst isSanityMutatePatch = (value: unknown): value is SanityMutatePatchMutation => {\n if (typeof value !== 'object' || !value) return false\n if (!('type' in value) || typeof value.type !== 'string' || value.type !== 'patch') return false\n if (!('id' in value) || typeof value.id !== 'string') return false\n if (!('patches' in value) || !Array.isArray(value.patches)) return false\n return true\n}\n\n/**\n * Represents an action to create a new document.\n * Specifies the document type and optionally a document ID (which will be treated as the published ID).\n * @beta\n */\nexport interface CreateDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentTypeHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.create'\n /**\n * Optional initial field values for the document.\n * These values will be set when the document is created.\n * System fields (_id, _type, _rev, _createdAt, _updatedAt) are omitted as they are set automatically.\n */\n initialValue?: Partial<\n Omit<\n ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>,\n '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'\n >\n >\n}\n\n/**\n * Represents an action to delete an existing document.\n * Requires the full document handle including the document ID.\n * @beta\n */\nexport interface DeleteDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.delete'\n}\n\n/**\n * Represents an action to edit an existing document using patches.\n * Requires the full document handle and an array of patch operations.\n * @beta\n */\nexport interface EditDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.edit'\n patches?: PatchOperations[]\n /**\n * When `true`, the given `patches` are forwarded verbatim to the server and\n * applied as-is locally, instead of being re-derived by diffing document\n * snapshots. This preserves the operational intent of the patches (e.g.\n * keyed array inserts/unsets), which lets concurrent edits from other\n * clients interleave coherently instead of being overwritten by\n * position-based patches computed from a stale snapshot.\n *\n * Use this when the patches were produced by an editor that tracks its own\n * operations (e.g. a collaborative text editor). During a rebase onto a\n * changed remote document, preserved patches are re-applied operationally;\n * if a patch can no longer apply, the transaction is skipped and reported\n * as a `rebase-error` document event.\n */\n preserveOperations?: boolean\n}\n\n/**\n * Represents an action to publish the draft version of a document.\n * Requires the full document handle.\n * @beta\n */\nexport interface PublishDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.publish'\n}\n\n/**\n * Represents an action to unpublish a document, moving its published content to a draft.\n * Requires the full document handle.\n * @beta\n */\nexport interface UnpublishDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.unpublish'\n}\n\n/**\n * Represents an action to discard the draft changes of a document.\n * Requires the full document handle.\n * @beta\n */\nexport interface DiscardDocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n type: 'document.discard'\n}\n\n/**\n * Union type representing all possible document actions within the SDK.\n * @beta\n */\nexport type DocumentAction<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> =\n | CreateDocumentAction<TDocumentType, TDataset, TProjectId>\n | DeleteDocumentAction<TDocumentType, TDataset, TProjectId>\n | EditDocumentAction<TDocumentType, TDataset, TProjectId>\n | PublishDocumentAction<TDocumentType, TDataset, TProjectId>\n | UnpublishDocumentAction<TDocumentType, TDataset, TProjectId>\n | DiscardDocumentAction<TDocumentType, TDataset, TProjectId>\n\n/**\n * Union of every action accepted by `applyDocumentActions` — both document-\n * level actions and release-lifecycle actions.\n * @beta\n */\nexport type Action<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> = DocumentAction<TDocumentType, TDataset, TProjectId> | ReleaseAction\n\n/**\n * Creates a `CreateDocumentAction` object.\n * @param doc - A handle identifying the document type, dataset, and project. An optional `documentId` can be provided.\n * @param initialValue - Optional initial field values for the document. (System fields are omitted as they are set automatically.)\n * @returns A `CreateDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function createDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentTypeHandle<TDocumentType, TDataset, TProjectId>,\n initialValue?: Partial<\n Omit<\n ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>,\n '_id' | '_type' | '_rev' | '_createdAt' | '_updatedAt'\n >\n >,\n): CreateDocumentAction<TDocumentType, TDataset, TProjectId> {\n // users may pass in an explicit documentId -- make sure we format it correctly for the action\n let effectiveDocumentId\n if (typeof doc.documentId === 'string') {\n effectiveDocumentId = getEffectiveDocumentId({...doc, documentId: doc.documentId})\n }\n return {\n type: 'document.create',\n ...doc,\n ...(effectiveDocumentId && {documentId: effectiveDocumentId}),\n ...(initialValue && {initialValue}),\n }\n}\n\n/**\n * Creates a `DeleteDocumentAction` object.\n * @param doc - A handle uniquely identifying the document to be deleted.\n * @returns A `DeleteDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function deleteDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): DeleteDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.delete',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\nfunction convertSanityMutatePatch(\n sanityPatchMutation: SanityMutatePatchMutation,\n): EditDocumentAction['patches'] {\n const encoded = SanityEncoder.encode(sanityPatchMutation) as PatchMutation[]\n return encoded.map((i) => {\n const copy: PatchOperations = {...i.patch}\n if ('id' in copy) delete copy.id\n return copy\n })\n}\n\n/**\n * Options for creating an `EditDocumentAction`.\n * @beta\n */\nexport interface EditDocumentOptions {\n /**\n * Forward the given patches verbatim instead of re-deriving them by\n * diffing document snapshots. See {@link EditDocumentAction.preserveOperations}.\n */\n preserveOperations?: boolean\n}\n\n/**\n * Creates an `EditDocumentAction` object with patches for modifying a document.\n * Accepts patches in either the standard `PatchOperations` format or as a `SanityMutatePatchMutation` from `@sanity/mutate`.\n *\n * @param doc - A handle uniquely identifying the document to be edited.\n * @param sanityMutatePatch - A patch mutation object from `@sanity/mutate`.\n * @param options - Options controlling how the patches are processed.\n * @returns An `EditDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function editDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n sanityMutatePatch: SanityMutatePatchMutation,\n options?: EditDocumentOptions,\n): EditDocumentAction<TDocumentType, TDataset, TProjectId>\n/**\n * Creates an `EditDocumentAction` object with patches for modifying a document.\n *\n * @param doc - A handle uniquely identifying the document to be edited.\n * @param patches - A single patch operation or an array of patch operations.\n * @param options - Options controlling how the patches are processed.\n * @returns An `EditDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function editDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n patches?: PatchOperations | PatchOperations[],\n options?: EditDocumentOptions,\n): EditDocumentAction<TDocumentType, TDataset, TProjectId>\n/**\n * Creates an `EditDocumentAction` object with patches for modifying a document.\n * This is the implementation signature and handles the different patch input types.\n *\n * @param doc - A handle uniquely identifying the document to be edited.\n * @param patches - Patches in various formats (`PatchOperations`, `PatchOperations[]`, or `SanityMutatePatchMutation`).\n * @param options - Options controlling how the patches are processed.\n * @returns An `EditDocumentAction` object ready for dispatch.\n */\nexport function editDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n patches?: PatchOperations | PatchOperations[] | SanityMutatePatchMutation,\n options?: EditDocumentOptions,\n): EditDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n const preserveOperations = options?.preserveOperations && {preserveOperations: true as const}\n\n if (isSanityMutatePatch(patches)) {\n const converted = convertSanityMutatePatch(patches) ?? []\n return {\n ...doc,\n type: 'document.edit',\n documentId: effectiveDocumentId,\n patches: converted,\n ...preserveOperations,\n }\n }\n\n return {\n ...doc,\n type: 'document.edit',\n documentId: effectiveDocumentId,\n ...(patches && {patches: Array.isArray(patches) ? patches : [patches]}),\n ...preserveOperations,\n }\n}\n\n/**\n * Creates a `PublishDocumentAction` object.\n * @param doc - A handle uniquely identifying the document to be published.\n * @returns A `PublishDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function publishDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): PublishDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.publish',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\n/**\n * Creates an `UnpublishDocumentAction` object.\n * @param doc - A handle uniquely identifying the document to be unpublished.\n * @returns An `UnpublishDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function unpublishDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): UnpublishDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.unpublish',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\n/**\n * Creates a `DiscardDocumentAction` object.\n * @param doc - A handle uniquely identifying the document whose draft changes are to be discarded.\n * @returns A `DiscardDocumentAction` object ready for dispatch.\n * @beta\n */\nexport function discardDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n doc: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): DiscardDocumentAction<TDocumentType, TDataset, TProjectId> {\n const effectiveDocumentId = getEffectiveDocumentId(doc)\n return {\n type: 'document.discard',\n ...doc,\n documentId: effectiveDocumentId,\n }\n}\n\n/**\n * Creates a new release. The `releaseId` must be unique within the current\n * retention period.\n * @beta\n */\nexport interface CreateReleaseAction extends ReleaseHandle {\n type: 'release.create'\n metadata: ReleaseDocument['metadata']\n}\n\n/**\n * Patches the metadata of an existing release.\n * @beta\n */\nexport interface EditReleaseAction extends ReleaseHandle {\n type: 'release.edit'\n patch: PatchOperations\n}\n\n/**\n * Publishes all version documents in a release.\n * @beta\n */\nexport interface PublishReleaseAction extends ReleaseHandle {\n type: 'release.publish'\n}\n\n/**\n * Schedules a release to be published at the given UTC time. Locks the\n * version documents server-side until the release is unscheduled or published.\n * @beta\n */\nexport interface ScheduleReleaseAction extends ReleaseHandle {\n type: 'release.schedule'\n publishAt: string\n}\n\n/**\n * Unschedules a release that was previously scheduled, returning it to the\n * active editable state.\n * @beta\n */\nexport interface UnscheduleReleaseAction extends ReleaseHandle {\n type: 'release.unschedule'\n}\n\n/**\n * Archives an active release. Version documents within the release are\n * removed and no longer queryable, though still recoverable through history\n * during the retention period.\n * @beta\n */\nexport interface ArchiveReleaseAction extends ReleaseHandle {\n type: 'release.archive'\n}\n\n/**\n * Restores an archived release. Only possible during the retention period.\n * @beta\n */\nexport interface UnarchiveReleaseAction extends ReleaseHandle {\n type: 'release.unarchive'\n}\n\n/**\n * Permanently deletes an archived or published release. To remove an active\n * release, use the archive action first.\n * @beta\n */\nexport interface DeleteReleaseAction extends ReleaseHandle {\n type: 'release.delete'\n}\n\n/**\n * Union of all release actions that can be dispatched alongside document\n * actions through `applyDocumentActions`.\n * @beta\n */\nexport type ReleaseAction =\n | CreateReleaseAction\n | EditReleaseAction\n | PublishReleaseAction\n | ScheduleReleaseAction\n | UnscheduleReleaseAction\n | ArchiveReleaseAction\n | UnarchiveReleaseAction\n | DeleteReleaseAction\n\n/** @beta */\nexport function createRelease(\n handle: ReleaseHandle,\n metadata: ReleaseDocument['metadata'] = {releaseType: 'undecided'},\n): CreateReleaseAction {\n return {type: 'release.create', ...handle, metadata}\n}\n\n/** @beta */\nexport function editRelease(handle: ReleaseHandle, patch: PatchOperations): EditReleaseAction {\n return {type: 'release.edit', ...handle, patch}\n}\n\n/** @beta */\nexport function publishRelease(handle: ReleaseHandle): PublishReleaseAction {\n return {type: 'release.publish', ...handle}\n}\n\n/** @beta */\nexport function scheduleRelease(handle: ReleaseHandle, publishAt: string): ScheduleReleaseAction {\n return {type: 'release.schedule', ...handle, publishAt}\n}\n\n/** @beta */\nexport function unscheduleRelease(handle: ReleaseHandle): UnscheduleReleaseAction {\n return {type: 'release.unschedule', ...handle}\n}\n\n/** @beta */\nexport function archiveRelease(handle: ReleaseHandle): ArchiveReleaseAction {\n return {type: 'release.archive', ...handle}\n}\n\n/** @beta */\nexport function unarchiveRelease(handle: ReleaseHandle): UnarchiveReleaseAction {\n return {type: 'release.unarchive', ...handle}\n}\n\n/** @beta */\nexport function deleteRelease(handle: ReleaseHandle): DeleteReleaseAction {\n return {type: 'release.delete', ...handle}\n}\n","/**\n * When a document has no more subscribers, its state is cleaned up and removed\n * from the store. A delay used to prevent re-creating resources when the last\n * subscriber is removed quickly before another one is added. This is helpful\n * when used in a frontend where components may suspend or transition to\n * different views quickly.\n */\nexport const DOCUMENT_STATE_CLEAR_DELAY = 1000\nexport const INITIAL_OUTGOING_THROTTLE_TIME = 1000\nexport const API_VERSION = 'v2025-05-06'\n\n/**\n * How long a submitted transaction's revision is kept around waiting to be\n * verified by its listener echo. After this, the transaction is no longer\n * treated as in flight and a document held onto only for that verification can\n * be evicted. Sized to match the listener's own chaining deadline, after which\n * an echo that has not arrived is not going to.\n */\nexport const UNVERIFIED_REVISION_RETENTION_TIME = 30_000\n\n/**\n * Base delay (ms) before retrying a document listener after a transient failure\n * (an `OutOfSyncError`, a 5xx response, 408 or 429). Backoff doubles on each\n * successive retry, capped at {@link OUT_OF_SYNC_RETRY_MAX_DELAY}, and gives up\n * after {@link OUT_OF_SYNC_RETRY_COUNT} retries so a lasting failure surfaces.\n */\nexport const OUT_OF_SYNC_RETRY_BASE_DELAY = 500\nexport const OUT_OF_SYNC_RETRY_MAX_DELAY = 10_000\nexport const OUT_OF_SYNC_RETRY_COUNT = 5\n\n/**\n * Base delay (ms) before retrying the dataset ACL fetch after a transient\n * failure (a network error or 5xx response). Backoff doubles on each\n * successive retry, capped at {@link ACL_RETRY_MAX_DELAY}.\n */\nexport const ACL_RETRY_BASE_DELAY = 500\nexport const ACL_RETRY_MAX_DELAY = 10_000\n\n/**\n * Base delay (ms) before resubmitting an outgoing transaction whose submission\n * failed with a retryable error (a network error, a 5xx, 408 or 429 response,\n * a 401 from an expired session, or a 409 `transactionConflictError` from\n * contention while committing). Backoff doubles on each successive\n * attempt, capped at {@link SUBMISSION_RETRY_MAX_DELAY}. A change of client\n * (new credentials) triggers the next attempt immediately.\n */\nexport const SUBMISSION_RETRY_BASE_DELAY = 1000\nexport const SUBMISSION_RETRY_MAX_DELAY = 10_000\n","import {type Action, type ReleaseAction} from '../actions'\n\n/**\n * Path prefix shared by every release document. Matches studio's\n * `RELEASE_DOCUMENTS_PATH` constant.\n */\nconst RELEASE_DOCUMENTS_PATH = '_.releases'\n\n/**\n * Returns the full release document ID for the given release name.\n * e.g. `getReleaseDocumentId('my-release') === '_.releases.my-release'`\n * @beta\n */\nexport function getReleaseDocumentId(releaseId: string): string {\n return `${RELEASE_DOCUMENTS_PATH}.${releaseId}`\n}\n\nconst RELEASE_ACTION_TYPES = new Set([\n 'release.create',\n 'release.edit',\n 'release.publish',\n 'release.schedule',\n 'release.unschedule',\n 'release.archive',\n 'release.unarchive',\n 'release.delete',\n])\n\nexport function isReleaseAction(action: Action): action is ReleaseAction {\n return RELEASE_ACTION_TYPES.has(action.type)\n}\n","import {applyPatches, parsePatch} from '@sanity/diff-match-patch'\nimport {getIndexForKey, jsonMatch, slicePath, stringifyPath} from '@sanity/json-match'\nimport {\n type IndexTuple,\n type InsertPatch,\n isKeyedObject,\n isKeySegment,\n type PathSegment,\n} from '@sanity/types'\n\ntype SingleValuePath = Exclude<PathSegment, IndexTuple>[]\n\ntype ToNumber<TInput extends string> = TInput extends `${infer TNumber extends number}`\n ? TNumber\n : TInput\n\n/**\n * Parse a single \"segment\" that may include bracket parts.\n *\n * For example, the literal\n *\n * ```\n * \"friends[0][1]\"\n * ```\n *\n * is parsed as:\n *\n * ```\n * [\"friends\", 0, 1]\n * ```\n */\ntype ParseSegment<TInput extends string> = TInput extends `${infer TProp}[${infer TRest}`\n ? TProp extends ''\n ? [...ParseBracket<`[${TRest}`>] // no property name before '['\n : [TProp, ...ParseBracket<`[${TRest}`>]\n : TInput extends ''\n ? []\n : [TInput]\n\n/**\n * Parse one or more bracketed parts from a segment.\n *\n * It recursively \"peels off\" a bracketed part and then continues.\n *\n * For example, given the string:\n *\n * ```\n * \"[0][foo]\"\n * ```\n *\n * it produces:\n *\n * ```\n * [ToNumber<\"0\">, \"foo\"]\n * ```\n */\ntype ParseBracket<TInput extends string> = TInput extends `[${infer TPart}]${infer TRest}`\n ? [ToNumber<TPart>, ...ParseSegment<TRest>]\n : [] // no leading bracket → end of this segment\n\n/**\n * Split the entire path string on dots \"outside\" of any brackets.\n *\n * For example:\n * ```\n * \"friends[0].name\"\n * ```\n *\n * becomes:\n *\n * ```\n * [...ParseSegment<\"friends[0]\">, ...ParseSegment<\"name\">]\n * ```\n *\n * (We use a simple recursion that splits on the first dot.)\n */\ntype PathParts<TPath extends string> = TPath extends `${infer Head}.${infer Tail}`\n ? [Head, ...PathParts<Tail>]\n : TPath extends ''\n ? []\n : [TPath]\n\n/**\n * Given a type T and an array of \"access keys\" Parts, recursively index into T.\n *\n * If a part is a key, it looks up that property.\n * If T is an array and the part is a number, it \"indexes\" into the element type.\n */\ntype DeepGet<TValue, TPath extends readonly (string | number)[]> = TPath extends []\n ? TValue\n : TPath extends readonly [infer THead, ...infer TTail]\n ? // Handle traversing into optional properties\n DeepGet<\n TValue extends undefined | null\n ? undefined // Stop traversal if current value is null/undefined\n : THead extends keyof TValue // Access property if key exists\n ? TValue[THead]\n : // Handle array indexing\n THead extends number\n ? TValue extends readonly (infer TElement)[]\n ? TElement | undefined // Array element or undefined if out of bounds\n : undefined // Cannot index non-array with number\n : undefined, // Key/index doesn't exist\n TTail extends readonly (string | number)[] ? TTail : [] // Continue with the rest of the path\n >\n : never // Should be unreachable\n\n/**\n * Given a document type TDocument and a JSON Match path string TPath,\n * compute the type found at that path.\n * @beta\n */\nexport type JsonMatch<TDocument, TPath extends string> = DeepGet<TDocument, PathParts<TPath>>\n\n// this is a similar array key to the studio:\n// https://github.com/sanity-io/sanity/blob/v3.74.1/packages/sanity/src/core/form/inputs/arrays/ArrayOfObjectsInput/createProtoArrayValue.ts\nfunction generateArrayKey(length: number = 12): string {\n // Each byte gives two hex characters, so generate enough bytes.\n const numBytes = Math.ceil(length / 2)\n const bytes = crypto.getRandomValues(new Uint8Array(numBytes))\n // Convert each byte to a 2-digit hex string and join them.\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'))\n .join('')\n .slice(0, length)\n}\n\nfunction memoize<TFunction extends (input: unknown) => unknown>(fn: TFunction): TFunction {\n const cache = new WeakMap<object, unknown>()\n return ((input) => {\n if (!input || typeof input !== 'object') return fn(input)\n\n const cached = cache.get(input)\n if (cached) return cached\n\n const result = fn(input)\n cache.set(input, result)\n return result\n }) as TFunction\n}\n\n/**\n * Recursively traverse a value. When an array is encountered, ensure that\n * each object item has a _key property. Memoized such that sub-objects that\n * have not changed aren't re-computed.\n */\nexport const ensureArrayKeysDeep = memoize(<R>(input: R): R => {\n if (!input || typeof input !== 'object') return input\n\n if (Array.isArray(input)) {\n // if the array is empty then just return the input\n if (!input.length) return input\n const first = input[0]\n // if the first input in the array isn't an object (null is allowed) then\n // assume that this is an array of primitives, just return the input\n if (typeof first !== 'object') return input\n\n // if all the items already have a key, then return the input\n if (input.every(isKeyedObject)) return input\n\n // otherwise return a new object item with a new key\n return input.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n if (isKeyedObject(item)) return ensureArrayKeysDeep(item)\n const next = ensureArrayKeysDeep(item)\n return {...next, _key: generateArrayKey()}\n }) as R\n }\n\n const entries = Object.entries(input).map(([key, value]) => [key, ensureArrayKeysDeep(value)])\n\n if (entries.every(([key, value]) => input[key as keyof typeof input] === value)) {\n return input\n }\n\n return Object.fromEntries(entries) as R\n})\n\n/**\n * Whether every array-addressing segment (keyed `[_key==\"…\"]` or numeric\n * index) in the given match path resolves to an existing array item.\n *\n * This mirrors Content Lake, which auto-creates missing *object* properties\n * when setting but never creates *array items*:\n *\n * ```js\n * // object properties are created, even nested ones:\n * set({}, {'meta.author.name': 'Ada'})\n * // -> {meta: {author: {name: 'Ada'}}} (applies)\n *\n * // array items are not:\n * set({items: [{_key: 'a'}]}, {'items[_key==\"b\"].title': 'x'})\n * // -> {items: [{_key: 'a'}]} (no-op)\n * ```\n *\n * Without this filter, the second example fabricated a degenerate item\n * (`{title: 'x'}`, no `_key`) that would never exist on the server, which\n * is how concurrent Portable Text edits grew phantom span-less children.\n */\nfunction resolvesArraySegments(input: unknown, path: SingleValuePath): boolean {\n let current: unknown = input\n for (const segment of path) {\n if (typeof segment === 'string') {\n // missing object properties (nested ones included) are created\n // server-side, so a string segment may descend into undefined\n current =\n typeof current === 'object' && current !== null && !Array.isArray(current)\n ? (current as Record<string, unknown>)[segment]\n : undefined\n continue\n }\n // keyed and numeric segments address array items, which are never created\n if (!Array.isArray(current)) return false\n if (isKeySegment(segment)) {\n const index = getIndexForKey(current, segment._key)\n if (index === undefined) return false\n current = current[index]\n } else {\n const index = segment < 0 ? current.length + segment : segment\n if (!(index in current)) return false\n current = current[index]\n }\n }\n return true\n}\n\n/**\n * Given an input object and a record of path expressions to values, this\n * function will set each match with the given value.\n *\n * ```js\n * const output = set(\n * {name: {first: 'initial', last: 'initial'}},\n * {'name.first': 'changed'}\n * );\n *\n * // { name: { first: 'changed', last: 'initial' } }\n * console.log(output);\n * ```\n */\nexport function set<R>(input: unknown, pathExpressionValues: Record<string, unknown>): R\nexport function set(input: unknown, pathExpressionValues: Record<string, unknown>): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, replacementValue]) =>\n Array.from(jsonMatch(input, pathExpression)).map((matchEntry) => ({\n ...matchEntry,\n replacementValue,\n })),\n )\n .filter(({path}) => resolvesArraySegments(input, path))\n .reduce((acc, {path, replacementValue}) => setDeep(acc, path, replacementValue), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of path expressions to values, this\n * function will set each match with the given value **if the value at the current\n * path is missing** (i.e. `null` or `undefined`).\n *\n * ```js\n * const output = setIfMissing(\n * {items: [null, 'initial']},\n * {'items[:]': 'changed'}\n * );\n *\n * // { items: ['changed', 'initial'] }\n * console.log(output);\n * ```\n */\nexport function setIfMissing<R>(input: unknown, pathExpressionValues: Record<string, unknown>): R\nexport function setIfMissing(\n input: unknown,\n pathExpressionValues: Record<string, unknown>,\n): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, replacementValue]) => {\n return Array.from(jsonMatch(input, pathExpression)).map((matchEntry) => ({\n ...matchEntry,\n replacementValue,\n }))\n })\n .filter((matchEntry) => matchEntry.value === null || matchEntry.value === undefined)\n .filter(({path}) => resolvesArraySegments(input, path))\n .reduce((acc, {path, replacementValue}) => setDeep(acc, path, replacementValue), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and an array of path expressions, this function will\n * remove each match from the input object.\n *\n * ```js\n * const output = unset(\n * {name: {first: 'one', last: 'two'}},\n * ['name.last']\n * );\n *\n * // { name: { first: 'one' } }\n * console.log(output);\n * ```\n */\nexport function unset<R>(input: unknown, pathExpressions: string[]): R\nexport function unset(input: unknown, pathExpressions: string[]): unknown {\n const result = pathExpressions\n .flatMap((pathExpression) => Array.from(jsonMatch(input, pathExpression)))\n // ensure that we remove in the reverse order the paths were found in\n // this is necessary for array unsets so the indexes don't change as we unset\n .reverse()\n .reduce((acc, {path}) => unsetDeep(acc, path), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object, a path expression (inside the insert patch object), and an array of items,\n * this function will insert or replace the matched items.\n *\n * **Insert before:**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c']}}\n * const output = insert(\n * input,\n * {\n * before: 'some.array[1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', '!', 'b', 'c'] } }\n * console.log(output);\n * ```\n *\n * **Insert before with negative index (append):**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c']}}\n * const output = insert(\n * input,\n * {\n * before: 'some.array[-1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', 'b', 'c', '!'] } }\n * console.log(output);\n * ```\n *\n * **Insert after:**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c']}}\n * const output = insert(\n * input,\n * {\n * after: 'some.array[1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', 'b', '!', 'c'] } }\n * console.log(output);\n * ```\n *\n * **Replace:**\n *\n * ```js\n * const output = insert(\n * { some: { array: ['a', 'b', 'c'] } },\n * {\n * replace: 'some.array[1]',\n * items: ['!']\n * }\n * );\n * // { some: { array: ['a', '!', 'c'] } }\n * console.log(output);\n * ```\n *\n * **Replace many:**\n *\n * ```js\n * const input = {some: {array: ['a', 'b', 'c', 'd']}}\n * const output = insert(\n * input,\n * {\n * replace: 'some.array[1:3]',\n * items: ['!', '?']\n * }\n * );\n * // { some: { array: ['a', '!', '?', 'd'] } }\n * console.log(output);\n * ```\n */\nexport function insert<R>(input: unknown, insertPatch: InsertPatch): R\nexport function insert(input: unknown, {items, ...insertPatch}: InsertPatch): unknown {\n let operation\n let pathExpression\n\n // behavior observed from content-lake when inserting:\n // 1. if the operation is before, out of all the matches, it will use the\n // insert the items before the first match that appears in the array\n // 2. if the operation is after, it will insert the items after the first\n // match that appears in the array\n // 3. if the operation is replace, then insert the items before the first\n // match and then delete the rest\n if ('before' in insertPatch) {\n operation = 'before' as const\n pathExpression = insertPatch.before\n } else if ('after' in insertPatch) {\n operation = 'after' as const\n pathExpression = insertPatch.after\n } else if ('replace' in insertPatch) {\n operation = 'replace' as const\n pathExpression = insertPatch.replace\n }\n if (!operation) return input\n if (typeof pathExpression !== 'string') return input\n\n // in order to do an insert patch, you need to provide at least one path segment\n if (!pathExpression.length) return input\n\n const arrayPath = slicePath(pathExpression, 0, -1)\n const positionPath = slicePath(pathExpression, -1)\n\n let result = input\n\n for (const {path, value} of jsonMatch(input, arrayPath)) {\n if (!Array.isArray(value)) continue\n let arr = value\n\n switch (operation) {\n case 'replace': {\n const indexesToRemove = new Set<number>()\n let position = Infinity\n\n for (const itemMatch of jsonMatch(arr, positionPath)) {\n // there should only be one path segment for an insert patch, invalid otherwise\n if (itemMatch.path.length !== 1) continue\n const [segment] = itemMatch.path\n if (typeof segment === 'string') continue\n\n let index\n\n if (typeof segment === 'number') index = segment\n if (typeof index === 'number' && index < 0) index = arr.length + index\n if (isKeySegment(segment)) index = getIndexForKey(arr, segment._key)\n if (typeof index !== 'number') continue\n if (index < 0) index = arr.length + index\n\n indexesToRemove.add(index)\n if (index < position) position = index\n }\n\n if (position === Infinity) continue\n\n // remove all other indexes\n arr = arr\n .map((item, index) => ({item, index}))\n .filter(({index}) => !indexesToRemove.has(index))\n .map(({item}) => item)\n\n // insert at the min index\n arr = [...arr.slice(0, position), ...items, ...arr.slice(position, arr.length)]\n\n break\n }\n case 'before': {\n let position = Infinity\n\n for (const itemMatch of jsonMatch(arr, positionPath)) {\n if (itemMatch.path.length !== 1) continue\n const [segment] = itemMatch.path\n\n if (typeof segment === 'string') continue\n\n let index\n\n if (typeof segment === 'number') index = segment\n if (typeof index === 'number' && index < 0) index = arr.length + index\n if (isKeySegment(segment)) index = getIndexForKey(arr, segment._key)\n if (typeof index !== 'number') continue\n if (index < 0) index = arr.length - index\n if (index < position) position = index\n }\n\n if (position === Infinity) continue\n\n arr = [...arr.slice(0, position), ...items, ...arr.slice(position, arr.length)]\n\n break\n }\n case 'after': {\n let position = -Infinity\n\n for (const itemMatch of jsonMatch(arr, positionPath)) {\n if (itemMatch.path.length !== 1) continue\n const [segment] = itemMatch.path\n\n if (typeof segment === 'string') continue\n\n let index\n\n if (typeof segment === 'number') index = segment\n if (typeof index === 'number' && index < 0) index = arr.length + index\n if (isKeySegment(segment)) index = getIndexForKey(arr, segment._key)\n if (typeof index !== 'number') continue\n if (index > position) position = index\n }\n\n if (position === -Infinity) continue\n\n arr = [...arr.slice(0, position + 1), ...items, ...arr.slice(position + 1, arr.length)]\n\n break\n }\n\n default: {\n continue\n }\n }\n\n result = setDeep(result, path, arr)\n }\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of path expressions to numeric values,\n * this function will increment each match with the given value.\n *\n * ```js\n * const output = inc(\n * {foo: {first: 3, second: 4.5}},\n * {'foo.first': 3, 'foo.second': 4}\n * );\n *\n * // { foo: { first: 6, second: 8.5 } }\n * console.log(output);\n * ```\n */\nexport function inc<R>(input: unknown, pathExpressionValues: Record<string, number>): R\nexport function inc(input: unknown, pathExpressionValues: Record<string, number>): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, valueToAdd]) =>\n Array.from(jsonMatch(input, pathExpression)).map((matchEntry) => ({\n ...matchEntry,\n valueToAdd,\n })),\n )\n .filter(\n <T extends {value: unknown}>(matchEntry: T): matchEntry is T & {value: number} =>\n typeof matchEntry.value === 'number',\n )\n .reduce((acc, {path, value, valueToAdd}) => setDeep(acc, path, value + valueToAdd), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of path expressions to numeric values,\n * this function will decrement each match with the given value.\n *\n * ```js\n * const output = dec(\n * {foo: {first: 3, second: 4.5}},\n * {'foo.first': 3, 'foo.second': 4}\n * );\n *\n * // { foo: { first: 0, second: 0.5 } }\n * console.log(output);\n * ```\n */\nexport function dec<R>(input: unknown, pathExpressionValues: Record<string, number>): R\nexport function dec(input: unknown, pathExpressionValues: Record<string, number>): unknown {\n const result = inc(\n input,\n Object.fromEntries(\n Object.entries(pathExpressionValues)\n .filter(([, value]) => typeof value === 'number')\n .map(([key, value]) => [key, -value]),\n ),\n )\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Given an input object and a record of paths to [diff match patches][0], this\n * function will apply the diff match patch for the string at each match.\n *\n * [0]: https://www.sanity.io/docs/http-patches#aTbJhlAJ\n *\n * ```js\n * const output = diffMatchPatch(\n * {foo: 'the quick brown fox'},\n * {'foo': '@@ -13,7 +13,7 @@\\n own \\n-fox\\n+cat\\n'}\n * );\n *\n * // { foo: 'the quick brown cat' }\n * console.log(output);\n * ```\n */\nexport function diffMatchPatch<R>(input: unknown, pathExpressionValues: Record<string, string>): R\nexport function diffMatchPatch(\n input: unknown,\n pathExpressionValues: Record<string, string>,\n): unknown {\n const result = Object.entries(pathExpressionValues)\n .flatMap(([pathExpression, dmp]) =>\n Array.from(jsonMatch(input, pathExpression)).map((m) => ({...m, dmp})),\n )\n .filter((i) => i.value !== undefined)\n .map(({path, value, dmp}) => {\n if (typeof value !== 'string') {\n throw new Error(\n `Can't diff-match-patch \\`${JSON.stringify(value)}\\` at path \\`${stringifyPath(path)}\\`, because it is not a string`,\n )\n }\n\n // Offsets arrive as UTF-8 byte positions and can overshoot when they\n // land inside a multi-byte character, which happens once the string a\n // patch was computed against has diverged. The default is to throw, and\n // that throw escapes the rebase loop in `reducers.ts`, kills the\n // outgoing-actions pipeline, and leaves the instance silently dropping\n // every write. Clamping to the nearest index applies the patch instead,\n // which is what the Studio and the Portable Text editor already do.\n const [nextValue] = applyPatches(parsePatch(dmp), value, {\n allowExceedingIndices: true,\n })\n return {path, value: nextValue}\n })\n .reduce((acc, {path, value}) => setDeep(acc, path, value), input)\n\n return ensureArrayKeysDeep(result)\n}\n\n/**\n * Simply checks if the given document input has a `_rev` that matches the given\n * `revisionId` and throws otherwise.\n *\n * (No code example provided.)\n */\nexport function ifRevisionID<R>(input: unknown, revisionId: string): R\nexport function ifRevisionID(input: unknown, revisionId: string): unknown {\n const inputRev =\n typeof input === 'object' && !!input && '_rev' in input && typeof input._rev === 'string'\n ? input._rev\n : undefined\n\n if (typeof inputRev !== 'string') {\n throw new Error(`Patch specified \\`ifRevisionID\\` but could not find document's revision ID.`)\n }\n\n if (revisionId !== inputRev) {\n throw new Error(\n `Patch's \\`ifRevisionID\\` \\`${revisionId}\\` does not match document's revision ID \\`${inputRev}\\``,\n )\n }\n\n return input\n}\n\n/**\n * Gets a value deep inside of an object given a path. If the path does not\n * exist in the object, `undefined` will be returned.\n */\nexport function getDeep<R>(input: unknown, path: SingleValuePath): R\nexport function getDeep(input: unknown, path: SingleValuePath): unknown {\n const [currentSegment, ...restOfPath] = path\n if (currentSegment === undefined) return input\n if (typeof input !== 'object' || input === null) return undefined\n\n let key\n if (isKeySegment(currentSegment)) {\n key = getIndexForKey(input, currentSegment._key)\n } else if (typeof currentSegment === 'string') {\n key = currentSegment\n } else if (typeof currentSegment === 'number') {\n key = currentSegment\n }\n\n if (key === undefined) return undefined\n\n // Use .at() to support negative indexes on arrays.\n const nestedInput =\n typeof key === 'number' && Array.isArray(input)\n ? input.at(key)\n : (input as Record<string, unknown>)[key]\n\n return getDeep(nestedInput, restOfPath)\n}\n\n/**\n * Sets a value deep inside of an object given a path. If the path does not\n * exist in the object, it will be created.\n */\nexport function setDeep<R>(input: unknown, path: SingleValuePath, value: unknown): R\nexport function setDeep(input: unknown, path: SingleValuePath, value: unknown): unknown {\n const [currentSegment, ...restOfPath] = path\n if (currentSegment === undefined) return value\n\n // If the current input is not an object, create a new container.\n if (typeof input !== 'object' || input === null) {\n if (typeof currentSegment === 'string') {\n return {[currentSegment]: setDeep(null, restOfPath, value)}\n }\n\n let index: number | undefined\n if (isKeySegment(currentSegment)) {\n // When creating a new array via a keyed segment, use index 0.\n index = 0\n } else if (typeof currentSegment === 'number' && currentSegment >= 0) {\n index = currentSegment\n } else {\n // For negative numbers in a non‐object we simply return input.\n return input\n }\n\n return [\n // fill until index\n ...Array.from({length: index}).fill(null),\n // then set deep here\n setDeep(null, restOfPath, value),\n ]\n }\n\n // When input is an array…\n if (Array.isArray(input)) {\n let index: number | undefined\n if (isKeySegment(currentSegment)) {\n index = getIndexForKey(input, currentSegment._key) ?? input.length\n } else if (typeof currentSegment === 'number') {\n // Support negative indexes by computing the proper positive index.\n index = currentSegment < 0 ? input.length + currentSegment : currentSegment\n }\n if (index === undefined) return input\n\n if (index in input) {\n // Update the element at the resolved index.\n return input.map((nestedInput, i) =>\n i === index ? setDeep(nestedInput, restOfPath, value) : nestedInput,\n )\n }\n\n // Expand the array if needed.\n return [\n ...input,\n ...Array.from({length: index - input.length}).fill(null),\n setDeep(null, restOfPath, value),\n ]\n }\n\n // For keyed segments that aren't arrays, do nothing.\n if (typeof currentSegment === 'object') return input\n\n // For plain objects, update an existing property if it exists…\n if (currentSegment in input) {\n return Object.fromEntries(\n Object.entries(input).map(([key, nestedInput]) =>\n key === currentSegment\n ? [key, setDeep(nestedInput, restOfPath, value)]\n : [key, nestedInput],\n ),\n )\n }\n\n // ...otherwise create the new nested path.\n return {...input, [currentSegment]: setDeep(null, restOfPath, value)}\n}\n\n/**\n * Given an object and an exact path as an array, this unsets the value at the\n * given path.\n */\nexport function unsetDeep<R>(input: unknown, path: SingleValuePath): R\nexport function unsetDeep(input: unknown, path: SingleValuePath): unknown {\n const [currentSegment, ...restOfPath] = path\n if (currentSegment === undefined) return input\n if (typeof input !== 'object' || input === null) return input\n\n let _segment: string | number | undefined\n if (isKeySegment(currentSegment)) {\n _segment = getIndexForKey(input, currentSegment._key)\n } else if (typeof currentSegment === 'string' || typeof currentSegment === 'number') {\n _segment = currentSegment\n }\n if (_segment === undefined) return input\n\n // For numeric segments in arrays, compute the positive index.\n let segment: string | number = _segment\n if (typeof segment === 'number' && Array.isArray(input)) {\n segment = segment < 0 ? input.length + segment : segment\n }\n if (!(segment in input)) return input\n\n // If we're at the final segment, remove the property/element.\n if (!restOfPath.length) {\n if (Array.isArray(input)) {\n return input.filter((_nestedInput, index) => index !== segment)\n }\n return Object.fromEntries(Object.entries(input).filter(([key]) => key !== segment.toString()))\n }\n\n // Otherwise, recurse into the nested value.\n if (Array.isArray(input)) {\n return input.map((nestedInput, index) =>\n index === segment ? unsetDeep(nestedInput, restOfPath) : nestedInput,\n )\n }\n\n return Object.fromEntries(\n Object.entries(input).map(([key, value]) =>\n key === segment ? [key, unsetDeep(value, restOfPath)] : [key, value],\n ),\n )\n}\n","import {\n type Mutation,\n type MutationSelection,\n type PatchOperations,\n type SanityDocument,\n} from '@sanity/types'\n\nimport {randomUuid} from '../utils/ids'\nimport {\n dec,\n diffMatchPatch,\n ifRevisionID,\n inc,\n insert,\n set,\n setIfMissing,\n unset,\n} from './patchOperations'\n\n/**\n * Maps document IDs to documents, using `null` for documents that do not exist.\n *\n * @beta\n */\nexport type DocumentSet<TDocument extends SanityDocument = SanityDocument> = {\n [TDocumentId in string]?: TDocument | null\n}\n\ntype SupportedPatchOperation = Exclude<keyof PatchOperations, 'merge'>\n\n/**\n * Exposes the patch operations used by {@link processMutations}.\n *\n * @remarks\n * Property order determines execution order in {@link processMutations}:\n * `ifRevisionID`, `set`, `setIfMissing`, `unset`, `inc`, `dec`, `insert`,\n * then `diffMatchPatch`. Reordering the properties changes mutation results.\n * Freezes the collection to prevent callers from replacing the functions\n * used by the SDK's mutation evaluator.\n *\n * @see https://www.sanity.io/docs/http-mutations#5b4db1396e56\n *\n * @example\n * ```ts\n * const document = {items: [{_key: 'a', title: 'Before'}]}\n * const updated = patchOperations.set<typeof document>(document, {\n * 'items[_key==\"a\"].title': 'After',\n * })\n * ```\n *\n * @internal\n */\nexport const patchOperations = Object.freeze({\n ifRevisionID,\n set,\n setIfMissing,\n unset,\n inc,\n dec,\n insert,\n diffMatchPatch,\n} satisfies {\n [K in SupportedPatchOperation]: (\n input: unknown,\n pathExpressions: NonNullable<PatchOperations[K]>,\n ) => unknown\n})\n\n/**\n * Implements ID generation:\n *\n * A create mutation creates a new document. It takes the literal document\n * content as its argument. The rules for the new document's identifier are as\n * follows:\n *\n * - If the `_id` attribute is missing, then a new, random, unique ID is\n * generated.\n * - If the `_id` attribute is present but ends with `.`, then it is used as a\n * prefix for a new, random, unique ID.\n * - If the _id attribute is present, it is used as-is.\n *\n * [- source](https://www.sanity.io/docs/http-mutations#c732f27330a4)\n */\nexport function getId(id?: string): string {\n if (!id || typeof id !== 'string') return randomUuid()\n if (id.endsWith('.')) return `${id}${randomUuid()}`\n return id\n}\n\n/**\n * Configures {@link processMutations}.\n *\n * @internal\n */\nexport interface ProcessMutationsOptions {\n /**\n * The transaction ID that will become the next `_rev` for documents mutated\n * by the given mutations.\n */\n transactionId: string\n /**\n * The input document set that the mutations will be applied to.\n */\n documents: DocumentSet\n /**\n * A list of mutations to apply to the given document set.\n */\n mutations: Mutation[]\n /**\n * Supplies the default `_createdAt` and `_updatedAt` timestamps.\n * Uses the current time when omitted or empty.\n */\n timestamp?: string\n}\n\nexport function getDocumentIds(selection: MutationSelection): string[] {\n if ('id' in selection) {\n // NOTE: the `MutationSelection` type within `@sanity/client` (instead of\n // `@sanity/types`) allows for the ID field to be an array of strings so we\n // support that as well\n const array = Array.isArray(selection.id) ? selection.id : [selection.id]\n const ids = array.filter((id): id is string => typeof id === 'string')\n return Array.from(new Set(ids))\n }\n\n if ('query' in selection) {\n throw new Error(`'query' in mutations is not supported.`)\n }\n\n return []\n}\n\n/**\n * Applies mutations to an in-memory set of Sanity documents.\n * Deleted documents remain in the returned set with a value of `null`.\n *\n * Include every document affected by `mutations` in `documents`, using `null`\n * for documents that do not exist.\n *\n * Returns `documents` itself when `mutations` is empty. Otherwise, returns a\n * shallow copy of that object. The result can reuse document objects and\n * nested objects from `documents`. Mutating a reused object through the\n * result also changes the input.\n *\n * Sets `_rev` to `transactionId` on created, replaced, and patched documents.\n * Uses `timestamp` for timestamps unless the mutation preserves them.\n * Ignores `merge` patches; `inc` and `dec` skip values that are not numbers.\n * Generates document IDs when omitted or ending in `.`.\n * Patch operations can generate missing array keys.\n *\n * Does not persist documents or run Content Lake's server validation.\n *\n * @throws If a selection uses a query, a `create` targets an existing document,\n * or a `patch` targets a document that does not exist.\n * @throws If a patch operation fails, including a revision check failure or\n * `diffMatchPatch` applied to a value other than a string or `undefined`.\n *\n * @internal\n */\nexport function processMutations({\n documents,\n mutations,\n transactionId,\n timestamp,\n}: ProcessMutationsOptions): DocumentSet {\n // early return if there are no mutations given\n if (!mutations.length) return documents\n\n const dataset = {...documents}\n const now = timestamp || new Date().toISOString()\n\n for (const mutation of mutations) {\n if ('create' in mutation) {\n const id = getId(mutation.create._id)\n\n if (dataset[id]) {\n throw new Error(\n `Cannot create document with \\`_id\\` \\`${id}\\` because another document with the same ID already exists.`,\n )\n }\n\n const document: SanityDocument = {\n // > `_createdAt` and `_updatedAt` may be submitted and will override\n // > the default which is of course the current time. This can be used\n // > to reconstruct a data-set with its timestamp structure intact.\n // >\n // > [- source](https://www.sanity.io/docs/http-mutations#c732f27330a4)\n _createdAt: now,\n _updatedAt: now,\n ...mutation.create, // prefer the user's `_createdAt` and `_updatedAt`\n _rev: transactionId,\n _id: id,\n }\n\n dataset[id] = document\n\n continue\n }\n\n if ('createOrReplace' in mutation) {\n const id = getId(mutation.createOrReplace._id)\n const prev = dataset[id]\n\n const document: SanityDocument = {\n ...mutation.createOrReplace,\n // otherwise, if the mutation provided, a `_createdAt` time, use it,\n // otherwise default to now\n _createdAt:\n // if there was an existing document, use the previous `_createdAt`\n // since we're replacing the current document\n prev?._createdAt ||\n // if there was no previous document, then we're creating this\n // document for the first time so we should use the `_createdAt` from\n // the mutation if the user included it\n (typeof mutation.createOrReplace['_createdAt'] === 'string' &&\n mutation.createOrReplace['_createdAt']) ||\n // otherwise, default to now\n now,\n\n _updatedAt:\n // if there was an existing document, then set the `_updatedAt` to now\n // since we're replacing the current document\n prev\n ? now\n : // otherwise, we're creating this document for the first time,\n // in that case, use the user's `_updatedAt` if included in the\n // mutation\n (typeof mutation.createOrReplace['_updatedAt'] === 'string' &&\n mutation.createOrReplace['_updatedAt']) ||\n // otherwise default to now\n now,\n _rev: transactionId,\n _id: id,\n }\n\n dataset[id] = document\n\n continue\n }\n\n if ('createIfNotExists' in mutation) {\n const id = getId(mutation.createIfNotExists._id)\n const prev = dataset[id]\n if (prev) continue\n\n const document: SanityDocument = {\n // same logic as `create`:\n // prefer the user's `_createdAt` and `_updatedAt`\n _createdAt: now,\n _updatedAt: now,\n ...mutation.createIfNotExists,\n _rev: transactionId,\n _id: id,\n }\n\n dataset[id] = document\n\n continue\n }\n\n if ('delete' in mutation) {\n for (const id of getDocumentIds(mutation.delete)) {\n dataset[id] = null\n }\n\n continue\n }\n\n if ('patch' in mutation) {\n const {patch} = mutation\n const ids = getDocumentIds(patch)\n\n const patched = ids.map((id) => {\n if (!dataset[id]) {\n throw new Error(`Cannot patch document with ID \\`${id}\\` because it was not found.`)\n }\n\n type Entries<T> = {[K in keyof T]: [K, T[K]]}[keyof T][]\n const entries = Object.entries(patchOperations) as Entries<typeof patchOperations>\n\n return entries.reduce((acc, [type, operation]) => {\n if (patch[type]) {\n return operation(\n acc,\n // @ts-expect-error TS doesn't handle this union very well\n patch[type],\n )\n }\n return acc\n }, dataset[id])\n })\n\n for (const result of patched) {\n dataset[result._id] = {\n ...result,\n _rev: transactionId,\n _updatedAt: now,\n }\n }\n\n continue\n }\n }\n\n return dataset\n}\n","import {type MutationEvent} from '@sanity/client'\nimport {type Mutation, type SanityDocument} from '@sanity/types'\nimport {\n concat,\n concatMap,\n distinctUntilChanged,\n EMPTY,\n filter,\n map,\n type Observable,\n of,\n switchMap,\n throwError,\n timer,\n withLatestFrom,\n} from 'rxjs'\nimport {mergeMap, scan} from 'rxjs/operators'\n\nimport {type StoreContext} from '../store/defineStore'\nimport {type DocumentStoreState} from './documentStore'\nimport {processMutations} from './processMutations'\n\nexport const DEFAULT_MAX_BUFFER_SIZE = 20\nconst DEFAULT_DEADLINE_MS = 30000\n\nexport interface RemoteDocument {\n type: 'sync' | 'mutation'\n documentId: string\n document: SanityDocument | null\n revision?: string\n previousRev?: string\n timestamp: string\n /**\n * The raw mutations from the listener event that produced this document.\n * Only present for `'mutation'` events. These carry the operational intent\n * of the remote change (e.g. keyed patches) before it is collapsed into a\n * whole document snapshot.\n */\n mutations?: Mutation[]\n}\n\nexport interface SyncEvent {\n type: 'sync'\n document: SanityDocument | null\n}\n\nexport type ListenerEvent = SyncEvent | MutationEvent\n\ninterface ListenerSequenceState {\n /**\n * Tracks the latest revision from the server that can be applied locally\n * Once we receive a mutation event that has a `previousRev` that equals `base.revision`\n * we will move `base.revision` to the event's `resultRev`\n * `base.revision` will be undefined if document doesn't exist.\n * `base` is `undefined` until the snapshot event is received\n */\n base: {revision: string | undefined} | undefined\n /**\n * Array of events to pass on to the stream, e.g. when mutation applies to current head revision, or a chain is complete\n */\n emitEvents: ListenerEvent[]\n /**\n * Buffer to keep track of events that doesn't line up in a [previousRev, resultRev] -- [previousRev, resultRev] sequence\n * This can happen if events arrive out of order, or if an event in the middle for some reason gets lost\n */\n buffer: MutationEvent[]\n}\n\nexport class OutOfSyncError extends Error {\n /**\n * Attach state to the error for debugging/reporting\n */\n state: ListenerSequenceState\n constructor(message: string, state: ListenerSequenceState) {\n super(message)\n this.name = 'OutOfSyncError'\n this.state = state\n }\n}\n\nexport class DeadlineExceededError extends OutOfSyncError {\n constructor(message: string, state: ListenerSequenceState) {\n super(message, state)\n this.name = 'DeadlineExceededError'\n }\n}\n\nexport class MaxBufferExceededError extends OutOfSyncError {\n constructor(message: string, state: ListenerSequenceState) {\n super(message, state)\n this.name = 'MaxBufferExceededError'\n }\n}\n\ninterface SortListenerEventsOptions {\n maxBufferSize?: number\n resolveChainDeadline?: number\n}\n\n/**\n * Orders the given mutation events into chains where each event's\n * `previousRev` points at another event's `resultRev`. Events whose\n * `previousRev` matches no other event start a new chain. The buffer may have\n * multiple holes in it, so multiple chains can exist at once.\n */\nfunction toOrderedChains(events: MutationEvent[]): MutationEvent[][] {\n const chainStarts = events.filter(\n (event) => !events.some((other) => other.resultRev === event.previousRev),\n )\n\n return chainStarts.map((start) => {\n const chain: MutationEvent[] = []\n let current: MutationEvent | undefined = start\n while (current) {\n chain.push(current)\n const currentResultRev: string | undefined = current.resultRev\n current = events.find((event) => event.previousRev === currentResultRev)\n }\n return chain\n })\n}\n\n/**\n * Discards the leading events of a chain up to and including the event whose\n * `resultRev` equals the given revision. These events describe changes that\n * are already reflected in the current base (e.g. events replayed by a\n * resumed listener after the snapshot was refetched).\n */\nfunction discardChainTo(chain: MutationEvent[], revision: string | undefined): MutationEvent[] {\n const revisionIndex = chain.findIndex((event) => event.resultRev === revision)\n if (revisionIndex === -1) return chain\n return chain.slice(revisionIndex + 1)\n}\n\n/**\n * Takes an input observable of listener events that might arrive out of order, and emits them in sequence\n * If we receive mutation events that doesn't line up in [previousRev, resultRev] pairs we'll put them in a buffer and\n * check if we have an unbroken chain every time we receive a new event\n *\n * Buffered chains that lead up to the current base revision are discarded:\n * they describe changes already reflected in the base (e.g. events replayed\n * by a resumed listener after the snapshot was refetched)\n *\n * If the buffer grows beyond `maxBufferSize`, or if `resolveChainDeadline` milliseconds passes before the chain resolves\n * an OutOfSyncError will be thrown on the stream\n *\n * @internal\n */\nexport function sortListenerEvents(options?: SortListenerEventsOptions) {\n const {resolveChainDeadline = DEFAULT_DEADLINE_MS, maxBufferSize = DEFAULT_MAX_BUFFER_SIZE} =\n options || {}\n\n return (input$: Observable<ListenerEvent>): Observable<ListenerEvent> => {\n return input$.pipe(\n // Maintain state: current base revision, a buffer of pending mutation events,\n // and a list of events to emit.\n scan(\n (state: ListenerSequenceState, event: ListenerEvent): ListenerSequenceState => {\n // When a sync event is received, reset the base and clear any pending mutations.\n if (event.type === 'sync') {\n return {\n base: {revision: event.document?._rev},\n buffer: [],\n emitEvents: [event],\n }\n }\n // For mutation events we must have a base revision (from a prior sync event)\n if (event.type === 'mutation') {\n if (!state.base) {\n throw new Error(\n 'Invalid state. Cannot process mutation event without a base sync event',\n )\n }\n const baseRevision = state.base.revision\n\n // Order the buffered events (plus the new one) into chains, then\n // drop the parts of each chain the base already reflects.\n const chains = toOrderedChains(state.buffer.concat(event))\n .map((chain) => discardChainTo(chain, baseRevision))\n .filter((chain) => chain.length > 0)\n\n // A chain whose head continues the current base revision can be\n // applied. There can be at most one; anything else stays buffered.\n const applicable = chains.find((chain) => chain[0].previousRev === baseRevision)\n const buffer = chains.filter((chain) => chain !== applicable).flat()\n\n if (applicable) {\n const last = applicable[applicable.length - 1]\n return {\n // If the chain ends in a deletion, the new base revision is undefined.\n base: {revision: last.transition === 'disappear' ? undefined : last.resultRev},\n buffer,\n emitEvents: applicable,\n }\n }\n\n if (buffer.length >= maxBufferSize) {\n throw new MaxBufferExceededError(\n `Too many unchainable mutation events (${buffer.length}) waiting to resolve.`,\n {base: {revision: baseRevision}, buffer, emitEvents: []},\n )\n }\n\n return {\n base: {revision: baseRevision},\n buffer,\n emitEvents: [],\n }\n }\n // Any other event is simply forwarded.\n return {...state, emitEvents: [event]}\n },\n {\n base: undefined,\n buffer: [] as MutationEvent[],\n emitEvents: [] as ListenerEvent[],\n },\n ),\n switchMap((state) => {\n if (state.buffer.length > 0) {\n return concat(\n of(state),\n timer(resolveChainDeadline).pipe(\n mergeMap(() =>\n throwError(\n () =>\n new DeadlineExceededError(\n `Did not resolve chain within a deadline of ${resolveChainDeadline}ms`,\n state,\n ),\n ),\n ),\n ),\n )\n }\n return of(state)\n }),\n // Emit all events that are ready to be applied.\n mergeMap((state) => of(...state.emitEvents)),\n )\n }\n}\n\nexport const listen = (\n {state}: StoreContext<DocumentStoreState>,\n documentId: string,\n): Observable<RemoteDocument> => {\n const {sharedListener, fetchDocument} = state.get()\n\n return sharedListener.events.pipe(\n concatMap((e) => {\n // `welcome` means a fresh listener connection and `reset` means a\n // resumed listener could not replay what was missed; both require\n // refetching the snapshot. a `welcomeback` means the resume succeeded\n // and the missed events were replayed, so no refetch is needed\n if (e.type === 'welcome' || e.type === 'reset') {\n return fetchDocument(documentId).pipe(\n map((document): SyncEvent => ({type: 'sync', document})),\n )\n }\n if (e.type === 'mutation' && e.documentId === documentId) return of(e)\n return EMPTY\n }),\n sortListenerEvents(),\n withLatestFrom(\n state.observable.pipe(\n map((s) => s.documentStates[documentId]),\n filter(Boolean),\n distinctUntilChanged(),\n ),\n ),\n map(([next, documentState]): RemoteDocument => {\n if (next.type === 'sync') {\n return {\n type: 'sync',\n documentId,\n document: next.document,\n revision: next.document?._rev,\n timestamp: next.document?._updatedAt ?? new Date().toISOString(),\n }\n }\n\n // TODO: from manual testing, mendoza patches seem to be applying\n // let document\n // if (next.effects?.apply) {\n // document = applyPatch(omit(documentState.remote, '_rev'), next.effects?.apply)\n // }\n\n const [document] = Object.values(\n processMutations({\n documents: {[documentId]: documentState.remote},\n mutations: next.mutations as Mutation[],\n transactionId: next.transactionId,\n timestamp: next.timestamp,\n }),\n )\n\n const {previousRev, transactionId, timestamp} = next\n\n return {\n type: 'mutation',\n documentId,\n document: document ?? null,\n revision: transactionId,\n timestamp,\n mutations: next.mutations as Mutation[],\n ...(previousRev && {previousRev}),\n }\n }),\n )\n}\n","interface MultiKeyWeakMapNode {\n value: unknown\n next: WeakMap<object, MultiKeyWeakMapNode>\n}\n\nexport class MultiKeyWeakMap<T = unknown> {\n // The root of our nested WeakMap structure.\n #rootMap = new WeakMap<object, MultiKeyWeakMapNode>()\n\n // Instead of random IDs, we use a counter for deterministic IDs.\n static #globalIdCounter = 0\n // Each instance keeps a cache mapping a key to its assigned ID.\n #idCache = new WeakMap<object, number>()\n\n /**\n * Assigns a numeric ID to the key.\n */\n #assignId(key: object): number {\n const cachedId = this.#idCache.get(key)\n if (cachedId !== undefined) return cachedId\n\n const id = MultiKeyWeakMap.#globalIdCounter\n this.#idCache.set(key, id)\n MultiKeyWeakMap.#globalIdCounter++\n return id\n }\n\n /**\n * Remove duplicate keys and arrange them in a consistent order\n * by sorting according to their assigned IDs.\n */\n #arrangeKeys(keys: object[]): object[] {\n const uniqueKeys = Array.from(new Set(keys))\n const keyed = uniqueKeys.map((key) => [this.#assignId(key), key] as const)\n keyed.sort((a, b) => a[0] - b[0])\n return keyed.map(([, key]) => key)\n }\n\n /**\n * Recursively search the nested WeakMap structure for the value.\n */\n #getDeep(keys: object[], map: WeakMap<object, MultiKeyWeakMapNode>): unknown {\n if (keys.length === 0) return undefined\n\n const [firstKey, ...restKeys] = keys\n const node = map.get(firstKey)\n if (!node) return undefined\n if (restKeys.length === 0) return node.value\n return this.#getDeep(restKeys, node.next)\n }\n\n /**\n * Recursively create nodes along the key chain until the final key\n * is reached, then assign the value.\n */\n #setDeep(keys: object[], map: WeakMap<object, MultiKeyWeakMapNode>, value: unknown): void {\n if (keys.length === 0) return\n\n const [firstKey, ...restKeys] = keys\n let node = map.get(firstKey)\n if (!node) {\n node = {\n value: undefined,\n next: new WeakMap(),\n }\n map.set(firstKey, node)\n }\n\n if (restKeys.length === 0) {\n node.value = value\n } else {\n this.#setDeep(restKeys, node.next, value)\n }\n }\n\n /**\n * Retrieves the value associated with the array of keys.\n * The keys are de-duplicated and sorted so that the order does not matter.\n */\n get(keys: object[]): T | undefined {\n const arrangedKeys = this.#arrangeKeys(keys)\n return this.#getDeep(arrangedKeys, this.#rootMap) as T | undefined\n }\n\n /**\n * Associates the value with the given array of keys.\n */\n set(keys: object[], value: T): void {\n const arrangedKeys = this.#arrangeKeys(keys)\n this.#setDeep(arrangedKeys, this.#rootMap, value)\n }\n}\n","import {diffValue} from '@sanity/diff-patch'\nimport {jsonMatch, stringifyPath} from '@sanity/json-match'\nimport {type Mutation, type PatchOperations, type SanityDocument} from '@sanity/types'\nimport {evaluateSync, type ExprNode} from 'groq-js'\n\nimport {type Grant} from '../permissions'\nimport {type DocumentSet, processMutations} from '../processMutations'\nimport {type HttpAction} from '../reducers'\n\nexport interface ActionHandlerContext {\n base: DocumentSet\n working: DocumentSet\n transactionId: string\n timestamp: string\n grants: Record<Grant, ExprNode>\n /**\n * The current user's ID, used by GROQ's `identity()` when evaluating ACL\n * filters. May be `undefined` before the user has loaded; in that case\n * `identity()` evaluates to null.\n */\n identity: string | undefined\n outgoingActions: HttpAction[]\n outgoingMutations: Mutation[]\n}\n\nexport interface ActionHandlerResult {\n base: DocumentSet\n working: DocumentSet\n}\n\nexport function checkGrant(\n grantExpr: ExprNode,\n document: SanityDocument,\n identity: string | undefined,\n): boolean {\n const value = evaluateSync(grantExpr, {params: {document}, identity})\n return value.type === 'boolean' && value.data\n}\n\ninterface ActionErrorOptions {\n message: string\n documentId: string\n transactionId: string\n}\n\n/**\n * Thrown when a precondition for an action failed.\n */\nexport class ActionError extends Error implements ActionErrorOptions {\n documentId!: string\n transactionId!: string\n\n constructor(options: ActionErrorOptions) {\n super(options.message)\n Object.assign(this, options)\n }\n}\n\nexport class PermissionActionError extends ActionError {}\n\n/**\n * Creates a `processMutations` wrapper that surfaces application failures as\n * `ActionError`s when the caller asked to preserve their patch operations.\n * With preserved operations, patches can legitimately fail to apply (e.g.\n * re-applied onto a diverged document during a rebase), so wrapping lets a\n * rebase skip the transaction instead of failing the store. Without\n * `preserveOperations`, errors are rethrown untouched.\n */\nexport function createMutationApplier(options: {\n documentId: string\n transactionId: string\n timestamp: string\n preserveOperations: boolean | undefined\n}): (\n documents: DocumentSet,\n mutations: Mutation[],\n documentSetName: 'base' | 'working',\n) => DocumentSet {\n const {documentId, transactionId, timestamp, preserveOperations} = options\n return (documents, mutations, documentSetName) => {\n try {\n return processMutations({documents, transactionId, mutations, timestamp})\n } catch (error) {\n if (!preserveOperations) throw error\n throw new ActionError({\n documentId,\n transactionId,\n message: `Failed to apply patches to the ${documentSetName} document: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n })\n }\n }\n}\n\n/**\n * Re-applies the operational intent of user-supplied `inc`/`dec` patches to a\n * set of snapshot-diffed patches.\n *\n * The outgoing patches sent to Content Lake are re-derived by diffing the\n * base document before and after the user's patches were applied. That diff\n * collapses an `inc` into a `set` of the resulting number, which turns\n * concurrent increments from different clients into last-write-wins. This\n * helper finds diffed `set` operations that are exactly explained by the\n * user's `inc`/`dec` operations and swaps them back, so the server applies\n * the increment against whatever value is current at commit time.\n *\n * A `set` is only swapped when its value equals the base value plus the\n * accumulated user deltas for that exact path; anything else (e.g. an `inc`\n * combined with a `set` of the same field) keeps the diffed `set`.\n *\n * Content Lake fails the whole transaction when `inc`/`dec` targets a missing\n * path (unlike `set`, which always succeeds). To avoid turning a concurrent\n * field removal into a transaction revert, the emitted patch seeds the target\n * with a `setIfMissing` of the base value; the server executes `setIfMissing`\n * before `inc`/`dec` within a single patch, so a concurrently removed field\n * degrades to the same result the diffed `set` would have produced. A path\n * into a concurrently removed keyed array item can still fail the\n * transaction (`setIfMissing` cannot create array items); the transaction is\n * then reverted and reported, which is preferable to silently resurrecting\n * the item.\n */\nexport function preserveNumericOperations(\n baseBefore: SanityDocument | null | undefined,\n userPatches: PatchOperations[] | undefined,\n diffedPatches: PatchOperations[],\n): PatchOperations[] {\n if (!baseBefore || !userPatches?.length) return diffedPatches\n\n // accumulate the user's inc/dec deltas per concrete (stringified) path,\n // tracking the expected final value by replaying the additions in order\n const targets = new Map<string, {baseValue: number; expectedValue: number; delta: number}>()\n for (const patch of userPatches) {\n for (const [op, sign] of [\n ['inc', 1],\n ['dec', -1],\n ] as const) {\n const record = patch[op]\n if (!record) continue\n for (const [pathExpression, amount] of Object.entries(record)) {\n if (typeof amount !== 'number') continue\n for (const match of jsonMatch(baseBefore, pathExpression)) {\n const path = stringifyPath(match.path)\n const existing = targets.get(path)\n if (existing) {\n existing.expectedValue += sign * amount\n existing.delta += sign * amount\n } else if (typeof match.value === 'number') {\n targets.set(path, {\n baseValue: match.value,\n expectedValue: match.value + sign * amount,\n delta: sign * amount,\n })\n }\n }\n }\n }\n }\n if (!targets.size) return diffedPatches\n\n const preserved = new Map<string, {delta: number; baseValue: number}>()\n const next = diffedPatches.flatMap((patch) => {\n if (!patch.set) return [patch]\n\n const keptSet: Record<string, unknown> = {}\n for (const [path, value] of Object.entries(patch.set)) {\n const target = targets.get(path)\n if (target && typeof value === 'number' && value === target.expectedValue) {\n preserved.set(path, {delta: target.delta, baseValue: target.baseValue})\n } else {\n keptSet[path] = value\n }\n }\n\n if (Object.keys(keptSet).length === Object.keys(patch.set).length) return [patch]\n const {set: _set, ...rest} = patch\n const withKept = Object.keys(keptSet).length ? {...rest, set: keptSet} : rest\n return Object.keys(withKept).length ? [withKept] : []\n })\n\n if (!preserved.size) return next\n\n const setIfMissing: Record<string, unknown> = {}\n const inc: Record<string, number> = {}\n const dec: Record<string, number> = {}\n for (const [path, {delta, baseValue}] of preserved) {\n setIfMissing[path] = baseValue\n if (delta >= 0) inc[path] = delta\n else dec[path] = -delta\n }\n return [\n ...next,\n {\n setIfMissing,\n ...(Object.keys(inc).length && {inc}),\n ...(Object.keys(dec).length && {dec}),\n },\n ]\n}\n\ninterface ApplySingleDocPatchOptions {\n base: DocumentSet\n working: DocumentSet\n documentId: string\n patches: PatchOperations[] | undefined\n transactionId: string\n timestamp: string\n grants: Record<Grant, ExprNode>\n identity: string | undefined\n /**\n * Error message thrown when the target document does not exist in either\n * the base or working set.\n */\n notFoundMessage?: string\n /**\n * Error message thrown when the working document fails the `update` grant.\n */\n permissionMessage?: string\n /**\n * When `true`, the given patches are used verbatim instead of being\n * re-derived by diffing the base document before and after application.\n * Patch application failures are surfaced as `ActionError`s so a rebase\n * can skip the transaction instead of failing the store.\n */\n preserveOperations?: boolean\n}\n\ninterface ApplySingleDocPatchResult {\n base: DocumentSet\n working: DocumentSet\n /**\n * Patch operations representing the minimal diff between base before and\n * after the user's patches were applied. These are the patches that should\n * be sent to the server (or applied to the working set as mutations).\n */\n diffedPatches: PatchOperations[]\n /**\n * Mutation envelopes for `diffedPatches` already keyed to `documentId`.\n * Useful for callers that want to push them to `outgoingMutations`.\n */\n workingMutations: Mutation[]\n}\n\n/**\n * Shared logic for applying user-provided patches to a single document that\n * is identified by an exact ID (no draft/published wrapping). Used by the\n * liveEdit branch of `document.edit` and by `release.edit`.\n *\n * Returns the updated base + working sets, plus the diffed patches in both\n * raw and mutation form so the caller can decide what to send to the server.\n */\nexport function applySingleDocPatch({\n base: initialBase,\n working: initialWorking,\n documentId,\n patches,\n transactionId,\n timestamp,\n grants,\n identity,\n notFoundMessage = 'Cannot edit document because it does not exist.',\n permissionMessage = `You do not have permission to edit document \"${documentId}\".`,\n preserveOperations,\n}: ApplySingleDocPatchOptions): ApplySingleDocPatchResult {\n let base = initialBase\n let working = initialWorking\n\n const userPatches = patches?.map((patch) => ({patch: {id: documentId, ...patch}}))\n\n if (!userPatches?.length) {\n return {base, working, diffedPatches: [], workingMutations: []}\n }\n\n if (!working[documentId] || !base[documentId]) {\n throw new ActionError({documentId, transactionId, message: notFoundMessage})\n }\n\n const applyMutations = createMutationApplier({\n documentId,\n transactionId,\n timestamp,\n preserveOperations,\n })\n\n const baseBefore = base[documentId]\n base = applyMutations(base, userPatches, 'base')\n const baseAfter = base[documentId]\n const diffedPatches = preserveOperations\n ? (patches as PatchOperations[])\n : preserveNumericOperations(\n baseBefore,\n patches,\n diffValue(baseBefore, baseAfter) as PatchOperations[],\n )\n\n const workingBefore = working[documentId] as SanityDocument\n if (!checkGrant(grants.update, workingBefore, identity)) {\n throw new PermissionActionError({documentId, transactionId, message: permissionMessage})\n }\n\n const workingMutations: Mutation[] = diffedPatches.map((patch) => ({\n patch: {id: documentId, ...patch},\n }))\n\n working = applyMutations(working, workingMutations, 'working')\n\n return {base, working, diffedPatches, workingMutations}\n}\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type SanityDocument} from '@sanity/types'\nimport {type ExprNode, parse} from 'groq-js'\nimport {createSelector} from 'reselect'\n\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type SelectorContext} from '../store/createStateSourceAction'\nimport {randomId} from '../utils/ids'\nimport {MultiKeyWeakMap} from '../utils/MultiKeyWeakMap'\nimport {type DocumentAction} from './actions'\nimport {ActionError, PermissionActionError, processActions} from './processActions/processActions'\nimport {checkGrant} from './processActions/shared'\nimport {type DocumentSet} from './processMutations'\nimport {type SyncTransactionState} from './reducers'\n\nexport type Grant = 'read' | 'update' | 'create' | 'history'\n\nexport type DatasetAcl = {\n filter: string\n permissions: Grant[]\n}[]\n\nexport function createGrantsLookup(datasetAcl: DatasetAcl): Record<Grant, ExprNode> {\n const filtersByGrant: Record<Grant, Set<string>> = {\n create: new Set(),\n history: new Set(),\n read: new Set(),\n update: new Set(),\n }\n\n for (const entry of datasetAcl) {\n for (const grant of entry.permissions) {\n const set = filtersByGrant[grant]\n // Ignore permissions we don't model (e.g. \"manage\")\n if (!set) continue\n set.add(entry.filter)\n filtersByGrant[grant] = set\n }\n }\n\n return Object.fromEntries(\n Object.entries(filtersByGrant).map(([grant, filters]) => {\n const combinedFilter = Array.from(filters)\n .map((i) => `(${i})`)\n .join('||')\n\n if (!combinedFilter) return [grant, parse('false')]\n return [grant, parse(`$document {\"_\": ${combinedFilter}}._`)]\n }),\n ) as Record<Grant, ExprNode>\n}\n\n// Cache for documents based on an array of document objects.\nconst documentsCache = new MultiKeyWeakMap<DocumentSet>()\n// Use a WeakMap so that when a computed DocumentSet is no longer in use,\n// its nested cache for actions can be garbage-collected.\nconst actionsCache = new WeakMap<DocumentSet, Map<string, DocumentAction[]>>()\n\nconst nullReplacer: object = {}\n\n// Compute documents from state and actions.\n// (If the same documents are computed, the MultiKeyWeakMap will return the same instance.)\nconst documentsSelector = createSelector(\n [\n ({state: {documentStates}}: SelectorContext<SyncTransactionState>) => documentStates,\n (_context: SelectorContext<SyncTransactionState>, {actions}: {actions: DocumentAction[]}) =>\n actions,\n ],\n (documentStates, actions) => {\n // Collect all document IDs needed for permission checks.\n // Important: liveEdit documents don't have drafts, so we only fetch the single document to avoid waiting for non-existent draft documents.\n const documentIds = new Set(\n actions\n .map((action) => {\n if (typeof action.documentId !== 'string') return []\n // For liveEdit documents, only fetch the single document\n if (action.liveEdit) return [action.documentId]\n // For standard documents, fetch both draft and published\n const ids: string[] = [\n getPublishedId(DocumentId(action.documentId)),\n getDraftId(DocumentId(action.documentId)),\n ]\n if (isReleasePerspective(action.perspective)) {\n ids.push(getVersionId(DocumentId(action.documentId), action.perspective.releaseName))\n }\n return ids\n })\n .flat(),\n )\n\n const documents: DocumentSet = {}\n\n for (const documentId of documentIds) {\n const local = documentStates[documentId]?.local\n\n // early exit if we don't have all the documents yet\n if (local === undefined) return undefined\n documents[documentId] = local\n }\n\n // Create a key from the documents values (using a nullReplacer when needed).\n const keys = Object.values(\n // value in this record will be `undefined` because\n // of the early return if undefined is found above\n documents as Record<string, SanityDocument | null>,\n ).map((doc) => (doc === null ? nullReplacer : doc))\n const cached = documentsCache.get(keys)\n if (cached) return cached\n\n documentsCache.set(keys, documents)\n return documents\n },\n)\n\n// Cache the actions array based on a serialized version, but “attach” the cache\n// to the computed documents. That way if the computed documents object is no longer in use,\n// the cache is eligible for GC.\nconst memoizedActionsSelector = createSelector(\n [\n documentsSelector,\n (_state: SelectorContext<SyncTransactionState>, {actions}: {actions: DocumentAction[]}) =>\n actions,\n ],\n (documents, actions) => {\n if (!documents) return undefined\n\n // Get (or create) the nested Map for this computed documents.\n let nestedCache = actionsCache.get(documents)\n if (!nestedCache) {\n nestedCache = new Map<string, DocumentAction[]>()\n actionsCache.set(documents, nestedCache)\n }\n\n const normalizedActions = Array.isArray(actions) ? actions : [actions]\n\n // Use JSON.stringify to get a serialized key for the actions.\n // TODO: considering swapping thisfor a more efficient or stable hash\n const actionsKey = JSON.stringify(normalizedActions)\n const cached = nestedCache.get(actionsKey)\n if (cached) return cached\n\n nestedCache.set(actionsKey, normalizedActions)\n return normalizedActions\n },\n)\n\n/** @beta */\nexport interface PermissionDeniedReason {\n type: 'precondition' | 'access'\n message: string\n documentId?: string\n}\n\n/** @beta */\nexport type DocumentPermissionsResult =\n | {\n allowed: false\n message: string\n reasons: PermissionDeniedReason[]\n }\n | {allowed: true; message?: undefined; reasons?: undefined}\n\nconst enNarrowConjunction = new Intl.ListFormat('en', {style: 'narrow', type: 'conjunction'})\n\nexport function calculatePermissions(\n ...args: Parameters<typeof _calculatePermissions>\n): ReturnType<typeof _calculatePermissions> {\n return _calculatePermissions(...args)\n}\n\nconst _calculatePermissions = createSelector(\n [\n ({state: {grants}}: SelectorContext<SyncTransactionState>) => grants,\n ({state: {identity}}: SelectorContext<SyncTransactionState>) => identity,\n documentsSelector,\n memoizedActionsSelector,\n ],\n (\n grants: Record<Grant, ExprNode> | undefined,\n identity: string | undefined,\n documents: DocumentSet | undefined,\n actions: DocumentAction[] | undefined,\n ): DocumentPermissionsResult | undefined => {\n if (!documents) return undefined\n if (!grants) return undefined\n if (!actions) return undefined\n\n const timestamp = new Date().toISOString()\n const reasons: PermissionDeniedReason[] = []\n\n try {\n processActions({\n actions,\n transactionId: randomId(16),\n working: documents,\n base: documents,\n timestamp,\n grants,\n identity,\n })\n } catch (error) {\n if (error instanceof PermissionActionError) {\n reasons.push({\n message: error.message,\n documentId: error.documentId,\n type: 'access',\n })\n } else if (error instanceof ActionError) {\n reasons.push({\n message: error.message,\n documentId: error.documentId,\n type: 'precondition',\n })\n } else {\n throw error\n }\n }\n\n for (const action of actions) {\n // Check edit actions with no patches\n if (action.type === 'document.edit' && !action.patches?.length) {\n const docId = action.documentId\n let doc: SanityDocument | null | undefined\n if (action.liveEdit) {\n doc = documents[docId]\n }\n // don't allow users to edit version documents that don't exist\n // they should be explicitly created first, as in studio\n else if (isReleasePerspective(action.perspective)) {\n doc = documents[getVersionId(DocumentId(docId), action.perspective.releaseName)]\n } else {\n doc =\n documents[getDraftId(DocumentId(docId))] ?? documents[getPublishedId(DocumentId(docId))]\n }\n if (!doc) {\n if (isReleasePerspective(action.perspective)) {\n reasons.push({\n type: 'precondition',\n message: `The version document with ID \"${docId}\" could not be found. Please create it or add it to the release first.`,\n documentId: docId,\n })\n } else {\n reasons.push({\n type: 'precondition',\n message: `The document with ID \"${docId}\" could not be found. Please check that it exists before editing.`,\n documentId: docId,\n })\n }\n } else if (!checkGrant(grants.update, doc, identity)) {\n reasons.push({\n type: 'access',\n message: `You are not allowed to edit the document with ID \"${docId}\".`,\n documentId: docId,\n })\n }\n }\n }\n\n const allowed = reasons.length === 0\n if (allowed) return {allowed}\n\n const sortedReasons = reasons\n .map((reason, index) => ({...reason, index}))\n .sort((a, b) => {\n if (a.type !== b.type) return a.type === 'access' ? -1 : 1\n return a.message.localeCompare(b.message, 'en-US')\n })\n .map(({index: _index, ...reason}) => reason)\n\n return {\n allowed,\n reasons: sortedReasons,\n message: enNarrowConjunction.format(sortedReasons.map((i) => i.message)),\n }\n },\n)\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type CreateDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleCreate(\n action: CreateDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit) {\n if (working[documentId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `This document already exists.`,\n })\n }\n\n const newDocBase = {_type: action.documentType, _id: documentId, ...action.initialValue}\n const newDocWorking = {\n _type: action.documentType,\n _id: documentId,\n ...action.initialValue,\n }\n const mutations: Mutation[] = [{create: newDocWorking}]\n\n base = processMutations({\n documents: base,\n transactionId,\n mutations: [{create: newDocBase}],\n timestamp,\n })\n working = processMutations({\n documents: working,\n transactionId,\n mutations,\n timestamp,\n })\n\n if (!checkGrant(grants.create, working[documentId] as SanityDocument, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create document \"${documentId}\".`,\n })\n }\n\n // liveEdit documents use the mutation endpoint directly -- we don't send actions\n outgoingMutations.push(...mutations)\n return {base, working}\n }\n\n // Standard draft/published/version logic\n const versionId = isReleasePerspective(action.perspective)\n ? getVersionId(DocumentId(documentId), action.perspective.releaseName)\n : undefined\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n const alreadyHasVersion = versionId ? working[versionId] : working[draftId]\n\n if (alreadyHasVersion) {\n const errorDocType = versionId ? 'release version' : 'draft'\n throw new ActionError({\n documentId,\n transactionId,\n message: `A ${errorDocType} of this document already exists. Please use or discard the existing ${errorDocType} before creating a new one.`,\n })\n }\n\n // Spread the (possibly undefined) draft or published version directly.\n // (studio uses the draft version as a base if you are in a release perspective)\n const newDocBase = {\n ...(base[draftId] ?? base[publishedId]),\n _type: action.documentType,\n _id: versionId ?? draftId,\n ...action.initialValue,\n }\n const newDocWorking = {\n ...(working[draftId] ?? working[publishedId]),\n _type: action.documentType,\n _id: versionId ?? draftId,\n ...action.initialValue,\n }\n const mutations: Mutation[] = [{create: newDocWorking}]\n\n base = processMutations({\n documents: base,\n transactionId,\n mutations: [{create: newDocBase}],\n timestamp,\n })\n working = processMutations({\n documents: working,\n transactionId,\n mutations,\n timestamp,\n })\n\n if (versionId && !checkGrant(grants.create, working[versionId] as SanityDocument, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a release version for document \"${documentId}\".`,\n })\n } else if (\n !versionId &&\n !checkGrant(grants.create, working[draftId] as SanityDocument, identity)\n ) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a draft for document \"${documentId}\".`,\n })\n }\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.version.create',\n publishedId,\n attributes: newDocWorking,\n })\n return {base, working}\n}\n","import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'\nimport {type Mutation} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type DeleteDocumentAction} from '../actions'\nimport {processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleDelete(\n action: DeleteDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = action.documentId\n\n if (isReleasePerspective(action.perspective)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot delete a version document. You may want to use the \"unpublish\" or \"discard\" actions instead.`,\n })\n }\n\n if (action.liveEdit) {\n if (!working[documentId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: 'The document you are trying to delete does not exist.',\n })\n }\n\n if (!checkGrant(grants.update, working[documentId], identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to delete this document.`,\n })\n }\n\n const mutations: Mutation[] = [{delete: {id: documentId}}]\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n // although liveEdit documents can use the actions API for deletion,\n // having this be an action while other operations are mutations creates an inconsistency\n // (and a possible race condition in document store where mutations might get skipped)\n outgoingMutations.push(...mutations)\n return {base, working}\n }\n\n // Standard draft/published logic\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n if (!working[publishedId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: working[draftId]\n ? 'Cannot delete a document without a published version.'\n : 'The document you are trying to delete does not exist.',\n })\n }\n\n const cantDeleteDraft = working[draftId] && !checkGrant(grants.update, working[draftId], identity)\n const cantDeletePublished =\n working[publishedId] && !checkGrant(grants.update, working[publishedId], identity)\n\n if (cantDeleteDraft || cantDeletePublished) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to delete this document.`,\n })\n }\n\n const mutations: Mutation[] = [{delete: {id: publishedId}}, {delete: {id: draftId}}]\n const includeDrafts = working[draftId] ? [draftId] : undefined\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.delete',\n publishedId,\n ...(includeDrafts ? {includeDrafts} : {}),\n })\n return {base, working}\n}\n","import {DocumentId, getDraftId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type DiscardDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleDiscard(\n action: DiscardDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot discard changes for liveEdit document \"${documentId}\". LiveEdit documents do not support drafts.`,\n })\n }\n\n // draft/published or version logic\n const versionId = isReleasePerspective(action.perspective)\n ? getVersionId(DocumentId(documentId), action.perspective.releaseName)\n : getDraftId(DocumentId(documentId))\n const mutations: Mutation[] = [{delete: {id: versionId}}]\n\n if (!working[versionId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `There is no draft or version available to discard for document \"${documentId}\".`,\n })\n }\n\n if (!checkGrant(grants.update, working[versionId], identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to discard changes for document \"${documentId}\".`,\n })\n }\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.version.discard',\n versionId,\n })\n return {base, working}\n}\n","import {diffValue} from '@sanity/diff-patch'\nimport {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation, type PatchOperations, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type EditDocumentAction} from '../actions'\nimport {getId} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n applySingleDocPatch,\n checkGrant,\n createMutationApplier,\n PermissionActionError,\n preserveNumericOperations,\n} from './shared'\n\nexport function handleEdit(\n action: EditDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit) {\n const result = applySingleDocPatch({\n base,\n working,\n documentId,\n patches: action.patches,\n transactionId,\n timestamp,\n grants,\n identity,\n preserveOperations: action.preserveOperations,\n })\n // liveEdit documents use the mutation endpoint directly -- we don't send actions\n outgoingMutations.push(...result.workingMutations)\n return {base: result.base, working: result.working}\n }\n\n const versionId = isReleasePerspective(action.perspective)\n ? getVersionId(DocumentId(documentId), action.perspective.releaseName)\n : undefined\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n const patchDocumentId = isReleasePerspective(action.perspective) ? versionId! : draftId\n const userPatches = action.patches?.map((patch) => ({\n patch: {id: patchDocumentId, ...patch},\n }))\n\n // skip this action if there are no associated patches\n if (!userPatches?.length) return {base, working}\n\n if (isReleasePerspective(action.perspective)) {\n if (!working[versionId!] && !base[versionId!]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `This document does not exist in the release. Please create it or add it to the release first.`,\n })\n }\n } else if (\n (!working[draftId] && !working[publishedId]) ||\n (!base[draftId] && !base[publishedId])\n ) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot edit document because it does not exist in draft or published form.`,\n })\n }\n\n const applyMutations = createMutationApplier({\n documentId,\n transactionId,\n timestamp,\n preserveOperations: action.preserveOperations,\n })\n\n const baseMutations: Mutation[] = []\n // don't create a draft from the published version in a release perspective\n if (!isReleasePerspective(action.perspective) && !base[draftId] && base[publishedId]) {\n // otherwise make a draft from the published version\n baseMutations.push({create: {...base[publishedId], _id: draftId}})\n }\n\n // the above statement and guards should make this never be null or undefined\n const baseBefore = base[patchDocumentId] ?? base[publishedId]\n if (userPatches) {\n baseMutations.push(...userPatches)\n }\n\n base = applyMutations(base, baseMutations, 'base')\n // this one will always be defined because a patch mutation will never\n // delete an input document\n const baseAfter = base[patchDocumentId] as SanityDocument\n // when the caller asked us to preserve their operations, forward their\n // patches verbatim instead of re-deriving them from a snapshot diff. this\n // keeps keyed array operations addressable so concurrent edits from other\n // clients interleave instead of being overwritten. otherwise, diff the\n // snapshots but swap back any `inc`/`dec` the diff collapsed into a `set`,\n // so concurrent increments accumulate instead of last-write-winning\n const patches = action.preserveOperations\n ? (action.patches ?? [])\n : preserveNumericOperations(\n baseBefore,\n action.patches,\n diffValue(baseBefore, baseAfter) as PatchOperations[],\n )\n\n const workingMutations: Mutation[] = []\n if (!isReleasePerspective(action.perspective) && !working[draftId] && working[publishedId]) {\n const newDraftFromPublished = {...working[publishedId], _id: draftId}\n\n if (!checkGrant(grants.create, newDraftFromPublished, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a draft for editing this document.`,\n })\n }\n\n workingMutations.push({create: newDraftFromPublished})\n }\n\n // the first if statement should make this never be null or undefined\n const workingBefore = working[patchDocumentId] ?? working[publishedId]\n if (!checkGrant(grants.update, workingBefore!, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to edit document \"${documentId}\".`,\n })\n }\n workingMutations.push(...patches.map((patch) => ({patch: {id: patchDocumentId, ...patch}})))\n\n working = applyMutations(working, workingMutations, 'working')\n\n outgoingMutations.push(...workingMutations)\n outgoingActions.push(\n ...patches.map((patch) => ({\n actionType: 'sanity.action.document.edit' as const,\n draftId: patchDocumentId,\n publishedId,\n patch: patch as PatchOperations,\n })),\n )\n\n return {base, working}\n}\n","import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'\nimport {type Mutation, type Reference, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {isDeepEqual} from '../../utils/object'\nimport {type PublishDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handlePublish(\n action: PublishDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit || isReleasePerspective(action.perspective)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot publish this document. Publishing is not supported for liveEdit or version (release) documents.`,\n })\n }\n\n // Standard draft/published logic\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n const workingDraft = working[draftId]\n const baseDraft = base[draftId]\n if (!workingDraft || !baseDraft) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot publish because no draft version was found for document \"${documentId}\".`,\n })\n }\n\n // Before proceeding, verify that the working draft is identical to the base draft.\n // TODO: is it enough just to check for the _rev or nah?\n if (!isDeepEqual(workingDraft, baseDraft)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Publish aborted: The document has changed elsewhere. Please try again.`,\n })\n }\n\n const newPublishedFromDraft = {...strengthenOnPublish(workingDraft), _id: publishedId}\n\n const mutations: Mutation[] = [{delete: {id: draftId}}, {createOrReplace: newPublishedFromDraft}]\n\n if (working[draftId] && !checkGrant(grants.update, working[draftId], identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `Publish failed: You do not have permission to update the draft for \"${documentId}\".`,\n })\n }\n\n if (working[publishedId] && !checkGrant(grants.update, newPublishedFromDraft, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `Publish failed: You do not have permission to update the published version of \"${documentId}\".`,\n })\n } else if (!working[publishedId] && !checkGrant(grants.create, newPublishedFromDraft, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `Publish failed: You do not have permission to publish a new version of \"${documentId}\".`,\n })\n }\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.publish',\n draftId,\n publishedId,\n })\n return {base, working}\n}\n\nfunction strengthenOnPublish(draft: SanityDocument): SanityDocument {\n const isStrengthenReference = (\n value: object,\n ): value is Reference & Required<Pick<Reference, '_strengthenOnPublish'>> =>\n '_strengthenOnPublish' in value\n\n function strengthen(value: unknown): unknown {\n if (typeof value !== 'object' || !value) return value\n\n if (isStrengthenReference(value)) {\n const {_strengthenOnPublish, _weak, ...rest} = value\n return {\n ...rest,\n ...(_strengthenOnPublish.weak && {_weak: true}),\n }\n }\n\n if (Array.isArray(value)) {\n return value.map(strengthen)\n }\n\n return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, strengthen(v)]))\n }\n\n return strengthen(draft) as SanityDocument\n}\n","import {type ArchiveReleaseAction, type UnarchiveReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleaseArchive(\n action: ArchiveReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot archive release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to archive release \"${action.releaseId}\".`,\n })\n }\n\n // Archiving deletes every version document in the release server-side.\n // Although technically we could search through the document store for all related\n // version documents, for now it's simpler to just let the server handle it.\n // (Those local documents will be eventually updated by the listener.)\n outgoingActions.push({\n actionType: 'sanity.action.release.archive',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n\nexport function handleReleaseUnarchive(\n action: UnarchiveReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot unarchive release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to unarchive release \"${action.releaseId}\".`,\n })\n }\n\n outgoingActions.push({\n actionType: 'sanity.action.release.unarchive',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {type Mutation, type SanityDocument} from '@sanity/types'\n\nimport {type CreateReleaseAction} from '../actions'\nimport {processMutations} from '../processMutations'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleaseCreate(\n action: CreateReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity} = ctx\n let {base, working} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n\n if (working[releaseDocumentId] || base[releaseDocumentId]) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `A release with id \"${action.releaseId}\" already exists.`,\n })\n }\n // Optimistic local release doc\n const releaseDoc = {\n _id: releaseDocumentId,\n _type: 'system.release',\n name: action.releaseId,\n state: 'active',\n metadata: action.metadata,\n }\n const mutations: Mutation[] = [{create: releaseDoc}]\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n if (!checkGrant(grants.create, working[releaseDocumentId] as SanityDocument, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to create release \"${action.releaseId}\".`,\n })\n }\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.release.create',\n releaseId: action.releaseId,\n metadata: action.metadata,\n })\n\n return {base, working}\n}\n","import {type Mutation} from '@sanity/types'\n\nimport {type DeleteReleaseAction} from '../actions'\nimport {processMutations} from '../processMutations'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\n// you can only delete archived or published releases\n// https://www.sanity.io/docs/content-lake/dispatch-actions#k22ab37420f3c\nconst DELETABLE_STATES = new Set(['archived', 'published'])\n\nexport function handleReleaseDelete(\n action: DeleteReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity} = ctx\n let {base, working} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot delete release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n const state = existing['state']\n if (state && typeof state === 'string' && !DELETABLE_STATES.has(state)) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot delete release \"${action.releaseId}\" while it is \"${state}\". Archive it first.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to delete release \"${action.releaseId}\".`,\n })\n }\n\n const mutations: Mutation[] = [{delete: {id: releaseDocumentId}}]\n\n base = processMutations({documents: base, transactionId, mutations, timestamp})\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.release.delete',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {type EditReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {type ActionHandlerContext, type ActionHandlerResult, applySingleDocPatch} from './shared'\n\nexport function handleReleaseEdit(\n action: EditReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, outgoingActions, outgoingMutations, identity} = ctx\n const {base, working} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n\n const result = applySingleDocPatch({\n base,\n working,\n documentId: releaseDocumentId,\n patches: [action.patch],\n transactionId,\n timestamp,\n grants,\n identity,\n notFoundMessage: `Cannot edit release \"${action.releaseId}\" because it does not exist.`,\n permissionMessage: `You do not have permission to edit release \"${action.releaseId}\".`,\n })\n\n outgoingMutations.push(...result.workingMutations)\n outgoingActions.push(\n ...result.diffedPatches.map((patch) => ({\n actionType: 'sanity.action.release.edit' as const,\n releaseId: action.releaseId,\n patch,\n })),\n )\n\n return {base: result.base, working: result.working}\n}\n","import {type PublishReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleasePublish(\n action: PublishReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot publish release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to publish release \"${action.releaseId}\".`,\n })\n }\n\n // a release publish cascades to every version document in the release\n // although technically we could search through the document store for all related\n // version documents, for now it's simpler to just let the server handle it.\n // (Those local documents will be eventually updated by the listener.)\n outgoingActions.push({\n actionType: 'sanity.action.release.publish',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {type ScheduleReleaseAction, type UnscheduleReleaseAction} from '../actions'\nimport {getReleaseDocumentId} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleReleaseSchedule(\n action: ScheduleReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n\n if (Number.isNaN(Date.parse(action.publishAt))) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot schedule release \"${action.releaseId}\": \"publishAt\" must be a valid ISO 8601 timestamp (received \"${action.publishAt}\").`,\n })\n }\n\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot schedule release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to schedule release \"${action.releaseId}\".`,\n })\n }\n\n // Scheduling flips `state` to 'scheduled' and locks version documents\n // server-side. We don't model the lock locally yet — local edits to those\n // version docs will be rejected at submit time. The listener will sync the\n // updated release state.\n outgoingActions.push({\n actionType: 'sanity.action.release.schedule',\n releaseId: action.releaseId,\n publishAt: action.publishAt,\n })\n\n return {base, working}\n}\n\nexport function handleReleaseUnschedule(\n action: UnscheduleReleaseAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {base, working, grants, outgoingActions, transactionId, identity} = ctx\n\n const releaseDocumentId = getReleaseDocumentId(action.releaseId)\n const existing = working[releaseDocumentId] ?? base[releaseDocumentId]\n if (!existing) {\n throw new ActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `Cannot unschedule release \"${action.releaseId}\" because it does not exist.`,\n })\n }\n\n if (!checkGrant(grants.update, existing, identity)) {\n throw new PermissionActionError({\n documentId: releaseDocumentId,\n transactionId,\n message: `You do not have permission to unschedule release \"${action.releaseId}\".`,\n })\n }\n\n outgoingActions.push({\n actionType: 'sanity.action.release.unschedule',\n releaseId: action.releaseId,\n })\n\n return {base, working}\n}\n","import {DocumentId, getDraftId, getPublishedId} from '@sanity/id-utils'\nimport {type Mutation, type SanityDocument} from '@sanity/types'\n\nimport {isReleasePerspective} from '../../releases/utils/isReleasePerspective'\nimport {type UnpublishDocumentAction} from '../actions'\nimport {getId, processMutations} from '../processMutations'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n checkGrant,\n PermissionActionError,\n} from './shared'\n\nexport function handleUnpublish(\n action: UnpublishDocumentAction,\n ctx: ActionHandlerContext,\n): ActionHandlerResult {\n const {transactionId, timestamp, grants, identity, outgoingActions, outgoingMutations} = ctx\n let {base, working} = ctx\n\n const documentId = getId(action.documentId)\n\n if (action.liveEdit || isReleasePerspective(action.perspective)) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot unpublish this document. Unpublishing is not supported for liveEdit or version (release) documents.`,\n })\n }\n\n // Standard draft/published or version logic\n const draftId = getDraftId(DocumentId(documentId))\n const publishedId = getPublishedId(DocumentId(documentId))\n\n if (!working[publishedId] && !base[publishedId]) {\n throw new ActionError({\n documentId,\n transactionId,\n message: `Cannot unpublish because the document \"${documentId}\" is not currently published.`,\n })\n }\n\n const sourceDoc = working[publishedId] ?? (base[publishedId] as SanityDocument)\n const newDraftFromPublished = {...sourceDoc, _id: draftId}\n const mutations: Mutation[] = [\n {delete: {id: publishedId}},\n {createIfNotExists: newDraftFromPublished},\n ]\n\n if (!checkGrant(grants.update, sourceDoc, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to unpublish the document \"${documentId}\".`,\n })\n }\n\n if (!working[draftId] && !checkGrant(grants.create, newDraftFromPublished, identity)) {\n throw new PermissionActionError({\n documentId,\n transactionId,\n message: `You do not have permission to create a draft from the published version of \"${documentId}\".`,\n })\n }\n\n base = processMutations({\n documents: base,\n transactionId,\n mutations: [\n {delete: {id: publishedId}},\n {createIfNotExists: {...(base[publishedId] ?? sourceDoc), _id: draftId}},\n ],\n timestamp,\n })\n working = processMutations({documents: working, transactionId, mutations, timestamp})\n\n outgoingMutations.push(...mutations)\n outgoingActions.push({\n actionType: 'sanity.action.document.unpublish',\n draftId,\n publishedId,\n })\n return {base, working}\n}\n","import {type Mutation} from '@sanity/types'\nimport {type ExprNode} from 'groq-js'\n\nimport {type Action, type DocumentAction} from '../actions'\nimport {type Grant} from '../permissions'\nimport {type DocumentSet} from '../processMutations'\nimport {type HttpAction} from '../reducers'\nimport {handleCreate} from './create'\nimport {handleDelete} from './delete'\nimport {handleDiscard} from './discard'\nimport {handleEdit} from './edit'\nimport {handlePublish} from './publish'\nimport {handleReleaseArchive, handleReleaseUnarchive} from './releaseArchive'\nimport {handleReleaseCreate} from './releaseCreate'\nimport {handleReleaseDelete} from './releaseDelete'\nimport {handleReleaseEdit} from './releaseEdit'\nimport {handleReleasePublish} from './releasePublish'\nimport {handleReleaseSchedule, handleReleaseUnschedule} from './releaseSchedule'\nimport {isReleaseAction} from './releaseUtil'\nimport {\n ActionError,\n type ActionHandlerContext,\n type ActionHandlerResult,\n PermissionActionError,\n} from './shared'\nimport {handleUnpublish} from './unpublish'\n\nexport {ActionError, PermissionActionError}\n\ninterface ProcessActionsOptions {\n /**\n * The ID of this transaction. This will become the resulting `_rev` for all\n * documents affected by changes derived from the current set of actions.\n */\n transactionId: string\n\n /**\n * The actions to apply to the given documents\n */\n actions: Action[]\n\n /**\n * The set of documents these actions were intended to be applied to. These\n * set of documents should be captured right before a queued action is\n * applied.\n */\n base: DocumentSet\n\n /**\n * The current \"working\" set of documents. A patch will be created by applying\n * the actions to the base. This patch will then be applied to the working\n * set for conflict resolution. Initially, this value should match the base\n * set.\n */\n working: DocumentSet\n\n /**\n * The timestamp to use for `_updateAt` and other similar timestamps for this\n * transaction\n */\n timestamp: string\n\n /**\n * the lookup with pre-parsed GROQ expressions\n */\n grants: Record<Grant, ExprNode>\n\n /**\n * The current user's ID, passed to GROQ as the value of `identity()` when\n * evaluating ACL filters. Optional because the user may not have loaded yet;\n * filters that reference `identity()` will evaluate to null in that case.\n */\n identity?: string\n\n // // TODO: implement initial values from the schema?\n // initialValues?: {[TDocumentType in string]?: {_type: string}}\n}\n\ninterface ProcessActionsResult {\n /**\n * The resulting document set after the actions have been applied. This is\n * derived from the working documents.\n */\n working: DocumentSet\n /**\n * The document set before the actions have been applied. This is simply the\n * input of the `working` document set.\n */\n previous: DocumentSet\n /**\n * The outgoing action that were collected when applying the actions. These\n * are sent to the Actions HTTP API\n */\n outgoingActions: HttpAction[]\n /**\n * The outgoing mutations that were collected when applying the actions. These\n * are here for debugging purposes.\n */\n outgoingMutations: Mutation[]\n /**\n * The previous revisions of the given documents before the actions were applied.\n */\n previousRevs: {[TDocumentId in string]?: string}\n}\n\n/**\n * Applies the given set of actions to the working set of documents and converts\n * high-level actions into lower-level outgoing mutations/actions that respect\n * the current state of the working documents.\n *\n * Supports a \"base\" and \"working\" set of documents to allow actions to be\n * applied on top of a different working set of documents in a 3-way merge\n *\n * Actions are applied to the base set of documents first. The difference\n * between the base before and after is used to create a patch. This patch is\n * then applied to the working set of documents and is set as the outgoing patch\n * sent to the server.\n */\nexport function processActions({\n actions,\n transactionId,\n working: initialWorking,\n base: initialBase,\n timestamp,\n grants,\n identity,\n}: ProcessActionsOptions): ProcessActionsResult {\n let base: DocumentSet = {...initialBase}\n let working: DocumentSet = {...initialWorking}\n\n const outgoingActions: HttpAction[] = []\n const outgoingMutations: Mutation[] = []\n\n // liveEdit document actions go to the mutations API, since the actions API\n // requires a draft+published pair. Mixing them with anything else in the same\n // transaction would silently lose atomicity for the non-liveEdit operations,\n // so require users to split the transaction.\n // (Note that the reducers already does this for us -- you'd have to try hard to mix them.)\n const liveEditAction = actions.find((action) => !isReleaseAction(action) && action.liveEdit) as\n | DocumentAction\n | undefined\n const otherAction = actions.find((action) => isReleaseAction(action) || !action.liveEdit)\n if (liveEditAction && otherAction) {\n throw new ActionError({\n documentId: liveEditAction.documentId!,\n transactionId,\n message:\n 'Cannot combine liveEdit document actions with other actions in the same transaction. Submit them as separate transactions.',\n })\n }\n\n for (const action of actions) {\n const result = dispatch(action, {\n base,\n working,\n transactionId,\n timestamp,\n grants,\n identity,\n outgoingActions,\n outgoingMutations,\n })\n base = result.base\n working = result.working\n }\n\n const previousRevs = Object.fromEntries(\n Object.entries(initialWorking).map(([id, doc]) => [id, doc?._rev]),\n )\n\n return {\n working,\n outgoingActions,\n outgoingMutations,\n previous: initialWorking,\n previousRevs,\n }\n}\n\nfunction dispatch(action: Action, ctx: ActionHandlerContext): ActionHandlerResult {\n switch (action.type) {\n case 'document.create':\n return handleCreate(action, ctx)\n case 'document.delete':\n return handleDelete(action, ctx)\n case 'document.discard':\n return handleDiscard(action, ctx)\n case 'document.edit':\n return handleEdit(action, ctx)\n case 'document.publish':\n return handlePublish(action, ctx)\n case 'document.unpublish':\n return handleUnpublish(action, ctx)\n case 'release.create':\n return handleReleaseCreate(action, ctx)\n case 'release.edit':\n return handleReleaseEdit(action, ctx)\n case 'release.publish':\n return handleReleasePublish(action, ctx)\n case 'release.schedule':\n return handleReleaseSchedule(action, ctx)\n case 'release.unschedule':\n return handleReleaseUnschedule(action, ctx)\n case 'release.archive':\n return handleReleaseArchive(action, ctx)\n case 'release.unarchive':\n return handleReleaseUnarchive(action, ctx)\n case 'release.delete':\n return handleReleaseDelete(action, ctx)\n default:\n throw new Error(\n `Unknown action type: \"${\n // @ts-expect-error invalid input\n action.type\n }\". Please contact support if this issue persists.`,\n )\n }\n}\n","import {DocumentId, getDraftId, getPublishedId, getVersionId} from '@sanity/id-utils'\nimport {type Mutation, type PatchOperations, type SanityDocumentLike} from '@sanity/types'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type StoreContext} from '../store/defineStore'\nimport {randomId} from '../utils/ids'\nimport {isDeepEqual, omitProperty} from '../utils/object'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {type Action} from './actions'\nimport {DOCUMENT_STATE_CLEAR_DELAY, UNVERIFIED_REVISION_RETENTION_TIME} from './documentConstants'\nimport {type DocumentState, type DocumentStoreState} from './documentStore'\nimport {type RemoteDocument} from './listen'\nimport {ActionError, processActions} from './processActions/processActions'\nimport {getReleaseDocumentId, isReleaseAction} from './processActions/releaseUtil'\nimport {type DocumentSet} from './processMutations'\n\nconst EMPTY_REVISIONS: NonNullable<Required<DocumentState['unverifiedRevisions']>> = {}\n\n/**\n * How many of this client's own transaction IDs to remember per document for\n * `remote-patches` origin labeling. Sized to comfortably outlast the window\n * between submitting a transaction and observing its listener echo.\n */\nconst MAX_RECENT_OWN_TRANSACTION_IDS = 50\n\nexport type SyncTransactionState = Pick<\n DocumentStoreState,\n 'queued' | 'applied' | 'documentStates' | 'outgoing' | 'grants' | 'identity'\n>\n\ntype DocumentHandleLike = Pick<DocumentHandle, 'perspective'> & {\n documentId?: string\n liveEdit?: boolean\n}\n\ntype ActionMap = {\n create: 'sanity.action.document.version.create'\n discard: 'sanity.action.document.version.discard'\n unpublish: 'sanity.action.document.unpublish'\n delete: 'sanity.action.document.delete'\n edit: 'sanity.action.document.edit'\n publish: 'sanity.action.document.publish'\n releaseCreate: 'sanity.action.release.create'\n releaseEdit: 'sanity.action.release.edit'\n releasePublish: 'sanity.action.release.publish'\n releaseSchedule: 'sanity.action.release.schedule'\n releaseUnschedule: 'sanity.action.release.unschedule'\n releaseArchive: 'sanity.action.release.archive'\n releaseUnarchive: 'sanity.action.release.unarchive'\n releaseDelete: 'sanity.action.release.delete'\n}\n\ntype OptimisticLock = {\n ifDraftRevisionId?: string\n ifPublishedRevisionId?: string\n}\n\ninterface ReleaseMetadataPayload {\n title?: string\n description?: string\n intendedPublishAt?: string\n releaseType?: 'asap' | 'scheduled' | 'undecided'\n cardinality?: 'one' | 'many'\n}\n\nexport type HttpAction =\n | {actionType: ActionMap['create']; publishedId: string; attributes: SanityDocumentLike}\n | {actionType: ActionMap['discard']; versionId: string; purge?: boolean}\n | {actionType: ActionMap['unpublish']; draftId: string; publishedId: string}\n | {actionType: ActionMap['delete']; publishedId: string; includeDrafts?: string[]}\n | {actionType: ActionMap['edit']; draftId: string; publishedId: string; patch: PatchOperations}\n | ({actionType: ActionMap['publish']; draftId: string; publishedId: string} & OptimisticLock)\n | {\n actionType: ActionMap['releaseCreate']\n releaseId: string\n metadata?: ReleaseMetadataPayload\n }\n | {actionType: ActionMap['releaseEdit']; releaseId: string; patch: PatchOperations}\n | {actionType: ActionMap['releasePublish']; releaseId: string}\n | {actionType: ActionMap['releaseSchedule']; releaseId: string; publishAt: string}\n | {actionType: ActionMap['releaseUnschedule']; releaseId: string}\n | {actionType: ActionMap['releaseArchive']; releaseId: string}\n | {actionType: ActionMap['releaseUnarchive']; releaseId: string}\n | {actionType: ActionMap['releaseDelete']; releaseId: string}\n\n/**\n * Represents a transaction that is queued to be applied but has not yet been\n * applied. A transaction will remain in a queued state until all required\n * documents for the transactions are available locally.\n */\nexport interface QueuedTransaction {\n /**\n * the ID of this transaction. this is generated client-side.\n */\n transactionId: string\n /**\n * the high-level actions associated with this transaction. note that these\n * actions don't mention draft IDs and is meant to abstract away the draft\n * model from users.\n */\n actions: Action[]\n /**\n * An optional flag set to disable this transaction from being batched with\n * other transactions.\n */\n disableBatching?: boolean\n}\n\n/**\n * Represents a transaction that has been applied locally but has not been\n * committed/transitioned-to-outgoing. These transactions are visible to the\n * user but may be rebased upon a new working document set. Applied transactions\n * also contain the resulting `outgoingActions` that will be submitted to\n * Content Lake. These `outgoingActions` depend on the state of the working\n * documents so they are recomputed on rebase and are only relevant to applied\n * actions (we cannot compute `outgoingActions` for queued transactions because\n * we haven't resolved the set of documents the actions are dependent on yet).\n *\n * In order to support better conflict resolution, the original `previous` set\n * is saved as the `base` set.\n */\nexport interface AppliedTransaction extends QueuedTransaction {\n /**\n * the resulting set of documents after the actions have been applied\n */\n working: DocumentSet\n\n /**\n * the previous set of documents before the action was applied\n */\n previous: DocumentSet\n\n /**\n * the original `previous` document set captured when this action was\n * originally applied. this is used as a reference point to do a 3-way merge\n * if this applied transaction ever needs to be reapplied on a different\n * set of documents.\n */\n base: DocumentSet\n\n /**\n * the `_rev`s from `previous` document set\n */\n previousRevs: {[TDocumentId in string]?: string}\n\n /**\n * a timestamp for when this transaction was applied locally\n */\n timestamp: string\n\n /**\n * the resulting HTTP actions derived from the state of the `working` document\n * set. these are sent to Content Lake as-is when this transaction is batched\n * and transitioned into an outgoing transaction.\n */\n outgoingActions: HttpAction[]\n\n /**\n * similar to `outgoingActions` but comprised of mutations instead of actions.\n * Useful for debugging, and is also used by liveEdit documents to send mutations,\n * since they can't use the Actions API which is pretty dependent on the draft model.\n */\n outgoingMutations: Mutation[]\n}\n\n/**\n * Represents a set of applied transactions batched into a single outgoing\n * transaction. An outgoing transaction is the result of batching many applied\n * actions. An outgoing transaction may be reverted locally if the server\n * does not accept it.\n */\nexport interface OutgoingTransaction extends AppliedTransaction {\n disableBatching: boolean\n batchedTransactionIds: string[]\n}\n\nexport interface UnverifiedDocumentRevision {\n transactionId: string\n documentId: string\n previousRev: string | undefined\n timestamp: string\n}\n\nexport function queueTransaction(\n prev: SyncTransactionState,\n transaction: QueuedTransaction,\n): SyncTransactionState {\n const {transactionId, actions} = transaction\n const prevWithSubscriptionIds = getDocumentIdsFromHandleLikes(actions).reduce(\n (acc, id) => addSubscriptionIdToDocument(acc, id, transactionId),\n prev,\n )\n\n return {\n ...prevWithSubscriptionIds,\n queued: [...prev.queued, transaction],\n }\n}\n\nexport function removeQueuedTransaction(\n prev: SyncTransactionState,\n transactionId: string,\n): SyncTransactionState {\n const transaction = prev.queued.find((t) => t.transactionId === transactionId)\n if (!transaction) return prev\n\n const prevWithSubscriptionIds = getDocumentIdsFromHandleLikes(transaction.actions).reduce(\n (acc, id) => removeSubscriptionIdFromDocument(acc, id, transactionId),\n prev,\n )\n\n return {\n ...prevWithSubscriptionIds,\n queued: prev.queued.filter((t) => transactionId !== t.transactionId),\n }\n}\n\nexport function applyFirstQueuedTransaction(prev: SyncTransactionState): SyncTransactionState {\n const queued = prev.queued.at(0)\n if (!queued) return prev\n if (!prev.grants) return prev\n\n const ids = getDocumentIdsFromHandleLikes(queued.actions)\n // the local value is only ever `undefined` if it has not been loaded yet\n // we can't get the next applied state unless all relevant documents are ready\n if (ids.some((id) => prev.documentStates[id]?.local === undefined)) return prev\n\n const working = ids.reduce<DocumentSet>((acc, id) => {\n acc[id] = prev.documentStates[id]?.local\n return acc\n }, {})\n\n const timestamp = new Date().toISOString()\n\n const result = processActions({\n ...queued,\n working,\n base: working,\n timestamp,\n grants: prev.grants,\n identity: prev.identity,\n })\n const applied: AppliedTransaction = {\n ...queued,\n ...result,\n base: result.previous,\n timestamp,\n }\n\n return {\n ...prev,\n applied: [...prev.applied, applied],\n queued: prev.queued.filter((t) => t.transactionId !== queued.transactionId),\n documentStates: Object.entries(result.working).reduce(\n (acc, [id, next]) => {\n const prevDoc = acc[id]\n if (!prevDoc) return acc\n acc[id] = {...prevDoc, local: next}\n return acc\n },\n {...prev.documentStates},\n ),\n }\n}\n\nexport function batchAppliedTransactions([curr, ...rest]: AppliedTransaction[]):\n | OutgoingTransaction\n | undefined {\n // No transactions? Nothing to batch.\n if (!curr) return undefined\n\n // Skip transactions with no actions.\n if (!curr.actions.length) return batchAppliedTransactions(rest)\n\n // If there are multiple actions, we cannot batch further.\n if (curr.actions.length > 1) {\n return {\n ...curr,\n disableBatching: true,\n batchedTransactionIds: [curr.transactionId],\n }\n }\n\n const [action] = curr.actions\n\n // If the single action isn't a document.edit or batching is disabled,\n // mark this transaction as non-batchable.\n if (action.type !== 'document.edit' || curr.disableBatching) {\n return {\n ...curr,\n disableBatching: true,\n batchedTransactionIds: [curr.transactionId],\n }\n }\n\n // Create an outgoing transaction for the single edit action.\n // At this point, batching is allowed.\n const editAction: OutgoingTransaction = {\n ...curr,\n actions: [action],\n disableBatching: false,\n batchedTransactionIds: [curr.transactionId],\n }\n if (!rest.length) return editAction\n\n const next = batchAppliedTransactions(rest)\n if (!next) return undefined\n if (next.disableBatching) return editAction\n\n // Don't batch a liveEdit edit with a non-liveEdit edit — they route to different APIs\n const nextFirst = next.actions[0]\n const nextLiveEdit = nextFirst && 'liveEdit' in nextFirst ? nextFirst.liveEdit : false\n if (!!action.liveEdit !== !!nextLiveEdit) return editAction\n\n return {\n disableBatching: false,\n // Use the transactionId from the later (next) transaction.\n transactionId: next.transactionId,\n // Accumulate actions: current action first, then later ones.\n actions: [action, ...next.actions],\n // Merge outgoingActions in order.\n outgoingActions: [...curr.outgoingActions, ...next.outgoingActions],\n // Batched transaction IDs: preserve order by placing curr first.\n batchedTransactionIds: [curr.transactionId, ...next.batchedTransactionIds],\n // Merge outgoingMutations in order.\n outgoingMutations: [...curr.outgoingMutations, ...next.outgoingMutations],\n // Working state reflects the latest optimistic changes: later transactions override earlier.\n working: {...curr.working, ...next.working},\n // Base state (base, previous, previousRevs) must reflect the original state.\n // Use curr values (the earliest transaction) to override later ones.\n previousRevs: {...next.previousRevs, ...curr.previousRevs},\n previous: {...next.previous, ...curr.previous},\n base: {...next.base, ...curr.base},\n // Use the earliest timestamp from curr.\n timestamp: curr.timestamp ?? next.timestamp,\n }\n}\n\nexport function transitionAppliedTransactionsToOutgoing(\n prev: SyncTransactionState,\n): SyncTransactionState {\n if (prev.outgoing) return prev\n\n const transaction = batchAppliedTransactions(prev.applied)\n if (!transaction) return prev\n\n const {\n transactionId,\n previousRevs,\n working,\n batchedTransactionIds: consumedTransactions,\n } = transaction\n const timestamp = new Date().toISOString()\n\n return {\n ...prev,\n outgoing: transaction,\n applied: prev.applied.filter((i) => !consumedTransactions.includes(i.transactionId)),\n documentStates: Object.entries(previousRevs).reduce(\n (acc, [documentId, previousRev]) => {\n if (working[documentId]?._rev === previousRev) return acc\n\n const documentState = prev.documentStates[documentId]\n if (!documentState) return acc\n\n acc[documentId] = {\n ...documentState,\n unverifiedRevisions: {\n ...documentState.unverifiedRevisions,\n // add unverified revision\n [transactionId]: {documentId, previousRev, transactionId, timestamp},\n },\n // also track the transaction ID durably (unverified revisions can be\n // pruned by sync events before the listener echo arrives) so\n // `remote-patches` events label our own transactions correctly\n recentOwnTransactionIds: [\n ...(documentState.recentOwnTransactionIds ?? []).slice(\n -(MAX_RECENT_OWN_TRANSACTION_IDS - 1),\n ),\n transactionId,\n ],\n }\n\n return acc\n },\n {...prev.documentStates},\n ),\n }\n}\n\nexport function cleanupOutgoingTransaction(prev: SyncTransactionState): SyncTransactionState {\n const {outgoing} = prev\n if (!outgoing) return prev\n\n let next = prev\n const ids = getDocumentIdsFromHandleLikes(outgoing.actions)\n for (const transactionId of outgoing.batchedTransactionIds) {\n for (const documentId of ids) {\n next = removeSubscriptionIdFromDocument(next, documentId, transactionId)\n }\n }\n\n // every ack is also when a previously retained document's echo may have\n // stopped being expected, so re-make that eviction decision here\n return {\n ...next,\n outgoing: undefined,\n documentStates: evictOrphanedDocumentStates(next.documentStates),\n }\n}\n\nexport function revertOutgoingTransaction(prev: SyncTransactionState): SyncTransactionState {\n if (!prev.grants) return prev\n\n // unregister the reverted batch's transaction-held subscriptions, symmetric\n // with `cleanupOutgoingTransaction` on ack and `removeQueuedTransaction` on\n // apply failure. without this, a reverted document's subscription IDs are\n // never released and it can never be evicted\n let base = prev\n if (prev.outgoing) {\n const ids = getDocumentIdsFromHandleLikes(prev.outgoing.actions)\n for (const transactionId of prev.outgoing.batchedTransactionIds) {\n for (const documentId of ids) {\n base = removeSubscriptionIdFromDocument(base, documentId, transactionId)\n }\n }\n }\n\n let working = Object.fromEntries(\n Object.entries(base.documentStates).map(([documentId, documentState]) => [\n documentId,\n documentState?.remote,\n ]),\n )\n const nextApplied: AppliedTransaction[] = []\n\n for (const t of prev.applied) {\n try {\n const next = processActions({...t, working, grants: prev.grants, identity: prev.identity})\n working = next.working\n nextApplied.push({...t, ...next})\n } catch (error) {\n // if we're already reverting a transaction, skip any applied actions if\n // they throw while we rebuild the state\n if (error instanceof ActionError) continue\n throw error\n }\n }\n\n return {\n ...base,\n applied: nextApplied,\n outgoing: undefined,\n // discarding the outgoing transaction drops its echo-recognition memory,\n // which can leave a document that was only being kept alive for that echo\n // with nothing left to wait for, so sweep afterwards\n documentStates: evictOrphanedDocumentStates(\n Object.fromEntries(\n Object.entries(base.documentStates)\n .filter((e): e is [string, DocumentState] => !!e[1])\n .map(([documentId, documentState]) => {\n const {unverifiedRevisions = {}} = documentState\n const next: DocumentState = {\n ...documentState,\n unverifiedRevisions:\n prev.outgoing && prev.outgoing.transactionId in unverifiedRevisions\n ? omitProperty(unverifiedRevisions, prev.outgoing.transactionId)\n : unverifiedRevisions,\n }\n\n // a document with no subscribers that is still waiting on the echo\n // of a different, already acked transaction of its own is not\n // represented in `working` (only `applied` transactions are\n // re-applied above), so rebuilding its `local` from `remote` would\n // drop that transaction's effect. leave it for the echo to resolve\n if (!next.subscriptions.length && isAwaitingOwnEcho(next)) {\n return [documentId, next] as const\n }\n\n return [\n documentId,\n {...next, local: documentId in working ? working[documentId] : next.local},\n ] as const\n }),\n ),\n ),\n }\n}\n\n/**\n * Extracts the patch operations addressed to the given document from a set of\n * raw listener mutations, stripping the mutation-level `id` addressing so the\n * patches are rooted at the document. A transaction can carry mutations for\n * other documents, so anything not explicitly id-addressed to this document\n * (including query-addressed patches) is dropped.\n */\nfunction extractPatchOperations(\n mutations: Mutation[] | undefined,\n documentId: string,\n): PatchOperations[] {\n if (!mutations) return []\n return mutations.flatMap((mutation) => {\n if (!('patch' in mutation) || !mutation.patch) return []\n const {id, ...operations} = mutation.patch as PatchOperations & {id?: string}\n if (id !== documentId) return []\n return [operations]\n })\n}\n\nexport function applyRemoteDocument(\n prev: SyncTransactionState,\n {document, documentId, previousRev, revision, timestamp, type, mutations}: RemoteDocument,\n events: DocumentStoreState['events'],\n): SyncTransactionState {\n if (!prev.grants) return prev\n const prevDocState = prev.documentStates[documentId]\n\n // document state is deleted when there are no more subscribers so we can\n // simply skip if there is no state\n if (!prevDocState) return prev\n\n // we send out transactions with IDs generated client-side to identify them\n // when they are observed through the listener. here we can check if this\n // incoming remote document is the result of one of our transactions\n const prevUnverifiedRevisions = prevDocState.unverifiedRevisions\n const revisionToVerify = revision ? prevUnverifiedRevisions?.[revision] : undefined\n let unverifiedRevisions = prevUnverifiedRevisions ?? EMPTY_REVISIONS\n if (revision && revisionToVerify) {\n unverifiedRevisions = omitProperty(prevUnverifiedRevisions, revision)\n }\n\n // surface the operational patches from this transaction before they are\n // collapsed into the whole-document snapshot below. consumers that keep\n // their own state (e.g. collaborative text editors) rely on these to apply\n // remote changes without re-diffing document snapshots\n if (type === 'mutation' && revision) {\n const patches = extractPatchOperations(mutations, documentId)\n if (patches.length) {\n // `unverifiedRevisions` alone isn't a reliable origin marker: a sync\n // event can prune an in-flight transaction's entry before its listener\n // echo arrives. `recentOwnTransactionIds` survives that race\n const isOwnTransaction =\n Boolean(revisionToVerify) ||\n (prevDocState.recentOwnTransactionIds?.includes(revision) ?? false)\n events.next({\n type: 'remote-patches',\n documentId,\n transactionId: revision,\n previousRev,\n timestamp,\n patches,\n origin: isOwnTransaction ? 'local' : 'remote',\n })\n }\n }\n\n // if this remote document is from a `'sync'` event (meaning that the whole\n // thing was just fetched and not re-created from mutations)\n if (type === 'sync') {\n // then remove unverified revisions that are older than our sync time. we\n // don't need to verify them for a rebase any more because we synced and\n // grabbed the latest document\n unverifiedRevisions = Object.fromEntries(\n Object.entries(unverifiedRevisions).filter(([, unverifiedRevision]) => {\n if (!unverifiedRevision) return false\n return new Date(timestamp).getTime() <= new Date(unverifiedRevision.timestamp).getTime()\n }),\n )\n }\n\n // if there is a revision to verify and the previous revision from remote\n // matches the previous revision we expected, we can \"fast-forward\" and skip\n // rebasing local changes on top of this new base\n if (revisionToVerify && revisionToVerify.previousRev === previousRev) {\n // even on a clean fast-forward, the server's result can differ from our\n // optimistic prediction: a publish derives the published content from the\n // server's copy of the draft, which may carry concurrent edits our\n // prediction never saw. once nothing else is pending locally for this\n // document (no applied transaction and no in-flight transaction, other\n // than the one this event just confirmed, modifies it), converge `local`\n // to the server's document. while local work that modifies this document\n // is pending, keep the optimistic `local` so those changes stay visible;\n // that work's own echo (or rebase) recomputes `local` later.\n //\n // \"modifies\" matters: pending work on other documents must not block\n // convergence here (nothing would ever revisit this document's `local`),\n // and a pending draft edit carries the published document in its working\n // set without changing it, so a presence check would block published-doc\n // convergence whenever a draft edit is pending. this is the same\n // modified-test `transitionAppliedTransactionsToOutgoing` uses\n const modifiesDocument = (transaction: AppliedTransaction) =>\n transaction.working[documentId]?._rev !== transaction.previousRevs[documentId]\n const nothingElsePending =\n !prev.applied.some(modifiesDocument) &&\n (!prev.outgoing ||\n prev.outgoing.transactionId === revision ||\n !modifiesDocument(prev.outgoing))\n const local =\n nothingElsePending && !isDeepEqual(prevDocState.local, document)\n ? document\n : prevDocState.local\n\n return {\n ...prev,\n documentStates: evictOrphanedDocumentStates({\n ...prev.documentStates,\n [documentId]: {\n ...prevDocState,\n remote: document,\n remoteRev: revision,\n local,\n unverifiedRevisions,\n },\n }),\n }\n }\n\n // if we got this far, this means that we could not fast-forward this revision\n // for this document. now we can rebase our local changes (if any) on top of\n // this new base from remote. in order to do that we grab the set of documents\n // captured before the earliest local transaction\n const previous = prev.applied.at(0)?.previous\n // our initial working set now is the state of the documents before any of our\n // local transactions plus the newly updated document from remote\n let working = {...previous, [documentId]: document}\n const nextApplied: AppliedTransaction[] = []\n\n // now we can iterate through our applied (but not yet committed) transactions\n // starting with the updated working set and re-apply each transaction in\n // order creating a new set of applied transactions as we go.\n //\n // NOTE: we don't want to rebase over the outgoing transaction because that\n // transaction is already on its way to the server. if an outgoing transaction\n // needs to be rebased, then it eventually will be when we see that\n // transaction again through the listener and this same flow will run then\n for (const curr of prev.applied) {\n try {\n const next = processActions({...curr, working, grants: prev.grants, identity: prev.identity})\n working = next.working\n // next includes an updated `previous` set and `working` set and updates\n // the `outgoingAction` and `outgoingMutations`. the `base` set from the\n // original applied transaction gets put back into the updated transaction\n // as-is to preserve the intended base for a 3-way merge\n nextApplied.push({...curr, ...next})\n } catch (error) {\n // if processing the action ever throws a related error, we can skip this\n // local transaction and report the error to the user\n if (error instanceof ActionError) {\n events.next({\n type: 'rebase-error',\n transactionId: error.transactionId,\n documentId: error.documentId,\n message: error.message,\n error,\n })\n continue\n }\n throw error\n }\n }\n\n // when the recompute lands on a value deep-equal to the current local\n // (the common case for echoes of our own transactions), keep the previous\n // reference so subscribers don't re-render for an identical value\n const nextLocal = working[documentId]\n const local = isDeepEqual(prevDocState.local, nextLocal) ? prevDocState.local : nextLocal\n\n return {\n ...prev,\n applied: nextApplied,\n documentStates: evictOrphanedDocumentStates({\n ...prev.documentStates,\n [documentId]: {\n ...prevDocState,\n remote: document,\n remoteRev: revision,\n local,\n unverifiedRevisions,\n },\n }),\n }\n}\n\nexport function addSubscriptionIdToDocument(\n prev: SyncTransactionState,\n documentId: string,\n subscriptionId: string,\n): SyncTransactionState {\n const prevDocState = prev.documentStates?.[documentId]\n const prevSubscriptions = prevDocState?.subscriptions ?? []\n\n return {\n ...prev,\n documentStates: {\n ...prev.documentStates,\n [documentId]: {\n ...prevDocState,\n id: documentId,\n subscriptions: [...prevSubscriptions, subscriptionId],\n },\n },\n }\n}\n\nexport function removeSubscriptionIdFromDocument(\n prev: SyncTransactionState,\n documentId: string,\n subscriptionId: string,\n): SyncTransactionState {\n const prevDocState = prev.documentStates?.[documentId]\n if (!prevDocState) return prev\n\n const subscriptions = prevDocState.subscriptions.filter((id) => id !== subscriptionId)\n\n // a document with no subscribers left is normally evicted below, but if a\n // transaction was just submitted for it, its echo-recognition memory\n // (`unverifiedRevisions`) is still waiting on the listener echo.\n // `cleanupOutgoingTransaction` removes the outgoing transaction's own\n // subscription right at HTTP ack, before the echo arrives, so deleting the\n // state here would lose that memory and make the echo mislabel as\n // `origin: 'remote'` (or, worse, get skipped outright). keep the state\n // alive, unsubscribed, until the echo verifies or the wait expires\n if (!subscriptions.length && !isAwaitingOwnEcho(prevDocState)) {\n return {...prev, documentStates: omitProperty(prev.documentStates, documentId)}\n }\n\n return {\n ...prev,\n documentStates: {\n ...prev.documentStates,\n [documentId]: {...prevDocState, subscriptions},\n },\n }\n}\n\n/**\n * Whether this document still expects the listener echo of one of its own\n * submitted transactions. That expectation is the only reason a document state\n * with no subscribers is kept around.\n *\n * Revisions stop counting after `UNVERIFIED_REVISION_RETENTION_TIME` because an\n * echo is not guaranteed to arrive at all: Content Lake records no transaction\n * when every operation resolves to a no-op (e.g. a keyed path that no longer\n * exists), while the client bumps `_rev` regardless and so tracks a revision\n * for it. Without the expiry, such a document (and the listener keyed off its\n * state) would be pinned for the lifetime of the store.\n *\n * `recentOwnTransactionIds` deliberately does not extend the wait: it is capped\n * rather than drained, so honoring it would keep every edited document alive.\n */\nfunction isAwaitingOwnEcho(documentState: DocumentState): boolean {\n const expiresAfter = Date.now() - UNVERIFIED_REVISION_RETENTION_TIME\n return Object.values(documentState.unverifiedRevisions ?? {}).some(\n (revision) => !!revision && new Date(revision.timestamp).getTime() > expiresAfter,\n )\n}\n\n/**\n * Removes every document state that has no subscribers left and no own\n * transaction still awaiting its echo.\n *\n * This is a sweep rather than a decision made where the last subscriber leaves\n * because `removeSubscriptionIdFromDocument` defers that eviction while an echo\n * is in flight, and both halves of the condition can change without a\n * subscriber going anywhere: the echo can verify, be dropped by a sync, be\n * discarded by a revert, or simply never arrive. Every reducer that can settle\n * an in-flight transaction sweeps, so the deferred decision is always re-made.\n *\n * Note this can evict the very document whose remote event is being processed,\n * which synchronously unsubscribes that document's listener from inside its own\n * emission (see `subscribeToSubscriptionsAndListenToDocuments`).\n */\nfunction evictOrphanedDocumentStates(\n documentStates: SyncTransactionState['documentStates'],\n): SyncTransactionState['documentStates'] {\n let next = documentStates\n for (const [documentId, documentState] of Object.entries(documentStates)) {\n if (!documentState) continue\n if (documentState.subscriptions.length) continue\n if (isAwaitingOwnEcho(documentState)) continue\n next = omitProperty(next, documentId)\n }\n return next\n}\n\nexport function manageSubscriberIds(\n {state}: StoreContext<SyncTransactionState>,\n handles: DocumentHandleLike[],\n): () => void {\n const documentIds = getDocumentIdsFromHandleLikes(handles)\n\n const subscriptionId = randomId(16)\n state.set('addSubscribers', (prev) =>\n documentIds.reduce(\n (acc, id) => addSubscriptionIdToDocument(acc, id, subscriptionId),\n prev as SyncTransactionState,\n ),\n )\n\n return () => {\n setCleanupTimeout(() => {\n state.set('removeSubscribers', (prev) =>\n documentIds.reduce(\n (acc, id) => removeSubscriptionIdFromDocument(acc, id, subscriptionId),\n prev as SyncTransactionState,\n ),\n )\n }, DOCUMENT_STATE_CLEAR_DELAY)\n }\n}\n\n// document handles are passed in via the public facing API, but we also need to\n// pull the correct document ids from action bodies, which have similar but not\n// identical shapes to the document handles. release actions also flow through\n// here, and resolve to the underlying release document id.\nexport function getDocumentIdsFromHandleLikes(handles: (DocumentHandleLike | Action)[]): string[] {\n return handles.flatMap((handle) => {\n if ('type' in handle && isReleaseAction(handle)) {\n return [getReleaseDocumentId(handle.releaseId)]\n }\n const idsForDocument = []\n if (!handle.documentId) return []\n if (handle.liveEdit) {\n return [handle.documentId]\n }\n if (isReleasePerspective(handle.perspective)) {\n idsForDocument.push(\n getVersionId(DocumentId(handle.documentId), handle.perspective.releaseName),\n )\n }\n idsForDocument.push(getDraftId(DocumentId(handle.documentId)))\n idsForDocument.push(getPublishedId(DocumentId(handle.documentId)))\n return idsForDocument\n })\n}\n","import {type MultipleMutationResult, type SanityClient} from '@sanity/client'\nimport {type PatchOperations} from '@sanity/types'\n\nimport {type DocumentAction, type ReleaseAction} from './actions'\nimport {getReleaseDocumentId, isReleaseAction} from './processActions/releaseUtil'\nimport {type OutgoingTransaction} from './reducers'\n\n/** @beta Response body from submitting an outgoing transaction (actions or mutations API). */\nexport type DocumentTransactionSubmissionResult =\n | Awaited<ReturnType<SanityClient['action']>>\n | MultipleMutationResult\n\n/** @beta */\nexport type DocumentEvent =\n | ActionErrorEvent\n | TransactionRevertedEvent\n | TransactionSubmissionFailedEvent\n | TransactionAcceptedEvent\n | DocumentRebaseErrorEvent\n | DocumentEditedEvent\n | DocumentCreatedEvent\n | DocumentDeletedEvent\n | DocumentPublishedEvent\n | DocumentUnpublishedEvent\n | DocumentDiscardedEvent\n | DocumentRemotePatchesEvent\n\n/**\n * @beta\n * Event emitted when a precondition to applying an action fails.\n * (For example: when trying to edit a document that no longer exists.)\n */\nexport interface ActionErrorEvent {\n type: 'error'\n documentId: string\n transactionId: string\n message: string\n error: unknown\n}\n/**\n * @beta\n * Event emitted when a transaction is accepted.\n */\nexport interface TransactionAcceptedEvent {\n type: 'accepted'\n outgoing: OutgoingTransaction\n result: DocumentTransactionSubmissionResult\n}\n/**\n * @beta\n * Event emitted when a transaction is reverted.\n */\nexport interface TransactionRevertedEvent {\n type: 'reverted'\n message: string\n error: unknown\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when submitting a transaction fails with an error the store\n * retries: a network failure, a 5xx, 408 or 429 response, a 401 (for example\n * an expired session), or a 409 `transactionConflictError` (contention while\n * committing). The transaction and its local changes are kept\n * and submitted again, so the document stays out of sync until an attempt\n * succeeds. Emitted once per failed attempt; `attempt` starts at 1.\n */\nexport interface TransactionSubmissionFailedEvent {\n type: 'submission-failed'\n message: string\n error: unknown\n outgoing: OutgoingTransaction\n attempt: number\n}\n/**\n * @beta\n * Event emitted when an attempt to apply local changes to a modified remote document fails.\n */\nexport interface DocumentRebaseErrorEvent {\n type: 'rebase-error'\n documentId: string\n transactionId: string\n message: string\n error: unknown\n}\n/**\n * @beta\n * Event emitted when a document is edited.\n */\nexport interface DocumentEditedEvent {\n type: 'edited'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is created.\n */\nexport interface DocumentCreatedEvent {\n type: 'created'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is deleted.\n */\nexport interface DocumentDeletedEvent {\n type: 'deleted'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is published.\n */\nexport interface DocumentPublishedEvent {\n type: 'published'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document is unpublished.\n */\nexport interface DocumentUnpublishedEvent {\n type: 'unpublished'\n documentId: string\n outgoing: OutgoingTransaction\n}\n/**\n * @beta\n * Event emitted when a document version is discarded.\n */\nexport interface DocumentDiscardedEvent {\n type: 'discarded'\n documentId: string\n outgoing: OutgoingTransaction\n}\n\n/**\n * @beta\n * Event emitted when patches for a document are observed through the listener.\n *\n * Unlike the whole-document snapshots the store keeps, these are the raw patch\n * operations from the transaction that produced the change, preserving the\n * operational intent of the edit (e.g. keyed array inserts/unsets). Consumers\n * that maintain their own local state, like collaborative text editors, can\n * apply these directly instead of diffing document snapshots.\n *\n * `origin` is `'local'` when the transaction was submitted by this document\n * store instance (an own edit observed back through the listener) and\n * `'remote'` when it came from another client.\n */\nexport interface DocumentRemotePatchesEvent {\n type: 'remote-patches'\n /** The ID of the document version the patches apply to (e.g. a draft ID). */\n documentId: string\n /** The transaction ID that carried these patches. */\n transactionId: string\n /** The revision the document was at before this transaction. */\n previousRev?: string\n /** The timestamp of the transaction. */\n timestamp: string\n /** The patch operations, rooted at the document. */\n patches: PatchOperations[]\n /** Whether the transaction originated from this client or another one. */\n origin: 'local' | 'remote'\n}\n\n// Release actions that write a mutation to the local release doc map onto\n// the regular per-document events with `documentId = '_.releases.<releaseId>'`.\n// The other release actions (publish/schedule/unschedule/archive/unarchive)\n// don't mutate local state, so they aren't in the map and get skipped — they\n// surface through the transaction-level `accepted`/`reverted` events instead.\nconst actionMap = {\n 'document.create': 'created',\n 'document.delete': 'deleted',\n 'document.discard': 'discarded',\n 'document.edit': 'edited',\n 'document.publish': 'published',\n 'document.unpublish': 'unpublished',\n 'release.create': 'created',\n 'release.edit': 'edited',\n 'release.delete': 'deleted',\n} satisfies Partial<Record<DocumentAction['type'] | ReleaseAction['type'], DocumentEvent['type']>>\n\ntype MappedActionType = keyof typeof actionMap\n\nexport function getDocumentEvents(outgoing: OutgoingTransaction): DocumentEvent[] {\n const documentIdsByAction = outgoing.actions.reduce(\n (acc, action) => {\n if (!(action.type in actionMap)) return acc\n const documentId = isReleaseAction(action)\n ? getReleaseDocumentId(action.releaseId)\n : action.documentId\n if (!documentId) return acc\n const type = action.type as MappedActionType\n const ids = acc[type] ?? new Set<string>()\n ids.add(documentId)\n acc[type] = ids\n return acc\n },\n {} as Partial<Record<MappedActionType, Set<string>>>,\n )\n\n return Object.entries(documentIdsByAction).flatMap(([actionType, documentIds]) =>\n Array.from(documentIds ?? []).map((documentId): DocumentEvent => ({\n type: actionMap[actionType as MappedActionType],\n documentId,\n outgoing,\n })),\n )\n}\n","import {type ListenEvent, type MutationEvent} from '@sanity/client'\nimport {type SanityDocument} from '@sanity/types'\nimport {defer, filter, type MonoTypeOperatorFunction, Observable} from 'rxjs'\n\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\n\nconst DEDUPE_TTL = 120_000\nconst DEDUPE_MAX_ENTRIES = 1_000\n\n/**\n * How long to hold an incomplete multi-document transaction (and the events\n * held behind it) before releasing everything anyway. Only reached when part\n * of a transaction is lost.\n */\nconst GROUP_FLUSH_DEADLINE = 30_000\n\n/**\n * How many intervening events to hold behind an incomplete multi-document\n * transaction before giving up on grouping and releasing everything in\n * arrival order.\n */\nconst GROUP_MAX_HELD_EVENTS = 100\n\ninterface DedupeOptions {\n ttl?: number\n maxEntries?: number\n}\n\n/**\n * Drops mutation events that have already been observed, keyed by\n * `transactionId#documentId`. With `enableResume`, the listener replays\n * missed events after a reconnect on an at-least-once basis, so the same\n * event can be delivered twice. A duplicate would never chain onto the\n * current base revision and would clog the sequencing buffer until it\n * overflows into an `OutOfSyncError`.\n *\n * Seen keys expire after `ttl` milliseconds and the cache is capped at\n * `maxEntries`, evicting the oldest entries first.\n *\n * @internal\n */\nexport function dedupeListenerEvents(\n options?: DedupeOptions,\n): MonoTypeOperatorFunction<ListenEvent<SanityDocument>> {\n const {ttl = DEDUPE_TTL, maxEntries = DEDUPE_MAX_ENTRIES} = options ?? {}\n\n return (source) =>\n defer(() => {\n // insertion-ordered map of dedupe key -> expiry timestamp\n const seen = new Map<string, number>()\n\n return source.pipe(\n filter((event) => {\n if (event.type !== 'mutation') return true\n\n const key = `${event.transactionId}#${event.documentId}`\n const now = Date.now()\n\n const expiry = seen.get(key)\n if (expiry !== undefined && expiry > now) return false\n\n seen.delete(key)\n seen.set(key, now + ttl)\n\n for (const [existingKey, existingExpiry] of seen) {\n if (seen.size <= maxEntries && existingExpiry > now) break\n seen.delete(existingKey)\n }\n\n return true\n }),\n )\n })\n}\n\ninterface GroupTransactionEventsOptions {\n flushDeadline?: number\n maxHeldEvents?: number\n}\n\n/**\n * Buffers the mutation events of multi-document transactions (e.g. a publish\n * touching both the draft and the published document) until every event of\n * the transaction has arrived, then emits them contiguously. Without this,\n * a rebase can run between the two halves of a transaction and observe the\n * dataset in a state that never existed on the server (draft deleted, but\n * published not yet updated).\n *\n * The backend's streaming listener pipeline delivers a transaction's events\n * contiguously and in order, but the legacy pipeline can interleave\n * concurrent transactions. So while a multi-document transaction is\n * incomplete, mutation events from other transactions are held (in arrival\n * order) rather than emitted, and released right after the transaction\n * completes; released events are re-processed so a held multi-document\n * transaction is grouped too. Per-document `previousRev` sequencing\n * downstream tolerates the reordering this introduces.\n *\n * Escape hatches so a lost event degrades to ungrouped delivery instead of\n * stalling the stream: everything is released in arrival order when a\n * non-mutation event (e.g. reconnect/welcome) arrives, when more than\n * `maxHeldEvents` are held, or after `flushDeadline` milliseconds.\n * Single-event transactions pass through untouched when nothing is buffered.\n *\n * @internal\n */\nexport function groupTransactionEvents(\n options?: GroupTransactionEventsOptions,\n): MonoTypeOperatorFunction<ListenEvent<SanityDocument>> {\n const {flushDeadline = GROUP_FLUSH_DEADLINE, maxHeldEvents = GROUP_MAX_HELD_EVENTS} =\n options ?? {}\n\n return (source) =>\n new Observable<ListenEvent<SanityDocument>>((subscriber) => {\n interface OpenTransaction {\n transactionId: string\n events: MutationEvent[]\n total: number\n timer: ReturnType<typeof setTimeout>\n }\n\n let open: OpenTransaction | null = null\n let held: MutationEvent[] = []\n\n // emits the open transaction's events (complete or not) without\n // releasing the held queue; callers decide what happens to `held`\n const closeOpen = () => {\n if (!open) return\n clearTimeout(open.timer)\n const {events} = open\n open = null\n for (const event of events) subscriber.next(event)\n }\n\n // re-processes the held queue so a held multi-document transaction\n // gets grouped in turn. processing may open a new transaction and top\n // up a new held queue; each drain only touches its own snapshot, so\n // this terminates\n const releaseHeld = () => {\n const queue = held\n held = []\n for (const event of queue) processEvent(event)\n }\n\n const releaseEverything = () => {\n closeOpen()\n const queue = held\n held = []\n for (const event of queue) subscriber.next(event)\n }\n\n const processEvent = (event: ListenEvent<SanityDocument>) => {\n if (event.type !== 'mutation') {\n // connection-level events (welcome/welcomeback/reset/reconnect)\n // reset the world downstream; release everything before them\n releaseEverything()\n subscriber.next(event)\n return\n }\n\n const isMultiDocument = (event.transactionTotalEvents ?? 0) > 1\n\n if (!open) {\n if (!isMultiDocument) {\n subscriber.next(event)\n return\n }\n // isMultiDocument guarantees total >= 2, so a freshly opened\n // transaction is always incomplete; completion is checked when\n // subsequent events of the same transaction arrive below\n open = {\n transactionId: event.transactionId,\n events: [event],\n total: event.transactionTotalEvents,\n timer: setCleanupTimeout(() => {\n closeOpen()\n releaseHeld()\n }, flushDeadline),\n }\n return\n }\n\n if (event.transactionId === open.transactionId) {\n open.events.push(event)\n if (open.events.length >= open.total) {\n closeOpen()\n releaseHeld()\n }\n return\n }\n\n // an event from another transaction while one is incomplete: hold it\n // (legacy listener pipeline interleaving) instead of giving up on the\n // open transaction\n held.push(event)\n if (held.length > maxHeldEvents) releaseEverything()\n }\n\n const subscription = source.subscribe({\n next: processEvent,\n error: (error) => {\n releaseEverything()\n subscriber.error(error)\n },\n complete: () => {\n releaseEverything()\n subscriber.complete()\n },\n })\n\n return () => {\n if (open) clearTimeout(open.timer)\n open = null\n held = []\n subscription.unsubscribe()\n }\n })\n}\n","import {type ListenEvent, type SanityDocument} from '@sanity/client'\nimport {createDocumentLoaderFromClient} from '@sanity/mutate/_unstable_store'\nimport {\n first,\n map,\n merge,\n Observable,\n partition,\n ReplaySubject,\n share,\n Subject,\n switchMap,\n takeUntil,\n} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {UPSTREAM_CLOSE_DELAY_MS} from '../store/createStoreInstance'\nimport {cleanupTimer} from '../utils/setCleanupTimeout'\nimport {dedupeListenerEvents, groupTransactionEvents} from './listenerEventOperators'\n\nconst API_VERSION = 'v2025-05-06'\n\nexport interface SharedListener {\n events: Observable<ListenEvent<SanityDocument>>\n dispose: () => void\n}\n\nexport function createSharedListener(\n instance: SanityInstance,\n resource?: DocumentResource,\n): SharedListener {\n const dispose$ = new Subject<void>()\n const events$ = getClientState(instance, {\n apiVersion: API_VERSION,\n resource,\n }).observable.pipe(\n switchMap((client) =>\n // TODO: it seems like the client.listen method is not emitting disconnected\n // events. this is important to ensure we have an up to date version of the\n // doc. probably should introduce our own events for when the user goes offline\n client.listen(\n '*',\n {},\n {\n // with `enableResume`, a reconnect replays the events missed while\n // offline (emitting `welcomeback`) instead of forcing a refetch of\n // every subscribed document. when the backend cannot resume, it\n // emits `welcome` or `reset`, both of which trigger a refetch\n events: ['mutation', 'welcome', 'welcomeback', 'reconnect', 'reset'],\n enableResume: true,\n includeResult: false,\n // the document store handles version documents, so we need to include all versions\n includeAllVersions: true,\n tag: 'document-listener',\n // // from manual testing, it seems like mendoza patches may be\n // // causing some ambiguity/wonkiness\n // includeMutations: false,\n // effectFormat: 'mendoza',\n },\n ),\n ),\n // resumed events are delivered at-least-once, so drop replayed duplicates\n dedupeListenerEvents(),\n // hold multi-document transactions (e.g. publish) until complete so\n // per-document processing never observes half a transaction\n groupTransactionEvents(),\n takeUntil(dispose$),\n share(),\n )\n\n const [welcome$, mutation$] = partition(events$, (e) => e.type === 'welcome')\n\n return {\n events: merge(\n // we replay the welcome event because that event kicks off fetching the document.\n // Resets once no document listens, so the connection can close instead of staying open\n // for the page. The delay stops a quick resubscribe from reconnecting.\n welcome$.pipe(\n share({\n connector: () => new ReplaySubject(1),\n resetOnComplete: false,\n resetOnRefCountZero: () => cleanupTimer(UPSTREAM_CLOSE_DELAY_MS),\n }),\n ),\n mutation$,\n ),\n dispose: () => dispose$.next(),\n }\n}\n\nexport function createFetchDocument(instance: SanityInstance, resource?: DocumentResource) {\n return function (documentId: string): Observable<SanityDocument | null> {\n return getClientState(instance, {\n apiVersion: API_VERSION,\n resource,\n }).observable.pipe(\n switchMap((client) => {\n // creates a observable request to the /doc/{documentId} endpoint for a given document id\n // should work across all kinds of document IDs (drafts.**, version.**., etc.)\n const loadDocument = createDocumentLoaderFromClient(client)\n return loadDocument(documentId)\n }),\n map((result) => {\n if (!result.accessible) {\n if (result.reason === 'existence') return null\n throw new Error(`Document with ID \\`${documentId}\\` is inaccessible due to permissions.`)\n }\n return result.document as SanityDocument\n }),\n first(),\n )\n }\n}\n","import {ClientError, CorsOriginError} from '@sanity/client'\n\n/**\n * What the document store does with an outgoing transaction whose submission\n * failed.\n *\n * - `retry`: the failure is about the connection, the credentials or\n * contention on the server, not the transaction. Keep the transaction and\n * its optimistic local state, and submit it again.\n * - `accepted`: the server already recorded this transaction ID, so an earlier\n * attempt was committed even though its response never arrived.\n * - `revert`: the server rejected the transaction itself. Submitting it again\n * cannot succeed, so drop it and rebuild local state from the server copy.\n *\n * @internal\n */\nexport type SubmissionErrorClass = 'retry' | 'accepted' | 'revert'\n\n// Mirrors Studio's `TERMINAL_COMMIT_STATUSES` (checkoutPair.ts) without 401. A\n// 401 rejects the credentials (usually an expired session), not the\n// transaction, so the same transaction succeeds once the user re-authenticates.\nconst REJECTED_TRANSACTION_STATUS_CODES = new Set([400, 402, 403, 404, 409, 410, 412, 413, 422])\n\nfunction getErrorType(error: ClientError): unknown {\n const details: unknown = error.details\n if (details && typeof details === 'object' && 'type' in details) return details.type\n return undefined\n}\n\n/**\n * Classifies a failed transaction submission. Unknown failures (network\n * errors, timeouts, 5xx and 429 responses, and anything that is not a\n * `ClientError`) default to `retry`: wrongly retrying a rejected transaction\n * leaves the document visibly unsynced with the edits still on screen, while\n * wrongly reverting a transient failure silently discards the user's work.\n *\n * @internal\n */\nexport function classifySubmissionError(error: unknown): SubmissionErrorClass {\n if (error instanceof CorsOriginError) return 'revert'\n if (!(error instanceof ClientError)) return 'retry'\n if (error.statusCode === 409) {\n const errorType = getErrorType(error)\n if (errorType === 'transactionAlreadyExistsError') return 'accepted'\n // contention while committing, not a verdict on the transaction; the\n // server rolled it back, so the same transaction ID is safe to resend\n if (errorType === 'transactionConflictError') return 'retry'\n }\n if (REJECTED_TRANSACTION_STATUS_CODES.has(error.statusCode)) return 'revert'\n return 'retry'\n}\n","import {\n type Action,\n ClientError,\n CorsOriginError,\n type Mutation,\n type SanityClient,\n ServerError,\n} from '@sanity/client'\nimport {DocumentId, getDraftId} from '@sanity/id-utils'\nimport {jsonMatch} from '@sanity/json-match'\nimport {type ExprNode} from 'groq-js'\nimport {\n catchError,\n concatMap,\n defer,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n Observable,\n of,\n pairwise,\n race,\n retry,\n startWith,\n Subject,\n switchMap,\n tap,\n throttle,\n throwError,\n timer,\n withLatestFrom,\n} from 'rxjs'\n\nimport {getCurrentUserState} from '../auth/authStore'\nimport {type ClientOptions, getClientState} from '../client/clientStore'\nimport {\n type DocumentHandle,\n type DocumentResource,\n isCanvasResource,\n isDatasetResource,\n isMediaLibraryResource,\n} from '../config/sanityConfig'\nimport {\n bindActionByResource,\n type BoundResourceKey,\n type StoreAction,\n} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {createStateSourceAction, type StateSource} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {type ResolveDocument} from '../typegen/resolve'\nimport {type DocumentAction} from './actions'\nimport {\n ACL_RETRY_BASE_DELAY,\n ACL_RETRY_MAX_DELAY,\n API_VERSION,\n INITIAL_OUTGOING_THROTTLE_TIME,\n OUT_OF_SYNC_RETRY_BASE_DELAY,\n OUT_OF_SYNC_RETRY_COUNT,\n OUT_OF_SYNC_RETRY_MAX_DELAY,\n SUBMISSION_RETRY_BASE_DELAY,\n SUBMISSION_RETRY_MAX_DELAY,\n} from './documentConstants'\nimport {\n type DocumentEvent,\n type DocumentTransactionSubmissionResult,\n getDocumentEvents,\n} from './events'\nimport {listen, OutOfSyncError} from './listen'\nimport {type JsonMatch} from './patchOperations'\nimport {\n calculatePermissions,\n createGrantsLookup,\n type DatasetAcl,\n type DocumentPermissionsResult,\n type Grant,\n} from './permissions'\nimport {ActionError} from './processActions/processActions'\nimport {isReleaseAction} from './processActions/releaseUtil'\nimport {\n type AppliedTransaction,\n applyFirstQueuedTransaction,\n applyRemoteDocument,\n cleanupOutgoingTransaction,\n getDocumentIdsFromHandleLikes,\n manageSubscriberIds,\n type OutgoingTransaction,\n type QueuedTransaction,\n removeQueuedTransaction,\n revertOutgoingTransaction,\n transitionAppliedTransactionsToOutgoing,\n type UnverifiedDocumentRevision,\n} from './reducers'\nimport {createFetchDocument, createSharedListener, type SharedListener} from './sharedListener'\nimport {classifySubmissionError} from './submissionErrors'\n\nexport interface DocumentStoreState {\n documentStates: {[TDocumentId in string]?: DocumentState}\n queued: QueuedTransaction[]\n applied: AppliedTransaction[]\n outgoing?: OutgoingTransaction\n grants?: Record<Grant, ExprNode>\n /**\n * The current user's identity (their user ID).\n */\n identity?: string\n error?: unknown\n sharedListener: SharedListener\n fetchDocument: (documentId: string) => Observable<ResolveDocument | null>\n events: Subject<DocumentEvent>\n}\n\nexport interface DocumentState {\n id: string\n error?: unknown\n /**\n * the \"remote\" local copy that matches the server. represents the last known\n * server state. this gets updated every time we confirm remote patches\n */\n remote?: ResolveDocument | null\n /**\n * the current ephemeral working copy that includes local optimistic changes\n * that have not yet been confirmed by the server\n */\n local?: ResolveDocument | null\n /**\n * the revision that our remote document is at\n */\n remoteRev?: string | null\n /**\n * Array of subscription IDs. This document state will be deleted if there are\n * no subscribers.\n */\n subscriptions: string[]\n /**\n * An object keyed by transaction ID of revisions sent out but that have not\n * yet been verified yet. When an applied transaction is transitioned to an\n * outgoing transaction, it also adds unverified revisions for each document\n * that is part of that outgoing transaction. Transactions are submitted to\n * the server with a locally generated transaction ID. This way we can observe\n * when our transaction comes back through the shared listener. Each listener\n * event that comes back contains a `previousRev`. If we see our own\n * transaction with a different `previousRev` than expected, we can rebase our\n * local transactions on top of this new remote.\n */\n unverifiedRevisions?: {[TTransactionId in string]?: UnverifiedDocumentRevision}\n /**\n * transaction IDs recently submitted by this client for this document,\n * newest last. used to label `remote-patches` events with the correct\n * `origin` even after the corresponding `unverifiedRevisions` entry has\n * been pruned (e.g. by a sync event that raced the listener echo). capped\n * to the most recent entries.\n */\n recentOwnTransactionIds?: string[]\n}\n\nexport const documentStore = defineStore<DocumentStoreState, BoundResourceKey>({\n name: 'Document',\n getInitialState: (instance, {resource}) => ({\n documentStates: {},\n // these can be emptied on refetch\n queued: [],\n applied: [],\n sharedListener: createSharedListener(instance, resource),\n fetchDocument: createFetchDocument(instance, resource),\n events: new Subject(),\n }),\n initialize(context) {\n const {sharedListener} = context.state.get()\n const subscriptions = [\n subscribeToQueuedAndApplyNextTransaction(context),\n subscribeToSubscriptionsAndListenToDocuments(context),\n subscribeToAppliedAndSubmitNextTransaction(context),\n subscribeToClientAndFetchDatasetAcl(context),\n subscribeToCurrentUserAndSetIdentity(context),\n ]\n\n return () => {\n sharedListener.dispose()\n subscriptions.forEach((subscription) => subscription.unsubscribe())\n }\n },\n})\n\n/**\n * @beta\n * Options for specifying a document and optionally a path within it.\n */\nexport interface DocumentOptions<\n TPath extends string | undefined = undefined,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n path?: TPath\n}\n\n/** @beta */\nexport function getDocumentState<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: DocumentOptions<undefined, TDocumentType, TDataset, TProjectId>,\n): StateSource<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`> | undefined | null>\n\n/** @beta */\nexport function getDocumentState<\n TPath extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: DocumentOptions<TPath, TDocumentType, TDataset, TProjectId>,\n): StateSource<\n JsonMatch<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>, TPath> | undefined\n>\n\n/** @beta */\nexport function getDocumentState<TData>(\n instance: SanityInstance,\n options: DocumentOptions<string | undefined>,\n): StateSource<TData | undefined | null>\n\n/** @beta */\nexport function getDocumentState(\n ...args: Parameters<typeof _getDocumentState>\n): StateSource<unknown> {\n return _getDocumentState(...args)\n}\n\nfunction throwDocumentError(\n documentStates: DocumentStoreState['documentStates'],\n documentIds: string[],\n): void {\n for (const documentId of documentIds) {\n const documentError = documentStates[documentId]?.error\n if (documentError) throw documentError\n }\n}\n\n/** Version wins over draft, and draft over published. Undefined until every read has arrived. */\nfunction selectLocalDocument(\n documentStates: DocumentStoreState['documentStates'],\n documentIds: string[],\n): ResolveDocument | null | undefined {\n throwDocumentError(documentStates, documentIds)\n let selected: ResolveDocument | null = null\n for (const documentId of documentIds) {\n const local = documentStates[documentId]?.local\n if (local === undefined) return undefined\n if (selected === null) selected = local\n }\n return selected\n}\n\nfunction hasEveryDocumentArrived(\n documentStates: DocumentStoreState['documentStates'],\n documentIds: string[],\n): boolean {\n throwDocumentError(documentStates, documentIds)\n return documentIds.every((documentId) => documentStates[documentId] !== undefined)\n}\n\nconst _getDocumentState = bindActionByResource(\n documentStore,\n createStateSourceAction({\n selector: ({state: {error, documentStates}}, options: DocumentOptions<string | undefined>) => {\n const {path} = options\n if (error) throw error\n const document = selectLocalDocument(documentStates, getDocumentIdsFromHandleLikes([options]))\n if (document === undefined) return undefined\n\n if (!path) return document\n const result = jsonMatch(document, path).next()\n if (result.done) return undefined\n const {value} = result.value\n return value\n },\n onSubscribe: (context, options: DocumentOptions<string | undefined>) =>\n manageSubscriberIds(context, [options]),\n }),\n)\n\n/** @beta */\nexport function resolveDocument<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n docHandle: DocumentHandle<TDocumentType, TDataset, TProjectId>,\n): Promise<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`> | null>\n/** @beta */\nexport function resolveDocument<TData extends ResolveDocument>(\n instance: SanityInstance,\n docHandle: DocumentHandle<string, string, string>,\n): Promise<TData | null>\n/** @beta */\nexport function resolveDocument(\n ...args: Parameters<typeof _resolveDocument>\n): Promise<ResolveDocument | null> {\n return _resolveDocument(...args)\n}\nconst _resolveDocument = bindActionByResource(\n documentStore,\n ({instance}, docHandle: DocumentHandle<string, string, string>) => {\n return firstValueFrom(\n getDocumentState(instance, {\n ...docHandle,\n path: undefined,\n }).observable.pipe(filter((i) => i !== undefined)),\n ) as Promise<ResolveDocument | null>\n },\n)\n\n/** @beta */\nexport const getDocumentSyncStatus = bindActionByResource(\n documentStore,\n createStateSourceAction({\n selector: (\n {state: {error, documentStates: documents, outgoing, applied, queued}},\n doc: DocumentHandle,\n ) => {\n if (error) throw error\n if (!hasEveryDocumentArrived(documents, getDocumentIdsFromHandleLikes([doc])))\n return undefined\n return !queued.length && !applied.length && !outgoing\n },\n onSubscribe: (context, doc: DocumentHandle) => {\n return manageSubscriberIds(context, [doc])\n },\n }),\n)\n\ntype PermissionsStateOptions = {\n resource?: DocumentResource\n actions: DocumentAction[]\n}\n\n/** @beta */\nexport const getPermissionsState = bindActionByResource(\n documentStore,\n createStateSourceAction({\n selector: calculatePermissions,\n onSubscribe: (context, {actions}: PermissionsStateOptions) => {\n manageSubscriberIds(context, actions)\n },\n }) as StoreAction<\n DocumentStoreState,\n [PermissionsStateOptions],\n StateSource<DocumentPermissionsResult>\n >,\n)\n\n/** @beta */\nexport const resolvePermissions = bindActionByResource(\n documentStore,\n ({instance}, options: PermissionsStateOptions) => {\n return firstValueFrom(\n getPermissionsState(instance, options).observable.pipe(filter((i) => i !== undefined)),\n )\n },\n)\n\n/** @beta */\nexport const subscribeDocumentEvents = bindActionByResource(\n documentStore,\n ({state}, options: {resource?: DocumentResource; eventHandler: (e: DocumentEvent) => void}) => {\n const {events} = state.get()\n const subscription = events.subscribe(options.eventHandler)\n return () => subscription.unsubscribe()\n },\n)\n\nfunction failTransactionOnUnreadableDocument({queued, documentStates}: DocumentStoreState): void {\n const transaction = queued.at(0)\n if (!transaction) return\n const ids = getDocumentIdsFromHandleLikes(transaction.actions)\n const unreadableId = ids.find((id) => documentStates[id]?.error)\n if (unreadableId === undefined) return\n const error = documentStates[unreadableId]?.error\n const actionError = new ActionError({\n message: error instanceof Error ? error.message : String(error),\n documentId: unreadableId,\n transactionId: transaction.transactionId,\n })\n actionError.cause = error\n throw actionError\n}\n\nconst subscribeToQueuedAndApplyNextTransaction = ({\n state,\n}: StoreContext<DocumentStoreState, BoundResourceKey>) => {\n const {events} = state.get()\n return state.observable\n .pipe(\n tap(failTransactionOnUnreadableDocument),\n map(applyFirstQueuedTransaction),\n distinctUntilChanged(),\n tap((next) => state.set('applyFirstQueuedTransaction', next)),\n catchError((error, caught) => {\n if (error instanceof ActionError) {\n state.set('removeQueuedTransaction', (prev) =>\n removeQueuedTransaction(prev, error.transactionId),\n )\n events.next({\n type: 'error',\n message: error.message,\n documentId: error.documentId,\n transactionId: error.transactionId,\n error,\n })\n return caught\n }\n\n throw error\n }),\n )\n .subscribe({error: (error) => state.set('setError', {error})})\n}\n\nconst getErrorMessage = (error: unknown) =>\n error instanceof Error ? error.message : 'Request failed'\n\nconst submitOutgoingTransaction = (client: SanityClient, outgoing: OutgoingTransaction) => {\n // liveEdit transactions route to the mutations API; everything else routes\n // to the actions API. processActions rejects transactions that mix the two,\n // and reducers won't batch across that boundary, so a batch is always\n // entirely liveEdit or entirely not.\n if (outgoing.actions.some((action) => !isReleaseAction(action) && action.liveEdit)) {\n return client.observable.mutate(outgoing.outgoingMutations as Mutation[], {\n transactionId: outgoing.transactionId,\n visibility: 'async',\n returnDocuments: false,\n returnFirst: false,\n tag: 'document.mutate',\n skipCrossDatasetReferenceValidation: true,\n })\n }\n\n return client.observable.action(outgoing.outgoingActions as Action[], {\n transactionId: outgoing.transactionId,\n skipCrossDatasetReferenceValidation: true,\n tag: 'document.action',\n })\n}\n\nconst subscribeToAppliedAndSubmitNextTransaction = ({\n state,\n instance,\n key: {resource},\n}: StoreContext<DocumentStoreState, BoundResourceKey>) => {\n const {events} = state.get()\n const clients$ = getClientState(instance, {apiVersion: API_VERSION, resource}).observable\n\n return state.observable\n .pipe(\n throttle(\n (s) =>\n // if there is no outgoing transaction, we can throttle by the\n // initial outgoing throttle time…\n !s.outgoing\n ? timer(INITIAL_OUTGOING_THROTTLE_TIME)\n : // …otherwise, wait until the outgoing has been cleared\n state.observable.pipe(first(({outgoing}) => !outgoing)),\n {leading: false, trailing: true},\n ),\n map(transitionAppliedTransactionsToOutgoing),\n distinctUntilChanged((a, b) => a.outgoing?.transactionId === b.outgoing?.transactionId),\n tap((next) => state.set('transitionAppliedTransactionsToOutgoing', next)),\n map((s) => s.outgoing),\n distinctUntilChanged(),\n withLatestFrom(clients$),\n concatMap(([outgoing, initialClient]) => {\n if (!outgoing) return EMPTY\n\n // the client that made the latest attempt. the client store rebuilds\n // its clients when the token changes, so a different client means\n // different credentials and a retry after a 401 can go right away\n let attemptClient: SanityClient | undefined\n\n return defer(() => (attemptClient ? clients$.pipe(first()) : of(initialClient))).pipe(\n concatMap((client) => {\n attemptClient = client\n return submitOutgoingTransaction(client, outgoing)\n }),\n retry({\n delay: (error: unknown, retryCount) => {\n if (classifySubmissionError(error) !== 'retry') return throwError(() => error)\n\n // keep the transaction and its optimistic local state. the\n // document stays out of sync (see `getDocumentSyncStatus`) and\n // later edits queue behind this transaction until it lands\n events.next({\n type: 'submission-failed',\n message: getErrorMessage(error),\n error,\n outgoing,\n attempt: retryCount,\n })\n const backoff = Math.min(\n SUBMISSION_RETRY_BASE_DELAY * 2 ** (retryCount - 1),\n SUBMISSION_RETRY_MAX_DELAY,\n )\n return race(\n timer(backoff),\n clients$.pipe(first((client) => client !== attemptClient)),\n )\n },\n }),\n catchError((error: unknown) => {\n if (classifySubmissionError(error) === 'accepted') {\n // an earlier attempt was committed and only its response was\n // lost, so this is an ack\n return of({transactionId: outgoing.transactionId, documentIds: [], results: []})\n }\n state.set('revertOutgoingTransaction', revertOutgoingTransaction)\n events.next({type: 'reverted', message: getErrorMessage(error), outgoing, error})\n return EMPTY\n }),\n map((result: unknown) => ({\n result: result as DocumentTransactionSubmissionResult,\n outgoing,\n })),\n )\n }),\n tap(({outgoing, result}) => {\n state.set('cleanupOutgoingTransaction', cleanupOutgoingTransaction)\n for (const e of getDocumentEvents(outgoing)) events.next(e)\n events.next({type: 'accepted', outgoing, result})\n }),\n )\n .subscribe({error: (error) => state.set('setError', {error})})\n}\n\nconst subscribeToSubscriptionsAndListenToDocuments = (\n context: StoreContext<DocumentStoreState, BoundResourceKey>,\n) => {\n const {state} = context\n const {events} = state.get()\n\n return state.observable\n .pipe(\n filter((s) => !!s.grants),\n map((s) => Object.keys(s.documentStates)),\n distinctUntilChanged((curr, next) => {\n if (curr.length !== next.length) return false\n const currSet = new Set(curr)\n return next.every((i) => currSet.has(i))\n }),\n startWith(new Set<string>()),\n pairwise(),\n switchMap((pair) => {\n const [curr, next] = pair.map((ids) => new Set(ids))\n const added = Array.from(next).filter((i) => !curr.has(i))\n const removed = Array.from(curr).filter((i) => !next.has(i))\n\n // NOTE: the order of which these go out is somewhat important\n // because that determines the order `applyRemoteDocument` is called\n // which in turn determines which document version get populated\n // first. because we prefer drafts, it's better to have those go out\n // first so that the published document doesn't flash for a frame\n const changes = [\n ...added.map((id) => ({id, add: true})),\n ...removed.map((id) => ({id, add: false})),\n ].sort((a, b) => {\n const aIsDraft = a.id === getDraftId(DocumentId(a.id))\n const bIsDraft = b.id === getDraftId(DocumentId(b.id))\n\n if (aIsDraft && bIsDraft) return a.id.localeCompare(b.id, 'en-US')\n if (aIsDraft) return -1\n if (bIsDraft) return 1\n return a.id.localeCompare(b.id, 'en-US')\n })\n\n return of<{id: string; add: boolean}[]>(...changes)\n }),\n groupBy((i) => i.id),\n mergeMap((group) =>\n group.pipe(\n switchMap((e) => {\n if (!e.add) return EMPTY\n return listen(context, e.id).pipe(\n retry({\n count: OUT_OF_SYNC_RETRY_COUNT,\n resetOnSuccess: true,\n delay: (error, retryCount) => {\n const isTransient =\n error instanceof OutOfSyncError ||\n error instanceof ServerError ||\n (error instanceof ClientError &&\n (error.statusCode === 408 || error.statusCode === 429))\n if (!isTransient) return throwError(() => error)\n const backoff = Math.min(\n OUT_OF_SYNC_RETRY_BASE_DELAY * 2 ** (retryCount - 1),\n OUT_OF_SYNC_RETRY_MAX_DELAY,\n )\n return timer(backoff)\n },\n }),\n catchError((error) => {\n state.set('setDocumentError', (prev) => {\n const documentState = prev.documentStates[e.id]\n if (!documentState) return prev\n return {\n ...prev,\n documentStates: {...prev.documentStates, [e.id]: {...documentState, error}},\n }\n })\n return EMPTY\n }),\n tap((remote) =>\n state.set('applyRemoteDocument', (prev) =>\n applyRemoteDocument(prev, remote, events),\n ),\n ),\n )\n }),\n ),\n ),\n )\n .subscribe({error: (error) => state.set('setError', {error})})\n}\n\nconst subscribeToClientAndFetchDatasetAcl = ({\n instance,\n state,\n key: {resource},\n}: StoreContext<DocumentStoreState, BoundResourceKey>) => {\n const clientOptions: ClientOptions = {apiVersion: API_VERSION, resource}\n\n let url: string\n if (resource && isDatasetResource(resource)) {\n url = `/projects/${resource.projectId}/datasets/${resource.dataset}/acl`\n } else if (resource && isMediaLibraryResource(resource)) {\n url = `/media-libraries/${resource.mediaLibraryId}/acl`\n } else if (resource && isCanvasResource(resource)) {\n url = `/canvases/${resource.canvasId}/acl`\n } else {\n throw new Error(`Received invalid resource: ${JSON.stringify(resource)}`)\n }\n\n return getClientState(instance, clientOptions)\n .observable.pipe(\n switchMap((client) =>\n client.observable\n .request<DatasetAcl>({\n url,\n tag: 'acl.get',\n })\n .pipe(\n retry({\n delay: (error, retryCount) => {\n // 4xx responses and CORS misconfigurations are not transient —\n // the server will keep rejecting the request, so rethrow to\n // surface them as a fatal store error. 408 (request timeout)\n // and 429 (rate limit) are the exceptions: they resolve on\n // their own, so they are retried like network errors\n const isTransientClientError =\n error instanceof ClientError &&\n (error.statusCode === 408 || error.statusCode === 429)\n if (\n (error instanceof ClientError && !isTransientClientError) ||\n error instanceof CorsOriginError\n ) {\n return throwError(() => error)\n }\n // network errors (no status code), 408/429 responses, and 5xx\n // responses are retried with exponential backoff so a transient\n // failure during startup doesn't permanently brick the store\n const backoff = Math.min(\n ACL_RETRY_BASE_DELAY * 2 ** (retryCount - 1),\n ACL_RETRY_MAX_DELAY,\n )\n return timer(backoff)\n },\n }),\n ),\n ),\n tap((datasetAcl) => state.set('setGrants', {grants: createGrantsLookup(datasetAcl)})),\n )\n .subscribe({\n error: (error) => state.set('setError', {error}),\n })\n}\n\nconst subscribeToCurrentUserAndSetIdentity = ({\n instance,\n state,\n}: StoreContext<DocumentStoreState, BoundResourceKey>) =>\n getCurrentUserState(instance).observable.subscribe({\n next: (currentUser) => state.set('setIdentity', {identity: currentUser?.id}),\n // A transient identity-fetch failure (network blip, expired token, or a\n // normal logout/re-login transition) should not brick all document\n // operations. Reset the identity to `undefined` and keep going — GROQ's\n // `identity()` then evaluates to null, matching the unauthenticated state.\n error: () => state.set('setIdentity', {identity: undefined}),\n })\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\n\nimport {\n type DocumentResource,\n isCanvasResource,\n isMediaLibraryResource,\n} from '../config/sanityConfig'\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type Action, type DocumentAction} from './actions'\nimport {getEffectiveDocumentId} from './util'\n\nexport interface EffectiveDocModel {\n /**\n * If this is `undefined`, the resource has no opinion and the handle's `liveEdit`\n * flag should be respected.\n */\n liveEdit: boolean | undefined\n /**\n * When `false`, the resource does not support release perspectives. Callers\n * should drop any release perspective and fall back to the standard path.\n */\n supportsReleases: boolean\n}\n\nconst MEDIA_LIBRARY_DRAFTED_TYPES = new Set(['sanity.asset'])\n\n/**\n * Different resources have different \"default\" editing models.\n *\n * Canvas uses a liveEdit model.\n * Medial Library is mostly liveEdit except for `sanity.asset`, which retains the\n * draft/published model. Neither resource supports release perspectives.\n */\nexport function getEffectiveDocumentModel(\n resource: DocumentResource | undefined,\n documentType: string | undefined,\n): EffectiveDocModel {\n if (!resource) {\n return {liveEdit: undefined, supportsReleases: true}\n }\n if (isCanvasResource(resource)) {\n return {liveEdit: true, supportsReleases: false}\n }\n if (isMediaLibraryResource(resource)) {\n const isDrafted = documentType ? MEDIA_LIBRARY_DRAFTED_TYPES.has(documentType) : false\n return {liveEdit: !isDrafted, supportsReleases: false}\n }\n return {liveEdit: undefined, supportsReleases: true}\n}\n\nfunction describeResource(resource: DocumentResource | undefined): string {\n if (resource && isCanvasResource(resource)) return 'Canvas'\n if (resource && isMediaLibraryResource(resource)) return 'Media Library'\n return 'this resource'\n}\n\n/**\n * Rewrites edit actions to match the editing model of the bound resource.\n *\n * Canvas and Media Library resources do not support release perspectives, and\n * Canvas (plus most Media Library types) does not have a draft/published\n * model. When an edit action arrives with a release perspective for a resource\n * that doesn't support it, the perspective is stripped and a single warning\n * is logged. When the resource forces liveEdit, the action's `liveEdit` flag\n * is set so the dispatcher takes the liveEdit branch.\n *\n * Only `document.edit` actions are normalized today — other action types\n * (publish, unpublish, release actions, etc.) pass through unchanged.\n *\n * @internal\n */\nexport function normalizeActionsForResource(\n actions: Action[],\n resource: DocumentResource | undefined,\n): Action[] {\n // collect actions that may have changed in unexpected ways\n const stripped: Array<{documentType: string; documentId: string}> = []\n\n const normalized = actions.map((action) => {\n if (action.type !== 'document.edit') return action\n\n const {liveEdit: forcedLiveEdit, supportsReleases} = getEffectiveDocumentModel(\n resource,\n action.documentType,\n )\n const shouldRemovePerspective = isReleasePerspective(action.perspective) && !supportsReleases\n const shouldForceLiveEdit = forcedLiveEdit === true && !action.liveEdit\n\n if (!shouldRemovePerspective && !shouldForceLiveEdit) return action\n\n const corrected: DocumentAction = {...action}\n if (shouldForceLiveEdit) corrected.liveEdit = true\n if (shouldRemovePerspective) corrected.perspective = undefined\n\n // ensure we're using the right document ID for the corrected model\n corrected.documentId = getEffectiveDocumentId({\n ...corrected,\n documentId: getPublishedId(DocumentId(corrected.documentId)),\n })\n\n if (shouldRemovePerspective) {\n stripped.push({documentType: action.documentType, documentId: corrected.documentId})\n }\n\n return corrected\n })\n\n if (stripped.length > 0) {\n const docs = stripped.map((e) => `${e.documentType} (${e.documentId})`).join(', ')\n // eslint-disable-next-line no-console\n console.warn(\n `[sanity-sdk] ${describeResource(resource)} does not support release perspectives — falling back to the standard editing path for: ${docs}`,\n )\n }\n\n return normalized\n}\n","import {distinctUntilChanged, filter, first, firstValueFrom, map, race} from 'rxjs'\n\nimport {type DocumentResource} from '../config/sanityConfig'\nimport {bindActionByResource} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type StoreContext} from '../store/defineStore'\nimport {type ResolveDocument} from '../typegen/resolve'\nimport {randomUuid} from '../utils/ids'\nimport {type Action} from './actions'\nimport {documentStore, type DocumentStoreState} from './documentStore'\nimport {type DocumentTransactionSubmissionResult} from './events'\nimport {type DocumentSet} from './processMutations'\nimport {type AppliedTransaction, type QueuedTransaction, queueTransaction} from './reducers'\nimport {normalizeActionsForResource} from './resourceRules'\n\n/** @beta */\nexport interface ActionsResult<TDocument extends ResolveDocument = ResolveDocument> {\n transactionId: string\n documents: DocumentSet<TDocument>\n previous: DocumentSet<TDocument>\n previousRevs: {[documentId: string]: string | undefined}\n appeared: string[]\n updated: string[]\n disappeared: string[]\n submitted: () => Promise<DocumentTransactionSubmissionResult>\n}\n\n/** @beta */\nexport interface ApplyDocumentActionsOptions {\n /**\n * List of actions to apply. Accepts both document actions and release\n * lifecycle actions because they share the same transaction pipeline.\n */\n actions: Action[]\n\n /**\n * The resource to which the documents being acted on belong.\n */\n resource?: DocumentResource\n\n /**\n * Optionally provide an ID to be used as this transaction ID\n */\n transactionId?: string\n /**\n * Set this to true to prevent this action from being batched with others.\n */\n disableBatching?: boolean\n}\n\n/** @beta */\nexport function applyDocumentActions<\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: ApplyDocumentActionsOptions,\n): Promise<ActionsResult<ResolveDocument<TDocumentType, `${TProjectId}.${TDataset}`>>>\n/** @beta */\nexport function applyDocumentActions(\n instance: SanityInstance,\n options: ApplyDocumentActionsOptions,\n): Promise<ActionsResult>\n\n/** @beta */\nexport function applyDocumentActions(\n ...args: Parameters<typeof boundApplyDocumentActions>\n): ReturnType<typeof boundApplyDocumentActions> {\n return boundApplyDocumentActions(...args)\n}\n\nconst boundApplyDocumentActions = bindActionByResource(documentStore, _applyDocumentActions)\n\n/**\n * Returns the result of a transaction with no actions. `batchAppliedTransactions`\n * skips empty transactions, so one that reached the queue would never be consumed\n * into an outgoing transaction: it would sit in `applied` and its `submitted()`\n * would never resolve.\n */\nfunction emptyActionsResult(transactionId: string): ActionsResult {\n return {\n transactionId,\n documents: {},\n previous: {},\n previousRevs: {},\n appeared: [],\n updated: [],\n disappeared: [],\n submitted: async () => ({transactionId}),\n }\n}\n\n/** @internal */\nasync function _applyDocumentActions(\n {state}: StoreContext<DocumentStoreState>,\n {actions, resource, transactionId = randomUuid(), disableBatching}: ApplyDocumentActionsOptions,\n): Promise<ActionsResult> {\n if (!actions.length) return emptyActionsResult(transactionId)\n\n const {events} = state.get()\n\n // Rewrite edit actions to match the bound resource's editing model (e.g.\n // forcing liveEdit for Canvas, stripping unsupported release perspectives).\n // Non-edit and release actions pass through unchanged.\n const normalizedActions = normalizeActionsForResource(actions, resource)\n\n const transaction: QueuedTransaction = {\n transactionId,\n actions: normalizedActions,\n ...(disableBatching && {disableBatching}),\n }\n\n const fatalError$ = state.observable.pipe(\n map((s) => s.error),\n first(Boolean),\n map((error) => ({type: 'error', error}) as const),\n )\n\n const transactionError$ = events.pipe(\n filter((e) => e.type === 'error'),\n first((e) => e.transactionId === transactionId),\n )\n\n const appliedTransaction$ = state.observable.pipe(\n map((s) => s.applied),\n distinctUntilChanged(),\n map((applied) => applied.find((t) => t.transactionId === transactionId)),\n first(Boolean),\n )\n\n const successfulTransaction$ = events.pipe(\n filter((e) => e.type === 'accepted'),\n first((e) => e.outgoing.batchedTransactionIds.includes(transactionId)),\n )\n\n const rejectedTransaction$ = events.pipe(\n filter((e) => e.type === 'reverted'),\n first((e) => e.outgoing.batchedTransactionIds.includes(transactionId)),\n )\n\n const appliedTransactionOrError = firstValueFrom(\n race([fatalError$, transactionError$, appliedTransaction$]),\n )\n const acceptedOrRejectedTransaction = firstValueFrom(\n race([successfulTransaction$, rejectedTransaction$, transactionError$]),\n )\n\n state.set('queueTransaction', (prev) => queueTransaction(prev, transaction))\n\n const result = await appliedTransactionOrError\n if ('type' in result && result.type === 'error') throw result.error\n\n const {working: documents, previous, previousRevs} = result as AppliedTransaction\n const existingIds = new Set(\n Object.entries(previous)\n .filter(([, value]) => !!value)\n .map(([key]) => key),\n )\n const resultingIds = new Set(\n Object.entries(documents)\n .filter(([, value]) => !!value)\n .map(([key]) => key),\n )\n const allIds = new Set([...existingIds, ...resultingIds])\n\n const updated: string[] = []\n const appeared: string[] = []\n const disappeared: string[] = []\n\n for (const id of allIds) {\n if (existingIds.has(id) && resultingIds.has(id)) {\n updated.push(id)\n } else if (!existingIds.has(id) && resultingIds.has(id)) {\n appeared.push(id)\n } else if (!resultingIds.has(id) && existingIds.has(id)) {\n disappeared.push(id)\n }\n }\n\n async function submitted() {\n const raceResult = await acceptedOrRejectedTransaction\n if (raceResult.type !== 'accepted') throw raceResult.error\n return raceResult.result\n }\n\n return {\n transactionId,\n documents,\n previous,\n previousRevs,\n appeared,\n updated,\n disappeared,\n submitted,\n }\n}\n","import {switchMap} from 'rxjs'\n\nimport {getClientState} from '../client/clientStore'\nimport {\n type OrganizationBase,\n type OrganizationMember,\n type OrganizationOptions,\n} from '../organization/organization'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {defineFetcher} from '../store/fetcherStore'\nimport {buildQuery} from '../utils/buildQuery'\n\nconst API_VERSION = 'v2025-02-19'\n\n/**\n * The list shape returned from `/organizations`, with `members` and/or\n * `features` conditionally included based on the query options used.\n * @public\n */\nexport type Organizations<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> = (Pick<\n OrganizationBase,\n | 'id'\n | 'name'\n | 'slug'\n | 'createdAt'\n | 'updatedAt'\n | 'defaultRoleName'\n | 'dashboardStatus'\n | 'aiFeaturesStatus'\n> &\n // `boolean extends T` is non-distributive — true only when T is the wide\n // `boolean`, in which case the field is optional. Literal `true`/`false`\n // fall through to the strict branch.\n (boolean extends IncludeMembers\n ? {members?: OrganizationMember[]}\n : IncludeMembers extends true\n ? {members: OrganizationMember[]}\n : unknown) &\n (boolean extends IncludeFeatures\n ? {features?: string[]}\n : IncludeFeatures extends true\n ? {features: string[]}\n : unknown))[]\n\n/** @public */\nexport interface OrganizationsOptions<\n IncludeMembers extends boolean = false,\n IncludeFeatures extends boolean = false,\n> extends Omit<OrganizationOptions<IncludeMembers, IncludeFeatures>, 'organizationId'> {\n /**\n * When `true`, includes organisations the user has access to via\n * project-level grants, not just direct organisation memberships.\n */\n includeImplicitMemberships?: boolean\n}\n\nfunction normalizeOrganizationsOptions(options?: OrganizationsOptions<boolean, boolean>) {\n return {\n includeImplicitMemberships: options?.includeImplicitMemberships ?? false,\n includeMembers: options?.includeMembers ?? false,\n includeFeatures: options?.includeFeatures ?? false,\n }\n}\n\n/** @internal */\nexport function getOrganizationsCacheKey(\n _instance: SanityInstance,\n options?: OrganizationsOptions<boolean, boolean>,\n): string {\n const {includeMembers, includeFeatures, includeImplicitMemberships} =\n normalizeOrganizationsOptions(options)\n const membersKey = includeMembers ? ':members' : ''\n const featuresKey = includeFeatures ? ':features' : ''\n const implicitKey = includeImplicitMemberships ? ':implicit' : ''\n return `organizations${membersKey}${featuresKey}${implicitKey}`\n}\n\n/**\n * Fetcher for the current user's organizations (`GET /organizations`), on the\n * shared fetcher cache.\n *\n * @internal\n */\nexport const organizations = defineFetcher<\n [options?: OrganizationsOptions<boolean, boolean>],\n Organizations<boolean, boolean>\n>({\n name: 'organizations',\n getKey: getOrganizationsCacheKey,\n fetch: (instance) => (options) =>\n getClientState(instance, {apiVersion: API_VERSION, scope: 'global'}).observable.pipe(\n switchMap((client) =>\n client.observable.request<Organizations<boolean, boolean>>({\n url: '/organizations',\n query: buildQuery(normalizeOrganizationsOptions(options)),\n tag: 'organizations.list',\n }),\n ),\n ),\n tags: (data) => [\n {type: 'organization', id: 'LIST'},\n ...data.map((org) => ({type: 'organization', id: org.id})),\n ],\n})\n","import {type SanityUser as SanityUserFromClient} from '@sanity/client'\nimport {createSelector} from 'reselect'\nimport {\n catchError,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n firstValueFrom,\n groupBy,\n map,\n mergeMap,\n NEVER,\n Observable,\n of,\n pairwise,\n race,\n skip,\n startWith,\n switchMap,\n tap,\n throwError,\n withLatestFrom,\n} from 'rxjs'\n\nimport {getClient, getClientState} from '../client/clientStore'\nimport {getDashboardOrganizationId} from '../dashboard/messageBus/topicStore'\nimport {bindActionGlobally} from '../store/createActionBinder'\nimport {createStateSourceAction, type SelectorContext} from '../store/createStateSourceAction'\nimport {type StoreState} from '../store/createStoreState'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {randomId} from '../utils/ids'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {\n addSubscription,\n cancelRequest,\n getUsersKey,\n initializeRequest,\n parseUsersKey,\n removeSubscription,\n setUsersData,\n setUsersError,\n updateLastLoadMoreRequest,\n} from './reducers'\nimport {\n type GetUserOptions,\n type GetUsersOptions,\n type ResolveUserOptions,\n type ResolveUsersOptions,\n type SanityUser,\n type SanityUserResponse,\n type UsersStoreState,\n} from './types'\nimport {API_VERSION, PROJECT_API_VERSION, USERS_STATE_CLEAR_DELAY} from './usersConstants'\n\n/** @internal */\nexport type PatchedSanityUserFromClient = Omit<SanityUserFromClient, 'id'> & {\n id: string\n sanityUserId: string\n email: string\n provider: string\n}\n\n/**\n * The users store resource that manages user data fetching and state.\n *\n * This store handles fetching, caching, and managing user data. It provides functionality for\n * retrieving users associated with specific resources and supports pagination through the\n * `loadMoreUsers` action.\n *\n * @internal\n */\nconst usersStore = defineStore<UsersStoreState>({\n name: 'UsersStore',\n getInitialState: () => ({users: {}}),\n initialize: (context) => {\n const subscription = listenForLoadMoreAndFetch(context)\n return () => subscription.unsubscribe()\n },\n})\n\nconst errorHandler =\n (state: StoreState<{error?: unknown}>) =>\n (error: unknown): void =>\n state.set('setError', {error})\n\n/**\n * Internal action that listens for new user subscriptions and load more requests.\n * Fetches user data when new subscriptions are added or when loadMoreUsers is called.\n */\nconst listenForLoadMoreAndFetch = ({state, instance}: StoreContext<UsersStoreState>) => {\n return state.observable\n .pipe(\n map((s) => new Set(Object.keys(s.users))),\n distinctUntilChanged((curr, next) => {\n if (curr.size !== next.size) return false\n return Array.from(next).every((i) => curr.has(i))\n }),\n startWith(new Set<string>()),\n pairwise(),\n mergeMap(([curr, next]) => {\n const added = Array.from(next).filter((i) => !curr.has(i))\n const removed = Array.from(curr).filter((i) => !next.has(i))\n\n return [\n ...added.map((key) => ({key, added: true})),\n ...removed.map((key) => ({key, added: false})),\n ]\n }),\n groupBy((i) => i.key),\n mergeMap((group$) =>\n group$.pipe(\n switchMap((e) => {\n if (!e.added) return EMPTY\n const {userId, batchSize, ...options} = parseUsersKey(group$.key)\n\n if (userId) {\n // In the future we will be able to fetch a user from the global API using the resourceUserId,\n // but for now we need to use the project subdomain to fetch a user if the userId is a project user (starts with \"p\")\n if (userId.startsWith('p')) {\n const client = getClient(instance, {\n apiVersion: PROJECT_API_VERSION,\n // this is a global store, so we need to use the projectId from the options when we're fetching\n // users from a project subdomain\n projectId: options.projectId,\n useProjectHostname: true,\n })\n\n return client.observable\n .request<PatchedSanityUserFromClient>({\n method: 'GET',\n url: `/users/${userId}`,\n tag: 'users.get',\n })\n .pipe(\n map((user) => {\n // We need to convert the user to the format we expect\n const convertedUser: SanityUser = {\n sanityUserId: user.sanityUserId,\n profile: {\n id: user.id,\n displayName: user.displayName,\n familyName: user.familyName ?? undefined,\n givenName: user.givenName ?? undefined,\n middleName: user.middleName ?? undefined,\n imageUrl: user.imageUrl ?? undefined,\n createdAt: user.createdAt,\n updatedAt: user.updatedAt,\n isCurrentUser: user.isCurrentUser,\n email: user.email,\n provider: user.provider,\n },\n memberships: [],\n }\n return {\n data: [convertedUser],\n totalCount: 1,\n nextCursor: null,\n }\n }),\n catchError((error) => {\n state.set('setUsersError', setUsersError(group$.key, error))\n return EMPTY\n }),\n tap((response) =>\n state.set('setUsersData', setUsersData(group$.key, response)),\n ),\n )\n }\n\n // Fetch the user from the global API\n const scope = userId.startsWith('g') ? 'global' : undefined\n const client = getClient(instance, {\n scope,\n apiVersion: API_VERSION,\n })\n const resourceType = options.resourceType || 'project'\n const resourceId =\n resourceType === 'organization' ? options.organizationId : options.projectId\n if (!resourceId) {\n return throwError(() => new Error('An organizationId or a projectId is required'))\n }\n return client.observable\n .request<SanityUser | SanityUserResponse>({\n method: 'GET',\n url: `access/${resourceType}/${resourceId}/users/${userId}`,\n tag: 'users.get',\n })\n .pipe(\n map((response) => {\n // If the response is a single user object (has sanityUserId), wrap it in the expected format\n if ('sanityUserId' in response) {\n return {\n data: [response],\n totalCount: 1,\n nextCursor: null,\n } as SanityUserResponse\n }\n return response as SanityUserResponse\n }),\n catchError((error) => {\n state.set('setUsersError', setUsersError(group$.key, error))\n return EMPTY\n }),\n tap((response) => state.set('setUsersData', setUsersData(group$.key, response))),\n )\n }\n const projectId = options.projectId\n\n // the resource type this request will use\n // If resourceType is explicitly provided, use it\n // Otherwise, infer from context: organization if organizationId exists,\n // project if projectId exists, or default to organization\n const resourceType =\n options.resourceType ??\n (options.organizationId ? 'organization' : projectId ? 'project' : 'organization')\n\n const organizationId$ = options.organizationId\n ? of(options.organizationId)\n : getDashboardOrganizationId(instance).observable.pipe(\n filter((i) => typeof i === 'string'),\n )\n\n const resource$: Observable<{\n type: 'project' | 'organization'\n id: string\n }> =\n resourceType === 'project'\n ? projectId\n ? of({type: 'project', id: projectId})\n : throwError(() => new Error('Project ID required for this API.'))\n : organizationId$.pipe(map((id) => ({type: 'organization', id})))\n\n const client$ = getClientState(instance, {\n scope: 'global',\n apiVersion: API_VERSION,\n }).observable\n\n const loadMore$ = state.observable.pipe(\n map((s) => s.users[group$.key]?.lastLoadMoreRequest),\n distinctUntilChanged(),\n )\n\n const cursor$ = state.observable.pipe(\n map((s) => s.users[group$.key]?.nextCursor),\n distinctUntilChanged(),\n filter((cursor) => cursor !== null),\n )\n\n return combineLatest([resource$, client$, loadMore$]).pipe(\n withLatestFrom(cursor$),\n switchMap(([[resource, client], cursor]) =>\n client.observable.request<SanityUserResponse>({\n method: 'GET',\n url: `access/${resource.type}/${resource.id}/users`,\n tag: 'users.list',\n query: cursor\n ? {nextCursor: cursor, limit: batchSize.toString()}\n : {limit: batchSize.toString()},\n }),\n ),\n catchError((error) => {\n state.set('setUsersError', setUsersError(group$.key, error))\n return EMPTY\n }),\n tap((response) => state.set('setUsersData', setUsersData(group$.key, response))),\n )\n }),\n ),\n ),\n )\n .subscribe({error: errorHandler(state)})\n}\n\n/**\n * Returns the state source for users associated with a specific resource.\n *\n * This function returns a state source that represents the current list of users for a given\n * resource. Subscribing to the state source will instruct the SDK to fetch the users (if not\n * already fetched) and will load more from this state source as well. When the last subscriber is\n * removed, the users state is automatically cleaned up from the store after a delay.\n *\n * Note: This functionality is for advanced users who want to build their own framework\n * integrations. Our SDK also provides a React integration for convenient usage.\n *\n * @beta\n */\nexport const getUsersState = bindActionGlobally(\n usersStore,\n createStateSourceAction({\n selector: createSelector(\n [\n ({instance, state}: SelectorContext<UsersStoreState>, options?: GetUsersOptions) =>\n state.error ?? state.users[getUsersKey(instance, options)]?.error,\n ({instance, state}: SelectorContext<UsersStoreState>, options: GetUsersOptions) =>\n state.users[getUsersKey(instance, options)]?.users,\n ({instance, state}: SelectorContext<UsersStoreState>, options: GetUsersOptions) =>\n state.users[getUsersKey(instance, options)]?.totalCount,\n ({instance, state}: SelectorContext<UsersStoreState>, options: GetUsersOptions) =>\n state.users[getUsersKey(instance, options)]?.nextCursor,\n ],\n (error, data, totalCount, nextCursor) => {\n if (error) throw error\n if (data === undefined || totalCount === undefined || nextCursor === undefined) {\n return undefined\n }\n\n return {data, totalCount, hasMore: nextCursor !== null}\n },\n ),\n onSubscribe: ({instance, state}, options?: GetUsersOptions) => {\n const subscriptionId = randomId(16)\n const key = getUsersKey(instance, options)\n state.set('addSubscription', addSubscription(subscriptionId, key))\n return () => {\n setCleanupTimeout(\n () => state.set('removeSubscription', removeSubscription(subscriptionId, key)),\n USERS_STATE_CLEAR_DELAY,\n )\n }\n },\n }),\n)\n\n/**\n * Resolves the users for a specific resource without registering a lasting subscriber.\n *\n * This function fetches the users for a given resource and returns a promise that resolves with\n * the users result. Unlike `getUsersState`, which registers subscribers to keep the data live and\n * performs automatic cleanup, `resolveUsers` does not track subscribers. This makes it ideal for\n * use with React Suspense, where the returned promise is thrown to delay rendering until the users\n * result becomes available. Once the promise resolves, it is expected that a real subscriber will\n * be added via `getUsersState` to manage ongoing updates.\n *\n * Additionally, an optional AbortSignal can be provided to cancel the request and immediately\n * clear the associated state if there are no active subscribers.\n *\n * @beta\n */\nexport const resolveUsers = bindActionGlobally(\n usersStore,\n async ({state, instance}, {signal, ...options}: ResolveUsersOptions) => {\n const key = getUsersKey(instance, options)\n const {getCurrent} = getUsersState(instance, options)\n\n const aborted$ = signal\n ? new Observable<never>((observer) => {\n const cleanup = () => {\n signal.removeEventListener('abort', listener)\n }\n\n const listener = () => {\n observer.error(new DOMException('The operation was aborted.', 'AbortError'))\n observer.complete()\n cleanup()\n }\n signal.addEventListener('abort', listener)\n\n return cleanup\n }).pipe(\n catchError((error) => {\n if (error instanceof Error && error.name === 'AbortError') {\n state.set('cancelRequest', cancelRequest(key))\n }\n throw error\n }),\n )\n : NEVER\n\n state.set('initializeRequest', initializeRequest(key))\n\n const resolved$ = state.observable.pipe(\n map(getCurrent),\n first((i) => i !== undefined),\n )\n\n return firstValueFrom(race([resolved$, aborted$]))\n },\n)\n\n/**\n * Loads more users for a specific resource.\n *\n * This function triggers a request to fetch the next page of users for a given resource. It\n * requires that users have already been loaded for the resource (via `resolveUsers` or\n * `getUsersState`), and that there are more users available to load (as indicated by the `hasMore`\n * property).\n *\n * The function returns a promise that resolves when the next page of users has been loaded.\n *\n * @beta\n */\nexport const loadMoreUsers = bindActionGlobally(\n usersStore,\n async ({state, instance}, options?: GetUsersOptions) => {\n const key = getUsersKey(instance, options)\n const users = getUsersState(instance, options)\n const usersState = users.getCurrent()\n if (!usersState) {\n throw new Error('Users not loaded for specified resource. Please call resolveUsers first.')\n }\n\n if (!usersState.hasMore) {\n throw new Error('No more users available to load for this resource.')\n }\n\n const promise = firstValueFrom(\n users.observable.pipe(\n filter((i) => i !== undefined),\n skip(1),\n ),\n )\n\n const timestamp = new Date().toISOString()\n state.set('updateLastLoadMoreRequest', updateLastLoadMoreRequest(timestamp, key))\n\n return await promise\n },\n)\n\n/**\n * @beta\n */\nexport const getUserState = bindActionGlobally(\n usersStore,\n ({instance}, {userId, ...options}: GetUserOptions) => {\n return getUsersState(instance, {userId, ...options}).observable.pipe(\n map((res) => res?.data[0]),\n distinctUntilChanged((a, b) => a?.profile.updatedAt === b?.profile.updatedAt),\n )\n },\n)\n\n/**\n * @beta\n */\nexport const resolveUser = bindActionGlobally(\n usersStore,\n async ({instance}, {signal, ...options}: ResolveUserOptions) => {\n const result = await resolveUsers(instance, {\n signal,\n ...options,\n })\n return result?.data[0]\n },\n)\n","import {type BifurClient, fromUrl} from '@sanity/bifur-client'\nimport {type SanityClient} from '@sanity/client'\nimport {defer, EMPTY, fromEvent, merge, type Observable, timer} from 'rxjs'\nimport {catchError, distinctUntilChanged, map, retry, share, switchMap} from 'rxjs/operators'\n\nimport {\n type BifurTransportOptions,\n type PresenceLocation,\n type PresenceTransport,\n type TransportEvent,\n type TransportMessage,\n} from './types'\n\n/**\n * Longest gap between reconnection attempts, matching the Studio's presence\n * transport so both behave the same way on a flaky connection.\n */\nconst CONNECT_RETRY_MAX_DELAY = 1000 * 240\n\ntype BifurStateMessage = {\n type: 'state'\n i: string\n m: {\n sessionId: string\n locations: PresenceLocation[]\n }\n}\n\ntype BifurDisconnectMessage = {\n type: 'disconnect'\n i: string\n m: {session: string}\n}\n\ntype RollCallEvent = {\n type: 'rollCall'\n i: string\n session: string\n}\n\ntype IncomingBifurEvent = RollCallEvent | BifurStateMessage | BifurDisconnectMessage\n\nfunction getBifurClient(client: SanityClient, token$: Observable<string | null>): BifurClient {\n const bifurVersionedClient = client.withConfig({apiVersion: '2022-06-30'})\n const {\n resource,\n dataset,\n url: baseUrl,\n requestTagPrefix = 'sanity.sdk.presence',\n } = bifurVersionedClient.config()\n\n let resourcePath: string\n if (resource?.type === 'canvas') {\n resourcePath = `canvases/${resource.id}`\n } else if (dataset) {\n // Dataset clients use project hostname — dataset name alone is the socket path\n resourcePath = dataset\n } else {\n throw new Error(`Unable to determine presence URL: no canvas resource or dataset configured`)\n }\n\n const url = `${baseUrl}/socket/${resourcePath}`.replace(/^http/, 'ws')\n\n const urlWithTag = `${url}?tag=${requestTagPrefix}`\n\n return fromUrl(urlWithTag, {token$})\n}\n\nconst handleIncomingMessage = (event: IncomingBifurEvent): TransportEvent => {\n switch (event.type) {\n case 'rollCall':\n return {\n type: 'rollCall',\n userId: event.i,\n sessionId: event.session,\n }\n case 'state': {\n const {sessionId, locations} = event.m\n return {\n type: 'state',\n userId: event.i,\n sessionId,\n timestamp: new Date().toISOString(),\n locations,\n }\n }\n case 'disconnect':\n return {\n type: 'disconnect',\n userId: event.i,\n sessionId: event.m.session,\n timestamp: new Date().toISOString(),\n }\n default: {\n throw new Error(`Got unknown presence event: ${JSON.stringify(event)}`)\n }\n }\n}\n\n/**\n * Emits an incrementing generation number each time the socket becomes live.\n *\n * `@sanity/bifur-client` does not reconnect on its own — it errors the stream\n * when the socket closes. Without the `retry` below, presence stops for the\n * life of the page after the first dropped connection.\n */\nconst createConnections = (bifur: BifurClient): Observable<number> => {\n let generation = 0\n\n return defer(() => {\n const current = ++generation\n // `heartbeats` emits as soon as the socket is open and authorized, then on\n // every server heartbeat and every response. Subscribing to it is also what\n // keeps the socket alive between requests.\n return bifur.heartbeats.pipe(map(() => current))\n }).pipe(\n retry({\n delay: (_error, retryCount) =>\n timer(Math.min(CONNECT_RETRY_MAX_DELAY, 2 ** retryCount * 100)),\n resetOnSuccess: true,\n }),\n // After `retry` so that one operator instance spans reconnects, making each\n // live socket yield exactly one emission.\n distinctUntilChanged(),\n share(),\n )\n}\n\n/**\n * Presence events from everyone else in the room, re-established on every live\n * connection.\n *\n * A socket close errors this stream just as it errors the heartbeats, so without\n * re-subscribing the client would keep announcing itself after a reconnect while\n * never hearing from anyone again. Driving it from `connections$` rather than\n * giving it its own `retry` keeps a single reconnect loop, instead of two\n * independent backoffs racing over one refcounted socket.\n */\nconst createIncomingEvents = (\n bifur: BifurClient,\n connections$: Observable<number>,\n): Observable<TransportEvent> =>\n connections$.pipe(\n switchMap(() =>\n bifur.listen<IncomingBifurEvent>('presence').pipe(\n // Let a dead listener go quietly. The next connection replaces it, and\n // surfacing the error here would tear down the reconnect loop with it.\n catchError(() => EMPTY),\n ),\n ),\n map(handleIncomingMessage),\n share(),\n )\n\n/**\n * Emits when the page is going away. `pagehide` is needed alongside\n * `beforeunload` because iOS Safari and pages entering the back/forward cache\n * never fire `beforeunload`.\n */\nconst createUnload = (): Observable<void> => {\n if (typeof window === 'undefined') return EMPTY\n\n return merge(fromEvent(window, 'beforeunload'), fromEvent(window, 'pagehide')).pipe(\n map(() => undefined),\n )\n}\n\nexport const createBifurTransport = (options: BifurTransportOptions): PresenceTransport => {\n const {client, token$, sessionId} = options\n const bifur = getBifurClient(client, token$)\n\n const connections$ = createConnections(bifur)\n const incomingEvents$ = createIncomingEvents(bifur, connections$)\n\n const dispatchMessage = (message: TransportMessage): Observable<void> => {\n switch (message.type) {\n case 'rollCall':\n return bifur.request('presence_rollcall', {session: sessionId})\n case 'state':\n return bifur.request('presence_announce', {\n data: {locations: message.locations, sessionId},\n })\n case 'disconnect':\n return bifur.request('presence_disconnect', {session: sessionId})\n default: {\n return EMPTY\n }\n }\n }\n\n return [incomingEvents$, dispatchMessage, connections$, createUnload()]\n}\n","import {type IndexTuple, type KeyedSegment, type Path, type PathSegment} from '@sanity/types'\n\n/**\n * Segments are not always strings, despite what `PresenceLocation.path` declares.\n * Array items arrive as `{_key}` and Portable Text spans as a mix of keys and\n * property names, because that is what the Studio sends.\n */\nfunction isKeyedSegment(segment: PathSegment): segment is KeyedSegment {\n return (\n typeof segment === 'object' && segment !== null && !Array.isArray(segment) && '_key' in segment\n )\n}\n\n/**\n * Index tuples (`[from, to]`) only occur in slice selections, which presence never\n * reports. Compared structurally rather than assumed absent.\n */\nfunction isEqualTuple(a: IndexTuple, b: IndexTuple): boolean {\n return a.length === b.length && a.every((value, index) => value === b[index])\n}\n\n/** Compares two path segments, whatever shape they arrive in. */\nfunction isEqualSegment(a: PathSegment, b: PathSegment): boolean {\n if (a === b) return true\n if (isKeyedSegment(a) && isKeyedSegment(b)) return a._key === b._key\n if (Array.isArray(a) && Array.isArray(b)) return isEqualTuple(a, b)\n return false\n}\n\n/**\n * True when `candidate` is at or below `prefix` in the document tree.\n *\n * Equivalent to `startsWith` from the Studio's `@sanity/util/paths`, which is not\n * an SDK dependency.\n *\n * @internal\n */\nexport function startsWithPath(prefix: Path, candidate: Path): boolean {\n if (prefix.length > candidate.length) return false\n return prefix.every((segment, index) => isEqualSegment(segment, candidate[index]))\n}\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\nimport {type Path} from '@sanity/types'\nimport {createSelector} from 'reselect'\nimport {\n auditTime,\n catchError,\n combineLatest,\n distinctUntilChanged,\n EMPTY,\n filter,\n first,\n map,\n merge,\n type Observable,\n of,\n Subscription,\n switchMap,\n timer,\n withLatestFrom,\n} from 'rxjs'\n\nimport {getTokenState} from '../auth/authStore'\nimport {getClient} from '../client/clientStore'\nimport {\n type DocumentResource,\n isCanvasResource,\n isDatasetResource,\n isMediaLibraryResource,\n} from '../config/sanityConfig'\nimport {getEditingDocumentId} from '../document/util'\nimport {bindActionByResource, type BoundResourceKey} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {defineStore, type StoreContext} from '../store/defineStore'\nimport {type SanityUser} from '../users/types'\nimport {getUserState} from '../users/usersStore'\nimport {randomId} from '../utils/ids'\nimport {createLogger} from '../utils/logger'\nimport {createBifurTransport} from './bifurTransport'\nimport {startsWithPath} from './paths'\nimport {\n type DocumentPresence,\n type DocumentPresenceOptions,\n type PresenceLocation,\n type ReportPresenceOptions,\n type TransportEvent,\n type TransportMessage,\n type UserPresence,\n type WirePresenceLocation,\n} from './types'\n\nconst logger = createLogger('presence')\n\n/**\n * Used for the canvas organization lookup only. The socket itself is pinned to\n * `2022-06-30` in `bifurTransport`, because that is the version Bifur compares\n * against when deciding whether a connection may authenticate over RPC rather\n * than requiring a JWT at upgrade. Do not align these two.\n */\nconst PRESENCE_API_VERSION = '2026-03-30'\n\n/**\n * How often the current user re-announces while idle. Matches the Studio so the\n * two agree on how long a quiet peer should be trusted.\n */\nconst REPORT_MIN_INTERVAL = 30_000\n\n/** Collapses a burst of focus changes into a single announcement. */\nconst REPORT_AUDIT_TIME = 200\n\n/**\n * How long a session may go without announcing before it is dropped. Bifur\n * publishes nothing when a peer's socket dies, so a peer that crashes, is\n * force-quit, or loses power would otherwise stay visible forever.\n *\n * Peers re-announce every 30 seconds, so this allows three missed announcements.\n */\nconst SESSION_TTL = 90_000\n\n/** How often to look for sessions that have gone quiet. */\nconst SWEEP_INTERVAL = 15_000\n\ntype PresenceSession = {\n userId: string\n locations: PresenceLocation[]\n /**\n * When we last heard from this session, on our own clock. Used for expiry in\n * preference to the sender's `lastActiveAt`, which is subject to clock skew:\n * a peer whose clock runs slow would otherwise be expired immediately, and one\n * whose clock runs fast would never be expired at all.\n */\n lastSeenAt: number\n}\n\ntype PresenceStoreState = {\n locations: Map<string, PresenceSession>\n users: Record<string, SanityUser | undefined>\n organizationId?: string\n /**\n * Set when the canvas organization lookup fails. Without it, consumers waiting\n * on `organizationId` would wait forever.\n */\n organizationIdError?: unknown\n /**\n * Where this client says it is. `undefined` means the app has never reported,\n * which keeps it silent: reading presence must not make an app start\n * broadcasting. An empty array is different, and means \"here, but not in any\n * particular document\".\n */\n localLocations?: WirePresenceLocation[]\n}\n\nconst getInitialState = (): PresenceStoreState => ({\n locations: new Map<string, PresenceSession>(),\n users: {},\n})\n\n/**\n * Sends a message without letting a failure tear down the stream it was sent on.\n *\n * This matters most for the announce pipeline, which carries the idle heartbeat,\n * roll-call responses, and the re-announce on reconnect. Those share one chain, so\n * an unhandled error from a single rejected RPC would silence this client\n * permanently while it carried on reading presence normally. A dropped socket\n * errors the in-flight request, which is exactly the case where staying alive\n * matters, since the reconnect that follows is what makes us visible again.\n *\n * No retry is needed here. The 30 second heartbeat sends again on its own, and a\n * recovered connection triggers an immediate re-announce.\n */\nconst sendSafely = (\n dispatch: (message: TransportMessage) => Observable<void>,\n message: TransportMessage,\n): Observable<void> =>\n dispatch(message).pipe(\n catchError((error: unknown) => {\n logger.warn('Failed to send presence message', {messageType: message.type, error})\n return EMPTY\n }),\n )\n\n/**\n * Reduces a stream to a bare trigger that cannot fail.\n *\n * The announce pipeline combines three inputs with `merge`, which propagates an\n * error from any one of them. Since that one pipeline carries the idle heartbeat,\n * roll-call responses, and the re-announce on reconnect, a single failing input\n * would silence this client for good. Losing one trigger is survivable; losing\n * all three is not.\n */\nconst asTrigger = (source$: Observable<unknown>, name: string): Observable<void> =>\n source$.pipe(\n map(() => undefined),\n catchError((error: unknown) => {\n logger.error('Presence announce trigger failed', {trigger: name, error})\n return EMPTY\n }),\n )\n\n/** Ignores `lastActiveAt`, which changes on every report even when nothing moved. */\nconst locationKey = ({documentId, path, selection}: WirePresenceLocation) =>\n JSON.stringify([documentId, path, selection ?? null])\n\n/**\n * Compares reported locations by value. Key order within a caller-supplied\n * `selection` could in principle differ and read as a change, which costs one\n * extra announcement and nothing else.\n */\nfunction isEqualLocations(\n a: WirePresenceLocation[] | undefined,\n b: WirePresenceLocation[] | undefined,\n): boolean {\n if (a === b) return true\n if (!a || !b || a.length !== b.length) return false\n return a.every((location, index) => locationKey(location) === locationKey(b[index]))\n}\n\n/** @public */\nexport const presenceStore = defineStore<PresenceStoreState, BoundResourceKey>({\n name: 'presence',\n getInitialState,\n initialize: (context: StoreContext<PresenceStoreState, BoundResourceKey>) => {\n if (isMediaLibraryResource(context.key.resource)) {\n throw new Error('Presence is not supported for media library resources.')\n }\n const subscription = resolvePresenceUsers(context)\n return () => subscription.unsubscribe()\n },\n // Upstream, so a hidden app's session ends rather than staying visible to peers. The\n // disconnect on close stops peers keeping a ghost session for up to 90 seconds.\n upstream: (context: StoreContext<PresenceStoreState, BoundResourceKey>) => {\n const {\n instance,\n state,\n key: {resource},\n } = context\n\n // A fresh id per connection, since peers were told the previous one disconnected,\n // and deliberately not persisted. Reusing an id across\n // page loads would collide whenever a tab inherits another's session\n // storage (`window.open`, or a same-origin iframe), making two live clients\n // filter each other out as self. Stale sessions are handled by the\n // disconnect on unload, with the expiry sweep above as the backstop.\n const sessionId = randomId(16)\n\n // Dataset resources must use the project hostname so the socket URL is project-specific.\n // Canvas resources use the global API endpoint via the resource config.\n const client = isDatasetResource(resource)\n ? getClient(instance, {\n apiVersion: PRESENCE_API_VERSION,\n projectId: resource.projectId,\n dataset: resource.dataset,\n useProjectHostname: true,\n })\n : getClient(instance, {\n apiVersion: PRESENCE_API_VERSION,\n resource,\n })\n\n const token$ = getTokenState(instance).observable.pipe(distinctUntilChanged())\n\n const [incomingEvents$, dispatch, connections$, unload$] = createBifurTransport({\n client,\n token$,\n sessionId,\n })\n\n const subscription = new Subscription()\n\n // Subscribed before the roll call below, deliberately. The transport\n // establishes its listener when this stream is subscribed, and a roll call\n // sent before we are listening would have its answers arrive to no one.\n subscription.add(\n incomingEvents$.subscribe({\n next: (event: TransportEvent) => {\n if ('sessionId' in event && event.sessionId === sessionId) {\n return\n }\n\n if (event.type === 'state') {\n state.set('presence/state', (prevState: PresenceStoreState) => {\n const newLocations = new Map(prevState.locations)\n newLocations.set(event.sessionId, {\n userId: event.userId,\n locations: event.locations,\n lastSeenAt: Date.now(),\n })\n\n return {\n ...prevState,\n locations: newLocations,\n }\n })\n } else if (event.type === 'disconnect') {\n state.set('presence/disconnect', (prevState: PresenceStoreState) => {\n const newLocations = new Map(prevState.locations)\n newLocations.delete(event.sessionId)\n return {...prevState, locations: newLocations}\n })\n }\n },\n // The transport re-establishes its listener on every live connection, so\n // this should not fire. Handled rather than left to become an unhandled\n // error, which is how presence used to die silently and for good.\n error: (error: unknown) => {\n logger.error('Presence event stream failed', {error})\n },\n }),\n )\n\n // Subscribing keeps the socket alive and reconnects it with backoff. Each\n // emission is a freshly live connection: anything we accumulated is now\n // suspect, so start over and ask everyone to announce themselves again.\n subscription.add(\n connections$.subscribe(() => {\n state.set('presence/reset', (prevState) => ({\n ...prevState,\n locations: new Map<string, PresenceSession>(),\n }))\n sendSafely(dispatch, {type: 'rollCall'}).subscribe()\n }),\n )\n\n // Restarted per connection so a long reconnect backoff does not expire\n // everyone while we are offline — reconnecting clears the map anyway.\n //\n // Uses an rxjs timer rather than `setCleanupTimeout`, despite the guidance in\n // core-package-conventions, because this is a recurring sweep and not a\n // cleanup timer. Unref'ing it would not help anyway: a live presence\n // subscription holds an open WebSocket, and that is what keeps a Node process\n // alive. Anything running server-side should not subscribe to presence at all.\n subscription.add(\n connections$.pipe(switchMap(() => timer(SWEEP_INTERVAL, SWEEP_INTERVAL))).subscribe(() => {\n state.set('presence/expire', (prevState) => {\n const cutoff = Date.now() - SESSION_TTL\n const stale = [...prevState.locations].filter(([, s]) => s.lastSeenAt < cutoff)\n if (stale.length === 0) return prevState\n\n const newLocations = new Map(prevState.locations)\n for (const [staleSessionId] of stale) newLocations.delete(staleSessionId)\n return {...prevState, locations: newLocations}\n })\n }),\n )\n\n subscription.add(\n unload$.pipe(switchMap(() => sendSafely(dispatch, {type: 'disconnect'}))).subscribe(),\n )\n\n // Announce where we are. A location change, a peer asking for a roll call, or\n // a reconnect all restart `timer(0, ...)`, so we announce immediately and then\n // every 30s while idle. That idle tick is what tells peers we are still here,\n // and is what their expiry sweep measures against.\n const localLocations$ = state.observable.pipe(\n map((s) => s.localLocations),\n // Compared by value, not by reference. The Studio compares by reference and\n // so never dedupes, because its form rebuilds the array on every call.\n distinctUntilChanged(isEqualLocations),\n )\n\n const rollCallRequests$ = incomingEvents$.pipe(\n // Bifur echoes our own roll call back to us, since we are subscribed to the\n // same topic we published to. Answering it would be a pointless round trip.\n filter((event) => event.type === 'rollCall' && event.sessionId !== sessionId),\n )\n\n subscription.add(\n merge(\n asTrigger(localLocations$, 'locationChange'),\n asTrigger(rollCallRequests$, 'rollCall'),\n asTrigger(connections$, 'connection'),\n )\n .pipe(\n switchMap(() => timer(0, REPORT_MIN_INTERVAL)),\n withLatestFrom(localLocations$),\n map(([, locations]) => locations),\n // Stay silent until the app has reported at least once.\n filter((locations): locations is WirePresenceLocation[] => locations !== undefined),\n auditTime(REPORT_AUDIT_TIME),\n switchMap((locations) => sendSafely(dispatch, {type: 'state', locations})),\n )\n .subscribe(),\n )\n\n return new Subscription(() => {\n sendSafely(dispatch, {type: 'disconnect'}).subscribe()\n subscription.unsubscribe()\n // Nothing expires these while the socket is closed, and the next connection starts over\n state.set('presence/reset', (prevState) => ({\n ...prevState,\n locations: new Map<string, PresenceSession>(),\n }))\n })\n },\n})\n\nfunction resolvePresenceUsers({\n instance,\n state,\n key: {resource},\n}: StoreContext<PresenceStoreState, BoundResourceKey>): Subscription {\n const subscription = new Subscription()\n\n // Resolve display names for everyone we can see.\n //\n // This belongs to the store rather than to one state source: it is driven by\n // which user ids are in state, and every selector needs it. It used to hang off\n // `getPresence`'s `onSubscribe`, which meant anything reading presence another\n // way saw only \"Unknown user\".\n const userIds$ = state.observable.pipe(\n map((s) =>\n Array.from(s.locations.values())\n .map((l) => l.userId)\n .filter((id): id is string => !!id),\n ),\n distinctUntilChanged((a, b) => a.length === b.length && a.every((v, i) => v === b[i])),\n )\n\n // For canvas resources, wait for the organizationId to arrive. A failed lookup\n // resolves to `undefined` rather than never emitting, so one failed request\n // does not leave every user permanently unresolved. Dataset resources emit\n // immediately so the stream is never blocked.\n const organizationId$: Observable<string | undefined> = isCanvasResource(resource)\n ? state.observable.pipe(\n filter((s) => s.organizationId !== undefined || s.organizationIdError !== undefined),\n first(),\n map((s) => s.organizationId),\n )\n : of(undefined)\n\n subscription.add(\n combineLatest([userIds$, organizationId$])\n .pipe(\n switchMap(([userIds, organizationId]) => {\n if (userIds.length === 0) {\n return of([])\n }\n // Without an organization there is nothing to scope the lookup to, so\n // skip the request rather than making one that cannot succeed.\n if (!isDatasetResource(resource) && !organizationId) {\n return of([])\n }\n const userObservables = userIds.map((userId) =>\n getUserState(instance, {\n userId,\n ...(isDatasetResource(resource)\n ? {resourceType: 'project', projectId: resource.projectId}\n : {resourceType: 'organization', organizationId}),\n }).pipe(filter((v): v is NonNullable<typeof v> => !!v)),\n )\n return combineLatest(userObservables)\n }),\n )\n .subscribe((users) => {\n state.set('presence/users', (prevState) => ({\n ...prevState,\n users: {\n ...prevState.users,\n ...users.reduce<Record<string, SanityUser>>((acc, user) => {\n if (user) {\n acc[user.profile.id] = user\n }\n return acc\n }, {}),\n },\n }))\n }),\n )\n\n // Canvas resources need the organizationId to resolve users — fetch it once from the canvas endpoint\n if (isCanvasResource(resource)) {\n const globalClient = getClient(instance, {apiVersion: PRESENCE_API_VERSION})\n subscription.add(\n globalClient.observable\n .request<{organizationId: string}>({\n url: `/canvases/${resource.canvasId}`,\n tag: 'canvases.get',\n })\n .subscribe({\n next: ({organizationId}) => {\n state.set('presence/organizationId', (prev) => ({...prev, organizationId}))\n },\n error: (organizationIdError: unknown) => {\n state.set('presence/organizationIdError', (prev) => ({...prev, organizationIdError}))\n },\n }),\n )\n }\n\n return subscription\n}\n\nconst selectLocations = (state: PresenceStoreState) => state.locations\nconst selectUsers = (state: PresenceStoreState) => state.users\n\n/**\n * Stands in for a participant whose profile has not resolved yet, or cannot be\n * resolved. This must be a structurally valid `SanityUser`: consumers read\n * `user.profile`, and a partial object would throw there on first render.\n */\nconst createUnresolvedUser = (userId: string): SanityUser => ({\n sanityUserId: userId,\n profile: {\n id: userId,\n displayName: 'Unknown user',\n email: '',\n provider: '',\n createdAt: '',\n },\n memberships: [],\n})\n\nconst selectPresence = createSelector(\n selectLocations,\n selectUsers,\n (locations, users): UserPresence[] => {\n return Array.from(locations.entries()).map(([sessionId, {userId, locations: locs}]) => ({\n user: users[userId] || createUnresolvedUser(userId),\n sessionId,\n locations: locs,\n }))\n },\n)\n\n/**\n * Results per state object, then per query.\n *\n * `createStateSourceAction` runs its selector afresh on every `getCurrent()`, and\n * `getCurrent` is what React's `useSyncExternalStore` calls as its snapshot. A\n * selector that builds a new array each call therefore sends React into an\n * infinite render loop, which is why the store docs point at memoized selectors.\n *\n * A single `reselect` memo slot is not enough here, because the selector is\n * parameterised per document and callers watching different documents would\n * evict each other. Keyed on the state object, which is replaced on every change,\n * so entries are collected once that state is unreachable.\n */\nconst documentPresenceCache = new WeakMap<PresenceStoreState, Map<string, DocumentPresence[]>>()\n\n/** Flattens the session map down to one document. */\nfunction selectDocumentPresence(\n state: PresenceStoreState,\n options: DocumentPresenceOptions,\n): DocumentPresence[] {\n const cacheKey = JSON.stringify([\n getEditingDocumentId(options),\n options.path ?? null,\n options.excludeVersions ?? false,\n ])\n\n let perState = documentPresenceCache.get(state)\n if (!perState) {\n perState = new Map<string, DocumentPresence[]>()\n documentPresenceCache.set(state, perState)\n }\n\n const cached = perState.get(cacheKey)\n if (cached) return cached\n\n const computed = computeDocumentPresence(state, options)\n perState.set(cacheKey, computed)\n return computed\n}\n\n/**\n * The id a query and a location are compared on. Exact when `excludeVersions` is\n * set, otherwise normalized so a draft, its published version, and any release\n * version count as one document.\n */\nconst scopeId = (documentId: string, excludeVersions: boolean | undefined): string =>\n excludeVersions ? documentId : getPublishedId(DocumentId(documentId))\n\n/**\n * Declared `string[]`, actually a `Path`. See the note on\n * {@link PresenceLocation.path}; `DocumentPresence.path` reports it honestly.\n */\nconst pathOf = (location: PresenceLocation): Path => location.path as unknown as Path\n\nfunction matchesQuery(\n location: PresenceLocation,\n target: string,\n {path, excludeVersions}: DocumentPresenceOptions,\n): boolean {\n if (scopeId(location.documentId, excludeVersions) !== target) return false\n return !path || startsWithPath(path, pathOf(location))\n}\n\nfunction toDocumentPresence(\n users: PresenceStoreState['users'],\n sessionId: string,\n userId: string,\n location: PresenceLocation,\n): DocumentPresence {\n return {\n user: users[userId] || createUnresolvedUser(userId),\n sessionId,\n documentId: location.documentId,\n path: pathOf(location),\n lastActiveAt: location.lastActiveAt,\n ...(location.selection === undefined ? {} : {selection: location.selection}),\n }\n}\n\nfunction computeDocumentPresence(\n state: PresenceStoreState,\n options: DocumentPresenceOptions,\n): DocumentPresence[] {\n // Resolved before scoping, and identically to the write side, or a query for a\n // document would not match the client reporting it.\n const target = scopeId(getEditingDocumentId(options), options.excludeVersions)\n\n return Array.from(state.locations).flatMap(([sessionId, session]) =>\n session.locations\n .filter((location) => matchesQuery(location, target, options))\n .map((location) => toDocumentPresence(state.users, sessionId, session.userId, location)),\n )\n}\n\nconst _getPresence = bindActionByResource(\n presenceStore,\n createStateSourceAction({\n selector: (context: SelectorContext<PresenceStoreState>): UserPresence[] =>\n selectPresence(context.state),\n }),\n)\n\n/** @beta */\nexport function getPresence(\n instance: SanityInstance,\n params?: {resource?: DocumentResource},\n): StateSource<UserPresence[]> {\n // bit of a hack to support the old bound action by dataset\n // in reality, this will always be passed a resource\n return _getPresence(instance, params ?? {})\n}\n\nconst _getDocumentPresence = bindActionByResource(\n presenceStore,\n createStateSourceAction({\n selector: (\n {state}: SelectorContext<PresenceStoreState>,\n options: {resource?: DocumentResource} & DocumentPresenceOptions,\n ): DocumentPresence[] => selectDocumentPresence(state, options),\n }),\n)\n\n/**\n * Presence within a single document, flattened to one entry per participant per\n * location so it can be rendered directly against a field.\n *\n * Reading presence never announces anything. Use `reportPresence` to make the\n * current user visible to others.\n *\n * @param instance - the Sanity instance\n * @param params - the document to look at, its perspective, an optional `path` to\n * narrow to a field subtree, and `excludeVersions` to compare document ids exactly\n *\n * @beta\n */\nexport function getDocumentPresence(\n instance: SanityInstance,\n params: {resource?: DocumentResource} & DocumentPresenceOptions,\n): StateSource<DocumentPresence[]> {\n return _getDocumentPresence(instance, params)\n}\n\nconst _reportPresence = bindActionByResource(\n presenceStore,\n (\n {state}: StoreContext<PresenceStoreState, BoundResourceKey>,\n {locations}: {resource?: DocumentResource; locations: ReportPresenceOptions[]},\n ) => {\n const lastActiveAt = new Date().toISOString()\n const wireLocations: WirePresenceLocation[] = locations.map((location) => ({\n type: 'document',\n // The specific document being edited, not the canonical id. Other clients,\n // the Studio included, compare exact ids for field-level presence.\n documentId: getEditingDocumentId(location),\n path: location.path ?? [],\n lastActiveAt,\n ...(location.selection === undefined ? {} : {selection: location.selection}),\n }))\n\n state.set('presence/report', (prevState) => ({...prevState, localLocations: wireLocations}))\n },\n)\n\n/**\n * Announces where the current user is, so other clients in the same project and\n * dataset can show them. Reading presence never announces anything, so this is\n * the only way an app becomes visible to others.\n *\n * Call it again whenever the user moves. Announcements are collapsed over a short\n * window and then repeated every 30 seconds while idle, which is what tells peers\n * the session is still alive.\n *\n * Announcements only go out while the presence connection is open, which is\n * while something subscribes to `getPresence` or `getDocumentPresence`. Keep a\n * subscription for as long as the user should appear present.\n *\n * Which specific document each location resolves to depends on its perspective, and\n * that matters for interoperability: other clients, the Studio included, compare\n * exact document ids for field-level presence. Pass the perspective you are editing\n * under rather than building a draft or version id yourself.\n *\n * @param instance - the Sanity instance\n * @param params - the resource to announce on, plus the locations to report.\n * Pass one location with a `documentId` for document-level presence, add a\n * `path` for field-level presence, and pass an empty array to appear present\n * without being in any document.\n *\n * @beta\n */\nexport function reportPresence(\n instance: SanityInstance,\n params: {resource?: DocumentResource; locations: ReportPresenceOptions[]},\n): void {\n return _reportPresence(instance, params)\n}\n","export function hashString(str: string): string {\n // Using a large prime number for the hash\n const PRIME = 31\n // Using a max 32-bit integer to prevent overflow\n const MOD = 2147483647\n\n let hash = 0\n\n // Process chunks of the string to reduce complexity\n for (let i = 0; i < str.length; i++) {\n // Rolling hash computation\n hash = (hash * PRIME + str.charCodeAt(i)) % MOD\n }\n\n // Ensure we return a positive hash\n return Math.abs(hash).toString(16).padStart(8, '0')\n}\n","export const PROJECTION_TAG = 'projection'\nexport const PROJECTION_STATE_CLEAR_DELAY = 1000\n\nexport const STABLE_EMPTY_PROJECTION = {\n data: null,\n isPending: false,\n}\n\nexport function validateProjection(projection: string): string {\n if (!projection.startsWith('{') || !projection.endsWith('}')) {\n throw new Error(\n `Invalid projection format: \"${projection}\". Projections must be enclosed in curly braces, e.g. \"{title, 'author': author.name}\"`,\n )\n }\n return projection\n}\n","import {type ClientPerspective} from '@sanity/client'\nimport {DocumentId, getPublishedId} from '@sanity/id-utils'\n\nimport {type ReleasePerspective} from '../config/sanityConfig'\nimport {\n type DocumentProjections,\n type DocumentProjectionValues,\n type ProjectionStoreState,\n} from './types'\nimport {validateProjection} from './util'\n\nexport type ProjectionQueryResult = {\n _id: string\n _type: string\n _updatedAt: string\n result: Record<string, unknown>\n __projectionHash: string\n}\n\ninterface CreateProjectionQueryResult {\n query: string\n params: Record<string, unknown>\n}\n\ntype ProjectionMap = Record<string, {projection: string; documentIds: Set<string>}>\n\nexport function createProjectionQuery(\n documentIds: Set<DocumentId>,\n documentProjections: {[TDocumentId in DocumentId]?: DocumentProjections},\n): CreateProjectionQueryResult {\n const projections = Array.from(documentIds)\n .flatMap((id) => {\n const projectionsForDoc = documentProjections[id]\n if (!projectionsForDoc) return []\n\n return Object.entries(projectionsForDoc).map(([projectionHash, projection]) => ({\n documentId: id,\n projection: validateProjection(projection),\n projectionHash,\n }))\n })\n .reduce<ProjectionMap>((acc, {documentId, projection, projectionHash}) => {\n const obj = acc[projectionHash] ?? {documentIds: new Set(), projection}\n obj.documentIds.add(documentId)\n\n acc[projectionHash] = obj\n return acc\n }, {})\n\n const query = `[${Object.entries(projections)\n .map(([projectionHash, {projection}]) => {\n return `...*[_id in $__ids_${projectionHash}]{_id,_type,_updatedAt,\"__projectionHash\":\"${projectionHash}\",\"result\":{...${projection}}}`\n })\n .join(',')}]`\n\n const params = Object.fromEntries(\n Object.entries(projections).map(([projectionHash, value]) => {\n const idsInProjection = Array.from(value.documentIds).flatMap((id) => DocumentId(id))\n return [`__ids_${projectionHash}`, Array.from(idsInProjection)]\n }),\n )\n\n return {query, params}\n}\n\ninterface ProcessProjectionQueryOptions {\n ids: Set<string>\n results: ProjectionQueryResult[]\n documentStatuses?: ProjectionStoreState['documentStatuses']\n perspective: ClientPerspective | ReleasePerspective\n}\n\nexport function processProjectionQuery({\n ids,\n results,\n documentStatuses,\n}: ProcessProjectionQueryOptions): {\n [TDocumentId in string]?: DocumentProjectionValues<Record<string, unknown>>\n} {\n const groupedResults: {\n [docId: string]: {\n [hash: string]: ProjectionQueryResult | undefined\n }\n } = {}\n\n for (const result of results) {\n const originalId = getPublishedId(DocumentId(result._id))\n const hash = result.__projectionHash\n\n if (!ids.has(originalId)) continue\n\n if (!groupedResults[originalId]) {\n groupedResults[originalId] = {}\n }\n if (!groupedResults[originalId][hash]) {\n groupedResults[originalId][hash] = undefined\n }\n\n groupedResults[originalId][hash] = result\n }\n\n const finalValues: {\n [docId: string]: DocumentProjectionValues<Record<string, unknown>>\n } = {}\n\n for (const originalId of ids) {\n finalValues[originalId] = {}\n\n const projectionsForDoc = groupedResults[originalId]\n if (!projectionsForDoc) continue\n\n for (const hash in projectionsForDoc) {\n const projectionResult = projectionsForDoc[hash]\n const projectionResultData = projectionResult?.result\n\n if (!projectionResultData) {\n finalValues[originalId][hash] = {data: null, isPending: false}\n continue\n }\n\n const statusFromStore = documentStatuses?.[originalId]\n\n finalValues[originalId][hash] = {\n data: {...projectionResultData, _status: statusFromStore},\n isPending: false,\n }\n }\n }\n\n return finalValues\n}\n","import {\n DocumentId,\n getDraftId,\n getPublishedId,\n getVersionId,\n isDraftId,\n isPublishedId,\n isVersionId,\n} from '@sanity/id-utils'\n\nimport {isReleasePerspective} from '../releases/utils/isReleasePerspective'\nimport {type BoundPerspectiveKey} from '../store/createActionBinder'\nimport {type ProjectionStoreState} from './types'\n\ninterface StatusQueryResult {\n _id: string\n _updatedAt: string\n}\n\n/**\n * Builds an array of document IDs to query for status information using the \"raw\" perspective.\n * Includes draft, published, and version IDs as needed.\n */\nexport function buildStatusQueryIds(\n documentIds: Set<string>,\n perspective: BoundPerspectiveKey['perspective'],\n): string[] {\n const ids: string[] = []\n const releaseName = isReleasePerspective(perspective) ? perspective.releaseName : null\n\n for (const id of documentIds) {\n const publishedId = getPublishedId(DocumentId(id))\n const draftId = getDraftId(publishedId)\n\n // Always query for draft and published versions\n ids.push(draftId, publishedId)\n\n // If it's a release perspective, also query for the version\n if (releaseName) {\n ids.push(getVersionId(publishedId, releaseName))\n }\n }\n\n return ids\n}\n\n/**\n * Processes status query results into documentStatuses (same shape as _status returned to users).\n */\nexport function processStatusQueryResults(\n results: StatusQueryResult[],\n): ProjectionStoreState['documentStatuses'] {\n const documentStatuses: ProjectionStoreState['documentStatuses'] = {}\n\n for (const result of results) {\n const id = DocumentId(result._id)\n const updatedAt = result._updatedAt\n const publishedId = getPublishedId(id)\n const statusData = documentStatuses[publishedId] ?? {}\n if (isDraftId(id)) {\n statusData.lastEditedDraftAt = updatedAt\n } else if (isVersionId(id)) {\n statusData.lastEditedVersionAt = updatedAt\n } else if (isPublishedId(id)) {\n statusData.lastEditedPublishedAt = updatedAt\n }\n documentStatuses[publishedId] = statusData\n }\n\n return documentStatuses\n}\n","import {DocumentId} from '@sanity/id-utils'\nimport {\n combineLatest,\n debounceTime,\n defer,\n distinctUntilChanged,\n EMPTY,\n filter,\n from,\n map,\n Observable,\n pairwise,\n startWith,\n Subscription,\n switchMap,\n tap,\n} from 'rxjs'\n\nimport {getQueryState, resolveQuery} from '../query/queryStore'\nimport {type BoundPerspectiveKey} from '../store/createActionBinder'\nimport {type StoreContext} from '../store/defineStore'\nimport {isDeepEqual} from '../utils/object'\nimport {\n createProjectionQuery,\n processProjectionQuery,\n type ProjectionQueryResult,\n} from './projectionQuery'\nimport {buildStatusQueryIds, processStatusQueryResults} from './statusQuery'\nimport {type ProjectionStoreState} from './types'\nimport {PROJECTION_TAG} from './util'\n\nconst BATCH_DEBOUNCE_TIME = 50\n\nconst isSetEqual = <T>(a: Set<T>, b: Set<T>) =>\n a.size === b.size && Array.from(a).every((i) => b.has(i))\n\ninterface StatusQueryResult {\n _id: string\n _updatedAt: string\n}\n\nexport const subscribeToStateAndFetchBatches = ({\n state,\n instance,\n key: {resource, perspective},\n}: StoreContext<ProjectionStoreState, BoundPerspectiveKey>): Subscription => {\n const documentProjections$ = state.observable.pipe(\n map((s) => s.documentProjections),\n distinctUntilChanged(isDeepEqual),\n )\n\n const activeDocumentIds$ = state.observable.pipe(\n map(({subscriptions}) => new Set(Object.keys(subscriptions).map((id) => DocumentId(id)))),\n distinctUntilChanged(isSetEqual),\n )\n\n const pendingUpdateSubscription = activeDocumentIds$\n .pipe(\n debounceTime(BATCH_DEBOUNCE_TIME),\n startWith(new Set<DocumentId>()),\n pairwise(),\n tap(([prevIds, currIds]) => {\n const newIds = [...currIds].filter((id) => !prevIds.has(id))\n if (newIds.length === 0) return\n\n state.set('updatingPending', (prev) => {\n const nextValues = {...prev.values}\n for (const id of newIds) {\n const projectionsForDoc = prev.documentProjections[id]\n if (!projectionsForDoc) continue\n\n const currentValuesForDoc = prev.values[id] ?? {}\n const updatedValuesForDoc = {...currentValuesForDoc}\n\n for (const hash in projectionsForDoc) {\n const currentValue = updatedValuesForDoc[hash]\n updatedValuesForDoc[hash] = {\n data: currentValue?.data ?? null,\n isPending: true,\n }\n }\n nextValues[id] = updatedValuesForDoc\n }\n return {values: nextValues}\n })\n }),\n )\n .subscribe()\n\n const queryTrigger$ = combineLatest([activeDocumentIds$, documentProjections$]).pipe(\n debounceTime(BATCH_DEBOUNCE_TIME),\n distinctUntilChanged(isDeepEqual),\n )\n\n const queryExecutionSubscription = queryTrigger$\n .pipe(\n switchMap(([ids, documentProjections]) => {\n if (!ids.size) return EMPTY\n const {query, params} = createProjectionQuery(ids, documentProjections)\n const controller = new AbortController()\n\n // Build status query IDs and query\n const statusQueryIds = buildStatusQueryIds(ids, perspective)\n const statusQuery = `*[_id in $statusIds]{_id, _updatedAt}`\n const statusParams = {statusIds: statusQueryIds}\n\n const projectionQuery$ = new Observable<ProjectionQueryResult[]>((observer) => {\n const {getCurrent, observable} = getQueryState<ProjectionQueryResult[]>(instance, {\n ...{\n query,\n params,\n tag: PROJECTION_TAG,\n perspective,\n },\n resource,\n })\n\n const querySource$ = defer(() => {\n if (getCurrent() === undefined) {\n return from(\n resolveQuery<ProjectionQueryResult[]>(instance, {\n ...{\n query,\n params,\n tag: PROJECTION_TAG,\n signal: controller.signal,\n perspective,\n },\n resource,\n }),\n ).pipe(switchMap(() => observable))\n }\n return observable\n }).pipe(filter((result): result is ProjectionQueryResult[] => result !== undefined))\n\n const subscription = querySource$.subscribe(observer)\n\n return () => {\n if (!controller.signal.aborted) {\n controller.abort()\n }\n subscription.unsubscribe()\n }\n })\n\n const statusQuery$ = new Observable<StatusQueryResult[]>((observer) => {\n const {getCurrent, observable} = getQueryState<StatusQueryResult[]>(instance, {\n ...{\n query: statusQuery,\n params: statusParams,\n tag: PROJECTION_TAG,\n perspective: 'raw',\n },\n resource,\n })\n\n const statusQuerySource$ = defer(() => {\n if (getCurrent() === undefined) {\n return from(\n resolveQuery<StatusQueryResult[]>(instance, {\n ...{\n query: statusQuery,\n params: statusParams,\n tag: PROJECTION_TAG,\n signal: controller.signal,\n perspective: 'raw',\n },\n resource,\n }),\n ).pipe(switchMap(() => observable))\n }\n return observable\n }).pipe(filter((result): result is StatusQueryResult[] => result !== undefined))\n\n const subscription = statusQuerySource$.subscribe(observer)\n\n return () => {\n subscription.unsubscribe()\n }\n })\n\n // Combine both streams: emit whenever either has a new value (after both have\n // emitted at least one defined value). This keeps reacting to query store updates.\n return combineLatest([projectionQuery$, statusQuery$]).pipe(\n filter(\n (pair): pair is [ProjectionQueryResult[], StatusQueryResult[]] =>\n pair[0] !== undefined && pair[1] !== undefined,\n ),\n map(([projection, status]) => ({\n data: projection,\n ids,\n statusResults: status,\n })),\n )\n }),\n map(({ids, data, statusResults}) => {\n // Process status results into documentStatuses (same shape as _status returned to users)\n const documentStatuses = processStatusQueryResults(statusResults)\n state.set('updateStatuses', (prev) => ({\n documentStatuses: {\n ...prev.documentStatuses,\n ...documentStatuses,\n },\n }))\n\n // Assemble projection values with _status (buildStatusForDocument in statusQuery)\n const currentState = state.get()\n return processProjectionQuery({\n ids,\n results: data,\n documentStatuses: currentState.documentStatuses,\n perspective: perspective,\n })\n }),\n )\n .subscribe({\n next: (processedValues) => {\n state.set('updateResult', (prev) => {\n const nextValues = {...prev.values}\n for (const docId in processedValues) {\n if (processedValues[docId]) {\n nextValues[docId] = {\n ...(prev.values[docId] ?? {}),\n ...processedValues[docId],\n }\n }\n }\n return {values: nextValues}\n })\n },\n error: (err) => {\n // eslint-disable-next-line no-console\n console.error('Error fetching projection batches:', err)\n // TODO: Potentially update state to reflect error state for affected projections?\n },\n })\n\n return new Subscription(() => {\n pendingUpdateSubscription.unsubscribe()\n queryExecutionSubscription.unsubscribe()\n })\n}\n","import {type BoundPerspectiveKey} from '../store/createActionBinder'\nimport {defineStore} from '../store/defineStore'\nimport {subscribeToStateAndFetchBatches} from './subscribeToStateAndFetchBatches'\nimport {type ProjectionStoreState} from './types'\n\nexport const projectionStore = defineStore<ProjectionStoreState, BoundPerspectiveKey>({\n name: 'Projection',\n getInitialState() {\n return {\n values: {},\n documentProjections: {},\n subscriptions: {},\n documentStatuses: {},\n }\n },\n initialize(context) {\n const batchSubscription = subscribeToStateAndFetchBatches(context)\n return () => batchSubscription.unsubscribe()\n },\n})\n","import {DocumentId, getPublishedId} from '@sanity/id-utils'\n\nimport {type DocumentHandle} from '../config/sanityConfig'\nimport {bindActionByResourceAndPerspective} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {\n createStateSourceAction,\n type SelectorContext,\n type StateSource,\n} from '../store/createStateSourceAction'\nimport {type ResolveProjectionResult} from '../typegen/resolve'\nimport {hashString} from '../utils/hashString'\nimport {randomId} from '../utils/ids'\nimport {omitProperty} from '../utils/object'\nimport {setCleanupTimeout} from '../utils/setCleanupTimeout'\nimport {projectionStore} from './projectionStore'\nimport {type ProjectionStoreState, type ProjectionValuePending} from './types'\nimport {PROJECTION_STATE_CLEAR_DELAY, STABLE_EMPTY_PROJECTION, validateProjection} from './util'\n\nexport interface ProjectionOptions<\n TProjection extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n> extends DocumentHandle<TDocumentType, TDataset, TProjectId> {\n projection: TProjection\n}\n\n/**\n * @beta\n */\nexport function getProjectionState<\n TProjection extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,\n): StateSource<\n | ProjectionValuePending<\n ResolveProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>\n >\n | undefined\n>\n\n/**\n * @beta\n */\nexport function getProjectionState<TData extends object>(\n instance: SanityInstance,\n options: ProjectionOptions,\n): StateSource<ProjectionValuePending<TData> | undefined>\n\n/**\n * @beta\n */\nexport function getProjectionState(\n instance: SanityInstance,\n options: ProjectionOptions,\n): StateSource<ProjectionValuePending<Record<string, unknown>> | undefined>\n\n/**\n * @beta\n */\nexport function getProjectionState(\n ...args: Parameters<typeof _getProjectionState>\n): ReturnType<typeof _getProjectionState> {\n return _getProjectionState(...args)\n}\n\n/**\n * @beta\n */\nexport const _getProjectionState = bindActionByResourceAndPerspective(\n projectionStore,\n createStateSourceAction({\n selector: (\n {state}: SelectorContext<ProjectionStoreState>,\n options: ProjectionOptions<string, string, string, string>,\n ): ProjectionValuePending<object> | undefined => {\n const documentId = getPublishedId(DocumentId(options.documentId))\n const projectionHash = hashString(options.projection)\n return state.values[documentId]?.[projectionHash] ?? STABLE_EMPTY_PROJECTION\n },\n onSubscribe: ({state}, options: ProjectionOptions<string, string, string, string>) => {\n const {projection, ...docHandle} = options\n const subscriptionId = randomId(16)\n const documentId = getPublishedId(DocumentId(docHandle.documentId))\n const validProjection = validateProjection(projection)\n const projectionHash = hashString(validProjection)\n\n state.set('addSubscription', (prev) => ({\n documentProjections: {\n ...prev.documentProjections,\n [documentId]: {\n ...prev.documentProjections[documentId],\n [projectionHash]: validProjection,\n },\n },\n subscriptions: {\n ...prev.subscriptions,\n [documentId]: {\n ...prev.subscriptions[documentId],\n [projectionHash]: {\n ...prev.subscriptions[documentId]?.[projectionHash],\n [subscriptionId]: true,\n },\n },\n },\n }))\n\n return () => {\n setCleanupTimeout(() => {\n state.set('removeSubscription', (prev): Partial<ProjectionStoreState> => {\n const documentSubscriptionsForHash = omitProperty(\n prev.subscriptions[documentId]?.[projectionHash],\n subscriptionId,\n )\n const hasSubscribersForProjection = !!Object.keys(documentSubscriptionsForHash).length\n\n const nextSubscriptions = {...prev.subscriptions}\n const nextDocumentProjections = {...prev.documentProjections}\n const nextValues = {...prev.values}\n\n // clean up the subscription and documentProjection if there are no subscribers\n if (!hasSubscribersForProjection) {\n delete nextSubscriptions[documentId]![projectionHash]\n delete nextDocumentProjections[documentId]![projectionHash]\n\n const currentProjectionValue = prev.values[documentId]?.[projectionHash]\n if (currentProjectionValue && nextValues[documentId]) {\n nextValues[documentId]![projectionHash] = {\n data: currentProjectionValue.data,\n isPending: false,\n }\n }\n } else {\n if (nextSubscriptions[documentId]) {\n nextSubscriptions[documentId]![projectionHash] = documentSubscriptionsForHash\n }\n }\n\n const hasAnySubscribersForDocument = Object.values(\n nextSubscriptions[documentId] ?? {},\n ).some((subs) => Object.keys(subs).length > 0)\n\n if (!hasAnySubscribersForDocument) {\n delete nextSubscriptions[documentId]\n delete nextDocumentProjections[documentId]\n // Keep nextValues[documentId] as cache\n }\n\n return {\n subscriptions: nextSubscriptions,\n documentProjections: nextDocumentProjections,\n values: nextValues,\n }\n })\n }, PROJECTION_STATE_CLEAR_DELAY)\n }\n },\n }),\n)\n","import {filter, firstValueFrom} from 'rxjs'\n\nimport {bindActionByResourceAndPerspective} from '../store/createActionBinder'\nimport {type SanityInstance} from '../store/createSanityInstance'\nimport {type ResolveProjectionResult} from '../typegen/resolve'\nimport {getProjectionState, type ProjectionOptions} from './getProjectionState'\nimport {projectionStore} from './projectionStore'\nimport {type ProjectionValuePending} from './types'\n\n/** @beta */\nexport function resolveProjection<\n TProjection extends string = string,\n TDocumentType extends string = string,\n TDataset extends string = string,\n TProjectId extends string = string,\n>(\n instance: SanityInstance,\n options: ProjectionOptions<TProjection, TDocumentType, TDataset, TProjectId>,\n): Promise<\n ProjectionValuePending<\n ResolveProjectionResult<TProjection, TDocumentType, `${TProjectId}.${TDataset}`>\n >\n>\n\n/** @beta */\nexport function resolveProjection<TData extends object>(\n instance: SanityInstance,\n options: ProjectionOptions,\n): Promise<ProjectionValuePending<TData>>\n\n/** @beta */\nexport function resolveProjection(\n ...args: Parameters<typeof _resolveProjection>\n): ReturnType<typeof _resolveProjection> {\n return _resolveProjection(...args)\n}\n\n/**\n * @beta\n */\nconst _resolveProjection = bindActionByResourceAndPerspective(\n projectionStore,\n (\n {instance}: {instance: SanityInstance},\n options: ProjectionOptions,\n ): Promise<ProjectionValuePending<Record<string, unknown>>> =>\n firstValueFrom(\n getProjectionState<Record<string, unknown>>(instance, options).observable.pipe(\n filter((state): state is ProjectionValuePending<Record<string, unknown>> => !!state?.data),\n ),\n ),\n)\n","/**\n * Marks a projection for Typegen and returns the projection string unchanged, with its literal\n * type. `useDocumentProjection` matches that literal against the generated projection types.\n *\n * Pass the document type first so Typegen evaluates the projection for that type only. A\n * projection passed alone is evaluated for every document type in the schema, which generates\n * a result type for each.\n *\n * @example\n * ```ts\n * const preview = defineProjection('book', '{title, \"author\": author->name}')\n * ```\n *\n * @beta\n */\nexport function defineProjection<const TProjection extends string>(\n documentType: string,\n projection: TProjection,\n): TProjection\n/**\n * Marks a projection for Typegen and returns it unchanged. Typegen evaluates a projection\n * passed alone for every document type in the schema; pass the document type first to limit it\n * to one.\n *\n * @beta\n */\nexport function defineProjection<const TProjection extends string>(\n projection: TProjection,\n): TProjection\nexport function defineProjection(first: string, second?: string): string {\n return second ?? first\n}\n","import {CorsOriginError} from '@sanity/client'\n\n/**\n * @public\n * Extracts the project ID from a CorsOriginError message.\n * @param error - The error to extract the project ID from.\n * @returns The project ID or null if the error is not a CorsOriginError.\n */\nexport function getCorsErrorProjectId(error: Error): string | null {\n if (!(error instanceof CorsOriginError)) return null\n\n const message = (error as unknown as {message?: string}).message || ''\n const projMatch = message.match(/manage\\/project\\/([^/?#]+)/)\n return projMatch ? projMatch[1] : null\n}\n","/**\n * Returns true when the given error looks like a dynamic-import or\n * code-split chunk-loading failure.\n *\n * These errors typically surface when a user has a tab open against a\n * previously-deployed version of an app and the JavaScript or CSS chunk\n * filenames have since changed: a fresh deployment removes the hashed assets\n * the open tab still references. Detecting them lets the SDK trigger an\n * automatic reload so the user gets the new build without manual intervention.\n *\n * Recognized shapes (webpack ChunkLoadError, Vite \"Failed to fetch\n * dynamically imported module\", Firefox \"error loading dynamically imported\n * module\", Safari \"Importing a module script failed\", and Vite \"Unable to\n * preload CSS\").\n *\n * @param error - The value to inspect. Anything that is not an Error\n * instance returns false.\n * @returns True if the error matches a known import/chunk-load failure.\n *\n * @public\n */\nexport function isImportError(error: unknown): boolean {\n if (!(error instanceof Error)) return false\n if (error.name === 'ChunkLoadError') return true\n\n const message = error.message || ''\n return (\n /Loading chunk [\\w-]+ failed/i.test(message) ||\n /Failed to fetch dynamically imported module/i.test(message) ||\n /error loading dynamically imported module/i.test(message) ||\n /Import(?:ing)? a module script failed/i.test(message) ||\n /Unable to preload CSS/i.test(message)\n )\n}\n"],"mappings":"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"}