@sanity-labs/client-fake-for-test 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -2
- package/dist/index.cjs +208 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -6
- package/dist/index.d.ts +35 -6
- package/dist/index.js +208 -83
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/history.ts","../src/resources.ts","../src/platform-api.ts","../src/access-control.ts","../src/mutation-guard.ts","../src/patch.ts","../src/perspective.ts","../src/store.ts","../src/actions.ts","../src/transaction.ts","../src/unsupported.ts","../src/client.ts"],"sourcesContent":["/**\n * Error helpers shared across the fake.\n *\n * Every error the fake throws is prefixed with {@link ERR_PREFIX} so it's\n * unmistakably attributable to *this fake* — not to `@sanity/client`,\n * groq-js, or the code under test — and carries enough context to fix\n * the problem without reading the fake's source. Short, bare messages\n * (\"not found\", \"invalid id\") are a poor developer experience in a test\n * double: prefer stating what happened, the offending value, and what to\n * do about it.\n */\nexport const ERR_PREFIX = \"[client-fake-for-test]\";\n\n/** Build a prefixed Error. Pass a fully-formed, dev-facing message. */\nexport function fakeError(message: string): Error {\n return new Error(`${ERR_PREFIX} ${message}`);\n}\n","/**\n * Document history for one store — the fake's mirror of the History API\n * (`<data base>/history/documents | transactions | events`).\n *\n * Every committed write is recorded as one transaction holding the net\n * change of each document it touched. Revisions are kept by `_rev`, so a\n * document can be read at any revision it has held since the client was\n * created, plus the revision it holds now (seeded documents included).\n */\n\nimport type { SanityDocument } from \"@sanity/types\";\n\n/** One document's net change in a committed transaction. */\ninterface HistoryEffect {\n documentId: string;\n /** The document after the transaction; `undefined` when it was deleted. */\n after: SanityDocument | undefined;\n /** Whether the document existed before the transaction. */\n existed: boolean;\n}\n\ninterface HistoryTransaction {\n id: string;\n timestamp: string;\n author: string | undefined;\n effects: HistoryEffect[];\n}\n\n/** A transaction as `GET <data base>/history/transactions/<ids>` serves it, one per NDJSON line. */\nexport interface HistoryTransactionRecord {\n id: string;\n timestamp: string;\n author?: string;\n mutations: (\n | { create: SanityDocument }\n | { createOrReplace: SanityDocument }\n | { delete: { id: string } }\n )[];\n documentIDs: string[];\n}\n\n/** An event as `GET <data base>/history/events/documents/<ids>` serves it. */\nexport interface HistoryEvent {\n type: \"createLiveDocument\" | \"updateLiveDocument\";\n id: string;\n documentId: string;\n revisionId: string;\n timestamp: string;\n author?: string;\n}\n\nexport interface DocumentHistory {\n /**\n * Record a committed write: compare the images from before the write with\n * the store after it, and keep one transaction for the documents that\n * changed. A write that changed nothing records nothing.\n */\n commit: (args: {\n transactionId: string;\n author: string | undefined;\n before: ReadonlyMap<string, SanityDocument>;\n after: readonly SanityDocument[];\n }) => void;\n /** `?revision=<rev>`: the listed documents as they were at that revision. */\n documentsAt: (args: {\n ids: readonly string[];\n revision: string;\n current: readonly SanityDocument[];\n }) => SanityDocument[];\n /** Every document id the history has recorded a change for. */\n documentIds: () => string[];\n /** The last image `id` held before it was deleted, or `undefined` if it never changed. */\n lastImage: (id: string) => SanityDocument | undefined;\n /**\n * Transactions touching any of `ids`, oldest first. Each record keeps only\n * the effects on documents `visible` lets through, so a transaction shared\n * with a document the reader cannot see does not reveal it.\n */\n transactions: (\n ids: readonly string[],\n visible: (documentId: string) => boolean,\n ) => HistoryTransactionRecord[];\n /** Live-document events for each of `ids`, newest first. */\n events: (ids: readonly string[]) => Record<string, HistoryEvent[]>;\n}\n\nexport function createDocumentHistory(): DocumentHistory {\n const transactions: HistoryTransaction[] = [];\n /** Every image a document has held, keyed `<id> <rev>`. */\n const revisions = new Map<string, SanityDocument>();\n /** Every image is a copy, so neither the store nor a caller's edits rewrite history. */\n const keep = (doc: SanityDocument) =>\n revisions.set(`${doc._id} ${doc._rev}`, structuredClone(doc));\n /** The last image each deleted document held. */\n const deleted = new Map<string, SanityDocument>();\n\n return {\n commit: ({ transactionId, author, before, after }) => {\n const afterById = new Map(after.map((doc) => [doc._id, doc]));\n const ids = new Set([...before.keys(), ...afterById.keys()]);\n const effects: HistoryEffect[] = [];\n for (const id of ids) {\n const previous = before.get(id);\n const next = afterById.get(id);\n if (previous === next) continue;\n if (previous !== undefined) keep(previous);\n if (next !== undefined) keep(next);\n if (next === undefined && previous !== undefined)\n deleted.set(id, structuredClone(previous));\n else deleted.delete(id);\n effects.push({\n documentId: id,\n after: next === undefined ? undefined : structuredClone(next),\n existed: previous !== undefined,\n });\n }\n if (effects.length === 0) return;\n transactions.push({\n id: transactionId,\n timestamp: new Date().toISOString(),\n author,\n effects,\n });\n },\n\n documentsAt: ({ ids, revision, current }) =>\n ids.flatMap((id) => {\n const image =\n revisions.get(`${id} ${revision}`) ??\n current.find((doc) => doc._id === id && doc._rev === revision);\n return image === undefined ? [] : [structuredClone(image)];\n }),\n\n lastImage: (id) => deleted.get(id),\n\n documentIds: () => [\n ...new Set(transactions.flatMap((txn) => txn.effects.map((effect) => effect.documentId))),\n ],\n\n transactions: (ids, visible) =>\n transactions\n .filter((txn) => txn.effects.some((effect) => ids.includes(effect.documentId)))\n .map((txn) => ({ ...txn, effects: txn.effects.filter((e) => visible(e.documentId)) }))\n .map((txn) => ({\n id: txn.id,\n timestamp: txn.timestamp,\n ...(txn.author !== undefined ? { author: txn.author } : {}),\n mutations: txn.effects.map((effect) => {\n if (effect.after === undefined) return { delete: { id: effect.documentId } };\n const after = structuredClone(effect.after);\n return effect.existed ? { createOrReplace: after } : { create: after };\n }),\n documentIDs: txn.effects.map((effect) => effect.documentId),\n })),\n\n events: (ids) =>\n Object.fromEntries(\n ids.map((id) => [\n id,\n transactions\n .flatMap((txn) =>\n txn.effects\n .filter((effect) => effect.documentId === id && effect.after !== undefined)\n .map(\n (effect): HistoryEvent => ({\n type: effect.existed ? \"updateLiveDocument\" : \"createLiveDocument\",\n id: txn.id,\n documentId: id,\n revisionId: (effect.after as SanityDocument)._rev,\n timestamp: txn.timestamp,\n ...(txn.author !== undefined ? { author: txn.author } : {}),\n }),\n ),\n )\n .toReversed(),\n ]),\n ),\n };\n}\n\n/** Split `documents/<ids>` style id lists: comma-separated, URI-encoded. */\nexport function historyIds(list: string): string[] {\n return list.split(\",\").map((id) => decodeURIComponent(id));\n}\n","/**\n * One capability table per resource type: how `@sanity/client` addresses\n * the resource, and what the platform serves or refuses there.\n *\n * Every value is taken from the staging capability probe (EDEX-2884): where\n * the platform says no, the entry holds the platform's own answer — status\n * and body — and the fake throws exactly that. Nothing here is policy of the\n * fake; adding a resource type means adding one row.\n */\n\nimport type { CurrentUser, TestClientIdentity } from \"./access-control.ts\";\nimport { fakeError } from \"./errors.ts\";\n\n/** Resource types `@sanity/client`'s `resource` config can name (the ones staging probed). */\nexport type ResourceType = \"dataset\" | \"media-library\" | \"canvas\" | \"dashboard\";\n\n/**\n * A resource, named exactly as `@sanity/client`'s `resource` config names\n * it: `{ type: \"media-library\", id: \"ml…\" }`, or `{ type: \"dataset\", id:\n * \"<projectId>.<dataset>\" }`.\n */\nexport interface TestClientResource {\n type: ResourceType;\n id: string;\n}\n\n/** The `response` of an HTTP error, shaped like `@sanity/client`'s `HttpError[\"response\"]`. */\ninterface PlatformErrorResponse {\n body: unknown;\n url: string;\n method: string;\n headers: Record<string, string>;\n statusCode: number;\n statusMessage: string;\n}\n\n/**\n * The platform's refusal, shaped like the `ClientError` `@sanity/client`\n * throws for the same response: `statusCode`, `response`, `responseBody`,\n * `details`, and a `message` derived from the body the way the client\n * derives it.\n */\nexport class PlatformHttpError extends Error {\n readonly statusCode: number;\n readonly response: PlatformErrorResponse;\n readonly responseBody: string;\n readonly details: unknown;\n\n constructor(args: {\n method: string;\n path: string;\n statusCode: number;\n statusMessage: string;\n body: unknown;\n }) {\n const { method, path, statusCode, statusMessage, body } = args;\n super(clientErrorMessage(args));\n this.name = \"PlatformHttpError\";\n this.statusCode = statusCode;\n this.response = { body, url: path, method, headers: {}, statusCode, statusMessage };\n this.responseBody = typeof body === \"string\" ? body : JSON.stringify(body, null, 2);\n const error = isRecord(body) ? body.error : undefined;\n this.details = isRecord(error) ? error : undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/** `@sanity/client`'s `extractErrorProps` message rules, for the body shapes the platform sends here. */\nfunction clientErrorMessage(args: {\n method: string;\n path: string;\n statusCode: number;\n statusMessage: string;\n body: unknown;\n}): string {\n const { body } = args;\n if (isRecord(body)) {\n const { error, message } = body;\n if (isRecord(error) && typeof error.description === \"string\") return error.description;\n if (typeof message === \"string\") return message;\n }\n // `@sanity/client` shows at most 100 characters of a text body in the message.\n const details =\n typeof body === \"string\" ? ` (${body.length > 100 ? `${body.slice(0, 100)}…` : body})` : \"\";\n return `${args.method}-request to ${args.path} resulted in HTTP ${args.statusCode} ${args.statusMessage}${details}`;\n}\n\n/** A request the platform answers. `path` is the API path, without host or API version. */\nexport interface PlatformRequest {\n method: \"GET\" | \"POST\";\n path: string;\n}\n\n/** The platform's answer where it says no. */\nexport type Refusal = (request: PlatformRequest) => PlatformHttpError;\n\n/** Either the platform serves the capability, or it refuses with a fixed answer. */\nexport type Capability = \"served\" | Refusal;\n\n/** Express's 404 for a route the resource does not mount: `Cannot GET /dashboards/<id>/acl`. */\nconst notMounted: Refusal = ({ method, path }) =>\n new PlatformHttpError({\n method,\n path,\n statusCode: 404,\n statusMessage: \"Not Found\",\n body: `Cannot ${method} ${path}`,\n });\n\n/** An Access API error: `{ statusCode, message }`, the body shape staging returned for its 403. */\nfunction accessApiError(args: {\n request: PlatformRequest;\n statusCode: number;\n statusMessage: string;\n message: string;\n}): PlatformHttpError {\n const { request, statusCode, statusMessage, message } = args;\n return new PlatformHttpError({\n ...request,\n statusCode,\n statusMessage,\n body: { statusCode, message },\n });\n}\n\n/** The Access API's 403 when the caller lacks the permission; also the answer for unproven cells. */\nconst noPermission: Refusal = (request) =>\n accessApiError({\n request,\n statusCode: 403,\n statusMessage: \"Forbidden\",\n message: \"You do not have permission to access this resource\",\n });\n\n/** The Access API's 400 for a resource type it does not know: `/access/bogustype/<id>/…`. */\nexport function invalidAccessType(request: PlatformRequest, type: string): PlatformHttpError {\n return accessApiError({\n request,\n statusCode: 400,\n statusMessage: \"Bad Request\",\n message: `Invalid resource type ${type}`,\n });\n}\n\n/** The Access API's 404 for an unknown resource id: `\"Canvas not found\"`. */\nfunction notFound(name: string): Refusal {\n return (request) =>\n accessApiError({\n request,\n statusCode: 404,\n statusMessage: \"Not Found\",\n message: `${name} not found`,\n });\n}\n\n/** Media Library's plan quota on releases: `releaseLimitExceededError`, limit 0. */\nconst releaseLimitExceeded: Refusal = ({ method, path }) =>\n new PlatformHttpError({\n method,\n path,\n statusCode: 403,\n statusMessage: \"Forbidden\",\n body: {\n error: {\n description: \"active release count exceeds the limit of 0\",\n type: \"releaseLimitExceededError\",\n },\n },\n });\n\n/** Throw the refusal, or run `serve` when the platform serves the capability. */\nexport function serveCapability<T>(\n capability: Capability,\n request: PlatformRequest,\n serve: () => T,\n): T {\n if (capability !== \"served\") throw capability(request);\n return serve();\n}\n\n/** The Access API's resource types (`/access/<type>/<id>/…`). */\nexport type AccessResourceType = \"project\" | \"media-library\" | \"canvas\" | \"dashboard\";\n\nexport interface ResourceCapabilities {\n /** How the fake's own error messages name the resource. */\n label: (id: string) => string;\n /** The data API base `@sanity/client`'s `resourceDataBase` builds for the resource. */\n dataBase: (id: string) => string;\n /** Match a request path on the data API base; `rest` is what follows it (`\"\"`, `/acl`, …). */\n matchBase: (path: string) => { id: string; rest: string } | undefined;\n /** Whether `GET <base>` answers the resource record (`{ organizationId, id, status }`). */\n lookupAtBase: boolean;\n /**\n * Whose id grant filters see as `identity()`: the project-scoped user\n * record for a dataset, the account-global one for every other resource.\n */\n identity: \"project\" | \"global\";\n /** `<base>/acl` → the caller's `Grant[]`. */\n acl: Capability;\n /** `<base>/history/documents | transactions | events`. */\n history: Capability;\n /** `<base>/actions` — release, version and document actions. */\n actions: Capability;\n /** Action types refused where `/actions` is mounted. */\n refusedActions: Partial<Record<string, Refusal>>;\n access: {\n /** The Access API addresses the resource as `/access/<type>/<id>`. */\n type: AccessResourceType;\n id: (id: string) => string;\n /** `GET /access/<type>/<id>/roles`. */\n roles: Capability;\n /** `GET /access/<type>/<id>/users`. */\n users: Capability;\n /**\n * The answer for an id the registry does not hold, on every endpoint but\n * `user-permissions/me/check` (which answers `false` even then). Absent\n * where staging did not probe it.\n */\n unknownId?: Refusal;\n /** The identity's role records on the resource, or `undefined` when it is not a member. */\n memberRoles: (args: {\n identity: TestClientIdentity;\n id: string;\n }) => NonNullable<CurrentUser[\"roles\"]> | undefined;\n };\n}\n\nconst ACCESS_KEY_SEPARATOR = \":\";\n\n/** Key of an identity's `access` entry for a resource: `\"<access type>:<access id>\"`. */\nexport function accessKey(type: AccessResourceType, id: string): string {\n return `${type}${ACCESS_KEY_SEPARATOR}${id}`;\n}\n\n/** Match `/<segment>/<id>` and whatever follows. */\nfunction segmentBase(segment: string): ResourceCapabilities[\"matchBase\"] {\n const pattern = new RegExp(`^/${segment}/([^/]+)(/.*)?$`);\n return (path) => {\n const match = path.match(pattern);\n if (match === null) return undefined;\n return { id: decodeURIComponent(match[1] as string), rest: match[2] ?? \"\" };\n };\n}\n\nconst DATASET_BASE = /^\\/projects\\/([^/]+)\\/datasets\\/([^/]+)(\\/.*)?$/;\n\n/**\n * Split a dataset resource id, `\"<projectId>.<dataset>\"`, at its first dot.\n * Lenient on purpose: the fake builds these ids from any `(projectId,\n * dataset)` pair; caller-supplied ids are checked by {@link validateResource}.\n */\nexport function datasetParts(id: string): { projectId: string; dataset: string } {\n const dot = id.indexOf(\".\");\n return { projectId: id.slice(0, dot), dataset: id.slice(dot + 1) };\n}\n\n/** A non-dataset resource's roles live in the identity's `access` entry for it. */\nfunction accessEntryRoles(type: AccessResourceType): ResourceCapabilities[\"access\"][\"memberRoles\"] {\n return ({ identity, id }) => identity.access?.[accessKey(type, id)]?.roles;\n}\n\nexport const RESOURCE_CAPABILITIES: Record<ResourceType, ResourceCapabilities> = {\n dataset: {\n label: (id) => {\n const { projectId, dataset } = datasetParts(id);\n return `dataset \"${projectId}:${dataset}\"`;\n },\n dataBase: (id) => {\n const { projectId, dataset } = datasetParts(id);\n return `/projects/${projectId}/datasets/${dataset}`;\n },\n matchBase: (path) => {\n const match = path.match(DATASET_BASE);\n if (match === null) return undefined;\n return { id: `${match[1]}.${match[2]}`, rest: match[3] ?? \"\" };\n },\n // A dataset's organization comes from its project: `GET /projects/<projectId>`.\n lookupAtBase: false,\n identity: \"project\",\n acl: \"served\",\n history: \"served\",\n actions: \"served\",\n refusedActions: {},\n access: {\n type: \"project\",\n id: (id) => datasetParts(id).projectId,\n roles: \"served\",\n users: \"served\",\n // Every registered identity is a member of every project, with its project roles.\n memberRoles: ({ identity, id }) =>\n (identity.usersByProject?.[id] ?? identity.user).roles ?? [],\n },\n },\n \"media-library\": {\n label: (id) => `media library \"${id}\"`,\n dataBase: (id) => `/media-libraries/${id}`,\n matchBase: segmentBase(\"media-libraries\"),\n lookupAtBase: true,\n identity: \"global\",\n acl: \"served\",\n history: \"served\",\n actions: \"served\",\n refusedActions: { \"sanity.action.release.create\": releaseLimitExceeded },\n access: {\n type: \"media-library\",\n id: (id) => id,\n // Unproven on staging (no token held roles read): refused like a missing permission.\n roles: noPermission,\n users: \"served\",\n unknownId: notFound(\"Media library\"),\n memberRoles: accessEntryRoles(\"media-library\"),\n },\n },\n canvas: {\n label: (id) => `canvas \"${id}\"`,\n dataBase: (id) => `/canvases/${id}`,\n matchBase: segmentBase(\"canvases\"),\n lookupAtBase: true,\n identity: \"global\",\n acl: \"served\",\n history: \"served\",\n actions: notMounted,\n refusedActions: {},\n access: {\n type: \"canvas\",\n id: (id) => id,\n // Unproven on staging (no canvas roles/members permission exists to grant).\n roles: noPermission,\n users: noPermission,\n unknownId: notFound(\"Canvas\"),\n memberRoles: accessEntryRoles(\"canvas\"),\n },\n },\n dashboard: {\n label: (id) => `dashboard \"${id}\"`,\n dataBase: (id) => `/dashboards/${id}`,\n matchBase: segmentBase(\"dashboards\"),\n lookupAtBase: true,\n identity: \"global\",\n acl: notMounted,\n history: notMounted,\n actions: notMounted,\n refusedActions: {},\n access: {\n type: \"dashboard\",\n id: (id) => id,\n // Unproven on staging, as for canvas.\n roles: noPermission,\n users: noPermission,\n unknownId: notFound(\"Dashboard\"),\n memberRoles: accessEntryRoles(\"dashboard\"),\n },\n },\n};\n\n/** The capability row for `type`, or a fake error naming the supported types. */\nexport function capabilitiesOf(type: string): ResourceCapabilities {\n const row = (RESOURCE_CAPABILITIES as Record<string, ResourceCapabilities | undefined>)[type];\n if (row === undefined) {\n throw fakeError(\n `Unsupported resource type ${JSON.stringify(type)}. The fake serves ` +\n `${Object.keys(RESOURCE_CAPABILITIES)\n .map((t) => `\"${t}\"`)\n .join(\", \")}.`,\n );\n }\n return row;\n}\n\n/** Validate a `resource` config the way `@sanity/client` does, returning its capability row. */\nexport function validateResource(resource: TestClientResource): ResourceCapabilities {\n const row = capabilitiesOf(resource.type);\n if (resource.type === \"dataset\" && resource.id.split(\".\").length !== 2) {\n throw fakeError(\n `Dataset resource ID must be in the format \"project.dataset\" (got ${JSON.stringify(resource.id)}), ` +\n \"the same rule @sanity/client applies to `resource: { type: 'dataset', id }`.\",\n );\n }\n return row;\n}\n\n/** Find the resource a data API path addresses, by trying every row's base. */\nexport function matchResourcePath(\n path: string,\n): { resource: TestClientResource; rest: string } | undefined {\n for (const [type, row] of Object.entries(RESOURCE_CAPABILITIES)) {\n const match = row.matchBase(path);\n if (match !== undefined) {\n return { resource: { type: type as ResourceType, id: match.id }, rest: match.rest };\n }\n }\n return undefined;\n}\n\n/** The row whose Access API type is `type`, if any. */\nexport function capabilitiesForAccessType(\n type: string,\n): { resourceType: ResourceType; row: ResourceCapabilities } | undefined {\n for (const [resourceType, row] of Object.entries(RESOURCE_CAPABILITIES)) {\n if (row.access.type === type) return { resourceType: resourceType as ResourceType, row };\n }\n return undefined;\n}\n\n/**\n * `@sanity/client`'s `getDataUrl(operation, path)`: a `resource`-configured\n * handle addresses `<resource base>/<operation>/<path>`, a plain dataset\n * handle `/data/<operation>/<dataset>/<path>`.\n */\nexport function dataUrl(args: {\n resource: TestClientResource | undefined;\n dataset: string;\n operation: string;\n path?: string | undefined;\n}): string {\n const { resource, dataset, operation, path } = args;\n if (resource !== undefined) {\n const uri = path !== undefined ? `${operation}/${path}` : operation;\n return `${capabilitiesOf(resource.type).dataBase(resource.id)}/${uri}`.replace(\n /\\/($|\\?)/,\n \"$1\",\n );\n }\n const base = `/${operation}/${dataset}`;\n const uri = path !== undefined ? `${base}/${path}` : base;\n return `/data${uri}`.replace(/\\/($|\\?)/, \"$1\");\n}\n","/**\n * The platform endpoints `client.request()` serves beyond identity: the\n * Access API (`/access/<type>/<id>/…`), resource lookup (`GET <data\n * base>`), ACL (`<data base>/acl`) and the History API (`<data\n * base>/history/…`, or `/data/history/<dataset>/…` on a plain dataset\n * handle). Routing goes through the capability table: a capability the\n * resource refuses throws the platform's own answer.\n */\n\nimport type { SanityDocument } from \"@sanity/types\";\n\nimport type { CurrentUser, Grant, ResourceAccess, TestClientIdentity } from \"./access-control.ts\";\nimport { fakeError } from \"./errors.ts\";\nimport { historyIds, type DocumentHistory } from \"./history.ts\";\nimport {\n accessKey,\n capabilitiesForAccessType,\n invalidAccessType,\n capabilitiesOf,\n matchResourcePath,\n serveCapability,\n type AccessResourceType,\n type PlatformRequest,\n type ResourceCapabilities,\n type TestClientResource,\n} from \"./resources.ts\";\n\n/** What the handle answers for its own resource: the actor's grants, and the history it may read. */\nexport interface OwnResource {\n grants: Grant[];\n documents: () => readonly SanityDocument[];\n history: DocumentHistory;\n /** The actor's read gate; history serves only documents it lets through. */\n canRead: (document: SanityDocument) => Promise<boolean>;\n}\n\n/** Everything {@link servePlatformRequest} needs from the owning handle. */\nexport interface PlatformApiContext {\n /** The handle's projectId, for `/data/history/<dataset>` paths. */\n projectId: string;\n /** The organization every resource of the family belongs to, if configured. */\n organizationId: string | undefined;\n /**\n * The handle's own resource, or `undefined` for any other: a token's\n * grants and history answer for the resource the handle addresses.\n */\n own: (resource: TestClientResource) => OwnResource | undefined;\n /** The acting identity's Access API entries, keyed `\"<access type>:<id>\"`. */\n actorAccess: () => Record<string, ResourceAccess> | undefined;\n /** Every registered identity, for member and role listings. */\n identities: () => TestClientIdentity[];\n /** Whether the registry holds `resource` (seeded, written, or addressed by a handle). */\n exists: (resource: TestClientResource) => boolean;\n}\n\n/** `{ value }` when the platform answers the path, `undefined` when the fake has no handler for it. */\ntype Served = { value: unknown } | undefined;\n\nconst ACCESS_PATH = /^\\/access\\/([^/]+)\\/([^/]+)(\\/.*)?$/;\nconst DATA_HISTORY_PATH = /^\\/data\\/history\\/([^/]+)(\\/.*)$/;\n\n/** An Access API listing page; the fake serves everything on one page. */\nfunction page<T>(data: T[]): { data: T[]; nextCursor: null } {\n return { data, nextCursor: null };\n}\n\n/** Serve `path` (query string split off) if it is a platform endpoint the table knows. */\nexport async function servePlatformRequest(ctx: PlatformApiContext, url: string): Promise<Served> {\n const queryStart = url.indexOf(\"?\");\n const path = queryStart === -1 ? url : url.slice(0, queryStart);\n const query = new URLSearchParams(queryStart === -1 ? \"\" : url.slice(queryStart + 1));\n const request: PlatformRequest = { method: \"GET\", path };\n\n const access = path.match(ACCESS_PATH);\n if (access !== null) {\n const [, type, id, rest] = access as [string, string, string, string | undefined];\n return serveAccess({ ctx, request, type, id: decodeURIComponent(id), rest: rest ?? \"\", query });\n }\n\n const dataHistory = path.match(DATA_HISTORY_PATH);\n if (dataHistory !== null) {\n const [, dataset, rest] = dataHistory as [string, string, string];\n const resource: TestClientResource = { type: \"dataset\", id: `${ctx.projectId}.${dataset}` };\n return serveHistory({ ctx, request, resource, rest, query });\n }\n\n const matched = matchResourcePath(path);\n if (matched === undefined) return undefined;\n const { resource, rest } = matched;\n const row = capabilitiesOf(resource.type);\n if (rest === \"\") return row.lookupAtBase ? serveLookup(ctx, resource) : undefined;\n if (rest === \"/acl\") {\n return serveCapability(row.acl, request, () => {\n const own = ctx.own(resource);\n return own === undefined ? undefined : { value: own.grants };\n });\n }\n if (rest.startsWith(\"/history/\")) {\n return serveHistory({ ctx, request, resource, rest: rest.slice(\"/history\".length), query });\n }\n return undefined;\n}\n\n/** `GET /media-libraries/<id>`, `/canvases/<id>`, `/dashboards/<id>`: the resource record. */\nfunction serveLookup(ctx: PlatformApiContext, resource: TestClientResource): Served {\n if (ctx.organizationId === undefined) {\n throw fakeError(\n `request(): GET ${capabilitiesOf(resource.type).dataBase(resource.id)} answers the ` +\n \"resource's organization, but no organization is configured. Pass \" +\n \"createTestClient({ organizationId }).\",\n );\n }\n return { value: { organizationId: ctx.organizationId, id: resource.id, status: \"active\" } };\n}\n\nfunction serveHistory(args: {\n ctx: PlatformApiContext;\n request: PlatformRequest;\n resource: TestClientResource;\n rest: string;\n query: URLSearchParams;\n}): Promise<Served> | undefined {\n const { ctx, request, resource, rest, query } = args;\n return serveCapability(capabilitiesOf(resource.type).history, request, () => {\n const own = ctx.own(resource);\n return own === undefined ? undefined : readHistory({ own, request, rest, query });\n });\n}\n\n/** Answer a History API path from the handle's own history, hiding what the actor cannot read. */\nasync function readHistory(args: {\n own: OwnResource;\n request: PlatformRequest;\n rest: string;\n query: URLSearchParams;\n}): Promise<Served> {\n const { own, request, rest, query } = args;\n // The read grant filters the document as it is now or, once deleted, as it last was.\n const current = new Map(own.documents().map((doc) => [doc._id, doc]));\n const hidden = new Set<string>();\n for (const id of new Set([...current.keys(), ...own.history.documentIds()])) {\n const image = current.get(id) ?? own.history.lastImage(id);\n if (image !== undefined && !(await own.canRead(image))) hidden.add(id);\n }\n const visible = (id: string) => !hidden.has(id);\n const readable = (list: string): string[] => historyIds(list).filter(visible);\n\n const documents = rest.match(/^\\/documents\\/([^/]+)$/);\n if (documents !== null) {\n const revision = query.get(\"revision\");\n if (revision === null) {\n throw fakeError(\n `request(): ${request.path} is served for \\`?revision=<rev>\\` reads only ` +\n \"(time-based history reads are not simulated).\",\n );\n }\n const ids = readable(documents[1] as string);\n return {\n value: { documents: own.history.documentsAt({ ids, revision, current: own.documents() }) },\n };\n }\n const transactions = rest.match(/^\\/transactions\\/([^/]+)$/);\n if (transactions !== null) {\n // NDJSON, one transaction per line, as the History API streams it.\n const records = own.history.transactions(readable(transactions[1] as string), visible);\n return { value: records.map((record) => JSON.stringify(record)).join(\"\\n\") };\n }\n const events = rest.match(/^\\/events\\/documents\\/([^/]+)$/);\n if (events !== null) {\n return {\n value: {\n events: own.history.events(readable(events[1] as string)),\n nextCursor: \"\",\n },\n };\n }\n return undefined;\n}\n\ntype RoleRecord = NonNullable<CurrentUser[\"roles\"]>[number];\n\n/** An Access API user row: the account-global id, a profile, and the membership on the resource. */\ninterface AccessUserRecord {\n sanityUserId: string;\n profile: { displayName: string; email?: string };\n memberships: { resourceType: AccessResourceType; resourceId: string; roleNames: string[] }[];\n}\n\n/** One `user-permissions/me` row. */\ninterface PermissionRecord {\n type: string;\n resourceType: AccessResourceType;\n resourceId: string;\n action: string;\n}\n\nfunction serveAccess(args: {\n ctx: PlatformApiContext;\n request: PlatformRequest;\n type: string;\n id: string;\n rest: string;\n query: URLSearchParams;\n}): Served {\n const { ctx, request, type, id, rest, query } = args;\n const found = capabilitiesForAccessType(type);\n if (found === undefined) throw invalidAccessType(request, type);\n const accessType = found.row.access.type;\n const { unknownId } = found.row.access;\n if (\n unknownId !== undefined &&\n rest !== \"/user-permissions/me/check\" &&\n !ctx.exists({ type: found.resourceType, id })\n ) {\n throw unknownId(request);\n }\n const permissions = ctx.actorAccess()?.[accessKey(accessType, id)]?.permissions ?? [];\n\n switch (rest) {\n case \"/roles\":\n return serveCapability(found.row.access.roles, request, () => ({\n value: page(rolesOn({ row: found.row, id, identities: ctx.identities() })),\n }));\n case \"/users\":\n return serveCapability(found.row.access.users, request, () => ({\n value: page(usersOn({ row: found.row, id, identities: ctx.identities() })),\n }));\n case \"/user-permissions/me\":\n return {\n value: page(\n permissions.map((permission): PermissionRecord => {\n const dot = permission.lastIndexOf(\".\");\n return {\n type: permission.slice(0, dot),\n resourceType: accessType,\n resourceId: id,\n action: permission.slice(dot + 1),\n };\n }),\n ),\n };\n case \"/user-permissions/me/check\": {\n const requested = query.getAll(\"permissions\").flatMap((value) => value.split(\",\"));\n return {\n value: {\n data: Object.fromEntries(requested.map((p) => [p, permissions.includes(p)])),\n },\n };\n }\n default:\n return undefined;\n }\n}\n\n/** Every role a registered member holds on the resource, once each, in canonical order. */\nfunction rolesOn(args: {\n row: ResourceCapabilities;\n id: string;\n identities: TestClientIdentity[];\n}): RoleRecord[] {\n const byName = new Map<string, RoleRecord>();\n for (const identity of args.identities) {\n for (const role of args.row.access.memberRoles({ identity, id: args.id }) ?? []) {\n byName.set(role.name, {\n name: role.name,\n ...(role.title !== undefined ? { title: role.title } : {}),\n ...(role.description !== undefined ? { description: role.description } : {}),\n });\n }\n }\n return [...byName.values()].toSorted((a, b) => a.name.localeCompare(b.name));\n}\n\n/** One row per registered member of the resource, keyed by account-global id. */\nfunction usersOn(args: {\n row: ResourceCapabilities;\n id: string;\n identities: TestClientIdentity[];\n}): AccessUserRecord[] {\n const { row, id, identities } = args;\n const bySanityUserId = new Map<string, AccessUserRecord>();\n for (const identity of identities) {\n const roles = row.access.memberRoles({ identity, id });\n if (roles === undefined) continue;\n const user = identity.globalUser ?? identity.user;\n const record: AccessUserRecord = {\n sanityUserId: user.id,\n profile: {\n displayName: user.name ?? user.id,\n ...(user.email !== undefined ? { email: user.email } : {}),\n },\n memberships: [\n {\n resourceType: row.access.type,\n resourceId: id,\n roleNames: roles.map((role) => role.name).toSorted(),\n },\n ],\n };\n const existing = bySanityUserId.get(record.sanityUserId);\n if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(record)) {\n throw fakeError(\n `request(): two registered identities resolve to user \"${record.sanityUserId}\" on ` +\n `${row.access.type} \"${id}\" with different records. Fix the identity registrations ` +\n \"so duplicate ids carry identical records.\",\n );\n }\n bySanityUserId.set(record.sanityUserId, record);\n }\n return [...bySanityUserId.values()];\n}\n","/**\n * Optional access-control simulation for the in-memory test client.\n *\n * When configured, the TestClient does two things a real `@sanity/client`\n * does over the wire but the bare in-memory client otherwise skips:\n *\n * 1. **Exposes `client.request({ url })`** that serves:\n * - `GET /users/me` → the configured `CurrentUser` — the project-scoped\n * record on a project-hosted handle, `globalUser` on a\n * `useProjectHostname: false` handle (Sanity's global API host,\n * set at creation or via `withConfig`)\n * - `GET <aclPath>` → the configured `Grant[]`\n * - `GET /projects/<projectId>` → the project's members (ids, roles, and robot flags)\n * - `GET /projects/<projectId>/users/<ids>` → project-user records\n * carrying `id` + `sanityUserId`, the bridge between the project\n * and global identity namespaces\n * This is what the engine reads from to resolve `WorkflowAccess`\n * when no override is passed.\n *\n * 2. **Gates writes through the configured grants.** Every mutation\n * (`create`, `createOrReplace`, `createIfNotExists`, `delete`,\n * `patch`, and per-op inside `transaction`) checks whether the\n * configured grants allow the relevant `DocumentValuePermission`\n * on the target document. On denial the client throws a\n * `WriteAccessDeniedError` shaped like Sanity's 403 — the same\n * failure mode a production write would hit, surfaced in tests.\n *\n * When `accessControl` is omitted, the client behaves as before:\n * `request` is undefined, writes are unconditional, and the engine\n * falls through to whatever override the caller (typically the\n * bench's `ALL_ACCESS`) provides.\n *\n * The grant evaluation runs the GROQ filter on a singleton dataset\n * containing the doc being checked — same shape as the engine's\n * `permissions.matchesFilter`, lifted from canvas.\n */\n\nimport type { SanityDocumentLike } from \"@sanity/types\";\nimport { evaluate, parse } from \"groq-js\";\n\nimport { fakeError } from \"./errors.ts\";\nimport { servePlatformRequest, type PlatformApiContext } from \"./platform-api.ts\";\nimport { capabilitiesOf, matchResourcePath, type PlatformHttpError } from \"./resources.ts\";\n\nexport type DocumentValuePermission = \"create\" | \"manage\" | \"read\" | \"update\";\n\nexport interface CurrentUser {\n id: string;\n name?: string;\n email?: string;\n /** @deprecated singular `role` on legacy responses */\n role?: string;\n roles?: { name: string; title?: string; description?: string }[];\n}\n\nexport interface Grant {\n filter: string;\n permissions: DocumentValuePermission[];\n}\n\n/**\n * What the Access API (`/access/<type>/<id>/…`) answers about one actor on\n * one resource: the roles it holds there (served by `…/users` and collected\n * into `…/roles`) and the permissions `…/user-permissions/me` and\n * `…/user-permissions/me/check` report, as `\"<type>.<action>\"` names such\n * as `\"sanity.media-library.read\"`.\n */\nexport interface ResourceAccess {\n roles?: NonNullable<CurrentUser[\"roles\"]>;\n permissions?: string[];\n}\n\nexport interface TestClientAccessControl {\n /** What `client.request({ url: \"/users/me\" })` returns on a project-hosted handle. */\n currentUser: CurrentUser;\n /**\n * What `/users/me` returns on a global-hosted handle\n * (`useProjectHostname: false`, at creation or via `withConfig`).\n * Sanity serves the\n * account-global user there (`g*` id, no roles) while the project host\n * serves the project-scoped record (`p*` id, project roles). Omit to\n * serve `currentUser` from both — the single-id collapse that is also\n * exactly how robot tokens behave in production.\n */\n globalUser?: CurrentUser;\n /** What `client.request({ url: aclPath })` returns. */\n grants: Grant[];\n /**\n * URL path the engine's access resolver hits to fetch grants.\n * Defaults to the handle's resource ACL: `/projects/<projectId>/datasets/<dataset>/acl`,\n * or `<resource base>/acl` on a `resource`-configured handle.\n */\n aclPath?: string;\n /**\n * Access API entries, keyed `\"<access type>:<id>\"` — `\"project:<projectId>\"`,\n * `\"media-library:<id>\"`, `\"canvas:<id>\"`, `\"dashboard:<id>\"`. See {@link ResourceAccess}.\n */\n access?: Record<string, ResourceAccess>;\n}\n\n/**\n * One entry in the token→identity registry (`createTestClient({ identities })`).\n * A handle bound to the entry's token behaves exactly like one configured\n * with `accessControl: { currentUser: projectUserOf(identity, projectId),\n * globalUser, grants }` — `/users/me` serves the record for the handle's\n * project and host, the default ACL path serves `grants`, and reads/writes\n * are gated through them.\n */\nexport interface TestClientIdentity {\n /**\n * The project-scoped user record: what `/users/me` returns on a\n * project-hosted handle, and the id `identity()` resolves to in grant\n * filters and mutation guards. Per-project variants go in\n * `usersByProject`; this is the record for any project without one.\n */\n user: CurrentUser;\n /**\n * The account-global user record, served by `/users/me` on a\n * global-hosted handle (`useProjectHostname: false`, at creation or\n * via `withConfig`).\n * The same person carries a different id per project (`p*`) and one\n * global id (`g*`); registering both lets tests model that split.\n * Omit for single-id principals (robot tokens resolve to one `p-*` id\n * on every host).\n */\n globalUser?: CurrentUser;\n /**\n * Per-project user records, keyed by projectId — the same token\n * resolves to a different project-scoped id in each project, which is\n * what these model. A handle's project falls back to `user` when its\n * projectId has no entry.\n */\n usersByProject?: Record<string, CurrentUser>;\n /** Grants served at the ACL path and enforced on reads/writes. */\n grants: Grant[];\n /**\n * Access API entries, keyed `\"<access type>:<id>\"` (`\"media-library:<id>\"`,\n * `\"canvas:<id>\"`, `\"dashboard:<id>\"`, `\"project:<projectId>\"`). On a\n * non-dataset resource, an entry with `roles` makes the identity a member\n * there. Project membership needs no entry: every identity is a member of\n * every project with its project-scoped roles.\n */\n access?: Record<string, ResourceAccess>;\n}\n\n/**\n * A row served by the project-users directory endpoint\n * (`/projects/<projectId>/users/<id>`), mirroring Sanity's project user\n * retrieval API: the project-scoped `id` alongside its account-global\n * `sanityUserId` — the one sanctioned bridge between the two namespaces.\n */\nexport interface ProjectUserRecord {\n id: string;\n sanityUserId: string;\n displayName: string;\n email?: string;\n}\n\n/**\n * Sanity-style 403 raised when a configured-grants check rejects a\n * write. The thrown object carries the doc id, the attempted\n * permission, and the count of matching grants (for parity with the\n * engine's `permission-denied` disabled-reason).\n */\nexport class WriteAccessDeniedError extends Error {\n readonly documentId: string;\n readonly permission: DocumentValuePermission;\n readonly matchingGrants: number;\n\n constructor(args: {\n documentId: string;\n permission: DocumentValuePermission;\n matchingGrants: number;\n }) {\n super(\n `Sanity (test): permission denied — actor lacks \"${args.permission}\" on document \"${args.documentId}\" ` +\n `(${args.matchingGrants} grant${args.matchingGrants === 1 ? \"\" : \"s\"} matched the document but ` +\n `none included \"${args.permission}\").`,\n );\n this.name = \"WriteAccessDeniedError\";\n this.documentId = args.documentId;\n this.permission = args.permission;\n this.matchingGrants = args.matchingGrants;\n }\n}\n\n/**\n * Predicate variant of `evaluateWriteAccess` — same logic but only\n * returns the boolean. Used by read-gating paths\n * (`fetch`/`getDocument`/`getDocuments`) where the documented Sanity\n * behavior is \"invisible doc → 404\", not \"throw 403\". So callers\n * can filter the dataset before GROQ runs and return `null` from\n * single-doc reads, instead of leaking a denial through the read\n * surface.\n */\nexport async function canAccess(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n grants: Grant[];\n permission: DocumentValuePermission;\n userId: string;\n}): Promise<boolean> {\n const result = await evaluateWriteAccess(args);\n return result.allowed;\n}\n\n// Parsed-filter cache. The GROQ AST is cheap to reuse; only the\n// per-document evaluation needs to re-run.\nconst parsedFilters = new Map<string, ReturnType<typeof parse>>();\n\nasync function matchesFilter(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n filter: string;\n userId: string;\n}): Promise<boolean> {\n const { document, filter, userId } = args;\n if (!parsedFilters.has(filter)) {\n parsedFilters.set(filter, parse(`*[${filter}]`));\n }\n const ast = parsedFilters.get(filter)!;\n const result = await evaluate(ast, {\n dataset: [document as Record<string, unknown>],\n identity: userId,\n });\n const data = await result.get();\n return Array.isArray(data) && data.length === 1;\n}\n\n/**\n * Resolve whether `grants` allow `permission` on `document` for the\n * given user. Same logic the engine uses in `permissions.grantsPermissionOn`\n * (most-permissive wins; multiple grants compose).\n *\n * Returns `{ allowed: boolean, matchingGrants: number }` so callers\n * can build the structured error without re-counting.\n */\nexport async function evaluateWriteAccess(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n grants: Grant[];\n permission: DocumentValuePermission;\n userId: string;\n}): Promise<{ allowed: boolean; matchingGrants: number }> {\n const { document, grants, permission, userId } = args;\n let matching = 0;\n for (const grant of grants) {\n if (\n await matchesFilter({\n document,\n filter: grant.filter,\n userId,\n })\n ) {\n matching++;\n if (grant.permissions.includes(permission)) {\n return { allowed: true, matchingGrants: matching };\n }\n }\n }\n return { allowed: false, matchingGrants: matching };\n}\n\n/**\n * Throw a `WriteAccessDeniedError` if `grants` don't permit the\n * supplied `permission` on `document`. No-op when allowed. Use this\n * from every mutation entrypoint that wants to simulate Sanity's\n * write boundary.\n */\nexport async function enforceWriteAccess(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n grants: Grant[];\n permission: DocumentValuePermission;\n userId: string;\n}): Promise<void> {\n const { document, grants, permission, userId } = args;\n const { allowed, matchingGrants } = await evaluateWriteAccess({\n document,\n grants,\n permission,\n userId,\n });\n if (allowed) return;\n if (typeof document._id !== \"string\") {\n throw new WriteAccessDeniedError({\n documentId: \"(unknown)\",\n permission,\n matchingGrants,\n });\n }\n throw new WriteAccessDeniedError({\n documentId: document._id,\n permission,\n matchingGrants,\n });\n}\n\n/**\n * Default ACL path the engine pulls grants from. Mirrors Sanity's\n * project/dataset ACL endpoint. Callers can override via\n * `accessControl.aclPath`.\n */\nexport function defaultAclPath(projectId: string, dataset: string): string {\n return `/projects/${projectId}/datasets/${dataset}/acl`;\n}\n\n/** The platform's refusal for `GET <path>` when it is the ACL of a resource type without one. */\nfunction refusedAcl(path: string): PlatformHttpError | undefined {\n const matched = matchResourcePath(path);\n if (matched?.rest !== \"/acl\") return undefined;\n const { acl } = capabilitiesOf(matched.resource.type);\n return acl === \"served\" ? undefined : acl({ method: \"GET\", path });\n}\n\n/** Everything {@link makeRequestImpl} needs from the owning handle. */\nexport interface RequestImplContext {\n /**\n * Active access-control resolver. A getter, not a value — token-bound\n * handles resolve their identity from the shared registry on every\n * call, so a re-registered identity (`addIdentity` upsert) takes\n * effect on already-minted siblings.\n */\n getAccess: () => TestClientAccessControl;\n projectId: string;\n /**\n * Whether this handle addresses the project host (default) or the\n * global API host (`useProjectHostname: false`, at creation or via\n * `withConfig`).\n * Decides which user record `/users/me` serves.\n */\n useProjectHostname: boolean;\n /**\n * All registered identities, for serving the project-users directory\n * endpoint. A getter so `addIdentity` upserts are visible.\n */\n listIdentities: () => TestClientIdentity[];\n /** See {@link CreateTestClientOptions.organizationId}; added to `/projects/<projectId>`. */\n organizationId: string | undefined;\n /** History, ACL, resource lookup and Access API routing; see `./platform-api.ts`. */\n platform: PlatformApiContext;\n}\n\n/** The project-scoped record a registry identity resolves to in `projectId`. */\nexport function projectUserOf(identity: TestClientIdentity, projectId: string): CurrentUser {\n return identity.usersByProject?.[projectId] ?? identity.user;\n}\n\n/**\n * Deduplicate tokens that resolve to one project user. Callers supply served\n * records in canonical form so stringify equality represents semantic\n * equality; divergent records fail instead of producing a last-wins answer.\n */\nfunction consistentProjectRecords<RecordType extends { id: string }>(args: {\n projectId: string;\n records: readonly RecordType[];\n}): Map<string, RecordType> {\n const byId = new Map<string, RecordType>();\n for (const record of args.records) {\n const existing = byId.get(record.id);\n if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(record)) {\n throw fakeError(\n `request(): two registered identities resolve to project user \"${record.id}\" in project ` +\n `\"${args.projectId}\" with different records. The project records cannot be served ` +\n `unambiguously — fix the identity registrations so duplicate ids carry identical records.`,\n );\n }\n byId.set(record.id, record);\n }\n return byId;\n}\n\n/** Normalize role key and array order for stable serving and duplicate comparison. */\nfunction canonicalProjectRoles(roles: NonNullable<CurrentUser[\"roles\"]>) {\n return roles\n .map((role) => ({\n name: role.name,\n ...(role.title !== undefined ? { title: role.title } : {}),\n ...(role.description !== undefined ? { description: role.description } : {}),\n }))\n .toSorted((left, right) => {\n const leftKey = JSON.stringify(left);\n const rightKey = JSON.stringify(right);\n if (leftKey < rightKey) return -1;\n if (leftKey > rightKey) return 1;\n return 0;\n });\n}\n\n/**\n * Serve `/projects/<projectId>/users/<ids>` from the identity registry —\n * the fake's mirror of Sanity's project user retrieval API, the bridge\n * between project-scoped ids and `sanityUserId`. Single id → one record;\n * comma-separated ids → array. Unknown single id throws with\n * `statusCode: 404`, like production; unknown ids in a list are omitted.\n */\nfunction serveProjectUsers(args: {\n requestedIds: string;\n pathProjectId: string;\n identities: TestClientIdentity[];\n}): ProjectUserRecord | ProjectUserRecord[] {\n const { requestedIds, pathProjectId, identities } = args;\n const toRecord = (identity: TestClientIdentity): ProjectUserRecord => {\n const projectUser = projectUserOf(identity, pathProjectId);\n return {\n id: projectUser.id,\n sanityUserId: identity.globalUser?.id ?? projectUser.id,\n displayName: projectUser.name ?? projectUser.id,\n ...(projectUser.email !== undefined ? { email: projectUser.email } : {}),\n };\n };\n const byId = consistentProjectRecords({\n projectId: pathProjectId,\n records: identities.map(toRecord),\n });\n const ids = requestedIds.split(\",\");\n const [firstId] = ids;\n if (ids.length === 1 && firstId !== undefined) {\n const record = byId.get(firstId);\n if (record === undefined) {\n throw Object.assign(\n fakeError(\n `request(): no registered identity resolves to project user \"${firstId}\" in project ` +\n `\"${pathProjectId}\". Register it via createTestClient({ identities }) or addIdentity().`,\n ),\n { statusCode: 404 },\n );\n }\n return record;\n }\n return ids.flatMap((id) => {\n const record = byId.get(id);\n return record === undefined ? [] : [record];\n });\n}\n\nconst PROJECT_USERS_PATH = /^\\/projects\\/([^/]+)\\/users\\/([^/]+)$/;\n\nconst PROJECT_PATH = /^\\/projects\\/([^/]+)$/;\n\n/** One row of the `/projects/<projectId>` members response. */\nexport interface ProjectMemberRecord {\n id: string;\n isRobot?: boolean;\n /** Project-scoped roles in canonical order, independent of registration order. */\n roles?: NonNullable<CurrentUser[\"roles\"]>;\n}\n\n/**\n * Serve `/projects/<projectId>` from the identity registry — the members\n * half of Sanity's project API, which member-list joins read\n * (`members[].id` carries the project-scoped principal id and `roles` carries\n * its project roles; robot principals are flagged `isRobot`, matching their\n * production `p-…` form). One row per distinct resolved project user, however\n * many tokens map to that person.\n */\nfunction serveProject(args: {\n pathProjectId: string;\n identities: TestClientIdentity[];\n organizationId: string | undefined;\n}): {\n organizationId?: string;\n members: ProjectMemberRecord[];\n} {\n const records = args.identities.map((identity) => {\n const projectUser = projectUserOf(identity, args.pathProjectId);\n return {\n id: projectUser.id,\n ...(projectUser.id.startsWith(\"p-\") ? { isRobot: true } : {}),\n ...(projectUser.roles !== undefined\n ? { roles: canonicalProjectRoles(projectUser.roles) }\n : {}),\n } satisfies ProjectMemberRecord;\n });\n const members = consistentProjectRecords({ projectId: args.pathProjectId, records });\n return {\n ...(args.organizationId !== undefined ? { organizationId: args.organizationId } : {}),\n members: [...members.values()],\n };\n}\n\n/**\n * Internal helper: turn the active access-control config into the bound\n * `request` function the client exposes.\n */\nexport function makeRequestImpl(\n ctx: RequestImplContext,\n): <T>(opts: {\n url?: string;\n uri?: string;\n signal?: AbortSignal;\n tag?: string | null;\n}) => Promise<T> {\n const { getAccess, useProjectHostname, listIdentities, organizationId, platform } = ctx;\n return async <T>(opts: {\n url?: string;\n uri?: string;\n signal?: AbortSignal;\n tag?: string | null;\n }): Promise<T> => {\n const accessControl = getAccess();\n // `@sanity/client` distinguishes `uri` (project-scoped, e.g.\n // `/users/me`) from `url` (raw). Treat both identically for the mock —\n // host selection rides the handle's `useProjectHostname`, as in\n // production, not the option name.\n const path = opts.uri ?? opts.url;\n if (path === \"/users/me\") {\n // The global host serves the account-global user; the project host\n // serves the project-scoped record. Absent `globalUser` both serve\n // the same record — the single-id shape robot tokens have in\n // production.\n const user = useProjectHostname\n ? accessControl.currentUser\n : (accessControl.globalUser ?? accessControl.currentUser);\n return user as unknown as T;\n }\n // An explicit `aclPath` aliases the grants, but never past a route the platform does not mount.\n if (path !== undefined && path === accessControl.aclPath && refusedAcl(path) === undefined) {\n return accessControl.grants as unknown as T;\n }\n const projectUsers = path?.match(PROJECT_USERS_PATH);\n const [, pathProjectId, requestedIds] = projectUsers ?? [];\n if (pathProjectId !== undefined && requestedIds !== undefined) {\n return serveProjectUsers({\n requestedIds,\n pathProjectId,\n identities: listIdentities(),\n }) as unknown as T;\n }\n const project = path?.match(PROJECT_PATH);\n const [, membersProjectId] = project ?? [];\n if (membersProjectId !== undefined) {\n return serveProject({\n pathProjectId: membersProjectId,\n identities: listIdentities(),\n organizationId,\n }) as unknown as T;\n }\n const served = path === undefined ? undefined : await servePlatformRequest(platform, path);\n if (served !== undefined) return served.value as T;\n throw fakeError(\n `request(): no handler for path \"${path}\". With access control configured, the fake serves ` +\n `\"/users/me\", the handle's ACL (\"<data base>/acl\"), \"/projects/<projectId>\" (members), ` +\n `\"/projects/<projectId>/users/<ids>\", the History API (client.getDataUrl(\"history\", …)), ` +\n `resource lookup (\"/media-libraries/<id>\", \"/canvases/<id>\", \"/dashboards/<id>\") and the ` +\n `Access API (\"/access/<type>/<id>/roles | users | user-permissions/me[/check]\"). For any ` +\n `other endpoint, register a stub (client.stub(\"request\", fn)) or intercept the HTTP call ` +\n `with a tool like MSW.`,\n );\n };\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\n\n/** The Content Lake mutation actions that a {@link MutationGuard} can match. */\nexport type MutationGuardAction = \"create\" | \"update\" | \"delete\";\n\nexport interface MutationGuardDenial {\n guardID: string;\n name?: string;\n}\n\nexport interface GuardDeniedResponse {\n body: {\n error: {\n type: \"guardDeniedError\";\n denials: MutationGuardDenial[];\n };\n };\n url: string;\n method: \"POST\";\n headers: Record<string, string>;\n statusCode: 403;\n statusMessage: \"Forbidden\";\n}\n\n/** HTTP-shaped guard rejection matching the error exposed by `@sanity/client`. */\nexport class GuardDeniedError extends Error {\n readonly statusCode = 403;\n readonly type = \"guardDeniedError\";\n readonly denials: MutationGuardDenial[];\n readonly details: GuardDeniedResponse[\"body\"][\"error\"];\n readonly response: GuardDeniedResponse;\n\n constructor(denials: readonly MutationGuardDenial[]) {\n const unique = uniqueDenials(denials);\n super(`Mutation denied by guard(s) [${unique.map((denial) => denial.guardID).join(\", \")}]`);\n this.name = \"GuardDeniedError\";\n this.denials = unique;\n this.details = { type: this.type, denials: unique };\n this.response = {\n body: { error: this.details },\n url: \"memory://client-fake-for-test/data/mutate\",\n method: \"POST\",\n headers: {},\n statusCode: this.statusCode,\n statusMessage: \"Forbidden\",\n };\n }\n}\n\nfunction uniqueDenials(denials: readonly MutationGuardDenial[]): MutationGuardDenial[] {\n const seen = new Set<string>();\n return denials.filter((denial) => {\n if (seen.has(denial.guardID)) return false;\n seen.add(denial.guardID);\n return true;\n });\n}\n\n/** The before/final-after image of one net document change in a commit. */\nexport interface MutationGuardContext {\n action: MutationGuardAction;\n /** Original live store image. Treat it as read-only; it is not a copy. */\n before: Readonly<SanityDocumentLike & { _id: string; _type: string }> | null;\n /** Final live store image. Treat it as read-only; it is not a copy. */\n after: Readonly<SanityDocumentLike & { _id: string; _type: string }> | null;\n /** Acting project identity, or `undefined` on an identity-free client. */\n identity: string | undefined;\n}\n\n/**\n * Evaluates one net document change and returns every denying guard. Returning\n * `void` allows observe-only hooks. A thrown value is a callback failure and\n * propagates unchanged; only returned entries become {@link GuardDeniedError}.\n */\nexport type MutationGuard = (\n context: MutationGuardContext,\n) => readonly MutationGuardDenial[] | void | Promise<readonly MutationGuardDenial[] | void>;\n\nexport interface MutationImages {\n before: SanityDocument | undefined;\n after: SanityDocument | undefined;\n}\n\nexport type MutationGuardImageContext = Omit<MutationGuardContext, \"identity\">;\n\n/** Derive the single lake action represented by a document's net commit image. */\nexport function mutationContext(images: MutationImages): MutationGuardImageContext | undefined {\n const { before, after } = images;\n // TestStore preserves object identity until a write replaces the document.\n // Equal references therefore mean an ID was remembered but never changed.\n if (before === after) return undefined;\n if (before === undefined) return { action: \"create\", before: null, after: after ?? null };\n if (after === undefined) return { action: \"delete\", before, after: null };\n return { action: \"update\", before, after };\n}\n\n/** Derive guard contexts for an ordered collection of net document images. */\nexport function mutationContexts(images: Iterable<MutationImages>): MutationGuardImageContext[] {\n return [...images].flatMap((image) => {\n const context = mutationContext(image);\n return context === undefined ? [] : [context];\n });\n}\n\n/** Evaluate every net context before rejecting once with all returned denials. */\nexport async function enforceMutationGuards(\n guard: MutationGuard | undefined,\n contexts: readonly MutationGuardContext[],\n): Promise<void> {\n if (guard === undefined) return;\n const denials: MutationGuardDenial[] = [];\n for (const context of contexts) {\n const denied = await guard(context);\n if (denied !== undefined) denials.push(...denied);\n }\n if (denials.length > 0) throw new GuardDeniedError(denials);\n}\n","import type { SanityDocument } from \"@sanity/types\";\nimport { fakeError } from \"./errors.ts\";\n\n/** Human-readable type of a value, for \"expected an array but got …\" errors. */\nfunction describeType(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n return `a ${typeof value}`;\n}\n\n/**\n * Patch operations the test client supports.\n *\n * Subset of `@sanity/client`'s PatchOperations. Each entry is one\n * mutation step applied in order at commit time.\n */\nexport type PatchOp =\n | { kind: \"set\"; props: Record<string, unknown> }\n | { kind: \"setIfMissing\"; props: Record<string, unknown> }\n | { kind: \"unset\"; paths: string[] }\n | { kind: \"inc\"; props: Record<string, number> }\n | { kind: \"dec\"; props: Record<string, number> }\n | {\n kind: \"insert\";\n position: \"before\" | \"after\" | \"replace\";\n anchor: string;\n items: unknown[];\n };\n\nexport interface PatchHandle {\n set: (props: Record<string, unknown>) => PatchHandle;\n setIfMissing: (props: Record<string, unknown>) => PatchHandle;\n unset: (paths: string[]) => PatchHandle;\n inc: (props: Record<string, number>) => PatchHandle;\n dec: (props: Record<string, number>) => PatchHandle;\n insert: (\n position: \"before\" | \"after\" | \"replace\",\n anchor: string,\n items: unknown[],\n ) => PatchHandle;\n ifRevisionId: (rev: string) => PatchHandle;\n commit: (options?: PatchCommitOptions) => Promise<SanityDocument>;\n /** Test-only introspection — not on real Patch */\n toJSON: () => { id: string; ops: PatchOp[]; ifRev?: string };\n}\n\n/** Options for `PatchHandle.commit()`. Only `tag` is meaningful to the fake. */\nexport interface PatchCommitOptions {\n /** Request tag, recorded on the mutation log entry (`null` = explicit suppression). */\n tag?: string | null;\n /** Accepted for signature parity; not simulated. */\n visibility?: \"sync\" | \"async\" | \"deferred\";\n}\n\nexport interface PatchExecutor {\n apply: (\n documentId: string,\n ops: PatchOp[],\n ifRev: string | undefined,\n options: PatchCommitOptions | undefined,\n ) => Promise<SanityDocument>;\n}\n\nexport function createPatch(documentId: string, executor: PatchExecutor): PatchHandle {\n const ops: PatchOp[] = [];\n let ifRev: string | undefined;\n\n const handle: PatchHandle = {\n set: (props) => {\n ops.push({ kind: \"set\", props });\n return handle;\n },\n setIfMissing: (props) => {\n ops.push({ kind: \"setIfMissing\", props });\n return handle;\n },\n unset: (paths) => {\n ops.push({ kind: \"unset\", paths });\n return handle;\n },\n inc: (props) => {\n ops.push({ kind: \"inc\", props });\n return handle;\n },\n dec: (props) => {\n ops.push({ kind: \"dec\", props });\n return handle;\n },\n insert: (position, anchor, items) => {\n ops.push({ kind: \"insert\", position, anchor, items });\n return handle;\n },\n ifRevisionId: (rev) => {\n ifRev = rev;\n return handle;\n },\n commit: (options) => executor.apply(documentId, ops, ifRev, options),\n toJSON: () => ({\n id: documentId,\n ops: [...ops],\n ...(ifRev !== undefined ? { ifRev } : {}),\n }),\n };\n\n return handle;\n}\n\n/**\n * Apply an ordered list of patch ops to a document, returning the new doc.\n *\n * The path semantics are deliberately a small subset:\n *\n * - **Dotted paths** (`a.b.c`) for nested object fields.\n * - **Array index** (`a.b[0]`) for positional access into arrays.\n * - **Keyed lookup** (`a.b[_key==\"x\"]`) for finding an array entry by `_key`.\n *\n * Anything more exotic (recursive `..`, GROQ filter expressions, multi-match)\n * is intentionally out of scope and will throw — the workflow engine only\n * needs the three forms above.\n */\nexport function applyPatchOps(doc: SanityDocument, ops: PatchOp[]): SanityDocument {\n let next: SanityDocument = structuredClone(doc);\n for (const op of ops) {\n next = applyOne(next, op);\n }\n return next;\n}\n\nfunction applyOne(doc: SanityDocument, op: PatchOp): SanityDocument {\n switch (op.kind) {\n case \"set\":\n for (const [path, value] of Object.entries(op.props)) {\n setAtPath(doc, path, value);\n }\n return doc;\n case \"setIfMissing\":\n for (const [path, value] of Object.entries(op.props)) {\n if (getAtPath(doc, path) === undefined) {\n setAtPath(doc, path, value);\n }\n }\n return doc;\n case \"unset\":\n for (const path of op.paths) {\n unsetAtPath(doc, path);\n }\n return doc;\n case \"inc\":\n for (const [path, delta] of Object.entries(op.props)) {\n const current = getAtPath(doc, path);\n const base = typeof current === \"number\" ? current : 0;\n setAtPath(doc, path, base + delta);\n }\n return doc;\n case \"dec\":\n for (const [path, delta] of Object.entries(op.props)) {\n const current = getAtPath(doc, path);\n const base = typeof current === \"number\" ? current : 0;\n setAtPath(doc, path, base - delta);\n }\n return doc;\n case \"insert\": {\n insertIntoArray(doc, op.position, op.anchor, op.items);\n return doc;\n }\n }\n}\n\ninterface PathSegment {\n key: string;\n // Optional array selector. Either a numeric index or a key=value match.\n selector?: { kind: \"index\"; index: number } | { kind: \"keyed\"; field: string; value: string };\n}\n\nfunction parsePath(path: string): PathSegment[] {\n // Examples:\n // \"currentStageId\"\n // \"taskStatus[0].status\"\n // \"taskStatus[_key==\\\"approve\\\"].status\"\n // \"context[key==\\\"releaseId\\\"].value\"\n const segments: PathSegment[] = [];\n for (const raw of path.split(\".\")) {\n const match = /^([A-Za-z_$][\\w$]*)(?:\\[(.+)\\])?$/.exec(raw);\n if (!match) {\n throw fakeError(\n `Unsupported patch path segment \"${raw}\" (in path \"${path}\"). This fake supports dotted ` +\n `field access (\"a.b.c\"), array index (\"items[0]\"), and keyed lookup (\\`items[_key==\"x\"]\\`). ` +\n `Recursive descent (\"..\") and GROQ filter expressions are not supported.`,\n );\n }\n const key = match[1] as string;\n const selectorRaw = match[2];\n if (selectorRaw === undefined) {\n segments.push({ key });\n continue;\n }\n const indexMatch = /^(\\d+)$/.exec(selectorRaw);\n if (indexMatch) {\n segments.push({ key, selector: { kind: \"index\", index: Number(indexMatch[1]) } });\n continue;\n }\n const keyedMatch = /^([A-Za-z_$][\\w$]*)\\s*==\\s*\"([^\"]*)\"$/.exec(selectorRaw);\n if (keyedMatch) {\n segments.push({\n key,\n selector: { kind: \"keyed\", field: keyedMatch[1] as string, value: keyedMatch[2] as string },\n });\n continue;\n }\n throw fakeError(\n `Unsupported array selector \"[${selectorRaw}]\" in patch path \"${path}\". Supported selectors ` +\n `are a numeric index (e.g. \"[0]\") and a single keyed match (e.g. \\`[_key==\"abc\"]\\`). ` +\n `Multi-key matches and arbitrary GROQ filters are not supported.`,\n );\n }\n return segments;\n}\n\nfunction resolveSelector(array: unknown[], selector: NonNullable<PathSegment[\"selector\"]>): number {\n if (selector.kind === \"index\") return selector.index;\n const idx = array.findIndex(\n (entry) =>\n typeof entry === \"object\" &&\n entry !== null &&\n (entry as Record<string, unknown>)[selector.field] === selector.value,\n );\n if (idx === -1) {\n throw fakeError(\n `Patch path matched no array entry where ${selector.field}==\"${selector.value}\". The array has ` +\n `${array.length} item(s); check the key exists (and that you're patching the right document).`,\n );\n }\n return idx;\n}\n\nfunction getAtPath(doc: SanityDocument, path: string): unknown {\n const segments = parsePath(path);\n let cursor: unknown = doc;\n for (const seg of segments) {\n if (cursor === undefined || cursor === null) return undefined;\n cursor = (cursor as Record<string, unknown>)[seg.key];\n if (seg.selector !== undefined) {\n if (!Array.isArray(cursor)) return undefined;\n const idx = resolveSelector(cursor, seg.selector);\n cursor = cursor[idx];\n }\n }\n return cursor;\n}\n\nfunction setAtPath(doc: SanityDocument, path: string, value: unknown): void {\n const segments = parsePath(path);\n if (segments.length === 0)\n throw fakeError(`Empty patch path — a set/setIfMissing op needs a target field.`);\n let cursor: Record<string, unknown> = doc as unknown as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i] as PathSegment;\n let next = cursor[seg.key];\n if (next === undefined) {\n next = seg.selector?.kind === \"index\" || seg.selector?.kind === \"keyed\" ? [] : {};\n cursor[seg.key] = next;\n }\n if (seg.selector !== undefined) {\n if (!Array.isArray(next)) {\n throw fakeError(\n `Patch path \"${path}\" applies an array selector to field \"${seg.key}\", but that field ` +\n `holds ${describeType(next)}, not an array.`,\n );\n }\n const idx = resolveSelector(next, seg.selector);\n cursor = next[idx] as Record<string, unknown>;\n } else {\n cursor = next as Record<string, unknown>;\n }\n }\n const last = segments[segments.length - 1] as PathSegment;\n if (last.selector !== undefined) {\n const arr = cursor[last.key];\n if (!Array.isArray(arr)) {\n throw fakeError(\n `Patch path \"${path}\" applies an array selector to field \"${last.key}\", but that field ` +\n `holds ${describeType(arr)}, not an array.`,\n );\n }\n const idx = resolveSelector(arr, last.selector);\n arr[idx] = value;\n } else {\n cursor[last.key] = value;\n }\n}\n\nfunction unsetAtPath(doc: SanityDocument, path: string): void {\n const segments = parsePath(path);\n if (segments.length === 0) return;\n let cursor: Record<string, unknown> = doc as unknown as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i] as PathSegment;\n let next = cursor[seg.key];\n if (next === undefined || next === null) return;\n if (seg.selector !== undefined) {\n if (!Array.isArray(next)) return;\n const idx = resolveSelector(next, seg.selector);\n next = next[idx];\n }\n cursor = next as Record<string, unknown>;\n }\n const last = segments[segments.length - 1] as PathSegment;\n if (last.selector !== undefined) {\n const arr = cursor[last.key];\n if (!Array.isArray(arr)) return;\n const idx = resolveSelector(arr, last.selector);\n arr.splice(idx, 1);\n } else {\n delete cursor[last.key];\n }\n}\n\nfunction insertIntoArray(\n doc: SanityDocument,\n position: \"before\" | \"after\" | \"replace\",\n anchor: string,\n items: unknown[],\n): void {\n // anchor is a path that must terminate at an array entry (selector required).\n // For \"replace\", we replace the entry; for \"before\"/\"after\", we insert\n // adjacent to it.\n const segments = parsePath(anchor);\n if (segments.length === 0)\n throw fakeError(`Empty insert anchor — insert() needs an array path to anchor against.`);\n const last = segments[segments.length - 1] as PathSegment;\n if (last.selector === undefined) {\n throw fakeError(\n `Insert anchor \"${anchor}\" must end with an array selector (an index like \"[0]\" or a keyed ` +\n `match like \\`[_key==\"x\"]\\`) so the fake knows where in the array to insert.`,\n );\n }\n\n let cursor: Record<string, unknown> = doc as unknown as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i] as PathSegment;\n const next = cursor[seg.key];\n if (next === undefined || next === null) {\n throw fakeError(\n `Insert anchor \"${anchor}\" traverses field \"${seg.key}\", which is missing on the document. ` +\n `setIfMissing the parent first, or anchor against an existing array.`,\n );\n }\n if (seg.selector !== undefined) {\n if (!Array.isArray(next)) {\n throw fakeError(\n `Insert anchor \"${anchor}\" applies an array selector to field \"${seg.key}\", but it holds ` +\n `${describeType(next)}, not an array.`,\n );\n }\n cursor = next[resolveSelector(next, seg.selector)] as Record<string, unknown>;\n } else {\n cursor = next as Record<string, unknown>;\n }\n }\n const arr = cursor[last.key];\n if (!Array.isArray(arr)) {\n throw fakeError(\n `Insert anchor \"${anchor}\" targets field \"${last.key}\", but it holds ${describeType(arr)}, ` +\n `not an array.`,\n );\n }\n const idx = resolveSelector(arr, last.selector);\n if (position === \"replace\") {\n arr.splice(idx, 1, ...items);\n } else if (position === \"before\") {\n arr.splice(idx, 0, ...items);\n } else {\n arr.splice(idx + 1, 0, ...items);\n }\n}\n","/**\n * Perspective layering — read-side filtering that mirrors Sanity's\n * `perspective` query/client option.\n *\n * The store always holds raw documents. Each fetch builds a *view* of\n * the store filtered by the active perspective before passing the\n * dataset to `groq-js`.\n *\n * Reference: https://www.sanity.io/docs/content-lake/perspectives\n */\n\nimport type { SanityDocument } from \"@sanity/types\";\n\n/**\n * `raw`, `published`, `drafts`, a release stack, or a single release name\n * (`\"rSpring\"`) — which the Content Lake reads as a one-release stack.\n */\nexport type Perspective = \"raw\" | \"published\" | \"drafts\" | (string & {}) | string[];\n\nconst DRAFTS_PREFIX = \"drafts.\";\nconst VERSIONS_PREFIX = \"versions.\";\nconst SYSTEM_PREFIX = \"_.\";\nconst RELEASE_DOC_PREFIX = \"_.releases.\";\n\n/** Strip `drafts.` / `versions.<name>.` prefix to recover the published id. */\nexport function publishedIdOf(id: string): string {\n if (id.startsWith(DRAFTS_PREFIX)) return id.slice(DRAFTS_PREFIX.length);\n if (id.startsWith(VERSIONS_PREFIX)) {\n // versions.<name>.<id> — split off the prefix + name\n const rest = id.slice(VERSIONS_PREFIX.length);\n const dot = rest.indexOf(\".\");\n if (dot === -1) return rest;\n return rest.slice(dot + 1);\n }\n return id;\n}\n\n/** Extract the release name from a `versions.<name>.<id>` id, else undefined. */\nexport function releaseNameOf(id: string): string | undefined {\n if (!id.startsWith(VERSIONS_PREFIX)) return undefined;\n const rest = id.slice(VERSIONS_PREFIX.length);\n const dot = rest.indexOf(\".\");\n if (dot === -1) return undefined;\n return rest.slice(0, dot);\n}\n\nexport const isDraftId = (id: string): boolean => id.startsWith(DRAFTS_PREFIX);\nexport const isVersionId = (id: string): boolean => id.startsWith(VERSIONS_PREFIX);\nexport const isSystemId = (id: string): boolean => id.startsWith(SYSTEM_PREFIX);\nexport const isReleaseDocId = (id: string): boolean => id.startsWith(RELEASE_DOC_PREFIX);\n\n/**\n * Apply a perspective to a raw set of documents and return the view\n * groq-js should evaluate against.\n *\n * For non-raw perspectives, each output document for a non-system id\n * gets `_id` projected to its published id and `_originalId` carrying\n * the source id (matching the wire shape of the real Content Lake).\n */\nexport function applyPerspective(\n docs: readonly SanityDocument[],\n requested: Perspective,\n): SanityDocument[] {\n const perspective = normalizePerspective(requested);\n if (perspective === \"raw\") return [...docs];\n\n if (perspective === \"published\") {\n return docs.filter((d) => !isDraftId(d._id) && !isVersionId(d._id) && !isSystemId(d._id));\n }\n\n if (perspective === \"drafts\") {\n return mergeStack(docs, [], { includeDrafts: true, includeReleaseDocs: false });\n }\n\n // string[] — release stack with optional \"drafts\" entry.\n const stack: string[] = [];\n let includeDrafts = false;\n for (const entry of perspective) {\n if (entry === \"drafts\") {\n includeDrafts = true;\n } else {\n stack.push(entry);\n }\n }\n return mergeStack(docs, stack, { includeDrafts, includeReleaseDocs: true });\n}\n\n/**\n * The perspective as the Content Lake receives it: `previewDrafts` is the\n * deprecated name of `drafts`, and any other string is a comma-joined\n * stack (`\"rA\"`, `\"drafts,rA\"`), the form `@sanity/client` sends.\n */\nfunction normalizePerspective(perspective: Perspective): \"raw\" | \"published\" | \"drafts\" | string[] {\n if (Array.isArray(perspective)) return perspective;\n switch (perspective) {\n case \"raw\":\n return \"raw\";\n case \"published\":\n return \"published\";\n case \"drafts\":\n case \"previewDrafts\":\n return \"drafts\";\n default:\n return perspective.split(\",\");\n }\n}\n\ninterface MergeOptions {\n includeDrafts: boolean;\n includeReleaseDocs: boolean;\n}\n\n/**\n * Walk the raw docs once, group by published id, then for each group\n * pick the highest-priority source according to `releaseStack` and\n * `includeDrafts`. Project the chosen doc to the published id.\n */\nfunction mergeStack(\n docs: readonly SanityDocument[],\n releaseStack: readonly string[],\n options: MergeOptions,\n): SanityDocument[] {\n // Group docs by published id. Track which sources exist.\n type Group = {\n published?: SanityDocument;\n draft?: SanityDocument;\n versions: Map<string, SanityDocument>;\n };\n const groups = new Map<string, Group>();\n const systemReleases: SanityDocument[] = [];\n\n for (const doc of docs) {\n if (isReleaseDocId(doc._id)) {\n if (options.includeReleaseDocs) systemReleases.push(doc);\n continue;\n }\n if (isSystemId(doc._id)) continue; // other system docs are hidden\n const pubId = publishedIdOf(doc._id);\n let group = groups.get(pubId);\n if (group === undefined) {\n group = { versions: new Map() };\n groups.set(pubId, group);\n }\n if (isDraftId(doc._id)) {\n group.draft = doc;\n } else if (isVersionId(doc._id)) {\n const releaseName = releaseNameOf(doc._id);\n if (releaseName !== undefined) group.versions.set(releaseName, doc);\n } else {\n group.published = doc;\n }\n }\n\n const out: SanityDocument[] = [];\n for (const [pubId, group] of groups) {\n // Walk the release stack first, then drafts (if enabled, in its\n // slot — here we model \"drafts after the stack\" matching the\n // typical `[r1, r2, \"drafts\"]` shape), then fall through to\n // published.\n let chosen: SanityDocument | undefined;\n for (const releaseName of releaseStack) {\n const v = group.versions.get(releaseName);\n if (v !== undefined) {\n // version.unpublish stub: hide the doc entirely\n if (isUnpublishStub(v)) {\n chosen = undefined;\n break;\n }\n chosen = v;\n break;\n }\n }\n if (chosen === undefined && options.includeDrafts && group.draft !== undefined) {\n chosen = group.draft;\n }\n if (chosen === undefined && group.published !== undefined) {\n // skip if any release in the stack carried an unpublish stub\n const blocked = releaseStack.some((name) => {\n const v = group.versions.get(name);\n return v !== undefined && isUnpublishStub(v);\n });\n if (!blocked) chosen = group.published;\n }\n if (chosen !== undefined) {\n out.push(projectToPublishedId(chosen, pubId));\n }\n }\n\n return [...out, ...systemReleases];\n}\n\nfunction isUnpublishStub(doc: SanityDocument): boolean {\n const sys = (doc as { _system?: { delete?: boolean } })._system;\n return sys?.delete === true;\n}\n\nfunction projectToPublishedId(doc: SanityDocument, pubId: string): SanityDocument {\n if (doc._id === pubId) return doc;\n return { ...doc, _id: pubId, _originalId: doc._id } as SanityDocument;\n}\n\n/**\n * `getDocument(id)` resolution under a perspective. Returns the chosen\n * source's *raw* doc (not projected), so the caller can decide whether\n * to expose `_originalId` etc. For perspective=\"raw\", this is just\n * `docs.find(d => d._id === id)`.\n */\nexport function resolveDocument(\n docs: readonly SanityDocument[],\n id: string,\n perspective: Perspective,\n): SanityDocument | undefined {\n if (perspective === \"raw\") {\n return docs.find((d) => d._id === id);\n }\n // Match the rules of applyPerspective: resolve to the same chosen\n // doc but key by the requested id (which the caller treated as a\n // published id under non-raw perspectives).\n const view = applyPerspective(docs, perspective);\n return view.find((d) => d._id === id);\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\nimport { fakeError } from \"./errors.ts\";\n\n/**\n * In-memory document store backing a TestClient. Closure-scoped — created\n * fresh per test, no global state.\n *\n * The shape mirrors what `groq-js` evaluates against: an array of\n * documents. Internally we keep a Map by `_id` for O(1) lookup, and\n * project to an array on each query.\n */\nexport interface TestStore {\n /** Replace the entire dataset with the given documents. Existing docs are dropped. */\n seed: (documents: SanityDocumentLike[]) => void;\n /** Add documents without dropping existing ones. Conflicts (same _id) overwrite. */\n add: (documents: SanityDocumentLike[]) => void;\n /** Remove all documents. */\n reset: () => void;\n /** All documents as an array, in insertion order. */\n all: () => SanityDocument[];\n /** Get one document by _id, or undefined. */\n get: (id: string) => SanityDocument | undefined;\n /** Insert a fully-formed document by _id. Replaces if it already exists. */\n put: (doc: SanityDocument) => void;\n /** Remove a document by _id. Returns the removed doc or undefined. */\n remove: (id: string) => SanityDocument | undefined;\n /** Snapshot of all docs, useful for assertions. */\n snapshot: () => readonly SanityDocument[];\n}\n\n/**\n * Create a fresh, isolated store. The returned object closes over a private\n * Map; two stores created from two calls do not see each other.\n */\nexport function createTestStore(initial?: SanityDocumentLike[]): TestStore {\n const docs = new Map<string, SanityDocument>();\n\n const promote = (doc: SanityDocumentLike): SanityDocument => {\n if (typeof doc._id !== \"string\" || doc._id.length === 0) {\n throw fakeError(\n `Cannot seed/store a document without a non-empty string \\`_id\\` (got ${JSON.stringify(doc._id)}). ` +\n `This fake never auto-generates ids — give every document an explicit _id. Document: ${JSON.stringify(doc)}`,\n );\n }\n if (typeof doc._type !== \"string\" || doc._type.length === 0) {\n throw fakeError(\n `Document \"${doc._id}\" is missing a non-empty string \\`_type\\` (got ${JSON.stringify(doc._type)}). ` +\n `Every Sanity document needs a _type.`,\n );\n }\n const now = new Date().toISOString();\n return {\n ...doc,\n _id: doc._id,\n _type: doc._type,\n _rev: doc._rev ?? cryptoRandomKey(),\n _createdAt: doc._createdAt ?? now,\n _updatedAt: doc._updatedAt ?? now,\n } as SanityDocument;\n };\n\n const seed = (documents: SanityDocumentLike[]) => {\n docs.clear();\n for (const d of documents) {\n const promoted = promote(d);\n docs.set(promoted._id, promoted);\n }\n };\n\n if (initial) seed(initial);\n\n return {\n seed,\n add: (documents) => {\n for (const d of documents) {\n const promoted = promote(d);\n docs.set(promoted._id, promoted);\n }\n },\n reset: () => docs.clear(),\n all: () => [...docs.values()],\n get: (id) => docs.get(id),\n put: (doc) => {\n docs.set(doc._id, doc);\n },\n remove: (id) => {\n const doc = docs.get(id);\n if (doc) docs.delete(id);\n return doc;\n },\n snapshot: () => [...docs.values()],\n };\n}\n\n/**\n * Generate a stable-ish random key without pulling in a dependency.\n * Use cases: synthesising `_rev` for newly-stored documents.\n */\nexport function cryptoRandomKey(length = 12): string {\n const bytes = new Uint8Array(length);\n // globalThis.crypto is available in Node 22+ and all modern browsers.\n globalThis.crypto.getRandomValues(bytes);\n return [...bytes]\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .slice(0, length);\n}\n","/**\n * Action handlers for `client.action()` — Content Releases, versions, and\n * document publish/unpublish — plus the `client.releases.*` and\n * `client.createVersion` / `client.unpublishVersion` helpers.\n *\n * Mirrors enough of the real `@sanity/client` action surface for tests\n * to exercise release-aware workflows. Transient states\n * (`scheduling`/`publishing`/`unscheduling`) are skipped — releases\n * transition synchronously between settled states.\n */\n\nimport type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\n\nimport { fakeError } from \"./errors.ts\";\nimport { mutationContexts, type MutationGuardImageContext } from \"./mutation-guard.ts\";\nimport { applyPatchOps, type PatchOp } from \"./patch.ts\";\nimport { publishedIdOf } from \"./perspective.ts\";\nimport type { TestStore } from \"./store.ts\";\nimport { cryptoRandomKey } from \"./store.ts\";\n\n/**\n * Document body accepted by `version.create` / `createVersion`. The id\n * is derived from `releaseId + publishedId` so callers may omit `_id`\n * here. `version.replace` requires `_id` since it identifies the doc\n * to replace.\n */\nexport interface DocumentBody {\n _id?: string;\n _type: string;\n [field: string]: unknown;\n}\n\nexport type ReleaseState = \"active\" | \"scheduled\" | \"published\" | \"archived\";\n\nexport type ReleaseType = \"asap\" | \"scheduled\" | \"undecided\";\n\nexport interface ReleaseMetadata {\n title?: string;\n description?: string;\n releaseType?: ReleaseType;\n intendedPublishAt?: string;\n}\n\nexport interface ReleaseDocument extends SanityDocument {\n _type: \"system.release\";\n name: string;\n state: ReleaseState;\n metadata: ReleaseMetadata;\n publishAt?: string;\n}\n\nexport type ReleaseAction =\n | {\n actionType: \"sanity.action.release.create\";\n releaseId: string;\n metadata?: Partial<ReleaseMetadata>;\n }\n | {\n actionType: \"sanity.action.release.edit\";\n releaseId: string;\n patch: { set?: Record<string, unknown>; unset?: string[] };\n }\n | { actionType: \"sanity.action.release.schedule\"; releaseId: string; publishAt: string }\n | { actionType: \"sanity.action.release.unschedule\"; releaseId: string }\n | { actionType: \"sanity.action.release.publish\"; releaseId: string }\n | { actionType: \"sanity.action.release.archive\"; releaseId: string }\n | { actionType: \"sanity.action.release.unarchive\"; releaseId: string }\n | { actionType: \"sanity.action.release.delete\"; releaseId: string };\n\nexport type VersionAction =\n | {\n actionType: \"sanity.action.document.version.create\";\n publishedId: string;\n document: DocumentBody;\n releaseId?: string; // inferred from document._id when omitted\n }\n | {\n actionType: \"sanity.action.document.version.replace\";\n document: SanityDocumentLike;\n }\n | {\n actionType: \"sanity.action.document.version.discard\";\n versionId: string;\n }\n | {\n actionType: \"sanity.action.document.version.unpublish\";\n versionId: string;\n publishedId: string;\n };\n\n/**\n * Draft↔published document actions — `sanity.action.document.publish`\n * promotes the draft onto the published id (and deletes the draft);\n * `sanity.action.document.unpublish` retracts the published document into a\n * draft (keeping an existing draft untouched) and deletes the published one.\n */\nexport type DocumentAction =\n | {\n actionType: \"sanity.action.document.publish\";\n draftId: string;\n publishedId: string;\n ifDraftRevisionId?: string;\n ifPublishedRevisionId?: string;\n }\n | {\n actionType: \"sanity.action.document.unpublish\";\n draftId: string;\n publishedId: string;\n };\n\nexport type Action = ReleaseAction | VersionAction | DocumentAction;\n\n/**\n * Client-layer interception for document publish/unpublish actions. `gate`\n * checks access before each write. `guard` receives the batch's net draft and\n * published mutations after staging; a rejection rolls back the whole batch.\n */\nexport interface DocumentActionHooks {\n gate?: (\n document: SanityDocumentLike & { _id: string; _type: string },\n permission: \"create\" | \"update\",\n ) => void | Promise<void>;\n guard?: (contexts: readonly MutationGuardImageContext[]) => void | Promise<void>;\n}\n\nexport interface ActionResult {\n transactionId: string;\n}\n\nconst RELEASE_DOC_PREFIX = \"_.releases.\";\nconst VERSIONS_PREFIX = \"versions.\";\n\nconst releaseDocId = (releaseId: string): string => `${RELEASE_DOC_PREFIX}${releaseId}`;\nconst versionDocId = (releaseId: string, publishedId: string): string =>\n `${VERSIONS_PREFIX}${releaseId}.${publishedId}`;\n\n/** Parse `versions.<release>.<id>` → `{ releaseId, publishedId }`. Throws otherwise. */\nfunction parseVersionId(versionId: string): { releaseId: string; publishedId: string } {\n if (!versionId.startsWith(VERSIONS_PREFIX)) {\n throw fakeError(\n `\"${versionId}\" is not a version document id. Version ids look like ` +\n `\"versions.<releaseId>.<publishedId>\" (e.g. \"versions.summer-drop.product-1\").`,\n );\n }\n const rest = versionId.slice(VERSIONS_PREFIX.length);\n const dot = rest.indexOf(\".\");\n if (dot === -1) {\n throw fakeError(\n `\"${versionId}\" is not a version document id. Version ids look like ` +\n `\"versions.<releaseId>.<publishedId>\" (e.g. \"versions.summer-drop.product-1\").`,\n );\n }\n return { releaseId: rest.slice(0, dot), publishedId: rest.slice(dot + 1) };\n}\n\nfunction getRelease(store: TestStore, releaseId: string): ReleaseDocument {\n const doc = store.get(releaseDocId(releaseId)) as ReleaseDocument | undefined;\n if (doc === undefined) {\n throw fakeError(\n `Release \"${releaseId}\" not found. Create it first with ` +\n `client.releases.create({ releaseId: \"${releaseId}\" }) (or a ` +\n `\"sanity.action.release.create\" action) before editing, scheduling, or adding versions to it.`,\n );\n }\n return doc;\n}\n\nfunction maybeRelease(store: TestStore, releaseId: string): ReleaseDocument | undefined {\n return store.get(releaseDocId(releaseId)) as ReleaseDocument | undefined;\n}\n\nfunction nowIso(): string {\n return new Date().toISOString();\n}\n\nfunction promote(doc: SanityDocumentLike): SanityDocument {\n const now = nowIso();\n return {\n ...doc,\n _id: doc._id as string,\n _type: doc._type as string,\n _rev: cryptoRandomKey(),\n _createdAt: doc._createdAt ?? now,\n _updatedAt: now,\n } as SanityDocument;\n}\n\nfunction bumpRev(doc: SanityDocument, patch: Partial<SanityDocument>): SanityDocument {\n return {\n ...doc,\n ...patch,\n _rev: cryptoRandomKey(),\n _updatedAt: nowIso(),\n } as SanityDocument;\n}\n\nfunction requireActive(release: ReleaseDocument): void {\n if (release.state !== \"active\") {\n throw fakeError(\n `Release \"${release.name}\" is \"${release.state}\", but only \"active\" releases accept version ` +\n `writes (create / replace / discard / unpublish). Unschedule or unarchive it back to ` +\n `\"active\" first, or target a different release.`,\n );\n }\n}\n\n/**\n * Apply a single action to the store. Returns a synthetic transactionId.\n * Throws on any invariant violation — callers wrap multi-action arrays\n * with a snapshot/rollback to keep atomicity. `hooks.gate` checks document\n * publish/unpublish writes here; {@link applyActions} evaluates their guard\n * contexts after staging the complete batch.\n */\nexport async function applyAction(\n store: TestStore,\n action: Action,\n hooks?: DocumentActionHooks,\n): Promise<ActionResult> {\n switch (action.actionType) {\n case \"sanity.action.release.create\": {\n if (maybeRelease(store, action.releaseId) !== undefined) {\n throw fakeError(\n `Release \"${action.releaseId}\" already exists. release.create expects a new releaseId; ` +\n `use release.edit to change its metadata, or pick a different releaseId.`,\n );\n }\n const now = nowIso();\n const metadata: ReleaseMetadata = {\n releaseType: \"undecided\",\n ...action.metadata,\n };\n const doc: ReleaseDocument = {\n _id: releaseDocId(action.releaseId),\n _type: \"system.release\",\n _rev: cryptoRandomKey(),\n _createdAt: now,\n _updatedAt: now,\n name: action.releaseId,\n state: \"active\",\n metadata,\n };\n store.put(doc);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.edit\": {\n const release = getRelease(store, action.releaseId);\n const patchOps: PatchOp[] = [];\n if (action.patch.set !== undefined) {\n patchOps.push({ kind: \"set\", props: action.patch.set });\n }\n if (action.patch.unset !== undefined) {\n patchOps.push({ kind: \"unset\", paths: action.patch.unset });\n }\n const patched = applyPatchOps(release, patchOps);\n store.put(bumpRev(patched, {}));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.schedule\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"active\") {\n throw fakeError(\n `Cannot schedule release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"active\" releases can be scheduled.`,\n );\n }\n store.put(\n bumpRev(release, {\n state: \"scheduled\",\n publishAt: action.publishAt,\n } as Partial<SanityDocument>),\n );\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.unschedule\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"scheduled\") {\n throw fakeError(\n `Cannot unschedule release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"scheduled\" releases can be unscheduled.`,\n );\n }\n const next: ReleaseDocument = { ...release, state: \"active\" };\n delete (next as { publishAt?: string }).publishAt;\n store.put(bumpRev(next, {}));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.publish\": {\n const release = getRelease(store, action.releaseId);\n if (release.state === \"published\") {\n // Idempotent.\n return { transactionId: cryptoRandomKey() };\n }\n if (release.state !== \"active\" && release.state !== \"scheduled\") {\n throw fakeError(\n `Cannot publish release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"active\" or \"scheduled\" releases can be published.`,\n );\n }\n // Materialise versions onto published ids, then drop the versions.\n const prefix = `${VERSIONS_PREFIX}${action.releaseId}.`;\n const sources = store.all().filter((d) => d._id.startsWith(prefix));\n for (const v of sources) {\n const pubId = v._id.slice(prefix.length);\n const isUnpublishStub = (v as { _system?: { delete?: boolean } })._system?.delete === true;\n if (isUnpublishStub) {\n store.remove(pubId);\n } else {\n const body: SanityDocumentLike = {\n ...(v as SanityDocumentLike),\n _id: pubId,\n };\n // Strip transient fields we projected through the version.\n delete (body as { _originalId?: string })._originalId;\n delete (body as { _system?: unknown })._system;\n const promoted = promote(body);\n // Preserve _createdAt of the existing published doc if present.\n const existing = store.get(pubId);\n if (existing !== undefined) {\n promoted._createdAt = existing._createdAt;\n }\n store.put(promoted);\n }\n store.remove(v._id);\n }\n store.put(bumpRev(release, { state: \"published\" } as Partial<SanityDocument>));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.archive\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"active\" && release.state !== \"scheduled\") {\n throw fakeError(\n `Cannot archive release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"active\" or \"scheduled\" releases can be archived.`,\n );\n }\n store.put(bumpRev(release, { state: \"archived\" } as Partial<SanityDocument>));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.unarchive\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"archived\") {\n throw fakeError(\n `Cannot unarchive release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"archived\" releases can be unarchived.`,\n );\n }\n store.put(bumpRev(release, { state: \"active\" } as Partial<SanityDocument>));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.delete\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"archived\" && release.state !== \"published\") {\n throw fakeError(\n `Cannot delete release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"archived\" or \"published\" releases can be deleted. Archive it first ` +\n `(sanity.action.release.archive) or publish it.`,\n );\n }\n // Clean up any leftover version docs (archived releases keep them around).\n const prefix = `${VERSIONS_PREFIX}${action.releaseId}.`;\n for (const v of store.all()) {\n if (v._id.startsWith(prefix)) store.remove(v._id);\n }\n store.remove(release._id);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.create\": {\n const releaseId =\n action.releaseId ??\n (action.document._id !== undefined\n ? parseVersionId(action.document._id as string).releaseId\n : undefined);\n if (releaseId === undefined) {\n throw fakeError(\n `version.create needs a release to attach to: pass \\`releaseId\\`, or a \\`document._id\\` ` +\n `shaped \"versions.<releaseId>.<publishedId>\" so the release can be inferred.`,\n );\n }\n const release = getRelease(store, releaseId);\n requireActive(release);\n const vid = versionDocId(releaseId, action.publishedId);\n const doc: SanityDocumentLike = {\n ...action.document,\n _id: vid,\n };\n const existing = store.get(vid);\n const promoted = promote(doc);\n if (existing !== undefined) {\n promoted._createdAt = existing._createdAt;\n }\n store.put(promoted);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.replace\": {\n const id = action.document._id;\n if (typeof id !== \"string\") {\n throw fakeError(\n `version.replace needs \\`document._id\\` (a version id like ` +\n `\"versions.<releaseId>.<publishedId>\") to identify which version document to replace.`,\n );\n }\n const { releaseId } = parseVersionId(id);\n const release = getRelease(store, releaseId);\n requireActive(release);\n const existing = store.get(id);\n const promoted = promote(action.document);\n if (existing !== undefined) {\n promoted._createdAt = existing._createdAt;\n }\n store.put(promoted);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.discard\": {\n const { releaseId } = parseVersionId(action.versionId);\n const release = getRelease(store, releaseId);\n requireActive(release);\n store.remove(action.versionId);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.unpublish\": {\n const { releaseId } = parseVersionId(action.versionId);\n const release = getRelease(store, releaseId);\n requireActive(release);\n const doc: SanityDocumentLike = {\n _id: action.versionId,\n _type: \"stub\",\n _system: { delete: true },\n } as SanityDocumentLike;\n store.put(promote(doc));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.publish\": {\n requireDraftOf(action);\n const draft = store.get(action.draftId);\n if (draft === undefined) {\n throw fakeError(\n `Cannot publish \"${action.publishedId}\": draft \"${action.draftId}\" not found. ` +\n `document.publish promotes an existing draft onto the published id — create the ` +\n `draft first (e.g. client.createOrReplace({ _id: \"${action.draftId}\", ... })).`,\n );\n }\n const published = store.get(action.publishedId);\n requireRevisionMatch(action.draftId, draft._rev, action.ifDraftRevisionId);\n requireRevisionMatch(action.publishedId, published?._rev, action.ifPublishedRevisionId);\n const promoted = promote({ ...draft, _id: action.publishedId });\n if (published !== undefined) {\n promoted._createdAt = published._createdAt;\n }\n await hooks?.gate?.(promoted, published === undefined ? \"create\" : \"update\");\n store.put(promoted);\n store.remove(action.draftId);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.unpublish\": {\n requireDraftOf(action);\n const published = store.get(action.publishedId);\n if (published === undefined) {\n throw fakeError(\n `Cannot unpublish \"${action.publishedId}\": no published document with that _id exists. ` +\n `document.unpublish retracts a live published document into \"${action.draftId}\".`,\n );\n }\n await hooks?.gate?.(published, \"update\");\n // The lake keeps an existing draft untouched; only a missing one is\n // materialised from the published content.\n if (store.get(action.draftId) === undefined) {\n const draft = promote({ ...published, _id: action.draftId });\n draft._createdAt = published._createdAt;\n store.put(draft);\n }\n store.remove(action.publishedId);\n return { transactionId: cryptoRandomKey() };\n }\n }\n}\n\n/** Throw unless the action's `draftId` is the draft of its `publishedId`. */\nfunction requireDraftOf(action: {\n actionType: string;\n draftId: string;\n publishedId: string;\n}): void {\n if (publishedIdOf(action.draftId) === action.publishedId && action.draftId !== action.publishedId)\n return;\n throw fakeError(\n `${action.actionType}: draftId \"${action.draftId}\" is not the draft of publishedId ` +\n `\"${action.publishedId}\" — expected draftId \"drafts.${action.publishedId}\".`,\n );\n}\n\n/** Enforce an `ifDraftRevisionId` / `ifPublishedRevisionId` precondition when given. */\nfunction requireRevisionMatch(\n documentId: string,\n actualRev: string | undefined,\n expectedRev: string | undefined,\n): void {\n if (expectedRev === undefined || actualRev === expectedRev) return;\n throw fakeError(\n `Revision precondition failed for \"${documentId}\": expected _rev \"${expectedRev}\" but the ` +\n `document is at ${actualRev === undefined ? \"no revision (missing)\" : `\"${actualRev}\"`}.`,\n );\n}\n\n/**\n * Apply an action or a list of actions atomically. On any failure all\n * mutations within the batch are rolled back to the pre-batch snapshot.\n * `hooks` intercepts document publish/unpublish actions\n * ({@link DocumentActionHooks}) and evaluates their net changes as one batch.\n */\nexport async function applyActions(\n store: TestStore,\n actions: Action | Action[],\n hooks?: DocumentActionHooks,\n): Promise<ActionResult> {\n const list = Array.isArray(actions) ? actions : [actions];\n const snapshot = new Map<string, SanityDocument | undefined>();\n const guardedIds = new Set<string>();\n const remember = (id: string) => {\n if (!snapshot.has(id)) snapshot.set(id, store.get(id));\n };\n\n try {\n let last: ActionResult = { transactionId: cryptoRandomKey() };\n for (const a of list) {\n // Pre-record everything we might touch so rollback is precise.\n switch (a.actionType) {\n case \"sanity.action.release.create\":\n case \"sanity.action.release.edit\":\n case \"sanity.action.release.schedule\":\n case \"sanity.action.release.unschedule\":\n case \"sanity.action.release.archive\":\n case \"sanity.action.release.unarchive\":\n case \"sanity.action.release.delete\":\n remember(`${RELEASE_DOC_PREFIX}${a.releaseId}`);\n break;\n case \"sanity.action.release.publish\": {\n remember(`${RELEASE_DOC_PREFIX}${a.releaseId}`);\n const prefix = `${VERSIONS_PREFIX}${a.releaseId}.`;\n for (const d of store.all()) {\n if (d._id.startsWith(prefix)) {\n remember(d._id);\n remember(d._id.slice(prefix.length));\n }\n }\n break;\n }\n case \"sanity.action.document.version.create\": {\n // version.create derives its id from releaseId + publishedId,\n // so we don't rely on document._id being set.\n const releaseId =\n a.releaseId ??\n (typeof a.document._id === \"string\"\n ? parseVersionId(a.document._id).releaseId\n : undefined);\n if (releaseId !== undefined) {\n remember(versionDocId(releaseId, a.publishedId));\n }\n break;\n }\n case \"sanity.action.document.version.replace\":\n if (typeof a.document._id === \"string\") remember(a.document._id);\n break;\n case \"sanity.action.document.version.discard\":\n case \"sanity.action.document.version.unpublish\":\n remember(a.versionId);\n break;\n case \"sanity.action.document.publish\":\n remember(a.draftId);\n remember(a.publishedId);\n // Preserve the lake-facing order: published write, then draft delete.\n guardedIds.add(a.publishedId);\n guardedIds.add(a.draftId);\n break;\n case \"sanity.action.document.unpublish\":\n remember(a.draftId);\n remember(a.publishedId);\n // Preserve the lake-facing order: draft create, then published delete.\n guardedIds.add(a.draftId);\n guardedIds.add(a.publishedId);\n break;\n }\n last = await applyAction(store, a, hooks);\n }\n const contexts = mutationContexts(\n [...guardedIds].map((id) => ({ before: snapshot.get(id), after: store.get(id) })),\n );\n await hooks?.guard?.(contexts);\n return last;\n } catch (err) {\n for (const [id, doc] of snapshot) {\n if (doc === undefined) {\n store.remove(id);\n } else {\n store.put(doc);\n }\n }\n throw err;\n }\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\nimport type { PatchHandle, PatchOp } from \"./patch.ts\";\n\n/**\n * A transaction collects mutations and applies them atomically at commit\n * time. Mutations are applied in declaration order; if any one fails, the\n * whole transaction throws and no writes land in the store.\n *\n * Subset of `@sanity/client`'s Transaction. Same shape, narrower surface.\n */\nexport type TxnMutation =\n | { kind: \"create\"; doc: SanityDocumentLike }\n | { kind: \"createOrReplace\"; doc: SanityDocumentLike }\n | { kind: \"createIfNotExists\"; doc: SanityDocumentLike }\n | { kind: \"delete\"; id: string }\n | { kind: \"patch\"; documentId: string; ops: PatchOp[]; ifRev?: string };\n\nexport interface TransactionHandle {\n create: (doc: SanityDocumentLike) => TransactionHandle;\n createOrReplace: (doc: SanityDocumentLike) => TransactionHandle;\n createIfNotExists: (doc: SanityDocumentLike) => TransactionHandle;\n delete: (id: string) => TransactionHandle;\n /** Accept either a Patch builder (drained at commit) or an inline doc id + ops. */\n patch: (\n documentIdOrPatch: string | PatchHandle,\n ops?: PatchOp[],\n ifRev?: string,\n ) => TransactionHandle;\n commit: (options?: TxnCommitOptions) => Promise<TxnCommitResult>;\n /** Test-only introspection — not on real Transaction. */\n toJSON: () => TxnMutation[];\n}\n\nexport interface TxnCommitOptions {\n /**\n * Shape the resolved value (mirrors `@sanity/client`):\n * - default (both unset) → {@link MultipleMutationResult}\n * - `returnDocuments: true` → the resulting document(s)\n * - `returnFirst: true` with `returnDocuments` → a single document\n * - `returnDocuments: false` → an id-shaped mutation result\n */\n returnDocuments?: boolean;\n returnFirst?: boolean;\n /** Override the synthetic transaction id (mirrors `client.transaction(undefined, { transactionId })`). */\n transactionId?: string;\n /**\n * Request tag, recorded on the mutation log entry for assertions.\n * `null` mirrors `@sanity/client`'s explicit-suppression form and is\n * recorded as-is. No other behavior is simulated.\n */\n tag?: string | null;\n}\n\n/** The operation recorded per affected document, matching `@sanity/client`. */\nexport type MutationOperation = \"create\" | \"update\" | \"delete\" | \"none\";\n\n/** Result of a mutation when `returnFirst` resolves true and ids are requested. */\nexport interface SingleMutationResult {\n transactionId: string;\n documentId: string;\n results: { id: string; operation: MutationOperation }[];\n}\n\n/** Result of a mutation when ids are requested for all affected documents. */\nexport interface MultipleMutationResult {\n transactionId: string;\n documentIds: string[];\n results: { id: string; operation: MutationOperation }[];\n}\n\nexport type TxnCommitResult =\n | MultipleMutationResult\n | SingleMutationResult\n | SanityDocument\n | SanityDocument[];\n\nexport interface TxnExecutor {\n apply: (mutations: TxnMutation[], options?: TxnCommitOptions) => Promise<TxnCommitResult>;\n}\n\nexport function createTransaction(executor: TxnExecutor): TransactionHandle {\n const mutations: TxnMutation[] = [];\n\n const handle: TransactionHandle = {\n create: (doc) => {\n mutations.push({ kind: \"create\", doc });\n return handle;\n },\n createOrReplace: (doc) => {\n mutations.push({ kind: \"createOrReplace\", doc });\n return handle;\n },\n createIfNotExists: (doc) => {\n mutations.push({ kind: \"createIfNotExists\", doc });\n return handle;\n },\n delete: (id) => {\n mutations.push({ kind: \"delete\", id });\n return handle;\n },\n patch: (documentIdOrPatch, ops, ifRev) => {\n if (typeof documentIdOrPatch === \"string\") {\n mutations.push({\n kind: \"patch\",\n documentId: documentIdOrPatch,\n ops: ops ?? [],\n ...(ifRev !== undefined ? { ifRev } : {}),\n });\n } else {\n const json = documentIdOrPatch.toJSON();\n mutations.push({\n kind: \"patch\",\n documentId: json.id,\n ops: json.ops,\n ...(json.ifRev !== undefined ? { ifRev: json.ifRev } : {}),\n });\n }\n return handle;\n },\n commit: (options) => executor.apply(mutations, options),\n toJSON: () => [...mutations],\n };\n\n return handle;\n}\n","/**\n * Graceful-degradation layer for the parts of `@sanity/client` this fake\n * doesn't implement.\n *\n * The client object is a fixed surface. Without a guard, reaching for a\n * member the fake doesn't have (`client.listen`, `client.assets.upload`,\n * a typo) yields a bare `TypeError: x is not a function` — no hint that\n * it's an intentional gap in a *fake*, no pointer to a workaround. The\n * `Proxy` built here turns every such access into an actionable error,\n * and lets tests fill gaps via a stub registry.\n *\n * Resolution order for a property read on the wrapped client:\n * 1. Real implemented member → passed straight through.\n * 2. Thenable / inspection probes (`then`, symbols, …) → pass through\n * so `await client` and `console.log(client)` stay well-behaved.\n * 3. User-registered stub (`client.stub(name, fn)`) → returned.\n * 4. Known-but-unimplemented `@sanity/client` member → a guard that\n * throws a curated, workaround-bearing error on call or deeper access.\n * 5. Anything else (typo / unknown) → a guard that throws a\n * \"no such method\" error listing the supported surface.\n */\n\nconst ISSUES_URL = \"https://github.com/sanity-labs/client-fake-for-test/issues\";\n\n/** Members this fake actually implements — surfaced in error messages. */\nexport const SUPPORTED_SURFACE = [\n \"fetch\",\n \"getDocument\",\n \"getDocuments\",\n \"create\",\n \"createOrReplace\",\n \"createIfNotExists\",\n \"delete\",\n \"patch\",\n \"transaction\",\n \"mutate\",\n \"action\",\n \"releases\",\n \"createVersion\",\n \"unpublishVersion\",\n \"request (only with accessControl or a bound token)\",\n \"getDataUrl\",\n \"config\",\n \"withConfig\",\n] as const;\n\ninterface UnsupportedEntry {\n /** What the real member is. */\n what: string;\n /** What to do instead. */\n workaround: string;\n}\n\n/**\n * Curated map of real `@sanity/client` members the fake deliberately omits.\n * Keyed by the top-level property name; nested access (e.g.\n * `assets.upload`) is reported with the full dotted path.\n */\nexport const UNSUPPORTED_MEMBERS: Record<string, UnsupportedEntry> = {\n listen: {\n what: \"real-time listener (returns an RxJS Observable of mutation events)\",\n workaround:\n \"Test your subscription logic in isolation, or register a fake: \" +\n `client.stub(\"listen\", () => myFakeObservable).`,\n },\n observable: {\n what: \"the RxJS-flavoured mirror of the client API\",\n workaround:\n \"Use the Promise-based methods and wrap them with `from()` if you need an Observable.\",\n },\n clone: {\n what: \"clone the client with the same config\",\n workaround: \"Use `withConfig({})` — it returns a sibling handle into the same store.\",\n },\n getUrl: {\n what: \"build an absolute API URL for a path\",\n workaround: \"Hard-code the expected URL in your test, or stub it.\",\n },\n assets: {\n what: \"asset upload / metadata namespace\",\n workaround: \"Seed asset documents into the store directly with `seed()` / `add()`.\",\n },\n agent: {\n what: \"Agent Actions namespace (generate / transform / …)\",\n workaround: `Stub the call site: client.stub(\"agent\", myFakeAgent).`,\n },\n projects: {\n what: \"project-management namespace\",\n workaround: \"Out of scope for a data-plane fake — stub the call site.\",\n },\n users: {\n what: \"user-management namespace\",\n workaround: \"Out of scope for a data-plane fake — stub the call site.\",\n },\n live: {\n what: \"Live Content API namespace\",\n workaround: \"Out of scope — stub the call site or test the consumer separately.\",\n },\n mediaLibrary: {\n what: \"Media Library namespace\",\n workaround: \"Out of scope — seed documents directly or stub the call site.\",\n },\n request: {\n what: \"generic HTTP request hook — only wired up when the handle has an acting identity\",\n workaround:\n \"Create the client with `accessControl`, or bind a registered token \" +\n \"(`identities` + `token` / `withConfig({ token })`), to enable request() (it serves \" +\n \"/users/me, the ACL path, /projects/<projectId>, /projects/<projectId>/users/<ids>, the \" +\n \"History API, resource lookup and the Access API). Or register a stub.\",\n },\n dryRun: {\n what: \"not a method — did you mean the `{ dryRun }` mutation option?\",\n workaround: \"Pass `{ dryRun: true }` as the second arg to create / mutate / delete.\",\n },\n};\n\n/**\n * Property names read by promise-resolution, serialization, and test\n * frameworks while probing an unknown object. These must resolve to\n * `undefined` (not a throwing guard) so `await client`,\n * `JSON.stringify(client)`, and matcher diffs stay well-behaved. Members\n * inherited from `Object.prototype` (`toString`, `valueOf`, …) already\n * pass through via the `in` check and don't need listing here.\n */\nconst PROBE_NAMES = new Set([\n \"then\",\n \"catch\",\n \"finally\",\n \"toJSON\",\n \"asymmetricMatch\",\n \"$$typeof\",\n \"nodeType\",\n \"_isMockFunction\",\n]);\n\n/** Build the multi-line error body shared by both guard flavours. */\nfunction guardMessage(path: string, detail: string, workaround: string): string {\n return [\n `[client-fake-for-test] \\`client.${path}\\` ${detail}`,\n \"\",\n \"This is an in-memory fake of @sanity/client. It implements:\",\n ` ${SUPPORTED_SURFACE.join(\", \")}.`,\n \"\",\n `→ ${workaround}`,\n \"\",\n `Register a stub to fill any gap: client.stub(${JSON.stringify(path.split(\".\")[0])}, handler)`,\n `If this should be supported, open an issue: ${ISSUES_URL}`,\n ].join(\"\\n\");\n}\n\n/**\n * A callable, infinitely-chainable guard. Reading any property returns a\n * deeper guard (so `client.assets.upload` reports the full path), and\n * calling at any depth throws the actionable error. `then` / symbols\n * resolve to `undefined` so `await` and inspection don't trip the guard.\n */\nfunction makeGuard(path: string, message: string): unknown {\n const thrower = () => {\n throw new Error(message);\n };\n return new Proxy(thrower, {\n apply: () => thrower(),\n get: (_t, prop) => {\n if (typeof prop === \"symbol\") return undefined;\n if (PROBE_NAMES.has(prop)) return undefined;\n // Re-derive the message for the deeper path so the error names the\n // exact member that was reached for.\n const childPath = `${path}.${String(prop)}`;\n const top = path.split(\".\")[0] as string;\n const entry = UNSUPPORTED_MEMBERS[top];\n const detail = entry ? `is not implemented (${entry.what}).` : \"is not implemented.\";\n const workaround = entry?.workaround ?? \"Register a stub or use a different test strategy.\";\n return makeGuard(childPath, guardMessage(childPath, detail, workaround));\n },\n });\n}\n\n/** Outcome of a recorded call, reported once a (possibly async) result is known. */\nexport type CallSettler = (outcome: { result?: unknown; error?: unknown }) => void;\n\nexport interface GuardConfig {\n /**\n * Resolve a registered stub for an unimplemented member, or `undefined`.\n * Checked only for members the client doesn't already implement, so\n * stubs fill gaps without shadowing core behaviour.\n */\n getStub: (name: string) => unknown;\n /**\n * Called synchronously when a recorded method is invoked, with the\n * method name and its arguments. Returns a settler the wrapper invokes\n * once the result (or rejection) is known. Omit to disable recording.\n */\n onCall?: (method: string, args: unknown[]) => CallSettler;\n /**\n * Top-level member names whose calls are recorded via `onCall`. A\n * recorded member that resolves to a namespace object (e.g. `releases`)\n * has each of its methods recorded as `name.method`.\n */\n recorded?: Set<string>;\n}\n\n/** Wrap a function so each call is reported to `onCall` (args now, outcome on settle). */\nfunction wrapRecorded(\n method: string,\n fn: (...args: unknown[]) => unknown,\n onCall: NonNullable<GuardConfig[\"onCall\"]>,\n): (...args: unknown[]) => unknown {\n return (...args: unknown[]) => {\n const settle = onCall(method, args);\n try {\n const out = fn(...args);\n if (\n out !== null &&\n typeof out === \"object\" &&\n typeof (out as PromiseLike<unknown>).then === \"function\"\n ) {\n return (out as Promise<unknown>).then(\n (result) => {\n settle({ result });\n return result;\n },\n (error: unknown) => {\n settle({ error });\n throw error;\n },\n );\n }\n settle({ result: out });\n return out;\n } catch (error) {\n settle({ error });\n throw error;\n }\n };\n}\n\n/** Wrap a namespace object so its method calls record as `prefix.method`. */\nfunction wrapNamespace(\n ns: Record<string, unknown>,\n prefix: string,\n onCall: NonNullable<GuardConfig[\"onCall\"]>,\n): Record<string, unknown> {\n return new Proxy(ns, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof prop === \"string\" && typeof value === \"function\") {\n return wrapRecorded(`${prefix}.${prop}`, value as (...a: unknown[]) => unknown, onCall);\n }\n return value;\n },\n });\n}\n\n/**\n * Wrap a client object so unimplemented members fail informatively,\n * registered stubs are served, and recorded methods report their calls.\n *\n * @param client - the fully-built client object (plain literal).\n * @param config - stub resolver plus optional call-recording hooks.\n */\nexport function wrapWithGuards<T extends object>(client: T, config: GuardConfig): T {\n const { getStub, onCall, recorded } = config;\n return new Proxy(client, {\n get(target, prop, receiver) {\n if (typeof prop === \"symbol\") return Reflect.get(target, prop, receiver);\n if (prop in target) {\n const value = Reflect.get(target, prop, receiver);\n if (onCall !== undefined && recorded?.has(prop) === true) {\n if (typeof value === \"function\") {\n return wrapRecorded(prop, value as (...a: unknown[]) => unknown, onCall);\n }\n if (value !== null && typeof value === \"object\") {\n return wrapNamespace(value as Record<string, unknown>, prop, onCall);\n }\n }\n return value;\n }\n // Promise / serialization / framework probes on a non-thenable object.\n if (PROBE_NAMES.has(prop)) return undefined;\n\n const stub = getStub(prop);\n if (stub !== undefined) return stub;\n\n const entry = UNSUPPORTED_MEMBERS[prop];\n if (entry !== undefined) {\n return makeGuard(\n prop,\n guardMessage(prop, `is not implemented (${entry.what}).`, entry.workaround),\n );\n }\n return makeGuard(\n prop,\n guardMessage(\n prop,\n \"is not a method on this fake (unknown member or unimplemented @sanity/client API).\",\n \"Check for a typo, register a stub, or use a different test strategy.\",\n ),\n );\n },\n });\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\nimport { evaluate, parse } from \"groq-js\";\n\nimport {\n canAccess,\n enforceWriteAccess,\n makeRequestImpl,\n projectUserOf,\n type DocumentValuePermission,\n type TestClientAccessControl,\n type TestClientIdentity,\n} from \"./access-control.ts\";\nimport {\n applyActions,\n type Action,\n type ActionResult,\n type DocumentBody,\n type ReleaseDocument,\n type ReleaseMetadata,\n} from \"./actions.ts\";\nimport { fakeError } from \"./errors.ts\";\nimport { createDocumentHistory, type DocumentHistory } from \"./history.ts\";\nimport {\n enforceMutationGuards,\n mutationContexts,\n type MutationGuard,\n type MutationGuardContext,\n type MutationGuardImageContext,\n} from \"./mutation-guard.ts\";\nimport { applyPatchOps, createPatch, type PatchHandle, type PatchOp } from \"./patch.ts\";\nimport { applyPerspective, isSystemId, resolveDocument, type Perspective } from \"./perspective.ts\";\nimport {\n capabilitiesOf,\n dataUrl,\n datasetParts,\n validateResource,\n type TestClientResource,\n} from \"./resources.ts\";\nimport { createTestStore, cryptoRandomKey, type TestStore } from \"./store.ts\";\nimport {\n createTransaction,\n type MultipleMutationResult,\n type MutationOperation,\n type SingleMutationResult,\n type TransactionHandle,\n type TxnCommitOptions,\n type TxnCommitResult,\n type TxnMutation,\n} from \"./transaction.ts\";\nimport { wrapWithGuards, type CallSettler } from \"./unsupported.ts\";\n\n/**\n * Data-plane subset of `@sanity/client`'s surface, with multi-dataset support.\n *\n * One `createTestClient()` call creates a registry of stores, one per\n * resource: a `(projectId, dataset)` pair, or a Media Library, Canvas or\n * dashboard named by `resource: { type, id }` exactly as `@sanity/client`\n * names it. The returned client points at one of them.\n * `withConfig({ projectId, dataset })` or `withConfig({ resource })` returns\n * a new handle into the same registry but pointing at a different store —\n * useful for workflows where definitions live in one dataset and instances\n * live in another.\n *\n * Two separate `createTestClient()` calls have separate registries, so\n * tests stay isolated from each other.\n *\n * Methods not on the real client:\n *\n * - `seed`, `add`, `reset`, `snapshot`, `store` — bulk-load helpers,\n * scoped to the current dataset.\n * - `simulateConcurrentEdit()` — force the next N revision-guarded writes\n * to a document to 409, for optimistic-locking retry tests.\n * - `datasets()` / `resources()` — list the stores known in this registry.\n *\n * Methods deliberately omitted:\n *\n * - Project / agent / asset / live / listen APIs.\n */\nexport interface FetchOptions {\n /** Override the client's configured perspective for this read. */\n perspective?: Perspective;\n /**\n * When `false`, resolve to a {@link RawQueryResponse} (`{ query, ms,\n * result }`) instead of the bare result — mirrors `@sanity/client`.\n * Defaults to `true`.\n */\n filterResponse?: boolean;\n /**\n * With `filterResponse: false`, set to `false` to omit `query` from the\n * raw response. Ignored when `filterResponse` is not `false`.\n */\n returnQuery?: boolean;\n /**\n * Request tag, recorded on the {@link CallRecord} for assertions.\n * `null` mirrors `@sanity/client`'s explicit-suppression form and is\n * recorded as-is. No other behavior is simulated.\n */\n tag?: string | null;\n /** Accepted for signature parity; not simulated. */\n signal?: AbortSignal;\n}\n\n/**\n * Shape returned by `fetch` when `filterResponse: false`. Matches\n * `@sanity/client`'s `RawQueryResponse`. `query` is omitted when the\n * caller also passes `returnQuery: false`.\n */\nexport interface RawQueryResponse<R> {\n query?: string;\n ms: number;\n result: R;\n}\n\n/**\n * A single mutation in the raw array form accepted by `mutate()` — the\n * same shape `@sanity/client` accepts. The inline `patch` form mirrors\n * the real client's keyed patch object.\n */\nexport type FakeMutation =\n | { create: SanityDocumentLike }\n | { createOrReplace: SanityDocumentLike }\n | { createIfNotExists: SanityDocumentLike }\n | { delete: { id: string } }\n | {\n patch: {\n id: string;\n ifRevisionID?: string;\n set?: Record<string, unknown>;\n setIfMissing?: Record<string, unknown>;\n unset?: string[];\n inc?: Record<string, number>;\n dec?: Record<string, number>;\n insert?: {\n before?: string;\n after?: string;\n replace?: string;\n items: unknown[];\n };\n };\n };\n\n/** Append-only record of one committed transaction (a single mutation is a 1-entry transaction). */\nexport interface MutationLogEntry {\n transactionId: string;\n projectId: string;\n dataset: string;\n /** The resource the transaction targeted, on a `resource`-configured handle. */\n resource?: TestClientResource;\n mutations: TxnMutation[];\n /** Request tag the commit carried, when one was passed (`null` = explicit suppression). */\n tag?: string | null;\n}\n\n/**\n * One recorded invocation of a client method — the spy layer behind\n * `client.calls()` / `client.callCount()`. `result` holds the resolved\n * value (awaited, for async methods); `error` holds the thrown/rejected\n * value. Builder-returning methods (`patch`, `transaction`) record the\n * builder as `result`; their eventual writes show up in the mutation log.\n */\nexport interface CallRecord {\n /** Method name; namespace methods are dotted, e.g. `releases.create`. */\n method: string;\n /** Arguments as passed (stored by reference, like `vi.fn`). */\n args: unknown[];\n /** Resolved/returned value, set once the call settles successfully. */\n result?: unknown;\n /** Thrown or rejected value, set if the call failed. */\n error?: unknown;\n /** Request tag from the call's options, when one was passed (`null` = explicit suppression). */\n tag?: string | null;\n projectId: string;\n dataset: string;\n /** The resource the call targeted, on a `resource`-configured handle. */\n resource?: TestClientResource;\n}\n\n/**\n * Where each recorded method carries its options bag, for {@link tagFromArgs}.\n * Positional (not \"last object arg\") because `fetch` params may legitimately\n * contain a `tag` key that is a GROQ param, not a request tag. Methods whose\n * tag only arrives at commit time (`patch`, `transaction`) surface it on the\n * {@link MutationLogEntry} instead.\n */\nconst OPTIONS_ARG_POSITION: Record<string, number> = {\n fetch: 2,\n getDocument: 1,\n getDocuments: 1,\n create: 1,\n createOrReplace: 1,\n createIfNotExists: 1,\n delete: 1,\n mutate: 1,\n action: 1,\n request: 0,\n};\n\n/** The request tag a call's options carried — `undefined` when absent. */\nfunction tagFromArgs(method: string, args: unknown[]): string | null | undefined {\n const position = OPTIONS_ARG_POSITION[method];\n if (position === undefined) return undefined;\n const options = args[position];\n if (typeof options !== \"object\" || options === null || !(\"tag\" in options)) return undefined;\n return (options as { tag?: string | null }).tag;\n}\n\n/** Client methods whose calls are recorded for `calls()` / `callCount()`. */\nconst RECORDED_METHODS = new Set([\n \"fetch\",\n \"getDocument\",\n \"getDocuments\",\n \"create\",\n \"createOrReplace\",\n \"createIfNotExists\",\n \"delete\",\n \"patch\",\n \"transaction\",\n \"mutate\",\n \"action\",\n \"releases\",\n \"createVersion\",\n \"unpublishVersion\",\n \"request\",\n]);\n\nexport interface TestClient {\n // --- query ---\n fetch: <T = unknown>(\n query: string,\n params?: Record<string, unknown>,\n options?: FetchOptions,\n ) => Promise<T>;\n getDocument: <T = SanityDocument>(id: string, options?: FetchOptions) => Promise<T | undefined>;\n getDocuments: <T = SanityDocument>(\n ids: string[],\n options?: FetchOptions,\n ) => Promise<(T | null)[]>;\n /**\n * Sanity-style HTTP request hook. Present iff the handle has an\n * acting identity — created with `accessControl`, or bound to a\n * registered token (`identities` + `token`). The engine's access\n * resolver pokes `/users/me` and the configured ACL path through\n * this — without it the engine throws on any verb that needs an\n * actor. Also serves the resource's history, ACL, lookup and Access API\n * endpoints, or the platform's refusal where the resource has none.\n */\n request?: <T>(opts: {\n url?: string;\n uri?: string;\n signal?: AbortSignal;\n tag?: string | null;\n }) => Promise<T>;\n\n /**\n * `@sanity/client`'s `getDataUrl(operation, path)`: `<resource\n * base>/<operation>/<path>` on a `resource`-configured handle,\n * `/data/<operation>/<dataset>/<path>` otherwise. Pass the result to\n * `request()` to reach e.g. the History API.\n */\n getDataUrl: (operation: string, path?: string) => string;\n\n // --- mutate ---\n create: <T extends SanityDocumentLike>(\n doc: T,\n options?: MutationOptions,\n ) => Promise<T & SystemFields>;\n createOrReplace: <T extends SanityDocumentLike>(\n doc: T,\n options?: MutationOptions,\n ) => Promise<T & SystemFields>;\n createIfNotExists: <T extends SanityDocumentLike>(\n doc: T,\n options?: MutationOptions,\n ) => Promise<T & SystemFields>;\n delete: (id: string, options?: MutationOptions) => Promise<{ _id: string } | null>;\n patch: (documentId: string) => PatchHandle;\n transaction: () => TransactionHandle;\n /**\n * Apply a batch of mutations in the raw array form (or a `patch()` /\n * `transaction()` builder) — mirrors `@sanity/client`'s `mutate()`.\n * Atomic: any failure rolls the whole batch back. The resolved shape\n * follows `options.returnDocuments` / `returnFirst` exactly like the\n * real client (default → {@link MultipleMutationResult}).\n */\n mutate: (\n operations: FakeMutation[] | PatchHandle | TransactionHandle,\n options?: TxnCommitOptions,\n ) => Promise<TxnCommitResult>;\n\n // --- actions / releases ---\n /**\n * Dispatch one or more release / version / document actions against the\n * store. Returns a synthetic `transactionId`. Multi-action arrays are\n * applied atomically — any failure rolls back the batch. The document\n * publish/unpublish actions run through the access gate and expose their\n * net draft/published mutations to `mutationGuard`; release/version actions\n * apply unhooked.\n */\n action: (action: Action | Action[], options?: ActionOptions) => Promise<ActionResult>;\n /** Helpers mirroring `@sanity/client`'s `client.releases.*` surface. */\n releases: ReleasesHandle;\n /** Helper for `sanity.action.document.version.create`. */\n createVersion: (params: {\n releaseId: string;\n publishedId: string;\n document: DocumentBody;\n }) => Promise<ActionResult>;\n /** Helper for `sanity.action.document.version.unpublish`. */\n unpublishVersion: (params: { releaseId: string; publishedId: string }) => Promise<ActionResult>;\n\n // --- config ---\n /**\n * Current `(projectId, dataset)` or `resource`, active perspective, and which API host\n * the handle models (`useProjectHostname`). `token` is present iff the\n * handle is bound to a registered identity.\n */\n config: () => {\n projectId: string;\n dataset: string;\n /** Present iff the handle is `resource`-configured. */\n resource?: TestClientResource;\n perspective: Perspective;\n requestTagPrefix?: string;\n token?: string;\n useProjectHostname: boolean;\n };\n /**\n * Return a new client handle pointing at a different `(projectId, dataset)`\n * within the SAME backing registry. Either field can be omitted to keep\n * the current value. Two clients produced by `withConfig` see each\n * other's writes if they point at the same `(projectId, dataset)`.\n */\n withConfig: (\n config: Partial<{\n projectId: string;\n dataset: string;\n /**\n * Point the sibling at a resource, named as `@sanity/client`'s\n * `resource` config names it. While set, it decides the store (over\n * `projectId` / `dataset`), as in `@sanity/client`. Pass `undefined`\n * to drop it and address `(projectId, dataset)` again.\n */\n resource: TestClientResource | undefined;\n perspective: Perspective;\n /** Prefix composed with request tags and used alone for untagged requests. */\n requestTagPrefix: string;\n /**\n * Model the global API host (`false`) instead of the project host\n * (default `true`). A global-hosted sibling's `/users/me` serves the\n * identity's account-global record (`globalUser`, falling back to the\n * project record) — mirroring `@sanity/client`, where a non-project\n * config routes requests to `api.sanity.io`.\n */\n useProjectHostname: boolean;\n /**\n * Rebind the acting identity (and its grants) on a sibling that shares\n * the same store — the test analogue of a token-scoped client. Lets two\n * actors write the same dataset, e.g. for `identity()`-based guards.\n * Mutually exclusive with `token`.\n */\n accessControl: TestClientAccessControl;\n /**\n * Bind the sibling to a token registered in the family's identity\n * registry (`createTestClient({ identities })` / `addIdentity`): its\n * `/users/me` serves that identity's record for the sibling's project\n * and host, its ACL path serves that identity's grants, and\n * reads/writes are gated through them — the token determines\n * identity, exactly like production. Unknown tokens throw. Mutually\n * exclusive with `accessControl`.\n */\n token: string;\n }>,\n ) => TestClient;\n\n // --- test helpers (not on real SanityClient) ---\n /** Replace the current dataset with the given documents. */\n seed: (documents: SanityDocumentLike[]) => void;\n /** Add documents to the current dataset without dropping existing ones. */\n add: (documents: SanityDocumentLike[]) => void;\n /** Drop all documents in the current dataset. */\n reset: () => void;\n /** Read-only snapshot of all documents in the current dataset. */\n snapshot: () => readonly SanityDocument[];\n /** Direct store access for the current dataset. */\n store: () => TestStore;\n /**\n * Simulate `count` concurrent edits to `id` in the current dataset: the\n * next `count` revision-guarded mutations (those passing `ifRevisionId`)\n * to that document throw a 409-shaped conflict, as if another client\n * committed between the caller's read and write. Mutations that don't\n * pass `ifRevisionId` are unaffected.\n *\n * Reach for this when the code under test reads a document's `_rev` and\n * commits with `ifRevisionId` internally — so you can't stage the race\n * by mutating the document first (the code would just re-read the new\n * revision and its commit would match). The counter is consumed one per\n * guarded commit, so `count` lets a test drive a recover-after-N-losses\n * path or exhaust a retry budget, deterministically and without relying\n * on promise interleaving.\n *\n * Note: this forces the conflict *outcome* — it short-circuits the\n * revision comparison rather than changing the stored `_rev`. The code\n * under test sees the identical 409 error a real lost race produces; the\n * stored revision is left untouched.\n */\n simulateConcurrentEdit: (id: string, count?: number) => void;\n /** List `(projectId, dataset)` pairs known in this registry. */\n datasets: () => { projectId: string; dataset: string }[];\n /** List every resource known in this registry — datasets as `{ type: \"dataset\", id: \"<projectId>.<dataset>\" }`. */\n resources: () => TestClientResource[];\n /**\n * Register a handler for a `@sanity/client` member the fake doesn't\n * implement (e.g. `listen`, `assets`). Accessing that member on the\n * client then returns the handler instead of throwing the\n * \"not implemented\" guard error. Shared across sibling `withConfig`\n * handles. Stubs fill gaps only — they don't shadow implemented methods.\n */\n stub: (name: string, handler: unknown) => void;\n /** The currently-registered stubs, keyed by member name. */\n stubs: () => Record<string, unknown>;\n /**\n * Register (or upsert) a token→identity entry in the family's shared\n * registry. Siblings already bound to `token` resolve lazily, so a\n * re-registration takes effect on them immediately. Registering an\n * identity does not bind it — pass `token` at `createTestClient` or\n * `withConfig({ token })` to act as it.\n */\n addIdentity: (token: string, identity: TestClientIdentity) => void;\n /** Snapshot of the registered token→identity entries. */\n identities: () => Record<string, TestClientIdentity>;\n /**\n * Every mutation committed through this client's registry, flattened\n * and in order — `create` / `createOrReplace` / `createIfNotExists` /\n * `delete` / `patch`, whether issued directly, via `transaction()`, or\n * via `mutate()`. The spy hook for \"assert exactly what writes my code\n * submitted\".\n */\n mutations: () => TxnMutation[];\n /**\n * Every committed transaction, in order. A direct mutation is recorded\n * as a one-mutation transaction. Each entry carries its synthetic\n * `transactionId` and the `(projectId, dataset)` it targeted.\n */\n transactions: () => MutationLogEntry[];\n /** Clear the recorded mutation / transaction log. */\n clearLog: () => void;\n /**\n * Every recorded method call, in order — the spy layer for \"was `fetch`\n * called, how many times, and with what params\". Records the\n * `@sanity/client`-surface methods (`fetch`, `getDocument`, `create`,\n * `patch`, `mutate`, `releases.*`, …), not the test-only helpers.\n * Pass a method name to filter (e.g. `calls(\"fetch\")`); namespace\n * methods are dotted (`calls(\"releases.create\")`). Shared across\n * `withConfig` siblings.\n */\n calls: (method?: string) => CallRecord[];\n /** Number of recorded calls, optionally filtered by method name. */\n callCount: (method?: string) => number;\n /** Clear the recorded call log. */\n clearCalls: () => void;\n}\n\nexport interface ReleasesHandle {\n create: (params: {\n releaseId: string;\n metadata?: Partial<ReleaseMetadata>;\n }) => Promise<ActionResult & { releaseId: string; metadata: ReleaseMetadata }>;\n edit: (params: {\n releaseId: string;\n patch: { set?: Record<string, unknown>; unset?: string[] };\n }) => Promise<ActionResult>;\n get: (params: { releaseId: string }) => Promise<ReleaseDocument | undefined>;\n fetchDocuments: (params: { releaseId: string }) => Promise<SanityDocument[]>;\n schedule: (params: { releaseId: string; publishAt: string }) => Promise<ActionResult>;\n unschedule: (params: { releaseId: string }) => Promise<ActionResult>;\n publish: (params: { releaseId: string }) => Promise<ActionResult>;\n archive: (params: { releaseId: string }) => Promise<ActionResult>;\n unarchive: (params: { releaseId: string }) => Promise<ActionResult>;\n delete: (params: { releaseId: string }) => Promise<ActionResult>;\n}\n\nexport interface MutationOptions {\n ifRevisionId?: string;\n /**\n * Shape the resolved value, mirroring `@sanity/client`:\n * - default → the mutated document\n * - `returnDocuments: false` → an id-shaped mutation result\n * ({@link SingleMutationResult} / {@link MultipleMutationResult})\n * - `returnFirst: false` with documents → an array\n *\n * The static return type reflects the default (the document); pass a\n * type argument or cast when requesting an id-shaped result.\n */\n returnDocuments?: boolean;\n returnFirst?: boolean;\n /** Override the synthetic transaction id used in the result and log. */\n transactionId?: string;\n /** When `true`, validate + shape the result but don't persist. */\n dryRun?: boolean;\n /** Accepted for signature parity; not simulated. */\n visibility?: \"sync\" | \"async\" | \"deferred\";\n /** Accepted for signature parity; not simulated. */\n autoGenerateArrayKeys?: boolean;\n /**\n * Request tag, recorded on the {@link CallRecord} and the\n * {@link MutationLogEntry} for assertions. `null` mirrors\n * `@sanity/client`'s explicit-suppression form and is recorded as-is.\n * No other behavior is simulated.\n */\n tag?: string | null;\n}\n\n/** Options for `action()`. Only `tag` is meaningful to the fake. */\nexport interface ActionOptions {\n /** Request tag, recorded on the {@link CallRecord} (`null` = explicit suppression). */\n tag?: string | null;\n}\n\nexport interface SystemFields {\n _rev: string;\n _createdAt: string;\n _updatedAt: string;\n}\n\nexport interface CreateTestClientOptions {\n /** Default project id. Defaults to `\"test\"`. */\n projectId?: string;\n /** Default dataset name. Defaults to `\"test\"`. */\n dataset?: string;\n /** Documents to seed into the default `(projectId, dataset)` store. */\n documents?: SanityDocumentLike[];\n /**\n * Optional: seed multiple datasets at once. Keys are `\"projectId:dataset\"`\n * pairs. Useful when workflows span multiple datasets.\n */\n datasets?: Record<string, SanityDocumentLike[]>;\n /**\n * Optional: seed any resource type. Keys are `\"<type>:<id>\"`, the\n * `resource` config's two fields: `\"media-library:ml1\"`, `\"canvas:c1\"`,\n * `\"dashboard:d1\"`, or `\"dataset:<projectId>.<dataset>\"` (the same store\n * as `datasets[\"<projectId>:<dataset>\"]`).\n */\n resources?: Record<string, SanityDocumentLike[]>;\n /**\n * The resource the root handle points at, named exactly as\n * `@sanity/client`'s `resource` config names it. While set, it decides\n * the store (over `projectId` / `dataset`), and each capability the\n * platform lacks on that resource type answers with the platform's\n * refusal. `withConfig({ resource })` mints siblings on other resources.\n */\n resource?: TestClientResource;\n /**\n * The organization every resource of this client belongs to — what\n * `GET /media-libraries/<id>`, `/canvases/<id>`, `/dashboards/<id>` and\n * `/projects/<projectId>` answer as `organizationId`.\n */\n organizationId?: string;\n /**\n * Opt-in access-control simulation. When set, the client exposes\n * `request({ url })` that serves `/users/me` (host-aware), the\n * configured ACL path, `/projects/<projectId>` (members), and\n * `/projects/<projectId>/users/<ids>`,\n * AND every mutation is gated through the supplied `grants`\n * — writes that the grants don't permit throw a `WriteAccessDeniedError`\n * shaped like Sanity's 403. Lets tests demonstrate the full\n * \"engine soft-gate + lake hard-gate\" contract end-to-end without\n * a real HTTP round-trip.\n *\n * When omitted, `request` is undefined and all writes proceed\n * unconditionally — the legacy behavior.\n */\n accessControl?: TestClientAccessControl;\n /**\n * Token→identity registry, shared by the whole `withConfig` family:\n * multiple identities over one store, keyed by the token a handle is\n * bound to — the token determines identity, exactly like production.\n *\n * ```ts\n * const client = createTestClient({\n * token: \"t-admin\",\n * identities: {\n * \"t-admin\": { user: { id: \"admin\", roles: [{ name: \"administrator\" }] }, grants: WILDCARD },\n * \"t-bob\": { user: { id: \"bob\", roles: [{ name: \"editor\" }] }, grants: READ_ONLY },\n * },\n * });\n * const bob = client.withConfig({ token: \"t-bob\" });\n * // same store; bob's /users/me → bob; bob's ACL path → bob's grants\n * ```\n *\n * A token-bound handle behaves exactly like one configured with\n * `accessControl` derived from its identity (identity serving + grant\n * gating; the ACL path defaults per `(projectId, dataset)`). Register\n * more identities later with `addIdentity`.\n */\n identities?: Record<string, TestClientIdentity>;\n /**\n * Token the root handle is bound to — must name an entry in\n * `identities` (unknown tokens throw). Omit to leave the root handle\n * identity-free (a bare factory for token-bound siblings): its\n * `request` is undefined and its reads/writes are ungated. Mutually\n * exclusive with `accessControl`.\n */\n token?: string;\n /**\n * Pluggable write interceptor — runs once for each document's net change in\n * a commit. Document publish/unpublish actions expose their underlying\n * draft/published mutations; release/version actions remain unhooked. Return\n * denial descriptors to reject atomically with `GuardDeniedError`; a\n * thrown callback error propagates unchanged. The client owns no policy.\n * Inherited by `withConfig` siblings. See {@link MutationGuard}.\n */\n mutationGuard?: MutationGuard;\n /**\n * Default perspective applied to reads. Per-fetch `options.perspective`\n * overrides this. `withConfig({ perspective })` returns a sibling\n * handle with a different default. Defaults to `\"raw\"` so existing\n * tests stay green.\n */\n perspective?: Perspective;\n /** Prefix composed with request tags and used alone for untagged requests. */\n requestTagPrefix?: string;\n /**\n * Which API host the root handle models: the project host (default\n * `true`) or the global host (`false`) — same flag as the real\n * `createClient` config. A global-hosted handle's `/users/me` serves\n * the identity's account-global record. `withConfig({ useProjectHostname })`\n * mints siblings on the other host.\n */\n useProjectHostname?: boolean;\n}\n\nexport type { Perspective } from \"./perspective.ts\";\nexport type { ResourceType, TestClientResource } from \"./resources.ts\";\nexport type {\n Action,\n ActionResult,\n DocumentAction,\n DocumentBody,\n ReleaseAction,\n ReleaseDocument,\n ReleaseMetadata,\n ReleaseState,\n ReleaseType,\n VersionAction,\n} from \"./actions.ts\";\n\nexport type {\n TestClientAccessControl,\n TestClientIdentity,\n DocumentValuePermission,\n} from \"./access-control.ts\";\nexport { WriteAccessDeniedError } from \"./access-control.ts\";\n\nconst DEFAULT_PROJECT_ID = \"test\";\nconst DEFAULT_DATASET = \"test\";\n\n/** One resource's store and the history of every write committed to it. */\ninterface RegistryEntry {\n resource: TestClientResource;\n store: TestStore;\n history: DocumentHistory;\n}\n\ntype Registry = Map<string, RegistryEntry>;\n\n/**\n * Shared backing context for one `createTestClient()` call and all the\n * `withConfig` siblings it spawns: the multi-dataset store registry, the\n * append-only mutation log, the stub registry, and the token→identity\n * registry. Sibling handles see each other's writes, log entries, stubs,\n * and registered identities.\n */\ninterface Backend {\n registry: Registry;\n /** See {@link CreateTestClientOptions.organizationId}. */\n organizationId: string | undefined;\n log: MutationLogEntry[];\n stubs: Map<string, unknown>;\n calls: CallRecord[];\n /**\n * Pending simulated concurrent edits, keyed per (resource, document id).\n * Each entry is a count of upcoming revision-guarded mutations that\n * should 409 as if another client raced them. See\n * {@link TestClient.simulateConcurrentEdit}.\n */\n concurrentEdits: Map<string, number>;\n /**\n * Token→identity registry. Token-bound handles resolve their acting\n * identity from here on every access, so `addIdentity` upserts take\n * effect on already-minted siblings. See\n * {@link CreateTestClientOptions.identities}.\n */\n identities: Map<string, TestClientIdentity>;\n /**\n * The registry-wide commit lock: every write path queues on this chain.\n * The real Content Lake validates and applies a mutation atomically\n * server-side; the fake's write paths await user hooks while staging or\n * committing a write. Without serialization two in-flight guarded writes\n * can both validate against the same `_rev` and the loser silently clobbers\n * the winner.\n */\n writeChain: Promise<void>;\n}\n\n/**\n * Queue one write on the backend's commit lock. Corollary of the lock: a\n * gate / `mutationGuard` hook that issues another write through the same\n * registry deadlocks — hooks are read-only by contract.\n */\nfunction serializeWrite<T>(backend: Backend, run: () => Promise<T>): Promise<T> {\n const result = backend.writeChain.then(run);\n backend.writeChain = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n}\n\n/** Throw unless `token` names a registered identity — every token-binding site validates eagerly. */\nfunction requireIdentity(backend: Backend, token: string, site: string): void {\n if (backend.identities.has(token)) return;\n const known = [...backend.identities.keys()];\n throw fakeError(\n `${site}: unknown token \"${token}\" — no identity is registered for it. ` +\n (known.length > 0\n ? `Registered tokens: ${known.map((t) => `\"${t}\"`).join(\", \")}. `\n : \"No identities are registered. \") +\n \"Register identities via createTestClient({ identities }) or client.addIdentity(token, identity).\",\n );\n}\n\n/** Throw when a single config names both identity mechanisms — a handle acts as exactly one. */\nfunction rejectAmbiguousIdentity(\n site: string,\n config: { token?: string; accessControl?: TestClientAccessControl },\n): void {\n if (config.token !== undefined && config.accessControl !== undefined) {\n throw fakeError(\n `${site}: \\`token\\` and \\`accessControl\\` are mutually exclusive — a handle acts either as ` +\n \"a registered identity (token) or as an inline accessControl config, not both.\",\n );\n }\n}\n\nconst registryKey = (resource: TestClientResource) => `${resource.type}:${resource.id}`;\n\n/** The dataset resource a `(projectId, dataset)` pair names. */\nconst datasetResource = (projectId: string, dataset: string): TestClientResource => ({\n type: \"dataset\",\n id: `${projectId}.${dataset}`,\n});\n\n/** Split a `datasets` seed key, `\"<projectId>:<dataset>\"`. */\nconst parseDatasetKey = (key: string): { projectId: string; dataset: string } => {\n const [projectId, dataset] = key.split(\":\") as [string, string];\n return { projectId, dataset };\n};\n\n/** Split a `resources` seed key, `\"<type>:<id>\"`, and validate it like a `resource` config. */\nconst parseResourceKey = (key: string): TestClientResource => {\n const separator = key.indexOf(\":\");\n const resource = {\n type: key.slice(0, separator),\n id: key.slice(separator + 1),\n } as TestClientResource;\n if (separator <= 0 || resource.id.length === 0) {\n throw fakeError(\n `createTestClient(): \\`resources\\` keys are \"<type>:<id>\" (e.g. \"media-library:ml1\"); got \"${key}\".`,\n );\n }\n validateResource(resource);\n return resource;\n};\n\n/** The registry entry for `resource`, created empty on first use. */\nfunction ensureEntry(registry: Registry, resource: TestClientResource): RegistryEntry {\n const key = registryKey(resource);\n let entry = registry.get(key);\n if (entry === undefined) {\n entry = { resource, store: createTestStore(), history: createDocumentHistory() };\n registry.set(key, entry);\n }\n return entry;\n}\n\n/**\n * Create a fresh, isolated test client backed by a private multi-dataset\n * registry. `withConfig` returns a sibling handle into the same registry.\n *\n * ```\n * const client = createTestClient({\n * datasets: {\n * \"test:definitions\": [...],\n * \"test:instances\": [...],\n * },\n * });\n * const defs = client.withConfig({ dataset: \"definitions\" });\n * const inst = client.withConfig({ dataset: \"instances\" });\n * ```\n */\nexport function createTestClient(options: CreateTestClientOptions = {}): TestClient {\n const defaultProjectId = options.projectId ?? DEFAULT_PROJECT_ID;\n const defaultDataset = options.dataset ?? DEFAULT_DATASET;\n\n const backend: Backend = {\n registry: new Map(),\n organizationId: options.organizationId,\n log: [],\n stubs: new Map(),\n calls: [],\n concurrentEdits: new Map(),\n identities: new Map(Object.entries(options.identities ?? {})),\n writeChain: Promise.resolve(),\n };\n const registry = backend.registry;\n\n rejectAmbiguousIdentity(\"createTestClient()\", options);\n if (options.token !== undefined) {\n requireIdentity(backend, options.token, \"createTestClient()\");\n }\n\n if (options.resource !== undefined) validateResource(options.resource);\n\n // Seed defaults. `documents` seeds the root handle's own store.\n if (options.documents) {\n const root = options.resource ?? datasetResource(defaultProjectId, defaultDataset);\n ensureEntry(registry, root).store.seed(options.documents);\n }\n if (options.datasets) {\n for (const [key, docs] of Object.entries(options.datasets)) {\n const { projectId, dataset } = parseDatasetKey(key);\n ensureEntry(registry, datasetResource(projectId, dataset)).store.seed(docs);\n }\n }\n if (options.resources) {\n for (const [key, docs] of Object.entries(options.resources)) {\n ensureEntry(registry, parseResourceKey(key)).store.seed(docs);\n }\n }\n\n return makeHandle({\n backend,\n projectId: defaultProjectId,\n dataset: defaultDataset,\n resource: options.resource,\n accessControl: options.accessControl,\n perspective: options.perspective ?? \"raw\",\n requestTagPrefix: options.requestTagPrefix,\n mutationGuard: options.mutationGuard,\n token: options.token,\n useProjectHostname: options.useProjectHostname ?? true,\n });\n}\n\n/** Affected ids in order, de-duplicated — the `documentIds` of a result. */\nfunction dedupeIds(results: { id: string }[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const r of results) {\n if (!seen.has(r.id)) {\n seen.add(r.id);\n out.push(r.id);\n }\n }\n return out;\n}\n\n/**\n * Resolve a mutation's return value the way `@sanity/client` does, from\n * already-resolved `returnDocuments` / `returnFirst` booleans (callers\n * apply their own defaults: document-first for create-family,\n * ids-for-all for transaction / mutate).\n */\nfunction formatMutationResult(\n documents: SanityDocument[],\n results: { id: string; operation: MutationOperation }[],\n transactionId: string,\n returnDocuments: boolean,\n returnFirst: boolean,\n): TxnCommitResult {\n if (returnDocuments) {\n return returnFirst ? (documents[0] as SanityDocument) : documents;\n }\n if (returnFirst) {\n return {\n transactionId,\n documentId: results[0]?.id as string,\n results,\n } satisfies SingleMutationResult;\n }\n return {\n transactionId,\n documentIds: dedupeIds(results),\n results,\n } satisfies MultipleMutationResult;\n}\n\n/** Convert one raw `mutate()` operation object into a {@link TxnMutation}. */\nfunction toSingleTxnMutation(op: FakeMutation): TxnMutation {\n if (\"create\" in op) return { kind: \"create\", doc: op.create };\n if (\"createOrReplace\" in op) return { kind: \"createOrReplace\", doc: op.createOrReplace };\n if (\"createIfNotExists\" in op) return { kind: \"createIfNotExists\", doc: op.createIfNotExists };\n if (\"delete\" in op) return { kind: \"delete\", id: op.delete.id };\n\n const p = op.patch;\n const ops: PatchOp[] = [];\n if (p.set !== undefined) ops.push({ kind: \"set\", props: p.set });\n if (p.setIfMissing !== undefined) ops.push({ kind: \"setIfMissing\", props: p.setIfMissing });\n if (p.unset !== undefined) ops.push({ kind: \"unset\", paths: p.unset });\n if (p.inc !== undefined) ops.push({ kind: \"inc\", props: p.inc });\n if (p.dec !== undefined) ops.push({ kind: \"dec\", props: p.dec });\n if (p.insert !== undefined) {\n const { before, after, replace, items } = p.insert;\n const position = before !== undefined ? \"before\" : after !== undefined ? \"after\" : \"replace\";\n const anchor = before ?? after ?? replace;\n if (anchor === undefined) {\n throw fakeError(\n \"mutate(): a `patch.insert` operation must name exactly one anchor — `before`, `after`, \" +\n 'or `replace` — pointing at an array path (e.g. \"tags[-1]\" or `tags[_key==\"x\"]`), ' +\n \"alongside `items`. None was set.\",\n );\n }\n ops.push({ kind: \"insert\", position, anchor, items });\n }\n return {\n kind: \"patch\",\n documentId: p.id,\n ops,\n ...(p.ifRevisionID !== undefined ? { ifRev: p.ifRevisionID } : {}),\n };\n}\n\n/** Normalise `mutate()` input (raw array, Patch, or Transaction) to mutations. */\nfunction toTxnMutations(\n operations: FakeMutation[] | PatchHandle | TransactionHandle,\n): TxnMutation[] {\n if (Array.isArray(operations)) return operations.map(toSingleTxnMutation);\n // Both builders expose toJSON(); a Transaction yields an array, a Patch\n // yields a single `{ id, ops, ifRev? }`.\n const json = operations.toJSON();\n if (Array.isArray(json)) return json;\n return [\n {\n kind: \"patch\",\n documentId: json.id,\n ops: json.ops,\n ...(json.ifRev !== undefined ? { ifRev: json.ifRev } : {}),\n },\n ];\n}\n\ninterface MakeHandleArgs {\n backend: Backend;\n projectId: string;\n dataset: string;\n /** The `resource` config, when set; it decides the store over `(projectId, dataset)`. */\n resource: TestClientResource | undefined;\n accessControl: TestClientAccessControl | undefined;\n perspective: Perspective;\n requestTagPrefix: string | undefined;\n mutationGuard: MutationGuard | undefined;\n token: string | undefined;\n /**\n * Which API host this handle models: the project host (true, default)\n * or the global host (`withConfig({ useProjectHostname: false })`).\n * Decides which user record `/users/me` serves — project-scoped vs\n * account-global — exactly like production host routing.\n */\n useProjectHostname: boolean;\n}\n\nfunction makeHandle(handleArgs: MakeHandleArgs): TestClient {\n const {\n backend,\n resource,\n accessControl,\n perspective,\n requestTagPrefix,\n mutationGuard,\n token,\n useProjectHostname,\n } = handleArgs;\n const { registry } = backend;\n // A dataset resource names its own project and dataset, as in `@sanity/client`.\n const { projectId, dataset } =\n resource?.type === \"dataset\" ? datasetParts(resource.id) : handleArgs;\n /** The resource this handle reads and writes: its `resource` config, else its dataset. */\n const target = resource ?? datasetResource(projectId, dataset);\n\n /**\n * The handle's acting identity, if any. Token-bound handles resolve\n * from the shared registry on every access — so an `addIdentity`\n * upsert takes effect on already-minted siblings; `accessControl`\n * handles use their fixed config. Registry identities resolve their\n * project-scoped record for THIS handle's project (`usersByProject`\n * fallback `user`), so siblings in different projects see different\n * project-scoped ids for the same token — as in production.\n */\n const activeAccess = (): TestClientAccessControl | undefined => {\n if (token === undefined) return accessControl;\n const identity = backend.identities.get(token);\n if (identity === undefined) {\n // Bind-time validation makes this unreachable today; guard anyway\n // so a future identity-removal API fails loudly.\n throw fakeError(`no identity is registered for token \"${token}\" (was it removed?).`);\n }\n return {\n // Per-project records apply only where the resource lives in a project.\n currentUser:\n capabilitiesOf(target.type).identity === \"project\"\n ? projectUserOf(identity, projectId)\n : identity.user,\n ...(identity.globalUser !== undefined ? { globalUser: identity.globalUser } : {}),\n grants: identity.grants,\n ...(identity.access !== undefined ? { access: identity.access } : {}),\n };\n };\n\n /** Non-optional view for sites that only exist when an identity is bound. */\n const requireActiveAccess = (): TestClientAccessControl => {\n const access = activeAccess();\n if (access === undefined) {\n throw fakeError(\"this handle has no acting identity (no token or accessControl bound).\");\n }\n return access;\n };\n const capabilities = capabilitiesOf(target.type);\n /** How error messages name the target, e.g. `dataset \"test:test\"`. */\n const targetLabel = capabilities.label(target.id);\n const { store, history } = ensureEntry(registry, target);\n\n /** Key into {@link Backend.concurrentEdits} for a doc in this resource. */\n const concurrentEditKey = (id: string): string => registryKey(target) + \" \" + id;\n\n /**\n * The id grant filters see as `identity()` and history records as the\n * author: the project-scoped record on a dataset, the account-global\n * one on every other resource type.\n */\n const actorId = (): string | undefined => {\n const access = activeAccess();\n if (access === undefined) return undefined;\n return capabilities.identity === \"global\"\n ? (access.globalUser ?? access.currentUser).id\n : access.currentUser.id;\n };\n\n /** The store before the write in flight; see {@link write}. */\n let beforeWrite: Map<string, SanityDocument> | undefined;\n\n /**\n * Queue a write on the commit lock and remember the store's images before\n * it, so {@link commitHistory} can record exactly what the write changed.\n */\n const write = <T>(run: () => Promise<T>): Promise<T> =>\n serializeWrite(backend, async () => {\n beforeWrite = new Map(store.all().map((doc) => [doc._id, doc]));\n try {\n return await run();\n } finally {\n beforeWrite = undefined;\n }\n });\n\n /** Record the write in flight in the resource's history, under `transactionId`. */\n const commitHistory = (transactionId: string): void => {\n if (beforeWrite === undefined) return;\n history.commit({ transactionId, author: actorId(), before: beforeWrite, after: store.all() });\n };\n\n /** Append a committed transaction to the shared log. */\n const record = (transactionId: string, mutations: TxnMutation[], tag?: string | null): void => {\n const effectiveTag = effectiveRequestTag(tag);\n commitHistory(transactionId);\n backend.log.push({\n transactionId,\n projectId,\n dataset,\n ...(resource !== undefined ? { resource } : {}),\n mutations,\n ...(effectiveTag !== undefined ? { tag: effectiveTag } : {}),\n });\n };\n\n const effectiveRequestTag = (tag: string | null | undefined): string | null | undefined => {\n if (requestTagPrefix === undefined) return tag;\n return tag ? `${requestTagPrefix}.${tag}` : requestTagPrefix;\n };\n\n /**\n * Shape a single-document write result. Create-family methods default\n * to returning the document (first), unlike transaction / mutate.\n */\n const writeResult = (\n documents: SanityDocument[],\n results: { id: string; operation: MutationOperation }[],\n transactionId: string,\n opts: MutationOptions | undefined,\n ): TxnCommitResult =>\n formatMutationResult(\n documents,\n results,\n transactionId,\n opts?.returnDocuments ?? true,\n opts?.returnFirst ?? true,\n );\n\n const promote = (doc: SanityDocumentLike): SanityDocument & SystemFields => {\n const now = new Date().toISOString();\n if (typeof doc._id !== \"string\" || doc._id.length === 0) {\n throw fakeError(\n `Cannot store a document without a non-empty string \\`_id\\` (got ${JSON.stringify(doc._id)}). ` +\n `Unlike @sanity/client, this fake never auto-generates ids — give every document an explicit _id.`,\n );\n }\n if (typeof doc._type !== \"string\" || doc._type.length === 0) {\n throw fakeError(\n `Document \"${doc._id}\" is missing a non-empty string \\`_type\\` (got ${JSON.stringify(doc._type)}). ` +\n `Every Sanity document needs a _type.`,\n );\n }\n return {\n ...doc,\n _id: doc._id,\n _type: doc._type,\n _rev: cryptoRandomKey(),\n _createdAt: doc._createdAt ?? now,\n _updatedAt: now,\n } as SanityDocument & SystemFields;\n };\n\n const checkRevision = (id: string, ifRevisionId: string | undefined) => {\n if (ifRevisionId === undefined) return;\n // A pending simulated concurrent edit beats the real revision check —\n // the guarded write loses the race even if its _rev currently matches.\n const editKey = concurrentEditKey(id);\n const pendingEdits = backend.concurrentEdits.get(editKey) ?? 0;\n if (pendingEdits > 0) {\n backend.concurrentEdits.set(editKey, pendingEdits - 1);\n throw fakeError(\n `ifRevisionId check failed: document \"${id}\" was concurrently edited (simulated via ` +\n `simulateConcurrentEdit) — the revision-guarded mutation lost the race; re-read and retry. ` +\n `(This mirrors Sanity's optimistic-locking 409.)`,\n );\n }\n const existing = store.get(id);\n if (existing === undefined) {\n throw fakeError(\n `ifRevisionId check failed: document \"${id}\" does not exist, so its revision cannot match ` +\n `the expected _rev=\"${ifRevisionId}\". Drop ifRevisionId when creating a new document, or ` +\n `seed/create the document before guarding on its revision.`,\n );\n }\n if (existing._rev !== ifRevisionId) {\n throw fakeError(\n `ifRevisionId check failed: document \"${id}\" is at _rev=\"${existing._rev}\" but the mutation ` +\n `required _rev=\"${ifRevisionId}\". The document changed since you read it — re-read it and ` +\n `retry. (This mirrors Sanity's optimistic-locking 409.)`,\n );\n }\n };\n\n /**\n * Gate a mutation through the configured `accessControl.grants`. No-op\n * when accessControl is absent (legacy bench behavior). On denial\n * throws `WriteAccessDeniedError` — same failure mode tests get from\n * a real Sanity 403.\n *\n * Pass the doc as-it-would-be at evaluation time: for `create` /\n * `createOrReplace`-as-create / `createIfNotExists`, that's the new\n * doc with its incoming fields; for `update` / `patch` / `delete`,\n * that's the existing doc (the grant filter identifies what the\n * actor can touch).\n */\n const gate = async (\n document: SanityDocumentLike & { _id?: string; _type?: string },\n permission: DocumentValuePermission,\n ): Promise<void> => {\n const access = activeAccess();\n if (access === undefined) return;\n await enforceWriteAccess({\n document,\n grants: access.grants,\n permission: document._id !== undefined && isSystemId(document._id) ? \"manage\" : permission,\n userId: actorId() as string,\n });\n };\n\n const guardGate = async (contexts: readonly MutationGuardImageContext[]): Promise<void> => {\n const identity = actorId();\n await enforceMutationGuards(\n mutationGuard,\n contexts.map((context) => ({ ...context, identity })),\n );\n };\n\n const guardOne = async (\n action: MutationGuardContext[\"action\"],\n before: MutationGuardContext[\"before\"],\n after: MutationGuardContext[\"after\"],\n ): Promise<void> => {\n await guardGate([{ action, before, after }]);\n };\n\n /**\n * Read-gate predicate. Sanity surfaces invisible docs as \"doesn't\n * exist\" on the wire (404 / null) rather than throwing 403 — so\n * reads filter rather than refuse.\n *\n * - `fetch` filters the dataset before running GROQ.\n * - `getDocument` / `getDocuments` return null for invisible docs.\n * - `snapshot` is a test-helper that bypasses the read gate\n * intentionally (tests need raw inspection).\n *\n * Returns `true` when accessControl is absent (no gating).\n */\n const canRead = async (\n document: SanityDocumentLike & { _id?: string; _type?: string },\n ): Promise<boolean> => {\n const access = activeAccess();\n if (access === undefined) return true;\n return canAccess({\n document,\n grants: access.grants,\n permission: \"read\",\n userId: actorId() as string,\n });\n };\n\n const applyPatch = async (\n documentId: string,\n ops: PatchOp[],\n ifRev: string | undefined,\n options: { evaluateGuard?: boolean } = {},\n ): Promise<SanityDocument> => {\n const existing = store.get(documentId);\n if (existing === undefined) {\n throw fakeError(\n `Cannot patch document \"${documentId}\": not found — no document with that _id exists in ` +\n `${targetLabel}. patch() requires the target to already exist; use ` +\n `createOrReplace() / createIfNotExists() to upsert, or seed the document first.`,\n );\n }\n checkRevision(documentId, ifRev);\n // Gate against the existing doc — that's what the grant filter\n // matches: \"what docs can this actor update?\".\n await gate(existing, \"update\");\n const patched = applyPatchOps(existing, ops);\n const now = new Date().toISOString();\n const next: SanityDocument = {\n ...patched,\n _rev: cryptoRandomKey(),\n _updatedAt: now,\n };\n if (options.evaluateGuard !== false) await guardOne(\"update\", existing, next);\n store.put(next);\n return next;\n };\n\n const applyTransaction = async (\n mutations: TxnMutation[],\n txnOptions?: TxnCommitOptions,\n ): Promise<TxnCommitResult> => {\n const transactionId = txnOptions?.transactionId ?? cryptoRandomKey();\n const snapshot = new Map<string, SanityDocument | undefined>();\n const touchedIds: string[] = [];\n const operations: { id: string; operation: MutationOperation }[] = [];\n\n const remember = (id: string) => {\n if (!snapshot.has(id)) snapshot.set(id, store.get(id));\n };\n\n try {\n for (const mut of mutations) {\n switch (mut.kind) {\n case \"create\": {\n remember(mut.doc._id as string);\n const promoted = promote(mut.doc);\n if (store.get(promoted._id) !== undefined) {\n throw fakeError(\n `create() failed: a document with _id \"${promoted._id}\" already exists in ` +\n `${targetLabel}. Use createOrReplace() to overwrite it, or ` +\n `createIfNotExists() to leave the existing one untouched.`,\n );\n }\n await gate(promoted, \"create\");\n store.put(promoted);\n touchedIds.push(promoted._id);\n operations.push({ id: promoted._id, operation: \"create\" });\n break;\n }\n case \"createOrReplace\": {\n remember(mut.doc._id as string);\n const promoted = promote(mut.doc);\n const existing = store.get(promoted._id);\n // For an existing doc, \"update\" is the relevant permission;\n // for a net-new one, \"create\".\n await gate(existing ?? promoted, existing !== undefined ? \"update\" : \"create\");\n const wasPresent = existing !== undefined;\n store.put(promoted);\n touchedIds.push(promoted._id);\n operations.push({ id: promoted._id, operation: wasPresent ? \"update\" : \"create\" });\n break;\n }\n case \"createIfNotExists\": {\n remember(mut.doc._id as string);\n if (store.get(mut.doc._id as string) === undefined) {\n const promoted = promote(mut.doc);\n await gate(promoted, \"create\");\n store.put(promoted);\n touchedIds.push(promoted._id);\n operations.push({ id: promoted._id, operation: \"create\" });\n }\n break;\n }\n case \"delete\": {\n remember(mut.id);\n const existing = store.get(mut.id);\n // Sanity's permission model has no separate \"delete\" —\n // delete is gated as an update against the existing doc.\n if (existing !== undefined) await gate(existing, \"update\");\n const removed = store.remove(mut.id);\n if (removed) {\n touchedIds.push(mut.id);\n operations.push({ id: mut.id, operation: \"delete\" });\n }\n break;\n }\n case \"patch\": {\n remember(mut.documentId);\n // applyPatch already gates internally.\n await applyPatch(mut.documentId, mut.ops, mut.ifRev, { evaluateGuard: false });\n touchedIds.push(mut.documentId);\n operations.push({ id: mut.documentId, operation: \"update\" });\n break;\n }\n }\n }\n const contexts = mutationContexts(\n [...snapshot].map(([id, before]) => ({ before, after: store.get(id) })),\n );\n await guardGate(contexts);\n } catch (error) {\n for (const [id, doc] of snapshot) {\n if (doc === undefined) {\n store.remove(id);\n } else {\n store.put(doc);\n }\n }\n throw error;\n }\n\n // A committed transaction is logged whole; a single-mutation form\n // (create / patch / mutate of one op) lands as a one-entry transaction.\n record(transactionId, mutations, txnOptions?.tag);\n\n const documents = touchedIds\n .map((id) => store.get(id))\n .filter((d): d is SanityDocument => d !== undefined);\n // Transaction / mutate default to the id-shaped `MultipleMutationResult`,\n // matching `@sanity/client` (create-family defaults differ).\n return formatMutationResult(\n documents,\n operations,\n transactionId,\n txnOptions?.returnDocuments ?? false,\n txnOptions?.returnFirst ?? false,\n );\n };\n\n /**\n * The action-API write path, on the same commit lock as every mutation.\n * Document publish/unpublish actions run through the access gate and expose\n * their net draft/published mutations to `mutationGuard`; release/version\n * actions apply unhooked.\n */\n const runAction = async (actions: Action | Action[]): Promise<ActionResult> => {\n // Refused before the write lock, as the platform refuses before applying anything.\n const request = {\n method: \"POST\" as const,\n path: `${capabilities.dataBase(target.id)}/actions`,\n };\n if (capabilities.actions !== \"served\") throw capabilities.actions(request);\n for (const action of Array.isArray(actions) ? actions : [actions]) {\n const refused = capabilities.refusedActions[action.actionType];\n if (refused !== undefined) throw refused(request);\n }\n return write(async () => {\n const result = await applyActions(store, actions, { gate, guard: guardGate });\n commitHistory(result.transactionId);\n return result;\n });\n };\n\n /**\n * Filter the store's docs to only those `canRead` returns true\n * for. When accessControl is unset, returns the full set (no\n * filtering cost). Used by `fetch` so GROQ runs against the\n * actor-visible subset, matching Sanity's per-token query behavior.\n */\n const accessVisibleDocs = async (): Promise<SanityDocument[]> => {\n const all = store.all();\n if (activeAccess() === undefined) return all;\n const out: SanityDocument[] = [];\n for (const doc of all) {\n if (await canRead(doc)) out.push(doc);\n }\n return out;\n };\n\n /**\n * Apply both access-control gating and perspective layering. The order\n * matters: access first (an invisible doc shouldn't even be a candidate\n * for perspective layering), perspective second (which can hide / merge\n * docs that the actor would otherwise see).\n */\n const visibleDocs = async (override?: Perspective): Promise<SanityDocument[]> => {\n const accessible = await accessVisibleDocs();\n return applyPerspective(accessible, override ?? perspective);\n };\n\n const client: TestClient = {\n fetch: async (query, params, opts) => {\n const start = Date.now();\n const tree = parse(query, params ? { params } : undefined);\n const value = await evaluate(tree, {\n dataset: await visibleDocs(opts?.perspective),\n ...(params !== undefined ? { params } : {}),\n });\n const result = await value.get();\n if (opts?.filterResponse === false) {\n const ms = Date.now() - start;\n const raw = opts.returnQuery === false ? { ms, result } : { query, ms, result };\n return raw as never;\n }\n return result as never;\n },\n\n getDocument: async (id, opts) => {\n const effective = opts?.perspective ?? perspective;\n if (effective === \"raw\") {\n const doc = store.get(id);\n if (doc === undefined) return undefined;\n if (!(await canRead(doc))) return undefined;\n return doc as never;\n }\n const view = await visibleDocs(effective);\n const found = resolveDocument(view, id, effective);\n return (found ?? undefined) as never;\n },\n\n getDocuments: async (ids, opts) => {\n const effective = opts?.perspective ?? perspective;\n if (effective === \"raw\") {\n const result: (SanityDocument | null)[] = [];\n for (const id of ids) {\n const doc = store.get(id);\n if (doc === undefined) result.push(null);\n else if (await canRead(doc)) result.push(doc);\n else result.push(null);\n }\n return result as never;\n }\n const view = await visibleDocs(effective);\n return ids.map((id) => resolveDocument(view, id, effective) ?? null) as never;\n },\n\n getDataUrl: (operation, path) => dataUrl({ resource, dataset, operation, path }),\n\n create: (doc, opts) =>\n write(async () => {\n checkRevision(doc._id as string, opts?.ifRevisionId);\n if (store.get(doc._id as string) !== undefined) {\n throw fakeError(\n `create() failed: a document with _id \"${doc._id}\" already exists in ` +\n `${targetLabel}. Use createOrReplace() to overwrite it, or ` +\n `createIfNotExists() to leave the existing one untouched.`,\n );\n }\n const promoted = promote(doc);\n await gate(promoted, \"create\");\n await guardOne(\"create\", null, promoted);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n if (opts?.dryRun !== true) {\n store.put(promoted);\n record(transactionId, [{ kind: \"create\", doc }], opts?.tag);\n }\n return writeResult(\n [promoted],\n [{ id: promoted._id, operation: \"create\" }],\n transactionId,\n opts,\n ) as never;\n }),\n\n createOrReplace: (doc, opts) =>\n write(async () => {\n checkRevision(doc._id as string, opts?.ifRevisionId);\n const promoted = promote(doc);\n const existing = store.get(doc._id as string);\n await gate(existing ?? promoted, existing !== undefined ? \"update\" : \"create\");\n await guardOne(existing !== undefined ? \"update\" : \"create\", existing ?? null, promoted);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n if (opts?.dryRun !== true) {\n store.put(promoted);\n record(transactionId, [{ kind: \"createOrReplace\", doc }], opts?.tag);\n }\n const operation: MutationOperation = existing !== undefined ? \"update\" : \"create\";\n return writeResult(\n [promoted],\n [{ id: promoted._id, operation }],\n transactionId,\n opts,\n ) as never;\n }),\n\n createIfNotExists: (doc, opts) =>\n write(async () => {\n checkRevision(doc._id as string, opts?.ifRevisionId);\n const existing = store.get(doc._id as string);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n if (existing !== undefined) {\n return writeResult(\n [existing],\n [{ id: existing._id, operation: \"none\" }],\n transactionId,\n opts,\n ) as never;\n }\n const promoted = promote(doc);\n await gate(promoted, \"create\");\n await guardOne(\"create\", null, promoted);\n if (opts?.dryRun !== true) {\n store.put(promoted);\n record(transactionId, [{ kind: \"createIfNotExists\", doc }], opts?.tag);\n }\n return writeResult(\n [promoted],\n [{ id: promoted._id, operation: \"create\" }],\n transactionId,\n opts,\n ) as never;\n }),\n\n delete: (id, opts) =>\n write(async () => {\n checkRevision(id, opts?.ifRevisionId);\n const existing = store.get(id);\n if (existing !== undefined) await gate(existing, \"update\");\n if (existing !== undefined) await guardOne(\"delete\", existing, null);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n let removed: SanityDocument | undefined = existing;\n if (opts?.dryRun !== true) {\n removed = store.remove(id);\n if (removed !== undefined) record(transactionId, [{ kind: \"delete\", id }], opts?.tag);\n }\n // Legacy default shape ({ _id } | null) unless the caller asked for\n // a specific return shape via returnDocuments / returnFirst.\n if (opts?.returnDocuments === undefined && opts?.returnFirst === undefined) {\n return (removed !== undefined ? { _id: id } : null) as never;\n }\n const results = removed !== undefined ? [{ id, operation: \"delete\" as const }] : [];\n const docs = removed !== undefined ? [removed] : [];\n return formatMutationResult(\n docs,\n results,\n transactionId,\n opts.returnDocuments ?? true,\n opts.returnFirst ?? true,\n ) as never;\n }),\n\n patch: (documentId) =>\n createPatch(documentId, {\n apply: (docId, ops, ifRev, options) =>\n write(async () => {\n const doc = await applyPatch(docId, ops, ifRev);\n record(\n cryptoRandomKey(),\n [\n {\n kind: \"patch\",\n documentId: docId,\n ops,\n ...(ifRev !== undefined ? { ifRev } : {}),\n },\n ],\n options?.tag,\n );\n return doc;\n }),\n }),\n\n transaction: () =>\n createTransaction({\n apply: (mutations, txnOptions) => write(() => applyTransaction(mutations, txnOptions)),\n }),\n\n mutate: (operations, options) =>\n write(() => applyTransaction(toTxnMutations(operations), options)),\n\n // Synchronous writes still take the lock: an action landing while a\n // guarded patch is parked in its gate would otherwise be clobbered\n // when that patch resumes over its pre-action snapshot.\n action: (action) => runAction(action),\n\n releases: {\n create: async ({ releaseId, metadata }) => {\n const result = await runAction({\n actionType: \"sanity.action.release.create\",\n releaseId,\n ...(metadata !== undefined ? { metadata } : {}),\n });\n const doc = store.get(`_.releases.${releaseId}`) as ReleaseDocument | undefined;\n return {\n ...result,\n releaseId,\n metadata: doc?.metadata ?? { releaseType: \"undecided\" },\n };\n },\n edit: ({ releaseId, patch }) =>\n runAction({ actionType: \"sanity.action.release.edit\", releaseId, patch }),\n get: async ({ releaseId }) => {\n const doc = store.get(`_.releases.${releaseId}`) as ReleaseDocument | undefined;\n return doc;\n },\n fetchDocuments: async ({ releaseId }) => {\n const prefix = `versions.${releaseId}.`;\n return store.all().filter((d) => d._id.startsWith(prefix));\n },\n schedule: ({ releaseId, publishAt }) =>\n runAction({\n actionType: \"sanity.action.release.schedule\",\n releaseId,\n publishAt,\n }),\n unschedule: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.unschedule\", releaseId }),\n publish: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.publish\", releaseId }),\n archive: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.archive\", releaseId }),\n unarchive: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.unarchive\", releaseId }),\n delete: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.delete\", releaseId }),\n },\n\n createVersion: ({ releaseId, publishedId, document }) =>\n runAction({\n actionType: \"sanity.action.document.version.create\",\n releaseId,\n publishedId,\n document,\n }),\n\n unpublishVersion: ({ releaseId, publishedId }) =>\n runAction({\n actionType: \"sanity.action.document.version.unpublish\",\n versionId: `versions.${releaseId}.${publishedId}`,\n publishedId,\n }),\n\n ...(token !== undefined || accessControl !== undefined\n ? {\n request: makeRequestImpl({\n getAccess: requireActiveAccess,\n projectId,\n // `@sanity/client` sends every request of a `resource`-configured\n // client to the global API host.\n useProjectHostname: useProjectHostname && resource === undefined,\n organizationId: backend.organizationId,\n listIdentities: () => [...backend.identities.values()],\n platform: {\n projectId,\n organizationId: backend.organizationId,\n own: (asked) =>\n registryKey(asked) === registryKey(target)\n ? {\n grants: requireActiveAccess().grants,\n documents: () => store.all(),\n history,\n canRead,\n }\n : undefined,\n actorAccess: () => requireActiveAccess().access,\n identities: () => [...backend.identities.values()],\n exists: (asked) => registry.has(registryKey(asked)),\n },\n }),\n }\n : {}),\n\n config: () => ({\n projectId,\n dataset,\n ...(resource !== undefined ? { resource } : {}),\n perspective,\n ...(requestTagPrefix !== undefined ? { requestTagPrefix } : {}),\n ...(token !== undefined ? { token } : {}),\n useProjectHostname,\n }),\n\n withConfig: (config) => {\n rejectAmbiguousIdentity(\"withConfig()\", config);\n if (config.token !== undefined) {\n requireIdentity(backend, config.token, \"withConfig()\");\n }\n // Naming either identity mechanism switches the sibling to it\n // wholesale; naming neither inherits this handle's binding.\n const rebinds = config.token !== undefined || config.accessControl !== undefined;\n // `resource: undefined` drops the resource; omitting the key keeps it.\n const nextResource = \"resource\" in config ? config.resource : resource;\n if (nextResource !== undefined) validateResource(nextResource);\n return makeHandle({\n backend,\n projectId: config.projectId ?? projectId,\n dataset: config.dataset ?? dataset,\n resource: nextResource,\n accessControl: rebinds ? config.accessControl : accessControl,\n perspective: config.perspective ?? perspective,\n requestTagPrefix: config.requestTagPrefix ?? requestTagPrefix,\n mutationGuard,\n token: rebinds ? config.token : token,\n useProjectHostname: config.useProjectHostname ?? useProjectHostname,\n });\n },\n\n seed: (documents) => store.seed(documents),\n add: (documents) => store.add(documents),\n reset: () => store.reset(),\n snapshot: () => store.snapshot(),\n store: () => store,\n simulateConcurrentEdit: (id, count = 1) => {\n const editKey = concurrentEditKey(id);\n backend.concurrentEdits.set(editKey, (backend.concurrentEdits.get(editKey) ?? 0) + count);\n },\n datasets: () =>\n [...registry.values()]\n .filter((entry) => entry.resource.type === \"dataset\")\n .map((entry) => datasetParts(entry.resource.id)),\n resources: () => [...registry.values()].map((entry) => ({ ...entry.resource })),\n stub: (name, handler) => {\n backend.stubs.set(name, handler);\n },\n stubs: () => Object.fromEntries(backend.stubs),\n addIdentity: (identityToken, identity) => {\n backend.identities.set(identityToken, identity);\n },\n identities: () => Object.fromEntries(backend.identities),\n mutations: () => backend.log.flatMap((entry) => entry.mutations),\n transactions: () => [...backend.log],\n clearLog: () => {\n backend.log.length = 0;\n },\n calls: (method) =>\n method === undefined ? [...backend.calls] : backend.calls.filter((c) => c.method === method),\n callCount: (method) =>\n method === undefined\n ? backend.calls.length\n : backend.calls.filter((c) => c.method === method).length,\n clearCalls: () => {\n backend.calls.length = 0;\n },\n };\n\n // Record an invocation: capture args now, fill the outcome on settle.\n const onCall = (method: string, args: unknown[]): CallSettler => {\n const rec: CallRecord = {\n method,\n args,\n projectId,\n dataset,\n ...(resource !== undefined ? { resource } : {}),\n };\n const tag =\n OPTIONS_ARG_POSITION[method] === undefined\n ? undefined\n : effectiveRequestTag(tagFromArgs(method, args));\n if (tag !== undefined) rec.tag = tag;\n backend.calls.push(rec);\n return (outcome) => {\n if (\"error\" in outcome) rec.error = outcome.error;\n else rec.result = outcome.result;\n };\n };\n\n // Wrap so unimplemented members fail with actionable errors, registered\n // stubs are served, and surface-method calls are recorded (see\n // ./unsupported.ts).\n return wrapWithGuards(client, {\n getStub: (name) => backend.stubs.get(name),\n onCall,\n recorded: RECORDED_METHODS,\n });\n}\n"],"names":["uri","rest","resource","parse","evaluate","VERSIONS_PREFIX","RELEASE_DOC_PREFIX","promote"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/history.ts","../src/resources.ts","../src/platform-api.ts","../src/access-control.ts","../src/mutation-guard.ts","../src/patch.ts","../src/perspective.ts","../src/store.ts","../src/actions.ts","../src/transaction.ts","../src/unsupported.ts","../src/client.ts"],"sourcesContent":["/**\n * Error helpers shared across the fake.\n *\n * Every error the fake throws is prefixed with {@link ERR_PREFIX} so it's\n * unmistakably attributable to *this fake* — not to `@sanity/client`,\n * groq-js, or the code under test — and carries enough context to fix\n * the problem without reading the fake's source. Short, bare messages\n * (\"not found\", \"invalid id\") are a poor developer experience in a test\n * double: prefer stating what happened, the offending value, and what to\n * do about it.\n */\nexport const ERR_PREFIX = \"[client-fake-for-test]\";\n\n/** Build a prefixed Error. Pass a fully-formed, dev-facing message. */\nexport function fakeError(message: string): Error {\n return new Error(`${ERR_PREFIX} ${message}`);\n}\n","/**\n * Document history for one store — the fake's mirror of the History API\n * (`<data base>/history/documents | transactions | events`).\n *\n * Every committed write is recorded as one transaction holding the net\n * change of each document it touched. Revisions are kept by `_rev`, so a\n * document can be read at any revision it has held since the client was\n * created, plus the revision it holds now (seeded documents included).\n */\n\nimport type { SanityDocument } from \"@sanity/types\";\n\n/** One document's net change in a committed transaction. */\ninterface HistoryEffect {\n documentId: string;\n /** The document after the transaction; `undefined` when it was deleted. */\n after: SanityDocument | undefined;\n /** Whether the document existed before the transaction. */\n existed: boolean;\n}\n\ninterface HistoryTransaction {\n id: string;\n timestamp: string;\n author: string | undefined;\n effects: HistoryEffect[];\n}\n\n/** A transaction as `GET <data base>/history/transactions/<ids>` serves it, one per NDJSON line. */\nexport interface HistoryTransactionRecord {\n id: string;\n timestamp: string;\n author?: string;\n mutations: (\n | { create: SanityDocument }\n | { createOrReplace: SanityDocument }\n | { delete: { id: string } }\n )[];\n documentIDs: string[];\n}\n\n/** An event as `GET <data base>/history/events/documents/<ids>` serves it. */\nexport interface HistoryEvent {\n type: \"createLiveDocument\" | \"updateLiveDocument\";\n id: string;\n documentId: string;\n revisionId: string;\n timestamp: string;\n author?: string;\n}\n\nexport interface DocumentHistory {\n /**\n * Record a committed write: compare the images from before the write with\n * the store after it, and keep one transaction for the documents that\n * changed. A write that changed nothing records nothing.\n */\n commit: (args: {\n transactionId: string;\n author: string | undefined;\n before: ReadonlyMap<string, SanityDocument>;\n after: readonly SanityDocument[];\n }) => void;\n /** `?revision=<rev>`: the listed documents as they were at that revision. */\n documentsAt: (args: {\n ids: readonly string[];\n revision: string;\n current: readonly SanityDocument[];\n }) => SanityDocument[];\n /** Every document id the history has recorded a change for. */\n documentIds: () => string[];\n /** The last image `id` held before it was deleted, or `undefined` if it never changed. */\n lastImage: (id: string) => SanityDocument | undefined;\n /**\n * Transactions touching any of `ids`, oldest first. Each record keeps only\n * the effects on documents `visible` lets through, so a transaction shared\n * with a document the reader cannot see does not reveal it.\n */\n transactions: (\n ids: readonly string[],\n visible: (documentId: string) => boolean,\n ) => HistoryTransactionRecord[];\n /** Live-document events for each of `ids`, newest first. */\n events: (ids: readonly string[]) => Record<string, HistoryEvent[]>;\n}\n\nexport function createDocumentHistory(): DocumentHistory {\n const transactions: HistoryTransaction[] = [];\n /** Every image a document has held, keyed `<id> <rev>`. */\n const revisions = new Map<string, SanityDocument>();\n /** Every image is a copy, so neither the store nor a caller's edits rewrite history. */\n const keep = (doc: SanityDocument) =>\n revisions.set(`${doc._id} ${doc._rev}`, structuredClone(doc));\n /** The last image each deleted document held. */\n const deleted = new Map<string, SanityDocument>();\n\n return {\n commit: ({ transactionId, author, before, after }) => {\n const afterById = new Map(after.map((doc) => [doc._id, doc]));\n const ids = new Set([...before.keys(), ...afterById.keys()]);\n const effects: HistoryEffect[] = [];\n for (const id of ids) {\n const previous = before.get(id);\n const next = afterById.get(id);\n if (previous === next) continue;\n if (previous !== undefined) keep(previous);\n if (next !== undefined) keep(next);\n if (next === undefined && previous !== undefined)\n deleted.set(id, structuredClone(previous));\n else deleted.delete(id);\n effects.push({\n documentId: id,\n after: next === undefined ? undefined : structuredClone(next),\n existed: previous !== undefined,\n });\n }\n if (effects.length === 0) return;\n transactions.push({\n id: transactionId,\n timestamp: new Date().toISOString(),\n author,\n effects,\n });\n },\n\n documentsAt: ({ ids, revision, current }) =>\n ids.flatMap((id) => {\n const image =\n revisions.get(`${id} ${revision}`) ??\n current.find((doc) => doc._id === id && doc._rev === revision);\n return image === undefined ? [] : [structuredClone(image)];\n }),\n\n lastImage: (id) => deleted.get(id),\n\n documentIds: () => [\n ...new Set(transactions.flatMap((txn) => txn.effects.map((effect) => effect.documentId))),\n ],\n\n transactions: (ids, visible) =>\n transactions\n .filter((txn) => txn.effects.some((effect) => ids.includes(effect.documentId)))\n .map((txn) => ({ ...txn, effects: txn.effects.filter((e) => visible(e.documentId)) }))\n .map((txn) => ({\n id: txn.id,\n timestamp: txn.timestamp,\n ...(txn.author !== undefined ? { author: txn.author } : {}),\n mutations: txn.effects.map((effect) => {\n if (effect.after === undefined) return { delete: { id: effect.documentId } };\n const after = structuredClone(effect.after);\n return effect.existed ? { createOrReplace: after } : { create: after };\n }),\n documentIDs: txn.effects.map((effect) => effect.documentId),\n })),\n\n events: (ids) =>\n Object.fromEntries(\n ids.map((id) => [\n id,\n transactions\n .flatMap((txn) =>\n txn.effects\n .filter((effect) => effect.documentId === id && effect.after !== undefined)\n .map(\n (effect): HistoryEvent => ({\n type: effect.existed ? \"updateLiveDocument\" : \"createLiveDocument\",\n id: txn.id,\n documentId: id,\n revisionId: (effect.after as SanityDocument)._rev,\n timestamp: txn.timestamp,\n ...(txn.author !== undefined ? { author: txn.author } : {}),\n }),\n ),\n )\n .toReversed(),\n ]),\n ),\n };\n}\n\n/** Split `documents/<ids>` style id lists: comma-separated, URI-encoded. */\nexport function historyIds(list: string): string[] {\n return list.split(\",\").map((id) => decodeURIComponent(id));\n}\n","/**\n * One capability table per resource type: how `@sanity/client` addresses\n * the resource, and what the platform serves or refuses there.\n *\n * Every value is taken from the staging capability probe (EDEX-2884): where\n * the platform says no, the entry holds the platform's own answer — status\n * and body — and the fake throws exactly that. Nothing here is policy of the\n * fake; adding a resource type means adding one row.\n */\n\nimport type { CurrentUser, TestClientIdentity } from \"./access-control.ts\";\nimport { fakeError } from \"./errors.ts\";\n\n/** Resource types `@sanity/client`'s `resource` config can name (the ones staging probed). */\nexport type ResourceType = \"dataset\" | \"media-library\" | \"canvas\" | \"dashboard\";\n\n/**\n * A resource, named exactly as `@sanity/client`'s `resource` config names\n * it: `{ type: \"media-library\", id: \"ml…\" }`, or `{ type: \"dataset\", id:\n * \"<projectId>.<dataset>\" }`.\n */\nexport interface TestClientResource {\n type: ResourceType;\n id: string;\n}\n\n/** The `response` of an HTTP error, shaped like `@sanity/client`'s `HttpError[\"response\"]`. */\ninterface PlatformErrorResponse {\n body: unknown;\n url: string;\n method: string;\n headers: Record<string, string>;\n statusCode: number;\n statusMessage: string;\n}\n\n/**\n * The platform's refusal, shaped like the `ClientError` `@sanity/client`\n * throws for the same response: `statusCode`, `response`, `responseBody`,\n * `details`, and a `message` derived from the body the way the client\n * derives it.\n */\nexport class PlatformHttpError extends Error {\n readonly statusCode: number;\n readonly response: PlatformErrorResponse;\n readonly responseBody: string;\n readonly details: unknown;\n\n constructor(args: {\n method: string;\n path: string;\n statusCode: number;\n statusMessage: string;\n body: unknown;\n }) {\n const { method, path, statusCode, statusMessage, body } = args;\n super(clientErrorMessage(args));\n this.name = \"PlatformHttpError\";\n this.statusCode = statusCode;\n this.response = { body, url: path, method, headers: {}, statusCode, statusMessage };\n this.responseBody = typeof body === \"string\" ? body : JSON.stringify(body, null, 2);\n const error = isRecord(body) ? body.error : undefined;\n this.details = isRecord(error) ? error : undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/** `@sanity/client`'s `extractErrorProps` message rules, for the body shapes the platform sends here. */\nfunction clientErrorMessage(args: {\n method: string;\n path: string;\n statusCode: number;\n statusMessage: string;\n body: unknown;\n}): string {\n const { body } = args;\n if (isRecord(body)) {\n const { error, message } = body;\n if (isRecord(error) && typeof error.description === \"string\") return error.description;\n if (typeof message === \"string\") return message;\n }\n // `@sanity/client` shows at most 100 characters of a text body in the message.\n const details =\n typeof body === \"string\" ? ` (${body.length > 100 ? `${body.slice(0, 100)}…` : body})` : \"\";\n return `${args.method}-request to ${args.path} resulted in HTTP ${args.statusCode} ${args.statusMessage}${details}`;\n}\n\n/** A request the platform answers. `path` is the API path, without host or API version. */\nexport interface PlatformRequest {\n method: \"GET\" | \"POST\";\n path: string;\n}\n\n/** The platform's answer where it says no. */\nexport type Refusal = (request: PlatformRequest) => PlatformHttpError;\n\n/** Either the platform serves the capability, or it refuses with a fixed answer. */\nexport type Capability = \"served\" | Refusal;\n\n/** Express's 404 for a route the resource does not mount: `Cannot GET /dashboards/<id>/acl`. */\nconst notMounted: Refusal = ({ method, path }) =>\n new PlatformHttpError({\n method,\n path,\n statusCode: 404,\n statusMessage: \"Not Found\",\n body: `Cannot ${method} ${path}`,\n });\n\n/** An Access API error: `{ statusCode, error, message }`, the body shape staging returns. */\nfunction accessApiError(args: {\n request: PlatformRequest;\n statusCode: number;\n statusMessage: string;\n message: string;\n}): PlatformHttpError {\n const { request, statusCode, statusMessage, message } = args;\n return new PlatformHttpError({\n ...request,\n statusCode,\n statusMessage,\n body: { statusCode, error: statusMessage, message },\n });\n}\n\n/** The Access API's 403 when the caller lacks the permission; also the answer for unproven cells. */\nexport const noPermission: Refusal = (request) =>\n accessApiError({\n request,\n statusCode: 403,\n statusMessage: \"Forbidden\",\n message: \"You do not have permission to access this resource\",\n });\n\n/** The Access API's 400 for a resource type it does not know: `/access/bogustype/<id>/…`. */\nexport function invalidAccessType(request: PlatformRequest, type: string): PlatformHttpError {\n return accessApiError({\n request,\n statusCode: 400,\n statusMessage: \"Bad Request\",\n message: `Invalid resource type ${type}`,\n });\n}\n\n/** The Access API's 404 for an unknown id: `\"Canvas not found\"`, `\"User not found\"`. */\nexport function notFound(name: string): Refusal {\n return (request) =>\n accessApiError({\n request,\n statusCode: 404,\n statusMessage: \"Not Found\",\n message: `${name} not found`,\n });\n}\n\n/** Media Library's plan quota on releases: `releaseLimitExceededError`, limit 0. */\nconst releaseLimitExceeded: Refusal = ({ method, path }) =>\n new PlatformHttpError({\n method,\n path,\n statusCode: 403,\n statusMessage: \"Forbidden\",\n body: {\n error: {\n description: \"active release count exceeds the limit of 0\",\n type: \"releaseLimitExceededError\",\n },\n },\n });\n\n/** Throw the refusal, or run `serve` when the platform serves the capability. */\nexport function serveCapability<T>(\n capability: Capability,\n request: PlatformRequest,\n serve: () => T,\n): T {\n if (capability !== \"served\") throw capability(request);\n return serve();\n}\n\n/** The Access API's resource types (`/access/<type>/<id>/…`). */\nexport type AccessResourceType =\n | \"organization\"\n | \"project\"\n | \"media-library\"\n | \"canvas\"\n | \"dashboard\";\n\nexport interface ResourceCapabilities {\n /** How the fake's own error messages name the resource. */\n label: (id: string) => string;\n /** The data API base `@sanity/client`'s `resourceDataBase` builds for the resource. */\n dataBase: (id: string) => string;\n /** Match a request path on the data API base; `rest` is what follows it (`\"\"`, `/acl`, …). */\n matchBase: (path: string) => { id: string; rest: string } | undefined;\n /** Whether `GET <base>` answers the resource record (`{ organizationId, id, status }`). */\n lookupAtBase: boolean;\n /**\n * Whose id grant filters see as `identity()`: the project-scoped user\n * record for a dataset, the account-global one for every other resource.\n */\n identity: \"project\" | \"global\";\n /** `<base>/acl` → the caller's `Grant[]`. */\n acl: Capability;\n /** `<base>/history/documents | transactions | events`. */\n history: Capability;\n /** `<base>/actions` — release, version and document actions. */\n actions: Capability;\n /** Action types refused where `/actions` is mounted. */\n refusedActions: Partial<Record<string, Refusal>>;\n /** The Access API addresses the resource as `/access/<type>/<id(resource id)>`. */\n access: { type: AccessResourceType; id: (id: string) => string };\n}\n\n/** An Access API resource: `/access/<type>/<id>`. */\nexport interface AccessResource {\n type: AccessResourceType;\n id: string;\n}\n\n/** What the Access API knows of the client family: its organization and the resources it holds. */\nexport interface AccessRegistry {\n organizationId: string | undefined;\n /** Every resource the registry holds (seeded, written, or addressed by a handle). */\n resources: () => TestClientResource[];\n}\n\n/** One Access API resource type: what `/access/<type>/<id>/…` serves or refuses. */\nexport interface AccessCapabilities {\n /** `GET …/roles`. */\n roles: Capability;\n /** `GET …/users`. */\n users: Capability;\n /** `GET …/users/<sanityUserId>`. */\n user: Capability;\n /**\n * The permission served `users` and `user` need, checked against the\n * actor's `access` entry for the resource (what `user-permissions/me`\n * reports). When absent, no permission check runs.\n */\n usersPermission?: string;\n /** The permission served `roles` needs, checked like {@link usersPermission}. */\n rolesPermission?: string;\n /**\n * The answer for an id the family does not hold, on every endpoint but\n * `user-permissions/me/check` (which answers `false` even then). Absent\n * where staging did not probe it.\n */\n unknown?: { holds: (registry: AccessRegistry, id: string) => boolean; refusal: Refusal };\n /** The identity's role records on exactly this resource, or `undefined` when it is not a member. */\n memberRoles: (args: {\n identity: TestClientIdentity;\n id: string;\n }) => NonNullable<CurrentUser[\"roles\"]> | undefined;\n /**\n * Resources whose memberships a user row here lists besides its own: the\n * organization's users answer every membership in the organization.\n */\n owns?: (registry: AccessRegistry) => AccessResource[];\n}\n\nconst ACCESS_KEY_SEPARATOR = \":\";\n\n/** Key of an identity's `access` entry for a resource: `\"<access type>:<access id>\"`. */\nexport function accessKey(type: AccessResourceType, id: string): string {\n return `${type}${ACCESS_KEY_SEPARATOR}${id}`;\n}\n\n/** Match `/<segment>/<id>` and whatever follows. */\nfunction segmentBase(segment: string): ResourceCapabilities[\"matchBase\"] {\n const pattern = new RegExp(`^/${segment}/([^/]+)(/.*)?$`);\n return (path) => {\n const match = path.match(pattern);\n if (match === null) return undefined;\n return { id: decodeURIComponent(match[1] as string), rest: match[2] ?? \"\" };\n };\n}\n\nconst DATASET_BASE = /^\\/projects\\/([^/]+)\\/datasets\\/([^/]+)(\\/.*)?$/;\n\n/**\n * Split a dataset resource id, `\"<projectId>.<dataset>\"`, at its first dot.\n * Lenient on purpose: the fake builds these ids from any `(projectId,\n * dataset)` pair; caller-supplied ids are checked by {@link validateResource}.\n */\nexport function datasetParts(id: string): { projectId: string; dataset: string } {\n const dot = id.indexOf(\".\");\n return { projectId: id.slice(0, dot), dataset: id.slice(dot + 1) };\n}\n\n/** A non-project resource's roles live in the identity's `access` entry for it. */\nfunction accessEntryRoles(type: AccessResourceType): AccessCapabilities[\"memberRoles\"] {\n return ({ identity, id }) => identity.access?.[accessKey(type, id)]?.roles;\n}\n\nexport const RESOURCE_CAPABILITIES: Record<ResourceType, ResourceCapabilities> = {\n dataset: {\n label: (id) => {\n const { projectId, dataset } = datasetParts(id);\n return `dataset \"${projectId}:${dataset}\"`;\n },\n dataBase: (id) => {\n const { projectId, dataset } = datasetParts(id);\n return `/projects/${projectId}/datasets/${dataset}`;\n },\n matchBase: (path) => {\n const match = path.match(DATASET_BASE);\n if (match === null) return undefined;\n return { id: `${match[1]}.${match[2]}`, rest: match[3] ?? \"\" };\n },\n // A dataset's organization comes from its project: `GET /projects/<projectId>`.\n lookupAtBase: false,\n identity: \"project\",\n acl: \"served\",\n history: \"served\",\n actions: \"served\",\n refusedActions: {},\n access: { type: \"project\", id: (id) => datasetParts(id).projectId },\n },\n \"media-library\": {\n label: (id) => `media library \"${id}\"`,\n dataBase: (id) => `/media-libraries/${id}`,\n matchBase: segmentBase(\"media-libraries\"),\n lookupAtBase: true,\n identity: \"global\",\n acl: \"served\",\n history: \"served\",\n actions: \"served\",\n refusedActions: { \"sanity.action.release.create\": releaseLimitExceeded },\n access: { type: \"media-library\", id: (id) => id },\n },\n canvas: {\n label: (id) => `canvas \"${id}\"`,\n dataBase: (id) => `/canvases/${id}`,\n matchBase: segmentBase(\"canvases\"),\n lookupAtBase: true,\n identity: \"global\",\n acl: \"served\",\n history: \"served\",\n actions: notMounted,\n refusedActions: {},\n access: { type: \"canvas\", id: (id) => id },\n },\n dashboard: {\n label: (id) => `dashboard \"${id}\"`,\n dataBase: (id) => `/dashboards/${id}`,\n matchBase: segmentBase(\"dashboards\"),\n lookupAtBase: true,\n identity: \"global\",\n acl: notMounted,\n history: notMounted,\n actions: notMounted,\n refusedActions: {},\n access: { type: \"dashboard\", id: (id) => id },\n },\n};\n\n/** The capability row for `type`, or a fake error naming the supported types. */\nexport function capabilitiesOf(type: string): ResourceCapabilities {\n // Own keys only: `toString` is no more a resource type than `bogustype`.\n const row = Object.hasOwn(RESOURCE_CAPABILITIES, type)\n ? RESOURCE_CAPABILITIES[type as ResourceType]\n : undefined;\n if (row === undefined) {\n throw fakeError(\n `Unsupported resource type ${JSON.stringify(type)}. The fake serves ` +\n `${Object.keys(RESOURCE_CAPABILITIES)\n .map((t) => `\"${t}\"`)\n .join(\", \")}.`,\n );\n }\n return row;\n}\n\n/** Validate a `resource` config the way `@sanity/client` does, returning its capability row. */\nexport function validateResource(resource: TestClientResource): ResourceCapabilities {\n const row = capabilitiesOf(resource.type);\n if (resource.type === \"dataset\" && resource.id.split(\".\").length !== 2) {\n throw fakeError(\n `Dataset resource ID must be in the format \"project.dataset\" (got ${JSON.stringify(resource.id)}), ` +\n \"the same rule @sanity/client applies to `resource: { type: 'dataset', id }`.\",\n );\n }\n return row;\n}\n\n/** Find the resource a data API path addresses, by trying every row's base. */\nexport function matchResourcePath(\n path: string,\n): { resource: TestClientResource; rest: string } | undefined {\n for (const [type, row] of Object.entries(RESOURCE_CAPABILITIES)) {\n const match = row.matchBase(path);\n if (match !== undefined) {\n return { resource: { type: type as ResourceType, id: match.id }, rest: match.rest };\n }\n }\n return undefined;\n}\n\n/** The Access API resource a registry resource is: a dataset is its project. */\nfunction accessResourceOf(resource: TestClientResource): AccessResource {\n const { access } = capabilitiesOf(resource.type);\n return { type: access.type, id: access.id(resource.id) };\n}\n\n/** Whether the registry holds an Access API resource of `type` with `id`. */\nfunction holdsResource(\n type: AccessResourceType,\n): (registry: AccessRegistry, id: string) => boolean {\n return (registry, id) =>\n registry.resources().some((resource) => {\n const access = accessResourceOf(resource);\n return access.type === type && access.id === id;\n });\n}\n\nexport const ACCESS_CAPABILITIES: Record<AccessResourceType, AccessCapabilities> = {\n organization: {\n // Proven on staging, but outside what the fake serves: refused like a missing permission.\n roles: noPermission,\n users: \"served\",\n user: \"served\",\n // Granted by the built-in organization `member` role.\n usersPermission: \"sanity.organization.members.read\",\n unknown: {\n holds: (registry, id) => id === registry.organizationId,\n refusal: notFound(\"Organization\"),\n },\n memberRoles: accessEntryRoles(\"organization\"),\n // Every resource of the family belongs to its organization (see `organizationId`).\n owns: (registry) => {\n const byKey = new Map<string, AccessResource>();\n for (const resource of registry.resources()) {\n const access = accessResourceOf(resource);\n byKey.set(accessKey(access.type, access.id), access);\n }\n return [...byKey.values()];\n },\n },\n project: {\n roles: \"served\",\n users: \"served\",\n // Single-user reads are served only where the fake checks members read (the\n // organization); elsewhere they refuse rather than answer unchecked.\n user: noPermission,\n // Every registered identity is a member of every project, with its project roles.\n memberRoles: ({ identity, id }) => (identity.usersByProject?.[id] ?? identity.user).roles ?? [],\n },\n \"media-library\": {\n // Staging refuses it only because no probe token held roles read.\n roles: \"served\",\n rolesPermission: \"sanity.media-library.roles.read\",\n users: \"served\",\n // Unproven on staging (only the listing was probed); refused as on `project`.\n user: noPermission,\n unknown: { holds: holdsResource(\"media-library\"), refusal: notFound(\"Media library\") },\n memberRoles: accessEntryRoles(\"media-library\"),\n },\n canvas: {\n // Unproven on staging (no canvas roles/members permission exists to grant).\n roles: noPermission,\n users: noPermission,\n user: noPermission,\n unknown: { holds: holdsResource(\"canvas\"), refusal: notFound(\"Canvas\") },\n memberRoles: accessEntryRoles(\"canvas\"),\n },\n dashboard: {\n // Unproven on staging, as for canvas.\n roles: noPermission,\n users: noPermission,\n user: noPermission,\n unknown: { holds: holdsResource(\"dashboard\"), refusal: notFound(\"Dashboard\") },\n memberRoles: accessEntryRoles(\"dashboard\"),\n },\n};\n\n/** The Access API row for `type`, if the Access API knows the type. */\nexport function accessCapabilitiesOf(type: string): AccessCapabilities | undefined {\n // Own keys only: `toString` or `constructor` is as unknown to the platform as `bogustype`.\n return Object.hasOwn(ACCESS_CAPABILITIES, type)\n ? ACCESS_CAPABILITIES[type as AccessResourceType]\n : undefined;\n}\n\n/**\n * `@sanity/client`'s `getDataUrl(operation, path)`: a `resource`-configured\n * handle addresses `<resource base>/<operation>/<path>`, a plain dataset\n * handle `/data/<operation>/<dataset>/<path>`.\n */\nexport function dataUrl(args: {\n resource: TestClientResource | undefined;\n dataset: string;\n operation: string;\n path?: string | undefined;\n}): string {\n const { resource, dataset, operation, path } = args;\n if (resource !== undefined) {\n const uri = path !== undefined ? `${operation}/${path}` : operation;\n return `${capabilitiesOf(resource.type).dataBase(resource.id)}/${uri}`.replace(\n /\\/($|\\?)/,\n \"$1\",\n );\n }\n const base = `/${operation}/${dataset}`;\n const uri = path !== undefined ? `${base}/${path}` : base;\n return `/data${uri}`.replace(/\\/($|\\?)/, \"$1\");\n}\n","/**\n * The platform endpoints `client.request()` serves beyond identity: the\n * Access API (`/access/<type>/<id>/…`, routed through `ACCESS_CAPABILITIES`),\n * the caller's organization attributes, resource lookup (`GET <data\n * base>`), ACL (`<data base>/acl`) and the History API (`<data\n * base>/history/…`, or `/data/history/<dataset>/…` on a plain dataset\n * handle). Routing goes through the capability table: a capability the\n * resource refuses throws the platform's own answer.\n */\n\nimport type { SanityDocument } from \"@sanity/types\";\n\nimport type {\n CurrentUser,\n Grant,\n ResourceAccess,\n TestClientIdentity,\n UserAttribute,\n UserAttributeValue,\n} from \"./access-control.ts\";\nimport { fakeError } from \"./errors.ts\";\nimport { historyIds, type DocumentHistory } from \"./history.ts\";\nimport {\n accessCapabilitiesOf,\n accessKey,\n ACCESS_CAPABILITIES,\n capabilitiesOf,\n invalidAccessType,\n matchResourcePath,\n noPermission,\n notFound,\n serveCapability,\n type AccessRegistry,\n type AccessResource,\n type AccessResourceType,\n type Capability,\n type PlatformRequest,\n type TestClientResource,\n} from \"./resources.ts\";\n\n/** What the handle answers for its own resource: the actor's grants, and the history it may read. */\nexport interface OwnResource {\n grants: Grant[];\n documents: () => readonly SanityDocument[];\n history: DocumentHistory;\n /** The actor's read gate; history serves only documents it lets through. */\n canRead: (document: SanityDocument) => Promise<boolean>;\n}\n\n/** Everything {@link servePlatformRequest} needs from the owning handle. */\nexport interface PlatformApiContext {\n /** The handle's projectId, for `/data/history/<dataset>` paths. */\n projectId: string;\n /** The organization every resource of the family belongs to, if configured. */\n organizationId: string | undefined;\n /**\n * The handle's own resource, or `undefined` for any other: a token's\n * grants and history answer for the resource the handle addresses.\n */\n own: (resource: TestClientResource) => OwnResource | undefined;\n /** The acting identity's account-global user record (its `sanityUserId`). */\n actorUser: () => CurrentUser;\n /** The acting identity's Access API entries, keyed `\"<access type>:<id>\"`. */\n actorAccess: () => Record<string, ResourceAccess> | undefined;\n /** The handle's members (registered identities, plus its inline `accessControl` actor), for member and role listings. */\n identities: () => TestClientIdentity[];\n /** Every resource the registry holds (seeded, written, or addressed by a handle). */\n resources: () => TestClientResource[];\n}\n\n/** `{ value }` when the platform answers the path, `undefined` when the fake has no handler for it. */\ntype Served = { value: unknown } | undefined;\n\nconst ACCESS_PATH = /^\\/access\\/([^/]+)\\/([^/]+)(\\/.*)?$/;\nconst ACCESS_USER_PATH = /^\\/users\\/([^/]+)$/;\nconst ACCESS_ROLE_PATH = /^\\/roles\\/([^/]+)$/;\nconst MY_ATTRIBUTES_PATH = /^\\/organizations\\/([^/]+)\\/users\\/me\\/attributes$/;\nconst DATA_HISTORY_PATH = /^\\/data\\/history\\/([^/]+)(\\/.*)$/;\n\n/** An Access API listing page; the fake serves everything on one page. */\nfunction page<T>(data: T[]): { data: T[]; nextCursor: null } {\n return { data, nextCursor: null };\n}\n\n/** A users listing page, which also counts the users. */\nfunction usersPage<T>(data: T[]): { data: T[]; nextCursor: null; totalCount: number } {\n return { ...page(data), totalCount: data.length };\n}\n\n/**\n * The platform's profile fields the fake does not model: every user signs in\n * with `sanity` and was created at the epoch. `email` is required too, and is\n * empty where the identity seeds none.\n */\nconst UNMODELLED_PROFILE = { provider: \"sanity\", createdAt: new Date(0).toISOString() } as const;\n\n/** Serve `path` (query string split off) if it is a platform endpoint the table knows. */\nexport async function servePlatformRequest(ctx: PlatformApiContext, url: string): Promise<Served> {\n const queryStart = url.indexOf(\"?\");\n const path = queryStart === -1 ? url : url.slice(0, queryStart);\n const query = new URLSearchParams(queryStart === -1 ? \"\" : url.slice(queryStart + 1));\n const request: PlatformRequest = { method: \"GET\", path };\n\n const myAttributes = path.match(MY_ATTRIBUTES_PATH);\n if (myAttributes !== null) {\n const organizationId = decodeURIComponent(myAttributes[1] as string);\n return serveMyAttributes({ ctx, request, organizationId, query });\n }\n\n const access = path.match(ACCESS_PATH);\n if (access !== null) {\n const [, type, id, rest] = access as [string, string, string, string | undefined];\n return serveAccess({ ctx, request, type, id: decodeURIComponent(id), rest: rest ?? \"\", query });\n }\n\n const dataHistory = path.match(DATA_HISTORY_PATH);\n if (dataHistory !== null) {\n const [, dataset, rest] = dataHistory as [string, string, string];\n const resource: TestClientResource = { type: \"dataset\", id: `${ctx.projectId}.${dataset}` };\n return serveHistory({ ctx, request, resource, rest, query });\n }\n\n const matched = matchResourcePath(path);\n if (matched === undefined) return undefined;\n const { resource, rest } = matched;\n const row = capabilitiesOf(resource.type);\n if (rest === \"\") return row.lookupAtBase ? serveLookup(ctx, resource) : undefined;\n if (rest === \"/acl\") {\n return serveCapability(row.acl, request, () => {\n const own = ctx.own(resource);\n return own === undefined ? undefined : { value: own.grants };\n });\n }\n if (rest.startsWith(\"/history/\")) {\n return serveHistory({ ctx, request, resource, rest: rest.slice(\"/history\".length), query });\n }\n return undefined;\n}\n\n/** `GET /media-libraries/<id>`, `/canvases/<id>`, `/dashboards/<id>`: the resource record. */\nfunction serveLookup(ctx: PlatformApiContext, resource: TestClientResource): Served {\n if (ctx.organizationId === undefined) {\n throw fakeError(\n `request(): GET ${capabilitiesOf(resource.type).dataBase(resource.id)} answers the ` +\n \"resource's organization, but no organization is configured. Pass \" +\n \"createTestClient({ organizationId }).\",\n );\n }\n return { value: { organizationId: ctx.organizationId, id: resource.id, status: \"active\" } };\n}\n\nfunction serveHistory(args: {\n ctx: PlatformApiContext;\n request: PlatformRequest;\n resource: TestClientResource;\n rest: string;\n query: URLSearchParams;\n}): Promise<Served> | undefined {\n const { ctx, request, resource, rest, query } = args;\n return serveCapability(capabilitiesOf(resource.type).history, request, () => {\n const own = ctx.own(resource);\n return own === undefined ? undefined : readHistory({ own, request, rest, query });\n });\n}\n\n/** Answer a History API path from the handle's own history, hiding what the actor cannot read. */\nasync function readHistory(args: {\n own: OwnResource;\n request: PlatformRequest;\n rest: string;\n query: URLSearchParams;\n}): Promise<Served> {\n const { own, request, rest, query } = args;\n // The read grant filters the document as it is now or, once deleted, as it last was.\n const current = new Map(own.documents().map((doc) => [doc._id, doc]));\n const hidden = new Set<string>();\n for (const id of new Set([...current.keys(), ...own.history.documentIds()])) {\n const image = current.get(id) ?? own.history.lastImage(id);\n if (image !== undefined && !(await own.canRead(image))) hidden.add(id);\n }\n const visible = (id: string) => !hidden.has(id);\n const readable = (list: string): string[] => historyIds(list).filter(visible);\n\n const documents = rest.match(/^\\/documents\\/([^/]+)$/);\n if (documents !== null) {\n const revision = query.get(\"revision\");\n if (revision === null) {\n throw fakeError(\n `request(): ${request.path} is served for \\`?revision=<rev>\\` reads only ` +\n \"(time-based history reads are not simulated).\",\n );\n }\n const ids = readable(documents[1] as string);\n return {\n value: { documents: own.history.documentsAt({ ids, revision, current: own.documents() }) },\n };\n }\n const transactions = rest.match(/^\\/transactions\\/([^/]+)$/);\n if (transactions !== null) {\n // NDJSON, one transaction per line, as the History API streams it.\n const records = own.history.transactions(readable(transactions[1] as string), visible);\n return { value: records.map((record) => JSON.stringify(record)).join(\"\\n\") };\n }\n const events = rest.match(/^\\/events\\/documents\\/([^/]+)$/);\n if (events !== null) {\n return {\n value: {\n events: own.history.events(readable(events[1] as string)),\n nextCursor: \"\",\n },\n };\n }\n return undefined;\n}\n\n/** One membership of an Access API user row: the roles held on one resource. */\ninterface AccessMembership {\n resourceType: AccessResourceType;\n resourceId: string;\n roleNames: string[];\n}\n\n/** An Access API user row: the account-global id, a profile, and its memberships. */\ninterface AccessUserRecord {\n sanityUserId: string;\n profile: {\n id: string;\n displayName: string;\n email: string;\n provider: string;\n createdAt: string;\n };\n memberships: AccessMembership[];\n}\n\n/** One `user-permissions/me` row. */\ninterface PermissionRecord {\n type: string;\n resourceType: AccessResourceType;\n resourceId: string;\n action: string;\n}\n\nfunction serveAccess(args: {\n ctx: PlatformApiContext;\n request: PlatformRequest;\n type: string;\n id: string;\n rest: string;\n query: URLSearchParams;\n}): Served {\n const { ctx, request, type, id, rest, query } = args;\n const row = accessCapabilitiesOf(type);\n if (row === undefined) throw invalidAccessType(request, type);\n const resource: AccessResource = { type: type as AccessResourceType, id };\n const registry: AccessRegistry = {\n organizationId: ctx.organizationId,\n resources: ctx.resources,\n };\n const permissions = ctx.actorAccess()?.[accessKey(resource.type, id)]?.permissions ?? [];\n // `check` answers `false` even for an id the platform does not hold.\n if (rest === \"/user-permissions/me/check\") {\n const requested = query.getAll(\"permissions\").flatMap((value) => value.split(\",\"));\n return {\n value: { data: Object.fromEntries(requested.map((p) => [p, permissions.includes(p)])) },\n };\n }\n if (row.unknown !== undefined && !row.unknown.holds(registry, id)) {\n throw row.unknown.refusal(request);\n }\n /** Serve the capability when the actor holds `permission` (if the row names one), else 403. */\n const gated = <T>(capability: Capability, permission: string | undefined, serve: () => T): T =>\n serveCapability(capability, request, () => {\n if (permission !== undefined && !permissions.includes(permission)) {\n throw noPermission(request);\n }\n return serve();\n });\n const users = <T>(capability: Capability, serve: () => T): T =>\n gated(capability, row.usersPermission, serve);\n const roles = () => rolesOn({ resource, identities: ctx.identities() });\n\n const user = rest.match(ACCESS_USER_PATH);\n if (user !== null) {\n return users(row.user, () => {\n const sanityUserId = decodeURIComponent(user[1] as string);\n const record = usersOn({ resource, registry, identities: ctx.identities() }).find(\n (candidate) => candidate.sanityUserId === sanityUserId,\n );\n if (record === undefined) throw notFound(\"User\")(request);\n return { value: record };\n });\n }\n\n const role = rest.match(ACCESS_ROLE_PATH);\n if (role !== null) {\n return gated(row.roles, row.rolesPermission, () => {\n const name = decodeURIComponent(role[1] as string);\n const record = roles().find((candidate) => candidate.name === name);\n if (record === undefined) throw notFound(\"Role\")(request);\n return { value: record };\n });\n }\n\n switch (rest) {\n case \"/roles\":\n return gated(row.roles, row.rolesPermission, () => ({ value: page(roles()) }));\n case \"/users\":\n return users(row.users, () => ({\n value: usersPage(usersOn({ resource, registry, identities: ctx.identities() })),\n }));\n case \"/user-permissions/me\":\n return {\n value: page(\n permissions.map((permission): PermissionRecord => {\n const dot = permission.lastIndexOf(\".\");\n return {\n type: permission.slice(0, dot),\n resourceType: resource.type,\n resourceId: id,\n action: permission.slice(dot + 1),\n };\n }),\n ),\n };\n default:\n return undefined;\n }\n}\n\n/** An Access API role row: the seeded role, on its resource. */\ninterface AccessRoleRecord {\n name: string;\n title: string;\n description?: string;\n resourceType: AccessResourceType;\n resourceId: string;\n appliesToUsers: boolean;\n appliesToRobots: boolean;\n permissions: { type: string; action: string; name?: string; params?: Record<string, unknown> }[];\n}\n\n/**\n * The platform's role fields the fake does not model: every role applies to\n * users and robots. `title` and `permissions` are required too: the title is\n * the role's name where the seed gives none, and the permissions are empty.\n */\nconst UNMODELLED_ROLE = { appliesToUsers: true, appliesToRobots: true } as const;\n\n/** Every role a member holds on the resource, once each, in canonical order. */\nfunction rolesOn(args: {\n resource: AccessResource;\n identities: TestClientIdentity[];\n}): AccessRoleRecord[] {\n const { resource } = args;\n const byName = new Map<string, AccessRoleRecord>();\n for (const identity of args.identities) {\n const roles = ACCESS_CAPABILITIES[resource.type].memberRoles({ identity, id: resource.id });\n for (const role of roles ?? []) {\n byName.set(role.name, {\n name: role.name,\n title: role.title ?? role.name,\n ...(role.description !== undefined ? { description: role.description } : {}),\n resourceType: resource.type,\n resourceId: resource.id,\n ...UNMODELLED_ROLE,\n permissions: [],\n });\n }\n }\n return [...byName.values()].toSorted((a, b) => a.name.localeCompare(b.name));\n}\n\n/** The identity's membership on `resource`, or `undefined` when it holds no role there. */\nfunction membershipOn(\n identity: TestClientIdentity,\n resource: AccessResource,\n): AccessMembership | undefined {\n const roles = ACCESS_CAPABILITIES[resource.type].memberRoles({ identity, id: resource.id });\n if (roles === undefined) return undefined;\n return {\n resourceType: resource.type,\n resourceId: resource.id,\n roleNames: roles.map((role) => role.name).toSorted(),\n };\n}\n\n/**\n * One row per member of the resource, keyed by account-global id.\n * A member holds a role on the resource itself; its row also lists its\n * memberships on every resource the resource owns.\n */\nfunction usersOn(args: {\n resource: AccessResource;\n registry: AccessRegistry;\n identities: TestClientIdentity[];\n}): AccessUserRecord[] {\n const { resource, registry, identities } = args;\n const owned = ACCESS_CAPABILITIES[resource.type].owns?.(registry) ?? [];\n const bySanityUserId = new Map<string, AccessUserRecord>();\n for (const identity of identities) {\n const own = membershipOn(identity, resource);\n if (own === undefined) continue;\n const user = identity.globalUser ?? identity.user;\n const record: AccessUserRecord = {\n sanityUserId: user.id,\n profile: {\n id: user.id,\n displayName: user.name ?? user.id,\n email: user.email ?? \"\",\n ...UNMODELLED_PROFILE,\n },\n memberships: [\n own,\n // A user is part of a resource only through a role on it.\n ...owned.flatMap((child) => {\n const membership = membershipOn(identity, child);\n return membership === undefined || membership.roleNames.length === 0 ? [] : [membership];\n }),\n ],\n };\n const existing = bySanityUserId.get(record.sanityUserId);\n if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(record)) {\n throw fakeError(\n `request(): two identities (registered, or the handle's inline accessControl) resolve to ` +\n `user \"${record.sanityUserId}\" on ${resource.type} \"${resource.id}\" with different ` +\n \"records. Fix the identities so duplicate ids carry identical records.\",\n );\n }\n bySanityUserId.set(record.sanityUserId, record);\n }\n return [...bySanityUserId.values()];\n}\n\n/** The page size \"Get My Attributes\" answers when the request names none. */\nconst DEFAULT_ATTRIBUTES_LIMIT = 50;\n\n/** One served attribute row: the seeded values plus the one in effect. */\ninterface UserAttributeRecord extends UserAttribute {\n activeSource: \"saml\" | \"sanity\";\n activeValue: UserAttributeValue;\n}\n\n/**\n * `GET /organizations/<id>/users/me/attributes`: the caller's own\n * attributes in the organization, ordered by key, one page of `limit` after\n * the `cursor` key. No permission is needed; a caller outside the\n * organization is not found there.\n */\nfunction serveMyAttributes(args: {\n ctx: PlatformApiContext;\n request: PlatformRequest;\n organizationId: string;\n query: URLSearchParams;\n}): Served {\n const { ctx, request, organizationId, query } = args;\n const organization = ACCESS_CAPABILITIES.organization;\n const registry: AccessRegistry = { organizationId: ctx.organizationId, resources: ctx.resources };\n if (organization.unknown !== undefined && !organization.unknown.holds(registry, organizationId)) {\n throw organization.unknown.refusal(request);\n }\n // Organization membership is a role in the actor's organization entry.\n const entry = ctx.actorAccess()?.[accessKey(\"organization\", organizationId)];\n if (entry?.roles === undefined) throw notFound(\"User\")(request);\n const limit = attributesLimit(query);\n const cursor = query.get(\"cursor\");\n const attributes = (entry.attributes ?? [])\n .map(attributeRecord)\n .toSorted((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0))\n .filter((attribute) => cursor === null || attribute.key > cursor)\n .slice(0, limit);\n return { value: { sanityUserId: ctx.actorUser().id, organizationId, attributes } };\n}\n\nfunction attributesLimit(query: URLSearchParams): number {\n const raw = query.get(\"limit\");\n if (raw === null) return DEFAULT_ATTRIBUTES_LIMIT;\n const limit = Number(raw);\n if (!Number.isInteger(limit) || limit < 1) {\n throw fakeError(\n `request(): users/me/attributes got limit=${JSON.stringify(raw)}; the fake serves positive ` +\n \"integer limits only (the platform's answer to others is not simulated).\",\n );\n }\n return limit;\n}\n\n/** Sanity values take precedence over SAML ones, as the platform resolves them. */\nfunction attributeRecord(attribute: UserAttribute): UserAttributeRecord {\n const { key, type, values } = attribute;\n const activeSource = values.sanity !== undefined ? \"sanity\" : \"saml\";\n const activeValue = values[activeSource];\n if (activeValue === undefined) {\n throw fakeError(\n `request(): user attribute \"${key}\" is seeded with no value; give it a \\`saml\\` or ` +\n \"`sanity` value.\",\n );\n }\n return { type, key, values: { ...values }, activeSource, activeValue };\n}\n","/**\n * Optional access-control simulation for the in-memory test client.\n *\n * When configured, the TestClient does two things a real `@sanity/client`\n * does over the wire but the bare in-memory client otherwise skips:\n *\n * 1. **Exposes `client.request({ url })`** that serves:\n * - `GET /users/me` → the configured `CurrentUser` — the project-scoped\n * record on a project-hosted handle, `globalUser` on a\n * `useProjectHostname: false` handle (Sanity's global API host,\n * set at creation or via `withConfig`)\n * - `GET <aclPath>` → the configured `Grant[]`\n * - `GET /projects/<projectId>` → the project's members (ids, roles, and robot flags)\n * - `GET /projects/<projectId>/users/<ids>` → project-user records\n * carrying `id` + `sanityUserId`, the bridge between the project\n * and global identity namespaces\n * This is what the engine reads from to resolve `WorkflowAccess`\n * when no override is passed.\n *\n * 2. **Gates writes through the configured grants.** Every mutation\n * (`create`, `createOrReplace`, `createIfNotExists`, `delete`,\n * `patch`, and per-op inside `transaction`) checks whether the\n * configured grants allow the relevant `DocumentValuePermission`\n * on the target document. On denial the client throws a\n * `WriteAccessDeniedError` shaped like Sanity's 403 — the same\n * failure mode a production write would hit, surfaced in tests.\n *\n * When `accessControl` is omitted, the client behaves as before:\n * `request` is undefined, writes are unconditional, and the engine\n * falls through to whatever override the caller (typically the\n * bench's `ALL_ACCESS`) provides.\n *\n * The grant evaluation runs the GROQ filter on a singleton dataset\n * containing the doc being checked — same shape as the engine's\n * `permissions.matchesFilter`, lifted from canvas.\n */\n\nimport type { SanityDocumentLike } from \"@sanity/types\";\nimport { evaluate, parse } from \"groq-js\";\n\nimport { fakeError } from \"./errors.ts\";\nimport { servePlatformRequest, type PlatformApiContext } from \"./platform-api.ts\";\nimport { capabilitiesOf, matchResourcePath, type PlatformHttpError } from \"./resources.ts\";\n\nexport type DocumentValuePermission = \"create\" | \"manage\" | \"read\" | \"update\";\n\nexport interface CurrentUser {\n id: string;\n name?: string;\n email?: string;\n /** @deprecated singular `role` on legacy responses */\n role?: string;\n roles?: { name: string; title?: string; description?: string }[];\n}\n\nexport interface Grant {\n filter: string;\n permissions: DocumentValuePermission[];\n}\n\n/**\n * What the Access API (`/access/<type>/<id>/…`) answers about one actor on\n * one resource: the roles it holds there (served by `…/users` and collected\n * into `…/roles`) and the permissions `…/user-permissions/me` and\n * `…/user-permissions/me/check` report, as `\"<type>.<action>\"` names such\n * as `\"sanity.media-library.read\"`.\n */\nexport interface ResourceAccess {\n roles?: NonNullable<CurrentUser[\"roles\"]>;\n permissions?: string[];\n /**\n * The user's attributes in the organization, as an administrator or SAML\n * sync set them; read on `\"organization:<id>\"` entries only. Served by\n * `GET /organizations/<id>/users/me/attributes`.\n */\n attributes?: UserAttribute[];\n}\n\n/** A value a user attribute can hold. */\nexport type UserAttributeValue = string | number | boolean | string[] | number[] | boolean[];\n\n/**\n * One user attribute: its key, its type, and its value from each source.\n * The served row adds `activeSource` and `activeValue`: the Sanity value\n * when there is one, else the SAML value.\n */\nexport interface UserAttribute {\n key: string;\n type: string;\n values: { saml?: UserAttributeValue; sanity?: UserAttributeValue };\n}\n\nexport interface TestClientAccessControl {\n /** What `client.request({ url: \"/users/me\" })` returns on a project-hosted handle. */\n currentUser: CurrentUser;\n /**\n * What `/users/me` returns on a global-hosted handle\n * (`useProjectHostname: false`, at creation or via `withConfig`).\n * Sanity serves the\n * account-global user there (`g*` id, no roles) while the project host\n * serves the project-scoped record (`p*` id, project roles). Omit to\n * serve `currentUser` from both — the single-id collapse that is also\n * exactly how robot tokens behave in production.\n */\n globalUser?: CurrentUser;\n /** What `client.request({ url: aclPath })` returns. */\n grants: Grant[];\n /**\n * URL path the engine's access resolver hits to fetch grants.\n * Defaults to the handle's resource ACL: `/projects/<projectId>/datasets/<dataset>/acl`,\n * or `<resource base>/acl` on a `resource`-configured handle.\n */\n aclPath?: string;\n /**\n * Access API entries, keyed `\"<access type>:<id>\"` — `\"organization:<id>\"`,\n * `\"project:<projectId>\"`, `\"media-library:<id>\"`, `\"canvas:<id>\"`, `\"dashboard:<id>\"`. See {@link ResourceAccess}.\n */\n access?: Record<string, ResourceAccess>;\n}\n\n/**\n * One entry in the token→identity registry (`createTestClient({ identities })`).\n * A handle bound to the entry's token behaves exactly like one configured\n * with `accessControl: { currentUser: projectUserOf(identity, projectId),\n * globalUser, grants }` — `/users/me` serves the record for the handle's\n * project and host, the default ACL path serves `grants`, and reads/writes\n * are gated through them.\n */\nexport interface TestClientIdentity {\n /**\n * The project-scoped user record: what `/users/me` returns on a\n * project-hosted handle, and the id `identity()` resolves to in grant\n * filters and mutation guards. Per-project variants go in\n * `usersByProject`; this is the record for any project without one.\n */\n user: CurrentUser;\n /**\n * The account-global user record, served by `/users/me` on a\n * global-hosted handle (`useProjectHostname: false`, at creation or\n * via `withConfig`).\n * The same person carries a different id per project (`p*`) and one\n * global id (`g*`); registering both lets tests model that split.\n * Omit for single-id principals (robot tokens resolve to one `p-*` id\n * on every host).\n */\n globalUser?: CurrentUser;\n /**\n * Per-project user records, keyed by projectId — the same token\n * resolves to a different project-scoped id in each project, which is\n * what these model. A handle's project falls back to `user` when its\n * projectId has no entry.\n */\n usersByProject?: Record<string, CurrentUser>;\n /** Grants served at the ACL path and enforced on reads/writes. */\n grants: Grant[];\n /**\n * Access API entries, keyed `\"<access type>:<id>\"` (`\"organization:<id>\"`,\n * `\"media-library:<id>\"`, `\"canvas:<id>\"`, `\"dashboard:<id>\"`,\n * `\"project:<projectId>\"`). On a non-project resource, an entry with\n * `roles` makes the identity a member there. Project membership needs no entry: every identity is a member of\n * every project with its project-scoped roles.\n */\n access?: Record<string, ResourceAccess>;\n}\n\n/**\n * A row served by the project-users directory endpoint\n * (`/projects/<projectId>/users/<id>`), mirroring Sanity's project user\n * retrieval API: the project-scoped `id` alongside its account-global\n * `sanityUserId` — the one sanctioned bridge between the two namespaces.\n */\nexport interface ProjectUserRecord {\n id: string;\n sanityUserId: string;\n displayName: string;\n email?: string;\n}\n\n/**\n * Sanity-style 403 raised when a configured-grants check rejects a\n * write. The thrown object carries the doc id, the attempted\n * permission, and the count of matching grants (for parity with the\n * engine's `permission-denied` disabled-reason).\n */\nexport class WriteAccessDeniedError extends Error {\n readonly documentId: string;\n readonly permission: DocumentValuePermission;\n readonly matchingGrants: number;\n\n constructor(args: {\n documentId: string;\n permission: DocumentValuePermission;\n matchingGrants: number;\n }) {\n super(\n `Sanity (test): permission denied — actor lacks \"${args.permission}\" on document \"${args.documentId}\" ` +\n `(${args.matchingGrants} grant${args.matchingGrants === 1 ? \"\" : \"s\"} matched the document but ` +\n `none included \"${args.permission}\").`,\n );\n this.name = \"WriteAccessDeniedError\";\n this.documentId = args.documentId;\n this.permission = args.permission;\n this.matchingGrants = args.matchingGrants;\n }\n}\n\n/**\n * Predicate variant of `evaluateWriteAccess` — same logic but only\n * returns the boolean. Used by read-gating paths\n * (`fetch`/`getDocument`/`getDocuments`) where the documented Sanity\n * behavior is \"invisible doc → 404\", not \"throw 403\". So callers\n * can filter the dataset before GROQ runs and return `null` from\n * single-doc reads, instead of leaking a denial through the read\n * surface.\n */\nexport async function canAccess(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n grants: Grant[];\n permission: DocumentValuePermission;\n userId: string;\n}): Promise<boolean> {\n const result = await evaluateWriteAccess(args);\n return result.allowed;\n}\n\n// Parsed-filter cache. The GROQ AST is cheap to reuse; only the\n// per-document evaluation needs to re-run.\nconst parsedFilters = new Map<string, ReturnType<typeof parse>>();\n\nasync function matchesFilter(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n filter: string;\n userId: string;\n}): Promise<boolean> {\n const { document, filter, userId } = args;\n if (!parsedFilters.has(filter)) {\n parsedFilters.set(filter, parse(`*[${filter}]`));\n }\n const ast = parsedFilters.get(filter)!;\n const result = await evaluate(ast, {\n dataset: [document as Record<string, unknown>],\n identity: userId,\n });\n const data = await result.get();\n return Array.isArray(data) && data.length === 1;\n}\n\n/**\n * Resolve whether `grants` allow `permission` on `document` for the\n * given user. Same logic the engine uses in `permissions.grantsPermissionOn`\n * (most-permissive wins; multiple grants compose).\n *\n * Returns `{ allowed: boolean, matchingGrants: number }` so callers\n * can build the structured error without re-counting.\n */\nexport async function evaluateWriteAccess(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n grants: Grant[];\n permission: DocumentValuePermission;\n userId: string;\n}): Promise<{ allowed: boolean; matchingGrants: number }> {\n const { document, grants, permission, userId } = args;\n let matching = 0;\n for (const grant of grants) {\n if (\n await matchesFilter({\n document,\n filter: grant.filter,\n userId,\n })\n ) {\n matching++;\n if (grant.permissions.includes(permission)) {\n return { allowed: true, matchingGrants: matching };\n }\n }\n }\n return { allowed: false, matchingGrants: matching };\n}\n\n/**\n * Throw a `WriteAccessDeniedError` if `grants` don't permit the\n * supplied `permission` on `document`. No-op when allowed. Use this\n * from every mutation entrypoint that wants to simulate Sanity's\n * write boundary.\n */\nexport async function enforceWriteAccess(args: {\n document: SanityDocumentLike & { _id?: string; _type?: string };\n grants: Grant[];\n permission: DocumentValuePermission;\n userId: string;\n}): Promise<void> {\n const { document, grants, permission, userId } = args;\n const { allowed, matchingGrants } = await evaluateWriteAccess({\n document,\n grants,\n permission,\n userId,\n });\n if (allowed) return;\n if (typeof document._id !== \"string\") {\n throw new WriteAccessDeniedError({\n documentId: \"(unknown)\",\n permission,\n matchingGrants,\n });\n }\n throw new WriteAccessDeniedError({\n documentId: document._id,\n permission,\n matchingGrants,\n });\n}\n\n/**\n * Default ACL path the engine pulls grants from. Mirrors Sanity's\n * project/dataset ACL endpoint. Callers can override via\n * `accessControl.aclPath`.\n */\nexport function defaultAclPath(projectId: string, dataset: string): string {\n return `/projects/${projectId}/datasets/${dataset}/acl`;\n}\n\n/** The platform's refusal for `GET <path>` when it is the ACL of a resource type without one. */\nfunction refusedAcl(path: string): PlatformHttpError | undefined {\n const matched = matchResourcePath(path);\n if (matched?.rest !== \"/acl\") return undefined;\n const { acl } = capabilitiesOf(matched.resource.type);\n return acl === \"served\" ? undefined : acl({ method: \"GET\", path });\n}\n\n/** Everything {@link makeRequestImpl} needs from the owning handle. */\nexport interface RequestImplContext {\n /**\n * Active access-control resolver. A getter, not a value — token-bound\n * handles resolve their identity from the shared registry on every\n * call, so a re-registered identity (`addIdentity` upsert) takes\n * effect on already-minted siblings.\n */\n getAccess: () => TestClientAccessControl;\n projectId: string;\n /**\n * Whether this handle addresses the project host (default) or the\n * global API host (`useProjectHostname: false`, at creation or via\n * `withConfig`).\n * Decides which user record `/users/me` serves.\n */\n useProjectHostname: boolean;\n /**\n * The handle's members (registered identities, plus its inline\n * `accessControl` actor), for the project endpoints. A getter so\n * `addIdentity` upserts are visible.\n */\n listIdentities: () => TestClientIdentity[];\n /** See {@link CreateTestClientOptions.organizationId}; added to `/projects/<projectId>`. */\n organizationId: string | undefined;\n /** History, ACL, resource lookup and Access API routing; see `./platform-api.ts`. */\n platform: PlatformApiContext;\n}\n\n/** The project-scoped record a registry identity resolves to in `projectId`. */\nexport function projectUserOf(identity: TestClientIdentity, projectId: string): CurrentUser {\n return identity.usersByProject?.[projectId] ?? identity.user;\n}\n\n/**\n * Deduplicate tokens that resolve to one project user. Callers supply served\n * records in canonical form so stringify equality represents semantic\n * equality; divergent records fail instead of producing a last-wins answer.\n */\nfunction consistentProjectRecords<RecordType extends { id: string }>(args: {\n projectId: string;\n records: readonly RecordType[];\n}): Map<string, RecordType> {\n const byId = new Map<string, RecordType>();\n for (const record of args.records) {\n const existing = byId.get(record.id);\n if (existing !== undefined && JSON.stringify(existing) !== JSON.stringify(record)) {\n throw fakeError(\n `request(): two identities (registered, or the handle's inline accessControl) resolve to ` +\n `project user \"${record.id}\" in project ` +\n `\"${args.projectId}\" with different records. The project records cannot be served ` +\n `unambiguously — fix the identities so duplicate ids carry identical records.`,\n );\n }\n byId.set(record.id, record);\n }\n return byId;\n}\n\n/** Normalize role key and array order for stable serving and duplicate comparison. */\nfunction canonicalProjectRoles(roles: NonNullable<CurrentUser[\"roles\"]>) {\n return roles\n .map((role) => ({\n name: role.name,\n ...(role.title !== undefined ? { title: role.title } : {}),\n ...(role.description !== undefined ? { description: role.description } : {}),\n }))\n .toSorted((left, right) => {\n const leftKey = JSON.stringify(left);\n const rightKey = JSON.stringify(right);\n if (leftKey < rightKey) return -1;\n if (leftKey > rightKey) return 1;\n return 0;\n });\n}\n\n/**\n * Serve `/projects/<projectId>/users/<ids>` from the identity registry —\n * the fake's mirror of Sanity's project user retrieval API, the bridge\n * between project-scoped ids and `sanityUserId`. Single id → one record;\n * comma-separated ids → array. Unknown single id throws with\n * `statusCode: 404`, like production; unknown ids in a list are omitted.\n */\nfunction serveProjectUsers(args: {\n requestedIds: string;\n pathProjectId: string;\n identities: TestClientIdentity[];\n}): ProjectUserRecord | ProjectUserRecord[] {\n const { requestedIds, pathProjectId, identities } = args;\n const toRecord = (identity: TestClientIdentity): ProjectUserRecord => {\n const projectUser = projectUserOf(identity, pathProjectId);\n return {\n id: projectUser.id,\n sanityUserId: identity.globalUser?.id ?? projectUser.id,\n displayName: projectUser.name ?? projectUser.id,\n ...(projectUser.email !== undefined ? { email: projectUser.email } : {}),\n };\n };\n const byId = consistentProjectRecords({\n projectId: pathProjectId,\n records: identities.map(toRecord),\n });\n const ids = requestedIds.split(\",\");\n const [firstId] = ids;\n if (ids.length === 1 && firstId !== undefined) {\n const record = byId.get(firstId);\n if (record === undefined) {\n throw Object.assign(\n fakeError(\n `request(): no registered identity resolves to project user \"${firstId}\" in project ` +\n `\"${pathProjectId}\". Register it via createTestClient({ identities }) or addIdentity().`,\n ),\n { statusCode: 404 },\n );\n }\n return record;\n }\n return ids.flatMap((id) => {\n const record = byId.get(id);\n return record === undefined ? [] : [record];\n });\n}\n\nconst PROJECT_USERS_PATH = /^\\/projects\\/([^/]+)\\/users\\/([^/]+)$/;\n\nconst PROJECT_PATH = /^\\/projects\\/([^/]+)$/;\n\n/** One row of the `/projects/<projectId>` members response. */\nexport interface ProjectMemberRecord {\n id: string;\n isRobot?: boolean;\n /** Project-scoped roles in canonical order, independent of registration order. */\n roles?: NonNullable<CurrentUser[\"roles\"]>;\n}\n\n/**\n * Serve `/projects/<projectId>` from the identity registry — the members\n * half of Sanity's project API, which member-list joins read\n * (`members[].id` carries the project-scoped principal id and `roles` carries\n * its project roles; robot principals are flagged `isRobot`, matching their\n * production `p-…` form). One row per distinct resolved project user, however\n * many tokens map to that person.\n */\nfunction serveProject(args: {\n pathProjectId: string;\n identities: TestClientIdentity[];\n organizationId: string | undefined;\n}): {\n organizationId?: string;\n members: ProjectMemberRecord[];\n} {\n const records = args.identities.map((identity) => {\n const projectUser = projectUserOf(identity, args.pathProjectId);\n return {\n id: projectUser.id,\n ...(projectUser.id.startsWith(\"p-\") ? { isRobot: true } : {}),\n ...(projectUser.roles !== undefined\n ? { roles: canonicalProjectRoles(projectUser.roles) }\n : {}),\n } satisfies ProjectMemberRecord;\n });\n const members = consistentProjectRecords({ projectId: args.pathProjectId, records });\n return {\n ...(args.organizationId !== undefined ? { organizationId: args.organizationId } : {}),\n members: [...members.values()],\n };\n}\n\n/**\n * Internal helper: turn the active access-control config into the bound\n * `request` function the client exposes.\n */\nexport function makeRequestImpl(\n ctx: RequestImplContext,\n): <T>(opts: {\n url?: string;\n uri?: string;\n signal?: AbortSignal;\n tag?: string | null;\n}) => Promise<T> {\n const { getAccess, useProjectHostname, listIdentities, organizationId, platform } = ctx;\n return async <T>(opts: {\n url?: string;\n uri?: string;\n signal?: AbortSignal;\n tag?: string | null;\n }): Promise<T> => {\n const accessControl = getAccess();\n // `@sanity/client` distinguishes `uri` (project-scoped, e.g.\n // `/users/me`) from `url` (raw). Treat both identically for the mock —\n // host selection rides the handle's `useProjectHostname`, as in\n // production, not the option name.\n const path = opts.uri ?? opts.url;\n if (path === \"/users/me\") {\n // The global host serves the account-global user; the project host\n // serves the project-scoped record. Absent `globalUser` both serve\n // the same record — the single-id shape robot tokens have in\n // production.\n const user = useProjectHostname\n ? accessControl.currentUser\n : (accessControl.globalUser ?? accessControl.currentUser);\n return user as unknown as T;\n }\n // An explicit `aclPath` aliases the grants, but never past a route the platform does not mount.\n if (path !== undefined && path === accessControl.aclPath && refusedAcl(path) === undefined) {\n return accessControl.grants as unknown as T;\n }\n const projectUsers = path?.match(PROJECT_USERS_PATH);\n const [, pathProjectId, requestedIds] = projectUsers ?? [];\n if (pathProjectId !== undefined && requestedIds !== undefined) {\n return serveProjectUsers({\n requestedIds,\n pathProjectId,\n identities: listIdentities(),\n }) as unknown as T;\n }\n const project = path?.match(PROJECT_PATH);\n const [, membersProjectId] = project ?? [];\n if (membersProjectId !== undefined) {\n return serveProject({\n pathProjectId: membersProjectId,\n identities: listIdentities(),\n organizationId,\n }) as unknown as T;\n }\n const served = path === undefined ? undefined : await servePlatformRequest(platform, path);\n if (served !== undefined) return served.value as T;\n throw fakeError(\n `request(): no handler for path \"${path}\". With access control configured, the fake serves ` +\n `\"/users/me\", the handle's ACL (\"<data base>/acl\"), \"/projects/<projectId>\" (members), ` +\n `\"/projects/<projectId>/users/<ids>\", the History API (client.getDataUrl(\"history\", …)), ` +\n `resource lookup (\"/media-libraries/<id>\", \"/canvases/<id>\", \"/dashboards/<id>\") and the ` +\n `Access API (\"/access/<type>/<id>/roles[/<name>] | users[/<id>] | user-permissions/me[/check]\") ` +\n `and the caller's attributes (\"/organizations/<id>/users/me/attributes\"). For any ` +\n `other endpoint, register a stub (client.stub(\"request\", fn)) or intercept the HTTP call ` +\n `with a tool like MSW.`,\n );\n };\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\n\n/** The Content Lake mutation actions that a {@link MutationGuard} can match. */\nexport type MutationGuardAction = \"create\" | \"update\" | \"delete\";\n\nexport interface MutationGuardDenial {\n guardID: string;\n name?: string;\n}\n\nexport interface GuardDeniedResponse {\n body: {\n error: {\n type: \"guardDeniedError\";\n denials: MutationGuardDenial[];\n };\n };\n url: string;\n method: \"POST\";\n headers: Record<string, string>;\n statusCode: 403;\n statusMessage: \"Forbidden\";\n}\n\n/** HTTP-shaped guard rejection matching the error exposed by `@sanity/client`. */\nexport class GuardDeniedError extends Error {\n readonly statusCode = 403;\n readonly type = \"guardDeniedError\";\n readonly denials: MutationGuardDenial[];\n readonly details: GuardDeniedResponse[\"body\"][\"error\"];\n readonly response: GuardDeniedResponse;\n\n constructor(denials: readonly MutationGuardDenial[]) {\n const unique = uniqueDenials(denials);\n super(`Mutation denied by guard(s) [${unique.map((denial) => denial.guardID).join(\", \")}]`);\n this.name = \"GuardDeniedError\";\n this.denials = unique;\n this.details = { type: this.type, denials: unique };\n this.response = {\n body: { error: this.details },\n url: \"memory://client-fake-for-test/data/mutate\",\n method: \"POST\",\n headers: {},\n statusCode: this.statusCode,\n statusMessage: \"Forbidden\",\n };\n }\n}\n\nfunction uniqueDenials(denials: readonly MutationGuardDenial[]): MutationGuardDenial[] {\n const seen = new Set<string>();\n return denials.filter((denial) => {\n if (seen.has(denial.guardID)) return false;\n seen.add(denial.guardID);\n return true;\n });\n}\n\n/** The before/final-after image of one net document change in a commit. */\nexport interface MutationGuardContext {\n action: MutationGuardAction;\n /** Original live store image. Treat it as read-only; it is not a copy. */\n before: Readonly<SanityDocumentLike & { _id: string; _type: string }> | null;\n /** Final live store image. Treat it as read-only; it is not a copy. */\n after: Readonly<SanityDocumentLike & { _id: string; _type: string }> | null;\n /** Acting project identity, or `undefined` on an identity-free client. */\n identity: string | undefined;\n}\n\n/**\n * Evaluates one net document change and returns every denying guard. Returning\n * `void` allows observe-only hooks. A thrown value is a callback failure and\n * propagates unchanged; only returned entries become {@link GuardDeniedError}.\n */\nexport type MutationGuard = (\n context: MutationGuardContext,\n) => readonly MutationGuardDenial[] | void | Promise<readonly MutationGuardDenial[] | void>;\n\nexport interface MutationImages {\n before: SanityDocument | undefined;\n after: SanityDocument | undefined;\n}\n\nexport type MutationGuardImageContext = Omit<MutationGuardContext, \"identity\">;\n\n/** Derive the single lake action represented by a document's net commit image. */\nexport function mutationContext(images: MutationImages): MutationGuardImageContext | undefined {\n const { before, after } = images;\n // TestStore preserves object identity until a write replaces the document.\n // Equal references therefore mean an ID was remembered but never changed.\n if (before === after) return undefined;\n if (before === undefined) return { action: \"create\", before: null, after: after ?? null };\n if (after === undefined) return { action: \"delete\", before, after: null };\n return { action: \"update\", before, after };\n}\n\n/** Derive guard contexts for an ordered collection of net document images. */\nexport function mutationContexts(images: Iterable<MutationImages>): MutationGuardImageContext[] {\n return [...images].flatMap((image) => {\n const context = mutationContext(image);\n return context === undefined ? [] : [context];\n });\n}\n\n/** Evaluate every net context before rejecting once with all returned denials. */\nexport async function enforceMutationGuards(\n guard: MutationGuard | undefined,\n contexts: readonly MutationGuardContext[],\n): Promise<void> {\n if (guard === undefined) return;\n const denials: MutationGuardDenial[] = [];\n for (const context of contexts) {\n const denied = await guard(context);\n if (denied !== undefined) denials.push(...denied);\n }\n if (denials.length > 0) throw new GuardDeniedError(denials);\n}\n","import type { SanityDocument } from \"@sanity/types\";\nimport { fakeError } from \"./errors.ts\";\n\n/** Human-readable type of a value, for \"expected an array but got …\" errors. */\nfunction describeType(value: unknown): string {\n if (value === null) return \"null\";\n if (Array.isArray(value)) return \"an array\";\n return `a ${typeof value}`;\n}\n\n/**\n * Patch operations the test client supports.\n *\n * Subset of `@sanity/client`'s PatchOperations. Each entry is one\n * mutation step applied in order at commit time.\n */\nexport type PatchOp =\n | { kind: \"set\"; props: Record<string, unknown> }\n | { kind: \"setIfMissing\"; props: Record<string, unknown> }\n | { kind: \"unset\"; paths: string[] }\n | { kind: \"inc\"; props: Record<string, number> }\n | { kind: \"dec\"; props: Record<string, number> }\n | {\n kind: \"insert\";\n position: \"before\" | \"after\" | \"replace\";\n anchor: string;\n items: unknown[];\n };\n\nexport interface PatchHandle {\n set: (props: Record<string, unknown>) => PatchHandle;\n setIfMissing: (props: Record<string, unknown>) => PatchHandle;\n unset: (paths: string[]) => PatchHandle;\n inc: (props: Record<string, number>) => PatchHandle;\n dec: (props: Record<string, number>) => PatchHandle;\n insert: (\n position: \"before\" | \"after\" | \"replace\",\n anchor: string,\n items: unknown[],\n ) => PatchHandle;\n ifRevisionId: (rev: string) => PatchHandle;\n commit: (options?: PatchCommitOptions) => Promise<SanityDocument>;\n /** Test-only introspection — not on real Patch */\n toJSON: () => { id: string; ops: PatchOp[]; ifRev?: string };\n}\n\n/** Options for `PatchHandle.commit()`. Only `tag` is meaningful to the fake. */\nexport interface PatchCommitOptions {\n /** Request tag, recorded on the mutation log entry (`null` = explicit suppression). */\n tag?: string | null;\n /** Accepted for signature parity; not simulated. */\n visibility?: \"sync\" | \"async\" | \"deferred\";\n}\n\nexport interface PatchExecutor {\n apply: (\n documentId: string,\n ops: PatchOp[],\n ifRev: string | undefined,\n options: PatchCommitOptions | undefined,\n ) => Promise<SanityDocument>;\n}\n\nexport function createPatch(documentId: string, executor: PatchExecutor): PatchHandle {\n const ops: PatchOp[] = [];\n let ifRev: string | undefined;\n\n const handle: PatchHandle = {\n set: (props) => {\n ops.push({ kind: \"set\", props });\n return handle;\n },\n setIfMissing: (props) => {\n ops.push({ kind: \"setIfMissing\", props });\n return handle;\n },\n unset: (paths) => {\n ops.push({ kind: \"unset\", paths });\n return handle;\n },\n inc: (props) => {\n ops.push({ kind: \"inc\", props });\n return handle;\n },\n dec: (props) => {\n ops.push({ kind: \"dec\", props });\n return handle;\n },\n insert: (position, anchor, items) => {\n ops.push({ kind: \"insert\", position, anchor, items });\n return handle;\n },\n ifRevisionId: (rev) => {\n ifRev = rev;\n return handle;\n },\n commit: (options) => executor.apply(documentId, ops, ifRev, options),\n toJSON: () => ({\n id: documentId,\n ops: [...ops],\n ...(ifRev !== undefined ? { ifRev } : {}),\n }),\n };\n\n return handle;\n}\n\n/**\n * Apply an ordered list of patch ops to a document, returning the new doc.\n *\n * The path semantics are deliberately a small subset:\n *\n * - **Dotted paths** (`a.b.c`) for nested object fields.\n * - **Array index** (`a.b[0]`) for positional access into arrays.\n * - **Keyed lookup** (`a.b[_key==\"x\"]`) for finding an array entry by `_key`.\n *\n * Anything more exotic (recursive `..`, GROQ filter expressions, multi-match)\n * is intentionally out of scope and will throw — the workflow engine only\n * needs the three forms above.\n */\nexport function applyPatchOps(doc: SanityDocument, ops: PatchOp[]): SanityDocument {\n let next: SanityDocument = structuredClone(doc);\n for (const op of ops) {\n next = applyOne(next, op);\n }\n return next;\n}\n\nfunction applyOne(doc: SanityDocument, op: PatchOp): SanityDocument {\n switch (op.kind) {\n case \"set\":\n for (const [path, value] of Object.entries(op.props)) {\n setAtPath(doc, path, value);\n }\n return doc;\n case \"setIfMissing\":\n for (const [path, value] of Object.entries(op.props)) {\n if (getAtPath(doc, path) === undefined) {\n setAtPath(doc, path, value);\n }\n }\n return doc;\n case \"unset\":\n for (const path of op.paths) {\n unsetAtPath(doc, path);\n }\n return doc;\n case \"inc\":\n for (const [path, delta] of Object.entries(op.props)) {\n const current = getAtPath(doc, path);\n const base = typeof current === \"number\" ? current : 0;\n setAtPath(doc, path, base + delta);\n }\n return doc;\n case \"dec\":\n for (const [path, delta] of Object.entries(op.props)) {\n const current = getAtPath(doc, path);\n const base = typeof current === \"number\" ? current : 0;\n setAtPath(doc, path, base - delta);\n }\n return doc;\n case \"insert\": {\n insertIntoArray(doc, op.position, op.anchor, op.items);\n return doc;\n }\n }\n}\n\ninterface PathSegment {\n key: string;\n // Optional array selector. Either a numeric index or a key=value match.\n selector?: { kind: \"index\"; index: number } | { kind: \"keyed\"; field: string; value: string };\n}\n\nfunction parsePath(path: string): PathSegment[] {\n // Examples:\n // \"currentStageId\"\n // \"taskStatus[0].status\"\n // \"taskStatus[_key==\\\"approve\\\"].status\"\n // \"context[key==\\\"releaseId\\\"].value\"\n const segments: PathSegment[] = [];\n for (const raw of path.split(\".\")) {\n const match = /^([A-Za-z_$][\\w$]*)(?:\\[(.+)\\])?$/.exec(raw);\n if (!match) {\n throw fakeError(\n `Unsupported patch path segment \"${raw}\" (in path \"${path}\"). This fake supports dotted ` +\n `field access (\"a.b.c\"), array index (\"items[0]\"), and keyed lookup (\\`items[_key==\"x\"]\\`). ` +\n `Recursive descent (\"..\") and GROQ filter expressions are not supported.`,\n );\n }\n const key = match[1] as string;\n const selectorRaw = match[2];\n if (selectorRaw === undefined) {\n segments.push({ key });\n continue;\n }\n const indexMatch = /^(\\d+)$/.exec(selectorRaw);\n if (indexMatch) {\n segments.push({ key, selector: { kind: \"index\", index: Number(indexMatch[1]) } });\n continue;\n }\n const keyedMatch = /^([A-Za-z_$][\\w$]*)\\s*==\\s*\"([^\"]*)\"$/.exec(selectorRaw);\n if (keyedMatch) {\n segments.push({\n key,\n selector: { kind: \"keyed\", field: keyedMatch[1] as string, value: keyedMatch[2] as string },\n });\n continue;\n }\n throw fakeError(\n `Unsupported array selector \"[${selectorRaw}]\" in patch path \"${path}\". Supported selectors ` +\n `are a numeric index (e.g. \"[0]\") and a single keyed match (e.g. \\`[_key==\"abc\"]\\`). ` +\n `Multi-key matches and arbitrary GROQ filters are not supported.`,\n );\n }\n return segments;\n}\n\nfunction resolveSelector(array: unknown[], selector: NonNullable<PathSegment[\"selector\"]>): number {\n if (selector.kind === \"index\") return selector.index;\n const idx = array.findIndex(\n (entry) =>\n typeof entry === \"object\" &&\n entry !== null &&\n (entry as Record<string, unknown>)[selector.field] === selector.value,\n );\n if (idx === -1) {\n throw fakeError(\n `Patch path matched no array entry where ${selector.field}==\"${selector.value}\". The array has ` +\n `${array.length} item(s); check the key exists (and that you're patching the right document).`,\n );\n }\n return idx;\n}\n\nfunction getAtPath(doc: SanityDocument, path: string): unknown {\n const segments = parsePath(path);\n let cursor: unknown = doc;\n for (const seg of segments) {\n if (cursor === undefined || cursor === null) return undefined;\n cursor = (cursor as Record<string, unknown>)[seg.key];\n if (seg.selector !== undefined) {\n if (!Array.isArray(cursor)) return undefined;\n const idx = resolveSelector(cursor, seg.selector);\n cursor = cursor[idx];\n }\n }\n return cursor;\n}\n\nfunction setAtPath(doc: SanityDocument, path: string, value: unknown): void {\n const segments = parsePath(path);\n if (segments.length === 0)\n throw fakeError(`Empty patch path — a set/setIfMissing op needs a target field.`);\n let cursor: Record<string, unknown> = doc as unknown as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i] as PathSegment;\n let next = cursor[seg.key];\n if (next === undefined) {\n next = seg.selector?.kind === \"index\" || seg.selector?.kind === \"keyed\" ? [] : {};\n cursor[seg.key] = next;\n }\n if (seg.selector !== undefined) {\n if (!Array.isArray(next)) {\n throw fakeError(\n `Patch path \"${path}\" applies an array selector to field \"${seg.key}\", but that field ` +\n `holds ${describeType(next)}, not an array.`,\n );\n }\n const idx = resolveSelector(next, seg.selector);\n cursor = next[idx] as Record<string, unknown>;\n } else {\n cursor = next as Record<string, unknown>;\n }\n }\n const last = segments[segments.length - 1] as PathSegment;\n if (last.selector !== undefined) {\n const arr = cursor[last.key];\n if (!Array.isArray(arr)) {\n throw fakeError(\n `Patch path \"${path}\" applies an array selector to field \"${last.key}\", but that field ` +\n `holds ${describeType(arr)}, not an array.`,\n );\n }\n const idx = resolveSelector(arr, last.selector);\n arr[idx] = value;\n } else {\n cursor[last.key] = value;\n }\n}\n\nfunction unsetAtPath(doc: SanityDocument, path: string): void {\n const segments = parsePath(path);\n if (segments.length === 0) return;\n let cursor: Record<string, unknown> = doc as unknown as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i] as PathSegment;\n let next = cursor[seg.key];\n if (next === undefined || next === null) return;\n if (seg.selector !== undefined) {\n if (!Array.isArray(next)) return;\n const idx = resolveSelector(next, seg.selector);\n next = next[idx];\n }\n cursor = next as Record<string, unknown>;\n }\n const last = segments[segments.length - 1] as PathSegment;\n if (last.selector !== undefined) {\n const arr = cursor[last.key];\n if (!Array.isArray(arr)) return;\n const idx = resolveSelector(arr, last.selector);\n arr.splice(idx, 1);\n } else {\n delete cursor[last.key];\n }\n}\n\nfunction insertIntoArray(\n doc: SanityDocument,\n position: \"before\" | \"after\" | \"replace\",\n anchor: string,\n items: unknown[],\n): void {\n // anchor is a path that must terminate at an array entry (selector required).\n // For \"replace\", we replace the entry; for \"before\"/\"after\", we insert\n // adjacent to it.\n const segments = parsePath(anchor);\n if (segments.length === 0)\n throw fakeError(`Empty insert anchor — insert() needs an array path to anchor against.`);\n const last = segments[segments.length - 1] as PathSegment;\n if (last.selector === undefined) {\n throw fakeError(\n `Insert anchor \"${anchor}\" must end with an array selector (an index like \"[0]\" or a keyed ` +\n `match like \\`[_key==\"x\"]\\`) so the fake knows where in the array to insert.`,\n );\n }\n\n let cursor: Record<string, unknown> = doc as unknown as Record<string, unknown>;\n for (let i = 0; i < segments.length - 1; i++) {\n const seg = segments[i] as PathSegment;\n const next = cursor[seg.key];\n if (next === undefined || next === null) {\n throw fakeError(\n `Insert anchor \"${anchor}\" traverses field \"${seg.key}\", which is missing on the document. ` +\n `setIfMissing the parent first, or anchor against an existing array.`,\n );\n }\n if (seg.selector !== undefined) {\n if (!Array.isArray(next)) {\n throw fakeError(\n `Insert anchor \"${anchor}\" applies an array selector to field \"${seg.key}\", but it holds ` +\n `${describeType(next)}, not an array.`,\n );\n }\n cursor = next[resolveSelector(next, seg.selector)] as Record<string, unknown>;\n } else {\n cursor = next as Record<string, unknown>;\n }\n }\n const arr = cursor[last.key];\n if (!Array.isArray(arr)) {\n throw fakeError(\n `Insert anchor \"${anchor}\" targets field \"${last.key}\", but it holds ${describeType(arr)}, ` +\n `not an array.`,\n );\n }\n const idx = resolveSelector(arr, last.selector);\n if (position === \"replace\") {\n arr.splice(idx, 1, ...items);\n } else if (position === \"before\") {\n arr.splice(idx, 0, ...items);\n } else {\n arr.splice(idx + 1, 0, ...items);\n }\n}\n","/**\n * Perspective layering — read-side filtering that mirrors Sanity's\n * `perspective` query/client option.\n *\n * The store always holds raw documents. Each fetch builds a *view* of\n * the store filtered by the active perspective before passing the\n * dataset to `groq-js`.\n *\n * Reference: https://www.sanity.io/docs/content-lake/perspectives\n */\n\nimport type { SanityDocument } from \"@sanity/types\";\n\n/**\n * `raw`, `published`, `drafts`, a release stack, or a single release name\n * (`\"rSpring\"`) — which the Content Lake reads as a one-release stack.\n */\nexport type Perspective = \"raw\" | \"published\" | \"drafts\" | (string & {}) | string[];\n\nconst DRAFTS_PREFIX = \"drafts.\";\nconst VERSIONS_PREFIX = \"versions.\";\nconst SYSTEM_PREFIX = \"_.\";\nconst RELEASE_DOC_PREFIX = \"_.releases.\";\n\n/** Strip `drafts.` / `versions.<name>.` prefix to recover the published id. */\nexport function publishedIdOf(id: string): string {\n if (id.startsWith(DRAFTS_PREFIX)) return id.slice(DRAFTS_PREFIX.length);\n if (id.startsWith(VERSIONS_PREFIX)) {\n // versions.<name>.<id> — split off the prefix + name\n const rest = id.slice(VERSIONS_PREFIX.length);\n const dot = rest.indexOf(\".\");\n if (dot === -1) return rest;\n return rest.slice(dot + 1);\n }\n return id;\n}\n\n/** Extract the release name from a `versions.<name>.<id>` id, else undefined. */\nexport function releaseNameOf(id: string): string | undefined {\n if (!id.startsWith(VERSIONS_PREFIX)) return undefined;\n const rest = id.slice(VERSIONS_PREFIX.length);\n const dot = rest.indexOf(\".\");\n if (dot === -1) return undefined;\n return rest.slice(0, dot);\n}\n\nexport const isDraftId = (id: string): boolean => id.startsWith(DRAFTS_PREFIX);\nexport const isVersionId = (id: string): boolean => id.startsWith(VERSIONS_PREFIX);\nexport const isSystemId = (id: string): boolean => id.startsWith(SYSTEM_PREFIX);\nexport const isReleaseDocId = (id: string): boolean => id.startsWith(RELEASE_DOC_PREFIX);\n\n/**\n * Apply a perspective to a raw set of documents and return the view\n * groq-js should evaluate against.\n *\n * For non-raw perspectives, each output document for a non-system id\n * gets `_id` projected to its published id and `_originalId` carrying\n * the source id (matching the wire shape of the real Content Lake).\n */\nexport function applyPerspective(\n docs: readonly SanityDocument[],\n requested: Perspective,\n): SanityDocument[] {\n const perspective = normalizePerspective(requested);\n if (perspective === \"raw\") return [...docs];\n\n if (perspective === \"published\") {\n return docs.filter((d) => !isDraftId(d._id) && !isVersionId(d._id) && !isSystemId(d._id));\n }\n\n if (perspective === \"drafts\") {\n return mergeStack(docs, [], { includeDrafts: true, includeReleaseDocs: false });\n }\n\n // string[] — release stack with optional \"drafts\" entry.\n const stack: string[] = [];\n let includeDrafts = false;\n for (const entry of perspective) {\n if (entry === \"drafts\") {\n includeDrafts = true;\n } else {\n stack.push(entry);\n }\n }\n return mergeStack(docs, stack, { includeDrafts, includeReleaseDocs: true });\n}\n\n/**\n * The perspective as the Content Lake receives it: `previewDrafts` is the\n * deprecated name of `drafts`, and any other string is a comma-joined\n * stack (`\"rA\"`, `\"drafts,rA\"`), the form `@sanity/client` sends.\n */\nfunction normalizePerspective(perspective: Perspective): \"raw\" | \"published\" | \"drafts\" | string[] {\n if (Array.isArray(perspective)) return perspective;\n switch (perspective) {\n case \"raw\":\n return \"raw\";\n case \"published\":\n return \"published\";\n case \"drafts\":\n case \"previewDrafts\":\n return \"drafts\";\n default:\n return perspective.split(\",\");\n }\n}\n\ninterface MergeOptions {\n includeDrafts: boolean;\n includeReleaseDocs: boolean;\n}\n\n/**\n * Walk the raw docs once, group by published id, then for each group\n * pick the highest-priority source according to `releaseStack` and\n * `includeDrafts`. Project the chosen doc to the published id.\n */\nfunction mergeStack(\n docs: readonly SanityDocument[],\n releaseStack: readonly string[],\n options: MergeOptions,\n): SanityDocument[] {\n // Group docs by published id. Track which sources exist.\n type Group = {\n published?: SanityDocument;\n draft?: SanityDocument;\n versions: Map<string, SanityDocument>;\n };\n const groups = new Map<string, Group>();\n const systemReleases: SanityDocument[] = [];\n\n for (const doc of docs) {\n if (isReleaseDocId(doc._id)) {\n if (options.includeReleaseDocs) systemReleases.push(doc);\n continue;\n }\n if (isSystemId(doc._id)) continue; // other system docs are hidden\n const pubId = publishedIdOf(doc._id);\n let group = groups.get(pubId);\n if (group === undefined) {\n group = { versions: new Map() };\n groups.set(pubId, group);\n }\n if (isDraftId(doc._id)) {\n group.draft = doc;\n } else if (isVersionId(doc._id)) {\n const releaseName = releaseNameOf(doc._id);\n if (releaseName !== undefined) group.versions.set(releaseName, doc);\n } else {\n group.published = doc;\n }\n }\n\n const out: SanityDocument[] = [];\n for (const [pubId, group] of groups) {\n // Walk the release stack first, then drafts (if enabled, in its\n // slot — here we model \"drafts after the stack\" matching the\n // typical `[r1, r2, \"drafts\"]` shape), then fall through to\n // published.\n let chosen: SanityDocument | undefined;\n for (const releaseName of releaseStack) {\n const v = group.versions.get(releaseName);\n if (v !== undefined) {\n // version.unpublish stub: hide the doc entirely\n if (isUnpublishStub(v)) {\n chosen = undefined;\n break;\n }\n chosen = v;\n break;\n }\n }\n if (chosen === undefined && options.includeDrafts && group.draft !== undefined) {\n chosen = group.draft;\n }\n if (chosen === undefined && group.published !== undefined) {\n // skip if any release in the stack carried an unpublish stub\n const blocked = releaseStack.some((name) => {\n const v = group.versions.get(name);\n return v !== undefined && isUnpublishStub(v);\n });\n if (!blocked) chosen = group.published;\n }\n if (chosen !== undefined) {\n out.push(projectToPublishedId(chosen, pubId));\n }\n }\n\n return [...out, ...systemReleases];\n}\n\nfunction isUnpublishStub(doc: SanityDocument): boolean {\n const sys = (doc as { _system?: { delete?: boolean } })._system;\n return sys?.delete === true;\n}\n\nfunction projectToPublishedId(doc: SanityDocument, pubId: string): SanityDocument {\n if (doc._id === pubId) return doc;\n return { ...doc, _id: pubId, _originalId: doc._id } as SanityDocument;\n}\n\n/**\n * `getDocument(id)` resolution under a perspective. Returns the chosen\n * source's *raw* doc (not projected), so the caller can decide whether\n * to expose `_originalId` etc. For perspective=\"raw\", this is just\n * `docs.find(d => d._id === id)`.\n */\nexport function resolveDocument(\n docs: readonly SanityDocument[],\n id: string,\n perspective: Perspective,\n): SanityDocument | undefined {\n if (perspective === \"raw\") {\n return docs.find((d) => d._id === id);\n }\n // Match the rules of applyPerspective: resolve to the same chosen\n // doc but key by the requested id (which the caller treated as a\n // published id under non-raw perspectives).\n const view = applyPerspective(docs, perspective);\n return view.find((d) => d._id === id);\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\nimport { fakeError } from \"./errors.ts\";\n\n/**\n * In-memory document store backing a TestClient. Closure-scoped — created\n * fresh per test, no global state.\n *\n * The shape mirrors what `groq-js` evaluates against: an array of\n * documents. Internally we keep a Map by `_id` for O(1) lookup, and\n * project to an array on each query.\n */\nexport interface TestStore {\n /** Replace the entire dataset with the given documents. Existing docs are dropped. */\n seed: (documents: SanityDocumentLike[]) => void;\n /** Add documents without dropping existing ones. Conflicts (same _id) overwrite. */\n add: (documents: SanityDocumentLike[]) => void;\n /** Remove all documents. */\n reset: () => void;\n /** All documents as an array, in insertion order. */\n all: () => SanityDocument[];\n /** Get one document by _id, or undefined. */\n get: (id: string) => SanityDocument | undefined;\n /** Insert a fully-formed document by _id. Replaces if it already exists. */\n put: (doc: SanityDocument) => void;\n /** Remove a document by _id. Returns the removed doc or undefined. */\n remove: (id: string) => SanityDocument | undefined;\n /** Snapshot of all docs, useful for assertions. */\n snapshot: () => readonly SanityDocument[];\n}\n\n/**\n * Create a fresh, isolated store. The returned object closes over a private\n * Map; two stores created from two calls do not see each other.\n */\nexport function createTestStore(initial?: SanityDocumentLike[]): TestStore {\n const docs = new Map<string, SanityDocument>();\n\n const promote = (doc: SanityDocumentLike): SanityDocument => {\n if (typeof doc._id !== \"string\" || doc._id.length === 0) {\n throw fakeError(\n `Cannot seed/store a document without a non-empty string \\`_id\\` (got ${JSON.stringify(doc._id)}). ` +\n `This fake never auto-generates ids — give every document an explicit _id. Document: ${JSON.stringify(doc)}`,\n );\n }\n if (typeof doc._type !== \"string\" || doc._type.length === 0) {\n throw fakeError(\n `Document \"${doc._id}\" is missing a non-empty string \\`_type\\` (got ${JSON.stringify(doc._type)}). ` +\n `Every Sanity document needs a _type.`,\n );\n }\n const now = new Date().toISOString();\n return {\n ...doc,\n _id: doc._id,\n _type: doc._type,\n _rev: doc._rev ?? cryptoRandomKey(),\n _createdAt: doc._createdAt ?? now,\n _updatedAt: doc._updatedAt ?? now,\n } as SanityDocument;\n };\n\n const seed = (documents: SanityDocumentLike[]) => {\n docs.clear();\n for (const d of documents) {\n const promoted = promote(d);\n docs.set(promoted._id, promoted);\n }\n };\n\n if (initial) seed(initial);\n\n return {\n seed,\n add: (documents) => {\n for (const d of documents) {\n const promoted = promote(d);\n docs.set(promoted._id, promoted);\n }\n },\n reset: () => docs.clear(),\n all: () => [...docs.values()],\n get: (id) => docs.get(id),\n put: (doc) => {\n docs.set(doc._id, doc);\n },\n remove: (id) => {\n const doc = docs.get(id);\n if (doc) docs.delete(id);\n return doc;\n },\n snapshot: () => [...docs.values()],\n };\n}\n\n/**\n * Generate a stable-ish random key without pulling in a dependency.\n * Use cases: synthesising `_rev` for newly-stored documents.\n */\nexport function cryptoRandomKey(length = 12): string {\n const bytes = new Uint8Array(length);\n // globalThis.crypto is available in Node 22+ and all modern browsers.\n globalThis.crypto.getRandomValues(bytes);\n return [...bytes]\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\")\n .slice(0, length);\n}\n","/**\n * Action handlers for `client.action()` — Content Releases, versions, and\n * document publish/unpublish — plus the `client.releases.*` and\n * `client.createVersion` / `client.unpublishVersion` helpers.\n *\n * Mirrors enough of the real `@sanity/client` action surface for tests\n * to exercise release-aware workflows. Transient states\n * (`scheduling`/`publishing`/`unscheduling`) are skipped — releases\n * transition synchronously between settled states.\n */\n\nimport type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\n\nimport { fakeError } from \"./errors.ts\";\nimport { mutationContexts, type MutationGuardImageContext } from \"./mutation-guard.ts\";\nimport { applyPatchOps, type PatchOp } from \"./patch.ts\";\nimport { publishedIdOf } from \"./perspective.ts\";\nimport type { TestStore } from \"./store.ts\";\nimport { cryptoRandomKey } from \"./store.ts\";\n\n/**\n * Document body accepted by `version.create` / `createVersion`. The id\n * is derived from `releaseId + publishedId` so callers may omit `_id`\n * here. `version.replace` requires `_id` since it identifies the doc\n * to replace.\n */\nexport interface DocumentBody {\n _id?: string;\n _type: string;\n [field: string]: unknown;\n}\n\nexport type ReleaseState = \"active\" | \"scheduled\" | \"published\" | \"archived\";\n\nexport type ReleaseType = \"asap\" | \"scheduled\" | \"undecided\";\n\nexport interface ReleaseMetadata {\n title?: string;\n description?: string;\n releaseType?: ReleaseType;\n intendedPublishAt?: string;\n}\n\nexport interface ReleaseDocument extends SanityDocument {\n _type: \"system.release\";\n name: string;\n state: ReleaseState;\n metadata: ReleaseMetadata;\n publishAt?: string;\n}\n\nexport type ReleaseAction =\n | {\n actionType: \"sanity.action.release.create\";\n releaseId: string;\n metadata?: Partial<ReleaseMetadata>;\n }\n | {\n actionType: \"sanity.action.release.edit\";\n releaseId: string;\n patch: { set?: Record<string, unknown>; unset?: string[] };\n }\n | { actionType: \"sanity.action.release.schedule\"; releaseId: string; publishAt: string }\n | { actionType: \"sanity.action.release.unschedule\"; releaseId: string }\n | { actionType: \"sanity.action.release.publish\"; releaseId: string }\n | { actionType: \"sanity.action.release.archive\"; releaseId: string }\n | { actionType: \"sanity.action.release.unarchive\"; releaseId: string }\n | { actionType: \"sanity.action.release.delete\"; releaseId: string };\n\nexport type VersionAction =\n | {\n actionType: \"sanity.action.document.version.create\";\n publishedId: string;\n document: DocumentBody;\n releaseId?: string; // inferred from document._id when omitted\n }\n | {\n actionType: \"sanity.action.document.version.replace\";\n document: SanityDocumentLike;\n }\n | {\n actionType: \"sanity.action.document.version.discard\";\n versionId: string;\n }\n | {\n actionType: \"sanity.action.document.version.unpublish\";\n versionId: string;\n publishedId: string;\n };\n\n/**\n * Draft↔published document actions — `sanity.action.document.publish`\n * promotes the draft onto the published id (and deletes the draft);\n * `sanity.action.document.unpublish` retracts the published document into a\n * draft (keeping an existing draft untouched) and deletes the published one.\n */\nexport type DocumentAction =\n | {\n actionType: \"sanity.action.document.publish\";\n draftId: string;\n publishedId: string;\n ifDraftRevisionId?: string;\n ifPublishedRevisionId?: string;\n }\n | {\n actionType: \"sanity.action.document.unpublish\";\n draftId: string;\n publishedId: string;\n };\n\nexport type Action = ReleaseAction | VersionAction | DocumentAction;\n\n/**\n * Client-layer interception for document publish/unpublish actions. `gate`\n * checks access before each write. `guard` receives the batch's net draft and\n * published mutations after staging; a rejection rolls back the whole batch.\n */\nexport interface DocumentActionHooks {\n gate?: (\n document: SanityDocumentLike & { _id: string; _type: string },\n permission: \"create\" | \"update\",\n ) => void | Promise<void>;\n guard?: (contexts: readonly MutationGuardImageContext[]) => void | Promise<void>;\n}\n\nexport interface ActionResult {\n transactionId: string;\n}\n\nconst RELEASE_DOC_PREFIX = \"_.releases.\";\nconst VERSIONS_PREFIX = \"versions.\";\n\nconst releaseDocId = (releaseId: string): string => `${RELEASE_DOC_PREFIX}${releaseId}`;\nconst versionDocId = (releaseId: string, publishedId: string): string =>\n `${VERSIONS_PREFIX}${releaseId}.${publishedId}`;\n\n/** Parse `versions.<release>.<id>` → `{ releaseId, publishedId }`. Throws otherwise. */\nfunction parseVersionId(versionId: string): { releaseId: string; publishedId: string } {\n if (!versionId.startsWith(VERSIONS_PREFIX)) {\n throw fakeError(\n `\"${versionId}\" is not a version document id. Version ids look like ` +\n `\"versions.<releaseId>.<publishedId>\" (e.g. \"versions.summer-drop.product-1\").`,\n );\n }\n const rest = versionId.slice(VERSIONS_PREFIX.length);\n const dot = rest.indexOf(\".\");\n if (dot === -1) {\n throw fakeError(\n `\"${versionId}\" is not a version document id. Version ids look like ` +\n `\"versions.<releaseId>.<publishedId>\" (e.g. \"versions.summer-drop.product-1\").`,\n );\n }\n return { releaseId: rest.slice(0, dot), publishedId: rest.slice(dot + 1) };\n}\n\nfunction getRelease(store: TestStore, releaseId: string): ReleaseDocument {\n const doc = store.get(releaseDocId(releaseId)) as ReleaseDocument | undefined;\n if (doc === undefined) {\n throw fakeError(\n `Release \"${releaseId}\" not found. Create it first with ` +\n `client.releases.create({ releaseId: \"${releaseId}\" }) (or a ` +\n `\"sanity.action.release.create\" action) before editing, scheduling, or adding versions to it.`,\n );\n }\n return doc;\n}\n\nfunction maybeRelease(store: TestStore, releaseId: string): ReleaseDocument | undefined {\n return store.get(releaseDocId(releaseId)) as ReleaseDocument | undefined;\n}\n\nfunction nowIso(): string {\n return new Date().toISOString();\n}\n\nfunction promote(doc: SanityDocumentLike): SanityDocument {\n const now = nowIso();\n return {\n ...doc,\n _id: doc._id as string,\n _type: doc._type as string,\n _rev: cryptoRandomKey(),\n _createdAt: doc._createdAt ?? now,\n _updatedAt: now,\n } as SanityDocument;\n}\n\nfunction bumpRev(doc: SanityDocument, patch: Partial<SanityDocument>): SanityDocument {\n return {\n ...doc,\n ...patch,\n _rev: cryptoRandomKey(),\n _updatedAt: nowIso(),\n } as SanityDocument;\n}\n\nfunction requireActive(release: ReleaseDocument): void {\n if (release.state !== \"active\") {\n throw fakeError(\n `Release \"${release.name}\" is \"${release.state}\", but only \"active\" releases accept version ` +\n `writes (create / replace / discard / unpublish). Unschedule or unarchive it back to ` +\n `\"active\" first, or target a different release.`,\n );\n }\n}\n\n/**\n * Apply a single action to the store. Returns a synthetic transactionId.\n * Throws on any invariant violation — callers wrap multi-action arrays\n * with a snapshot/rollback to keep atomicity. `hooks.gate` checks document\n * publish/unpublish writes here; {@link applyActions} evaluates their guard\n * contexts after staging the complete batch.\n */\nexport async function applyAction(\n store: TestStore,\n action: Action,\n hooks?: DocumentActionHooks,\n): Promise<ActionResult> {\n switch (action.actionType) {\n case \"sanity.action.release.create\": {\n if (maybeRelease(store, action.releaseId) !== undefined) {\n throw fakeError(\n `Release \"${action.releaseId}\" already exists. release.create expects a new releaseId; ` +\n `use release.edit to change its metadata, or pick a different releaseId.`,\n );\n }\n const now = nowIso();\n const metadata: ReleaseMetadata = {\n releaseType: \"undecided\",\n ...action.metadata,\n };\n const doc: ReleaseDocument = {\n _id: releaseDocId(action.releaseId),\n _type: \"system.release\",\n _rev: cryptoRandomKey(),\n _createdAt: now,\n _updatedAt: now,\n name: action.releaseId,\n state: \"active\",\n metadata,\n };\n store.put(doc);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.edit\": {\n const release = getRelease(store, action.releaseId);\n const patchOps: PatchOp[] = [];\n if (action.patch.set !== undefined) {\n patchOps.push({ kind: \"set\", props: action.patch.set });\n }\n if (action.patch.unset !== undefined) {\n patchOps.push({ kind: \"unset\", paths: action.patch.unset });\n }\n const patched = applyPatchOps(release, patchOps);\n store.put(bumpRev(patched, {}));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.schedule\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"active\") {\n throw fakeError(\n `Cannot schedule release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"active\" releases can be scheduled.`,\n );\n }\n store.put(\n bumpRev(release, {\n state: \"scheduled\",\n publishAt: action.publishAt,\n } as Partial<SanityDocument>),\n );\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.unschedule\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"scheduled\") {\n throw fakeError(\n `Cannot unschedule release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"scheduled\" releases can be unscheduled.`,\n );\n }\n const next: ReleaseDocument = { ...release, state: \"active\" };\n delete (next as { publishAt?: string }).publishAt;\n store.put(bumpRev(next, {}));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.publish\": {\n const release = getRelease(store, action.releaseId);\n if (release.state === \"published\") {\n // Idempotent.\n return { transactionId: cryptoRandomKey() };\n }\n if (release.state !== \"active\" && release.state !== \"scheduled\") {\n throw fakeError(\n `Cannot publish release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"active\" or \"scheduled\" releases can be published.`,\n );\n }\n // Materialise versions onto published ids, then drop the versions.\n const prefix = `${VERSIONS_PREFIX}${action.releaseId}.`;\n const sources = store.all().filter((d) => d._id.startsWith(prefix));\n for (const v of sources) {\n const pubId = v._id.slice(prefix.length);\n const isUnpublishStub = (v as { _system?: { delete?: boolean } })._system?.delete === true;\n if (isUnpublishStub) {\n store.remove(pubId);\n } else {\n const body: SanityDocumentLike = {\n ...(v as SanityDocumentLike),\n _id: pubId,\n };\n // Strip transient fields we projected through the version.\n delete (body as { _originalId?: string })._originalId;\n delete (body as { _system?: unknown })._system;\n const promoted = promote(body);\n // Preserve _createdAt of the existing published doc if present.\n const existing = store.get(pubId);\n if (existing !== undefined) {\n promoted._createdAt = existing._createdAt;\n }\n store.put(promoted);\n }\n store.remove(v._id);\n }\n store.put(bumpRev(release, { state: \"published\" } as Partial<SanityDocument>));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.archive\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"active\" && release.state !== \"scheduled\") {\n throw fakeError(\n `Cannot archive release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"active\" or \"scheduled\" releases can be archived.`,\n );\n }\n store.put(bumpRev(release, { state: \"archived\" } as Partial<SanityDocument>));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.unarchive\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"archived\") {\n throw fakeError(\n `Cannot unarchive release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"archived\" releases can be unarchived.`,\n );\n }\n store.put(bumpRev(release, { state: \"active\" } as Partial<SanityDocument>));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.release.delete\": {\n const release = getRelease(store, action.releaseId);\n if (release.state !== \"archived\" && release.state !== \"published\") {\n throw fakeError(\n `Cannot delete release \"${action.releaseId}\": it is \"${release.state}\", and only ` +\n `\"archived\" or \"published\" releases can be deleted. Archive it first ` +\n `(sanity.action.release.archive) or publish it.`,\n );\n }\n // Clean up any leftover version docs (archived releases keep them around).\n const prefix = `${VERSIONS_PREFIX}${action.releaseId}.`;\n for (const v of store.all()) {\n if (v._id.startsWith(prefix)) store.remove(v._id);\n }\n store.remove(release._id);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.create\": {\n const releaseId =\n action.releaseId ??\n (action.document._id !== undefined\n ? parseVersionId(action.document._id as string).releaseId\n : undefined);\n if (releaseId === undefined) {\n throw fakeError(\n `version.create needs a release to attach to: pass \\`releaseId\\`, or a \\`document._id\\` ` +\n `shaped \"versions.<releaseId>.<publishedId>\" so the release can be inferred.`,\n );\n }\n const release = getRelease(store, releaseId);\n requireActive(release);\n const vid = versionDocId(releaseId, action.publishedId);\n const doc: SanityDocumentLike = {\n ...action.document,\n _id: vid,\n };\n const existing = store.get(vid);\n const promoted = promote(doc);\n if (existing !== undefined) {\n promoted._createdAt = existing._createdAt;\n }\n store.put(promoted);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.replace\": {\n const id = action.document._id;\n if (typeof id !== \"string\") {\n throw fakeError(\n `version.replace needs \\`document._id\\` (a version id like ` +\n `\"versions.<releaseId>.<publishedId>\") to identify which version document to replace.`,\n );\n }\n const { releaseId } = parseVersionId(id);\n const release = getRelease(store, releaseId);\n requireActive(release);\n const existing = store.get(id);\n const promoted = promote(action.document);\n if (existing !== undefined) {\n promoted._createdAt = existing._createdAt;\n }\n store.put(promoted);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.discard\": {\n const { releaseId } = parseVersionId(action.versionId);\n const release = getRelease(store, releaseId);\n requireActive(release);\n store.remove(action.versionId);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.version.unpublish\": {\n const { releaseId } = parseVersionId(action.versionId);\n const release = getRelease(store, releaseId);\n requireActive(release);\n const doc: SanityDocumentLike = {\n _id: action.versionId,\n _type: \"stub\",\n _system: { delete: true },\n } as SanityDocumentLike;\n store.put(promote(doc));\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.publish\": {\n requireDraftOf(action);\n const draft = store.get(action.draftId);\n if (draft === undefined) {\n throw fakeError(\n `Cannot publish \"${action.publishedId}\": draft \"${action.draftId}\" not found. ` +\n `document.publish promotes an existing draft onto the published id — create the ` +\n `draft first (e.g. client.createOrReplace({ _id: \"${action.draftId}\", ... })).`,\n );\n }\n const published = store.get(action.publishedId);\n requireRevisionMatch(action.draftId, draft._rev, action.ifDraftRevisionId);\n requireRevisionMatch(action.publishedId, published?._rev, action.ifPublishedRevisionId);\n const promoted = promote({ ...draft, _id: action.publishedId });\n if (published !== undefined) {\n promoted._createdAt = published._createdAt;\n }\n await hooks?.gate?.(promoted, published === undefined ? \"create\" : \"update\");\n store.put(promoted);\n store.remove(action.draftId);\n return { transactionId: cryptoRandomKey() };\n }\n\n case \"sanity.action.document.unpublish\": {\n requireDraftOf(action);\n const published = store.get(action.publishedId);\n if (published === undefined) {\n throw fakeError(\n `Cannot unpublish \"${action.publishedId}\": no published document with that _id exists. ` +\n `document.unpublish retracts a live published document into \"${action.draftId}\".`,\n );\n }\n await hooks?.gate?.(published, \"update\");\n // The lake keeps an existing draft untouched; only a missing one is\n // materialised from the published content.\n if (store.get(action.draftId) === undefined) {\n const draft = promote({ ...published, _id: action.draftId });\n draft._createdAt = published._createdAt;\n store.put(draft);\n }\n store.remove(action.publishedId);\n return { transactionId: cryptoRandomKey() };\n }\n }\n}\n\n/** Throw unless the action's `draftId` is the draft of its `publishedId`. */\nfunction requireDraftOf(action: {\n actionType: string;\n draftId: string;\n publishedId: string;\n}): void {\n if (publishedIdOf(action.draftId) === action.publishedId && action.draftId !== action.publishedId)\n return;\n throw fakeError(\n `${action.actionType}: draftId \"${action.draftId}\" is not the draft of publishedId ` +\n `\"${action.publishedId}\" — expected draftId \"drafts.${action.publishedId}\".`,\n );\n}\n\n/** Enforce an `ifDraftRevisionId` / `ifPublishedRevisionId` precondition when given. */\nfunction requireRevisionMatch(\n documentId: string,\n actualRev: string | undefined,\n expectedRev: string | undefined,\n): void {\n if (expectedRev === undefined || actualRev === expectedRev) return;\n throw fakeError(\n `Revision precondition failed for \"${documentId}\": expected _rev \"${expectedRev}\" but the ` +\n `document is at ${actualRev === undefined ? \"no revision (missing)\" : `\"${actualRev}\"`}.`,\n );\n}\n\n/**\n * Apply an action or a list of actions atomically. On any failure all\n * mutations within the batch are rolled back to the pre-batch snapshot.\n * `hooks` intercepts document publish/unpublish actions\n * ({@link DocumentActionHooks}) and evaluates their net changes as one batch.\n */\nexport async function applyActions(\n store: TestStore,\n actions: Action | Action[],\n hooks?: DocumentActionHooks,\n): Promise<ActionResult> {\n const list = Array.isArray(actions) ? actions : [actions];\n const snapshot = new Map<string, SanityDocument | undefined>();\n const guardedIds = new Set<string>();\n const remember = (id: string) => {\n if (!snapshot.has(id)) snapshot.set(id, store.get(id));\n };\n\n try {\n let last: ActionResult = { transactionId: cryptoRandomKey() };\n for (const a of list) {\n // Pre-record everything we might touch so rollback is precise.\n switch (a.actionType) {\n case \"sanity.action.release.create\":\n case \"sanity.action.release.edit\":\n case \"sanity.action.release.schedule\":\n case \"sanity.action.release.unschedule\":\n case \"sanity.action.release.archive\":\n case \"sanity.action.release.unarchive\":\n case \"sanity.action.release.delete\":\n remember(`${RELEASE_DOC_PREFIX}${a.releaseId}`);\n break;\n case \"sanity.action.release.publish\": {\n remember(`${RELEASE_DOC_PREFIX}${a.releaseId}`);\n const prefix = `${VERSIONS_PREFIX}${a.releaseId}.`;\n for (const d of store.all()) {\n if (d._id.startsWith(prefix)) {\n remember(d._id);\n remember(d._id.slice(prefix.length));\n }\n }\n break;\n }\n case \"sanity.action.document.version.create\": {\n // version.create derives its id from releaseId + publishedId,\n // so we don't rely on document._id being set.\n const releaseId =\n a.releaseId ??\n (typeof a.document._id === \"string\"\n ? parseVersionId(a.document._id).releaseId\n : undefined);\n if (releaseId !== undefined) {\n remember(versionDocId(releaseId, a.publishedId));\n }\n break;\n }\n case \"sanity.action.document.version.replace\":\n if (typeof a.document._id === \"string\") remember(a.document._id);\n break;\n case \"sanity.action.document.version.discard\":\n case \"sanity.action.document.version.unpublish\":\n remember(a.versionId);\n break;\n case \"sanity.action.document.publish\":\n remember(a.draftId);\n remember(a.publishedId);\n // Preserve the lake-facing order: published write, then draft delete.\n guardedIds.add(a.publishedId);\n guardedIds.add(a.draftId);\n break;\n case \"sanity.action.document.unpublish\":\n remember(a.draftId);\n remember(a.publishedId);\n // Preserve the lake-facing order: draft create, then published delete.\n guardedIds.add(a.draftId);\n guardedIds.add(a.publishedId);\n break;\n }\n last = await applyAction(store, a, hooks);\n }\n const contexts = mutationContexts(\n [...guardedIds].map((id) => ({ before: snapshot.get(id), after: store.get(id) })),\n );\n await hooks?.guard?.(contexts);\n return last;\n } catch (err) {\n for (const [id, doc] of snapshot) {\n if (doc === undefined) {\n store.remove(id);\n } else {\n store.put(doc);\n }\n }\n throw err;\n }\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\nimport type { PatchHandle, PatchOp } from \"./patch.ts\";\n\n/**\n * A transaction collects mutations and applies them atomically at commit\n * time. Mutations are applied in declaration order; if any one fails, the\n * whole transaction throws and no writes land in the store.\n *\n * Subset of `@sanity/client`'s Transaction. Same shape, narrower surface.\n */\nexport type TxnMutation =\n | { kind: \"create\"; doc: SanityDocumentLike }\n | { kind: \"createOrReplace\"; doc: SanityDocumentLike }\n | { kind: \"createIfNotExists\"; doc: SanityDocumentLike }\n | { kind: \"delete\"; id: string }\n | { kind: \"patch\"; documentId: string; ops: PatchOp[]; ifRev?: string };\n\nexport interface TransactionHandle {\n create: (doc: SanityDocumentLike) => TransactionHandle;\n createOrReplace: (doc: SanityDocumentLike) => TransactionHandle;\n createIfNotExists: (doc: SanityDocumentLike) => TransactionHandle;\n delete: (id: string) => TransactionHandle;\n /** Accept either a Patch builder (drained at commit) or an inline doc id + ops. */\n patch: (\n documentIdOrPatch: string | PatchHandle,\n ops?: PatchOp[],\n ifRev?: string,\n ) => TransactionHandle;\n commit: (options?: TxnCommitOptions) => Promise<TxnCommitResult>;\n /** Test-only introspection — not on real Transaction. */\n toJSON: () => TxnMutation[];\n}\n\nexport interface TxnCommitOptions {\n /**\n * Shape the resolved value (mirrors `@sanity/client`):\n * - default (both unset) → {@link MultipleMutationResult}\n * - `returnDocuments: true` → the resulting document(s)\n * - `returnFirst: true` with `returnDocuments` → a single document\n * - `returnDocuments: false` → an id-shaped mutation result\n */\n returnDocuments?: boolean;\n returnFirst?: boolean;\n /** Override the synthetic transaction id (mirrors `client.transaction(undefined, { transactionId })`). */\n transactionId?: string;\n /**\n * Request tag, recorded on the mutation log entry for assertions.\n * `null` mirrors `@sanity/client`'s explicit-suppression form and is\n * recorded as-is. No other behavior is simulated.\n */\n tag?: string | null;\n}\n\n/** The operation recorded per affected document, matching `@sanity/client`. */\nexport type MutationOperation = \"create\" | \"update\" | \"delete\" | \"none\";\n\n/** Result of a mutation when `returnFirst` resolves true and ids are requested. */\nexport interface SingleMutationResult {\n transactionId: string;\n documentId: string;\n results: { id: string; operation: MutationOperation }[];\n}\n\n/** Result of a mutation when ids are requested for all affected documents. */\nexport interface MultipleMutationResult {\n transactionId: string;\n documentIds: string[];\n results: { id: string; operation: MutationOperation }[];\n}\n\nexport type TxnCommitResult =\n | MultipleMutationResult\n | SingleMutationResult\n | SanityDocument\n | SanityDocument[];\n\nexport interface TxnExecutor {\n apply: (mutations: TxnMutation[], options?: TxnCommitOptions) => Promise<TxnCommitResult>;\n}\n\nexport function createTransaction(executor: TxnExecutor): TransactionHandle {\n const mutations: TxnMutation[] = [];\n\n const handle: TransactionHandle = {\n create: (doc) => {\n mutations.push({ kind: \"create\", doc });\n return handle;\n },\n createOrReplace: (doc) => {\n mutations.push({ kind: \"createOrReplace\", doc });\n return handle;\n },\n createIfNotExists: (doc) => {\n mutations.push({ kind: \"createIfNotExists\", doc });\n return handle;\n },\n delete: (id) => {\n mutations.push({ kind: \"delete\", id });\n return handle;\n },\n patch: (documentIdOrPatch, ops, ifRev) => {\n if (typeof documentIdOrPatch === \"string\") {\n mutations.push({\n kind: \"patch\",\n documentId: documentIdOrPatch,\n ops: ops ?? [],\n ...(ifRev !== undefined ? { ifRev } : {}),\n });\n } else {\n const json = documentIdOrPatch.toJSON();\n mutations.push({\n kind: \"patch\",\n documentId: json.id,\n ops: json.ops,\n ...(json.ifRev !== undefined ? { ifRev: json.ifRev } : {}),\n });\n }\n return handle;\n },\n commit: (options) => executor.apply(mutations, options),\n toJSON: () => [...mutations],\n };\n\n return handle;\n}\n","/**\n * Graceful-degradation layer for the parts of `@sanity/client` this fake\n * doesn't implement.\n *\n * The client object is a fixed surface. Without a guard, reaching for a\n * member the fake doesn't have (`client.listen`, `client.assets.upload`,\n * a typo) yields a bare `TypeError: x is not a function` — no hint that\n * it's an intentional gap in a *fake*, no pointer to a workaround. The\n * `Proxy` built here turns every such access into an actionable error,\n * and lets tests fill gaps via a stub registry.\n *\n * Resolution order for a property read on the wrapped client:\n * 1. Real implemented member → passed straight through.\n * 2. Thenable / inspection probes (`then`, symbols, …) → pass through\n * so `await client` and `console.log(client)` stay well-behaved.\n * 3. User-registered stub (`client.stub(name, fn)`) → returned.\n * 4. Known-but-unimplemented `@sanity/client` member → a guard that\n * throws a curated, workaround-bearing error on call or deeper access.\n * 5. Anything else (typo / unknown) → a guard that throws a\n * \"no such method\" error listing the supported surface.\n */\n\nconst ISSUES_URL = \"https://github.com/sanity-labs/client-fake-for-test/issues\";\n\n/** Members this fake actually implements — surfaced in error messages. */\nexport const SUPPORTED_SURFACE = [\n \"fetch\",\n \"getDocument\",\n \"getDocuments\",\n \"create\",\n \"createOrReplace\",\n \"createIfNotExists\",\n \"delete\",\n \"patch\",\n \"transaction\",\n \"mutate\",\n \"action\",\n \"releases\",\n \"createVersion\",\n \"unpublishVersion\",\n \"request (only with accessControl or a bound token)\",\n \"getDataUrl\",\n \"config\",\n \"withConfig\",\n] as const;\n\ninterface UnsupportedEntry {\n /** What the real member is. */\n what: string;\n /** What to do instead. */\n workaround: string;\n}\n\n/**\n * Curated map of real `@sanity/client` members the fake deliberately omits.\n * Keyed by the top-level property name; nested access (e.g.\n * `assets.upload`) is reported with the full dotted path.\n */\nexport const UNSUPPORTED_MEMBERS: Record<string, UnsupportedEntry> = {\n listen: {\n what: \"real-time listener (returns an RxJS Observable of mutation events)\",\n workaround:\n \"Test your subscription logic in isolation, or register a fake: \" +\n `client.stub(\"listen\", () => myFakeObservable).`,\n },\n observable: {\n what: \"the RxJS-flavoured mirror of the client API\",\n workaround:\n \"Use the Promise-based methods and wrap them with `from()` if you need an Observable.\",\n },\n clone: {\n what: \"clone the client with the same config\",\n workaround: \"Use `withConfig({})` — it returns a sibling handle into the same store.\",\n },\n getUrl: {\n what: \"build an absolute API URL for a path\",\n workaround: \"Hard-code the expected URL in your test, or stub it.\",\n },\n assets: {\n what: \"asset upload / metadata namespace\",\n workaround: \"Seed asset documents into the store directly with `seed()` / `add()`.\",\n },\n agent: {\n what: \"Agent Actions namespace (generate / transform / …)\",\n workaround: `Stub the call site: client.stub(\"agent\", myFakeAgent).`,\n },\n projects: {\n what: \"project-management namespace\",\n workaround: \"Out of scope for a data-plane fake — stub the call site.\",\n },\n users: {\n what: \"user-management namespace\",\n workaround: \"Out of scope for a data-plane fake — stub the call site.\",\n },\n live: {\n what: \"Live Content API namespace\",\n workaround: \"Out of scope — stub the call site or test the consumer separately.\",\n },\n mediaLibrary: {\n what: \"Media Library namespace\",\n workaround: \"Out of scope — seed documents directly or stub the call site.\",\n },\n request: {\n what: \"generic HTTP request hook — only wired up when the handle has an acting identity\",\n workaround:\n \"Create the client with `accessControl`, or bind a registered token \" +\n \"(`identities` + `token` / `withConfig({ token })`), to enable request() (it serves \" +\n \"/users/me, the ACL path, /projects/<projectId>, /projects/<projectId>/users/<ids>, the \" +\n \"History API, resource lookup and the Access API). Or register a stub.\",\n },\n dryRun: {\n what: \"not a method — did you mean the `{ dryRun }` mutation option?\",\n workaround: \"Pass `{ dryRun: true }` as the second arg to create / mutate / delete.\",\n },\n};\n\n/**\n * Property names read by promise-resolution, serialization, and test\n * frameworks while probing an unknown object. These must resolve to\n * `undefined` (not a throwing guard) so `await client`,\n * `JSON.stringify(client)`, and matcher diffs stay well-behaved. Members\n * inherited from `Object.prototype` (`toString`, `valueOf`, …) already\n * pass through via the `in` check and don't need listing here.\n */\nconst PROBE_NAMES = new Set([\n \"then\",\n \"catch\",\n \"finally\",\n \"toJSON\",\n \"asymmetricMatch\",\n \"$$typeof\",\n \"nodeType\",\n \"_isMockFunction\",\n]);\n\n/** Build the multi-line error body shared by both guard flavours. */\nfunction guardMessage(path: string, detail: string, workaround: string): string {\n return [\n `[client-fake-for-test] \\`client.${path}\\` ${detail}`,\n \"\",\n \"This is an in-memory fake of @sanity/client. It implements:\",\n ` ${SUPPORTED_SURFACE.join(\", \")}.`,\n \"\",\n `→ ${workaround}`,\n \"\",\n `Register a stub to fill any gap: client.stub(${JSON.stringify(path.split(\".\")[0])}, handler)`,\n `If this should be supported, open an issue: ${ISSUES_URL}`,\n ].join(\"\\n\");\n}\n\n/**\n * A callable, infinitely-chainable guard. Reading any property returns a\n * deeper guard (so `client.assets.upload` reports the full path), and\n * calling at any depth throws the actionable error. `then` / symbols\n * resolve to `undefined` so `await` and inspection don't trip the guard.\n */\nfunction makeGuard(path: string, message: string): unknown {\n const thrower = () => {\n throw new Error(message);\n };\n return new Proxy(thrower, {\n apply: () => thrower(),\n get: (_t, prop) => {\n if (typeof prop === \"symbol\") return undefined;\n if (PROBE_NAMES.has(prop)) return undefined;\n // Re-derive the message for the deeper path so the error names the\n // exact member that was reached for.\n const childPath = `${path}.${String(prop)}`;\n const top = path.split(\".\")[0] as string;\n const entry = UNSUPPORTED_MEMBERS[top];\n const detail = entry ? `is not implemented (${entry.what}).` : \"is not implemented.\";\n const workaround = entry?.workaround ?? \"Register a stub or use a different test strategy.\";\n return makeGuard(childPath, guardMessage(childPath, detail, workaround));\n },\n });\n}\n\n/** Outcome of a recorded call, reported once a (possibly async) result is known. */\nexport type CallSettler = (outcome: { result?: unknown; error?: unknown }) => void;\n\nexport interface GuardConfig {\n /**\n * Resolve a registered stub for an unimplemented member, or `undefined`.\n * Checked only for members the client doesn't already implement, so\n * stubs fill gaps without shadowing core behaviour.\n */\n getStub: (name: string) => unknown;\n /**\n * Called synchronously when a recorded method is invoked, with the\n * method name and its arguments. Returns a settler the wrapper invokes\n * once the result (or rejection) is known. Omit to disable recording.\n */\n onCall?: (method: string, args: unknown[]) => CallSettler;\n /**\n * Top-level member names whose calls are recorded via `onCall`. A\n * recorded member that resolves to a namespace object (e.g. `releases`)\n * has each of its methods recorded as `name.method`.\n */\n recorded?: Set<string>;\n}\n\n/** Wrap a function so each call is reported to `onCall` (args now, outcome on settle). */\nfunction wrapRecorded(\n method: string,\n fn: (...args: unknown[]) => unknown,\n onCall: NonNullable<GuardConfig[\"onCall\"]>,\n): (...args: unknown[]) => unknown {\n return (...args: unknown[]) => {\n const settle = onCall(method, args);\n try {\n const out = fn(...args);\n if (\n out !== null &&\n typeof out === \"object\" &&\n typeof (out as PromiseLike<unknown>).then === \"function\"\n ) {\n return (out as Promise<unknown>).then(\n (result) => {\n settle({ result });\n return result;\n },\n (error: unknown) => {\n settle({ error });\n throw error;\n },\n );\n }\n settle({ result: out });\n return out;\n } catch (error) {\n settle({ error });\n throw error;\n }\n };\n}\n\n/** Wrap a namespace object so its method calls record as `prefix.method`. */\nfunction wrapNamespace(\n ns: Record<string, unknown>,\n prefix: string,\n onCall: NonNullable<GuardConfig[\"onCall\"]>,\n): Record<string, unknown> {\n return new Proxy(ns, {\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof prop === \"string\" && typeof value === \"function\") {\n return wrapRecorded(`${prefix}.${prop}`, value as (...a: unknown[]) => unknown, onCall);\n }\n return value;\n },\n });\n}\n\n/**\n * Wrap a client object so unimplemented members fail informatively,\n * registered stubs are served, and recorded methods report their calls.\n *\n * @param client - the fully-built client object (plain literal).\n * @param config - stub resolver plus optional call-recording hooks.\n */\nexport function wrapWithGuards<T extends object>(client: T, config: GuardConfig): T {\n const { getStub, onCall, recorded } = config;\n return new Proxy(client, {\n get(target, prop, receiver) {\n if (typeof prop === \"symbol\") return Reflect.get(target, prop, receiver);\n if (prop in target) {\n const value = Reflect.get(target, prop, receiver);\n if (onCall !== undefined && recorded?.has(prop) === true) {\n if (typeof value === \"function\") {\n return wrapRecorded(prop, value as (...a: unknown[]) => unknown, onCall);\n }\n if (value !== null && typeof value === \"object\") {\n return wrapNamespace(value as Record<string, unknown>, prop, onCall);\n }\n }\n return value;\n }\n // Promise / serialization / framework probes on a non-thenable object.\n if (PROBE_NAMES.has(prop)) return undefined;\n\n const stub = getStub(prop);\n if (stub !== undefined) return stub;\n\n const entry = UNSUPPORTED_MEMBERS[prop];\n if (entry !== undefined) {\n return makeGuard(\n prop,\n guardMessage(prop, `is not implemented (${entry.what}).`, entry.workaround),\n );\n }\n return makeGuard(\n prop,\n guardMessage(\n prop,\n \"is not a method on this fake (unknown member or unimplemented @sanity/client API).\",\n \"Check for a typo, register a stub, or use a different test strategy.\",\n ),\n );\n },\n });\n}\n","import type { SanityDocument, SanityDocumentLike } from \"@sanity/types\";\nimport { evaluate, parse } from \"groq-js\";\n\nimport {\n canAccess,\n enforceWriteAccess,\n makeRequestImpl,\n projectUserOf,\n type DocumentValuePermission,\n type TestClientAccessControl,\n type TestClientIdentity,\n} from \"./access-control.ts\";\nimport {\n applyActions,\n type Action,\n type ActionResult,\n type DocumentBody,\n type ReleaseDocument,\n type ReleaseMetadata,\n} from \"./actions.ts\";\nimport { fakeError } from \"./errors.ts\";\nimport { createDocumentHistory, type DocumentHistory } from \"./history.ts\";\nimport {\n enforceMutationGuards,\n mutationContexts,\n type MutationGuard,\n type MutationGuardContext,\n type MutationGuardImageContext,\n} from \"./mutation-guard.ts\";\nimport { applyPatchOps, createPatch, type PatchHandle, type PatchOp } from \"./patch.ts\";\nimport { applyPerspective, isSystemId, resolveDocument, type Perspective } from \"./perspective.ts\";\nimport {\n capabilitiesOf,\n dataUrl,\n datasetParts,\n validateResource,\n type TestClientResource,\n} from \"./resources.ts\";\nimport { createTestStore, cryptoRandomKey, type TestStore } from \"./store.ts\";\nimport {\n createTransaction,\n type MultipleMutationResult,\n type MutationOperation,\n type SingleMutationResult,\n type TransactionHandle,\n type TxnCommitOptions,\n type TxnCommitResult,\n type TxnMutation,\n} from \"./transaction.ts\";\nimport { wrapWithGuards, type CallSettler } from \"./unsupported.ts\";\n\n/**\n * Data-plane subset of `@sanity/client`'s surface, with multi-dataset support.\n *\n * One `createTestClient()` call creates a registry of stores, one per\n * resource: a `(projectId, dataset)` pair, or a Media Library, Canvas or\n * dashboard named by `resource: { type, id }` exactly as `@sanity/client`\n * names it. The returned client points at one of them.\n * `withConfig({ projectId, dataset })` or `withConfig({ resource })` returns\n * a new handle into the same registry but pointing at a different store —\n * useful for workflows where definitions live in one dataset and instances\n * live in another.\n *\n * Two separate `createTestClient()` calls have separate registries, so\n * tests stay isolated from each other.\n *\n * Methods not on the real client:\n *\n * - `seed`, `add`, `reset`, `snapshot`, `store` — bulk-load helpers,\n * scoped to the current dataset.\n * - `simulateConcurrentEdit()` — force the next N revision-guarded writes\n * to a document to 409, for optimistic-locking retry tests.\n * - `datasets()` / `resources()` — list the stores known in this registry.\n *\n * Methods deliberately omitted:\n *\n * - Project / agent / asset / live / listen APIs.\n */\nexport interface FetchOptions {\n /** Override the client's configured perspective for this read. */\n perspective?: Perspective;\n /**\n * When `false`, resolve to a {@link RawQueryResponse} (`{ query, ms,\n * result }`) instead of the bare result — mirrors `@sanity/client`.\n * Defaults to `true`.\n */\n filterResponse?: boolean;\n /**\n * With `filterResponse: false`, set to `false` to omit `query` from the\n * raw response. Ignored when `filterResponse` is not `false`.\n */\n returnQuery?: boolean;\n /**\n * Request tag, recorded on the {@link CallRecord} for assertions.\n * `null` mirrors `@sanity/client`'s explicit-suppression form and is\n * recorded as-is. No other behavior is simulated.\n */\n tag?: string | null;\n /** Accepted for signature parity; not simulated. */\n signal?: AbortSignal;\n}\n\n/**\n * Shape returned by `fetch` when `filterResponse: false`. Matches\n * `@sanity/client`'s `RawQueryResponse`. `query` is omitted when the\n * caller also passes `returnQuery: false`.\n */\nexport interface RawQueryResponse<R> {\n query?: string;\n ms: number;\n result: R;\n}\n\n/**\n * A single mutation in the raw array form accepted by `mutate()` — the\n * same shape `@sanity/client` accepts. The inline `patch` form mirrors\n * the real client's keyed patch object.\n */\nexport type FakeMutation =\n | { create: SanityDocumentLike }\n | { createOrReplace: SanityDocumentLike }\n | { createIfNotExists: SanityDocumentLike }\n | { delete: { id: string } }\n | {\n patch: {\n id: string;\n ifRevisionID?: string;\n set?: Record<string, unknown>;\n setIfMissing?: Record<string, unknown>;\n unset?: string[];\n inc?: Record<string, number>;\n dec?: Record<string, number>;\n insert?: {\n before?: string;\n after?: string;\n replace?: string;\n items: unknown[];\n };\n };\n };\n\n/** Append-only record of one committed transaction (a single mutation is a 1-entry transaction). */\nexport interface MutationLogEntry {\n transactionId: string;\n projectId: string;\n dataset: string;\n /** The resource the transaction targeted, on a `resource`-configured handle. */\n resource?: TestClientResource;\n mutations: TxnMutation[];\n /** Request tag the commit carried, when one was passed (`null` = explicit suppression). */\n tag?: string | null;\n}\n\n/**\n * One recorded invocation of a client method — the spy layer behind\n * `client.calls()` / `client.callCount()`. `result` holds the resolved\n * value (awaited, for async methods); `error` holds the thrown/rejected\n * value. Builder-returning methods (`patch`, `transaction`) record the\n * builder as `result`; their eventual writes show up in the mutation log.\n */\nexport interface CallRecord {\n /** Method name; namespace methods are dotted, e.g. `releases.create`. */\n method: string;\n /** Arguments as passed (stored by reference, like `vi.fn`). */\n args: unknown[];\n /** Resolved/returned value, set once the call settles successfully. */\n result?: unknown;\n /** Thrown or rejected value, set if the call failed. */\n error?: unknown;\n /** Request tag from the call's options, when one was passed (`null` = explicit suppression). */\n tag?: string | null;\n projectId: string;\n dataset: string;\n /** The resource the call targeted, on a `resource`-configured handle. */\n resource?: TestClientResource;\n}\n\n/**\n * Where each recorded method carries its options bag, for {@link tagFromArgs}.\n * Positional (not \"last object arg\") because `fetch` params may legitimately\n * contain a `tag` key that is a GROQ param, not a request tag. Methods whose\n * tag only arrives at commit time (`patch`, `transaction`) surface it on the\n * {@link MutationLogEntry} instead.\n */\nconst OPTIONS_ARG_POSITION: Record<string, number> = {\n fetch: 2,\n getDocument: 1,\n getDocuments: 1,\n create: 1,\n createOrReplace: 1,\n createIfNotExists: 1,\n delete: 1,\n mutate: 1,\n action: 1,\n request: 0,\n};\n\n/** The request tag a call's options carried — `undefined` when absent. */\nfunction tagFromArgs(method: string, args: unknown[]): string | null | undefined {\n const position = OPTIONS_ARG_POSITION[method];\n if (position === undefined) return undefined;\n const options = args[position];\n if (typeof options !== \"object\" || options === null || !(\"tag\" in options)) return undefined;\n return (options as { tag?: string | null }).tag;\n}\n\n/** Client methods whose calls are recorded for `calls()` / `callCount()`. */\nconst RECORDED_METHODS = new Set([\n \"fetch\",\n \"getDocument\",\n \"getDocuments\",\n \"create\",\n \"createOrReplace\",\n \"createIfNotExists\",\n \"delete\",\n \"patch\",\n \"transaction\",\n \"mutate\",\n \"action\",\n \"releases\",\n \"createVersion\",\n \"unpublishVersion\",\n \"request\",\n]);\n\nexport interface TestClient {\n // --- query ---\n fetch: <T = unknown>(\n query: string,\n params?: Record<string, unknown>,\n options?: FetchOptions,\n ) => Promise<T>;\n getDocument: <T = SanityDocument>(id: string, options?: FetchOptions) => Promise<T | undefined>;\n getDocuments: <T = SanityDocument>(\n ids: string[],\n options?: FetchOptions,\n ) => Promise<(T | null)[]>;\n /**\n * Sanity-style HTTP request hook. Present iff the handle has an\n * acting identity — created with `accessControl`, or bound to a\n * registered token (`identities` + `token`). The engine's access\n * resolver pokes `/users/me` and the configured ACL path through\n * this — without it the engine throws on any verb that needs an\n * actor. Also serves the resource's history, ACL, lookup and Access API\n * endpoints, or the platform's refusal where the resource has none.\n */\n request?: <T>(opts: {\n url?: string;\n uri?: string;\n signal?: AbortSignal;\n tag?: string | null;\n }) => Promise<T>;\n\n /**\n * `@sanity/client`'s `getDataUrl(operation, path)`: `<resource\n * base>/<operation>/<path>` on a `resource`-configured handle,\n * `/data/<operation>/<dataset>/<path>` otherwise. Pass the result to\n * `request()` to reach e.g. the History API.\n */\n getDataUrl: (operation: string, path?: string) => string;\n\n // --- mutate ---\n create: <T extends SanityDocumentLike>(\n doc: T,\n options?: MutationOptions,\n ) => Promise<T & SystemFields>;\n createOrReplace: <T extends SanityDocumentLike>(\n doc: T,\n options?: MutationOptions,\n ) => Promise<T & SystemFields>;\n createIfNotExists: <T extends SanityDocumentLike>(\n doc: T,\n options?: MutationOptions,\n ) => Promise<T & SystemFields>;\n delete: (id: string, options?: MutationOptions) => Promise<{ _id: string } | null>;\n patch: (documentId: string) => PatchHandle;\n transaction: () => TransactionHandle;\n /**\n * Apply a batch of mutations in the raw array form (or a `patch()` /\n * `transaction()` builder) — mirrors `@sanity/client`'s `mutate()`.\n * Atomic: any failure rolls the whole batch back. The resolved shape\n * follows `options.returnDocuments` / `returnFirst` exactly like the\n * real client (default → {@link MultipleMutationResult}).\n */\n mutate: (\n operations: FakeMutation[] | PatchHandle | TransactionHandle,\n options?: TxnCommitOptions,\n ) => Promise<TxnCommitResult>;\n\n // --- actions / releases ---\n /**\n * Dispatch one or more release / version / document actions against the\n * store. Returns a synthetic `transactionId`. Multi-action arrays are\n * applied atomically — any failure rolls back the batch. The document\n * publish/unpublish actions run through the access gate and expose their\n * net draft/published mutations to `mutationGuard`; release/version actions\n * apply unhooked.\n */\n action: (action: Action | Action[], options?: ActionOptions) => Promise<ActionResult>;\n /** Helpers mirroring `@sanity/client`'s `client.releases.*` surface. */\n releases: ReleasesHandle;\n /** Helper for `sanity.action.document.version.create`. */\n createVersion: (params: {\n releaseId: string;\n publishedId: string;\n document: DocumentBody;\n }) => Promise<ActionResult>;\n /** Helper for `sanity.action.document.version.unpublish`. */\n unpublishVersion: (params: { releaseId: string; publishedId: string }) => Promise<ActionResult>;\n\n // --- config ---\n /**\n * Current `(projectId, dataset)` or `resource`, active perspective, and which API host\n * the handle models (`useProjectHostname`). `token` is present iff the\n * handle is bound to a registered identity.\n */\n config: () => {\n projectId: string;\n dataset: string;\n /** Present iff the handle is `resource`-configured. */\n resource?: TestClientResource;\n perspective: Perspective;\n requestTagPrefix?: string;\n token?: string;\n useProjectHostname: boolean;\n };\n /**\n * Return a new client handle pointing at a different `(projectId, dataset)`\n * within the SAME backing registry. Either field can be omitted to keep\n * the current value. Two clients produced by `withConfig` see each\n * other's writes if they point at the same `(projectId, dataset)`.\n */\n withConfig: (\n config: Partial<{\n projectId: string;\n dataset: string;\n /**\n * Point the sibling at a resource, named as `@sanity/client`'s\n * `resource` config names it. While set, it decides the store (over\n * `projectId` / `dataset`), as in `@sanity/client`. Pass `undefined`\n * to drop it and address `(projectId, dataset)` again.\n */\n resource: TestClientResource | undefined;\n perspective: Perspective;\n /** Prefix composed with request tags and used alone for untagged requests. */\n requestTagPrefix: string;\n /**\n * Model the global API host (`false`) instead of the project host\n * (default `true`). A global-hosted sibling's `/users/me` serves the\n * identity's account-global record (`globalUser`, falling back to the\n * project record) — mirroring `@sanity/client`, where a non-project\n * config routes requests to `api.sanity.io`.\n */\n useProjectHostname: boolean;\n /**\n * Rebind the acting identity (and its grants) on a sibling that shares\n * the same store — the test analogue of a token-scoped client. Lets two\n * actors write the same dataset, e.g. for `identity()`-based guards.\n * Mutually exclusive with `token`.\n */\n accessControl: TestClientAccessControl;\n /**\n * Bind the sibling to a token registered in the family's identity\n * registry (`createTestClient({ identities })` / `addIdentity`): its\n * `/users/me` serves that identity's record for the sibling's project\n * and host, its ACL path serves that identity's grants, and\n * reads/writes are gated through them — the token determines\n * identity, exactly like production. Unknown tokens throw. Mutually\n * exclusive with `accessControl`.\n */\n token: string;\n }>,\n ) => TestClient;\n\n // --- test helpers (not on real SanityClient) ---\n /** Replace the current dataset with the given documents. */\n seed: (documents: SanityDocumentLike[]) => void;\n /** Add documents to the current dataset without dropping existing ones. */\n add: (documents: SanityDocumentLike[]) => void;\n /** Drop all documents in the current dataset. */\n reset: () => void;\n /** Read-only snapshot of all documents in the current dataset. */\n snapshot: () => readonly SanityDocument[];\n /** Direct store access for the current dataset. */\n store: () => TestStore;\n /**\n * Simulate `count` concurrent edits to `id` in the current dataset: the\n * next `count` revision-guarded mutations (those passing `ifRevisionId`)\n * to that document throw a 409-shaped conflict, as if another client\n * committed between the caller's read and write. Mutations that don't\n * pass `ifRevisionId` are unaffected.\n *\n * Reach for this when the code under test reads a document's `_rev` and\n * commits with `ifRevisionId` internally — so you can't stage the race\n * by mutating the document first (the code would just re-read the new\n * revision and its commit would match). The counter is consumed one per\n * guarded commit, so `count` lets a test drive a recover-after-N-losses\n * path or exhaust a retry budget, deterministically and without relying\n * on promise interleaving.\n *\n * Note: this forces the conflict *outcome* — it short-circuits the\n * revision comparison rather than changing the stored `_rev`. The code\n * under test sees the identical 409 error a real lost race produces; the\n * stored revision is left untouched.\n */\n simulateConcurrentEdit: (id: string, count?: number) => void;\n /** List `(projectId, dataset)` pairs known in this registry. */\n datasets: () => { projectId: string; dataset: string }[];\n /** List every resource known in this registry — datasets as `{ type: \"dataset\", id: \"<projectId>.<dataset>\" }`. */\n resources: () => TestClientResource[];\n /**\n * Register a handler for a `@sanity/client` member the fake doesn't\n * implement (e.g. `listen`, `assets`). Accessing that member on the\n * client then returns the handler instead of throwing the\n * \"not implemented\" guard error. Shared across sibling `withConfig`\n * handles. Stubs fill gaps only — they don't shadow implemented methods.\n */\n stub: (name: string, handler: unknown) => void;\n /** The currently-registered stubs, keyed by member name. */\n stubs: () => Record<string, unknown>;\n /**\n * Register (or upsert) a token→identity entry in the family's shared\n * registry. Siblings already bound to `token` resolve lazily, so a\n * re-registration takes effect on them immediately. Registering an\n * identity does not bind it — pass `token` at `createTestClient` or\n * `withConfig({ token })` to act as it.\n */\n addIdentity: (token: string, identity: TestClientIdentity) => void;\n /** Snapshot of the registered token→identity entries. */\n identities: () => Record<string, TestClientIdentity>;\n /**\n * Every mutation committed through this client's registry, flattened\n * and in order — `create` / `createOrReplace` / `createIfNotExists` /\n * `delete` / `patch`, whether issued directly, via `transaction()`, or\n * via `mutate()`. The spy hook for \"assert exactly what writes my code\n * submitted\".\n */\n mutations: () => TxnMutation[];\n /**\n * Every committed transaction, in order. A direct mutation is recorded\n * as a one-mutation transaction. Each entry carries its synthetic\n * `transactionId` and the `(projectId, dataset)` it targeted.\n */\n transactions: () => MutationLogEntry[];\n /** Clear the recorded mutation / transaction log. */\n clearLog: () => void;\n /**\n * Every recorded method call, in order — the spy layer for \"was `fetch`\n * called, how many times, and with what params\". Records the\n * `@sanity/client`-surface methods (`fetch`, `getDocument`, `create`,\n * `patch`, `mutate`, `releases.*`, …), not the test-only helpers.\n * Pass a method name to filter (e.g. `calls(\"fetch\")`); namespace\n * methods are dotted (`calls(\"releases.create\")`). Shared across\n * `withConfig` siblings.\n */\n calls: (method?: string) => CallRecord[];\n /** Number of recorded calls, optionally filtered by method name. */\n callCount: (method?: string) => number;\n /** Clear the recorded call log. */\n clearCalls: () => void;\n}\n\nexport interface ReleasesHandle {\n create: (params: {\n releaseId: string;\n metadata?: Partial<ReleaseMetadata>;\n }) => Promise<ActionResult & { releaseId: string; metadata: ReleaseMetadata }>;\n edit: (params: {\n releaseId: string;\n patch: { set?: Record<string, unknown>; unset?: string[] };\n }) => Promise<ActionResult>;\n get: (params: { releaseId: string }) => Promise<ReleaseDocument | undefined>;\n fetchDocuments: (params: { releaseId: string }) => Promise<SanityDocument[]>;\n schedule: (params: { releaseId: string; publishAt: string }) => Promise<ActionResult>;\n unschedule: (params: { releaseId: string }) => Promise<ActionResult>;\n publish: (params: { releaseId: string }) => Promise<ActionResult>;\n archive: (params: { releaseId: string }) => Promise<ActionResult>;\n unarchive: (params: { releaseId: string }) => Promise<ActionResult>;\n delete: (params: { releaseId: string }) => Promise<ActionResult>;\n}\n\nexport interface MutationOptions {\n ifRevisionId?: string;\n /**\n * Shape the resolved value, mirroring `@sanity/client`:\n * - default → the mutated document\n * - `returnDocuments: false` → an id-shaped mutation result\n * ({@link SingleMutationResult} / {@link MultipleMutationResult})\n * - `returnFirst: false` with documents → an array\n *\n * The static return type reflects the default (the document); pass a\n * type argument or cast when requesting an id-shaped result.\n */\n returnDocuments?: boolean;\n returnFirst?: boolean;\n /** Override the synthetic transaction id used in the result and log. */\n transactionId?: string;\n /** When `true`, validate + shape the result but don't persist. */\n dryRun?: boolean;\n /** Accepted for signature parity; not simulated. */\n visibility?: \"sync\" | \"async\" | \"deferred\";\n /** Accepted for signature parity; not simulated. */\n autoGenerateArrayKeys?: boolean;\n /**\n * Request tag, recorded on the {@link CallRecord} and the\n * {@link MutationLogEntry} for assertions. `null` mirrors\n * `@sanity/client`'s explicit-suppression form and is recorded as-is.\n * No other behavior is simulated.\n */\n tag?: string | null;\n}\n\n/** Options for `action()`. Only `tag` is meaningful to the fake. */\nexport interface ActionOptions {\n /** Request tag, recorded on the {@link CallRecord} (`null` = explicit suppression). */\n tag?: string | null;\n}\n\nexport interface SystemFields {\n _rev: string;\n _createdAt: string;\n _updatedAt: string;\n}\n\nexport interface CreateTestClientOptions {\n /** Default project id. Defaults to `\"test\"`. */\n projectId?: string;\n /** Default dataset name. Defaults to `\"test\"`. */\n dataset?: string;\n /** Documents to seed into the default `(projectId, dataset)` store. */\n documents?: SanityDocumentLike[];\n /**\n * Optional: seed multiple datasets at once. Keys are `\"projectId:dataset\"`\n * pairs. Useful when workflows span multiple datasets.\n */\n datasets?: Record<string, SanityDocumentLike[]>;\n /**\n * Optional: seed any resource type. Keys are `\"<type>:<id>\"`, the\n * `resource` config's two fields: `\"media-library:ml1\"`, `\"canvas:c1\"`,\n * `\"dashboard:d1\"`, or `\"dataset:<projectId>.<dataset>\"` (the same store\n * as `datasets[\"<projectId>:<dataset>\"]`).\n */\n resources?: Record<string, SanityDocumentLike[]>;\n /**\n * The resource the root handle points at, named exactly as\n * `@sanity/client`'s `resource` config names it. While set, it decides\n * the store (over `projectId` / `dataset`), and each capability the\n * platform lacks on that resource type answers with the platform's\n * refusal. `withConfig({ resource })` mints siblings on other resources.\n */\n resource?: TestClientResource;\n /**\n * The organization every resource of this client belongs to — what\n * `GET /media-libraries/<id>`, `/canvases/<id>`, `/dashboards/<id>` and\n * `/projects/<projectId>` answer as `organizationId`.\n */\n organizationId?: string;\n /**\n * Opt-in access-control simulation. When set, the client exposes\n * `request({ url })` that serves `/users/me` (host-aware), the\n * configured ACL path, `/projects/<projectId>` (members), and\n * `/projects/<projectId>/users/<ids>`,\n * AND every mutation is gated through the supplied `grants`\n * — writes that the grants don't permit throw a `WriteAccessDeniedError`\n * shaped like Sanity's 403. Lets tests demonstrate the full\n * \"engine soft-gate + lake hard-gate\" contract end-to-end without\n * a real HTTP round-trip.\n *\n * When omitted, `request` is undefined and all writes proceed\n * unconditionally — the legacy behavior.\n */\n accessControl?: TestClientAccessControl;\n /**\n * Token→identity registry, shared by the whole `withConfig` family:\n * multiple identities over one store, keyed by the token a handle is\n * bound to — the token determines identity, exactly like production.\n *\n * ```ts\n * const client = createTestClient({\n * token: \"t-admin\",\n * identities: {\n * \"t-admin\": { user: { id: \"admin\", roles: [{ name: \"administrator\" }] }, grants: WILDCARD },\n * \"t-bob\": { user: { id: \"bob\", roles: [{ name: \"editor\" }] }, grants: READ_ONLY },\n * },\n * });\n * const bob = client.withConfig({ token: \"t-bob\" });\n * // same store; bob's /users/me → bob; bob's ACL path → bob's grants\n * ```\n *\n * A token-bound handle behaves exactly like one configured with\n * `accessControl` derived from its identity (identity serving + grant\n * gating; the ACL path defaults per `(projectId, dataset)`). Register\n * more identities later with `addIdentity`.\n */\n identities?: Record<string, TestClientIdentity>;\n /**\n * Token the root handle is bound to — must name an entry in\n * `identities` (unknown tokens throw). Omit to leave the root handle\n * identity-free (a bare factory for token-bound siblings): its\n * `request` is undefined and its reads/writes are ungated. Mutually\n * exclusive with `accessControl`.\n */\n token?: string;\n /**\n * Pluggable write interceptor — runs once for each document's net change in\n * a commit. Document publish/unpublish actions expose their underlying\n * draft/published mutations; release/version actions remain unhooked. Return\n * denial descriptors to reject atomically with `GuardDeniedError`; a\n * thrown callback error propagates unchanged. The client owns no policy.\n * Inherited by `withConfig` siblings. See {@link MutationGuard}.\n */\n mutationGuard?: MutationGuard;\n /**\n * Default perspective applied to reads. Per-fetch `options.perspective`\n * overrides this. `withConfig({ perspective })` returns a sibling\n * handle with a different default. Defaults to `\"raw\"` so existing\n * tests stay green.\n */\n perspective?: Perspective;\n /** Prefix composed with request tags and used alone for untagged requests. */\n requestTagPrefix?: string;\n /**\n * Which API host the root handle models: the project host (default\n * `true`) or the global host (`false`) — same flag as the real\n * `createClient` config. A global-hosted handle's `/users/me` serves\n * the identity's account-global record. `withConfig({ useProjectHostname })`\n * mints siblings on the other host.\n */\n useProjectHostname?: boolean;\n}\n\nexport type { Perspective } from \"./perspective.ts\";\nexport type { ResourceType, TestClientResource } from \"./resources.ts\";\nexport type {\n Action,\n ActionResult,\n DocumentAction,\n DocumentBody,\n ReleaseAction,\n ReleaseDocument,\n ReleaseMetadata,\n ReleaseState,\n ReleaseType,\n VersionAction,\n} from \"./actions.ts\";\n\nexport type {\n TestClientAccessControl,\n TestClientIdentity,\n DocumentValuePermission,\n} from \"./access-control.ts\";\nexport { WriteAccessDeniedError } from \"./access-control.ts\";\n\nconst DEFAULT_PROJECT_ID = \"test\";\nconst DEFAULT_DATASET = \"test\";\n\n/** One resource's store and the history of every write committed to it. */\ninterface RegistryEntry {\n resource: TestClientResource;\n store: TestStore;\n history: DocumentHistory;\n}\n\ntype Registry = Map<string, RegistryEntry>;\n\n/**\n * Shared backing context for one `createTestClient()` call and all the\n * `withConfig` siblings it spawns: the multi-dataset store registry, the\n * append-only mutation log, the stub registry, and the token→identity\n * registry. Sibling handles see each other's writes, log entries, stubs,\n * and registered identities.\n */\ninterface Backend {\n registry: Registry;\n /** See {@link CreateTestClientOptions.organizationId}. */\n organizationId: string | undefined;\n log: MutationLogEntry[];\n stubs: Map<string, unknown>;\n calls: CallRecord[];\n /**\n * Pending simulated concurrent edits, keyed per (resource, document id).\n * Each entry is a count of upcoming revision-guarded mutations that\n * should 409 as if another client raced them. See\n * {@link TestClient.simulateConcurrentEdit}.\n */\n concurrentEdits: Map<string, number>;\n /**\n * Token→identity registry. Token-bound handles resolve their acting\n * identity from here on every access, so `addIdentity` upserts take\n * effect on already-minted siblings. See\n * {@link CreateTestClientOptions.identities}.\n */\n identities: Map<string, TestClientIdentity>;\n /**\n * The registry-wide commit lock: every write path queues on this chain.\n * The real Content Lake validates and applies a mutation atomically\n * server-side; the fake's write paths await user hooks while staging or\n * committing a write. Without serialization two in-flight guarded writes\n * can both validate against the same `_rev` and the loser silently clobbers\n * the winner.\n */\n writeChain: Promise<void>;\n}\n\n/**\n * Queue one write on the backend's commit lock. Corollary of the lock: a\n * gate / `mutationGuard` hook that issues another write through the same\n * registry deadlocks — hooks are read-only by contract.\n */\nfunction serializeWrite<T>(backend: Backend, run: () => Promise<T>): Promise<T> {\n const result = backend.writeChain.then(run);\n backend.writeChain = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n}\n\n/** Throw unless `token` names a registered identity — every token-binding site validates eagerly. */\nfunction requireIdentity(backend: Backend, token: string, site: string): void {\n if (backend.identities.has(token)) return;\n const known = [...backend.identities.keys()];\n throw fakeError(\n `${site}: unknown token \"${token}\" — no identity is registered for it. ` +\n (known.length > 0\n ? `Registered tokens: ${known.map((t) => `\"${t}\"`).join(\", \")}. `\n : \"No identities are registered. \") +\n \"Register identities via createTestClient({ identities }) or client.addIdentity(token, identity).\",\n );\n}\n\n/** Throw when a single config names both identity mechanisms — a handle acts as exactly one. */\nfunction rejectAmbiguousIdentity(\n site: string,\n config: { token?: string; accessControl?: TestClientAccessControl },\n): void {\n if (config.token !== undefined && config.accessControl !== undefined) {\n throw fakeError(\n `${site}: \\`token\\` and \\`accessControl\\` are mutually exclusive — a handle acts either as ` +\n \"a registered identity (token) or as an inline accessControl config, not both.\",\n );\n }\n}\n\nconst registryKey = (resource: TestClientResource) => `${resource.type}:${resource.id}`;\n\n/** The dataset resource a `(projectId, dataset)` pair names. */\nconst datasetResource = (projectId: string, dataset: string): TestClientResource => ({\n type: \"dataset\",\n id: `${projectId}.${dataset}`,\n});\n\n/** Split a `datasets` seed key, `\"<projectId>:<dataset>\"`. */\nconst parseDatasetKey = (key: string): { projectId: string; dataset: string } => {\n const [projectId, dataset] = key.split(\":\") as [string, string];\n return { projectId, dataset };\n};\n\n/** Split a `resources` seed key, `\"<type>:<id>\"`, and validate it like a `resource` config. */\nconst parseResourceKey = (key: string): TestClientResource => {\n const separator = key.indexOf(\":\");\n const resource = {\n type: key.slice(0, separator),\n id: key.slice(separator + 1),\n } as TestClientResource;\n if (separator <= 0 || resource.id.length === 0) {\n throw fakeError(\n `createTestClient(): \\`resources\\` keys are \"<type>:<id>\" (e.g. \"media-library:ml1\"); got \"${key}\".`,\n );\n }\n validateResource(resource);\n return resource;\n};\n\n/** The registry entry for `resource`, created empty on first use. */\nfunction ensureEntry(registry: Registry, resource: TestClientResource): RegistryEntry {\n const key = registryKey(resource);\n let entry = registry.get(key);\n if (entry === undefined) {\n entry = { resource, store: createTestStore(), history: createDocumentHistory() };\n registry.set(key, entry);\n }\n return entry;\n}\n\n/**\n * Create a fresh, isolated test client backed by a private multi-dataset\n * registry. `withConfig` returns a sibling handle into the same registry.\n *\n * ```\n * const client = createTestClient({\n * datasets: {\n * \"test:definitions\": [...],\n * \"test:instances\": [...],\n * },\n * });\n * const defs = client.withConfig({ dataset: \"definitions\" });\n * const inst = client.withConfig({ dataset: \"instances\" });\n * ```\n */\nexport function createTestClient(options: CreateTestClientOptions = {}): TestClient {\n const defaultProjectId = options.projectId ?? DEFAULT_PROJECT_ID;\n const defaultDataset = options.dataset ?? DEFAULT_DATASET;\n\n const backend: Backend = {\n registry: new Map(),\n organizationId: options.organizationId,\n log: [],\n stubs: new Map(),\n calls: [],\n concurrentEdits: new Map(),\n identities: new Map(Object.entries(options.identities ?? {})),\n writeChain: Promise.resolve(),\n };\n const registry = backend.registry;\n\n rejectAmbiguousIdentity(\"createTestClient()\", options);\n if (options.token !== undefined) {\n requireIdentity(backend, options.token, \"createTestClient()\");\n }\n\n if (options.resource !== undefined) validateResource(options.resource);\n\n // Seed defaults. `documents` seeds the root handle's own store.\n if (options.documents) {\n const root = options.resource ?? datasetResource(defaultProjectId, defaultDataset);\n ensureEntry(registry, root).store.seed(options.documents);\n }\n if (options.datasets) {\n for (const [key, docs] of Object.entries(options.datasets)) {\n const { projectId, dataset } = parseDatasetKey(key);\n ensureEntry(registry, datasetResource(projectId, dataset)).store.seed(docs);\n }\n }\n if (options.resources) {\n for (const [key, docs] of Object.entries(options.resources)) {\n ensureEntry(registry, parseResourceKey(key)).store.seed(docs);\n }\n }\n\n return makeHandle({\n backend,\n projectId: defaultProjectId,\n dataset: defaultDataset,\n resource: options.resource,\n accessControl: options.accessControl,\n perspective: options.perspective ?? \"raw\",\n requestTagPrefix: options.requestTagPrefix,\n mutationGuard: options.mutationGuard,\n token: options.token,\n useProjectHostname: options.useProjectHostname ?? true,\n });\n}\n\n/** Affected ids in order, de-duplicated — the `documentIds` of a result. */\nfunction dedupeIds(results: { id: string }[]): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const r of results) {\n if (!seen.has(r.id)) {\n seen.add(r.id);\n out.push(r.id);\n }\n }\n return out;\n}\n\n/**\n * Resolve a mutation's return value the way `@sanity/client` does, from\n * already-resolved `returnDocuments` / `returnFirst` booleans (callers\n * apply their own defaults: document-first for create-family,\n * ids-for-all for transaction / mutate).\n */\nfunction formatMutationResult(\n documents: SanityDocument[],\n results: { id: string; operation: MutationOperation }[],\n transactionId: string,\n returnDocuments: boolean,\n returnFirst: boolean,\n): TxnCommitResult {\n if (returnDocuments) {\n return returnFirst ? (documents[0] as SanityDocument) : documents;\n }\n if (returnFirst) {\n return {\n transactionId,\n documentId: results[0]?.id as string,\n results,\n } satisfies SingleMutationResult;\n }\n return {\n transactionId,\n documentIds: dedupeIds(results),\n results,\n } satisfies MultipleMutationResult;\n}\n\n/** Convert one raw `mutate()` operation object into a {@link TxnMutation}. */\nfunction toSingleTxnMutation(op: FakeMutation): TxnMutation {\n if (\"create\" in op) return { kind: \"create\", doc: op.create };\n if (\"createOrReplace\" in op) return { kind: \"createOrReplace\", doc: op.createOrReplace };\n if (\"createIfNotExists\" in op) return { kind: \"createIfNotExists\", doc: op.createIfNotExists };\n if (\"delete\" in op) return { kind: \"delete\", id: op.delete.id };\n\n const p = op.patch;\n const ops: PatchOp[] = [];\n if (p.set !== undefined) ops.push({ kind: \"set\", props: p.set });\n if (p.setIfMissing !== undefined) ops.push({ kind: \"setIfMissing\", props: p.setIfMissing });\n if (p.unset !== undefined) ops.push({ kind: \"unset\", paths: p.unset });\n if (p.inc !== undefined) ops.push({ kind: \"inc\", props: p.inc });\n if (p.dec !== undefined) ops.push({ kind: \"dec\", props: p.dec });\n if (p.insert !== undefined) {\n const { before, after, replace, items } = p.insert;\n const position = before !== undefined ? \"before\" : after !== undefined ? \"after\" : \"replace\";\n const anchor = before ?? after ?? replace;\n if (anchor === undefined) {\n throw fakeError(\n \"mutate(): a `patch.insert` operation must name exactly one anchor — `before`, `after`, \" +\n 'or `replace` — pointing at an array path (e.g. \"tags[-1]\" or `tags[_key==\"x\"]`), ' +\n \"alongside `items`. None was set.\",\n );\n }\n ops.push({ kind: \"insert\", position, anchor, items });\n }\n return {\n kind: \"patch\",\n documentId: p.id,\n ops,\n ...(p.ifRevisionID !== undefined ? { ifRev: p.ifRevisionID } : {}),\n };\n}\n\n/** Normalise `mutate()` input (raw array, Patch, or Transaction) to mutations. */\nfunction toTxnMutations(\n operations: FakeMutation[] | PatchHandle | TransactionHandle,\n): TxnMutation[] {\n if (Array.isArray(operations)) return operations.map(toSingleTxnMutation);\n // Both builders expose toJSON(); a Transaction yields an array, a Patch\n // yields a single `{ id, ops, ifRev? }`.\n const json = operations.toJSON();\n if (Array.isArray(json)) return json;\n return [\n {\n kind: \"patch\",\n documentId: json.id,\n ops: json.ops,\n ...(json.ifRev !== undefined ? { ifRev: json.ifRev } : {}),\n },\n ];\n}\n\ninterface MakeHandleArgs {\n backend: Backend;\n projectId: string;\n dataset: string;\n /** The `resource` config, when set; it decides the store over `(projectId, dataset)`. */\n resource: TestClientResource | undefined;\n accessControl: TestClientAccessControl | undefined;\n perspective: Perspective;\n requestTagPrefix: string | undefined;\n mutationGuard: MutationGuard | undefined;\n token: string | undefined;\n /**\n * Which API host this handle models: the project host (true, default)\n * or the global host (`withConfig({ useProjectHostname: false })`).\n * Decides which user record `/users/me` serves — project-scoped vs\n * account-global — exactly like production host routing.\n */\n useProjectHostname: boolean;\n}\n\nfunction makeHandle(handleArgs: MakeHandleArgs): TestClient {\n const {\n backend,\n resource,\n accessControl,\n perspective,\n requestTagPrefix,\n mutationGuard,\n token,\n useProjectHostname,\n } = handleArgs;\n const { registry } = backend;\n // A dataset resource names its own project and dataset, as in `@sanity/client`.\n const { projectId, dataset } =\n resource?.type === \"dataset\" ? datasetParts(resource.id) : handleArgs;\n /** The resource this handle reads and writes: its `resource` config, else its dataset. */\n const target = resource ?? datasetResource(projectId, dataset);\n\n /**\n * The handle's acting identity, if any. Token-bound handles resolve\n * from the shared registry on every access — so an `addIdentity`\n * upsert takes effect on already-minted siblings; `accessControl`\n * handles use their fixed config. Registry identities resolve their\n * project-scoped record for THIS handle's project (`usersByProject`\n * fallback `user`), so siblings in different projects see different\n * project-scoped ids for the same token — as in production.\n */\n const activeAccess = (): TestClientAccessControl | undefined => {\n if (token === undefined) return accessControl;\n const identity = backend.identities.get(token);\n if (identity === undefined) {\n // Bind-time validation makes this unreachable today; guard anyway\n // so a future identity-removal API fails loudly.\n throw fakeError(`no identity is registered for token \"${token}\" (was it removed?).`);\n }\n return {\n // Per-project records apply only where the resource lives in a project.\n currentUser:\n capabilitiesOf(target.type).identity === \"project\"\n ? projectUserOf(identity, projectId)\n : identity.user,\n ...(identity.globalUser !== undefined ? { globalUser: identity.globalUser } : {}),\n grants: identity.grants,\n ...(identity.access !== undefined ? { access: identity.access } : {}),\n };\n };\n\n /** Non-optional view for sites that only exist when an identity is bound. */\n const requireActiveAccess = (): TestClientAccessControl => {\n const access = activeAccess();\n if (access === undefined) {\n throw fakeError(\"this handle has no acting identity (no token or accessControl bound).\");\n }\n return access;\n };\n /**\n * Every principal the handle's member listings see: the registered\n * identities, plus the handle's own inline `accessControl` actor, which is\n * a member wherever its config gives it roles.\n */\n const members = (): TestClientIdentity[] => [\n ...backend.identities.values(),\n ...(token === undefined && accessControl !== undefined\n ? [\n {\n user: accessControl.currentUser,\n ...(accessControl.globalUser !== undefined\n ? { globalUser: accessControl.globalUser }\n : {}),\n grants: accessControl.grants,\n ...(accessControl.access !== undefined ? { access: accessControl.access } : {}),\n },\n ]\n : []),\n ];\n const capabilities = capabilitiesOf(target.type);\n /** How error messages name the target, e.g. `dataset \"test:test\"`. */\n const targetLabel = capabilities.label(target.id);\n const { store, history } = ensureEntry(registry, target);\n\n /** Key into {@link Backend.concurrentEdits} for a doc in this resource. */\n const concurrentEditKey = (id: string): string => registryKey(target) + \" \" + id;\n\n /**\n * The id grant filters see as `identity()` and history records as the\n * author: the project-scoped record on a dataset, the account-global\n * one on every other resource type.\n */\n const actorId = (): string | undefined => {\n const access = activeAccess();\n if (access === undefined) return undefined;\n return capabilities.identity === \"global\"\n ? (access.globalUser ?? access.currentUser).id\n : access.currentUser.id;\n };\n\n /** The store before the write in flight; see {@link write}. */\n let beforeWrite: Map<string, SanityDocument> | undefined;\n\n /**\n * Queue a write on the commit lock and remember the store's images before\n * it, so {@link commitHistory} can record exactly what the write changed.\n */\n const write = <T>(run: () => Promise<T>): Promise<T> =>\n serializeWrite(backend, async () => {\n beforeWrite = new Map(store.all().map((doc) => [doc._id, doc]));\n try {\n return await run();\n } finally {\n beforeWrite = undefined;\n }\n });\n\n /** Record the write in flight in the resource's history, under `transactionId`. */\n const commitHistory = (transactionId: string): void => {\n if (beforeWrite === undefined) return;\n history.commit({ transactionId, author: actorId(), before: beforeWrite, after: store.all() });\n };\n\n /** Append a committed transaction to the shared log. */\n const record = (transactionId: string, mutations: TxnMutation[], tag?: string | null): void => {\n const effectiveTag = effectiveRequestTag(tag);\n commitHistory(transactionId);\n backend.log.push({\n transactionId,\n projectId,\n dataset,\n ...(resource !== undefined ? { resource } : {}),\n mutations,\n ...(effectiveTag !== undefined ? { tag: effectiveTag } : {}),\n });\n };\n\n const effectiveRequestTag = (tag: string | null | undefined): string | null | undefined => {\n if (requestTagPrefix === undefined) return tag;\n return tag ? `${requestTagPrefix}.${tag}` : requestTagPrefix;\n };\n\n /**\n * Shape a single-document write result. Create-family methods default\n * to returning the document (first), unlike transaction / mutate.\n */\n const writeResult = (\n documents: SanityDocument[],\n results: { id: string; operation: MutationOperation }[],\n transactionId: string,\n opts: MutationOptions | undefined,\n ): TxnCommitResult =>\n formatMutationResult(\n documents,\n results,\n transactionId,\n opts?.returnDocuments ?? true,\n opts?.returnFirst ?? true,\n );\n\n const promote = (doc: SanityDocumentLike): SanityDocument & SystemFields => {\n const now = new Date().toISOString();\n if (typeof doc._id !== \"string\" || doc._id.length === 0) {\n throw fakeError(\n `Cannot store a document without a non-empty string \\`_id\\` (got ${JSON.stringify(doc._id)}). ` +\n `Unlike @sanity/client, this fake never auto-generates ids — give every document an explicit _id.`,\n );\n }\n if (typeof doc._type !== \"string\" || doc._type.length === 0) {\n throw fakeError(\n `Document \"${doc._id}\" is missing a non-empty string \\`_type\\` (got ${JSON.stringify(doc._type)}). ` +\n `Every Sanity document needs a _type.`,\n );\n }\n return {\n ...doc,\n _id: doc._id,\n _type: doc._type,\n _rev: cryptoRandomKey(),\n _createdAt: doc._createdAt ?? now,\n _updatedAt: now,\n } as SanityDocument & SystemFields;\n };\n\n const checkRevision = (id: string, ifRevisionId: string | undefined) => {\n if (ifRevisionId === undefined) return;\n // A pending simulated concurrent edit beats the real revision check —\n // the guarded write loses the race even if its _rev currently matches.\n const editKey = concurrentEditKey(id);\n const pendingEdits = backend.concurrentEdits.get(editKey) ?? 0;\n if (pendingEdits > 0) {\n backend.concurrentEdits.set(editKey, pendingEdits - 1);\n throw fakeError(\n `ifRevisionId check failed: document \"${id}\" was concurrently edited (simulated via ` +\n `simulateConcurrentEdit) — the revision-guarded mutation lost the race; re-read and retry. ` +\n `(This mirrors Sanity's optimistic-locking 409.)`,\n );\n }\n const existing = store.get(id);\n if (existing === undefined) {\n throw fakeError(\n `ifRevisionId check failed: document \"${id}\" does not exist, so its revision cannot match ` +\n `the expected _rev=\"${ifRevisionId}\". Drop ifRevisionId when creating a new document, or ` +\n `seed/create the document before guarding on its revision.`,\n );\n }\n if (existing._rev !== ifRevisionId) {\n throw fakeError(\n `ifRevisionId check failed: document \"${id}\" is at _rev=\"${existing._rev}\" but the mutation ` +\n `required _rev=\"${ifRevisionId}\". The document changed since you read it — re-read it and ` +\n `retry. (This mirrors Sanity's optimistic-locking 409.)`,\n );\n }\n };\n\n /**\n * Gate a mutation through the configured `accessControl.grants`. No-op\n * when accessControl is absent (legacy bench behavior). On denial\n * throws `WriteAccessDeniedError` — same failure mode tests get from\n * a real Sanity 403.\n *\n * Pass the doc as-it-would-be at evaluation time: for `create` /\n * `createOrReplace`-as-create / `createIfNotExists`, that's the new\n * doc with its incoming fields; for `update` / `patch` / `delete`,\n * that's the existing doc (the grant filter identifies what the\n * actor can touch).\n */\n const gate = async (\n document: SanityDocumentLike & { _id?: string; _type?: string },\n permission: DocumentValuePermission,\n ): Promise<void> => {\n const access = activeAccess();\n if (access === undefined) return;\n await enforceWriteAccess({\n document,\n grants: access.grants,\n permission: document._id !== undefined && isSystemId(document._id) ? \"manage\" : permission,\n userId: actorId() as string,\n });\n };\n\n const guardGate = async (contexts: readonly MutationGuardImageContext[]): Promise<void> => {\n const identity = actorId();\n await enforceMutationGuards(\n mutationGuard,\n contexts.map((context) => ({ ...context, identity })),\n );\n };\n\n const guardOne = async (\n action: MutationGuardContext[\"action\"],\n before: MutationGuardContext[\"before\"],\n after: MutationGuardContext[\"after\"],\n ): Promise<void> => {\n await guardGate([{ action, before, after }]);\n };\n\n /**\n * Read-gate predicate. Sanity surfaces invisible docs as \"doesn't\n * exist\" on the wire (404 / null) rather than throwing 403 — so\n * reads filter rather than refuse.\n *\n * - `fetch` filters the dataset before running GROQ.\n * - `getDocument` / `getDocuments` return null for invisible docs.\n * - `snapshot` is a test-helper that bypasses the read gate\n * intentionally (tests need raw inspection).\n *\n * Returns `true` when accessControl is absent (no gating).\n */\n const canRead = async (\n document: SanityDocumentLike & { _id?: string; _type?: string },\n ): Promise<boolean> => {\n const access = activeAccess();\n if (access === undefined) return true;\n return canAccess({\n document,\n grants: access.grants,\n permission: \"read\",\n userId: actorId() as string,\n });\n };\n\n const applyPatch = async (\n documentId: string,\n ops: PatchOp[],\n ifRev: string | undefined,\n options: { evaluateGuard?: boolean } = {},\n ): Promise<SanityDocument> => {\n const existing = store.get(documentId);\n if (existing === undefined) {\n throw fakeError(\n `Cannot patch document \"${documentId}\": not found — no document with that _id exists in ` +\n `${targetLabel}. patch() requires the target to already exist; use ` +\n `createOrReplace() / createIfNotExists() to upsert, or seed the document first.`,\n );\n }\n checkRevision(documentId, ifRev);\n // Gate against the existing doc — that's what the grant filter\n // matches: \"what docs can this actor update?\".\n await gate(existing, \"update\");\n const patched = applyPatchOps(existing, ops);\n const now = new Date().toISOString();\n const next: SanityDocument = {\n ...patched,\n _rev: cryptoRandomKey(),\n _updatedAt: now,\n };\n if (options.evaluateGuard !== false) await guardOne(\"update\", existing, next);\n store.put(next);\n return next;\n };\n\n const applyTransaction = async (\n mutations: TxnMutation[],\n txnOptions?: TxnCommitOptions,\n ): Promise<TxnCommitResult> => {\n const transactionId = txnOptions?.transactionId ?? cryptoRandomKey();\n const snapshot = new Map<string, SanityDocument | undefined>();\n const touchedIds: string[] = [];\n const operations: { id: string; operation: MutationOperation }[] = [];\n\n const remember = (id: string) => {\n if (!snapshot.has(id)) snapshot.set(id, store.get(id));\n };\n\n try {\n for (const mut of mutations) {\n switch (mut.kind) {\n case \"create\": {\n remember(mut.doc._id as string);\n const promoted = promote(mut.doc);\n if (store.get(promoted._id) !== undefined) {\n throw fakeError(\n `create() failed: a document with _id \"${promoted._id}\" already exists in ` +\n `${targetLabel}. Use createOrReplace() to overwrite it, or ` +\n `createIfNotExists() to leave the existing one untouched.`,\n );\n }\n await gate(promoted, \"create\");\n store.put(promoted);\n touchedIds.push(promoted._id);\n operations.push({ id: promoted._id, operation: \"create\" });\n break;\n }\n case \"createOrReplace\": {\n remember(mut.doc._id as string);\n const promoted = promote(mut.doc);\n const existing = store.get(promoted._id);\n // For an existing doc, \"update\" is the relevant permission;\n // for a net-new one, \"create\".\n await gate(existing ?? promoted, existing !== undefined ? \"update\" : \"create\");\n const wasPresent = existing !== undefined;\n store.put(promoted);\n touchedIds.push(promoted._id);\n operations.push({ id: promoted._id, operation: wasPresent ? \"update\" : \"create\" });\n break;\n }\n case \"createIfNotExists\": {\n remember(mut.doc._id as string);\n if (store.get(mut.doc._id as string) === undefined) {\n const promoted = promote(mut.doc);\n await gate(promoted, \"create\");\n store.put(promoted);\n touchedIds.push(promoted._id);\n operations.push({ id: promoted._id, operation: \"create\" });\n }\n break;\n }\n case \"delete\": {\n remember(mut.id);\n const existing = store.get(mut.id);\n // Sanity's permission model has no separate \"delete\" —\n // delete is gated as an update against the existing doc.\n if (existing !== undefined) await gate(existing, \"update\");\n const removed = store.remove(mut.id);\n if (removed) {\n touchedIds.push(mut.id);\n operations.push({ id: mut.id, operation: \"delete\" });\n }\n break;\n }\n case \"patch\": {\n remember(mut.documentId);\n // applyPatch already gates internally.\n await applyPatch(mut.documentId, mut.ops, mut.ifRev, { evaluateGuard: false });\n touchedIds.push(mut.documentId);\n operations.push({ id: mut.documentId, operation: \"update\" });\n break;\n }\n }\n }\n const contexts = mutationContexts(\n [...snapshot].map(([id, before]) => ({ before, after: store.get(id) })),\n );\n await guardGate(contexts);\n } catch (error) {\n for (const [id, doc] of snapshot) {\n if (doc === undefined) {\n store.remove(id);\n } else {\n store.put(doc);\n }\n }\n throw error;\n }\n\n // A committed transaction is logged whole; a single-mutation form\n // (create / patch / mutate of one op) lands as a one-entry transaction.\n record(transactionId, mutations, txnOptions?.tag);\n\n const documents = touchedIds\n .map((id) => store.get(id))\n .filter((d): d is SanityDocument => d !== undefined);\n // Transaction / mutate default to the id-shaped `MultipleMutationResult`,\n // matching `@sanity/client` (create-family defaults differ).\n return formatMutationResult(\n documents,\n operations,\n transactionId,\n txnOptions?.returnDocuments ?? false,\n txnOptions?.returnFirst ?? false,\n );\n };\n\n /**\n * The action-API write path, on the same commit lock as every mutation.\n * Document publish/unpublish actions run through the access gate and expose\n * their net draft/published mutations to `mutationGuard`; release/version\n * actions apply unhooked.\n */\n const runAction = async (actions: Action | Action[]): Promise<ActionResult> => {\n // Refused before the write lock, as the platform refuses before applying anything.\n const request = {\n method: \"POST\" as const,\n path: `${capabilities.dataBase(target.id)}/actions`,\n };\n if (capabilities.actions !== \"served\") throw capabilities.actions(request);\n for (const action of Array.isArray(actions) ? actions : [actions]) {\n const refused = capabilities.refusedActions[action.actionType];\n if (refused !== undefined) throw refused(request);\n }\n return write(async () => {\n const result = await applyActions(store, actions, { gate, guard: guardGate });\n commitHistory(result.transactionId);\n return result;\n });\n };\n\n /**\n * Filter the store's docs to only those `canRead` returns true\n * for. When accessControl is unset, returns the full set (no\n * filtering cost). Used by `fetch` so GROQ runs against the\n * actor-visible subset, matching Sanity's per-token query behavior.\n */\n const accessVisibleDocs = async (): Promise<SanityDocument[]> => {\n const all = store.all();\n if (activeAccess() === undefined) return all;\n const out: SanityDocument[] = [];\n for (const doc of all) {\n if (await canRead(doc)) out.push(doc);\n }\n return out;\n };\n\n /**\n * Apply both access-control gating and perspective layering. The order\n * matters: access first (an invisible doc shouldn't even be a candidate\n * for perspective layering), perspective second (which can hide / merge\n * docs that the actor would otherwise see).\n */\n const visibleDocs = async (override?: Perspective): Promise<SanityDocument[]> => {\n const accessible = await accessVisibleDocs();\n return applyPerspective(accessible, override ?? perspective);\n };\n\n const client: TestClient = {\n fetch: async (query, params, opts) => {\n const start = Date.now();\n const tree = parse(query, params ? { params } : undefined);\n const value = await evaluate(tree, {\n dataset: await visibleDocs(opts?.perspective),\n ...(params !== undefined ? { params } : {}),\n });\n const result = await value.get();\n if (opts?.filterResponse === false) {\n const ms = Date.now() - start;\n const raw = opts.returnQuery === false ? { ms, result } : { query, ms, result };\n return raw as never;\n }\n return result as never;\n },\n\n getDocument: async (id, opts) => {\n const effective = opts?.perspective ?? perspective;\n if (effective === \"raw\") {\n const doc = store.get(id);\n if (doc === undefined) return undefined;\n if (!(await canRead(doc))) return undefined;\n return doc as never;\n }\n const view = await visibleDocs(effective);\n const found = resolveDocument(view, id, effective);\n return (found ?? undefined) as never;\n },\n\n getDocuments: async (ids, opts) => {\n const effective = opts?.perspective ?? perspective;\n if (effective === \"raw\") {\n const result: (SanityDocument | null)[] = [];\n for (const id of ids) {\n const doc = store.get(id);\n if (doc === undefined) result.push(null);\n else if (await canRead(doc)) result.push(doc);\n else result.push(null);\n }\n return result as never;\n }\n const view = await visibleDocs(effective);\n return ids.map((id) => resolveDocument(view, id, effective) ?? null) as never;\n },\n\n getDataUrl: (operation, path) => dataUrl({ resource, dataset, operation, path }),\n\n create: (doc, opts) =>\n write(async () => {\n checkRevision(doc._id as string, opts?.ifRevisionId);\n if (store.get(doc._id as string) !== undefined) {\n throw fakeError(\n `create() failed: a document with _id \"${doc._id}\" already exists in ` +\n `${targetLabel}. Use createOrReplace() to overwrite it, or ` +\n `createIfNotExists() to leave the existing one untouched.`,\n );\n }\n const promoted = promote(doc);\n await gate(promoted, \"create\");\n await guardOne(\"create\", null, promoted);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n if (opts?.dryRun !== true) {\n store.put(promoted);\n record(transactionId, [{ kind: \"create\", doc }], opts?.tag);\n }\n return writeResult(\n [promoted],\n [{ id: promoted._id, operation: \"create\" }],\n transactionId,\n opts,\n ) as never;\n }),\n\n createOrReplace: (doc, opts) =>\n write(async () => {\n checkRevision(doc._id as string, opts?.ifRevisionId);\n const promoted = promote(doc);\n const existing = store.get(doc._id as string);\n await gate(existing ?? promoted, existing !== undefined ? \"update\" : \"create\");\n await guardOne(existing !== undefined ? \"update\" : \"create\", existing ?? null, promoted);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n if (opts?.dryRun !== true) {\n store.put(promoted);\n record(transactionId, [{ kind: \"createOrReplace\", doc }], opts?.tag);\n }\n const operation: MutationOperation = existing !== undefined ? \"update\" : \"create\";\n return writeResult(\n [promoted],\n [{ id: promoted._id, operation }],\n transactionId,\n opts,\n ) as never;\n }),\n\n createIfNotExists: (doc, opts) =>\n write(async () => {\n checkRevision(doc._id as string, opts?.ifRevisionId);\n const existing = store.get(doc._id as string);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n if (existing !== undefined) {\n return writeResult(\n [existing],\n [{ id: existing._id, operation: \"none\" }],\n transactionId,\n opts,\n ) as never;\n }\n const promoted = promote(doc);\n await gate(promoted, \"create\");\n await guardOne(\"create\", null, promoted);\n if (opts?.dryRun !== true) {\n store.put(promoted);\n record(transactionId, [{ kind: \"createIfNotExists\", doc }], opts?.tag);\n }\n return writeResult(\n [promoted],\n [{ id: promoted._id, operation: \"create\" }],\n transactionId,\n opts,\n ) as never;\n }),\n\n delete: (id, opts) =>\n write(async () => {\n checkRevision(id, opts?.ifRevisionId);\n const existing = store.get(id);\n if (existing !== undefined) await gate(existing, \"update\");\n if (existing !== undefined) await guardOne(\"delete\", existing, null);\n const transactionId = opts?.transactionId ?? cryptoRandomKey();\n let removed: SanityDocument | undefined = existing;\n if (opts?.dryRun !== true) {\n removed = store.remove(id);\n if (removed !== undefined) record(transactionId, [{ kind: \"delete\", id }], opts?.tag);\n }\n // Legacy default shape ({ _id } | null) unless the caller asked for\n // a specific return shape via returnDocuments / returnFirst.\n if (opts?.returnDocuments === undefined && opts?.returnFirst === undefined) {\n return (removed !== undefined ? { _id: id } : null) as never;\n }\n const results = removed !== undefined ? [{ id, operation: \"delete\" as const }] : [];\n const docs = removed !== undefined ? [removed] : [];\n return formatMutationResult(\n docs,\n results,\n transactionId,\n opts.returnDocuments ?? true,\n opts.returnFirst ?? true,\n ) as never;\n }),\n\n patch: (documentId) =>\n createPatch(documentId, {\n apply: (docId, ops, ifRev, options) =>\n write(async () => {\n const doc = await applyPatch(docId, ops, ifRev);\n record(\n cryptoRandomKey(),\n [\n {\n kind: \"patch\",\n documentId: docId,\n ops,\n ...(ifRev !== undefined ? { ifRev } : {}),\n },\n ],\n options?.tag,\n );\n return doc;\n }),\n }),\n\n transaction: () =>\n createTransaction({\n apply: (mutations, txnOptions) => write(() => applyTransaction(mutations, txnOptions)),\n }),\n\n mutate: (operations, options) =>\n write(() => applyTransaction(toTxnMutations(operations), options)),\n\n // Synchronous writes still take the lock: an action landing while a\n // guarded patch is parked in its gate would otherwise be clobbered\n // when that patch resumes over its pre-action snapshot.\n action: (action) => runAction(action),\n\n releases: {\n create: async ({ releaseId, metadata }) => {\n const result = await runAction({\n actionType: \"sanity.action.release.create\",\n releaseId,\n ...(metadata !== undefined ? { metadata } : {}),\n });\n const doc = store.get(`_.releases.${releaseId}`) as ReleaseDocument | undefined;\n return {\n ...result,\n releaseId,\n metadata: doc?.metadata ?? { releaseType: \"undecided\" },\n };\n },\n edit: ({ releaseId, patch }) =>\n runAction({ actionType: \"sanity.action.release.edit\", releaseId, patch }),\n get: async ({ releaseId }) => {\n const doc = store.get(`_.releases.${releaseId}`) as ReleaseDocument | undefined;\n return doc;\n },\n fetchDocuments: async ({ releaseId }) => {\n const prefix = `versions.${releaseId}.`;\n return store.all().filter((d) => d._id.startsWith(prefix));\n },\n schedule: ({ releaseId, publishAt }) =>\n runAction({\n actionType: \"sanity.action.release.schedule\",\n releaseId,\n publishAt,\n }),\n unschedule: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.unschedule\", releaseId }),\n publish: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.publish\", releaseId }),\n archive: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.archive\", releaseId }),\n unarchive: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.unarchive\", releaseId }),\n delete: ({ releaseId }) =>\n runAction({ actionType: \"sanity.action.release.delete\", releaseId }),\n },\n\n createVersion: ({ releaseId, publishedId, document }) =>\n runAction({\n actionType: \"sanity.action.document.version.create\",\n releaseId,\n publishedId,\n document,\n }),\n\n unpublishVersion: ({ releaseId, publishedId }) =>\n runAction({\n actionType: \"sanity.action.document.version.unpublish\",\n versionId: `versions.${releaseId}.${publishedId}`,\n publishedId,\n }),\n\n ...(token !== undefined || accessControl !== undefined\n ? {\n request: makeRequestImpl({\n getAccess: requireActiveAccess,\n projectId,\n // `@sanity/client` sends every request of a `resource`-configured\n // client to the global API host.\n useProjectHostname: useProjectHostname && resource === undefined,\n organizationId: backend.organizationId,\n listIdentities: members,\n platform: {\n projectId,\n organizationId: backend.organizationId,\n own: (asked) =>\n registryKey(asked) === registryKey(target)\n ? {\n grants: requireActiveAccess().grants,\n documents: () => store.all(),\n history,\n canRead,\n }\n : undefined,\n actorUser: () => {\n const access = requireActiveAccess();\n return access.globalUser ?? access.currentUser;\n },\n actorAccess: () => requireActiveAccess().access,\n identities: members,\n resources: () => [...registry.values()].map((entry) => entry.resource),\n },\n }),\n }\n : {}),\n\n config: () => ({\n projectId,\n dataset,\n ...(resource !== undefined ? { resource } : {}),\n perspective,\n ...(requestTagPrefix !== undefined ? { requestTagPrefix } : {}),\n ...(token !== undefined ? { token } : {}),\n useProjectHostname,\n }),\n\n withConfig: (config) => {\n rejectAmbiguousIdentity(\"withConfig()\", config);\n if (config.token !== undefined) {\n requireIdentity(backend, config.token, \"withConfig()\");\n }\n // Naming either identity mechanism switches the sibling to it\n // wholesale; naming neither inherits this handle's binding.\n const rebinds = config.token !== undefined || config.accessControl !== undefined;\n // `resource: undefined` drops the resource; omitting the key keeps it.\n const nextResource = \"resource\" in config ? config.resource : resource;\n if (nextResource !== undefined) validateResource(nextResource);\n return makeHandle({\n backend,\n projectId: config.projectId ?? projectId,\n dataset: config.dataset ?? dataset,\n resource: nextResource,\n accessControl: rebinds ? config.accessControl : accessControl,\n perspective: config.perspective ?? perspective,\n requestTagPrefix: config.requestTagPrefix ?? requestTagPrefix,\n mutationGuard,\n token: rebinds ? config.token : token,\n useProjectHostname: config.useProjectHostname ?? useProjectHostname,\n });\n },\n\n seed: (documents) => store.seed(documents),\n add: (documents) => store.add(documents),\n reset: () => store.reset(),\n snapshot: () => store.snapshot(),\n store: () => store,\n simulateConcurrentEdit: (id, count = 1) => {\n const editKey = concurrentEditKey(id);\n backend.concurrentEdits.set(editKey, (backend.concurrentEdits.get(editKey) ?? 0) + count);\n },\n datasets: () =>\n [...registry.values()]\n .filter((entry) => entry.resource.type === \"dataset\")\n .map((entry) => datasetParts(entry.resource.id)),\n resources: () => [...registry.values()].map((entry) => ({ ...entry.resource })),\n stub: (name, handler) => {\n backend.stubs.set(name, handler);\n },\n stubs: () => Object.fromEntries(backend.stubs),\n addIdentity: (identityToken, identity) => {\n backend.identities.set(identityToken, identity);\n },\n identities: () => Object.fromEntries(backend.identities),\n mutations: () => backend.log.flatMap((entry) => entry.mutations),\n transactions: () => [...backend.log],\n clearLog: () => {\n backend.log.length = 0;\n },\n calls: (method) =>\n method === undefined ? [...backend.calls] : backend.calls.filter((c) => c.method === method),\n callCount: (method) =>\n method === undefined\n ? backend.calls.length\n : backend.calls.filter((c) => c.method === method).length,\n clearCalls: () => {\n backend.calls.length = 0;\n },\n };\n\n // Record an invocation: capture args now, fill the outcome on settle.\n const onCall = (method: string, args: unknown[]): CallSettler => {\n const rec: CallRecord = {\n method,\n args,\n projectId,\n dataset,\n ...(resource !== undefined ? { resource } : {}),\n };\n const tag =\n OPTIONS_ARG_POSITION[method] === undefined\n ? undefined\n : effectiveRequestTag(tagFromArgs(method, args));\n if (tag !== undefined) rec.tag = tag;\n backend.calls.push(rec);\n return (outcome) => {\n if (\"error\" in outcome) rec.error = outcome.error;\n else rec.result = outcome.result;\n };\n };\n\n // Wrap so unimplemented members fail with actionable errors, registered\n // stubs are served, and surface-method calls are recorded (see\n // ./unsupported.ts).\n return wrapWithGuards(client, {\n getStub: (name) => backend.stubs.get(name),\n onCall,\n recorded: RECORDED_METHODS,\n });\n}\n"],"names":["uri","rest","resource","parse","evaluate","VERSIONS_PREFIX","RELEASE_DOC_PREFIX","promote"],"mappings":"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"}
|